Skip to content
This repository has been archived by the owner on Jun 16, 2018. It is now read-only.

Commit

Permalink
Merge eb4582b into a243e4a
Browse files Browse the repository at this point in the history
  • Loading branch information
poelstra committed Oct 27, 2017
2 parents a243e4a + eb4582b commit df1ca13
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 14 deletions.
1 change: 1 addition & 0 deletions karma.conf.js
Expand Up @@ -20,6 +20,7 @@ module.exports = function(config) {
'src/components/q/q.js',
'src/components/idbwrapper/idbstore.js',
'spec/helpers/*.js',
'src/js/factories/*.js',
'src/js/directives/*.js',
'src/js/services/*.js',
'src/js/views/*.js',
Expand Down
103 changes: 103 additions & 0 deletions spec/factories/pollerSpec.js
@@ -0,0 +1,103 @@
describe('poller', function () {
"use strict";

var ngFactories = factory('factories/ng-factories');
var module = factory('factories/poller', {
'factories/ng-factories': ngFactories
});

var Poller;
var $interval;
var poller;
var callback;
var INTERVAL = 1000;

beforeEach(function () {
angular.mock.module(module.name);
angular.mock.inject(["Poller", "$interval", function (_Poller_, _$interval_) {
Poller = _Poller_;
$interval = _$interval_;
callback = jasmine.createSpy("callback");
poller = new Poller(INTERVAL, callback);
}]);

});

describe('constructor', function () {
it('should not start poller', function () {
$interval.flush(1001);
expect(callback).not.toHaveBeenCalled();
});
});

describe('start', function () {
it('should be started when someone is interested', function () {
poller.start();
$interval.flush(INTERVAL + 1);
expect(callback).toHaveBeenCalledTimes(1);
});
it('should keep running when someone is interested', function () {
poller.start();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(2);
});
});

describe('stop', function () {
it('should stop when no-one is interested, case 1', function () {
poller.start();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
poller.stop();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
});
it('should stop when no-one is interested, case 2', function () {
poller.start();
poller.start();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
poller.stop();
poller.stop();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
});
it('should not stop when at least one user is interested', function () {
poller.start();
poller.start();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(1);
poller.stop();
$interval.flush(INTERVAL);
expect(callback).toHaveBeenCalledTimes(2);
});
it('should throw when stopping more then starting', function () {
poller.start();
poller.stop();
expect(function () {
poller.stop();
}).toThrowError();
})
});

describe('reset', function () {
it('should not start timer when no-one interested', function () {
$interval.flush(INTERVAL / 2);
poller.reset();
$interval.flush(2 * INTERVAL);
expect(callback).not.toHaveBeenCalled();
});
it('should restart timer when someone interested', function () {
poller.start();
$interval.flush(INTERVAL / 2);
expect(callback).not.toHaveBeenCalled();
poller.reset();
$interval.flush(INTERVAL / 2);
expect(callback).not.toHaveBeenCalled();
$interval.flush(INTERVAL / 2);
expect(callback).toHaveBeenCalledTimes(1);
});
});
});
2 changes: 2 additions & 0 deletions spec/mocks/scoresMock.js
Expand Up @@ -25,6 +25,8 @@ function createScoresMock($q,scoreboard) {
update: jasmine.createSpy('scoreUpdateSpy'),
_update: jasmine.createSpy('score_UpdateSpy'),
save: jasmine.createSpy('scoreSaveSpy'),
enableAutoRefresh: jasmine.createSpy('enableAutoRefresh'),
disableAutoRefresh: jasmine.createSpy('disableAutoRefresh'),
getRankings: jasmine.createSpy('getRankings').and.returnValue(scoreboard),
};
}
66 changes: 60 additions & 6 deletions spec/services/ng-scoresSpec.js
Expand Up @@ -5,6 +5,7 @@ describe('ng-scores',function() {
var $stages;
var $teams;
var $q;
var $interval;
var dummyTeam = {
number: 123,
name: 'foo'
Expand Down Expand Up @@ -40,12 +41,16 @@ describe('ng-scores',function() {
angular.mock.module(function($provide) {
$provide.value('$fs', fsMock);
});
angular.mock.inject(["$scores", "$stages", "$teams", "$q", function(_$scores_, _$stages_, _$teams_,_$q_) {
$scores = _$scores_;
$stages = _$stages_;
$teams = _$teams_;
$q = _$q_;
}]);
angular.mock.inject([
"$scores", "$stages", "$teams", "$q", "$interval",
function(_$scores_, _$stages_, _$teams_, _$q_, $_interval_) {
$scores = _$scores_;
$stages = _$stages_;
$teams = _$teams_;
$q = _$q_;
$interval = $_interval_;
}
]);

return $stages.init().then(function() {
mockStage = $stages.get(rawMockStage.id);
Expand Down Expand Up @@ -560,5 +565,54 @@ describe('ng-scores',function() {
});
});

describe('autoRefresh', function() {
it('should not be started when no-one is interested', function () {
fsMock.read.calls.reset();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).not.toHaveBeenCalled();
});
it('should be started when someone is interested', function () {
fsMock.read.calls.reset();
$scores.enableAutoRefresh();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).toHaveBeenCalled();
});
it('should keep refreshing when someone is interested', function () {
fsMock.read.calls.reset();
$scores.enableAutoRefresh();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).toHaveBeenCalled();
fsMock.read.calls.reset();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).toHaveBeenCalled();
});
it('should be stopped when everyone unregistered', function () {
$scores.enableAutoRefresh();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
$scores.disableAutoRefresh();

fsMock.read.calls.reset();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).not.toHaveBeenCalled();
});
it('should skip refresh when busy', function () {
fsMock.read.calls.reset();
$scores.enableAutoRefresh();
$scores.beginupdate();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).not.toHaveBeenCalled();
});
it('should continue refresh after it was busy before', function () {
fsMock.read.calls.reset();
$scores.enableAutoRefresh();
$scores.beginupdate();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).not.toHaveBeenCalled();
$scores.endupdate();
$interval.flush($scores.AUTO_REFRESH_INTERVAL + 1);
expect(fsMock.read).toHaveBeenCalled();
});
});

});

3 changes: 3 additions & 0 deletions src/js/factories/ng-factories.js
@@ -0,0 +1,3 @@
define('factories/ng-factories', ['angular'], function () {
return angular.module('factories', []);
});
79 changes: 79 additions & 0 deletions src/js/factories/poller.js
@@ -0,0 +1,79 @@
/**
* Variant of $interval() which only starts the interval
* when at least one user is interested.
*/
define('factories/poller', [
'factories/ng-factories',
], function (module) {
"use strict";

return module.factory('Poller', [
'$interval',
function($interval) {

/**
* Ref-counted $interval.
* Starts calling `callback` every `interval` milliseconds when
* number of calls to `start()` is bigger than number of calls to `stop()`.
*
* Usage example:
* const p = new Poller(10000, () => { console.log("tick"); });
* // when instantiating controller:
* p.start();
* // when destroying controller:
* p.stop();
*/
function Poller(interval, callback) {
this._interval = interval;
this._callback = callback;
this._refs = 0;
this._handle = undefined; // $interval handle
}

/**
* Enable polling.
* Multiple calls to start() can be made. As long as the number of
* stop() calls is smaller than the number of start() calls, the
* poller keeps running.
*/
Poller.prototype.start = function () {
this._refs++;
this.reset();
};

/**
* Disable polling when the number of stop calls is equal to
* the number of start calls.
* It is an error to call stop more often than start.
*/
Poller.prototype.stop = function () {
if (this._refs <= 0) {
throw new Error("start/stop calls mismatched");
}
this._refs--;
if (this._refs === 0) {
// Keep timer running if others are still interested
this.reset();
}
};

/**
* Restart poller, if it is currently running.
*/
Poller.prototype.reset = function() {
var self = this;
if (this._handle !== undefined) {
$interval.cancel(this._handle);
this._handle = undefined;
}
if (this._refs > 0) {
this._handle = $interval(function () {
self._callback();
}, this._interval);
}
};

return Poller;
}
]);
});
6 changes: 4 additions & 2 deletions src/js/main.js
Expand Up @@ -6,6 +6,7 @@ define([
'views/scoresheet',
'views/scores',
'views/ranking',
'factories/ng-factories',
'services/ng-services',
'directives/ng-directives',
'directives/size',
Expand All @@ -17,7 +18,7 @@ define([
'angular-touch',
'angular-sanitize',
'angular'
],function(log,session,settings,teams,scoresheet,scores,ranking,services,directives,size,filters,indexFilter,fsTest,dbTest) {
],function(log,session,settings,teams,scoresheet,scores,ranking,factories,services,directives,size,filters,indexFilter,fsTest,dbTest) {

log('device ready');

Expand Down Expand Up @@ -107,6 +108,7 @@ define([
ranking.name,
filters.name,
services.name,
directives.name
directives.name,
factories.name,
]);
});

0 comments on commit df1ca13

Please sign in to comment.