Skip to content

Commit

Permalink
Fix burst of in-the-past pedal events.
Browse files Browse the repository at this point in the history
Fix issue where a burst of pedal events from the past would be emitted
when transitioning from zero cadence to non-zero or similar. Also add
some tests for the pedal event timestamps.

Co-authored-by: Jeremy Klein <[email protected]>
  • Loading branch information
ptx2 and jeremydk committed Oct 8, 2020
1 parent 3d42206 commit e1e9c68
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 1 deletion.
131 changes: 131 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@babel/plugin-transform-parameters": "^7.9.5",
"@babel/preset-env": "^7.9.5",
"eslint": "^6.8.0",
"sinon": "^9.2.0",
"tape": "^5.0.0"
}
}
2 changes: 1 addition & 1 deletion src/app/simulation.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class Simulation extends EventEmitter {
let now = Date.now();
let timeSinceLast = now - this._lastPedalTime;
let timeUntilNext = Math.max(0, this._interval - timeSinceLast);
let nextPedalTime = now - timeSinceLast + this._interval;
let nextPedalTime = now + timeUntilNext;
this._timeoutId = setTimeout(() => {
this.onPedal(nextPedalTime);
this.schedulePedal();
Expand Down
143 changes: 143 additions & 0 deletions src/test/app/simulation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {test} from 'tape';
import sinon from 'sinon';
import {Simulation} from '../../app/simulation';

test('constant cadence', t => {
const timeline = [
cadenceChange(0, 60),
pedalEvent(0),
pedalEvent(1000),
pedalEvent(2000),
pedalEvent(3000),
];

testTimeline(timeline, t);
});

test('start/stop/start', t => {
const timeline = [
cadenceChange(0, 60),
pedalEvent(0),
pedalEvent(1000),
pedalEvent(2000),
pedalEvent(3000),

cadenceChange(3001, 0),

cadenceChange(100000, 1000),
pedalEvent(100000),
pedalEvent(100060),
]

testTimeline(timeline, t);
});

test('inconsequential cadence changes', t => {
const timeline = [
cadenceChange(0, 10),
pedalEvent(0),
pedalEvent(6000),
pedalEvent(12000),
cadenceChange(12001, 20),
cadenceChange(12002, 30),
cadenceChange(12020, 40),
cadenceChange(12100, 50),
cadenceChange(12150, 120),
cadenceChange(12499, 60),
cadenceChange(12999, 30),
pedalEvent(14000),
]

testTimeline(timeline, t);
});

test('increase/decrease cadence', t => {
const timeline = [
cadenceChange(0, 10),
pedalEvent(0),
pedalEvent(6000),

cadenceChange(6001, 1000),
pedalEvent(6060),
pedalEvent(6120),
pedalEvent(6180),

cadenceChange(6181, 60),
pedalEvent(7180),
pedalEvent(8180),
]

testTimeline(timeline, t);
});

test('varying cadence', t => {
const timeline = [
cadenceChange(0, 60),
pedalEvent(0),
pedalEvent(1000),
pedalEvent(2000),

cadenceChange(2001, 120),
pedalEvent(2500),
pedalEvent(3000),

cadenceChange(3100, 30),
pedalEvent(5000),
pedalEvent(7000),

cadenceChange(8999, 10),
pedalEvent(13000),
pedalEvent(19000),

cadenceChange(24999, 1000),
pedalEvent(24999),
pedalEvent(25059),
pedalEvent(25119),

cadenceChange(25178, 60),
pedalEvent(26119),
]

testTimeline(timeline, t);
});


const C = 'CADENCE_CHANGE';
const P = 'PEDAL_EMIT';
const cadenceChange = (timestamp, cadence) => ({timestamp, type: C, cadence})
const pedalEvent = (timestamp) => ({timestamp, type: P})
const isPedalEvent = (evt) => evt.type === P
const isCadenceChange = (evt) => evt.type === C

/**
* Test that pedal events are emitted with the expected timestamps.
* @param {object[]} timeline - timestamped events (pedal or cadence change)
* @param {string} timeline[].type - event type P|C (pedal or cadence change)
* @param {number} timeline[].timestamp - millisecond timestamp
* @param {number} [timeline[].cadence] - cadence in rpm (only for cadence change event)
* @param {Test} t - tape test object
*/
function testTimeline(timeline, t) {
const timestamps = timeline.filter(isPedalEvent).map(e => e.timestamp);
const cadenceChanges = timeline.filter(isCadenceChange);
const duration = Math.max(...timestamps);

const clock = sinon.useFakeTimers();
const sim = new Simulation();

// change sim.cadence at the appropriate times
for (let {timestamp, cadence} of cadenceChanges) {
setTimeout(() => { sim.cadence = cadence; }, timestamp);
}

t.plan(timestamps.length);

let i = 0;
sim.on('pedal', (timestamp) => {
t.equal(timestamp, timestamps[i], `pedal event ${timestamp}`);
i++;
});

clock.tick(duration);
clock.restore();
}

0 comments on commit e1e9c68

Please sign in to comment.