| var test = require('tap').test |
| var lockFile = require('../lockfile.js') |
| var path = require('path') |
| var fs = require('fs') |
| var touch = require('touch') |
| |
| // On Unix systems, it uses ctime by default for staleness checks, since it's |
| // the most reliable. However, because this test artificially sets some locks |
| // to an earlier time to simulate staleness, we use mtime here. |
| lockFile.filetime = 'mtime' |
| |
| 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) {} |
| try { lockFile.unlockSync('stale-windows-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 |
| |
| // simulate 2s old |
| touch.sync('stale-lock', { time: new Date(Date.now() - 2000) }) |
| |
| var opts = { stale: 1 } |
| 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') |
| // simulate 2s old |
| touch.sync('stale-lock', { time: new Date(Date.now() - 2000) }) |
| var locked |
| locked = lockFile.checkSync('stale-lock', opts) |
| t.notOk(locked) |
| lockFile.lockSync('stale-lock', opts) |
| lockFile.unlockSync('stale-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) { |
| if (er) throw er |
| t.equal(opens, 0) |
| 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) { |
| if (er) throw er |
| t.equal(opens, 0) |
| 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, pollInterval: 1000 } |
| 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('stale windows file tunneling test', function (t) { |
| // for windows only |
| // nt file system tunneling feature will make file creation time not updated |
| var opts = { stale: 1000 } |
| lockFile.lockSync('stale-windows-lock') |
| touch.sync('stale-windows-lock', { time: new Date(Date.now() - 3000) }) |
| |
| var locked |
| lockFile.unlockSync('stale-windows-lock') |
| lockFile.lockSync('stale-windows-lock', opts) |
| locked = lockFile.checkSync('stale-windows-lock', opts) |
| t.ok(locked, "should be locked and not stale") |
| lockFile.lock('stale-windows-lock', opts, function (er) { |
| if (!er) |
| t.fail('got second lock? impossible, windows file tunneling problem!') |
| else |
| t.pass('second lock failed, windows file tunneling problem fixed') |
| 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) {} |
| try { lockFile.unlockSync('stale-windows-lock') } catch (er) {} |
| t.end() |
| }) |
| |