/*------------------------------------------------------------------------- * 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 #include using std::string; using std::vector; namespace xs { typedef std::auto_ptr 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 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& 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 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 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& 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& testCases) { int numPassed = 0; int numCases = (int)testCases.size(); for (std::vector::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(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(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(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(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(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 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::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::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::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; }