You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							947 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
	
	
							947 lines
						
					
					
						
							21 KiB
						
					
					
				'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 = []
 | 
						|
let 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 {
 | 
						|
      if (fs.existsSync(file)) {
 | 
						|
        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, 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, sync })
 | 
						|
 | 
						|
    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()
 | 
						|
        })
 | 
						|
      })
 | 
						|
    })
 | 
						|
 | 
						|
    t.ok(stream.write('hello world\n'))
 | 
						|
 | 
						|
    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, 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, 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, minLength: 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, 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, minLength: 4096, sync })
 | 
						|
 | 
						|
    stream.on('ready', () => {
 | 
						|
      t.pass('ready emitted')
 | 
						|
    })
 | 
						|
 | 
						|
    t.ok(stream.write('hello world\n'))
 | 
						|
    t.ok(stream.write('something else\n'))
 | 
						|
 | 
						|
    const 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, minLength: 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, 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, minLength: 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, 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, minLength: 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, minLength: 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, minLength: 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, minLength: 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.equal(data, str)
 | 
						|
    })
 | 
						|
 | 
						|
    child.on('close', function (code) {
 | 
						|
      t.equal(code, 0)
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  test('write later on recoverable error', (t) => {
 | 
						|
    t.plan(8)
 | 
						|
 | 
						|
    const fakeFs = Object.create(fs)
 | 
						|
    const SonicBoom = proxyquire('.', {
 | 
						|
      fs: fakeFs
 | 
						|
    })
 | 
						|
 | 
						|
    const dest = file()
 | 
						|
    const fd = fs.openSync(dest, 'w')
 | 
						|
    const stream = new SonicBoom({ fd, minLength: 0, sync })
 | 
						|
 | 
						|
    stream.on('ready', () => {
 | 
						|
      t.pass('ready emitted')
 | 
						|
    })
 | 
						|
    stream.on('error', () => {
 | 
						|
      t.pass('error emitted')
 | 
						|
    })
 | 
						|
 | 
						|
    if (sync) {
 | 
						|
      fakeFs.writeSync = function (fd, buf, enc) {
 | 
						|
        t.pass('fake fs.writeSync called')
 | 
						|
        throw new Error('recoverable error')
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      fakeFs.write = function (fd, buf, enc, cb) {
 | 
						|
        t.pass('fake fs.write called')
 | 
						|
        setTimeout(() => cb(new Error('recoverable error')), 0)
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    t.ok(stream.write('hello world\n'))
 | 
						|
 | 
						|
    setTimeout(() => {
 | 
						|
      if (sync) {
 | 
						|
        fakeFs.writeSync = fs.writeSync
 | 
						|
      } else {
 | 
						|
        fakeFs.write = fs.write
 | 
						|
      }
 | 
						|
 | 
						|
      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')
 | 
						|
      })
 | 
						|
    }, 0)
 | 
						|
  })
 | 
						|
 | 
						|
  test('reopen throws an error', (t) => {
 | 
						|
    t.plan(sync ? 10 : 9)
 | 
						|
 | 
						|
    const fakeFs = Object.create(fs)
 | 
						|
    const SonicBoom = proxyquire('.', {
 | 
						|
      fs: fakeFs
 | 
						|
    })
 | 
						|
 | 
						|
    const dest = file()
 | 
						|
    const stream = new SonicBoom({ dest, sync })
 | 
						|
 | 
						|
    t.ok(stream.write('hello world\n'))
 | 
						|
    t.ok(stream.write('something else\n'))
 | 
						|
 | 
						|
    const after = dest + '-moved'
 | 
						|
 | 
						|
    stream.on('error', () => {
 | 
						|
      t.pass('error emitted')
 | 
						|
    })
 | 
						|
 | 
						|
    stream.once('drain', () => {
 | 
						|
      t.pass('drain emitted')
 | 
						|
 | 
						|
      fs.renameSync(dest, after)
 | 
						|
      if (sync) {
 | 
						|
        fakeFs.openSync = function (file, flags) {
 | 
						|
          t.pass('fake fs.openSync called')
 | 
						|
          throw new Error('open error')
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        fakeFs.open = function (file, flags, cb) {
 | 
						|
          t.pass('fake fs.open called')
 | 
						|
          setTimeout(() => cb(new Error('open error')), 0)
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (sync) {
 | 
						|
        try {
 | 
						|
          stream.reopen()
 | 
						|
        } catch (err) {
 | 
						|
          t.pass('reopen throwed')
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        stream.reopen()
 | 
						|
      }
 | 
						|
 | 
						|
      setTimeout(() => {
 | 
						|
        t.ok(stream.write('after reopen\n'))
 | 
						|
 | 
						|
        stream.end()
 | 
						|
        stream.on('finish', () => {
 | 
						|
          fs.readFile(after, 'utf8', (err, data) => {
 | 
						|
            t.error(err)
 | 
						|
            t.equal(data, 'hello world\nsomething else\nafter reopen\n')
 | 
						|
          })
 | 
						|
        })
 | 
						|
        stream.on('close', () => {
 | 
						|
          t.pass('close emitted')
 | 
						|
        })
 | 
						|
      }, 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, sync: false, minLength: 0 })
 | 
						|
 | 
						|
  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, minLength: 0, sync: 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('retry in flushSync on EAGAIN', (t) => {
 | 
						|
  t.plan(7)
 | 
						|
 | 
						|
  const fakeFs = Object.create(fs)
 | 
						|
  const SonicBoom = proxyquire('.', {
 | 
						|
    fs: fakeFs
 | 
						|
  })
 | 
						|
 | 
						|
  const dest = file()
 | 
						|
  const fd = fs.openSync(dest, 'w')
 | 
						|
  const stream = new SonicBoom({ fd, sync: false, minLength: 0 })
 | 
						|
 | 
						|
  stream.on('ready', () => {
 | 
						|
    t.pass('ready emitted')
 | 
						|
  })
 | 
						|
 | 
						|
  t.ok(stream.write('hello world\n'))
 | 
						|
 | 
						|
  fakeFs.writeSync = function (fd, buf, enc) {
 | 
						|
    t.pass('fake fs.write called')
 | 
						|
    fakeFs.writeSync = fs.writeSync
 | 
						|
    const err = new Error('EAGAIN')
 | 
						|
    err.code = 'EAGAIN'
 | 
						|
    throw err
 | 
						|
  }
 | 
						|
 | 
						|
  t.ok(stream.write('something else\n'))
 | 
						|
 | 
						|
  stream.flushSync()
 | 
						|
  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, minLength: 0, sync: 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, minLength: 0, sync: 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, minLength: 0, sync: 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, minLength: 0, sync: false })
 | 
						|
 | 
						|
    const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
 | 
						|
    let length = 0
 | 
						|
 | 
						|
    for (let i = 0; i < 1024 * 512; 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, minLength: 0, sync: true })
 | 
						|
 | 
						|
    const buf = Buffer.alloc(1024).fill('x').toString() // 1 MB
 | 
						|
    let length = 0
 | 
						|
 | 
						|
    for (let i = 0; i < 1024 * 512; 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, minLength: 0, sync: 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')
 | 
						|
  })
 | 
						|
})
 | 
						|
 | 
						|
// for context see this issue https://github.com/pinojs/pino/issues/871
 | 
						|
test('file specified by dest path available immediately when options.sync is true', (t) => {
 | 
						|
  t.plan(3)
 | 
						|
  const dest = file()
 | 
						|
  const stream = new SonicBoom({ dest, sync: true })
 | 
						|
  t.ok(stream.write('hello world\n'))
 | 
						|
  t.ok(stream.write('something else\n'))
 | 
						|
  stream.flushSync()
 | 
						|
  t.pass('file opened and written to without error')
 | 
						|
})
 | 
						|
 | 
						|
test('sync error handling', (t) => {
 | 
						|
  t.plan(1)
 | 
						|
  try {
 | 
						|
    /* eslint no-new: off */
 | 
						|
    new SonicBoom({ dest: '/path/to/nowwhere', sync: true })
 | 
						|
    t.fail('must throw synchronously')
 | 
						|
  } catch (err) {
 | 
						|
    t.pass('an error happened')
 | 
						|
  }
 | 
						|
})
 |