1var test = require('tap').test 2var lockFile = require('../lockfile.js') 3var path = require('path') 4var fs = require('fs') 5var touch = require('touch') 6 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. 10lockFile.filetime = 'mtime' 11 12test('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) {} 22 t.end() 23}) 24 25test('lock contention', function (t) { 26 var gotlocks = 0; 27 var N = 200 28 var delay = 10 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. 34 var overhead = 200 35 var wait = N * overhead + delay 36 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') 40 if (er) throw er; 41 t.pass('acquired starter lock') 42 setTimeout(function() { 43 lockFile.unlock('contentious-lock', function (er) { 44 t.ifError(er, 'unlocking starter') 45 if (er) throw er 46 t.pass('unlocked starter') 47 }) 48 }, delay) 49 }) 50 51 for (var i=0; i < N; i++) 52 lockFile.lock('contentious-lock', { wait: wait }, function(er, lock) { 53 if (er) throw er; 54 lockFile.unlock('contentious-lock', function(er) { 55 if (er) throw er 56 gotlocks++ 57 t.pass('locked and unlocked #' + gotlocks) 58 if (gotlocks === N) { 59 t.pass('got all locks') 60 t.end() 61 } 62 }) 63 }) 64}) 65 66test('basic test', function (t) { 67 lockFile.check('basic-lock', function (er, locked) { 68 if (er) throw er 69 t.notOk(locked) 70 lockFile.lock('basic-lock', function (er) { 71 if (er) throw er 72 lockFile.lock('basic-lock', function (er) { 73 t.ok(er) 74 lockFile.check('basic-lock', function (er, locked) { 75 if (er) throw er 76 t.ok(locked) 77 lockFile.unlock('basic-lock', function (er) { 78 if (er) throw er 79 lockFile.check('basic-lock', function (er, locked) { 80 if (er) throw er 81 t.notOk(locked) 82 t.end() 83 }) 84 }) 85 }) 86 }) 87 }) 88 }) 89}) 90 91test('sync test', function (t) { 92 var locked 93 locked = lockFile.checkSync('sync-lock') 94 t.notOk(locked) 95 lockFile.lockSync('sync-lock') 96 locked = lockFile.checkSync('sync-lock') 97 t.ok(locked) 98 lockFile.unlockSync('sync-lock') 99 locked = lockFile.checkSync('sync-lock') 100 t.notOk(locked) 101 t.end() 102}) 103 104test('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') 111 t.notOk(locked) 112 t.end() 113 }, 100) 114 }) 115}) 116 117test('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') 124 t.notOk(locked) 125 t.end() 126 }, 100) 127 }) 128}) 129 130 131test('staleness test', function (t) { 132 lockFile.lock('stale-lock', function (er) { 133 if (er) throw er 134 135 // simulate 2s old 136 touch.sync('stale-lock', { time: new Date(Date.now() - 2000) }) 137 138 var opts = { stale: 1 } 139 lockFile.check('stale-lock', opts, function (er, locked) { 140 if (er) throw er 141 t.notOk(locked) 142 lockFile.lock('stale-lock', opts, function (er) { 143 if (er) throw er 144 lockFile.unlock('stale-lock', function (er) { 145 if (er) throw er 146 t.end() 147 }) 148 }) 149 }) 150 }) 151}) 152 153test('staleness sync test', function (t) { 154 var opts = { stale: 1 } 155 lockFile.lockSync('stale-lock') 156 // simulate 2s old 157 touch.sync('stale-lock', { time: new Date(Date.now() - 2000) }) 158 var locked 159 locked = lockFile.checkSync('stale-lock', opts) 160 t.notOk(locked) 161 lockFile.lockSync('stale-lock', opts) 162 lockFile.unlockSync('stale-lock') 163 t.end() 164}) 165 166test('retries', function (t) { 167 // next 5 opens will fail. 168 var opens = 5 169 fs._open = fs.open 170 fs.open = function (path, mode, cb) { 171 if (--opens === 0) { 172 fs.open = fs._open 173 return fs.open(path, mode, cb) 174 } 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)) 179 } 180 181 lockFile.lock('retry-lock', { retries: opens }, function (er) { 182 if (er) throw er 183 t.equal(opens, 0) 184 lockFile.unlockSync('retry-lock') 185 t.end() 186 }) 187}) 188 189test('retryWait', function (t) { 190 // next 5 opens will fail. 191 var opens = 5 192 fs._open = fs.open 193 fs.open = function (path, mode, cb) { 194 if (--opens === 0) { 195 fs.open = fs._open 196 return fs.open(path, mode, cb) 197 } 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)) 202 } 203 204 var opts = { retries: opens, retryWait: 100 } 205 lockFile.lock('retry-lock', opts, function (er) { 206 if (er) throw er 207 t.equal(opens, 0) 208 lockFile.unlockSync('retry-lock') 209 t.end() 210 }) 211}) 212 213test('retry sync', function (t) { 214 // next 5 opens will fail. 215 var opens = 5 216 fs._openSync = fs.openSync 217 fs.openSync = function (path, mode) { 218 if (--opens === 0) { 219 fs.openSync = fs._openSync 220 return fs.openSync(path, mode) 221 } 222 var er = new Error('bogus') 223 // to be, or not to be, that is the question. 224 er.code = opens % 2 ? 'EEXIST' : 'ENOENT' 225 throw er 226 } 227 228 var opts = { retries: opens } 229 lockFile.lockSync('retry-lock', opts) 230 t.equal(opens, 0) 231 lockFile.unlockSync('retry-lock') 232 t.end() 233}) 234 235test('wait and stale together', function (t) { 236 // first locker. 237 var interval 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') 242 }, 10) 243 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) { 247 if (!er) 248 t.fail('got second lock? that unpossible!') 249 else 250 t.pass('second lock failed, as i have foreseen it') 251 clearInterval(interval) 252 t.end() 253 }) 254 }) 255}) 256 257 258test('stale windows file tunneling test', function (t) { 259 // for windows only 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) }) 264 265 var locked 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) { 271 if (!er) 272 t.fail('got second lock? impossible, windows file tunneling problem!') 273 else 274 t.pass('second lock failed, windows file tunneling problem fixed') 275 t.end() 276 }) 277}) 278 279 280test('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) {} 290 t.end() 291}) 292 293