| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Execution Server |
| * --------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief ExecServer Client. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "xsDefs.hpp" |
| #include "xsProtocol.hpp" |
| #include "deSocket.hpp" |
| |
| #include "deString.h" |
| |
| #include <memory> |
| #include <sstream> |
| #include <fstream> |
| #include <cstdio> |
| #include <cstdlib> |
| |
| using std::string; |
| using std::vector; |
| |
| namespace xs |
| { |
| |
| typedef std::auto_ptr<Message> ScopedMsgPtr; |
| |
| class SocketError : public Error |
| { |
| public: |
| SocketError (deSocketResult result, const char* message, const char* file, int line) |
| : Error (message, deGetSocketResultName(result), file, line) |
| , m_result (result) |
| { |
| } |
| |
| deSocketResult getResult (void) const |
| { |
| return m_result; |
| } |
| |
| private: |
| deSocketResult m_result; |
| }; |
| |
| // Helpers. |
| void sendMessage (de::Socket& socket, const Message& message) |
| { |
| // Format message. |
| vector<deUint8> buf; |
| message.write(buf); |
| |
| // Write to socket. |
| int pos = 0; |
| while (pos < (int)buf.size()) |
| { |
| int numLeft = (int)buf.size() - pos; |
| int numSent = 0; |
| deSocketResult result = socket.send(&buf[pos], numLeft, &numSent); |
| |
| if (result != DE_SOCKETRESULT_SUCCESS) |
| throw SocketError(result, "send() failed", __FILE__, __LINE__); |
| |
| pos += numSent; |
| } |
| } |
| |
| void readBytes (de::Socket& socket, vector<deUint8>& dst, int numBytes) |
| { |
| int numRead = 0; |
| dst.resize(numBytes); |
| while (numRead < numBytes) |
| { |
| int numLeft = numBytes - numRead; |
| int curNumRead = 0; |
| deSocketResult result = socket.receive(&dst[numRead], numLeft, &curNumRead); |
| |
| if (result != DE_SOCKETRESULT_SUCCESS) |
| throw SocketError(result, "receive() failed", __FILE__, __LINE__); |
| |
| numRead += curNumRead; |
| } |
| } |
| |
| Message* readMessage (de::Socket& socket) |
| { |
| // Header. |
| vector<deUint8> header; |
| readBytes(socket, header, MESSAGE_HEADER_SIZE); |
| |
| MessageType type; |
| int messageSize; |
| Message::parseHeader(&header[0], (int)header.size(), type, messageSize); |
| |
| // Simple messages without any data. |
| switch (type) |
| { |
| case MESSAGETYPE_KEEPALIVE: return new KeepAliveMessage(); |
| case MESSAGETYPE_PROCESS_STARTED: return new ProcessStartedMessage(); |
| default: |
| break; // Read message with data. |
| } |
| |
| vector<deUint8> messageBuf; |
| readBytes(socket, messageBuf, messageSize-MESSAGE_HEADER_SIZE); |
| |
| switch (type) |
| { |
| case MESSAGETYPE_HELLO: return new HelloMessage(&messageBuf[0], (int)messageBuf.size()); |
| case MESSAGETYPE_TEST: return new TestMessage(&messageBuf[0], (int)messageBuf.size()); |
| case MESSAGETYPE_PROCESS_LOG_DATA: return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size()); |
| case MESSAGETYPE_INFO: return new InfoMessage(&messageBuf[0], (int)messageBuf.size()); |
| case MESSAGETYPE_PROCESS_LAUNCH_FAILED: return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size()); |
| case MESSAGETYPE_PROCESS_FINISHED: return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size()); |
| default: |
| XS_FAIL("Unknown message"); |
| } |
| } |
| |
| class CommandLine |
| { |
| public: |
| de::SocketAddress address; |
| std::string program; |
| std::string params; |
| std::string workingDir; |
| std::string caseList; |
| std::string dstFileName; |
| }; |
| |
| class Client |
| { |
| public: |
| Client (const CommandLine& cmdLine); |
| ~Client (void); |
| |
| void run (void); |
| |
| private: |
| const CommandLine& m_cmdLine; |
| de::Socket m_socket; |
| }; |
| |
| Client::Client (const CommandLine& cmdLine) |
| : m_cmdLine(cmdLine) |
| { |
| } |
| |
| Client::~Client (void) |
| { |
| } |
| |
| void Client::run (void) |
| { |
| // Connect to server. |
| m_socket.connect(m_cmdLine.address); |
| |
| printf("Connected to %s:%d!\n", m_cmdLine.address.getHost(), m_cmdLine.address.getPort()); |
| |
| // Open result file. |
| std::fstream out(m_cmdLine.dstFileName.c_str(), std::fstream::out|std::fstream::binary); |
| |
| printf(" writing to %s\n", m_cmdLine.dstFileName.c_str()); |
| |
| // Send execution request. |
| { |
| ExecuteBinaryMessage msg; |
| |
| msg.name = m_cmdLine.program; |
| msg.params = m_cmdLine.params; |
| msg.workDir = m_cmdLine.workingDir; |
| msg.caseList = m_cmdLine.caseList; |
| |
| sendMessage(m_socket, msg); |
| printf(" execution request sent.\n"); |
| } |
| |
| // Run client loop. |
| bool isRunning = true; |
| while (isRunning) |
| { |
| ScopedMsgPtr msg(readMessage(m_socket)); |
| |
| switch (msg->type) |
| { |
| case MESSAGETYPE_HELLO: |
| printf(" HelloMessage\n"); |
| break; |
| |
| case MESSAGETYPE_KEEPALIVE: |
| { |
| printf(" KeepAliveMessage\n"); |
| |
| // Reply with keepalive. |
| sendMessage(m_socket, KeepAliveMessage()); |
| break; |
| } |
| |
| case MESSAGETYPE_INFO: |
| printf(" InfoMessage: '%s'\n", static_cast<InfoMessage*>(msg.get())->info.c_str()); |
| break; |
| |
| case MESSAGETYPE_PROCESS_STARTED: |
| printf(" ProcessStartedMessage\n"); |
| break; |
| |
| case MESSAGETYPE_PROCESS_FINISHED: |
| printf(" ProcessFinished: exit code = %d\n", static_cast<ProcessFinishedMessage*>(msg.get())->exitCode); |
| isRunning = false; |
| break; |
| |
| case MESSAGETYPE_PROCESS_LAUNCH_FAILED: |
| printf(" ProcessLaunchFailed: '%s'\n", static_cast<ProcessLaunchFailedMessage*>(msg.get())->reason.c_str()); |
| isRunning = false; |
| break; |
| |
| case MESSAGETYPE_PROCESS_LOG_DATA: |
| { |
| ProcessLogDataMessage* logDataMsg = static_cast<ProcessLogDataMessage*>(msg.get()); |
| printf(" ProcessLogDataMessage: %d bytes\n", (int)logDataMsg->logData.length()); |
| out << logDataMsg->logData; |
| break; |
| } |
| |
| default: |
| XS_FAIL("Unknown message"); |
| break; |
| } |
| } |
| |
| // Close output file. |
| out.close(); |
| |
| // Close connection. |
| m_socket.shutdown(); |
| m_socket.close(); |
| |
| printf("Done!\n"); |
| } |
| |
| string parseString (const char* str) |
| { |
| if (str[0] == '\'' || str[0] == '"') |
| { |
| const char* p = str; |
| char endChar = *p++; |
| std::ostringstream o; |
| |
| while (*p != endChar && *p) |
| { |
| if (*p == '\\') |
| { |
| switch (p[1]) |
| { |
| case 0: DE_ASSERT(DE_FALSE); break; |
| case 'n': o << '\n'; break; |
| case 't': o << '\t'; break; |
| default: o << p[1]; break; |
| } |
| |
| p += 2; |
| } |
| else |
| o << *p++; |
| } |
| |
| return o.str(); |
| } |
| else |
| return string(str); |
| } |
| |
| void printHelp (const char* binName) |
| { |
| printf("%s:\n", binName); |
| printf(" --host=[host] Connect to host [host]\n"); |
| printf(" --port=[name] Use port [port]\n"); |
| printf(" --program=[program] Test program\n"); |
| printf(" --params=[params] Test program params\n"); |
| printf(" --workdir=[dir] Working directory\n"); |
| printf(" --caselist=[caselist] Test case list\n"); |
| printf(" --out=filename Test result file\n"); |
| } |
| |
| int runClient (int argc, const char* const* argv) |
| { |
| CommandLine cmdLine; |
| |
| // Defaults. |
| cmdLine.address.setHost("127.0.0.1"); |
| cmdLine.address.setPort(50016); |
| cmdLine.dstFileName = "TestResults.qpa"; |
| |
| // Parse command line. |
| for (int argNdx = 1; argNdx < argc; argNdx++) |
| { |
| const char* arg = argv[argNdx]; |
| |
| if (deStringBeginsWith(arg, "--port=")) |
| cmdLine.address.setPort(atoi(arg+7)); |
| else if (deStringBeginsWith(arg, "--host=")) |
| cmdLine.address.setHost(parseString(arg+7).c_str()); |
| else if (deStringBeginsWith(arg, "--program=")) |
| cmdLine.program = parseString(arg+10); |
| else if (deStringBeginsWith(arg, "--params=")) |
| cmdLine.params = parseString(arg+9); |
| else if (deStringBeginsWith(arg, "--workdir=")) |
| cmdLine.workingDir = parseString(arg+10); |
| else if (deStringBeginsWith(arg, "--caselist=")) |
| cmdLine.caseList = parseString(arg+11); |
| else if (deStringBeginsWith(arg, "--out=")) |
| cmdLine.dstFileName = parseString(arg+6); |
| else |
| { |
| printHelp(argv[0]); |
| return -1; |
| } |
| } |
| |
| // Run client. |
| try |
| { |
| Client client(cmdLine); |
| client.run(); |
| } |
| catch (const std::exception& e) |
| { |
| printf("%s\n", e.what()); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| } // xs |
| |
| int main (int argc, const char* const* argv) |
| { |
| return xs::runClient(argc, argv); |
| } |