You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
8.3 KiB
358 lines
8.3 KiB
/*-------------------------------------------------------------------------
|
|
* 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 "deUniquePtr.hpp"
|
|
|
|
#include "deString.h"
|
|
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
namespace xs
|
|
{
|
|
|
|
typedef de::UniquePtr<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.
|
|
size_t pos = 0;
|
|
while (pos < buf.size())
|
|
{
|
|
size_t numLeft = buf.size() - pos;
|
|
size_t 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, size_t numBytes)
|
|
{
|
|
size_t numRead = 0;
|
|
dst.resize(numBytes);
|
|
while (numRead < numBytes)
|
|
{
|
|
size_t numLeft = numBytes - numRead;
|
|
size_t 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;
|
|
size_t 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);
|
|
}
|