• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

benrr101 / node-taglib-sharp / 51048458

24 Nov 2024 10:06PM UTC coverage: 92.545% (-0.02%) from 92.565%
51048458

push

appveyor

web-flow
Merge 0f4de9fd0 into 39a345ad7

3250 of 4131 branches covered (78.67%)

Branch coverage included in aggregate %.

492 of 502 new or added lines in 11 files covered. (98.01%)

1 existing line in 1 file now uncovered.

26753 of 28289 relevant lines covered (94.57%)

471.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.91
/test-unit/mpeg/mpegAudioHeaderTests.ts
1
import * as TypeMoq from "typemoq";
1✔
2
import {suite, test} from "@testdeck/mocha";
1✔
3
import {assert} from "chai";
1✔
4

5
import MpegAudioHeader from "../../src/mpeg/mpegAudioHeader";
1✔
6
import TestFile from "../utilities/testFile";
1✔
7
import {ByteVector, StringType} from "../../src/byteVector";
1✔
8
import {File} from "../../src/file";
9
import {MediaTypes} from "../../src/properties";
1✔
10
import {ChannelMode, MpegVersion} from "../../src/mpeg/mpegEnums";
1✔
11
import {Allow, Testers} from "../utilities/testers";
1✔
12

13
@suite class Mpeg_AudioHeader_ConstructorTests {
1✔
14
    private mockFile = TypeMoq.Mock.ofType<File>().object;
7✔
15

16
    @test
17
    public fromFile_invalidArguments() {
1✔
18
        // Act / Assert
19
        Testers.testTruthy((v: File) => {
1✔
20
            MpegAudioHeader.fromFile(v, 1, 2);
2✔
21
        });
22
        Testers.testSafeUint((v: number) => {
1✔
23
            MpegAudioHeader.fromFile(this.mockFile, v, 1);
5✔
24
        });
25
        Testers.testSafeUint((v: number) => {
1✔
26
            MpegAudioHeader.fromFile(this.mockFile, 1, v);
5✔
27
        });
28
        Testers.testSafeUint((v: number) => {
1✔
29
            MpegAudioHeader.fromFile(this.mockFile, 1, 2, v)
4✔
30
        }, Allow.Undefined);
31
    }
32

33
    @test
34
    public fromFile_fileTooShort() {
1✔
35
        // Act / Assert
36
        // Total file length too short
37
        assert.isUndefined(MpegAudioHeader.fromFile(TestFile.getFile(ByteVector.fromSize(2)), 0, 2))
1✔
38

39
        // Start position to file end is too short
40
        assert.isUndefined(MpegAudioHeader.fromFile(TestFile.getFile(ByteVector.fromSize(6)), 4, 6));
1✔
41

42
        // File beginning to end position is too short
43
        assert.isUndefined(MpegAudioHeader.fromFile(TestFile.getFile(ByteVector.fromSize(6)), 0, 2));
1✔
44
    }
45

46
    @test
47
    public fromFile_fileDoesNotContainMpegSyncByte1() {
1✔
48
        // Arrange
49
        // Note: bytes 0-1 and 5-6 are valid, but should be excluded by start/end bounds
50
        const file = TestFile.getFile([0xFF, 0xE6, 0xEE, 0xEE, 0xEE, 0xFF, 0xE6]);
1✔
51

52
        // Act / Assert
53
        assert.isUndefined(MpegAudioHeader.fromFile(file, 1, 6));
1✔
54
    }
55

56
    @test
57
    public fromFile_fileDoesNotContainMpegSyncByte2() {
1✔
58
        // Arrange
59
        // Note: bytes 0-1 and 5-6 are valid, but should be excluded by start/end bounds
60
        const file = TestFile.getFile([0xFF, 0xE6, 0xFF, 0xD6, 0xFF, 0xFF, 0xE6]);
1✔
61

62
        // Act / Assert
63
        assert.isUndefined(MpegAudioHeader.fromFile(file, 1, 6));
1✔
64
    }
65

66
    @test
67
    public fromFile_reservedVersion() {
1✔
68
        // Arrange
69
        const file = TestFile.getFile([0xFF, 0xEE, 0x00, 0x00]);
1✔
70

71
        // Act / Assert
72
        assert.isUndefined(MpegAudioHeader.fromFile(file, 0, file.length));
1✔
73
    }
74

75
    @test
76
    public fromFile_reservedLayer() {
1✔
77
        // Arrange
78
        const file = TestFile.getFile([0xFF, 0xF8, 0x00, 0x00]);
1✔
79

80
        // Act / Assert
81
        assert.isUndefined(MpegAudioHeader.fromFile(file, 0, file.length));
1✔
82
    }
83

84
    @test
85
    public fromFile_reservedBitrate() {
1✔
86
        // Arrange
87
        const file = TestFile.getFile([0xFF, 0xFA, 0xF0, 0x00]);
1✔
88

89
        // Act / Assert
90
        assert.isUndefined(MpegAudioHeader.fromFile(file, 0, file.length));
1✔
91
    }
92

93
    public fromFile_reservedSampleRate() {
94
        // Arrange
NEW
95
        const file = TestFile.getFile([0xFF, 0xFA, 0x0C, 0x00]);
×
96

97
        // Act / Assert
NEW
98
        assert.isUndefined(MpegAudioHeader.fromFile(file, 0, file.length));
×
99
    }
100
}
101

102
@suite class Mpeg_AudioHeader_PropertyTests {
1✔
103
    @test
104
    public bitrate_noVbrHeader() {
1✔
105
        const testCases = [
1✔
106
            {bytes: [0xFE, 0x00], bitrate: 0},   // V1, L1
107
            {bytes: [0xFE, 0x10], bitrate: 32},
108
            {bytes: [0xFE, 0x20], bitrate: 64},
109
            {bytes: [0xFE, 0x30], bitrate: 96},
110
            {bytes: [0xFE, 0x40], bitrate: 128},
111
            {bytes: [0xFE, 0x50], bitrate: 160},
112
            {bytes: [0xFE, 0x60], bitrate: 192},
113
            {bytes: [0xFE, 0x70], bitrate: 224},
114
            {bytes: [0xFE, 0x80], bitrate: 256},
115
            {bytes: [0xFE, 0x90], bitrate: 288},
116
            {bytes: [0xFE, 0xA0], bitrate: 320},
117
            {bytes: [0xFE, 0xB0], bitrate: 352},
118
            {bytes: [0xFE, 0xC0], bitrate: 384},
119
            {bytes: [0xFE, 0xD0], bitrate: 416},
120
            {bytes: [0xFE, 0xE0], bitrate: 448},
121
            {bytes: [0xFC, 0x00], bitrate: 0},   // V1, L2
122
            {bytes: [0xFC, 0x10], bitrate: 32},
123
            {bytes: [0xFC, 0x20], bitrate: 48},
124
            {bytes: [0xFC, 0x30], bitrate: 56},
125
            {bytes: [0xFC, 0x40], bitrate: 64},
126
            {bytes: [0xFC, 0x50], bitrate: 80},
127
            {bytes: [0xFC, 0x60], bitrate: 96},
128
            {bytes: [0xFC, 0x70], bitrate: 112},
129
            {bytes: [0xFC, 0x80], bitrate: 128},
130
            {bytes: [0xFC, 0x90], bitrate: 160},
131
            {bytes: [0xFC, 0xA0], bitrate: 192},
132
            {bytes: [0xFC, 0xB0], bitrate: 224},
133
            {bytes: [0xFC, 0xC0], bitrate: 256},
134
            {bytes: [0xFC, 0xD0], bitrate: 320},
135
            {bytes: [0xFC, 0xE0], bitrate: 384},
136
            {bytes: [0xFA, 0x00], bitrate: 0},   // V1, L3
137
            {bytes: [0xFA, 0x10], bitrate: 32},
138
            {bytes: [0xFA, 0x20], bitrate: 40},
139
            {bytes: [0xFA, 0x30], bitrate: 48},
140
            {bytes: [0xFA, 0x40], bitrate: 56},
141
            {bytes: [0xFA, 0x50], bitrate: 64},
142
            {bytes: [0xFA, 0x60], bitrate: 80},
143
            {bytes: [0xFA, 0x70], bitrate: 96},
144
            {bytes: [0xFA, 0x80], bitrate: 112},
145
            {bytes: [0xFA, 0x90], bitrate: 128},
146
            {bytes: [0xFA, 0xA0], bitrate: 160},
147
            {bytes: [0xFA, 0xB0], bitrate: 192},
148
            {bytes: [0xFA, 0xC0], bitrate: 224},
149
            {bytes: [0xFA, 0xD0], bitrate: 256},
150
            {bytes: [0xFA, 0xE0], bitrate: 320},
151
            {bytes: [0xF6, 0x00], bitrate: 0},   // V2, L1
152
            {bytes: [0xF6, 0x10], bitrate: 32},
153
            {bytes: [0xF6, 0x20], bitrate: 48},
154
            {bytes: [0xF6, 0x30], bitrate: 56},
155
            {bytes: [0xF6, 0x40], bitrate: 64},
156
            {bytes: [0xF6, 0x50], bitrate: 80},
157
            {bytes: [0xF6, 0x60], bitrate: 96},
158
            {bytes: [0xF6, 0x70], bitrate: 112},
159
            {bytes: [0xF6, 0x80], bitrate: 128},
160
            {bytes: [0xF6, 0x90], bitrate: 144},
161
            {bytes: [0xF6, 0xA0], bitrate: 160},
162
            {bytes: [0xF6, 0xB0], bitrate: 176},
163
            {bytes: [0xF6, 0xC0], bitrate: 192},
164
            {bytes: [0xF6, 0xD0], bitrate: 224},
165
            {bytes: [0xF6, 0xE0], bitrate: 256},
166
            {bytes: [0xF4, 0x00], bitrate: 0},   // V2, L2
167
            {bytes: [0xF4, 0x10], bitrate: 8},
168
            {bytes: [0xF4, 0x20], bitrate: 16},
169
            {bytes: [0xF4, 0x30], bitrate: 24},
170
            {bytes: [0xF4, 0x40], bitrate: 32},
171
            {bytes: [0xF4, 0x50], bitrate: 40},
172
            {bytes: [0xF4, 0x60], bitrate: 48},
173
            {bytes: [0xF4, 0x70], bitrate: 56},
174
            {bytes: [0xF4, 0x80], bitrate: 64},
175
            {bytes: [0xF4, 0x90], bitrate: 80},
176
            {bytes: [0xF4, 0xA0], bitrate: 96},
177
            {bytes: [0xF4, 0xB0], bitrate: 112},
178
            {bytes: [0xF4, 0xC0], bitrate: 128},
179
            {bytes: [0xF4, 0xD0], bitrate: 144},
180
            {bytes: [0xF4, 0xE0], bitrate: 160},
181
            {bytes: [0xF2, 0x00], bitrate: 0},   // V2, L3
182
            {bytes: [0xF2, 0x10], bitrate: 8},
183
            {bytes: [0xF2, 0x20], bitrate: 16},
184
            {bytes: [0xF2, 0x30], bitrate: 24},
185
            {bytes: [0xF2, 0x40], bitrate: 32},
186
            {bytes: [0xF2, 0x50], bitrate: 40},
187
            {bytes: [0xF2, 0x60], bitrate: 48},
188
            {bytes: [0xF2, 0x70], bitrate: 56},
189
            {bytes: [0xF2, 0x80], bitrate: 64},
190
            {bytes: [0xF2, 0x90], bitrate: 80},
191
            {bytes: [0xF2, 0xA0], bitrate: 96},
192
            {bytes: [0xF2, 0xB0], bitrate: 112},
193
            {bytes: [0xF2, 0xC0], bitrate: 128},
194
            {bytes: [0xF2, 0xD0], bitrate: 144},
195
            {bytes: [0xF2, 0xE0], bitrate: 160},
196
            {bytes: [0xE6, 0x00], bitrate: 0},   // V2.5, L1
197
            {bytes: [0xE6, 0x10], bitrate: 32},
198
            {bytes: [0xE6, 0x20], bitrate: 48},
199
            {bytes: [0xE6, 0x30], bitrate: 56},
200
            {bytes: [0xE6, 0x40], bitrate: 64},
201
            {bytes: [0xE6, 0x50], bitrate: 80},
202
            {bytes: [0xE6, 0x60], bitrate: 96},
203
            {bytes: [0xE6, 0x70], bitrate: 112},
204
            {bytes: [0xE6, 0x80], bitrate: 128},
205
            {bytes: [0xE6, 0x90], bitrate: 144},
206
            {bytes: [0xE6, 0xA0], bitrate: 160},
207
            {bytes: [0xE6, 0xB0], bitrate: 176},
208
            {bytes: [0xE6, 0xC0], bitrate: 192},
209
            {bytes: [0xE6, 0xD0], bitrate: 224},
210
            {bytes: [0xE6, 0xE0], bitrate: 256},
211
            {bytes: [0xE4, 0x00], bitrate: 0},   // V2.5, L2
212
            {bytes: [0xE4, 0x10], bitrate: 8},
213
            {bytes: [0xE4, 0x20], bitrate: 16},
214
            {bytes: [0xE4, 0x30], bitrate: 24},
215
            {bytes: [0xE4, 0x40], bitrate: 32},
216
            {bytes: [0xE4, 0x50], bitrate: 40},
217
            {bytes: [0xE4, 0x60], bitrate: 48},
218
            {bytes: [0xE4, 0x70], bitrate: 56},
219
            {bytes: [0xE4, 0x80], bitrate: 64},
220
            {bytes: [0xE4, 0x90], bitrate: 80},
221
            {bytes: [0xE4, 0xA0], bitrate: 96},
222
            {bytes: [0xE4, 0xB0], bitrate: 112},
223
            {bytes: [0xE4, 0xC0], bitrate: 128},
224
            {bytes: [0xE4, 0xD0], bitrate: 144},
225
            {bytes: [0xE4, 0xE0], bitrate: 160},
226
            {bytes: [0xE2, 0x00], bitrate: 0},   // V2.5, L3
227
            {bytes: [0xE2, 0x10], bitrate: 8},
228
            {bytes: [0xE2, 0x20], bitrate: 16},
229
            {bytes: [0xE2, 0x30], bitrate: 24},
230
            {bytes: [0xE2, 0x40], bitrate: 32},
231
            {bytes: [0xE2, 0x50], bitrate: 40},
232
            {bytes: [0xE2, 0x60], bitrate: 48},
233
            {bytes: [0xE2, 0x70], bitrate: 56},
234
            {bytes: [0xE2, 0x80], bitrate: 64},
235
            {bytes: [0xE2, 0x90], bitrate: 80},
236
            {bytes: [0xE2, 0xA0], bitrate: 96},
237
            {bytes: [0xE2, 0xB0], bitrate: 112},
238
            {bytes: [0xE2, 0xC0], bitrate: 128},
239
            {bytes: [0xE2, 0xD0], bitrate: 144},
240
            {bytes: [0xE2, 0xE0], bitrate: 160},
241
        ];
242

243
        for (const c of testCases) {
1✔
244
            // Arrange
245
            const file = TestFile.getFile([0xFF, c.bytes[0], c.bytes[1], 0x00]);
135✔
246

247
            // Act
248
            const header = MpegAudioHeader.fromFile(file, 0, file.length);
135✔
249

250
            // Assert
251
            assert.isOk(header, `0x${c.bytes[0].toString(16)} 0x${c.bytes[1].toString(16)} failed`);
135✔
252
            assert.strictEqual(
135✔
253
                header.audioBitrate,
254
                c.bitrate,
255
                `version: ${header.version}, layer: ${header.layer}, 0x${c.bytes[1].toString(16)} ` +
256
                `=> ${header.audioBitrate}; expected: ${c.bitrate}`
257
            );
258
        }
259
    }
260

261
    @test
262
    public bitrate_vbrHeaderSpecifiesBitrate() {
1✔
263
        // Arrange
264
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]); // 160kbps
1✔
265
        const vbrBytes = ByteVector.concatenate(
1✔
266
            ByteVector.fromString("Xing", StringType.Latin1),
267
            0x00, 0x00, 0x00, 0x03,    // Flags
268
            ByteVector.fromUint(1234), // Frames
269
            ByteVector.fromUint(23456) // Bytes
270
        );
271
        const fileBytes = ByteVector.concatenate(
1✔
272
            headerBytes,
273
            ByteVector.fromSize(17),
274
            vbrBytes
275
        );
276
        const file = TestFile.getFile(fileBytes);
1✔
277

278
        // Act
279
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
280

281
        // Assert
282
        assert.isOk(header);
1✔
283
        assert.strictEqual(header.audioBitrate, 2);
1✔
284
    }
285

286
    @test
287
    public bitrate_vbrHeaderDoesNotSpecifyBitrate() {
1✔
288
        // Arrange
289
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]);
1✔
290
        const vbrBytes = ByteVector.concatenate(
1✔
291
            ByteVector.fromString("Xing", StringType.Latin1),
292
            0x00, 0x00, 0x00, 0x00, // Flags
293
            ByteVector.fromSize(8)
294
        );
295
        const fileBytes = ByteVector.concatenate(
1✔
296
            headerBytes, // MPEG 2.5, Layer 3, 160kbps, 576spf, 11025Hz
297
            ByteVector.fromSize(17),
298
            vbrBytes
299
        );
300
        const file = TestFile.getFile(fileBytes);
1✔
301

302
        // Act
303
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
304

305
        // Assert
306
        assert.isOk(header);
1✔
307
        assert.strictEqual(header.audioBitrate, 160);
1✔
308
    }
309

310
    @test
311
    public audioSampleRate() {
1✔
312
        const testCases = [
1✔
313
            {bytes: [0xFE, 0x00], sampleRate: 44100}, // V1
314
            {bytes: [0xFE, 0x04], sampleRate: 48000},
315
            {bytes: [0xFE, 0x08], sampleRate: 32000},
316
            {bytes: [0xF6, 0x00], sampleRate: 22050}, // V2
317
            {bytes: [0xF6, 0x04], sampleRate: 24000},
318
            {bytes: [0xF6, 0x08], sampleRate: 16000},
319
            {bytes: [0xE6, 0x00], sampleRate: 11025}, // V2.5
320
            {bytes: [0xE6, 0x04], sampleRate: 12000},
321
            {bytes: [0xE6, 0x08], sampleRate: 8000}
322
        ];
323

324
        for (const c of testCases) {
1✔
325
            // Arrange
326
            const file = TestFile.getFile([0xFF, c.bytes[0], c.bytes[1], 0x00]);
9✔
327

328
            // Act
329
            const header = MpegAudioHeader.fromFile(file, 0, file.length);
9✔
330

331
            // Assert
332
            assert.isOk(header, `0x${c.bytes[0].toString(16)} 0x${c.bytes[1].toString(16)} failed`);
9✔
333
            assert.strictEqual(header.audioSampleRate, c.sampleRate);
9✔
334
        }
335
    }
336

337
    @test
338
    public audioChannelsAndChannelMode() {
1✔
339
        const testCases = [
1✔
340
            {byte: 0xC0, channels: 1, mode: ChannelMode.SingleChannel},
341
            {byte: 0x80, channels: 2, mode: ChannelMode.DualChannel},
342
            {byte: 0x40, channels: 2, mode: ChannelMode.JointStereo},
343
            {byte: 0x00, channels: 2, mode: ChannelMode.Stereo}
344
        ];
345

346
        for (const c of testCases) {
1✔
347
            // Arrange
348
            const file = TestFile.getFile([0xFF, 0xFE, 0x00, c.byte]);
4✔
349

350
            // Act
351
            const header = MpegAudioHeader.fromFile(file, 0, file.length);
4✔
352

353
            // Assert
354
            assert.isOk(header, `0x${c.byte.toString(16)} failed`);
4✔
355
            assert.strictEqual(header.audioChannels, c.channels);
4✔
356
            assert.strictEqual(header.channelMode, c.mode);
4✔
357
        }
358
    }
359

360
    @test
361
    public description_vbrHeaderVariableBitrate() {
1✔
362
        // Arrange
363
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]);
1✔
364
        const vbrBytes = ByteVector.concatenate(
1✔
365
            ByteVector.fromString("Xing", StringType.Latin1),
366
            0x00, 0x00, 0x00, 0x03,    // Flags
367
            ByteVector.fromUint(1234), // Frames
368
            ByteVector.fromUint(23456) // Bytes
369
        );
370
        const fileBytes = ByteVector.concatenate(
1✔
371
            headerBytes, // MPEG 2.5, Layer 3
372
            ByteVector.fromSize(17),
373
            vbrBytes
374
        );
375
        const file = TestFile.getFile(fileBytes);
1✔
376

377
        // Act
378
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
379

380
        // Assert
381
        assert.isOk(header);
1✔
382
        assert.strictEqual(header.description, "MPEG Version 2.5 Audio, Layer 3 VBR");
1✔
383
    }
384

385
    @test
386
    public description_vbrHeaderIsConstantBitrate() {
1✔
387
        // Arrange
388
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]);
1✔
389
        const vbrBytes = ByteVector.concatenate(
1✔
390
            ByteVector.fromString("Xing", StringType.Latin1),
391
            0x00, 0x00, 0x00, 0x00 // Flags
392
        );
393
        const fileBytes = ByteVector.concatenate(
1✔
394
            headerBytes, // MPEG 2.5, Layer 3, 160kbps, 576spf, 11025Hz
395
            ByteVector.fromSize(17),
396
            vbrBytes
397
        );
398
        const file = TestFile.getFile(fileBytes);
1✔
399

400
        // Act
401
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
402

403
        // Assert
404
        assert.isOk(header);
1✔
405
        assert.strictEqual(header.description, "MPEG Version 2.5 Audio, Layer 3");
1✔
406
    }
407

408
    @test
409
    public duration_freeBitrate() {
1✔
410
        // Arrange
411
        const fileBytes = ByteVector.concatenate(
1✔
412
            ByteVector.fromByteArray([0xFF, 0xE2, 0x00, 0x00]), // MPEG 2.5, Layer 3, free bitrate
413
            ByteVector.fromSize(4192 - 4)                       // 4k total file size
414
        );
415
        const file = TestFile.getFile(fileBytes);
1✔
416

417
        // Act
418
        const header = MpegAudioHeader.fromFile(file, 0, file.length, file.length);
1✔
419

420
        // Assert
421
        assert.isOk(header);
1✔
422
        assert.strictEqual(header.durationMilliseconds, 0);
1✔
423
    }
424

425
    @test
426
    public duration_noVbrHeader_noStreamSize() {
1✔
427
        // Arrange
428
        const fileBytes = ByteVector.concatenate(
1✔
429
            ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]), // MPEG 2.5, layer 3, 160kbps, 11025Hz 576spf
430
            ByteVector.fromSize(4192 - 4)                       // 4k total file size
431
        );
432
        const file = TestFile.getFile(fileBytes);
1✔
433

434
        // Act
435
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
436

437
        // Assert
438
        assert.isOk(header);
1✔
439
        assert.strictEqual(header.durationMilliseconds, 0);
1✔
440
    }
441

442
    @test
443
    public duration_noVbrHeader_streamSize() {
1✔
444
        // Arrange
445
        const fileBytes = ByteVector.concatenate(
1✔
446
            ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]), // MPEG 2.5, layer 3, 160kbps, 11025Hz 576spf
447
            ByteVector.fromSize(4192 - 4)                       // 4k total file size
448
        );
449
        const file = TestFile.getFile(fileBytes);
1✔
450

451
        // Act
452
        const header = MpegAudioHeader.fromFile(file, 0, file.length, file.length);
1✔
453

454
        // Assert
455
        assert.isOk(header);
1✔
456
        assert.approximately(header.durationMilliseconds, 209.6, 0.1);
1✔
457
    }
458

459
    @test
460
    public duration_vbrHeaderSpecifiesDuration() {
1✔
461
        // Arrange
462
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]);
1✔
463
        const vbrBytes = ByteVector.concatenate(
1✔
464
            ByteVector.fromString("Xing", StringType.Latin1),
465
            0x00, 0x00, 0x00, 0x03,    // Flags
466
            ByteVector.fromUint(1234), // Frames
467
            ByteVector.fromUint(23456) // Bytes
468
        );
469
        const fileBytes = ByteVector.concatenate(
1✔
470
            headerBytes, // MPEG 2.5, Layer 3, 160kbps, 576spf, 11025Hz
471
            ByteVector.fromSize(17),
472
            vbrBytes
473
        );
474
        const file = TestFile.getFile(fileBytes);
1✔
475

476
        // Act
477
        const header = MpegAudioHeader.fromFile(file, 0, file.length, file.length);
1✔
478

479
        // Assert
480
        assert.isOk(header);
1✔
481
        assert.approximately(header.durationMilliseconds, 64470, 1);
1✔
482
    }
483

484
    @test
485
    public duration_vbrHeaderDoesNotSpecifyDuration() {
1✔
486
        // Arrange
487
        const headerBytes = ByteVector.fromByteArray([0xFF, 0xE2, 0xE0, 0x00]);
1✔
488
        const vbrBytes = ByteVector.concatenate(
1✔
489
            ByteVector.fromString("Xing", StringType.Latin1),
490
            0x00, 0x00, 0x00, 0x00, // Flags
491
            ByteVector.fromSize(8)
492
        );
493
        const fileBytes = ByteVector.concatenate(
1✔
494
            headerBytes,                            // MPEG 2.5, Layer 3, 160kbps, 576spf, 11025Hz
495
            ByteVector.fromSize(17),
496
            vbrBytes,
497
            ByteVector.fromSize(4192 - 4 - 17 - 16) // 4k total file size
498
        );
499
        const file = TestFile.getFile(fileBytes);
1✔
500

501
        // Act
502
        const header = MpegAudioHeader.fromFile(file, 0, file.length, file.length);
1✔
503

504
        // Assert
505
        assert.isOk(header);
1✔
506
        assert.approximately(header.durationMilliseconds, 209.6, 0.1);
1✔
507
    }
508

509
    @test
510
    public isCopyrighted() {
1✔
511
        // Case 1: Audio is not copyrighted
512
        // Arrange / Act / Assert
513
        let file = TestFile.getFile([0xFF, 0xFE, 0x00, 0x00]);
1✔
514
        let header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
515
        assert.isFalse(header.isCopyrighted);
1✔
516

517
        // Case 2: Audio is copyrighted
518
        file = TestFile.getFile([0xFF, 0xFE, 0x00, 0x08]);
1✔
519
        header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
520
        assert.isTrue(header.isCopyrighted);
1✔
521
    }
522

523
    @test
524
    public isOriginal() {
1✔
525
        // Case 1: Audio is a copy
526
        // Arrange / Act / Assert
527
        let file = TestFile.getFile([0xFF, 0xFE, 0x00, 0x00]);
1✔
528
        let header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
529
        assert.isFalse(header.isOriginal);
1✔
530

531
        // Case 2: Audio is original
532
        file = TestFile.getFile([0xFF, 0xFE, 0x00, 0x04]);
1✔
533
        header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
534
        assert.isTrue(header.isOriginal);
1✔
535
    }
536

537
    @test
538
    public isProtected() {
1✔
539
        // Case 1: True
540
        // Arrange / Act / Assert
541
        let file = TestFile.getFile([0xFF, 0xE2, 0x00, 0x00]);
1✔
542
        let header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
543
        assert.isTrue(header.isProtected);
1✔
544

545
        // Case 2: False
546
        // Arrange / Act / Assert
547
        file = TestFile.getFile([0xFF, 0xE3, 0x00, 0x00]);
1✔
548
        header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
549
        assert.isFalse(header.isProtected);
1✔
550
    }
551

552
    @test
553
    public layerAndDescription() {
1✔
554
        const testCases = [
1✔
555
            {byte: 0xE6, layer: 1, description: "MPEG Version 2.5 Audio, Layer 1"},
556
            {byte: 0xE4, layer: 2, description: "MPEG Version 2.5 Audio, Layer 2"},
557
            {byte: 0xE2, layer: 3, description: "MPEG Version 2.5 Audio, Layer 3"}
558
        ];
559

560
        for (const c of testCases) {
1✔
561
            // Arrange
562
            const file = TestFile.getFile([0xFF, c.byte, 0x00, 0x00]);
3✔
563

564
            // Act
565
            const header = MpegAudioHeader.fromFile(file, 0, file.length);
3✔
566

567
            // Assert
568
            assert.isOk(header);
3✔
569
            assert.strictEqual(header.description, c.description);
3✔
570
            assert.strictEqual(header.layer, c.layer);
3✔
571
        }
572
    }
573

574
    @test
575
    public mediaType() {
1✔
576
        // Arrange
577
        const file = TestFile.getFile([0xFF, 0xE6, 0x00, 0x00]);
1✔
578

579
        // Act
580
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
581

582
        // Assert
583
        assert.isOk(header);
1✔
584
        assert.strictEqual(header.mediaTypes, MediaTypes.Audio);
1✔
585
    }
586

587
    @test
588
    public vbrHeader_hasHeader() {
1✔
589
        // Arrange
590
        const fileBytes = ByteVector.concatenate(
1✔
591
            0xFF, 0xE2, 0xE0, 0x00,
592
            ByteVector.fromSize(17),
593
            ByteVector.fromString("Xing", StringType.Latin1),
594
            ByteVector.fromSize(12)
595
        );
596
        const file = TestFile.getFile(fileBytes);
1✔
597

598
        // Act
599
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
600

601
        // Assert
602
        assert.isOk(header);
1✔
603
        assert.isOk(header.vbrHeader);
1✔
604
    }
605

606
    @test
607
    public vbrHeader_noHeader() {
1✔
608
        // Arrange
609
        const file = TestFile.getFile([0xFF, 0xE2, 0xE0, 0x00]);
1✔
610

611
        // Act
612
        const header = MpegAudioHeader.fromFile(file, 0, file.length);
1✔
613

614
        // Assert
615
        assert.isOk(header);
1✔
616
        assert.isUndefined(header.vbrHeader);
1✔
617
    }
618

619
    @test
620
    public versionAndDescription() {
1✔
621
        const testCases = [
1✔
622
            {byte: 0xFA, description: "MPEG Version 1 Audio, Layer 3", version: MpegVersion.Version1},
623
            {byte: 0xF2, description: "MPEG Version 2 Audio, Layer 3", version: MpegVersion.Version2},
624
            {byte: 0xE2, description: "MPEG Version 2.5 Audio, Layer 3", version: MpegVersion.Version25}
625
        ];
626

627
        for (const c of testCases) {
1✔
628
            // Arrange
629
            const file = TestFile.getFile([0xFF, c.byte, 0x00, 0x00]);
3✔
630

631
            // Act
632
            const header = MpegAudioHeader.fromFile(file, 0, file.length);
3✔
633

634
            // Assert
635
            assert.isOk(header);
3✔
636
            assert.strictEqual(header.description, c.description);
3✔
637
            assert.strictEqual(header.version, c.version);
3✔
638
        }
639
    }
640
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc