update
This commit is contained in:
parent
d9becc67b6
commit
9308795b8b
964 changed files with 104265 additions and 16 deletions
9
node_modules/sonic-boom/.travis.yml
generated
vendored
Normal file
9
node_modules/sonic-boom/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '6'
|
||||
- '8'
|
||||
- '10'
|
||||
- '11'
|
||||
- '12'
|
||||
- '13'
|
21
node_modules/sonic-boom/LICENSE
generated
vendored
Normal file
21
node_modules/sonic-boom/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Matteo Collina
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
100
node_modules/sonic-boom/README.md
generated
vendored
Normal file
100
node_modules/sonic-boom/README.md
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
# sonic-boom [](https://travis-ci.org/mcollina/sonic-boom)
|
||||
|
||||
Extremely fast utf8-only stream implementation to write to files and
|
||||
file descriptors.
|
||||
|
||||
This implementation is partial, but support backpressure and `.pipe()` in is here.
|
||||
However, it is 2-3x faster than Node Core `fs.createWriteStream()`:
|
||||
|
||||
```
|
||||
benchSonic*1000: 2215.220ms
|
||||
benchSonicSync*1000: 8315.173ms
|
||||
benchSonic4k*1000: 2184.558ms
|
||||
benchSonicSync4k*1000: 1733.582ms
|
||||
benchCore*1000: 6513.752ms
|
||||
```
|
||||
|
||||
Note that sync mode without buffering is _slower_ than a Node Core WritableStream, however
|
||||
this mode matches the expected behavior of `console.log()`.
|
||||
|
||||
Note that if this is used to log to a windows terminal (`cmd.exe` or
|
||||
powershell), it is needed to run `chcp 65001` in the terminal to
|
||||
correctly display utf-8 characters, see
|
||||
[chcp](https://ss64.com/nt/chcp.html) for more details.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm i sonic-boom
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
'use strict'
|
||||
|
||||
const SonicBoom = require('sonic-boom')
|
||||
const sonic = new SonicBoom(process.stdout.fd) // or '/path/to/destination'
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### SonicBoom(String|Number, [minLength], [sync])
|
||||
|
||||
Creates a new instance of SonicBoom.
|
||||
|
||||
The first argument can be:
|
||||
|
||||
1. a string that is a path to a file to be written to (mode `'a'`)
|
||||
2. a file descriptor, something that is returned by `fs.open` or
|
||||
`fs.openSync`.
|
||||
|
||||
The second argument is the minimum length of the internal buffer that is
|
||||
required before flushing.
|
||||
|
||||
The third argument is a flag that, when true, causes `SonicBoom` to perform synchronous writes.
|
||||
|
||||
It will emit the `'ready'` event when a file descriptor is available.
|
||||
|
||||
### SonicBoom#write(string)
|
||||
|
||||
Writes the string to the file.
|
||||
It will return false to signal the producer to slow down.
|
||||
|
||||
### SonicBoom#flush()
|
||||
|
||||
Writes the current buffer to the file if a write was not in progress.
|
||||
Do nothing if `minLength` is zero or if it is already writing.
|
||||
|
||||
### SonicBoom#reopen([file])
|
||||
|
||||
Reopen the file in place, useful for log rotation.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const stream = new SonicBoom('./my.log')
|
||||
process.on('SIGUSR2', function () {
|
||||
stream.reopen()
|
||||
})
|
||||
```
|
||||
|
||||
### SonicBoom#flushSync()
|
||||
|
||||
Flushes the buffered data synchronously. This is a costly operation.
|
||||
|
||||
### SonicBoom#end()
|
||||
|
||||
Closes the stream, the data will be flushed down asynchronously
|
||||
|
||||
### SonicBook#destroy()
|
||||
|
||||
Closes the stream immediately, the data is not flushed.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
63
node_modules/sonic-boom/bench.js
generated
vendored
Normal file
63
node_modules/sonic-boom/bench.js
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
'use strict'
|
||||
|
||||
var bench = require('fastbench')
|
||||
var SonicBoom = require('./')
|
||||
var fs = require('fs')
|
||||
|
||||
var core = fs.createWriteStream('/dev/null')
|
||||
var fd = fs.openSync('/dev/null', 'w')
|
||||
var sonic = new SonicBoom(fd)
|
||||
var sonic4k = new SonicBoom(fd, 4096)
|
||||
var sonicSync = new SonicBoom(fd, 0, true)
|
||||
var sonicSync4k = new SonicBoom(fd, 4096, true)
|
||||
|
||||
var MAX = 10000
|
||||
|
||||
function str () {
|
||||
var res = ''
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
res += 'hello'
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
setTimeout(doBench, 100)
|
||||
|
||||
var run = bench([
|
||||
function benchSonic (cb) {
|
||||
sonic.once('drain', cb)
|
||||
for (var i = 0; i < MAX; i++) {
|
||||
sonic.write(str())
|
||||
}
|
||||
},
|
||||
function benchSonicSync (cb) {
|
||||
sonicSync.once('drain', cb)
|
||||
for (var i = 0; i < MAX; i++) {
|
||||
sonicSync.write(str())
|
||||
}
|
||||
},
|
||||
function benchSonic4k (cb) {
|
||||
sonic4k.once('drain', cb)
|
||||
for (var i = 0; i < MAX; i++) {
|
||||
sonic4k.write(str())
|
||||
}
|
||||
},
|
||||
function benchSonicSync4k (cb) {
|
||||
sonicSync4k.once('drain', cb)
|
||||
for (var i = 0; i < MAX; i++) {
|
||||
sonicSync4k.write(str())
|
||||
}
|
||||
},
|
||||
function benchCore (cb) {
|
||||
core.once('drain', cb)
|
||||
for (var i = 0; i < MAX; i++) {
|
||||
core.write(str())
|
||||
}
|
||||
}
|
||||
], 1000)
|
||||
|
||||
function doBench () {
|
||||
run(run)
|
||||
}
|
18
node_modules/sonic-boom/check.js
generated
vendored
Normal file
18
node_modules/sonic-boom/check.js
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
'use strict'
|
||||
|
||||
const SonicBoom = require('.')
|
||||
const sonic = new SonicBoom(process.stdout.fd)
|
||||
|
||||
let count = 0
|
||||
function scheduleWrites () {
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
console.log('hello console')
|
||||
}
|
||||
|
||||
if (++count < 10) {
|
||||
setTimeout(scheduleWrites, 100)
|
||||
}
|
||||
}
|
||||
|
||||
scheduleWrites()
|
8
node_modules/sonic-boom/example.js
generated
vendored
Normal file
8
node_modules/sonic-boom/example.js
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
'use strict'
|
||||
|
||||
const SonicBoom = require('.')
|
||||
const sonic = new SonicBoom(process.stdout.fd) // or 'destination'
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
sonic.write('hello sonic\n')
|
||||
}
|
22
node_modules/sonic-boom/fixtures/firehose.js
generated
vendored
Normal file
22
node_modules/sonic-boom/fixtures/firehose.js
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict'
|
||||
|
||||
const SonicBoom = require('..')
|
||||
|
||||
const out = new SonicBoom(process.stdout.fd)
|
||||
const str = Buffer.alloc(1000).fill('a').toString()
|
||||
|
||||
let i = 0
|
||||
|
||||
function write () {
|
||||
if (i++ === 10) {
|
||||
return
|
||||
}
|
||||
|
||||
if (out.write(str)) {
|
||||
write()
|
||||
} else {
|
||||
out.once('drain', write)
|
||||
}
|
||||
}
|
||||
|
||||
write()
|
317
node_modules/sonic-boom/index.js
generated
vendored
Normal file
317
node_modules/sonic-boom/index.js
generated
vendored
Normal file
|
@ -0,0 +1,317 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const EventEmitter = require('events')
|
||||
const flatstr = require('flatstr')
|
||||
const inherits = require('util').inherits
|
||||
|
||||
const BUSY_WRITE_TIMEOUT = 100
|
||||
|
||||
const sleep = require('atomic-sleep')
|
||||
|
||||
// 16 MB - magic number
|
||||
// This constant ensures that SonicBoom only needs
|
||||
// 32 MB of free memory to run. In case of having 1GB+
|
||||
// of data to write, this prevents an out of memory
|
||||
// condition.
|
||||
const MAX_WRITE = 16 * 1024 * 1024
|
||||
|
||||
function openFile (file, sonic) {
|
||||
sonic._opening = true
|
||||
sonic._writing = true
|
||||
sonic.file = file
|
||||
fs.open(file, 'a', (err, fd) => {
|
||||
if (err) {
|
||||
sonic.emit('error', err)
|
||||
return
|
||||
}
|
||||
|
||||
sonic.fd = fd
|
||||
sonic._reopening = false
|
||||
sonic._opening = false
|
||||
sonic._writing = false
|
||||
|
||||
sonic.emit('ready')
|
||||
|
||||
if (sonic._reopening) {
|
||||
return
|
||||
}
|
||||
|
||||
// start
|
||||
var len = sonic._buf.length
|
||||
if (len > 0 && len > sonic.minLength && !sonic.destroyed) {
|
||||
actualWrite(sonic)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function SonicBoom (fd, minLength, sync) {
|
||||
if (!(this instanceof SonicBoom)) {
|
||||
return new SonicBoom(fd, minLength, sync)
|
||||
}
|
||||
|
||||
this._buf = ''
|
||||
this.fd = -1
|
||||
this._writing = false
|
||||
this._writingBuf = ''
|
||||
this._ending = false
|
||||
this._reopening = false
|
||||
this._asyncDrainScheduled = false
|
||||
this.file = null
|
||||
this.destroyed = false
|
||||
this.sync = sync || false
|
||||
|
||||
this.minLength = minLength || 0
|
||||
|
||||
if (typeof fd === 'number') {
|
||||
this.fd = fd
|
||||
process.nextTick(() => this.emit('ready'))
|
||||
} else if (typeof fd === 'string') {
|
||||
openFile(fd, this)
|
||||
} else {
|
||||
throw new Error('SonicBoom supports only file descriptors and files')
|
||||
}
|
||||
|
||||
this.release = (err, n) => {
|
||||
if (err) {
|
||||
if (err.code === 'EAGAIN') {
|
||||
if (this.sync) {
|
||||
// This error code should not happen in sync mode, because it is
|
||||
// not using the underlining operating system asynchronous functions.
|
||||
// However it happens, and so we handle it.
|
||||
// Ref: https://github.com/pinojs/pino/issues/783
|
||||
try {
|
||||
sleep(BUSY_WRITE_TIMEOUT)
|
||||
this.release(undefined, 0)
|
||||
} catch (err) {
|
||||
this.release(err)
|
||||
}
|
||||
} else {
|
||||
// Let's give the destination some time to process the chunk.
|
||||
setTimeout(() => {
|
||||
fs.write(this.fd, this._writingBuf, 'utf8', this.release)
|
||||
}, BUSY_WRITE_TIMEOUT)
|
||||
}
|
||||
} else {
|
||||
this.emit('error', err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this._writingBuf.length !== n) {
|
||||
this._writingBuf = this._writingBuf.slice(n)
|
||||
if (this.sync) {
|
||||
try {
|
||||
do {
|
||||
n = fs.writeSync(this.fd, this._writingBuf, 'utf8')
|
||||
this._writingBuf = this._writingBuf.slice(n)
|
||||
} while (this._writingBuf.length !== 0)
|
||||
} catch (err) {
|
||||
this.release(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fs.write(this.fd, this._writingBuf, 'utf8', this.release)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this._writingBuf = ''
|
||||
|
||||
if (this.destroyed) {
|
||||
return
|
||||
}
|
||||
|
||||
var len = this._buf.length
|
||||
if (this._reopening) {
|
||||
this._writing = false
|
||||
this._reopening = false
|
||||
this.reopen()
|
||||
} else if (len > 0 && len > this.minLength) {
|
||||
actualWrite(this)
|
||||
} else if (this._ending) {
|
||||
if (len > 0) {
|
||||
actualWrite(this)
|
||||
} else {
|
||||
this._writing = false
|
||||
actualClose(this)
|
||||
}
|
||||
} else {
|
||||
this._writing = false
|
||||
if (this.sync) {
|
||||
if (!this._asyncDrainScheduled) {
|
||||
this._asyncDrainScheduled = true
|
||||
process.nextTick(emitDrain, this)
|
||||
}
|
||||
} else {
|
||||
this.emit('drain')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitDrain (sonic) {
|
||||
sonic._asyncDrainScheduled = false
|
||||
sonic.emit('drain')
|
||||
}
|
||||
|
||||
inherits(SonicBoom, EventEmitter)
|
||||
|
||||
SonicBoom.prototype.write = function (data) {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
this._buf += data
|
||||
var len = this._buf.length
|
||||
if (!this._writing && len > this.minLength) {
|
||||
actualWrite(this)
|
||||
}
|
||||
return len < 16384
|
||||
}
|
||||
|
||||
SonicBoom.prototype.flush = function () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this._writing || this.minLength <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
actualWrite(this)
|
||||
}
|
||||
|
||||
SonicBoom.prototype.reopen = function (file) {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this._opening) {
|
||||
this.once('ready', () => {
|
||||
this.reopen(file)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this._ending) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.file) {
|
||||
throw new Error('Unable to reopen a file descriptor, you must pass a file to SonicBoom')
|
||||
}
|
||||
|
||||
this._reopening = true
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
fs.close(this.fd, (err) => {
|
||||
if (err) {
|
||||
return this.emit('error', err)
|
||||
}
|
||||
})
|
||||
|
||||
openFile(file || this.file, this)
|
||||
}
|
||||
|
||||
SonicBoom.prototype.end = function () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this._opening) {
|
||||
this.once('ready', () => {
|
||||
this.end()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this._ending) {
|
||||
return
|
||||
}
|
||||
|
||||
this._ending = true
|
||||
|
||||
if (!this._writing && this._buf.length > 0 && this.fd >= 0) {
|
||||
actualWrite(this)
|
||||
return
|
||||
}
|
||||
|
||||
if (this._writing) {
|
||||
return
|
||||
}
|
||||
|
||||
actualClose(this)
|
||||
}
|
||||
|
||||
SonicBoom.prototype.flushSync = function () {
|
||||
if (this.destroyed) {
|
||||
throw new Error('SonicBoom destroyed')
|
||||
}
|
||||
|
||||
if (this.fd < 0) {
|
||||
throw new Error('sonic boom is not ready yet')
|
||||
}
|
||||
|
||||
if (this._buf.length > 0) {
|
||||
fs.writeSync(this.fd, this._buf, 'utf8')
|
||||
this._buf = ''
|
||||
}
|
||||
}
|
||||
|
||||
SonicBoom.prototype.destroy = function () {
|
||||
if (this.destroyed) {
|
||||
return
|
||||
}
|
||||
actualClose(this)
|
||||
}
|
||||
|
||||
function actualWrite (sonic) {
|
||||
sonic._writing = true
|
||||
var buf = sonic._buf
|
||||
var release = sonic.release
|
||||
if (buf.length > MAX_WRITE) {
|
||||
buf = buf.slice(0, MAX_WRITE)
|
||||
sonic._buf = sonic._buf.slice(MAX_WRITE)
|
||||
} else {
|
||||
sonic._buf = ''
|
||||
}
|
||||
flatstr(buf)
|
||||
sonic._writingBuf = buf
|
||||
if (sonic.sync) {
|
||||
try {
|
||||
var written = fs.writeSync(sonic.fd, buf, 'utf8')
|
||||
release(null, written)
|
||||
} catch (err) {
|
||||
release(err)
|
||||
}
|
||||
} else {
|
||||
fs.write(sonic.fd, buf, 'utf8', release)
|
||||
}
|
||||
}
|
||||
|
||||
function actualClose (sonic) {
|
||||
if (sonic.fd === -1) {
|
||||
sonic.once('ready', actualClose.bind(null, sonic))
|
||||
return
|
||||
}
|
||||
// TODO write a test to check if we are not leaking fds
|
||||
fs.close(sonic.fd, (err) => {
|
||||
if (err) {
|
||||
sonic.emit('error', err)
|
||||
return
|
||||
}
|
||||
|
||||
if (sonic._ending && !sonic._writing) {
|
||||
sonic.emit('finish')
|
||||
}
|
||||
sonic.emit('close')
|
||||
})
|
||||
sonic.destroyed = true
|
||||
sonic._buf = ''
|
||||
}
|
||||
|
||||
module.exports = SonicBoom
|
67
node_modules/sonic-boom/package.json
generated
vendored
Normal file
67
node_modules/sonic-boom/package.json
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"_from": "sonic-boom@^0.7.5",
|
||||
"_id": "sonic-boom@0.7.7",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-Ei5YOo5J64GKClHIL/5evJPgASXFVpfVYbJV9PILZQytTK6/LCwHvsZJW2Ig4p9FMC2OrBrMnXKgRN/OEoAWfg==",
|
||||
"_location": "/sonic-boom",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "sonic-boom@^0.7.5",
|
||||
"name": "sonic-boom",
|
||||
"escapedName": "sonic-boom",
|
||||
"rawSpec": "^0.7.5",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^0.7.5"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/pino"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.7.7.tgz",
|
||||
"_shasum": "d921de887428208bfa07b0ae32c278de043f350a",
|
||||
"_spec": "sonic-boom@^0.7.5",
|
||||
"_where": "/home/dawidd6/github/dawidd6/action-debian-package/node_modules/pino",
|
||||
"author": {
|
||||
"name": "Matteo Collina",
|
||||
"email": "hello@matteocollina.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/mcollina/sonic-boom/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"flatstr": "^1.0.12"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Extremely fast utf8 only stream implementation",
|
||||
"devDependencies": {
|
||||
"fastbench": "^1.0.1",
|
||||
"pre-commit": "^1.2.2",
|
||||
"proxyquire": "^2.1.0",
|
||||
"standard": "^13.0.2",
|
||||
"tap": "^12.1.0"
|
||||
},
|
||||
"homepage": "https://github.com/mcollina/sonic-boom#readme",
|
||||
"keywords": [
|
||||
"stream",
|
||||
"fs",
|
||||
"net",
|
||||
"fd",
|
||||
"file",
|
||||
"descriptor",
|
||||
"fast"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"name": "sonic-boom",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mcollina/sonic-boom.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "standard && tap --no-esm -t 120 test.js"
|
||||
},
|
||||
"version": "0.7.7"
|
||||
}
|
762
node_modules/sonic-boom/test.js
generated
vendored
Normal file
762
node_modules/sonic-boom/test.js
generated
vendored
Normal file
|
@ -0,0 +1,762 @@
|
|||
'use strict'
|
||||
|
||||
const { test, tearDown } = require('tap')
|
||||
const { join } = require('path')
|
||||
const { fork } = require('child_process')
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const proxyquire = require('proxyquire')
|
||||
const SonicBoom = require('.')
|
||||
|
||||
const files = []
|
||||
var count = 0
|
||||
|
||||
function file () {
|
||||
const file = path.join(os.tmpdir(), `sonic-boom-${process.pid}-${process.hrtime().toString()}-${count++}`)
|
||||
files.push(file)
|
||||
return file
|
||||
}
|
||||
|
||||
tearDown(() => {
|
||||
files.forEach((file) => {
|
||||
try {
|
||||
fs.unlinkSync(file)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('sync false', (t) => {
|
||||
buildTests(t.test, false)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('sync true', (t) => {
|
||||
buildTests(t.test, true)
|
||||
t.end()
|
||||
})
|
||||
|
||||
function buildTests (test, sync) {
|
||||
test('write things to a file descriptor', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, sync)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write things in a streaming fashion', (t) => {
|
||||
t.plan(8)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
t.ok(stream.write('something else\n'))
|
||||
})
|
||||
|
||||
stream.once('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('can be piped into', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, sync)
|
||||
const source = fs.createReadStream(__filename)
|
||||
|
||||
source.pipe(stream)
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(__filename, 'utf8', (err, expected) => {
|
||||
t.error(err)
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write things to a file', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 0, sync)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('flushSync', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 4096, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flushSync()
|
||||
|
||||
// let the file system settle down things
|
||||
setImmediate(function () {
|
||||
stream.end()
|
||||
const data = fs.readFileSync(dest, 'utf8')
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('destroy', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
stream.destroy()
|
||||
t.throws(() => { stream.write('hello world\n') })
|
||||
|
||||
fs.readFile(dest, 'utf8', function (err, data) {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\n')
|
||||
})
|
||||
|
||||
stream.on('finish', () => {
|
||||
t.fail('finish emitted')
|
||||
})
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('destroy while opening', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest)
|
||||
|
||||
stream.destroy()
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('minLength', (t) => {
|
||||
t.plan(8)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 4096, sync)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
var fail = t.fail
|
||||
stream.on('drain', fail)
|
||||
|
||||
// bad use of timer
|
||||
// TODO refactor
|
||||
setTimeout(function () {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, '')
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
})
|
||||
}, 100)
|
||||
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('flush', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 4096, sync)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 0, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
fs.renameSync(dest, after)
|
||||
stream.reopen()
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen with buffer', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 4096, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-moved'
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
stream.flush()
|
||||
fs.renameSync(dest, after)
|
||||
stream.reopen()
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
stream.flush()
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('reopen if not open', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 0, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.reopen()
|
||||
|
||||
stream.end()
|
||||
stream.on('close', function () {
|
||||
t.pass('ended')
|
||||
})
|
||||
})
|
||||
|
||||
test('end after reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 4096, sync)
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end after 2x reopen', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 4096, sync)
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
stream.reopen(dest + '-moved')
|
||||
const after = dest + '-moved-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('end if not ready', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 4096, sync)
|
||||
const after = dest + '-moved'
|
||||
stream.reopen(after)
|
||||
stream.write('after reopen\n')
|
||||
stream.on('finish', () => {
|
||||
t.pass('finish emitted')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
})
|
||||
})
|
||||
stream.end()
|
||||
})
|
||||
|
||||
test('reopen with file', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom(dest, 0, sync)
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
const after = dest + '-new'
|
||||
|
||||
stream.once('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
|
||||
stream.reopen(after)
|
||||
|
||||
stream.once('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
t.ok(stream.write('after reopen\n'))
|
||||
|
||||
stream.on('drain', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
fs.readFile(after, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'after reopen\n')
|
||||
stream.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('chunk data accordingly', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const child = fork(join(__dirname, 'fixtures', 'firehose.js'), { silent: true })
|
||||
const str = Buffer.alloc(10000).fill('a').toString()
|
||||
|
||||
let data = ''
|
||||
|
||||
child.stdout.on('data', function (chunk) {
|
||||
data += chunk.toString()
|
||||
})
|
||||
|
||||
child.stdout.on('end', function () {
|
||||
t.is(data, str)
|
||||
})
|
||||
|
||||
child.on('close', function (code) {
|
||||
t.is(code, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('retry on EAGAIN', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, enc, cb) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = fs.write
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
process.nextTick(cb, err)
|
||||
}
|
||||
const SonicBoom = proxyquire('.', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, false)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('retry on EAGAIN (sync)', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc, cb) {
|
||||
t.pass('fake fs.writeSync called')
|
||||
fakeFs.writeSync = fs.writeSync
|
||||
const err = new Error('EAGAIN')
|
||||
err.code = 'EAGAIN'
|
||||
throw err
|
||||
}
|
||||
const SonicBoom = proxyquire('.', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, true)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write buffers that are not totally written', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.write = function (fd, buf, enc, cb) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.write = function (fd, buf, enc, cb) {
|
||||
t.pass('calling real fs.write, ' + buf)
|
||||
fs.write(fd, buf, enc, cb)
|
||||
}
|
||||
process.nextTick(cb, null, 0)
|
||||
}
|
||||
const SonicBoom = proxyquire('.', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, false)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write buffers that are not totally written with sync mode', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc) {
|
||||
t.pass('fake fs.write called')
|
||||
fakeFs.writeSync = (fd, buf, enc) => {
|
||||
t.pass('calling real fs.writeSync, ' + buf)
|
||||
return fs.writeSync(fd, buf, enc)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
const SonicBoom = proxyquire('.', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, true)
|
||||
|
||||
stream.on('ready', () => {
|
||||
t.pass('ready emitted')
|
||||
})
|
||||
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.readFile(dest, 'utf8', (err, data) => {
|
||||
t.error(err)
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('sync writing is fully sync', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const fakeFs = Object.create(fs)
|
||||
fakeFs.writeSync = function (fd, buf, enc, cb) {
|
||||
t.pass('fake fs.write called')
|
||||
return fs.writeSync(fd, buf, enc)
|
||||
}
|
||||
const SonicBoom = proxyquire('.', {
|
||||
fs: fakeFs
|
||||
})
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, true)
|
||||
t.ok(stream.write('hello world\n'))
|
||||
t.ok(stream.write('something else\n'))
|
||||
|
||||
// 'drain' will be only emitted once,
|
||||
// the number of assertions at the top check this.
|
||||
stream.on('drain', () => {
|
||||
t.pass('drain emitted')
|
||||
})
|
||||
|
||||
const data = fs.readFileSync(dest, 'utf8')
|
||||
t.equal(data, 'hello world\nsomething else\n')
|
||||
})
|
||||
|
||||
// These they will fail on Node 6, as we cannot allocate a string this
|
||||
// big. It's considered a won't fix on Node 6, as it's deprecated.
|
||||
if (process.versions.node.indexOf('6.') !== 0) {
|
||||
test('write enormously large buffers async', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, false)
|
||||
|
||||
const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
|
||||
let length = 0
|
||||
|
||||
for (let i = 0; i < 1024 * 1024; i++) {
|
||||
length += buf.length
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
|
||||
test('write enormously large buffers sync', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, true)
|
||||
|
||||
const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
|
||||
let length = 0
|
||||
|
||||
for (let i = 0; i < 1024 * 1024; i++) {
|
||||
length += buf.length
|
||||
stream.write(buf)
|
||||
}
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('write enormously large buffers sync with utf8 multi-byte split', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
const stream = new SonicBoom(fd, 0, true)
|
||||
|
||||
let buf = Buffer.alloc((1024 * 16) - 2).fill('x') // 16MB - 3B
|
||||
const length = buf.length + 4
|
||||
buf = buf.toString() + '🌲' // 16 MB + 1B
|
||||
|
||||
stream.write(buf)
|
||||
|
||||
stream.end()
|
||||
|
||||
stream.on('finish', () => {
|
||||
fs.stat(dest, (err, stat) => {
|
||||
t.error(err)
|
||||
t.equal(stat.size, length)
|
||||
const char = Buffer.alloc(4)
|
||||
const fd = fs.openSync(dest, 'r')
|
||||
fs.readSync(fd, char, 0, 4, length - 4)
|
||||
t.equal(char.toString(), '🌲')
|
||||
})
|
||||
})
|
||||
stream.on('close', () => {
|
||||
t.pass('close emitted')
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue