import { describe, it, expect, vi } from "vitest"; import log from "../../../../../../log"; import type { ITNode } from "../../../../../../utils/xml-parser"; import { parseXml } from "../../../../../../utils/xml-parser"; import { createAdaptationSetIntermediateRepresentation } from "../AdaptationSet"; import { MPDError } from "../utils"; function testBooleanAttribute(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: true }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: false }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; const error1 = new MPDError( `\`${attributeName}\` property is not a boolean value but "foobar"`, ); expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: false }, children: { baseURLs: [], representations: [] }, }, [error1], ]); expect(spyLog).toHaveBeenCalledTimes(1); expect(spyLog).toHaveBeenNthCalledWith( 1, "dash", "failed to parse DASH value:", error1.message, { dashName: attributeName }, ); const element2 = parseXml(``)[0] as ITNode; const error2 = new MPDError( `\`${attributeName}\` property is not a boolean value but ""`, ); expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: false }, children: { baseURLs: [], representations: [] }, }, [error2], ]); expect(spyLog).toHaveBeenCalledTimes(2); expect(spyLog).toHaveBeenNthCalledWith( 2, "dash", "failed to parse DASH value:", error2.message, { dashName: attributeName }, ); spyLog.mockRestore(); }); } function testStringAttribute(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: "foobar" }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: "" }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); } function testMaybeDividedNumber(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: 12.4 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: 0 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element3 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: { [_variableName]: 13.5 }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; const error1 = new MPDError(`\`${attributeName}\` property is invalid: "toto"`); expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error1], ]); expect(spyLog).toHaveBeenCalledTimes(1); expect(spyLog).toHaveBeenNthCalledWith( 1, "dash", "failed to parse DASH value:", error1.message, { dashName: attributeName }, ); const element2 = parseXml(``)[0] as ITNode; const error2 = new MPDError(`\`${attributeName}\` property is invalid: "PT5M"`); expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error2], ]); expect(spyLog).toHaveBeenCalledTimes(2); expect(spyLog).toHaveBeenNthCalledWith( 2, "dash", "failed to parse DASH value:", error2.message, { dashName: attributeName }, ); const element3 = parseXml(``)[0] as ITNode; const error3 = new MPDError(`\`${attributeName}\` property is invalid: ""`); expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error3], ]); expect(spyLog).toHaveBeenCalledTimes(3); expect(spyLog).toHaveBeenNthCalledWith( 3, "dash", "failed to parse DASH value:", error3.message, { dashName: attributeName }, ); spyLog.mockRestore(); }); } function testFloatAttribute(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: 12 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: 0 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element3 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: { [_variableName]: -50.12 }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; const error1 = new MPDError(`\`${attributeName}\` property is invalid: "toto"`); expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error1], ]); expect(spyLog).toHaveBeenCalledTimes(1); expect(spyLog).toHaveBeenNthCalledWith( 1, "dash", "failed to parse DASH value:", error1.message, { dashName: attributeName }, ); const element2 = parseXml(``)[0] as ITNode; const error2 = new MPDError(`\`${attributeName}\` property is invalid: "PT5M"`); expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error2], ]); expect(spyLog).toHaveBeenCalledTimes(2); expect(spyLog).toHaveBeenNthCalledWith( 2, "dash", "failed to parse DASH value:", error2.message, { dashName: attributeName }, ); const element3 = parseXml(``)[0] as ITNode; const error3 = new MPDError(`\`${attributeName}\` property is invalid: ""`); expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error3], ]); expect(spyLog).toHaveBeenCalledTimes(3); expect(spyLog).toHaveBeenNthCalledWith( 3, "dash", "failed to parse DASH value:", error3.message, { dashName: attributeName }, ); spyLog.mockRestore(); }); } function testIntegerAttribute(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: 12 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: 0 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element3 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: { [_variableName]: -50 }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; const error1 = new MPDError( `\`${attributeName}\` property is not an integer value but "toto"`, ); expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error1], ]); expect(spyLog).toHaveBeenCalledTimes(1); expect(spyLog).toHaveBeenNthCalledWith( 1, "dash", "failed to parse DASH value:", error1.message, { dashName: attributeName }, ); const element2 = parseXml(``)[0] as ITNode; const error2 = new MPDError( `\`${attributeName}\` property is not an integer value but "PT5M"`, ); expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error2], ]); expect(spyLog).toHaveBeenCalledTimes(2); expect(spyLog).toHaveBeenNthCalledWith( 2, "dash", "failed to parse DASH value:", error2.message, { dashName: attributeName }, ); const element3 = parseXml(``)[0] as ITNode; const error3 = new MPDError( `\`${attributeName}\` property is not an integer value but ""`, ); expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error3], ]); expect(spyLog).toHaveBeenCalledTimes(3); expect(spyLog).toHaveBeenNthCalledWith( 3, "dash", "failed to parse DASH value:", error3.message, { dashName: attributeName }, ); spyLog.mockRestore(); }); } function testNumberOrBooleanAttribute( attributeName: string, variableName?: string, ): void { const _variableName = variableName ?? attributeName; it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: 12 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: 0 }, children: { baseURLs: [], representations: [] }, }, [], ]); const element3 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: { [_variableName]: -50 }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; const error1 = new MPDError( `\`${attributeName}\` property is not a boolean nor an integer but "toto"`, ); expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error1], ]); expect(spyLog).toHaveBeenCalledTimes(1); expect(spyLog).toHaveBeenNthCalledWith( 1, "dash", "failed to parse DASH value:", error1.message, { dashName: attributeName }, ); const element2 = parseXml(``)[0] as ITNode; const error2 = new MPDError( `\`${attributeName}\` property is not a boolean nor an integer but "PT5M"`, ); expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error2], ]); expect(spyLog).toHaveBeenCalledTimes(2); expect(spyLog).toHaveBeenNthCalledWith( 2, "dash", "failed to parse DASH value:", error2.message, { dashName: attributeName }, ); const element3 = parseXml(``)[0] as ITNode; const error3 = new MPDError( `\`${attributeName}\` property is not a boolean nor an integer but ""`, ); expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [error3], ]); expect(spyLog).toHaveBeenCalledTimes(3); expect(spyLog).toHaveBeenNthCalledWith( 3, "dash", "failed to parse DASH value:", error3.message, { dashName: attributeName }, ); spyLog.mockRestore(); }); it(`should correctly parse an AdaptationSet element with a boolean ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: { [_variableName]: true }, children: { baseURLs: [], representations: [] }, }, [], ]); const element2 = parseXml(``)[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: { [_variableName]: false }, children: { baseURLs: [], representations: [] }, }, [], ]); expect(spyLog).not.toHaveBeenCalled(); spyLog.mockRestore(); }); } describe("DASH Node Parsers - AdaptationSet", () => { it("should correctly parse an AdaptationSet element without attributes nor children", () => { const element = parseXml("")[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [], ]); }); testStringAttribute("audioSamplingRate"); testBooleanAttribute("bitstreamSwitching"); testStringAttribute("codecs"); testBooleanAttribute("codingDependency"); testStringAttribute("contentType"); testMaybeDividedNumber("frameRate"); testIntegerAttribute("group"); testIntegerAttribute("height"); testStringAttribute("id"); testStringAttribute("lang", "language"); testIntegerAttribute("maxBandwidth", "maxBitrate"); testMaybeDividedNumber("maxFrameRate"); testIntegerAttribute("maxHeight"); testFloatAttribute("maxPlayoutRate"); testIntegerAttribute("maxWidth"); testFloatAttribute("maximumSAPPeriod"); testStringAttribute("mimeType"); testIntegerAttribute("minBandwidth", "minBitrate"); testMaybeDividedNumber("minFrameRate"); testIntegerAttribute("minHeight"); testIntegerAttribute("minWidth"); testStringAttribute("par"); testStringAttribute("profiles"); testNumberOrBooleanAttribute("segmentAlignment"); testStringAttribute("segmentProfiles"); testNumberOrBooleanAttribute("subsegmentAlignment"); testIntegerAttribute("width"); testFloatAttribute("availabilityTimeOffset"); it("should correctly parse an empty baseURLs", () => { const element1 = parseXml("")[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [], ]); const element2 = parseXml( "", )[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [], representations: [] } }, [], ]); }); it("should correctly parse a non-empty baseURLs", () => { const element1 = parseXml( 'a', )[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [{ value: "a" }], representations: [] }, }, [], ]); const element2 = parseXml( 'foo bar', )[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ { attributes: {}, children: { baseURLs: [{ value: "foo bar" }], representations: [] }, }, [], ]); }); it("should correctly parse multiple non-empty baseURLs", () => { const element1 = parseXml( 'ab', )[0] as ITNode; expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ { attributes: {}, children: { baseURLs: [{ value: "a" }, { value: "b" }], representations: [], }, }, [], ]); }); });