Sync from Piper @455586341

PROTOBUF_SYNC_PIPER
diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc
index 5782789..1185880 100644
--- a/conformance/conformance_cpp.cc
+++ b/conformance/conformance_cpp.cc
@@ -32,165 +32,176 @@
 #include <stdarg.h>
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
 #include <google/protobuf/message.h>
 #include <google/protobuf/text_format.h>
 #include <google/protobuf/util/json_util.h>
 #include <google/protobuf/util/type_resolver_util.h>
 #include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+#include "conformance.pb.h"
 #include "conformance.pb.h"
 #include <google/protobuf/test_messages_proto2.pb.h>
 #include <google/protobuf/test_messages_proto3.pb.h>
+#include <google/protobuf/test_messages_proto3.pb.h>
+#include <google/protobuf/util/type_resolver.h>
 #include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/status_macros.h>
 
-using conformance::ConformanceRequest;
-using conformance::ConformanceResponse;
-using google::protobuf::Descriptor;
-using google::protobuf::DescriptorPool;
-using google::protobuf::Message;
-using google::protobuf::MessageFactory;
-using google::protobuf::TextFormat;
-using google::protobuf::util::BinaryToJsonString;
-using google::protobuf::util::JsonParseOptions;
-using google::protobuf::util::JsonToBinaryString;
-using google::protobuf::util::NewTypeResolverForDescriptorPool;
-using google::protobuf::util::TypeResolver;
-using protobuf_test_messages::proto3::TestAllTypesProto3;
-using protobuf_test_messages::proto2::TestAllTypesProto2;
-using std::string;
-
-static const char kTypeUrlPrefix[] = "type.googleapis.com";
-
-const char* kFailures[] = {
-};
-
-static string GetTypeUrl(const Descriptor* message) {
-  return string(kTypeUrlPrefix) + "/" + message->full_name();
-}
-
-int test_count = 0;
-bool verbose = false;
-TypeResolver* type_resolver;
-string* type_url;
+// Must be included last.
+#include <google/protobuf/port_def.inc>
 
 namespace google {
 namespace protobuf {
+namespace {
+using ::conformance::ConformanceRequest;
+using ::conformance::ConformanceResponse;
+using ::google::protobuf::util::BinaryToJsonString;
+using ::google::protobuf::util::JsonParseOptions;
+using ::google::protobuf::util::JsonToBinaryString;
+using ::google::protobuf::util::NewTypeResolverForDescriptorPool;
+using ::google::protobuf::util::TypeResolver;
+using ::protobuf_test_messages::proto2::TestAllTypesProto2;
+using ::protobuf_test_messages::proto3::TestAllTypesProto3;
 
-using util::Status;
-
-bool CheckedRead(int fd, void *buf, size_t len) {
-  size_t ofs = 0;
+util::Status ReadFd(int fd, char* buf, size_t len) {
   while (len > 0) {
-    ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
+    ssize_t bytes_read = read(fd, buf, len);
 
-    if (bytes_read == 0) return false;
+    if (bytes_read == 0) {
+      return util::DataLossError("unexpected EOF");
+    }
 
     if (bytes_read < 0) {
-      GOOGLE_LOG(FATAL) << "Error reading from test runner: " << strerror(errno);
+      return util::ErrnoToStatus(errno, "error reading from test runner");
     }
 
     len -= bytes_read;
-    ofs += bytes_read;
+    buf += bytes_read;
   }
-
-  return true;
+  return util::OkStatus();
 }
 
-void CheckedWrite(int fd, const void *buf, size_t len) {
+util::Status WriteFd(int fd, const void* buf, size_t len) {
   if (write(fd, buf, len) != len) {
-    GOOGLE_LOG(FATAL) << "Error writing to test runner: " << strerror(errno);
+    return util::ErrnoToStatus(errno, "error reading to test runner");
   }
+  return util::OkStatus();
 }
 
-void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
-  Message *test_message;
-  google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
-  google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
-  const Descriptor *descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(
-      request.message_type());
-  if (!descriptor) {
-    GOOGLE_LOG(FATAL) << "No such message type: " << request.message_type();
+class Harness {
+ public:
+  Harness() {
+    google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
+    google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
+
+    resolver_.reset(NewTypeResolverForDescriptorPool(
+        "type.googleapis.com", DescriptorPool::generated_pool()));
+    type_url_ = StrCat("type.googleapis.com/",
+                             TestAllTypesProto3::GetDescriptor()->full_name());
   }
-  test_message = MessageFactory::generated_factory()->GetPrototype(descriptor)->New();
+
+  util::StatusOr<ConformanceResponse> RunTest(
+      const ConformanceRequest& request);
+
+  // Returns Ok(true) if we're done processing requests.
+  util::StatusOr<bool> ServeConformanceRequest();
+
+ private:
+  bool verbose_ = false;
+  std::unique_ptr<TypeResolver> resolver_;
+  std::string type_url_;
+};
+
+util::StatusOr<ConformanceResponse> Harness::RunTest(
+    const ConformanceRequest& request) {
+  const Descriptor* descriptor =
+      DescriptorPool::generated_pool()->FindMessageTypeByName(
+          request.message_type());
+  if (descriptor == nullptr) {
+    return util::NotFoundError(
+        StrCat("No such message type: ", request.message_type()));
+  }
+
+  std::unique_ptr<Message> test_message(
+      MessageFactory::generated_factory()->GetPrototype(descriptor)->New());
+  ConformanceResponse response;
 
   switch (request.payload_case()) {
     case ConformanceRequest::kProtobufPayload: {
       if (!test_message->ParseFromString(request.protobuf_payload())) {
-        // Getting parse details would involve something like:
-        //   http://stackoverflow.com/questions/22121922/how-can-i-get-more-details-about-errors-generated-during-protobuf-parsing-c
-        response->set_parse_error("Parse error (no more details available).");
-        return;
+        response.set_parse_error("parse error (no more details available)");
+        return response;
       }
       break;
     }
 
     case ConformanceRequest::kJsonPayload: {
-      string proto_binary;
       JsonParseOptions options;
       options.ignore_unknown_fields =
           (request.test_category() ==
-              conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
+           conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
+
+      std::string proto_binary;
       util::Status status =
-          JsonToBinaryString(type_resolver, *type_url, request.json_payload(),
+          JsonToBinaryString(resolver_.get(), type_url_, request.json_payload(),
                              &proto_binary, options);
       if (!status.ok()) {
-        response->set_parse_error(string("Parse error: ") +
-                                  std::string(status.message()));
-        return;
+        response.set_parse_error(
+            StrCat("parse error: ", status.message()));
+        return response;
       }
 
       if (!test_message->ParseFromString(proto_binary)) {
-        response->set_runtime_error(
-            "Parsing JSON generates invalid proto output.");
-        return;
+        response.set_runtime_error(
+            "parsing JSON generated invalid proto output");
+        return response;
       }
+
       break;
     }
 
     case ConformanceRequest::kTextPayload: {
-      if (!TextFormat::ParseFromString(request.text_payload(), test_message)) {
-        response->set_parse_error("Parse error");
-        return;
+      if (!TextFormat::ParseFromString(request.text_payload(),
+                                       test_message.get())) {
+        response.set_parse_error("parse error (no more details available)");
+        return response;
       }
       break;
     }
 
     case ConformanceRequest::PAYLOAD_NOT_SET:
-      GOOGLE_LOG(FATAL) << "Request didn't have payload.";
-      break;
+      return util::InvalidArgumentError("request didn't have payload");
 
     default:
-      GOOGLE_LOG(FATAL) << "unknown payload type: " << request.payload_case();
-      break;
-  }
-
-  conformance::FailureSet failures;
-  if (descriptor == failures.GetDescriptor()) {
-    for (const char* s : kFailures) failures.add_failure(s);
-    test_message = &failures;
+      return util::InvalidArgumentError(
+          StrCat("unknown payload type", request.payload_case()));
   }
 
   switch (request.requested_output_format()) {
     case conformance::UNSPECIFIED:
-      GOOGLE_LOG(FATAL) << "Unspecified output format";
-      break;
+      return util::InvalidArgumentError("unspecified output format");
 
     case conformance::PROTOBUF: {
-      GOOGLE_CHECK(test_message->SerializeToString(
-          response->mutable_protobuf_payload()));
+      GOOGLE_CHECK(
+          test_message->SerializeToString(response.mutable_protobuf_payload()));
       break;
     }
 
     case conformance::JSON: {
-      string proto_binary;
+      std::string proto_binary;
       GOOGLE_CHECK(test_message->SerializeToString(&proto_binary));
       util::Status status =
-          BinaryToJsonString(type_resolver, *type_url, proto_binary,
-                             response->mutable_json_payload());
+          BinaryToJsonString(resolver_.get(), type_url_, proto_binary,
+                             response.mutable_json_payload());
       if (!status.ok()) {
-        response->set_serialize_error(
-            string("Failed to serialize JSON output: ") +
-            std::string(status.message()));
-        return;
+        response.set_serialize_error(StrCat(
+            "failed to serialize JSON output: ", status.message()));
       }
       break;
     }
@@ -199,70 +210,66 @@
       TextFormat::Printer printer;
       printer.SetHideUnknownFields(!request.print_unknown_fields());
       GOOGLE_CHECK(printer.PrintToString(*test_message,
-                                  response->mutable_text_payload()));
+                                  response.mutable_text_payload()));
       break;
     }
 
     default:
-      GOOGLE_LOG(FATAL) << "Unknown output format: "
-                 << request.requested_output_format();
+      return util::InvalidArgumentError(StrCat(
+          "unknown output format", request.requested_output_format()));
   }
+
+  return response;
 }
 
-bool DoTestIo() {
-  string serialized_input;
-  string serialized_output;
+util::StatusOr<bool> Harness::ServeConformanceRequest() {
+  uint32_t in_len;
+  if (!ReadFd(STDIN_FILENO, reinterpret_cast<char*>(&in_len), sizeof(in_len))
+           .ok()) {
+    // EOF means we're done.
+    return true;
+  }
+
+  std::string serialized_input;
+  serialized_input.resize(in_len);
+  RETURN_IF_ERROR(ReadFd(STDIN_FILENO, serialized_input.data(), in_len));
+
   ConformanceRequest request;
-  ConformanceResponse response;
-  uint32_t bytes;
+  GOOGLE_CHECK(request.ParseFromString(serialized_input));
 
-  if (!CheckedRead(STDIN_FILENO, &bytes, sizeof(uint32_t))) {
-    // EOF.
-    return false;
+  util::StatusOr<ConformanceResponse> response = RunTest(request);
+  RETURN_IF_ERROR(response.status());
+
+  std::string serialized_output;
+  response->SerializeToString(&serialized_output);
+
+  uint32_t out_len = static_cast<uint32_t>(serialized_output.size());
+  RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, &out_len, sizeof(out_len)));
+  RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, serialized_output.data(), out_len));
+
+  if (verbose_) {
+    GOOGLE_LOG(INFO) << "conformance-cpp: request=" << request.ShortDebugString()
+              << ", response=" << response->ShortDebugString();
   }
-
-  serialized_input.resize(bytes);
-
-  if (!CheckedRead(STDIN_FILENO, (char*)serialized_input.c_str(), bytes)) {
-    GOOGLE_LOG(ERROR) << "Unexpected EOF on stdin. " << strerror(errno);
-  }
-
-  if (!request.ParseFromString(serialized_input)) {
-    GOOGLE_LOG(FATAL) << "Parse of ConformanceRequest proto failed.";
-    return false;
-  }
-
-  DoTest(request, &response);
-
-  response.SerializeToString(&serialized_output);
-
-  bytes = serialized_output.size();
-  CheckedWrite(STDOUT_FILENO, &bytes, sizeof(uint32_t));
-  CheckedWrite(STDOUT_FILENO, serialized_output.c_str(), bytes);
-
-  if (verbose) {
-    fprintf(stderr, "conformance-cpp: request=%s, response=%s\n",
-            request.ShortDebugString().c_str(),
-            response.ShortDebugString().c_str());
-  }
-
-  test_count++;
-
-  return true;
+  return false;
 }
-
+}  // namespace
 }  // namespace protobuf
 }  // namespace google
 
 int main() {
-  type_resolver = NewTypeResolverForDescriptorPool(
-      kTypeUrlPrefix, DescriptorPool::generated_pool());
-  type_url = new string(GetTypeUrl(TestAllTypesProto3::descriptor()));
-  while (1) {
-    if (!google::protobuf::DoTestIo()) {
-      fprintf(stderr, "conformance-cpp: received EOF from test runner "
-                      "after %d tests, exiting\n", test_count);
-      return 0;
+  google::protobuf::Harness harness;
+  int total_runs = 0;
+  while (true) {
+    auto is_done = harness.ServeConformanceRequest();
+    if (!is_done.ok()) {
+      GOOGLE_LOG(FATAL) << is_done.status();
     }
+    if (*is_done) {
+      break;
+    }
+    total_runs++;
   }
+  GOOGLE_LOG(INFO) << "conformance-cpp: recieved EOF from test runner after "
+            << total_runs << " tests";
 }