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