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.
1034 lines
24 KiB
1034 lines
24 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 Tests. |
|
*//*--------------------------------------------------------------------*/ |
|
|
|
#include "xsDefs.hpp" |
|
|
|
#include "xsProtocol.hpp" |
|
#include "deSocket.hpp" |
|
#include "deRingBuffer.hpp" |
|
#include "deFilePath.hpp" |
|
#include "deBlockBuffer.hpp" |
|
#include "deThread.hpp" |
|
#include "deStringUtil.hpp" |
|
#include "deUniquePtr.hpp" |
|
|
|
#include "deClock.h" |
|
#include "deProcess.h" |
|
#include "deString.h" |
|
#include "deRandom.h" |
|
|
|
#include <memory> |
|
#include <algorithm> |
|
|
|
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 TestClock |
|
{ |
|
public: |
|
inline TestClock (void) |
|
{ |
|
reset(); |
|
} |
|
|
|
inline void reset (void) |
|
{ |
|
m_initTime = deGetMicroseconds(); |
|
} |
|
|
|
inline int getMilliseconds (void) |
|
{ |
|
return (int)((deGetMicroseconds() - m_initTime) / 1000); |
|
} |
|
|
|
private: |
|
deUint64 m_initTime; |
|
}; |
|
|
|
class TestContext |
|
{ |
|
public: |
|
TestContext (void) : startServer(false) {} |
|
|
|
std::string serverPath; |
|
std::string testerPath; |
|
de::SocketAddress address; |
|
bool startServer; |
|
|
|
// Passed from execserver. |
|
std::string logFileName; |
|
std::string caseList; |
|
|
|
private: |
|
TestContext (const TestContext& other); |
|
TestContext& operator= (const TestContext& other); |
|
}; |
|
|
|
class TestCase |
|
{ |
|
public: |
|
TestCase (TestContext& testCtx, const char* name) : m_testCtx(testCtx), m_name(name) {} |
|
virtual ~TestCase (void) {} |
|
|
|
const char* getName (void) const { return m_name.c_str(); } |
|
|
|
virtual void runClient (de::Socket& socket) = DE_NULL; |
|
virtual void runProgram (void) = DE_NULL; |
|
|
|
protected: |
|
TestContext& m_testCtx; |
|
std::string m_name; |
|
}; |
|
|
|
class TestExecutor |
|
{ |
|
public: |
|
TestExecutor (TestContext& testCtx); |
|
~TestExecutor (void); |
|
|
|
void runCases (const std::vector<TestCase*>& testCases); |
|
bool runCase (TestCase* testCase); |
|
|
|
private: |
|
TestContext& m_testCtx; |
|
}; |
|
|
|
TestExecutor::TestExecutor (TestContext& testCtx) |
|
: m_testCtx(testCtx) |
|
{ |
|
} |
|
|
|
TestExecutor::~TestExecutor (void) |
|
{ |
|
} |
|
|
|
void TestExecutor::runCases (const std::vector<TestCase*>& testCases) |
|
{ |
|
int numPassed = 0; |
|
int numCases = (int)testCases.size(); |
|
|
|
for (std::vector<TestCase*>::const_iterator i = testCases.begin(); i != testCases.end(); i++) |
|
{ |
|
if (runCase(*i)) |
|
numPassed += 1; |
|
} |
|
|
|
printf("\n %d/%d passed!\n", numPassed, numCases); |
|
} |
|
|
|
class FilePrinter : public de::Thread |
|
{ |
|
public: |
|
FilePrinter (void) |
|
: m_curFile(DE_NULL) |
|
{ |
|
} |
|
|
|
void start (deFile* file) |
|
{ |
|
DE_ASSERT(!m_curFile); |
|
m_curFile = file; |
|
de::Thread::start(); |
|
} |
|
|
|
void run (void) |
|
{ |
|
char buf[256]; |
|
deInt64 numRead = 0; |
|
|
|
while (deFile_read(m_curFile, &buf[0], (deInt64)sizeof(buf), &numRead) == DE_FILERESULT_SUCCESS) |
|
fwrite(&buf[0], 1, (size_t)numRead, stdout); |
|
|
|
m_curFile = DE_NULL; |
|
} |
|
|
|
private: |
|
deFile* m_curFile; |
|
}; |
|
|
|
bool TestExecutor::runCase (TestCase* testCase) |
|
{ |
|
printf("%s\n", testCase->getName()); |
|
|
|
bool success = false; |
|
deProcess* serverProc = DE_NULL; |
|
FilePrinter stdoutPrinter; |
|
FilePrinter stderrPrinter; |
|
|
|
try |
|
{ |
|
if (m_testCtx.startServer) |
|
{ |
|
string cmdLine = m_testCtx.serverPath + " --port=" + de::toString(m_testCtx.address.getPort()); |
|
serverProc = deProcess_create(); |
|
XS_CHECK(serverProc); |
|
|
|
if (!deProcess_start(serverProc, cmdLine.c_str(), DE_NULL)) |
|
{ |
|
string errMsg = deProcess_getLastError(serverProc); |
|
deProcess_destroy(serverProc); |
|
XS_FAIL(errMsg.c_str()); |
|
} |
|
|
|
deSleep(200); /* Give 200ms for server to start. */ |
|
XS_CHECK(deProcess_isRunning(serverProc)); |
|
|
|
// Start stdout/stderr printers. |
|
stdoutPrinter.start(deProcess_getStdOut(serverProc)); |
|
stderrPrinter.start(deProcess_getStdErr(serverProc)); |
|
} |
|
|
|
// Connect. |
|
de::Socket socket; |
|
socket.connect(m_testCtx.address); |
|
|
|
// Flags. |
|
socket.setFlags(DE_SOCKET_CLOSE_ON_EXEC); |
|
|
|
// Run case. |
|
testCase->runClient(socket); |
|
|
|
// Disconnect. |
|
if (socket.isConnected()) |
|
socket.shutdown(); |
|
|
|
// Kill server. |
|
if (serverProc && deProcess_isRunning(serverProc)) |
|
{ |
|
XS_CHECK(deProcess_terminate(serverProc)); |
|
deSleep(100); |
|
XS_CHECK(deProcess_waitForFinish(serverProc)); |
|
|
|
stdoutPrinter.join(); |
|
stderrPrinter.join(); |
|
} |
|
|
|
success = true; |
|
} |
|
catch (const std::exception& e) |
|
{ |
|
printf("FAIL: %s\n\n", e.what()); |
|
} |
|
|
|
if (serverProc) |
|
deProcess_destroy(serverProc); |
|
|
|
return success; |
|
} |
|
|
|
class ConnectTest : public TestCase |
|
{ |
|
public: |
|
ConnectTest (TestContext& testCtx) |
|
: TestCase(testCtx, "connect") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
DE_UNREF(socket); |
|
} |
|
|
|
void runProgram (void) { /* nothing */ } |
|
}; |
|
|
|
class HelloTest : public TestCase |
|
{ |
|
public: |
|
HelloTest (TestContext& testCtx) |
|
: TestCase(testCtx, "hello") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::HelloMessage msg; |
|
sendMessage(socket, (const xs::Message&)msg); |
|
} |
|
|
|
void runProgram (void) { /* nothing */ } |
|
}; |
|
|
|
class ExecFailTest : public TestCase |
|
{ |
|
public: |
|
ExecFailTest (TestContext& testCtx) |
|
: TestCase(testCtx, "exec-fail") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::ExecuteBinaryMessage execMsg; |
|
execMsg.name = "foobar-notfound"; |
|
execMsg.params = ""; |
|
execMsg.caseList = ""; |
|
execMsg.workDir = ""; |
|
|
|
sendMessage(socket, execMsg); |
|
|
|
const int timeout = 100; // 100ms. |
|
TestClock clock; |
|
|
|
for (;;) |
|
{ |
|
if (clock.getMilliseconds() > timeout) |
|
XS_FAIL("Didn't receive PROCESS_LAUNCH_FAILED"); |
|
|
|
ScopedMsgPtr msg(readMessage(socket)); |
|
|
|
if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED) |
|
break; |
|
else if (msg->type == MESSAGETYPE_KEEPALIVE) |
|
continue; |
|
else |
|
XS_FAIL("Invalid message"); |
|
} |
|
} |
|
|
|
void runProgram (void) { /* nothing */ } |
|
}; |
|
|
|
class SimpleExecTest : public TestCase |
|
{ |
|
public: |
|
SimpleExecTest (TestContext& testCtx) |
|
: TestCase(testCtx, "simple-exec") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::ExecuteBinaryMessage execMsg; |
|
execMsg.name = m_testCtx.testerPath; |
|
execMsg.params = "--program=simple-exec"; |
|
execMsg.caseList = ""; |
|
execMsg.workDir = ""; |
|
|
|
sendMessage(socket, execMsg); |
|
|
|
const int timeout = 5000; // 5s. |
|
TestClock clock; |
|
|
|
bool gotProcessStarted = false; |
|
bool gotProcessFinished = false; |
|
|
|
for (;;) |
|
{ |
|
if (clock.getMilliseconds() > timeout) |
|
break; |
|
|
|
ScopedMsgPtr msg(readMessage(socket)); |
|
|
|
if (msg->type == MESSAGETYPE_PROCESS_STARTED) |
|
gotProcessStarted = true; |
|
else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED) |
|
XS_FAIL("Got PROCESS_LAUNCH_FAILED"); |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED) |
|
{ |
|
gotProcessFinished = true; |
|
break; |
|
} |
|
else if (msg->type == MESSAGETYPE_KEEPALIVE || msg->type == MESSAGETYPE_INFO) |
|
continue; |
|
else |
|
XS_FAIL((string("Invalid message: ") + de::toString(msg->type)).c_str()); |
|
} |
|
|
|
if (!gotProcessStarted) |
|
XS_FAIL("Did't get PROCESS_STARTED message"); |
|
|
|
if (!gotProcessFinished) |
|
XS_FAIL("Did't get PROCESS_FINISHED message"); |
|
} |
|
|
|
void runProgram (void) { /* print nothing. */ } |
|
}; |
|
|
|
class InfoTest : public TestCase |
|
{ |
|
public: |
|
std::string infoStr; |
|
|
|
InfoTest (TestContext& testCtx) |
|
: TestCase (testCtx, "info") |
|
, infoStr ("Hello, World") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::ExecuteBinaryMessage execMsg; |
|
execMsg.name = m_testCtx.testerPath; |
|
execMsg.params = "--program=info"; |
|
execMsg.caseList = ""; |
|
execMsg.workDir = ""; |
|
|
|
sendMessage(socket, execMsg); |
|
|
|
const int timeout = 10000; // 10s. |
|
TestClock clock; |
|
|
|
bool gotProcessStarted = false; |
|
bool gotProcessFinished = false; |
|
std::string receivedInfo = ""; |
|
|
|
for (;;) |
|
{ |
|
if (clock.getMilliseconds() > timeout) |
|
break; |
|
|
|
ScopedMsgPtr msg(readMessage(socket)); |
|
|
|
if (msg->type == MESSAGETYPE_PROCESS_STARTED) |
|
gotProcessStarted = true; |
|
else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED) |
|
XS_FAIL("Got PROCESS_LAUNCH_FAILED"); |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_INFO) |
|
receivedInfo += static_cast<const InfoMessage*>(msg.get())->info; |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED) |
|
{ |
|
gotProcessFinished = true; |
|
break; |
|
} |
|
else if (msg->type == MESSAGETYPE_KEEPALIVE) |
|
continue; |
|
else |
|
XS_FAIL("Invalid message"); |
|
} |
|
|
|
if (!gotProcessStarted) |
|
XS_FAIL("Did't get PROCESS_STARTED message"); |
|
|
|
if (!gotProcessFinished) |
|
XS_FAIL("Did't get PROCESS_FINISHED message"); |
|
|
|
if (receivedInfo != infoStr) |
|
XS_FAIL("Info data doesn't match"); |
|
} |
|
|
|
void runProgram (void) { printf("%s", infoStr.c_str()); } |
|
}; |
|
|
|
class LogDataTest : public TestCase |
|
{ |
|
public: |
|
LogDataTest (TestContext& testCtx) |
|
: TestCase(testCtx, "logdata") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::ExecuteBinaryMessage execMsg; |
|
execMsg.name = m_testCtx.testerPath; |
|
execMsg.params = "--program=logdata"; |
|
execMsg.caseList = ""; |
|
execMsg.workDir = ""; |
|
|
|
sendMessage(socket, execMsg); |
|
|
|
const int timeout = 10000; // 10s. |
|
TestClock clock; |
|
|
|
bool gotProcessStarted = false; |
|
bool gotProcessFinished = false; |
|
std::string receivedData = ""; |
|
|
|
for (;;) |
|
{ |
|
if (clock.getMilliseconds() > timeout) |
|
break; |
|
|
|
ScopedMsgPtr msg(readMessage(socket)); |
|
|
|
if (msg->type == MESSAGETYPE_PROCESS_STARTED) |
|
gotProcessStarted = true; |
|
else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED) |
|
XS_FAIL("Got PROCESS_LAUNCH_FAILED"); |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_LOG_DATA) |
|
receivedData += static_cast<const ProcessLogDataMessage*>(msg.get())->logData; |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED) |
|
{ |
|
gotProcessFinished = true; |
|
break; |
|
} |
|
else if (msg->type == MESSAGETYPE_KEEPALIVE) |
|
continue; |
|
else if (msg->type == MESSAGETYPE_INFO) |
|
XS_FAIL(static_cast<const InfoMessage*>(msg.get())->info.c_str()); |
|
else |
|
XS_FAIL("Invalid message"); |
|
} |
|
|
|
if (!gotProcessStarted) |
|
XS_FAIL("Did't get PROCESS_STARTED message"); |
|
|
|
if (!gotProcessFinished) |
|
XS_FAIL("Did't get PROCESS_FINISHED message"); |
|
|
|
const char* expected = "Foo\nBar\n"; |
|
if (receivedData != expected) |
|
{ |
|
printf(" received: '%s'\n expected: '%s'\n", receivedData.c_str(), expected); |
|
XS_FAIL("Log data doesn't match"); |
|
} |
|
} |
|
|
|
void runProgram (void) |
|
{ |
|
deFile* file = deFile_create(m_testCtx.logFileName.c_str(), DE_FILEMODE_OPEN|DE_FILEMODE_CREATE|DE_FILEMODE_TRUNCATE|DE_FILEMODE_WRITE); |
|
XS_CHECK(file); |
|
|
|
const char line0[] = "Foo\n"; |
|
const char line1[] = "Bar\n"; |
|
deInt64 numWritten = 0; |
|
|
|
// Write first line. |
|
XS_CHECK(deFile_write(file, line0, sizeof(line0)-1, &numWritten) == DE_FILERESULT_SUCCESS); |
|
XS_CHECK(numWritten == sizeof(line0)-1); |
|
|
|
// Sleep for 0.5s and write line 2. |
|
deSleep(500); |
|
XS_CHECK(deFile_write(file, line1, sizeof(line1)-1, &numWritten) == DE_FILERESULT_SUCCESS); |
|
XS_CHECK(numWritten == sizeof(line1)-1); |
|
|
|
deFile_destroy(file); |
|
} |
|
}; |
|
|
|
class BigLogDataTest : public TestCase |
|
{ |
|
public: |
|
enum |
|
{ |
|
DATA_SIZE = 100*1024*1024 |
|
}; |
|
|
|
BigLogDataTest (TestContext& testCtx) |
|
: TestCase(testCtx, "biglogdata") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
xs::ExecuteBinaryMessage execMsg; |
|
execMsg.name = m_testCtx.testerPath; |
|
execMsg.params = "--program=biglogdata"; |
|
execMsg.caseList = ""; |
|
execMsg.workDir = ""; |
|
|
|
sendMessage(socket, execMsg); |
|
|
|
const int timeout = 30000; // 30s. |
|
TestClock clock; |
|
|
|
bool gotProcessStarted = false; |
|
bool gotProcessFinished = false; |
|
int receivedBytes = 0; |
|
|
|
for (;;) |
|
{ |
|
if (clock.getMilliseconds() > timeout) |
|
break; |
|
|
|
ScopedMsgPtr msg(readMessage(socket)); |
|
|
|
if (msg->type == MESSAGETYPE_PROCESS_STARTED) |
|
gotProcessStarted = true; |
|
else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED) |
|
XS_FAIL("Got PROCESS_LAUNCH_FAILED"); |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_LOG_DATA) |
|
receivedBytes += (int)static_cast<const ProcessLogDataMessage*>(msg.get())->logData.length(); |
|
else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED) |
|
{ |
|
gotProcessFinished = true; |
|
break; |
|
} |
|
else if (msg->type == MESSAGETYPE_KEEPALIVE) |
|
{ |
|
// Reply with keepalive. |
|
sendMessage(socket, KeepAliveMessage()); |
|
continue; |
|
} |
|
else if (msg->type == MESSAGETYPE_INFO) |
|
printf("%s", static_cast<const InfoMessage*>(msg.get())->info.c_str()); |
|
else |
|
XS_FAIL("Invalid message"); |
|
} |
|
|
|
if (!gotProcessStarted) |
|
XS_FAIL("Did't get PROCESS_STARTED message"); |
|
|
|
if (!gotProcessFinished) |
|
XS_FAIL("Did't get PROCESS_FINISHED message"); |
|
|
|
if (receivedBytes != DATA_SIZE) |
|
{ |
|
printf(" received: %d bytes\n expected: %d bytes\n", receivedBytes, DATA_SIZE); |
|
XS_FAIL("Log data size doesn't match"); |
|
} |
|
|
|
int timeMs = clock.getMilliseconds(); |
|
printf(" Streamed %d bytes in %d ms: %.2f MiB/s\n", DATA_SIZE, timeMs, ((float)DATA_SIZE / (float)(1024*1024)) / ((float)timeMs / 1000.0f)); |
|
} |
|
|
|
void runProgram (void) |
|
{ |
|
deFile* file = deFile_create(m_testCtx.logFileName.c_str(), DE_FILEMODE_OPEN|DE_FILEMODE_CREATE|DE_FILEMODE_TRUNCATE|DE_FILEMODE_WRITE); |
|
XS_CHECK(file); |
|
|
|
deUint8 tmpBuf[1024*16]; |
|
int numWritten = 0; |
|
|
|
deMemset(&tmpBuf, 'a', sizeof(tmpBuf)); |
|
|
|
while (numWritten < DATA_SIZE) |
|
{ |
|
deInt64 numWrittenInBatch = 0; |
|
XS_CHECK(deFile_write(file, &tmpBuf[0], de::min((int)sizeof(tmpBuf), DATA_SIZE-numWritten), &numWrittenInBatch) == DE_FILERESULT_SUCCESS); |
|
numWritten += (int)numWrittenInBatch; |
|
} |
|
|
|
deFile_destroy(file); |
|
} |
|
}; |
|
|
|
class KeepAliveTest : public TestCase |
|
{ |
|
public: |
|
KeepAliveTest (TestContext& testCtx) |
|
: TestCase(testCtx, "keepalive") |
|
{ |
|
} |
|
|
|
void runClient (de::Socket& socket) |
|
{ |
|
// In milliseconds. |
|
const int sendInterval = 5000; |
|
const int minReceiveInterval = 10000; |
|
const int testTime = 30000; |
|
const int sleepTime = 200; |
|
const int expectedTimeout = 40000; |
|
int curTime = 0; |
|
int lastSendTime = 0; |
|
int lastReceiveTime = 0; |
|
TestClock clock; |
|
|
|
DE_ASSERT(sendInterval < minReceiveInterval); |
|
|
|
curTime = clock.getMilliseconds(); |
|
|
|
while (curTime < testTime) |
|
{ |
|
bool tryGetKeepalive = false; |
|
|
|
if (curTime-lastSendTime > sendInterval) |
|
{ |
|
printf(" %d ms: sending keepalive\n", curTime); |
|
sendMessage(socket, KeepAliveMessage()); |
|
curTime = clock.getMilliseconds(); |
|
lastSendTime = curTime; |
|
tryGetKeepalive = true; |
|
} |
|
|
|
if (tryGetKeepalive) |
|
{ |
|
// Try to acquire keepalive. |
|
printf(" %d ms: waiting for keepalive\n", curTime); |
|
ScopedMsgPtr msg(readMessage(socket)); |
|
int recvTime = clock.getMilliseconds(); |
|
|
|
if (msg->type != MESSAGETYPE_KEEPALIVE) |
|
XS_FAIL("Got invalid message"); |
|
|
|
printf(" %d ms: got keepalive\n", curTime); |
|
|
|
if (recvTime-lastReceiveTime > minReceiveInterval) |
|
XS_FAIL("Server doesn't send keepalives"); |
|
|
|
lastReceiveTime = recvTime; |
|
} |
|
|
|
deSleep(sleepTime); |
|
curTime = clock.getMilliseconds(); |
|
} |
|
|
|
// Verify that server actually kills the connection upon timeout. |
|
sendMessage(socket, KeepAliveMessage()); |
|
printf(" waiting %d ms for keepalive timeout...\n", expectedTimeout); |
|
bool isClosed = false; |
|
|
|
try |
|
{ |
|
// Reset timer. |
|
clock.reset(); |
|
curTime = clock.getMilliseconds(); |
|
|
|
while (curTime < expectedTimeout) |
|
{ |
|
// Try to get keepalive message. |
|
ScopedMsgPtr msg(readMessage(socket)); |
|
if (msg->type != MESSAGETYPE_KEEPALIVE) |
|
XS_FAIL("Got invalid message"); |
|
|
|
curTime = clock.getMilliseconds(); |
|
printf(" %d ms: got keepalive\n", curTime); |
|
} |
|
} |
|
catch (const SocketError& e) |
|
{ |
|
if (e.getResult() == DE_SOCKETRESULT_CONNECTION_CLOSED) |
|
{ |
|
printf(" %d ms: server closed connection", clock.getMilliseconds()); |
|
isClosed = true; |
|
} |
|
else |
|
throw; |
|
} |
|
|
|
if (isClosed) |
|
printf(" ok!\n"); |
|
else |
|
XS_FAIL("Server didn't close connection"); |
|
} |
|
|
|
void runProgram (void) { /* nothing */ } |
|
}; |
|
|
|
void printHelp (const char* binName) |
|
{ |
|
printf("%s:\n", binName); |
|
printf(" --client=[name] Run test [name]\n"); |
|
printf(" --program=[name] Run program for test [name]\n"); |
|
printf(" --host=[host] Connect to host [host]\n"); |
|
printf(" --port=[name] Use port [port]\n"); |
|
printf(" --tester-cmd=[cmd] Launch tester with [cmd]\n"); |
|
printf(" --server-cmd=[cmd] Launch server with [cmd]\n"); |
|
printf(" --start-server Start server for test execution\n"); |
|
} |
|
|
|
struct CompareCaseName |
|
{ |
|
std::string name; |
|
|
|
CompareCaseName (const string& name_) : name(name_) {} |
|
|
|
bool operator() (const TestCase* testCase) const |
|
{ |
|
return name == testCase->getName(); |
|
} |
|
}; |
|
|
|
void runExecServerTests (int argc, const char* const* argv) |
|
{ |
|
// Construct test context. |
|
TestContext testCtx; |
|
|
|
testCtx.serverPath = "execserver"; |
|
testCtx.testerPath = argv[0]; |
|
testCtx.startServer = false; |
|
testCtx.address.setHost("127.0.0.1"); |
|
testCtx.address.setPort(50016); |
|
|
|
std::string runClient = ""; |
|
std::string runProgram = ""; |
|
|
|
// Parse command line. |
|
for (int argNdx = 1; argNdx < argc; argNdx++) |
|
{ |
|
const char* arg = argv[argNdx]; |
|
|
|
if (deStringBeginsWith(arg, "--client=")) |
|
runClient = arg+9; |
|
else if (deStringBeginsWith(arg, "--program=")) |
|
runProgram = arg+10; |
|
else if (deStringBeginsWith(arg, "--port=")) |
|
testCtx.address.setPort(atoi(arg+7)); |
|
else if (deStringBeginsWith(arg, "--host=")) |
|
testCtx.address.setHost(arg+7); |
|
else if (deStringBeginsWith(arg, "--server-cmd=")) |
|
testCtx.serverPath = arg+13; |
|
else if (deStringBeginsWith(arg, "--tester-cmd=")) |
|
testCtx.testerPath = arg+13; |
|
else if (deStringBeginsWith(arg, "--deqp-log-filename=")) |
|
testCtx.logFileName = arg+20; |
|
else if (deStringBeginsWith(arg, "--deqp-caselist=")) |
|
testCtx.caseList = arg+16; |
|
else if (deStringEqual(arg, "--deqp-stdin-caselist")) |
|
{ |
|
// \todo [pyry] This is rather brute-force solution... |
|
char c; |
|
while (fread(&c, 1, 1, stdin) == 1 && c != 0) |
|
testCtx.caseList += c; |
|
} |
|
else if (deStringEqual(arg, "--start-server")) |
|
testCtx.startServer = true; |
|
else |
|
{ |
|
printHelp(argv[0]); |
|
return; |
|
} |
|
} |
|
|
|
// Test case list. |
|
std::vector<TestCase*> testCases; |
|
testCases.push_back(new ConnectTest(testCtx)); |
|
testCases.push_back(new HelloTest(testCtx)); |
|
testCases.push_back(new ExecFailTest(testCtx)); |
|
testCases.push_back(new SimpleExecTest(testCtx)); |
|
testCases.push_back(new InfoTest(testCtx)); |
|
testCases.push_back(new LogDataTest(testCtx)); |
|
testCases.push_back(new KeepAliveTest(testCtx)); |
|
testCases.push_back(new BigLogDataTest(testCtx)); |
|
|
|
try |
|
{ |
|
if (!runClient.empty()) |
|
{ |
|
// Run single case. |
|
vector<TestCase*>::iterator casePos = std::find_if(testCases.begin(), testCases.end(), CompareCaseName(runClient)); |
|
XS_CHECK(casePos != testCases.end()); |
|
TestExecutor executor(testCtx); |
|
executor.runCase(*casePos); |
|
} |
|
else if (!runProgram.empty()) |
|
{ |
|
// Run program part. |
|
vector<TestCase*>::iterator casePos = std::find_if(testCases.begin(), testCases.end(), CompareCaseName(runProgram)); |
|
XS_CHECK(casePos != testCases.end()); |
|
(*casePos)->runProgram(); |
|
fflush(stdout); // Make sure handles are flushed. |
|
fflush(stderr); |
|
} |
|
else |
|
{ |
|
// Run all tests. |
|
TestExecutor executor(testCtx); |
|
executor.runCases(testCases); |
|
} |
|
} |
|
catch (const std::exception& e) |
|
{ |
|
printf("ERROR: %s\n", e.what()); |
|
} |
|
|
|
// Destroy cases. |
|
for (std::vector<TestCase*>::const_iterator i = testCases.begin(); i != testCases.end(); i++) |
|
delete *i; |
|
} |
|
|
|
} // xs |
|
|
|
#if 0 |
|
void testProcFile (void) |
|
{ |
|
/* Test file api. */ |
|
if (deFileExists("test.txt")) |
|
deDeleteFile("test.txt"); |
|
deFile* file = deFile_create("test.txt", DE_FILEMODE_CREATE|DE_FILEMODE_WRITE); |
|
const char test[] = "Hello"; |
|
XS_CHECK(deFile_write(file, test, sizeof(test), DE_NULL) == DE_FILERESULT_SUCCESS); |
|
deFile_destroy(file); |
|
|
|
/* Read. */ |
|
char buf[10] = { 0 }; |
|
file = deFile_create("test.txt", DE_FILEMODE_OPEN|DE_FILEMODE_READ); |
|
XS_CHECK(deFile_read(file, buf, sizeof(test), DE_NULL) == DE_FILERESULT_SUCCESS); |
|
printf("buf: %s\n", buf); |
|
deFile_destroy(file); |
|
|
|
/* Process test. */ |
|
deProcess* proc = deProcess_create("ls -lah /Users/pyry", DE_NULL); |
|
deFile* out = deProcess_getStdOut(proc); |
|
|
|
deInt64 numRead = 0; |
|
printf("ls:\n"); |
|
while (deFile_read(out, buf, sizeof(buf)-1, &numRead) == DE_FILERESULT_SUCCESS) |
|
{ |
|
buf[numRead] = 0; |
|
printf("%s", buf); |
|
} |
|
deProcess_destroy(proc); |
|
} |
|
#endif |
|
|
|
#if 0 |
|
void testBlockingFile (const char* filename) |
|
{ |
|
deRandom rnd; |
|
int dataSize = 1024*1024; |
|
deUint8* data = (deUint8*)deCalloc(dataSize); |
|
deFile* file; |
|
|
|
deRandom_init(&rnd, 0); |
|
|
|
if (deFileExists(filename)) |
|
DE_VERIFY(deDeleteFile(filename)); |
|
|
|
/* Fill in with random data. */ |
|
DE_ASSERT(dataSize % sizeof(int) == 0); |
|
for (int ndx = 0; ndx < (int)(dataSize/sizeof(int)); ndx++) |
|
((deUint32*)data)[ndx] = deRandom_getUint32(&rnd); |
|
|
|
/* Write with random-sized blocks. */ |
|
file = deFile_create(filename, DE_FILEMODE_CREATE|DE_FILEMODE_WRITE); |
|
DE_VERIFY(file); |
|
|
|
int curPos = 0; |
|
while (curPos < dataSize) |
|
{ |
|
int blockSize = 1 + deRandom_getUint32(&rnd) % (dataSize-curPos); |
|
deInt64 numWritten = 0; |
|
deFileResult result = deFile_write(file, &data[curPos], blockSize, &numWritten); |
|
|
|
DE_VERIFY(result == DE_FILERESULT_SUCCESS); |
|
DE_VERIFY(numWritten == blockSize); |
|
|
|
curPos += blockSize; |
|
} |
|
|
|
deFile_destroy(file); |
|
|
|
/* Read and verify file. */ |
|
file = deFile_create(filename, DE_FILEMODE_OPEN|DE_FILEMODE_READ); |
|
curPos = 0; |
|
while (curPos < dataSize) |
|
{ |
|
deUint8 block[1024]; |
|
int numToRead = 1 + deRandom_getUint32(&rnd) % deMin(dataSize-curPos, DE_LENGTH_OF_ARRAY(block)); |
|
deInt64 numRead = 0; |
|
deFileResult result = deFile_read(file, block, numToRead, &numRead); |
|
|
|
DE_VERIFY(result == DE_FILERESULT_SUCCESS); |
|
DE_VERIFY((int)numRead == numToRead); |
|
DE_VERIFY(deMemCmp(block, &data[curPos], numToRead) == 0); |
|
|
|
curPos += numToRead; |
|
} |
|
deFile_destroy(file); |
|
} |
|
#endif |
|
|
|
int main (int argc, const char* const* argv) |
|
{ |
|
xs::runExecServerTests(argc, argv); |
|
return 0; |
|
}
|
|
|