blob: f92eb7581c478a3133770198e37bea2a9f5e039a [file] [edit]
// Protocol Buffers - Google's data interchange format
// Copyright 2026 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// JSON conformance tests for map fields: keys are always JSON strings (so
// unquoted integer and bool keys are rejected and escapes in keys are
// honored) and message values are nested objects. This replaces the "Map
// fields" block of the legacy
// BinaryAndJsonConformanceSuiteImpl<M>::RunJsonTestsForNonRepeatedTypes(),
// for every test message type; the test names and the requests sent to the
// testee are identical to the legacy ones.
//
// Like the legacy RunValidJsonTest(), the valid input is sent twice: once to
// be serialized as binary ("<name>.ProtobufOutput") and once as JSON
// ("<name>.JsonOutput"). The legacy ExpectParseFailureForJson() asked for
// JSON output under a name without an output suffix.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/string_view.h"
#include "conformance/binary_test_util.h"
#include "conformance/matchers.h"
#include "conformance/message_type_fixtures.h"
#include "conformance/testee.h"
namespace google {
namespace protobuf {
namespace conformance {
namespace {
using ::testing::ValuesIn;
using JsonMapTest = MessageTypeConformanceTest;
// Map keys are always JSON strings, even for integer and bool key types.
TEST_P(JsonMapTest, Int32MapField) {
constexpr absl::string_view kInput =
R"json({"mapInt32Int32": {"1": 2, "3": 4}})json";
constexpr absl::string_view kExpected = R"pb(
map_int32_int32: { key: 1 value: 2 }
map_int32_int32: { key: 3 value: 4 }
)pb";
EXPECT_THAT(
Testee("Int32MapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Int32MapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, Int32MapFieldKeyNotQuoted) {
EXPECT_THAT(
Testee(kP1, "Int32MapFieldKeyNotQuoted")
.ParseJson(message(), R"json({"mapInt32Int32": {1: 2, 3: 4}})json")
.ParseOnly(),
Yields(IsParseError()));
}
TEST_P(JsonMapTest, Uint32MapField) {
constexpr absl::string_view kInput =
R"json({"mapUint32Uint32": {"1": 2, "3": 4}})json";
constexpr absl::string_view kExpected = R"pb(
map_uint32_uint32: { key: 1 value: 2 }
map_uint32_uint32: { key: 3 value: 4 }
)pb";
EXPECT_THAT(
Testee("Uint32MapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Uint32MapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, Uint32MapFieldKeyNotQuoted) {
EXPECT_THAT(
Testee(kP1, "Uint32MapFieldKeyNotQuoted")
.ParseJson(message(), R"json({"mapUint32Uint32": {1: 2, 3: 4}})json")
.ParseOnly(),
Yields(IsParseError()));
}
TEST_P(JsonMapTest, Int64MapField) {
constexpr absl::string_view kInput =
R"json({"mapInt64Int64": {"1": 2, "3": 4}})json";
constexpr absl::string_view kExpected = R"pb(
map_int64_int64: { key: 1 value: 2 }
map_int64_int64: { key: 3 value: 4 }
)pb";
EXPECT_THAT(
Testee("Int64MapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Int64MapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, Int64MapFieldKeyNotQuoted) {
EXPECT_THAT(
Testee(kP1, "Int64MapFieldKeyNotQuoted")
.ParseJson(message(), R"json({"mapInt64Int64": {1: 2, 3: 4}})json")
.ParseOnly(),
Yields(IsParseError()));
}
TEST_P(JsonMapTest, Uint64MapField) {
constexpr absl::string_view kInput =
R"json({"mapUint64Uint64": {"1": 2, "3": 4}})json";
constexpr absl::string_view kExpected = R"pb(
map_uint64_uint64: { key: 1 value: 2 }
map_uint64_uint64: { key: 3 value: 4 }
)pb";
EXPECT_THAT(
Testee("Uint64MapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Uint64MapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, Uint64MapFieldKeyNotQuoted) {
EXPECT_THAT(
Testee(kP1, "Uint64MapFieldKeyNotQuoted")
.ParseJson(message(), R"json({"mapUint64Uint64": {1: 2, 3: 4}})json")
.ParseOnly(),
Yields(IsParseError()));
}
TEST_P(JsonMapTest, BoolMapField) {
constexpr absl::string_view kInput =
R"json({"mapBoolBool": {"true": true, "false": false}})json";
constexpr absl::string_view kExpected = R"pb(
map_bool_bool: { key: true value: true }
map_bool_bool: { key: false value: false }
)pb";
EXPECT_THAT(
Testee("BoolMapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("BoolMapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, BoolMapFieldKeyNotQuoted) {
EXPECT_THAT(
Testee(kP1, "BoolMapFieldKeyNotQuoted")
.ParseJson(message(),
R"json({"mapBoolBool": {true: true, false: false}})json")
.ParseOnly(),
Yields(IsParseError()));
}
TEST_P(JsonMapTest, MessageMapField) {
constexpr absl::string_view kInput = R"json({
"mapStringNestedMessage": {
"hello": {"a": 1234},
"world": {"a": 5678}
}
})json";
constexpr absl::string_view kExpected = R"pb(
map_string_nested_message: {
key: "hello"
value: { a: 1234 }
}
map_string_nested_message: {
key: "world"
value: { a: 5678 }
}
)pb";
EXPECT_THAT(
Testee("MessageMapField").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("MessageMapField").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
// Since Map keys are represented as JSON strings, escaping should be allowed.
TEST_P(JsonMapTest, Int32MapEscapedKey) {
constexpr absl::string_view kInput =
R"json({"mapInt32Int32": {"\u0031": 2}})json";
constexpr absl::string_view kExpected =
R"pb(map_int32_int32: { key: 1 value: 2 })pb";
EXPECT_THAT(Testee("Int32MapEscapedKey")
.ParseJson(message(), kInput)
.SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Int32MapEscapedKey").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, Int64MapEscapedKey) {
constexpr absl::string_view kInput =
R"json({"mapInt64Int64": {"\u0031": 2}})json";
constexpr absl::string_view kExpected =
R"pb(map_int64_int64: { key: 1 value: 2 })pb";
EXPECT_THAT(Testee("Int64MapEscapedKey")
.ParseJson(message(), kInput)
.SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("Int64MapEscapedKey").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonMapTest, BoolMapEscapedKey) {
constexpr absl::string_view kInput =
R"json({"mapBoolBool": {"tr\u0075e": true}})json";
constexpr absl::string_view kExpected =
R"pb(map_bool_bool: { key: true value: true })pb";
EXPECT_THAT(Testee("BoolMapEscapedKey")
.ParseJson(message(), kInput)
.SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(
Testee("BoolMapEscapedKey").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
INSTANTIATE_TEST_SUITE_P(All, JsonMapTest, ValuesIn(AllTestMessageTypes()),
MessageTypeParamName);
} // namespace
} // namespace conformance
} // namespace protobuf
} // namespace google