blob: a8b68a3c76e03873730d6d4a7ae5c18784dbff75 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/util/json_util.h>
#include <algorithm>
#include <cstdint>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/status.h>
#include <google/protobuf/stubs/statusor.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/descriptor_database.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/util/internal/testdata/maps.pb.h>
#include <google/protobuf/util/json_format.pb.h>
#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/util/type_resolver_util.h>
#include <google/protobuf/stubs/status_macros.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace util {
namespace {
using ::proto3::TestAny;
using ::proto3::TestEnumValue;
using ::proto3::TestMap;
using ::proto3::TestMessage;
using ::proto3::TestOneof;
using ::proto_util_converter::testing::MapIn;
// TODO(b/234474291): Use the gtest versions once that's available in OSS.
MATCHER_P(IsOkAndHolds, inner,
StrCat("is OK and holds ", testing::PrintToString(inner))) {
if (!arg.ok()) {
*result_listener << arg.status();
return false;
}
return testing::ExplainMatchResult(inner, *arg, result_listener);
}
util::Status GetStatus(const util::Status& s) { return s; }
template <typename T>
util::Status GetStatus(const util::StatusOr<T>& s) {
return s.status();
}
MATCHER_P(StatusIs, status,
StrCat(".status() is ", testing::PrintToString(status))) {
return GetStatus(arg).code() == status;
}
#define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(util::StatusCode::kOk))
#define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(util::StatusCode::kOk))
// As functions defined in json_util.h are just thin wrappers around the
// JSON conversion code in //net/proto2/util/converter, in this test we
// only cover some very basic cases to make sure the wrappers have forwarded
// parameters to the underlying implementation correctly. More detailed
// tests are contained in the //net/proto2/util/converter directory.
util::StatusOr<std::string> ToJson(const Message& message,
const JsonPrintOptions& options = {}) {
std::string result;
RETURN_IF_ERROR(MessageToJsonString(message, &result, options));
return result;
}
util::Status FromJson(StringPiece json, Message* message,
const JsonParseOptions& options = {}) {
return JsonStringToMessage(json, message, options);
}
TEST(JsonUtilTest, TestWhitespaces) {
TestMessage m;
m.mutable_message_value();
EXPECT_THAT(ToJson(m), IsOkAndHolds("{\"messageValue\":{}}"));
JsonPrintOptions options;
options.add_whitespace = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\n"
" \"messageValue\": {}\n"
"}\n"));
}
TEST(JsonUtilTest, TestDefaultValues) {
TestMessage m;
EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));
JsonPrintOptions options;
options.always_print_primitive_fields = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"boolValue\":false,"
"\"int32Value\":0,"
"\"int64Value\":\"0\","
"\"uint32Value\":0,"
"\"uint64Value\":\"0\","
"\"floatValue\":0,"
"\"doubleValue\":0,"
"\"stringValue\":\"\","
"\"bytesValue\":\"\","
"\"enumValue\":\"FOO\","
"\"repeatedBoolValue\":[],"
"\"repeatedInt32Value\":[],"
"\"repeatedInt64Value\":[],"
"\"repeatedUint32Value\":[],"
"\"repeatedUint64Value\":[],"
"\"repeatedFloatValue\":[],"
"\"repeatedDoubleValue\":[],"
"\"repeatedStringValue\":[],"
"\"repeatedBytesValue\":[],"
"\"repeatedEnumValue\":[],"
"\"repeatedMessageValue\":[]"
"}"));
options.always_print_primitive_fields = true;
m.set_string_value("i am a test string value");
m.set_bytes_value("i am a test bytes value");
EXPECT_THAT(
ToJson(m, options),
IsOkAndHolds("{\"boolValue\":false,"
"\"int32Value\":0,"
"\"int64Value\":\"0\","
"\"uint32Value\":0,"
"\"uint64Value\":\"0\","
"\"floatValue\":0,"
"\"doubleValue\":0,"
"\"stringValue\":\"i am a test string value\","
"\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\","
"\"enumValue\":\"FOO\","
"\"repeatedBoolValue\":[],"
"\"repeatedInt32Value\":[],"
"\"repeatedInt64Value\":[],"
"\"repeatedUint32Value\":[],"
"\"repeatedUint64Value\":[],"
"\"repeatedFloatValue\":[],"
"\"repeatedDoubleValue\":[],"
"\"repeatedStringValue\":[],"
"\"repeatedBytesValue\":[],"
"\"repeatedEnumValue\":[],"
"\"repeatedMessageValue\":[]"
"}"));
options.preserve_proto_field_names = true;
m.set_string_value("i am a test string value");
m.set_bytes_value("i am a test bytes value");
EXPECT_THAT(
ToJson(m, options),
IsOkAndHolds("{\"bool_value\":false,"
"\"int32_value\":0,"
"\"int64_value\":\"0\","
"\"uint32_value\":0,"
"\"uint64_value\":\"0\","
"\"float_value\":0,"
"\"double_value\":0,"
"\"string_value\":\"i am a test string value\","
"\"bytes_value\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\","
"\"enum_value\":\"FOO\","
"\"repeated_bool_value\":[],"
"\"repeated_int32_value\":[],"
"\"repeated_int64_value\":[],"
"\"repeated_uint32_value\":[],"
"\"repeated_uint64_value\":[],"
"\"repeated_float_value\":[],"
"\"repeated_double_value\":[],"
"\"repeated_string_value\":[],"
"\"repeated_bytes_value\":[],"
"\"repeated_enum_value\":[],"
"\"repeated_message_value\":[]"
"}"));
}
TEST(JsonUtilTest, TestPreserveProtoFieldNames) {
TestMessage m;
m.mutable_message_value();
JsonPrintOptions options;
options.preserve_proto_field_names = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"message_value\":{}}"));
}
TEST(JsonUtilTest, TestAlwaysPrintEnumsAsInts) {
TestMessage orig;
orig.set_enum_value(proto3::BAR);
orig.add_repeated_enum_value(proto3::FOO);
orig.add_repeated_enum_value(proto3::BAR);
JsonPrintOptions print_options;
print_options.always_print_enums_as_ints = true;
auto printed = ToJson(orig, print_options);
ASSERT_THAT(printed,
IsOkAndHolds("{\"enumValue\":1,\"repeatedEnumValue\":[0,1]}"));
TestMessage parsed;
ASSERT_OK(FromJson(*printed, &parsed));
EXPECT_EQ(parsed.enum_value(), proto3::BAR);
EXPECT_EQ(parsed.repeated_enum_value_size(), 2);
EXPECT_EQ(parsed.repeated_enum_value(0), proto3::FOO);
EXPECT_EQ(parsed.repeated_enum_value(1), proto3::BAR);
}
TEST(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) {
TestEnumValue orig;
// orig.set_enum_value1(proto3::FOO)
orig.set_enum_value2(proto3::FOO);
orig.set_enum_value3(proto3::BAR);
JsonPrintOptions print_options;
print_options.always_print_enums_as_ints = true;
print_options.always_print_primitive_fields = true;
auto printed = ToJson(orig, print_options);
ASSERT_THAT(
printed,
IsOkAndHolds("{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"));
TestEnumValue parsed;
ASSERT_OK(FromJson(*printed, &parsed));
EXPECT_EQ(parsed.enum_value1(), proto3::FOO);
EXPECT_EQ(parsed.enum_value2(), proto3::FOO);
EXPECT_EQ(parsed.enum_value3(), proto3::BAR);
}
TEST(JsonUtilTest, TestPrintProto2EnumAsIntWithDefaultValue) {
protobuf_unittest::TestDefaultEnumValue orig;
JsonPrintOptions print_options;
// use enum as int
print_options.always_print_enums_as_ints = true;
print_options.always_print_primitive_fields = true;
// result should be int rather than string
auto printed = ToJson(orig, print_options);
ASSERT_THAT(printed, IsOkAndHolds("{\"enumValue\":2}"));
protobuf_unittest::TestDefaultEnumValue parsed;
ASSERT_OK(FromJson(*printed, &parsed));
EXPECT_EQ(parsed.enum_value(), protobuf_unittest::DEFAULT);
}
TEST(JsonUtilTest, ParseMessage) {
// Some random message but good enough to verify that the parsing wrapper
// functions are working properly.
TestMessage m;
ASSERT_OK(FromJson(R"json(
{
"int32Value": 1234567891,
"int64Value": 5302428716536692736,
"floatValue": 3.4028235e+38,
"repeatedInt32Value": [1, 2],
"messageValue": {
"value": 2048
},
"repeatedMessageValue": [
{"value": 40},
{"value": 96}
]
}
)json",
&m));
EXPECT_EQ(m.int32_value(), 1234567891);
EXPECT_EQ(m.int64_value(), 5302428716536692736);
EXPECT_EQ(m.float_value(), 3.402823466e+38f);
ASSERT_EQ(m.repeated_int32_value_size(), 2);
EXPECT_EQ(m.repeated_int32_value(0), 1);
EXPECT_EQ(m.repeated_int32_value(1), 2);
EXPECT_EQ(m.message_value().value(), 2048);
ASSERT_EQ(m.repeated_message_value_size(), 2);
EXPECT_EQ(m.repeated_message_value(0).value(), 40);
EXPECT_EQ(m.repeated_message_value(1).value(), 96);
}
TEST(JsonUtilTest, ParseMap) {
TestMap message;
(*message.mutable_string_map())["hello"] = 1234;
auto printed = ToJson(message);
ASSERT_THAT(printed, IsOkAndHolds("{\"stringMap\":{\"hello\":1234}}"));
TestMap other;
ASSERT_OK(FromJson(*printed, &other));
EXPECT_EQ(other.DebugString(), message.DebugString());
}
TEST(JsonUtilTest, ParsePrimitiveMapIn) {
MapIn message;
JsonPrintOptions print_options;
print_options.always_print_primitive_fields = true;
auto printed = ToJson(message, print_options);
ASSERT_THAT(
ToJson(message, print_options),
IsOkAndHolds(
"{\"other\":\"\",\"things\":[],\"mapInput\":{},\"mapAny\":{}}"));
MapIn other;
ASSERT_OK(FromJson(*printed, &other));
EXPECT_EQ(other.DebugString(), message.DebugString());
}
TEST(JsonUtilTest, PrintPrimitiveOneof) {
TestOneof message;
JsonPrintOptions options;
options.always_print_primitive_fields = true;
message.mutable_oneof_message_value();
EXPECT_THAT(ToJson(message, options),
IsOkAndHolds("{\"oneofMessageValue\":{\"value\":0}}"));
message.set_oneof_int32_value(1);
EXPECT_THAT(ToJson(message, options),
IsOkAndHolds("{\"oneofInt32Value\":1}"));
}
TEST(JsonUtilTest, TestParseIgnoreUnknownFields) {
TestMessage m;
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_OK(FromJson("{\"unknownName\":0}", &m, options));
}
TEST(JsonUtilTest, TestParseErrors) {
TestMessage m;
// Parsing should fail if the field name can not be recognized.
EXPECT_THAT(FromJson(R"json({"unknownName": 0})json", &m),
StatusIs(util::StatusCode::kInvalidArgument));
// Parsing should fail if the value is invalid.
EXPECT_THAT(FromJson(R"json("{"int32Value": 2147483648})json", &m),
StatusIs(util::StatusCode::kInvalidArgument));
}
TEST(JsonUtilTest, TestDynamicMessage) {
// Create a new DescriptorPool with the same protos as the generated one.
DescriptorPoolDatabase database(*DescriptorPool::generated_pool());
DescriptorPool pool(&database);
// A dynamic version of the test proto.
DynamicMessageFactory factory;
std::unique_ptr<Message> message(
factory.GetPrototype(pool.FindMessageTypeByName("proto3.TestMessage"))
->New());
EXPECT_OK(FromJson(R"json(
{
"int32Value": 1024,
"repeatedInt32Value": [1, 2],
"messageValue": {
"value": 2048
},
"repeatedMessageValue": [
{"value": 40},
{"value": 96}
]
}
)json",
message.get()));
// Convert to generated message for easy inspection.
TestMessage generated;
EXPECT_TRUE(generated.ParseFromString(message->SerializeAsString()));
EXPECT_EQ(generated.int32_value(), 1024);
ASSERT_EQ(generated.repeated_int32_value_size(), 2);
EXPECT_EQ(generated.repeated_int32_value(0), 1);
EXPECT_EQ(generated.repeated_int32_value(1), 2);
EXPECT_EQ(generated.message_value().value(), 2048);
ASSERT_EQ(generated.repeated_message_value_size(), 2);
EXPECT_EQ(generated.repeated_message_value(0).value(), 40);
EXPECT_EQ(generated.repeated_message_value(1).value(), 96);
auto message_json = ToJson(*message);
ASSERT_OK(message_json);
auto generated_json = ToJson(generated);
ASSERT_OK(generated_json);
EXPECT_EQ(*message_json, *generated_json);
}
TEST(JsonUtilTest, TestParsingUnknownAnyFields) {
StringPiece input = R"json(
{
"value": {
"@type": "type.googleapis.com/proto3.TestMessage",
"unknown_field": "UNKNOWN_VALUE",
"string_value": "expected_value"
}
}
)json";
TestAny m;
EXPECT_THAT(FromJson(input, &m),
StatusIs(util::StatusCode::kInvalidArgument));
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_OK(FromJson(input, &m, options));
TestMessage t;
EXPECT_TRUE(m.value().UnpackTo(&t));
EXPECT_EQ(t.string_value(), "expected_value");
}
TEST(JsonUtilTest, TestParsingUnknownEnumsProto2) {
StringPiece input = R"json({"a": "UNKNOWN_VALUE"})json";
protobuf_unittest::TestNumbers m;
JsonParseOptions options;
EXPECT_THAT(FromJson(input, &m, options),
StatusIs(util::StatusCode::kInvalidArgument));
options.ignore_unknown_fields = true;
EXPECT_OK(FromJson(input, &m, options));
EXPECT_FALSE(m.has_a());
}
TEST(JsonUtilTest, TestParsingUnknownEnumsProto3) {
TestMessage m;
StringPiece input = R"json({"enum_value":"UNKNOWN_VALUE"})json";
m.set_enum_value(proto3::BAR);
EXPECT_THAT(FromJson(input, &m),
StatusIs(util::StatusCode::kInvalidArgument));
ASSERT_EQ(m.enum_value(), proto3::BAR); // Keep previous value
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_OK(FromJson(input, &m, options));
EXPECT_EQ(m.enum_value(), 0); // Unknown enum value must be decoded as 0
}
TEST(JsonUtilTest, TestParsingUnknownEnumsProto3FromInt) {
TestMessage m;
StringPiece input = R"json({"enum_value":12345})json";
m.set_enum_value(proto3::BAR);
EXPECT_OK(FromJson(input, &m));
ASSERT_EQ(m.enum_value(), 12345);
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_OK(FromJson(input, &m, options));
EXPECT_EQ(m.enum_value(), 12345);
}
// Trying to pass an object as an enum field value is always treated as an
// error
TEST(JsonUtilTest, TestParsingUnknownEnumsProto3FromObject) {
TestMessage m;
StringPiece input = R"json({"enum_value": {}})json";
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_THAT(FromJson(input, &m, options),
StatusIs(util::StatusCode::kInvalidArgument));
EXPECT_THAT(FromJson(input, &m),
StatusIs(util::StatusCode::kInvalidArgument));
}
TEST(JsonUtilTest, TestParsingUnknownEnumsProto3FromArray) {
TestMessage m;
StringPiece input = R"json({"enum_value": []})json";
EXPECT_THAT(FromJson(input, &m),
StatusIs(util::StatusCode::kInvalidArgument));
JsonParseOptions options;
options.ignore_unknown_fields = true;
EXPECT_THAT(FromJson(input, &m, options),
StatusIs(util::StatusCode::kInvalidArgument));
}
TEST(JsonUtilTest, TestParsingEnumCaseSensitive) {
TestMessage m;
StringPiece input = R"json({"enum_value": "bar"})json";
m.set_enum_value(proto3::FOO);
EXPECT_THAT(FromJson(input, &m),
StatusIs(util::StatusCode::kInvalidArgument));
// Default behavior is case-sensitive, so keep previous value.
ASSERT_EQ(m.enum_value(), proto3::FOO);
}
TEST(JsonUtilTest, TestParsingEnumIgnoreCase) {
TestMessage m;
StringPiece input = R"json({"enum_value":"bar"})json";
m.set_enum_value(proto3::FOO);
JsonParseOptions options;
options.case_insensitive_enum_parsing = true;
EXPECT_OK(FromJson(input, &m, options));
ASSERT_EQ(m.enum_value(), proto3::BAR);
}
// A ZeroCopyOutputStream that writes to multiple buffers.
class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream {
public:
explicit SegmentedZeroCopyOutputStream(
std::vector<StringPiece> segments)
: segments_(segments) {
// absl::c_* functions are not cloned in OSS.
std::reverse(segments_.begin(), segments_.end());
}
bool Next(void** buffer, int* length) override {
if (segments_.empty()) {
return false;
}
last_segment_ = segments_.back();
segments_.pop_back();
// TODO(b/234159981): This is only ever constructed in test code, and only
// from non-const bytes, so this is a valid cast. We need to do this since
// OSS proto does not yet have absl::Span; once we take a full Abseil
// dependency we should use that here instead.
*buffer = const_cast<char*>(last_segment_.data());
*length = static_cast<int>(last_segment_.size());
byte_count_ += static_cast<int64_t>(last_segment_.size());
return true;
}
void BackUp(int length) override {
GOOGLE_CHECK(length <= static_cast<int>(last_segment_.size()));
size_t backup = last_segment_.size() - static_cast<size_t>(length);
segments_.push_back(last_segment_.substr(backup));
last_segment_ = last_segment_.substr(0, backup);
byte_count_ -= static_cast<int64_t>(length);
}
int64_t ByteCount() const override { return byte_count_; }
private:
std::vector<StringPiece> segments_;
StringPiece last_segment_;
int64_t byte_count_ = 0;
};
// This test splits the output buffer and also the input data into multiple
// segments and checks that the implementation of ZeroCopyStreamByteSink
// handles all possible cases correctly.
TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) {
static constexpr int kOutputBufferLength = 10;
// An exhaustive test takes too long, skip some combinations to make the test
// run faster.
static constexpr int kSkippedPatternCount = 7;
char buffer[kOutputBufferLength];
for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1));
split_pattern += kSkippedPatternCount) {
// Split the buffer into small segments according to the split_pattern.
std::vector<StringPiece> segments;
int segment_start = 0;
for (int i = 0; i < kOutputBufferLength - 1; ++i) {
if (split_pattern & (1 << i)) {
segments.push_back(
StringPiece(buffer + segment_start, i - segment_start + 1));
segment_start = i + 1;
}
}
segments.push_back(StringPiece(buffer + segment_start,
kOutputBufferLength - segment_start));
// Write exactly 10 bytes through the ByteSink.
std::string input_data = "0123456789";
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
input_pattern += kSkippedPatternCount) {
memset(buffer, 0, sizeof(buffer));
{
SegmentedZeroCopyOutputStream output_stream(segments);
internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
int start = 0;
for (int j = 0; j < input_data.length() - 1; ++j) {
if (input_pattern & (1 << j)) {
byte_sink.Append(&input_data[start], j - start + 1);
start = j + 1;
}
}
byte_sink.Append(&input_data[start], input_data.length() - start);
}
EXPECT_EQ(std::string(buffer, input_data.length()), input_data);
}
// Write only 9 bytes through the ByteSink.
input_data = "012345678";
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
input_pattern += kSkippedPatternCount) {
memset(buffer, 0, sizeof(buffer));
{
SegmentedZeroCopyOutputStream output_stream(segments);
internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
int start = 0;
for (int j = 0; j < input_data.length() - 1; ++j) {
if (input_pattern & (1 << j)) {
byte_sink.Append(&input_data[start], j - start + 1);
start = j + 1;
}
}
byte_sink.Append(&input_data[start], input_data.length() - start);
}
EXPECT_EQ(std::string(buffer, input_data.length()), input_data);
EXPECT_EQ(buffer[input_data.length()], 0);
}
// Write 11 bytes through the ByteSink. The extra byte will just
// be ignored.
input_data = "0123456789A";
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
input_pattern += kSkippedPatternCount) {
memset(buffer, 0, sizeof(buffer));
{
SegmentedZeroCopyOutputStream output_stream(segments);
internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
int start = 0;
for (int j = 0; j < input_data.length() - 1; ++j) {
if (input_pattern & (1 << j)) {
byte_sink.Append(&input_data[start], j - start + 1);
start = j + 1;
}
}
byte_sink.Append(&input_data[start], input_data.length() - start);
}
EXPECT_EQ(input_data.substr(0, kOutputBufferLength),
std::string(buffer, kOutputBufferLength));
}
}
}
TEST(JsonUtilTest, TestWrongJsonInput) {
StringPiece json = "{\"unknown_field\":\"some_value\"}";
io::ArrayInputStream input_stream(json.data(), json.size());
char proto_buffer[10000];
io::ArrayOutputStream output_stream(proto_buffer, sizeof(proto_buffer));
std::string message_type = "type.googleapis.com/proto3.TestMessage";
auto* resolver = NewTypeResolverForDescriptorPool(
"type.googleapis.com", DescriptorPool::generated_pool());
EXPECT_THAT(JsonToBinaryStream(resolver, message_type, &input_stream,
&output_stream),
StatusIs(util::StatusCode::kInvalidArgument));
delete resolver;
}
TEST(JsonUtilTest, HtmlEscape) {
TestMessage m;
m.set_string_value("</script>");
JsonPrintOptions options;
EXPECT_THAT(ToJson(m, options),
IsOkAndHolds("{\"stringValue\":\"\\u003c/script\\u003e\"}"));
}
} // namespace
} // namespace util
} // namespace protobuf
} // namespace google