| // 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/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, 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 |