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.
 
 
 
 
 
 

294 lines
8.0 KiB

var test = require('tap').test
var lockFile = require('../lockfile.js')
var path = require('path')
var fs = require('fs')
var touch = require('touch')
test('setup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('sync-lock') } catch (er) {}
try { lockFile.unlockSync('never-forget') } catch (er) {}
try { lockFile.unlockSync('stale-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
t.end()
})
test('lock contention', function (t) {
var gotlocks = 0;
var N = 200
var delay = 10
// allow for some time for each lock acquisition and release.
// note that raising N higher will mean that the overhead
// increases, because we're creating more and more watchers.
// irl, you should never have several hundred contenders for a
// single lock, so this situation is somewhat pathological.
var overhead = 200
var wait = N * overhead + delay
// first make it locked, so that everyone has to wait
lockFile.lock('contentious-lock', function(er, lock) {
t.ifError(er, 'acquiring starter')
if (er) throw er;
t.pass('acquired starter lock')
setTimeout(function() {
lockFile.unlock('contentious-lock', function (er) {
t.ifError(er, 'unlocking starter')
if (er) throw er
t.pass('unlocked starter')
})
}, delay)
})
for (var i=0; i < N; i++)
lockFile.lock('contentious-lock', { wait: wait }, function(er, lock) {
if (er) throw er;
lockFile.unlock('contentious-lock', function(er) {
if (er) throw er
gotlocks++
t.pass('locked and unlocked #' + gotlocks)
if (gotlocks === N) {
t.pass('got all locks')
t.end()
}
})
})
})
test('basic test', function (t) {
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.notOk(locked)
lockFile.lock('basic-lock', function (er) {
if (er) throw er
lockFile.lock('basic-lock', function (er) {
t.ok(er)
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.ok(locked)
lockFile.unlock('basic-lock', function (er) {
if (er) throw er
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.notOk(locked)
t.end()
})
})
})
})
})
})
})
test('sync test', function (t) {
var locked
locked = lockFile.checkSync('sync-lock')
t.notOk(locked)
lockFile.lockSync('sync-lock')
locked = lockFile.checkSync('sync-lock')
t.ok(locked)
lockFile.unlockSync('sync-lock')
locked = lockFile.checkSync('sync-lock')
t.notOk(locked)
t.end()
})
test('exit cleanup test', function (t) {
var child = require.resolve('./fixtures/child.js')
var node = process.execPath
var spawn = require('child_process').spawn
spawn(node, [child]).on('exit', function () {
setTimeout(function () {
var locked = lockFile.checkSync('never-forget')
t.notOk(locked)
t.end()
}, 100)
})
})
test('error exit cleanup test', function (t) {
var child = require.resolve('./fixtures/bad-child.js')
var node = process.execPath
var spawn = require('child_process').spawn
spawn(node, [child]).on('exit', function () {
setTimeout(function () {
var locked = lockFile.checkSync('never-forget')
t.notOk(locked)
t.end()
}, 100)
})
})
test('staleness test', function (t) {
lockFile.lock('stale-lock', function (er) {
if (er) throw er
var opts = { stale: 1 }
setTimeout(next, 1000)
function next () {
lockFile.check('stale-lock', opts, function (er, locked) {
if (er) throw er
t.notOk(locked)
lockFile.lock('stale-lock', opts, function (er) {
if (er) throw er
lockFile.unlock('stale-lock', function (er) {
if (er) throw er
t.end()
})
})
})
}
})
})
test('staleness sync test', function (t) {
var opts = { stale: 1 }
lockFile.lockSync('stale-lock')
setTimeout(next, 1000)
function next () {
var locked
locked = lockFile.checkSync('stale-lock', opts)
t.notOk(locked)
lockFile.lockSync('stale-lock', opts)
lockFile.unlockSync('stale-lock')
t.end()
}
})
test('watch test', function (t) {
var opts = { wait: 100 }
var fdx
lockFile.lock('watch-lock', function (er, fd1) {
if (er) throw er
setTimeout(unlock, 10)
function unlock () {
console.error('unlocking it')
lockFile.unlockSync('watch-lock')
// open another file, so the fd gets reused
// so we can know that it actually re-opened it fresh,
// rather than just getting the same lock as before.
fdx = fs.openSync('x', 'w')
fdy = fs.openSync('x', 'w')
}
// should have gotten a new fd
lockFile.lock('watch-lock', opts, function (er, fd2) {
if (er) throw er
t.notEqual(fd1, fd2)
fs.closeSync(fdx)
fs.closeSync(fdy)
fs.unlinkSync('x')
lockFile.unlockSync('watch-lock')
t.end()
})
})
})
test('retries', function (t) {
// next 5 opens will fail.
var opens = 5
fs._open = fs.open
fs.open = function (path, mode, cb) {
if (--opens === 0) {
fs.open = fs._open
return fs.open(path, mode, cb)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
process.nextTick(cb.bind(null, er))
}
lockFile.lock('retry-lock', { retries: opens }, function (er, fd) {
if (er) throw er
t.equal(opens, 0)
t.ok(fd)
lockFile.unlockSync('retry-lock')
t.end()
})
})
test('retryWait', function (t) {
// next 5 opens will fail.
var opens = 5
fs._open = fs.open
fs.open = function (path, mode, cb) {
if (--opens === 0) {
fs.open = fs._open
return fs.open(path, mode, cb)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
process.nextTick(cb.bind(null, er))
}
var opts = { retries: opens, retryWait: 100 }
lockFile.lock('retry-lock', opts, function (er, fd) {
if (er) throw er
t.equal(opens, 0)
t.ok(fd)
lockFile.unlockSync('retry-lock')
t.end()
})
})
test('retry sync', function (t) {
// next 5 opens will fail.
var opens = 5
fs._openSync = fs.openSync
fs.openSync = function (path, mode) {
if (--opens === 0) {
fs.openSync = fs._openSync
return fs.openSync(path, mode)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
throw er
}
var opts = { retries: opens }
lockFile.lockSync('retry-lock', opts)
t.equal(opens, 0)
lockFile.unlockSync('retry-lock')
t.end()
})
test('wait and stale together', function (t) {
// first locker.
var interval
lockFile.lock('stale-wait-lock', function(er) {
// keep refreshing the lock, so we keep it forever
interval = setInterval(function() {
touch.sync('stale-wait-lock')
}, 10)
// try to get another lock. this must fail!
var opt = { stale: 1000, wait: 2000 }
lockFile.lock('stale-wait-lock', opt, function (er) {
if (!er)
t.fail('got second lock? that unpossible!')
else
t.pass('second lock failed, as i have foreseen it')
clearInterval(interval)
t.end()
})
})
})
test('cleanup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('sync-lock') } catch (er) {}
try { lockFile.unlockSync('never-forget') } catch (er) {}
try { lockFile.unlockSync('stale-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
t.end()
})