| // 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 "cbor/parse.h" | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "cbor/cbor.h" | |
| #include "cppbor/cppbor.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "testing/cbor_utils.h" | |
| namespace { | |
| using ::absl_testing::IsOkAndHolds; | |
| using ::absl_testing::StatusIs; | |
| using ::cbor::FromJson; | |
| using ::testing::Eq; | |
| using ::testing::Gt; | |
| using ::testing::HasSubstr; | |
| using ::testing::IsFalse; | |
| using ::testing::IsTrue; | |
| TEST(ParseTest, Okay) { | |
| auto cbor = FromJson(R"json({ | |
| "str": "Google", | |
| "uint": 0, | |
| "int": -1, | |
| "nest_map": {"a": 1}, | |
| "empty_map": {}, | |
| "uint_array": [1, 2, 3], | |
| "int_array": [-1, -2, -3], | |
| "str_array": ["a", "b", "c"], | |
| "array_of_map": [{"a": 1}, {"a": 2}], | |
| "array_of_array": [[1, 2], [3, 4]], | |
| "empty_array": [], | |
| "null_array": [null] | |
| })json"); | |
| auto value1 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value1.ok()); | |
| auto result = std::move(value1).value(); | |
| auto value2 = result->AsMap(); | |
| ASSERT_TRUE(value2.ok()); | |
| auto map = std::move(value2).value(); | |
| EXPECT_THAT(map.GetString("str"), IsOkAndHolds("Google")); | |
| EXPECT_THAT(map.GetUint64("uint"), IsOkAndHolds(0)); | |
| EXPECT_THAT(map.GetInt64("int"), IsOkAndHolds(-1)); | |
| auto value3 = map.GetMap("nest_map"); | |
| ASSERT_TRUE(value3.ok()); | |
| auto nest_map = std::move(value3).value(); | |
| EXPECT_THAT(nest_map.GetInt64("a"), IsOkAndHolds(1)); | |
| auto value4 = map.GetMap("empty_map"); | |
| ASSERT_TRUE(value4.ok()); | |
| auto empty_map = std::move(value4).value(); | |
| EXPECT_THAT(empty_map.GetInt64("a"), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| auto value5 = map.GetArray("uint_array"); | |
| ASSERT_TRUE(value5.ok()); | |
| auto uint_array = std::move(value5).value(); | |
| EXPECT_THAT(uint_array.size(), Eq(3)); | |
| for (int i = 0; i < 3; ++i) { | |
| EXPECT_THAT(uint_array.GetUint64(i), IsOkAndHolds(i + 1)); | |
| } | |
| auto value6 = map.GetArray("int_array"); | |
| ASSERT_TRUE(value6.ok()); | |
| auto int_array = std::move(value6).value(); | |
| EXPECT_THAT(int_array.size(), Eq(3)); | |
| for (int i = 0; i < 3; ++i) { | |
| EXPECT_THAT(int_array.GetInt64(i), IsOkAndHolds(-1 - i)); | |
| } | |
| auto value7 = map.GetArray("str_array"); | |
| ASSERT_TRUE(value7.ok()); | |
| auto str_array = std::move(value7).value(); | |
| EXPECT_THAT(str_array.size(), Eq(3)); | |
| EXPECT_THAT(str_array.empty(), IsFalse()); | |
| for (int i = 0; i < 3; ++i) { | |
| EXPECT_THAT(str_array.GetString(i), IsOkAndHolds(std::string(1, 'a' + i))); | |
| } | |
| auto value8 = map.GetArray("array_of_map"); | |
| ASSERT_TRUE(value8.ok()); | |
| auto array_of_map = std::move(value8).value(); | |
| EXPECT_THAT(array_of_map.size(), Eq(2)); | |
| auto value9 = array_of_map.GetMap(0); | |
| ASSERT_TRUE(value9.ok()); | |
| auto array_of_map_0 = std::move(value9).value(); | |
| EXPECT_THAT(array_of_map_0.GetInt64("a"), IsOkAndHolds(1)); | |
| auto value10 = array_of_map.GetMap(1); | |
| ASSERT_TRUE(value10.ok()); | |
| auto array_of_map_1 = std::move(value10).value(); | |
| EXPECT_THAT(array_of_map_1.GetInt64("a"), IsOkAndHolds(2)); | |
| auto value11 = map.GetArray("array_of_array"); | |
| ASSERT_TRUE(value11.ok()); | |
| auto array_of_array = std::move(value11).value(); | |
| EXPECT_THAT(array_of_array.size(), Eq(2)); | |
| auto value12 = array_of_array.GetArray(0); | |
| ASSERT_TRUE(value12.ok()); | |
| auto array_of_array_0 = std::move(value12).value(); | |
| EXPECT_THAT(array_of_array_0.size(), Eq(2)); | |
| EXPECT_THAT(array_of_array_0.GetUint64(0), IsOkAndHolds(1)); | |
| EXPECT_THAT(array_of_array_0.GetUint64(1), IsOkAndHolds(2)); | |
| auto value13 = array_of_array.GetArray(1); | |
| ASSERT_TRUE(value13.ok()); | |
| auto array_of_array_1 = std::move(value13).value(); | |
| EXPECT_THAT(array_of_array_1.size(), Eq(2)); | |
| EXPECT_THAT(array_of_array_1.GetUint64(0), IsOkAndHolds(3)); | |
| EXPECT_THAT(array_of_array_1.GetUint64(1), IsOkAndHolds(4)); | |
| auto value14 = map.GetArray("empty_array"); | |
| ASSERT_TRUE(value14.ok()); | |
| auto empty_array = std::move(value14).value(); | |
| EXPECT_THAT(empty_array.size(), Eq(0)); | |
| EXPECT_THAT(empty_array.empty(), IsTrue()); | |
| auto value15 = map.GetArray("null_array"); | |
| ASSERT_TRUE(value15.ok()); | |
| auto null_array = std::move(value15).value(); | |
| EXPECT_THAT(null_array.size(), Eq(1)); | |
| EXPECT_THAT(null_array.IsNull(0), IsOkAndHolds(IsTrue())); | |
| EXPECT_THAT(str_array.IsNull(0), IsOkAndHolds(IsFalse())); | |
| } | |
| TEST(ParseTest, SemanticTag) { | |
| cppbor::Map orig_map; | |
| orig_map.add("when", | |
| cppbor::SemanticTag(0, cppbor::Tstr("2026-05-19T00:00:00Z"))); | |
| orig_map.add("epoch", cppbor::SemanticTag(1, cppbor::Uint(1684454400))); | |
| auto bytes = orig_map.encode(); | |
| absl::string_view bytes_view(reinterpret_cast<const char*>(bytes.data()), | |
| bytes.size()); | |
| auto value = cbor::Parse(bytes_view); | |
| ASSERT_TRUE(value.ok()); | |
| auto result = std::move(value).value(); | |
| absl::StatusOr<cbor::MapView> map = result->AsMap(); | |
| ABSL_ASSERT_OK(map); | |
| EXPECT_THAT(map->GetString("when"), IsOkAndHolds("2026-05-19T00:00:00Z")); | |
| EXPECT_THAT(map->GetUint64("epoch"), IsOkAndHolds(1684454400)); | |
| } | |
| TEST(ParseTest, OptionalString) { | |
| auto cbor = FromJson(R"json({"str": "Google", "int": 0})json"); | |
| auto value16 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value16.ok()); | |
| auto result = std::move(value16).value(); | |
| auto value17 = result->AsMap(); | |
| ASSERT_TRUE(value17.ok()); | |
| auto map = std::move(value17).value(); | |
| EXPECT_THAT(map.GetOptionalString("str"), Eq("Google")); | |
| EXPECT_THAT(map.GetOptionalString("not_found"), Eq(std::nullopt)); | |
| EXPECT_THAT(map.GetOptionalString("int"), Eq(std::nullopt)); | |
| } | |
| TEST(ParseTest, OptionalMap) { | |
| auto cbor = FromJson( | |
| R"json({"not_map": "Google", "map": {"a": 1}, "empty_map": {}})json"); | |
| auto value18 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value18.ok()); | |
| auto result = std::move(value18).value(); | |
| auto value19 = result->AsMap(); | |
| ASSERT_TRUE(value19.ok()); | |
| auto map = std::move(value19).value(); | |
| EXPECT_THAT(map.GetOptionalMap("not_map"), Eq(std::nullopt)); | |
| EXPECT_THAT(map.GetOptionalMap("not_found"), Eq(std::nullopt)); | |
| EXPECT_TRUE(map.GetOptionalMap("map").has_value()); | |
| EXPECT_TRUE(map.GetOptionalMap("empty_map").has_value()); | |
| } | |
| TEST(ParseTest, OptionalArray) { | |
| auto cbor = FromJson( | |
| R"json({"not_array": "Google", "array": [1, 2], "empty_array": []})json"); | |
| auto value20 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value20.ok()); | |
| auto result = std::move(value20).value(); | |
| auto value21 = result->AsMap(); | |
| ASSERT_TRUE(value21.ok()); | |
| auto map = std::move(value21).value(); | |
| EXPECT_THAT(map.GetOptionalArray("not_array"), Eq(std::nullopt)); | |
| EXPECT_THAT(map.GetOptionalArray("not_found"), Eq(std::nullopt)); | |
| EXPECT_TRUE(map.GetOptionalArray("array").has_value()); | |
| EXPECT_TRUE(map.GetOptionalArray("empty_array").has_value()); | |
| } | |
| TEST(ParseTest, OptionalUint64) { | |
| auto cbor = FromJson(R"json({"not_uint": "Google", "uint": 1})json"); | |
| auto value22 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value22.ok()); | |
| auto result = std::move(value22).value(); | |
| auto value23 = result->AsMap(); | |
| ASSERT_TRUE(value23.ok()); | |
| auto map = std::move(value23).value(); | |
| EXPECT_THAT(map.GetOptionalUint64("not_uint"), Eq(std::nullopt)); | |
| EXPECT_THAT(map.GetOptionalUint64("not_found"), Eq(std::nullopt)); | |
| EXPECT_THAT(map.GetOptionalUint64("uint").value(), Eq(1)); | |
| } | |
| TEST(ParseTest, UintMapKeys) { | |
| // FromJson doesn't support creating integer-keyed maps -- would need | |
| // special support like the "b64'blah" bytestring hack because JSON doesn't | |
| // support integer map keys. So we'll create the map directly using cppbor. | |
| cppbor::Map orig_map; | |
| orig_map.add(cppbor::Uint(0), cppbor::Bstr("zero")); | |
| orig_map.add(cppbor::Uint(3), cppbor::Nint(-333)); | |
| orig_map.add(cppbor::Uint(10), | |
| cppbor::Array(cppbor::Tstr("a"), cppbor::Nint(-12))); | |
| auto bytes = orig_map.encode(); | |
| absl::string_view bytes_view(reinterpret_cast<char*>(bytes.data()), | |
| bytes.size()); | |
| auto value24 = cbor::Parse(bytes_view); | |
| ASSERT_TRUE(value24.ok()); | |
| auto result = std::move(value24).value(); | |
| auto value25 = result->AsMap(); | |
| ASSERT_TRUE(value25.ok()); | |
| auto map = std::move(value25).value(); | |
| EXPECT_THAT(map.GetByteString(0), IsOkAndHolds("zero")); | |
| EXPECT_THAT(map.GetInt64(3), IsOkAndHolds(-333)); | |
| auto value26 = map.GetArray(10); | |
| ASSERT_TRUE(value26.ok()); | |
| auto arr = std::move(value26).value(); | |
| EXPECT_THAT(arr.size(), Eq(2)); | |
| EXPECT_THAT(arr.GetString(0), IsOkAndHolds("a")); | |
| EXPECT_THAT(arr.GetInt64(1), IsOkAndHolds(-12)); | |
| } | |
| TEST(ParseTest, ErrorResultNotAMap) { | |
| auto cbor = FromJson(R"json([1, 2, 3])json"); | |
| auto value27 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value27.ok()); | |
| auto result = std::move(value27).value(); | |
| EXPECT_THAT(result->AsMap(), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR parsed result is not a map"))); | |
| } | |
| TEST(ParseTest, ErrorResultNotAnArray) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| auto value28 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value28.ok()); | |
| auto result = std::move(value28).value(); | |
| EXPECT_THAT(result->AsArray(), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR parsed result is not an array"))); | |
| } | |
| TEST(ParseTest, ErrorMapGetStringNotAString) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| auto value29 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value29.ok()); | |
| auto result = std::move(value29).value(); | |
| auto value30 = result->AsMap(); | |
| ASSERT_TRUE(value30.ok()); | |
| auto map = std::move(value30).value(); | |
| EXPECT_THAT(map.GetString("schema"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR item is not a string"))); | |
| } | |
| TEST(ParseTest, ErrorMapGetInt64NotAnInt) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| auto value31 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value31.ok()); | |
| auto result = std::move(value31).value(); | |
| auto value32 = result->AsMap(); | |
| ASSERT_TRUE(value32.ok()); | |
| auto map = std::move(value32).value(); | |
| EXPECT_THAT(map.GetInt64("vendor"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR item is not an int"))); | |
| } | |
| TEST(ParseTest, ErrorMapGetUint64NotAUint) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| auto value33 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value33.ok()); | |
| auto result = std::move(value33).value(); | |
| auto value34 = result->AsMap(); | |
| ASSERT_TRUE(value34.ok()); | |
| auto map = std::move(value34).value(); | |
| EXPECT_THAT(map.GetUint64("vendor"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR item is not an uint"))); | |
| } | |
| TEST(ParseTest, ErrorNotAUintInArray) { | |
| auto cbor = FromJson(R"json(["vendor", "Google"])json"); | |
| auto value35 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value35.ok()); | |
| auto result = std::move(value35).value(); | |
| auto value36 = result->AsArray(); | |
| ASSERT_TRUE(value36.ok()); | |
| auto array = std::move(value36).value(); | |
| EXPECT_THAT(array.GetUint64(0), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR item is not an uint"))); | |
| } | |
| TEST(ParseTest, ErrorNotAMapInArray) { | |
| auto cbor = FromJson(R"json([{"vendor": "Google"}, "schema"])json"); | |
| auto value37 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value37.ok()); | |
| auto result = std::move(value37).value(); | |
| auto value38 = result->AsArray(); | |
| ASSERT_TRUE(value38.ok()); | |
| auto array = std::move(value38).value(); | |
| EXPECT_THAT(array.GetMap(1), StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR item is not a map"))); | |
| } | |
| TEST(ParseTest, ErrorKeyNotFound) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| auto value39 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value39.ok()); | |
| auto result = std::move(value39).value(); | |
| auto value40 = result->AsMap(); | |
| ASSERT_TRUE(value40.ok()); | |
| auto map = std::move(value40).value(); | |
| EXPECT_THAT( | |
| map.GetString("not_found"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Key not found"))); | |
| } | |
| TEST(ParseTest, DuplicateKey) { | |
| std::string cbor; | |
| // This is a CBOR map {"color": "red", "color": "green"}. | |
| ASSERT_TRUE(absl::HexStringToBytes( | |
| "a265636f6c6f726372656465636f6c6f7265677265656e", &cbor)); | |
| auto value41 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value41.ok()); | |
| auto result = std::move(value41).value(); | |
| auto value42 = result->AsMap(); | |
| ASSERT_TRUE(value42.ok()); | |
| auto map = std::move(value42).value(); | |
| EXPECT_THAT(map.GetString("color"), "red"); | |
| } | |
| TEST(ParseTest, ErrorIndexOutOfRange) { | |
| auto cbor = FromJson(R"json([0, 1, 2])json"); | |
| auto value43 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value43.ok()); | |
| auto result = std::move(value43).value(); | |
| auto value44 = result->AsArray(); | |
| ASSERT_TRUE(value44.ok()); | |
| auto array = std::move(value44).value(); | |
| EXPECT_THAT(array.GetUint64(3), StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Index out of range"))); | |
| } | |
| TEST(ParseTest, ErrorFailedToParse) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| EXPECT_THAT(cbor::Parse(cbor.substr(0, 10)), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("CBOR parsing failed"))); | |
| } | |
| TEST(ParseTest, TrailingBytesIgnored) { | |
| auto cbor = FromJson(R"json({"vendor": "Google", "schema": 0})json"); | |
| absl::StatusOr<std::unique_ptr<cbor::ParseResult>> result = | |
| cbor::Parse(cbor + "trailing bytes"); | |
| ABSL_ASSERT_OK(result); | |
| absl::StatusOr<cbor::MapView> result_map = (*result)->AsMap(); | |
| ABSL_ASSERT_OK(result_map); | |
| EXPECT_THAT(result_map->GetString("vendor"), IsOkAndHolds("Google")); | |
| EXPECT_THAT(result_map->GetUint64("schema"), IsOkAndHolds(0)); | |
| } | |
| void CheckMap(const cbor::MapView& map, absl::string_view str_key, | |
| uint32_t int_key); | |
| void CheckArray(const cbor::ArrayView& array, absl::string_view str_key, | |
| uint32_t int_key) { | |
| if (array.empty()) { | |
| ASSERT_THAT(array.size(), Eq(0)); | |
| ASSERT_THAT(array.value_type(0), Eq(cbor::ItemView::Type::kUnknown)); | |
| } else { | |
| ASSERT_THAT(array.size(), Gt(0)); | |
| } | |
| for (auto i = 0; i < array.size(); ++i) { | |
| SCOPED_TRACE(absl::StrCat(i)); | |
| switch (array.value_type(i)) { | |
| case cbor::ItemView::Type::kUnknown: | |
| EXPECT_THAT(array.GetBool(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetFloat(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetDouble(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetString(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetByteString(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetInt64(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetUint64(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetMap(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| EXPECT_THAT(array.GetArray(i), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| break; | |
| case cbor::ItemView::Type::kBool: | |
| EXPECT_THAT(array.GetBool(i), StatusIs(absl::StatusCode::kOk)); | |
| break; | |
| case cbor::ItemView::Type::kFloat: | |
| EXPECT_THAT(array.GetFloat(i), StatusIs(absl::StatusCode::kOk)); | |
| break; | |
| case cbor::ItemView::Type::kDouble: | |
| EXPECT_THAT(array.GetDouble(i), StatusIs(absl::StatusCode::kOk)); | |
| break; | |
| case cbor::ItemView::Type::kString: { | |
| auto value45 = array.GetString(i); | |
| ASSERT_TRUE(value45.ok()); | |
| absl::string_view str = std::move(value45).value(); | |
| std::string force_copy(str); | |
| break; | |
| } | |
| case cbor::ItemView::Type::kByteString: { | |
| auto value46 = array.GetByteString(i); | |
| ASSERT_TRUE(value46.ok()); | |
| absl::string_view str = std::move(value46).value(); | |
| std::string force_copy(str); | |
| break; | |
| } | |
| case cbor::ItemView::Type::kInt64: | |
| EXPECT_THAT(array.GetInt64(i), StatusIs(absl::StatusCode::kOk)); | |
| break; | |
| case cbor::ItemView::Type::kUint64: | |
| EXPECT_THAT(array.GetUint64(i), StatusIs(absl::StatusCode::kOk)); | |
| break; | |
| case cbor::ItemView::Type::kMap: { | |
| auto value47 = array.GetMap(i); | |
| ASSERT_TRUE(value47.ok()); | |
| auto child = std::move(value47).value(); | |
| CheckMap(child, str_key, int_key); | |
| break; | |
| } | |
| case cbor::ItemView::Type::kArray: { | |
| auto value48 = array.GetArray(i); | |
| ASSERT_TRUE(value48.ok()); | |
| auto child = std::move(value48).value(); | |
| CheckArray(child, str_key, int_key); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| void CheckMap(const cbor::MapView& map, absl::string_view str_key, | |
| uint32_t int_key) { | |
| { | |
| // Bool: | |
| const auto status_bool = map.GetBool(str_key); | |
| const auto opt_bool = map.GetOptionalBool(str_key); | |
| ASSERT_THAT(status_bool.ok(), Eq(opt_bool.has_value())); | |
| if (status_bool.ok()) { | |
| EXPECT_THAT(*status_bool, Eq(*opt_bool)); | |
| } | |
| } | |
| { | |
| // String: | |
| const auto status_str = map.GetString(str_key); | |
| const auto opt_str = map.GetOptionalString(str_key); | |
| ASSERT_THAT(status_str.ok(), Eq(opt_str.has_value())); | |
| if (status_str.ok()) { | |
| EXPECT_THAT(*status_str, Eq(*opt_str)); | |
| } | |
| } | |
| } | |
| } // namespace |