// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "hls_attribute_list.h" #include #include #include #include #include #include #include "string_conversions.h" #include "string_utils.h" namespace wvutil { namespace test { namespace { constexpr uint64_t kZeroInt = 0; constexpr uint64_t kMaxInt = std::numeric_limits::max(); constexpr char kMaxIntString[] = "18446744073709551615"; // FYI: 2^52 is the largest float value with whole integer // precision. constexpr double kMaxIntFloat = 9007199254740992.0; constexpr uint64_t kMaxIntFloatInt = 9007199254740992; constexpr char kMaxIntFloatString[] = "9007199254740992"; } // namespace TEST(HlsAttributeListTest, IsValidName) { // Valid attribute names. EXPECT_TRUE(HlsAttributeList::IsValidName("METHOD")); EXPECT_TRUE(HlsAttributeList::IsValidName("START-DATE")); EXPECT_TRUE(HlsAttributeList::IsValidName("SCTE35-CMD")); EXPECT_TRUE(HlsAttributeList::IsValidName("SCTE35-IN")); EXPECT_TRUE(HlsAttributeList::IsValidName("GROUP-ID")); EXPECT_TRUE(HlsAttributeList::IsValidName("X-WIDEVINE-1337")); // Unlikely but still valid. EXPECT_TRUE(HlsAttributeList::IsValidName("-")); EXPECT_TRUE(HlsAttributeList::IsValidName("-----------")); EXPECT_TRUE(HlsAttributeList::IsValidName("123456789")); EXPECT_TRUE(HlsAttributeList::IsValidName("-0")); // Invalid attribute names. EXPECT_FALSE(HlsAttributeList::IsValidName("")); EXPECT_FALSE(HlsAttributeList::IsValidName("lower-case")); EXPECT_FALSE(HlsAttributeList::IsValidName("sOME-LOWER-CASE")); EXPECT_FALSE(HlsAttributeList::IsValidName("SOME-LOWER-CASe")); EXPECT_FALSE(HlsAttributeList::IsValidName("sOME-LOwER-CASE")); EXPECT_FALSE(HlsAttributeList::IsValidName("METHOD=NONE")); EXPECT_FALSE(HlsAttributeList::IsValidName("WHITE SPACE")); EXPECT_FALSE(HlsAttributeList::IsValidName(" WHITE-SPACE")); EXPECT_FALSE(HlsAttributeList::IsValidName("WHITE-SPACE ")); EXPECT_FALSE(HlsAttributeList::IsValidName("NO,COMMA")); EXPECT_FALSE(HlsAttributeList::IsValidName("NO\"QUOTE")); EXPECT_FALSE(HlsAttributeList::IsValidName("NO\nLF")); EXPECT_FALSE(HlsAttributeList::IsValidName("NO\rCR")); } TEST(HlsAttributeListTest, IsValidEnumStringValue) { // Valid from HLS standard. EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("NONE")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("AES-128")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("SAMPLE-AES")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("CLOSED-CAPTIONS")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("YES")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("TYPE-0")); // Other valid values. EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("lower-case")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("------")); // All punctuation except commas and double quotes. EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("!@#$%^&*()")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("-=_+{}[]\\|")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue(";:'<>./?~`")); // Values that look like other types. EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("1234")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("-12.3")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("0x1234")); EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue("1920x1080")); // Invalid EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("WHITE SPACE")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue(" WHITESPACE")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("WHITESPACE ")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("NO,COMMAS")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("NO\"QUOTE")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("NO\nLF")); EXPECT_FALSE(HlsAttributeList::IsValidEnumStringValue("NO\rCR")); } TEST(HlsAttributeListTest, IsValidQuotedStringValue) { // Valid from standard. EXPECT_TRUE(HlsAttributeList::IsValidQuotedStringValue( "uri:text/plain;base64,SGVsbG8sIFdvcmxkIQ==")); EXPECT_TRUE(HlsAttributeList::IsValidQuotedStringValue("com.widevine")); EXPECT_TRUE(HlsAttributeList::IsValidQuotedStringValue("1/2/5")); EXPECT_TRUE( HlsAttributeList::IsValidQuotedStringValue("2024-07-31T11:43:55-12:00")); EXPECT_TRUE(HlsAttributeList::IsValidQuotedStringValue("")); EXPECT_TRUE( HlsAttributeList::IsValidQuotedStringValue("white space, and commas")); EXPECT_FALSE(HlsAttributeList::IsValidQuotedStringValue("NO\"QUOTE")); EXPECT_FALSE(HlsAttributeList::IsValidQuotedStringValue("NO\nLF")); EXPECT_FALSE(HlsAttributeList::IsValidQuotedStringValue("NO\rCR")); } TEST(HlsAttributeListTest, EmptyList) { const HlsAttributeList list; constexpr size_t kZero = 0; EXPECT_EQ(list.Count(), kZero); EXPECT_TRUE(list.IsEmpty()); const std::vector kEmptyVector; EXPECT_EQ(list.GetNames(), kEmptyVector); EXPECT_FALSE(list.Contains("METHOD")); } TEST(HlsAttributeListTest, SetEnumString) { HlsAttributeList list; EXPECT_TRUE(list.SetEnumString("METHOD", "NONE")); EXPECT_TRUE(list.SetEnumString("TYPE", "AUDIO")); EXPECT_TRUE(list.SetEnumString("DEFAULT", "NO")); // Overwrite. EXPECT_TRUE(list.SetEnumString("TYPE", "VIDEO")); EXPECT_EQ(list.Count(), static_cast(3)); EXPECT_TRUE(list.Contains("METHOD")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"DEFAULT", "METHOD", "TYPE"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::string value; EXPECT_TRUE(list.GetEnumString("METHOD", &value)); EXPECT_EQ(value, "NONE"); EXPECT_TRUE(list.GetEnumString("TYPE", &value)); EXPECT_EQ(value, "VIDEO"); EXPECT_TRUE(list.GetEnumString("DEFAULT", &value)); EXPECT_EQ(value, "NO"); EXPECT_FALSE(list.GetEnumString("INSTREAM-ID", &value)); } TEST(HlsAttributeListTest, SetEnumString_Invalid) { HlsAttributeList list; EXPECT_FALSE(list.SetEnumString("", "NONE")); EXPECT_FALSE(list.SetEnumString("BAD NAME", "NONE")); EXPECT_FALSE(list.SetEnumString("bad-name", "NONE")); EXPECT_FALSE(list.SetEnumString("METHOD", "")); EXPECT_FALSE(list.SetEnumString("METHOD", "BAD VALUE")); EXPECT_FALSE(list.SetEnumString("METHOD", "BAD,VALUE")); EXPECT_FALSE(list.SetEnumString("METHOD", "BAD\"VALUE")); EXPECT_TRUE(list.IsEmpty()); } TEST(HlsAttributeListTest, SetQuotedString) { HlsAttributeList list; const std::string kUriValue = "uri:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="; EXPECT_TRUE(list.SetQuotedString("URI", kUriValue)); EXPECT_TRUE(list.SetQuotedString("VERSION", "")); EXPECT_TRUE(list.SetQuotedString("MESSAGE", "Hello, world!")); EXPECT_EQ(list.Count(), static_cast(3)); EXPECT_TRUE(list.Contains("VERSION")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"MESSAGE", "URI", "VERSION"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::string value; EXPECT_TRUE(list.GetQuotedString("URI", &value)); EXPECT_EQ(value, kUriValue); EXPECT_TRUE(list.GetQuotedString("VERSION", &value)); EXPECT_EQ(value, ""); EXPECT_TRUE(list.GetQuotedString("MESSAGE", &value)); EXPECT_EQ(value, "Hello, world!"); EXPECT_FALSE(list.GetQuotedString("INSTREAM-ID", &value)); } TEST(HlsAttributeListTest, SetQuotedString_Invalid) { HlsAttributeList list; EXPECT_FALSE(list.SetQuotedString("", "NONE")); EXPECT_FALSE(list.SetQuotedString("BAD NAME", "NONE")); EXPECT_FALSE(list.SetQuotedString("bad-name", "NONE")); EXPECT_FALSE(list.SetQuotedString("METHOD", "BAD\rVALUE")); EXPECT_FALSE(list.SetQuotedString("METHOD", "BAD\nVALUE")); EXPECT_FALSE(list.SetQuotedString("METHOD", "BAD\"VALUE")); EXPECT_TRUE(list.IsEmpty()); } TEST(HlsAttributeListTest, SetHexSequenceAsString) { HlsAttributeList list; EXPECT_TRUE(list.SetHexSequence("IV", "some IV value")); EXPECT_TRUE(list.SetHexSequence("OTHERWISE-FORBIDDEN", "\r\n \"\b")); EXPECT_TRUE(list.SetHexSequence("SMALL", "-")); EXPECT_EQ(list.Count(), static_cast(3)); EXPECT_TRUE(list.Contains("IV")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"IV", "OTHERWISE-FORBIDDEN", "SMALL"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::string value; EXPECT_TRUE(list.GetHexSequence("IV", &value)); EXPECT_EQ(value, "some IV value"); EXPECT_TRUE(list.GetHexSequence("OTHERWISE-FORBIDDEN", &value)); EXPECT_EQ(value, "\r\n \"\b"); EXPECT_TRUE(list.GetHexSequence("SMALL", &value)); EXPECT_EQ(value, "-"); EXPECT_FALSE(list.GetHexSequence("INSTREAM-ID", &value)); } TEST(HlsAttributeListTest, SetHexSequenceAsString_Invalid) { HlsAttributeList list; EXPECT_FALSE(list.SetHexSequence("", "NONE")); EXPECT_FALSE(list.SetHexSequence("BAD NAME", "NONE")); EXPECT_FALSE(list.SetHexSequence("bad-name", "NONE")); EXPECT_FALSE(list.SetHexSequence("METHOD", "")); EXPECT_TRUE(list.IsEmpty()); } TEST(HlsAttributeListTest, SetHexSequenceAsVector) { HlsAttributeList list; const std::vector kIvValue(16, 0x33); EXPECT_TRUE(list.SetHexSequence("IV", kIvValue)); const std::vector kSmallValue(1, 0xff); EXPECT_TRUE(list.SetHexSequence("SMALL", kSmallValue)); EXPECT_EQ(list.Count(), static_cast(2)); EXPECT_TRUE(list.Contains("IV")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"IV", "SMALL"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::vector value; EXPECT_TRUE(list.GetHexSequence("IV", &value)); EXPECT_EQ(value, kIvValue); EXPECT_TRUE(list.GetHexSequence("SMALL", &value)); EXPECT_EQ(value, kSmallValue); EXPECT_FALSE(list.GetHexSequence("INSTREAM-ID", &value)); } TEST(HlsAttributeListTest, SetHexSequenceAsVector_Invalid) { HlsAttributeList list; const std::vector kValidValue(4, 0x20); EXPECT_FALSE(list.SetHexSequence("", kValidValue)); EXPECT_FALSE(list.SetHexSequence("BAD NAME", kValidValue)); EXPECT_FALSE(list.SetHexSequence("bad-name", kValidValue)); const std::vector kEmptyVector; EXPECT_FALSE(list.SetHexSequence("METHOD", kEmptyVector)); EXPECT_TRUE(list.IsEmpty()); } TEST(HlsAttributeListTest, SetInteger) { HlsAttributeList list; const uint64_t kInStreamId = 32; EXPECT_TRUE(list.SetInteger("INSTREAM-ID", kInStreamId)); EXPECT_TRUE(list.SetInteger("ZERO", kZeroInt)); EXPECT_TRUE(list.SetInteger("MAX", kMaxInt)); EXPECT_EQ(list.Count(), static_cast(3)); EXPECT_TRUE(list.Contains("ZERO")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"INSTREAM-ID", "MAX", "ZERO"}; EXPECT_EQ(list.GetNames(), kExpectedNames); uint64_t value = 0; EXPECT_TRUE(list.GetInteger("INSTREAM-ID", &value)); EXPECT_EQ(value, kInStreamId); EXPECT_TRUE(list.GetInteger("ZERO", &value)); EXPECT_EQ(value, kZeroInt); EXPECT_TRUE(list.GetInteger("MAX", &value)); EXPECT_EQ(value, kMaxInt); EXPECT_FALSE(list.GetInteger("VERSION", &value)); } TEST(HlsAttributeListTest, SetInteger_Invalid) { HlsAttributeList list; const uint64_t kValidValue = 1337; EXPECT_FALSE(list.SetInteger("", kValidValue)); EXPECT_FALSE(list.SetInteger("BAD NAME", kValidValue)); EXPECT_FALSE(list.SetInteger("bad-name", kValidValue)); } TEST(HlsAttributeListTest, SetFloat) { HlsAttributeList list; const double kFrameRate = 29.97; EXPECT_TRUE(list.SetFloat("FRAME-RATE", kFrameRate)); const double kTimeOffset = -5.5; EXPECT_TRUE(list.SetFloat("TIME-OFFSET", kTimeOffset)); const double kZero = 0.0; EXPECT_TRUE(list.SetFloat("ZERO", kZero)); EXPECT_EQ(list.Count(), static_cast(3)); EXPECT_TRUE(list.Contains("TIME-OFFSET")); EXPECT_FALSE(list.Contains("AUDIO")); const std::vector kExpectedNames = {"FRAME-RATE", "TIME-OFFSET", "ZERO"}; EXPECT_EQ(list.GetNames(), kExpectedNames); double value = 0; EXPECT_TRUE(list.GetFloat("FRAME-RATE", &value)); EXPECT_EQ(value, kFrameRate); EXPECT_TRUE(list.GetFloat("ZERO", &value)); EXPECT_EQ(value, kZero); EXPECT_TRUE(list.GetFloat("TIME-OFFSET", &value)); EXPECT_EQ(value, kTimeOffset); EXPECT_FALSE(list.GetFloat("VERSION", &value)); } TEST(HlsAttributeListTest, SetFloat_Invalid) { HlsAttributeList list; const double kValidValue = 1337.0; EXPECT_FALSE(list.SetFloat("", kValidValue)); EXPECT_FALSE(list.SetFloat("BAD NAME", kValidValue)); EXPECT_FALSE(list.SetFloat("bad-name", kValidValue)); } TEST(HlsAttributeListTest, SetResolution) { HlsAttributeList list; EXPECT_TRUE(list.SetResolution("RESOLUTION", 1920, 1080)); EXPECT_TRUE(list.SetResolution("SVGA-RESOLUTION", 800, 600)); EXPECT_TRUE(list.SetResolution("NULL-RESOLUTION", 0, 0)); const std::vector kExpectedNames = { "NULL-RESOLUTION", "RESOLUTION", "SVGA-RESOLUTION"}; EXPECT_EQ(list.GetNames(), kExpectedNames); uint64_t width = 0; uint64_t height = 0; EXPECT_TRUE(list.GetResolution("RESOLUTION", &width, &height)); EXPECT_EQ(width, static_cast(1920)); EXPECT_EQ(height, static_cast(1080)); EXPECT_TRUE(list.GetResolution("NULL-RESOLUTION", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, kZeroInt); EXPECT_TRUE(list.GetResolution("SVGA-RESOLUTION", &width, &height)); EXPECT_EQ(width, static_cast(800)); EXPECT_EQ(height, static_cast(600)); EXPECT_FALSE(list.GetResolution("VERSION", &width, &height)); } TEST(HlsAttributeListTest, SetResolution_Invalid) { HlsAttributeList list; EXPECT_FALSE(list.SetResolution("", 1920, 1080)); EXPECT_FALSE(list.SetResolution("BAD NAME", 1920, 1080)); EXPECT_FALSE(list.SetResolution("bad-name", 1920, 1080)); } TEST(HlsAttributeListTest, Remove) { HlsAttributeList list; EXPECT_TRUE(list.SetEnumString("METHOD", "SAMPLE-AES")); EXPECT_TRUE(list.SetQuotedString("VERSION", "1/2/5")); const std::vector kIv(16, 0x42); EXPECT_TRUE(list.SetHexSequence("IV", kIv)); const uint64_t kInStreamId = 32; EXPECT_TRUE(list.SetInteger("INSTREAM-ID", kInStreamId)); const double kFrameRate = 29.97; EXPECT_TRUE(list.SetFloat("FRAME-RATE", kFrameRate)); const uint64_t kWidth = 1920; const uint64_t kHeight = 1080; EXPECT_TRUE(list.SetResolution("RESOLUTION", kWidth, kHeight)); const std::vector kExpectedInitialNames = { "FRAME-RATE", "INSTREAM-ID", "IV", "METHOD", "RESOLUTION", "VERSION"}; EXPECT_EQ(list.GetNames(), kExpectedInitialNames); EXPECT_TRUE(list.Remove("IV")); EXPECT_TRUE(list.Remove("VERSION")); EXPECT_FALSE(list.Remove("TIME-OFFSET")); EXPECT_EQ(list.Count(), static_cast(4)); const std::vector kExpectedFinalNames = { "FRAME-RATE", "INSTREAM-ID", "METHOD", "RESOLUTION"}; EXPECT_EQ(list.GetNames(), kExpectedFinalNames); std::string string_value; EXPECT_TRUE(list.GetEnumString("METHOD", &string_value)); EXPECT_EQ(string_value, "SAMPLE-AES"); EXPECT_FALSE(list.GetEnumString("VERSION", &string_value)); std::vector vec_value; EXPECT_FALSE(list.GetHexSequence("IV", &vec_value)); uint64_t int_value = 0; EXPECT_TRUE(list.GetInteger("INSTREAM-ID", &int_value)); EXPECT_EQ(int_value, kInStreamId); } TEST(HlsAttributeListTest, AmbiguousValues_EnumStringAsInteger) { HlsAttributeList list; EXPECT_TRUE(list.SetEnumString("POSITIVE-INT", "1234")); // Max u64 2^64-1 EXPECT_TRUE(list.SetEnumString("MAX-INT", kMaxIntString)); EXPECT_TRUE(list.SetEnumString("BIG-ZERO-INT", "00000000000000000000")); // Over max, 2^64 EXPECT_TRUE(list.SetEnumString("OVER-MAX-INT", "18446744073709551616")); // Integers cannot be negative EXPECT_TRUE(list.SetEnumString("NEGATIVE-INT", "-1337")); // Integers cannot use positive sign. EXPECT_TRUE(list.SetEnumString("BAD-SIGN", "+42")); EXPECT_TRUE(list.SetEnumString("NOT-INT", "OTHER")); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("MAX-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("BIG-ZERO-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("BIG-ZERO-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("OVER-MAX-INT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("OVER-MAX-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("BAD-SIGN", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("BAD-SIGN", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NEGATIVE-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("NOT-INT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NOT-INT", HlsAttributeList::kIntegerType)); uint64_t value = 0; EXPECT_TRUE(list.GetInteger("POSITIVE-INT", &value)); EXPECT_EQ(value, static_cast(1234)); EXPECT_TRUE(list.GetInteger("MAX-INT", &value)); EXPECT_EQ(value, kMaxInt); EXPECT_TRUE(list.GetInteger("BIG-ZERO-INT", &value)); EXPECT_EQ(value, kZeroInt); EXPECT_FALSE(list.GetInteger("OVER-MAX-INT", &value)); EXPECT_FALSE(list.GetInteger("NEGATIVE-INT", &value)); EXPECT_FALSE(list.GetInteger("BAD-SIGN", &value)); EXPECT_FALSE(list.GetInteger("NOT-INT", &value)); } TEST(HlsAttributeListTest, AmbiguousValues_EnumStringAsFloat) { HlsAttributeList list; EXPECT_TRUE(list.SetEnumString("POSITIVE-INT", "1234")); EXPECT_TRUE(list.SetEnumString("NEGATIVE-INT", "-1337")); EXPECT_TRUE(list.SetEnumString("POSITIVE-FLOAT", "29.97")); EXPECT_TRUE(list.SetEnumString("NEGATIVE-FLOAT", "-180.0")); EXPECT_TRUE(list.SetEnumString("MAX-INT-FLOAT", kMaxIntFloatString)); EXPECT_TRUE(list.SetEnumString("NO-FRAC", "12.")); EXPECT_TRUE(list.SetEnumString("NO-INT", ".345")); // Floats cannot have positive signs. EXPECT_TRUE(list.SetEnumString("BAD-SIGN", "+12.345")); EXPECT_TRUE(list.SetEnumString("NOT-FLOAT", "OTHER")); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("POSITIVE-FLOAT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("POSITIVE-FLOAT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NEGATIVE-FLOAT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("NEGATIVE-FLOAT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("MAX-INT-FLOAT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-INT-FLOAT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NO-FRAC", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NO-FRAC", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NO-INT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NO-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("BAD-SIGN", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("BAD-SIGN", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NOT-FLOAT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NOT-FLOAT", HlsAttributeList::kFloatType)); double value = 0.0; EXPECT_TRUE(list.GetFloat("POSITIVE-INT", &value)); EXPECT_EQ(value, 1234.0); EXPECT_TRUE(list.GetFloat("NEGATIVE-INT", &value)); EXPECT_EQ(value, -1337.0); EXPECT_TRUE(list.GetFloat("POSITIVE-FLOAT", &value)); EXPECT_EQ(value, 29.97); EXPECT_TRUE(list.GetFloat("NEGATIVE-FLOAT", &value)); EXPECT_EQ(value, -180.0); EXPECT_TRUE(list.GetFloat("MAX-INT-FLOAT", &value)); EXPECT_EQ(value, kMaxIntFloat); EXPECT_FALSE(list.GetFloat("NO-FRAC", &value)); EXPECT_FALSE(list.GetFloat("NO-INT", &value)); EXPECT_FALSE(list.GetFloat("BAD-SIGN", &value)); EXPECT_FALSE(list.GetFloat("NOT-FLOAT", &value)); } TEST(HlsAttributeListTest, AmbiguousValues_EnumStringAsHexSequence) { HlsAttributeList list; // Hex of "Hello, World!" EXPECT_TRUE(list.SetEnumString("VALID-HEX", "0x48656C6C6F2C20576F726C6421")); // Hex does not need to be even length. EXPECT_TRUE(list.SetEnumString("ODD-HEX", "0X1020304")); // Lower case is not valid HLS hex. EXPECT_TRUE( list.SetEnumString("INVALID-HEX", "0x48656c6c6f2c20576f726c6421")); EXPECT_TRUE(list.SetEnumString("NULL-HEX", "0X")); EXPECT_TRUE(list.SetEnumString("NOT-HEX", "OTHER")); EXPECT_TRUE(list.IsType("VALID-HEX", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("VALID-HEX", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("ODD-HEX", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("ODD-HEX", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("INVALID-HEX", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("INVALID-HEX", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("NULL-HEX", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NULL-HEX", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("NOT-HEX", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NOT-HEX", HlsAttributeList::kHexSequenceType)); std::string string_value; EXPECT_TRUE(list.GetHexSequence("VALID-HEX", &string_value)); EXPECT_EQ(string_value, "Hello, World!"); std::vector vec_value; EXPECT_TRUE(list.GetHexSequence("ODD-HEX", &vec_value)); const std::vector kOddHexValue = {1, 2, 3, 4}; EXPECT_EQ(vec_value, kOddHexValue); EXPECT_FALSE(list.GetHexSequence("INVALID-HEX", &string_value)); EXPECT_FALSE(list.GetHexSequence("NULL-HEX", &string_value)); EXPECT_FALSE(list.GetHexSequence("NOT-HEX", &string_value)); EXPECT_FALSE(list.GetHexSequence("INVALID-HEX", &vec_value)); EXPECT_FALSE(list.GetHexSequence("NULL-HEX", &vec_value)); EXPECT_FALSE(list.GetHexSequence("NOT-HEX", &vec_value)); } TEST(HlsAttributeListTest, AmbiguousValues_EnumStringAsResolution) { HlsAttributeList list; EXPECT_TRUE(list.SetEnumString("VALID-RES", "1920x1080")); EXPECT_TRUE(list.SetEnumString("ZERO-RES", "0x0")); const std::string kMaxIntResString = std::to_string(kMaxInt) + "x" + std::to_string(kMaxInt); EXPECT_TRUE(list.SetEnumString("MAX-RES", kMaxIntResString)); // Resolution separator must be lower 'x'. EXPECT_TRUE(list.SetEnumString("CAP-SEP-RES", "1920X1080")); EXPECT_TRUE(list.SetEnumString("MISSING-WIDTH", "x1080")); EXPECT_TRUE(list.SetEnumString("MISSING-HEIGHT", "1920x")); EXPECT_TRUE(list.SetEnumString("BAD-WIDTH", "BADx1080")); EXPECT_TRUE(list.SetEnumString("BAD-HEIGHT", "1920xBAD")); EXPECT_TRUE(list.SetEnumString("NOT-RES", "OTHER")); EXPECT_TRUE(list.IsType("VALID-RES", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("VALID-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("CAP-SEP-RES", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("CAP-SEP-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MISSING-WIDTH", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("MISSING-WIDTH", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MISSING-HEIGHT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE( list.IsType("MISSING-HEIGHT", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("BAD-WIDTH", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("BAD-WIDTH", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("BAD-HEIGHT", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("BAD-HEIGHT", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("NOT-RES", HlsAttributeList::kEnumStringType)); EXPECT_FALSE(list.IsType("NOT-RES", HlsAttributeList::kResolutionType)); uint64_t width = 0; uint64_t height = 0; EXPECT_TRUE(list.GetResolution("VALID-RES", &width, &height)); EXPECT_EQ(width, static_cast(1920)); EXPECT_EQ(height, static_cast(1080)); EXPECT_TRUE(list.GetResolution("ZERO-RES", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, kZeroInt); EXPECT_TRUE(list.GetResolution("MAX-RES", &width, &height)); EXPECT_EQ(width, kMaxInt); EXPECT_EQ(height, kMaxInt); EXPECT_FALSE(list.GetResolution("CAP-SEP-RES", &width, &height)); EXPECT_FALSE(list.GetResolution("MISSING-WIDTH", &width, &height)); EXPECT_FALSE(list.GetResolution("MISSING-HEIGHT", &width, &height)); EXPECT_FALSE(list.GetResolution("BAD-WIDTH", &width, &height)); EXPECT_FALSE(list.GetResolution("BAD-HEIGHT", &width, &height)); EXPECT_FALSE(list.GetResolution("NOT-RES", &width, &height)); } // Note: All valid hex sequences are also valid enum strings. TEST(HlsAttributeListTest, AmbiguousValues_HexSequenceAsEnumString) { HlsAttributeList list; const std::vector kValueData = a2b_hex("DEADBEAF"); EXPECT_TRUE(list.SetHexSequence("VALUE", kValueData)); EXPECT_TRUE(list.IsType("VALUE", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("VALUE", HlsAttributeList::kEnumStringType)); std::string value; EXPECT_TRUE(list.GetEnumString("VALUE", &value)); // Our implementation will use "0x" prefix. EXPECT_EQ(value, "0xDEADBEAF"); } TEST(HlsAttributeListTest, AmbiguousValues_HexSequenceAsResolution) { HlsAttributeList list; const std::vector kValidResData = a2b_hex("1920"); EXPECT_TRUE(list.SetHexSequence("VALID-RES", kValidResData)); const std::vector kZeroResData = {0}; EXPECT_TRUE(list.SetHexSequence("ZERO-RES", kZeroResData)); // Max integer length is 20 (10 bytes -> 20 hex digits) const std::vector kBigZeroResData(10, 0); EXPECT_TRUE(list.SetHexSequence("BIG-ZERO-RES", kBigZeroResData)); const std::vector kTooBigZeroResData(11, 0); EXPECT_TRUE(list.SetHexSequence("TOO-BIG-ZERO-RES", kTooBigZeroResData)); // Max integer as hex. const std::vector kMaxResData = a2b_hex(kMaxIntString); EXPECT_TRUE(list.SetHexSequence("MAX-RES", kMaxResData)); // Over max as hex. const std::vector kOverMaxResData = a2b_hex("18446744073709551616"); EXPECT_TRUE(list.SetHexSequence("OVER-MAX-RES", kOverMaxResData)); // Resolution must be decimal, not hex. const std::vector kNotDecimalResData = a2b_hex("B000"); EXPECT_TRUE(list.SetHexSequence("NOT-DEC-RES", kNotDecimalResData)); EXPECT_TRUE(list.IsType("VALID-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("VALID-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("BIG-ZERO-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("BIG-ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE( list.IsType("TOO-BIG-ZERO-RES", HlsAttributeList::kHexSequenceType)); EXPECT_FALSE( list.IsType("TOO-BIG-ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("OVER-MAX-RES", HlsAttributeList::kHexSequenceType)); EXPECT_FALSE(list.IsType("OVER-MAX-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("NOT-DEC-RES", HlsAttributeList::kHexSequenceType)); EXPECT_FALSE(list.IsType("NOT-DEC-RES", HlsAttributeList::kResolutionType)); uint64_t width = 0; uint64_t height = 0; EXPECT_TRUE(list.GetResolution("VALID-RES", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, static_cast(1920)); EXPECT_TRUE(list.GetResolution("ZERO-RES", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, kZeroInt); EXPECT_TRUE(list.GetResolution("BIG-ZERO-RES", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, kZeroInt); EXPECT_TRUE(list.GetResolution("MAX-RES", &width, &height)); EXPECT_EQ(width, kZeroInt); EXPECT_EQ(height, kMaxInt); EXPECT_FALSE(list.GetResolution("TOO-BIG-ZERO-RES", &width, &height)); EXPECT_FALSE(list.GetResolution("OVER-MAX-RES", &width, &height)); EXPECT_FALSE(list.GetResolution("NOT-DEC-RES", &width, &height)); } TEST(HlsAttributeListTest, AmbiguousValues_FloatAsInteger) { HlsAttributeList list; EXPECT_TRUE(list.SetFloat("ZERO-INT", 0.0)); EXPECT_TRUE(list.SetFloat("POSITIVE-INT", 123.0)); EXPECT_TRUE(list.SetFloat("NOT-WHOLE", 123.456)); EXPECT_TRUE(list.SetFloat("FRAC-ONLY", 0.456)); EXPECT_TRUE(list.SetFloat("NEGATIVE-INT", -10.0)); EXPECT_TRUE(list.SetFloat("MAX-FLOAT-INT", kMaxIntFloat)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("MAX-FLOAT-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("MAX-FLOAT-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("NOT-WHOLE", HlsAttributeList::kFloatType)); EXPECT_FALSE(list.IsType("NOT-WHOLE", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("FRAC-ONLY", HlsAttributeList::kFloatType)); EXPECT_FALSE(list.IsType("FRAC-ONLY", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kFloatType)); EXPECT_FALSE(list.IsType("NEGATIVE-INT", HlsAttributeList::kIntegerType)); uint64_t value = 0; EXPECT_TRUE(list.GetInteger("ZERO-INT", &value)); EXPECT_EQ(value, kZeroInt); EXPECT_TRUE(list.GetInteger("POSITIVE-INT", &value)); EXPECT_EQ(value, static_cast(123)); EXPECT_TRUE(list.GetInteger("MAX-FLOAT-INT", &value)); EXPECT_EQ(value, kMaxIntFloatInt); EXPECT_FALSE(list.GetInteger("NOT-WHOLE", &value)); EXPECT_FALSE(list.GetInteger("FRAC-ONLY", &value)); EXPECT_FALSE(list.GetInteger("NEGATIVE-INT", &value)); } // Note: All floating point values are valid enum strings. TEST(HlsAttributeListTest, AmbiguousValues_FloatAsEnumString) { HlsAttributeList list; EXPECT_TRUE(list.SetFloat("ZERO-INT", 0.0)); EXPECT_TRUE(list.SetFloat("POSITIVE-INT", 123.0)); EXPECT_TRUE(list.SetFloat("NOT-WHOLE", 123.456)); EXPECT_TRUE(list.SetFloat("FRAC-ONLY", 0.456)); EXPECT_TRUE(list.SetFloat("NEGATIVE-INT", -10.0)); EXPECT_TRUE(list.SetFloat("MAX-FLOAT-INT", kMaxIntFloat)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("POSITIVE-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-FLOAT-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("MAX-FLOAT-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("NOT-WHOLE", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NOT-WHOLE", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("FRAC-ONLY", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("FRAC-ONLY", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("NEGATIVE-INT", HlsAttributeList::kEnumStringType)); std::string value; EXPECT_TRUE(list.GetEnumString("ZERO-INT", &value)); EXPECT_EQ(value, "0"); EXPECT_TRUE(list.GetEnumString("POSITIVE-INT", &value)); EXPECT_EQ(value, "123"); EXPECT_TRUE(list.GetEnumString("NOT-WHOLE", &value)); EXPECT_EQ(value, "123.456"); EXPECT_TRUE(list.GetEnumString("FRAC-ONLY", &value)); EXPECT_EQ(value, "0.456"); EXPECT_TRUE(list.GetEnumString("NEGATIVE-INT", &value)); EXPECT_EQ(value, "-10"); EXPECT_TRUE(list.GetEnumString("MAX-FLOAT-INT", &value)); EXPECT_EQ(value, kMaxIntFloatString); } // Note: All integers values are valid enum strings. TEST(HlsAttributeListTest, AmbiguousValues_IntegerAsEnumString) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("ZERO-INT", kZeroInt)); EXPECT_TRUE(list.SetInteger("SOME-INT", 123456)); EXPECT_TRUE(list.SetInteger("MAX-INT", kMaxInt)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("SOME-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("SOME-INT", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("MAX-INT", HlsAttributeList::kEnumStringType)); std::string value; EXPECT_TRUE(list.GetEnumString("ZERO-INT", &value)); EXPECT_EQ(value, "0"); EXPECT_TRUE(list.GetEnumString("SOME-INT", &value)); EXPECT_EQ(value, "123456"); EXPECT_TRUE(list.GetEnumString("MAX-INT", &value)); EXPECT_EQ(value, kMaxIntString); } // Note: All integer values appear as valid float values; // however, double-precision floats have limited range. TEST(HlsAttributeListTest, AmbiguousValues_IntegerAsFloat) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("ZERO-INT", kZeroInt)); EXPECT_TRUE(list.SetInteger("SOME-INT", 123456)); EXPECT_TRUE(list.SetInteger("MAX-INT-FLOAT", kMaxIntFloat)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("ZERO-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("SOME-INT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("SOME-INT", HlsAttributeList::kFloatType)); EXPECT_TRUE(list.IsType("MAX-INT-FLOAT", HlsAttributeList::kIntegerType)); EXPECT_TRUE(list.IsType("MAX-INT-FLOAT", HlsAttributeList::kFloatType)); double value; EXPECT_TRUE(list.GetFloat("ZERO-INT", &value)); EXPECT_EQ(value, 0.0); EXPECT_TRUE(list.GetFloat("SOME-INT", &value)); EXPECT_EQ(value, 123456); EXPECT_TRUE(list.GetFloat("MAX-INT-FLOAT", &value)); EXPECT_EQ(value, kMaxIntFloat); } // Note: All resolutions are valid enum strings. TEST(HlsAttributeListTest, AmbiguousValues_ResolutionAsEnumString) { HlsAttributeList list; EXPECT_TRUE(list.SetResolution("ZERO-RES", kZeroInt, kZeroInt)); EXPECT_TRUE(list.SetResolution("RESOLUTION", 1920, 1080)); EXPECT_TRUE(list.SetResolution("MAX-RES", kMaxInt, kMaxInt)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("RESOLUTION", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("RESOLUTION", HlsAttributeList::kEnumStringType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kEnumStringType)); std::string value; EXPECT_TRUE(list.GetEnumString("ZERO-RES", &value)); EXPECT_EQ(value, "0x0"); EXPECT_TRUE(list.GetEnumString("RESOLUTION", &value)); EXPECT_EQ(value, "1920x1080"); EXPECT_TRUE(list.GetEnumString("MAX-RES", &value)); const std::string kMaxIntResString = std::to_string(kMaxInt) + "x" + std::to_string(kMaxInt); EXPECT_EQ(value, kMaxIntResString); } TEST(HlsAttributeListTest, AmbiguousValues_ResolutionAsHexSequence) { HlsAttributeList list; // Note: Only appears are resolution if width is zero. EXPECT_TRUE(list.SetResolution("ZERO-RES", kZeroInt, kZeroInt)); EXPECT_TRUE(list.SetResolution("RESOLUTION", kZeroInt, 1080)); EXPECT_TRUE(list.SetResolution("MAX-RES", kZeroInt, kMaxInt)); EXPECT_TRUE(list.SetResolution("NON-ZERO-WIDTH", 1920, 1080)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("ZERO-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("RESOLUTION", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("RESOLUTION", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kResolutionType)); EXPECT_TRUE(list.IsType("MAX-RES", HlsAttributeList::kHexSequenceType)); EXPECT_TRUE(list.IsType("NON-ZERO-WIDTH", HlsAttributeList::kResolutionType)); EXPECT_FALSE( list.IsType("NON-ZERO-WIDTH", HlsAttributeList::kHexSequenceType)); std::vector value; EXPECT_TRUE(list.GetHexSequence("ZERO-RES", &value)); const std::vector kZeroResData = {0}; EXPECT_EQ(value, kZeroResData); EXPECT_TRUE(list.GetHexSequence("RESOLUTION", &value)); const std::vector kResData = {0x10, 0x80}; EXPECT_EQ(value, kResData); EXPECT_TRUE(list.GetHexSequence("MAX-RES", &value)); const std::vector kMaxResData = a2b_hex(kMaxIntString); EXPECT_EQ(value, kMaxResData); EXPECT_FALSE(list.GetHexSequence("NON-ZERO-WIDTH", &value)); } // Quoted strings are always unambiguous. TEST(HlsAttributeListTest, UnambiguousValues_QuotedStringAsAnything) { HlsAttributeList list; // Settings to quoted values of otherwise valid types. EXPECT_TRUE(list.SetQuotedString("INT-LIKE", "1234")); EXPECT_TRUE(list.SetQuotedString("FLOAT-LIKE", "-12.34")); EXPECT_TRUE(list.SetQuotedString("ENUM-LIKE", "VALUE")); EXPECT_TRUE(list.SetQuotedString("RES-LIKE", "1920x1080")); EXPECT_TRUE(list.SetQuotedString("HEX-LIKE", "0xDEADBEAF")); const std::vector kNonQuotedStringTypes = { HlsAttributeList::kIntegerType, HlsAttributeList::kFloatType, HlsAttributeList::kEnumStringType, HlsAttributeList::kResolutionType, HlsAttributeList::kHexSequenceType}; const std::vector names = list.GetNames(); const std::vector kExpectedNames = { "ENUM-LIKE", "FLOAT-LIKE", "HEX-LIKE", "INT-LIKE", "RES-LIKE"}; // Validity check, if this fails, something other that what // this test is checking for has failed. ASSERT_EQ(names, kExpectedNames); for (const auto& name : names) { EXPECT_TRUE(list.IsType(name, HlsAttributeList::kQuotedStringType)) << "name = " << name; for (const auto& type : kNonQuotedStringTypes) { EXPECT_FALSE(list.IsType(name, type)) << "name = " << name << ", type = " << HlsAttributeList::ValueTypeToString(type); } } // Accessor test for only similar looking types. uint64_t int_value = 0; EXPECT_FALSE(list.GetInteger("INT-LIKE", &int_value)); double float_value = 0.0; EXPECT_FALSE(list.GetFloat("FLOAT-LIKE", &float_value)); std::string string_value; EXPECT_FALSE(list.GetEnumString("ENUM-LIKE", &string_value)); EXPECT_FALSE(list.GetHexSequence("HEX-LIKE", &string_value)); std::vector vec_value; EXPECT_FALSE(list.GetHexSequence("HEX-LIKE", &vec_value)); uint64_t other_int_value = 0; EXPECT_FALSE(list.GetResolution("RES-LIKE", &int_value, &other_int_value)); } // Nothing can appear as a quoted string. TEST(HlsAttributeListTest, UnambiguousValues_AnythingAsQuotedString) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("INT", 1234)); EXPECT_TRUE(list.SetHexSequence("HEX", "data")); EXPECT_TRUE(list.SetEnumString("ENUM", "VALUE")); EXPECT_TRUE(list.SetFloat("FLOAT", 12.34)); EXPECT_TRUE(list.SetResolution("RESOLUTION", 1920, 1080)); const std::vector kNonQuotedStringTypes = { HlsAttributeList::kIntegerType, HlsAttributeList::kFloatType, HlsAttributeList::kEnumStringType, HlsAttributeList::kResolutionType, HlsAttributeList::kHexSequenceType}; const std::vector names = list.GetNames(); const std::vector kExpectedNames = {"ENUM", "FLOAT", "HEX", "INT", "RESOLUTION"}; // Validity check, if this fails, something other that what // this test is checking for has failed. ASSERT_EQ(names, kExpectedNames); for (const auto& name : names) { EXPECT_FALSE(list.IsType(name, HlsAttributeList::kQuotedStringType)) << "name = " << name; std::string value; EXPECT_FALSE(list.GetQuotedString(name, &value)); } } // Integers can never appear are hex sequences or resolutions. // Note: Also quoted strings, tested above. TEST(HlsAttributeListTest, UnambiguousValues_Integer) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("ZERO-INT", kZeroInt)); EXPECT_TRUE(list.SetInteger("SOME-INT", 1234)); EXPECT_TRUE(list.SetInteger("MAX-INT", kMaxInt)); const std::vector names = list.GetNames(); ASSERT_EQ(names.size(), static_cast(3)); for (const auto& name : names) { EXPECT_TRUE(list.IsType(name, HlsAttributeList::kIntegerType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kHexSequenceType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kResolutionType)) << "name = " << name; std::string string_value; EXPECT_FALSE(list.GetHexSequence(name, &string_value)) << "name = " << name; std::vector vec_value; EXPECT_FALSE(list.GetHexSequence(name, &vec_value)) << "name = " << name; uint64_t width = 0; uint64_t height = 0; EXPECT_FALSE(list.GetResolution(name, &width, &height)) << "name = " << name; } } // Hex sequences can never appear as integers or floats. // Note: Also quoted strings, tested above. TEST(HlsAttributeListTest, UnambiguousValues_HexSequence) { HlsAttributeList list; const std::vector kSmallZeroData = {0}; EXPECT_TRUE(list.SetHexSequence("SMALL-ZERO", kSmallZeroData)); const std::vector kBigZeroData(10, 0); EXPECT_TRUE(list.SetHexSequence("BIG-ZERO", kBigZeroData)); const std::vector kDecimalData = a2b_hex("1234"); EXPECT_TRUE(list.SetHexSequence("DEC", kDecimalData)); const std::vector kHexData = a2b_hex("deadbeaf"); EXPECT_TRUE(list.SetHexSequence("HEX", kHexData)); const std::vector names = list.GetNames(); ASSERT_EQ(names.size(), static_cast(4)); for (const auto& name : names) { EXPECT_TRUE(list.IsType(name, HlsAttributeList::kHexSequenceType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kIntegerType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kFloatType)) << "name = " << name; uint64_t int_value = 0; EXPECT_FALSE(list.GetInteger(name, &int_value)) << "name = " << name; double float_value = 0.0; EXPECT_FALSE(list.GetFloat(name, &float_value)) << "name = " << name; } } // Floats can never appear as hex sequences or resolutions. // Note: Also quoted strings, tested above. TEST(HlsAttributeListTest, UnambiguousValues_Float) { HlsAttributeList list; EXPECT_TRUE(list.SetFloat("ZERO", 0.0)); EXPECT_TRUE(list.SetFloat("MAX-INT-FLOAT", kMaxIntFloat)); EXPECT_TRUE(list.SetFloat("SOME-FLOAT", 12.34)); const std::vector names = list.GetNames(); ASSERT_EQ(names.size(), static_cast(3)); for (const auto& name : names) { EXPECT_TRUE(list.IsType(name, HlsAttributeList::kFloatType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kHexSequenceType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kResolutionType)) << "name = " << name; std::string string_value; EXPECT_FALSE(list.GetHexSequence(name, &string_value)) << "name = " << name; std::vector vec_value; EXPECT_FALSE(list.GetHexSequence(name, &vec_value)) << "name = " << name; uint64_t width = 0; uint64_t height = 0; EXPECT_FALSE(list.GetResolution(name, &width, &height)) << "name = " << name; } } // Resolutions can never appear as integers or floats. // Note: Also quoted strings, tested above. TEST(HlsAttributeListTest, UnambiguousValues_Resolution) { HlsAttributeList list; EXPECT_TRUE(list.SetResolution("ZERO-RES", kZeroInt, kZeroInt)); EXPECT_TRUE(list.SetResolution("SOME-RES", 1920, 1080)); EXPECT_TRUE(list.SetResolution("ZERO-WIDTH-RES", kZeroInt, 1080)); EXPECT_TRUE(list.SetResolution("ZERO-HEIGHT-RES", 1920, kZeroInt)); EXPECT_TRUE(list.SetResolution("MAX-RES", kMaxInt, kMaxInt)); const std::vector names = list.GetNames(); ASSERT_EQ(names.size(), static_cast(5)); for (const auto& name : names) { EXPECT_TRUE(list.IsType(name, HlsAttributeList::kResolutionType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kIntegerType)) << "name = " << name; EXPECT_FALSE(list.IsType(name, HlsAttributeList::kFloatType)) << "name = " << name; uint64_t int_value = 0; EXPECT_FALSE(list.GetInteger(name, &int_value)) << "name = " << name; double float_value = 0.0; EXPECT_FALSE(list.GetFloat(name, &float_value)) << "name = " << name; } } TEST(HlsAttributeListTest, Parse_SingleInteger) { const std::string rep = "INSTREAM-ID=32"; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const uint64_t kInStreamId = 32; uint64_t value = 0; EXPECT_TRUE(list.GetInteger("INSTREAM-ID", &value)); EXPECT_EQ(value, kInStreamId); } TEST(HlsAttributeListTest, Parse_SingleFloat) { const std::string rep = "FRAME-RATE=29.97"; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const double kFrameRate = 29.97; double value = 0; EXPECT_TRUE(list.GetFloat("FRAME-RATE", &value)); EXPECT_EQ(value, kFrameRate); } TEST(HlsAttributeListTest, Parse_SingleEnumString) { const std::string rep = "METHOD=AES-128"; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const std::string kMethod = "AES-128"; std::string value; EXPECT_TRUE(list.GetEnumString("METHOD", &value)); EXPECT_EQ(value, kMethod); } TEST(HlsAttributeListTest, Parse_SingleQuotedString) { const std::string rep = "VERSION=\"1/2/5\""; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const std::string kVersion = "1/2/5"; std::string value; EXPECT_TRUE(list.GetQuotedString("VERSION", &value)); EXPECT_EQ(value, kVersion); } TEST(HlsAttributeListTest, Parse_SingleEmptyQuotedString) { const std::string rep = "KEYFORMAT=\"\""; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); std::string value = "NOT-EMPTY"; EXPECT_TRUE(list.GetQuotedString("KEYFORMAT", &value)); EXPECT_EQ(value, ""); } TEST(HlsAttributeListTest, Parse_SingleHexSequence) { const std::string rep = "IV=0xDEADBEAF"; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const std::string kIvString = a2bs_hex("DEADBEAF"); const std::vector kIvVector(kIvString.begin(), kIvString.end()); std::string string_value; EXPECT_TRUE(list.GetHexSequence("IV", &string_value)); EXPECT_EQ(string_value, kIvString); std::vector vec_value; EXPECT_TRUE(list.GetHexSequence("IV", &vec_value)); EXPECT_EQ(vec_value, kIvVector); } TEST(HlsAttributeListTest, Parse_SingleResolution) { const std::string rep = "RESOLUTION=1920x1080"; HlsAttributeList list; EXPECT_TRUE(list.Parse(rep)); EXPECT_FALSE(list.IsEmpty()); EXPECT_EQ(list.Count(), static_cast(1)); const uint64_t kWidth = 1920; const uint64_t kHeight = 1080; uint64_t width = 0; uint64_t height = 0; EXPECT_TRUE(list.GetResolution("RESOLUTION", &width, &height)); EXPECT_EQ(width, kWidth); EXPECT_EQ(height, kHeight); } TEST(HlsAttributeListTest, Parse_OneOfEach) { const uint64_t kInStreamId = 32; const double kFrameRate = 29.97; const std::string kIvString = a2bs_hex("DEADBEAF"); const uint64_t kWidth = 1920; const uint64_t kHeight = 1080; std::ostringstream hls_stream; hls_stream << "INSTREAM-ID=32,"; hls_stream << "FRAME-RATE=29.97,"; hls_stream << "METHOD=AES-128,"; hls_stream << "VERSION=\"1/2/5\","; hls_stream << "IV=0xDEADBEAF,"; hls_stream << "RESOLUTION=1920x1080"; const std::string rep = hls_stream.str(); HlsAttributeList list; ASSERT_TRUE(list.Parse(rep)); EXPECT_EQ(list.Count(), static_cast(6)); const std::vector kExpectedNames = { "FRAME-RATE", "INSTREAM-ID", "IV", "METHOD", "RESOLUTION", "VERSION", }; uint64_t int_value = 0; EXPECT_TRUE(list.GetInteger("INSTREAM-ID", &int_value)); EXPECT_EQ(int_value, kInStreamId); double float_value = 0.0; EXPECT_TRUE(list.GetFloat("FRAME-RATE", &float_value)); EXPECT_EQ(float_value, kFrameRate); std::string string_value; EXPECT_TRUE(list.GetEnumString("METHOD", &string_value)); EXPECT_EQ(string_value, "AES-128"); EXPECT_TRUE(list.GetQuotedString("VERSION", &string_value)); EXPECT_EQ(string_value, "1/2/5"); EXPECT_TRUE(list.GetHexSequence("IV", &string_value)); EXPECT_EQ(string_value, kIvString); uint64_t other_int_value = 0; EXPECT_TRUE(list.GetResolution("RESOLUTION", &int_value, &other_int_value)); EXPECT_EQ(int_value, kWidth); EXPECT_EQ(other_int_value, kHeight); } TEST(HlsAttributeListTest, Parse_KeyLike) { // #EXT-X-KEY const std::string uri = "data:text/plain;base64,SSdtIGEga2V5IQ=="; std::ostringstream hls_stream; hls_stream << "METHOD=AES-128,"; hls_stream << "URI=\"" << uri << "\","; hls_stream << "IV=0xDEADBEAFDEADBEAFDEADBEAFDEADBEAF,"; hls_stream << "KEYFORMAT=\"com.widevine\","; hls_stream << "KEYFORMATVERSIONS=\"1/2/5\""; const std::string rep = hls_stream.str(); HlsAttributeList list; ASSERT_TRUE(list.Parse(rep)); EXPECT_EQ(list.Count(), static_cast(5)); const std::vector kExpectedNames = { "IV", "KEYFORMAT", "KEYFORMATVERSIONS", "METHOD", "URI"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::string string_value; EXPECT_TRUE(list.GetEnumString("METHOD", &string_value)); EXPECT_EQ(string_value, "AES-128"); EXPECT_TRUE(list.GetQuotedString("URI", &string_value)); EXPECT_EQ(string_value, uri); const std::string iv = a2bs_hex("DEADBEAFDEADBEAFDEADBEAFDEADBEAF"); EXPECT_TRUE(list.GetHexSequence("IV", &string_value)); EXPECT_EQ(string_value, iv); EXPECT_TRUE(list.GetQuotedString("KEYFORMAT", &string_value)); EXPECT_EQ(string_value, "com.widevine"); EXPECT_TRUE(list.GetQuotedString("KEYFORMATVERSIONS", &string_value)); EXPECT_EQ(string_value, "1/2/5"); } TEST(HlsAttributeListTest, Parse_MediaLike) { // #EXT-X-MEDIA const std::string uri = "https://media.src/playlist"; const std::string group_id = "The Best Group"; const std::string language = "en"; const std::string associated_language = "fr"; const std::string name = "The Best Captioning"; const std::string characteristics = "public.easy-to-read"; std::ostringstream hls_stream; hls_stream << "TYPE=CLOSED-CAPTIONS,"; hls_stream << "URI=\"" << uri << "\","; hls_stream << "GROUP-ID=\"" << group_id << "\","; hls_stream << "LANGUAGE=\"" << language << "\","; hls_stream << "ASSOC-LANGUAGE=\"" << associated_language << "\","; hls_stream << "NAME=\"" << name << "\","; hls_stream << "DEFAULT=YES,"; hls_stream << "AUTOSELECT=NO,"; hls_stream << "FORCED=NO,"; hls_stream << "INSTREAM-ID=CC1,"; hls_stream << "CHARACTERISTICS=\"" << characteristics << "\","; hls_stream << "CHANNELS=\"1/2/4/7\""; const std::string rep = hls_stream.str(); HlsAttributeList list; ASSERT_TRUE(list.Parse(rep)); EXPECT_EQ(list.Count(), static_cast(12)); const std::vector kExpectedNames = { "ASSOC-LANGUAGE", "AUTOSELECT", "CHANNELS", "CHARACTERISTICS", "DEFAULT", "FORCED", "GROUP-ID", "INSTREAM-ID", "LANGUAGE", "NAME", "TYPE", "URI"}; EXPECT_EQ(list.GetNames(), kExpectedNames); std::string string_value; EXPECT_TRUE(list.GetEnumString("TYPE", &string_value)); EXPECT_EQ(string_value, "CLOSED-CAPTIONS"); EXPECT_TRUE(list.GetQuotedString("URI", &string_value)); EXPECT_EQ(string_value, uri); EXPECT_TRUE(list.GetQuotedString("GROUP-ID", &string_value)); EXPECT_EQ(string_value, group_id); EXPECT_TRUE(list.GetQuotedString("LANGUAGE", &string_value)); EXPECT_EQ(string_value, language); EXPECT_TRUE(list.GetQuotedString("ASSOC-LANGUAGE", &string_value)); EXPECT_EQ(string_value, associated_language); EXPECT_TRUE(list.GetQuotedString("NAME", &string_value)); EXPECT_EQ(string_value, name); EXPECT_TRUE(list.GetEnumString("DEFAULT", &string_value)); EXPECT_EQ(string_value, "YES"); EXPECT_TRUE(list.GetEnumString("AUTOSELECT", &string_value)); EXPECT_EQ(string_value, "NO"); string_value.clear(); EXPECT_TRUE(list.GetEnumString("FORCED", &string_value)); EXPECT_EQ(string_value, "NO"); EXPECT_TRUE(list.GetEnumString("INSTREAM-ID", &string_value)); EXPECT_EQ(string_value, "CC1"); EXPECT_TRUE(list.GetQuotedString("CHARACTERISTICS", &string_value)); EXPECT_EQ(string_value, characteristics); EXPECT_TRUE(list.GetQuotedString("CHANNELS", &string_value)); EXPECT_EQ(string_value, "1/2/4/7"); } TEST(HlsAttributeListTest, BadParse_MissingValue) { HlsAttributeList list; EXPECT_FALSE(list.Parse("A=,B=2,C=3")); EXPECT_FALSE(list.Parse("A=1,B=,C=3")); EXPECT_FALSE(list.Parse("A=1,B=2,C=")); } TEST(HlsAttributeListTest, BadParse_BadWhitespace) { HlsAttributeList list; EXPECT_FALSE(list.Parse("KEY=THIS VALUE")); EXPECT_FALSE(list.Parse("THIS KEY=1")); EXPECT_FALSE(list.Parse(" A=1,B=2")); EXPECT_FALSE(list.Parse("A =1,B=2")); EXPECT_FALSE(list.Parse("A= 1,B=2")); EXPECT_FALSE(list.Parse("A=1 ,B=2")); EXPECT_FALSE(list.Parse("A=1, B=2")); EXPECT_FALSE(list.Parse("A=1,B =2")); EXPECT_FALSE(list.Parse("A=1,B= 2")); EXPECT_FALSE(list.Parse("A=1,B=2 ")); } TEST(HlsAttributeListTest, BadParse_BadQuotes) { HlsAttributeList list; EXPECT_FALSE(list.Parse("\"KEY\"=VALUE")); EXPECT_FALSE(list.Parse("KEY=\"VALUE")); EXPECT_FALSE(list.Parse("KEY=VALUE\"")); EXPECT_FALSE(list.Parse("KEY=\"VALUE\"AND")); } TEST(HlsAttributeListTest, Serialize) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("INSTREAM-ID", 32)); EXPECT_TRUE(list.SetFloat("FRAME-RATE", 29.97)); EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128")); EXPECT_TRUE(list.SetQuotedString("VERSION", "1/2/5")); EXPECT_TRUE(list.SetHexSequence("IV", a2b_hex("DEADBEAF"))); EXPECT_TRUE(list.SetResolution("RESOLUTION", 1920, 1080)); // Note: The HlsAttributeList class does not guarantee any particular // order when serializing to a string. const std::vector kExpectedParts = { "FRAME-RATE=29.97", "INSTREAM-ID=32", // Note: HlsAttributeList uses lower prefix "0x" for hex sequences. "IV=0xDEADBEAF", "METHOD=AES-128", "RESOLUTION=1920x1080", "VERSION=\"1/2/5\""}; const std::string result = list.Serialize(); ASSERT_FALSE(result.empty()); size_t expected_length = kExpectedParts.size() - 1; // Start with commas. for (const auto& part : kExpectedParts) { ASSERT_TRUE(StringContains(result, part)) << "result: " << result; expected_length += part.size(); } EXPECT_EQ(expected_length, result.size()) << "result: " << result; } TEST(HlsAttributeListTest, Serialize_Empty) { HlsAttributeList list; EXPECT_EQ(list.Serialize(), ""); } TEST(HlsAttributeListTest, Serialize_One) { HlsAttributeList list; EXPECT_TRUE(list.SetInteger("INSTREAM-ID", 32)); EXPECT_EQ(list.Serialize(), "INSTREAM-ID=32"); } TEST(HlsAttributeListTest, Serialize_QuotedEmpty) { HlsAttributeList list; EXPECT_TRUE(list.SetQuotedString("VERSION", "")); EXPECT_EQ(list.Serialize(), "VERSION=\"\""); } } // namespace test } // namespace wvutil