blob: 6d2fd031d798b3a1fdb1fdd8398f9fc9e319e92d [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 google.protobuf.Struct fields: a Struct holding
// every kind of Value, an empty ListValue member, and Structs nested 25 deep
// (must be accepted) and 200 deep (must be rejected). This replaces the
// legacy BinaryAndJsonConformanceSuiteImpl<M>::RunJsonTestsForStruct(), which
// ran for the proto3-style message types only; the test names and the
// requests sent to the testee are identical to the legacy ones.
//
// Like the legacy RunValidJsonTest(), each valid input is sent twice: once to
// be serialized as binary ("<name>.ProtobufOutput") and once as JSON
// ("<name>.JsonOutput"). ExpectParseFailureForJson() asked for JSON output
// under a name without an output suffix.
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "conformance/binary_test_util.h"
#include "conformance/json_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 JsonStructTest = MessageTypeConformanceTest;
TEST_P(JsonStructTest, Struct) {
constexpr absl::string_view kInput = R"json({
"optionalStruct": {
"nullValue": null,
"intValue": 1234,
"boolValue": true,
"doubleValue": 1234.5678,
"stringValue": "Hello world!",
"listValue": [1234, "5678"],
"objectValue": {
"value": 0
}
}
})json";
constexpr absl::string_view kExpected = R"pb(
optional_struct: {
fields: {
key: "nullValue"
value: { null_value: NULL_VALUE }
}
fields: {
key: "intValue"
value: { number_value: 1234 }
}
fields: {
key: "boolValue"
value: { bool_value: true }
}
fields: {
key: "doubleValue"
value: { number_value: 1234.5678 }
}
fields: {
key: "stringValue"
value: { string_value: "Hello world!" }
}
fields: {
key: "listValue"
value: {
list_value: {
values: { number_value: 1234 }
values: { string_value: "5678" }
}
}
}
fields: {
key: "objectValue"
value: {
struct_value: {
fields: {
key: "value"
value: { number_value: 0 }
}
}
}
}
}
)pb";
EXPECT_THAT(Testee("Struct").ParseJson(message(), kInput).SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(Testee("Struct").ParseJson(message(), kInput).SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonStructTest, StructWithEmptyListValue) {
constexpr absl::string_view kInput = R"json({
"optionalStruct": {
"listValue": []
}
})json";
constexpr absl::string_view kExpected = R"pb(
optional_struct: {
fields: {
key: "listValue"
value: { list_value: {} }
}
}
)pb";
EXPECT_THAT(Testee("StructWithEmptyListValue")
.ParseJson(message(), kInput)
.SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
EXPECT_THAT(Testee("StructWithEmptyListValue")
.ParseJson(message(), kInput)
.SerializeJson(),
Yields(WhenParsed(EqualsTextProto(kExpected))));
}
TEST_P(JsonStructTest, StructDeepNesting25) {
const std::string input =
absl::StrCat(R"({"optionalStruct": {)", Repeat(R"("n": {)", 25),
R"("value": 1)", std::string(25, '}'), "}}");
const std::string expected =
absl::StrCat("optional_struct: {\n",
Repeat(" fields: {\n"
" key: \"n\"\n"
" value: {\n"
" struct_value: {\n",
25),
" fields: {\n"
" key: \"value\"\n"
" value: { number_value: 1 }\n"
" }\n",
Repeat(" }\n"
" }\n"
" }\n",
25),
"}\n");
EXPECT_THAT(Testee(kP1, "StructDeepNesting25")
.ParseJson(message(), input)
.SerializeBinary(),
Yields(WhenParsed(EqualsTextProto(expected))));
EXPECT_THAT(Testee(kP1, "StructDeepNesting25")
.ParseJson(message(), input)
.SerializeJson(),
Yields(WhenParsed(EqualsTextProto(expected))));
}
TEST_P(JsonStructTest, StructDeepNesting200) {
const std::string input =
absl::StrCat(R"({"optionalStruct": {)", Repeat(R"("n": {)", 200),
R"("value": 1)", std::string(200, '}'), "}}");
EXPECT_THAT(Testee(kP1, "StructDeepNesting200")
.ParseJson(message(), input)
.ParseOnly(),
Yields(IsParseError()));
}
INSTANTIATE_TEST_SUITE_P(All, JsonStructTest,
ValuesIn(Proto3TestMessageTypes()),
MessageTypeParamName);
} // namespace
} // namespace conformance
} // namespace protobuf
} // namespace google