Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit b95f1f7

Browse files
committed
test: unit tests for Abstract, with documentation
1 parent 54a6ba7 commit b95f1f7

File tree

5 files changed

+240
-18
lines changed

5 files changed

+240
-18
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ node_modules/
33
examples/deep-copy/
44
examples/path/
55
examples/filter-copy/
6+
.nyc_output/
7+
coverage/

README.md

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ same as the intended size, if the size is set.
2121

2222
```javascript
2323
fstream
24-
.Writer({ path: "path/to/file"
25-
, mode: 0755
26-
, size: 6
27-
})
28-
.write("hello\n")
24+
.Writer({
25+
path: 'path/to/file',
26+
mode: parseInt('0755', 8),
27+
size: 6
28+
})
29+
.write('hello\n')
2930
.end()
3031
```
3132

@@ -35,24 +36,26 @@ been written when it's done.
3536

3637
```javascript
3738
fstream
38-
.Writer({ path: "path/to/file"
39-
, mode: 0755
40-
, size: 6
41-
, flags: "a"
42-
})
43-
.write("hello\n")
39+
.Writer({
40+
path: 'path/to/file',
41+
mode: parseInt('0755', 8),
42+
size: 6,
43+
flags: 'a'
44+
})
45+
.write('hello\n')
4446
.end()
4547
```
4648

4749
You can pass flags in, if you want to append to a file.
4850

4951
```javascript
5052
fstream
51-
.Writer({ path: "path/to/symlink"
52-
, linkpath: "./file"
53-
, SymbolicLink: true
54-
, mode: "0755" // octal strings supported
55-
})
53+
.Writer({
54+
path: 'path/to/symlink',
55+
linkpath: './file',
56+
SymbolicLink: true,
57+
mode: '0755' // octal strings supported
58+
})
5659
.end()
5760
```
5861

@@ -74,3 +77,56 @@ This will do like `cp -Rp path/to/dir path/to/other/dir`. If the other
7477
dir exists and isn't a directory, then it'll emit an error. It'll also
7578
set the uid, gid, mode, etc. to be identical. In this way, it's more
7679
like `rsync -a` than simply a copy.
80+
81+
# API
82+
83+
## Abstract (extends `Stream`)
84+
85+
A base class that extends [`Stream`](https://nodejs.org/api/stream.html) with
86+
useful utility methods. `fstream` streams are based on [streams1
87+
semantics](https://gist.github.com/caike/ebccc95bd46f5fa1404d#file-streams-1-js).
88+
89+
### events
90+
91+
- `abort`: Stop further processing on the stream.
92+
- `ready`: The stream is ready for reading; handlers passed to `.on()` will
93+
still be called if the stream is ready even if they're added after `ready` is
94+
emitted.
95+
- `info`: Quasi-logging event emitted for diagnostic information.
96+
- `warn`: Quasi-logging event emitted on non-fatal errors.
97+
- `error`: Quasi-logging event emitted on fatal errors.
98+
99+
### properties
100+
101+
- `ready`: Whether the current file stream is ready to start processing. _Default: `false`_
102+
- `path`: Path to the filesystem object this node is bound to.
103+
- `linkpath`: Target path to which a link points.
104+
- `type`: What type of filesystem entity this file stream node points to.
105+
106+
### abstract.abort()
107+
108+
Stop any further processing on the file stream by setting `this._aborted`; for
109+
use by subclasses.
110+
111+
### abstract.destroy()
112+
113+
Abstract base method; overrides `Stream`'s `destroy` as a no-op.
114+
115+
### abstract.info(msg, code)
116+
117+
Quasi-logging method.
118+
119+
Emits an `info` event with `msg` and `code` attached.
120+
121+
### abstract.warn(msg, code)
122+
123+
Quasi-logging method.
124+
125+
Emits a `warn` event if it has any listeners; otherwise prints out an error
126+
object decorated with `msg` and `code` to stderr.
127+
128+
### abstract.error(msg, code, throw)
129+
130+
If `throw` is true, throw an Error decorated with the message or code.
131+
Otherwise, emit `error` with the decorated Error. `msg` can also be an Error
132+
object itself; it will be wrapped in a new Error before being annotated.

lib/abstract.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ var inherits = require('inherits')
77

88
function Abstract () {
99
Stream.call(this)
10+
this.ready = false
11+
this.path = null
12+
this.linkpath = null
13+
this.type = null
14+
this._aborted = false
15+
this._path = null
16+
this._paused = false
1017
}
1118

1219
inherits(Abstract, Stream)
@@ -30,7 +37,7 @@ Abstract.prototype.destroy = function () {}
3037
Abstract.prototype.warn = function (msg, code) {
3138
var self = this
3239
var er = decorate(msg, code, self)
33-
if (!self.listeners('warn')) {
40+
if (!self.listeners('warn').length) {
3441
console.error('%s %s\n' +
3542
'path = %s\n' +
3643
'syscall = %s\n' +

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"tap": "^5.7.1"
2323
},
2424
"scripts": {
25-
"test": "standard && tap examples/*.js"
25+
"test": "standard && tap --coverage test/*.js",
26+
"test-legacy": "tap examples/*.js",
27+
"coverage-report": "tap --coverage --coverage-report=html examples/*.js test/*.js"
2628
},
2729
"license": "ISC"
2830
}

test/abstract.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
var format = require('util').format
2+
3+
var test = require('tap').test
4+
5+
var Abstract = require('../').Abstract
6+
7+
test('basic Abstract contract', function (t) {
8+
t.doesNotThrow(function () {
9+
t.ok(new Abstract())
10+
})
11+
var fstream = new Abstract()
12+
t.is(typeof fstream.on, 'function')
13+
14+
// extra ways to end streams
15+
t.is(typeof fstream.abort, 'function')
16+
t.is(typeof fstream.destroy, 'function')
17+
18+
// loggingish functions
19+
t.is(typeof fstream.warn, 'function')
20+
t.is(typeof fstream.info, 'function')
21+
t.is(typeof fstream.error, 'function')
22+
23+
t.end()
24+
})
25+
26+
test('calls "ready" callbacks even after event emitted', function (t) {
27+
var fstream = new Abstract()
28+
fstream.ready = true
29+
fstream.on('ready', function () {
30+
t.is(this._aborted, false, 'this is bound correctly')
31+
// called asap even though ready isn't emitted
32+
t.end()
33+
})
34+
})
35+
36+
test('aborting abstractly', function (t) {
37+
var fstream = new Abstract()
38+
// gross, but no other way to observe this state for the base class
39+
t.is(fstream._aborted, false)
40+
fstream.on('abort', function () {
41+
// see above
42+
t.is(fstream._aborted, true)
43+
t.end()
44+
})
45+
46+
fstream.abort()
47+
})
48+
49+
test('destroying abstractly', function (t) {
50+
var fstream = new Abstract()
51+
t.doesNotThrow(function () { fstream.destroy() }, 'do nothing')
52+
t.end()
53+
})
54+
55+
test('informing abstractly', function (t) {
56+
var fstream = new Abstract()
57+
t.doesNotThrow(function () { fstream.info('hi', 'EYO') })
58+
fstream.on('info', function (message, code) {
59+
t.is(message, 'yup')
60+
t.is(code, 'EHOWDY')
61+
t.end()
62+
})
63+
64+
fstream.info('yup', 'EHOWDY')
65+
})
66+
67+
test('warning abstractly', function (t) {
68+
t.test('emits with a listener', function (t) {
69+
var fstream = new Abstract()
70+
fstream.path = '/dev/null'
71+
fstream.on('warn', function (err) {
72+
t.is(err.message, 'hi')
73+
t.is(err.code, 'EFRIENDLY')
74+
t.is(err.fstream_class, 'Abstract')
75+
t.is(err.fstream_path, '/dev/null')
76+
})
77+
78+
fstream.warn('hi', 'EFRIENDLY')
79+
t.end()
80+
})
81+
82+
t.test('prints without a listener', function (t) {
83+
var fstream = new Abstract()
84+
fstream.path = '/dev/null'
85+
var _error = console.error
86+
console.error = function () {
87+
console.error = _error
88+
var formatted = format.apply(console, [].slice.call(arguments))
89+
t.matches(formatted, /^EUNFRIENDLY Error: ono/)
90+
t.matches(formatted, /fstream_class = Abstract/)
91+
t.matches(formatted, /path = \/dev\/null/)
92+
t.end()
93+
}
94+
95+
fstream.warn('ono', 'EUNFRIENDLY')
96+
})
97+
98+
t.test('prints without a listener and defaults to code of UNKNOWN', function (t) {
99+
var fstream = new Abstract()
100+
fstream.path = '/dev/null'
101+
var _error = console.error
102+
console.error = function () {
103+
console.error = _error
104+
var formatted = format.apply(console, [].slice.call(arguments))
105+
t.matches(formatted, /^UNKNOWN Error: wow mom/)
106+
t.matches(formatted, /fstream_class = Abstract/)
107+
t.matches(formatted, /path = \/dev\/null/)
108+
t.end()
109+
}
110+
111+
fstream.warn('wow mom')
112+
})
113+
114+
t.end()
115+
})
116+
117+
test('erroring abstractly', function (t) {
118+
t.test('emits by default if handler set', function (t) {
119+
var fstream = new Abstract()
120+
t.throws(
121+
function () { fstream.error('whoops', 'EYIKES') },
122+
{ message: 'whoops', code: 'EYIKES' },
123+
'streams throw if no handler is set'
124+
)
125+
126+
fstream.linkpath = '/road/to/nowhere'
127+
fstream.on('error', function (err) {
128+
t.is(err.message, 'candygram!')
129+
t.is(err.code, 'ELANDSHARK')
130+
t.is(err.fstream_linkpath, '/road/to/nowhere')
131+
t.end()
132+
})
133+
134+
fstream.error(new Error('candygram!'), 'ELANDSHARK')
135+
})
136+
137+
t.test('throws when told to do so', function (t) {
138+
var fstream = new Abstract()
139+
140+
fstream.linkpath = '/floor/13'
141+
142+
t.throws(
143+
function () { fstream.error('candyman!', 'EBEES', true) },
144+
{
145+
message: 'candyman!',
146+
code: 'EBEES',
147+
fstream_linkpath: '/floor/13',
148+
fstream_class: 'Abstract'
149+
}
150+
)
151+
t.end()
152+
})
153+
154+
t.end()
155+
})

0 commit comments

Comments
 (0)