Skip to content

Commit d19d40d

Browse files
committed
test: Fix real time player not triggered during event loop
Running tests synchronously preventing rt player from advancing which cause each player update to be considered an overdrift
1 parent b3e1c7f commit d19d40d

File tree

3 files changed

+48
-23
lines changed

3 files changed

+48
-23
lines changed

src/backend/sources/PlayerState/RealtimePlayer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { childLogger, Logger } from "@foxxmd/logging";
22
import dayjs, { Dayjs } from "dayjs";
33
import { SimpleIntervalJob, Task, ToadScheduler } from "toad-scheduler";
44

5-
const RT_TICK = 500;
5+
export const RT_TICK_DEFAULT = 500;
6+
let RT_TICK = 500;
7+
8+
export const setRtTick = (tick: number) => {
9+
RT_TICK = tick;
10+
}
611

712
export abstract class RealtimePlayer {
813

src/backend/tests/source/TestSource.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ export class TestSource extends AbstractSource {
99
}
1010
}
1111

12-
export class TestMemorySource extends MemoryPositionalSource {
13-
12+
export class TestMemorySource extends MemorySource {
1413
}
1514

1615
export class TestMemoryPositionalSource extends MemoryPositionalSource {

src/backend/tests/source/source.test.ts

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { SourceConfig } from "../../common/infrastructure/config/source/sources.
1717
import MemorySource from "../../sources/MemorySource.js";
1818
import { timePassesScrobbleThreshold } from "../../utils/TimeUtils.js";
1919
import { DEFAULT_SCROBBLE_DURATION_THRESHOLD, DEFAULT_SCROBBLE_PERCENT_THRESHOLD } from "../../common/infrastructure/Atomic.js";
20+
import { RT_TICK_DEFAULT, setRtTick } from "../../sources/PlayerState/RealtimePlayer.js";
21+
import { sleep } from "../../utils.js";
2022

2123
chai.use(asPromised);
2224

@@ -186,9 +188,13 @@ describe('Player Cleanup', function () {
186188

187189
this.afterEach(() => {
188190
MockDate.reset();
191+
setRtTick(RT_TICK_DEFAULT);
192+
});
193+
this.beforeEach(() => {
194+
setRtTick(1);
189195
});
190196

191-
const cleanedUpDuration = (generateSource: (config: SourceConfig) => MemorySource) => {
197+
const cleanedUpDuration = async (generateSource: (config: SourceConfig) => MemorySource) => {
192198
const source = generateSource({data: {staleAfter: 21, orphanedAfter: 40}, options: {}});
193199
const initialDate = dayjs();
194200
const initialState = generatePlayerStateData({position: 0, playData: {duration: 50}, timestamp: initialDate, status: REPORTED_PLAYER_STATUSES.playing});
@@ -202,6 +208,7 @@ describe('Player Cleanup', function () {
202208
position += 10;
203209
timeSince += 10;
204210
MockDate.set(initialDate.add(position, 'seconds').toDate());
211+
await sleep(1);
205212
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: dayjs(), position, status: REPORTED_PLAYER_STATUSES.playing});
206213
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
207214
}
@@ -210,25 +217,27 @@ describe('Player Cleanup', function () {
210217
for(let i = 0; i < 2; i++) {
211218
timeSince += 10;
212219
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
220+
await sleep(1);
213221
expect(source.processRecentPlays([]).length).to.be.eq(0);
214222
}
215223

216224
MockDate.set(initialDate.add(timeSince + 2, 'seconds').toDate());
225+
await sleep(1);
217226
const discoveredPlays = source.processRecentPlays([]);
218227
// cleanup should discover stale play
219228
expect(discoveredPlays.length).to.be.eq(1);
220229
expect(discoveredPlays[0].data.listenedFor).closeTo(30, 2);
221230
}
222231

223-
it('Discovers cleaned up Play with correct duration (Non Positional Source)', function () {
224-
cleanedUpDuration(generateMemorySource);
232+
it('Discovers cleaned up Play with correct duration (Non Positional Source)', async function () {
233+
await cleanedUpDuration(generateMemorySource);
225234
});
226235

227-
it('Discovers cleaned up Play with correct duration (Positional Source)', function () {
228-
cleanedUpDuration(generateMemoryPositionalSource);
236+
it('Discovers cleaned up Play with correct duration (Positional Source)', async function () {
237+
await cleanedUpDuration(generateMemoryPositionalSource);
229238
});
230239

231-
const noScrobbleRediscoveryOnActive = (generateSource: (config: SourceConfig) => MemorySource) => {
240+
const noScrobbleRediscoveryOnActive = async (generateSource: (config: SourceConfig) => MemorySource) => {
232241

233242
const source = generateSource({data: {staleAfter: 21, orphanedAfter: 40}, options: {}});
234243
const initialDate = dayjs();
@@ -243,6 +252,7 @@ describe('Player Cleanup', function () {
243252
position += 10;
244253
timeSince += 10;
245254
MockDate.set(initialDate.add(position, 'seconds').toDate());
255+
await sleep(1);
246256
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: dayjs(), position, status: REPORTED_PLAYER_STATUSES.playing});
247257
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
248258
}
@@ -251,12 +261,14 @@ describe('Player Cleanup', function () {
251261
for(let i = 0; i < 2; i++) {
252262
timeSince += 10;
253263
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
264+
await sleep(1);
254265
expect(source.processRecentPlays([]).length).to.be.eq(0);
255266
}
256267

257268
timeSince += 2;
258269

259270
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
271+
await sleep(1);
260272
const discoveredPlays = source.processRecentPlays([]);
261273
// cleanup should discover stale play
262274
expect(discoveredPlays.length).to.be.eq(1);
@@ -269,12 +281,14 @@ describe('Player Cleanup', function () {
269281
for(let i = 0; i < 2; i++) {
270282
timeSince += 10;
271283
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
284+
await sleep(1);
272285
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: dayjs(), position, status: REPORTED_PLAYER_STATUSES.playing});
273286
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
274287
}
275288

276289
timeSince += 10;
277290
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
291+
await sleep(1);
278292
// new Play
279293
const advancedState = generatePlayerStateData({timestamp: dayjs(), position: 0, status: REPORTED_PLAYER_STATUSES.playing});
280294
// should not return play because it has only been played for ~20 seconds, less than 50% of duration
@@ -283,15 +297,15 @@ describe('Player Cleanup', function () {
283297
}
284298

285299

286-
it('Does not discover same Play after becoming active again (Non Positional Source)', function () {
287-
noScrobbleRediscoveryOnActive(generateMemorySource);
300+
it('Does not discover same Play after becoming active again (Non Positional Source)', async function () {
301+
await noScrobbleRediscoveryOnActive(generateMemorySource);
288302
});
289303

290-
it('Does not discover same Play after becoming active again (Positional Source)', function () {
291-
noScrobbleRediscoveryOnActive(generateMemoryPositionalSource);
304+
it('Does not discover same Play after becoming active again (Positional Source)', async function () {
305+
await noScrobbleRediscoveryOnActive(generateMemoryPositionalSource);
292306
});
293307

294-
const noScrobbleStale = (generateSource: (config: SourceConfig) => MemorySource) => {
308+
const noScrobbleStale = async (generateSource: (config: SourceConfig) => MemorySource) => {
295309

296310
const source = generateSource({data: {staleAfter: 21, orphanedAfter: 40}, options: {}});
297311
const initialDate = dayjs();
@@ -308,6 +322,7 @@ describe('Player Cleanup', function () {
308322
position += 10;
309323
timeSince += 10;
310324
MockDate.set(initialDate.add(position, 'seconds').toDate());
325+
await sleep(1);
311326
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: initialDate, position, status: REPORTED_PLAYER_STATUSES.playing});
312327
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
313328
}
@@ -316,6 +331,7 @@ describe('Player Cleanup', function () {
316331
for(let i = 0; i < 2; i++) {
317332
timeSince += 10;
318333
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
334+
await sleep(1);
319335
expect(source.processRecentPlays([]).length).to.be.eq(0);
320336
}
321337

@@ -326,15 +342,15 @@ describe('Player Cleanup', function () {
326342

327343
}
328344

329-
it('Does not discover cleaned up Play that did not meet threshold (Non Positional Source)', function () {
330-
noScrobbleStale(generateMemorySource);
345+
it('Does not discover cleaned up Play that did not meet threshold (Non Positional Source)', async function () {
346+
await noScrobbleStale(generateMemorySource);
331347
});
332348

333-
it('Does not discover cleaned up Play that did not meet threshold (Positional Source)', function () {
334-
noScrobbleStale(generateMemoryPositionalSource);
349+
it('Does not discover cleaned up Play that did not meet threshold (Positional Source)', async function () {
350+
await noScrobbleStale(generateMemoryPositionalSource);
335351
});
336352

337-
const scrobbleRediscoveryOnActive = (generateSource: (config: SourceConfig) => MemorySource) => {
353+
const scrobbleRediscoveryOnActive = async (generateSource: (config: SourceConfig) => MemorySource) => {
338354

339355
const source = generateSource({data: {staleAfter: 21, orphanedAfter: 40}, options: {}});
340356
const initialDate = dayjs();
@@ -351,6 +367,7 @@ describe('Player Cleanup', function () {
351367
position += 10;
352368
timeSince += 10;
353369
MockDate.set(initialDate.add(position, 'seconds').toDate());
370+
await sleep(1);
354371
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: dayjs(), position, status: REPORTED_PLAYER_STATUSES.playing});
355372
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
356373
}
@@ -359,12 +376,14 @@ describe('Player Cleanup', function () {
359376
for(let i = 0; i < 2; i++) {
360377
timeSince += 10;
361378
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
379+
await sleep(1);
362380
expect(source.processRecentPlays([]).length).to.be.eq(0);
363381
}
364382

365383
timeSince += 2;
366384

367385
MockDate.set(initialDate.add(timeSince, 'seconds').toDate());
386+
await sleep(1);
368387
const discoveredPlays = source.processRecentPlays([]);
369388
// cleanup should not discover stale play
370389
expect(discoveredPlays.length).to.be.eq(0);
@@ -378,12 +397,14 @@ describe('Player Cleanup', function () {
378397
position += 10;
379398
timeSince += 10;
380399
MockDate.set(initialDate.add(position, 'seconds').toDate());
400+
await sleep(1);
381401
const advancedState = generatePlayerStateData({play: initialState.play, timestamp: dayjs(), position, status: REPORTED_PLAYER_STATUSES.playing});
382402
expect(source.processRecentPlays([advancedState]).length).to.be.eq(0);
383403
}
384404

385405
timeSince += 10;
386406
MockDate.set(initialDate.add(position, 'seconds').toDate());
407+
await sleep(1);
387408
// new Play
388409
const advancedState = generatePlayerStateData({timestamp: dayjs(), position: 0, status: REPORTED_PLAYER_STATUSES.playing});
389410
// should return discovered play with ~90 seconds of duration
@@ -393,12 +414,12 @@ describe('Player Cleanup', function () {
393414

394415
}
395416

396-
it('Does discover Play after becoming active again (Non Positional Source)', function () {
397-
scrobbleRediscoveryOnActive(generateMemorySource);
417+
it('Does discover Play after becoming active again (Non Positional Source)', async function () {
418+
await scrobbleRediscoveryOnActive(generateMemorySource);
398419
});
399420

400-
it('Does discover Play after becoming active again (Positional Source)', function () {
401-
scrobbleRediscoveryOnActive(generateMemoryPositionalSource);
421+
it('Does discover Play after becoming active again (Positional Source)', async function () {
422+
await scrobbleRediscoveryOnActive(generateMemoryPositionalSource);
402423
});
403424
});
404425

0 commit comments

Comments
 (0)