| // 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 <memory> | |
| #include <utility> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.h" // IWYU pragma: keep | |
| #include "absl/status/status_matchers.h" | |
| #include "cbor/cbor.h" | |
| #include "cbor/options.h" | |
| #include "cbor/test/custom_namespace.cbor.h" | |
| #include "cbor/test/custom_namespace.pb.h" | |
| #include "cbor/test/external.pb.h" | |
| #include "cbor/test/migrated.cbor.h" // IWYU pragma: keep | |
| #include "cbor/test/test.cbor.h" | |
| #include "cbor/test/test.pb.h" | |
| #include "cppbor/cppbor.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| namespace cbor_test { | |
| namespace { | |
| using ::testing::Eq; | |
| TEST(CborGeneratorTest, CustomNamespaceRoundTrip) { | |
| custom_cbor::test::CustomNamespaceMessage msg; | |
| msg.set_name("custom_name"); | |
| msg.set_version(42); | |
| // Serialize to CBOR map | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| ASSERT_NE(map, nullptr); | |
| // Deserialize from CBOR map | |
| custom_cbor::test::CustomNamespaceMessage parsed_msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &parsed_msg); | |
| ABSL_ASSERT_OK(status); | |
| EXPECT_EQ(parsed_msg.name(), "custom_name"); | |
| EXPECT_EQ(parsed_msg.version(), 42); | |
| } | |
| TEST(CborGeneratorTest, RoundTrip) { | |
| TestMessage msg; | |
| msg.set_name("hello"); | |
| msg.set_version(1); | |
| // Serialize to CBOR map | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| ASSERT_NE(map, nullptr); | |
| // Deserialize from CBOR map | |
| TestMessage parsed_msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &parsed_msg); | |
| ABSL_ASSERT_OK(status); | |
| EXPECT_EQ(parsed_msg.name(), "hello"); | |
| EXPECT_EQ(parsed_msg.version(), 1); | |
| } | |
| TEST(CborGeneratorTest, ComplexRoundTrip) { | |
| ComplexMessage msg; | |
| msg.set_int32_field(10); | |
| msg.set_int64_field(20); | |
| msg.set_uint32_field(30); | |
| msg.set_uint64_field(40); | |
| msg.set_string_field("test_string"); | |
| msg.set_bytes_field("test_bytes"); | |
| msg.set_bool_field(true); | |
| msg.set_float_field(1.5f); | |
| msg.set_double_field(2.5); | |
| msg.add_repeated_int32(100); | |
| msg.add_repeated_int32(200); | |
| msg.add_repeated_string("item1"); | |
| msg.add_repeated_string("item2"); | |
| msg.mutable_nested()->set_value("nested_val"); | |
| msg.add_repeated_nested()->set_value("rn1"); | |
| msg.add_repeated_nested()->set_value("rn2"); | |
| msg.add_repeated_bytes("b1"); | |
| msg.add_repeated_bytes("b2"); | |
| msg.add_repeated_string_array("s1"); | |
| msg.add_repeated_string_array("s2"); | |
| msg.add_repeated_int64(2000000000L); | |
| msg.add_repeated_int64(3000000000L); | |
| msg.add_repeated_uint32(300); | |
| msg.add_repeated_uint32(400); | |
| msg.add_repeated_uint64(4000000000L); | |
| msg.add_repeated_uint64(5000000000L); | |
| msg.add_repeated_bool(true); | |
| msg.add_repeated_bool(false); | |
| msg.add_repeated_float(1.5f); | |
| msg.add_repeated_float(2.5f); | |
| msg.add_repeated_double(3.5); | |
| msg.add_repeated_double(4.5); | |
| msg.set_read_only_field("ro"); | |
| msg.set_write_only_field("wo"); | |
| msg.set_skip_field("skip"); | |
| msg.mutable_external_field()->set_external_value("ext_val"); | |
| // Serialize to CBOR map | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| ASSERT_NE(map, nullptr); | |
| // Deserialize from CBOR map | |
| ComplexMessage parsed_msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &parsed_msg); | |
| ABSL_ASSERT_OK(status); | |
| EXPECT_EQ(parsed_msg.int32_field(), 10); | |
| EXPECT_EQ(parsed_msg.int64_field(), 20); | |
| EXPECT_EQ(parsed_msg.uint32_field(), 30); | |
| EXPECT_EQ(parsed_msg.uint64_field(), 40); | |
| EXPECT_EQ(parsed_msg.string_field(), "test_string"); | |
| EXPECT_EQ(parsed_msg.bytes_field(), "test_bytes"); | |
| EXPECT_EQ(parsed_msg.bool_field(), true); | |
| EXPECT_FLOAT_EQ(parsed_msg.float_field(), 1.5f); | |
| EXPECT_DOUBLE_EQ(parsed_msg.double_field(), 2.5); | |
| ASSERT_EQ(parsed_msg.repeated_int32_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_int32(0), 100); | |
| EXPECT_EQ(parsed_msg.repeated_int32(1), 200); | |
| ASSERT_EQ(parsed_msg.repeated_string_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_string(0), "item1"); | |
| EXPECT_EQ(parsed_msg.repeated_string(1), "item2"); | |
| EXPECT_EQ(parsed_msg.nested().value(), "nested_val"); | |
| ASSERT_EQ(parsed_msg.repeated_nested_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_nested(0).value(), "rn1"); | |
| EXPECT_EQ(parsed_msg.repeated_nested(1).value(), "rn2"); | |
| ASSERT_EQ(parsed_msg.repeated_bytes_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_bytes(0), "b1"); | |
| EXPECT_EQ(parsed_msg.repeated_bytes(1), "b2"); | |
| ASSERT_EQ(parsed_msg.repeated_string_array_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_string_array(0), "s1"); | |
| EXPECT_EQ(parsed_msg.repeated_string_array(1), "s2"); | |
| ASSERT_EQ(parsed_msg.repeated_int64_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_int64(0), 2000000000L); | |
| EXPECT_EQ(parsed_msg.repeated_int64(1), 3000000000L); | |
| ASSERT_EQ(parsed_msg.repeated_uint32_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_uint32(0), 300); | |
| EXPECT_EQ(parsed_msg.repeated_uint32(1), 400); | |
| ASSERT_EQ(parsed_msg.repeated_uint64_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_uint64(0), 4000000000L); | |
| EXPECT_EQ(parsed_msg.repeated_uint64(1), 5000000000L); | |
| ASSERT_EQ(parsed_msg.repeated_bool_size(), 2); | |
| EXPECT_EQ(parsed_msg.repeated_bool(0), true); | |
| EXPECT_EQ(parsed_msg.repeated_bool(1), false); | |
| ASSERT_EQ(parsed_msg.repeated_float_size(), 2); | |
| EXPECT_FLOAT_EQ(parsed_msg.repeated_float(0), 1.5f); | |
| EXPECT_FLOAT_EQ(parsed_msg.repeated_float(1), 2.5f); | |
| ASSERT_EQ(parsed_msg.repeated_double_size(), 2); | |
| EXPECT_DOUBLE_EQ(parsed_msg.repeated_double(0), 3.5); | |
| EXPECT_DOUBLE_EQ(parsed_msg.repeated_double(1), 4.5); | |
| EXPECT_EQ(parsed_msg.read_only_field(), ""); | |
| EXPECT_EQ(parsed_msg.write_only_field(), ""); | |
| EXPECT_EQ(parsed_msg.skip_field(), ""); | |
| EXPECT_EQ(parsed_msg.external_field().external_value(), "ext_val"); | |
| } | |
| TEST(CborGeneratorTest, RequiredFieldMissing) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| RequiredMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| EXPECT_THAT(status.code(), Eq(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(CborGeneratorTest, RequiredFieldPresent) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| map->add("RequiredField", cppbor::Tstr("value")); | |
| RequiredMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| EXPECT_EQ(msg.required_field(), "value"); | |
| } | |
| TEST(CborGeneratorTest, RequiredRepeatedFieldEmpty) { | |
| RequiredRepeatedMessage msg; | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| ASSERT_NE(map, nullptr); | |
| cbor::MapView map_view(map.get()); | |
| auto array_view = map_view.GetOptionalArray("RequiredRepeatedField"); | |
| ASSERT_TRUE(array_view.has_value()); | |
| EXPECT_EQ(array_view->size(), 0); | |
| } | |
| TEST(CborGeneratorTest, RequiredDisallowEmptyPresent) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| auto array = std::make_unique<cppbor::Array>(); | |
| array->add(cppbor::Tstr("item")); | |
| map->add("RequiredNonEmptyList", std::move(array)); | |
| RequiredDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| ASSERT_EQ(msg.required_non_empty_list_size(), 1); | |
| EXPECT_EQ(msg.required_non_empty_list(0), "item"); | |
| } | |
| TEST(CborGeneratorTest, RequiredDisallowEmptyEmpty) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| auto array = std::make_unique<cppbor::Array>(); | |
| map->add("RequiredNonEmptyList", std::move(array)); | |
| RequiredDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| EXPECT_THAT(status.code(), Eq(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(CborGeneratorTest, RequiredDisallowEmptyMissing) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| RequiredDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| EXPECT_THAT(status.code(), Eq(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(CborGeneratorTest, OptionalDisallowEmptyPresent) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| auto array = std::make_unique<cppbor::Array>(); | |
| array->add(cppbor::Tstr("item")); | |
| map->add("OptionalNonEmptyList", std::move(array)); | |
| OptionalDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| ASSERT_EQ(msg.optional_non_empty_list_size(), 1); | |
| EXPECT_EQ(msg.optional_non_empty_list(0), "item"); | |
| } | |
| TEST(CborGeneratorTest, OptionalDisallowEmptyEmpty) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| auto array = std::make_unique<cppbor::Array>(); | |
| map->add("OptionalNonEmptyList", std::move(array)); | |
| OptionalDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| EXPECT_THAT(status.code(), Eq(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(CborGeneratorTest, OptionalDisallowEmptyMissing) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| OptionalDisallowEmptyMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| // Proto2 does not distinguish between empty and missing, so this is 0. | |
| EXPECT_EQ(msg.optional_non_empty_list_size(), 0); | |
| } | |
| TEST(CborGeneratorTest, TypeOverrideTextString) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| map->add("TextAsBytes", cppbor::Tstr("text_value")); | |
| TypeOverrideMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| EXPECT_EQ(msg.text_as_bytes(), "text_value"); | |
| } | |
| TEST(CborGeneratorTest, TypeOverrideByteString) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| map->add("BytesAsText", cppbor::Bstr("byte_value")); | |
| TypeOverrideMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| EXPECT_EQ(msg.bytes_as_text(), "byte_value"); | |
| } | |
| TEST(CborGeneratorTest, TypeOverrideUint) { | |
| auto map = std::make_unique<cppbor::Map>(); | |
| map->add("UintAsInt", cppbor::Uint(42)); | |
| TypeOverrideMessage msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &msg); | |
| ABSL_EXPECT_OK(status); | |
| EXPECT_EQ(msg.uint_as_int(), 42); | |
| } | |
| TEST(CborGeneratorTest, SkipValidationMissingRequired) { | |
| RequiredMessage msg; | |
| cbor::FromProtoOptions options; | |
| options.skip_validation = true; | |
| auto map_or = cbor::FromProto(msg, options); | |
| ABSL_EXPECT_OK(map_or); | |
| } | |
| TEST(CborGeneratorTest, SkipValidationEmptyRequiredDisallowEmpty) { | |
| RequiredDisallowEmptyMessage msg; | |
| cbor::FromProtoOptions options; | |
| options.skip_validation = true; | |
| auto map_or = cbor::FromProto(msg, options); | |
| ABSL_EXPECT_OK(map_or); | |
| } | |
| TEST(CborGeneratorTest, SkipValidationEmptyOptionalDisallowEmpty) { | |
| // Proto2 does not distinguish between empty and missing, so an empty list in | |
| // the proto should be considered "not present" when converting to cbor. | |
| OptionalDisallowEmptyMessage msg; | |
| cbor::FromProtoOptions options; | |
| options.skip_validation = true; | |
| auto map_or = cbor::FromProto(msg, options); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| cbor::MapView map_view(map.get()); | |
| EXPECT_FALSE(map_view.GetOptionalArray("OptionalNonEmptyList").has_value()); | |
| } | |
| TEST(CborGeneratorTest, FromProtoMissingRequired) { | |
| RequiredMessage msg; | |
| auto map_or = cbor::FromProto(msg); | |
| EXPECT_FALSE(map_or.ok()); | |
| EXPECT_EQ(map_or.status().code(), absl::StatusCode::kInvalidArgument); | |
| } | |
| TEST(CborGeneratorTest, FromProtoEmptyRequiredDisallowEmpty) { | |
| RequiredDisallowEmptyMessage msg; | |
| auto map_or = cbor::FromProto(msg); | |
| EXPECT_FALSE(map_or.ok()); | |
| EXPECT_EQ(map_or.status().code(), absl::StatusCode::kInvalidArgument); | |
| } | |
| TEST(CborGeneratorTest, FromProtoEmptyOptionalDisallowEmpty) { | |
| OptionalDisallowEmptyMessage msg; | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| cbor::MapView map_view(map.get()); | |
| EXPECT_FALSE(map_view.GetOptionalArray("OptionalNonEmptyList").has_value()); | |
| } | |
| TEST(CborGeneratorTest, FromProtoNonEmptyOptionalDisallowEmpty) { | |
| OptionalDisallowEmptyMessage msg; | |
| msg.add_optional_non_empty_list("item"); | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| cbor::MapView map_view(map.get()); | |
| auto list_opt = map_view.GetOptionalArray("OptionalNonEmptyList"); | |
| ASSERT_TRUE(list_opt.has_value()); | |
| EXPECT_EQ(list_opt->size(), 1); | |
| auto first_item_or = list_opt->GetString(0); | |
| ABSL_ASSERT_OK(first_item_or); | |
| EXPECT_EQ(first_item_or.value(), "item"); | |
| } | |
| TEST(CborGeneratorTest, ImportedPublicRoundTrip) { | |
| ImportedPublicMessage msg; | |
| msg.set_public_value("imported_val"); | |
| auto map_or = cbor::FromProto(msg); | |
| ABSL_ASSERT_OK(map_or); | |
| auto map = std::move(map_or.value()); | |
| ASSERT_NE(map, nullptr); | |
| ImportedPublicMessage parsed_msg; | |
| cbor::MapView map_view(map.get()); | |
| auto status = cbor::ToProto(map_view, &parsed_msg); | |
| ABSL_ASSERT_OK(status); | |
| EXPECT_EQ(parsed_msg.public_value(), "imported_val"); | |
| } | |
| } // namespace | |
| } // namespace cbor_test |