Skip to content

Commit 0aa407b

Browse files
committed
fix(dvb): correct off-by-one in DVBSelector weighted BaseURL selection
Remove erroneous `- 1` in random number generation that shrank the selection range, causing the last BaseURL in a weighted group to never be selected. With 2 equal-weight CDNs, only the first was ever picked. Also add stub.restore() to existing DVB A168 test to prevent sinon stub leak, and add two unit tests for equal-weight selection (2 and 3 CDNs). Fixes Dash-Industry-Forum#4981
1 parent 00a296a commit 0aa407b

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

src/streaming/utils/baseUrlResolution/DVBSelector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function DVBSelector(config) {
111111
});
112112

113113
// pick a random number between zero and totalWeight
114-
rn = Math.floor(Math.random() * (totalWeight - 1));
114+
rn = Math.floor(Math.random() * totalWeight);
115115

116116
// select the index for the range rn falls within
117117
cumulWeights.every((limit, index) => {

test/unit/test/streaming/streaming.utils.DVBSelector.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,63 @@ describe('BaseURLResolution/DVBSelector', function () {
154154

155155
const fourthSelection = dvbSelector.select(baseUrls);
156156
expect(fourthSelection).to.be.undefined;
157+
158+
stub.restore();
159+
});
160+
161+
it('should select all equal-weight baseUrls with fair distribution (RFC 2782)', () => {
162+
const baseUrls = [
163+
{ dvbPriority: 1, dvbWeight: 1, serviceLocation: 'cdn-a' },
164+
{ dvbPriority: 1, dvbWeight: 1, serviceLocation: 'cdn-b' }
165+
];
166+
167+
const stub = sinon.stub(Math, 'random');
168+
const dvbSelector = DVBSelector(context).create(defaultConfig);
169+
170+
// With totalWeight=2 and cumulWeights=[1,2]:
171+
// Math.random()=0.0 → rn=0 → 0<1 → cdn-a
172+
stub.returns(0.0);
173+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-a');
174+
175+
// Math.random()=0.4 → rn=0 → 0<1 → cdn-a
176+
stub.returns(0.4);
177+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-a');
178+
179+
// Math.random()=0.5 → rn=1 → 1<2 → cdn-b
180+
stub.returns(0.5);
181+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-b');
182+
183+
// Math.random()=0.99 → rn=1 → 1<2 → cdn-b
184+
stub.returns(0.99);
185+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-b');
186+
187+
stub.restore();
188+
});
189+
190+
it('should select all three equal-weight baseUrls (multi-CDN without dvb:weight)', () => {
191+
const baseUrls = [
192+
{ dvbPriority: 1, dvbWeight: 1, serviceLocation: 'cdn-a' },
193+
{ dvbPriority: 1, dvbWeight: 1, serviceLocation: 'cdn-b' },
194+
{ dvbPriority: 1, dvbWeight: 1, serviceLocation: 'cdn-c' }
195+
];
196+
197+
const stub = sinon.stub(Math, 'random');
198+
const dvbSelector = DVBSelector(context).create(defaultConfig);
199+
200+
// With totalWeight=3 and cumulWeights=[1,2,3]:
201+
// Math.random()=0.0 → rn=0 → 0<1 → cdn-a
202+
stub.returns(0.0);
203+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-a');
204+
205+
// Math.random()=0.5 → rn=1 → 1<2 → cdn-b
206+
stub.returns(0.5);
207+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-b');
208+
209+
// Math.random()=0.8 → rn=2 → 2<3 → cdn-c
210+
stub.returns(0.8);
211+
expect(dvbSelector.select(baseUrls).serviceLocation).to.equal('cdn-c');
212+
213+
stub.restore();
157214
});
158215

159216
it('should not select baseUrls with invalid priority when there is another option', () => {

0 commit comments

Comments
 (0)