Skip to content

Commit 38660f3

Browse files
committed
extend tests for controllers
1 parent 207e619 commit 38660f3

File tree

4 files changed

+299
-16
lines changed

4 files changed

+299
-16
lines changed

test/unit/mocks/ThroughputControllerMock.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ class ThroughputControllerMock {
1616
return this.averageThroughput;
1717
}
1818

19+
getArithmeticMean(values) {
20+
if (!values || values.length === 0) {
21+
return 0;
22+
}
23+
24+
let sum = 0;
25+
values.forEach((entry) => {
26+
if (entry && typeof entry.value === 'number') {
27+
sum += entry.value;
28+
}
29+
});
30+
31+
if (sum === 0) {
32+
return 0;
33+
}
34+
35+
return sum / values.length;
36+
}
37+
1938
setConfig() {
2039

2140
}

test/unit/test/dash/dash.controllers.ContentSteeringController.js

Lines changed: 199 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,42 @@ describe('ContentSteeringController', function () {
423423
contentSteeringController.initialize();
424424
});
425425

426-
it('should track service locations from fragment loading', function () {
426+
it('should track service locations from fragment loading', async function () {
427+
let capturedUrl;
428+
429+
const mockLoaderInstance = {
430+
load: ({ request, success, complete }) => {
431+
capturedUrl = request.url;
432+
433+
const responseData = {};
434+
responseData[DashConstants.CONTENT_STEERING_RESPONSE.VERSION] = '1';
435+
436+
success(responseData);
437+
if (typeof complete === 'function') {
438+
complete();
439+
}
440+
},
441+
abort: () => {},
442+
reset: () => {},
443+
resetInitialSettings: () => {}
444+
};
445+
446+
function MockLoader() {
447+
return {
448+
create: () => mockLoaderInstance
449+
};
450+
}
451+
452+
const schemeLoaderFactory = SchemeLoaderFactory(context).getInstance();
453+
454+
schemeLoaderFactory.registerLoader('https://', MockLoader);
455+
456+
manifestModelMock.getValue = sinon.stub().returns({});
457+
adapterMock.getContentSteering = sinon.stub().returns({
458+
serverUrl: 'https://steering.example.com',
459+
queryBeforeStart: true
460+
});
461+
427462
eventBus.trigger(MediaPlayerEvents.FRAGMENT_LOADING_STARTED, {
428463
request: {
429464
serviceLocation: 'cdn1'
@@ -435,9 +470,57 @@ describe('ContentSteeringController', function () {
435470
serviceLocation: 'cdn2'
436471
}
437472
});
473+
474+
try {
475+
await contentSteeringController.loadSteeringData();
476+
477+
expect(capturedUrl).to.be.a('string');
478+
const queryString = capturedUrl.split('?')[1];
479+
const params = new URLSearchParams(queryString);
480+
const pathway = params.get('_DASH_pathway');
481+
482+
expect(pathway).to.equal('"cdn1,cdn2"');
483+
} finally {
484+
schemeLoaderFactory.unregisterLoader('https://');
485+
}
438486
});
439487

440-
it('should track service locations from manifest loading', function () {
488+
it('should track service locations from manifest loading', async function () {
489+
let capturedUrl;
490+
491+
const mockLoaderInstance = {
492+
load: ({ request, success, complete }) => {
493+
capturedUrl = request.url;
494+
495+
const responseData = {};
496+
responseData[DashConstants.CONTENT_STEERING_RESPONSE.VERSION] = '1';
497+
498+
success(responseData);
499+
if (typeof complete === 'function') {
500+
complete();
501+
}
502+
},
503+
abort: () => {},
504+
reset: () => {},
505+
resetInitialSettings: () => {}
506+
};
507+
508+
function MockLoader() {
509+
return {
510+
create: () => mockLoaderInstance
511+
};
512+
}
513+
514+
const schemeLoaderFactory = SchemeLoaderFactory(context).getInstance();
515+
516+
schemeLoaderFactory.registerLoader('https://', MockLoader);
517+
518+
manifestModelMock.getValue = sinon.stub().returns({});
519+
adapterMock.getContentSteering = sinon.stub().returns({
520+
serverUrl: 'https://steering.example.com',
521+
queryBeforeStart: true
522+
});
523+
441524
eventBus.trigger(MediaPlayerEvents.MANIFEST_LOADING_STARTED, {
442525
request: {
443526
serviceLocation: 'cdn1'
@@ -449,9 +532,57 @@ describe('ContentSteeringController', function () {
449532
serviceLocation: 'cdn2'
450533
}
451534
});
535+
536+
try {
537+
await contentSteeringController.loadSteeringData();
538+
539+
expect(capturedUrl).to.be.a('string');
540+
const queryString = capturedUrl.split('?')[1];
541+
const params = new URLSearchParams(queryString);
542+
const pathway = params.get('_DASH_pathway');
543+
544+
expect(pathway).to.equal('"cdn1,cdn2"');
545+
} finally {
546+
schemeLoaderFactory.unregisterLoader('https://');
547+
}
452548
});
453549

454-
it('should not duplicate service locations', function () {
550+
it('should not duplicate service locations', async function () {
551+
let capturedUrl;
552+
553+
const mockLoaderInstance = {
554+
load: ({ request, success, complete }) => {
555+
capturedUrl = request.url;
556+
557+
const responseData = {};
558+
responseData[DashConstants.CONTENT_STEERING_RESPONSE.VERSION] = '1';
559+
560+
success(responseData);
561+
if (typeof complete === 'function') {
562+
complete();
563+
}
564+
},
565+
abort: () => {},
566+
reset: () => {},
567+
resetInitialSettings: () => {}
568+
};
569+
570+
function MockLoader() {
571+
return {
572+
create: () => mockLoaderInstance
573+
};
574+
}
575+
576+
const schemeLoaderFactory = SchemeLoaderFactory(context).getInstance();
577+
578+
schemeLoaderFactory.registerLoader('https://', MockLoader);
579+
580+
manifestModelMock.getValue = sinon.stub().returns({});
581+
adapterMock.getContentSteering = sinon.stub().returns({
582+
serverUrl: 'https://steering.example.com',
583+
queryBeforeStart: true
584+
});
585+
455586
eventBus.trigger(MediaPlayerEvents.FRAGMENT_LOADING_STARTED, {
456587
request: {
457588
serviceLocation: 'cdn1'
@@ -463,6 +594,19 @@ describe('ContentSteeringController', function () {
463594
serviceLocation: 'cdn1'
464595
}
465596
});
597+
598+
try {
599+
await contentSteeringController.loadSteeringData();
600+
601+
expect(capturedUrl).to.be.a('string');
602+
const queryString = capturedUrl.split('?')[1];
603+
const params = new URLSearchParams(queryString);
604+
const pathway = params.get('_DASH_pathway');
605+
606+
expect(pathway).to.equal('"cdn1"');
607+
} finally {
608+
schemeLoaderFactory.unregisterLoader('https://');
609+
}
466610
});
467611
});
468612

@@ -471,7 +615,49 @@ describe('ContentSteeringController', function () {
471615
contentSteeringController.initialize();
472616
});
473617

474-
it('should store throughput measurements for service locations', function () {
618+
it('should store throughput measurements for service locations', async function () {
619+
let capturedUrl;
620+
621+
const mockLoaderInstance = {
622+
load: ({ request, success, complete }) => {
623+
capturedUrl = request.url;
624+
625+
const responseData = {};
626+
responseData[DashConstants.CONTENT_STEERING_RESPONSE.VERSION] = '1';
627+
628+
success(responseData);
629+
if (typeof complete === 'function') {
630+
complete();
631+
}
632+
},
633+
abort: () => {},
634+
reset: () => {},
635+
resetInitialSettings: () => {}
636+
};
637+
638+
function MockLoader() {
639+
return {
640+
create: () => mockLoaderInstance
641+
};
642+
}
643+
644+
const schemeLoaderFactory = SchemeLoaderFactory(context).getInstance();
645+
646+
schemeLoaderFactory.registerLoader('https://', MockLoader);
647+
648+
manifestModelMock.getValue = sinon.stub().returns({});
649+
adapterMock.getContentSteering = sinon.stub().returns({
650+
serverUrl: 'https://steering.example.com',
651+
queryBeforeStart: true
652+
});
653+
654+
// Add service location so throughput is included in the request URL
655+
eventBus.trigger(MediaPlayerEvents.FRAGMENT_LOADING_STARTED, {
656+
request: {
657+
serviceLocation: 'cdn1'
658+
}
659+
});
660+
475661
eventBus.trigger(MediaPlayerEvents.THROUGHPUT_MEASUREMENT_STORED, {
476662
throughputValues: {
477663
serviceLocation: 'cdn1',
@@ -485,6 +671,15 @@ describe('ContentSteeringController', function () {
485671
value: 6000
486672
}
487673
});
674+
675+
try {
676+
await contentSteeringController.loadSteeringData();
677+
678+
expect(capturedUrl).to.be.a('string');
679+
expect(capturedUrl).to.contain('_DASH_throughput=5500000');
680+
} finally {
681+
schemeLoaderFactory.unregisterLoader('https://');
682+
}
488683
});
489684

490685
it('should store throughput for multiple service locations', function () {

test/unit/test/streaming/streaming.controllers.CatchupController.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,16 @@ describe('CatchupController', function () {
117117
playbackControllerMock.isPaused = sinon.stub().returns(false);
118118
playbackControllerMock.isSeeking = sinon.stub().returns(false);
119119
playbackControllerMock.getTime = sinon.stub().returns(10);
120+
playbackControllerMock.getCurrentLiveLatency = sinon.stub().returns(5);
121+
playbackControllerMock.getLiveDelay = sinon.stub().returns(3);
122+
playbackControllerMock.getBufferLevel = sinon.stub().returns(2);
123+
playbackControllerMock.getPlaybackStalled = sinon.stub().returns(false);
124+
videoModelMock.getPlaybackRate = sinon.stub().returns(1.0);
125+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
120126

121127
eventBus.trigger(MediaPlayerEvents.PLAYBACK_PROGRESS);
128+
129+
expect(setPlaybackRateSpy.called).to.be.true;
122130
});
123131

124132
it('should handle PLAYBACK_TIME_UPDATED event', function () {
@@ -128,8 +136,16 @@ describe('CatchupController', function () {
128136
playbackControllerMock.isPaused = sinon.stub().returns(false);
129137
playbackControllerMock.isSeeking = sinon.stub().returns(false);
130138
playbackControllerMock.getTime = sinon.stub().returns(10);
139+
playbackControllerMock.getCurrentLiveLatency = sinon.stub().returns(5);
140+
playbackControllerMock.getLiveDelay = sinon.stub().returns(3);
141+
playbackControllerMock.getBufferLevel = sinon.stub().returns(2);
142+
playbackControllerMock.getPlaybackStalled = sinon.stub().returns(false);
143+
videoModelMock.getPlaybackRate = sinon.stub().returns(1.0);
144+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
131145

132146
eventBus.trigger(MediaPlayerEvents.PLAYBACK_TIME_UPDATED);
147+
148+
expect(setPlaybackRateSpy.called).to.be.true;
133149
});
134150

135151
it('should handle PLAYBACK_SEEKED event', function () {
@@ -186,17 +202,34 @@ describe('CatchupController', function () {
186202
playbackControllerMock.getIsDynamic = sinon.stub().returns(true);
187203
mediaPlayerModelMock.getCatchupModeEnabled = sinon.stub().returns(false);
188204
mediaPlayerModelMock.getCatchupPlaybackRates = sinon.stub().returns({ min: -0.5, max: 0.5 });
205+
playbackControllerMock.isPaused = sinon.stub().returns(false);
206+
playbackControllerMock.isSeeking = sinon.stub().returns(false);
207+
playbackControllerMock.getTime = sinon.stub().returns(10);
208+
209+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
210+
const seekToCurrentLiveSpy = sinon.spy(playbackControllerMock, 'seekToCurrentLive');
189211

190212
eventBus.trigger(MediaPlayerEvents.PLAYBACK_PROGRESS);
213+
214+
expect(setPlaybackRateSpy.notCalled).to.be.true;
215+
expect(seekToCurrentLiveSpy.notCalled).to.be.true;
191216
});
192217

193218
it('should not apply catchup when paused', function () {
194219
playbackControllerMock.getIsDynamic = sinon.stub().returns(true);
195220
mediaPlayerModelMock.getCatchupModeEnabled = sinon.stub().returns(true);
196221
mediaPlayerModelMock.getCatchupPlaybackRates = sinon.stub().returns({ min: -0.5, max: 0.5 });
197222
playbackControllerMock.isPaused = sinon.stub().returns(true);
223+
playbackControllerMock.isSeeking = sinon.stub().returns(false);
224+
playbackControllerMock.getTime = sinon.stub().returns(10);
225+
226+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
227+
const seekToCurrentLiveSpy = sinon.spy(playbackControllerMock, 'seekToCurrentLive');
198228

199229
eventBus.trigger(MediaPlayerEvents.PLAYBACK_PROGRESS);
230+
231+
expect(setPlaybackRateSpy.notCalled).to.be.true;
232+
expect(seekToCurrentLiveSpy.notCalled).to.be.true;
200233
});
201234

202235
it('should not apply catchup when seeking', function () {
@@ -205,8 +238,15 @@ describe('CatchupController', function () {
205238
mediaPlayerModelMock.getCatchupPlaybackRates = sinon.stub().returns({ min: -0.5, max: 0.5 });
206239
playbackControllerMock.isPaused = sinon.stub().returns(false);
207240
playbackControllerMock.isSeeking = sinon.stub().returns(true);
241+
playbackControllerMock.getTime = sinon.stub().returns(10);
242+
243+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
244+
const seekToCurrentLiveSpy = sinon.spy(playbackControllerMock, 'seekToCurrentLive');
208245

209246
eventBus.trigger(MediaPlayerEvents.PLAYBACK_PROGRESS);
247+
248+
expect(setPlaybackRateSpy.notCalled).to.be.true;
249+
expect(seekToCurrentLiveSpy.notCalled).to.be.true;
210250
});
211251

212252
it('should apply catchup in default mode when latency drift exists', function () {
@@ -221,8 +261,11 @@ describe('CatchupController', function () {
221261
playbackControllerMock.getBufferLevel = sinon.stub().returns(2);
222262
playbackControllerMock.getPlaybackStalled = sinon.stub().returns(false);
223263
videoModelMock.getPlaybackRate = sinon.stub().returns(1.0);
264+
const setPlaybackRateSpy = sinon.spy(videoModelMock, 'setPlaybackRate');
224265

225266
eventBus.trigger(MediaPlayerEvents.PLAYBACK_PROGRESS);
267+
268+
expect(setPlaybackRateSpy.called).to.be.true;
226269
});
227270

228271
it('should seek to live when max drift is exceeded', function () {

0 commit comments

Comments
 (0)