1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Execution Server
3 * ---------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief ExecServer Client.
22 *//*--------------------------------------------------------------------*/
23
24 #include "xsDefs.hpp"
25 #include "xsProtocol.hpp"
26 #include "deSocket.hpp"
27
28 #include "deString.h"
29
30 #include <memory>
31 #include <sstream>
32 #include <fstream>
33 #include <cstdio>
34 #include <cstdlib>
35
36 using std::string;
37 using std::vector;
38
39 namespace xs
40 {
41
42 typedef std::auto_ptr<Message> ScopedMsgPtr;
43
44 class SocketError : public Error
45 {
46 public:
SocketError(deSocketResult result,const char * message,const char * file,int line)47 SocketError (deSocketResult result, const char* message, const char* file, int line)
48 : Error (message, deGetSocketResultName(result), file, line)
49 , m_result (result)
50 {
51 }
52
getResult(void) const53 deSocketResult getResult (void) const
54 {
55 return m_result;
56 }
57
58 private:
59 deSocketResult m_result;
60 };
61
62 // Helpers.
sendMessage(de::Socket & socket,const Message & message)63 void sendMessage (de::Socket& socket, const Message& message)
64 {
65 // Format message.
66 vector<deUint8> buf;
67 message.write(buf);
68
69 // Write to socket.
70 int pos = 0;
71 while (pos < (int)buf.size())
72 {
73 int numLeft = (int)buf.size() - pos;
74 int numSent = 0;
75 deSocketResult result = socket.send(&buf[pos], numLeft, &numSent);
76
77 if (result != DE_SOCKETRESULT_SUCCESS)
78 throw SocketError(result, "send() failed", __FILE__, __LINE__);
79
80 pos += numSent;
81 }
82 }
83
readBytes(de::Socket & socket,vector<deUint8> & dst,int numBytes)84 void readBytes (de::Socket& socket, vector<deUint8>& dst, int numBytes)
85 {
86 int numRead = 0;
87 dst.resize(numBytes);
88 while (numRead < numBytes)
89 {
90 int numLeft = numBytes - numRead;
91 int curNumRead = 0;
92 deSocketResult result = socket.receive(&dst[numRead], numLeft, &curNumRead);
93
94 if (result != DE_SOCKETRESULT_SUCCESS)
95 throw SocketError(result, "receive() failed", __FILE__, __LINE__);
96
97 numRead += curNumRead;
98 }
99 }
100
readMessage(de::Socket & socket)101 Message* readMessage (de::Socket& socket)
102 {
103 // Header.
104 vector<deUint8> header;
105 readBytes(socket, header, MESSAGE_HEADER_SIZE);
106
107 MessageType type;
108 int messageSize;
109 Message::parseHeader(&header[0], (int)header.size(), type, messageSize);
110
111 // Simple messages without any data.
112 switch (type)
113 {
114 case MESSAGETYPE_KEEPALIVE: return new KeepAliveMessage();
115 case MESSAGETYPE_PROCESS_STARTED: return new ProcessStartedMessage();
116 default:
117 break; // Read message with data.
118 }
119
120 vector<deUint8> messageBuf;
121 readBytes(socket, messageBuf, messageSize-MESSAGE_HEADER_SIZE);
122
123 switch (type)
124 {
125 case MESSAGETYPE_HELLO: return new HelloMessage(&messageBuf[0], (int)messageBuf.size());
126 case MESSAGETYPE_TEST: return new TestMessage(&messageBuf[0], (int)messageBuf.size());
127 case MESSAGETYPE_PROCESS_LOG_DATA: return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size());
128 case MESSAGETYPE_INFO: return new InfoMessage(&messageBuf[0], (int)messageBuf.size());
129 case MESSAGETYPE_PROCESS_LAUNCH_FAILED: return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size());
130 case MESSAGETYPE_PROCESS_FINISHED: return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size());
131 default:
132 XS_FAIL("Unknown message");
133 }
134 }
135
136 class CommandLine
137 {
138 public:
139 de::SocketAddress address;
140 std::string program;
141 std::string params;
142 std::string workingDir;
143 std::string caseList;
144 std::string dstFileName;
145 };
146
147 class Client
148 {
149 public:
150 Client (const CommandLine& cmdLine);
151 ~Client (void);
152
153 void run (void);
154
155 private:
156 const CommandLine& m_cmdLine;
157 de::Socket m_socket;
158 };
159
Client(const CommandLine & cmdLine)160 Client::Client (const CommandLine& cmdLine)
161 : m_cmdLine(cmdLine)
162 {
163 }
164
~Client(void)165 Client::~Client (void)
166 {
167 }
168
run(void)169 void Client::run (void)
170 {
171 // Connect to server.
172 m_socket.connect(m_cmdLine.address);
173
174 printf("Connected to %s:%d!\n", m_cmdLine.address.getHost(), m_cmdLine.address.getPort());
175
176 // Open result file.
177 std::fstream out(m_cmdLine.dstFileName.c_str(), std::fstream::out|std::fstream::binary);
178
179 printf(" writing to %s\n", m_cmdLine.dstFileName.c_str());
180
181 // Send execution request.
182 {
183 ExecuteBinaryMessage msg;
184
185 msg.name = m_cmdLine.program;
186 msg.params = m_cmdLine.params;
187 msg.workDir = m_cmdLine.workingDir;
188 msg.caseList = m_cmdLine.caseList;
189
190 sendMessage(m_socket, msg);
191 printf(" execution request sent.\n");
192 }
193
194 // Run client loop.
195 bool isRunning = true;
196 while (isRunning)
197 {
198 ScopedMsgPtr msg(readMessage(m_socket));
199
200 switch (msg->type)
201 {
202 case MESSAGETYPE_HELLO:
203 printf(" HelloMessage\n");
204 break;
205
206 case MESSAGETYPE_KEEPALIVE:
207 {
208 printf(" KeepAliveMessage\n");
209
210 // Reply with keepalive.
211 sendMessage(m_socket, KeepAliveMessage());
212 break;
213 }
214
215 case MESSAGETYPE_INFO:
216 printf(" InfoMessage: '%s'\n", static_cast<InfoMessage*>(msg.get())->info.c_str());
217 break;
218
219 case MESSAGETYPE_PROCESS_STARTED:
220 printf(" ProcessStartedMessage\n");
221 break;
222
223 case MESSAGETYPE_PROCESS_FINISHED:
224 printf(" ProcessFinished: exit code = %d\n", static_cast<ProcessFinishedMessage*>(msg.get())->exitCode);
225 isRunning = false;
226 break;
227
228 case MESSAGETYPE_PROCESS_LAUNCH_FAILED:
229 printf(" ProcessLaunchFailed: '%s'\n", static_cast<ProcessLaunchFailedMessage*>(msg.get())->reason.c_str());
230 isRunning = false;
231 break;
232
233 case MESSAGETYPE_PROCESS_LOG_DATA:
234 {
235 ProcessLogDataMessage* logDataMsg = static_cast<ProcessLogDataMessage*>(msg.get());
236 printf(" ProcessLogDataMessage: %d bytes\n", (int)logDataMsg->logData.length());
237 out << logDataMsg->logData;
238 break;
239 }
240
241 default:
242 XS_FAIL("Unknown message");
243 break;
244 }
245 }
246
247 // Close output file.
248 out.close();
249
250 // Close connection.
251 m_socket.shutdown();
252 m_socket.close();
253
254 printf("Done!\n");
255 }
256
parseString(const char * str)257 string parseString (const char* str)
258 {
259 if (str[0] == '\'' || str[0] == '"')
260 {
261 const char* p = str;
262 char endChar = *p++;
263 std::ostringstream o;
264
265 while (*p != endChar && *p)
266 {
267 if (*p == '\\')
268 {
269 switch (p[1])
270 {
271 case 0: DE_ASSERT(DE_FALSE); break;
272 case 'n': o << '\n'; break;
273 case 't': o << '\t'; break;
274 default: o << p[1]; break;
275 }
276
277 p += 2;
278 }
279 else
280 o << *p++;
281 }
282
283 return o.str();
284 }
285 else
286 return string(str);
287 }
288
printHelp(const char * binName)289 void printHelp (const char* binName)
290 {
291 printf("%s:\n", binName);
292 printf(" --host=[host] Connect to host [host]\n");
293 printf(" --port=[name] Use port [port]\n");
294 printf(" --program=[program] Test program\n");
295 printf(" --params=[params] Test program params\n");
296 printf(" --workdir=[dir] Working directory\n");
297 printf(" --caselist=[caselist] Test case list\n");
298 printf(" --out=filename Test result file\n");
299 }
300
runClient(int argc,const char * const * argv)301 int runClient (int argc, const char* const* argv)
302 {
303 CommandLine cmdLine;
304
305 // Defaults.
306 cmdLine.address.setHost("127.0.0.1");
307 cmdLine.address.setPort(50016);
308 cmdLine.dstFileName = "TestResults.qpa";
309
310 // Parse command line.
311 for (int argNdx = 1; argNdx < argc; argNdx++)
312 {
313 const char* arg = argv[argNdx];
314
315 if (deStringBeginsWith(arg, "--port="))
316 cmdLine.address.setPort(atoi(arg+7));
317 else if (deStringBeginsWith(arg, "--host="))
318 cmdLine.address.setHost(parseString(arg+7).c_str());
319 else if (deStringBeginsWith(arg, "--program="))
320 cmdLine.program = parseString(arg+10);
321 else if (deStringBeginsWith(arg, "--params="))
322 cmdLine.params = parseString(arg+9);
323 else if (deStringBeginsWith(arg, "--workdir="))
324 cmdLine.workingDir = parseString(arg+10);
325 else if (deStringBeginsWith(arg, "--caselist="))
326 cmdLine.caseList = parseString(arg+11);
327 else if (deStringBeginsWith(arg, "--out="))
328 cmdLine.dstFileName = parseString(arg+6);
329 else
330 {
331 printHelp(argv[0]);
332 return -1;
333 }
334 }
335
336 // Run client.
337 try
338 {
339 Client client(cmdLine);
340 client.run();
341 }
342 catch (const std::exception& e)
343 {
344 printf("%s\n", e.what());
345 return -1;
346 }
347
348 return 0;
349 }
350
351 } // xs
352
main(int argc,const char * const * argv)353 int main (int argc, const char* const* argv)
354 {
355 return xs::runClient(argc, argv);
356 }
357