@@ -804,6 +804,7 @@ python_EXTRA_DIST= \
python/google/protobuf/json_format.py \
python/google/protobuf/message.py \
python/google/protobuf/message_factory.py \
+ python/google/protobuf/python_api.h \
python/google/protobuf/python_protobuf.h \
python/google/protobuf/proto_api.h \
python/google/protobuf/proto_builder.py \
@@ -30,7 +30,7 @@
#include <fstream>
#include <iostream>
-#include "benchmark/benchmark_api.h"
+#include "benchmark/benchmark.h"
#include "benchmarks.pb.h"
#include "datasets/google_message1/proto2/benchmark_message1_proto2.pb.h"
#include "datasets/google_message1/proto3/benchmark_message1_proto3.pb.h"
@@ -78,6 +78,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.cc
${protobuf_source_dir}/src/google/protobuf/compiler/python/python_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/ruby/ruby_generator.cc
+ ${protobuf_source_dir}/src/google/protobuf/compiler/scc.cc
${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.cc
${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.cc
)
@@ -153,6 +154,7 @@ set(libprotoc_headers
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_message_field.h
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_oneof.h
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h
+ ${protobuf_source_dir}/src/google/protobuf/compiler/scc.h
${protobuf_source_dir}/src/google/protobuf/compiler/subprocess.h
${protobuf_source_dir}/src/google/protobuf/compiler/zip_writer.h
@@ -177,3 +179,4 @@ set_target_properties(libprotoc PROPERTIES
OUTPUT_NAME ${LIB_PREFIX}protoc
DEBUG_POSTFIX "${protobuf_DEBUG_POSTFIX}")
add_library(protobuf::libprotoc ALIAS libprotoc)
+
@@ -63,6 +63,7 @@ set(tests_protos
google/protobuf/unittest_optimize_for.proto
google/protobuf/unittest_preserve_unknown_enum.proto
google/protobuf/unittest_preserve_unknown_enum2.proto
+ google/protobuf/unittest_proto3.proto
google/protobuf/unittest_proto3_arena.proto
google/protobuf/unittest_proto3_arena_lite.proto
google/protobuf/unittest_proto3_lite.proto
@@ -78,6 +79,7 @@ set(tests_protos
google/protobuf/util/internal/testdata/struct.proto
google/protobuf/util/internal/testdata/timestamp_duration.proto
google/protobuf/util/internal/testdata/wrappers.proto
+ google/protobuf/util/json_format.proto
google/protobuf/util/json_format_proto3.proto
google/protobuf/util/message_differencer_unittest.proto
@@ -233,10 +233,14 @@ class ConformanceJava {
}
case JSON_PAYLOAD: {
try {
- TestMessagesProto3.TestAllTypesProto3.Builder builder =
+ TestMessagesProto3.TestAllTypesProto3.Builder builder =
TestMessagesProto3.TestAllTypesProto3.newBuilder();
- JsonFormat.parser().usingTypeRegistry(typeRegistry)
- .merge(request.getJsonPayload(), builder);
+ JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(typeRegistry);
+ if (request.getTestCategory()
+ == Conformance.TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST) {
+ parser = parser.ignoringUnknownFields();
+ }
+ parser.merge(request.getJsonPayload(), builder);
testMessage = builder.build();
} catch (InvalidProtocolBufferException e) {
return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
@@ -279,6 +279,8 @@ $(protoc_outputs): protoc_middleman
$(other_language_protoc_outputs): protoc_middleman
+BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs)
CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java javac_middleman_lite conformance-java-lite conformance-csharp conformance-php conformance-php-c $(other_language_protoc_outputs)
MAINTAINERCLEANFILES = \
@@ -57,6 +57,18 @@ enum WireFormat {
JSON = 2;
+enum TestCategory {
+ UNSPECIFIED_TEST = 0;
+ BINARY_TEST = 1; // Test binary wire format.
+ JSON_TEST = 2; // Test json wire format.
+ // Similar to JSON_TEST. However, during parsing json, testee should ignore
+ // unknown fields. This feature is optional. Each implementation can descide
+ // whether to support it. See
+ // https://developers.google.com/protocol-buffers/docs/proto3#json_options
+ // for more detail.
+ JSON_IGNORE_UNKNOWN_PARSING_TEST = 3;
+}
// Represents a single test case's input. The testee should:
//
// 1. parse this proto (which should always succeed)
@@ -83,7 +95,10 @@ message ConformanceRequest {
// protobuf_test_messages.proto2.TestAllTypesProto2.
string message_type = 4;
- bool ignore_unknown_json = 5;
+ // Each test is given a specific test category. Some category may need
+ // spedific support in testee programs. Refer to the defintion of TestCategory
+ // for more information.
+ TestCategory test_category = 5;
// Represents a single test case's output.
@@ -46,6 +46,7 @@ using google::protobuf::DescriptorPool;
using google::protobuf::Message;
using google::protobuf::MessageFactory;
using google::protobuf::util::BinaryToJsonString;
+using google::protobuf::util::JsonParseOptions;
using google::protobuf::util::JsonToBinaryString;
using google::protobuf::util::NewTypeResolverForDescriptorPool;
using google::protobuf::util::Status;
@@ -112,8 +113,13 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
case ConformanceRequest::kJsonPayload: {
string proto_binary;
+ JsonParseOptions options;
+ options.ignore_unknown_fields =
+ (request.test_category() ==
+ conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
Status status = JsonToBinaryString(type_resolver, *type_url,
- request.json_payload(), &proto_binary);
+ request.json_payload(), &proto_binary,
+ options);
if (!status.ok()) {
response->set_parse_error(string("Parse error: ") +
status.error_message().as_string());
@@ -3,6 +3,7 @@
require_once("Conformance/WireFormat.php");
require_once("Conformance/ConformanceResponse.php");
require_once("Conformance/ConformanceRequest.php");
+require_once("Conformance/TestCategory.php");
require_once("Protobuf_test_messages/Proto3/ForeignMessage.php");
require_once("Protobuf_test_messages/Proto3/ForeignEnum.php");
require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3.php");
@@ -12,6 +13,7 @@ require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedEnum.php");
require_once("GPBMetadata/Conformance.php");
require_once("GPBMetadata/Google/Protobuf/TestMessagesProto3.php");
+use \Conformance\TestCategory;
use \Conformance\WireFormat;
if (!ini_get("date.timezone")) {
@@ -39,7 +41,9 @@ function doTest($request)
trigger_error("Protobuf request doesn't have specific payload type", E_USER_ERROR);
} elseif ($request->getPayload() == "json_payload") {
- $ignore_json_unknown = $request->getIgnoreUnknownJson();
+ $ignore_json_unknown =
+ ($request->getTestCategory() ==
+ TestCategory::JSON_IGNORE_UNKNOWN_PARSING_TEST);
$test_message->mergeFromJsonString($request->getJsonPayload(),
$ignore_json_unknown);
@@ -78,7 +78,11 @@ def do_test(request):
elif request.WhichOneof('payload') == 'json_payload':
try:
- json_format.Parse(request.json_payload, test_message)
+ ignore_unknown_fields = \
+ request.test_category == \
+ conformance_pb2.JSON_IGNORE_UNKNOWN_PARSING_TEST
+ json_format.Parse(request.json_payload, test_message,
+ ignore_unknown_fields)
except Exception as e:
response.parse_error = str(e)
return response
@@ -28,199 +28,83 @@
// (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 <set>
#include <stdarg.h>
#include <string>
#include "conformance.pb.h"
#include "conformance_test.h"
-#include <google/protobuf/test_messages_proto3.pb.h>
-#include <google/protobuf/test_messages_proto2.pb.h>
-#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/message.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/util/field_comparator.h>
#include <google/protobuf/util/json_util.h>
#include <google/protobuf/util/message_differencer.h>
-#include <google/protobuf/util/type_resolver_util.h>
-#include <google/protobuf/wire_format_lite.h>
-
-#include "third_party/jsoncpp/json.h"
using conformance::ConformanceRequest;
using conformance::ConformanceResponse;
using conformance::WireFormat;
-using google::protobuf::Descriptor;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::internal::WireFormatLite;
using google::protobuf::TextFormat;
using google::protobuf::util::DefaultFieldComparator;
using google::protobuf::util::MessageDifferencer;
-using google::protobuf::util::NewTypeResolverForDescriptorPool;
-using protobuf_test_messages::proto3::TestAllTypesProto3;
-using protobuf_test_messages::proto2::TestAllTypesProto2;
using std::string;
-namespace {
-static const char kTypeUrlPrefix[] = "type.googleapis.com";
-static string GetTypeUrl(const Descriptor* message) {
- return string(kTypeUrlPrefix) + "/" + message->full_name();
-}
-/* Routines for building arbitrary protos *************************************/
-// We would use CodedOutputStream except that we want more freedom to build
-// arbitrary protos (even invalid ones).
-const string empty;
-string cat(const string& a, const string& b,
- const string& c = empty,
- const string& d = empty,
- const string& e = empty,
- const string& f = empty,
- const string& g = empty,
- const string& h = empty,
- const string& i = empty,
- const string& j = empty,
- const string& k = empty,
- const string& l = empty) {
- string ret;
- ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() +
- g.size() + h.size() + i.size() + j.size() + k.size() + l.size());
- ret.append(a);
- ret.append(b);
- ret.append(c);
- ret.append(d);
- ret.append(e);
- ret.append(f);
- ret.append(g);
- ret.append(h);
- ret.append(i);
- ret.append(j);
- ret.append(k);
- ret.append(l);
- return ret;
+namespace google {
+namespace protobuf {
-// The maximum number of bytes that it takes to encode a 64-bit varint.
-#define VARINT_MAX_LEN 10
+std::set<ConformanceTestSuite*> *conformance_test_suite_set;
+GOOGLE_PROTOBUF_DECLARE_ONCE(conformance_test_suite_set_init_);
-size_t vencode64(uint64_t val, int over_encoded_bytes, char *buf) {
- if (val == 0) { buf[0] = 0; return 1; }
- size_t i = 0;
- while (val) {
- uint8_t byte = val & 0x7fU;
- val >>= 7;
- if (val || over_encoded_bytes) byte |= 0x80U;
- buf[i++] = byte;
- }
- while (over_encoded_bytes--) {
- assert(i < 10);
- uint8_t byte = over_encoded_bytes ? 0x80 : 0;
- return i;
+void DeleteConformanceTestSuiteSet() {
+ delete conformance_test_suite_set;
-string varint(uint64_t x) {
- char buf[VARINT_MAX_LEN];
- size_t len = vencode64(x, 0, buf);
- return string(buf, len);
+static void InitConformanceTestSuiteSet() {
+ conformance_test_suite_set = new std::set<ConformanceTestSuite*>();
+ internal::OnShutdown(&DeleteConformanceTestSuiteSet);
-// Encodes a varint that is |extra| bytes longer than it needs to be, but still
-// valid.
-string longvarint(uint64_t x, int extra) {
- size_t len = vencode64(x, extra, buf);
+static void InitConformanceTestSuiteSetOnce() {
+ ::google::protobuf::GoogleOnceInit(
+ &conformance_test_suite_set_init_,
+ &InitConformanceTestSuiteSet);
-// TODO: proper byte-swapping for big-endian machines.
-string fixed32(void *data) { return string(static_cast<char*>(data), 4); }
-string fixed64(void *data) { return string(static_cast<char*>(data), 8); }
-string delim(const string& buf) { return cat(varint(buf.size()), buf); }
-string u32(uint32_t u32) { return fixed32(&u32); }
-string u64(uint64_t u64) { return fixed64(&u64); }
-string flt(float f) { return fixed32(&f); }
-string dbl(double d) { return fixed64(&d); }
-string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); }
-string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); }
-string tag(uint32_t fieldnum, char wire_type) {
- return varint((fieldnum << 3) | wire_type);
+void AddTestSuite(ConformanceTestSuite* suite) {
+ InitConformanceTestSuiteSetOnce();
+ conformance_test_suite_set->insert(suite);
-string submsg(uint32_t fn, const string& buf) {
- return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) );
+const std::set<ConformanceTestSuite*>& GetTestSuiteSet() {
+ return *conformance_test_suite_set;
-#define UNKNOWN_FIELD 666
-const FieldDescriptor* GetFieldForType(FieldDescriptor::Type type,
- bool repeated, bool isProto3) {
- const Descriptor* d = isProto3 ?
- TestAllTypesProto3().GetDescriptor() : TestAllTypesProto2().GetDescriptor();
- for (int i = 0; i < d->field_count(); i++) {
- const FieldDescriptor* f = d->field(i);
- if (f->type() == type && f->is_repeated() == repeated) {
- return f;
- GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type;
- return nullptr;
-string UpperCase(string str) {
- for (int i = 0; i < str.size(); i++) {
- str[i] = toupper(str[i]);
- return str;
-} // anonymous namespace
-namespace google {
-namespace protobuf {
ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting(
- ConformanceLevel level, conformance::WireFormat input_format,
- conformance::WireFormat output_format, bool is_proto3,
+ ConformanceLevel level,
+ conformance::WireFormat input_format,
+ conformance::WireFormat output_format,
+ conformance::TestCategory test_category,
+ const Message& prototype_message,
const string& test_name, const string& input)
- : level_(level), input_format_(input_format),
- output_format_(output_format), is_proto3_(is_proto3) {
- auto newTestMessage = [&is_proto3]() {
- Message* newMessage;
- if (is_proto3) {
- newMessage = new TestAllTypesProto3;
- } else {
- newMessage = new TestAllTypesProto2;
- return newMessage;
- };
- string input_format_string;
- string output_format_string;
- string rname = is_proto3 ? ".Proto3" : ".Proto2";
+ : level_(level),
+ input_format_(input_format),
+ output_format_(output_format),
+ prototype_message_(prototype_message),
+ test_name_(test_name) {
switch (input_format) {
case conformance::PROTOBUF: {
request_.set_protobuf_payload(input);
- input_format_string = ".ProtobufInput.";
break;
case conformance::JSON: {
request_.set_json_payload(input);
- input_format_string = ".JsonInput.";
@@ -228,39 +112,74 @@ ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting(
GOOGLE_LOG(FATAL) << "Unspecified input format";
- switch (output_format) {
- case conformance::PROTOBUF: {
- output_format_string = ".ProtobufOutput";
- break;
+ request_.set_test_category(test_category);
- case conformance::JSON: {
- output_format_string = ".JsonOutput";
+ request_.set_message_type(prototype_message.GetDescriptor()->full_name());
+ request_.set_requested_output_format(output_format);
+Message* ConformanceTestSuite::ConformanceRequestSetting::
+ GetTestMessage() const {
+ return prototype_message_.New();
+string ConformanceTestSuite::ConformanceRequestSetting::
+ GetTestName() const {
+ string rname =
+ prototype_message_.GetDescriptor()->file()->syntax() ==
+ FileDescriptor::SYNTAX_PROTO3 ? "Proto3" : "Proto2";
+ return StrCat(ConformanceLevelToString(level_), ".",
+ rname, ".",
+ InputFormatString(input_format_),
+ ".", test_name_, ".",
+ OutputFormatString(output_format_));
+ ConformanceLevelToString(
+ ConformanceLevel level) const {
+ switch (level) {
+ case REQUIRED: return "Required";
+ case RECOMMENDED: return "Recommended";
+ GOOGLE_LOG(FATAL) << "Unknown value: " << level;
+ return "";
+ InputFormatString(conformance::WireFormat format) const {
+ switch (format) {
+ case conformance::PROTOBUF:
+ return "ProtobufInput";
+ case conformance::JSON:
+ return "JsonInput";
default:
GOOGLE_LOG(FATAL) << "Unspecified output format";
- test_name_ = ConformanceLevelToString(level) + rname +
- input_format_string + test_name +
- output_format_string;
- request_.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3");
- request_.set_message_type("protobuf_test_messages.proto2.TestAllTypesProto2");
+ OutputFormatString(conformance::WireFormat format) const {
+ return "ProtobufOutput";
+ return "JsonOutput";
+ default:
+ GOOGLE_LOG(FATAL) << "Unspecified output format";
- request_.set_requested_output_format(output_format);
-Message* ConformanceTestSuite::ConformanceRequestSetting::GetTestMessage() const {
- if (is_proto3_) {
- return new TestAllTypesProto3();
- return new TestAllTypesProto2();
+void ConformanceTestSuite::SetFailureList(
+ const string& filename,
+ const std::vector<string>& failure_list) {
+ failure_list_filename_ = filename;
+ expected_to_fail_.clear();
+ std::copy(failure_list.begin(), failure_list.end(),
+ std::inserter(expected_to_fail_, expected_to_fail_.end()));
void ConformanceTestSuite::ReportSuccess(const string& test_name) {
@@ -309,41 +228,6 @@ void ConformanceTestSuite::ReportSkip(const string& test_name,
skipped_.insert(test_name);
-string ConformanceTestSuite::ConformanceLevelToString(ConformanceLevel level) {
- switch (level) {
- case REQUIRED: return "Required";
- case RECOMMENDED: return "Recommended";
- GOOGLE_LOG(FATAL) << "Unknown value: " << level;
- return "";
-void ConformanceTestSuite::RunTest(const string& test_name,
- const ConformanceRequest& request,
- ConformanceResponse* response) {
- if (test_names_.insert(test_name).second == false) {
- GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name;
- string serialized_request;
- string serialized_response;
- request.SerializeToString(&serialized_request);
- runner_->RunTest(test_name, serialized_request, &serialized_response);
- if (!response->ParseFromString(serialized_response)) {
- response->Clear();
- response->set_runtime_error("response proto could not be parsed.");
- if (verbose_) {
- StringAppendF(&output_, "conformance test: name=%s, request=%s, response=%s\n",
- test_name.c_str(),
- request.ShortDebugString().c_str(),
- response->ShortDebugString().c_str());
void ConformanceTestSuite::RunValidInputTest(
const ConformanceRequestSetting& setting,
const string& equivalent_text_format) {
@@ -459,348 +343,38 @@ void ConformanceTestSuite::RunValidBinaryInputTest(
differences.c_str());
-void ConformanceTestSuite::ExpectParseFailureForProtoWithProtoVersion (
- const string& proto, const string& test_name, ConformanceLevel level,
- bool isProto3) {
- ConformanceRequest request;
- ConformanceResponse response;
- request.set_protobuf_payload(proto);
- if (isProto3) {
- request.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3");
- request.set_message_type("protobuf_test_messages.proto2.TestAllTypesProto2");
- string effective_test_name = ConformanceLevelToString(level) +
- (isProto3 ? ".Proto3" : ".Proto2") +
- ".ProtobufInput." + test_name;
- // We don't expect output, but if the program erroneously accepts the protobuf
- // we let it send its response as this. We must not leave it unspecified.
- request.set_requested_output_format(conformance::PROTOBUF);
- RunTest(effective_test_name, request, &response);
- if (response.result_case() == ConformanceResponse::kParseError) {
- ReportSuccess(effective_test_name);
- } else if (response.result_case() == ConformanceResponse::kSkipped) {
- ReportSkip(effective_test_name, request, response);
- ReportFailure(effective_test_name, level, request, response,
- "Should have failed to parse, but didn't.");
-// Expect that this precise protobuf will cause a parse error.
-void ConformanceTestSuite::ExpectParseFailureForProto(
- const string& proto, const string& test_name, ConformanceLevel level) {
- ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, true);
- ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, false);
-// Expect that this protobuf will cause a parse error, even if it is followed
-// by valid protobuf data. We can try running this twice: once with this
-// data verbatim and once with this data followed by some valid data.
-//
-// TODO(haberman): implement the second of these.
-void ConformanceTestSuite::ExpectHardParseFailureForProto(
- return ExpectParseFailureForProto(proto, test_name, level);
-void ConformanceTestSuite::RunValidJsonTest(
- const string& test_name, ConformanceLevel level, const string& input_json,
- const string& equivalent_text_format) {
- ConformanceRequestSetting setting1(
- level, conformance::JSON, conformance::PROTOBUF,
- true, test_name, input_json);
- RunValidInputTest(setting1, equivalent_text_format);
- ConformanceRequestSetting setting2(
- level, conformance::JSON, conformance::JSON,
- RunValidInputTest(setting2, equivalent_text_format);
-void ConformanceTestSuite::RunValidJsonTestWithProtobufInput(
- const string& test_name, ConformanceLevel level, const TestAllTypesProto3& input,
- ConformanceRequestSetting setting(
- level, conformance::PROTOBUF, conformance::JSON,
- true, test_name, input.SerializeAsString());
- RunValidInputTest(setting, equivalent_text_format);
-void ConformanceTestSuite::RunValidJsonIgnoreUnknownTest(
- setting.SetIgnoreUnknownJson(true);
-void ConformanceTestSuite::RunValidProtobufTest(
- const string& test_name, ConformanceLevel level,
- const string& input_protobuf, const string& equivalent_text_format,
- level, conformance::PROTOBUF, conformance::PROTOBUF,
- isProto3, test_name, input_protobuf);
- true, test_name, input_protobuf);
-void ConformanceTestSuite::RunValidBinaryProtobufTest(
- const string& input_protobuf, bool isProto3) {
- RunValidBinaryInputTest(setting, input_protobuf);
-void ConformanceTestSuite::RunValidProtobufTestWithMessage(
- const string& test_name, ConformanceLevel level, const Message *input,
- const string& equivalent_text_format, bool isProto3) {
- RunValidProtobufTest(test_name, level, input->SerializeAsString(), equivalent_text_format, isProto3);
-// According to proto3 JSON specification, JSON serializers follow more strict
-// rules than parsers (e.g., a serializer must serialize int32 values as JSON
-// numbers while the parser is allowed to accept them as JSON strings). This
-// method allows strict checking on a proto3 JSON serializer by inspecting
-// the JSON output directly.
-void ConformanceTestSuite::RunValidJsonTestWithValidator(
- const Validator& validator) {
- request.set_json_payload(input_json);
- request.set_requested_output_format(conformance::JSON);
- ".Proto3.JsonInput." + test_name + ".Validator";
- if (response.result_case() == ConformanceResponse::kSkipped) {
- return;
- if (response.result_case() != ConformanceResponse::kJsonPayload) {
- "Expected JSON payload but got type %d.",
- response.result_case());
- Json::Reader reader;
- Json::Value value;
- if (!reader.parse(response.json_payload(), value)) {
- "JSON payload cannot be parsed as valid JSON: %s",
- reader.getFormattedErrorMessages().c_str());
- if (!validator(value)) {
- "JSON payload validation failed.");
-void ConformanceTestSuite::ExpectParseFailureForJson(
- const string& test_name, ConformanceLevel level, const string& input_json) {
- string effective_test_name =
- ConformanceLevelToString(level) + ".Proto3.JsonInput." + test_name;
-void ConformanceTestSuite::ExpectSerializeFailureForJson(
- const string& test_name, ConformanceLevel level, const string& text_format) {
- TestAllTypesProto3 payload_message;
- GOOGLE_CHECK(
- TextFormat::ParseFromString(text_format, &payload_message))
- << "Failed to parse: " << text_format;
- request.set_protobuf_payload(payload_message.SerializeAsString());
- ConformanceLevelToString(level) + "." + test_name + ".JsonOutput";
- if (response.result_case() == ConformanceResponse::kSerializeError) {
- "Should have failed to serialize, but didn't.");
+void ConformanceTestSuite::RunTest(const string& test_name,
+ const ConformanceRequest& request,
+ ConformanceResponse* response) {
+ if (test_names_.insert(test_name).second == false) {
+ GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name;
-//TODO: proto2?
-void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) {
- // Incomplete values for each wire type.
- static const string incompletes[6] = {
- string("\x80"), // VARINT
- string("abcdefg"), // 64BIT
- string("\x80"), // DELIMITED (partial length)
- string(), // START_GROUP (no value required)
- string(), // END_GROUP (no value required)
- string("abc") // 32BIT
- const FieldDescriptor* field = GetFieldForType(type, false, true);
- const FieldDescriptor* rep_field = GetFieldForType(type, true, true);
- WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
- static_cast<WireFormatLite::FieldType>(type));
- const string& incomplete = incompletes[wire_type];
- const string type_name =
- UpperCase(string(".") + FieldDescriptor::TypeName(type));
- ExpectParseFailureForProto(
- tag(field->number(), wire_type),
- "PrematureEofBeforeKnownNonRepeatedValue" + type_name, REQUIRED);
- tag(rep_field->number(), wire_type),
- "PrematureEofBeforeKnownRepeatedValue" + type_name, REQUIRED);
- tag(UNKNOWN_FIELD, wire_type),
- "PrematureEofBeforeUnknownValue" + type_name, REQUIRED);
- cat( tag(field->number(), wire_type), incomplete ),
- "PrematureEofInsideKnownNonRepeatedValue" + type_name, REQUIRED);
- cat( tag(rep_field->number(), wire_type), incomplete ),
- "PrematureEofInsideKnownRepeatedValue" + type_name, REQUIRED);
- cat( tag(UNKNOWN_FIELD, wire_type), incomplete ),
- "PrematureEofInsideUnknownValue" + type_name, REQUIRED);
- if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
- cat( tag(field->number(), wire_type), varint(1) ),
- "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name,
- REQUIRED);
- cat( tag(rep_field->number(), wire_type), varint(1) ),
- "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name,
- // EOF in the middle of delimited data for unknown value.
- cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ),
- "PrematureEofInDelimitedDataForUnknownValue" + type_name, REQUIRED);
- if (type == FieldDescriptor::TYPE_MESSAGE) {
- // Submessage ends in the middle of a value.
- string incomplete_submsg =
- cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT),
- incompletes[WireFormatLite::WIRETYPE_VARINT] );
- ExpectHardParseFailureForProto(
- cat( tag(field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED),
- varint(incomplete_submsg.size()),
- incomplete_submsg ),
- "PrematureEofInSubmessageValue" + type_name, REQUIRED);
- } else if (type != FieldDescriptor::TYPE_GROUP) {
- // Non-delimited, non-group: eligible for packing.
+ string serialized_request;
+ string serialized_response;
+ request.SerializeToString(&serialized_request);
- // Packed region ends in the middle of a value.
- cat(tag(rep_field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED),
- varint(incomplete.size()), incomplete),
- "PrematureEofInPackedFieldValue" + type_name, REQUIRED);
+ runner_->RunTest(test_name, serialized_request, &serialized_response);
- // EOF in the middle of packed region.
- varint(1)),
- "PrematureEofInPackedField" + type_name, REQUIRED);
+ if (!response->ParseFromString(serialized_response)) {
+ response->Clear();
+ response->set_runtime_error("response proto could not be parsed.");
-void ConformanceTestSuite::TestValidDataForType(
- FieldDescriptor::Type type,
- std::vector<std::pair<std::string, std::string>> values) {
- for (int isProto3 = 0; isProto3 < 2; isProto3++) {
- const FieldDescriptor* field = GetFieldForType(type, false, isProto3);
- const FieldDescriptor* rep_field = GetFieldForType(type, true, isProto3);
- RunValidProtobufTest("ValidDataScalar" + type_name, REQUIRED,
- cat(tag(field->number(), wire_type), values[0].first),
- field->name() + ": " + values[0].second, isProto3);
- string proto;
- string text = field->name() + ": " + values.back().second;
- for (size_t i = 0; i < values.size(); i++) {
- proto += cat(tag(field->number(), wire_type), values[i].first);
- RunValidProtobufTest("RepeatedScalarSelectsLast" + type_name, REQUIRED,
- proto, text, isProto3);
- proto.clear();
- text.clear();
- proto += cat(tag(rep_field->number(), wire_type), values[i].first);
- text += rep_field->name() + ": " + values[i].second + " ";
- RunValidProtobufTest("ValidDataRepeated" + type_name, REQUIRED,
+ if (verbose_) {
+ StringAppendF(&output_,
+ "conformance test: name=%s, request=%s, response=%s\n",
+ test_name.c_str(),
+ request.ShortDebugString().c_str(),
+ response->ShortDebugString().c_str());
-void ConformanceTestSuite::SetFailureList(const string& filename,
- const std::vector<string>& failure_list) {
- failure_list_filename_ = filename;
- expected_to_fail_.clear();
- std::copy(failure_list.begin(), failure_list.end(),
- std::inserter(expected_to_fail_, expected_to_fail_.end()));
-bool ConformanceTestSuite::CheckSetEmpty(const std::set<string>& set_to_check,
- const std::string& write_to_file,
- const std::string& msg) {
+bool ConformanceTestSuite::CheckSetEmpty(
+ const std::set<string>& set_to_check,
+ const std::string& write_to_file,
+ const std::string& msg) {
if (set_to_check.empty()) {
return true;
} else {
@@ -829,70 +403,8 @@ bool ConformanceTestSuite::CheckSetEmpty(const std::set<string>& set_to_check,
-// TODO: proto2?
-void ConformanceTestSuite::TestIllegalTags() {
- // field num 0 is illegal
- string nullfield[] = {
- "\1DEADBEEF",
- "\2\1\1",
- "\3\4",
- "\5DEAD"
- for (int i = 0; i < 4; i++) {
- string name = "IllegalZeroFieldNum_Case_0";
- name.back() += i;
- ExpectParseFailureForProto(nullfield[i], name, REQUIRED);
-template <class MessageType>
-void ConformanceTestSuite::TestOneofMessage (MessageType &message,
- message.set_oneof_uint32(0);
- RunValidProtobufTestWithMessage(
- "OneofZeroUint32", RECOMMENDED, &message, "oneof_uint32: 0", isProto3);
- message.mutable_oneof_nested_message()->set_a(0);
- "OneofZeroMessage", RECOMMENDED, &message,
- isProto3 ? "oneof_nested_message: {}" : "oneof_nested_message: {a: 0}",
- isProto3);
- message.mutable_oneof_nested_message()->set_a(1);
- "OneofZeroMessageSetTwice", RECOMMENDED, &message,
- "oneof_nested_message: {a: 1}",
- message.set_oneof_string("");
- "OneofZeroString", RECOMMENDED, &message, "oneof_string: \"\"", isProto3);
- message.set_oneof_bytes("");
- "OneofZeroBytes", RECOMMENDED, &message, "oneof_bytes: \"\"", isProto3);
- message.set_oneof_bool(false);
- "OneofZeroBool", RECOMMENDED, &message, "oneof_bool: false", isProto3);
- message.set_oneof_uint64(0);
- "OneofZeroUint64", RECOMMENDED, &message, "oneof_uint64: 0", isProto3);
- message.set_oneof_float(0.0f);
- "OneofZeroFloat", RECOMMENDED, &message, "oneof_float: 0", isProto3);
- message.set_oneof_double(0.0);
- "OneofZeroDouble", RECOMMENDED, &message, "oneof_double: 0", isProto3);
- message.set_oneof_enum(MessageType::FOO);
- "OneofZeroEnum", RECOMMENDED, &message, "oneof_enum: FOO", isProto3);
-void ConformanceTestSuite::TestUnknownMessage(MessageType& message,
- message.ParseFromString("\xA8\x1F\x01");
- RunValidBinaryProtobufTest("UnknownVarint", REQUIRED,
- message.SerializeAsString(), isProto3);
-bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
- std::string* output) {
+bool ConformanceTestSuite::RunSuite(
+ ConformanceTestRunner* runner, std::string* output) {
runner_ = runner;
successes_ = 0;
expected_failures_ = 0;
@@ -900,1714 +412,10 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
test_names_.clear();
unexpected_failing_tests_.clear();
unexpected_succeeding_tests_.clear();
- type_resolver_.reset(NewTypeResolverForDescriptorPool(
- kTypeUrlPrefix, DescriptorPool::generated_pool()));
- type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor());
output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n";
- for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) {
- if (i == FieldDescriptor::TYPE_GROUP) continue;
- TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i));
- TestIllegalTags();
- int64 kInt64Min = -9223372036854775808ULL;
- int64 kInt64Max = 9223372036854775807ULL;
- uint64 kUint64Max = 18446744073709551615ULL;
- int32 kInt32Max = 2147483647;
- int32 kInt32Min = -2147483648;
- uint32 kUint32Max = 4294967295UL;
- TestValidDataForType(FieldDescriptor::TYPE_DOUBLE, {
- {dbl(0.1), "0.1"},
- {dbl(1.7976931348623157e+308), "1.7976931348623157e+308"},
- {dbl(2.22507385850720138309e-308), "2.22507385850720138309e-308"}
- });
- TestValidDataForType(FieldDescriptor::TYPE_FLOAT, {
- {flt(0.1), "0.1"},
- {flt(1.00000075e-36), "1.00000075e-36"},
- {flt(3.402823e+38), "3.402823e+38"}, // 3.40282347e+38
- {flt(1.17549435e-38f), "1.17549435e-38"}
- TestValidDataForType(FieldDescriptor::TYPE_INT64, {
- {varint(12345), "12345"},
- {varint(kInt64Max), std::to_string(kInt64Max)},
- {varint(kInt64Min), std::to_string(kInt64Min)}
- TestValidDataForType(FieldDescriptor::TYPE_UINT64, {
- {varint(kUint64Max), std::to_string(kUint64Max)},
- {varint(0), "0"}
- TestValidDataForType(FieldDescriptor::TYPE_INT32, {
- {longvarint(12345, 2), "12345"},
- {longvarint(12345, 7), "12345"},
- {varint(kInt32Max), std::to_string(kInt32Max)},
- {varint(kInt32Min), std::to_string(kInt32Min)},
- {varint(1LL << 33), std::to_string(static_cast<int32>(1LL << 33))},
- {varint((1LL << 33) - 1),
- std::to_string(static_cast<int32>((1LL << 33) - 1))},
- TestValidDataForType(FieldDescriptor::TYPE_UINT32, {
- {varint(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX
- {varint(0), "0"},
- {varint(1LL << 33), std::to_string(static_cast<uint32>(1LL << 33))},
- std::to_string(static_cast<uint32>((1LL << 33) - 1))},
- TestValidDataForType(FieldDescriptor::TYPE_FIXED64, {
- {u64(12345), "12345"},
- {u64(kUint64Max), std::to_string(kUint64Max)},
- {u64(0), "0"}
- TestValidDataForType(FieldDescriptor::TYPE_FIXED32, {
- {u32(12345), "12345"},
- {u32(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX
- {u32(0), "0"}
- TestValidDataForType(FieldDescriptor::TYPE_SFIXED64, {
- {u64(kInt64Max), std::to_string(kInt64Max)},
- {u64(kInt64Min), std::to_string(kInt64Min)}
- TestValidDataForType(FieldDescriptor::TYPE_SFIXED32, {
- {u32(kInt32Max), std::to_string(kInt32Max)},
- {u32(kInt32Min), std::to_string(kInt32Min)}
- TestValidDataForType(FieldDescriptor::TYPE_BOOL, {
- {varint(1), "true"},
- {varint(0), "false"},
- {varint(12345678), "true"}
- TestValidDataForType(FieldDescriptor::TYPE_SINT32, {
- {zz32(12345), "12345"},
- {zz32(kInt32Max), std::to_string(kInt32Max)},
- {zz32(kInt32Min), std::to_string(kInt32Min)}
- TestValidDataForType(FieldDescriptor::TYPE_SINT64, {
- {zz64(12345), "12345"},
- {zz64(kInt64Max), std::to_string(kInt64Max)},
- {zz64(kInt64Min), std::to_string(kInt64Min)}
- // TODO(haberman):
- // TestValidDataForType(FieldDescriptor::TYPE_STRING
- // TestValidDataForType(FieldDescriptor::TYPE_GROUP
- // TestValidDataForType(FieldDescriptor::TYPE_MESSAGE
- // TestValidDataForType(FieldDescriptor::TYPE_BYTES
- // TestValidDataForType(FieldDescriptor::TYPE_ENUM
- RunValidJsonTest("HelloWorld", REQUIRED,
- "{\"optionalString\":\"Hello, World!\"}",
- "optional_string: 'Hello, World!'");
- // NOTE: The spec for JSON support is still being sorted out, these may not
- // all be correct.
- // Test field name conventions.
- RunValidJsonTest(
- "FieldNameInSnakeCase", REQUIRED,
- R"({
- "fieldname1": 1,
- "fieldName2": 2,
- "FieldName3": 3,
- "fieldName4": 4
- })",
- R"(
- fieldname1: 1
- field_name2: 2
- _field_name3: 3
- field__name4_: 4
- )");
- "FieldNameWithNumbers", REQUIRED,
- "field0name5": 5,
- "field0Name6": 6
- field0name5: 5
- field_0_name6: 6
- "FieldNameWithMixedCases", REQUIRED,
- "fieldName7": 7,
- "FieldName8": 8,
- "fieldName9": 9,
- "FieldName10": 10,
- "FIELDNAME11": 11,
- "FIELDName12": 12
- fieldName7: 7
- FieldName8: 8
- field_Name9: 9
- Field_Name10: 10
- FIELD_NAME11: 11
- FIELD_name12: 12
- "FieldNameWithDoubleUnderscores", RECOMMENDED,
- "FieldName13": 13,
- "FieldName14": 14,
- "fieldName15": 15,
- "fieldName16": 16,
- "fieldName17": 17,
- "FieldName18": 18
- __field_name13: 13
- __Field_name14: 14
- field__name15: 15
- field__Name16: 16
- field_name17__: 17
- Field_name18__: 18
- // Using the original proto field name in JSON is also allowed.
- "OriginalProtoFieldName", REQUIRED,
- "field_name2": 2,
- "_field_name3": 3,
- "field__name4_": 4,
- "field_0_name6": 6,
- "field_Name9": 9,
- "Field_Name10": 10,
- "FIELD_NAME11": 11,
- "FIELD_name12": 12,
- "__field_name13": 13,
- "__Field_name14": 14,
- "field__name15": 15,
- "field__Name16": 16,
- "field_name17__": 17,
- "Field_name18__": 18
- // Field names can be escaped.
- "FieldNameEscaped", REQUIRED,
- R"({"fieldn\u0061me1": 1})",
- "fieldname1: 1");
- // String ends with escape character.
- ExpectParseFailureForJson(
- "StringEndsWithEscapeChar", RECOMMENDED,
- "{\"optionalString\": \"abc\\");
- // Field names must be quoted (or it's not valid JSON).
- "FieldNameNotQuoted", RECOMMENDED,
- "{fieldname1: 1}");
- // Trailing comma is not allowed (not valid JSON).
- "TrailingCommaInAnObject", RECOMMENDED,
- R"({"fieldname1":1,})");
- "TrailingCommaInAnObjectWithSpace", RECOMMENDED,
- R"({"fieldname1":1 ,})");
- "TrailingCommaInAnObjectWithSpaceCommaSpace", RECOMMENDED,
- R"({"fieldname1":1 , })");
- "TrailingCommaInAnObjectWithNewlines", RECOMMENDED,
- "fieldname1":1,
- })");
- // JSON doesn't support comments.
- "JsonWithComments", RECOMMENDED,
- // This is a comment.
- "fieldname1": 1
- // JSON spec says whitespace doesn't matter, so try a few spacings to be sure.
- "OneLineNoSpaces", RECOMMENDED,
- "{\"optionalInt32\":1,\"optionalInt64\":2}",
- optional_int32: 1
- optional_int64: 2
- "OneLineWithSpaces", RECOMMENDED,
- "{ \"optionalInt32\" : 1 , \"optionalInt64\" : 2 }",
- "MultilineNoSpaces", RECOMMENDED,
- "{\n\"optionalInt32\"\n:\n1\n,\n\"optionalInt64\"\n:\n2\n}",
- "MultilineWithSpaces", RECOMMENDED,
- "{\n \"optionalInt32\" : 1\n ,\n \"optionalInt64\" : 2\n}\n",
- // Missing comma between key/value pairs.
- "MissingCommaOneLine", RECOMMENDED,
- "{ \"optionalInt32\": 1 \"optionalInt64\": 2 }");
- "MissingCommaMultiline", RECOMMENDED,
- "{\n \"optionalInt32\": 1\n \"optionalInt64\": 2\n}");
- // Duplicated field names are not allowed.
- "FieldNameDuplicate", RECOMMENDED,
- "optionalNestedMessage": {a: 1},
- "optionalNestedMessage": {}
- "FieldNameDuplicateDifferentCasing1", RECOMMENDED,
- "optional_nested_message": {a: 1},
- "FieldNameDuplicateDifferentCasing2", RECOMMENDED,
- "optional_nested_message": {}
- // Serializers should use lowerCamelCase by default.
- RunValidJsonTestWithValidator(
- "FieldNameInLowerCamelCase", REQUIRED,
- [](const Json::Value& value) {
- return value.isMember("fieldname1") &&
- value.isMember("fieldName2") &&
- value.isMember("FieldName3") &&
- value.isMember("fieldName4");
- return value.isMember("field0name5") &&
- value.isMember("field0Name6");
- return value.isMember("fieldName7") &&
- value.isMember("FieldName8") &&
- value.isMember("fieldName9") &&
- value.isMember("FieldName10") &&
- value.isMember("FIELDNAME11") &&
- value.isMember("FIELDName12");
- return value.isMember("FieldName13") &&
- value.isMember("FieldName14") &&
- value.isMember("fieldName15") &&
- value.isMember("fieldName16") &&
- value.isMember("fieldName17") &&
- value.isMember("FieldName18");
- // Integer fields.
- "Int32FieldMaxValue", REQUIRED,
- R"({"optionalInt32": 2147483647})",
- "optional_int32: 2147483647");
- "Int32FieldMinValue", REQUIRED,
- R"({"optionalInt32": -2147483648})",
- "optional_int32: -2147483648");
- "Uint32FieldMaxValue", REQUIRED,
- R"({"optionalUint32": 4294967295})",
- "optional_uint32: 4294967295");
- "Int64FieldMaxValue", REQUIRED,
- R"({"optionalInt64": "9223372036854775807"})",
- "optional_int64: 9223372036854775807");
- "Int64FieldMinValue", REQUIRED,
- R"({"optionalInt64": "-9223372036854775808"})",
- "optional_int64: -9223372036854775808");
- "Uint64FieldMaxValue", REQUIRED,
- R"({"optionalUint64": "18446744073709551615"})",
- "optional_uint64: 18446744073709551615");
- // While not the largest Int64, this is the largest
- // Int64 which can be exactly represented within an
- // IEEE-754 64-bit float, which is the expected level
- // of interoperability guarantee. Larger values may
- // work in some implementations, but should not be
- // relied upon.
- "Int64FieldMaxValueNotQuoted", REQUIRED,
- R"({"optionalInt64": 9223372036854774784})",
- "optional_int64: 9223372036854774784");
- "Int64FieldMinValueNotQuoted", REQUIRED,
- R"({"optionalInt64": -9223372036854775808})",
- // Largest interoperable Uint64; see comment above
- // for Int64FieldMaxValueNotQuoted.
- "Uint64FieldMaxValueNotQuoted", REQUIRED,
- R"({"optionalUint64": 18446744073709549568})",
- "optional_uint64: 18446744073709549568");
- // Values can be represented as JSON strings.
- "Int32FieldStringValue", REQUIRED,
- R"({"optionalInt32": "2147483647"})",
- "Int32FieldStringValueEscaped", REQUIRED,
- R"({"optionalInt32": "2\u003147483647"})",
- // Parsers reject out-of-bound integer values.
- "Int32FieldTooLarge", REQUIRED,
- R"({"optionalInt32": 2147483648})");
- "Int32FieldTooSmall", REQUIRED,
- R"({"optionalInt32": -2147483649})");
- "Uint32FieldTooLarge", REQUIRED,
- R"({"optionalUint32": 4294967296})");
- "Int64FieldTooLarge", REQUIRED,
- R"({"optionalInt64": "9223372036854775808"})");
- "Int64FieldTooSmall", REQUIRED,
- R"({"optionalInt64": "-9223372036854775809"})");
- "Uint64FieldTooLarge", REQUIRED,
- R"({"optionalUint64": "18446744073709551616"})");
- // Parser reject non-integer numeric values as well.
- "Int32FieldNotInteger", REQUIRED,
- R"({"optionalInt32": 0.5})");
- "Uint32FieldNotInteger", REQUIRED,
- R"({"optionalUint32": 0.5})");
- "Int64FieldNotInteger", REQUIRED,
- R"({"optionalInt64": "0.5"})");
- "Uint64FieldNotInteger", REQUIRED,
- R"({"optionalUint64": "0.5"})");
- // Integers but represented as float values are accepted.
- "Int32FieldFloatTrailingZero", REQUIRED,
- R"({"optionalInt32": 100000.000})",
- "optional_int32: 100000");
- "Int32FieldExponentialFormat", REQUIRED,
- R"({"optionalInt32": 1e5})",
- "Int32FieldMaxFloatValue", REQUIRED,
- R"({"optionalInt32": 2.147483647e9})",
- "Int32FieldMinFloatValue", REQUIRED,
- R"({"optionalInt32": -2.147483648e9})",
- "Uint32FieldMaxFloatValue", REQUIRED,
- R"({"optionalUint32": 4.294967295e9})",
- // Parser reject non-numeric values.
- "Int32FieldNotNumber", REQUIRED,
- R"({"optionalInt32": "3x3"})");
- "Uint32FieldNotNumber", REQUIRED,
- R"({"optionalUint32": "3x3"})");
- "Int64FieldNotNumber", REQUIRED,
- R"({"optionalInt64": "3x3"})");
- "Uint64FieldNotNumber", REQUIRED,
- R"({"optionalUint64": "3x3"})");
- // JSON does not allow "+" on numric values.
- "Int32FieldPlusSign", REQUIRED,
- R"({"optionalInt32": +1})");
- // JSON doesn't allow leading 0s.
- "Int32FieldLeadingZero", REQUIRED,
- R"({"optionalInt32": 01})");
- "Int32FieldNegativeWithLeadingZero", REQUIRED,
- R"({"optionalInt32": -01})");
- // String values must follow the same syntax rule. Specifically leading
- // or trailing spaces are not allowed.
- "Int32FieldLeadingSpace", REQUIRED,
- R"({"optionalInt32": " 1"})");
- "Int32FieldTrailingSpace", REQUIRED,
- R"({"optionalInt32": "1 "})");
- // 64-bit values are serialized as strings.
- "Int64FieldBeString", RECOMMENDED,
- R"({"optionalInt64": 1})",
- return value["optionalInt64"].type() == Json::stringValue &&
- value["optionalInt64"].asString() == "1";
- "Uint64FieldBeString", RECOMMENDED,
- R"({"optionalUint64": 1})",
- return value["optionalUint64"].type() == Json::stringValue &&
- value["optionalUint64"].asString() == "1";
- // Bool fields.
- "BoolFieldTrue", REQUIRED,
- R"({"optionalBool":true})",
- "optional_bool: true");
- "BoolFieldFalse", REQUIRED,
- R"({"optionalBool":false})",
- "optional_bool: false");
- // Other forms are not allowed.
- "BoolFieldIntegerZero", RECOMMENDED,
- R"({"optionalBool":0})");
- "BoolFieldIntegerOne", RECOMMENDED,
- R"({"optionalBool":1})");
- "BoolFieldCamelCaseTrue", RECOMMENDED,
- R"({"optionalBool":True})");
- "BoolFieldCamelCaseFalse", RECOMMENDED,
- R"({"optionalBool":False})");
- "BoolFieldAllCapitalTrue", RECOMMENDED,
- R"({"optionalBool":TRUE})");
- "BoolFieldAllCapitalFalse", RECOMMENDED,
- R"({"optionalBool":FALSE})");
- "BoolFieldDoubleQuotedTrue", RECOMMENDED,
- R"({"optionalBool":"true"})");
- "BoolFieldDoubleQuotedFalse", RECOMMENDED,
- R"({"optionalBool":"false"})");
- // Float fields.
- "FloatFieldMinPositiveValue", REQUIRED,
- R"({"optionalFloat": 1.175494e-38})",
- "optional_float: 1.175494e-38");
- "FloatFieldMaxNegativeValue", REQUIRED,
- R"({"optionalFloat": -1.175494e-38})",
- "optional_float: -1.175494e-38");
- "FloatFieldMaxPositiveValue", REQUIRED,
- R"({"optionalFloat": 3.402823e+38})",
- "optional_float: 3.402823e+38");
- "FloatFieldMinNegativeValue", REQUIRED,
- // Values can be quoted.
- "FloatFieldQuotedValue", REQUIRED,
- R"({"optionalFloat": "1"})",
- "optional_float: 1");
- // Special values.
- "FloatFieldNan", REQUIRED,
- R"({"optionalFloat": "NaN"})",
- "optional_float: nan");
- "FloatFieldInfinity", REQUIRED,
- R"({"optionalFloat": "Infinity"})",
- "optional_float: inf");
- "FloatFieldNegativeInfinity", REQUIRED,
- R"({"optionalFloat": "-Infinity"})",
- "optional_float: -inf");
- // Non-cannonical Nan will be correctly normalized.
- {
- TestAllTypesProto3 message;
- // IEEE floating-point standard 32-bit quiet NaN:
- // 0111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
- message.set_optional_float(
- WireFormatLite::DecodeFloat(0x7FA12345));
- RunValidJsonTestWithProtobufInput(
- "FloatFieldNormalizeQuietNan", REQUIRED, message,
- // IEEE floating-point standard 64-bit signaling NaN:
- // 1111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
- WireFormatLite::DecodeFloat(0xFFB54321));
- "FloatFieldNormalizeSignalingNan", REQUIRED, message,
- // Special values must be quoted.
- "FloatFieldNanNotQuoted", RECOMMENDED,
- R"({"optionalFloat": NaN})");
- "FloatFieldInfinityNotQuoted", RECOMMENDED,
- R"({"optionalFloat": Infinity})");
- "FloatFieldNegativeInfinityNotQuoted", RECOMMENDED,
- R"({"optionalFloat": -Infinity})");
- // Parsers should reject out-of-bound values.
- "FloatFieldTooSmall", REQUIRED,
- R"({"optionalFloat": -3.502823e+38})");
- "FloatFieldTooLarge", REQUIRED,
- R"({"optionalFloat": 3.502823e+38})");
- // Double fields.
- "DoubleFieldMinPositiveValue", REQUIRED,
- R"({"optionalDouble": 2.22507e-308})",
- "optional_double: 2.22507e-308");
- "DoubleFieldMaxNegativeValue", REQUIRED,
- R"({"optionalDouble": -2.22507e-308})",
- "optional_double: -2.22507e-308");
- "DoubleFieldMaxPositiveValue", REQUIRED,
- R"({"optionalDouble": 1.79769e+308})",
- "optional_double: 1.79769e+308");
- "DoubleFieldMinNegativeValue", REQUIRED,
- R"({"optionalDouble": -1.79769e+308})",
- "optional_double: -1.79769e+308");
- "DoubleFieldQuotedValue", REQUIRED,
- R"({"optionalDouble": "1"})",
- "optional_double: 1");
- // Speical values.
- "DoubleFieldNan", REQUIRED,
- R"({"optionalDouble": "NaN"})",
- "optional_double: nan");
- "DoubleFieldInfinity", REQUIRED,
- R"({"optionalDouble": "Infinity"})",
- "optional_double: inf");
- "DoubleFieldNegativeInfinity", REQUIRED,
- R"({"optionalDouble": "-Infinity"})",
- "optional_double: -inf");
- message.set_optional_double(
- WireFormatLite::DecodeDouble(0x7FFA123456789ABCLL));
- "DoubleFieldNormalizeQuietNan", REQUIRED, message,
- WireFormatLite::DecodeDouble(0xFFFBCBA987654321LL));
- "DoubleFieldNormalizeSignalingNan", REQUIRED, message,
- "DoubleFieldNanNotQuoted", RECOMMENDED,
- R"({"optionalDouble": NaN})");
- "DoubleFieldInfinityNotQuoted", RECOMMENDED,
- R"({"optionalDouble": Infinity})");
- "DoubleFieldNegativeInfinityNotQuoted", RECOMMENDED,
- R"({"optionalDouble": -Infinity})");
- "DoubleFieldTooSmall", REQUIRED,
- R"({"optionalDouble": -1.89769e+308})");
- "DoubleFieldTooLarge", REQUIRED,
- R"({"optionalDouble": +1.89769e+308})");
- // Enum fields.
- "EnumField", REQUIRED,
- R"({"optionalNestedEnum": "FOO"})",
- "optional_nested_enum: FOO");
- // Enum values must be represented as strings.
- "EnumFieldNotQuoted", REQUIRED,
- R"({"optionalNestedEnum": FOO})");
- // Numeric values are allowed.
- "EnumFieldNumericValueZero", REQUIRED,
- R"({"optionalNestedEnum": 0})",
- "EnumFieldNumericValueNonZero", REQUIRED,
- R"({"optionalNestedEnum": 1})",
- "optional_nested_enum: BAR");
- // Unknown enum values are represented as numeric values.
- "EnumFieldUnknownValue", REQUIRED,
- R"({"optionalNestedEnum": 123})",
- return value["optionalNestedEnum"].type() == Json::intValue &&
- value["optionalNestedEnum"].asInt() == 123;
- // String fields.
- "StringField", REQUIRED,
- R"({"optionalString": "Hello world!"})",
- "optional_string: \"Hello world!\"");
- "StringFieldUnicode", REQUIRED,
- // Google in Chinese.
- R"({"optionalString": "谷歌"})",
- R"(optional_string: "谷歌")");
- "StringFieldEscape", REQUIRED,
- R"({"optionalString": "\"\\\/\b\f\n\r\t"})",
- R"(optional_string: "\"\\/\b\f\n\r\t")");
- "StringFieldUnicodeEscape", REQUIRED,
- R"({"optionalString": "\u8C37\u6B4C"})",
- "StringFieldUnicodeEscapeWithLowercaseHexLetters", REQUIRED,
- R"({"optionalString": "\u8c37\u6b4c"})",
- "StringFieldSurrogatePair", REQUIRED,
- // The character is an emoji: grinning face with smiling eyes. 😁
- R"({"optionalString": "\uD83D\uDE01"})",
- R"(optional_string: "\xF0\x9F\x98\x81")");
- // Unicode escapes must start with "\u" (lowercase u).
- "StringFieldUppercaseEscapeLetter", RECOMMENDED,
- R"({"optionalString": "\U8C37\U6b4C"})");
- "StringFieldInvalidEscape", RECOMMENDED,
- R"({"optionalString": "\uXXXX\u6B4C"})");
- "StringFieldUnterminatedEscape", RECOMMENDED,
- R"({"optionalString": "\u8C3"})");
- "StringFieldUnpairedHighSurrogate", RECOMMENDED,
- R"({"optionalString": "\uD800"})");
- "StringFieldUnpairedLowSurrogate", RECOMMENDED,
- R"({"optionalString": "\uDC00"})");
- "StringFieldSurrogateInWrongOrder", RECOMMENDED,
- R"({"optionalString": "\uDE01\uD83D"})");
- "StringFieldNotAString", REQUIRED,
- R"({"optionalString": 12345})");
- // Bytes fields.
- "BytesField", REQUIRED,
- R"({"optionalBytes": "AQI="})",
- R"(optional_bytes: "\x01\x02")");
- "BytesFieldBase64Url", RECOMMENDED,
- R"({"optionalBytes": "-_"})",
- R"(optional_bytes: "\xfb")");
- // Message fields.
- "MessageField", REQUIRED,
- R"({"optionalNestedMessage": {"a": 1234}})",
- "optional_nested_message: {a: 1234}");
- // Oneof fields.
- "OneofFieldDuplicate", REQUIRED,
- R"({"oneofUint32": 1, "oneofString": "test"})");
- // Ensure zero values for oneof make it out/backs.
- TestAllTypesProto3 messageProto3;
- TestAllTypesProto2 messageProto2;
- TestOneofMessage(messageProto3, true);
- TestOneofMessage(messageProto2, false);
- "OneofZeroUint32", RECOMMENDED,
- R"({"oneofUint32": 0})", "oneof_uint32: 0");
- "OneofZeroMessage", RECOMMENDED,
- R"({"oneofNestedMessage": {}})", "oneof_nested_message: {}");
- "OneofZeroString", RECOMMENDED,
- R"({"oneofString": ""})", "oneof_string: \"\"");
- "OneofZeroBytes", RECOMMENDED,
- R"({"oneofBytes": ""})", "oneof_bytes: \"\"");
- "OneofZeroBool", RECOMMENDED,
- R"({"oneofBool": false})", "oneof_bool: false");
- "OneofZeroUint64", RECOMMENDED,
- R"({"oneofUint64": 0})", "oneof_uint64: 0");
- "OneofZeroFloat", RECOMMENDED,
- R"({"oneofFloat": 0.0})", "oneof_float: 0");
- "OneofZeroDouble", RECOMMENDED,
- R"({"oneofDouble": 0.0})", "oneof_double: 0");
- "OneofZeroEnum", RECOMMENDED,
- R"({"oneofEnum":"FOO"})", "oneof_enum: FOO");
- // Repeated fields.
- "PrimitiveRepeatedField", REQUIRED,
- R"({"repeatedInt32": [1, 2, 3, 4]})",
- "repeated_int32: [1, 2, 3, 4]");
- "EnumRepeatedField", REQUIRED,
- R"({"repeatedNestedEnum": ["FOO", "BAR", "BAZ"]})",
- "repeated_nested_enum: [FOO, BAR, BAZ]");
- "StringRepeatedField", REQUIRED,
- R"({"repeatedString": ["Hello", "world"]})",
- R"(repeated_string: ["Hello", "world"])");
- "BytesRepeatedField", REQUIRED,
- R"({"repeatedBytes": ["AAEC", "AQI="]})",
- R"(repeated_bytes: ["\x00\x01\x02", "\x01\x02"])");
- "MessageRepeatedField", REQUIRED,
- R"({"repeatedNestedMessage": [{"a": 1234}, {"a": 5678}]})",
- "repeated_nested_message: {a: 1234}"
- "repeated_nested_message: {a: 5678}");
- // Repeated field elements are of incorrect type.
- "RepeatedFieldWrongElementTypeExpectingIntegersGotBool", REQUIRED,
- R"({"repeatedInt32": [1, false, 3, 4]})");
- "RepeatedFieldWrongElementTypeExpectingIntegersGotString", REQUIRED,
- R"({"repeatedInt32": [1, 2, "name", 4]})");
- "RepeatedFieldWrongElementTypeExpectingIntegersGotMessage", REQUIRED,
- R"({"repeatedInt32": [1, 2, 3, {"a": 4}]})");
- "RepeatedFieldWrongElementTypeExpectingStringsGotInt", REQUIRED,
- R"({"repeatedString": ["1", 2, "3", "4"]})");
- "RepeatedFieldWrongElementTypeExpectingStringsGotBool", REQUIRED,
- R"({"repeatedString": ["1", "2", false, "4"]})");
- "RepeatedFieldWrongElementTypeExpectingStringsGotMessage", REQUIRED,
- R"({"repeatedString": ["1", 2, "3", {"a": 4}]})");
- "RepeatedFieldWrongElementTypeExpectingMessagesGotInt", REQUIRED,
- R"({"repeatedNestedMessage": [{"a": 1}, 2]})");
- "RepeatedFieldWrongElementTypeExpectingMessagesGotBool", REQUIRED,
- R"({"repeatedNestedMessage": [{"a": 1}, false]})");
- "RepeatedFieldWrongElementTypeExpectingMessagesGotString", REQUIRED,
- R"({"repeatedNestedMessage": [{"a": 1}, "2"]})");
- // Trailing comma in the repeated field is not allowed.
- "RepeatedFieldTrailingComma", RECOMMENDED,
- R"({"repeatedInt32": [1, 2, 3, 4,]})");
- "RepeatedFieldTrailingCommaWithSpace", RECOMMENDED,
- "{\"repeatedInt32\": [1, 2, 3, 4 ,]}");
- "RepeatedFieldTrailingCommaWithSpaceCommaSpace", RECOMMENDED,
- "{\"repeatedInt32\": [1, 2, 3, 4 , ]}");
- "RepeatedFieldTrailingCommaWithNewlines", RECOMMENDED,
- "{\"repeatedInt32\": [\n 1,\n 2,\n 3,\n 4,\n]}");
- // Map fields.
- "Int32MapField", REQUIRED,
- R"({"mapInt32Int32": {"1": 2, "3": 4}})",
- "map_int32_int32: {key: 1 value: 2}"
- "map_int32_int32: {key: 3 value: 4}");
- "Int32MapFieldKeyNotQuoted", RECOMMENDED,
- R"({"mapInt32Int32": {1: 2, 3: 4}})");
- "Uint32MapField", REQUIRED,
- R"({"mapUint32Uint32": {"1": 2, "3": 4}})",
- "map_uint32_uint32: {key: 1 value: 2}"
- "map_uint32_uint32: {key: 3 value: 4}");
- "Uint32MapFieldKeyNotQuoted", RECOMMENDED,
- R"({"mapUint32Uint32": {1: 2, 3: 4}})");
- "Int64MapField", REQUIRED,
- R"({"mapInt64Int64": {"1": 2, "3": 4}})",
- "map_int64_int64: {key: 1 value: 2}"
- "map_int64_int64: {key: 3 value: 4}");
- "Int64MapFieldKeyNotQuoted", RECOMMENDED,
- R"({"mapInt64Int64": {1: 2, 3: 4}})");
- "Uint64MapField", REQUIRED,
- R"({"mapUint64Uint64": {"1": 2, "3": 4}})",
- "map_uint64_uint64: {key: 1 value: 2}"
- "map_uint64_uint64: {key: 3 value: 4}");
- "Uint64MapFieldKeyNotQuoted", RECOMMENDED,
- R"({"mapUint64Uint64": {1: 2, 3: 4}})");
- "BoolMapField", REQUIRED,
- R"({"mapBoolBool": {"true": true, "false": false}})",
- "map_bool_bool: {key: true value: true}"
- "map_bool_bool: {key: false value: false}");
- "BoolMapFieldKeyNotQuoted", RECOMMENDED,
- R"({"mapBoolBool": {true: true, false: false}})");
- "MessageMapField", REQUIRED,
- "mapStringNestedMessage": {
- "hello": {"a": 1234},
- "world": {"a": 5678}
- map_string_nested_message: {
- key: "hello"
- value: {a: 1234}
- key: "world"
- value: {a: 5678}
- // Since Map keys are represented as JSON strings, escaping should be allowed.
- "Int32MapEscapedKey", REQUIRED,
- R"({"mapInt32Int32": {"\u0031": 2}})",
- "map_int32_int32: {key: 1 value: 2}");
- "Int64MapEscapedKey", REQUIRED,
- R"({"mapInt64Int64": {"\u0031": 2}})",
- "map_int64_int64: {key: 1 value: 2}");
- "BoolMapEscapedKey", REQUIRED,
- R"({"mapBoolBool": {"tr\u0075e": true}})",
- "map_bool_bool: {key: true value: true}");
- // "null" is accepted for all fields types.
- "AllFieldAcceptNull", REQUIRED,
- "optionalInt32": null,
- "optionalInt64": null,
- "optionalUint32": null,
- "optionalUint64": null,
- "optionalSint32": null,
- "optionalSint64": null,
- "optionalFixed32": null,
- "optionalFixed64": null,
- "optionalSfixed32": null,
- "optionalSfixed64": null,
- "optionalFloat": null,
- "optionalDouble": null,
- "optionalBool": null,
- "optionalString": null,
- "optionalBytes": null,
- "optionalNestedEnum": null,
- "optionalNestedMessage": null,
- "repeatedInt32": null,
- "repeatedInt64": null,
- "repeatedUint32": null,
- "repeatedUint64": null,
- "repeatedSint32": null,
- "repeatedSint64": null,
- "repeatedFixed32": null,
- "repeatedFixed64": null,
- "repeatedSfixed32": null,
- "repeatedSfixed64": null,
- "repeatedFloat": null,
- "repeatedDouble": null,
- "repeatedBool": null,
- "repeatedString": null,
- "repeatedBytes": null,
- "repeatedNestedEnum": null,
- "repeatedNestedMessage": null,
- "mapInt32Int32": null,
- "mapBoolBool": null,
- "mapStringNestedMessage": null
- "");
- // Repeated field elements cannot be null.
- "RepeatedFieldPrimitiveElementIsNull", RECOMMENDED,
- R"({"repeatedInt32": [1, null, 2]})");
- "RepeatedFieldMessageElementIsNull", RECOMMENDED,
- R"({"repeatedNestedMessage": [{"a":1}, null, {"a":2}]})");
- // Map field keys cannot be null.
- "MapFieldKeyIsNull", RECOMMENDED,
- R"({"mapInt32Int32": {null: 1}})");
- // Map field values cannot be null.
- "MapFieldValueIsNull", RECOMMENDED,
- R"({"mapInt32Int32": {"0": null}})");
- // http://www.rfc-editor.org/rfc/rfc7159.txt says strings have to use double
- // quotes.
- "StringFieldSingleQuoteKey", RECOMMENDED,
- R"({'optionalString': "Hello world!"})");
- "StringFieldSingleQuoteValue", RECOMMENDED,
- R"({"optionalString": 'Hello world!'})");
- "StringFieldSingleQuoteBoth", RECOMMENDED,
- R"({'optionalString': 'Hello world!'})");
- // Unknown fields.
- //TODO(yilunchong): update this behavior when unknown field's behavior
- // changed in open source. Also delete
- // Required.Proto3.ProtobufInput.UnknownVarint.ProtobufOutput
- // from failure list of python_cpp python java
- TestUnknownMessage(messageProto3, true);
- TestUnknownMessage(messageProto2, false);
- // Wrapper types.
- "OptionalBoolWrapper", REQUIRED,
- R"({"optionalBoolWrapper": false})",
- "optional_bool_wrapper: {value: false}");
- "OptionalInt32Wrapper", REQUIRED,
- R"({"optionalInt32Wrapper": 0})",
- "optional_int32_wrapper: {value: 0}");
- "OptionalUint32Wrapper", REQUIRED,
- R"({"optionalUint32Wrapper": 0})",
- "optional_uint32_wrapper: {value: 0}");
- "OptionalInt64Wrapper", REQUIRED,
- R"({"optionalInt64Wrapper": 0})",
- "optional_int64_wrapper: {value: 0}");
- "OptionalUint64Wrapper", REQUIRED,
- R"({"optionalUint64Wrapper": 0})",
- "optional_uint64_wrapper: {value: 0}");
- "OptionalFloatWrapper", REQUIRED,
- R"({"optionalFloatWrapper": 0})",
- "optional_float_wrapper: {value: 0}");
- "OptionalDoubleWrapper", REQUIRED,
- R"({"optionalDoubleWrapper": 0})",
- "optional_double_wrapper: {value: 0}");
- "OptionalStringWrapper", REQUIRED,
- R"({"optionalStringWrapper": ""})",
- R"(optional_string_wrapper: {value: ""})");
- "OptionalBytesWrapper", REQUIRED,
- R"({"optionalBytesWrapper": ""})",
- R"(optional_bytes_wrapper: {value: ""})");
- "OptionalWrapperTypesWithNonDefaultValue", REQUIRED,
- "optionalBoolWrapper": true,
- "optionalInt32Wrapper": 1,
- "optionalUint32Wrapper": 1,
- "optionalInt64Wrapper": "1",
- "optionalUint64Wrapper": "1",
- "optionalFloatWrapper": 1,
- "optionalDoubleWrapper": 1,
- "optionalStringWrapper": "1",
- "optionalBytesWrapper": "AQI="
- optional_bool_wrapper: {value: true}
- optional_int32_wrapper: {value: 1}
- optional_uint32_wrapper: {value: 1}
- optional_int64_wrapper: {value: 1}
- optional_uint64_wrapper: {value: 1}
- optional_float_wrapper: {value: 1}
- optional_double_wrapper: {value: 1}
- optional_string_wrapper: {value: "1"}
- optional_bytes_wrapper: {value: "\x01\x02"}
- "RepeatedBoolWrapper", REQUIRED,
- R"({"repeatedBoolWrapper": [true, false]})",
- "repeated_bool_wrapper: {value: true}"
- "repeated_bool_wrapper: {value: false}");
- "RepeatedInt32Wrapper", REQUIRED,
- R"({"repeatedInt32Wrapper": [0, 1]})",
- "repeated_int32_wrapper: {value: 0}"
- "repeated_int32_wrapper: {value: 1}");
- "RepeatedUint32Wrapper", REQUIRED,
- R"({"repeatedUint32Wrapper": [0, 1]})",
- "repeated_uint32_wrapper: {value: 0}"
- "repeated_uint32_wrapper: {value: 1}");
- "RepeatedInt64Wrapper", REQUIRED,
- R"({"repeatedInt64Wrapper": [0, 1]})",
- "repeated_int64_wrapper: {value: 0}"
- "repeated_int64_wrapper: {value: 1}");
- "RepeatedUint64Wrapper", REQUIRED,
- R"({"repeatedUint64Wrapper": [0, 1]})",
- "repeated_uint64_wrapper: {value: 0}"
- "repeated_uint64_wrapper: {value: 1}");
- "RepeatedFloatWrapper", REQUIRED,
- R"({"repeatedFloatWrapper": [0, 1]})",
- "repeated_float_wrapper: {value: 0}"
- "repeated_float_wrapper: {value: 1}");
- "RepeatedDoubleWrapper", REQUIRED,
- R"({"repeatedDoubleWrapper": [0, 1]})",
- "repeated_double_wrapper: {value: 0}"
- "repeated_double_wrapper: {value: 1}");
- "RepeatedStringWrapper", REQUIRED,
- R"({"repeatedStringWrapper": ["", "AQI="]})",
- repeated_string_wrapper: {value: ""}
- repeated_string_wrapper: {value: "AQI="}
- "RepeatedBytesWrapper", REQUIRED,
- R"({"repeatedBytesWrapper": ["", "AQI="]})",
- repeated_bytes_wrapper: {value: ""}
- repeated_bytes_wrapper: {value: "\x01\x02"}
- "WrapperTypesWithNullValue", REQUIRED,
- "optionalBoolWrapper": null,
- "optionalInt32Wrapper": null,
- "optionalUint32Wrapper": null,
- "optionalInt64Wrapper": null,
- "optionalUint64Wrapper": null,
- "optionalFloatWrapper": null,
- "optionalDoubleWrapper": null,
- "optionalStringWrapper": null,
- "optionalBytesWrapper": null,
- "repeatedBoolWrapper": null,
- "repeatedInt32Wrapper": null,
- "repeatedUint32Wrapper": null,
- "repeatedInt64Wrapper": null,
- "repeatedUint64Wrapper": null,
- "repeatedFloatWrapper": null,
- "repeatedDoubleWrapper": null,
- "repeatedStringWrapper": null,
- "repeatedBytesWrapper": null
- // Duration
- "DurationMinValue", REQUIRED,
- R"({"optionalDuration": "-315576000000.999999999s"})",
- "optional_duration: {seconds: -315576000000 nanos: -999999999}");
- "DurationMaxValue", REQUIRED,
- R"({"optionalDuration": "315576000000.999999999s"})",
- "optional_duration: {seconds: 315576000000 nanos: 999999999}");
- "DurationRepeatedValue", REQUIRED,
- R"({"repeatedDuration": ["1.5s", "-1.5s"]})",
- "repeated_duration: {seconds: 1 nanos: 500000000}"
- "repeated_duration: {seconds: -1 nanos: -500000000}");
- "DurationNull", REQUIRED,
- R"({"optionalDuration": null})",
- "DurationMissingS", REQUIRED,
- R"({"optionalDuration": "1"})");
- "DurationJsonInputTooSmall", REQUIRED,
- R"({"optionalDuration": "-315576000001.000000000s"})");
- "DurationJsonInputTooLarge", REQUIRED,
- R"({"optionalDuration": "315576000001.000000000s"})");
- ExpectSerializeFailureForJson(
- "DurationProtoInputTooSmall", REQUIRED,
- "optional_duration: {seconds: -315576000001 nanos: 0}");
- "DurationProtoInputTooLarge", REQUIRED,
- "optional_duration: {seconds: 315576000001 nanos: 0}");
- "DurationHasZeroFractionalDigit", RECOMMENDED,
- R"({"optionalDuration": "1.000000000s"})",
- return value["optionalDuration"].asString() == "1s";
- "DurationHas3FractionalDigits", RECOMMENDED,
- R"({"optionalDuration": "1.010000000s"})",
- return value["optionalDuration"].asString() == "1.010s";
- "DurationHas6FractionalDigits", RECOMMENDED,
- R"({"optionalDuration": "1.000010000s"})",
- return value["optionalDuration"].asString() == "1.000010s";
- "DurationHas9FractionalDigits", RECOMMENDED,
- R"({"optionalDuration": "1.000000010s"})",
- return value["optionalDuration"].asString() == "1.000000010s";
- // Timestamp
- "TimestampMinValue", REQUIRED,
- R"({"optionalTimestamp": "0001-01-01T00:00:00Z"})",
- "optional_timestamp: {seconds: -62135596800}");
- "TimestampMaxValue", REQUIRED,
- R"({"optionalTimestamp": "9999-12-31T23:59:59.999999999Z"})",
- "optional_timestamp: {seconds: 253402300799 nanos: 999999999}");
- "TimestampRepeatedValue", REQUIRED,
- "repeatedTimestamp": [
- "0001-01-01T00:00:00Z",
- "9999-12-31T23:59:59.999999999Z"
- ]
- "repeated_timestamp: {seconds: -62135596800}"
- "repeated_timestamp: {seconds: 253402300799 nanos: 999999999}");
- "TimestampWithPositiveOffset", REQUIRED,
- R"({"optionalTimestamp": "1970-01-01T08:00:00+08:00"})",
- "optional_timestamp: {seconds: 0}");
- "TimestampWithNegativeOffset", REQUIRED,
- R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})",
- "TimestampNull", REQUIRED,
- R"({"optionalTimestamp": null})",
- "TimestampJsonInputTooSmall", REQUIRED,
- R"({"optionalTimestamp": "0000-01-01T00:00:00Z"})");
- "TimestampJsonInputTooLarge", REQUIRED,
- R"({"optionalTimestamp": "10000-01-01T00:00:00Z"})");
- "TimestampJsonInputMissingZ", REQUIRED,
- R"({"optionalTimestamp": "0001-01-01T00:00:00"})");
- "TimestampJsonInputMissingT", REQUIRED,
- R"({"optionalTimestamp": "0001-01-01 00:00:00Z"})");
- "TimestampJsonInputLowercaseZ", REQUIRED,
- R"({"optionalTimestamp": "0001-01-01T00:00:00z"})");
- "TimestampJsonInputLowercaseT", REQUIRED,
- R"({"optionalTimestamp": "0001-01-01t00:00:00Z"})");
- "TimestampProtoInputTooSmall", REQUIRED,
- "optional_timestamp: {seconds: -62135596801}");
- "TimestampProtoInputTooLarge", REQUIRED,
- "optional_timestamp: {seconds: 253402300800}");
- "TimestampZeroNormalized", RECOMMENDED,
- return value["optionalTimestamp"].asString() ==
- "1970-01-01T00:00:00Z";
- "TimestampHasZeroFractionalDigit", RECOMMENDED,
- R"({"optionalTimestamp": "1970-01-01T00:00:00.000000000Z"})",
- "TimestampHas3FractionalDigits", RECOMMENDED,
- R"({"optionalTimestamp": "1970-01-01T00:00:00.010000000Z"})",
- "1970-01-01T00:00:00.010Z";
- "TimestampHas6FractionalDigits", RECOMMENDED,
- R"({"optionalTimestamp": "1970-01-01T00:00:00.000010000Z"})",
- "1970-01-01T00:00:00.000010Z";
- "TimestampHas9FractionalDigits", RECOMMENDED,
- R"({"optionalTimestamp": "1970-01-01T00:00:00.000000010Z"})",
- "1970-01-01T00:00:00.000000010Z";
- // FieldMask
- "FieldMask", REQUIRED,
- R"({"optionalFieldMask": "foo,barBaz"})",
- R"(optional_field_mask: {paths: "foo" paths: "bar_baz"})");
- "FieldMaskInvalidCharacter", RECOMMENDED,
- R"({"optionalFieldMask": "foo,bar_bar"})");
- "FieldMaskPathsDontRoundTrip", RECOMMENDED,
- R"(optional_field_mask: {paths: "fooBar"})");
- "FieldMaskNumbersDontRoundTrip", RECOMMENDED,
- R"(optional_field_mask: {paths: "foo_3_bar"})");
- "FieldMaskTooManyUnderscore", RECOMMENDED,
- R"(optional_field_mask: {paths: "foo__bar"})");
- // Struct
- "Struct", REQUIRED,
- "optionalStruct": {
- "nullValue": null,
- "intValue": 1234,
- "boolValue": true,
- "doubleValue": 1234.5678,
- "stringValue": "Hello world!",
- "listValue": [1234, "5678"],
- "objectValue": {
- "value": 0
- optional_struct: {
- fields: {
- key: "nullValue"
- value: {null_value: NULL_VALUE}
- key: "intValue"
- value: {number_value: 1234}
- key: "boolValue"
- value: {bool_value: true}
- key: "doubleValue"
- value: {number_value: 1234.5678}
- key: "stringValue"
- value: {string_value: "Hello world!"}
- key: "listValue"
- value: {
- list_value: {
- values: {
- number_value: 1234
- string_value: "5678"
- key: "objectValue"
- struct_value: {
- key: "value"
- number_value: 0
- // Value
- "ValueAcceptInteger", REQUIRED,
- R"({"optionalValue": 1})",
- "optional_value: { number_value: 1}");
- "ValueAcceptFloat", REQUIRED,
- R"({"optionalValue": 1.5})",
- "optional_value: { number_value: 1.5}");
- "ValueAcceptBool", REQUIRED,
- R"({"optionalValue": false})",
- "optional_value: { bool_value: false}");
- "ValueAcceptNull", REQUIRED,
- R"({"optionalValue": null})",
- "optional_value: { null_value: NULL_VALUE}");
- "ValueAcceptString", REQUIRED,
- R"({"optionalValue": "hello"})",
- R"(optional_value: { string_value: "hello"})");
- "ValueAcceptList", REQUIRED,
- R"({"optionalValue": [0, "hello"]})",
- optional_value: {
- string_value: "hello"
- "ValueAcceptListWithNull", REQUIRED,
- R"({"optionalValue": ["x", null, "y"]})",
- string_value: "x"
- null_value: NULL_VALUE
- string_value: "y"
- "ValueAcceptObject", REQUIRED,
- R"({"optionalValue": {"value": 1}})",
- number_value: 1
- // Any
- "Any", REQUIRED,
- "optionalAny": {
- "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3",
- "optionalInt32": 12345
- optional_any: {
- [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
- optional_int32: 12345
- "AnyNested", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Any",
- "value": {
- [type.googleapis.com/google.protobuf.Any] {
- // The special "@type" tag is not required to appear first.
- "AnyUnorderedTypeTag", REQUIRED,
- "optionalInt32": 12345,
- "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
- // Well-known types in Any.
- "AnyWithInt32ValueWrapper", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Int32Value",
- "value": 12345
- [type.googleapis.com/google.protobuf.Int32Value] {
- value: 12345
- "AnyWithDuration", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Duration",
- "value": "1.5s"
- [type.googleapis.com/google.protobuf.Duration] {
- seconds: 1
- nanos: 500000000
- "AnyWithTimestamp", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Timestamp",
- "value": "1970-01-01T00:00:00Z"
- [type.googleapis.com/google.protobuf.Timestamp] {
- seconds: 0
- nanos: 0
- "AnyWithFieldMask", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.FieldMask",
- "value": "foo,barBaz"
- [type.googleapis.com/google.protobuf.FieldMask] {
- paths: ["foo", "bar_baz"]
- "AnyWithStruct", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Struct",
- "foo": 1
- [type.googleapis.com/google.protobuf.Struct] {
- key: "foo"
- "AnyWithValueForJsonObject", REQUIRED,
- "@type": "type.googleapis.com/google.protobuf.Value",
- [type.googleapis.com/google.protobuf.Value] {
- "AnyWithValueForInteger", REQUIRED,
- "value": 1
- RunValidJsonIgnoreUnknownTest(
- "IgnoreUnknownJsonNumber", REQUIRED,
- "unknown": 1
- "IgnoreUnknownJsonString", REQUIRED,
- "unknown": "a"
- "IgnoreUnknownJsonTrue", REQUIRED,
- "unknown": true
- "IgnoreUnknownJsonFalse", REQUIRED,
- "unknown": false
- "IgnoreUnknownJsonNull", REQUIRED,
- "unknown": null
- "IgnoreUnknownJsonObject", REQUIRED,
- "unknown": {"a": 1}
+ RunSuiteImpl();
bool ok = true;
if (!CheckSetEmpty(expected_to_fail_, "nonexistent_tests.txt",
@@ -40,12 +40,13 @@
#include <functional>
+#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/wire_format_lite.h>
namespace conformance {
class ConformanceRequest;
@@ -78,7 +79,23 @@ class ConformanceTestRunner {
};
// Class representing the test suite itself. To run it, implement your own
-// class derived from ConformanceTestRunner and then write code like:
+// class derived from ConformanceTestRunner, class derived from
+// ConformanceTestSuite and then write code like:
+//
+// class MyConformanceTestSuite : public ConformanceTestSuite {
+// public:
+// void RunSuiteImpl() {
+// // INSERT ACTURAL TESTS.
+// }
+// };
+// // Force MyConformanceTestSuite to be added at dynamic initialization
+// // time.
+// struct StaticTestSuiteInitializer {
+// StaticTestSuiteInitializer() {
+// AddTestSuite(new MyConformanceTestSuite());
+// } static_test_suite_initializer;
// class MyConformanceTestRunner : public ConformanceTestRunner {
// public:
@@ -89,15 +106,17 @@ class ConformanceTestRunner {
// int main() {
// MyConformanceTestRunner runner;
-// google::protobuf::ConformanceTestSuite suite;
-// std::string output;
-// suite.RunSuite(&runner, &output);
+// const std::set<ConformanceTestSuite*>& test_suite_set =
+// ::google::protobuf::GetTestSuiteSet();
+// for (auto suite : test_suite_set) {
+// suite->RunSuite(&runner, &output);
// }
class ConformanceTestSuite {
public:
ConformanceTestSuite() : verbose_(false), enforce_recommended_(false) {}
+ virtual ~ConformanceTestSuite() {}
void SetVerbose(bool verbose) { verbose_ = verbose; }
@@ -130,7 +149,7 @@ class ConformanceTestSuite {
// tests passed.
bool RunSuite(ConformanceTestRunner* runner, std::string* output);
- private:
+ protected:
// Test cases are classified into a few categories:
// REQUIRED: the test case must be passed for an implementation to be
// interoperable with other implementations. For example, a
@@ -151,38 +170,43 @@ class ConformanceTestSuite {
class ConformanceRequestSetting {
ConformanceRequestSetting(
const string& test_name, const string& input);
+ virtual ~ConformanceRequestSetting() {}
+ Message* GetTestMessage() const;
- Message* GetTestMessage() const;
+ string GetTestName() const;
- const string& GetTestName() const {
- return test_name_;
+ const conformance::ConformanceRequest& GetRequest() const {
+ return request_;
- const conformance::ConformanceRequest& GetRequest() const {
- return request_;
+ const ConformanceLevel GetLevel() const {
+ return level_;
- const ConformanceLevel GetLevel() const {
- return level_;
+ string ConformanceLevelToString(ConformanceLevel level) const;
+ virtual string InputFormatString(conformance::WireFormat format) const;
+ virtual string OutputFormatString(conformance::WireFormat format) const;
- void SetIgnoreUnknownJson(bool ignore_unknown_json) {
- request_.set_ignore_unknown_json(ignore_unknown_json);
private:
ConformanceLevel level_;
- conformance::WireFormat input_format_;
- conformance::WireFormat output_format_;
- bool is_proto3_;
+ ::conformance::WireFormat input_format_;
+ ::conformance::WireFormat output_format_;
+ const Message& prototype_message_;
string test_name_;
conformance::ConformanceRequest request_;
- static string ConformanceLevelToString(ConformanceLevel level);
+ bool CheckSetEmpty(const std::set<string>& set_to_check,
+ const std::string& write_to_file, const std::string& msg);
void ReportSuccess(const std::string& test_name);
void ReportFailure(const string& test_name,
@@ -193,73 +217,18 @@ class ConformanceTestSuite {
void ReportSkip(const string& test_name,
const conformance::ConformanceRequest& request,
const conformance::ConformanceResponse& response);
- void RunTest(const std::string& test_name,
- const conformance::ConformanceRequest& request,
- conformance::ConformanceResponse* response);
void RunValidInputTest(const ConformanceRequestSetting& setting,
const string& equivalent_text_format);
void RunValidBinaryInputTest(const ConformanceRequestSetting& setting,
const string& equivalent_wire_format);
- void RunValidJsonTest(const string& test_name,
- ConformanceLevel level,
- const string& input_json,
- const string& equivalent_text_format);
- void RunValidJsonIgnoreUnknownTest(
- void RunValidJsonTestWithProtobufInput(
- const string& test_name,
- const protobuf_test_messages::proto3::TestAllTypesProto3& input,
- void RunValidProtobufTest(const string& test_name, ConformanceLevel level,
- const string& input_protobuf,
- const string& equivalent_text_format,
- bool isProto3);
- void RunValidBinaryProtobufTest(const string& test_name,
- void RunValidProtobufTestWithMessage(
- const Message *input,
- typedef std::function<bool(const Json::Value&)> Validator;
- void RunValidJsonTestWithValidator(const string& test_name,
- const Validator& validator);
- void ExpectParseFailureForJson(const string& test_name,
- const string& input_json);
- void ExpectSerializeFailureForJson(const string& test_name,
- const string& text_format);
- void ExpectParseFailureForProtoWithProtoVersion (const string& proto,
- void ExpectParseFailureForProto(const std::string& proto,
- const std::string& test_name,
- ConformanceLevel level);
- void ExpectHardParseFailureForProto(const std::string& proto,
- void TestPrematureEOFForType(google::protobuf::FieldDescriptor::Type type);
- void TestIllegalTags();
- template <class MessageType>
- void TestOneofMessage (MessageType &message,
- void TestUnknownMessage (MessageType &message,
- void TestValidDataForType(
- google::protobuf::FieldDescriptor::Type,
- std::vector<std::pair<std::string, std::string>> values);
- bool CheckSetEmpty(const std::set<string>& set_to_check,
- const std::string& write_to_file, const std::string& msg);
+ void RunTest(const std::string& test_name,
+ const conformance::ConformanceRequest& request,
+ conformance::ConformanceResponse* response);
+ virtual void RunSuiteImpl() = 0;
ConformanceTestRunner* runner_;
int successes_;
int expected_failures_;
@@ -285,10 +254,14 @@ class ConformanceTestSuite {
// The set of tests that the testee opted out of;
std::set<std::string> skipped_;
- std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver_;
+ std::unique_ptr<google::protobuf::util::TypeResolver>
+ type_resolver_;
std::string type_url_;
+void AddTestSuite(ConformanceTestSuite* suite);
+const std::set<ConformanceTestSuite*>& GetTestSuiteSet();
} // namespace protobuf
} // namespace google
@@ -0,0 +1,2368 @@
+// 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 "conformance_test.h"
+#include "third_party/jsoncpp/json.h"
+#include <google/protobuf/test_messages_proto3.pb.h>
+#include <google/protobuf/test_messages_proto2.pb.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/type_resolver_util.h>
+#include <google/protobuf/wire_format_lite.h>
+using conformance::ConformanceRequest;
+using conformance::ConformanceResponse;
+using google::protobuf::Descriptor;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::Message;
+using google::protobuf::internal::WireFormatLite;
+using google::protobuf::TextFormat;
+using google::protobuf::util::NewTypeResolverForDescriptorPool;
+using protobuf_test_messages::proto3::TestAllTypesProto3;
+using protobuf_test_messages::proto2::TestAllTypesProto2;
+using std::string;
+namespace {
+static const char kTypeUrlPrefix[] = "type.googleapis.com";
+static string GetTypeUrl(const Descriptor* message) {
+ return string(kTypeUrlPrefix) + "/" + message->full_name();
+/* Routines for building arbitrary protos *************************************/
+// We would use CodedOutputStream except that we want more freedom to build
+// arbitrary protos (even invalid ones).
+const string empty;
+string cat(const string& a, const string& b,
+ const string& c = empty,
+ const string& d = empty,
+ const string& e = empty,
+ const string& f = empty,
+ const string& g = empty,
+ const string& h = empty,
+ const string& i = empty,
+ const string& j = empty,
+ const string& k = empty,
+ const string& l = empty) {
+ string ret;
+ ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() +
+ g.size() + h.size() + i.size() + j.size() + k.size() + l.size());
+ ret.append(a);
+ ret.append(b);
+ ret.append(c);
+ ret.append(d);
+ ret.append(e);
+ ret.append(f);
+ ret.append(g);
+ ret.append(h);
+ ret.append(i);
+ ret.append(j);
+ ret.append(k);
+ ret.append(l);
+ return ret;
+// The maximum number of bytes that it takes to encode a 64-bit varint.
+#define VARINT_MAX_LEN 10
+size_t vencode64(uint64_t val, int over_encoded_bytes, char *buf) {
+ if (val == 0) { buf[0] = 0; return 1; }
+ size_t i = 0;
+ while (val) {
+ uint8_t byte = val & 0x7fU;
+ val >>= 7;
+ if (val || over_encoded_bytes) byte |= 0x80U;
+ buf[i++] = byte;
+ while (over_encoded_bytes--) {
+ assert(i < 10);
+ uint8_t byte = over_encoded_bytes ? 0x80 : 0;
+ return i;
+string varint(uint64_t x) {
+ char buf[VARINT_MAX_LEN];
+ size_t len = vencode64(x, 0, buf);
+ return string(buf, len);
+// Encodes a varint that is |extra| bytes longer than it needs to be, but still
+// valid.
+string longvarint(uint64_t x, int extra) {
+ size_t len = vencode64(x, extra, buf);
+// TODO: proper byte-swapping for big-endian machines.
+string fixed32(void *data) { return string(static_cast<char*>(data), 4); }
+string fixed64(void *data) { return string(static_cast<char*>(data), 8); }
+string delim(const string& buf) { return cat(varint(buf.size()), buf); }
+string u32(uint32_t u32) { return fixed32(&u32); }
+string u64(uint64_t u64) { return fixed64(&u64); }
+string flt(float f) { return fixed32(&f); }
+string dbl(double d) { return fixed64(&d); }
+string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); }
+string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); }
+string tag(uint32_t fieldnum, char wire_type) {
+ return varint((fieldnum << 3) | wire_type);
+string submsg(uint32_t fn, const string& buf) {
+ return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) );
+#define UNKNOWN_FIELD 666
+const FieldDescriptor* GetFieldForType(FieldDescriptor::Type type,
+ bool repeated, bool is_proto3) {
+ const Descriptor* d = is_proto3 ?
+ TestAllTypesProto3().GetDescriptor() : TestAllTypesProto2().GetDescriptor();
+ for (int i = 0; i < d->field_count(); i++) {
+ const FieldDescriptor* f = d->field(i);
+ if (f->type() == type && f->is_repeated() == repeated) {
+ return f;
+ GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type;
+ return nullptr;
+string UpperCase(string str) {
+ for (int i = 0; i < str.size(); i++) {
+ str[i] = toupper(str[i]);
+ return str;
+std::unique_ptr<Message> NewTestMessage(bool is_proto3) {
+ std::unique_ptr<Message> prototype;
+ if (is_proto3) {
+ prototype.reset(new TestAllTypesProto3());
+ } else {
+ prototype.reset(new TestAllTypesProto2());
+ return prototype;
+} // anonymous namespace
+class ConformanceTestSuiteImpl : public ConformanceTestSuite {
+ public:
+ ConformanceTestSuiteImpl() {}
+ private:
+ void RunSuiteImpl();
+ void RunValidJsonTest(const string& test_name,
+ const string& input_json,
+ const string& equivalent_text_format);
+ void RunValidJsonTestWithProtobufInput(
+ const string& test_name,
+ const protobuf_test_messages::proto3::TestAllTypesProto3& input,
+ void RunValidJsonIgnoreUnknownTest(
+ const string& test_name, ConformanceLevel level, const string& input_json,
+ void RunValidProtobufTest(const string& test_name, ConformanceLevel level,
+ const string& input_protobuf,
+ const string& equivalent_text_format,
+ bool is_proto3);
+ void RunValidBinaryProtobufTest(const string& test_name,
+ void RunValidProtobufTestWithMessage(
+ const string& test_name, ConformanceLevel level,
+ const Message *input,
+ typedef std::function<bool(const Json::Value&)> Validator;
+ void RunValidJsonTestWithValidator(const string& test_name,
+ const Validator& validator);
+ void ExpectParseFailureForJson(const string& test_name,
+ const string& input_json);
+ void ExpectSerializeFailureForJson(const string& test_name,
+ const string& text_format);
+ void ExpectParseFailureForProtoWithProtoVersion (const string& proto,
+ void ExpectParseFailureForProto(const std::string& proto,
+ const std::string& test_name,
+ ConformanceLevel level);
+ void ExpectHardParseFailureForProto(const std::string& proto,
+ void TestPrematureEOFForType(google::protobuf::FieldDescriptor::Type type);
+ void TestIllegalTags();
+ template <class MessageType>
+ void TestOneofMessage (MessageType &message,
+ void TestUnknownMessage (MessageType &message,
+ void TestValidDataForType(
+ google::protobuf::FieldDescriptor::Type,
+ std::vector<std::pair<std::string, std::string>> values);
+};
+void ConformanceTestSuiteImpl::ExpectParseFailureForProtoWithProtoVersion (
+ const string& proto, const string& test_name, ConformanceLevel level,
+ bool is_proto3) {
+ std::unique_ptr<Message> prototype = NewTestMessage(is_proto3);
+ // We don't expect output, but if the program erroneously accepts the protobuf
+ // we let it send its response as this. We must not leave it unspecified.
+ ConformanceRequestSetting setting(
+ level, conformance::PROTOBUF, conformance::PROTOBUF,
+ conformance::BINARY_TEST,
+ *prototype, test_name, proto);
+ const ConformanceRequest& request = setting.GetRequest();
+ ConformanceResponse response;
+ string effective_test_name =
+ StrCat(setting.ConformanceLevelToString(level),
+ (is_proto3 ? ".Proto3" : ".Proto2"),
+ ".ProtobufInput.", test_name);
+ RunTest(effective_test_name, request, &response);
+ if (response.result_case() == ConformanceResponse::kParseError) {
+ ReportSuccess(effective_test_name);
+ } else if (response.result_case() == ConformanceResponse::kSkipped) {
+ ReportSkip(effective_test_name, request, response);
+ ReportFailure(effective_test_name, level, request, response,
+ "Should have failed to parse, but didn't.");
+// Expect that this precise protobuf will cause a parse error.
+void ConformanceTestSuiteImpl::ExpectParseFailureForProto(
+ const string& proto, const string& test_name, ConformanceLevel level) {
+ ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, true);
+ ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, false);
+// Expect that this protobuf will cause a parse error, even if it is followed
+// by valid protobuf data. We can try running this twice: once with this
+// data verbatim and once with this data followed by some valid data.
+// TODO(haberman): implement the second of these.
+void ConformanceTestSuiteImpl::ExpectHardParseFailureForProto(
+ return ExpectParseFailureForProto(proto, test_name, level);
+void ConformanceTestSuiteImpl::RunValidJsonTest(
+ const string& equivalent_text_format) {
+ TestAllTypesProto3 prototype;
+ ConformanceRequestSetting setting1(
+ level, conformance::JSON, conformance::PROTOBUF,
+ conformance::JSON_TEST,
+ prototype, test_name, input_json);
+ RunValidInputTest(setting1, equivalent_text_format);
+ ConformanceRequestSetting setting2(
+ level, conformance::JSON, conformance::JSON,
+ RunValidInputTest(setting2, equivalent_text_format);
+void ConformanceTestSuiteImpl::RunValidJsonTestWithProtobufInput(
+ const string& test_name, ConformanceLevel level, const TestAllTypesProto3& input,
+ level, conformance::PROTOBUF, conformance::JSON,
+ input, test_name, input.SerializeAsString());
+ RunValidInputTest(setting, equivalent_text_format);
+void ConformanceTestSuiteImpl::RunValidJsonIgnoreUnknownTest(
+ conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST,
+void ConformanceTestSuiteImpl::RunValidProtobufTest(
+ const string& input_protobuf, const string& equivalent_text_format,
+ *prototype, test_name, input_protobuf);
+void ConformanceTestSuiteImpl::RunValidBinaryProtobufTest(
+ const string& input_protobuf, bool is_proto3) {
+ RunValidBinaryInputTest(setting, input_protobuf);
+void ConformanceTestSuiteImpl::RunValidProtobufTestWithMessage(
+ const string& test_name, ConformanceLevel level, const Message *input,
+ const string& equivalent_text_format, bool is_proto3) {
+ RunValidProtobufTest(test_name, level, input->SerializeAsString(),
+ equivalent_text_format, is_proto3);
+// According to proto3 JSON specification, JSON serializers follow more strict
+// rules than parsers (e.g., a serializer must serialize int32 values as JSON
+// numbers while the parser is allowed to accept them as JSON strings). This
+// method allows strict checking on a proto3 JSON serializer by inspecting
+// the JSON output directly.
+void ConformanceTestSuiteImpl::RunValidJsonTestWithValidator(
+ const Validator& validator) {
+ ".Proto3.JsonInput.",
+ test_name, ".Validator");
+ if (response.result_case() == ConformanceResponse::kSkipped) {
+ return;
+ if (response.result_case() != ConformanceResponse::kJsonPayload) {
+ "Expected JSON payload but got type %d.",
+ response.result_case());
+ Json::Reader reader;
+ Json::Value value;
+ if (!reader.parse(response.json_payload(), value)) {
+ "JSON payload cannot be parsed as valid JSON: %s",
+ reader.getFormattedErrorMessages().c_str());
+ if (!validator(value)) {
+ "JSON payload validation failed.");
+void ConformanceTestSuiteImpl::ExpectParseFailureForJson(
+ const string& test_name, ConformanceLevel level, const string& input_json) {
+ ".Proto3.JsonInput.", test_name);
+void ConformanceTestSuiteImpl::ExpectSerializeFailureForJson(
+ const string& test_name, ConformanceLevel level, const string& text_format) {
+ TestAllTypesProto3 payload_message;
+ GOOGLE_CHECK(
+ TextFormat::ParseFromString(text_format, &payload_message))
+ << "Failed to parse: " << text_format;
+ prototype, test_name, payload_message.SerializeAsString());
+ ".", test_name, ".JsonOutput");
+ if (response.result_case() == ConformanceResponse::kSerializeError) {
+ "Should have failed to serialize, but didn't.");
+//TODO: proto2?
+void ConformanceTestSuiteImpl::TestPrematureEOFForType(
+ FieldDescriptor::Type type) {
+ // Incomplete values for each wire type.
+ static const string incompletes[6] = {
+ string("\x80"), // VARINT
+ string("abcdefg"), // 64BIT
+ string("\x80"), // DELIMITED (partial length)
+ string(), // START_GROUP (no value required)
+ string(), // END_GROUP (no value required)
+ string("abc") // 32BIT
+ };
+ const FieldDescriptor* field = GetFieldForType(type, false, true);
+ const FieldDescriptor* rep_field = GetFieldForType(type, true, true);
+ WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
+ static_cast<WireFormatLite::FieldType>(type));
+ const string& incomplete = incompletes[wire_type];
+ const string type_name =
+ UpperCase(string(".") + FieldDescriptor::TypeName(type));
+ ExpectParseFailureForProto(
+ tag(field->number(), wire_type),
+ "PrematureEofBeforeKnownNonRepeatedValue" + type_name, REQUIRED);
+ tag(rep_field->number(), wire_type),
+ "PrematureEofBeforeKnownRepeatedValue" + type_name, REQUIRED);
+ tag(UNKNOWN_FIELD, wire_type),
+ "PrematureEofBeforeUnknownValue" + type_name, REQUIRED);
+ cat( tag(field->number(), wire_type), incomplete ),
+ "PrematureEofInsideKnownNonRepeatedValue" + type_name, REQUIRED);
+ cat( tag(rep_field->number(), wire_type), incomplete ),
+ "PrematureEofInsideKnownRepeatedValue" + type_name, REQUIRED);
+ cat( tag(UNKNOWN_FIELD, wire_type), incomplete ),
+ "PrematureEofInsideUnknownValue" + type_name, REQUIRED);
+ if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+ cat( tag(field->number(), wire_type), varint(1) ),
+ "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name,
+ REQUIRED);
+ cat( tag(rep_field->number(), wire_type), varint(1) ),
+ "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name,
+ // EOF in the middle of delimited data for unknown value.
+ cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ),
+ "PrematureEofInDelimitedDataForUnknownValue" + type_name, REQUIRED);
+ if (type == FieldDescriptor::TYPE_MESSAGE) {
+ // Submessage ends in the middle of a value.
+ string incomplete_submsg =
+ cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT),
+ incompletes[WireFormatLite::WIRETYPE_VARINT] );
+ ExpectHardParseFailureForProto(
+ cat( tag(field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED),
+ varint(incomplete_submsg.size()),
+ incomplete_submsg ),
+ "PrematureEofInSubmessageValue" + type_name, REQUIRED);
+ } else if (type != FieldDescriptor::TYPE_GROUP) {
+ // Non-delimited, non-group: eligible for packing.
+ // Packed region ends in the middle of a value.
+ cat(tag(rep_field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED),
+ varint(incomplete.size()), incomplete),
+ "PrematureEofInPackedFieldValue" + type_name, REQUIRED);
+ // EOF in the middle of packed region.
+ varint(1)),
+ "PrematureEofInPackedField" + type_name, REQUIRED);
+void ConformanceTestSuiteImpl::TestValidDataForType(
+ FieldDescriptor::Type type,
+ std::vector<std::pair<std::string, std::string>> values) {
+ for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) {
+ const FieldDescriptor* field = GetFieldForType(type, false, is_proto3);
+ const FieldDescriptor* rep_field = GetFieldForType(type, true, is_proto3);
+ RunValidProtobufTest("ValidDataScalar" + type_name, REQUIRED,
+ cat(tag(field->number(), wire_type), values[0].first),
+ field->name() + ": " + values[0].second, is_proto3);
+ string proto;
+ string text = field->name() + ": " + values.back().second;
+ for (size_t i = 0; i < values.size(); i++) {
+ proto += cat(tag(field->number(), wire_type), values[i].first);
+ RunValidProtobufTest("RepeatedScalarSelectsLast" + type_name, REQUIRED,
+ proto, text, is_proto3);
+ proto.clear();
+ text.clear();
+ proto += cat(tag(rep_field->number(), wire_type), values[i].first);
+ text += rep_field->name() + ": " + values[i].second + " ";
+ RunValidProtobufTest("ValidDataRepeated" + type_name, REQUIRED,
+// TODO: proto2?
+void ConformanceTestSuiteImpl::TestIllegalTags() {
+ // field num 0 is illegal
+ string nullfield[] = {
+ "\1DEADBEEF",
+ "\2\1\1",
+ "\3\4",
+ "\5DEAD"
+ for (int i = 0; i < 4; i++) {
+ string name = "IllegalZeroFieldNum_Case_0";
+ name.back() += i;
+ ExpectParseFailureForProto(nullfield[i], name, REQUIRED);
+template <class MessageType>
+void ConformanceTestSuiteImpl::TestOneofMessage (
+ MessageType &message, bool is_proto3) {
+ message.set_oneof_uint32(0);
+ RunValidProtobufTestWithMessage(
+ "OneofZeroUint32", RECOMMENDED, &message, "oneof_uint32: 0", is_proto3);
+ message.mutable_oneof_nested_message()->set_a(0);
+ "OneofZeroMessage", RECOMMENDED, &message,
+ is_proto3 ? "oneof_nested_message: {}" : "oneof_nested_message: {a: 0}",
+ is_proto3);
+ message.mutable_oneof_nested_message()->set_a(1);
+ "OneofZeroMessageSetTwice", RECOMMENDED, &message,
+ "oneof_nested_message: {a: 1}",
+ message.set_oneof_string("");
+ "OneofZeroString", RECOMMENDED, &message, "oneof_string: \"\"", is_proto3);
+ message.set_oneof_bytes("");
+ "OneofZeroBytes", RECOMMENDED, &message, "oneof_bytes: \"\"", is_proto3);
+ message.set_oneof_bool(false);
+ "OneofZeroBool", RECOMMENDED, &message, "oneof_bool: false", is_proto3);
+ message.set_oneof_uint64(0);
+ "OneofZeroUint64", RECOMMENDED, &message, "oneof_uint64: 0", is_proto3);
+ message.set_oneof_float(0.0f);
+ "OneofZeroFloat", RECOMMENDED, &message, "oneof_float: 0", is_proto3);
+ message.set_oneof_double(0.0);
+ "OneofZeroDouble", RECOMMENDED, &message, "oneof_double: 0", is_proto3);
+ message.set_oneof_enum(MessageType::FOO);
+ "OneofZeroEnum", RECOMMENDED, &message, "oneof_enum: FOO", is_proto3);
+void ConformanceTestSuiteImpl::TestUnknownMessage(
+ MessageType& message, bool is_proto3) {
+ message.ParseFromString("\xA8\x1F\x01");
+ RunValidBinaryProtobufTest("UnknownVarint", REQUIRED,
+ message.SerializeAsString(), is_proto3);
+void ConformanceTestSuiteImpl::RunSuiteImpl() {
+ type_resolver_.reset(NewTypeResolverForDescriptorPool(
+ kTypeUrlPrefix, DescriptorPool::generated_pool()));
+ type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor());
+ for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) {
+ if (i == FieldDescriptor::TYPE_GROUP) continue;
+ TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i));
+ TestIllegalTags();
+ int64 kInt64Min = -9223372036854775808ULL;
+ int64 kInt64Max = 9223372036854775807ULL;
+ uint64 kUint64Max = 18446744073709551615ULL;
+ int32 kInt32Max = 2147483647;
+ int32 kInt32Min = -2147483648;
+ uint32 kUint32Max = 4294967295UL;
+ TestValidDataForType(FieldDescriptor::TYPE_DOUBLE, {
+ {dbl(0.1), "0.1"},
+ {dbl(1.7976931348623157e+308), "1.7976931348623157e+308"},
+ {dbl(2.22507385850720138309e-308), "2.22507385850720138309e-308"}
+ });
+ TestValidDataForType(FieldDescriptor::TYPE_FLOAT, {
+ {flt(0.1), "0.1"},
+ {flt(1.00000075e-36), "1.00000075e-36"},
+ {flt(3.402823e+38), "3.402823e+38"}, // 3.40282347e+38
+ {flt(1.17549435e-38f), "1.17549435e-38"}
+ TestValidDataForType(FieldDescriptor::TYPE_INT64, {
+ {varint(12345), "12345"},
+ {varint(kInt64Max), std::to_string(kInt64Max)},
+ {varint(kInt64Min), std::to_string(kInt64Min)}
+ TestValidDataForType(FieldDescriptor::TYPE_UINT64, {
+ {varint(kUint64Max), std::to_string(kUint64Max)},
+ {varint(0), "0"}
+ TestValidDataForType(FieldDescriptor::TYPE_INT32, {
+ {longvarint(12345, 2), "12345"},
+ {longvarint(12345, 7), "12345"},
+ {varint(kInt32Max), std::to_string(kInt32Max)},
+ {varint(kInt32Min), std::to_string(kInt32Min)},
+ {varint(1LL << 33), std::to_string(static_cast<int32>(1LL << 33))},
+ {varint((1LL << 33) - 1),
+ std::to_string(static_cast<int32>((1LL << 33) - 1))},
+ TestValidDataForType(FieldDescriptor::TYPE_UINT32, {
+ {varint(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX
+ {varint(0), "0"},
+ {varint(1LL << 33), std::to_string(static_cast<uint32>(1LL << 33))},
+ std::to_string(static_cast<uint32>((1LL << 33) - 1))},
+ TestValidDataForType(FieldDescriptor::TYPE_FIXED64, {
+ {u64(12345), "12345"},
+ {u64(kUint64Max), std::to_string(kUint64Max)},
+ {u64(0), "0"}
+ TestValidDataForType(FieldDescriptor::TYPE_FIXED32, {
+ {u32(12345), "12345"},
+ {u32(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX
+ {u32(0), "0"}
+ TestValidDataForType(FieldDescriptor::TYPE_SFIXED64, {
+ {u64(kInt64Max), std::to_string(kInt64Max)},
+ {u64(kInt64Min), std::to_string(kInt64Min)}
+ TestValidDataForType(FieldDescriptor::TYPE_SFIXED32, {
+ {u32(kInt32Max), std::to_string(kInt32Max)},
+ {u32(kInt32Min), std::to_string(kInt32Min)}
+ TestValidDataForType(FieldDescriptor::TYPE_BOOL, {
+ {varint(1), "true"},
+ {varint(0), "false"},
+ {varint(12345678), "true"}
+ TestValidDataForType(FieldDescriptor::TYPE_SINT32, {
+ {zz32(12345), "12345"},
+ {zz32(kInt32Max), std::to_string(kInt32Max)},
+ {zz32(kInt32Min), std::to_string(kInt32Min)}
+ TestValidDataForType(FieldDescriptor::TYPE_SINT64, {
+ {zz64(12345), "12345"},
+ {zz64(kInt64Max), std::to_string(kInt64Max)},
+ {zz64(kInt64Min), std::to_string(kInt64Min)}
+ // TODO(haberman):
+ // TestValidDataForType(FieldDescriptor::TYPE_STRING
+ // TestValidDataForType(FieldDescriptor::TYPE_GROUP
+ // TestValidDataForType(FieldDescriptor::TYPE_MESSAGE
+ // TestValidDataForType(FieldDescriptor::TYPE_BYTES
+ // TestValidDataForType(FieldDescriptor::TYPE_ENUM
+ RunValidJsonTest("HelloWorld", REQUIRED,
+ "{\"optionalString\":\"Hello, World!\"}",
+ "optional_string: 'Hello, World!'");
+ // NOTE: The spec for JSON support is still being sorted out, these may not
+ // all be correct.
+ // Test field name conventions.
+ RunValidJsonTest(
+ "FieldNameInSnakeCase", REQUIRED,
+ R"({
+ "fieldname1": 1,
+ "fieldName2": 2,
+ "FieldName3": 3,
+ "fieldName4": 4
+ })",
+ R"(
+ fieldname1: 1
+ field_name2: 2
+ _field_name3: 3
+ field__name4_: 4
+ )");
+ "FieldNameWithNumbers", REQUIRED,
+ "field0name5": 5,
+ "field0Name6": 6
+ field0name5: 5
+ field_0_name6: 6
+ "FieldNameWithMixedCases", REQUIRED,
+ "fieldName7": 7,
+ "FieldName8": 8,
+ "fieldName9": 9,
+ "FieldName10": 10,
+ "FIELDNAME11": 11,
+ "FIELDName12": 12
+ fieldName7: 7
+ FieldName8: 8
+ field_Name9: 9
+ Field_Name10: 10
+ FIELD_NAME11: 11
+ FIELD_name12: 12
+ "FieldNameWithDoubleUnderscores", RECOMMENDED,
+ "FieldName13": 13,
+ "FieldName14": 14,
+ "fieldName15": 15,
+ "fieldName16": 16,
+ "fieldName17": 17,
+ "FieldName18": 18
+ __field_name13: 13
+ __Field_name14: 14
+ field__name15: 15
+ field__Name16: 16
+ field_name17__: 17
+ Field_name18__: 18
+ // Using the original proto field name in JSON is also allowed.
+ "OriginalProtoFieldName", REQUIRED,
+ "field_name2": 2,
+ "_field_name3": 3,
+ "field__name4_": 4,
+ "field_0_name6": 6,
+ "field_Name9": 9,
+ "Field_Name10": 10,
+ "FIELD_NAME11": 11,
+ "FIELD_name12": 12,
+ "__field_name13": 13,
+ "__Field_name14": 14,
+ "field__name15": 15,
+ "field__Name16": 16,
+ "field_name17__": 17,
+ "Field_name18__": 18
+ // Field names can be escaped.
+ "FieldNameEscaped", REQUIRED,
+ R"({"fieldn\u0061me1": 1})",
+ "fieldname1: 1");
+ // String ends with escape character.
+ ExpectParseFailureForJson(
+ "StringEndsWithEscapeChar", RECOMMENDED,
+ "{\"optionalString\": \"abc\\");
+ // Field names must be quoted (or it's not valid JSON).
+ "FieldNameNotQuoted", RECOMMENDED,
+ "{fieldname1: 1}");
+ // Trailing comma is not allowed (not valid JSON).
+ "TrailingCommaInAnObject", RECOMMENDED,
+ R"({"fieldname1":1,})");
+ "TrailingCommaInAnObjectWithSpace", RECOMMENDED,
+ R"({"fieldname1":1 ,})");
+ "TrailingCommaInAnObjectWithSpaceCommaSpace", RECOMMENDED,
+ R"({"fieldname1":1 , })");
+ "TrailingCommaInAnObjectWithNewlines", RECOMMENDED,
+ "fieldname1":1,
+ })");
+ // JSON doesn't support comments.
+ "JsonWithComments", RECOMMENDED,
+ // This is a comment.
+ "fieldname1": 1
+ // JSON spec says whitespace doesn't matter, so try a few spacings to be sure.
+ "OneLineNoSpaces", RECOMMENDED,
+ "{\"optionalInt32\":1,\"optionalInt64\":2}",
+ optional_int32: 1
+ optional_int64: 2
+ "OneLineWithSpaces", RECOMMENDED,
+ "{ \"optionalInt32\" : 1 , \"optionalInt64\" : 2 }",
+ "MultilineNoSpaces", RECOMMENDED,
+ "{\n\"optionalInt32\"\n:\n1\n,\n\"optionalInt64\"\n:\n2\n}",
+ "MultilineWithSpaces", RECOMMENDED,
+ "{\n \"optionalInt32\" : 1\n ,\n \"optionalInt64\" : 2\n}\n",
+ // Missing comma between key/value pairs.
+ "MissingCommaOneLine", RECOMMENDED,
+ "{ \"optionalInt32\": 1 \"optionalInt64\": 2 }");
+ "MissingCommaMultiline", RECOMMENDED,
+ "{\n \"optionalInt32\": 1\n \"optionalInt64\": 2\n}");
+ // Duplicated field names are not allowed.
+ "FieldNameDuplicate", RECOMMENDED,
+ "optionalNestedMessage": {a: 1},
+ "optionalNestedMessage": {}
+ "FieldNameDuplicateDifferentCasing1", RECOMMENDED,
+ "optional_nested_message": {a: 1},
+ "FieldNameDuplicateDifferentCasing2", RECOMMENDED,
+ "optional_nested_message": {}
+ // Serializers should use lowerCamelCase by default.
+ RunValidJsonTestWithValidator(
+ "FieldNameInLowerCamelCase", REQUIRED,
+ [](const Json::Value& value) {
+ return value.isMember("fieldname1") &&
+ value.isMember("fieldName2") &&
+ value.isMember("FieldName3") &&
+ value.isMember("fieldName4");
+ return value.isMember("field0name5") &&
+ value.isMember("field0Name6");
+ return value.isMember("fieldName7") &&
+ value.isMember("FieldName8") &&
+ value.isMember("fieldName9") &&
+ value.isMember("FieldName10") &&
+ value.isMember("FIELDNAME11") &&
+ value.isMember("FIELDName12");
+ return value.isMember("FieldName13") &&
+ value.isMember("FieldName14") &&
+ value.isMember("fieldName15") &&
+ value.isMember("fieldName16") &&
+ value.isMember("fieldName17") &&
+ value.isMember("FieldName18");
+ // Integer fields.
+ "Int32FieldMaxValue", REQUIRED,
+ R"({"optionalInt32": 2147483647})",
+ "optional_int32: 2147483647");
+ "Int32FieldMinValue", REQUIRED,
+ R"({"optionalInt32": -2147483648})",
+ "optional_int32: -2147483648");
+ "Uint32FieldMaxValue", REQUIRED,
+ R"({"optionalUint32": 4294967295})",
+ "optional_uint32: 4294967295");
+ "Int64FieldMaxValue", REQUIRED,
+ R"({"optionalInt64": "9223372036854775807"})",
+ "optional_int64: 9223372036854775807");
+ "Int64FieldMinValue", REQUIRED,
+ R"({"optionalInt64": "-9223372036854775808"})",
+ "optional_int64: -9223372036854775808");
+ "Uint64FieldMaxValue", REQUIRED,
+ R"({"optionalUint64": "18446744073709551615"})",
+ "optional_uint64: 18446744073709551615");
+ // While not the largest Int64, this is the largest
+ // Int64 which can be exactly represented within an
+ // IEEE-754 64-bit float, which is the expected level
+ // of interoperability guarantee. Larger values may
+ // work in some implementations, but should not be
+ // relied upon.
+ "Int64FieldMaxValueNotQuoted", REQUIRED,
+ R"({"optionalInt64": 9223372036854774784})",
+ "optional_int64: 9223372036854774784");
+ "Int64FieldMinValueNotQuoted", REQUIRED,
+ R"({"optionalInt64": -9223372036854775808})",
+ // Largest interoperable Uint64; see comment above
+ // for Int64FieldMaxValueNotQuoted.
+ "Uint64FieldMaxValueNotQuoted", REQUIRED,
+ R"({"optionalUint64": 18446744073709549568})",
+ "optional_uint64: 18446744073709549568");
+ // Values can be represented as JSON strings.
+ "Int32FieldStringValue", REQUIRED,
+ R"({"optionalInt32": "2147483647"})",
+ "Int32FieldStringValueEscaped", REQUIRED,
+ R"({"optionalInt32": "2\u003147483647"})",
+ // Parsers reject out-of-bound integer values.
+ "Int32FieldTooLarge", REQUIRED,
+ R"({"optionalInt32": 2147483648})");
+ "Int32FieldTooSmall", REQUIRED,
+ R"({"optionalInt32": -2147483649})");
+ "Uint32FieldTooLarge", REQUIRED,
+ R"({"optionalUint32": 4294967296})");
+ "Int64FieldTooLarge", REQUIRED,
+ R"({"optionalInt64": "9223372036854775808"})");
+ "Int64FieldTooSmall", REQUIRED,
+ R"({"optionalInt64": "-9223372036854775809"})");
+ "Uint64FieldTooLarge", REQUIRED,
+ R"({"optionalUint64": "18446744073709551616"})");
+ // Parser reject non-integer numeric values as well.
+ "Int32FieldNotInteger", REQUIRED,
+ R"({"optionalInt32": 0.5})");
+ "Uint32FieldNotInteger", REQUIRED,
+ R"({"optionalUint32": 0.5})");
+ "Int64FieldNotInteger", REQUIRED,
+ R"({"optionalInt64": "0.5"})");
+ "Uint64FieldNotInteger", REQUIRED,
+ R"({"optionalUint64": "0.5"})");
+ // Integers but represented as float values are accepted.
+ "Int32FieldFloatTrailingZero", REQUIRED,
+ R"({"optionalInt32": 100000.000})",
+ "optional_int32: 100000");
+ "Int32FieldExponentialFormat", REQUIRED,
+ R"({"optionalInt32": 1e5})",
+ "Int32FieldMaxFloatValue", REQUIRED,
+ R"({"optionalInt32": 2.147483647e9})",
+ "Int32FieldMinFloatValue", REQUIRED,
+ R"({"optionalInt32": -2.147483648e9})",
+ "Uint32FieldMaxFloatValue", REQUIRED,
+ R"({"optionalUint32": 4.294967295e9})",
+ // Parser reject non-numeric values.
+ "Int32FieldNotNumber", REQUIRED,
+ R"({"optionalInt32": "3x3"})");
+ "Uint32FieldNotNumber", REQUIRED,
+ R"({"optionalUint32": "3x3"})");
+ "Int64FieldNotNumber", REQUIRED,
+ R"({"optionalInt64": "3x3"})");
+ "Uint64FieldNotNumber", REQUIRED,
+ R"({"optionalUint64": "3x3"})");
+ // JSON does not allow "+" on numric values.
+ "Int32FieldPlusSign", REQUIRED,
+ R"({"optionalInt32": +1})");
+ // JSON doesn't allow leading 0s.
+ "Int32FieldLeadingZero", REQUIRED,
+ R"({"optionalInt32": 01})");
+ "Int32FieldNegativeWithLeadingZero", REQUIRED,
+ R"({"optionalInt32": -01})");
+ // String values must follow the same syntax rule. Specifically leading
+ // or trailing spaces are not allowed.
+ "Int32FieldLeadingSpace", REQUIRED,
+ R"({"optionalInt32": " 1"})");
+ "Int32FieldTrailingSpace", REQUIRED,
+ R"({"optionalInt32": "1 "})");
+ // 64-bit values are serialized as strings.
+ "Int64FieldBeString", RECOMMENDED,
+ R"({"optionalInt64": 1})",
+ return value["optionalInt64"].type() == Json::stringValue &&
+ value["optionalInt64"].asString() == "1";
+ "Uint64FieldBeString", RECOMMENDED,
+ R"({"optionalUint64": 1})",
+ return value["optionalUint64"].type() == Json::stringValue &&
+ value["optionalUint64"].asString() == "1";
+ // Bool fields.
+ "BoolFieldTrue", REQUIRED,
+ R"({"optionalBool":true})",
+ "optional_bool: true");
+ "BoolFieldFalse", REQUIRED,
+ R"({"optionalBool":false})",
+ "optional_bool: false");
+ // Other forms are not allowed.
+ "BoolFieldIntegerZero", RECOMMENDED,
+ R"({"optionalBool":0})");
+ "BoolFieldIntegerOne", RECOMMENDED,
+ R"({"optionalBool":1})");
+ "BoolFieldCamelCaseTrue", RECOMMENDED,
+ R"({"optionalBool":True})");
+ "BoolFieldCamelCaseFalse", RECOMMENDED,
+ R"({"optionalBool":False})");
+ "BoolFieldAllCapitalTrue", RECOMMENDED,
+ R"({"optionalBool":TRUE})");
+ "BoolFieldAllCapitalFalse", RECOMMENDED,
+ R"({"optionalBool":FALSE})");
+ "BoolFieldDoubleQuotedTrue", RECOMMENDED,
+ R"({"optionalBool":"true"})");
+ "BoolFieldDoubleQuotedFalse", RECOMMENDED,
+ R"({"optionalBool":"false"})");
+ // Float fields.
+ "FloatFieldMinPositiveValue", REQUIRED,
+ R"({"optionalFloat": 1.175494e-38})",
+ "optional_float: 1.175494e-38");
+ "FloatFieldMaxNegativeValue", REQUIRED,
+ R"({"optionalFloat": -1.175494e-38})",
+ "optional_float: -1.175494e-38");
+ "FloatFieldMaxPositiveValue", REQUIRED,
+ R"({"optionalFloat": 3.402823e+38})",
+ "optional_float: 3.402823e+38");
+ "FloatFieldMinNegativeValue", REQUIRED,
+ // Values can be quoted.
+ "FloatFieldQuotedValue", REQUIRED,
+ R"({"optionalFloat": "1"})",
+ "optional_float: 1");
+ // Special values.
+ "FloatFieldNan", REQUIRED,
+ R"({"optionalFloat": "NaN"})",
+ "optional_float: nan");
+ "FloatFieldInfinity", REQUIRED,
+ R"({"optionalFloat": "Infinity"})",
+ "optional_float: inf");
+ "FloatFieldNegativeInfinity", REQUIRED,
+ R"({"optionalFloat": "-Infinity"})",
+ "optional_float: -inf");
+ // Non-cannonical Nan will be correctly normalized.
+ {
+ TestAllTypesProto3 message;
+ // IEEE floating-point standard 32-bit quiet NaN:
+ // 0111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
+ message.set_optional_float(
+ WireFormatLite::DecodeFloat(0x7FA12345));
+ RunValidJsonTestWithProtobufInput(
+ "FloatFieldNormalizeQuietNan", REQUIRED, message,
+ // IEEE floating-point standard 64-bit signaling NaN:
+ // 1111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
+ WireFormatLite::DecodeFloat(0xFFB54321));
+ "FloatFieldNormalizeSignalingNan", REQUIRED, message,
+ // Special values must be quoted.
+ "FloatFieldNanNotQuoted", RECOMMENDED,
+ R"({"optionalFloat": NaN})");
+ "FloatFieldInfinityNotQuoted", RECOMMENDED,
+ R"({"optionalFloat": Infinity})");
+ "FloatFieldNegativeInfinityNotQuoted", RECOMMENDED,
+ R"({"optionalFloat": -Infinity})");
+ // Parsers should reject out-of-bound values.
+ "FloatFieldTooSmall", REQUIRED,
+ R"({"optionalFloat": -3.502823e+38})");
+ "FloatFieldTooLarge", REQUIRED,
+ R"({"optionalFloat": 3.502823e+38})");
+ // Double fields.
+ "DoubleFieldMinPositiveValue", REQUIRED,
+ R"({"optionalDouble": 2.22507e-308})",
+ "optional_double: 2.22507e-308");
+ "DoubleFieldMaxNegativeValue", REQUIRED,
+ R"({"optionalDouble": -2.22507e-308})",
+ "optional_double: -2.22507e-308");
+ "DoubleFieldMaxPositiveValue", REQUIRED,
+ R"({"optionalDouble": 1.79769e+308})",
+ "optional_double: 1.79769e+308");
+ "DoubleFieldMinNegativeValue", REQUIRED,
+ R"({"optionalDouble": -1.79769e+308})",
+ "optional_double: -1.79769e+308");
+ "DoubleFieldQuotedValue", REQUIRED,
+ R"({"optionalDouble": "1"})",
+ "optional_double: 1");
+ // Speical values.
+ "DoubleFieldNan", REQUIRED,
+ R"({"optionalDouble": "NaN"})",
+ "optional_double: nan");
+ "DoubleFieldInfinity", REQUIRED,
+ R"({"optionalDouble": "Infinity"})",
+ "optional_double: inf");
+ "DoubleFieldNegativeInfinity", REQUIRED,
+ R"({"optionalDouble": "-Infinity"})",
+ "optional_double: -inf");
+ message.set_optional_double(
+ WireFormatLite::DecodeDouble(0x7FFA123456789ABCLL));
+ "DoubleFieldNormalizeQuietNan", REQUIRED, message,
+ WireFormatLite::DecodeDouble(0xFFFBCBA987654321LL));
+ "DoubleFieldNormalizeSignalingNan", REQUIRED, message,
+ "DoubleFieldNanNotQuoted", RECOMMENDED,
+ R"({"optionalDouble": NaN})");
+ "DoubleFieldInfinityNotQuoted", RECOMMENDED,
+ R"({"optionalDouble": Infinity})");
+ "DoubleFieldNegativeInfinityNotQuoted", RECOMMENDED,
+ R"({"optionalDouble": -Infinity})");
+ "DoubleFieldTooSmall", REQUIRED,
+ R"({"optionalDouble": -1.89769e+308})");
+ "DoubleFieldTooLarge", REQUIRED,
+ R"({"optionalDouble": +1.89769e+308})");
+ // Enum fields.
+ "EnumField", REQUIRED,
+ R"({"optionalNestedEnum": "FOO"})",
+ "optional_nested_enum: FOO");
+ // Enum values must be represented as strings.
+ "EnumFieldNotQuoted", REQUIRED,
+ R"({"optionalNestedEnum": FOO})");
+ // Numeric values are allowed.
+ "EnumFieldNumericValueZero", REQUIRED,
+ R"({"optionalNestedEnum": 0})",
+ "EnumFieldNumericValueNonZero", REQUIRED,
+ R"({"optionalNestedEnum": 1})",
+ "optional_nested_enum: BAR");
+ // Unknown enum values are represented as numeric values.
+ "EnumFieldUnknownValue", REQUIRED,
+ R"({"optionalNestedEnum": 123})",
+ return value["optionalNestedEnum"].type() == Json::intValue &&
+ value["optionalNestedEnum"].asInt() == 123;
+ // String fields.
+ "StringField", REQUIRED,
+ R"({"optionalString": "Hello world!"})",
+ "optional_string: \"Hello world!\"");
+ "StringFieldUnicode", REQUIRED,
+ // Google in Chinese.
+ R"({"optionalString": "谷歌"})",
+ R"(optional_string: "谷歌")");
+ "StringFieldEscape", REQUIRED,
+ R"({"optionalString": "\"\\\/\b\f\n\r\t"})",
+ R"(optional_string: "\"\\/\b\f\n\r\t")");
+ "StringFieldUnicodeEscape", REQUIRED,
+ R"({"optionalString": "\u8C37\u6B4C"})",
+ "StringFieldUnicodeEscapeWithLowercaseHexLetters", REQUIRED,
+ R"({"optionalString": "\u8c37\u6b4c"})",
+ "StringFieldSurrogatePair", REQUIRED,
+ // The character is an emoji: grinning face with smiling eyes. 😁
+ R"({"optionalString": "\uD83D\uDE01"})",
+ R"(optional_string: "\xF0\x9F\x98\x81")");
+ // Unicode escapes must start with "\u" (lowercase u).
+ "StringFieldUppercaseEscapeLetter", RECOMMENDED,
+ R"({"optionalString": "\U8C37\U6b4C"})");
+ "StringFieldInvalidEscape", RECOMMENDED,
+ R"({"optionalString": "\uXXXX\u6B4C"})");
+ "StringFieldUnterminatedEscape", RECOMMENDED,
+ R"({"optionalString": "\u8C3"})");
+ "StringFieldUnpairedHighSurrogate", RECOMMENDED,
+ R"({"optionalString": "\uD800"})");
+ "StringFieldUnpairedLowSurrogate", RECOMMENDED,
+ R"({"optionalString": "\uDC00"})");
+ "StringFieldSurrogateInWrongOrder", RECOMMENDED,
+ R"({"optionalString": "\uDE01\uD83D"})");
+ "StringFieldNotAString", REQUIRED,
+ R"({"optionalString": 12345})");
+ // Bytes fields.
+ "BytesField", REQUIRED,
+ R"({"optionalBytes": "AQI="})",
+ R"(optional_bytes: "\x01\x02")");
+ "BytesFieldBase64Url", RECOMMENDED,
+ R"({"optionalBytes": "-_"})",
+ R"(optional_bytes: "\xfb")");
+ // Message fields.
+ "MessageField", REQUIRED,
+ R"({"optionalNestedMessage": {"a": 1234}})",
+ "optional_nested_message: {a: 1234}");
+ // Oneof fields.
+ "OneofFieldDuplicate", REQUIRED,
+ R"({"oneofUint32": 1, "oneofString": "test"})");
+ // Ensure zero values for oneof make it out/backs.
+ TestAllTypesProto3 messageProto3;
+ TestAllTypesProto2 messageProto2;
+ TestOneofMessage(messageProto3, true);
+ TestOneofMessage(messageProto2, false);
+ "OneofZeroUint32", RECOMMENDED,
+ R"({"oneofUint32": 0})", "oneof_uint32: 0");
+ "OneofZeroMessage", RECOMMENDED,
+ R"({"oneofNestedMessage": {}})", "oneof_nested_message: {}");
+ "OneofZeroString", RECOMMENDED,
+ R"({"oneofString": ""})", "oneof_string: \"\"");
+ "OneofZeroBytes", RECOMMENDED,
+ R"({"oneofBytes": ""})", "oneof_bytes: \"\"");
+ "OneofZeroBool", RECOMMENDED,
+ R"({"oneofBool": false})", "oneof_bool: false");
+ "OneofZeroUint64", RECOMMENDED,
+ R"({"oneofUint64": 0})", "oneof_uint64: 0");
+ "OneofZeroFloat", RECOMMENDED,
+ R"({"oneofFloat": 0.0})", "oneof_float: 0");
+ "OneofZeroDouble", RECOMMENDED,
+ R"({"oneofDouble": 0.0})", "oneof_double: 0");
+ "OneofZeroEnum", RECOMMENDED,
+ R"({"oneofEnum":"FOO"})", "oneof_enum: FOO");
+ // Repeated fields.
+ "PrimitiveRepeatedField", REQUIRED,
+ R"({"repeatedInt32": [1, 2, 3, 4]})",
+ "repeated_int32: [1, 2, 3, 4]");
+ "EnumRepeatedField", REQUIRED,
+ R"({"repeatedNestedEnum": ["FOO", "BAR", "BAZ"]})",
+ "repeated_nested_enum: [FOO, BAR, BAZ]");
+ "StringRepeatedField", REQUIRED,
+ R"({"repeatedString": ["Hello", "world"]})",
+ R"(repeated_string: ["Hello", "world"])");
+ "BytesRepeatedField", REQUIRED,
+ R"({"repeatedBytes": ["AAEC", "AQI="]})",
+ R"(repeated_bytes: ["\x00\x01\x02", "\x01\x02"])");
+ "MessageRepeatedField", REQUIRED,
+ R"({"repeatedNestedMessage": [{"a": 1234}, {"a": 5678}]})",
+ "repeated_nested_message: {a: 1234}"
+ "repeated_nested_message: {a: 5678}");
+ // Repeated field elements are of incorrect type.
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotBool", REQUIRED,
+ R"({"repeatedInt32": [1, false, 3, 4]})");
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotString", REQUIRED,
+ R"({"repeatedInt32": [1, 2, "name", 4]})");
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotMessage", REQUIRED,
+ R"({"repeatedInt32": [1, 2, 3, {"a": 4}]})");
+ "RepeatedFieldWrongElementTypeExpectingStringsGotInt", REQUIRED,
+ R"({"repeatedString": ["1", 2, "3", "4"]})");
+ "RepeatedFieldWrongElementTypeExpectingStringsGotBool", REQUIRED,
+ R"({"repeatedString": ["1", "2", false, "4"]})");
+ "RepeatedFieldWrongElementTypeExpectingStringsGotMessage", REQUIRED,
+ R"({"repeatedString": ["1", 2, "3", {"a": 4}]})");
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotInt", REQUIRED,
+ R"({"repeatedNestedMessage": [{"a": 1}, 2]})");
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotBool", REQUIRED,
+ R"({"repeatedNestedMessage": [{"a": 1}, false]})");
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotString", REQUIRED,
+ R"({"repeatedNestedMessage": [{"a": 1}, "2"]})");
+ // Trailing comma in the repeated field is not allowed.
+ "RepeatedFieldTrailingComma", RECOMMENDED,
+ R"({"repeatedInt32": [1, 2, 3, 4,]})");
+ "RepeatedFieldTrailingCommaWithSpace", RECOMMENDED,
+ "{\"repeatedInt32\": [1, 2, 3, 4 ,]}");
+ "RepeatedFieldTrailingCommaWithSpaceCommaSpace", RECOMMENDED,
+ "{\"repeatedInt32\": [1, 2, 3, 4 , ]}");
+ "RepeatedFieldTrailingCommaWithNewlines", RECOMMENDED,
+ "{\"repeatedInt32\": [\n 1,\n 2,\n 3,\n 4,\n]}");
+ // Map fields.
+ "Int32MapField", REQUIRED,
+ R"({"mapInt32Int32": {"1": 2, "3": 4}})",
+ "map_int32_int32: {key: 1 value: 2}"
+ "map_int32_int32: {key: 3 value: 4}");
+ "Int32MapFieldKeyNotQuoted", RECOMMENDED,
+ R"({"mapInt32Int32": {1: 2, 3: 4}})");
+ "Uint32MapField", REQUIRED,
+ R"({"mapUint32Uint32": {"1": 2, "3": 4}})",
+ "map_uint32_uint32: {key: 1 value: 2}"
+ "map_uint32_uint32: {key: 3 value: 4}");
+ "Uint32MapFieldKeyNotQuoted", RECOMMENDED,
+ R"({"mapUint32Uint32": {1: 2, 3: 4}})");
+ "Int64MapField", REQUIRED,
+ R"({"mapInt64Int64": {"1": 2, "3": 4}})",
+ "map_int64_int64: {key: 1 value: 2}"
+ "map_int64_int64: {key: 3 value: 4}");
+ "Int64MapFieldKeyNotQuoted", RECOMMENDED,
+ R"({"mapInt64Int64": {1: 2, 3: 4}})");
+ "Uint64MapField", REQUIRED,
+ R"({"mapUint64Uint64": {"1": 2, "3": 4}})",
+ "map_uint64_uint64: {key: 1 value: 2}"
+ "map_uint64_uint64: {key: 3 value: 4}");
+ "Uint64MapFieldKeyNotQuoted", RECOMMENDED,
+ R"({"mapUint64Uint64": {1: 2, 3: 4}})");
+ "BoolMapField", REQUIRED,
+ R"({"mapBoolBool": {"true": true, "false": false}})",
+ "map_bool_bool: {key: true value: true}"
+ "map_bool_bool: {key: false value: false}");
+ "BoolMapFieldKeyNotQuoted", RECOMMENDED,
+ R"({"mapBoolBool": {true: true, false: false}})");
+ "MessageMapField", REQUIRED,
+ "mapStringNestedMessage": {
+ "hello": {"a": 1234},
+ "world": {"a": 5678}
+ map_string_nested_message: {
+ key: "hello"
+ value: {a: 1234}
+ key: "world"
+ value: {a: 5678}
+ // Since Map keys are represented as JSON strings, escaping should be allowed.
+ "Int32MapEscapedKey", REQUIRED,
+ R"({"mapInt32Int32": {"\u0031": 2}})",
+ "map_int32_int32: {key: 1 value: 2}");
+ "Int64MapEscapedKey", REQUIRED,
+ R"({"mapInt64Int64": {"\u0031": 2}})",
+ "map_int64_int64: {key: 1 value: 2}");
+ "BoolMapEscapedKey", REQUIRED,
+ R"({"mapBoolBool": {"tr\u0075e": true}})",
+ "map_bool_bool: {key: true value: true}");
+ // "null" is accepted for all fields types.
+ "AllFieldAcceptNull", REQUIRED,
+ "optionalInt32": null,
+ "optionalInt64": null,
+ "optionalUint32": null,
+ "optionalUint64": null,
+ "optionalSint32": null,
+ "optionalSint64": null,
+ "optionalFixed32": null,
+ "optionalFixed64": null,
+ "optionalSfixed32": null,
+ "optionalSfixed64": null,
+ "optionalFloat": null,
+ "optionalDouble": null,
+ "optionalBool": null,
+ "optionalString": null,
+ "optionalBytes": null,
+ "optionalNestedEnum": null,
+ "optionalNestedMessage": null,
+ "repeatedInt32": null,
+ "repeatedInt64": null,
+ "repeatedUint32": null,
+ "repeatedUint64": null,
+ "repeatedSint32": null,
+ "repeatedSint64": null,
+ "repeatedFixed32": null,
+ "repeatedFixed64": null,
+ "repeatedSfixed32": null,
+ "repeatedSfixed64": null,
+ "repeatedFloat": null,
+ "repeatedDouble": null,
+ "repeatedBool": null,
+ "repeatedString": null,
+ "repeatedBytes": null,
+ "repeatedNestedEnum": null,
+ "repeatedNestedMessage": null,
+ "mapInt32Int32": null,
+ "mapBoolBool": null,
+ "mapStringNestedMessage": null
+ "");
+ // Repeated field elements cannot be null.
+ "RepeatedFieldPrimitiveElementIsNull", RECOMMENDED,
+ R"({"repeatedInt32": [1, null, 2]})");
+ "RepeatedFieldMessageElementIsNull", RECOMMENDED,
+ R"({"repeatedNestedMessage": [{"a":1}, null, {"a":2}]})");
+ // Map field keys cannot be null.
+ "MapFieldKeyIsNull", RECOMMENDED,
+ R"({"mapInt32Int32": {null: 1}})");
+ // Map field values cannot be null.
+ "MapFieldValueIsNull", RECOMMENDED,
+ R"({"mapInt32Int32": {"0": null}})");
+ // http://www.rfc-editor.org/rfc/rfc7159.txt says strings have to use double
+ // quotes.
+ "StringFieldSingleQuoteKey", RECOMMENDED,
+ R"({'optionalString': "Hello world!"})");
+ "StringFieldSingleQuoteValue", RECOMMENDED,
+ R"({"optionalString": 'Hello world!'})");
+ "StringFieldSingleQuoteBoth", RECOMMENDED,
+ R"({'optionalString': 'Hello world!'})");
+ // Unknown fields.
+ //TODO(yilunchong): update this behavior when unknown field's behavior
+ // changed in open source. Also delete
+ // Required.Proto3.ProtobufInput.UnknownVarint.ProtobufOutput
+ // from failure list of python_cpp python java
+ TestUnknownMessage(messageProto3, true);
+ TestUnknownMessage(messageProto2, false);
+ // Wrapper types.
+ "OptionalBoolWrapper", REQUIRED,
+ R"({"optionalBoolWrapper": false})",
+ "optional_bool_wrapper: {value: false}");
+ "OptionalInt32Wrapper", REQUIRED,
+ R"({"optionalInt32Wrapper": 0})",
+ "optional_int32_wrapper: {value: 0}");
+ "OptionalUint32Wrapper", REQUIRED,
+ R"({"optionalUint32Wrapper": 0})",
+ "optional_uint32_wrapper: {value: 0}");
+ "OptionalInt64Wrapper", REQUIRED,
+ R"({"optionalInt64Wrapper": 0})",
+ "optional_int64_wrapper: {value: 0}");
+ "OptionalUint64Wrapper", REQUIRED,
+ R"({"optionalUint64Wrapper": 0})",
+ "optional_uint64_wrapper: {value: 0}");
+ "OptionalFloatWrapper", REQUIRED,
+ R"({"optionalFloatWrapper": 0})",
+ "optional_float_wrapper: {value: 0}");
+ "OptionalDoubleWrapper", REQUIRED,
+ R"({"optionalDoubleWrapper": 0})",
+ "optional_double_wrapper: {value: 0}");
+ "OptionalStringWrapper", REQUIRED,
+ R"({"optionalStringWrapper": ""})",
+ R"(optional_string_wrapper: {value: ""})");
+ "OptionalBytesWrapper", REQUIRED,
+ R"({"optionalBytesWrapper": ""})",
+ R"(optional_bytes_wrapper: {value: ""})");
+ "OptionalWrapperTypesWithNonDefaultValue", REQUIRED,
+ "optionalBoolWrapper": true,
+ "optionalInt32Wrapper": 1,
+ "optionalUint32Wrapper": 1,
+ "optionalInt64Wrapper": "1",
+ "optionalUint64Wrapper": "1",
+ "optionalFloatWrapper": 1,
+ "optionalDoubleWrapper": 1,
+ "optionalStringWrapper": "1",
+ "optionalBytesWrapper": "AQI="
+ optional_bool_wrapper: {value: true}
+ optional_int32_wrapper: {value: 1}
+ optional_uint32_wrapper: {value: 1}
+ optional_int64_wrapper: {value: 1}
+ optional_uint64_wrapper: {value: 1}
+ optional_float_wrapper: {value: 1}
+ optional_double_wrapper: {value: 1}
+ optional_string_wrapper: {value: "1"}
+ optional_bytes_wrapper: {value: "\x01\x02"}
+ "RepeatedBoolWrapper", REQUIRED,
+ R"({"repeatedBoolWrapper": [true, false]})",
+ "repeated_bool_wrapper: {value: true}"
+ "repeated_bool_wrapper: {value: false}");
+ "RepeatedInt32Wrapper", REQUIRED,
+ R"({"repeatedInt32Wrapper": [0, 1]})",
+ "repeated_int32_wrapper: {value: 0}"
+ "repeated_int32_wrapper: {value: 1}");
+ "RepeatedUint32Wrapper", REQUIRED,
+ R"({"repeatedUint32Wrapper": [0, 1]})",
+ "repeated_uint32_wrapper: {value: 0}"
+ "repeated_uint32_wrapper: {value: 1}");
+ "RepeatedInt64Wrapper", REQUIRED,
+ R"({"repeatedInt64Wrapper": [0, 1]})",
+ "repeated_int64_wrapper: {value: 0}"
+ "repeated_int64_wrapper: {value: 1}");
+ "RepeatedUint64Wrapper", REQUIRED,
+ R"({"repeatedUint64Wrapper": [0, 1]})",
+ "repeated_uint64_wrapper: {value: 0}"
+ "repeated_uint64_wrapper: {value: 1}");
+ "RepeatedFloatWrapper", REQUIRED,
+ R"({"repeatedFloatWrapper": [0, 1]})",
+ "repeated_float_wrapper: {value: 0}"
+ "repeated_float_wrapper: {value: 1}");
+ "RepeatedDoubleWrapper", REQUIRED,
+ R"({"repeatedDoubleWrapper": [0, 1]})",
+ "repeated_double_wrapper: {value: 0}"
+ "repeated_double_wrapper: {value: 1}");
+ "RepeatedStringWrapper", REQUIRED,
+ R"({"repeatedStringWrapper": ["", "AQI="]})",
+ repeated_string_wrapper: {value: ""}
+ repeated_string_wrapper: {value: "AQI="}
+ "RepeatedBytesWrapper", REQUIRED,
+ R"({"repeatedBytesWrapper": ["", "AQI="]})",
+ repeated_bytes_wrapper: {value: ""}
+ repeated_bytes_wrapper: {value: "\x01\x02"}
+ "WrapperTypesWithNullValue", REQUIRED,
+ "optionalBoolWrapper": null,
+ "optionalInt32Wrapper": null,
+ "optionalUint32Wrapper": null,
+ "optionalInt64Wrapper": null,
+ "optionalUint64Wrapper": null,
+ "optionalFloatWrapper": null,
+ "optionalDoubleWrapper": null,
+ "optionalStringWrapper": null,
+ "optionalBytesWrapper": null,
+ "repeatedBoolWrapper": null,
+ "repeatedInt32Wrapper": null,
+ "repeatedUint32Wrapper": null,
+ "repeatedInt64Wrapper": null,
+ "repeatedUint64Wrapper": null,
+ "repeatedFloatWrapper": null,
+ "repeatedDoubleWrapper": null,
+ "repeatedStringWrapper": null,
+ "repeatedBytesWrapper": null
+ // Duration
+ "DurationMinValue", REQUIRED,
+ R"({"optionalDuration": "-315576000000.999999999s"})",
+ "optional_duration: {seconds: -315576000000 nanos: -999999999}");
+ "DurationMaxValue", REQUIRED,
+ R"({"optionalDuration": "315576000000.999999999s"})",
+ "optional_duration: {seconds: 315576000000 nanos: 999999999}");
+ "DurationRepeatedValue", REQUIRED,
+ R"({"repeatedDuration": ["1.5s", "-1.5s"]})",
+ "repeated_duration: {seconds: 1 nanos: 500000000}"
+ "repeated_duration: {seconds: -1 nanos: -500000000}");
+ "DurationNull", REQUIRED,
+ R"({"optionalDuration": null})",
+ "DurationMissingS", REQUIRED,
+ R"({"optionalDuration": "1"})");
+ "DurationJsonInputTooSmall", REQUIRED,
+ R"({"optionalDuration": "-315576000001.000000000s"})");
+ "DurationJsonInputTooLarge", REQUIRED,
+ R"({"optionalDuration": "315576000001.000000000s"})");
+ ExpectSerializeFailureForJson(
+ "DurationProtoInputTooSmall", REQUIRED,
+ "optional_duration: {seconds: -315576000001 nanos: 0}");
+ "DurationProtoInputTooLarge", REQUIRED,
+ "optional_duration: {seconds: 315576000001 nanos: 0}");
+ "DurationHasZeroFractionalDigit", RECOMMENDED,
+ R"({"optionalDuration": "1.000000000s"})",
+ return value["optionalDuration"].asString() == "1s";
+ "DurationHas3FractionalDigits", RECOMMENDED,
+ R"({"optionalDuration": "1.010000000s"})",
+ return value["optionalDuration"].asString() == "1.010s";
+ "DurationHas6FractionalDigits", RECOMMENDED,
+ R"({"optionalDuration": "1.000010000s"})",
+ return value["optionalDuration"].asString() == "1.000010s";
+ "DurationHas9FractionalDigits", RECOMMENDED,
+ R"({"optionalDuration": "1.000000010s"})",
+ return value["optionalDuration"].asString() == "1.000000010s";
+ // Timestamp
+ "TimestampMinValue", REQUIRED,
+ R"({"optionalTimestamp": "0001-01-01T00:00:00Z"})",
+ "optional_timestamp: {seconds: -62135596800}");
+ "TimestampMaxValue", REQUIRED,
+ R"({"optionalTimestamp": "9999-12-31T23:59:59.999999999Z"})",
+ "optional_timestamp: {seconds: 253402300799 nanos: 999999999}");
+ "TimestampRepeatedValue", REQUIRED,
+ "repeatedTimestamp": [
+ "0001-01-01T00:00:00Z",
+ "9999-12-31T23:59:59.999999999Z"
+ ]
+ "repeated_timestamp: {seconds: -62135596800}"
+ "repeated_timestamp: {seconds: 253402300799 nanos: 999999999}");
+ "TimestampWithPositiveOffset", REQUIRED,
+ R"({"optionalTimestamp": "1970-01-01T08:00:00+08:00"})",
+ "optional_timestamp: {seconds: 0}");
+ "TimestampWithNegativeOffset", REQUIRED,
+ R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})",
+ "TimestampNull", REQUIRED,
+ R"({"optionalTimestamp": null})",
+ "TimestampJsonInputTooSmall", REQUIRED,
+ R"({"optionalTimestamp": "0000-01-01T00:00:00Z"})");
+ "TimestampJsonInputTooLarge", REQUIRED,
+ R"({"optionalTimestamp": "10000-01-01T00:00:00Z"})");
+ "TimestampJsonInputMissingZ", REQUIRED,
+ R"({"optionalTimestamp": "0001-01-01T00:00:00"})");
+ "TimestampJsonInputMissingT", REQUIRED,
+ R"({"optionalTimestamp": "0001-01-01 00:00:00Z"})");
+ "TimestampJsonInputLowercaseZ", REQUIRED,
+ R"({"optionalTimestamp": "0001-01-01T00:00:00z"})");
+ "TimestampJsonInputLowercaseT", REQUIRED,
+ R"({"optionalTimestamp": "0001-01-01t00:00:00Z"})");
+ "TimestampProtoInputTooSmall", REQUIRED,
+ "optional_timestamp: {seconds: -62135596801}");
+ "TimestampProtoInputTooLarge", REQUIRED,
+ "optional_timestamp: {seconds: 253402300800}");
+ "TimestampZeroNormalized", RECOMMENDED,
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00Z";
+ "TimestampHasZeroFractionalDigit", RECOMMENDED,
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000000000Z"})",
+ "TimestampHas3FractionalDigits", RECOMMENDED,
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.010000000Z"})",
+ "1970-01-01T00:00:00.010Z";
+ "TimestampHas6FractionalDigits", RECOMMENDED,
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000010000Z"})",
+ "1970-01-01T00:00:00.000010Z";
+ "TimestampHas9FractionalDigits", RECOMMENDED,
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000000010Z"})",
+ "1970-01-01T00:00:00.000000010Z";
+ // FieldMask
+ "FieldMask", REQUIRED,
+ R"({"optionalFieldMask": "foo,barBaz"})",
+ R"(optional_field_mask: {paths: "foo" paths: "bar_baz"})");
+ "FieldMaskInvalidCharacter", RECOMMENDED,
+ R"({"optionalFieldMask": "foo,bar_bar"})");
+ "FieldMaskPathsDontRoundTrip", RECOMMENDED,
+ R"(optional_field_mask: {paths: "fooBar"})");
+ "FieldMaskNumbersDontRoundTrip", RECOMMENDED,
+ R"(optional_field_mask: {paths: "foo_3_bar"})");
+ "FieldMaskTooManyUnderscore", RECOMMENDED,
+ R"(optional_field_mask: {paths: "foo__bar"})");
+ // Struct
+ "Struct", REQUIRED,
+ "optionalStruct": {
+ "nullValue": null,
+ "intValue": 1234,
+ "boolValue": true,
+ "doubleValue": 1234.5678,
+ "stringValue": "Hello world!",
+ "listValue": [1234, "5678"],
+ "objectValue": {
+ "value": 0
+ optional_struct: {
+ fields: {
+ key: "nullValue"
+ value: {null_value: NULL_VALUE}
+ key: "intValue"
+ value: {number_value: 1234}
+ key: "boolValue"
+ value: {bool_value: true}
+ key: "doubleValue"
+ value: {number_value: 1234.5678}
+ key: "stringValue"
+ value: {string_value: "Hello world!"}
+ key: "listValue"
+ value: {
+ list_value: {
+ values: {
+ number_value: 1234
+ string_value: "5678"
+ key: "objectValue"
+ struct_value: {
+ key: "value"
+ number_value: 0
+ // Value
+ "ValueAcceptInteger", REQUIRED,
+ R"({"optionalValue": 1})",
+ "optional_value: { number_value: 1}");
+ "ValueAcceptFloat", REQUIRED,
+ R"({"optionalValue": 1.5})",
+ "optional_value: { number_value: 1.5}");
+ "ValueAcceptBool", REQUIRED,
+ R"({"optionalValue": false})",
+ "optional_value: { bool_value: false}");
+ "ValueAcceptNull", REQUIRED,
+ R"({"optionalValue": null})",
+ "optional_value: { null_value: NULL_VALUE}");
+ "ValueAcceptString", REQUIRED,
+ R"({"optionalValue": "hello"})",
+ R"(optional_value: { string_value: "hello"})");
+ "ValueAcceptList", REQUIRED,
+ R"({"optionalValue": [0, "hello"]})",
+ optional_value: {
+ string_value: "hello"
+ "ValueAcceptObject", REQUIRED,
+ R"({"optionalValue": {"value": 1}})",
+ number_value: 1
+ // Any
+ "Any", REQUIRED,
+ "optionalAny": {
+ "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3",
+ "optionalInt32": 12345
+ optional_any: {
+ [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
+ optional_int32: 12345
+ "AnyNested", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Any",
+ "value": {
+ [type.googleapis.com/google.protobuf.Any] {
+ // The special "@type" tag is not required to appear first.
+ "AnyUnorderedTypeTag", REQUIRED,
+ "optionalInt32": 12345,
+ "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
+ // Well-known types in Any.
+ "AnyWithInt32ValueWrapper", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Int32Value",
+ "value": 12345
+ [type.googleapis.com/google.protobuf.Int32Value] {
+ value: 12345
+ "AnyWithDuration", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Duration",
+ "value": "1.5s"
+ [type.googleapis.com/google.protobuf.Duration] {
+ seconds: 1
+ nanos: 500000000
+ "AnyWithTimestamp", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Timestamp",
+ "value": "1970-01-01T00:00:00Z"
+ [type.googleapis.com/google.protobuf.Timestamp] {
+ seconds: 0
+ nanos: 0
+ "AnyWithFieldMask", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.FieldMask",
+ "value": "foo,barBaz"
+ [type.googleapis.com/google.protobuf.FieldMask] {
+ paths: ["foo", "bar_baz"]
+ "AnyWithStruct", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Struct",
+ "foo": 1
+ [type.googleapis.com/google.protobuf.Struct] {
+ key: "foo"
+ "AnyWithValueForJsonObject", REQUIRED,
+ "@type": "type.googleapis.com/google.protobuf.Value",
+ [type.googleapis.com/google.protobuf.Value] {
+ "AnyWithValueForInteger", REQUIRED,
+ "value": 1
+ RunValidJsonIgnoreUnknownTest(
+ "IgnoreUnknownJsonNumber", REQUIRED,
+ "unknown": 1
+ "IgnoreUnknownJsonString", REQUIRED,
+ "unknown": "a"
+ "IgnoreUnknownJsonTrue", REQUIRED,
+ "unknown": true
+ "IgnoreUnknownJsonFalse", REQUIRED,
+ "unknown": false
+ "IgnoreUnknownJsonNull", REQUIRED,
+ "unknown": null
+ "IgnoreUnknownJsonObject", REQUIRED,
+ "unknown": {"a": 1}
+struct StaticTestSuiteInitializer {
+ StaticTestSuiteInitializer() {
+ AddTestSuite(new ConformanceTestSuiteImpl());
+} static_test_suite_initializer;
+} // namespace protobuf
+} // namespace google
@@ -66,9 +66,9 @@
-using conformance::ConformanceRequest;
using google::protobuf::StringAppendF;
+using google::protobuf::ConformanceTestSuite;
using std::vector;
@@ -287,7 +287,8 @@ void ParseFailureList(const char *filename, std::vector<string>* failure_list) {
int main(int argc, char *argv[]) {
char *program;
- google::protobuf::ConformanceTestSuite suite;
+ const std::set<ConformanceTestSuite*>& test_suite_set =
+ ::google::protobuf::GetTestSuiteSet();
string failure_list_filename;
std::vector<string> failure_list;
@@ -298,9 +299,13 @@ int main(int argc, char *argv[]) {
failure_list_filename = argv[arg];
ParseFailureList(argv[arg], &failure_list);
} else if (strcmp(argv[arg], "--verbose") == 0) {
- suite.SetVerbose(true);
+ for (auto *suite : test_suite_set) {
+ suite->SetVerbose(true);
} else if (strcmp(argv[arg], "--enforce_recommended") == 0) {
- suite.SetEnforceRecommended(true);
+ for (auto suite : test_suite_set) {
+ suite->SetEnforceRecommended(true);
} else if (argv[arg][0] == '-') {
fprintf(stderr, "Unknown option: %s\n", argv[arg]);
UsageError();
@@ -313,11 +318,16 @@ int main(int argc, char *argv[]) {
- suite.SetFailureList(failure_list_filename, failure_list);
+ suite->SetFailureList(failure_list_filename, failure_list);
ForkPipeRunner runner(program);
std::string output;
- bool ok = suite.RunSuite(&runner, &output);
+ bool ok = true;
+ ok &= suite->RunSuite(&runner, &output);
fwrite(output.c_str(), 1, output.size(), stderr);
@@ -54,9 +54,3 @@ Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32
Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64
Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32
Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64
-Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput
-Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput
-Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput
-Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput
-Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput
-Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput
@@ -1,8 +1,2 @@
Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
@@ -45,9 +45,3 @@ Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValu
Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
@@ -1,6 +1,8 @@
Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput
+Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput
Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator
Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator
Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator
@@ -14,6 +16,11 @@ Recommended.Proto3.JsonInput.StringEndsWithEscapeChar
Recommended.Proto3.JsonInput.StringFieldSurrogateInWrongOrder
Recommended.Proto3.JsonInput.StringFieldUnpairedHighSurrogate
Recommended.Proto3.JsonInput.StringFieldUnpairedLowSurrogate
+Recommended.Proto3.JsonInput.TimestampHas3FractionalDigits.Validator
+Recommended.Proto3.JsonInput.TimestampHas6FractionalDigits.Validator
+Recommended.Proto3.JsonInput.TimestampHas9FractionalDigits.Validator
+Recommended.Proto3.JsonInput.TimestampHasZeroFractionalDigit.Validator
+Recommended.Proto3.JsonInput.TimestampZeroNormalized.Validator
Recommended.Proto3.JsonInput.Uint64FieldBeString.Validator
Recommended.Proto3.ProtobufInput.OneofZeroBytes.JsonOutput
Required.DurationProtoInputTooLarge.JsonOutput
@@ -55,6 +62,7 @@ Required.Proto3.JsonInput.FieldMask.ProtobufOutput
Required.Proto3.JsonInput.FloatFieldInfinity.JsonOutput
Required.Proto3.JsonInput.FloatFieldNan.JsonOutput
Required.Proto3.JsonInput.FloatFieldNegativeInfinity.JsonOutput
+Required.Proto3.JsonInput.OneofFieldDuplicate
Required.Proto3.JsonInput.OptionalBoolWrapper.JsonOutput
Required.Proto3.JsonInput.OptionalBoolWrapper.ProtobufOutput
Required.Proto3.JsonInput.OptionalBytesWrapper.JsonOutput
@@ -103,6 +111,16 @@ Required.Proto3.JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.JsonOu
Required.Proto3.JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.ProtobufOutput
Required.Proto3.JsonInput.Struct.JsonOutput
Required.Proto3.JsonInput.Struct.ProtobufOutput
+Required.Proto3.JsonInput.TimestampMaxValue.JsonOutput
+Required.Proto3.JsonInput.TimestampMaxValue.ProtobufOutput
+Required.Proto3.JsonInput.TimestampMinValue.JsonOutput
+Required.Proto3.JsonInput.TimestampMinValue.ProtobufOutput
+Required.Proto3.JsonInput.TimestampRepeatedValue.JsonOutput
+Required.Proto3.JsonInput.TimestampRepeatedValue.ProtobufOutput
+Required.Proto3.JsonInput.TimestampWithNegativeOffset.JsonOutput
+Required.Proto3.JsonInput.TimestampWithNegativeOffset.ProtobufOutput
+Required.Proto3.JsonInput.TimestampWithPositiveOffset.JsonOutput
+Required.Proto3.JsonInput.TimestampWithPositiveOffset.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptBool.JsonOutput
Required.Proto3.JsonInput.ValueAcceptBool.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptFloat.JsonOutput
@@ -111,6 +129,8 @@ Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput
Required.Proto3.JsonInput.ValueAcceptInteger.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptList.JsonOutput
Required.Proto3.JsonInput.ValueAcceptList.ProtobufOutput
+Required.Proto3.JsonInput.ValueAcceptListWithNull.JsonOutput
+Required.Proto3.JsonInput.ValueAcceptListWithNull.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptNull.JsonOutput
Required.Proto3.JsonInput.ValueAcceptNull.ProtobufOutput
Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput
JsonInput.StringFieldSurrogateInWrongOrder
JsonInput.StringFieldUnpairedHighSurrogate
@@ -19,9 +19,3 @@ Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_0
Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_1
Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_2
Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_3
@@ -52,9 +52,3 @@ Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32
@@ -120,8 +120,6 @@ Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput
-Required.Proto3.JsonInput.ValueAcceptListWithNull.JsonOutput
-Required.Proto3.JsonInput.ValueAcceptListWithNull.ProtobufOutput
@@ -135,9 +133,3 @@ Required.Proto3.ProtobufInput.FloatFieldNormalizeSignalingNan.JsonOutput
Required.Proto3.ProtobufInput.ValidDataRepeated.FLOAT.JsonOutput
Required.TimestampProtoInputTooLarge.JsonOutput
Required.TimestampProtoInputTooSmall.JsonOutput
@@ -32,7 +32,6 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Internal.EnumLite;
import java.io.IOException;
@@ -446,10 +445,7 @@ public abstract class AbstractMessage
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
- boolean discardUnknown =
- getDescriptorForType().getFile().getSyntax() == Syntax.PROTO3
- ? input.shouldDiscardUnknownFieldsProto3()
- : input.shouldDiscardUnknownFields();
+ boolean discardUnknown = input.shouldDiscardUnknownFields();
final UnknownFieldSet.Builder unknownFields =
discardUnknown ? null : UnknownFieldSet.newBuilder(getUnknownFields());
while (true) {
@@ -42,7 +42,8 @@ import java.util.RandomAccess;
*
* @author dweis@google.com (Daniel Weis)
*/
-final class BooleanArrayList extends AbstractProtobufList<Boolean>
+final class BooleanArrayList
+ extends AbstractProtobufList<Boolean>
implements BooleanList, RandomAccess, PrimitiveNonBoxingCollection {
private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList();
@@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
@@ -221,6 +222,67 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
return size() == 0;
+ // =================================================================
+ // Comparison
+ private static final int UNSIGNED_BYTE_MASK = 0xFF;
+ /**
+ * Returns the value of the given byte as an integer, interpreting the byte as an unsigned value.
+ * That is, returns {@code value + 256} if {@code value} is negative; {@code value} itself
+ * otherwise.
+ *
+ * <p>Note: This code was copied from {@link com.google.common.primitives.UnsignedBytes#toInt}, as
+ * Guava libraries cannot be used in the {@code com.google.protobuf} package.
+ */
+ private static int toInt(byte value) {
+ return value & UNSIGNED_BYTE_MASK;
+ * Compares two {@link ByteString}s lexicographically, treating their contents as unsigned byte
+ * values between 0 and 255 (inclusive).
+ * <p>For example, {@code (byte) -1} is considered to be greater than {@code (byte) 1} because
+ * it is interpreted as an unsigned value, {@code 255}.
+ private static final Comparator<ByteString> UNSIGNED_LEXICOGRAPHICAL_COMPARATOR =
+ new Comparator<ByteString>() {
+ @Override
+ public int compare(ByteString former, ByteString latter) {
+ ByteIterator formerBytes = former.iterator();
+ ByteIterator latterBytes = latter.iterator();
+ while (formerBytes.hasNext() && latterBytes.hasNext()) {
+ // Note: This code was copied from com.google.common.primitives.UnsignedBytes#compare,
+ // as Guava libraries cannot be used in the {@code com.google.protobuf} package.
+ int result =
+ Integer.compare(toInt(formerBytes.nextByte()), toInt(latterBytes.nextByte()));
+ if (result != 0) {
+ return result;
+ return Integer.compare(former.size(), latter.size());
+ * Returns a {@link Comparator<ByteString>} which compares {@link ByteString}-s lexicographically
+ * as sequences of unsigned bytes (i.e. values between 0 and 255, inclusive).
+ * it is interpreted as an unsigned value, {@code 255}:
+ * <ul>
+ * <li>{@code `-1` -> 0b11111111 (two's complement) -> 255}
+ * <li>{@code `1` -> 0b00000001 -> 1}
+ * </ul>
+ public static Comparator<ByteString> unsignedLexicographicalComparator() {
+ return UNSIGNED_LEXICOGRAPHICAL_COMPARATOR;
// =================================================================
// ByteString -> substring
@@ -287,8 +349,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param offset offset in source array
* @param size number of bytes to copy
* @return new {@code ByteString}
+ * @throws IndexOutOfBoundsException if {@code offset} or {@code size} are out of bounds
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
+ checkRange(offset, offset + size, bytes.length);
return new LiteralByteString(byteArrayCopier.copyFrom(bytes, offset, size));
@@ -339,8 +403,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param bytes source buffer
+ * @throws IndexOutOfBoundsException if {@code size > bytes.remaining()}
public static ByteString copyFrom(ByteBuffer bytes, int size) {
+ checkRange(0, size, bytes.remaining());
byte[] copy = new byte[size];
bytes.get(copy);
return new LiteralByteString(copy);
@@ -578,6 +644,9 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
/**
* Copies bytes into a buffer at the given offset.
+ * <p>To copy a subset of bytes, you call this method on the return value of {@link
+ * #substring(int, int)}. Example: {@code byteString.substring(start, end).copyTo(target, offset)}
* @param target buffer to copy into
* @param offset in the target buffer
* @throws IndexOutOfBoundsException if the offset is negative or too large
@@ -589,15 +658,16 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* Copies bytes into a buffer.
- * @param target buffer to copy into
+ * @param target buffer to copy into
* @param sourceOffset offset within these bytes
* @param targetOffset offset within the target buffer
* @param numberToCopy number of bytes to copy
- * @throws IndexOutOfBoundsException if an offset or size is negative or too
- * large
+ * @throws IndexOutOfBoundsException if an offset or size is negative or too large
+ * @deprecation Instead, call {@code byteString.substring(sourceOffset, sourceOffset +
+ * numberToCopy).copyTo(target, targetOffset)}
- public final void copyTo(byte[] target, int sourceOffset, int targetOffset,
- int numberToCopy) {
+ @Deprecated
+ public final void copyTo(byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
checkRange(sourceOffset, sourceOffset + numberToCopy, size());
checkRange(targetOffset, targetOffset + numberToCopy, target.length);
if (numberToCopy > 0) {
@@ -617,10 +687,13 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* Copies bytes into a ByteBuffer.
+ * #substring(int, int)}. Example: {@code byteString.substring(start, end).copyTo(target)}
* @param target ByteBuffer to copy into.
* @throws java.nio.ReadOnlyBufferException if the {@code target} is read-only
- * @throws java.nio.BufferOverflowException if the {@code target}'s
- * remaining() space is not large enough to hold the data.
+ * @throws java.nio.BufferOverflowException if the {@code target}'s remaining() space is not large
+ * enough to hold the data.
public abstract void copyTo(ByteBuffer target);
@@ -1258,6 +1331,9 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param bytes array to wrap
LiteralByteString(byte[] bytes) {
+ if (bytes == null) {
+ throw new NullPointerException();
this.bytes = bytes;
@@ -64,12 +64,6 @@ public abstract class CodedInputStream {
// Integer.MAX_VALUE == 0x7FFFFFF == INT_MAX from limits.h
private static final int DEFAULT_SIZE_LIMIT = Integer.MAX_VALUE;
- /**
- * Whether to enable our custom UTF-8 decode codepath which does not use {@link StringCoding}.
- * Currently disabled.
- */
- private static final boolean ENABLE_CUSTOM_UTF8_DECODE = false;
/** Visible for subclasses. See setRecursionLimit() */
int recursionDepth;
@@ -417,21 +411,7 @@ public abstract class CodedInputStream {
- private boolean explicitDiscardUnknownFields = false;
- private static volatile boolean proto3DiscardUnknownFieldsDefault = false;
- static void setProto3DiscardUnknownsByDefaultForTest() {
- proto3DiscardUnknownFieldsDefault = true;
- static void setProto3KeepUnknownsByDefaultForTest() {
- proto3DiscardUnknownFieldsDefault = false;
- static boolean getProto3DiscardUnknownFieldsDefault() {
- return proto3DiscardUnknownFieldsDefault;
+ private boolean shouldDiscardUnknownFields = false;
* Sets this {@code CodedInputStream} to discard unknown fields. Only applies to full runtime
@@ -442,7 +422,7 @@ public abstract class CodedInputStream {
* runtime.
final void discardUnknownFields() {
- explicitDiscardUnknownFields = true;
+ shouldDiscardUnknownFields = true;
@@ -450,7 +430,7 @@ public abstract class CodedInputStream {
* default.
final void unsetDiscardUnknownFields() {
- explicitDiscardUnknownFields = false;
+ shouldDiscardUnknownFields = false;
@@ -458,19 +438,7 @@ public abstract class CodedInputStream {
* runtime messages.
final boolean shouldDiscardUnknownFields() {
- return explicitDiscardUnknownFields;
- * Whether unknown fields in this input stream should be discarded during parsing for proto3 full
- * runtime messages.
- *
- * <p>This function was temporarily introduced before proto3 unknown fields behavior is changed.
- * TODO(liujisi): remove this and related code in GeneratedMessage after proto3 unknown
- * fields migration is done.
- final boolean shouldDiscardUnknownFieldsProto3() {
- return explicitDiscardUnknownFields ? true : proto3DiscardUnknownFieldsDefault;
+ return shouldDiscardUnknownFields;
@@ -831,19 +799,9 @@ public abstract class CodedInputStream {
public String readStringRequireUtf8() throws IOException {
final int size = readRawVarint32();
if (size > 0 && size <= (limit - pos)) {
- if (ENABLE_CUSTOM_UTF8_DECODE) {
- String result = Utf8.decodeUtf8(buffer, pos, size);
- pos += size;
- return result;
- // TODO(martinrb): We could save a pass by validating while decoding.
- if (!Utf8.isValidUtf8(buffer, pos, pos + size)) {
- throw InvalidProtocolBufferException.invalidUtf8();
- final int tempPos = pos;
- return new String(buffer, tempPos, size, UTF_8);
+ String result = Utf8.decodeUtf8(buffer, pos, size);
+ pos += size;
if (size == 0) {
@@ -1559,25 +1517,10 @@ public abstract class CodedInputStream {
if (size > 0 && size <= remaining()) {
- final int bufferPos = bufferPos(pos);
- String result = Utf8.decodeUtf8(buffer, bufferPos, size);
- // TODO(nathanmittler): Is there a way to avoid this copy?
- // The same as readBytes' logic
- byte[] bytes = new byte[size];
- UnsafeUtil.copyMemory(pos, bytes, 0, size);
- if (!Utf8.isValidUtf8(bytes)) {
- String result = new String(bytes, UTF_8);
+ final int bufferPos = bufferPos(pos);
+ String result = Utf8.decodeUtf8(buffer, bufferPos, size);
@@ -2345,15 +2288,7 @@ public abstract class CodedInputStream {
bytes = readRawBytesSlowPath(size);
tempPos = 0;
- return Utf8.decodeUtf8(bytes, tempPos, size);
- if (!Utf8.isValidUtf8(bytes, tempPos, tempPos + size)) {
- return new String(bytes, tempPos, size, UTF_8);
+ return Utf8.decodeUtf8(bytes, tempPos, size);
@Override
@@ -3373,34 +3308,15 @@ public abstract class CodedInputStream {
if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) {
- final int bufferPos = (int) (currentByteBufferPos - currentByteBufferStartPos);
- String result = Utf8.decodeUtf8(currentByteBuffer, bufferPos, size);
- currentByteBufferPos += size;
- UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size);
+ final int bufferPos = (int) (currentByteBufferPos - currentByteBufferStartPos);
+ String result = Utf8.decodeUtf8(currentByteBuffer, bufferPos, size);
+ currentByteBufferPos += size;
if (size >= 0 && size <= remaining()) {
byte[] bytes = new byte[size];
readRawBytesTo(bytes, 0, size);
- return Utf8.decodeUtf8(bytes, 0, size);
+ return Utf8.decodeUtf8(bytes, 0, size);
-final class DoubleArrayList extends AbstractProtobufList<Double>
+final class DoubleArrayList
+ extends AbstractProtobufList<Double>
implements DoubleList, RandomAccess, PrimitiveNonBoxingCollection {
private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList();
@@ -608,20 +608,12 @@ public final class DynamicMessage extends AbstractMessage {
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
- if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3
- && CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
- return this;
this.unknownFields = unknownFields;
return this;
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
this.unknownFields =
UnknownFieldSet.newBuilder(this.unknownFields)
.mergeFrom(unknownFields)
-final class FloatArrayList extends AbstractProtobufList<Float>
+final class FloatArrayList
+ extends AbstractProtobufList<Float>
implements FloatList, RandomAccess, PrimitiveNonBoxingCollection {
private static final FloatArrayList EMPTY_LIST = new FloatArrayList();
@@ -33,7 +33,6 @@ package com.google.protobuf;
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
import com.google.protobuf.Internal.BooleanList;
import com.google.protobuf.Internal.DoubleList;
-import com.google.protobuf.Internal.EnumLiteMap;
import com.google.protobuf.Internal.FloatList;
import com.google.protobuf.Internal.IntList;
import com.google.protobuf.Internal.LongList;
@@ -1600,7 +1599,7 @@ public abstract class GeneratedMessageLite<
protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
extends AbstractParser<T> {
- private T defaultInstance;
+ private final T defaultInstance;
public DefaultInstanceBasedParser(T defaultInstance) {
this.defaultInstance = defaultInstance;
@@ -38,6 +38,11 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
+import com.google.protobuf.Internal.BooleanList;
+import com.google.protobuf.Internal.DoubleList;
+import com.google.protobuf.Internal.FloatList;
+import com.google.protobuf.Internal.IntList;
+import com.google.protobuf.Internal.LongList;
// In opensource protobuf, we have versioned this GeneratedMessageV3 class to GeneratedMessageV3V3 and
// in the future may have GeneratedMessageV3V4 etc. This allows us to change some aspects of this
// class without breaking binary compatibility with old generated code that still subclasses
@@ -293,16 +298,17 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
return unknownFields.mergeFieldFrom(tag, input);
+ * Delegates to parseUnknownField. This method is obsolete, but we must retain it for
+ * compatibility with older generated code.
protected boolean parseUnknownFieldProto3(
CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
int tag)
- if (input.shouldDiscardUnknownFieldsProto3()) {
- return input.skipField(tag);
- return unknownFields.mergeFieldFrom(tag, input);
+ return parseUnknownField(input, unknownFields, extensionRegistry, tag);
protected static <M extends Message> M parseWithIOException(Parser<M> parser, InputStream input)
@@ -363,6 +369,76 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations();
+ protected static IntList emptyIntList() {
+ return IntArrayList.emptyList();
+ protected static IntList newIntList() {
+ return new IntArrayList();
+ protected static IntList mutableCopy(IntList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ protected static LongList emptyLongList() {
+ return LongArrayList.emptyList();
+ protected static LongList newLongList() {
+ return new LongArrayList();
+ protected static LongList mutableCopy(LongList list) {
+ protected static FloatList emptyFloatList() {
+ return FloatArrayList.emptyList();
+ protected static FloatList newFloatList() {
+ return new FloatArrayList();
+ protected static FloatList mutableCopy(FloatList list) {
+ protected static DoubleList emptyDoubleList() {
+ return DoubleArrayList.emptyList();
+ protected static DoubleList newDoubleList() {
+ return new DoubleArrayList();
+ protected static DoubleList mutableCopy(DoubleList list) {
+ protected static BooleanList emptyBooleanList() {
+ return BooleanArrayList.emptyList();
+ protected static BooleanList newBooleanList() {
+ return new BooleanArrayList();
+ protected static BooleanList mutableCopy(BooleanList list) {
public void writeTo(final CodedOutputStream output) throws IOException {
MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false);
@@ -641,13 +717,12 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
return (BuilderType) this;
+ * Delegates to setUnknownFields. This method is obsolete, but we must retain it for
protected BuilderType setUnknownFieldsProto3(final UnknownFieldSet unknownFields) {
- if (CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
- return (BuilderType) this;
- this.unknownFields = unknownFields;
- onChanged();
+ return setUnknownFields(unknownFields);
@@ -1009,19 +1084,17 @@ public abstract class GeneratedMessageV3 extends AbstractMessage
getDescriptorForType(), new MessageReflection.ExtensionAdapter(extensions), tag);
int tag) throws IOException {
- return MessageReflection.mergeFieldFrom(
- input,
- input.shouldDiscardUnknownFieldsProto3() ? null : unknownFields,
- extensionRegistry,
- getDescriptorForType(),
- new MessageReflection.ExtensionAdapter(extensions),
- tag);
-final class IntArrayList extends AbstractProtobufList<Integer>
+final class IntArrayList
+ extends AbstractProtobufList<Integer>
implements IntList, RandomAccess, PrimitiveNonBoxingCollection {
private static final IntArrayList EMPTY_LIST = new IntArrayList();
@@ -234,6 +234,11 @@ public final class Internal {
T findValueByNumber(int number);
+ /** Interface for an object which verifies integers are in range. */
+ public interface EnumVerifier {
+ boolean isInRange(int number);
* Helper method for implementing {@link Message#hashCode()} for longs.
* @see Long#hashCode()
@@ -69,7 +69,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
static {
EMPTY_LIST.makeImmutable();
static LazyStringArrayList emptyList() {
return EMPTY_LIST;
@@ -83,8 +83,8 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
this(DEFAULT_CAPACITY);
- public LazyStringArrayList(int intialCapacity) {
- this(new ArrayList<Object>(intialCapacity));
+ public LazyStringArrayList(int initialCapacity) {
+ this(new ArrayList<Object>(initialCapacity));
public LazyStringArrayList(LazyStringList from) {
@@ -95,7 +95,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
public LazyStringArrayList(List<String> from) {
this(new ArrayList<Object>(from));
private LazyStringArrayList(ArrayList<Object> list) {
this.list = list;
@@ -150,13 +150,13 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
list.add(index, element);
modCount++;
private void add(int index, ByteString element) {
ensureIsMutable();
private void add(int index, byte[] element) {
@@ -221,7 +221,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
list.add(element);
public void add(byte[] element) {
@@ -233,7 +233,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
public Object getRaw(int index) {
return list.get(index);
public ByteString getByteString(int index) {
Object o = list.get(index);
@@ -243,7 +243,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return b;
public byte[] getByteArray(int index) {
@@ -258,7 +258,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
public void set(int index, ByteString s) {
setAndReturn(index, s);
private Object setAndReturn(int index, ByteString s) {
return list.set(index, s);
@@ -268,7 +268,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
public void set(int index, byte[] s) {
private Object setAndReturn(int index, byte[] s) {
@@ -283,7 +283,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return Internal.toStringUtf8((byte[]) o);
private static ByteString asByteString(Object o) {
if (o instanceof ByteString) {
return (ByteString) o;
@@ -293,7 +293,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return ByteString.copyFrom((byte[]) o);
private static byte[] asByteArray(Object o) {
if (o instanceof byte[]) {
return (byte[]) o;
@@ -327,11 +327,11 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
private static class ByteArrayListView extends AbstractList<byte[]>
implements RandomAccess {
private final LazyStringArrayList list;
ByteArrayListView(LazyStringArrayList list) {
public byte[] get(int index) {
return list.getByteArray(index);
@@ -362,7 +362,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return asByteArray(o);
public List<byte[]> asByteArrayList() {
return new ByteArrayListView(this);
-final class LongArrayList extends AbstractProtobufList<Long>
+final class LongArrayList
+ extends AbstractProtobufList<Long>
implements LongList, RandomAccess, PrimitiveNonBoxingCollection {
private static final LongArrayList EMPTY_LIST = new LongArrayList();
@@ -30,6 +30,8 @@
package com.google.protobuf;
+import static com.google.protobuf.Internal.checkNotNull;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -290,9 +292,7 @@ public class RepeatedFieldBuilder
public RepeatedFieldBuilder<MType, BType, IType> setMessage(
int index, MType message) {
- if (message == null) {
- throw new NullPointerException();
+ checkNotNull(message);
ensureMutableMessageList();
messages.set(index, message);
if (builders != null) {
@@ -315,9 +315,7 @@ public class RepeatedFieldBuilder
public RepeatedFieldBuilder<MType, BType, IType> addMessage(
MType message) {
messages.add(message);
@@ -339,9 +337,7 @@ public class RepeatedFieldBuilder
messages.add(index, message);
@@ -363,9 +359,7 @@ public class RepeatedFieldBuilder
public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
Iterable<? extends MType> values) {
for (final MType value : values) {
- if (value == null) {
+ checkNotNull(value);
// If we can inspect the size, we can more efficiently add messages.
* {@code SingleFieldBuilder} implements a structure that a protocol
* message uses to hold a single field of another protocol message. It supports
@@ -84,10 +86,7 @@ public class SingleFieldBuilder
MType message,
GeneratedMessage.BuilderParent parent,
boolean isClean) {
- this.message = message;
+ this.message = checkNotNull(message);
this.parent = parent;
this.isClean = isClean;
@@ -169,10 +168,7 @@ public class SingleFieldBuilder
public SingleFieldBuilder<MType, BType, IType> setMessage(
if (builder != null) {
builder.dispose();
builder = null;
@@ -1444,8 +1444,8 @@ public final class TextFormat {
logger.warning(msg.toString());
String[] lineColumn = unknownFields.get(0).split(":");
- throw new ParseException(Integer.valueOf(lineColumn[0]),
- Integer.valueOf(lineColumn[1]), msg.toString());
+ throw new ParseException(
+ Integer.parseInt(lineColumn[0]), Integer.parseInt(lineColumn[1]), msg.toString());
@@ -785,6 +785,23 @@ public final class UnknownFieldSet implements MessageLite {
group};
+ * Serializes the message to a {@code ByteString} and returns it. This is just a trivial wrapper
+ * around {@link #writeTo(int, CodedOutputStream)}.
+ public ByteString toByteString(int fieldNumber) {
+ try {
+ // TODO(lukes): consider caching serialized size in a volatile long
+ final ByteString.CodedBuilder out =
+ ByteString.newCodedBuilder(getSerializedSize(fieldNumber));
+ writeTo(fieldNumber, out.getCodedOutput());
+ return out.build();
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "Serializing to a ByteString should never fail with an IOException", e);
* Serializes the field, including field number, and writes it to
* {@code output}.
@@ -33,6 +33,7 @@ package com.google.protobuf;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
@@ -146,6 +147,10 @@ final class UnsafeUtil {
return MEMORY_ACCESSOR.getObject(target, offset);
+ static void putObject(Object target, long offset, Object value) {
+ MEMORY_ACCESSOR.putObject(target, offset, value);
static byte getByte(byte[] target, long index) {
return MEMORY_ACCESSOR.getByte(target, BYTE_ARRAY_BASE_OFFSET + index);
@@ -370,12 +375,6 @@ final class UnsafeUtil {
return field != null && field.getType() == long.class ? field : null;
- /** Finds the value field within a {@link String}. */
- private static Field stringValueField() {
- Field field = field(String.class, "value");
- return field != null && field.getType() == char[].class ? field : null;
* Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
* available.
@@ -42,7 +42,6 @@ import static java.lang.Character.isSurrogatePair;
import static java.lang.Character.toCodePoint;
-import java.util.Arrays;
* A set of low-level, high-performance static utility methods related
@@ -87,7 +86,9 @@ final class Utf8 {
* delegate for which all methods are delegated directly to.
private static final Processor processor =
- UnsafeProcessor.isAvailable() ? new UnsafeProcessor() : new SafeProcessor();
+ (UnsafeProcessor.isAvailable() && !Android.isOnAndroidDevice())
+ ? new UnsafeProcessor()
+ : new SafeProcessor();
* A mask used when performing unsafe reads to determine if a long value contains any non-ASCII
@@ -210,8 +210,8 @@ public class AbstractMessageTest extends TestCase {
new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
TestUtil.ReflectionTester extensionsReflectionTester =
- new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
- TestUtil.getExtensionRegistry());
+ new TestUtil.ReflectionTester(
+ TestAllExtensions.getDescriptor(), TestUtil.getFullExtensionRegistry());
public void testClear() throws Exception {
AbstractMessageWrapper message =
@@ -299,20 +299,22 @@ public class BooleanArrayListTest extends TestCase {
public void testRemoveEndOfCapacity() {
- BooleanList toRemove = BooleanArrayList.emptyList().mutableCopyWithCapacity(1);
+ BooleanList toRemove =
+ BooleanArrayList.emptyList().mutableCopyWithCapacity(1);
toRemove.addBoolean(true);
toRemove.remove(0);
assertEquals(0, toRemove.size());
public void testSublistRemoveEndOfCapacity() {
toRemove.subList(0, 1).clear();
- private void assertImmutable(BooleanArrayList list) {
+ private void assertImmutable(BooleanList list) {
list.add(true);
@@ -41,6 +41,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
@@ -86,6 +87,40 @@ public class ByteStringTest extends TestCase {
return left.length == right.length && isArrayRange(left, right, 0, left.length);
+ public void testCompare_equalByteStrings_compareEqual() throws Exception {
+ byte[] referenceBytes = getTestBytes();
+ ByteString string1 = ByteString.copyFrom(referenceBytes);
+ ByteString string2 = ByteString.copyFrom(referenceBytes);
+ assertEquals(
+ "ByteString instances containing the same data must compare equal.",
+ 0,
+ ByteString.unsignedLexicographicalComparator().compare(string1, string2));
+ public void testCompare_byteStringsSortLexicographically() throws Exception {
+ ByteString app = ByteString.copyFromUtf8("app");
+ ByteString apple = ByteString.copyFromUtf8("apple");
+ ByteString banana = ByteString.copyFromUtf8("banana");
+ Comparator<ByteString> comparator = ByteString.unsignedLexicographicalComparator();
+ assertTrue("ByteString(app) < ByteString(apple)", comparator.compare(app, apple) < 0);
+ assertTrue("ByteString(app) < ByteString(banana)", comparator.compare(app, banana) < 0);
+ assertTrue("ByteString(apple) < ByteString(banana)", comparator.compare(apple, banana) < 0);
+ public void testCompare_interpretsByteValuesAsUnsigned() throws Exception {
+ // Two's compliment of `-1` == 0b11111111 == 255
+ ByteString twoHundredFiftyFive = ByteString.copyFrom(new byte[] {-1});
+ // 0b00000001 == 1
+ ByteString one = ByteString.copyFrom(new byte[] {1});
+ assertTrue(
+ "ByteString comparison treats bytes as unsigned values",
+ ByteString.unsignedLexicographicalComparator().compare(one, twoHundredFiftyFive) < 0);
public void testSubstring_BeginIndex() {
byte[] bytes = getTestBytes();
ByteString substring = ByteString.copyFrom(bytes).substring(500);
@@ -161,6 +196,34 @@ public class ByteStringTest extends TestCase {
byteString, byteStringAlt);
+ public void testCopyFrom_LengthTooBig() {
+ byte[] testBytes = getTestBytes(100);
+ ByteString.copyFrom(testBytes, 0, 200);
+ fail("Should throw");
+ } catch (IndexOutOfBoundsException expected) {
+ ByteString.copyFrom(testBytes, 99, 2);
+ fail();
+ ByteBuffer buf = ByteBuffer.wrap(testBytes);
+ ByteString.copyFrom(buf, 101);
+ ByteString.copyFrom(testBytes, -1, 10);
public void testCopyTo_TargetOffset() {
ByteString byteString = ByteString.copyFrom(bytes);
@@ -761,6 +824,9 @@ public class ByteStringTest extends TestCase {
* Tests ByteString uses Arrays based byte copier when running under Hotstop VM.
public void testByteArrayCopier() throws Exception {
+ if (Android.isOnAndroidDevice()) {
Field field = ByteString.class.getDeclaredField("byteArrayCopier");
field.setAccessible(true);
Object byteArrayCopier = field.get(null);
@@ -62,22 +62,17 @@ public class DiscardUnknownFieldsTest {
private static void testProto2Message(Message message) throws Exception {
- assertUnknownFieldsDefaultPreserved(message);
+ assertUnknownFieldsPreserved(message);
assertUnknownFieldsExplicitlyDiscarded(message);
assertReuseCodedInputStreamPreserve(message);
assertUnknownFieldsInUnknownFieldSetArePreserve(message);
private static void testProto3Message(Message message) throws Exception {
- CodedInputStream.setProto3KeepUnknownsByDefaultForTest();
- CodedInputStream.setProto3DiscardUnknownsByDefaultForTest();
- assertUnknownFieldsDefaultDiscarded(message);
- assertUnknownFieldsExplicitlyDiscarded(message);
- assertUnknownFieldsInUnknownFieldSetAreDiscarded(message);
private static void assertReuseCodedInputStreamPreserve(Message message) throws Exception {
@@ -122,7 +117,7 @@ public class DiscardUnknownFieldsTest {
assertEquals(message.getClass().getName(), 0, built.getSerializedSize());
- private static void assertUnknownFieldsDefaultPreserved(MessageLite message) throws Exception {
+ private static void assertUnknownFieldsPreserved(MessageLite message) throws Exception {
{
MessageLite parsed = message.getParserForType().parseFrom(payload);
assertEquals(message.getClass().getName(), payload, parsed.toByteString());
@@ -134,18 +129,6 @@ public class DiscardUnknownFieldsTest {
- private static void assertUnknownFieldsDefaultDiscarded(MessageLite message) throws Exception {
- MessageLite parsed = message.getParserForType().parseFrom(payload);
- assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
- MessageLite parsed = message.newBuilderForType().mergeFrom(payload).build();
private static void assertUnknownFieldsExplicitlyDiscarded(Message message) throws Exception {
Message parsed =
DiscardUnknownFieldsParser.wrap(message.getParserForType()).parseFrom(payload);
@@ -78,10 +78,10 @@ public class DoubleArrayListTest extends TestCase {
list.addAll(asList(1D, 2D, 3D, 4D));
Iterator<Double> iterator = list.iterator();
assertEquals(4, list.size());
- assertEquals(1D, (double) list.get(0));
- assertEquals(1D, (double) iterator.next());
+ assertEquals(1D, (double) list.get(0), 0.0);
+ assertEquals(1D, (double) iterator.next(), 0.0);
list.set(0, 1D);
- assertEquals(2D, (double) iterator.next());
+ assertEquals(2D, (double) iterator.next(), 0.0);
list.remove(0);
@@ -102,9 +102,9 @@ public class DoubleArrayListTest extends TestCase {
public void testGet() {
- assertEquals(1D, (double) TERTIARY_LIST.get(0));
- assertEquals(2D, (double) TERTIARY_LIST.get(1));
- assertEquals(3D, (double) TERTIARY_LIST.get(2));
+ assertEquals(1D, (double) TERTIARY_LIST.get(0), 0.0);
+ assertEquals(2D, (double) TERTIARY_LIST.get(1), 0.0);
+ assertEquals(3D, (double) TERTIARY_LIST.get(2), 0.0);
TERTIARY_LIST.get(-1);
@@ -122,9 +122,9 @@ public class DoubleArrayListTest extends TestCase {
public void testGetDouble() {
- assertEquals(1D, TERTIARY_LIST.getDouble(0));
- assertEquals(2D, TERTIARY_LIST.getDouble(1));
- assertEquals(3D, TERTIARY_LIST.getDouble(2));
+ assertEquals(1D, TERTIARY_LIST.getDouble(0), 0.0);
+ assertEquals(2D, TERTIARY_LIST.getDouble(1), 0.0);
+ assertEquals(3D, TERTIARY_LIST.getDouble(2), 0.0);
@@ -163,11 +163,11 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(2);
list.addDouble(4);
- assertEquals(2D, (double) list.set(0, 3D));
- assertEquals(3D, list.getDouble(0));
+ assertEquals(2D, (double) list.set(0, 3D), 0.0);
+ assertEquals(3D, list.getDouble(0), 0.0);
- assertEquals(4D, (double) list.set(1, 0D));
- assertEquals(0D, list.getDouble(1));
+ assertEquals(4D, (double) list.set(1, 0D), 0.0);
+ assertEquals(0D, list.getDouble(1), 0.0);
list.set(-1, 0D);
@@ -188,11 +188,11 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(1);
list.addDouble(3);
- assertEquals(1D, list.setDouble(0, 0));
- assertEquals(0D, list.getDouble(0));
+ assertEquals(1D, list.setDouble(0, 0), 0.0);
+ assertEquals(0D, list.getDouble(0), 0.0);
- assertEquals(3D, list.setDouble(1, 0));
+ assertEquals(3D, list.setDouble(1, 0), 0.0);
list.setDouble(-1, 0);
@@ -257,8 +257,8 @@ public class DoubleArrayListTest extends TestCase {
assertTrue(list.addAll(Collections.singleton(1D)));
assertEquals(1, list.size());
- assertEquals(1D, list.getDouble(0));
+ assertEquals(1D, list.getDouble(0), 0.0);
assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D)));
assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list);
@@ -272,7 +272,7 @@ public class DoubleArrayListTest extends TestCase {
public void testRemove() {
list.addAll(TERTIARY_LIST);
- assertEquals(1D, (double) list.remove(0));
+ assertEquals(1D, (double) list.remove(0), 0.0);
assertEquals(asList(2D, 3D), list);
assertTrue(list.remove(Double.valueOf(3)));
@@ -281,7 +281,7 @@ public class DoubleArrayListTest extends TestCase {
assertFalse(list.remove(Double.valueOf(3)));
assertEquals(asList(2D), list);
- assertEquals(2D, (double) list.remove(0));
+ assertEquals(2D, (double) list.remove(0), 0.0);
assertEquals(asList(), list);
@@ -299,20 +299,22 @@ public class DoubleArrayListTest extends TestCase {
- DoubleList toRemove = DoubleArrayList.emptyList().mutableCopyWithCapacity(1);
+ DoubleList toRemove =
+ DoubleArrayList.emptyList().mutableCopyWithCapacity(1);
toRemove.addDouble(3);
- private void assertImmutable(DoubleArrayList list) {
+ private void assertImmutable(DoubleList list) {
if (list.contains(1D)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
@@ -51,8 +51,8 @@ public class DynamicMessageTest extends TestCase {
TestUtil.ReflectionTester packedReflectionTester =
new TestUtil.ReflectionTester(TestPackedTypes.getDescriptor(), null);
@@ -194,9 +194,9 @@ public class DynamicMessageTest extends TestCase {
public void testDynamicMessageExtensionParsing() throws Exception {
ByteString rawBytes = TestUtil.getAllExtensionsSet().toByteString();
- Message message = DynamicMessage.parseFrom(
- TestAllExtensions.getDescriptor(), rawBytes,
+ Message message =
+ DynamicMessage.parseFrom(
+ TestAllExtensions.getDescriptor(), rawBytes, TestUtil.getFullExtensionRegistry());
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
// Test Parser interface.
@@ -78,10 +78,10 @@ public class FloatArrayListTest extends TestCase {
list.addAll(asList(1F, 2F, 3F, 4F));
Iterator<Float> iterator = list.iterator();
- assertEquals(1F, (float) list.get(0));
- assertEquals(1F, (float) iterator.next());
+ assertEquals(1F, (float) list.get(0), 0.0f);
+ assertEquals(1F, (float) iterator.next(), 0.0f);
list.set(0, 1F);
- assertEquals(2F, (float) iterator.next());
+ assertEquals(2F, (float) iterator.next(), 0.0f);
@@ -102,9 +102,9 @@ public class FloatArrayListTest extends TestCase {
- assertEquals(1F, (float) TERTIARY_LIST.get(0));
- assertEquals(2F, (float) TERTIARY_LIST.get(1));
- assertEquals(3F, (float) TERTIARY_LIST.get(2));
+ assertEquals(1F, (float) TERTIARY_LIST.get(0), 0.0f);
+ assertEquals(2F, (float) TERTIARY_LIST.get(1), 0.0f);
+ assertEquals(3F, (float) TERTIARY_LIST.get(2), 0.0f);
@@ -122,9 +122,9 @@ public class FloatArrayListTest extends TestCase {
public void testGetFloat() {
- assertEquals(1F, TERTIARY_LIST.getFloat(0));
- assertEquals(2F, TERTIARY_LIST.getFloat(1));
- assertEquals(3F, TERTIARY_LIST.getFloat(2));
+ assertEquals(1F, TERTIARY_LIST.getFloat(0), 0.0f);
+ assertEquals(2F, TERTIARY_LIST.getFloat(1), 0.0f);
+ assertEquals(3F, TERTIARY_LIST.getFloat(2), 0.0f);
@@ -163,11 +163,11 @@ public class FloatArrayListTest extends TestCase {
list.addFloat(2);
list.addFloat(4);
- assertEquals(2F, (float) list.set(0, 3F));
- assertEquals(3F, list.getFloat(0));
+ assertEquals(2F, (float) list.set(0, 3F), 0.0f);
+ assertEquals(3F, list.getFloat(0), 0.0f);
- assertEquals(4F, (float) list.set(1, 0F));
- assertEquals(0F, list.getFloat(1));
+ assertEquals(4F, (float) list.set(1, 0F), 0.0f);
+ assertEquals(0F, list.getFloat(1), 0.0f);
list.set(-1, 0F);
@@ -188,11 +188,11 @@ public class FloatArrayListTest extends TestCase {
list.addFloat(1);
list.addFloat(3);
- assertEquals(1F, list.setFloat(0, 0));
- assertEquals(0F, list.getFloat(0));
+ assertEquals(1F, list.setFloat(0, 0), 0.0f);
+ assertEquals(0F, list.getFloat(0), 0.0f);
- assertEquals(3F, list.setFloat(1, 0));
+ assertEquals(3F, list.setFloat(1, 0), 0.0f);
list.setFloat(-1, 0);
@@ -257,8 +257,8 @@ public class FloatArrayListTest extends TestCase {
assertTrue(list.addAll(Collections.singleton(1F)));
- assertEquals(1F, list.getFloat(0));
+ assertEquals(1F, list.getFloat(0), 0.0f);
assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F)));
assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list);
@@ -272,7 +272,7 @@ public class FloatArrayListTest extends TestCase {
- assertEquals(1F, (float) list.remove(0));
+ assertEquals(1F, (float) list.remove(0), 0.0f);
assertEquals(asList(2F, 3F), list);
assertTrue(list.remove(Float.valueOf(3)));
@@ -281,7 +281,7 @@ public class FloatArrayListTest extends TestCase {
assertFalse(list.remove(Float.valueOf(3)));
assertEquals(asList(2F), list);
- assertEquals(2F, (float) list.remove(0));
+ assertEquals(2F, (float) list.remove(0), 0.0f);
@@ -299,20 +299,22 @@ public class FloatArrayListTest extends TestCase {
- FloatList toRemove = FloatArrayList.emptyList().mutableCopyWithCapacity(1);
+ FloatList toRemove =
+ FloatArrayList.emptyList().mutableCopyWithCapacity(1);
toRemove.addFloat(3);
- private void assertImmutable(FloatArrayList list) {
+ private void assertImmutable(FloatList list) {
if (list.contains(1F)) {
@@ -590,8 +590,8 @@ public class GeneratedMessageTest extends TestCase {
// Extensions.
public void testExtensionMessageOrBuilder() throws Exception {
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
@@ -299,20 +299,22 @@ public class IntArrayListTest extends TestCase {
- IntList toRemove = IntArrayList.emptyList().mutableCopyWithCapacity(1);
+ IntList toRemove =
+ IntArrayList.emptyList().mutableCopyWithCapacity(1);
toRemove.addInt(3);
- private void assertImmutable(IntArrayList list) {
+ private void assertImmutable(IntList list) {
if (list.contains(1)) {
@@ -57,6 +57,8 @@ import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
@@ -2378,4 +2380,63 @@ public class LiteTest extends TestCase {
} catch (NullPointerException expected) {
+ public void testSerializeToOutputStreamThrowsIOException() {
+ TestAllTypesLite.newBuilder()
+ .setOptionalBytes(ByteString.copyFromUtf8("hello"))
+ .build()
+ .writeTo(
+ new OutputStream() {
+ public void write(int b) throws IOException {
+ throw new IOException();
+ } catch (IOException expected) {
+ public void testUnpairedSurrogatesReplacedByQuestionMark() throws InvalidProtocolBufferException {
+ String testString = "foo \ud83d bar";
+ String expectedString = "foo ? bar";
+ TestAllTypesLite testMessage =
+ TestAllTypesLite.newBuilder().setOptionalString(testString).build();
+ ByteString serializedMessage = testMessage.toByteString();
+ // Behavior is compatible with String.getBytes("UTF-8"), which replaces
+ // unpaired surrogates with a question mark.
+ TestAllTypesLite parsedMessage = TestAllTypesLite.parseFrom(serializedMessage);
+ assertEquals(expectedString, parsedMessage.getOptionalString());
+ // Conversion happens during serialization.
+ ByteString expectedBytes = ByteString.copyFromUtf8(expectedString);
+ String.format(
+ "Expected serializedMessage (%s) to contain \"%s\" (%s).",
+ encodeHex(serializedMessage), expectedString, encodeHex(expectedBytes)),
+ contains(serializedMessage, expectedBytes));
+ private String encodeHex(ByteString bytes) {
+ String hexDigits = "0123456789abcdef";
+ StringBuilder stringBuilder = new StringBuilder(bytes.size() * 2);
+ for (byte b : bytes) {
+ stringBuilder.append(hexDigits.charAt((b & 0xf0) >> 4));
+ stringBuilder.append(hexDigits.charAt(b & 0x0f));
+ return stringBuilder.toString();
+ private boolean contains(ByteString a, ByteString b) {
+ for (int i = 0; i <= a.size() - b.size(); ++i) {
+ if (a.substring(i, i + b.size()).equals(b)) {
+ return true;
+ return false;
@@ -299,20 +299,22 @@ public class LongArrayListTest extends TestCase {
- LongList toRemove = LongArrayList.emptyList().mutableCopyWithCapacity(1);
+ LongList toRemove =
+ LongArrayList.emptyList().mutableCopyWithCapacity(1);
toRemove.addLong(3);
- private void assertImmutable(LongArrayList list) {
+ private void assertImmutable(LongList list) {
if (list.contains(1L)) {
@@ -42,33 +42,33 @@ import junit.framework.TestCase;
* Tests for {@link ProtobufArrayList}.
public class ProtobufArrayListTest extends TestCase {
private static final ProtobufArrayList<Integer> UNARY_LIST = newImmutableProtoArrayList(1);
private static final ProtobufArrayList<Integer> TERTIARY_LIST =
newImmutableProtoArrayList(1, 2, 3);
private ProtobufArrayList<Integer> list;
protected void setUp() throws Exception {
list = new ProtobufArrayList<Integer>();
public void testEmptyListReturnsSameInstance() {
assertSame(ProtobufArrayList.emptyList(), ProtobufArrayList.emptyList());
public void testEmptyListIsImmutable() {
assertImmutable(ProtobufArrayList.<Integer>emptyList());
public void testModificationWithIteration() {
list.addAll(asList(1, 2, 3, 4));
Iterator<Integer> iterator = list.iterator();
assertEquals(1, (int) list.get(0));
assertEquals(1, (int) iterator.next());
iterator.next();
@@ -76,7 +76,7 @@ public class ProtobufArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
iterator = list.iterator();
list.set(0, 1);
@@ -85,7 +85,7 @@ public class ProtobufArrayListTest extends TestCase {
list.add(0, 0);
@@ -95,7 +95,7 @@ public class ProtobufArrayListTest extends TestCase {
public void testMakeImmutable() {
list.add(2);
list.add(4);
@@ -104,107 +104,213 @@ public class ProtobufArrayListTest extends TestCase {
list.makeImmutable();
assertImmutable(list);
- list.add(2);
- list.add(4);
- list.add(6);
+ list.addAll(TERTIARY_LIST);
+ assertEquals(1, (int) list.remove(0));
+ assertEquals(asList(2, 3), list);
- list.remove(1);
- assertEquals(asList(2, 6), list);
+ assertTrue(list.remove(Integer.valueOf(3)));
+ assertEquals(asList(2), list);
+ assertFalse(list.remove(Integer.valueOf(3)));
assertEquals(asList(2), list);
- list.remove(0);
+ assertEquals(2, (int) list.remove(0));
+ list.remove(-1);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ list.remove(0);
- assertEquals(2, (int) list.get(0));
- assertEquals(6, (int) list.get(1));
+ assertEquals(1, (int) TERTIARY_LIST.get(0));
+ assertEquals(2, (int) TERTIARY_LIST.get(1));
+ assertEquals(3, (int) TERTIARY_LIST.get(2));
+ TERTIARY_LIST.get(-1);
+ TERTIARY_LIST.get(3);
public void testSet() {
- list.set(0, 1);
+ list.add(4);
+ assertEquals(2, (int) list.set(0, 3));
+ assertEquals(3, (int) list.get(0));
+ assertEquals(4, (int) list.set(1, 0));
+ assertEquals(0, (int) list.get(1));
+ list.set(-1, 0);
+ list.set(2, 0);
+ public void testAdd() {
+ assertEquals(0, list.size());
+ assertTrue(list.add(2));
+ assertTrue(list.add(3));
+ list.add(0, 4);
+ assertEquals(asList(4, 2, 3), list);
+ list.add(0, 1);
+ list.add(0, 0);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(Integer.valueOf(5 + i));
+ assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list);
+ list.add(-1, 5);
+ list.add(4, 5);
+ public void testAddAll() {
+ assertTrue(list.addAll(Collections.singleton(1)));
+ assertEquals(1, list.size());
- list.set(1, 2);
- assertEquals(2, (int) list.get(1));
+ assertTrue(list.addAll(asList(2, 3, 4, 5, 6)));
+ assertEquals(asList(1, 2, 3, 4, 5, 6), list);
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list);
+ assertFalse(list.addAll(Collections.<Integer>emptyList()));
+ assertFalse(list.addAll(IntArrayList.emptyList()));
+ public void testSize() {
+ assertEquals(0, ProtobufArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+ list.add(3);
+ list.add(6);
+ list.add(8);
+ assertEquals(4, list.size());
+ assertEquals(3, list.size());
+ list.add(17);
private void assertImmutable(List<Integer> list) {
list.add(1);
fail();
} catch (UnsupportedOperationException e) {
list.add(0, 1);
list.addAll(Collections.<Integer>emptyList());
list.addAll(Collections.singletonList(1));
list.addAll(new ProtobufArrayList<Integer>());
list.addAll(UNARY_LIST);
list.addAll(0, Collections.singleton(1));
list.addAll(0, UNARY_LIST);
list.addAll(0, Collections.<Integer>emptyList());
list.clear();
@@ -219,56 +325,56 @@ public class ProtobufArrayListTest extends TestCase {
list.remove(new Object());
list.removeAll(Collections.emptyList());
list.removeAll(Collections.singleton(1));
list.removeAll(UNARY_LIST);
list.retainAll(Collections.emptyList());
list.retainAll(Collections.singleton(1));
list.retainAll(UNARY_LIST);
list.set(0, 0);
@@ -276,7 +382,7 @@ public class ProtobufArrayListTest extends TestCase {
private static ProtobufArrayList<Integer> newImmutableProtoArrayList(int... elements) {
ProtobufArrayList<Integer> list = new ProtobufArrayList<Integer>();
for (int element : elements) {
@@ -130,8 +130,6 @@ import static protobuf_unittest.UnittestProto.defaultFixed64Extension;
import static protobuf_unittest.UnittestProto.defaultFloatExtension;
import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension;
import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
-// The static imports are to avoid 100+ char lines. The following is roughly equivalent to
-// import static protobuf_unittest.UnittestProto.*;
import static protobuf_unittest.UnittestProto.defaultInt32Extension;
import static protobuf_unittest.UnittestProto.defaultInt64Extension;
import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension;
@@ -263,12 +261,14 @@ public final class TestUtil {
return ByteString.copyFrom(str.getBytes(Internal.UTF_8));
+ // BEGIN FULL-RUNTIME
* Dirties the message by resetting the momoized serialized size.
public static void resetMemoizedSize(AbstractMessage message) {
message.memoizedSize = -1;
+ // END FULL-RUNTIME
* Get a {@code TestAllTypes} with all fields set as they would be by
@@ -1201,17 +1201,29 @@ public final class TestUtil {
* Get an unmodifiable {@link ExtensionRegistry} containing all the
* extensions of {@code TestAllExtensions}.
- public static ExtensionRegistry getExtensionRegistry() {
+ public static ExtensionRegistryLite getExtensionRegistry() {
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ registerAllExtensions(registry);
+ return registry.getUnmodifiable();
+ * Get an unmodifiable {@link ExtensionRegistry} containing all the
+ * extensions of {@code TestAllExtensions}.
+ public static ExtensionRegistry getFullExtensionRegistry() {
ExtensionRegistry registry = ExtensionRegistry.newInstance();
registerAllExtensions(registry);
return registry.getUnmodifiable();
* Register all of {@code TestAllExtensions}'s extensions with the
* given {@link ExtensionRegistry}.
- public static void registerAllExtensions(ExtensionRegistry registry) {
+ public static void registerAllExtensions(ExtensionRegistryLite registry) {
UnittestProto.registerAllExtensions(registry);
TestUtilLite.registerAllExtensionsLite(registry);
@@ -2634,6 +2646,7 @@ public final class TestUtil {
* Performs the same things that the methods of {@code TestUtil} do, but
@@ -3819,6 +3832,16 @@ public final class TestUtil {
"Couldn't read file: " + fullPath.getPath(), e);
+ private static ByteString readBytesFromResource(String name) {
+ return ByteString.copyFrom(
+ com.google.common.io.ByteStreams.toByteArray(TestUtil.class.getResourceAsStream(name)));
+ throw new RuntimeException(e);
* Get the bytes of the "golden message". This is a serialized TestAllTypes
@@ -3829,7 +3852,7 @@ public final class TestUtil {
public static ByteString getGoldenMessage() {
if (goldenMessage == null) {
- goldenMessage = readBytesFromFile("golden_message_oneof_implemented");
+ goldenMessage = readBytesFromResource("/google/protobuf/testdata/golden_message_oneof_implemented");
return goldenMessage;
@@ -3846,12 +3869,13 @@ public final class TestUtil {
public static ByteString getGoldenPackedFieldsMessage() {
if (goldenPackedFieldsMessage == null) {
goldenPackedFieldsMessage =
- readBytesFromFile("golden_packed_fields_message");
+ readBytesFromResource("/google/protobuf/testdata/golden_packed_fields_message");
return goldenPackedFieldsMessage;
private static ByteString goldenPackedFieldsMessage = null;
* Mock implementation of {@link GeneratedMessage.BuilderParent} for testing.
@@ -3871,4 +3895,5 @@ public final class TestUtil {
return invalidations;
@@ -382,17 +382,14 @@ public class TextFormatTest extends TestCase {
public void testMergeExtensions() throws Exception {
- TextFormat.merge(allExtensionsSetText,
- TestUtil.getExtensionRegistry(),
- builder);
+ TextFormat.merge(allExtensionsSetText, TestUtil.getFullExtensionRegistry(), builder);
TestUtil.assertAllExtensionsSet(builder.build());
public void testParseExtensions() throws Exception {
TestUtil.assertAllExtensionsSet(
- TextFormat.parse(allExtensionsSetText,
- TestAllExtensions.class));
+ TextFormat.parse(
+ allExtensionsSetText, TestUtil.getFullExtensionRegistry(), TestAllExtensions.class));
public void testMergeAndParseCompatibility() throws Exception {
@@ -523,7 +520,7 @@ public class TextFormatTest extends TestCase {
// Test merge().
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
- TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder);
+ TextFormat.merge(text, TestUtil.getFullExtensionRegistry(), builder);
fail("Expected parse exception.");
} catch (TextFormat.ParseException e) {
assertEquals(error, e.getMessage());
@@ -531,8 +528,7 @@ public class TextFormatTest extends TestCase {
// Test parse().
- TextFormat.parse(
- text, TestUtil.getExtensionRegistry(), TestAllTypes.class);
+ TextFormat.parse(text, TestUtil.getFullExtensionRegistry(), TestAllTypes.class);
@@ -544,8 +540,7 @@ public class TextFormatTest extends TestCase {
String text) {
- parserWithOverwriteForbidden.merge(
- text, TestUtil.getExtensionRegistry(), builder);
+ parserWithOverwriteForbidden.merge(text, TestUtil.getFullExtensionRegistry(), builder);
@@ -555,8 +550,7 @@ public class TextFormatTest extends TestCase {
private TestAllTypes assertParseSuccessWithOverwriteForbidden(
String text) throws TextFormat.ParseException {
return builder.build();
@@ -1118,8 +1112,7 @@ public class TextFormatTest extends TestCase {
String input = "foo_string: \"stringvalue\" foo_int: 123";
TestOneof2.Builder builder = TestOneof2.newBuilder();
- input, TestUtil.getExtensionRegistry(), builder);
+ parserWithOverwriteForbidden.merge(input, TestUtil.getFullExtensionRegistry(), builder);
assertEquals("1:36: Field \"protobuf_unittest.TestOneof2.foo_int\""
@@ -1131,7 +1124,7 @@ public class TextFormatTest extends TestCase {
public void testOneofOverwriteAllowed() throws Exception {
- defaultParser.merge(input, TestUtil.getExtensionRegistry(), builder);
+ defaultParser.merge(input, TestUtil.getFullExtensionRegistry(), builder);
// Only the last value sticks.
TestOneof2 oneof = builder.build();
assertFalse(oneof.hasFooString());
@@ -132,7 +132,7 @@ public class WireFormatTest extends TestCase {
TestAllTypes message = TestUtil.getAllSet();
ByteString rawBytes = message.toByteString();
- ExtensionRegistry registry = TestUtil.getExtensionRegistry();
+ ExtensionRegistryLite registry = TestUtil.getExtensionRegistry();
TestAllExtensions message2 =
TestAllExtensions.parseFrom(rawBytes, registry);
@@ -145,7 +145,7 @@ public class WireFormatTest extends TestCase {
TestPackedExtensions message = TestUtil.getPackedExtensionsSet();
TestPackedExtensions message2 =
TestPackedExtensions.parseFrom(rawBytes, registry);
@@ -36,8 +36,6 @@ syntax = "proto2";
package protobuf_unittest;
-option optimize_for = LITE_RUNTIME;
message LazyMessageLite {
optional int32 num = 1;
optional int32 num_with_default = 2 [default = 421];
@@ -34,7 +34,6 @@ syntax = "proto2";
package protobuf_unittest.lite_equals_and_hash;
message TestOneofEquals {
oneof oneof_field {
@@ -30,10 +30,9 @@
syntax = "proto3";
-package map_lite_test;
+package map_test;
-option java_package = "map_lite_test";
+option java_package = "map_test";
option java_outer_classname = "MapTestProto";
message TestMap {
@@ -38,7 +38,6 @@ syntax = "proto2";
import "com/google/protobuf/non_nested_extension_lite.proto";
@@ -36,7 +36,6 @@ syntax = "proto2";
message MessageLiteToBeExtended {
extensions 1 to max;
@@ -61,6 +61,9 @@ public final class Durations {
public static final Duration MAX_VALUE =
Duration.newBuilder().setSeconds(DURATION_SECONDS_MAX).setNanos(999999999).build();
+ /** A constant holding the duration of zero. */
+ public static final Duration ZERO = Duration.newBuilder().setSeconds(0L).setNanos(0).build();
private Durations() {}
private static final Comparator<Duration> COMPARATOR =
@@ -249,12 +249,9 @@ final class FieldMaskTree {
continue;
String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
- merge(
- entry.getValue(),
- childPath,
- (Message) source.getField(field),
- destination.getFieldBuilder(field),
- options);
+ Message.Builder childBuilder = ((Message) destination.getField(field)).toBuilder();
+ merge(entry.getValue(), childPath, (Message) source.getField(field), childBuilder, options);
+ destination.setField(field, childBuilder.buildPartial());
if (field.isRepeated()) {
@@ -275,7 +272,12 @@ final class FieldMaskTree {
if (source.hasField(field)) {
- destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
+ destination.setField(
+ field,
+ ((Message) destination.getField(field))
+ .toBuilder()
+ .mergeFrom((Message) source.getField(field))
+ .build());
@@ -235,7 +235,7 @@ public class FieldMaskUtil {
* Converts a FieldMask to its canonical form. In the canonical form of a
* FieldMask, all field paths are sorted alphabetically and redundant field
- * paths are moved.
+ * paths are removed.
public static FieldMask normalize(FieldMask mask) {
return new FieldMaskTree(mask).toFieldMask();
@@ -610,7 +610,7 @@ public class JsonFormat {
private final CharSequence blankOrNewLine;
private static class GsonHolder {
- private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
+ private static final Gson DEFAULT_GSON = new GsonBuilder().create();
PrinterImpl(
@@ -30,9 +30,14 @@
package com.google.protobuf.util;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.Message;
+import com.google.protobuf.UninitializedMessageException;
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
+import protobuf_unittest.UnittestProto.TestRequired;
+import protobuf_unittest.UnittestProto.TestRequiredMessage;
import junit.framework.TestCase;
public class FieldMaskTreeTest extends TestCase {
@@ -90,8 +95,68 @@ public class FieldMaskTreeTest extends TestCase {
tree.intersectFieldPath("bar", result);
assertEquals("bar.baz,bar.quz,foo", result.toString());
public void testMerge() throws Exception {
+ testMergeImpl(true);
+ testMergeImpl(false);
+ testMergeRequire(false);
+ testMergeRequire(true);
+ private void merge(
+ FieldMaskTree tree,
+ Message source,
+ Message.Builder builder,
+ FieldMaskUtil.MergeOptions options,
+ boolean useDynamicMessage)
+ throws Exception {
+ if (useDynamicMessage) {
+ Message.Builder newBuilder =
+ DynamicMessage.newBuilder(source.getDescriptorForType())
+ .mergeFrom(builder.buildPartial().toByteArray());
+ tree.merge(
+ .mergeFrom(source.toByteArray())
+ .build(),
+ newBuilder,
+ builder.clear();
+ builder.mergeFrom(newBuilder.buildPartial());
+ tree.merge(source, builder, options);
+ private void testMergeRequire(boolean useDynamicMessage) throws Exception {
+ TestRequired value = TestRequired.newBuilder().setA(4321).setB(8765).setC(233333).build();
+ TestRequiredMessage source = TestRequiredMessage.newBuilder().setRequiredMessage(value).build();
+ FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
+ TestRequiredMessage.Builder builder = TestRequiredMessage.newBuilder();
+ merge(
+ new FieldMaskTree().addFieldPath("required_message.a"),
+ source,
+ builder,
+ options,
+ useDynamicMessage);
+ assertTrue(builder.hasRequiredMessage());
+ assertTrue(builder.getRequiredMessage().hasA());
+ assertFalse(builder.getRequiredMessage().hasB());
+ assertFalse(builder.getRequiredMessage().hasC());
+ new FieldMaskTree().addFieldPath("required_message.b").addFieldPath("required_message.c"),
+ assertEquals(builder.build(), source);
+ } catch (UninitializedMessageException e) {
+ throw new AssertionError("required field isn't set", e);
+ private void testMergeImpl(boolean useDynamicMessage) throws Exception {
TestAllTypes value =
TestAllTypes.newBuilder()
.setOptionalInt32(1234)
@@ -119,45 +184,51 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging each individual field.
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
+ source, builder, options, useDynamicMessage);
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.optional_nested_message")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_nested_message"),
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
- new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
expected.getPayloadBuilder().addRepeatedInt32(4321);
- .addFieldPath("payload.repeated_nested_message")
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_nested_message"),
expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
- .addFieldPath("child.payload.optional_int32")
+ new FieldMaskTree().addFieldPath("child.payload.optional_int32"),
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
- .addFieldPath("child.payload.optional_nested_message")
+ new FieldMaskTree().addFieldPath("child.payload.optional_nested_message"),
expected
.getChildBuilder()
@@ -166,17 +237,15 @@ public class FieldMaskTreeTest extends TestCase {
- .addFieldPath("child.payload.repeated_int32")
+ merge(new FieldMaskTree().addFieldPath("child.payload.repeated_int32"),
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
- .addFieldPath("child.payload.repeated_nested_message")
+ merge(new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message"),
@@ -186,23 +255,23 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging all fields.
- .addFieldPath("child")
- .addFieldPath("payload")
+ merge(new FieldMaskTree().addFieldPath("child").addFieldPath("payload"),
assertEquals(source, builder.build());
// Test repeated options.
builder.getPayloadBuilder().addRepeatedInt32(1000);
// Default behavior is to append repeated fields.
assertEquals(2, builder.getPayload().getRepeatedInt32Count());
assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
// Change to replace repeated fields.
options.setReplaceRepeatedFields(true);
assertEquals(1, builder.getPayload().getRepeatedInt32Count());
assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
@@ -210,7 +279,8 @@ public class FieldMaskTreeTest extends TestCase {
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload"),
// Default behavior is to merge message fields.
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(2000, builder.getPayload().getOptionalUint32());
@@ -218,14 +288,14 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging unset message fields.
NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
- new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
+ clearedSource, builder, options, useDynamicMessage);
assertEquals(false, builder.hasPayload());
// Skip a message field if they are unset in both source and target.
- .addFieldPath("payload.optional_int32")
- .merge(clearedSource, builder, options);
// Change to replace message fields.
@@ -233,7 +303,8 @@ public class FieldMaskTreeTest extends TestCase {
assertEquals(0, builder.getPayload().getOptionalUint32());
@@ -241,7 +312,8 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging unset primitive fields.
@@ -249,18 +321,16 @@ public class FieldMaskTreeTest extends TestCase {
builder.getPayloadBuilder().clearOptionalInt32();
NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build();
builder = source.toBuilder();
- .merge(sourceWithPayloadInt32Unset, builder, options);
+ sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
assertEquals(true, builder.getPayload().hasOptionalInt32());
assertEquals(0, builder.getPayload().getOptionalInt32());
// Change to clear unset primitive fields.
options.setReplacePrimitiveFields(true);
assertEquals(true, builder.hasPayload());
assertEquals(false, builder.getPayload().hasOptionalInt32());
@@ -70,7 +70,6 @@ import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
@@ -1188,10 +1187,14 @@ public class JsonFormatTest extends TestCase {
assertRoundTripEquals(message);
- public void testDefaultGsonDoesNotHtmlEscape() throws Exception {
- TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("=").build();
- assertEquals(
- "{\n" + " \"optionalString\": \"=\"" + "\n}", JsonFormat.printer().print(message));
+ // Regression test for b/73832901. Make sure html tags are escaped.
+ public void testHtmlEscape() throws Exception {
+ TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("</script>").build();
+ assertEquals("{\n \"optionalString\": \"\\u003c/script\\u003e\"\n}", toJsonString(message));
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ JsonFormat.parser().merge(toJsonString(message), builder);
+ assertEquals(message.getOptionalString(), builder.getOptionalString());
public void testIncludingDefaultValueFields() throws Exception {
@@ -174,7 +174,7 @@ jspb.PrunerFunction;
* A comparer function returns true if two protos are equal.
- * @typedef {!function(?jspb.ConstBinaryMessage,
+ * @typedef {function(?jspb.ConstBinaryMessage,
* ?jspb.ConstBinaryMessage):boolean}
jspb.ComparerFunction;
@@ -290,7 +290,9 @@ jspb.BinaryReader.prototype.nextField = function() {
nextWireType != jspb.BinaryConstants.WireType.DELIMITED &&
nextWireType != jspb.BinaryConstants.WireType.START_GROUP &&
nextWireType != jspb.BinaryConstants.WireType.END_GROUP) {
- goog.asserts.fail('Invalid wire type');
+ goog.asserts.fail(
+ 'Invalid wire type: %s (at position %s)', nextWireType,
+ this.fieldCursor_);
this.error_ = true;
return false;
@@ -388,8 +390,7 @@ jspb.BinaryReader.prototype.skipFixed64Field = function() {
* Skips over the next group field in the binary stream.
jspb.BinaryReader.prototype.skipGroup = function() {
- // Keep a stack of start-group tags that must be matched by end-group tags.
- var nestedGroups = [this.nextField_];
+ var previousField = this.nextField_;
do {
if (!this.nextField()) {
goog.asserts.fail('Unmatched start-group tag: stream EOF');
@@ -397,19 +398,17 @@ jspb.BinaryReader.prototype.skipGroup = function() {
return;
if (this.nextWireType_ ==
- jspb.BinaryConstants.WireType.START_GROUP) {
- // Nested group start.
- nestedGroups.push(this.nextField_);
- } else if (this.nextWireType_ ==
jspb.BinaryConstants.WireType.END_GROUP) {
// Group end: check that it matches top-of-stack.
- if (this.nextField_ != nestedGroups.pop()) {
+ if (this.nextField_ != previousField) {
goog.asserts.fail('Unmatched end-group tag');
- } while (nestedGroups.length > 0);
+ this.skipField();
+ } while (true);
@@ -679,9 +679,24 @@ describe('binaryReaderTest', function() {
writer.writeInt32(5, sentinel);
var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
writer.writeGroup(5, dummyMessage, function() {
+ // Previously the skipGroup implementation was wrong, which only consume
+ // the decoder by nextField. This case is for making the previous
+ // implementation failed in skipGroup by an early end group tag.
+ // The reason is 44 = 5 * 8 + 4, this will be translated in to a field
+ // with number 5 and with type 4 (end group)
+ writer.writeInt64(44, 44);
+ // This will make previous implementation failed by invalid tag (7).
+ writer.writeInt64(42, 47);
writer.writeInt64(42, 42);
+ // This is for making the previous implementation failed by an invalid
+ // varint. The bytes have at least 9 consecutive minus byte, which will
+ // fail in this.nextField for previous implementation.
+ writer.writeBytes(43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
writer.writeGroup(6, dummyMessage, function() {
writer.writeInt64(84, 42);
+ writer.writeInt64(84, 44);
+ writer.writeBytes(
+ 43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
});
@@ -971,8 +971,9 @@ jspb.utils.byteSourceToUint8Array = function(data) {
return /** @type {!Uint8Array} */(new Uint8Array(data));
- if (data.constructor === Buffer) {
- return /** @type {!Uint8Array} */(new Uint8Array(data));
+ if (typeof Buffer != 'undefined' && data.constructor === Buffer) {
+ return /** @type {!Uint8Array} */ (
+ new Uint8Array(/** @type {?} */ (data)));
if (data.constructor === Array) {
@@ -136,7 +136,7 @@ jspb.Map.prototype.toArray = function() {
* @param {boolean=} includeInstance Whether to include the JSPB instance for
* transitional soy proto support: http://goto/soy-param-migration
- * @param {!function((boolean|undefined),V):!Object=} valueToObject
+ * @param {function((boolean|undefined),V):!Object=} valueToObject
* The static toObject() method, if V is a message type.
* @return {!Array<!Array<!Object>>}
@@ -165,9 +165,9 @@ jspb.Map.prototype.toObject = function(includeInstance, valueToObject) {
* @template K, V
* @param {!Array<!Array<!Object>>} entries
- * @param {!function(new:V,?=)} valueCtor
+ * @param {function(new:V,?=)} valueCtor
* The constructor for type V.
- * @param {!function(!Object):V} valueFromObject
+ * @param {function(!Object):V} valueFromObject
* The fromObject function for type V.
* @return {!jspb.Map<K, V>}
@@ -410,9 +410,9 @@ jspb.Map.prototype.has = function(key) {
* number.
* @param {number} fieldNumber
* @param {!jspb.BinaryWriter} writer
- * @param {!function(this:jspb.BinaryWriter,number,K)} keyWriterFn
+ * @param {function(this:jspb.BinaryWriter,number,K)} keyWriterFn
* The method on BinaryWriter that writes type K to the stream.
- * @param {!function(this:jspb.BinaryWriter,number,V,?=)|
+ * @param {function(this:jspb.BinaryWriter,number,V,?=)|
* function(this:jspb.BinaryWriter,number,V,?)} valueWriterFn
* The method on BinaryWriter that writes type V to the stream. May be
* writeMessage, in which case the second callback arg form is used.
@@ -448,10 +448,10 @@ jspb.Map.prototype.serializeBinary = function(
* @param {!jspb.Map} map
* @param {!jspb.BinaryReader} reader
- * @param {!function(this:jspb.BinaryReader):K} keyReaderFn
+ * @param {function(this:jspb.BinaryReader):K} keyReaderFn
* The method on BinaryReader that reads type K from the stream.
- * @param {!function(this:jspb.BinaryReader):V|
+ * @param {function(this:jspb.BinaryReader):V|
* function(this:jspb.BinaryReader,V,
* function(V,!jspb.BinaryReader))} valueReaderFn
* The method on BinaryReader that reads type V from the stream. May be
@@ -439,9 +439,19 @@ jspb.Message.isArray_ = function(o) {
* @private
jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) {
- if (msg.array.length) {
- var foundIndex = msg.array.length - 1;
- var obj = msg.array[foundIndex];
+ // There are 3 variants that need to be dealt with which are the
+ // combination of whether there exists an extension object (EO) and
+ // whether there is a suggested pivot (SP).
+ //
+ // EO, ? : pivot is the index of the EO
+ // no-EO, no-SP: pivot is MAX_INT
+ // no-EO, SP : pivot is the max(lastindex + 1, SP)
+ var msgLength = msg.array.length;
+ var lastIndex = -1;
+ if (msgLength) {
+ lastIndex = msgLength - 1;
+ var obj = msg.array[lastIndex];
// Normal fields are never objects, so we can be sure that if we find an
// object here, then it's the extension object. However, we must ensure that
// the object is not an array, since arrays are valid field values.
@@ -449,14 +459,17 @@ jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) {
// in Safari on iOS 8. See the description of CL/86511464 for details.
if (obj && typeof obj == 'object' && !jspb.Message.isArray_(obj) &&
!(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) {
- msg.pivot_ = jspb.Message.getFieldNumber_(msg, foundIndex);
+ msg.pivot_ = jspb.Message.getFieldNumber_(msg, lastIndex);
msg.extensionObject_ = obj;
if (suggestedPivot > -1) {
- msg.pivot_ = suggestedPivot;
+ // If a extension object is not present, set the pivot value as being
+ // after the last value in the array to avoid overwriting values, etc.
+ msg.pivot_ = Math.max(
+ suggestedPivot, jspb.Message.getFieldNumber_(msg, lastIndex + 1));
// Avoid changing the shape of the proto with an empty extension object by
// deferring the materialization of the extension object until the first
// time a field set into it (may be due to getting a repeated proto field
@@ -922,17 +935,6 @@ jspb.Message.setProto3IntField = function(msg, fieldNumber, value) {
-/**
- * Sets the value of a non-extension integer, handled as string, field of a proto3
- * @param {!jspb.Message} msg A jspb proto.
- * @param {number} fieldNumber The field number.
- * @param {number} value New value
- * @protected
-jspb.Message.setProto3StringIntField = function(msg, fieldNumber, value) {
- jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, '0');
-};
* Sets the value of a non-extension floating point field of a proto3
* @param {!jspb.Message} msg A jspb proto.
@@ -993,12 +995,22 @@ jspb.Message.setProto3EnumField = function(msg, fieldNumber, value) {
+/**
+ * Sets the value of a non-extension int field of a proto3 that has jstype set
+ * to String.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {string} value New value
+ * @protected
+jspb.Message.setProto3StringIntField = function(msg, fieldNumber, value) {
+ jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, "0");
* Sets the value of a non-extension primitive field, with proto3 (non-nullable
* primitives) semantics of ignoring values that are equal to the type's
- * @template T
* @param {number} fieldNumber The field number.
* @param {!Uint8Array|string|number|boolean|undefined} value New value
@@ -1007,7 +1019,7 @@ jspb.Message.setProto3EnumField = function(msg, fieldNumber, value) {
jspb.Message.setFieldIgnoringDefault_ = function(
msg, fieldNumber, value, defaultValue) {
- if (value != defaultValue) {
+ if (value !== defaultValue) {
jspb.Message.setField(msg, fieldNumber, value);
msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = null;
@@ -1127,7 +1139,7 @@ jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) {
* @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
- * @return {Array<!jspb.Message>} The repeated field as an array of protos.
+ * @return {!Array<!jspb.Message>} The repeated field as an array of protos.
* @protected
jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) {
@@ -73,6 +73,7 @@ goog.require('proto.jspb.test.Simple1');
goog.require('proto.jspb.test.Simple2');
goog.require('proto.jspb.test.SpecialCases');
goog.require('proto.jspb.test.TestClone');
+goog.require('proto.jspb.test.TestCloneExtension');
goog.require('proto.jspb.test.TestEndsWithBytes');
goog.require('proto.jspb.test.TestGroup');
goog.require('proto.jspb.test.TestGroup1');
@@ -165,6 +165,13 @@ message TestClone {
extensions 10 to max;
+message TestCloneExtension {
+ extend TestClone {
+ optional TestCloneExtension low_ext = 11;
+ optional int32 f = 1;
message CloneExtension {
extend TestClone {
optional CloneExtension ext_field = 100;
@@ -410,7 +410,8 @@ class TextFormatTest(unittest.TestCase):
text = 'optional_nested_enum: BARR'
self.assertRaisesWithMessage(
text_format.ParseError,
- ('1:23 : Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
+ ('1:23 : \'optional_nested_enum: BARR\': '
+ 'Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
'has no value named BARR.'),
text_format.Merge, text, message)
@@ -418,7 +419,8 @@ class TextFormatTest(unittest.TestCase):
text = 'optional_nested_enum: 100'
+ ('1:23 : \'optional_nested_enum: 100\': '
'has no value with number 100.'),
@@ -427,7 +429,8 @@ class TextFormatTest(unittest.TestCase):
text = 'optional_int32: bork'
- ('1:17 : Couldn\'t parse integer: bork'),
+ ('1:17 : \'optional_int32: bork\': '
+ 'Couldn\'t parse integer: bork'),
def testMergeStringFieldUnescape(self):
@@ -76,6 +76,9 @@ class DescriptorDatabase(object):
self._AddSymbol(name, file_desc_proto)
for enum in file_desc_proto.enum_type:
self._AddSymbol(('.'.join((package, enum.name))), file_desc_proto)
+ for enum_value in enum.value:
+ self._file_desc_protos_by_symbol[
+ '.'.join((package, enum_value.name))] = file_desc_proto
for extension in file_desc_proto.extension:
self._AddSymbol(('.'.join((package, extension.name))), file_desc_proto)
for service in file_desc_proto.service:
@@ -133,6 +136,14 @@ class DescriptorDatabase(object):
top_level, _, _ = symbol.rpartition('.')
return self._file_desc_protos_by_symbol[top_level]
+ def FindFileContainingExtension(self, extendee_name, extension_number):
+ # TODO(jieluo): implement this API.
+ return None
+ def FindAllExtensionNumbers(self, extendee_name):
+ return []
def _AddSymbol(self, name, file_desc_proto):
if name in self._file_desc_protos_by_symbol:
warn_msg = ('Conflict register for file "' + file_desc_proto.name +
@@ -131,33 +131,46 @@ class DescriptorPool(object):
# TODO(jieluo): Remove _file_desc_by_toplevel_extension after
# maybe year 2020 for compatibility issue (with 3.4.1 only).
self._file_desc_by_toplevel_extension = {}
+ self._top_enum_values = {}
# We store extensions in two two-level mappings: The first key is the
# descriptor of the message being extended, the second key is the extension
# full name or its tag number.
self._extensions_by_name = collections.defaultdict(dict)
self._extensions_by_number = collections.defaultdict(dict)
- def _CheckConflictRegister(self, desc):
+ def _CheckConflictRegister(self, desc, desc_name, file_name):
"""Check if the descriptor name conflicts with another of the same name.
Args:
- desc: Descriptor of a message, enum, service or extension.
+ desc: Descriptor of a message, enum, service, extension or enum value.
+ desc_name: the full name of desc.
+ file_name: The file name of descriptor.
"""
- desc_name = desc.full_name
for register, descriptor_type in [
(self._descriptors, descriptor.Descriptor),
(self._enum_descriptors, descriptor.EnumDescriptor),
(self._service_descriptors, descriptor.ServiceDescriptor),
- (self._toplevel_extensions, descriptor.FieldDescriptor)]:
+ (self._toplevel_extensions, descriptor.FieldDescriptor),
+ (self._top_enum_values, descriptor.EnumValueDescriptor)]:
if desc_name in register:
- file_name = register[desc_name].file.name
+ old_desc = register[desc_name]
+ if isinstance(old_desc, descriptor.EnumValueDescriptor):
+ old_file = old_desc.type.file.name
+ else:
+ old_file = old_desc.file.name
if not isinstance(desc, descriptor_type) or (
- file_name != desc.file.name):
- warn_msg = ('Conflict register for file "' + desc.file.name +
+ old_file != file_name):
+ warn_msg = ('Conflict register for file "' + file_name +
'": ' + desc_name +
' is already defined in file "' +
- file_name + '"')
+ old_file + '"')
+ if isinstance(desc, descriptor.EnumValueDescriptor):
+ warn_msg += ('\nNote: enum values appear as '
+ 'siblings of the enum type instead of '
+ 'children of it.')
warnings.warn(warn_msg, RuntimeWarning)
return
def Add(self, file_desc_proto):
@@ -196,7 +209,7 @@ class DescriptorPool(object):
if not isinstance(desc, descriptor.Descriptor):
raise TypeError('Expected instance of descriptor.Descriptor.')
- self._CheckConflictRegister(desc)
+ self._CheckConflictRegister(desc, desc.full_name, desc.file.name)
self._descriptors[desc.full_name] = desc
self._AddFileDescriptor(desc.file)
@@ -213,8 +226,26 @@ class DescriptorPool(object):
if not isinstance(enum_desc, descriptor.EnumDescriptor):
raise TypeError('Expected instance of descriptor.EnumDescriptor.')
- self._CheckConflictRegister(enum_desc)
+ file_name = enum_desc.file.name
+ self._CheckConflictRegister(enum_desc, enum_desc.full_name, file_name)
self._enum_descriptors[enum_desc.full_name] = enum_desc
+ # Top enum values need to be indexed.
+ # Count the number of dots to see whether the enum is toplevel or nested
+ # in a message. We cannot use enum_desc.containing_type at this stage.
+ if enum_desc.file.package:
+ top_level = (enum_desc.full_name.count('.')
+ - enum_desc.file.package.count('.') == 1)
+ top_level = enum_desc.full_name.count('.') == 0
+ if top_level:
+ package = enum_desc.file.package
+ for enum_value in enum_desc.values:
+ full_name = _NormalizeFullyQualifiedName(
+ '.'.join((package, enum_value.name)))
+ self._CheckConflictRegister(enum_value, full_name, file_name)
+ self._top_enum_values[full_name] = enum_value
self._AddFileDescriptor(enum_desc.file)
def AddServiceDescriptor(self, service_desc):
@@ -227,7 +258,8 @@ class DescriptorPool(object):
if not isinstance(service_desc, descriptor.ServiceDescriptor):
raise TypeError('Expected instance of descriptor.ServiceDescriptor.')
- self._CheckConflictRegister(service_desc)
+ self._CheckConflictRegister(service_desc, service_desc.full_name,
+ service_desc.file.name)
self._service_descriptors[service_desc.full_name] = service_desc
def AddExtensionDescriptor(self, extension):
@@ -247,7 +279,6 @@ class DescriptorPool(object):
raise TypeError('Expected an extension descriptor.')
if extension.extension_scope is None:
- self._CheckConflictRegister(extension)
self._toplevel_extensions[extension.full_name] = extension
@@ -348,6 +379,30 @@ class DescriptorPool(object):
symbol = _NormalizeFullyQualifiedName(symbol)
+ try:
+ return self._InternalFindFileContainingSymbol(symbol)
+ except KeyError:
+ pass
+ # Try fallback database. Build and find again if possible.
+ self._FindFileContainingSymbolInDb(symbol)
+ raise KeyError('Cannot find a file containing %s' % symbol)
+ def _InternalFindFileContainingSymbol(self, symbol):
+ """Gets the already built FileDescriptor containing the specified symbol.
+ Args:
+ symbol: The name of the symbol to search for.
+ Returns:
+ A FileDescriptor that contains the specified symbol.
+ Raises:
+ KeyError: if the file cannot be found in the pool.
+ """
return self._descriptors[symbol].file
except KeyError:
@@ -364,7 +419,7 @@ class DescriptorPool(object):
pass
- return self._FindFileContainingSymbolInDb(symbol)
+ return self._top_enum_values[symbol].type.file
@@ -373,13 +428,15 @@ class DescriptorPool(object):
- # Try nested extensions inside a message.
- message_name, _, extension_name = symbol.rpartition('.')
+ # Try fields, enum values and nested extensions inside a message.
+ top_name, _, sub_name = symbol.rpartition('.')
- message = self.FindMessageTypeByName(message_name)
- assert message.extensions_by_name[extension_name]
+ message = self.FindMessageTypeByName(top_name)
+ assert (sub_name in message.extensions_by_name or
+ sub_name in message.fields_by_name or
+ sub_name in message.enum_values_by_name)
return message.file
- except KeyError:
+ except (KeyError, AssertionError):
raise KeyError('Cannot find a file containing %s' % symbol)
def FindMessageTypeByName(self, full_name):
@@ -499,7 +556,11 @@ class DescriptorPool(object):
KeyError: when no extension with the given number is known for the
specified message.
- return self._extensions_by_number[message_descriptor][number]
+ return self._extensions_by_number[message_descriptor][number]
+ self._TryLoadExtensionFromDB(message_descriptor, number)
def FindAllExtensions(self, message_descriptor):
"""Gets all the known extension of a given message.
@@ -513,8 +574,57 @@ class DescriptorPool(object):
Returns:
A list of FieldDescriptor describing the extensions.
+ # Fallback to descriptor db if FindAllExtensionNumbers is provided.
+ if self._descriptor_db and hasattr(
+ self._descriptor_db, 'FindAllExtensionNumbers'):
+ full_name = message_descriptor.full_name
+ all_numbers = self._descriptor_db.FindAllExtensionNumbers(full_name)
+ for number in all_numbers:
+ if number in self._extensions_by_number[message_descriptor]:
+ continue
return list(self._extensions_by_number[message_descriptor].values())
+ def _TryLoadExtensionFromDB(self, message_descriptor, number):
+ """Try to Load extensions from decriptor db.
+ message_descriptor: descriptor of the extended message.
+ number: the extension number that needs to be loaded.
+ if not self._descriptor_db:
+ return
+ # Only supported when FindFileContainingExtension is provided.
+ if not hasattr(
+ self._descriptor_db, 'FindFileContainingExtension'):
+ file_proto = self._descriptor_db.FindFileContainingExtension(
+ full_name, number)
+ if file_proto is None:
+ file_desc = self._ConvertFileProtoToFileDescriptor(file_proto)
+ for extension in file_desc.extensions_by_name.values():
+ self._extensions_by_number[extension.containing_type][
+ extension.number] = extension
+ self._extensions_by_name[extension.containing_type][
+ extension.full_name] = extension
+ for message_type in file_desc.message_types_by_name.values():
+ for extension in message_type.extensions:
+ except:
+ warn_msg = ('Unable to load proto file %s for extension number %d.' %
+ (file_proto.name, number))
+ warnings.warn(warn_msg, RuntimeWarning)
def FindServiceByName(self, full_name):
"""Loads the named service descriptor from the pool.
@@ -532,6 +642,23 @@ class DescriptorPool(object):
self._FindFileContainingSymbolInDb(full_name)
return self._service_descriptors[full_name]
+ def FindMethodByName(self, full_name):
+ """Loads the named service method descriptor from the pool.
+ full_name: The full name of the method descriptor to load.
+ The method descriptor for the service method.
+ KeyError: if the method cannot be found in the pool.
+ full_name = _NormalizeFullyQualifiedName(full_name)
+ service_name, _, method_name = full_name.rpartition('.')
+ service_descriptor = self.FindServiceByName(service_name)
+ return service_descriptor.methods_by_name[method_name]
def _FindFileContainingSymbolInDb(self, symbol):
"""Finds the file in descriptor DB containing the specified symbol.
@@ -567,7 +694,6 @@ class DescriptorPool(object):
A FileDescriptor matching the passed in proto.
if file_proto.name not in self._file_descriptors:
built_deps = list(self._GetDeps(file_proto.dependency))
direct_deps = [self.FindFileByName(n) for n in file_proto.dependency]
@@ -604,7 +730,7 @@ class DescriptorPool(object):
for enum_type in file_proto.enum_type:
file_descriptor.enum_types_by_name[enum_type.name] = (
self._ConvertEnumDescriptor(enum_type, file_proto.package,
- file_descriptor, None, scope))
+ file_descriptor, None, scope, True))
for index, extension_proto in enumerate(file_proto.extension):
extension_desc = self._MakeFieldDescriptor(
@@ -616,6 +742,8 @@ class DescriptorPool(object):
file_descriptor.package, scope)
file_descriptor.extensions_by_name[extension_desc.name] = (
extension_desc)
+ self._file_desc_by_toplevel_extension[extension_desc.full_name] = (
+ file_descriptor)
for desc_proto in file_proto.message_type:
self._SetAllFieldTypes(file_proto.package, desc_proto, scope)
@@ -673,7 +801,8 @@ class DescriptorPool(object):
nested, desc_name, file_desc, scope, syntax)
for nested in desc_proto.nested_type]
enums = [
- self._ConvertEnumDescriptor(enum, desc_name, file_desc, None, scope)
+ self._ConvertEnumDescriptor(enum, desc_name, file_desc, None,
+ scope, False)
for enum in desc_proto.enum_type]
fields = [self._MakeFieldDescriptor(field, desc_name, index, file_desc)
for index, field in enumerate(desc_proto.field)]
@@ -718,12 +847,12 @@ class DescriptorPool(object):
fields[field_index].containing_oneof = oneofs[oneof_index]
scope[_PrefixWithDot(desc_name)] = desc
self._descriptors[desc_name] = desc
return desc
def _ConvertEnumDescriptor(self, enum_proto, package=None, file_desc=None,
- containing_type=None, scope=None):
+ containing_type=None, scope=None, top_level=False):
"""Make a protobuf EnumDescriptor given an EnumDescriptorProto protobuf.
@@ -732,6 +861,8 @@ class DescriptorPool(object):
file_desc: The file containing the enum descriptor.
containing_type: The type containing this enum.
scope: Scope containing available types.
+ top_level: If True, the enum is a top level symbol. If False, the enum
+ is defined inside a message.
The added descriptor
@@ -757,8 +888,17 @@ class DescriptorPool(object):
containing_type=containing_type,
options=_OptionsOrNone(enum_proto))
scope['.%s' % enum_name] = desc
self._enum_descriptors[enum_name] = desc
+ # Add top level enum values.
+ for value in values:
+ '.'.join((package, value.name)))
+ self._CheckConflictRegister(value, full_name, file_name)
+ self._top_enum_values[full_name] = value
def _MakeFieldDescriptor(self, field_proto, message_name, index,
@@ -885,6 +1025,8 @@ class DescriptorPool(object):
elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES:
field_desc.default_value = text_encoding.CUnescape(
field_proto.default_value)
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE:
+ field_desc.default_value = None
else:
# All other types are of the "int" type.
field_desc.default_value = int(field_proto.default_value)
@@ -901,6 +1043,8 @@ class DescriptorPool(object):
field_desc.default_value = field_desc.enum_type.values[0].number
field_desc.default_value = b''
field_desc.default_value = 0
@@ -954,7 +1098,7 @@ class DescriptorPool(object):
methods=methods,
options=_OptionsOrNone(service_proto),
file=file_desc)
self._service_descriptors[service_name] = desc
@@ -0,0 +1,30 @@
+# 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.
@@ -145,29 +145,3 @@ def Version():
# For internal use only
def IsPythonDefaultSerializationDeterministic():
return _python_deterministic_proto_serialization
-# DO NOT USE: For migration and testing only. Will be removed when Proto3
-# defaults to preserve unknowns.
-if _implementation_type == 'cpp':
- try:
- # pylint: disable=g-import-not-at-top
- from google.protobuf.pyext import _message
- def GetPythonProto3PreserveUnknownsDefault():
- return _message.GetPythonProto3PreserveUnknownsDefault()
- def SetPythonProto3PreserveUnknownsDefault(preserve):
- _message.SetPythonProto3PreserveUnknownsDefault(preserve)
- except ImportError:
- # Unrecognized cpp implementation. Skipping the unknown fields APIs.
- pass
-else:
- _python_proto3_preserve_unknowns_default = True
- return _python_proto3_preserve_unknowns_default
- global _python_proto3_preserve_unknowns_default
- _python_proto3_preserve_unknowns_default = preserve
@@ -628,3 +628,130 @@ class MessageMap(MutableMapping):
def GetEntryClass(self):
return self._entry_descriptor._concrete_class
+class _UnknownField(object):
+ """A parsed unknown field."""
+ # Disallows assignment to other attributes.
+ __slots__ = ['_field_number', '_wire_type', '_data']
+ def __init__(self, field_number, wire_type, data):
+ self._field_number = field_number
+ self._wire_type = wire_type
+ self._data = data
+ def __lt__(self, other):
+ # pylint: disable=protected-access
+ return self._field_number < other._field_number
+ def __eq__(self, other):
+ if self is other:
+ return True
+ return (self._field_number == other._field_number and
+ self._wire_type == other._wire_type and
+ self._data == other._data)
+class UnknownFieldRef(object):
+ def __init__(self, parent, index):
+ self._parent = parent
+ self._index = index
+ def _check_valid(self):
+ if not self._parent:
+ raise ValueError('UnknownField does not exist. '
+ 'The parent message might be cleared.')
+ if self._index >= len(self._parent):
+ @property
+ def field_number(self):
+ self._check_valid()
+ return self._parent._internal_get(self._index)._field_number
+ def wire_type(self):
+ return self._parent._internal_get(self._index)._wire_type
+ def data(self):
+ return self._parent._internal_get(self._index)._data
+class UnknownFieldSet(object):
+ """UnknownField container"""
+ __slots__ = ['_values']
+ def __init__(self):
+ self._values = []
+ def __getitem__(self, index):
+ if self._values is None:
+ raise ValueError('UnknownFields does not exist. '
+ size = len(self._values)
+ if index < 0:
+ index += size
+ if index < 0 or index >= size:
+ raise IndexError('index %d out of range'.index)
+ return UnknownFieldRef(self, index)
+ def _internal_get(self, index):
+ return self._values[index]
+ def __len__(self):
+ return len(self._values)
+ def _add(self, field_number, wire_type, data):
+ unknown_field = _UnknownField(field_number, wire_type, data)
+ self._values.append(unknown_field)
+ return unknown_field
+ def __iter__(self):
+ for i in range(len(self)):
+ yield UnknownFieldRef(self, i)
+ def _extend(self, other):
+ if other is None:
+ self._values.extend(other._values)
+ # Sort unknown fields because their order shouldn't
+ # affect equality test.
+ values = list(self._values)
+ return not values
+ values.sort()
+ other_values = sorted(other._values)
+ return values == other_values
+ def _clear(self):
+ for value in self._values:
+ if isinstance(value._data, UnknownFieldSet):
+ value._data._clear() # pylint: disable=protected-access
+ self._values = None
@@ -86,7 +86,11 @@ import six
if six.PY3:
long = int
+else:
+ import re # pylint: disable=g-import-not-at-top
+ _SURROGATE_PATTERN = re.compile(six.u(r'[\ud800-\udfff]'))
+from google.protobuf.internal import containers
from google.protobuf.internal import encoder
from google.protobuf.internal import wire_format
from google.protobuf import message
@@ -167,7 +171,7 @@ _DecodeSignedVarint32 = _SignedVarintDecoder(32, int)
def ReadTag(buffer, pos):
- """Read a tag from the buffer, and return a (tag_bytes, new_pos) tuple.
+ """Read a tag from the memoryview, and return a (tag_bytes, new_pos) tuple.
We return the raw bytes of the tag rather than decoding them. The raw
bytes can then be used to look up the proper decoder. This effectively allows
@@ -175,13 +179,21 @@ def ReadTag(buffer, pos):
for work that is done in C (searching for a byte string in a hash table).
In a low-level language it would be much cheaper to decode the varint and
use that, but not in Python.
- """
+ buffer: memoryview object of the encoded bytes
+ pos: int of the current position to start from
+ Tuple[bytes, int] of the tag data and new position.
start = pos
while six.indexbytes(buffer, pos) & 0x80:
pos += 1
- return (six.binary_type(buffer[start:pos]), pos)
+ tag_bytes = buffer[start:pos].tobytes()
+ return tag_bytes, pos
# --------------------------------------------------------------------
@@ -295,10 +307,20 @@ def _FloatDecoder():
local_unpack = struct.unpack
def InnerDecode(buffer, pos):
+ """Decode serialized float to a float and new position.
+ buffer: memoryview of the serialized bytes
+ pos: int, position in the memory view to start at.
+ Tuple[float, int] of the deserialized float value and new position
+ in the serialized data.
# We expect a 32-bit value in little-endian byte order. Bit 1 is the sign
# bit, bits 2-9 represent the exponent, and bits 10-32 are the significand.
new_pos = pos + 4
- float_bytes = buffer[pos:new_pos]
+ float_bytes = buffer[pos:new_pos].tobytes()
# If this value has all its exponent bits set, then it's non-finite.
# In Python 2.4, struct.unpack will convert it to a finite 64-bit value.
@@ -329,10 +351,20 @@ def _DoubleDecoder():
+ """Decode serialized double to a double and new position.
+ buffer: memoryview of the serialized bytes.
+ Tuple[float, int] of the decoded double value and new position
# We expect a 64-bit value in little-endian byte order. Bit 1 is the sign
# bit, bits 2-12 represent the exponent, and bits 13-64 are the significand.
new_pos = pos + 8
- double_bytes = buffer[pos:new_pos]
+ double_bytes = buffer[pos:new_pos].tobytes()
# If this value has all its exponent bits set and at least one significand
# bit set, it's not a number. In Python 2.4, struct.unpack will treat it
@@ -355,6 +387,18 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
if is_packed:
local_DecodeVarint = _DecodeVarint
def DecodePackedField(buffer, pos, end, message, field_dict):
+ """Decode serialized packed enum to its value and a new position.
+ end: int, end position of serialized data
+ message: Message object to store unknown fields in
+ field_dict: Map[Descriptor, Any] to store decoded values in.
+ int, new position in serialized data.
value = field_dict.get(key)
if value is None:
value = field_dict.setdefault(key, new_default(message))
@@ -365,6 +409,7 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
while pos < endpoint:
value_start_pos = pos
(element, pos) = _DecodeSignedVarint32(buffer, pos)
if element in enum_type.values_by_number:
value.append(element)
@@ -372,8 +417,10 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
message._unknown_fields = []
tag_bytes = encoder.TagBytes(field_number,
wire_format.WIRETYPE_VARINT)
message._unknown_fields.append(
- (tag_bytes, buffer[value_start_pos:pos]))
+ (tag_bytes, buffer[value_start_pos:pos].tobytes()))
+ # pylint: enable=protected-access
if pos > endpoint:
del value[-1] # Discard corrupt value.
@@ -386,18 +433,32 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT)
tag_len = len(tag_bytes)
def DecodeRepeatedField(buffer, pos, end, message, field_dict):
+ """Decode serialized repeated enum to its value and a new position.
while 1:
(element, new_pos) = _DecodeSignedVarint32(buffer, pos)
if not message._unknown_fields:
- (tag_bytes, buffer[pos:new_pos]))
+ (tag_bytes, buffer[pos:new_pos].tobytes()))
# Predict that the next tag is another copy of the same repeated
# field.
pos = new_pos + tag_len
@@ -409,10 +470,23 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
return DecodeRepeatedField
def DecodeField(buffer, pos, end, message, field_dict):
(enum_value, pos) = _DecodeSignedVarint32(buffer, pos)
if pos > end:
raise _DecodeError('Truncated message.')
if enum_value in enum_type.values_by_number:
field_dict[key] = enum_value
@@ -421,7 +495,8 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
return pos
return DecodeField
@@ -458,20 +533,33 @@ BoolDecoder = _ModifiedDecoder(
wire_format.WIRETYPE_VARINT, _DecodeVarint, bool)
-def StringDecoder(field_number, is_repeated, is_packed, key, new_default):
+def StringDecoder(field_number, is_repeated, is_packed, key, new_default,
+ is_strict_utf8=False):
"""Returns a decoder for a string field."""
local_unicode = six.text_type
- def _ConvertToUnicode(byte_str):
+ def _ConvertToUnicode(memview):
+ """Convert byte to unicode."""
+ byte_str = memview.tobytes()
- return local_unicode(byte_str, 'utf-8')
+ value = local_unicode(byte_str, 'utf-8')
except UnicodeDecodeError as e:
# add more information to the error message and re-raise it.
e.reason = '%s in field: %s' % (e, key.full_name)
raise
+ if is_strict_utf8 and six.PY2:
+ if _SURROGATE_PATTERN.search(value):
+ reason = ('String field %s contains invalid UTF-8 data when parsing'
+ 'a protocol buffer: surrogates not allowed. Use'
+ 'the bytes type if you intend to send raw bytes.') % (
+ key.full_name)
+ raise message.DecodeError(reason)
+ return value
assert not is_packed
if is_repeated:
@@ -523,7 +611,7 @@ def BytesDecoder(field_number, is_repeated, is_packed, key, new_default):
new_pos = pos + size
if new_pos > end:
raise _DecodeError('Truncated string.')
- value.append(buffer[pos:new_pos])
+ value.append(buffer[pos:new_pos].tobytes())
# Predict that the next tag is another copy of the same repeated field.
if buffer[new_pos:pos] != tag_bytes or new_pos == end:
@@ -536,7 +624,7 @@ def BytesDecoder(field_number, is_repeated, is_packed, key, new_default):
- field_dict[key] = buffer[pos:new_pos]
+ field_dict[key] = buffer[pos:new_pos].tobytes()
return new_pos
@@ -665,6 +753,18 @@ def MessageSetItemDecoder(descriptor):
local_SkipField = SkipField
def DecodeItem(buffer, pos, end, message, field_dict):
+ """Decode serialized message set to its value and new position.
message_set_item_start = pos
type_id = -1
message_start = -1
@@ -695,6 +795,7 @@ def MessageSetItemDecoder(descriptor):
raise _DecodeError('MessageSet item missing message.')
extension = message.Extensions._FindExtensionByNumber(type_id)
if extension is not None:
value = field_dict.get(extension)
@@ -707,8 +808,9 @@ def MessageSetItemDecoder(descriptor):
- message._unknown_fields.append((MESSAGE_SET_ITEM_TAG,
- buffer[message_set_item_start:pos]))
+ message._unknown_fields.append(
+ (MESSAGE_SET_ITEM_TAG, buffer[message_set_item_start:pos].tobytes()))
@@ -767,7 +869,7 @@ def _SkipVarint(buffer, pos, end):
# Previously ord(buffer[pos]) raised IndexError when pos is out of range.
# With this code, ord(b'') raises TypeError. Both are handled in
# python_message.py to generate a 'Truncated message' error.
- while ord(buffer[pos:pos+1]) & 0x80:
+ while ord(buffer[pos:pos+1].tobytes()) & 0x80:
@@ -782,6 +884,13 @@ def _SkipFixed64(buffer, pos, end):
+def _DecodeFixed64(buffer, pos):
+ """Decode a fixed64."""
+ new_pos = pos + 8
+ return (struct.unpack('<Q', buffer[pos:new_pos])[0], new_pos)
def _SkipLengthDelimited(buffer, pos, end):
"""Skip a length-delimited value. Returns the new position."""
@@ -791,6 +900,7 @@ def _SkipLengthDelimited(buffer, pos, end):
def _SkipGroup(buffer, pos, end):
"""Skip sub-group. Returns the new position."""
@@ -801,11 +911,53 @@ def _SkipGroup(buffer, pos, end):
pos = new_pos
+def _DecodeGroup(buffer, pos):
+ """Decode group. Returns the UnknownFieldSet and new position."""
+ unknown_field_set = containers.UnknownFieldSet()
+ while 1:
+ (tag_bytes, pos) = ReadTag(buffer, pos)
+ (tag, _) = _DecodeVarint(tag_bytes, 0)
+ field_number, wire_type = wire_format.UnpackTag(tag)
+ if wire_type == wire_format.WIRETYPE_END_GROUP:
+ break
+ (data, pos) = _DecodeUnknownField(buffer, pos, wire_type)
+ unknown_field_set._add(field_number, wire_type, data)
+ return (unknown_field_set, pos)
+def _DecodeUnknownField(buffer, pos, wire_type):
+ """Decode a unknown field. Returns the UnknownField and new position."""
+ if wire_type == wire_format.WIRETYPE_VARINT:
+ (data, pos) = _DecodeVarint(buffer, pos)
+ elif wire_type == wire_format.WIRETYPE_FIXED64:
+ (data, pos) = _DecodeFixed64(buffer, pos)
+ elif wire_type == wire_format.WIRETYPE_FIXED32:
+ (data, pos) = _DecodeFixed32(buffer, pos)
+ elif wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED:
+ (size, pos) = _DecodeVarint(buffer, pos)
+ data = buffer[pos:pos+size]
+ pos += size
+ elif wire_type == wire_format.WIRETYPE_START_GROUP:
+ (data, pos) = _DecodeGroup(buffer, pos)
+ elif wire_type == wire_format.WIRETYPE_END_GROUP:
+ return (0, -1)
+ raise _DecodeError('Wrong wire type in tag.')
+ return (data, pos)
def _EndGroup(buffer, pos, end):
"""Skipping an END_GROUP tag returns -1 to tell the parent loop to break."""
return -1
def _SkipFixed32(buffer, pos, end):
"""Skip a fixed32 value. Returns the new position."""
@@ -814,6 +966,14 @@ def _SkipFixed32(buffer, pos, end):
+def _DecodeFixed32(buffer, pos):
+ """Decode a fixed32."""
+ new_pos = pos + 4
+ return (struct.unpack('<I', buffer[pos:new_pos])[0], new_pos)
def _RaiseInvalidWireType(buffer, pos, end):
"""Skip function for unknown wire types. Raises an exception."""
@@ -43,6 +43,7 @@ import warnings
from google.protobuf import unittest_pb2
from google.protobuf import descriptor_pb2
from google.protobuf.internal import factory_test2_pb2
+from google.protobuf.internal import no_package_pb2
from google.protobuf import descriptor_database
@@ -52,7 +53,10 @@ class DescriptorDatabaseTest(unittest.TestCase):
db = descriptor_database.DescriptorDatabase()
file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString(
factory_test2_pb2.DESCRIPTOR.serialized_pb)
+ file_desc_proto2 = descriptor_pb2.FileDescriptorProto.FromString(
+ no_package_pb2.DESCRIPTOR.serialized_pb)
db.Add(file_desc_proto)
+ db.Add(file_desc_proto2)
self.assertEqual(file_desc_proto, db.FindFileByName(
'google/protobuf/internal/factory_test2.proto'))
@@ -76,6 +80,10 @@ class DescriptorDatabaseTest(unittest.TestCase):
# Can find enum value.
self.assertEqual(file_desc_proto, db.FindFileContainingSymbol(
'google.protobuf.python.internal.Factory2Enum.FACTORY_2_VALUE_0'))
+ self.assertEqual(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.FACTORY_2_VALUE_0'))
+ self.assertEqual(file_desc_proto2, db.FindFileContainingSymbol(
+ '.NO_PACKAGE_VALUE_0'))
# Can find top level extension.
'google.protobuf.python.internal.another_field'))
@@ -36,7 +36,6 @@ __author__ = 'matthewtoia@google.com (Matt Toia)'
import copy
import os
-import sys
import warnings
@@ -55,6 +54,7 @@ from google.protobuf.internal import factory_test1_pb2
from google.protobuf.internal import file_options_test_pb2
from google.protobuf.internal import more_messages_pb2
from google.protobuf import descriptor
from google.protobuf import descriptor_pool
@@ -120,7 +120,6 @@ class DescriptorPoolTestBase(object):
self.assertIsInstance(file_desc5, descriptor.FileDescriptor)
self.assertEqual('google/protobuf/unittest.proto',
file_desc5.name)
# Tests the generated pool.
assert descriptor_pool.Default().FindFileContainingSymbol(
'google.protobuf.python.internal.Factory2Message.one_more_field')
@@ -129,6 +128,32 @@ class DescriptorPoolTestBase(object):
'protobuf_unittest.TestService')
+ # Can find field.
+ file_desc6 = self.pool.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory1Message.list_value')
+ self.assertIsInstance(file_desc6, descriptor.FileDescriptor)
+ self.assertEqual('google/protobuf/internal/factory_test1.proto',
+ file_desc6.name)
+ # Can find top level Enum value.
+ file_desc7 = self.pool.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.FACTORY_1_VALUE_0')
+ self.assertIsInstance(file_desc7, descriptor.FileDescriptor)
+ file_desc7.name)
+ # Can find nested Enum value.
+ file_desc8 = self.pool.FindFileContainingSymbol(
+ 'protobuf_unittest.TestAllTypes.FOO')
+ self.assertIsInstance(file_desc8, descriptor.FileDescriptor)
+ self.assertEqual('google/protobuf/unittest.proto',
+ file_desc8.name)
+ # TODO(jieluo): Add tests for no package when b/13860351 is fixed.
+ self.assertRaises(KeyError, self.pool.FindFileContainingSymbol,
+ 'google.protobuf.python.internal.Factory1Message.none_field')
def testFindFileContainingSymbolFailure(self):
with self.assertRaises(KeyError):
self.pool.FindFileContainingSymbol('Does not exist')
@@ -217,11 +242,10 @@ class DescriptorPoolTestBase(object):
def testFindTypeErrors(self):
self.assertRaises(TypeError, self.pool.FindExtensionByNumber, '')
+ self.assertRaises(KeyError, self.pool.FindMethodByName, '')
# TODO(jieluo): Fix python to raise correct errors.
if api_implementation.Type() == 'cpp':
- self.assertRaises(TypeError, self.pool.FindMethodByName, 0)
- self.assertRaises(KeyError, self.pool.FindMethodByName, '')
error_type = TypeError
error_type = AttributeError
@@ -231,6 +255,7 @@ class DescriptorPoolTestBase(object):
self.assertRaises(error_type, self.pool.FindEnumTypeByName, 0)
self.assertRaises(error_type, self.pool.FindOneofByName, 0)
self.assertRaises(error_type, self.pool.FindServiceByName, 0)
+ self.assertRaises(error_type, self.pool.FindMethodByName, 0)
self.assertRaises(error_type, self.pool.FindFileContainingSymbol, 0)
if api_implementation.Type() == 'python':
error_type = KeyError
@@ -275,11 +300,6 @@ class DescriptorPoolTestBase(object):
self.pool.FindEnumTypeByName('Does not exist')
def testFindFieldByName(self):
- if isinstance(self, SecondaryDescriptorFromDescriptorDB):
- if api_implementation.Type() == 'cpp':
- # TODO(jieluo): Fix cpp extension to find field correctly
- # when descriptor pool is using an underlying database.
- return
field = self.pool.FindFieldByName(
'google.protobuf.python.internal.Factory1Message.list_value')
self.assertEqual(field.name, 'list_value')
@@ -290,11 +310,6 @@ class DescriptorPoolTestBase(object):
self.pool.FindFieldByName('Does not exist')
def testFindOneofByName(self):
- # TODO(jieluo): Fix cpp extension to find oneof correctly
oneof = self.pool.FindOneofByName(
'google.protobuf.python.internal.Factory2Message.oneof_field')
self.assertEqual(oneof.name, 'oneof_field')
@@ -302,11 +317,6 @@ class DescriptorPoolTestBase(object):
self.pool.FindOneofByName('Does not exist')
def testFindExtensionByName(self):
- # TODO(jieluo): Fix cpp extension to find extension correctly
# An extension defined in a message.
extension = self.pool.FindExtensionByName(
@@ -382,6 +392,11 @@ class DescriptorPoolTestBase(object):
self.pool.FindServiceByName('Does not exist')
+ method = self.pool.FindMethodByName('protobuf_unittest.TestService.Foo')
+ self.assertIs(method.containing_service, service)
+ with self.assertRaises(KeyError):
+ self.pool.FindMethodByName('protobuf_unittest.TestService.Doesnotexist')
def testUserDefinedDB(self):
self.pool = descriptor_pool.DescriptorPool(db)
@@ -601,6 +616,8 @@ class CreateDescriptorPoolTest(DescriptorPoolTestBase, unittest.TestCase):
unittest_import_pb2.DESCRIPTOR.serialized_pb))
self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString(
unittest_pb2.DESCRIPTOR.serialized_pb))
+ self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString(
+ no_package_pb2.DESCRIPTOR.serialized_pb))
class SecondaryDescriptorFromDescriptorDB(DescriptorPoolTestBase,
@@ -620,6 +637,8 @@ class SecondaryDescriptorFromDescriptorDB(DescriptorPoolTestBase,
db.Add(descriptor_pb2.FileDescriptorProto.FromString(
+ db.Add(descriptor_pb2.FileDescriptorProto.FromString(
self.pool = descriptor_pool.DescriptorPool(descriptor_db=db)
@@ -746,11 +765,7 @@ class MessageField(object):
test.assertEqual(msg_desc, field_desc.containing_type)
test.assertEqual(field_type_desc, field_desc.message_type)
test.assertEqual(file_desc, field_desc.file)
- # TODO(jieluo): Fix python and cpp extension diff for message field
- # default value.
- test.assertRaises(
- NotImplementedError, getattr, field_desc, 'default_value')
+ test.assertEqual(field_desc.default_value, None)
class StringField(object):
@@ -452,6 +452,17 @@ class DescriptorTest(unittest.TestCase):
self.assertEqual('attribute is not writable: has_options',
str(e.exception))
+ def testDefault(self):
+ message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ field = message_descriptor.fields_by_name['repeated_int32']
+ self.assertEqual(field.default_value, [])
+ field = message_descriptor.fields_by_name['repeated_nested_message']
+ field = message_descriptor.fields_by_name['optionalgroup']
+ self.assertEqual(field.default_value, None)
+ field = message_descriptor.fields_by_name['optional_nested_message']
class NewDescriptorTest(DescriptorTest):
"""Redo the same tests as above, but with a separate DescriptorPool."""
@@ -56,3 +56,17 @@ message Factory1Message {
extensions 1000 to max;
+message Factory1MethodRequest {
+ optional string argument = 1;
+message Factory1MethodResponse {
+ optional string result = 1;
+service Factory1Service {
+ // Dummy method for this dummy service.
+ rpc Factory1Method(Factory1MethodRequest) returns (Factory1MethodResponse) {
@@ -142,10 +142,8 @@ class MessageFactoryTest(unittest.TestCase):
self.assertEqual('test2', msg1.Extensions[ext2])
self.assertEqual(None,
msg1.Extensions._FindExtensionByNumber(12321))
+ self.assertRaises(TypeError, len, msg1.Extensions)
- # TODO(jieluo): Fix len to return the correct value.
- # self.assertEqual(2, len(msg1.Extensions))
- self.assertEqual(len(msg1.Extensions), len(msg1.Extensions))
self.assertRaises(TypeError,
msg1.Extensions._FindExtensionByName, 0)
@@ -1,4 +1,5 @@
#! /usr/bin/env python
+# -*- coding: utf-8 -*-
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
@@ -49,6 +50,7 @@ import copy
import math
import operator
import pickle
+import pydoc
import six
import sys
@@ -72,6 +74,7 @@ from google.protobuf import message_factory
from google.protobuf import text_format
from google.protobuf.internal import api_implementation
+from google.protobuf.internal import more_extensions_pb2
from google.protobuf.internal import packed_field_test_pb2
from google.protobuf.internal import test_util
from google.protobuf.internal import testing_refleaks
@@ -415,6 +418,37 @@ class MessageTest(BaseTestCase):
empty.ParseFromString(populated.SerializeToString())
self.assertEqual(str(empty), '')
+ def testMergeFromRepeatedField(self, message_module):
+ msg = message_module.TestAllTypes()
+ msg.repeated_int32.append(1)
+ msg.repeated_int32.append(3)
+ msg.repeated_nested_message.add(bb=1)
+ msg.repeated_nested_message.add(bb=2)
+ other_msg = message_module.TestAllTypes()
+ other_msg.repeated_nested_message.add(bb=3)
+ other_msg.repeated_nested_message.add(bb=4)
+ other_msg.repeated_int32.append(5)
+ other_msg.repeated_int32.append(7)
+ msg.repeated_int32.MergeFrom(other_msg.repeated_int32)
+ self.assertEqual(4, len(msg.repeated_int32))
+ msg.repeated_nested_message.MergeFrom(other_msg.repeated_nested_message)
+ self.assertEqual([1, 2, 3, 4],
+ [m.bb for m in msg.repeated_nested_message])
+ def testAddWrongRepeatedNestedField(self, message_module):
+ msg.repeated_nested_message.add('wrong')
+ except TypeError:
+ msg.repeated_nested_message.add(value_field='wrong')
+ except ValueError:
+ self.assertEqual(len(msg.repeated_nested_message), 0)
def testRepeatedNestedFieldIteration(self, message_module):
msg = message_module.TestAllTypes()
msg.repeated_nested_message.add(bb=1)
@@ -645,6 +679,82 @@ class MessageTest(BaseTestCase):
m.payload.repeated_int32.extend([])
self.assertTrue(m.HasField('payload'))
+ def testMergeFrom(self, message_module):
+ m1 = message_module.TestAllTypes()
+ m2 = message_module.TestAllTypes()
+ # Cpp extension will lazily create a sub message which is immutable.
+ self.assertEqual(0, m1.optional_nested_message.bb)
+ m2.optional_nested_message.bb = 1
+ # Make sure cmessage pointing to a mutable message after merge instead of
+ # the lazily created message.
+ m1.MergeFrom(m2)
+ self.assertEqual(1, m1.optional_nested_message.bb)
+ # Test more nested sub message.
+ msg1 = message_module.NestedTestAllTypes()
+ msg2 = message_module.NestedTestAllTypes()
+ self.assertEqual(0, msg1.child.payload.optional_nested_message.bb)
+ msg2.child.payload.optional_nested_message.bb = 1
+ msg1.MergeFrom(msg2)
+ self.assertEqual(1, msg1.child.payload.optional_nested_message.bb)
+ # Test repeated field.
+ self.assertEqual(msg1.payload.repeated_nested_message,
+ msg1.payload.repeated_nested_message)
+ msg2.payload.repeated_nested_message.add().bb = 1
+ self.assertEqual(1, len(msg1.payload.repeated_nested_message))
+ self.assertEqual(1, msg1.payload.repeated_nested_message[0].bb)
+ def testMergeFromString(self, message_module):
+ m1.MergeFromString(m2.SerializeToString())
+ @unittest.skipIf(six.PY2, 'memoryview objects are not supported on py2')
+ def testMergeFromStringUsingMemoryViewWorksInPy3(self, message_module):
+ m2.optional_string = 'scalar string'
+ m2.repeated_string.append('repeated string')
+ m2.optional_bytes = b'scalar bytes'
+ m2.repeated_bytes.append(b'repeated bytes')
+ serialized = m2.SerializeToString()
+ memview = memoryview(serialized)
+ m1 = message_module.TestAllTypes.FromString(memview)
+ self.assertEqual(m1.optional_bytes, b'scalar bytes')
+ self.assertEqual(m1.repeated_bytes, [b'repeated bytes'])
+ self.assertEqual(m1.optional_string, 'scalar string')
+ self.assertEqual(m1.repeated_string, ['repeated string'])
+ # Make sure that the memoryview was correctly converted to bytes, and
+ # that a sub-sliced memoryview is not being used.
+ self.assertIsInstance(m1.optional_bytes, bytes)
+ self.assertIsInstance(m1.repeated_bytes[0], bytes)
+ self.assertIsInstance(m1.optional_string, six.text_type)
+ self.assertIsInstance(m1.repeated_string[0], six.text_type)
+ @unittest.skipIf(six.PY3, 'memoryview is supported by py3')
+ def testMergeFromStringUsingMemoryViewIsPy2Error(self, message_module):
+ memview = memoryview(b'')
+ with self.assertRaises(TypeError):
+ message_module.TestAllTypes.FromString(memview)
+ def testMergeFromEmpty(self, message_module):
+ self.assertFalse(m1.HasField('optional_nested_message'))
+ # Make sure the sub message is still immutable after merge from empty.
+ m1.MergeFromString(b'') # field state should not change
def ensureNestedMessageExists(self, msg, attribute):
"""Make sure that a nested message object exists.
@@ -1067,14 +1177,8 @@ class MessageTest(BaseTestCase):
with self.assertRaises(AttributeError):
m.repeated_int32 = []
m.repeated_int32.append(1)
- # For test coverage: cpp has a different path if composite
- # field is in cache
- with self.assertRaises(TypeError):
- m.repeated_int32 = []
- else:
- with self.assertRaises(AttributeError):
+ with self.assertRaises(AttributeError):
+ m.repeated_int32 = []
# Class to test proto2-only features (required, extensions, etc.)
@@ -1169,6 +1273,21 @@ class Proto2Test(BaseTestCase):
msg = unittest_pb2.TestAllTypes()
self.assertRaises(AttributeError, getattr, msg, 'Extensions')
+ def testMergeFromExtensions(self):
+ msg1 = more_extensions_pb2.TopLevelMessage()
+ msg2 = more_extensions_pb2.TopLevelMessage()
+ self.assertEqual(0, msg1.submessage.Extensions[
+ more_extensions_pb2.optional_int_extension])
+ self.assertFalse(msg1.HasField('submessage'))
+ msg2.submessage.Extensions[
+ more_extensions_pb2.optional_int_extension] = 123
+ # Make sure cmessage and extensions pointing to a mutable message
+ # after merge instead of the lazily created message.
+ self.assertEqual(123, msg1.submessage.Extensions[
def testGoldenExtensions(self):
golden_data = test_util.GoldenFileData('golden_message')
golden_message = unittest_pb2.TestAllExtensions()
@@ -1316,6 +1435,15 @@ class Proto2Test(BaseTestCase):
unittest_pb2.TestAllTypes(repeated_nested_enum='FOO')
+ def test_documentation(self):
+ # Also used by the interactive help() function.
+ doc = pydoc.html.document(unittest_pb2.TestAllTypes, 'message')
+ self.assertIn('class TestAllTypes', doc)
+ self.assertIn('SerializePartialToString', doc)
+ self.assertIn('repeated_float', doc)
+ base = unittest_pb2.TestAllTypes.__bases__[0]
+ self.assertRaises(AttributeError, getattr, base, '_extensions_by_name')
# Class to test proto3-only features/behavior (updated field presence & enums)
class Proto3Test(BaseTestCase):
@@ -1539,10 +1667,8 @@ class Proto3Test(BaseTestCase):
self.assertEqual(True, msg2.map_bool_bool[True])
self.assertEqual(2, msg2.map_int32_enum[888])
self.assertEqual(456, msg2.map_int32_enum[123])
- # TODO(jieluo): Add cpp extension support.
- if api_implementation.Type() == 'python':
- self.assertEqual('{-123: -456}',
- str(msg2.map_int32_int32))
+ self.assertEqual('{-123: -456}',
+ str(msg2.map_int32_int32))
def testMapEntryAlwaysSerialized(self):
msg = map_unittest_pb2.TestMap()
@@ -1603,11 +1729,10 @@ class Proto3Test(BaseTestCase):
self.assertIn(123, msg2.map_int32_foreign_message)
self.assertIn(-456, msg2.map_int32_foreign_message)
self.assertEqual(2, len(msg2.map_int32_foreign_message))
+ msg2.map_int32_foreign_message[123].c = 1
# TODO(jieluo): Fix text format for message map.
- self.assertEqual(15,
- len(str(msg2.map_int32_foreign_message)))
+ self.assertIn(str(msg2.map_int32_foreign_message),
+ ('{-456: , 123: c: 1\n}', '{123: c: 1\n, -456: }'))
def testNestedMessageMapItemDelete(self):
@@ -1721,6 +1846,15 @@ class Proto3Test(BaseTestCase):
self.assertEqual(10, msg2.map_int32_foreign_message[222].c)
self.assertFalse(msg2.map_int32_foreign_message[222].HasField('d'))
+ # Test when cpp extension cache a map.
+ m1 = map_unittest_pb2.TestMap()
+ m2 = map_unittest_pb2.TestMap()
+ self.assertEqual(m1.map_int32_foreign_message,
+ m1.map_int32_foreign_message)
+ m2.map_int32_foreign_message[123].c = 10
+ self.assertEqual(10, m2.map_int32_foreign_message[123].c)
def testMergeFromBadType(self):
with self.assertRaisesRegexp(
@@ -1972,7 +2106,7 @@ class Proto3Test(BaseTestCase):
def testMapValidAfterFieldCleared(self):
# Map needs to work even if field is cleared.
# For the C++ implementation this tests the correctness of
- # ScalarMapContainer::Release()
+ # MapContainer::Release()
int32_map = msg.map_int32_int32
@@ -1988,7 +2122,7 @@ class Proto3Test(BaseTestCase):
def testMessageMapValidAfterFieldCleared(self):
int32_foreign_message = msg.map_int32_foreign_message
@@ -1998,6 +2132,24 @@ class Proto3Test(BaseTestCase):
self.assertEqual(b'', msg.SerializeToString())
self.assertTrue(2 in int32_foreign_message.keys())
+ def testMessageMapItemValidAfterTopMessageCleared(self):
+ # Message map item needs to work even if it is cleared.
+ # For the C++ implementation this tests the correctness of
+ msg = map_unittest_pb2.TestMap()
+ msg.map_int32_all_types[2].optional_string = 'bar'
+ if api_implementation.Type() == 'cpp':
+ # Need to keep the map reference because of b/27942626.
+ # TODO(jieluo): Remove it.
+ unused_map = msg.map_int32_all_types # pylint: disable=unused-variable
+ msg_value = msg.map_int32_all_types[2]
+ msg.Clear()
+ # Reset to trigger sync between repeated field and map in c++.
+ msg.map_int32_all_types[3].optional_string = 'foo'
+ self.assertEqual(msg_value.optional_string, 'bar')
def testMapIterInvalidatedByClearField(self):
# Map iterator is invalidated when field is cleared.
# But this case does need to not crash the interpreter.
@@ -2058,6 +2210,80 @@ class Proto3Test(BaseTestCase):
msg.map_string_foreign_message['foo'].c = 5
self.assertEqual(0, len(msg.FindInitializationErrors()))
+ def testStrictUtf8Check(self):
+ # Test u'\ud801' is rejected at parser in both python2 and python3.
+ serialized = (b'r\x03\xed\xa0\x81')
+ msg = unittest_proto3_arena_pb2.TestAllTypes()
+ with self.assertRaises(Exception) as context:
+ msg.MergeFromString(serialized)
+ if api_implementation.Type() == 'python':
+ self.assertIn('optional_string', str(context.exception))
+ self.assertIn('Error parsing message', str(context.exception))
+ # Test optional_string=u'😍' is accepted.
+ serialized = unittest_proto3_arena_pb2.TestAllTypes(
+ optional_string=u'😍').SerializeToString()
+ msg2 = unittest_proto3_arena_pb2.TestAllTypes()
+ msg2.MergeFromString(serialized)
+ self.assertEqual(msg2.optional_string, u'😍')
+ msg = unittest_proto3_arena_pb2.TestAllTypes(
+ optional_string=u'\ud001')
+ self.assertEqual(msg.optional_string, u'\ud001')
+ @unittest.skipIf(six.PY2, 'Surrogates are acceptable in python2')
+ def testSurrogatesInPython3(self):
+ # Surrogates like U+D83D is an invalid unicode character, it is
+ # supported by Python2 only because in some builds, unicode strings
+ # use 2-bytes code units. Since Python 3.3, we don't have this problem.
+ #
+ # Surrogates are utf16 code units, in a unicode string they are invalid
+ # characters even when they appear in pairs like u'\ud801\udc01'. Protobuf
+ # Python3 reject such cases at setters and parsers. Python2 accpect it
+ # to keep same features with the language itself. 'Unpaired pairs'
+ # like u'\ud801' are rejected at parsers when strict utf8 check is enabled
+ # in proto3 to keep same behavior with c extension.
+ # Surrogates are rejected at setters in Python3.
+ with self.assertRaises(ValueError):
+ unittest_proto3_arena_pb2.TestAllTypes(
+ optional_string=u'\ud801\udc01')
+ optional_string=b'\xed\xa0\x81')
+ optional_string=u'\ud801')
+ optional_string=u'\ud801\ud801')
+ @unittest.skipIf(six.PY3, 'Surrogates are rejected at setters in Python3')
+ def testSurrogatesInPython2(self):
+ # Test optional_string=u'\ud801\udc01'.
+ # surrogate pair is acceptable in python2.
+ # TODO(jieluo): Change pure python to have same behavior with c extension.
+ # Some build in python2 consider u'\ud801\udc01' and u'\U00010401' are
+ # equal, some are not equal.
+ self.assertEqual(msg.optional_string, u'\ud801\udc01')
+ self.assertEqual(msg.optional_string, u'\U00010401')
+ serialized = msg.SerializeToString()
+ self.assertEqual(msg2.optional_string, u'\U00010401')
+ # Python2 does not reject surrogates at setters.
class ValidTypeNamesTest(BaseTestCase):
@@ -1,3 +1,33 @@
syntax = "proto2";
enum NoPackageEnum {
@@ -56,6 +56,7 @@ import sys
import weakref
+from six.moves import range
# We use "as" to avoid name collisions with variables.
@@ -124,6 +125,21 @@ class GeneratedProtocolMessageType(type):
Newly-allocated class.
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
+ # If a concrete class already exists for this descriptor, don't try to
+ # create another. Doing so will break any messages that already exist with
+ # the existing class.
+ # The C++ implementation appears to have its own internal `PyMessageFactory`
+ # to achieve similar results.
+ # This most commonly happens in `text_format.py` when using descriptors from
+ # a custom pool; it calls symbol_database.Global().getPrototype() on a
+ # descriptor which already has an existing concrete class.
+ new_class = getattr(descriptor, '_concrete_class', None)
+ if new_class:
+ return new_class
if descriptor.full_name in well_known_types.WKTBASES:
bases += (well_known_types.WKTBASES[descriptor.full_name],)
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
@@ -151,6 +167,16 @@ class GeneratedProtocolMessageType(type):
type.
+ # If this is an _existing_ class looked up via `_concrete_class` in the
+ # __new__ method above, then we don't need to re-initialize anything.
+ existing_class = getattr(descriptor, '_concrete_class', None)
+ if existing_class:
+ assert existing_class is cls, (
+ 'Duplicate `GeneratedProtocolMessageType` created for descriptor %r'
+ % (descriptor.full_name))
cls._decoders_by_tag = {}
if (descriptor.has_options and
descriptor.GetOptions().message_set_wire_format):
@@ -245,6 +271,7 @@ def _AddSlots(message_descriptor, dictionary):
'_cached_byte_size_dirty',
'_fields',
'_unknown_fields',
+ '_unknown_field_set',
'_is_present_in_parent',
'_listener',
'_listener_for_children',
@@ -271,6 +298,13 @@ def _IsMessageMapField(field):
return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE
+def _IsStrictUtf8Check(field):
+ if field.containing_type.syntax != 'proto3':
+ return False
+ enforce_utf8 = True
+ return enforce_utf8
def _AttachFieldHelpers(cls, field_descriptor):
is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED)
is_packable = (is_repeated and
@@ -322,10 +356,16 @@ def _AttachFieldHelpers(cls, field_descriptor):
field_decoder = decoder.MapDecoder(
field_descriptor, _GetInitializeDefaultForMap(field_descriptor),
is_message_map)
+ elif decode_type == _FieldDescriptor.TYPE_STRING:
+ is_strict_utf8_check = _IsStrictUtf8Check(field_descriptor)
+ field_decoder = decoder.StringDecoder(
+ field_descriptor.number, is_repeated, is_packed,
+ field_descriptor, field_descriptor._default_constructor,
+ is_strict_utf8_check)
field_decoder = type_checkers.TYPE_TO_DECODER[decode_type](
- field_descriptor.number, is_repeated, is_packed,
- field_descriptor, field_descriptor._default_constructor)
+ field_descriptor, field_descriptor._default_constructor)
cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor)
@@ -422,6 +462,9 @@ def _DefaultValueConstructorForField(field):
# _concrete_class may not yet be initialized.
message_type = field.message_type
def MakeSubMessageDefault(message):
+ assert getattr(message_type, '_concrete_class', None), (
+ 'Uninitialized concrete class found for field %r (message type %r)'
+ % (field.full_name, message_type.full_name))
result = message_type._concrete_class()
result._SetListener(
_OneofListener(message, field)
@@ -477,6 +520,9 @@ def _AddInitMethod(message_descriptor, cls):
# _unknown_fields is () when empty for efficiency, and will be turned into
# a list if fields are added.
self._unknown_fields = ()
+ # _unknown_field_set is None when empty for efficiency, and will be
+ # turned into UnknownFieldSet struct if fields are added.
+ self._unknown_field_set = None # pylint: disable=protected-access
self._is_present_in_parent = False
self._listener = message_listener_mod.NullMessageListener()
self._listener_for_children = _Listener(self)
@@ -584,6 +630,14 @@ def _AddPropertiesForField(field, cls):
_AddPropertiesForNonRepeatedScalarField(field, cls)
+class _FieldProperty(property):
+ __slots__ = ('DESCRIPTOR',)
+ def __init__(self, descriptor, getter, setter, doc):
+ property.__init__(self, getter, setter, doc=doc)
+ self.DESCRIPTOR = descriptor
def _AddPropertiesForRepeatedField(field, cls):
"""Adds a public property for a "repeated" protocol message field. Clients
can use this property to get the value of the field, which will be either a
@@ -625,7 +679,7 @@ def _AddPropertiesForRepeatedField(field, cls):
'"%s" in protocol message object.' % proto_field_name)
doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name
- setattr(cls, property_name, property(getter, setter, doc=doc))
+ setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc))
def _AddPropertiesForNonRepeatedScalarField(field, cls):
@@ -681,7 +735,7 @@ def _AddPropertiesForNonRepeatedScalarField(field, cls):
# Add a property to encapsulate the getter/setter.
def _AddPropertiesForNonRepeatedCompositeField(field, cls):
@@ -725,7 +779,7 @@ def _AddPropertiesForNonRepeatedCompositeField(field, cls):
# Add a property to encapsulate the getter.
def _AddPropertiesForExtensions(descriptor, cls):
@@ -949,13 +1003,8 @@ def _AddEqualsMethod(message_descriptor, cls):
if not self.ListFields() == other.ListFields():
return False
- # Sort unknown fields because their order shouldn't affect equality test.
- unknown_fields = list(self._unknown_fields)
- unknown_fields.sort()
- other_unknown_fields = list(other._unknown_fields)
- other_unknown_fields.sort()
- return unknown_fields == other_unknown_fields
+ return self._unknown_field_set == other._unknown_field_set
cls.__eq__ = __eq__
@@ -1078,6 +1127,13 @@ def _AddSerializePartialToStringMethod(message_descriptor, cls):
def _AddMergeFromStringMethod(message_descriptor, cls):
"""Helper for _AddMessageMethods()."""
def MergeFromString(self, serialized):
+ if isinstance(serialized, memoryview) and six.PY2:
+ raise TypeError(
+ 'memoryview not supported in Python 2 with the pure Python proto '
+ 'implementation: this is to maintain compatibility with the C++ '
+ 'implementation')
+ serialized = memoryview(serialized)
length = len(serialized)
if self._InternalParse(serialized, 0, length) != length:
@@ -1095,26 +1151,54 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
local_ReadTag = decoder.ReadTag
local_SkipField = decoder.SkipField
decoders_by_tag = cls._decoders_by_tag
- is_proto3 = message_descriptor.syntax == "proto3"
def InternalParse(self, buffer, pos, end):
+ """Create a message from serialized bytes.
+ self: Message, instance of the proto message object.
+ buffer: memoryview of the serialized data.
+ pos: int, position to start in the serialized data.
+ end: int, end position of the serialized data.
+ Message object.
+ # Guard against internal misuse, since this function is called internally
+ # quite extensively, and its easy to accidentally pass bytes.
+ assert isinstance(buffer, memoryview)
self._Modified()
field_dict = self._fields
- unknown_field_list = self._unknown_fields
+ unknown_field_set = self._unknown_field_set
while pos != end:
(tag_bytes, new_pos) = local_ReadTag(buffer, pos)
field_decoder, field_desc = decoders_by_tag.get(tag_bytes, (None, None))
if field_decoder is None:
- value_start_pos = new_pos
- new_pos = local_SkipField(buffer, new_pos, end, tag_bytes)
+ if not self._unknown_fields: # pylint: disable=protected-access
+ self._unknown_fields = [] # pylint: disable=protected-access
+ if unknown_field_set is None:
+ self._unknown_field_set = containers.UnknownFieldSet()
+ (tag, _) = decoder._DecodeVarint(tag_bytes, 0)
+ # TODO(jieluo): remove old_pos.
+ old_pos = new_pos
+ (data, new_pos) = decoder._DecodeUnknownField(
+ buffer, new_pos, wire_type) # pylint: disable=protected-access
if new_pos == -1:
- if (not is_proto3 or
- api_implementation.GetPythonProto3PreserveUnknownsDefault()):
- if not unknown_field_list:
- unknown_field_list = self._unknown_fields = []
- unknown_field_list.append(
- (tag_bytes, buffer[value_start_pos:new_pos]))
+ # TODO(jieluo): remove _unknown_fields.
+ new_pos = local_SkipField(buffer, old_pos, end, tag_bytes)
+ if new_pos == -1:
+ return pos
+ self._unknown_fields.append(
+ (tag_bytes, buffer[old_pos:new_pos].tobytes()))
pos = field_decoder(buffer, new_pos, end, self, field_dict)
@@ -1259,6 +1343,10 @@ def _AddMergeFromMethod(cls):
if not self._unknown_fields:
self._unknown_fields = []
self._unknown_fields.extend(msg._unknown_fields)
+ if self._unknown_field_set is None:
+ self._unknown_field_set._extend(msg._unknown_field_set)
cls.MergeFrom = MergeFrom
@@ -1291,12 +1379,25 @@ def _Clear(self):
# Clear fields.
self._fields = {}
+ if self._unknown_field_set is not None:
+ self._unknown_field_set._clear()
+ self._unknown_field_set = None
self._oneofs = {}
+def _UnknownFields(self):
+ if self._unknown_field_set is None: # pylint: disable=protected-access
+ return self._unknown_field_set # pylint: disable=protected-access
def _DiscardUnknownFields(self):
for field, value in self.ListFields():
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
if field.label == _FieldDescriptor.LABEL_REPEATED:
@@ -1335,6 +1436,7 @@ def _AddMessageMethods(message_descriptor, cls):
_AddReduceMethod(cls)
# Adds methods which do not depend on cls.
cls.Clear = _Clear
+ cls.UnknownFields = _UnknownFields
cls.DiscardUnknownFields = _DiscardUnknownFields
cls._SetListener = _SetListener
@@ -1471,6 +1573,10 @@ class _ExtensionDict(object):
if extension_handle.label == _FieldDescriptor.LABEL_REPEATED:
result = extension_handle._default_constructor(self._extended_message)
elif extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
+ assert getattr(extension_handle.message_type, '_concrete_class', None), (
+ % (extension_handle.full_name,
+ extension_handle.message_type.full_name))
result = extension_handle.message_type._concrete_class()
result._SetListener(self._extended_message._listener_for_children)
@@ -64,6 +64,10 @@ from google.protobuf.internal import testing_refleaks
from google.protobuf.internal import decoder
+if six.PY3:
+ long = int # pylint: disable=redefined-builtin,invalid-name
BaseTestCase = testing_refleaks.BaseTestCase
@@ -647,10 +651,7 @@ class ReflectionTest(BaseTestCase):
TestGetAndDeserialize('optional_int32', 1, int)
TestGetAndDeserialize('optional_int32', 1 << 30, int)
TestGetAndDeserialize('optional_uint32', 1 << 30, int)
- integer_64 = long
- except NameError: # Python3
- integer_64 = int
+ integer_64 = long
if struct.calcsize('L') == 4:
# Python only has signed ints, so 32-bit python can't fit an uint32
# in an int.
@@ -1103,6 +1104,7 @@ class ReflectionTest(BaseTestCase):
self.assertEqual(23, myproto_instance.foo_field)
self.assertTrue(myproto_instance.HasField('foo_field'))
+ @testing_refleaks.SkipReferenceLeakChecker('MakeDescriptor is not repeatable')
def testDescriptorProtoSupport(self):
# Hand written descriptors/reflection are only supported by the pure-Python
# implementation of the API.
@@ -1141,7 +1143,8 @@ class ReflectionTest(BaseTestCase):
self.assertTrue('price' in desc.fields_by_name)
self.assertTrue('owners' in desc.fields_by_name)
- class CarMessage(six.with_metaclass(reflection.GeneratedProtocolMessageType, message.Message)):
+ class CarMessage(six.with_metaclass(reflection.GeneratedProtocolMessageType,
+ message.Message)):
DESCRIPTOR = desc
prius = CarMessage()
@@ -2435,7 +2438,7 @@ class SerializationTest(BaseTestCase):
first_proto = unittest_pb2.TestAllTypes()
test_util.SetAllFields(first_proto)
- serialized = first_proto.SerializeToString()
+ serialized = memoryview(first_proto.SerializeToString())
for truncation_point in range(len(serialized) + 1):
@@ -2857,6 +2860,38 @@ class SerializationTest(BaseTestCase):
self.assertEqual(unittest_pb2.REPEATED_NESTED_ENUM_EXTENSION_FIELD_NUMBER,
51)
+ def testFieldProperties(self):
+ cls = unittest_pb2.TestAllTypes
+ self.assertIs(cls.optional_int32.DESCRIPTOR,
+ cls.DESCRIPTOR.fields_by_name['optional_int32'])
+ self.assertEqual(cls.OPTIONAL_INT32_FIELD_NUMBER,
+ cls.optional_int32.DESCRIPTOR.number)
+ self.assertIs(cls.optional_nested_message.DESCRIPTOR,
+ cls.DESCRIPTOR.fields_by_name['optional_nested_message'])
+ self.assertEqual(cls.OPTIONAL_NESTED_MESSAGE_FIELD_NUMBER,
+ cls.optional_nested_message.DESCRIPTOR.number)
+ self.assertIs(cls.repeated_int32.DESCRIPTOR,
+ cls.DESCRIPTOR.fields_by_name['repeated_int32'])
+ self.assertEqual(cls.REPEATED_INT32_FIELD_NUMBER,
+ cls.repeated_int32.DESCRIPTOR.number)
+ def testFieldDataDescriptor(self):
+ msg = unittest_pb2.TestAllTypes()
+ msg.optional_int32 = 42
+ self.assertEqual(unittest_pb2.TestAllTypes.optional_int32.__get__(msg), 42)
+ unittest_pb2.TestAllTypes.optional_int32.__set__(msg, 25)
+ self.assertEqual(msg.optional_int32, 25)
+ del msg.optional_int32
+ unittest_pb2.ForeignMessage.c.__get__(msg)
+ pass # The cpp implementation cannot mix fields from other messages.
+ # This test exercises a specific check that avoids a crash.
+ pass # The python implementation allows fields from other messages.
+ # This is useless, but works.
def testInitKwargs(self):
proto = unittest_pb2.TestAllTypes(
optional_int32=1,
@@ -2963,6 +2998,7 @@ class ClassAPITest(BaseTestCase):
@unittest.skipIf(
api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
'C++ implementation requires a call to MakeDescriptor()')
+ @testing_refleaks.SkipReferenceLeakChecker('MakeClass is not repeatable')
def testMakeClassWithNestedDescriptor(self):
leaf_desc = descriptor.Descriptor('leaf', 'package.parent.child.leaf', '',
containing_type=None, fields=[],
@@ -2980,10 +3016,7 @@ class ClassAPITest(BaseTestCase):
nested_types=[child_desc, sibling_desc],
enum_types=[], extensions=[])
- message_class = reflection.MakeClass(parent_desc)
- self.assertIn('child', message_class.__dict__)
- self.assertIn('sibling', message_class.__dict__)
- self.assertIn('leaf', message_class.child.__dict__)
+ reflection.MakeClass(parent_desc)
def _GetSerializedFileDescriptor(self, name):
"""Get a serialized representation of a test FileDescriptorProto.
@@ -33,20 +33,19 @@
"""Test for google.protobuf.text_format."""
-__author__ = 'kenton@google.com (Kenton Varda)'
+import io
import re
-import six
import string
+import textwrap
+import six
+# pylint: disable=g-import-not-at-top
- import unittest2 as unittest # PY26, pylint: disable=g-import-not-at-top
+ import unittest2 as unittest # PY26
except ImportError:
- import unittest # pylint: disable=g-import-not-at-top
-from google.protobuf.internal import _parameterized
+ import unittest
from google.protobuf import any_pb2
from google.protobuf import any_test_pb2
@@ -54,12 +53,13 @@ from google.protobuf import map_unittest_pb2
from google.protobuf import unittest_mset_pb2
from google.protobuf import unittest_proto3_arena_pb2
-from google.protobuf.internal import api_implementation
from google.protobuf.internal import any_test_pb2 as test_extend_any
from google.protobuf.internal import message_set_extensions_pb2
+from google.protobuf.internal import _parameterized
+# pylint: enable=g-import-not-at-top
# Low-level nuts-n-bolts tests.
@@ -100,8 +100,8 @@ class TextFormatBase(unittest.TestCase):
return text
-@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2))
-class TextFormatTest(TextFormatBase):
+@_parameterized.parameters(unittest_pb2, unittest_proto3_arena_pb2)
+class TextFormatMessageToStringTests(TextFormatBase):
def testPrintExotic(self, message_module):
message = message_module.TestAllTypes()
@@ -154,6 +154,40 @@ class TextFormatTest(TextFormatBase):
'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
'repeated_string: "Google" repeated_string: "Zurich"')
+ def VerifyPrintShortFormatRepeatedFields(self, message_module, as_one_line):
+ message = message_module.TestAllTypes()
+ message.repeated_int32.append(1)
+ message.repeated_string.append('Google')
+ message.repeated_string.append('Hello,World')
+ message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_FOO)
+ message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR)
+ message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ)
+ message.optional_nested_message.bb = 3
+ for i in (21, 32):
+ msg = message.repeated_nested_message.add()
+ msg.bb = i
+ expected_ascii = (
+ 'optional_nested_message {\n bb: 3\n}\n'
+ 'repeated_int32: [1]\n'
+ 'repeated_string: "Google"\n'
+ 'repeated_string: "Hello,World"\n'
+ 'repeated_nested_message {\n bb: 21\n}\n'
+ 'repeated_nested_message {\n bb: 32\n}\n'
+ 'repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ]\n')
+ if as_one_line:
+ expected_ascii = expected_ascii.replace('\n ', '').replace('\n', '')
+ actual_ascii = text_format.MessageToString(
+ message, use_short_repeated_primitives=True,
+ as_one_line=as_one_line)
+ self.CompareToGoldenText(actual_ascii, expected_ascii)
+ parsed_message = message_module.TestAllTypes()
+ text_format.Parse(actual_ascii, parsed_message)
+ self.assertEqual(parsed_message, message)
+ def tesPrintShortFormatRepeatedFields(self, message_module, as_one_line):
+ self.VerifyPrintShortFormatRepeatedFields(message_module, False)
+ self.VerifyPrintShortFormatRepeatedFields(message_module, True)
def testPrintNestedNewLineInStringAsOneLine(self, message_module):
message.optional_string = 'a\nnew\nline'
@@ -213,13 +247,18 @@ class TextFormatTest(TextFormatBase):
def testPrintRawUtf8String(self, message_module):
- message.repeated_string.append(u'\u00fc\ua71f')
+ message.repeated_string.append(u'\u00fc\t\ua71f')
text = text_format.MessageToString(message, as_utf8=True)
- self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
+ golden_unicode = u'repeated_string: "\u00fc\\t\ua71f"\n'
+ golden_text = golden_unicode if six.PY3 else golden_unicode.encode('utf-8')
+ # MessageToString always returns a native str.
+ self.CompareToGoldenText(text, golden_text)
parsed_message = message_module.TestAllTypes()
text_format.Parse(text, parsed_message)
- self.assertEqual(message, parsed_message,
- '\n%s != %s' % (message, parsed_message))
+ self.assertEqual(
+ message, parsed_message, '\n%s != %s (%s != %s)' %
+ (message, parsed_message, message.repeated_string[0],
+ parsed_message.repeated_string[0]))
def testPrintFloatFormat(self, message_module):
# Check that float_format argument is passed to sub-message formatting.
@@ -259,6 +298,36 @@ class TextFormatTest(TextFormatBase):
message.c = 123
self.assertEqual('c: 123\n', str(message))
+ def testMessageToStringUnicode(self, message_module):
+ golden_unicode = u'Á short desçription and a 🍌.'
+ golden_bytes = golden_unicode.encode('utf-8')
+ message.optional_string = golden_unicode
+ message.optional_bytes = golden_bytes
+ text = text_format.MessageToString(message, as_utf8=True)
+ golden_message = textwrap.dedent(
+ 'optional_string: "Á short desçription and a 🍌."\n'
+ 'optional_bytes: '
+ r'"\303\201 short des\303\247ription and a \360\237\215\214."'
+ '\n')
+ self.CompareToGoldenText(text, golden_message)
+ def testMessageToStringASCII(self, message_module):
+ text = text_format.MessageToString(message, as_utf8=False) # ASCII
+ golden_message = (
+ 'optional_string: '
+ '\n'
def testPrintField(self, message_module):
field = message.DESCRIPTOR.fields_by_name['optional_float']
@@ -289,6 +358,45 @@ class TextFormatTest(TextFormatBase):
self.assertEqual('0.0', out.getvalue())
out.close()
+class TextFormatMessageToTextBytesTests(TextFormatBase):
+ def testMessageToBytes(self, message_module):
+ message = message_module.ForeignMessage()
+ message.c = 123
+ self.assertEqual(b'c: 123\n', text_format.MessageToBytes(message))
+ def testRawUtf8RoundTrip(self, message_module):
+ utf8_text = text_format.MessageToBytes(message, as_utf8=True)
+ golden_bytes = b'repeated_string: "\xc3\xbc\\t\xea\x9c\x9f"\n'
+ self.CompareToGoldenText(utf8_text, golden_bytes)
+ text_format.Parse(utf8_text, parsed_message)
+ def testEscapedUtf8ASCIIRoundTrip(self, message_module):
+ ascii_text = text_format.MessageToBytes(message) # as_utf8=False default
+ golden_bytes = b'repeated_string: "\\303\\274\\t\\352\\234\\237"\n'
+ self.CompareToGoldenText(ascii_text, golden_bytes)
+ text_format.Parse(ascii_text, parsed_message)
+class TextFormatParserTests(TextFormatBase):
def testParseAllFields(self, message_module):
test_util.SetAllFields(message)
@@ -318,14 +426,14 @@ class TextFormatTest(TextFormatBase):
if message_module is unittest_pb2:
test_util.ExpectAllFieldsSet(self, message)
- if six.PY2:
- msg2 = message_module.TestAllTypes()
- text = (u'optional_string: "café"')
- text_format.Merge(text, msg2)
- self.assertEqual(msg2.optional_string, u'café')
- msg2.Clear()
- text_format.Parse(text, msg2)
+ msg2 = message_module.TestAllTypes()
+ text = (u'optional_string: "café"')
+ text_format.Merge(text, msg2)
+ self.assertEqual(msg2.optional_string, u'café')
+ msg2.Clear()
+ self.assertEqual(msg2.optional_string, u'')
+ text_format.Parse(text, msg2)
def testParseExotic(self, message_module):
@@ -425,7 +533,8 @@ class TextFormatTest(TextFormatBase):
six.assertRaisesRegex(self, text_format.ParseError,
- (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
+ (r'1:23 : \'optional_nested_enum: BARR\': '
+ r'Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value named BARR.'), text_format.Parse,
text, message)
@@ -433,7 +542,8 @@ class TextFormatTest(TextFormatBase):
text_format.Parse, text, message)
def testParseStringFieldUnescape(self, message_module):
@@ -457,6 +567,96 @@ class TextFormatTest(TextFormatBase):
message.repeated_string[4])
self.assertEqual(SLASH + 'x20', message.repeated_string[5])
+ def testParseOneof(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ text_format.Parse(text_format.MessageToString(m), m2)
+ self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
+ def testParseMultipleOneof(self, message_module):
+ m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
+ with six.assertRaisesRegex(self, text_format.ParseError,
+ ' is specified along with field '):
+ text_format.Parse(m_string, m2)
+ # This example contains non-ASCII codepoint unicode data as literals
+ # which should come through as utf-8 for bytes, and as the unicode
+ # itself for string fields. It also demonstrates escaped binary data.
+ # The ur"" string prefix is unfortunately missing from Python 3
+ # so we resort to double escaping our \s so that they come through.
+ _UNICODE_SAMPLE = u"""
+ optional_bytes: 'Á short desçription'
+ optional_string: 'Á short desçription'
+ repeated_bytes: '\\303\\201 short des\\303\\247ription'
+ repeated_bytes: '\\x12\\x34\\x56\\x78\\x90\\xab\\xcd\\xef'
+ repeated_string: '\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82'
+ _BYTES_SAMPLE = _UNICODE_SAMPLE.encode('utf-8')
+ _GOLDEN_UNICODE = u'Á short desçription'
+ _GOLDEN_BYTES = _GOLDEN_UNICODE.encode('utf-8')
+ _GOLDEN_BYTES_1 = b'\x12\x34\x56\x78\x90\xab\xcd\xef'
+ _GOLDEN_STR_0 = u'Привет'
+ def testParseUnicode(self, message_module):
+ text_format.Parse(self._UNICODE_SAMPLE, m)
+ self.assertEqual(m.optional_bytes, self._GOLDEN_BYTES)
+ self.assertEqual(m.optional_string, self._GOLDEN_UNICODE)
+ self.assertEqual(m.repeated_bytes[0], self._GOLDEN_BYTES)
+ # repeated_bytes[1] contained simple \ escaped non-UTF-8 raw binary data.
+ self.assertEqual(m.repeated_bytes[1], self._GOLDEN_BYTES_1)
+ # repeated_string[0] contained \ escaped data representing the UTF-8
+ # representation of _GOLDEN_STR_0 - it needs to decode as such.
+ self.assertEqual(m.repeated_string[0], self._GOLDEN_STR_0)
+ def testParseBytes(self, message_module):
+ text_format.Parse(self._BYTES_SAMPLE, m)
+ def testFromBytesFile(self, message_module):
+ f = io.BytesIO(self._BYTES_SAMPLE)
+ text_format.ParseLines(f, m)
+ def testFromUnicodeFile(self, message_module):
+ f = io.StringIO(self._UNICODE_SAMPLE)
+ def testFromBytesLines(self, message_module):
+ text_format.ParseLines(self._BYTES_SAMPLE.split(b'\n'), m)
+ def testFromUnicodeLines(self, message_module):
+ text_format.ParseLines(self._UNICODE_SAMPLE.split(u'\n'), m)
+class TextFormatMergeTests(TextFormatBase):
def testMergeDuplicateScalars(self, message_module):
text = ('optional_int32: 42 ' 'optional_int32: 67')
@@ -472,26 +672,12 @@ class TextFormatTest(TextFormatBase):
self.assertTrue(r is message)
self.assertEqual(2, message.optional_nested_message.bb)
- def testParseOneof(self, message_module):
- m = message_module.TestAllTypes()
- m.oneof_uint32 = 11
- m2 = message_module.TestAllTypes()
- text_format.Parse(text_format.MessageToString(m), m2)
- self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
def testMergeMultipleOneof(self, message_module):
m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
m2 = message_module.TestAllTypes()
text_format.Merge(m_string, m2)
self.assertEqual('oneof_string', m2.WhichOneof('oneof_field'))
- def testParseMultipleOneof(self, message_module):
- m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
- with self.assertRaisesRegexp(text_format.ParseError,
- ' is specified along with field '):
- text_format.Parse(m_string, m2)
# These are tests that aren't fundamentally specific to proto2, but are at
# the moment because of differences between the proto2 and proto3 test schemas.
@@ -938,7 +1124,7 @@ class Proto2Tests(TextFormatBase):
'}\n')
six.assertRaisesRegex(self,
- '5:1 : Expected ">".',
+ '5:1 : \'}\': Expected ">".',
text_format.Parse,
malformed,
message,
@@ -981,7 +1167,8 @@ class Proto2Tests(TextFormatBase):
with self.assertRaises(text_format.ParseError) as e:
text_format.Parse(text, message)
self.assertEqual(str(e.exception),
- '1:27 : Expected identifier or number, got "bb".')
+ '1:27 : \'optional_nested_message { "bb": 1 }\': '
+ 'Expected identifier or number, got "bb".')
def testParseBadExtension(self):
message = unittest_pb2.TestAllExtensions()
@@ -998,7 +1185,8 @@ class Proto2Tests(TextFormatBase):
message = unittest_pb2.TestAllTypes()
+ (r'1:23 : \'optional_nested_enum: 100\': '
r'has no value with number 100.'), text_format.Parse,
@@ -1448,6 +1636,26 @@ class TokenizerTest(unittest.TestCase):
self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
self.assertTrue(tokenizer.AtEnd())
+ def testConsumeOctalIntegers(self):
+ """Test support for C style octal integers."""
+ text = '00 -00 04 0755 -010 007 -0033 08 -09 01'
+ tokenizer = text_format.Tokenizer(text.splitlines())
+ self.assertEqual(0, tokenizer.ConsumeInteger())
+ self.assertEqual(4, tokenizer.ConsumeInteger())
+ self.assertEqual(0o755, tokenizer.ConsumeInteger())
+ self.assertEqual(-0o10, tokenizer.ConsumeInteger())
+ self.assertEqual(7, tokenizer.ConsumeInteger())
+ self.assertEqual(-0o033, tokenizer.ConsumeInteger())
+ with self.assertRaises(text_format.ParseError):
+ tokenizer.ConsumeInteger() # 08
+ tokenizer.NextToken()
+ tokenizer.ConsumeInteger() # -09
+ self.assertEqual(1, tokenizer.ConsumeInteger())
+ self.assertTrue(tokenizer.AtEnd())
def testConsumeByteString(self):
text = '"string1\''
tokenizer = text_format.Tokenizer(text.splitlines())
@@ -1556,6 +1764,12 @@ class TokenizerTest(unittest.TestCase):
tokenizer.ConsumeCommentOrTrailingComment())
+ def testHugeString(self):
+ # With pathologic backtracking, fails with Forge OOM.
+ text = '"' + 'a' * (10 * 1024 * 1024) + '"'
+ tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
+ tokenizer.ConsumeString()
# Tests for pretty printer functionality.
@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2))
@@ -185,6 +185,14 @@ class UnicodeValueChecker(object):
'encoding. Non-UTF-8 strings must be converted to '
'unicode objects before being added.' %
(proposed_value))
+ proposed_value.encode('utf8')
+ except UnicodeEncodeError:
+ raise ValueError('%.1024r isn\'t a valid unicode string and '
+ 'can\'t be encoded in UTF-8.'%
+ (proposed_value))
return proposed_value
def DefaultValue(self):
@@ -49,20 +49,12 @@ from google.protobuf.internal import missing_enum_values_pb2
from google.protobuf.internal import type_checkers
+from google.protobuf import descriptor
-# CheckUnknownField() cannot be used by the C++ implementation because
-# some protect members are called. It is not a behavior difference
-# for python and C++ implementation.
-def SkipCheckUnknownFieldIfCppImplementation(func):
- return unittest.skipIf(
- api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
- 'Addtional test for pure python involved protect members')(func)
class UnknownFieldsTest(BaseTestCase):
def setUp(self):
@@ -80,23 +72,11 @@ class UnknownFieldsTest(BaseTestCase):
# stdout.
self.assertTrue(data == self.all_fields_data)
- def expectSerializeProto3(self, preserve):
+ def testSerializeProto3(self):
+ # Verify proto3 unknown fields behavior.
message = unittest_proto3_arena_pb2.TestEmptyMessage()
message.ParseFromString(self.all_fields_data)
- if preserve:
- self.assertEqual(self.all_fields_data, message.SerializeToString())
- self.assertEqual(0, len(message.SerializeToString()))
- def testSerializeProto3(self):
- # Verify that proto3 unknown fields behavior.
- default_preserve = (api_implementation
- .GetPythonProto3PreserveUnknownsDefault())
- self.expectSerializeProto3(default_preserve)
- api_implementation.SetPythonProto3PreserveUnknownsDefault(
- not default_preserve)
- self.expectSerializeProto3(not default_preserve)
- api_implementation.SetPythonProto3PreserveUnknownsDefault(default_preserve)
+ self.assertEqual(self.all_fields_data, message.SerializeToString())
def testByteSize(self):
self.assertEqual(self.all_fields.ByteSize(), self.empty_message.ByteSize())
@@ -169,13 +149,15 @@ class UnknownFieldsAccessorsTest(BaseTestCase):
self.empty_message = unittest_pb2.TestEmptyMessage()
self.empty_message.ParseFromString(self.all_fields_data)
- # CheckUnknownField() is an additional Pure Python check which checks
+ # InternalCheckUnknownField() is an additional Pure Python check which checks
# a detail of unknown fields. It cannot be used by the C++
# implementation because some protect members are called.
# The test is added for historical reasons. It is not necessary as
# serialized string is checked.
- def CheckUnknownField(self, name, expected_value):
+ # TODO(jieluo): Remove message._unknown_fields.
+ def InternalCheckUnknownField(self, name, expected_value):
field_descriptor = self.descriptor.fields_by_name[name]
wire_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type]
field_tag = encoder.TagBytes(field_descriptor.number, wire_type)
@@ -183,36 +165,80 @@ class UnknownFieldsAccessorsTest(BaseTestCase):
for tag_bytes, value in self.empty_message._unknown_fields:
if tag_bytes == field_tag:
decoder = unittest_pb2.TestAllTypes._decoders_by_tag[tag_bytes][0]
- decoder(value, 0, len(value), self.all_fields, result_dict)
+ decoder(memoryview(value), 0, len(value), self.all_fields, result_dict)
self.assertEqual(expected_value, result_dict[field_descriptor])
- @SkipCheckUnknownFieldIfCppImplementation
+ def CheckUnknownField(self, name, unknown_fields, expected_value):
+ field_descriptor = self.descriptor.fields_by_name[name]
+ expected_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[
+ field_descriptor.type]
+ for unknown_field in unknown_fields:
+ if unknown_field.field_number == field_descriptor.number:
+ self.assertEqual(expected_type, unknown_field.wire_type)
+ if expected_type == 3:
+ # Check group
+ self.assertEqual(expected_value[0],
+ unknown_field.data[0].field_number)
+ self.assertEqual(expected_value[1], unknown_field.data[0].wire_type)
+ self.assertEqual(expected_value[2], unknown_field.data[0].data)
+ if field_descriptor.label == descriptor.FieldDescriptor.LABEL_REPEATED:
+ self.assertIn(unknown_field.data, expected_value)
+ self.assertEqual(expected_value, unknown_field.data)
def testCheckUnknownFieldValue(self):
+ unknown_fields = self.empty_message.UnknownFields()
# Test enum.
self.CheckUnknownField('optional_nested_enum',
+ unknown_fields,
self.all_fields.optional_nested_enum)
+ self.InternalCheckUnknownField('optional_nested_enum',
+ self.all_fields.optional_nested_enum)
# Test repeated enum.
self.CheckUnknownField('repeated_nested_enum',
self.all_fields.repeated_nested_enum)
+ self.InternalCheckUnknownField('repeated_nested_enum',
+ self.all_fields.repeated_nested_enum)
# Test varint.
self.CheckUnknownField('optional_int32',
self.all_fields.optional_int32)
+ self.InternalCheckUnknownField('optional_int32',
+ self.all_fields.optional_int32)
# Test fixed32.
self.CheckUnknownField('optional_fixed32',
self.all_fields.optional_fixed32)
+ self.InternalCheckUnknownField('optional_fixed32',
+ self.all_fields.optional_fixed32)
# Test fixed64.
self.CheckUnknownField('optional_fixed64',
self.all_fields.optional_fixed64)
+ self.InternalCheckUnknownField('optional_fixed64',
+ self.all_fields.optional_fixed64)
# Test lengthd elimited.
self.CheckUnknownField('optional_string',
- self.all_fields.optional_string)
+ self.all_fields.optional_string.encode('utf-8'))
+ self.InternalCheckUnknownField('optional_string',
+ self.all_fields.optional_string)
# Test group.
self.CheckUnknownField('optionalgroup',
- self.all_fields.optionalgroup)
+ (17, 0, 117))
+ self.InternalCheckUnknownField('optionalgroup',
+ self.all_fields.optionalgroup)
+ self.assertEqual(97, len(unknown_fields))
def testCopyFrom(self):
message = unittest_pb2.TestEmptyMessage()
@@ -230,9 +256,18 @@ class UnknownFieldsAccessorsTest(BaseTestCase):
message.optional_int64 = 3
message.optional_uint32 = 4
destination = unittest_pb2.TestEmptyMessage()
+ unknown_fields = destination.UnknownFields()
+ self.assertEqual(0, len(unknown_fields))
destination.ParseFromString(message.SerializeToString())
+ # ParseFromString clears the message thus unknown fields is invalid.
+ with self.assertRaises(ValueError) as context:
+ len(unknown_fields)
+ self.assertIn('UnknownFields does not exist.',
+ str(context.exception))
+ self.assertEqual(2, len(unknown_fields))
destination.MergeFrom(source)
+ self.assertEqual(4, len(unknown_fields))
# Check that the fields where correctly merged, even stored in the unknown
# fields set.
message.ParseFromString(destination.SerializeToString())
@@ -241,9 +276,58 @@ class UnknownFieldsAccessorsTest(BaseTestCase):
self.assertEqual(message.optional_int64, 3)
def testClear(self):
self.empty_message.Clear()
# All cleared, even unknown fields.
self.assertEqual(self.empty_message.SerializeToString(), b'')
+ def testSubUnknownFields(self):
+ message = unittest_pb2.TestAllTypes()
+ message.optionalgroup.a = 123
+ destination = unittest_pb2.TestEmptyMessage()
+ destination.ParseFromString(message.SerializeToString())
+ sub_unknown_fields = destination.UnknownFields()[0].data
+ self.assertEqual(1, len(sub_unknown_fields))
+ self.assertEqual(sub_unknown_fields[0].data, 123)
+ destination.Clear()
+ len(sub_unknown_fields)
+ # pylint: disable=pointless-statement
+ sub_unknown_fields[0]
+ message.Clear()
+ message.optional_uint32 = 456
+ nested_message = unittest_pb2.NestedTestAllTypes()
+ nested_message.payload.optional_nested_message.ParseFromString(
+ message.SerializeToString())
+ unknown_fields = (
+ nested_message.payload.optional_nested_message.UnknownFields())
+ self.assertEqual(unknown_fields[0].data, 456)
+ nested_message.ClearField('payload')
+ def testUnknownField(self):
+ message.optional_int32 = 123
+ unknown_field = destination.UnknownFields()[0]
+ unknown_field.data # pylint: disable=pointless-statement
+ self.assertIn('The parent message might be cleared.',
def testUnknownExtensions(self):
message = unittest_pb2.TestEmptyMessageWithExtensions()
@@ -280,15 +364,13 @@ class UnknownEnumValuesTest(BaseTestCase):
def CheckUnknownField(self, name, expected_value):
- wire_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type]
- field_tag = encoder.TagBytes(field_descriptor.number, wire_type)
- result_dict = {}
- for tag_bytes, value in self.missing_message._unknown_fields:
- if tag_bytes == field_tag:
- decoder = missing_enum_values_pb2.TestEnumValues._decoders_by_tag[
- tag_bytes][0]
- decoder(value, 0, len(value), self.message, result_dict)
- self.assertEqual(expected_value, result_dict[field_descriptor])
+ unknown_fields = self.missing_message.UnknownFields()
+ for field in unknown_fields:
+ if field.field_number == field_descriptor.number:
+ self.assertIn(field.data, expected_value)
+ self.assertEqual(expected_value, field.data)
def testUnknownParseMismatchEnumValue(self):
just_string = missing_enum_values_pb2.JustString()
@@ -317,7 +399,6 @@ class UnknownEnumValuesTest(BaseTestCase):
def testUnknownPackedEnumValue(self):
self.assertEqual([], self.missing_message.packed_nested_enum)
def testCheckUnknownFieldValueForEnum(self):
self.message.optional_nested_enum)
@@ -482,7 +482,7 @@ class _Parser(object):
('Message type "{0}" has no field named "{1}".\n'
' Available Fields(except extensions): {2}').format(
message_descriptor.full_name, name,
- message_descriptor.fields))
+ [f.json_name for f in message_descriptor.fields]))
if name in names:
raise ParseError('Message type "{0}" should not have multiple '
'"{1}" fields.'.format(
@@ -268,6 +268,10 @@ class Message(object):
def ClearExtension(self, extension_handle):
raise NotImplementedError
+ def UnknownFields(self):
+ """Returns the UnknownFieldSet."""
+ raise NotImplementedError
def DiscardUnknownFields(self):
@@ -39,9 +39,18 @@ my_proto_instance = message_classes['some.proto.package.MessageName']()
__author__ = 'matthewtoia@google.com (Matt Toia)'
+from google.protobuf.internal import api_implementation
-from google.protobuf import reflection
+if api_implementation.Type() == 'cpp':
+ from google.protobuf.pyext import cpp_message as message_impl
+ from google.protobuf.internal import python_message as message_impl
+# The type of all Message classes.
+_GENERATED_PROTOCOL_MESSAGE_TYPE = message_impl.GeneratedProtocolMessageType
class MessageFactory(object):
@@ -70,11 +79,11 @@ class MessageFactory(object):
descriptor_name = descriptor.name
if str is bytes: # PY2
descriptor_name = descriptor.name.encode('ascii', 'ignore')
- result_class = reflection.GeneratedProtocolMessageType(
+ result_class = _GENERATED_PROTOCOL_MESSAGE_TYPE(
descriptor_name,
(message.Message,),
{'DESCRIPTOR': descriptor, '__module__': None})
- # If module not set, it wrongly points to the reflection.py module.
+ # If module not set, it wrongly points to message_factory module.
self._classes[descriptor] = result_class
for field in descriptor.fields:
if field.message_type:
@@ -42,16 +42,15 @@
// Then use the methods of the returned class:
// py_proto_api->GetMessagePointer(...);
-#ifndef PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
-#define PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
+#ifndef GOOGLE_PROTOBUF_PYTHON_PROTO_API_H__
+#define GOOGLE_PROTOBUF_PYTHON_PROTO_API_H__
#include <Python.h>
namespace google {
namespace protobuf {
-class Message;
namespace python {
// Note on the implementation:
@@ -89,4 +88,4 @@ inline const char* PyProtoAPICapsuleName() {
-#endif // PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
+#endif // GOOGLE_PROTOBUF_PYTHON_PROTO_API_H__
@@ -32,8 +32,8 @@
#include <frameobject.h>
-#include <google/protobuf/stubs/hash.h>
+#include <unordered_map>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/descriptor.pb.h>
@@ -44,6 +44,7 @@
#include <google/protobuf/pyext/message.h>
#include <google/protobuf/pyext/message_factory.h>
#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+#include <google/protobuf/stubs/hash.h>
#if PY_MAJOR_VERSION >= 3
#define PyString_FromStringAndSize PyUnicode_FromStringAndSize
@@ -54,10 +55,12 @@
#if PY_VERSION_HEX < 0x03030000
#error "Python 3.0 - 3.2 are not supported."
#endif
- #define PyString_AsStringAndSize(ob, charpp, sizep) \
- (PyUnicode_Check(ob)? \
- ((*(charpp) = const_cast<char*>(PyUnicode_AsUTF8AndSize(ob, (sizep)))) == NULL? -1: 0): \
- PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+#define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob) ? ((*(charpp) = const_cast<char*>( \
+ PyUnicode_AsUTF8AndSize(ob, (sizep)))) == NULL \
+ ? -1 \
+ : 0) \
+ : PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
@@ -70,7 +73,7 @@ namespace python {
// released.
// This is enough to support the "is" operator on live objects.
// All descriptors are stored here.
-hash_map<const void*, PyObject*> interned_descriptors;
+std::unordered_map<const void*, PyObject*>* interned_descriptors;
PyObject* PyString_FromCppString(const string& str) {
return PyString_FromStringAndSize(str.c_str(), str.size());
@@ -119,8 +122,10 @@ bool _CalledFromGeneratedFile(int stacklevel) {
PyErr_Clear();
- if ((filename_size < 3) || (strcmp(&filename[filename_size - 3], ".py") != 0)) {
- // Cython's stack does not have .py file name and is not at global module scope.
+ if ((filename_size < 3) ||
+ (strcmp(&filename[filename_size - 3], ".py") != 0)) {
+ // Cython's stack does not have .py file name and is not at global module
+ // scope.
if (filename_size < 7) {
@@ -131,7 +136,7 @@ bool _CalledFromGeneratedFile(int stacklevel) {
// Filename is not ending with _pb2.
if (frame->f_globals != frame->f_locals) {
// Not at global module scope
@@ -197,7 +202,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
// First search in the cache.
PyDescriptorPool* caching_pool = GetDescriptorPool_FromPool(
GetFileDescriptor(descriptor)->pool());
- hash_map<const void*, PyObject*>* descriptor_options =
+ std::unordered_map<const void*, PyObject*>* descriptor_options =
caching_pool->descriptor_options;
if (descriptor_options->find(descriptor) != descriptor_options->end()) {
PyObject *value = (*descriptor_options)[descriptor];
@@ -232,7 +237,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
if (value == NULL) {
return NULL;
- if (!PyObject_TypeCheck(value.get(), &CMessage_Type)) {
+ if (!PyObject_TypeCheck(value.get(), CMessage_Type)) {
PyErr_Format(PyExc_TypeError, "Invalid class for %s: %s",
message_type->full_name().c_str(),
Py_TYPE(value.get())->tp_name);
@@ -275,7 +280,7 @@ static PyObject* CopyToPythonProto(const DescriptorClass *descriptor,
const Descriptor* self_descriptor =
DescriptorProtoClass::default_instance().GetDescriptor();
CMessage* message = reinterpret_cast<CMessage*>(target);
- if (!PyObject_TypeCheck(target, &CMessage_Type) ||
+ if (!PyObject_TypeCheck(target, CMessage_Type) ||
message->message->GetDescriptor() != self_descriptor) {
PyErr_Format(PyExc_TypeError, "Not a %s message",
self_descriptor->full_name().c_str());
@@ -332,9 +337,9 @@ PyObject* NewInternedDescriptor(PyTypeObject* type,
// See if the object is in the map of interned descriptors
- hash_map<const void*, PyObject*>::iterator it =
- interned_descriptors.find(descriptor);
- if (it != interned_descriptors.end()) {
+ std::unordered_map<const void*, PyObject*>::iterator it =
+ interned_descriptors->find(descriptor);
+ if (it != interned_descriptors->end()) {
GOOGLE_DCHECK(Py_TYPE(it->second) == type);
Py_INCREF(it->second);
return it->second;
@@ -348,7 +353,7 @@ PyObject* NewInternedDescriptor(PyTypeObject* type,
py_descriptor->descriptor = descriptor;
// and cache it.
- interned_descriptors.insert(
+ interned_descriptors->insert(
std::make_pair(descriptor, reinterpret_cast<PyObject*>(py_descriptor)));
// Ensures that the DescriptorPool stays alive.
@@ -370,7 +375,7 @@ PyObject* NewInternedDescriptor(PyTypeObject* type,
static void Dealloc(PyBaseDescriptor* self) {
// Remove from interned dictionary
- interned_descriptors.erase(self->descriptor);
+ interned_descriptors->erase(self->descriptor);
Py_CLEAR(self->pool);
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
@@ -758,6 +763,11 @@ static PyObject* HasDefaultValue(PyBaseDescriptor *self, void *closure) {
static PyObject* GetDefaultValue(PyBaseDescriptor *self, void *closure) {
PyObject *result;
+ if (_GetDescriptor(self)->is_repeated()) {
+ return PyList_New(0);
switch (_GetDescriptor(self)->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32: {
int32 value = _GetDescriptor(self)->default_value_int32();
@@ -805,6 +815,10 @@ static PyObject* GetDefaultValue(PyBaseDescriptor *self, void *closure) {
result = PyInt_FromLong(value->number());
+ case FieldDescriptor::CPPTYPE_MESSAGE: {
+ Py_RETURN_NONE;
+ break;
PyErr_Format(PyExc_NotImplementedError, "default value for %s",
_GetDescriptor(self)->full_name().c_str());
@@ -1919,6 +1933,9 @@ bool InitDescriptor() {
if (!InitDescriptorMappingTypes())
+ // Initialize globals defined in this file.
+ interned_descriptors = new std::unordered_map<const void*, PyObject*>;
@@ -100,6 +100,6 @@ bool InitDescriptor();
} // namespace python
#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__