• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SocketError(deSocketResult result,const char * message,const char * file,int line)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 
getResult(void) const60 	deSocketResult getResult (void) const
61 	{
62 		return m_result;
63 	}
64 
65 private:
66 	deSocketResult m_result;
67 };
68 
69 // Helpers.
sendMessage(de::Socket & socket,const Message & message)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 
readBytes(de::Socket & socket,vector<deUint8> & dst,size_t numBytes)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 
readMessage(de::Socket & socket)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:
TestClock(void)146 	inline TestClock (void)
147 	{
148 		reset();
149 	}
150 
reset(void)151 	inline void reset (void)
152 	{
153 		m_initTime = deGetMicroseconds();
154 	}
155 
getMilliseconds(void)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:
TestContext(void)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:
TestCase(TestContext & testCtx,const char * name)187 					TestCase		(TestContext& testCtx, const char* name) : m_testCtx(testCtx), m_name(name) {}
~TestCase(void)188 	virtual			~TestCase		(void) {}
189 
getName(void) const190 	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 
TestExecutor(TestContext & testCtx)213 TestExecutor::TestExecutor (TestContext& testCtx)
214 	: m_testCtx(testCtx)
215 {
216 }
217 
~TestExecutor(void)218 TestExecutor::~TestExecutor (void)
219 {
220 }
221 
runCases(const std::vector<TestCase * > & testCases)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:
FilePrinter(void)239 	FilePrinter (void)
240 		: m_curFile(DE_NULL)
241 	{
242 	}
243 
start(deFile * file)244 	void start (deFile* file)
245 	{
246 		DE_ASSERT(!m_curFile);
247 		m_curFile = file;
248 		de::Thread::start();
249 	}
250 
run(void)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 
runCase(TestCase * testCase)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:
ConnectTest(TestContext & testCtx)339 	ConnectTest (TestContext& testCtx)
340 		: TestCase(testCtx, "connect")
341 	{
342 	}
343 
runClient(de::Socket & socket)344 	void runClient (de::Socket& socket)
345 	{
346 		DE_UNREF(socket);
347 	}
348 
runProgram(void)349 	void runProgram (void) { /* nothing */ }
350 };
351 
352 class HelloTest : public TestCase
353 {
354 public:
HelloTest(TestContext & testCtx)355 	HelloTest (TestContext& testCtx)
356 		: TestCase(testCtx, "hello")
357 	{
358 	}
359 
runClient(de::Socket & socket)360 	void runClient (de::Socket& socket)
361 	{
362 		xs::HelloMessage msg;
363 		sendMessage(socket, (const xs::Message&)msg);
364 	}
365 
runProgram(void)366 	void runProgram (void) { /* nothing */ }
367 };
368 
369 class ExecFailTest : public TestCase
370 {
371 public:
ExecFailTest(TestContext & testCtx)372 	ExecFailTest (TestContext& testCtx)
373 		: TestCase(testCtx, "exec-fail")
374 	{
375 	}
376 
runClient(de::Socket & socket)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 
runProgram(void)406 	void runProgram (void) { /* nothing */ }
407 };
408 
409 class SimpleExecTest : public TestCase
410 {
411 public:
SimpleExecTest(TestContext & testCtx)412 	SimpleExecTest (TestContext& testCtx)
413 		: TestCase(testCtx, "simple-exec")
414 	{
415 	}
416 
runClient(de::Socket & socket)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 
runProgram(void)462 	void runProgram (void) { /* print nothing. */ }
463 };
464 
465 class InfoTest : public TestCase
466 {
467 public:
468 	std::string infoStr;
469 
InfoTest(TestContext & testCtx)470 	InfoTest (TestContext& testCtx)
471 		: TestCase	(testCtx, "info")
472 		, infoStr	("Hello, World")
473 	{
474 	}
475 
runClient(de::Socket & socket)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 
runProgram(void)527 	void runProgram (void) { printf("%s", infoStr.c_str()); }
528 };
529 
530 class LogDataTest : public TestCase
531 {
532 public:
LogDataTest(TestContext & testCtx)533 	LogDataTest (TestContext& testCtx)
534 		: TestCase(testCtx, "logdata")
535 	{
536 	}
537 
runClient(de::Socket & socket)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 
runProgram(void)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 
BigLogDataTest(TestContext & testCtx)625 	BigLogDataTest (TestContext& testCtx)
626 		: TestCase(testCtx, "biglogdata")
627 	{
628 	}
629 
runClient(de::Socket & socket)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 
runProgram(void)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:
KeepAliveTest(TestContext & testCtx)717 	KeepAliveTest (TestContext& testCtx)
718 		: TestCase(testCtx, "keepalive")
719 	{
720 	}
721 
runClient(de::Socket & socket)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 
runProgram(void)813 	void runProgram (void) { /* nothing */ }
814 };
815 
printHelp(const char * binName)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 
CompareCaseNamexs::CompareCaseName832 	CompareCaseName (const string& name_) : name(name_) {}
833 
operator ()xs::CompareCaseName834 	bool operator() (const TestCase* testCase) const
835 	{
836 		return name == testCase->getName();
837 	}
838 };
839 
runExecServerTests(int argc,const char * const * argv)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 
main(int argc,const char * const * argv)1030 int main (int argc, const char* const* argv)
1031 {
1032 	xs::runExecServerTests(argc, argv);
1033 	return 0;
1034 }
1035