| OLD | NEW |
| 1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
| 2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
| 3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
| 4 // | 4 // |
| 5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
| 6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
| 7 // met: | 7 // met: |
| 8 // | 8 // |
| 9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
| 10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 48 // Every test consists of a ConformanceRequest/ConformanceResponse | 48 // Every test consists of a ConformanceRequest/ConformanceResponse |
| 49 // request/reply pair. The protocol on the pipe is simply: | 49 // request/reply pair. The protocol on the pipe is simply: |
| 50 // | 50 // |
| 51 // 1. tester sends 4-byte length N (little endian) | 51 // 1. tester sends 4-byte length N (little endian) |
| 52 // 2. tester sends N bytes representing a ConformanceRequest proto | 52 // 2. tester sends N bytes representing a ConformanceRequest proto |
| 53 // 3. testee sends 4-byte length M (little endian) | 53 // 3. testee sends 4-byte length M (little endian) |
| 54 // 4. testee sends M bytes representing a ConformanceResponse proto | 54 // 4. testee sends M bytes representing a ConformanceResponse proto |
| 55 | 55 |
| 56 #include <algorithm> | 56 #include <algorithm> |
| 57 #include <errno.h> | 57 #include <errno.h> |
| 58 #include <fstream> |
| 59 #include <sys/types.h> |
| 60 #include <sys/wait.h> |
| 58 #include <unistd.h> | 61 #include <unistd.h> |
| 59 #include <fstream> | |
| 60 #include <vector> | 62 #include <vector> |
| 61 | 63 |
| 64 #include <google/protobuf/stubs/stringprintf.h> |
| 65 |
| 62 #include "conformance.pb.h" | 66 #include "conformance.pb.h" |
| 63 #include "conformance_test.h" | 67 #include "conformance_test.h" |
| 64 | 68 |
| 65 using conformance::ConformanceRequest; | 69 using conformance::ConformanceRequest; |
| 66 using conformance::ConformanceResponse; | 70 using conformance::ConformanceResponse; |
| 67 using google::protobuf::internal::scoped_array; | 71 using google::protobuf::internal::scoped_array; |
| 72 using google::protobuf::StringAppendF; |
| 68 using std::string; | 73 using std::string; |
| 69 using std::vector; | 74 using std::vector; |
| 70 | 75 |
| 71 #define STRINGIFY(x) #x | 76 #define STRINGIFY(x) #x |
| 72 #define TOSTRING(x) STRINGIFY(x) | 77 #define TOSTRING(x) STRINGIFY(x) |
| 73 #define CHECK_SYSCALL(call) \ | 78 #define CHECK_SYSCALL(call) \ |
| 74 if (call < 0) { \ | 79 if (call < 0) { \ |
| 75 perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \ | 80 perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \ |
| 76 exit(1); \ | 81 exit(1); \ |
| 77 } | 82 } |
| 78 | 83 |
| 79 // Test runner that spawns the process being tested and communicates with it | 84 // Test runner that spawns the process being tested and communicates with it |
| 80 // over a pipe. | 85 // over a pipe. |
| 81 class ForkPipeRunner : public google::protobuf::ConformanceTestRunner { | 86 class ForkPipeRunner : public google::protobuf::ConformanceTestRunner { |
| 82 public: | 87 public: |
| 83 ForkPipeRunner(const std::string &executable) | 88 ForkPipeRunner(const std::string &executable) |
| 84 : running_(false), executable_(executable) {} | 89 : child_pid_(-1), executable_(executable) {} |
| 85 | 90 |
| 86 virtual ~ForkPipeRunner() {} | 91 virtual ~ForkPipeRunner() {} |
| 87 | 92 |
| 88 void RunTest(const std::string& test_name, | 93 void RunTest(const std::string& test_name, |
| 89 const std::string& request, | 94 const std::string& request, |
| 90 std::string* response) { | 95 std::string* response) { |
| 91 if (!running_) { | 96 if (child_pid_ < 0) { |
| 92 SpawnTestProgram(); | 97 SpawnTestProgram(); |
| 93 } | 98 } |
| 94 | 99 |
| 95 current_test_name_ = test_name; | 100 current_test_name_ = test_name; |
| 96 | 101 |
| 97 uint32_t len = request.size(); | 102 uint32_t len = request.size(); |
| 98 CheckedWrite(write_fd_, &len, sizeof(uint32_t)); | 103 CheckedWrite(write_fd_, &len, sizeof(uint32_t)); |
| 99 CheckedWrite(write_fd_, request.c_str(), request.size()); | 104 CheckedWrite(write_fd_, request.c_str(), request.size()); |
| 100 CheckedRead(read_fd_, &len, sizeof(uint32_t)); | 105 |
| 106 if (!TryRead(read_fd_, &len, sizeof(uint32_t))) { |
| 107 // We failed to read from the child, assume a crash and try to reap. |
| 108 GOOGLE_LOG(INFO) << "Trying to reap child, pid=" << child_pid_; |
| 109 |
| 110 int status; |
| 111 waitpid(child_pid_, &status, WEXITED); |
| 112 |
| 113 string error_msg; |
| 114 if (WIFEXITED(status)) { |
| 115 StringAppendF(&error_msg, |
| 116 "child exited, status=%d", WEXITSTATUS(status)); |
| 117 } else if (WIFSIGNALED(status)) { |
| 118 StringAppendF(&error_msg, |
| 119 "child killed by signal %d", WTERMSIG(status)); |
| 120 } |
| 121 GOOGLE_LOG(INFO) << error_msg; |
| 122 child_pid_ = -1; |
| 123 |
| 124 conformance::ConformanceResponse response_obj; |
| 125 response_obj.set_runtime_error(error_msg); |
| 126 response_obj.SerializeToString(response); |
| 127 return; |
| 128 } |
| 129 |
| 101 response->resize(len); | 130 response->resize(len); |
| 102 CheckedRead(read_fd_, (void*)response->c_str(), len); | 131 CheckedRead(read_fd_, (void*)response->c_str(), len); |
| 103 } | 132 } |
| 104 | 133 |
| 105 private: | 134 private: |
| 106 // TODO(haberman): make this work on Windows, instead of using these | 135 // TODO(haberman): make this work on Windows, instead of using these |
| 107 // UNIX-specific APIs. | 136 // UNIX-specific APIs. |
| 108 // | 137 // |
| 109 // There is a platform-agnostic API in | 138 // There is a platform-agnostic API in |
| 110 // src/google/protobuf/compiler/subprocess.h | 139 // src/google/protobuf/compiler/subprocess.h |
| (...skipping 23 matching lines...) Expand all Loading... |
| 134 perror("fork"); | 163 perror("fork"); |
| 135 exit(1); | 164 exit(1); |
| 136 } | 165 } |
| 137 | 166 |
| 138 if (pid) { | 167 if (pid) { |
| 139 // Parent. | 168 // Parent. |
| 140 CHECK_SYSCALL(close(toproc_pipe_fd[0])); | 169 CHECK_SYSCALL(close(toproc_pipe_fd[0])); |
| 141 CHECK_SYSCALL(close(fromproc_pipe_fd[1])); | 170 CHECK_SYSCALL(close(fromproc_pipe_fd[1])); |
| 142 write_fd_ = toproc_pipe_fd[1]; | 171 write_fd_ = toproc_pipe_fd[1]; |
| 143 read_fd_ = fromproc_pipe_fd[0]; | 172 read_fd_ = fromproc_pipe_fd[0]; |
| 144 running_ = true; | 173 child_pid_ = pid; |
| 145 } else { | 174 } else { |
| 146 // Child. | 175 // Child. |
| 147 CHECK_SYSCALL(close(STDIN_FILENO)); | 176 CHECK_SYSCALL(close(STDIN_FILENO)); |
| 148 CHECK_SYSCALL(close(STDOUT_FILENO)); | 177 CHECK_SYSCALL(close(STDOUT_FILENO)); |
| 149 CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO)); | 178 CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO)); |
| 150 CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO)); | 179 CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO)); |
| 151 | 180 |
| 152 CHECK_SYSCALL(close(toproc_pipe_fd[0])); | 181 CHECK_SYSCALL(close(toproc_pipe_fd[0])); |
| 153 CHECK_SYSCALL(close(fromproc_pipe_fd[1])); | 182 CHECK_SYSCALL(close(fromproc_pipe_fd[1])); |
| 154 CHECK_SYSCALL(close(toproc_pipe_fd[1])); | 183 CHECK_SYSCALL(close(toproc_pipe_fd[1])); |
| 155 CHECK_SYSCALL(close(fromproc_pipe_fd[0])); | 184 CHECK_SYSCALL(close(fromproc_pipe_fd[0])); |
| 156 | 185 |
| 157 scoped_array<char> executable(new char[executable_.size() + 1]); | 186 scoped_array<char> executable(new char[executable_.size() + 1]); |
| 158 memcpy(executable.get(), executable_.c_str(), executable_.size()); | 187 memcpy(executable.get(), executable_.c_str(), executable_.size()); |
| 159 executable[executable_.size()] = '\0'; | 188 executable[executable_.size()] = '\0'; |
| 160 | 189 |
| 161 char *const argv[] = {executable.get(), NULL}; | 190 char *const argv[] = {executable.get(), NULL}; |
| 162 CHECK_SYSCALL(execv(executable.get(), argv)); // Never returns. | 191 CHECK_SYSCALL(execv(executable.get(), argv)); // Never returns. |
| 163 } | 192 } |
| 164 } | 193 } |
| 165 | 194 |
| 166 void CheckedWrite(int fd, const void *buf, size_t len) { | 195 void CheckedWrite(int fd, const void *buf, size_t len) { |
| 167 if (write(fd, buf, len) != len) { | 196 if (write(fd, buf, len) != len) { |
| 168 GOOGLE_LOG(FATAL) << current_test_name_ | 197 GOOGLE_LOG(FATAL) << current_test_name_ |
| 169 << ": error writing to test program: " | 198 << ": error writing to test program: " |
| 170 << strerror(errno); | 199 << strerror(errno); |
| 171 } | 200 } |
| 172 } | 201 } |
| 173 | 202 |
| 174 void CheckedRead(int fd, void *buf, size_t len) { | 203 bool TryRead(int fd, void *buf, size_t len) { |
| 175 size_t ofs = 0; | 204 size_t ofs = 0; |
| 176 while (len > 0) { | 205 while (len > 0) { |
| 177 ssize_t bytes_read = read(fd, (char*)buf + ofs, len); | 206 ssize_t bytes_read = read(fd, (char*)buf + ofs, len); |
| 178 | 207 |
| 179 if (bytes_read == 0) { | 208 if (bytes_read == 0) { |
| 180 GOOGLE_LOG(FATAL) << current_test_name_ | 209 GOOGLE_LOG(ERROR) << current_test_name_ |
| 181 << ": unexpected EOF from test program"; | 210 << ": unexpected EOF from test program"; |
| 211 return false; |
| 182 } else if (bytes_read < 0) { | 212 } else if (bytes_read < 0) { |
| 183 GOOGLE_LOG(FATAL) << current_test_name_ | 213 GOOGLE_LOG(ERROR) << current_test_name_ |
| 184 << ": error reading from test program: " | 214 << ": error reading from test program: " |
| 185 << strerror(errno); | 215 << strerror(errno); |
| 216 return false; |
| 186 } | 217 } |
| 187 | 218 |
| 188 len -= bytes_read; | 219 len -= bytes_read; |
| 189 ofs += bytes_read; | 220 ofs += bytes_read; |
| 190 } | 221 } |
| 222 |
| 223 return true; |
| 224 } |
| 225 |
| 226 void CheckedRead(int fd, void *buf, size_t len) { |
| 227 if (!TryRead(fd, buf, len)) { |
| 228 GOOGLE_LOG(FATAL) << current_test_name_ |
| 229 << ": error reading from test program: " |
| 230 << strerror(errno); |
| 231 } |
| 191 } | 232 } |
| 192 | 233 |
| 193 int write_fd_; | 234 int write_fd_; |
| 194 int read_fd_; | 235 int read_fd_; |
| 195 bool running_; | 236 pid_t child_pid_; |
| 196 std::string executable_; | 237 std::string executable_; |
| 197 std::string current_test_name_; | 238 std::string current_test_name_; |
| 198 }; | 239 }; |
| 199 | 240 |
| 200 void UsageError() { | 241 void UsageError() { |
| 201 fprintf(stderr, | 242 fprintf(stderr, |
| 202 "Usage: conformance-test-runner [options] <test-program>\n"); | 243 "Usage: conformance-test-runner [options] <test-program>\n"); |
| 203 fprintf(stderr, "\n"); | 244 fprintf(stderr, "\n"); |
| 204 fprintf(stderr, "Options:\n"); | 245 fprintf(stderr, "Options:\n"); |
| 205 fprintf(stderr, | 246 fprintf(stderr, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 232 if (!line.empty()) { | 273 if (!line.empty()) { |
| 233 failure_list->push_back(line); | 274 failure_list->push_back(line); |
| 234 } | 275 } |
| 235 } | 276 } |
| 236 } | 277 } |
| 237 | 278 |
| 238 int main(int argc, char *argv[]) { | 279 int main(int argc, char *argv[]) { |
| 239 char *program; | 280 char *program; |
| 240 google::protobuf::ConformanceTestSuite suite; | 281 google::protobuf::ConformanceTestSuite suite; |
| 241 | 282 |
| 283 vector<string> failure_list; |
| 284 |
| 242 for (int arg = 1; arg < argc; ++arg) { | 285 for (int arg = 1; arg < argc; ++arg) { |
| 243 if (strcmp(argv[arg], "--failure_list") == 0) { | 286 if (strcmp(argv[arg], "--failure_list") == 0) { |
| 244 if (++arg == argc) UsageError(); | 287 if (++arg == argc) UsageError(); |
| 245 vector<string> failure_list; | |
| 246 ParseFailureList(argv[arg], &failure_list); | 288 ParseFailureList(argv[arg], &failure_list); |
| 247 suite.SetFailureList(failure_list); | |
| 248 } else if (strcmp(argv[arg], "--verbose") == 0) { | 289 } else if (strcmp(argv[arg], "--verbose") == 0) { |
| 249 suite.SetVerbose(true); | 290 suite.SetVerbose(true); |
| 250 } else if (argv[arg][0] == '-') { | 291 } else if (argv[arg][0] == '-') { |
| 251 fprintf(stderr, "Unknown option: %s\n", argv[arg]); | 292 fprintf(stderr, "Unknown option: %s\n", argv[arg]); |
| 252 UsageError(); | 293 UsageError(); |
| 253 } else { | 294 } else { |
| 254 if (arg != argc - 1) { | 295 if (arg != argc - 1) { |
| 255 fprintf(stderr, "Too many arguments.\n"); | 296 fprintf(stderr, "Too many arguments.\n"); |
| 256 UsageError(); | 297 UsageError(); |
| 257 } | 298 } |
| 258 program = argv[arg]; | 299 program = argv[arg]; |
| 259 } | 300 } |
| 260 } | 301 } |
| 261 | 302 |
| 303 suite.SetFailureList(failure_list); |
| 262 ForkPipeRunner runner(program); | 304 ForkPipeRunner runner(program); |
| 263 | 305 |
| 264 std::string output; | 306 std::string output; |
| 265 bool ok = suite.RunSuite(&runner, &output); | 307 bool ok = suite.RunSuite(&runner, &output); |
| 266 | 308 |
| 267 fwrite(output.c_str(), 1, output.size(), stderr); | 309 fwrite(output.c_str(), 1, output.size(), stderr); |
| 268 | 310 |
| 269 return ok ? EXIT_SUCCESS : EXIT_FAILURE; | 311 return ok ? EXIT_SUCCESS : EXIT_FAILURE; |
| 270 } | 312 } |
| OLD | NEW |