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
43using std::string;
44using std::vector;
45
46namespace xs
47{
48
49typedef de::UniquePtr<Message> ScopedMsgPtr;
50
51class SocketError : public Error
52{
53public:
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
65private:
66	deSocketResult m_result;
67};
68
69// Helpers.
70void 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
91void 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
108Message* 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
143class TestClock
144{
145public:
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
161private:
162	deUint64 m_initTime;
163};
164
165class TestContext
166{
167public:
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
179private:
180						TestContext		(const TestContext& other);
181	TestContext&		operator=		(const TestContext& other);
182};
183
184class TestCase
185{
186public:
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
195protected:
196	TestContext&	m_testCtx;
197	std::string		m_name;
198};
199
200class TestExecutor
201{
202public:
203					TestExecutor	(TestContext& testCtx);
204					~TestExecutor	(void);
205
206	void			runCases		(const std::vector<TestCase*>& testCases);
207	bool			runCase			(TestCase* testCase);
208
209private:
210	TestContext&	m_testCtx;
211};
212
213TestExecutor::TestExecutor (TestContext& testCtx)
214	: m_testCtx(testCtx)
215{
216}
217
218TestExecutor::~TestExecutor (void)
219{
220}
221
222void 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
236class FilePrinter : public de::Thread
237{
238public:
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
262private:
263	deFile* m_curFile;
264};
265
266bool 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
336class ConnectTest : public TestCase
337{
338public:
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
352class HelloTest : public TestCase
353{
354public:
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
369class ExecFailTest : public TestCase
370{
371public:
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
409class SimpleExecTest : public TestCase
410{
411public:
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
465class InfoTest : public TestCase
466{
467public:
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
530class LogDataTest : public TestCase
531{
532public:
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
617class BigLogDataTest : public TestCase
618{
619public:
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
714class KeepAliveTest : public TestCase
715{
716public:
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
816void 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
828struct 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
840void 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
941void 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
974void 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
1030int main (int argc, const char* const* argv)
1031{
1032	xs::runExecServerTests(argc, argv);
1033	return 0;
1034}
1035