Browse Source

refactor tests to use fake timers

activeAddress
Ivan Socolsky 10 years ago
parent
commit
59996ef073
  1. 57
      lib/locallock.js
  2. 69
      test/locallock.js

57
lib/locallock.js

@ -1,55 +1,62 @@
var _ = require('lodash'); var _ = require('lodash');
var $ = require('preconditions').singleton(); var $ = require('preconditions').singleton();
var locks = {};
function Lock() { function Lock() {
this.tasks = {};
};
Lock.prototype._release = function(token, task) {
if (!task.running) return;
task.running = false;
this.tasks[token] = _.without(this.tasks[token], task);
this._runOne(token);
}; };
Lock.prototype._runOne = function(token) { Lock.prototype._runOne = function(token) {
var self = this; var self = this;
var item = _.first(locks[token]); if (_.any(self.tasks[token], {
if (!item || item.started) return; running: true
})) return;
var task = _.first(self.tasks[token]);
if (!task) return;
item.started = true; task.running = true;
if (item.maxRunningTime > 0) {
if (task.timeout > 0) {
setTimeout(function() { setTimeout(function() {
var it = _.first(locks[token]); self._release(token, task);
if (it != item) return; }, task.timeout);
locks[token].shift();
self._runOne(token);
}, item.maxRunningTime);
} }
item.fn(null, function() { task.fn(null, function() {
locks[token].shift(); self._release(token, task);
self._runOne(token);
}); });
}; };
Lock.prototype.locked = function(token, wait, max, task) { Lock.prototype.locked = function(token, wait, max, userTask) {
var self = this; var self = this;
if (_.isUndefined(locks[token])) { if (_.isUndefined(self.tasks[token])) {
locks[token] = []; self.tasks[token] = [];
} }
var item = { var task = {
maxRunningTime: max, timeout: max,
started: false, running: false,
fn: task, fn: userTask,
}; };
locks[token].push(item); self.tasks[token].push(task);
if (wait > 0) { if (wait > 0) {
setTimeout(function() { setTimeout(function() {
var it = _.find(locks[token], item); if (task.running || !_.contains(self.tasks[token], task)) return;
if (!it || it.started) return; self.tasks[token] = _.without(self.tasks[token], task);
locks[token] = _.without(locks[token], it); task.fn(new Error('Could not acquire lock ' + token));
it.fn(new Error('Could not acquire lock ' + token));
}, wait); }, wait);
} }
self._runOne(token); self._runOne(token);
}; };

69
test/locallock.js

@ -10,9 +10,13 @@ var Lock = require('../lib/locallock');
describe('Local locks', function() { describe('Local locks', function() {
var lock; var lock;
beforeEach(function() { beforeEach(function() {
this.clock = sinon.useFakeTimers();
lock = new Lock(); lock = new Lock();
}); });
it('should lock tasks using the same token', function(done) { afterEach(function() {
this.clock.restore();
});
it('should lock tasks using the same token', function() {
var a = false, var a = false,
b = false; b = false;
lock.locked('123', 0, 0, function(err, release) { lock.locked('123', 0, 0, function(err, release) {
@ -27,17 +31,13 @@ describe('Local locks', function() {
release(); release();
}); });
}); });
setTimeout(function() { a.should.equal(true);
a.should.equal(true); b.should.equal(false);
b.should.equal(false); this.clock.tick(10);
}, 1); a.should.equal(true);
setTimeout(function() { b.should.equal(true);
a.should.equal(true);
b.should.equal(true);
done();
}, 8);
}); });
it('should not lock tasks using different tokens', function(done) { it('should not lock tasks using different tokens', function() {
var i = 0; var i = 0;
lock.locked('123', 0, 0, function(err, release) { lock.locked('123', 0, 0, function(err, release) {
should.not.exist(err); should.not.exist(err);
@ -51,12 +51,9 @@ describe('Local locks', function() {
release(); release();
}); });
}); });
setTimeout(function() { i.should.equal(2);
i.should.equal(2);
done();
}, 1);
}); });
it('should return error if unable to acquire lock', function(done) { it('should return error if unable to acquire lock', function() {
lock.locked('123', 0, 0, function(err, release) { lock.locked('123', 0, 0, function(err, release) {
should.not.exist(err); should.not.exist(err);
setTimeout(function() { setTimeout(function() {
@ -65,24 +62,50 @@ describe('Local locks', function() {
lock.locked('123', 1, 0, function(err, release) { lock.locked('123', 1, 0, function(err, release) {
should.exist(err); should.exist(err);
err.toString().should.contain('Could not acquire lock 123'); err.toString().should.contain('Could not acquire lock 123');
done();
}); });
}); });
this.clock.tick(2);
}); });
it('should release lock if acquired for a long time', function(done) { it('should release lock if acquired for a long time', function() {
var i = 0; var i = 0;
lock.locked('123', 0, 3, function(err, release) { lock.locked('123', 0, 3, function(err, release) {
should.not.exist(err); should.not.exist(err);
i++; i++;
lock.locked('123', 15, 0, function(err, release) { lock.locked('123', 20, 0, function(err, release) {
should.not.exist(err); should.not.exist(err);
i++; i++;
release();
});
});
i.should.equal(1);
this.clock.tick(1);
i.should.equal(1);
this.clock.tick(10);
i.should.equal(2);
});
it('should only release one pending task on lock timeout', function() {
var i = 0;
lock.locked('123', 0, 3, function(err, release) {
should.not.exist(err);
i++;
lock.locked('123', 5, 0, function(err, release) {
should.not.exist(err);
i++;
setTimeout(function() {
release();
}, 5);
});
lock.locked('123', 20, 0, function(err, release) {
should.not.exist(err);
i++;
release();
}); });
}); });
setTimeout(function() { i.should.equal(1);
i.should.equal(2); this.clock.tick(4);
done(); i.should.equal(2)
}, 10); this.clock.tick(7);
i.should.equal(3)
}); });
}); });

Loading…
Cancel
Save