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 |