diff --git a/java/core/src/main/java/com/google/protobuf/Message.java b/java/core/src/main/java/com/google/protobuf/Message.java index 3db1c771e6..f641739e32 100644 --- a/java/core/src/main/java/com/google/protobuf/Message.java +++ b/java/core/src/main/java/com/google/protobuf/Message.java @@ -39,11 +39,12 @@ import java.util.Map; * *

See also {@link MessageLite}, which defines most of the methods that typical users care about. * {@link Message} adds methods that are not available in the "lite" runtime. The biggest added - * features are introspection and reflection; that is, getting descriptors for the message type - * and accessing the field values dynamically. + * features are introspection and reflection; that is, getting descriptors for the message type and + * accessing the field values dynamically. * * @author kenton@google.com Kenton Varda */ +@CheckReturnValue public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite, re-declared here only for return type covariance.) @@ -102,6 +103,7 @@ public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) @Override + @CanIgnoreReturnValue Builder clear(); /** @@ -121,6 +123,7 @@ public interface Message extends MessageLite, MessageOrBuilder { * *

This is equivalent to the {@code Message::MergeFrom} method in C++. */ + @CanIgnoreReturnValue Builder mergeFrom(Message other); // (From MessageLite.Builder, re-declared here only for return type @@ -135,9 +138,11 @@ public interface Message extends MessageLite, MessageOrBuilder { Builder clone(); @Override + @CanIgnoreReturnValue Builder mergeFrom(CodedInputStream input) throws IOException; @Override + @CanIgnoreReturnValue Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; @@ -190,18 +195,21 @@ public interface Message extends MessageLite, MessageOrBuilder { * Sets a field to the given value. The value must be of the correct type for this field, that * is, the same type that {@link Message#getField(Descriptors.FieldDescriptor)} returns. */ + @CanIgnoreReturnValue Builder setField(Descriptors.FieldDescriptor field, Object value); /** * Clears the field. This is exactly equivalent to calling the generated "clear" accessor method * corresponding to the field. */ + @CanIgnoreReturnValue Builder clearField(Descriptors.FieldDescriptor field); /** * Clears the oneof. This is exactly equivalent to calling the generated "clear" accessor method * corresponding to the oneof. */ + @CanIgnoreReturnValue Builder clearOneof(Descriptors.OneofDescriptor oneof); /** @@ -212,6 +220,7 @@ public interface Message extends MessageLite, MessageOrBuilder { * @throws IllegalArgumentException if the field is not a repeated field, or {@code * field.getContainingType() != getDescriptorForType()}. */ + @CanIgnoreReturnValue Builder setRepeatedField(Descriptors.FieldDescriptor field, int index, Object value); /** @@ -220,12 +229,15 @@ public interface Message extends MessageLite, MessageOrBuilder { * @throws IllegalArgumentException if the field is not a repeated field, or {@code * field.getContainingType() != getDescriptorForType()} */ + @CanIgnoreReturnValue Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); /** Set the {@link UnknownFieldSet} for this message. */ + @CanIgnoreReturnValue Builder setUnknownFields(UnknownFieldSet unknownFields); /** Merge some unknown fields into the {@link UnknownFieldSet} for this message. */ + @CanIgnoreReturnValue Builder mergeUnknownFields(UnknownFieldSet unknownFields); // --------------------------------------------------------------- @@ -234,30 +246,38 @@ public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) @Override + @CanIgnoreReturnValue Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException; @Override + @CanIgnoreReturnValue Builder mergeFrom(InputStream input) throws IOException; @Override + @CanIgnoreReturnValue Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; diff --git a/java/core/src/main/java/com/google/protobuf/MessageLite.java b/java/core/src/main/java/com/google/protobuf/MessageLite.java index 8c172eef8c..d6314691be 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/MessageLite.java @@ -344,7 +344,6 @@ public interface MessageLite extends MessageLiteOrBuilder { * a protobuf in the first place. * @throws IOException an I/O error reading from the stream */ - @CanIgnoreReturnValue // TODO(kak): should this be @CheckReturnValue instead? boolean mergeDelimitedFrom(InputStream input) throws IOException; /** @@ -357,7 +356,6 @@ public interface MessageLite extends MessageLiteOrBuilder { * a protobuf in the first place. * @throws IOException an I/O error reading from the stream */ - @CanIgnoreReturnValue // TODO(kak): should this be @CheckReturnValue instead? boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; } diff --git a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java index 0254df99ac..2a4d867ae1 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java +++ b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java @@ -39,6 +39,7 @@ import java.util.Map; * * @author jonp@google.com (Jon Perlow) */ +@CheckReturnValue public interface MessageOrBuilder extends MessageLiteOrBuilder { // (From MessageLite, re-declared here only for return type covariance.) diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index bd1df8cc77..3671c31a66 100644 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -34,10 +34,6 @@ Note that the golden messages exercise every known field type, thus this test ends up exercising and verifying nearly all of the parsing and serialization code in the whole library. - -TODO(kenton): Merge with wire_format_test? It doesn't make a whole lot of -sense to call this a test of the "message" module, which only declares an -abstract interface. """ __author__ = 'gps@google.com (Gregory P. Smith)' @@ -476,6 +472,12 @@ class MessageTest(unittest.TestCase): '}\n') self.assertEqual(sub_msg.bb, 1) + def testAssignRepeatedField(self, message_module): + msg = message_module.NestedTestAllTypes() + msg.payload.repeated_int32[:] = [1, 2, 3, 4] + self.assertEqual(4, len(msg.payload.repeated_int32)) + self.assertEqual([1, 2, 3, 4], msg.payload.repeated_int32) + def testMergeFromRepeatedField(self, message_module): msg = message_module.TestAllTypes() msg.repeated_int32.append(1) diff --git a/src/google/protobuf/message_unittest.inc b/src/google/protobuf/message_unittest.inc index 19cc9c1486..7526ead779 100644 --- a/src/google/protobuf/message_unittest.inc +++ b/src/google/protobuf/message_unittest.inc @@ -422,6 +422,25 @@ TEST(MESSAGE_TEST_NAME, ParseFailsIfExtensionWireMalformed) { EXPECT_FALSE(p.ParseFromString(serialized)); } +TEST(MESSAGE_TEST_NAME, ParseFailsIfGroupFieldMalformed) { + UNITTEST::TestMutualRecursionA original, parsed; + original.mutable_bb() + ->mutable_a() + ->mutable_subgroup() + ->mutable_sub_message() + ->mutable_b() + ->set_optional_int32(-1); + + std::string data; + ASSERT_TRUE(original.SerializeToString(&data)); + // Should parse correctly. + ASSERT_TRUE(parsed.ParseFromString(data)); + // Overwriting the last byte of varint (-1) to 0xFF results in malformed wire. + data[data.size() - 2] = 0xFF; + + EXPECT_FALSE(parsed.ParseFromString(data)); +} + TEST(MESSAGE_TEST_NAME, UninitializedAndMalformed) { UNITTEST::TestRequiredForeign o, p1, p2; o.mutable_optional_message()->set_a(-1); diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc index d534411b63..b32cda8ca2 100644 --- a/src/google/protobuf/port_def.inc +++ b/src/google/protobuf/port_def.inc @@ -865,6 +865,8 @@ // Inconvenient macro names from usr/include/sys/syslimits.h in some macOS SDKs. #pragma push_macro("UID_MAX") #undef UID_MAX +#pragma push_macro("GID_MAX") +#undef GID_MAX #endif // __APPLE__ #if defined(__clang__) || PROTOBUF_GNUC_MIN(3, 0) || defined(_MSC_VER) diff --git a/src/google/protobuf/port_undef.inc b/src/google/protobuf/port_undef.inc index e880fa5c59..f8968d9a86 100644 --- a/src/google/protobuf/port_undef.inc +++ b/src/google/protobuf/port_undef.inc @@ -144,6 +144,7 @@ #pragma pop_macro("TRUE") #pragma pop_macro("FALSE") #pragma pop_macro("UID_MAX") +#pragma pop_macro("GID_MAX") #endif // __APPLE__ #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 730b06708c..a74832f787 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -454,8 +454,13 @@ class RepeatedField final { // // Typically, due to the fact that adder is a local stack variable, the // compiler will be successful in mem-to-reg transformation and the machine - // code will be loop: cmp %size, %capacity jae fallback mov dword ptr [%buffer - // + %size * 4], %val inc %size jmp loop + // code will be + // loop: + // cmp %size, %capacity + // jae fallback + // mov dword ptr [%buffer + %size * 4], %val + // inc %size + // jmp loop // // The first version executes at 7 cycles per iteration while the second // version executes at only 1 or 2 cycles. @@ -467,6 +472,8 @@ class RepeatedField final { capacity_ = repeated_field_->total_size_; buffer_ = repeated_field_->unsafe_elements(); } + FastAdderImpl(const FastAdderImpl&) = delete; + FastAdderImpl& operator=(const FastAdderImpl&) = delete; ~FastAdderImpl() { repeated_field_->current_size_ = index_; } void Add(Element val) { @@ -484,8 +491,6 @@ class RepeatedField final { int index_; int capacity_; Element* buffer_; - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdderImpl); }; // FastAdder is a wrapper for adding fields. The specialization above handles @@ -494,11 +499,12 @@ class RepeatedField final { class FastAdderImpl { public: explicit FastAdderImpl(RepeatedField* rf) : repeated_field_(rf) {} + FastAdderImpl(const FastAdderImpl&) = delete; + FastAdderImpl& operator=(const FastAdderImpl&) = delete; void Add(const Element& val) { repeated_field_->Add(val); } private: RepeatedField* repeated_field_; - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdderImpl); }; using FastAdder = FastAdderImpl<>; diff --git a/src/google/protobuf/stubs/status_macros.h b/src/google/protobuf/stubs/status_macros.h index 407ff4c280..5a2cbf06ae 100644 --- a/src/google/protobuf/stubs/status_macros.h +++ b/src/google/protobuf/stubs/status_macros.h @@ -37,9 +37,14 @@ #include #include +#include + namespace google { namespace protobuf { namespace util { +namespace status_macros_internal { +using PROTOBUF_NAMESPACE_ID::util::Status; +} // namespace status_macros_internal // Run a command that returns a util::Status. If the called code returns an // error status, return that status up out of this method too. @@ -49,7 +54,8 @@ namespace util { #define RETURN_IF_ERROR(expr) \ do { \ /* Using _status below to avoid capture problems if expr is "status". */ \ - const PROTOBUF_NAMESPACE_ID::util::Status _status = (expr); \ + const ::google::protobuf::util::status_macros_internal::Status _status = \ + (expr); \ if (PROTOBUF_PREDICT_FALSE(!_status.ok())) return _status; \ } while (0) @@ -57,7 +63,7 @@ namespace util { #define STATUS_MACROS_CONCAT_NAME_INNER(x, y) x##y #define STATUS_MACROS_CONCAT_NAME(x, y) STATUS_MACROS_CONCAT_NAME_INNER(x, y) -template +template Status DoAssignOrReturn(T& lhs, StatusOr result) { if (result.ok()) { lhs = result.value(); @@ -79,11 +85,13 @@ Status DoAssignOrReturn(T& lhs, StatusOr result) { // WARNING: ASSIGN_OR_RETURN expands into multiple statements; it cannot be used // in a single statement (e.g. as the body of an if statement without {})! #define ASSIGN_OR_RETURN(lhs, rexpr) \ - ASSIGN_OR_RETURN_IMPL( \ + ASSIGN_OR_RETURN_IMPL( \ STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr); } // namespace util } // namespace protobuf } // namespace google +#include + #endif // GOOGLE_PROTOBUF_STUBS_STATUS_H_ diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h index 20e603ea04..1ec748d1fc 100644 --- a/src/google/protobuf/stubs/statusor.h +++ b/src/google/protobuf/stubs/statusor.h @@ -72,12 +72,13 @@ #ifndef GOOGLE_PROTOBUF_STUBS_STATUSOR_H_ #define GOOGLE_PROTOBUF_STUBS_STATUSOR_H_ +#include + #include #include #include -#include - +// Must be included last. #include namespace google { @@ -85,9 +86,10 @@ namespace protobuf { namespace util { namespace statusor_internal { -template +template class StatusOr { - template friend class StatusOr; + template + friend class StatusOr; public: using value_type = T; @@ -125,14 +127,14 @@ class StatusOr { StatusOr(const StatusOr& other); // Conversion copy constructor, T must be copy constructible from U - template + template StatusOr(const StatusOr& other); // Assignment operator. StatusOr& operator=(const StatusOr& other); // Conversion assignment operator, T must be assignable from U - template + template StatusOr& operator=(const StatusOr& other); // Returns a reference to our status. If this contains a T, then @@ -143,7 +145,14 @@ class StatusOr { bool ok() const; // Returns a reference to our current value, or CHECK-fails if !this->ok(). - const T& value () const; + const T& value() const; + T& value(); + + // Returns a reference to our current value; UB if not OK. + const T& operator*() const { return value(); } + T& operator*() { return value(); } + const T* operator->() const { return &value(); } + T* operator->() { return &value(); } private: Status status_; @@ -159,17 +168,17 @@ class PROTOBUF_EXPORT StatusOrHelper { static void Crash(const util::Status& status); // Customized behavior for StatusOr vs. StatusOr - template + template struct Specialize; }; -template +template struct StatusOrHelper::Specialize { // For non-pointer T, a reference can never be nullptr. static inline bool IsValueNull(const T& /*t*/) { return false; } }; -template +template struct StatusOrHelper::Specialize { static inline bool IsValueNull(const T* t) { return t == nullptr; } }; @@ -177,7 +186,7 @@ struct StatusOrHelper::Specialize { template inline StatusOr::StatusOr() : status_(util::UnknownError("")) {} -template +template inline StatusOr::StatusOr(const Status& status) { if (status.ok()) { status_ = util::InternalError("OkStatus() is not a valid argument."); @@ -186,7 +195,7 @@ inline StatusOr::StatusOr(const Status& status) { } } -template +template inline StatusOr::StatusOr(const T& value) { if (StatusOrHelper::Specialize::IsValueNull(value)) { status_ = util::InternalError("nullptr is not a valid argument."); @@ -196,43 +205,41 @@ inline StatusOr::StatusOr(const T& value) { } } -template +template inline StatusOr::StatusOr(const StatusOr& other) - : status_(other.status_), value_(other.value_) { -} + : status_(other.status_), value_(other.value_) {} -template +template inline StatusOr& StatusOr::operator=(const StatusOr& other) { status_ = other.status_; value_ = other.value_; return *this; } -template -template +template +template inline StatusOr::StatusOr(const StatusOr& other) - : status_(other.status_), value_(other.status_.ok() ? other.value_ : T()) { -} + : status_(other.status_), value_(other.status_.ok() ? other.value_ : T()) {} -template -template +template +template inline StatusOr& StatusOr::operator=(const StatusOr& other) { status_ = other.status_; if (status_.ok()) value_ = other.value_; return *this; } -template +template inline const Status& StatusOr::status() const { return status_; } -template +template inline bool StatusOr::ok() const { return status().ok(); } -template +template inline const T& StatusOr::value() const { if (!status_.ok()) { StatusOrHelper::Crash(status_); @@ -240,6 +247,13 @@ inline const T& StatusOr::value() const { return value_; } +template +inline T& StatusOr::value() { + if (!status_.ok()) { + StatusOrHelper::Crash(status_); + } + return value_; +} } // namespace statusor_internal using ::google::protobuf::util::statusor_internal::StatusOr; diff --git a/src/google/protobuf/stubs/statusor_test.cc b/src/google/protobuf/stubs/statusor_test.cc index 403adcc0b6..17c9a4dee2 100644 --- a/src/google/protobuf/stubs/statusor_test.cc +++ b/src/google/protobuf/stubs/statusor_test.cc @@ -87,6 +87,16 @@ TEST(StatusOr, TestValueCtor) { EXPECT_EQ(kI, thing.value()); } +TEST(StatusOr, TestPtrOps) { + const int kI = 4; + StatusOr thing(kI); + EXPECT_TRUE(thing.ok()); + EXPECT_EQ(kI, *thing); + + StatusOr> thing2(thing); + EXPECT_EQ(kI, thing2->value()); +} + TEST(StatusOr, TestCopyCtorStatusOk) { const int kI = 4; StatusOr original(kI); diff --git a/src/google/protobuf/unittest_mset.proto b/src/google/protobuf/unittest_mset.proto index dd8a3afbf4..fd6aaa568a 100644 --- a/src/google/protobuf/unittest_mset.proto +++ b/src/google/protobuf/unittest_mset.proto @@ -55,6 +55,7 @@ message NestedTestMessageSetContainer { message NestedTestInt { optional fixed32 a = 1; + optional int32 b = 3; optional NestedTestInt child = 2; } diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc index ac9408d8cc..a8b68a3c76 100644 --- a/src/google/protobuf/util/json_util_test.cc +++ b/src/google/protobuf/util/json_util_test.cc @@ -30,11 +30,16 @@ #include +#include #include -#include #include +#include +#include #include +#include +#include +#include #include #include #include @@ -42,161 +47,177 @@ #include #include #include +#include #include #include +#include + +// Must be included last. +#include namespace google { namespace protobuf { namespace util { namespace { -using proto3::BAR; -using proto3::FOO; -using proto3::TestAny; -using proto3::TestEnumValue; -using proto3::TestMap; -using proto3::TestMessage; -using proto3::TestOneof; -using proto_util_converter::testing::MapIn; +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 +util::Status GetStatus(const util::StatusOr& 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. -class JsonUtilTest : public ::testing::Test { - protected: - JsonUtilTest() {} - - std::string ToJson(const Message& message, const JsonPrintOptions& options) { - std::string result; - GOOGLE_CHECK_OK(MessageToJsonString(message, &result, options)); - return result; - } - bool FromJson(const std::string& json, Message* message, - const JsonParseOptions& options) { - return JsonStringToMessage(json, message, options).ok(); - } - - bool FromJson(const std::string& json, Message* message) { - return FromJson(json, message, JsonParseOptions()); - } +util::StatusOr ToJson(const Message& message, + const JsonPrintOptions& options = {}) { + std::string result; + RETURN_IF_ERROR(MessageToJsonString(message, &result, options)); + return result; +} - std::unique_ptr resolver_; -}; +util::Status FromJson(StringPiece json, Message* message, + const JsonParseOptions& options = {}) { + return JsonStringToMessage(json, message, options); +} -TEST_F(JsonUtilTest, TestWhitespaces) { +TEST(JsonUtilTest, TestWhitespaces) { TestMessage m; m.mutable_message_value(); + EXPECT_THAT(ToJson(m), IsOkAndHolds("{\"messageValue\":{}}")); + JsonPrintOptions options; - EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); options.add_whitespace = true; - EXPECT_EQ( - "{\n" - " \"messageValue\": {}\n" - "}\n", - ToJson(m, options)); + EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\n" + " \"messageValue\": {}\n" + "}\n")); } -TEST_F(JsonUtilTest, TestDefaultValues) { +TEST(JsonUtilTest, TestDefaultValues) { TestMessage m; + EXPECT_THAT(ToJson(m), IsOkAndHolds("{}")); + JsonPrintOptions options; - EXPECT_EQ("{}", ToJson(m, options)); options.always_print_primitive_fields = true; - EXPECT_EQ( - "{\"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\":[]" - "}", - ToJson(m, options)); + 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_EQ( - "{\"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\":[]" - "}", - ToJson(m, options)); + 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_EQ( - "{\"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\":[]" - "}", - ToJson(m, options)); + 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_F(JsonUtilTest, TestPreserveProtoFieldNames) { +TEST(JsonUtilTest, TestPreserveProtoFieldNames) { TestMessage m; m.mutable_message_value(); JsonPrintOptions options; options.preserve_proto_field_names = true; - EXPECT_EQ("{\"message_value\":{}}", ToJson(m, options)); + EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"message_value\":{}}")); } -TEST_F(JsonUtilTest, TestAlwaysPrintEnumsAsInts) { +TEST(JsonUtilTest, TestAlwaysPrintEnumsAsInts) { TestMessage orig; orig.set_enum_value(proto3::BAR); orig.add_repeated_enum_value(proto3::FOO); @@ -205,20 +226,20 @@ TEST_F(JsonUtilTest, TestAlwaysPrintEnumsAsInts) { JsonPrintOptions print_options; print_options.always_print_enums_as_ints = true; - std::string expected_json = "{\"enumValue\":1,\"repeatedEnumValue\":[0,1]}"; - EXPECT_EQ(expected_json, ToJson(orig, print_options)); + auto printed = ToJson(orig, print_options); + ASSERT_THAT(printed, + IsOkAndHolds("{\"enumValue\":1,\"repeatedEnumValue\":[0,1]}")); TestMessage parsed; - JsonParseOptions parse_options; - ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + ASSERT_OK(FromJson(*printed, &parsed)); - EXPECT_EQ(proto3::BAR, parsed.enum_value()); - EXPECT_EQ(2, parsed.repeated_enum_value_size()); - EXPECT_EQ(proto3::FOO, parsed.repeated_enum_value(0)); - EXPECT_EQ(proto3::BAR, parsed.repeated_enum_value(1)); + 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_F(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) { +TEST(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) { TestEnumValue orig; // orig.set_enum_value1(proto3::FOO) orig.set_enum_value2(proto3::FOO); @@ -228,20 +249,20 @@ TEST_F(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) { print_options.always_print_enums_as_ints = true; print_options.always_print_primitive_fields = true; - std::string expected_json = - "{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"; - EXPECT_EQ(expected_json, ToJson(orig, print_options)); + auto printed = ToJson(orig, print_options); + ASSERT_THAT( + printed, + IsOkAndHolds("{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}")); TestEnumValue parsed; - JsonParseOptions parse_options; - ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + ASSERT_OK(FromJson(*printed, &parsed)); - EXPECT_EQ(proto3::FOO, parsed.enum_value1()); - EXPECT_EQ(proto3::FOO, parsed.enum_value2()); - EXPECT_EQ(proto3::BAR, parsed.enum_value3()); + EXPECT_EQ(parsed.enum_value1(), proto3::FOO); + EXPECT_EQ(parsed.enum_value2(), proto3::FOO); + EXPECT_EQ(parsed.enum_value3(), proto3::BAR); } -TEST_F(JsonUtilTest, TestPrintProto2EnumAsIntWithDefaultValue) { +TEST(JsonUtilTest, TestPrintProto2EnumAsIntWithDefaultValue) { protobuf_unittest::TestDefaultEnumValue orig; JsonPrintOptions print_options; @@ -250,111 +271,105 @@ TEST_F(JsonUtilTest, TestPrintProto2EnumAsIntWithDefaultValue) { print_options.always_print_primitive_fields = true; // result should be int rather than string - std::string expected_json = "{\"enumValue\":2}"; - EXPECT_EQ(expected_json, ToJson(orig, print_options)); + auto printed = ToJson(orig, print_options); + ASSERT_THAT(printed, IsOkAndHolds("{\"enumValue\":2}")); protobuf_unittest::TestDefaultEnumValue parsed; - JsonParseOptions parse_options; - ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + ASSERT_OK(FromJson(*printed, &parsed)); - EXPECT_EQ(protobuf_unittest::DEFAULT, parsed.enum_value()); + EXPECT_EQ(parsed.enum_value(), protobuf_unittest::DEFAULT); } -TEST_F(JsonUtilTest, ParseMessage) { +TEST(JsonUtilTest, ParseMessage) { // Some random message but good enough to verify that the parsing wrapper // functions are working properly. - std::string input = - "{\n" - " \"int32Value\": 1234567891,\n" - " \"int64Value\": 5302428716536692736,\n" - " \"floatValue\": 3.4028235e+38,\n" - " \"repeatedInt32Value\": [1, 2],\n" - " \"messageValue\": {\n" - " \"value\": 2048\n" - " },\n" - " \"repeatedMessageValue\": [\n" - " {\"value\": 40}, {\"value\": 96}\n" - " ]\n" - "}\n"; - JsonParseOptions options; TestMessage m; - ASSERT_TRUE(FromJson(input, &m, options)); - EXPECT_EQ(1234567891, m.int32_value()); - EXPECT_EQ(5302428716536692736, m.int64_value()); - EXPECT_EQ(3.402823466e+38f, m.float_value()); - ASSERT_EQ(2, m.repeated_int32_value_size()); - EXPECT_EQ(1, m.repeated_int32_value(0)); - EXPECT_EQ(2, m.repeated_int32_value(1)); - EXPECT_EQ(2048, m.message_value().value()); - ASSERT_EQ(2, m.repeated_message_value_size()); - EXPECT_EQ(40, m.repeated_message_value(0).value()); - EXPECT_EQ(96, m.repeated_message_value(1).value()); + 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_F(JsonUtilTest, ParseMap) { +TEST(JsonUtilTest, ParseMap) { TestMap message; (*message.mutable_string_map())["hello"] = 1234; - JsonPrintOptions print_options; - JsonParseOptions parse_options; - EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, print_options)); + auto printed = ToJson(message); + ASSERT_THAT(printed, IsOkAndHolds("{\"stringMap\":{\"hello\":1234}}")); + TestMap other; - ASSERT_TRUE(FromJson(ToJson(message, print_options), &other, parse_options)); - EXPECT_EQ(message.DebugString(), other.DebugString()); + ASSERT_OK(FromJson(*printed, &other)); + EXPECT_EQ(other.DebugString(), message.DebugString()); } -TEST_F(JsonUtilTest, ParsePrimitiveMapIn) { +TEST(JsonUtilTest, ParsePrimitiveMapIn) { MapIn message; JsonPrintOptions print_options; print_options.always_print_primitive_fields = true; - JsonParseOptions parse_options; - EXPECT_EQ("{\"other\":\"\",\"things\":[],\"mapInput\":{},\"mapAny\":{}}", - ToJson(message, print_options)); + auto printed = ToJson(message, print_options); + ASSERT_THAT( + ToJson(message, print_options), + IsOkAndHolds( + "{\"other\":\"\",\"things\":[],\"mapInput\":{},\"mapAny\":{}}")); + MapIn other; - ASSERT_TRUE(FromJson(ToJson(message, print_options), &other, parse_options)); - EXPECT_EQ(message.DebugString(), other.DebugString()); + ASSERT_OK(FromJson(*printed, &other)); + EXPECT_EQ(other.DebugString(), message.DebugString()); } -TEST_F(JsonUtilTest, PrintPrimitiveOneof) { +TEST(JsonUtilTest, PrintPrimitiveOneof) { TestOneof message; JsonPrintOptions options; options.always_print_primitive_fields = true; message.mutable_oneof_message_value(); - EXPECT_EQ("{\"oneofMessageValue\":{\"value\":0}}", ToJson(message, options)); + EXPECT_THAT(ToJson(message, options), + IsOkAndHolds("{\"oneofMessageValue\":{\"value\":0}}")); message.set_oneof_int32_value(1); - EXPECT_EQ("{\"oneofInt32Value\":1}", ToJson(message, options)); + EXPECT_THAT(ToJson(message, options), + IsOkAndHolds("{\"oneofInt32Value\":1}")); } -TEST_F(JsonUtilTest, TestParseIgnoreUnknownFields) { +TEST(JsonUtilTest, TestParseIgnoreUnknownFields) { TestMessage m; JsonParseOptions options; options.ignore_unknown_fields = true; - EXPECT_TRUE(FromJson("{\"unknownName\":0}", &m, options)); + EXPECT_OK(FromJson("{\"unknownName\":0}", &m, options)); } -TEST_F(JsonUtilTest, TestParseErrors) { +TEST(JsonUtilTest, TestParseErrors) { TestMessage m; - JsonParseOptions options; // Parsing should fail if the field name can not be recognized. - EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m, options)); + EXPECT_THAT(FromJson(R"json({"unknownName": 0})json", &m), + StatusIs(util::StatusCode::kInvalidArgument)); // Parsing should fail if the value is invalid. - EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m, options)); + EXPECT_THAT(FromJson(R"json("{"int32Value": 2147483648})json", &m), + StatusIs(util::StatusCode::kInvalidArgument)); } -TEST_F(JsonUtilTest, TestDynamicMessage) { - // Some random message but good enough to test the wrapper functions. - std::string input = - "{\n" - " \"int32Value\": 1024,\n" - " \"repeatedInt32Value\": [1, 2],\n" - " \"messageValue\": {\n" - " \"value\": 2048\n" - " },\n" - " \"repeatedMessageValue\": [\n" - " {\"value\": 40}, {\"value\": 96}\n" - " ]\n" - "}\n"; - +TEST(JsonUtilTest, TestDynamicMessage) { // Create a new DescriptorPool with the same protos as the generated one. DescriptorPoolDatabase database(*DescriptorPool::generated_pool()); DescriptorPool pool(&database); @@ -363,218 +378,223 @@ TEST_F(JsonUtilTest, TestDynamicMessage) { std::unique_ptr message( factory.GetPrototype(pool.FindMessageTypeByName("proto3.TestMessage")) ->New()); - EXPECT_TRUE(FromJson(input, message.get())); + 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(1024, generated.int32_value()); - ASSERT_EQ(2, generated.repeated_int32_value_size()); - EXPECT_EQ(1, generated.repeated_int32_value(0)); - EXPECT_EQ(2, generated.repeated_int32_value(1)); - EXPECT_EQ(2048, generated.message_value().value()); - ASSERT_EQ(2, generated.repeated_message_value_size()); - EXPECT_EQ(40, generated.repeated_message_value(0).value()); - EXPECT_EQ(96, generated.repeated_message_value(1).value()); - - JsonOptions options; - EXPECT_EQ(ToJson(generated, options), ToJson(*message, options)); + 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_F(JsonUtilTest, TestParsingUnknownAnyFields) { - std::string input = - "{\n" - " \"value\": {\n" - " \"@type\": \"type.googleapis.com/proto3.TestMessage\",\n" - " \"unknown_field\": \"UNKNOWN_VALUE\",\n" - " \"string_value\": \"expected_value\"\n" - " }\n" - "}"; +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; - JsonParseOptions options; - EXPECT_FALSE(FromJson(input, &m, options)); + EXPECT_THAT(FromJson(input, &m), + StatusIs(util::StatusCode::kInvalidArgument)); + JsonParseOptions options; options.ignore_unknown_fields = true; - EXPECT_TRUE(FromJson(input, &m, options)); + EXPECT_OK(FromJson(input, &m, options)); TestMessage t; EXPECT_TRUE(m.value().UnpackTo(&t)); - EXPECT_EQ("expected_value", t.string_value()); + EXPECT_EQ(t.string_value(), "expected_value"); } -TEST_F(JsonUtilTest, TestParsingUnknownEnumsProto2) { - std::string input = - "{\n" - " \"a\": \"UNKNOWN_VALUE\"\n" - "}"; +TEST(JsonUtilTest, TestParsingUnknownEnumsProto2) { + StringPiece input = R"json({"a": "UNKNOWN_VALUE"})json"; protobuf_unittest::TestNumbers m; JsonParseOptions options; - EXPECT_FALSE(FromJson(input, &m, options)); + EXPECT_THAT(FromJson(input, &m, options), + StatusIs(util::StatusCode::kInvalidArgument)); options.ignore_unknown_fields = true; - EXPECT_TRUE(FromJson(input, &m, options)); + EXPECT_OK(FromJson(input, &m, options)); EXPECT_FALSE(m.has_a()); } -TEST_F(JsonUtilTest, TestParsingUnknownEnumsProto3) { +TEST(JsonUtilTest, TestParsingUnknownEnumsProto3) { TestMessage m; - { - JsonParseOptions options; - ASSERT_FALSE(options.ignore_unknown_fields); - std::string input = - "{\n" - " \"enum_value\":\"UNKNOWN_VALUE\"\n" - "}"; - m.set_enum_value(proto3::BAR); - EXPECT_FALSE(FromJson(input, &m, options)); - ASSERT_EQ(proto3::BAR, m.enum_value()); // Keep previous value - - options.ignore_unknown_fields = true; - EXPECT_TRUE(FromJson(input, &m, options)); - EXPECT_EQ(0, m.enum_value()); // Unknown enum value must be decoded as 0 - } - // Integer values are read as usual - { - JsonParseOptions options; - std::string input = - "{\n" - " \"enum_value\":12345\n" - "}"; - m.set_enum_value(proto3::BAR); - EXPECT_TRUE(FromJson(input, &m, options)); - ASSERT_EQ(12345, m.enum_value()); - - options.ignore_unknown_fields = true; - EXPECT_TRUE(FromJson(input, &m, options)); - EXPECT_EQ(12345, m.enum_value()); - } + StringPiece input = R"json({"enum_value":"UNKNOWN_VALUE"})json"; - // Trying to pass an object as an enum field value is always treated as an - // error - { - JsonParseOptions options; - std::string input = - "{\n" - " \"enum_value\":{}\n" - "}"; - options.ignore_unknown_fields = true; - EXPECT_FALSE(FromJson(input, &m, options)); - options.ignore_unknown_fields = false; - EXPECT_FALSE(FromJson(input, &m, options)); - } - // Trying to pass an array as an enum field value is always treated as an - // error - { - JsonParseOptions options; - std::string input = - "{\n" - " \"enum_value\":[]\n" - "}"; - EXPECT_FALSE(FromJson(input, &m, options)); - options.ignore_unknown_fields = true; - EXPECT_FALSE(FromJson(input, &m, options)); - } + 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_F(JsonUtilTest, TestParsingEnumIgnoreCase) { +TEST(JsonUtilTest, TestParsingUnknownEnumsProto3FromInt) { TestMessage m; - { - JsonParseOptions options; - std::string input = - "{\n" - " \"enum_value\":\"bar\"\n" - "}"; - m.set_enum_value(proto3::FOO); - EXPECT_FALSE(FromJson(input, &m, options)); - // Default behavior is case-sensitive, so keep previous value. - ASSERT_EQ(proto3::FOO, m.enum_value()); - } - { - JsonParseOptions options; - options.case_insensitive_enum_parsing = false; - std::string input = - "{\n" - " \"enum_value\":\"bar\"\n" - "}"; - m.set_enum_value(proto3::FOO); - EXPECT_FALSE(FromJson(input, &m, options)); - ASSERT_EQ(proto3::FOO, m.enum_value()); // Keep previous value - } - { - JsonParseOptions options; - options.case_insensitive_enum_parsing = true; - std::string input = - "{\n" - " \"enum_value\":\"bar\"\n" - "}"; - m.set_enum_value(proto3::FOO); - EXPECT_TRUE(FromJson(input, &m, options)); - ASSERT_EQ(proto3::BAR, m.enum_value()); - } + 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); } -typedef std::pair Segment; // A ZeroCopyOutputStream that writes to multiple buffers. class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { public: - explicit SegmentedZeroCopyOutputStream(std::list segments) - : segments_(segments), - last_segment_(static_cast(NULL), 0), - byte_count_(0) {} + explicit SegmentedZeroCopyOutputStream( + std::vector 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_.front(); - segments_.pop_front(); - *buffer = last_segment_.first; - *length = last_segment_.second; - byte_count_ += *length; + 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(last_segment_.data()); + *length = static_cast(last_segment_.size()); + byte_count_ += static_cast(last_segment_.size()); return true; } void BackUp(int length) override { - GOOGLE_CHECK(length <= last_segment_.second); - segments_.push_front( - Segment(last_segment_.first + last_segment_.second - length, length)); - last_segment_ = Segment(last_segment_.first, last_segment_.second - length); - byte_count_ -= length; + GOOGLE_CHECK(length <= static_cast(last_segment_.size())); + + size_t backup = last_segment_.size() - static_cast(length); + segments_.push_back(last_segment_.substr(backup)); + last_segment_ = last_segment_.substr(0, backup); + byte_count_ -= static_cast(length); } int64_t ByteCount() const override { return byte_count_; } private: - std::list segments_; - Segment last_segment_; - int64_t byte_count_; + std::vector 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 const int kOutputBufferLength = 10; + static constexpr int kOutputBufferLength = 10; // An exhaustive test takes too long, skip some combinations to make the test // run faster. - static const int kSkippedPatternCount = 7; + 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::list segments; + std::vector segments; int segment_start = 0; for (int i = 0; i < kOutputBufferLength - 1; ++i) { if (split_pattern & (1 << i)) { segments.push_back( - Segment(buffer + segment_start, i - segment_start + 1)); + StringPiece(buffer + segment_start, i - segment_start + 1)); segment_start = i + 1; } } - segments.push_back( - Segment(buffer + segment_start, kOutputBufferLength - segment_start)); + segments.push_back(StringPiece(buffer + segment_start, + kOutputBufferLength - segment_start)); // Write exactly 10 bytes through the ByteSink. std::string input_data = "0123456789"; @@ -593,7 +613,7 @@ TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { } byte_sink.Append(&input_data[start], input_data.length() - start); } - EXPECT_EQ(input_data, std::string(buffer, input_data.length())); + EXPECT_EQ(std::string(buffer, input_data.length()), input_data); } // Write only 9 bytes through the ByteSink. @@ -613,8 +633,8 @@ TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { } byte_sink.Append(&input_data[start], input_data.length() - start); } - EXPECT_EQ(input_data, std::string(buffer, input_data.length())); - EXPECT_EQ(0, buffer[input_data.length()]); + 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 @@ -641,29 +661,29 @@ TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { } } -TEST_F(JsonUtilTest, TestWrongJsonInput) { - const char json[] = "{\"unknown_field\":\"some_value\"}"; - io::ArrayInputStream input_stream(json, strlen(json)); +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"; - TypeResolver* resolver = NewTypeResolverForDescriptorPool( - "type.googleapis.com", DescriptorPool::generated_pool()); - auto result_status = util::JsonToBinaryStream(resolver, message_type, - &input_stream, &output_stream); + 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; - - EXPECT_FALSE(result_status.ok()); - EXPECT_TRUE(util::IsInvalidArgument(result_status)); } -TEST_F(JsonUtilTest, HtmlEscape) { +TEST(JsonUtilTest, HtmlEscape) { TestMessage m; m.set_string_value(""); JsonPrintOptions options; - EXPECT_EQ("{\"stringValue\":\"\\u003c/script\\u003e\"}", ToJson(m, options)); + EXPECT_THAT(ToJson(m, options), + IsOkAndHolds("{\"stringValue\":\"\\u003c/script\\u003e\"}")); } } // namespace diff --git a/third_party/benchmark b/third_party/benchmark index 5b7683f49e..0baacde361 160000 --- a/third_party/benchmark +++ b/third_party/benchmark @@ -1 +1 @@ -Subproject commit 5b7683f49e1e9223cf9927b24f6fd3d6bd82e3f8 +Subproject commit 0baacde3618ca617da95375e0af13ce1baadea47