| // Copyright 2026 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "jumbf/parse.h" |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/status/status.h" |
| #include "absl/status/status_matchers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_join.h" |
| #include "absl/strings/string_view.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "jumbf/box.h" |
| #include "jumbf/constants.h" |
| #include "jumbf/test_utils.h" |
| #include "riegeli/endian/endian_writing.h" |
| #include "uuid/uuid.h" |
| |
| namespace jumbf { |
| namespace { |
| using ::absl_testing::IsOkAndHolds; |
| using ::absl_testing::StatusIs; |
| using ::credentio::Uuid; |
| using ::testing::Eq; |
| using ::testing::IsEmpty; |
| |
| constexpr absl::string_view kFakeUuid = "DEADBEEF-F00D-BABE-B4DF-00D528013378"; |
| |
| std::string FakeDescriptionBoxContents() { |
| // Can't use absl::string_view because the compiler warns against assuming it |
| // is null terminated, even when it truly is null terminated. |
| constexpr char kFakeLabel[] = "fakelabel"; |
| const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1 + |
| strlen(kFakeLabel) + |
| /*label null terminator*/ 1; |
| std::string fake_description_contents(description_content_length, '\0'); |
| char* content_cursor = fake_description_contents.data(); |
| |
| SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid)); |
| riegeli::WriteBigEndian<uint8_t>(kDescriptionToggleLabelPresent, |
| content_cursor); |
| content_cursor += sizeof(uint8_t); |
| memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel) + 1); |
| content_cursor += strlen(kFakeLabel) + 1; |
| |
| return fake_description_contents; |
| } |
| |
| TEST(ParseTest, InvalidSuperBoxFails) { |
| absl::string_view empty; |
| |
| EXPECT_THAT( |
| ConsumeSuperBox(&empty, /*recursion_limit=*/1), |
| StatusIs( |
| absl::StatusCode::kOutOfRange, |
| R"(cannot unwrap super box: cannot read LBox value: not enough input bytes remaining; have 0 need 4)")); |
| EXPECT_THAT(empty, IsEmpty()); |
| } |
| |
| TEST(ParseTest, InvalidSuperBoxPayloadFails) { |
| absl::string_view kContent = |
| R"(This content doesn't really matter for this test case.)"; |
| std::string serialized = WrapBox(kContent, 0xf00dbabe); |
| absl::string_view serialized_view = serialized; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| StatusIs(absl::StatusCode::kInvalidArgument, |
| "box is not a super box: got type 0xf00dbabe need " |
| "0x6a756d62 ('jumb')")); |
| EXPECT_THAT(serialized_view, Eq(serialized)); |
| } |
| |
| TEST(ParseTest, InvalidDescriptionBoxFails) { |
| constexpr uint32_t kLBox = 5; |
| constexpr uint32_t kTBox = 12345; |
| |
| std::string inner_box(8, '\0'); |
| char* cursor = inner_box.data(); |
| |
| riegeli::WriteBigEndian<uint32_t>(kLBox, cursor); |
| cursor += sizeof(uint32_t); |
| riegeli::WriteBigEndian<uint32_t>(kTBox, cursor); |
| cursor += sizeof(uint32_t); |
| |
| const std::string super_box = WrapBox(inner_box, kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| StatusIs(absl::StatusCode::kInvalidArgument, |
| "cannot unwrap description box: LBox is 5; LBox values " |
| "2-7 are reserved by the standard")); |
| EXPECT_THAT(serialized_view, Eq(super_box)); |
| } |
| |
| TEST(ParseTest, InvalidDescriptionBoxPayloadFails) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), /*tbox=*/12345); |
| std::string content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| StatusIs(absl::StatusCode::kInvalidArgument, |
| "cannot parse description box: box is not a description " |
| "box; got type 0x3039 need 0x6a756d64 ('jumd')")); |
| EXPECT_THAT(serialized_view, Eq(super_box)); |
| } |
| |
| TEST(ParseTest, NegativeRecursionLimitCausesNoUnderflow) { |
| absl::string_view box_from_fuzzer( |
| "\000\000\000\000jumb\000\000\000\031jumd\000\000\000\000\000\000\000\000" |
| "\000\000\000\000\000\000\000\000\000\000\000\000\000jumb\000\000\000\031" |
| "jumd\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" |
| "\000\000\031\000\000\000\000\000\000\000\000\000", |
| 78); |
| EXPECT_THAT( |
| ConsumeSuperBox(&box_from_fuzzer, /*recursion_limit=*/-2147483648), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseToProtoTest, AllDescriptionBoxTogglesSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| constexpr absl::string_view kFakeSalt = R"(0123456701234567)"; |
| const std::string private_box = jumbf::WrapBox(kFakeSalt, kC2paSaltBoxType); |
| |
| // Can't use absl::string_view because the compiler warns against assuming it |
| // is null terminated, even when it truly is null terminated. |
| constexpr char kFakeLabel[] = "fakelabel"; |
| constexpr uint32_t kFakeId = 0xB33FD00D; |
| const std::string fake_hash(32, 'X'); |
| const size_t description_content_length = |
| /*uuid*/ 16 + /*toggles*/ 1 + strlen(kFakeLabel) + |
| /*label null terminator*/ 1 + /*id*/ 4 + fake_hash.length() + |
| private_box.length(); |
| std::string fake_description_contents(description_content_length, 'A'); |
| char* content_cursor = fake_description_contents.data(); |
| |
| SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid)); |
| uint8_t toggles = jumbf::kDescriptionToggleRequestable | |
| jumbf::kDescriptionToggleLabelPresent | |
| jumbf::kDescriptionToggleIdPresent | |
| jumbf::kDescriptionToggleHashPresent | |
| jumbf::kDescriptionTogglePrivatePresent; |
| riegeli::WriteBigEndian<uint8_t>(toggles, content_cursor); |
| content_cursor += sizeof(uint8_t); |
| memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel) + 1); |
| content_cursor += strlen(kFakeLabel) + 1; |
| riegeli::WriteBigEndian<uint32_t>(kFakeId, content_cursor); |
| content_cursor += sizeof(uint32_t); |
| memcpy(content_cursor, fake_hash.data(), 32); |
| content_cursor += 32; |
| memcpy(content_cursor, private_box.data(), private_box.length()); |
| |
| std::string content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string super_box = WrapBox( |
| absl::StrCat(WrapBox(fake_description_contents, kDescriptionBoxType), |
| content_box), |
| kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .requestable = true, |
| .label = "fakelabel", |
| .id = kFakeId, |
| .unvalidated_hash = fake_hash, |
| .private_content{ContentBox{.raw_bytes = private_box, |
| .payload = |
| C2paSaltBox{ |
| .salt = kFakeSalt, |
| }}}}, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseToProtoTest, DescriptionBoxMultiplePrivateContentSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| constexpr absl::string_view kFirstPrivatePayload = |
| R"(The format of this data is unimportant for this test case.)"; |
| constexpr absl::string_view kSecondPrivatePayload = |
| R"(The format of this data is also unimportant for this test case.)"; |
| const std::string private_box_payload[] = {WrapBox(kFirstPrivatePayload, 1), |
| WrapBox(kSecondPrivatePayload, 2)}; |
| const std::string private_box = |
| WrapBox(absl::StrJoin(private_box_payload, ""), kPrivateContentBoxType); |
| |
| // Can't use absl::string_view because the compiler warns against assuming it |
| // is null terminated, even when it truly is null terminated. |
| constexpr char kFakeLabel[] = "fakelabel"; |
| constexpr uint32_t kFakeId = 0xB33FD00D; |
| const std::string fake_hash(32, 'X'); |
| const size_t description_content_length = |
| /*uuid*/ 16 + /*toggles*/ 1 + strlen(kFakeLabel) + |
| /*label null terminator*/ 1 + /*id*/ 4 + fake_hash.length() + |
| private_box.length(); |
| std::string fake_description_contents(description_content_length, 'A'); |
| char* content_cursor = fake_description_contents.data(); |
| |
| SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid)); |
| uint8_t toggles = |
| kDescriptionToggleRequestable | kDescriptionToggleLabelPresent | |
| kDescriptionToggleIdPresent | kDescriptionToggleHashPresent | |
| kDescriptionTogglePrivatePresent; |
| riegeli::WriteBigEndian<uint8_t>(toggles, content_cursor); |
| content_cursor += sizeof(uint8_t); |
| memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel) + 1); |
| content_cursor += strlen(kFakeLabel) + 1; |
| riegeli::WriteBigEndian<uint32_t>(kFakeId, content_cursor); |
| content_cursor += sizeof(uint32_t); |
| memcpy(content_cursor, fake_hash.data(), 32); |
| content_cursor += 32; |
| memcpy(content_cursor, private_box.data(), private_box.length()); |
| |
| std::string content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string super_box = WrapBox( |
| absl::StrCat(WrapBox(fake_description_contents, kDescriptionBoxType), |
| content_box), |
| kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT( |
| ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .requestable = true, |
| .label = "fakelabel", |
| .id = kFakeId, |
| .unvalidated_hash = fake_hash, |
| .private_content{ |
| ContentBox{ |
| .raw_bytes = WrapBox(kFirstPrivatePayload, 1), |
| .payload = |
| UnknownBox{ |
| .type = 1, |
| .payload = kFirstPrivatePayload, |
| }, |
| }, |
| ContentBox{ |
| .raw_bytes = WrapBox(kSecondPrivatePayload, 2), |
| .payload = |
| UnknownBox{ |
| .type = 2, |
| .payload = kSecondPrivatePayload, |
| }, |
| }, |
| }}, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, SingleCborContentBoxSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, MultipleCborContentBoxSucceeds) { |
| constexpr absl::string_view kFakeFirstCborData = |
| R"(first content: the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| constexpr absl::string_view kFakeSecondCborData = |
| R"(second content: the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string super_box = WrapBox( |
| absl::StrCat(description_box, WrapBox(kFakeFirstCborData, kCborBoxType), |
| WrapBox(kFakeSecondCborData, kCborBoxType)), |
| kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = |
| { |
| ContentBox{.raw_bytes = WrapBox(kFakeFirstCborData, |
| kCborBoxType), |
| .payload = |
| CborBox{ |
| .payload = kFakeFirstCborData, |
| }}, |
| ContentBox{.raw_bytes = WrapBox(kFakeSecondCborData, |
| kCborBoxType), |
| .payload = |
| CborBox{ |
| .payload = kFakeSecondCborData, |
| }}, |
| }, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, SingleEmbeddedFileDescriptionBoxSucceeds) { |
| constexpr uint8_t kToggles = 0; |
| constexpr const char kFakeMediatype[] = "media/MyFormat"; |
| |
| const size_t payload_length = 1 /*toggles*/ + sizeof(kFakeMediatype); |
| std::string payload(payload_length, 'A'); |
| char* cursor = payload.data(); |
| riegeli::WriteBigEndian<uint8_t>(kToggles, cursor); |
| cursor += sizeof(uint8_t); |
| memcpy(cursor, kFakeMediatype, sizeof(kFakeMediatype)); |
| cursor += sizeof(kFakeMediatype); |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(payload, kEmbeddedFileDescriptionBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| EmbeddedFileDescriptionBox{ |
| .media_type = kFakeMediatype, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, SingleBinaryDataBoxSucceeds) { |
| constexpr absl::string_view kPayload = |
| R"(blah blah blah blah this is just some data in no particular format)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(kPayload, kBinaryDataBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| BinaryDataBox{ |
| .payload = kPayload, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, RecursiveSuperBoxUnderRecursionLimitSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string inner_content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string inner_super_box = |
| WrapBox(absl::StrCat(description_box, inner_content_box), kSuperBoxType); |
| |
| std::string outer_super_box = |
| WrapBox(absl::StrCat(description_box, inner_super_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = outer_super_box; |
| |
| EXPECT_THAT( |
| ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{ |
| .raw_bytes = inner_super_box, |
| .payload = |
| SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = inner_content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = inner_super_box}}}, |
| .raw_bytes = outer_super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, RecursiveSuperBoxNoRecursionLimitSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string inner_content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string inner_super_box = |
| WrapBox(absl::StrCat(description_box, inner_content_box), kSuperBoxType); |
| |
| std::string outer_super_box = |
| WrapBox(absl::StrCat(description_box, inner_super_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = outer_super_box; |
| |
| EXPECT_THAT( |
| ConsumeSuperBox(&serialized_view, /*recursion_limit=*/-1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{ |
| .raw_bytes = inner_super_box, |
| .payload = |
| SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = inner_content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = inner_super_box}}}, |
| .raw_bytes = outer_super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, RecursiveSuperBoxExceedsZeroRecursionLimitSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string inner_content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string inner_super_box = |
| WrapBox(absl::StrCat(description_box, inner_content_box), kSuperBoxType); |
| |
| std::string outer_super_box = |
| WrapBox(absl::StrCat(description_box, inner_super_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = outer_super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/0), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = inner_super_box, |
| .payload = |
| SerializedSuperbox{ |
| .serialized = inner_super_box, |
| }}}, |
| .raw_bytes = outer_super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, RecursiveSuperBoxExceedsNonzeroRecursionLimitSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string inner_content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string inner_super_box = |
| WrapBox(absl::StrCat(description_box, inner_content_box), kSuperBoxType); |
| |
| std::string middle_super_box = |
| WrapBox(absl::StrCat(description_box, inner_super_box), kSuperBoxType); |
| |
| std::string outer_super_box = |
| WrapBox(absl::StrCat(description_box, middle_super_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = outer_super_box; |
| |
| EXPECT_THAT( |
| ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{ |
| .raw_bytes = middle_super_box, |
| .payload = SuperBox{.description = |
| DescriptionBox{ |
| .type_uuid = |
| Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{ |
| .raw_bytes = inner_super_box, |
| .payload = |
| SerializedSuperbox{ |
| .serialized = inner_super_box, |
| }}}, |
| .raw_bytes = middle_super_box}}}, |
| .raw_bytes = outer_super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, SingleUnknownContentBoxSucceeds) { |
| constexpr absl::string_view kUnknownContent = |
| R"(this is some data in a format this parser does not understand)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(kUnknownContent, 12345678); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| UnknownBox{ |
| .type = 12345678, |
| .payload = kUnknownContent, |
| }}}, |
| .raw_bytes = super_box})); |
| |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, ExcessContentSucceeds) { |
| constexpr absl::string_view kFakeCborData = |
| R"(the function under test does not actually parse CBOR data so we'll just use a totally fake string here)"; |
| |
| constexpr absl::string_view kExtraData = |
| R"(this is some extra data in the input, the parser should not look at this)"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(kFakeCborData, kCborBoxType); |
| std::string super_box = absl::StrCat( |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType), |
| kExtraData); |
| |
| absl::string_view serialized_view = super_box; |
| |
| EXPECT_THAT(ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1), |
| IsOkAndHolds(SuperBox{ |
| .description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| CborBox{ |
| .payload = kFakeCborData, |
| }}}, |
| .raw_bytes = super_box.substr( |
| 0, super_box.size() - kExtraData.size())})); |
| |
| EXPECT_THAT(serialized_view, Eq(kExtraData)); |
| } |
| |
| TEST(ParseTest, SingleJsonDataBoxSucceeds) { |
| constexpr absl::string_view kPayload = R"({"abc": "xyz"})"; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(kPayload, kJsonBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| auto superbox_or = ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1); |
| ASSERT_TRUE(superbox_or.ok()); |
| auto superbox = std::move(*superbox_or); |
| EXPECT_THAT(superbox, |
| Eq(SuperBox{.description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| JsonBox{ |
| .payload = kPayload, |
| }}}, |
| .raw_bytes = super_box})); |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| TEST(ParseTest, UuidBoxSucceeds) { |
| std::string uuid_box_payload = jumbf::kC2PARedactionUuid.ToProtoBytes(); |
| absl::string_view payload = uuid_box_payload; |
| |
| std::string description_box = |
| WrapBox(FakeDescriptionBoxContents(), kDescriptionBoxType); |
| std::string content_box = WrapBox(payload, kUuidBoxType); |
| std::string super_box = |
| WrapBox(absl::StrCat(description_box, content_box), kSuperBoxType); |
| |
| absl::string_view serialized_view = super_box; |
| |
| auto superbox_or = ConsumeSuperBox(&serialized_view, /*recursion_limit=*/1); |
| ASSERT_TRUE(superbox_or.ok()); |
| auto superbox = std::move(*superbox_or); |
| EXPECT_THAT(superbox, |
| Eq(SuperBox{.description = |
| DescriptionBox{ |
| .type_uuid = Uuid::FromStringOrDie(kFakeUuid), |
| .label = "fakelabel", |
| }, |
| .contents = {ContentBox{.raw_bytes = content_box, |
| .payload = |
| UuidBox{ |
| .payload = payload, |
| }}}, |
| .raw_bytes = super_box})); |
| EXPECT_THAT(serialized_view, IsEmpty()); |
| } |
| |
| } // namespace |
| } // namespace jumbf |