// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  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

#include "google/protobuf/json/json.h"

#include <cstdint>
#include <memory>
#include <string>

#include "google/protobuf/duration.pb.h"
#include "google/protobuf/field_mask.pb.h"
#include "google/protobuf/struct.pb.h"
#include "google/protobuf/timestamp.pb.h"
#include "google/protobuf/wrappers.pb.h"
#include "google/protobuf/descriptor.pb.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor_database.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/io/test_zero_copy_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "google/protobuf/util/json_format.pb.h"
#include "google/protobuf/util/json_format_proto3.pb.h"
#include "google/protobuf/test_textproto.h"
#include "google/protobuf/unittest.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 json {
namespace {
using ::google::protobuf::util::TypeResolver;
using ::proto3::MapIn;
using ::proto3::TestAny;
using ::proto3::TestEnumValue;
using ::proto3::TestMap;
using ::proto3::TestMessage;
using ::proto3::TestOneof;
using ::proto3::TestStringMapOverlay;
using ::proto3::TestWrapper;
using ::testing::ContainsRegex;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::Pair;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;

// TODO: Use the gtest versions once that's available in OSS.
MATCHER_P(IsOkAndHolds, inner,
          absl::StrCat("is OK and holds ", testing::PrintToString(inner))) {
  if (!arg.ok()) {
    *result_listener << arg.status();
    return false;
  }
  return testing::ExplainMatchResult(inner, *arg, result_listener);
}

absl::Status GetStatus(const absl::Status& s) { return s; }

template <typename T>
absl::Status GetStatus(const absl::StatusOr<T>& s) {
  return s.status();
}

MATCHER_P(StatusIs, status,
          absl::StrCat(".status() is ", testing::PrintToString(status))) {
  return GetStatus(arg).code() == status;
}

#define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk))
#define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk))

enum class Codec {
  kReflective,
  kResolver,
};

class JsonTest : public testing::TestWithParam<Codec> {
 protected:
  absl::StatusOr<std::string> ToJson(const Message& proto,
                                     PrintOptions options = {}) {
    if (GetParam() == Codec::kReflective) {
      std::string result;
      RETURN_IF_ERROR(MessageToJsonString(proto, &result, options));
      return result;
    }
    std::string proto_data = proto.SerializeAsString();
    io::ArrayInputStream in(proto_data.data(), proto_data.size());

    std::string result;
    io::StringOutputStream out(&result);

    RETURN_IF_ERROR(BinaryToJsonStream(
        resolver_.get(),
        absl::StrCat("type.googleapis.com/", proto.GetTypeName()), &in, &out,
        options));
    return result;
  }

  // The out parameter comes first since `json` tends to be a very long string,
  // and clang-format does a poor job if it is not the last parameter.
  absl::Status ToProto(Message& proto, absl::string_view json,
                       ParseOptions options = {}) {
    if (GetParam() == Codec::kReflective) {
      return JsonStringToMessage(json, &proto, options);
    }
    io::ArrayInputStream in(json.data(), json.size());

    std::string result;
    io::StringOutputStream out(&result);

    RETURN_IF_ERROR(JsonToBinaryStream(
        resolver_.get(),
        absl::StrCat("type.googleapis.com/", proto.GetTypeName()), &in, &out,
        options));

    if (!proto.ParseFromString(result)) {
      return absl::InternalError("wire format parse failed");
    }
    return absl::OkStatus();
  }

  template <typename Proto>
  absl::StatusOr<Proto> ToProto(absl::string_view json,
                                ParseOptions options = {}) {
    Proto proto;
    RETURN_IF_ERROR(ToProto(proto, json, options));
    return proto;
  }

  std::unique_ptr<TypeResolver> resolver_{
      google::protobuf::util::NewTypeResolverForDescriptorPool(
          "type.googleapis.com", DescriptorPool::generated_pool())};
};

INSTANTIATE_TEST_SUITE_P(JsonTestSuite, JsonTest,
                         testing::Values(Codec::kReflective, Codec::kResolver));

TEST_P(JsonTest, TestWhitespaces) {
  TestMessage m;
  m.mutable_message_value();
  m.set_string_value("foo");
  m.add_repeated_bool_value(true);
  m.add_repeated_bool_value(false);

  EXPECT_THAT(
      ToJson(m),
      IsOkAndHolds(
          R"({"stringValue":"foo","messageValue":{},"repeatedBoolValue":[true,false]})"));

  PrintOptions options;
  options.add_whitespace = true;
  // Note: whitespace here is significant.
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({
 "stringValue": "foo",
 "messageValue": {},
 "repeatedBoolValue": [
  true,
  false
 ]
}
)"));
}


TEST_P(JsonTest, TestAlwaysPrintFieldsWithNoPresence) {
  TestMessage m;
  EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));

  PrintOptions options;
  options.always_print_fields_with_no_presence = true;
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
                                               R"("int32Value":0,)"
                                               R"("int64Value":"0",)"
                                               R"("uint32Value":0,)"
                                               R"("uint64Value":"0",)"
                                               R"("floatValue":0,)"
                                               R"("doubleValue":0,)"
                                               R"("stringValue":"",)"
                                               R"("bytesValue":"",)"
                                               R"("enumValue":"FOO",)"
                                               R"("repeatedBoolValue":[],)"
                                               R"("repeatedInt32Value":[],)"
                                               R"("repeatedInt64Value":[],)"
                                               R"("repeatedUint32Value":[],)"
                                               R"("repeatedUint64Value":[],)"
                                               R"("repeatedFloatValue":[],)"
                                               R"("repeatedDoubleValue":[],)"
                                               R"("repeatedStringValue":[],)"
                                               R"("repeatedBytesValue":[],)"
                                               R"("repeatedEnumValue":[],)"
                                               R"("repeatedMessageValue":[],)"
                                               R"("NonStandardName":0)"
                                               "}"));

  m.set_string_value("i am a test string value");
  m.set_bytes_value("i am a test bytes value");
  m.set_optional_bool_value(false);
  m.set_optional_string_value("");
  m.set_optional_bytes_value("");
  EXPECT_THAT(ToJson(m, options),
              IsOkAndHolds(R"({"boolValue":false,)"
                           R"("int32Value":0,)"
                           R"("int64Value":"0",)"
                           R"("uint32Value":0,)"
                           R"("uint64Value":"0",)"
                           R"("floatValue":0,)"
                           R"("doubleValue":0,)"
                           R"("stringValue":"i am a test string value",)"
                           R"("bytesValue":"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=",)"
                           R"("enumValue":"FOO",)"
                           R"("repeatedBoolValue":[],)"
                           R"("repeatedInt32Value":[],)"
                           R"("repeatedInt64Value":[],)"
                           R"("repeatedUint32Value":[],)"
                           R"("repeatedUint64Value":[],)"
                           R"("repeatedFloatValue":[],)"
                           R"("repeatedDoubleValue":[],)"
                           R"("repeatedStringValue":[],)"
                           R"("repeatedBytesValue":[],)"
                           R"("repeatedEnumValue":[],)"
                           R"("repeatedMessageValue":[],)"
                           R"("optionalBoolValue":false,)"
                           R"("optionalStringValue":"",)"
                           R"("optionalBytesValue":"",)"
                           R"("NonStandardName":0)"
                           "}"));

  EXPECT_THAT(
      ToJson(proto2_unittest::TestAllTypes(), options),
      IsOkAndHolds(
          R"({"repeatedInt32":[],"repeatedInt64":[],"repeatedUint32":[],"repeatedUint64":[],)"
          R"("repeatedSint32":[],"repeatedSint64":[],"repeatedFixed32":[],"repeatedFixed64":[],)"
          R"("repeatedSfixed32":[],"repeatedSfixed64":[],"repeatedFloat":[],"repeatedDouble":[],)"
          R"("repeatedBool":[],"repeatedString":[],"repeatedBytes":[],"repeatedgroup":[],)"
          R"("repeatedNestedMessage":[],"repeatedForeignMessage":[],"repeatedImportMessage":[],)"
          R"("repeatedNestedEnum":[],"repeatedForeignEnum":[],"repeatedImportEnum":[],)"
          R"("repeatedStringPiece":[],"repeatedCord":[],"repeatedLazyMessage":[]})"));
}

TEST_P(JsonTest, TestDisableLegacyNonconformantBehavior) {
  EXPECT_THAT(ToProto<TestMessage>("{\"repeated_bool_value\": true}"),
              StatusIs(absl::StatusCode::kOk));

  ParseOptions options;
  options.allow_legacy_nonconformant_behavior = false;
  EXPECT_THAT(ToProto<TestMessage>("{\"repeated_bool_value\": true}", options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestPreserveProtoFieldNames) {
  TestMessage m;
  m.mutable_message_value();

  PrintOptions options;
  options.preserve_proto_field_names = true;
  EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"message_value\":{}}"));

}

TEST_P(JsonTest, Camels) {
  proto2_unittest::TestCamelCaseFieldNames m;
  m.set_stringfield("sTRINGfIELD");

  EXPECT_THAT(ToJson(m), IsOkAndHolds(R"({"StringField":"sTRINGfIELD"})"));
}

TEST_P(JsonTest, EvilString) {
  auto m = ToProto<TestMessage>(R"json(
    {"string_value": ")json"
                                "\n\r\b\f\1\2\3"
                                "\"}");
  ASSERT_OK(m);
  EXPECT_EQ(m->string_value(), "\n\r\b\f\1\2\3");
}

TEST_P(JsonTest, Unquoted64) {
  TestMessage m;
  m.add_repeated_int64_value(0);
  m.add_repeated_int64_value(42);
  m.add_repeated_int64_value(-((int64_t{1} << 60) + 1));
  m.add_repeated_int64_value(INT64_MAX);
  // This is a power of two and is therefore representable.
  m.add_repeated_int64_value(INT64_MIN);
  m.add_repeated_uint64_value(0);
  m.add_repeated_uint64_value(42);
  m.add_repeated_uint64_value((uint64_t{1} << 60) + 1);
  // This will be UB without the min/max check in RoundTripsThroughDouble().
  m.add_repeated_uint64_value(UINT64_MAX);

  PrintOptions opts;
  opts.unquote_int64_if_possible = true;
  EXPECT_THAT(
      ToJson(m, opts),
      R"({"repeatedInt64Value":[0,42,"-1152921504606846977","9223372036854775807",-9223372036854775808],)"
      R"("repeatedUint64Value":[0,42,"1152921504606846977","18446744073709551615"]})");
}

TEST_P(JsonTest, TestAlwaysPrintEnumsAsInts) {
  TestMessage orig;
  orig.set_enum_value(proto3::BAR);
  orig.add_repeated_enum_value(proto3::FOO);
  orig.add_repeated_enum_value(proto3::BAR);

  PrintOptions 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]}"));

  auto parsed = ToProto<TestMessage>(*printed);
  ASSERT_OK(parsed);

  EXPECT_EQ(parsed->enum_value(), proto3::BAR);
  EXPECT_THAT(parsed->repeated_enum_value(),
              ElementsAre(proto3::FOO, proto3::BAR));
}

TEST_P(JsonTest, TestPrintEnumsAsIntsWithDefaultValue) {
  TestEnumValue orig;
  // orig.set_enum_value1(proto3::FOO)
  orig.set_enum_value2(proto3::FOO);
  orig.set_enum_value3(proto3::BAR);

  PrintOptions print_options;
  print_options.always_print_enums_as_ints = true;
  print_options.always_print_fields_with_no_presence = true;

  auto printed = ToJson(orig, print_options);
  ASSERT_THAT(
      printed,
      IsOkAndHolds("{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"));

  auto parsed = ToProto<TestEnumValue>(*printed);

  EXPECT_EQ(parsed->enum_value1(), proto3::FOO);
  EXPECT_EQ(parsed->enum_value2(), proto3::FOO);
  EXPECT_EQ(parsed->enum_value3(), proto3::BAR);
}


TEST_P(JsonTest, QuotedEnumValue) {
  auto m = ToProto<TestEnumValue>(R"json(
    {"enumValue1": "1"}
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->enum_value1(), proto3::BAR);
}

TEST_P(JsonTest, QuotedIntegerValue) {
  auto m = ToProto<TestMessage>(R"json(
    {"int32Value": "2"}
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->int32_value(), 2);
}

TEST_P(JsonTest, QuotedIntegerFloatValue) {
  auto m = ToProto<TestMessage>(R"json(
    {"int32Value": "2.0"}
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->int32_value(), 2);
}

TEST_P(JsonTest, QuotedIntegerExponentValue) {
  auto m = ToProto<TestMessage>(R"json(
    {"int32Value": "1e2"}
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->int32_value(), 100);
}

TEST_P(JsonTest, TestInvalidIntegerValue) {
  EXPECT_THAT(ToProto<TestMessage>(R"("{"int32Value": ""})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_THAT(ToProto<TestMessage>(R"("{"int32Value": 1.001})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_THAT(ToProto<TestMessage>(R"("{"int32Value": "1.2e1"})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, WebSafeBytes) {
  auto m = ToProto<TestMessage>(R"json({
      "bytesValue": "-_"
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->bytes_value(), "\xfb");
}

TEST_P(JsonTest, ParseMessage) {
  auto m = ToProto<TestMessage>(R"json(
    {
      "boolValue": true,
      "int32Value": 1234567891,
      "int64Value": -5302428716536692736,
      "uint32Value": 42,
      "uint64Value": 530242871653669,
      "floatValue": 3.4e+38,
      "doubleValue": -55.3,
      "stringValue": "foo bar baz",
      "enumValue": "BAR",
      "messageValue": {
        "value": 2048
      },

      "repeatedBoolValue": [true],
      "repeatedInt32Value": [0, -42],
      "repeatedUint64Value": [1, 2],
      "repeatedDoubleValue": [1.5, -2],
      "repeatedStringValue": ["foo", "bar ", ""],
      "repeatedEnumValue": [1, "FOO"],
      "repeatedMessageValue": [
        {"value": 40},
        {},
        {"value": 96}
      ]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_TRUE(m->bool_value());
  EXPECT_EQ(m->int32_value(), 1234567891);
  EXPECT_EQ(m->int64_value(), -5302428716536692736);
  EXPECT_EQ(m->uint32_value(), 42);
  EXPECT_EQ(m->uint64_value(), 530242871653669);
  EXPECT_EQ(m->float_value(), 3.4e+38f);
  EXPECT_EQ(m->double_value(),
            -55.3);  // This value is intentionally not a nice
                     // round number in base 2, so its floating point
                     // representation has many digits at the end, which
                     // printing back to JSON must handle well.
  EXPECT_EQ(m->string_value(), "foo bar baz");
  EXPECT_EQ(m->enum_value(), proto3::EnumType::BAR);
  EXPECT_EQ(m->message_value().value(), 2048);

  EXPECT_THAT(m->repeated_bool_value(), ElementsAre(true));
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(0, -42));
  EXPECT_THAT(m->repeated_uint64_value(), ElementsAre(1, 2));
  EXPECT_THAT(m->repeated_double_value(), ElementsAre(1.5, -2));
  EXPECT_THAT(m->repeated_string_value(), ElementsAre("foo", "bar ", ""));
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::BAR, proto3::FOO));

  ASSERT_THAT(m->repeated_message_value(), SizeIs(3));
  EXPECT_EQ(m->repeated_message_value(0).value(), 40);
  EXPECT_EQ(m->repeated_message_value(1).value(), 0);
  EXPECT_EQ(m->repeated_message_value(2).value(), 96);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"boolValue":true,"int32Value":1234567891,"int64Value":"-5302428716536692736",)"
          R"("uint32Value":42,"uint64Value":"530242871653669","floatValue":3.4e+38,)"
          R"("doubleValue":-55.3,"stringValue":"foo bar baz","enumValue":"BAR",)"
          R"("messageValue":{"value":2048},"repeatedBoolValue":[true],"repeatedInt32Value":[0,-42])"
          R"(,"repeatedUint64Value":["1","2"],"repeatedDoubleValue":[1.5,-2],)"
          R"("repeatedStringValue":["foo","bar ",""],"repeatedEnumValue":["BAR","FOO"],)"
          R"("repeatedMessageValue":[{"value":40},{},{"value":96}]})"));
}

TEST_P(JsonTest, CurseOfAtob) {
  auto m = ToProto<TestMessage>(R"json(
    {
      repeatedBoolValue: ["0", "1", "false", "true", "f", "t", "no", "yes", "n", "y"]
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_bool_value(),
              ElementsAre(false, true, false, true, false, true, false, true,
                          false, true));
}

TEST_P(JsonTest, FloatPrecision) {
  google::protobuf::Value v;
  v.mutable_list_value()->add_values()->set_number_value(0.9900000095367432);
  v.mutable_list_value()->add_values()->set_number_value(0.8799999952316284);

  EXPECT_THAT(ToJson(v),
              IsOkAndHolds("[0.99000000953674316,0.87999999523162842]"));
}

TEST_P(JsonTest, FloatMinMaxValue) {
  // 3.4028235e38 is FLT_MAX to 8-significant-digits. The final digit (5)
  // is rounded up; that means that when parsing this as a 64-bit FP number,
  // the value ends up higher than FLT_MAX. We still want to accept it though,
  // as a reasonable representation of FLT_MAX.
  auto m = ToProto<TestMessage>(R"json(
    {
      "repeatedFloatValue": [3.4028235e38, -3.4028235e38],
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_float_value(), ElementsAre(FLT_MAX, -FLT_MAX));
}

TEST_P(JsonTest, FloatOutOfRange) {
  // Check that the slightly-lenient parsing demonstrated in FloatMinMaxValue
  // doesn't mean we allow all values. The value being parsed differs only
  // in the least significant (represented) digit.
  auto m = ToProto<TestMessage>(R"json(
    {
      "floatValue": 3.4028236e38
    }
  )json");
  EXPECT_THAT(m, StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParseLegacySingleRepeatedField) {
  auto m = ToProto<TestMessage>(R"json({
    "repeatedInt32Value": 1997,
    "repeatedStringValue": "oh no",
    "repeatedEnumValue": "BAR",
    "repeatedMessageValue": {"value": -1}
  })json");
  ASSERT_OK(m);

  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(1997));
  EXPECT_THAT(m->repeated_string_value(), ElementsAre("oh no"));
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::EnumType::BAR));

  ASSERT_THAT(m->repeated_message_value(), SizeIs(1));
  EXPECT_EQ(m->repeated_message_value(0).value(), -1);

  EXPECT_THAT(ToJson(*m),
              IsOkAndHolds(R"({"repeatedInt32Value":[1997],)"
                           R"("repeatedStringValue":["oh no"],)"
                           R"("repeatedEnumValue":["BAR"],)"
                           R"("repeatedMessageValue":[{"value":-1}]})"));
}

TEST_P(JsonTest, ParseMapWithEnumValuesProto2) {
  ParseOptions options;
  options.ignore_unknown_fields = false;
  proto2_unittest::TestMapOfEnums message;
  const std::string input_json = R"json({
    "enum_map": {
      "key1": "PROTOCOL",
      "key2": "UNKNOWN_ENUM_STRING_VALUE",
      "key3": "BUFFER",
      "key4": "UNKNOWN_ENUM_STRING_VALUE",
      "key5": "PROTOCOL",
    }
  })json";

  // Without ignore_unknown_fields, the unknown enum string value fails to
  // parse.
  EXPECT_THAT(ToProto(message, input_json, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParseMapWithEnumValuesProto3) {
  ParseOptions options;
  options.ignore_unknown_fields = false;
  proto3::MapOfEnums message;
  const std::string input_json = R"json({
    "map": {
      "key1": "FOO",
      "key2": "UNKNOWN_ENUM_STRING_VALUE",
      "key3": "BAR",
      "key4": "UNKNOWN_ENUM_STRING_VALUE",
      "key5": "FOO",
    }
  })json";

  // Without ignore_unknown_fields, the unknown enum string value fails to
  // parse.
  EXPECT_THAT(ToProto(message, input_json, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, MalformedUtf8) {
  ParseOptions options;
  options.ignore_unknown_fields = false;

  const std::string input_json = R"json({
    "stringValue": "\x80\x81"
  })json";

  TestMessage message;
  EXPECT_THAT(ToProto(message, input_json, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParseMapWithEnumValuesProto2WithUnknownFields) {
  ParseOptions options;
  options.ignore_unknown_fields = true;
  proto2_unittest::TestMapOfEnums message;
  const std::string input_json = R"json({
    "enum_map": {
      "key1": "PROTOCOL",
      "key2": "UNKNOWN_ENUM_STRING_VALUE",
      "key3": "BUFFER",
      "key4": "UNKNOWN_ENUM_STRING_VALUE",
      "key5": "PROTOCOL",
    }
  })json";

  ASSERT_OK(ToProto(message, input_json, options));

  EXPECT_EQ(message.enum_map().size(), 3);
  EXPECT_EQ(message.enum_map().contains("key2"), false);
  EXPECT_EQ(message.enum_map().contains("key4"), false);
}

TEST_P(JsonTest, ParseMapWithEnumValuesProto3WithUnknownFields) {
  ParseOptions options;
  options.ignore_unknown_fields = true;
  proto3::MapOfEnums message;
  const std::string input_json = R"json({
    "map": {
      "key1": "FOO",
      "key2": "UNKNOWN_ENUM_STRING_VALUE",
      "key3": "BAR",
      "key4": "UNKNOWN_ENUM_STRING_VALUE",
      "key5": "FOO",
    }
  })json";

  ASSERT_OK(ToProto(message, input_json, options));

  EXPECT_EQ(message.map().size(), 3);
  EXPECT_EQ(message.map().contains("key2"), false);
  EXPECT_EQ(message.map().contains("key4"), false);
}

TEST_P(JsonTest, ParseMap) {
  TestMap message;
  (*message.mutable_string_map())["hello"] = 1234;
  auto printed = ToJson(message);
  ASSERT_THAT(printed, IsOkAndHolds(R"({"stringMap":{"hello":1234}})"));

  auto other = ToProto<TestMap>(*printed);
  ASSERT_OK(other);
  EXPECT_EQ(other->DebugString(), message.DebugString());
}

TEST_P(JsonTest, BinaryToJsonStreamMapEntryWithNoValue) {
  TestStringMapOverlay emulated_message;
  auto* entry = emulated_message.add_string_map();
  entry->set_key("hello");

  std::string proto_data_emulated = emulated_message.SerializeAsString();
  io::ArrayInputStream in(proto_data_emulated.data(),
                          proto_data_emulated.size());

  std::string printed;
  io::StringOutputStream out(&printed);

  PrintOptions options = {};
  auto status = BinaryToJsonStream(resolver_.get(),
                                   "type.googleapis.com/proto3.TestStringMap",
                                   &in, &out, options);
  ASSERT_OK(status);

  ASSERT_EQ(printed, R"({"stringMap":{"hello":""}})");
}

TEST_P(JsonTest, BinaryToJsonStreamMapEntryWithNoKey) {
  TestStringMapOverlay emulated_message;
  auto* entry = emulated_message.add_string_map();
  entry->set_value("1234");

  std::string proto_data_emulated = emulated_message.SerializeAsString();
  io::ArrayInputStream in(proto_data_emulated.data(),
                          proto_data_emulated.size());

  std::string printed;
  io::StringOutputStream out(&printed);

  PrintOptions options = {};
  auto status = BinaryToJsonStream(resolver_.get(),
                                   "type.googleapis.com/proto3.TestStringMap",
                                   &in, &out, options);
  ASSERT_OK(status);

  ASSERT_EQ(printed, R"({"stringMap":{"":"1234"}})");
}

TEST_P(JsonTest, BinaryToJsonStreamMapEntryWithNoKeyOrValue) {
  TestStringMapOverlay emulated_message;
  (void)emulated_message.add_string_map();

  std::string proto_data_emulated = emulated_message.SerializeAsString();
  io::ArrayInputStream in(proto_data_emulated.data(),
                          proto_data_emulated.size());

  std::string printed;
  io::StringOutputStream out(&printed);

  PrintOptions options = {};
  auto status = BinaryToJsonStream(resolver_.get(),
                                   "type.googleapis.com/proto3.TestStringMap",
                                   &in, &out, options);
  ASSERT_OK(status);

  ASSERT_EQ(printed, R"({"stringMap":{"":""}})");
}

TEST_P(JsonTest, BinaryToJsonStreamDuplicateNonRepeatedField) {
  // The wire tag for field 6 (float_value) is (6 << 3) | 5 = 0x35.
  // We provide multiple values for the same non-repeated field.
  std::string binary_data("\x35\x00\x00\x80\x3F\x35\x00\x00\x00\x40", 10);
  io::ArrayInputStream in(binary_data.data(), binary_data.size());

  std::string printed;
  io::StringOutputStream out(&printed);

  PrintOptions options = {};
  options.always_print_fields_with_no_presence = true;
  auto status = BinaryToJsonStream(resolver_.get(),
                                   "type.googleapis.com/proto3.TestMessage",
                                   &in, &out, options);
  EXPECT_THAT(status, StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_THAT(status.message(),
              ContainsRegex("repeated entries for singular field number 6"));
}

TEST_P(JsonTest, BinaryToJsonStreamAlwaysPrintPrimitivesNoValueOnWire) {
  // Missing field should fall back to default value.
  std::string binary_data = "";  // Empty message
  io::ArrayInputStream in(binary_data.data(), binary_data.size());

  std::string printed;
  io::StringOutputStream out(&printed);

  PrintOptions options;
  options.always_print_fields_with_no_presence = true;
  auto status = BinaryToJsonStream(resolver_.get(),
                                   "type.googleapis.com/proto3.TestMessage",
                                   &in, &out, options);
  ASSERT_OK(status);

  // Verify that floatValue is printed with default 0.
  EXPECT_THAT(printed, ContainsRegex(R"("floatValue":0)"));
}

TEST_P(JsonTest, RepeatedMapKey) {
  EXPECT_THAT(ToProto<TestMap>(R"json({
    "string_map": {
      "twiceKey": 0,
      "twiceKey": 1
    }
  })json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ParsePrimitiveMapIn) {
  MapIn message;
  PrintOptions print_options;
  print_options.always_print_fields_with_no_presence = true;
  auto printed = ToJson(message, print_options);
  ASSERT_THAT(
      ToJson(message, print_options),
      IsOkAndHolds(R"({"other":"","things":[],"mapInput":{},"mapAny":{}})"));

  auto other = ToProto<MapIn>(*printed);
  ASSERT_OK(other);
  EXPECT_EQ(other->DebugString(), message.DebugString());
}

TEST_P(JsonTest, PrintPrimitiveOneof) {
  TestOneof message;
  PrintOptions options;
  options.always_print_fields_with_no_presence = true;
  message.mutable_oneof_message_value();
  EXPECT_THAT(ToJson(message, options),
              IsOkAndHolds(R"({"oneofMessageValue":{"value":0}})"));

  message.set_oneof_int32_value(1);
  EXPECT_THAT(ToJson(message, options),
              IsOkAndHolds(R"({"oneofInt32Value":1})"));
}

TEST_P(JsonTest, ParseOverOneof) {
  TestOneof m;
  m.set_oneof_string_value("foo");
  ASSERT_OK(ToProto(m, R"json({
    "oneofInt32Value": 5,
  })json"));
  EXPECT_EQ(m.oneof_int32_value(), 5);
}

TEST_P(JsonTest, RepeatedSingularKeys) {
  auto m = ToProto<TestMessage>(R"json({
    "int32Value": 1,
    "int32Value": 2
  })json");
  EXPECT_OK(m);
  EXPECT_EQ(m->int32_value(), 2);
}

TEST_P(JsonTest, RepeatedRepeatedKeys) {
  auto m = ToProto<TestMessage>(R"json({
    "repeatedInt32Value": [1],
    "repeatedInt32Value": [2, 3]
  })json");
  EXPECT_OK(m);
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(1, 2, 3));
}

TEST_P(JsonTest, RepeatedOneofKeys) {
  EXPECT_THAT(ToProto<TestOneof>(R"json({
    "oneofInt32Value": 1,
    "oneofStringValue": "foo"
  })json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParseIgnoreUnknownFields) {
  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_OK(ToProto<TestMessage>(R"({"unknownName":0})", options));

  TestMessage m;
  m.GetReflection()->MutableUnknownFields(&m)->AddFixed32(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddFixed64(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddVarint(9001, 9001);
  m.GetReflection()->MutableUnknownFields(&m)->AddLengthDelimited(9001, "9001");
  EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));
}

TEST_P(JsonTest, TestParseErrors) {
  // Parsing should fail if the field name can not be recognized.
  EXPECT_THAT(ToProto<TestMessage>(R"({"unknownName": 0})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  // Parsing should fail if the value is invalid.
  EXPECT_THAT(ToProto<TestMessage>(R"("{"int32Value": 2147483648})"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, 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());
  ASSERT_OK(ToProto(*message, R"json(
    {
      "int32Value": 1024,
      "repeatedInt32Value": [1, 2],
      "messageValue": {
        "value": 2048
      },
      "repeatedMessageValue": [
        {"value": 40},
        {"value": 96}
      ]
    }
  )json"));

  // Convert to generated message for easy inspection.
  TestMessage generated;
  EXPECT_TRUE(generated.ParseFromString(message->SerializeAsString()));

  EXPECT_EQ(generated.int32_value(), 1024);
  EXPECT_THAT(generated.repeated_int32_value(), ElementsAre(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_P(JsonTest, TestParsingAny) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "@type": "type.googleapis.com/proto3.TestMessage",
        "int32_value": 5,
        "string_value": "expected_value",
        "message_value": {"value": 1}
      }
    }
  )json");
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/proto3.TestMessage",)"
          R"("int32Value":5,"stringValue":"expected_value","messageValue":{"value":1}}})"));
}

TEST_P(JsonTest, TestParsingAnyWithRequiredFields) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "@type": "type.googleapis.com/proto2_unittest.TestRequired",
        "a": 5,
        "dummy2": 6,
        "b": 7
      }
    }
  )json");
  ASSERT_OK(m);

  proto2_unittest::TestRequired t;
  // Can't use UnpackTo directly, since that checks IsInitialized.
  ASSERT_FALSE(m->value().UnpackTo(&t));

  t.Clear();
  EXPECT_TRUE(t.ParsePartialFromString(m->value().value()));
  EXPECT_EQ(t.a(), 5);
  EXPECT_EQ(t.dummy2(), 6);
  EXPECT_EQ(t.b(), 7);
  EXPECT_FALSE(t.has_c());

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/proto2_unittest.TestRequired",)"
          R"("a":5,"dummy2":6,"b":7}})"));
}

TEST_P(JsonTest, TestParsingAnyEndAtType) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "int32_value": 5,
        "string_value": "expected_value",
        "message_value": {"value": 1},
        "@type": "type.googleapis.com/proto3.TestMessage"
      }
    }
  )json");
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);
}

TEST_P(JsonTest, TestParsingNestedAnys) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {
        "value": {
          "int32_value": 5,
          "string_value": "expected_value",
          "message_value": {"value": 1},
          "@type": "type.googleapis.com/proto3.TestMessage"
        },
        "@type": "type.googleapis.com/google.protobuf.Any"
      }
    }
  )json");
  ASSERT_OK(m);

  google::protobuf::Any inner;
  ASSERT_TRUE(m->value().UnpackTo(&inner));

  TestMessage t;
  ASSERT_TRUE(inner.UnpackTo(&t));
  EXPECT_EQ(t.int32_value(), 5);
  EXPECT_EQ(t.string_value(), "expected_value");
  EXPECT_EQ(t.message_value().value(), 1);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/google.protobuf.Any",)"
          R"("value":{"@type":"type.googleapis.com/proto3.TestMessage",)"
          R"("int32Value":5,"stringValue":"expected_value","messageValue":{"value":1}}}})"));
}

TEST_P(JsonTest, TestParsingBrokenAny) {
  auto m = ToProto<TestAny>(R"json(
    {
      "value": {}
    }
  )json");
  ASSERT_OK(m);
  EXPECT_EQ(m->value().type_url(), "");
  EXPECT_EQ(m->value().value(), "");

  EXPECT_THAT(ToProto<TestAny>(R"json(
    {
      "value": {
        "type_url": "garbage",
        "value": "bW9yZSBnYXJiYWdl"
      }
    }
  )json"),
              StatusIs(absl::StatusCode::kInvalidArgument));

  TestAny m2;
  m2.mutable_value();
  EXPECT_THAT(ToJson(m2), IsOkAndHolds(R"({"value":{}})"));
  m2.mutable_value()->set_value("garbage");
  // The ESF parser does not return InvalidArgument for this error.
  EXPECT_THAT(ToJson(m2), Not(StatusIs(absl::StatusCode::kOk)));

  m2.Clear();
  m2.mutable_value()->set_type_url("type.googleapis.com/proto3.TestMessage");
  EXPECT_THAT(
      ToJson(m2),
      IsOkAndHolds(
          R"({"value":{"@type":"type.googleapis.com/proto3.TestMessage"}})"));
}

TEST_P(JsonTest, TestFlatList) {
  auto m = ToProto<TestMessage>(R"json(
    {
      "repeatedInt32Value": [[[5]], [6]]
    }
  )json");
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_int32_value(), ElementsAre(5, 6));

  // The above flatteing behavior is suppressed for google::protobuf::ListValue.
  auto m2 = ToProto<google::protobuf::Value>(R"json(
    {
      "repeatedInt32Value": [[[5]], [6]]
    }
  )json");
  ASSERT_OK(m2);
  auto fields = m2->struct_value().fields();
  auto list = fields["repeatedInt32Value"].list_value();
  EXPECT_EQ(list.values(0)
                .list_value()
                .values(0)
                .list_value()
                .values(0)
                .number_value(),
            5);
  EXPECT_EQ(list.values(1).list_value().values(0).number_value(), 6);
}

TEST_P(JsonTest, ParseWrappers) {
  auto m = ToProto<TestWrapper>(R"json(
    {
      "boolValue": true,
      "int32Value": 42,
      "stringValue": "ieieo",
    }
  )json");
  ASSERT_OK(m);

  EXPECT_TRUE(m->bool_value().value());
  EXPECT_EQ(m->int32_value().value(), 42);
  EXPECT_EQ(m->string_value().value(), "ieieo");

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"boolValue":true,"int32Value":42,"stringValue":"ieieo"})"));

  auto m2 = ToProto<TestWrapper>(R"json(
    {
      "boolValue": { "value": true },
      "int32Value": { "value": 42 },
      "stringValue": { "value": "ieieo" },
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_TRUE(m2->bool_value().value());
  EXPECT_EQ(m2->int32_value().value(), 42);
  EXPECT_EQ(m2->string_value().value(), "ieieo");
}

TEST_P(JsonTest, TestParsingUnknownAnyFields) {
  absl::string_view input = R"json(
    {
      "value": {
        "@type": "type.googleapis.com/proto3.TestMessage",
        "unknown_field": "UNKNOWN_VALUE",
        "string_value": "expected_value"
      }
    }
  )json";

  EXPECT_THAT(ToProto<TestAny>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<TestAny>(input, options);
  ASSERT_OK(m);

  TestMessage t;
  ASSERT_TRUE(m->value().UnpackTo(&t));
  EXPECT_EQ(t.string_value(), "expected_value");
}

TEST_P(JsonTest, TestHugeBareString) {
  auto m = ToProto<TestMessage>(R"json({
    "int64Value": 6009652459062546621
  })json");
  ASSERT_OK(m);
  EXPECT_EQ(m->int64_value(), 6009652459062546621);
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto2) {
  absl::string_view input = R"json({"ayuLmao": "UNKNOWN_VALUE"})json";

  EXPECT_THAT(ToProto<proto2_unittest::TestNumbers>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<proto2_unittest::TestNumbers>(input, options);
  ASSERT_OK(m);
  EXPECT_FALSE(m->has_a());
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3) {
  TestMessage m;
  absl::string_view input = R"json({"enum_value":"UNKNOWN_VALUE"})json";

  m.set_enum_value(proto3::BAR);
  ASSERT_THAT(ToProto(m, input), StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_EQ(m.enum_value(), proto3::BAR);  // Keep previous value

  ParseOptions options;
  options.ignore_unknown_fields = true;
  ASSERT_OK(ToProto(m, input, options));
  EXPECT_EQ(m.enum_value(), 0);  // Unknown enum value must be decoded as 0
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromInt) {
  TestMessage m;
  absl::string_view input = R"json({"enum_value":12345})json";

  m.set_enum_value(proto3::BAR);
  ASSERT_OK(ToProto(m, input));
  EXPECT_EQ(m.enum_value(), 12345);

  ParseOptions options;
  options.ignore_unknown_fields = true;
  ASSERT_OK(ToProto(m, input, options));
  EXPECT_EQ(m.enum_value(), 12345);
}

// Trying to pass an object as an enum field value is always treated as an
// error
TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromObject) {
  absl::string_view input = R"json({"enum_value": {}})json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_THAT(ToProto<TestMessage>(input, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParsingUnknownEnumsProto3FromArray) {
  absl::string_view input = R"json({"enum_value": []})json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  EXPECT_THAT(ToProto<TestMessage>(input, options),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, TestParsingRepeatedUnknownEnums) {
  absl::string_view input = R"json({
    "repeated_enum_value": ["FOO", "BAZ", "BAR"]
  })json";

  EXPECT_THAT(ToProto<TestMessage>(input),
              StatusIs(absl::StatusCode::kInvalidArgument));

  ParseOptions options;
  options.ignore_unknown_fields = true;
  auto m = ToProto<TestMessage>(input, options);
  ASSERT_OK(m);
  EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::FOO, proto3::BAR));
}

TEST_P(JsonTest, TestParsingEnumCaseSensitive) {
  TestMessage m;
  m.set_enum_value(proto3::FOO);
  EXPECT_THAT(ToProto(m, R"json({"enum_value": "bar"})json"),
              StatusIs(absl::StatusCode::kInvalidArgument));
  // Default behavior is case-sensitive, so keep previous value.
  EXPECT_EQ(m.enum_value(), proto3::FOO);
}

TEST_P(JsonTest, TestParsingEnumLowercase) {
  ParseOptions options;
  options.case_insensitive_enum_parsing = true;
  auto m =
      ToProto<TestMessage>(R"json({"enum_value": "TLSv1_2"})json", options);
  ASSERT_OK(m);
  EXPECT_THAT(m->enum_value(), proto3::TLSv1_2);
}

TEST_P(JsonTest, TestParsingEnumIgnoreCase) {
  TestMessage m;
  m.set_enum_value(proto3::FOO);

  ParseOptions options;
  options.case_insensitive_enum_parsing = true;
  ASSERT_OK(ToProto(m, R"json({"enum_value":"bar"})json", options));
  EXPECT_EQ(m.enum_value(), proto3::BAR);
}

TEST_P(JsonTest, Extensions) {
  if (GetParam() == Codec::kResolver) {
    GTEST_SKIP();
  }

  auto m = ToProto<proto2_unittest::TestMixedFieldsAndExtensions>(R"json({
    "[proto2_unittest.TestMixedFieldsAndExtensions.c]": 42,
    "a": 5,
    "b": [1, 2, 3],
    "[proto2_unittest.TestMixedFieldsAndExtensions.d]": [1, 1, 2, 3, 5, 8, 13]
  })json");
  ASSERT_OK(m);
  EXPECT_EQ(m->a(), 5);
  EXPECT_THAT(m->b(), ElementsAre(1, 2, 3));
  EXPECT_EQ(m->GetExtension(proto2_unittest::TestMixedFieldsAndExtensions::c),
            42);
  EXPECT_THAT(
      m->GetRepeatedExtension(proto2_unittest::TestMixedFieldsAndExtensions::d),
      ElementsAre(1, 1, 2, 3, 5, 8, 13));

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"a":5,)"
          R"("[proto2_unittest.TestMixedFieldsAndExtensions.c]":42,)"
          R"("b":[1,2,3],)"
          R"("[proto2_unittest.TestMixedFieldsAndExtensions.d]":[1,1,2,3,5,8,13]})"));

  auto m2 = ToProto<proto2_unittest::TestAllTypes>(R"json({
    "[this.extension.does.not.exist]": 42
  })json");
  EXPECT_THAT(m2, StatusIs(absl::StatusCode::kInvalidArgument));

  auto m3 = ToProto<proto2_unittest::TestAllTypes>(R"json({
    "[proto2_unittest.TestMixedFieldsAndExtensions.c]": 42
  })json");
  EXPECT_THAT(m3, StatusIs(absl::StatusCode::kInvalidArgument));
}

// Parsing does NOT work like MergeFrom: existing repeated field values are
// clobbered, not appended to.
TEST_P(JsonTest, TestOverwriteRepeated) {
  TestMessage m;
  m.add_repeated_int32_value(5);

  ASSERT_OK(ToProto(m, R"json({"repeated_int32_value": [1, 2, 3]})json"));
  EXPECT_THAT(m.repeated_int32_value(), ElementsAre(1, 2, 3));
}


TEST_P(JsonTest, TestAny) {
  // Not setting 'value' is legal because it is the the representation of an
  // empty message (since 'bytes' is an implicit presence field).
  google::protobuf::Any any;
  any.set_type_url("type.googleapis.com/proto3.TestMessage");
  PrintOptions options;
  options.allow_legacy_nonconformant_behavior = false;
  EXPECT_THAT(
      ToJson(any),
      IsOkAndHolds(R"({"@type":"type.googleapis.com/proto3.TestMessage"})"));
  EXPECT_THAT(
      ToJson(any, options),
      IsOkAndHolds(R"({"@type":"type.googleapis.com/proto3.TestMessage"})"));

  auto round_trip = ToProto<google::protobuf::Any>(*ToJson(any));
  ASSERT_OK(round_trip);
  EXPECT_EQ(round_trip->type_url(), "type.googleapis.com/proto3.TestMessage");
  EXPECT_EQ(round_trip->value(), "");
}

TEST_P(JsonTest, TestDuration) {
  auto m = ToProto<proto3::TestDuration>(R"json(
    {
      "value": "123456.789s",
      "repeated_value": ["0.1s", "999s"]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_EQ(m->value().seconds(), 123456);
  EXPECT_EQ(m->value().nanos(), 789000000);

  EXPECT_THAT(m->repeated_value(), SizeIs(2));
  EXPECT_EQ(m->repeated_value(0).seconds(), 0);
  EXPECT_EQ(m->repeated_value(0).nanos(), 100000000);
  EXPECT_EQ(m->repeated_value(1).seconds(), 999);
  EXPECT_EQ(m->repeated_value(1).nanos(), 0);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":"123456.789s","repeatedValue":["0.100s","999s"]})"));

  auto m2 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"seconds": 4, "nanos": 5},
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 4);
  EXPECT_EQ(m2->value().nanos(), 5);

  // Negative duration with zero seconds.
  auto m3 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"nanos": -5},
    }
  )json");
  ASSERT_OK(m3);
  EXPECT_EQ(m3->value().seconds(), 0);
  EXPECT_EQ(m3->value().nanos(), -5);
  EXPECT_THAT(ToJson(m3->value()), IsOkAndHolds("\"-0.000000005s\""));

  // Negative duration with zero nanos.
  auto m4 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": {"seconds": -5},
    }
  )json");
  ASSERT_OK(m4);
  EXPECT_EQ(m4->value().seconds(), -5);
  EXPECT_EQ(m4->value().nanos(), 0);
  EXPECT_THAT(ToJson(m4->value()), IsOkAndHolds("\"-5s\""));

  // Parse "0.5s" as a JSON string.
  auto m5 = ToProto<proto3::TestDuration>(R"json(
    {
      "value": "0.5s",
    }
  )json");
  ASSERT_OK(m5);
  EXPECT_EQ(m5->value().seconds(), 0);
  EXPECT_EQ(m5->value().nanos(), 500000000);
  EXPECT_THAT(ToJson(m5->value()), IsOkAndHolds("\"0.500s\""));
}

// These tests are not exhaustive; tests in //google/protobuf/conformance
// are more comprehensive.
TEST_P(JsonTest, TestTimestamp) {
  auto m = ToProto<proto3::TestTimestamp>(R"json(
    {
      "value": "1996-02-27T12:00:00Z",
      "repeated_value": ["9999-12-31T23:59:59Z"]
    }
  )json");
  ASSERT_OK(m);

  EXPECT_EQ(m->value().seconds(), 825422400);
  EXPECT_EQ(m->value().nanos(), 0);
  EXPECT_THAT(m->repeated_value(), SizeIs(1));
  EXPECT_EQ(m->repeated_value(0).seconds(), 253402300799);
  EXPECT_EQ(m->repeated_value(0).nanos(), 0);

  EXPECT_THAT(
      ToJson(*m),
      IsOkAndHolds(
          R"({"value":"1996-02-27T12:00:00Z","repeatedValue":["9999-12-31T23:59:59Z"]})"));

  auto m2 = ToProto<proto3::TestTimestamp>(R"json(
    {
      "value": {"seconds": 4, "nanos": 5},
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 4);
  EXPECT_EQ(m2->value().nanos(), 5);
}

// This test case comes from Envoy's tests. They like to parse a Value out of
// YAML, turn it into JSON, and then parse it as a different proto. This means
// we must be extremely careful with integer fields, because they need to
// round-trip through doubles. This happens all over Envoy. :(
TEST_P(JsonTest, TestEnvoyRoundTrip) {
  auto m = ToProto<google::protobuf::Value>(R"json(
    {
      "value": {"seconds": 1234567891, "nanos": 234000000},
    }
  )json");
  ASSERT_OK(m);

  auto j = ToJson(*m);
  ASSERT_OK(j);

  auto m2 = ToProto<proto3::TestTimestamp>(*j);
  ASSERT_OK(m2);

  EXPECT_EQ(m2->value().seconds(), 1234567891);
  EXPECT_EQ(m2->value().nanos(), 234000000);
}

TEST_P(JsonTest, TestFieldMask) {
  auto m = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": "foo,bar.bazBaz"
    }
  )json");
  ASSERT_OK(m);

  EXPECT_THAT(m->value().paths(), ElementsAre("foo", "bar.baz_baz"));
  EXPECT_THAT(ToJson(*m), IsOkAndHolds(R"({"value":"foo,bar.bazBaz"})"));

  auto m2 = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": {
        "paths": ["yep.really"]
      },
    }
  )json");
  ASSERT_OK(m2);

  EXPECT_THAT(m2->value().paths(), ElementsAre("yep.really"));
}

TEST_P(JsonTest, TestFieldMaskSnakeCase) {
  auto m = ToProto<proto3::TestFieldMask>(R"json(
    {
      "value": "foo_bar"
    }
  )json");
  ASSERT_OK(m);

  EXPECT_THAT(m->value().paths(), ElementsAre("foo_bar"));
}

// Documents a known spec-level design limitation in ProtoJSON FieldMask
// representation: certain field paths (leading underscores, uppercase letters)
// are not round-trip safe through the snake_case<->lowerCamelCase conversion.
// This is a design bug in the ProtoJSON WKT spec, not in the C++
// implementation. See:
// https://protobuf.dev/programming-guides/json/#wkt-roundtrip-limitations
TEST_P(JsonTest, TestFieldMaskJsonRoundTripNonIdempotent) {
  // Start with a non-canonical path containing leading underscores and
  // uppercase.
  google::protobuf::FieldMask fm0;
  fm0.add_paths("__Y");

  // Round-trip through JSON once.
  proto3::TestFieldMask wrapper0;
  *wrapper0.mutable_value() = fm0;
  auto json0 = ToJson(wrapper0);
  ASSERT_OK(json0);

  auto wrapper1 = ToProto<proto3::TestFieldMask>(*json0);
  ASSERT_OK(wrapper1);
  auto json1 = ToJson(*wrapper1);
  ASSERT_OK(json1);

  // Round-trip again.
  auto wrapper2 = ToProto<proto3::TestFieldMask>(*json1);
  ASSERT_OK(wrapper2);
  auto json2 = ToJson(*wrapper2);
  ASSERT_OK(json2);

  // Spec limitation: The path shifts across round-trips because the
  // snake_case<->lowerCamelCase conversion is not bijective for paths with
  // leading underscores or uppercase letters.  "__Y" -> "__y" -> "_y" over
  // two hops.
  // See:
  // https://protobuf.dev/programming-guides/json/#wkt-roundtrip-limitations
  EXPECT_NE(*json0, *json1)
      << "If this starts passing, the FieldMask JSON round-trip "
         "non-idempotency bug may be fixed. Update this test.";

  // The third round-trip does stabilize (fixed point after 2-3 hops).
  EXPECT_EQ(*json1, *json2)
      << "Expected JSON to stabilize after two round-trips";
}

TEST_P(JsonTest, TestLegalNullsInArray) {
  auto m = ToProto<proto3::TestNullValue>(R"json({
    "repeatedNullValue": [null]
  })json");
  ASSERT_OK(m);

  EXPECT_THAT(m->repeated_null_value(),
              ElementsAre(google::protobuf::NULL_VALUE));

  auto m2 = ToProto<proto3::TestValue>(R"json({
    "repeatedValue": [null]
  })json");
  ASSERT_OK(m2);

  ASSERT_THAT(m2->repeated_value(), SizeIs(1));
  EXPECT_TRUE(m2->repeated_value(0).has_null_value());

  m2->Clear();
  m2->mutable_value();  // Materialize an empty singular Value.
  m2->add_repeated_value();
  m2->add_repeated_value()->set_string_value("solitude");
  m2->add_repeated_value();
  EXPECT_THAT(ToJson(*m2), IsOkAndHolds(R"({"repeatedValue":["solitude"]})"));
}

TEST_P(JsonTest, EmptyValue) {
  EXPECT_THAT(ToJson(google::protobuf::Value()), IsOkAndHolds(""));

  google::protobuf::Struct s;
  s.mutable_fields()->emplace("empty", google::protobuf::Value());
  EXPECT_THAT(ToJson(s), IsOkAndHolds("{}"));
}

TEST_P(JsonTest, TrailingGarbage) {
  EXPECT_THAT(ToProto<TestMessage>("{}garbage"),
              StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, ListList) {
  auto m = ToProto<proto3::TestListValue>(R"json({
    "repeated_value": [["ayy", "lmao"]]
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
  EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");

  m = ToProto<proto3::TestListValue>(R"json({
    "repeated_value": [{
      "values": ["ayy", "lmao"]
    }]
  })json");
  ASSERT_OK(m);

  EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
  EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");
}

TEST_P(JsonTest, HtmlEscape) {
  TestMessage m;
  m.set_string_value("</script>");
  EXPECT_THAT(ToJson(m),
              IsOkAndHolds(R"({"stringValue":"\u003c/script\u003e"})"));

}

TEST_P(JsonTest, FieldOrder) {
  // $ protoscope -s <<< "3: 3 22: 2 1: 1 22: 2"
  std::string out;
  absl::Status s = BinaryToJsonString(
      resolver_.get(), "type.googleapis.com/proto3.TestMessage",
      "\x18\x03\xb0\x01\x02\x08\x01\xb0\x01\x02", &out);
  ASSERT_OK(s);
  EXPECT_EQ(
      out, R"({"boolValue":true,"int64Value":"3","repeatedInt32Value":[2,2]})");
}

TEST_P(JsonTest, UnknownGroupField) {
  // $ protoscope -s <<< "999: !{1: 99}"
  std::string out;
  absl::Status s = BinaryToJsonString(resolver_.get(),
                                      "type.googleapis.com/proto3.TestMessage",
                                      "\273>\010c\274>", &out);
  ASSERT_OK(s);
  EXPECT_EQ(out, "{}");
}

TEST_P(JsonTest, MalformedLengthDelimitedField) {
  std::string out;
  // An unknown length delimited field where the length is larger than
  // the remaining input.
  absl::Status s = BinaryToJsonString(resolver_.get(),
                                      "type.googleapis.com/proto3.TestMessage",
                                      "\xC2\x3E\x80\x80\x80\x80\x08", &out);
  ASSERT_THAT(s, StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST_P(JsonTest, DeeplyNestedGroupsRejected) {
  // Verify that deeply nested TYPE_GROUP fields are rejected with an error
  // rather than causing unbounded stack recursion.
  FileDescriptorProto file_proto;
  file_proto.set_name("group_depth_test.proto");
  file_proto.set_syntax("proto2");

  auto* msg = file_proto.add_message_type();
  msg->set_name("RecursiveGroup");

  auto* nested = msg->add_nested_type();
  nested->set_name("Nested");

  auto* inner = nested->add_field();
  inner->set_name("nested");
  inner->set_number(1);
  inner->set_type(FieldDescriptorProto::TYPE_GROUP);
  inner->set_type_name("Nested");
  inner->set_label(FieldDescriptorProto::LABEL_OPTIONAL);

  auto* outer = msg->add_field();
  outer->set_name("nested");
  outer->set_number(1);
  outer->set_type(FieldDescriptorProto::TYPE_GROUP);
  outer->set_type_name("Nested");
  outer->set_label(FieldDescriptorProto::LABEL_OPTIONAL);

  DescriptorPool pool;
  const FileDescriptor* fd = pool.BuildFile(file_proto);
  ASSERT_NE(fd, nullptr);

  std::unique_ptr<TypeResolver> resolver(
      google::protobuf::util::NewTypeResolverForDescriptorPool("type.googleapis.com",
                                                     &pool));

  // 200 nested groups: 200x START_GROUP(field=1) + 200x END_GROUP(field=1)
  // Field 1, wire type 3 = 0x0B; Field 1, wire type 4 = 0x0C
  std::string payload(200, 0x0B);
  payload.append(200, 0x0C);

  std::string out;
  absl::Status s = BinaryToJsonString(
      resolver.get(), "type.googleapis.com/RecursiveGroup", payload, &out);
  EXPECT_THAT(s, StatusIs(absl::StatusCode::kInvalidArgument));
}

// JSON values get special treatment when it comes to pre-existing values in
// their repeated fields, when parsing through their dedicated syntax.
TEST_P(JsonTest, ClearPreExistingRepeatedInJsonValues) {
  google::protobuf::ListValue l;
  l.add_values()->set_string_value("hello");
  ASSERT_OK(JsonStringToMessage("[]", &l));
  EXPECT_THAT(l.values(), IsEmpty());

  google::protobuf::Struct s;
  (*s.mutable_fields())["hello"].set_string_value("world");
  ASSERT_OK(JsonStringToMessage("{}", &s));
  EXPECT_THAT(s.fields(), IsEmpty());
}

TEST(JsonErrorTest, FieldNameAndSyntaxErrorInSeparateChunks) {
  std::unique_ptr<TypeResolver> resolver{
      google::protobuf::util::NewTypeResolverForDescriptorPool(
          "type.googleapis.com", DescriptorPool::generated_pool())};
  io::internal::TestZeroCopyInputStream input_stream(
      {"{\"bool_value\":", "5}"});
  std::string result;
  io::StringOutputStream output_stream(&result);
  absl::Status s = JsonToBinaryStream(
      resolver.get(), "type.googleapis.com/proto3.TestMessage", &input_stream,
      &output_stream, ParseOptions{});
  ASSERT_FALSE(s.ok());
  EXPECT_THAT(
      s.message(),
      ContainsRegex("invalid *JSON *in *type.googleapis.com/proto3.TestMessage "
                    "*@ *bool_value"));
}

TEST_P(JsonTest, OversizedStringRejected) {
#ifndef NDEBUG
  GTEST_SKIP() << "Test is too slow in non-opt builds.";
#else

  if (sizeof(void*) < 8) {
    GTEST_SKIP() << "Test requires 64-bit environment.";
  }

  absl::Cord chunk(std::string(1024 * 1024, 'a'));
  absl::Cord cord;
  cord.Append(R"({"stringValue": ")");
  for (int i = 0; i < 2048; ++i) {
    cord.Append(chunk);
  }
  cord.Append(R"("})");

  io::CordInputStream input_stream(&cord);
  TestMessage m;
  absl::Status s;

  if (GetParam() == Codec::kReflective) {
    s = JsonStreamToMessage(&input_stream, &m, ParseOptions());
  } else {
    std::string result;
    io::StringOutputStream out(&result);
    s = JsonToBinaryStream(
        resolver_.get(), absl::StrCat("type.googleapis.com/", m.GetTypeName()),
        &input_stream, &out, ParseOptions());
  }

  EXPECT_THAT(s, StatusIs(absl::StatusCode::kInvalidArgument));
  EXPECT_THAT(s.message(), ContainsRegex("string *value *too *large"));

#endif  // NDEBUG
}

absl::StatusOr<google::protobuf::Struct> MessageToJsonAndBack(
    const google::protobuf::Struct& msg) {
  std::string str;
  auto status = MessageToJsonString(msg, &str);
  if (!status.ok()) return status;

  google::protobuf::Struct out;
  status = JsonStringToMessage(str, &out);
  if (!status.ok()) return status;
  return out;
}

TEST(JsonTest, MapsThroughCodegenWorksProperly) {
  google::protobuf::Struct s;
  auto& fields = *s.mutable_fields();

  fields["first"].set_string_value("str1");

  EXPECT_THAT(MessageToJsonAndBack(s), IsOkAndHolds(EqualsProto(R"pb(
                fields {
                  key: "first"
                  value { string_value: "str1" }
                }
              )pb")));

  fields["second"].set_string_value("str2");

  EXPECT_THAT(MessageToJsonAndBack(s), IsOkAndHolds(EqualsProto(R"pb(
                fields {
                  key: "first"
                  value { string_value: "str1" }
                }
                fields {
                  key: "second"
                  value { string_value: "str2" }
                }
              )pb")));

  fields["first"].set_bool_value(true);

  EXPECT_THAT(MessageToJsonAndBack(s), IsOkAndHolds(EqualsProto(R"pb(
                fields {
                  key: "first"
                  value { bool_value: true }
                }
                fields {
                  key: "second"
                  value { string_value: "str2" }
                }
              )pb")));
}

}  // namespace
}  // namespace json
}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"
