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