| /*------------------------------------------------------------------------- |
| * 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 "deClock.h" |
| #include "deProcess.h" |
| #include "deString.h" |
| #include "deRandom.h" |
| |
| #include <memory> |
| #include <algorithm> |
| |
| 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 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; |
| } |