From 5db0abe064f0a1e88e26c60fd04bd80570768efd Mon Sep 17 00:00:00 2001 From: Micah Chalmer Date: Sun, 13 Jan 2013 17:29:50 -0500 Subject: [PATCH 1/3] Add test for slow-running callback This test makes a tick callback run for longer than the update interval by busy-waiting. The purpose of having it is that the stopwatch should not rely on being able to get an interval event for each and every interval. Instead, it should use the amount of elapsed time as measured by the system clock. --- test/test.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/test.js b/test/test.js index 7f505a7..77d59d1 100644 --- a/test/test.js +++ b/test/test.js @@ -185,3 +185,33 @@ test("Custom update interval", function(){ start(); }, 2000); }); + +test("Slow callback should not affect accuracy", function(){ + $elem.stopwatch('init', {updateInterval: 100}); + // We're going to sleep for 50 millisecs, with the stopwatch running + // on a 10-milisecond interval. The result should be two ticks: + // the first after 10 milliseconds, and the next after 60. The second + // one should nevertheless report a 60 millisec elapsed time. + var ticksEncountered = 0; + var data = $elem.data('stopwatch'); + var tickFunction; + tickFunction = function(e, elapsed) { + if (ticksEncountered >= 1) { + equals(ticksEncountered, 1); + equals(data.elapsed, 600); + $elem.stopwatch().unbind('tick.stopwatch', tickFunction); + start(); + } else { + // Yes, this is a ridiculous busy-wait. But the point is to test + // for callbacks that inherently take longer than our interval to + // complete. + var startDt = new Date(); + var currDt = null; + do { currDt = new Date(); } while (currDt - startDt < 500); + } + ++ticksEncountered; + }; + $elem.stopwatch().bind('tick.stopwatch', tickFunction); + stop(); + $elem.stopwatch('start'); +}); From e2cb40b46fcb8c6a013bb8d36c74c15aff1475ea Mon Sep 17 00:00:00 2001 From: Micah Chalmer Date: Sun, 13 Jan 2013 17:42:55 -0500 Subject: [PATCH 2/3] Add test where stopwatch is paused and restarted This test is to verify that the stopwatch's time does NOT increase while the stopwatch is in a "stopped" state. The codebase already passes the test as of this commit, but when it is switched to actually use the system clock to get the elapsed time, this becomes important to verify. --- test/test.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/test.js b/test/test.js index 77d59d1..7449e03 100644 --- a/test/test.js +++ b/test/test.js @@ -62,6 +62,34 @@ test("Stop", function(){ equals($elem.data('stopwatch').active, false); }); +test("Stop and then Start", function(){ + var interval = $elem.data('stopwatch').updateInterval; + // Start the stopwatch for one tick, stop it for more than two ticks, then + // restart it for one more tick. The elapsed time should be two ticks, even + // though three and a half ticks worth of actual wall clock time went by. + var firstTick = true; + var restart = function() { + // in-between waiting time is over; restart the stopwatch + $elem.stopwatch('start'); + } + $elem.stopwatch().bind('tick.stopwatch', function(e, elapsed) { + if (firstTick) { + // First tick: stop and wait 2.5 ticks + equals($elem.stopwatch('getTime'), interval); + $elem.stopwatch('stop'); + setTimeout(restart, Math.floor(interval*2.5)); + } else { + // Second tick: stop the stopwatch, elapsed should be 2 ticks + equals($elem.stopwatch('getTime'), interval * 2); + $elem.stopwatch('stop'); + start(); + } + firstTick = false; + }); + stop(); + $elem.stopwatch('start'); +}); + test("Toggle", function(){ $elem.stopwatch('stop'); $elem.stopwatch('toggle'); From 3c186ae5444b61afac70e001da116a8c43de39c6 Mon Sep 17 00:00:00 2001 From: Micah Chalmer Date: Sun, 13 Jan 2013 17:48:23 -0500 Subject: [PATCH 3/3] Use wall clock for reporting elapsed time --- jquery.stopwatch.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/jquery.stopwatch.js b/jquery.stopwatch.js index eeeb48b..4f223a7 100644 --- a/jquery.stopwatch.js +++ b/jquery.stopwatch.js @@ -1,7 +1,20 @@ (function( $ ){ function incrementer(ct, increment) { - return function() { ct+=increment; return ct; }; + var startDate = new Date(); + var startCt = ct; + // Make sure we have an actual number for startCt + if (!startCt) { + startCt = 0; + } + var result = function() { + return startCt + Math.round((new Date() - startDate)/increment)*increment; + }; + result.restart = function(newCt) { + startCt = newCt; + startDate = new Date(); + } + return result; } function pad2(number) { @@ -79,6 +92,9 @@ var $this = $(this), data = $this.data('stopwatch'); // Mark as active + if(!data.active) { + data.incrementer.restart(data.elapsed); + } data.active = true; data.timerID = setInterval(data.tick_function, data.updateInterval); $this.data('stopwatch', data);