| // 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; |
| |
| #ifndef ASSERT_OK_AND_ASSIGN |
| #define ASSERT_OK_AND_ASSIGN_CONCAT2(x, y) x##y |
| #define ASSERT_OK_AND_ASSIGN_CONCAT(x, y) ASSERT_OK_AND_ASSIGN_CONCAT2(x, y) |
| |
| #define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ |
| ASSERT_OK_AND_ASSIGN_IMPL(lhs, rexpr, __COUNTER__) |
| |
| #define ASSERT_OK_AND_ASSIGN_IMPL(lhs, rexpr, id) \ |
| auto ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id) = (rexpr); \ |
| ASSERT_THAT(ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id), \ |
| ::absl_testing::IsOk()); \ |
| lhs = std::move(*ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id)) |
| #endif |
| |
| 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(); |
| auto map_or = result->AsMap(); |
| ASSERT_TRUE(map_or.ok()); |
| auto map = std::move(map_or).value(); |
| 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"); |
| ASSERT_OK_AND_ASSIGN(auto result, cbor::Parse(cbor + "trailing bytes")); |
| ASSERT_OK_AND_ASSIGN(auto result_map, result->AsMap()); |
| 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 |