Home | History | Annotate | Download | only in tools
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Execution Server
      3  * ---------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief ExecServer Tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xsDefs.hpp"
     25 
     26 #include "xsProtocol.hpp"
     27 #include "deSocket.hpp"
     28 #include "deRingBuffer.hpp"
     29 #include "deFilePath.hpp"
     30 #include "deBlockBuffer.hpp"
     31 #include "deThread.hpp"
     32 #include "deStringUtil.hpp"
     33 #include "deUniquePtr.hpp"
     34 
     35 #include "deClock.h"
     36 #include "deProcess.h"
     37 #include "deString.h"
     38 #include "deRandom.h"
     39 
     40 #include <memory>
     41 #include <algorithm>
     42 
     43 using std::string;
     44 using std::vector;
     45 
     46 namespace xs
     47 {
     48 
     49 typedef de::UniquePtr<Message> ScopedMsgPtr;
     50 
     51 class SocketError : public Error
     52 {
     53 public:
     54 	SocketError (deSocketResult result, const char* message, const char* file, int line)
     55 		: Error		(message, deGetSocketResultName(result), file, line)
     56 		, m_result	(result)
     57 	{
     58 	}
     59 
     60 	deSocketResult getResult (void) const
     61 	{
     62 		return m_result;
     63 	}
     64 
     65 private:
     66 	deSocketResult m_result;
     67 };
     68 
     69 // Helpers.
     70 void sendMessage (de::Socket& socket, const Message& message)
     71 {
     72 	// Format message.
     73 	vector<deUint8> buf;
     74 	message.write(buf);
     75 
     76 	// Write to socket.
     77 	size_t pos = 0;
     78 	while (pos < buf.size())
     79 	{
     80 		size_t			numLeft		= buf.size() - pos;
     81 		size_t			numSent		= 0;
     82 		deSocketResult	result		= socket.send(&buf[pos], numLeft, &numSent);
     83 
     84 		if (result != DE_SOCKETRESULT_SUCCESS)
     85 			throw SocketError(result, "send() failed", __FILE__, __LINE__);
     86 
     87 		pos += numSent;
     88 	}
     89 }
     90 
     91 void readBytes (de::Socket& socket, vector<deUint8>& dst, size_t numBytes)
     92 {
     93 	size_t numRead = 0;
     94 	dst.resize(numBytes);
     95 	while (numRead < numBytes)
     96 	{
     97 		size_t			numLeft		= numBytes - numRead;
     98 		size_t			curNumRead	= 0;
     99 		deSocketResult	result		= socket.receive(&dst[numRead], numLeft, &curNumRead);
    100 
    101 		if (result != DE_SOCKETRESULT_SUCCESS)
    102 			throw SocketError(result, "receive() failed", __FILE__, __LINE__);
    103 
    104 		numRead += curNumRead;
    105 	}
    106 }
    107 
    108 Message* readMessage (de::Socket& socket)
    109 {
    110 	// Header.
    111 	vector<deUint8> header;
    112 	readBytes(socket, header, MESSAGE_HEADER_SIZE);
    113 
    114 	MessageType	type;
    115 	size_t		messageSize;
    116 	Message::parseHeader(&header[0], (int)header.size(), type, messageSize);
    117 
    118 	// Simple messages without any data.
    119 	switch (type)
    120 	{
    121 		case MESSAGETYPE_KEEPALIVE:				return new KeepAliveMessage();
    122 		case MESSAGETYPE_PROCESS_STARTED:		return new ProcessStartedMessage();
    123 		default:
    124 			break; // Read message with data.
    125 	}
    126 
    127 	vector<deUint8> messageBuf;
    128 	readBytes(socket, messageBuf, messageSize-MESSAGE_HEADER_SIZE);
    129 
    130 	switch (type)
    131 	{
    132 		case MESSAGETYPE_HELLO:					return new HelloMessage(&messageBuf[0], (int)messageBuf.size());
    133 		case MESSAGETYPE_TEST:					return new TestMessage(&messageBuf[0], (int)messageBuf.size());
    134 		case MESSAGETYPE_PROCESS_LOG_DATA:		return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size());
    135 		case MESSAGETYPE_INFO:					return new InfoMessage(&messageBuf[0], (int)messageBuf.size());
    136 		case MESSAGETYPE_PROCESS_LAUNCH_FAILED:	return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size());
    137 		case MESSAGETYPE_PROCESS_FINISHED:		return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size());
    138 		default:
    139 			XS_FAIL("Unknown message");
    140 	}
    141 }
    142 
    143 class TestClock
    144 {
    145 public:
    146 	inline TestClock (void)
    147 	{
    148 		reset();
    149 	}
    150 
    151 	inline void reset (void)
    152 	{
    153 		m_initTime = deGetMicroseconds();
    154 	}
    155 
    156 	inline int getMilliseconds (void)
    157 	{
    158 		return (int)((deGetMicroseconds() - m_initTime) / 1000);
    159 	}
    160 
    161 private:
    162 	deUint64 m_initTime;
    163 };
    164 
    165 class TestContext
    166 {
    167 public:
    168 						TestContext		(void) : startServer(false) {}
    169 
    170 	std::string			serverPath;
    171 	std::string			testerPath;
    172 	de::SocketAddress	address;
    173 	bool				startServer;
    174 
    175 	// Passed from execserver.
    176 	std::string			logFileName;
    177 	std::string			caseList;
    178 
    179 private:
    180 						TestContext		(const TestContext& other);
    181 	TestContext&		operator=		(const TestContext& other);
    182 };
    183 
    184 class TestCase
    185 {
    186 public:
    187 					TestCase		(TestContext& testCtx, const char* name) : m_testCtx(testCtx), m_name(name) {}
    188 	virtual			~TestCase		(void) {}
    189 
    190 	const char*		getName			(void) const { return m_name.c_str(); }
    191 
    192 	virtual void	runClient		(de::Socket& socket) = DE_NULL;
    193 	virtual void	runProgram		(void) = DE_NULL;
    194 
    195 protected:
    196 	TestContext&	m_testCtx;
    197 	std::string		m_name;
    198 };
    199 
    200 class TestExecutor
    201 {
    202 public:
    203 					TestExecutor	(TestContext& testCtx);
    204 					~TestExecutor	(void);
    205 
    206 	void			runCases		(const std::vector<TestCase*>& testCases);
    207 	bool			runCase			(TestCase* testCase);
    208 
    209 private:
    210 	TestContext&	m_testCtx;
    211 };
    212 
    213 TestExecutor::TestExecutor (TestContext& testCtx)
    214 	: m_testCtx(testCtx)
    215 {
    216 }
    217 
    218 TestExecutor::~TestExecutor (void)
    219 {
    220 }
    221 
    222 void TestExecutor::runCases (const std::vector<TestCase*>& testCases)
    223 {
    224 	int numPassed	= 0;
    225 	int numCases	= (int)testCases.size();
    226 
    227 	for (std::vector<TestCase*>::const_iterator i = testCases.begin(); i != testCases.end(); i++)
    228 	{
    229 		if (runCase(*i))
    230 			numPassed += 1;
    231 	}
    232 
    233 	printf("\n  %d/%d passed!\n", numPassed, numCases);
    234 }
    235 
    236 class FilePrinter : public de::Thread
    237 {
    238 public:
    239 	FilePrinter (void)
    240 		: m_curFile(DE_NULL)
    241 	{
    242 	}
    243 
    244 	void start (deFile* file)
    245 	{
    246 		DE_ASSERT(!m_curFile);
    247 		m_curFile = file;
    248 		de::Thread::start();
    249 	}
    250 
    251 	void run (void)
    252 	{
    253 		char	buf[256];
    254 		deInt64 numRead	= 0;
    255 
    256 		while (deFile_read(m_curFile, &buf[0], (deInt64)sizeof(buf), &numRead) == DE_FILERESULT_SUCCESS)
    257 			fwrite(&buf[0], 1, (size_t)numRead, stdout);
    258 
    259 		m_curFile = DE_NULL;
    260 	}
    261 
    262 private:
    263 	deFile* m_curFile;
    264 };
    265 
    266 bool TestExecutor::runCase (TestCase* testCase)
    267 {
    268 	printf("%s\n", testCase->getName());
    269 
    270 	bool		success		= false;
    271 	deProcess*	serverProc	= DE_NULL;
    272 	FilePrinter	stdoutPrinter;
    273 	FilePrinter	stderrPrinter;
    274 
    275 	try
    276 	{
    277 		if (m_testCtx.startServer)
    278 		{
    279 			string cmdLine = m_testCtx.serverPath + " --port=" + de::toString(m_testCtx.address.getPort());
    280 			serverProc = deProcess_create();
    281 			XS_CHECK(serverProc);
    282 
    283 			if (!deProcess_start(serverProc, cmdLine.c_str(), DE_NULL))
    284 			{
    285 				string errMsg = deProcess_getLastError(serverProc);
    286 				deProcess_destroy(serverProc);
    287 				XS_FAIL(errMsg.c_str());
    288 			}
    289 
    290 			deSleep(200); /* Give 200ms for server to start. */
    291 			XS_CHECK(deProcess_isRunning(serverProc));
    292 
    293 			// Start stdout/stderr printers.
    294 			stdoutPrinter.start(deProcess_getStdOut(serverProc));
    295 			stderrPrinter.start(deProcess_getStdErr(serverProc));
    296 		}
    297 
    298 		// Connect.
    299 		de::Socket socket;
    300 		socket.connect(m_testCtx.address);
    301 
    302 		// Flags.
    303 		socket.setFlags(DE_SOCKET_CLOSE_ON_EXEC);
    304 
    305 		// Run case.
    306 		testCase->runClient(socket);
    307 
    308 		// Disconnect.
    309 		if (socket.isConnected())
    310 			socket.shutdown();
    311 
    312 		// Kill server.
    313 		if (serverProc && deProcess_isRunning(serverProc))
    314 		{
    315 			XS_CHECK(deProcess_terminate(serverProc));
    316 			deSleep(100);
    317 			XS_CHECK(deProcess_waitForFinish(serverProc));
    318 
    319 			stdoutPrinter.join();
    320 			stderrPrinter.join();
    321 		}
    322 
    323 		success = true;
    324 	}
    325 	catch (const std::exception& e)
    326 	{
    327 		printf("FAIL: %s\n\n", e.what());
    328 	}
    329 
    330 	if (serverProc)
    331 		deProcess_destroy(serverProc);
    332 
    333 	return success;
    334 }
    335 
    336 class ConnectTest : public TestCase
    337 {
    338 public:
    339 	ConnectTest (TestContext& testCtx)
    340 		: TestCase(testCtx, "connect")
    341 	{
    342 	}
    343 
    344 	void runClient (de::Socket& socket)
    345 	{
    346 		DE_UNREF(socket);
    347 	}
    348 
    349 	void runProgram (void) { /* nothing */ }
    350 };
    351 
    352 class HelloTest : public TestCase
    353 {
    354 public:
    355 	HelloTest (TestContext& testCtx)
    356 		: TestCase(testCtx, "hello")
    357 	{
    358 	}
    359 
    360 	void runClient (de::Socket& socket)
    361 	{
    362 		xs::HelloMessage msg;
    363 		sendMessage(socket, (const xs::Message&)msg);
    364 	}
    365 
    366 	void runProgram (void) { /* nothing */ }
    367 };
    368 
    369 class ExecFailTest : public TestCase
    370 {
    371 public:
    372 	ExecFailTest (TestContext& testCtx)
    373 		: TestCase(testCtx, "exec-fail")
    374 	{
    375 	}
    376 
    377 	void runClient (de::Socket& socket)
    378 	{
    379 		xs::ExecuteBinaryMessage execMsg;
    380 		execMsg.name		= "foobar-notfound";
    381 		execMsg.params		= "";
    382 		execMsg.caseList	= "";
    383 		execMsg.workDir		= "";
    384 
    385 		sendMessage(socket, execMsg);
    386 
    387 		const int		timeout		= 100; // 100ms.
    388 		TestClock		clock;
    389 
    390 		for (;;)
    391 		{
    392 			if (clock.getMilliseconds() > timeout)
    393 				XS_FAIL("Didn't receive PROCESS_LAUNCH_FAILED");
    394 
    395 			ScopedMsgPtr msg(readMessage(socket));
    396 
    397 			if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED)
    398 				break;
    399 			else if (msg->type == MESSAGETYPE_KEEPALIVE)
    400 				continue;
    401 			else
    402 				XS_FAIL("Invalid message");
    403 		}
    404 	}
    405 
    406 	void runProgram (void) { /* nothing */ }
    407 };
    408 
    409 class SimpleExecTest : public TestCase
    410 {
    411 public:
    412 	SimpleExecTest (TestContext& testCtx)
    413 		: TestCase(testCtx, "simple-exec")
    414 	{
    415 	}
    416 
    417 	void runClient (de::Socket& socket)
    418 	{
    419 		xs::ExecuteBinaryMessage execMsg;
    420 		execMsg.name		= m_testCtx.testerPath;
    421 		execMsg.params		= "--program=simple-exec";
    422 		execMsg.caseList	= "";
    423 		execMsg.workDir		= "";
    424 
    425 		sendMessage(socket, execMsg);
    426 
    427 		const int		timeout		= 5000; // 5s.
    428 		TestClock		clock;
    429 
    430 		bool	gotProcessStarted	= false;
    431 		bool	gotProcessFinished	= false;
    432 
    433 		for (;;)
    434 		{
    435 			if (clock.getMilliseconds() > timeout)
    436 				break;
    437 
    438 			ScopedMsgPtr msg(readMessage(socket));
    439 
    440 			if (msg->type == MESSAGETYPE_PROCESS_STARTED)
    441 				gotProcessStarted = true;
    442 			else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED)
    443 				XS_FAIL("Got PROCESS_LAUNCH_FAILED");
    444 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED)
    445 			{
    446 				gotProcessFinished = true;
    447 				break;
    448 			}
    449 			else if (msg->type == MESSAGETYPE_KEEPALIVE || msg->type == MESSAGETYPE_INFO)
    450 				continue;
    451 			else
    452 				XS_FAIL((string("Invalid message: ") + de::toString(msg->type)).c_str());
    453 		}
    454 
    455 		if (!gotProcessStarted)
    456 			XS_FAIL("Did't get PROCESS_STARTED message");
    457 
    458 		if (!gotProcessFinished)
    459 			XS_FAIL("Did't get PROCESS_FINISHED message");
    460 	}
    461 
    462 	void runProgram (void) { /* print nothing. */ }
    463 };
    464 
    465 class InfoTest : public TestCase
    466 {
    467 public:
    468 	std::string infoStr;
    469 
    470 	InfoTest (TestContext& testCtx)
    471 		: TestCase	(testCtx, "info")
    472 		, infoStr	("Hello, World")
    473 	{
    474 	}
    475 
    476 	void runClient (de::Socket& socket)
    477 	{
    478 		xs::ExecuteBinaryMessage execMsg;
    479 		execMsg.name		= m_testCtx.testerPath;
    480 		execMsg.params		= "--program=info";
    481 		execMsg.caseList	= "";
    482 		execMsg.workDir		= "";
    483 
    484 		sendMessage(socket, execMsg);
    485 
    486 		const int		timeout		= 10000; // 10s.
    487 		TestClock		clock;
    488 
    489 		bool			gotProcessStarted	= false;
    490 		bool			gotProcessFinished	= false;
    491 		std::string		receivedInfo		= "";
    492 
    493 		for (;;)
    494 		{
    495 			if (clock.getMilliseconds() > timeout)
    496 				break;
    497 
    498 			ScopedMsgPtr msg(readMessage(socket));
    499 
    500 			if (msg->type == MESSAGETYPE_PROCESS_STARTED)
    501 				gotProcessStarted = true;
    502 			else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED)
    503 				XS_FAIL("Got PROCESS_LAUNCH_FAILED");
    504 			else if (gotProcessStarted && msg->type == MESSAGETYPE_INFO)
    505 				receivedInfo += static_cast<const InfoMessage*>(msg.get())->info;
    506 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED)
    507 			{
    508 				gotProcessFinished = true;
    509 				break;
    510 			}
    511 			else if (msg->type == MESSAGETYPE_KEEPALIVE)
    512 				continue;
    513 			else
    514 				XS_FAIL("Invalid message");
    515 		}
    516 
    517 		if (!gotProcessStarted)
    518 			XS_FAIL("Did't get PROCESS_STARTED message");
    519 
    520 		if (!gotProcessFinished)
    521 			XS_FAIL("Did't get PROCESS_FINISHED message");
    522 
    523 		if (receivedInfo != infoStr)
    524 			XS_FAIL("Info data doesn't match");
    525 	}
    526 
    527 	void runProgram (void) { printf("%s", infoStr.c_str()); }
    528 };
    529 
    530 class LogDataTest : public TestCase
    531 {
    532 public:
    533 	LogDataTest (TestContext& testCtx)
    534 		: TestCase(testCtx, "logdata")
    535 	{
    536 	}
    537 
    538 	void runClient (de::Socket& socket)
    539 	{
    540 		xs::ExecuteBinaryMessage execMsg;
    541 		execMsg.name		= m_testCtx.testerPath;
    542 		execMsg.params		= "--program=logdata";
    543 		execMsg.caseList	= "";
    544 		execMsg.workDir		= "";
    545 
    546 		sendMessage(socket, execMsg);
    547 
    548 		const int		timeout		= 10000; // 10s.
    549 		TestClock		clock;
    550 
    551 		bool			gotProcessStarted	= false;
    552 		bool			gotProcessFinished	= false;
    553 		std::string		receivedData		= "";
    554 
    555 		for (;;)
    556 		{
    557 			if (clock.getMilliseconds() > timeout)
    558 				break;
    559 
    560 			ScopedMsgPtr msg(readMessage(socket));
    561 
    562 			if (msg->type == MESSAGETYPE_PROCESS_STARTED)
    563 				gotProcessStarted = true;
    564 			else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED)
    565 				XS_FAIL("Got PROCESS_LAUNCH_FAILED");
    566 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_LOG_DATA)
    567 				receivedData += static_cast<const ProcessLogDataMessage*>(msg.get())->logData;
    568 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED)
    569 			{
    570 				gotProcessFinished = true;
    571 				break;
    572 			}
    573 			else if (msg->type == MESSAGETYPE_KEEPALIVE)
    574 				continue;
    575 			else if (msg->type == MESSAGETYPE_INFO)
    576 				XS_FAIL(static_cast<const InfoMessage*>(msg.get())->info.c_str());
    577 			else
    578 				XS_FAIL("Invalid message");
    579 		}
    580 
    581 		if (!gotProcessStarted)
    582 			XS_FAIL("Did't get PROCESS_STARTED message");
    583 
    584 		if (!gotProcessFinished)
    585 			XS_FAIL("Did't get PROCESS_FINISHED message");
    586 
    587 		const char* expected = "Foo\nBar\n";
    588 		if (receivedData != expected)
    589 		{
    590 			printf("  received: '%s'\n  expected: '%s'\n", receivedData.c_str(), expected);
    591 			XS_FAIL("Log data doesn't match");
    592 		}
    593 	}
    594 
    595 	void runProgram (void)
    596 	{
    597 		deFile* file = deFile_create(m_testCtx.logFileName.c_str(), DE_FILEMODE_OPEN|DE_FILEMODE_CREATE|DE_FILEMODE_TRUNCATE|DE_FILEMODE_WRITE);
    598 		XS_CHECK(file);
    599 
    600 		const char line0[] = "Foo\n";
    601 		const char line1[] = "Bar\n";
    602 		deInt64 numWritten = 0;
    603 
    604 		// Write first line.
    605 		XS_CHECK(deFile_write(file, line0, sizeof(line0)-1, &numWritten) == DE_FILERESULT_SUCCESS);
    606 		XS_CHECK(numWritten == sizeof(line0)-1);
    607 
    608 		// Sleep for 0.5s and write line 2.
    609 		deSleep(500);
    610 		XS_CHECK(deFile_write(file, line1, sizeof(line1)-1, &numWritten) == DE_FILERESULT_SUCCESS);
    611 		XS_CHECK(numWritten == sizeof(line1)-1);
    612 
    613 		deFile_destroy(file);
    614 	}
    615 };
    616 
    617 class BigLogDataTest : public TestCase
    618 {
    619 public:
    620 	enum
    621 	{
    622 		DATA_SIZE = 100*1024*1024
    623 	};
    624 
    625 	BigLogDataTest (TestContext& testCtx)
    626 		: TestCase(testCtx, "biglogdata")
    627 	{
    628 	}
    629 
    630 	void runClient (de::Socket& socket)
    631 	{
    632 		xs::ExecuteBinaryMessage execMsg;
    633 		execMsg.name		= m_testCtx.testerPath;
    634 		execMsg.params		= "--program=biglogdata";
    635 		execMsg.caseList	= "";
    636 		execMsg.workDir		= "";
    637 
    638 		sendMessage(socket, execMsg);
    639 
    640 		const int		timeout		= 30000; // 30s.
    641 		TestClock		clock;
    642 
    643 		bool			gotProcessStarted	= false;
    644 		bool			gotProcessFinished	= false;
    645 		int				receivedBytes		= 0;
    646 
    647 		for (;;)
    648 		{
    649 			if (clock.getMilliseconds() > timeout)
    650 				break;
    651 
    652 			ScopedMsgPtr msg(readMessage(socket));
    653 
    654 			if (msg->type == MESSAGETYPE_PROCESS_STARTED)
    655 				gotProcessStarted = true;
    656 			else if (msg->type == MESSAGETYPE_PROCESS_LAUNCH_FAILED)
    657 				XS_FAIL("Got PROCESS_LAUNCH_FAILED");
    658 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_LOG_DATA)
    659 				receivedBytes += (int)static_cast<const ProcessLogDataMessage*>(msg.get())->logData.length();
    660 			else if (gotProcessStarted && msg->type == MESSAGETYPE_PROCESS_FINISHED)
    661 			{
    662 				gotProcessFinished = true;
    663 				break;
    664 			}
    665 			else if (msg->type == MESSAGETYPE_KEEPALIVE)
    666 			{
    667 				// Reply with keepalive.
    668 				sendMessage(socket, KeepAliveMessage());
    669 				continue;
    670 			}
    671 			else if (msg->type == MESSAGETYPE_INFO)
    672 				printf("%s", static_cast<const InfoMessage*>(msg.get())->info.c_str());
    673 			else
    674 				XS_FAIL("Invalid message");
    675 		}
    676 
    677 		if (!gotProcessStarted)
    678 			XS_FAIL("Did't get PROCESS_STARTED message");
    679 
    680 		if (!gotProcessFinished)
    681 			XS_FAIL("Did't get PROCESS_FINISHED message");
    682 
    683 		if (receivedBytes != DATA_SIZE)
    684 		{
    685 			printf("  received: %d bytes\n  expected: %d bytes\n", receivedBytes, DATA_SIZE);
    686 			XS_FAIL("Log data size doesn't match");
    687 		}
    688 
    689 		int timeMs = clock.getMilliseconds();
    690 		printf("  Streamed %d bytes in %d ms: %.2f MiB/s\n", DATA_SIZE, timeMs, ((float)DATA_SIZE / (float)(1024*1024)) / ((float)timeMs / 1000.0f));
    691 	}
    692 
    693 	void runProgram (void)
    694 	{
    695 		deFile* file = deFile_create(m_testCtx.logFileName.c_str(), DE_FILEMODE_OPEN|DE_FILEMODE_CREATE|DE_FILEMODE_TRUNCATE|DE_FILEMODE_WRITE);
    696 		XS_CHECK(file);
    697 
    698 		deUint8 tmpBuf[1024*16];
    699 		int numWritten = 0;
    700 
    701 		deMemset(&tmpBuf, 'a', sizeof(tmpBuf));
    702 
    703 		while (numWritten < DATA_SIZE)
    704 		{
    705 			deInt64 numWrittenInBatch = 0;
    706 			XS_CHECK(deFile_write(file, &tmpBuf[0], de::min((int)sizeof(tmpBuf), DATA_SIZE-numWritten), &numWrittenInBatch) == DE_FILERESULT_SUCCESS);
    707 			numWritten += (int)numWrittenInBatch;
    708 		}
    709 
    710 		deFile_destroy(file);
    711 	}
    712 };
    713 
    714 class KeepAliveTest : public TestCase
    715 {
    716 public:
    717 	KeepAliveTest (TestContext& testCtx)
    718 		: TestCase(testCtx, "keepalive")
    719 	{
    720 	}
    721 
    722 	void runClient (de::Socket& socket)
    723 	{
    724 		// In milliseconds.
    725 		const int	sendInterval			= 5000;
    726 		const int	minReceiveInterval		= 10000;
    727 		const int	testTime				= 30000;
    728 		const int	sleepTime				= 200;
    729 		const int	expectedTimeout			= 40000;
    730 		int			curTime					= 0;
    731 		int			lastSendTime			= 0;
    732 		int			lastReceiveTime			= 0;
    733 		TestClock	clock;
    734 
    735 		DE_ASSERT(sendInterval < minReceiveInterval);
    736 
    737 		curTime = clock.getMilliseconds();
    738 
    739 		while (curTime < testTime)
    740 		{
    741 			bool tryGetKeepalive = false;
    742 
    743 			if (curTime-lastSendTime > sendInterval)
    744 			{
    745 				printf("  %d ms: sending keepalive\n", curTime);
    746 				sendMessage(socket, KeepAliveMessage());
    747 				curTime = clock.getMilliseconds();
    748 				lastSendTime = curTime;
    749 				tryGetKeepalive = true;
    750 			}
    751 
    752 			if (tryGetKeepalive)
    753 			{
    754 				// Try to acquire keepalive.
    755 				printf("  %d ms: waiting for keepalive\n", curTime);
    756 				ScopedMsgPtr msg(readMessage(socket));
    757 				int recvTime = clock.getMilliseconds();
    758 
    759 				if (msg->type != MESSAGETYPE_KEEPALIVE)
    760 					XS_FAIL("Got invalid message");
    761 
    762 				printf("  %d ms: got keepalive\n", curTime);
    763 
    764 				if (recvTime-lastReceiveTime > minReceiveInterval)
    765 					XS_FAIL("Server doesn't send keepalives");
    766 
    767 				lastReceiveTime = recvTime;
    768 			}
    769 
    770 			deSleep(sleepTime);
    771 			curTime = clock.getMilliseconds();
    772 		}
    773 
    774 		// Verify that server actually kills the connection upon timeout.
    775 		sendMessage(socket, KeepAliveMessage());
    776 		printf("  waiting %d ms for keepalive timeout...\n", expectedTimeout);
    777 		bool isClosed = false;
    778 
    779 		try
    780 		{
    781 			// Reset timer.
    782 			clock.reset();
    783 			curTime = clock.getMilliseconds();
    784 
    785 			while (curTime < expectedTimeout)
    786 			{
    787 				// Try to get keepalive message.
    788 				ScopedMsgPtr msg(readMessage(socket));
    789 				if (msg->type != MESSAGETYPE_KEEPALIVE)
    790 					XS_FAIL("Got invalid message");
    791 
    792 				curTime = clock.getMilliseconds();
    793 				printf("  %d ms: got keepalive\n", curTime);
    794 			}
    795 		}
    796 		catch (const SocketError& e)
    797 		{
    798 			if (e.getResult() == DE_SOCKETRESULT_CONNECTION_CLOSED)
    799 			{
    800 				printf("  %d ms: server closed connection", clock.getMilliseconds());
    801 				isClosed = true;
    802 			}
    803 			else
    804 				throw;
    805 		}
    806 
    807 		if (isClosed)
    808 			printf("  ok!\n");
    809 		else
    810 			XS_FAIL("Server didn't close connection");
    811 	}
    812 
    813 	void runProgram (void) { /* nothing */ }
    814 };
    815 
    816 void printHelp (const char* binName)
    817 {
    818 	printf("%s:\n", binName);
    819 	printf("  --client=[name]       Run test [name]\n");
    820 	printf("  --program=[name]      Run program for test [name]\n");
    821 	printf("  --host=[host]         Connect to host [host]\n");
    822 	printf("  --port=[name]         Use port [port]\n");
    823 	printf("  --tester-cmd=[cmd]    Launch tester with [cmd]\n");
    824 	printf("  --server-cmd=[cmd]    Launch server with [cmd]\n");
    825 	printf("  --start-server        Start server for test execution\n");
    826 }
    827 
    828 struct CompareCaseName
    829 {
    830 	std::string name;
    831 
    832 	CompareCaseName (const string& name_) : name(name_) {}
    833 
    834 	bool operator() (const TestCase* testCase) const
    835 	{
    836 		return name == testCase->getName();
    837 	}
    838 };
    839 
    840 void runExecServerTests (int argc, const char* const* argv)
    841 {
    842 	// Construct test context.
    843 	TestContext testCtx;
    844 
    845 	testCtx.serverPath	= "execserver";
    846 	testCtx.testerPath	= argv[0];
    847 	testCtx.startServer	= false;
    848 	testCtx.address.setHost("127.0.0.1");
    849 	testCtx.address.setPort(50016);
    850 
    851 	std::string runClient = "";
    852 	std::string runProgram = "";
    853 
    854 	// Parse command line.
    855 	for (int argNdx = 1; argNdx < argc; argNdx++)
    856 	{
    857 		const char* arg = argv[argNdx];
    858 
    859 		if (deStringBeginsWith(arg, "--client="))
    860 			runClient = arg+9;
    861 		else if (deStringBeginsWith(arg, "--program="))
    862 			runProgram = arg+10;
    863 		else if (deStringBeginsWith(arg, "--port="))
    864 			testCtx.address.setPort(atoi(arg+7));
    865 		else if (deStringBeginsWith(arg, "--host="))
    866 			testCtx.address.setHost(arg+7);
    867 		else if (deStringBeginsWith(arg, "--server-cmd="))
    868 			testCtx.serverPath = arg+13;
    869 		else if (deStringBeginsWith(arg, "--tester-cmd="))
    870 			testCtx.testerPath = arg+13;
    871 		else if (deStringBeginsWith(arg, "--deqp-log-filename="))
    872 			testCtx.logFileName = arg+20;
    873 		else if (deStringBeginsWith(arg, "--deqp-caselist="))
    874 			testCtx.caseList = arg+16;
    875 		else if (deStringEqual(arg, "--deqp-stdin-caselist"))
    876 		{
    877 			// \todo [pyry] This is rather brute-force solution...
    878 			char c;
    879 			while (fread(&c, 1, 1, stdin) == 1 && c != 0)
    880 				testCtx.caseList += c;
    881 		}
    882 		else if (deStringEqual(arg, "--start-server"))
    883 			testCtx.startServer = true;
    884 		else
    885 		{
    886 			printHelp(argv[0]);
    887 			return;
    888 		}
    889 	}
    890 
    891 	// Test case list.
    892 	std::vector<TestCase*> testCases;
    893 	testCases.push_back(new ConnectTest(testCtx));
    894 	testCases.push_back(new HelloTest(testCtx));
    895 	testCases.push_back(new ExecFailTest(testCtx));
    896 	testCases.push_back(new SimpleExecTest(testCtx));
    897 	testCases.push_back(new InfoTest(testCtx));
    898 	testCases.push_back(new LogDataTest(testCtx));
    899 	testCases.push_back(new KeepAliveTest(testCtx));
    900 	testCases.push_back(new BigLogDataTest(testCtx));
    901 
    902 	try
    903 	{
    904 		if (!runClient.empty())
    905 		{
    906 			// Run single case.
    907 			vector<TestCase*>::iterator casePos = std::find_if(testCases.begin(), testCases.end(), CompareCaseName(runClient));
    908 			XS_CHECK(casePos != testCases.end());
    909 			TestExecutor executor(testCtx);
    910 			executor.runCase(*casePos);
    911 		}
    912 		else if (!runProgram.empty())
    913 		{
    914 			// Run program part.
    915 			vector<TestCase*>::iterator casePos = std::find_if(testCases.begin(), testCases.end(), CompareCaseName(runProgram));
    916 			XS_CHECK(casePos != testCases.end());
    917 			(*casePos)->runProgram();
    918 			fflush(stdout);	// Make sure handles are flushed.
    919 			fflush(stderr);
    920 		}
    921 		else
    922 		{
    923 			// Run all tests.
    924 			TestExecutor executor(testCtx);
    925 			executor.runCases(testCases);
    926 		}
    927 	}
    928 	catch (const std::exception& e)
    929 	{
    930 		printf("ERROR: %s\n", e.what());
    931 	}
    932 
    933 	// Destroy cases.
    934 	for (std::vector<TestCase*>::const_iterator i = testCases.begin(); i != testCases.end(); i++)
    935 		delete *i;
    936 }
    937 
    938 } // xs
    939 
    940 #if 0
    941 void testProcFile (void)
    942 {
    943 	/* Test file api. */
    944 	if (deFileExists("test.txt"))
    945 		deDeleteFile("test.txt");
    946 	deFile* file = deFile_create("test.txt", DE_FILEMODE_CREATE|DE_FILEMODE_WRITE);
    947 	const char test[] = "Hello";
    948 	XS_CHECK(deFile_write(file, test, sizeof(test), DE_NULL) == DE_FILERESULT_SUCCESS);
    949 	deFile_destroy(file);
    950 
    951 	/* Read. */
    952 	char buf[10] = { 0 };
    953 	file = deFile_create("test.txt", DE_FILEMODE_OPEN|DE_FILEMODE_READ);
    954 	XS_CHECK(deFile_read(file, buf, sizeof(test), DE_NULL) == DE_FILERESULT_SUCCESS);
    955 	printf("buf: %s\n", buf);
    956 	deFile_destroy(file);
    957 
    958 	/* Process test. */
    959 	deProcess* proc = deProcess_create("ls -lah /Users/pyry", DE_NULL);
    960 	deFile* out = deProcess_getStdOut(proc);
    961 
    962 	deInt64 numRead = 0;
    963 	printf("ls:\n");
    964 	while (deFile_read(out, buf, sizeof(buf)-1, &numRead) == DE_FILERESULT_SUCCESS)
    965 	{
    966 		buf[numRead] = 0;
    967 		printf("%s", buf);
    968 	}
    969 	deProcess_destroy(proc);
    970 }
    971 #endif
    972 
    973 #if 0
    974 void testBlockingFile (const char* filename)
    975 {
    976 	deRandom	rnd;
    977 	int			dataSize	= 1024*1024;
    978 	deUint8*	data		= (deUint8*)deCalloc(dataSize);
    979 	deFile*		file;
    980 
    981 	deRandom_init(&rnd, 0);
    982 
    983 	if (deFileExists(filename))
    984 		DE_VERIFY(deDeleteFile(filename));
    985 
    986 	/* Fill in with random data. */
    987 	DE_ASSERT(dataSize % sizeof(int) == 0);
    988 	for (int ndx = 0; ndx < (int)(dataSize/sizeof(int)); ndx++)
    989 		((deUint32*)data)[ndx] = deRandom_getUint32(&rnd);
    990 
    991 	/* Write with random-sized blocks. */
    992 	file = deFile_create(filename, DE_FILEMODE_CREATE|DE_FILEMODE_WRITE);
    993 	DE_VERIFY(file);
    994 
    995 	int curPos = 0;
    996 	while (curPos < dataSize)
    997 	{
    998 		int				blockSize	= 1 + deRandom_getUint32(&rnd) % (dataSize-curPos);
    999 		deInt64			numWritten	= 0;
   1000 		deFileResult	result		= deFile_write(file, &data[curPos], blockSize, &numWritten);
   1001 
   1002 		DE_VERIFY(result == DE_FILERESULT_SUCCESS);
   1003 		DE_VERIFY(numWritten == blockSize);
   1004 
   1005 		curPos += blockSize;
   1006 	}
   1007 
   1008 	deFile_destroy(file);
   1009 
   1010 	/* Read and verify file. */
   1011 	file	= deFile_create(filename, DE_FILEMODE_OPEN|DE_FILEMODE_READ);
   1012 	curPos	= 0;
   1013 	while (curPos < dataSize)
   1014 	{
   1015 		deUint8			block[1024];
   1016 		int				numToRead	= 1 + deRandom_getUint32(&rnd) % deMin(dataSize-curPos, DE_LENGTH_OF_ARRAY(block));
   1017 		deInt64			numRead		= 0;
   1018 		deFileResult	result		= deFile_read(file, block, numToRead, &numRead);
   1019 
   1020 		DE_VERIFY(result == DE_FILERESULT_SUCCESS);
   1021 		DE_VERIFY((int)numRead == numToRead);
   1022 		DE_VERIFY(deMemCmp(block, &data[curPos], numToRead) == 0);
   1023 
   1024 		curPos += numToRead;
   1025 	}
   1026 	deFile_destroy(file);
   1027 }
   1028 #endif
   1029 
   1030 int main (int argc, const char* const* argv)
   1031 {
   1032 	xs::runExecServerTests(argc, argv);
   1033 	return 0;
   1034 }
   1035