|  | @@ -80,162 +80,31 @@ using std::vector;
 | 
	
		
			
				|  |  |      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);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +namespace google {
 | 
	
		
			
				|  |  | +namespace protobuf {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | - 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]));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      std::unique_ptr<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 ParseFailureList(const char *filename,
 | 
	
		
			
				|  |  | +                      std::vector<string>* failure_list) {
 | 
	
		
			
				|  |  | +  std::ifstream infile(filename);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  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);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  if (!infile.is_open()) {
 | 
	
		
			
				|  |  | +    fprintf(stderr, "Couldn't open failure list file: %s\n", filename);
 | 
	
		
			
				|  |  | +    exit(1);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  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;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  for (string line; getline(infile, line);) {
 | 
	
		
			
				|  |  | +    // Remove whitespace.
 | 
	
		
			
				|  |  | +    line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
 | 
	
		
			
				|  |  | +               line.end());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    // Remove comments.
 | 
	
		
			
				|  |  | +    line = line.substr(0, line.find("#"));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  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);
 | 
	
		
			
				|  |  | +    if (!line.empty()) {
 | 
	
		
			
				|  |  | +      failure_list->push_back(line);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  int write_fd_;
 | 
	
		
			
				|  |  | -  int read_fd_;
 | 
	
		
			
				|  |  | -  pid_t child_pid_;
 | 
	
		
			
				|  |  | -  std::string executable_;
 | 
	
		
			
				|  |  | -  std::string current_test_name_;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void UsageError() {
 | 
	
		
			
				|  |  |    fprintf(stderr,
 | 
	
	
		
			
				|  | @@ -263,33 +132,51 @@ void UsageError() {
 | 
	
		
			
				|  |  |    exit(1);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void ParseFailureList(const char *filename, std::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);
 | 
	
		
			
				|  |  | +void ForkPipeRunner::RunTest(
 | 
	
		
			
				|  |  | +    const std::string& test_name,
 | 
	
		
			
				|  |  | +    const std::string& request,
 | 
	
		
			
				|  |  | +    std::string* response) {
 | 
	
		
			
				|  |  | +  if (child_pid_ < 0) {
 | 
	
		
			
				|  |  | +    SpawnTestProgram();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  for (string line; getline(infile, line);) {
 | 
	
		
			
				|  |  | -    // Remove whitespace.
 | 
	
		
			
				|  |  | -    line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
 | 
	
		
			
				|  |  | -               line.end());
 | 
	
		
			
				|  |  | +  current_test_name_ = test_name;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Remove comments.
 | 
	
		
			
				|  |  | -    line = line.substr(0, line.find("#"));
 | 
	
		
			
				|  |  | +  uint32_t len = request.size();
 | 
	
		
			
				|  |  | +  CheckedWrite(write_fd_, &len, sizeof(uint32_t));
 | 
	
		
			
				|  |  | +  CheckedWrite(write_fd_, request.c_str(), request.size());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!line.empty()) {
 | 
	
		
			
				|  |  | -      failure_list->push_back(line);
 | 
	
		
			
				|  |  | +  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);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int main(int argc, char *argv[]) {
 | 
	
		
			
				|  |  | +int ForkPipeRunner::Run(
 | 
	
		
			
				|  |  | +    int argc, char *argv[], ConformanceTestSuite* suite) {
 | 
	
		
			
				|  |  |    char *program;
 | 
	
		
			
				|  |  | -  const std::set<ConformanceTestSuite*>& test_suite_set =
 | 
	
		
			
				|  |  | -      ::google::protobuf::GetTestSuiteSet();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    string failure_list_filename;
 | 
	
		
			
				|  |  |    std::vector<string> failure_list;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -299,13 +186,9 @@ int main(int argc, char *argv[]) {
 | 
	
		
			
				|  |  |        failure_list_filename = argv[arg];
 | 
	
		
			
				|  |  |        ParseFailureList(argv[arg], &failure_list);
 | 
	
		
			
				|  |  |      } else if (strcmp(argv[arg], "--verbose") == 0) {
 | 
	
		
			
				|  |  | -      for (auto *suite : test_suite_set) {
 | 
	
		
			
				|  |  | -        suite->SetVerbose(true);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      suite->SetVerbose(true);
 | 
	
		
			
				|  |  |      } else if (strcmp(argv[arg], "--enforce_recommended") == 0) {
 | 
	
		
			
				|  |  | -      for (auto suite : test_suite_set) {
 | 
	
		
			
				|  |  | -        suite->SetEnforceRecommended(true);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      suite->SetEnforceRecommended(true);
 | 
	
		
			
				|  |  |      } else if (argv[arg][0] == '-') {
 | 
	
		
			
				|  |  |        fprintf(stderr, "Unknown option: %s\n", argv[arg]);
 | 
	
		
			
				|  |  |        UsageError();
 | 
	
	
		
			
				|  | @@ -318,18 +201,97 @@ int main(int argc, char *argv[]) {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  for (auto suite : test_suite_set) {
 | 
	
		
			
				|  |  | -    suite->SetFailureList(failure_list_filename, failure_list);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  suite->SetFailureList(failure_list_filename, failure_list);
 | 
	
		
			
				|  |  |    ForkPipeRunner runner(program);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    std::string output;
 | 
	
		
			
				|  |  | -  bool ok = true;
 | 
	
		
			
				|  |  | -  for (auto suite : test_suite_set) {
 | 
	
		
			
				|  |  | -    ok &= suite->RunSuite(&runner, &output);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  bool ok =  suite->RunSuite(&runner, &output);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    fwrite(output.c_str(), 1, output.size(), stderr);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return ok ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void ForkPipeRunner::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]));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    std::unique_ptr<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 ForkPipeRunner::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 ForkPipeRunner::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 ForkPipeRunner::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);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace protobuf
 | 
	
		
			
				|  |  | +}  // namespace google
 |