1 var test = require('tap').test
2 var lockFile = require('../lockfile.js')
3 var path = require('path')
5 var touch = require('touch')
7 // On Unix systems, it uses ctime by default for staleness checks, since it's
8 // the most reliable. However, because this test artificially sets some locks
9 // to an earlier time to simulate staleness, we use mtime here.
10 lockFile.filetime = 'mtime'
12 test('setup', function (t) {
13 try { lockFile.unlockSync('basic-lock') } catch (er) {}
14 try { lockFile.unlockSync('sync-lock') } catch (er) {}
15 try { lockFile.unlockSync('never-forget') } catch (er) {}
16 try { lockFile.unlockSync('stale-lock') } catch (er) {}
17 try { lockFile.unlockSync('watch-lock') } catch (er) {}
18 try { lockFile.unlockSync('retry-lock') } catch (er) {}
19 try { lockFile.unlockSync('contentious-lock') } catch (er) {}
20 try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
21 try { lockFile.unlockSync('stale-windows-lock') } catch (er) {}
25 test('lock contention', function (t) {
29 // allow for some time for each lock acquisition and release.
30 // note that raising N higher will mean that the overhead
31 // increases, because we're creating more and more watchers.
32 // irl, you should never have several hundred contenders for a
33 // single lock, so this situation is somewhat pathological.
35 var wait = N * overhead + delay
37 // first make it locked, so that everyone has to wait
38 lockFile.lock('contentious-lock', function(er, lock) {
39 t.ifError(er, 'acquiring starter')
41 t.pass('acquired starter lock')
42 setTimeout(function() {
43 lockFile.unlock('contentious-lock', function (er) {
44 t.ifError(er, 'unlocking starter')
46 t.pass('unlocked starter')
51 for (var i=0; i < N; i++)
52 lockFile.lock('contentious-lock', { wait: wait }, function(er, lock) {
54 lockFile.unlock('contentious-lock', function(er) {
57 t.pass('locked and unlocked #' + gotlocks)
59 t.pass('got all locks')
66 test('basic test', function (t) {
67 lockFile.check('basic-lock', function (er, locked) {
70 lockFile.lock('basic-lock', function (er) {
72 lockFile.lock('basic-lock', function (er) {
74 lockFile.check('basic-lock', function (er, locked) {
77 lockFile.unlock('basic-lock', function (er) {
79 lockFile.check('basic-lock', function (er, locked) {
91 test('sync test', function (t) {
93 locked = lockFile.checkSync('sync-lock')
95 lockFile.lockSync('sync-lock')
96 locked = lockFile.checkSync('sync-lock')
98 lockFile.unlockSync('sync-lock')
99 locked = lockFile.checkSync('sync-lock')
104 test('exit cleanup test', function (t) {
105 var child = require.resolve('./fixtures/child.js')
106 var node = process.execPath
107 var spawn = require('child_process').spawn
108 spawn(node, [child]).on('exit', function () {
109 setTimeout(function () {
110 var locked = lockFile.checkSync('never-forget')
117 test('error exit cleanup test', function (t) {
118 var child = require.resolve('./fixtures/bad-child.js')
119 var node = process.execPath
120 var spawn = require('child_process').spawn
121 spawn(node, [child]).on('exit', function () {
122 setTimeout(function () {
123 var locked = lockFile.checkSync('never-forget')
131 test('staleness test', function (t) {
132 lockFile.lock('stale-lock', function (er) {
136 touch.sync('stale-lock', { time: new Date(Date.now() - 2000) })
138 var opts = { stale: 1 }
139 lockFile.check('stale-lock', opts, function (er, locked) {
142 lockFile.lock('stale-lock', opts, function (er) {
144 lockFile.unlock('stale-lock', function (er) {
153 test('staleness sync test', function (t) {
154 var opts = { stale: 1 }
155 lockFile.lockSync('stale-lock')
157 touch.sync('stale-lock', { time: new Date(Date.now() - 2000) })
159 locked = lockFile.checkSync('stale-lock', opts)
161 lockFile.lockSync('stale-lock', opts)
162 lockFile.unlockSync('stale-lock')
166 test('retries', function (t) {
167 // next 5 opens will fail.
170 fs.open = function (path, mode, cb) {
173 return fs.open(path, mode, cb)
175 var er = new Error('bogus')
176 // to be, or not to be, that is the question.
177 er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
178 process.nextTick(cb.bind(null, er))
181 lockFile.lock('retry-lock', { retries: opens }, function (er) {
184 lockFile.unlockSync('retry-lock')
189 test('retryWait', function (t) {
190 // next 5 opens will fail.
193 fs.open = function (path, mode, cb) {
196 return fs.open(path, mode, cb)
198 var er = new Error('bogus')
199 // to be, or not to be, that is the question.
200 er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
201 process.nextTick(cb.bind(null, er))
204 var opts = { retries: opens, retryWait: 100 }
205 lockFile.lock('retry-lock', opts, function (er) {
208 lockFile.unlockSync('retry-lock')
213 test('retry sync', function (t) {
214 // next 5 opens will fail.
216 fs._openSync = fs.openSync
217 fs.openSync = function (path, mode) {
219 fs.openSync = fs._openSync
220 return fs.openSync(path, mode)
222 var er = new Error('bogus')
223 // to be, or not to be, that is the question.
224 er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
228 var opts = { retries: opens }
229 lockFile.lockSync('retry-lock', opts)
231 lockFile.unlockSync('retry-lock')
235 test('wait and stale together', function (t) {
238 lockFile.lock('stale-wait-lock', function(er) {
239 // keep refreshing the lock, so we keep it forever
240 interval = setInterval(function() {
241 touch.sync('stale-wait-lock')
244 // try to get another lock. this must fail!
245 var opt = { stale: 1000, wait: 2000, pollInterval: 1000 }
246 lockFile.lock('stale-wait-lock', opt, function (er) {
248 t.fail('got second lock? that unpossible!')
250 t.pass('second lock failed, as i have foreseen it')
251 clearInterval(interval)
258 test('stale windows file tunneling test', function (t) {
260 // nt file system tunneling feature will make file creation time not updated
261 var opts = { stale: 1000 }
262 lockFile.lockSync('stale-windows-lock')
263 touch.sync('stale-windows-lock', { time: new Date(Date.now() - 3000) })
266 lockFile.unlockSync('stale-windows-lock')
267 lockFile.lockSync('stale-windows-lock', opts)
268 locked = lockFile.checkSync('stale-windows-lock', opts)
269 t.ok(locked, "should be locked and not stale")
270 lockFile.lock('stale-windows-lock', opts, function (er) {
272 t.fail('got second lock? impossible, windows file tunneling problem!')
274 t.pass('second lock failed, windows file tunneling problem fixed')
280 test('cleanup', function (t) {
281 try { lockFile.unlockSync('basic-lock') } catch (er) {}
282 try { lockFile.unlockSync('sync-lock') } catch (er) {}
283 try { lockFile.unlockSync('never-forget') } catch (er) {}
284 try { lockFile.unlockSync('stale-lock') } catch (er) {}
285 try { lockFile.unlockSync('watch-lock') } catch (er) {}
286 try { lockFile.unlockSync('retry-lock') } catch (er) {}
287 try { lockFile.unlockSync('contentious-lock') } catch (er) {}
288 try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
289 try { lockFile.unlockSync('stale-windows-lock') } catch (er) {}