resolve merge conflicts of eb48f49dd to oc-dev
[platform/upstream/VK-GL-CTS.git] / execserver / tools / xsTest.cpp
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:
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
65 private:
66         deSocketResult m_result;
67 };
68
69 // Helpers.
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
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
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:
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
161 private:
162         deUint64 m_initTime;
163 };
164
165 class TestContext
166 {
167 public:
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:
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
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
213 TestExecutor::TestExecutor (TestContext& testCtx)
214         : m_testCtx(testCtx)
215 {
216 }
217
218 TestExecutor::~TestExecutor (void)
219 {
220 }
221
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:
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
262 private:
263         deFile* m_curFile;
264 };
265
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:
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
352 class HelloTest : public TestCase
353 {
354 public:
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
369 class ExecFailTest : public TestCase
370 {
371 public:
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
409 class SimpleExecTest : public TestCase
410 {
411 public:
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
465 class InfoTest : public TestCase
466 {
467 public:
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
530 class LogDataTest : public TestCase
531 {
532 public:
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
617 class BigLogDataTest : public TestCase
618 {
619 public:
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
714 class KeepAliveTest : public TestCase
715 {
716 public:
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
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
832         CompareCaseName (const string& name_) : name(name_) {}
833
834         bool operator() (const TestCase* testCase) const
835         {
836                 return name == testCase->getName();
837         }
838 };
839
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
1030 int main (int argc, const char* const* argv)
1031 {
1032         xs::runExecServerTests(argc, argv);
1033         return 0;
1034 }