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
42using std::string;
43using std::vector;
44
45namespace xs
46{
47
48typedef std::auto_ptr<Message> ScopedMsgPtr;
49
50class SocketError : public Error
51{
52public:
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
64private:
65	deSocketResult m_result;
66};
67
68// Helpers.
69void 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	size_t pos = 0;
77	while (pos < buf.size())
78	{
79		size_t			numLeft		= buf.size() - pos;
80		size_t			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
90void readBytes (de::Socket& socket, vector<deUint8>& dst, size_t numBytes)
91{
92	size_t numRead = 0;
93	dst.resize(numBytes);
94	while (numRead < numBytes)
95	{
96		size_t			numLeft		= numBytes - numRead;
97		size_t			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
107Message* readMessage (de::Socket& socket)
108{
109	// Header.
110	vector<deUint8> header;
111	readBytes(socket, header, MESSAGE_HEADER_SIZE);
112
113	MessageType	type;
114	size_t		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
142class TestClock
143{
144public:
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
160private:
161	deUint64 m_initTime;
162};
163
164class TestContext
165{
166public:
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
178private:
179						TestContext		(const TestContext& other);
180	TestContext&		operator=		(const TestContext& other);
181};
182
183class TestCase
184{
185public:
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
194protected:
195	TestContext&	m_testCtx;
196	std::string		m_name;
197};
198
199class TestExecutor
200{
201public:
202					TestExecutor	(TestContext& testCtx);
203					~TestExecutor	(void);
204
205	void			runCases		(const std::vector<TestCase*>& testCases);
206	bool			runCase			(TestCase* testCase);
207
208private:
209	TestContext&	m_testCtx;
210};
211
212TestExecutor::TestExecutor (TestContext& testCtx)
213	: m_testCtx(testCtx)
214{
215}
216
217TestExecutor::~TestExecutor (void)
218{
219}
220
221void 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
235class FilePrinter : public de::Thread
236{
237public:
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
261private:
262	deFile* m_curFile;
263};
264
265bool 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
335class ConnectTest : public TestCase
336{
337public:
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
351class HelloTest : public TestCase
352{
353public:
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
368class ExecFailTest : public TestCase
369{
370public:
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
408class SimpleExecTest : public TestCase
409{
410public:
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
464class InfoTest : public TestCase
465{
466public:
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
529class LogDataTest : public TestCase
530{
531public:
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
616class BigLogDataTest : public TestCase
617{
618public:
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
713class KeepAliveTest : public TestCase
714{
715public:
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
815void 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
827struct 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
839void 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
940void 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
973void 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
1029int main (int argc, const char* const* argv)
1030{
1031	xs::runExecServerTests(argc, argv);
1032	return 0;
1033}
1034