Some exploration and refactoring re waitsFor() bug: waitsFor() hangs forever if latch function never returns true.

This commit is contained in:
Christian Williams 2010-08-19 23:55:21 -07:00
parent b3715075e3
commit 762f88e3c8
3 changed files with 99 additions and 85 deletions

View File

@ -258,86 +258,89 @@ describe("jasmine spec running", function () {
expect(another_spec.results().getItems()[0].passed()).toEqual(true);
});
it("testWaitsFor", function() {
var doneWaiting = false;
var runsBlockExecuted = false;
describe("waitsFor", function() {
it("testWaitsFor", function() {
var doneWaiting = false;
var runsBlockExecuted = false;
var spec;
env.describe('foo', function() {
spec = env.it('has a waits for', function() {
this.runs(function() {
});
var spec;
env.describe('foo', function() {
spec = env.it('has a waits for', function() {
this.runs(function() {
});
this.waitsFor(500, function() {
return doneWaiting;
});
this.waitsFor(500, function() {
return doneWaiting;
});
this.runs(function() {
runsBlockExecuted = true;
this.runs(function() {
runsBlockExecuted = true;
});
});
});
spec.execute();
expect(runsBlockExecuted).toEqual(false); //, 'should not have executed runs block yet');
fakeTimer.tick(100);
doneWaiting = true;
fakeTimer.tick(100);
expect(runsBlockExecuted).toEqual(true); //, 'should have executed runs block');
});
spec.execute();
expect(runsBlockExecuted).toEqual(false); //, 'should not have executed runs block yet');
fakeTimer.tick(100);
doneWaiting = true;
fakeTimer.tick(100);
expect(runsBlockExecuted).toEqual(true); //, 'should have executed runs block');
});
it("fails with message", function() {
var spec;
env.describe('foo', function() {
spec = env.it('has a waits for', function() {
this.runs(function() {
});
it("testWaitsForFailsWithMessage", function() {
var spec;
env.describe('foo', function() {
spec = env.it('has a waits for', function() {
this.runs(function() {
});
this.waitsFor(500, function() {
return false; // force a timeout
}, 'my awesome condition');
this.waitsFor(500, function() {
return false; // force a timeout
}, 'my awesome condition');
this.runs(function() {
this.runs(function() {
});
});
});
spec.execute();
fakeTimer.tick(1000);
expect(spec.results().getItems()[0].message).toEqual('timeout: timed out after 500 msec waiting for my awesome condition');
});
spec.execute();
fakeTimer.tick(1000);
var actual = spec.results().getItems()[0].message;
var expected = 'timeout: timed out after 500 msec waiting for my awesome condition';
expect(actual).toEqual(expected);
});
it("fails and skips the rest of the spec if timeout is reached and the latch function hasn't returned true", function() {
var runsBlockExecuted = false;
var subsequentSpecRan = false;
it("waitsFor fails and skips the rest of the spec if timeout is reached and the latch function is still false", function() {
var runsBlockExecuted = false;
var timeoutSpec, subsequentSpec;
var suite = env.describe('foo', function() {
timeoutSpec = env.it('has a waits for', function() {
this.runs(function() {
});
var spec;
env.describe('foo', function() {
spec = env.it('has a waits for', function() {
this.runs(function() {
this.waitsFor(500, function() {
return false;
});
this.runs(function() {
runsBlockExecuted = true;
});
});
this.waitsFor(500, function() {
return false;
});
this.runs(function() {
runsBlockExecuted = true;
subsequentSpec = env.it('then carries on to the next test', function() {
subsequentSpecRan = true;
});
});
});
spec.execute();
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(100);
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(400);
expect(runsBlockExecuted).toEqual(false);
var actual = spec.results().getItems()[0].message;
var expected = 'timeout: timed out after 500 msec waiting for something to happen';
expect(actual).toEqual(expected,
'expected "' + expected + '" but found "' + actual + '"');
env.execute();
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(100);
expect(runsBlockExecuted).toEqual(false);
fakeTimer.tick(400);
expect(runsBlockExecuted).toEqual(false);
expect(timeoutSpec.results().getItems()[0].message).toEqual('timeout: timed out after 500 msec waiting for something to happen');
// todo: expect(subsequentSpecRan).toEqual(true); [xw 20100819]
});
});
it("testSpecAfter", function() {
@ -520,17 +523,15 @@ describe("jasmine spec running", function () {
});
describe('#waitsFor should allow consecutive calls', function () {
var foo;
beforeEach(function () {
foo = 0;
});
it('exits immediately (does not stack) if the latchFunction times out', function () {
var reachedFirstWaitsFor = false;
var reachedSecondWaitsFor = false;
var waitsSuite = env.describe('suite that waits', function () {
env.describe('suite that waits', function () {
env.it('should stack timeouts', function() {
this.waitsFor(500, function () {
reachedFirstWaitsFor = true;
@ -546,7 +547,7 @@ describe("jasmine spec running", function () {
});
expect(reachedFirstWaitsFor).toEqual(false);
waitsSuite.execute();
env.execute();
expect(reachedFirstWaitsFor).toEqual(true);
expect(foo).toEqual(0);

View File

@ -15,7 +15,7 @@ describe('WaitsForBlock', function () {
return true;
};
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(onComplete).wasNotCalled();
expect(onComplete).not.toHaveBeenCalled();
block.execute(onComplete);
expect(onComplete).toHaveBeenCalled();
});
@ -51,22 +51,22 @@ describe('WaitsForBlock', function () {
env.clearInterval = fakeTimer.clearInterval;
});
it('latchFunction should be retried after 100 ms', function () {
it('latchFunction should be retried after 10 ms', function () {
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(latchFunction).wasNotCalled();
expect(latchFunction).not.toHaveBeenCalled();
block.execute(onComplete);
expect(latchFunction.callCount).toEqual(1);
fakeTimer.tick(50);
fakeTimer.tick(5);
expect(latchFunction.callCount).toEqual(1);
fakeTimer.tick(50);
fakeTimer.tick(5);
expect(latchFunction.callCount).toEqual(2);
});
it('onComplete should be called if latchFunction returns true before timeout', function () {
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
expect(onComplete).wasNotCalled();
expect(onComplete).not.toHaveBeenCalled();
block.execute(onComplete);
expect(onComplete).wasNotCalled();
expect(onComplete).not.toHaveBeenCalled();
latchFunction.andReturn(true);
fakeTimer.tick(100);
expect(onComplete).toHaveBeenCalled();
@ -76,12 +76,12 @@ describe('WaitsForBlock', function () {
spyOn(spec, 'fail');
var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec);
block.execute(onComplete);
expect(spec.fail).wasNotCalled();
expect(spec.fail).not.toHaveBeenCalled();
fakeTimer.tick(timeout);
expect(spec.fail).toHaveBeenCalled();
var failMessage = spec.fail.mostRecentCall.args[0].message;
expect(failMessage).toMatch(message);
expect(onComplete).wasNotCalled();
expect(onComplete).not.toHaveBeenCalled(); // todo: this is an issue... [xw 20100819]
});
});
});

View File

@ -1,3 +1,14 @@
/**
* A block which waits for some condition to become true, with timeout.
*
* @constructor
* @extends jasmine.Block
* @param {jasmine.Env} env The Jasmine environment.
* @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
* @param {Function} latchFunction A function which returns true when the desired condition has been met.
* @param {String} message The message to display if the desired condition hasn't been met within the given time period.
* @param {jasmine.Spec} spec The Jasmine spec.
*/
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
this.timeout = timeout;
this.latchFunction = latchFunction;
@ -5,33 +16,35 @@ jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
this.totalTimeSpentWaitingForLatch = 0;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 100;
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
jasmine.WaitsForBlock.prototype.execute = function (onComplete) {
var self = this;
self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen'));
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
var latchFunctionResult;
try {
latchFunctionResult = self.latchFunction.apply(self.spec);
latchFunctionResult = this.latchFunction.apply(this.spec);
} catch (e) {
self.spec.fail(e);
this.spec.fail(e);
onComplete();
return;
}
if (latchFunctionResult) {
onComplete();
} else if (self.totalTimeSpentWaitingForLatch >= self.timeout) {
var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen');
self.spec.fail({
} else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
this.spec.fail({
name: 'timeout',
message: message
});
// todo: need to prevent additional blocks in this spec from running... [xw 20100819]
} else {
self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
self.env.setTimeout(function () { self.execute(onComplete); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
var self = this;
this.env.setTimeout(function() {
self.execute(onComplete);
}, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
}
};