| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <google/protobuf/util/json_util.h> |
| |
| #include <list> |
| #include <string> |
| |
| #include <google/protobuf/io/zero_copy_stream.h> |
| #include <google/protobuf/util/json_format_proto3.pb.h> |
| #include <google/protobuf/util/type_resolver.h> |
| #include <google/protobuf/util/type_resolver_util.h> |
| #include <gtest/gtest.h> |
| |
| namespace google { |
| namespace protobuf { |
| namespace util { |
| namespace { |
| |
| using proto3::FOO; |
| using proto3::BAR; |
| using proto3::TestMessage; |
| using proto3::TestMap; |
| |
| static const char kTypeUrlPrefix[] = "type.googleapis.com"; |
| |
| static string GetTypeUrl(const Descriptor* message) { |
| return string(kTypeUrlPrefix) + "/" + message->full_name(); |
| } |
| |
| // 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() { |
| resolver_.reset(NewTypeResolverForDescriptorPool( |
| kTypeUrlPrefix, DescriptorPool::generated_pool())); |
| } |
| |
| string ToJson(const Message& message, const JsonOptions& options) { |
| string result; |
| GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(), |
| GetTypeUrl(message.GetDescriptor()), |
| message.SerializeAsString(), &result, options)); |
| return result; |
| } |
| |
| bool FromJson(const string& json, Message* message) { |
| string binary; |
| if (!JsonToBinaryString(resolver_.get(), |
| GetTypeUrl(message->GetDescriptor()), json, &binary) |
| .ok()) { |
| return false; |
| } |
| return message->ParseFromString(binary); |
| } |
| |
| google::protobuf::scoped_ptr<TypeResolver> resolver_; |
| }; |
| |
| TEST_F(JsonUtilTest, TestWhitespaces) { |
| TestMessage m; |
| m.mutable_message_value(); |
| |
| JsonOptions options; |
| EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); |
| options.add_whitespace = true; |
| EXPECT_EQ( |
| "{\n" |
| " \"messageValue\": {}\n" |
| "}\n", |
| ToJson(m, options)); |
| } |
| |
| TEST_F(JsonUtilTest, TestDefaultValues) { |
| TestMessage m; |
| JsonOptions 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)); |
| } |
| |
| TEST_F(JsonUtilTest, ParseMessage) { |
| // Some random message but good enough to verify that the parsing warpper |
| // functions are working properly. |
| 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"; |
| TestMessage m; |
| ASSERT_TRUE(FromJson(input, &m)); |
| EXPECT_EQ(1024, m.int32_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()); |
| } |
| |
| TEST_F(JsonUtilTest, ParseMap) { |
| TestMap message; |
| (*message.mutable_string_map())["hello"] = 1234; |
| JsonOptions options; |
| EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, options)); |
| TestMap other; |
| ASSERT_TRUE(FromJson(ToJson(message, options), &other)); |
| EXPECT_EQ(message.DebugString(), other.DebugString()); |
| } |
| |
| TEST_F(JsonUtilTest, TestParseErrors) { |
| TestMessage m; |
| JsonOptions options; |
| // Parsing should fail if the field name can not be recognized. |
| EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m)); |
| // Parsing should fail if the value is invalid. |
| EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m)); |
| } |
| |
| typedef pair<char*, int> Segment; |
| // A ZeroCopyOutputStream that writes to multiple buffers. |
| class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { |
| public: |
| explicit SegmentedZeroCopyOutputStream(list<Segment> segments) |
| : segments_(segments), last_segment_(static_cast<char*>(NULL), 0), byte_count_(0) {} |
| |
| virtual bool Next(void** buffer, int* length) { |
| if (segments_.empty()) { |
| return false; |
| } |
| last_segment_ = segments_.front(); |
| segments_.pop_front(); |
| *buffer = last_segment_.first; |
| *length = last_segment_.second; |
| byte_count_ += *length; |
| return true; |
| } |
| |
| virtual void BackUp(int length) { |
| 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; |
| } |
| |
| virtual int64 ByteCount() const { return byte_count_; } |
| |
| private: |
| list<Segment> segments_; |
| Segment last_segment_; |
| int64 byte_count_; |
| }; |
| |
| // 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; |
| // An exhaustive test takes too long, skip some combinations to make the test |
| // run faster. |
| static const 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. |
| list<Segment> 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)); |
| segment_start = i + 1; |
| } |
| } |
| segments.push_back( |
| Segment(buffer + segment_start, kOutputBufferLength - segment_start)); |
| |
| // Write exactly 10 bytes through the ByteSink. |
| string input_data = "0123456789"; |
| for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
| input_pattern += kSkippedPatternCount) { |
| memset(buffer, 0, sizeof(buffer)); |
| { |
| SegmentedZeroCopyOutputStream output_stream(segments); |
| internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
| int start = 0; |
| for (int j = 0; j < input_data.length() - 1; ++j) { |
| if (input_pattern & (1 << j)) { |
| byte_sink.Append(&input_data[start], j - start + 1); |
| start = j + 1; |
| } |
| } |
| byte_sink.Append(&input_data[start], input_data.length() - start); |
| } |
| EXPECT_EQ(input_data, string(buffer, input_data.length())); |
| } |
| |
| // Write only 9 bytes through the ByteSink. |
| input_data = "012345678"; |
| for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
| input_pattern += kSkippedPatternCount) { |
| memset(buffer, 0, sizeof(buffer)); |
| { |
| SegmentedZeroCopyOutputStream output_stream(segments); |
| internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
| int start = 0; |
| for (int j = 0; j < input_data.length() - 1; ++j) { |
| if (input_pattern & (1 << j)) { |
| byte_sink.Append(&input_data[start], j - start + 1); |
| start = j + 1; |
| } |
| } |
| byte_sink.Append(&input_data[start], input_data.length() - start); |
| } |
| EXPECT_EQ(input_data, string(buffer, input_data.length())); |
| EXPECT_EQ(0, buffer[input_data.length()]); |
| } |
| |
| // Write 11 bytes through the ByteSink. The extra byte will just |
| // be ignored. |
| input_data = "0123456789A"; |
| for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
| input_pattern += kSkippedPatternCount) { |
| memset(buffer, 0, sizeof(buffer)); |
| { |
| SegmentedZeroCopyOutputStream output_stream(segments); |
| internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
| int start = 0; |
| for (int j = 0; j < input_data.length() - 1; ++j) { |
| if (input_pattern & (1 << j)) { |
| byte_sink.Append(&input_data[start], j - start + 1); |
| start = j + 1; |
| } |
| } |
| byte_sink.Append(&input_data[start], input_data.length() - start); |
| } |
| EXPECT_EQ(input_data.substr(0, kOutputBufferLength), |
| string(buffer, kOutputBufferLength)); |
| } |
| } |
| } |
| |
| } // namespace |
| } // namespace util |
| } // namespace protobuf |
| } // namespace google |