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<deUint8> 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<deUint8> & dst,size_t numBytes)85 void readBytes (de::Socket& socket, vector<deUint8>& 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<deUint8> 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: return new KeepAliveMessage();
116 case MESSAGETYPE_PROCESS_STARTED: return new ProcessStartedMessage();
117 default:
118 break; // Read message with data.
119 }
120
121 vector<deUint8> messageBuf;
122 readBytes(socket, messageBuf, messageSize-MESSAGE_HEADER_SIZE);
123
124 switch (type)
125 {
126 case MESSAGETYPE_HELLO: return new HelloMessage(&messageBuf[0], (int)messageBuf.size());
127 case MESSAGETYPE_TEST: return new TestMessage(&messageBuf[0], (int)messageBuf.size());
128 case MESSAGETYPE_PROCESS_LOG_DATA: return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size());
129 case MESSAGETYPE_INFO: return new InfoMessage(&messageBuf[0], (int)messageBuf.size());
130 case MESSAGETYPE_PROCESS_LAUNCH_FAILED: return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size());
131 case MESSAGETYPE_PROCESS_FINISHED: return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size());
132 default:
133 XS_FAIL("Unknown message");
134 }
135 }
136
137 class CommandLine
138 {
139 public:
140 de::SocketAddress address;
141 std::string program;
142 std::string params;
143 std::string workingDir;
144 std::string caseList;
145 std::string dstFileName;
146 };
147
148 class Client
149 {
150 public:
151 Client (const CommandLine& cmdLine);
152 ~Client (void);
153
154 void run (void);
155
156 private:
157 const CommandLine& m_cmdLine;
158 de::Socket m_socket;
159 };
160
Client(const CommandLine & cmdLine)161 Client::Client (const CommandLine& cmdLine)
162 : m_cmdLine(cmdLine)
163 {
164 }
165
~Client(void)166 Client::~Client (void)
167 {
168 }
169
run(void)170 void Client::run (void)
171 {
172 // Connect to server.
173 m_socket.connect(m_cmdLine.address);
174
175 printf("Connected to %s:%d!\n", m_cmdLine.address.getHost(), m_cmdLine.address.getPort());
176
177 // Open result file.
178 std::fstream out(m_cmdLine.dstFileName.c_str(), std::fstream::out|std::fstream::binary);
179
180 printf(" writing to %s\n", m_cmdLine.dstFileName.c_str());
181
182 // Send execution request.
183 {
184 ExecuteBinaryMessage msg;
185
186 msg.name = m_cmdLine.program;
187 msg.params = m_cmdLine.params;
188 msg.workDir = m_cmdLine.workingDir;
189 msg.caseList = m_cmdLine.caseList;
190
191 sendMessage(m_socket, msg);
192 printf(" execution request sent.\n");
193 }
194
195 // Run client loop.
196 bool isRunning = true;
197 while (isRunning)
198 {
199 ScopedMsgPtr msg(readMessage(m_socket));
200
201 switch (msg->type)
202 {
203 case MESSAGETYPE_HELLO:
204 printf(" HelloMessage\n");
205 break;
206
207 case MESSAGETYPE_KEEPALIVE:
208 {
209 printf(" KeepAliveMessage\n");
210
211 // Reply with keepalive.
212 sendMessage(m_socket, KeepAliveMessage());
213 break;
214 }
215
216 case MESSAGETYPE_INFO:
217 printf(" InfoMessage: '%s'\n", static_cast<InfoMessage*>(msg.get())->info.c_str());
218 break;
219
220 case MESSAGETYPE_PROCESS_STARTED:
221 printf(" ProcessStartedMessage\n");
222 break;
223
224 case MESSAGETYPE_PROCESS_FINISHED:
225 printf(" ProcessFinished: exit code = %d\n", static_cast<ProcessFinishedMessage*>(msg.get())->exitCode);
226 isRunning = false;
227 break;
228
229 case MESSAGETYPE_PROCESS_LAUNCH_FAILED:
230 printf(" ProcessLaunchFailed: '%s'\n", static_cast<ProcessLaunchFailedMessage*>(msg.get())->reason.c_str());
231 isRunning = false;
232 break;
233
234 case MESSAGETYPE_PROCESS_LOG_DATA:
235 {
236 ProcessLogDataMessage* logDataMsg = static_cast<ProcessLogDataMessage*>(msg.get());
237 printf(" ProcessLogDataMessage: %d bytes\n", (int)logDataMsg->logData.length());
238 out << logDataMsg->logData;
239 break;
240 }
241
242 default:
243 XS_FAIL("Unknown message");
244 break;
245 }
246 }
247
248 // Close output file.
249 out.close();
250
251 // Close connection.
252 m_socket.shutdown();
253 m_socket.close();
254
255 printf("Done!\n");
256 }
257
parseString(const char * str)258 string parseString (const char* str)
259 {
260 if (str[0] == '\'' || str[0] == '"')
261 {
262 const char* p = str;
263 char endChar = *p++;
264 std::ostringstream o;
265
266 while (*p != endChar && *p)
267 {
268 if (*p == '\\')
269 {
270 switch (p[1])
271 {
272 case 0: DE_ASSERT(DE_FALSE); break;
273 case 'n': o << '\n'; break;
274 case 't': o << '\t'; break;
275 default: o << p[1]; break;
276 }
277
278 p += 2;
279 }
280 else
281 o << *p++;
282 }
283
284 return o.str();
285 }
286 else
287 return string(str);
288 }
289
printHelp(const char * binName)290 void printHelp (const char* binName)
291 {
292 printf("%s:\n", binName);
293 printf(" --host=[host] Connect to host [host]\n");
294 printf(" --port=[name] Use port [port]\n");
295 printf(" --program=[program] Test program\n");
296 printf(" --params=[params] Test program params\n");
297 printf(" --workdir=[dir] Working directory\n");
298 printf(" --caselist=[caselist] Test case list\n");
299 printf(" --out=filename Test result file\n");
300 }
301
runClient(int argc,const char * const * argv)302 int runClient (int argc, const char* const* argv)
303 {
304 CommandLine cmdLine;
305
306 // Defaults.
307 cmdLine.address.setHost("127.0.0.1");
308 cmdLine.address.setPort(50016);
309 cmdLine.dstFileName = "TestResults.qpa";
310
311 // Parse command line.
312 for (int argNdx = 1; argNdx < argc; argNdx++)
313 {
314 const char* arg = argv[argNdx];
315
316 if (deStringBeginsWith(arg, "--port="))
317 cmdLine.address.setPort(atoi(arg+7));
318 else if (deStringBeginsWith(arg, "--host="))
319 cmdLine.address.setHost(parseString(arg+7).c_str());
320 else if (deStringBeginsWith(arg, "--program="))
321 cmdLine.program = parseString(arg+10);
322 else if (deStringBeginsWith(arg, "--params="))
323 cmdLine.params = parseString(arg+9);
324 else if (deStringBeginsWith(arg, "--workdir="))
325 cmdLine.workingDir = parseString(arg+10);
326 else if (deStringBeginsWith(arg, "--caselist="))
327 cmdLine.caseList = parseString(arg+11);
328 else if (deStringBeginsWith(arg, "--out="))
329 cmdLine.dstFileName = parseString(arg+6);
330 else
331 {
332 printHelp(argv[0]);
333 return -1;
334 }
335 }
336
337 // Run client.
338 try
339 {
340 Client client(cmdLine);
341 client.run();
342 }
343 catch (const std::exception& e)
344 {
345 printf("%s\n", e.what());
346 return -1;
347 }
348
349 return 0;
350 }
351
352 } // xs
353
main(int argc,const char * const * argv)354 int main (int argc, const char* const* argv)
355 {
356 return xs::runClient(argc, argv);
357 }
358