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