Squashed 'third_party/protobuf/' content from commit e35e248

Change-Id: I6cbe123d09fe50fdcad0e51466665daeee7433c7
git-subtree-dir: third_party/protobuf
git-subtree-split: e35e24800fb8d694bdeea5fd63dc7d1b14d68723
diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc
new file mode 100644
index 0000000..376a60b
--- /dev/null
+++ b/conformance/conformance_test_runner.cc
@@ -0,0 +1,312 @@
+// 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.
+
+// This file contains a program for running the test suite in a separate
+// process.  The other alternative is to run the suite in-process.  See
+// conformance.proto for pros/cons of these two options.
+//
+// This program will fork the process under test and communicate with it over
+// its stdin/stdout:
+//
+//     +--------+   pipe   +----------+
+//     | tester | <------> | testee   |
+//     |        |          |          |
+//     |  C++   |          | any lang |
+//     +--------+          +----------+
+//
+// The tester contains all of the test cases and their expected output.
+// The testee is a simple program written in the target language that reads
+// each test case and attempts to produce acceptable output for it.
+//
+// Every test consists of a ConformanceRequest/ConformanceResponse
+// request/reply pair.  The protocol on the pipe is simply:
+//
+//   1. tester sends 4-byte length N (little endian)
+//   2. tester sends N bytes representing a ConformanceRequest proto
+//   3. testee sends 4-byte length M (little endian)
+//   4. testee sends M bytes representing a ConformanceResponse proto
+
+#include <algorithm>
+#include <errno.h>
+#include <fstream>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <vector>
+
+#include <google/protobuf/stubs/stringprintf.h>
+
+#include "conformance.pb.h"
+#include "conformance_test.h"
+
+using conformance::ConformanceRequest;
+using conformance::ConformanceResponse;
+using google::protobuf::internal::scoped_array;
+using google::protobuf::StringAppendF;
+using std::string;
+using std::vector;
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define CHECK_SYSCALL(call) \
+  if (call < 0) { \
+    perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \
+    exit(1); \
+  }
+
+// Test runner that spawns the process being tested and communicates with it
+// over a pipe.
+class ForkPipeRunner : public google::protobuf::ConformanceTestRunner {
+ public:
+  ForkPipeRunner(const std::string &executable)
+      : child_pid_(-1), executable_(executable) {}
+
+  virtual ~ForkPipeRunner() {}
+
+  void RunTest(const std::string& test_name,
+               const std::string& request,
+               std::string* response) {
+    if (child_pid_ < 0) {
+      SpawnTestProgram();
+    }
+
+    current_test_name_ = test_name;
+
+    uint32_t len = request.size();
+    CheckedWrite(write_fd_, &len, sizeof(uint32_t));
+    CheckedWrite(write_fd_, request.c_str(), request.size());
+
+    if (!TryRead(read_fd_, &len, sizeof(uint32_t))) {
+      // We failed to read from the child, assume a crash and try to reap.
+      GOOGLE_LOG(INFO) << "Trying to reap child, pid=" << child_pid_;
+
+      int status;
+      waitpid(child_pid_, &status, WEXITED);
+
+      string error_msg;
+      if (WIFEXITED(status)) {
+        StringAppendF(&error_msg,
+                      "child exited, status=%d", WEXITSTATUS(status));
+      } else if (WIFSIGNALED(status)) {
+        StringAppendF(&error_msg,
+                      "child killed by signal %d", WTERMSIG(status));
+      }
+      GOOGLE_LOG(INFO) << error_msg;
+      child_pid_ = -1;
+
+      conformance::ConformanceResponse response_obj;
+      response_obj.set_runtime_error(error_msg);
+      response_obj.SerializeToString(response);
+      return;
+    }
+
+    response->resize(len);
+    CheckedRead(read_fd_, (void*)response->c_str(), len);
+  }
+
+ private:
+  // TODO(haberman): make this work on Windows, instead of using these
+  // UNIX-specific APIs.
+  //
+  // There is a platform-agnostic API in
+  //    src/google/protobuf/compiler/subprocess.h
+  //
+  // However that API only supports sending a single message to the subprocess.
+  // We really want to be able to send messages and receive responses one at a
+  // time:
+  //
+  // 1. Spawning a new process for each test would take way too long for thousands
+  //    of tests and subprocesses like java that can take 100ms or more to start
+  //    up.
+  //
+  // 2. Sending all the tests in one big message and receiving all results in one
+  //    big message would take away our visibility about which test(s) caused a
+  //    crash or other fatal error.  It would also give us only a single failure
+  //    instead of all of them.
+  void SpawnTestProgram() {
+    int toproc_pipe_fd[2];
+    int fromproc_pipe_fd[2];
+    if (pipe(toproc_pipe_fd) < 0 || pipe(fromproc_pipe_fd) < 0) {
+      perror("pipe");
+      exit(1);
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {
+      perror("fork");
+      exit(1);
+    }
+
+    if (pid) {
+      // Parent.
+      CHECK_SYSCALL(close(toproc_pipe_fd[0]));
+      CHECK_SYSCALL(close(fromproc_pipe_fd[1]));
+      write_fd_ = toproc_pipe_fd[1];
+      read_fd_ = fromproc_pipe_fd[0];
+      child_pid_ = pid;
+    } else {
+      // Child.
+      CHECK_SYSCALL(close(STDIN_FILENO));
+      CHECK_SYSCALL(close(STDOUT_FILENO));
+      CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO));
+      CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO));
+
+      CHECK_SYSCALL(close(toproc_pipe_fd[0]));
+      CHECK_SYSCALL(close(fromproc_pipe_fd[1]));
+      CHECK_SYSCALL(close(toproc_pipe_fd[1]));
+      CHECK_SYSCALL(close(fromproc_pipe_fd[0]));
+
+      scoped_array<char> executable(new char[executable_.size() + 1]);
+      memcpy(executable.get(), executable_.c_str(), executable_.size());
+      executable[executable_.size()] = '\0';
+
+      char *const argv[] = {executable.get(), NULL};
+      CHECK_SYSCALL(execv(executable.get(), argv));  // Never returns.
+    }
+  }
+
+  void CheckedWrite(int fd, const void *buf, size_t len) {
+    if (write(fd, buf, len) != len) {
+      GOOGLE_LOG(FATAL) << current_test_name_
+                        << ": error writing to test program: "
+                        << strerror(errno);
+    }
+  }
+
+  bool TryRead(int fd, void *buf, size_t len) {
+    size_t ofs = 0;
+    while (len > 0) {
+      ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
+
+      if (bytes_read == 0) {
+        GOOGLE_LOG(ERROR) << current_test_name_
+                          << ": unexpected EOF from test program";
+        return false;
+      } else if (bytes_read < 0) {
+        GOOGLE_LOG(ERROR) << current_test_name_
+                          << ": error reading from test program: "
+                          << strerror(errno);
+        return false;
+      }
+
+      len -= bytes_read;
+      ofs += bytes_read;
+    }
+
+    return true;
+  }
+
+  void CheckedRead(int fd, void *buf, size_t len) {
+    if (!TryRead(fd, buf, len)) {
+      GOOGLE_LOG(FATAL) << current_test_name_
+                        << ": error reading from test program: "
+                        << strerror(errno);
+    }
+  }
+
+  int write_fd_;
+  int read_fd_;
+  pid_t child_pid_;
+  std::string executable_;
+  std::string current_test_name_;
+};
+
+void UsageError() {
+  fprintf(stderr,
+          "Usage: conformance-test-runner [options] <test-program>\n");
+  fprintf(stderr, "\n");
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr,
+          "  --failure_list <filename>   Use to specify list of tests\n");
+  fprintf(stderr,
+          "                              that are expected to fail.  File\n");
+  fprintf(stderr,
+          "                              should contain one test name per\n");
+  fprintf(stderr,
+          "                              line.  Use '#' for comments.\n");
+  exit(1);
+}
+
+void ParseFailureList(const char *filename, vector<string>* failure_list) {
+  std::ifstream infile(filename);
+
+  if (!infile.is_open()) {
+    fprintf(stderr, "Couldn't open failure list file: %s\n", filename);
+    exit(1);
+  }
+
+  for (string line; getline(infile, line);) {
+    // Remove whitespace.
+    line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
+               line.end());
+
+    // Remove comments.
+    line = line.substr(0, line.find("#"));
+
+    if (!line.empty()) {
+      failure_list->push_back(line);
+    }
+  }
+}
+
+int main(int argc, char *argv[]) {
+  char *program;
+  google::protobuf::ConformanceTestSuite suite;
+
+  vector<string> failure_list;
+
+  for (int arg = 1; arg < argc; ++arg) {
+    if (strcmp(argv[arg], "--failure_list") == 0) {
+      if (++arg == argc) UsageError();
+      ParseFailureList(argv[arg], &failure_list);
+    } else if (strcmp(argv[arg], "--verbose") == 0) {
+      suite.SetVerbose(true);
+    } else if (argv[arg][0] == '-') {
+      fprintf(stderr, "Unknown option: %s\n", argv[arg]);
+      UsageError();
+    } else {
+      if (arg != argc - 1) {
+        fprintf(stderr, "Too many arguments.\n");
+        UsageError();
+      }
+      program = argv[arg];
+    }
+  }
+
+  suite.SetFailureList(failure_list);
+  ForkPipeRunner runner(program);
+
+  std::string output;
+  bool ok = suite.RunSuite(&runner, &output);
+
+  fwrite(output.c_str(), 1, output.size(), stderr);
+
+  return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}