Merge branch 'tizen' into new_text
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-toolkit / dali-toolkit-test-utils / test-harness.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "test-harness.h"
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22 #include <vector>
23 #include <map>
24 #include <cstring>
25 #include <testcase.h>
26
27 namespace TestHarness
28 {
29
30 typedef std::map<int, TestCase> RunningTestCases;
31
32 namespace
33 {
34 const char* RED_COLOR="\e[1;31m";
35 const char* GREEN_COLOR="\e[1;32m";
36 const char* ASCII_RESET="\e[0m";
37 const char* ASCII_BOLD="\e[1m";
38 }
39
40
41 int RunTestCase( struct ::testcase_s& testCase )
42 {
43   int result = EXIT_STATUS_TESTCASE_FAILED;
44
45   try
46   {
47     if( testCase.startup )
48     {
49       testCase.startup();
50     }
51     result = testCase.function();
52     if( testCase.cleanup )
53     {
54       testCase.cleanup();
55     }
56   }
57   catch (...)
58   {
59     printf("Caught exception in test case.\n");
60     result = EXIT_STATUS_TESTCASE_ABORTED;
61   }
62
63   return result;
64 }
65
66 int RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput )
67 {
68   int testResult = EXIT_STATUS_TESTCASE_FAILED;
69
70   int pid = fork();
71   if( pid == 0 ) // Child process
72   {
73     if( suppressOutput )
74     {
75       close(STDOUT_FILENO);
76       close(STDERR_FILENO);
77     }
78     exit( RunTestCase( testCase ) );
79   }
80   else if(pid == -1)
81   {
82     perror("fork");
83     exit(EXIT_STATUS_FORK_FAILED);
84   }
85   else // Parent process
86   {
87     int status = 0;
88     int childPid = waitpid(-1, &status, 0);
89     if( childPid == -1 )
90     {
91       perror("waitpid");
92       exit(EXIT_STATUS_WAITPID_FAILED);
93     }
94     if( WIFEXITED(status) )
95     {
96       if( childPid > 0 )
97       {
98         testResult = WEXITSTATUS(status);
99         if( testResult )
100         {
101           printf("Test case %s failed: %d\n", testCase.name, testResult);
102         }
103       }
104     }
105     else if(WIFSIGNALED(status) )
106     {
107       testResult = EXIT_STATUS_TESTCASE_ABORTED;
108
109 #ifdef WCOREDUMP
110       if(WCOREDUMP(status))
111       {
112         printf("Test case %s crashed\n", testCase.name);
113       }
114 #endif
115       printf("Test case %s exited with signal %s\n", testCase.name, strsignal(WTERMSIG(status)));
116     }
117     else if(WIFSTOPPED(status))
118     {
119       printf("Test case %s stopped with signal %s\n", testCase.name, strsignal(WSTOPSIG(status)));
120     }
121   }
122   return testResult;
123 }
124
125 void OutputStatistics( int numPasses, int numFailures )
126 {
127   const char* failureColor = GREEN_COLOR;
128   if( numFailures > 0 )
129   {
130     failureColor = RED_COLOR;
131   }
132   printf("\rNumber of test passes:   %s%4d (%5.2f%%)%s\n", ASCII_BOLD, numPasses, 100.0f * (float)numPasses / (numPasses+numFailures),  ASCII_RESET);
133   printf("%sNumber of test failures:%s %s%4d%s\n", failureColor, ASCII_RESET, ASCII_BOLD, numFailures, ASCII_RESET);
134
135 }
136
137
138 int RunAll(const char* processName, ::testcase tc_array[], bool reRunFailed)
139 {
140   int numFailures = 0;
141   int numPasses = 0;
142
143   // Run test cases in child process( to kill output/handle signals ), but run serially.
144   for( unsigned int i=0; tc_array[i].name; i++)
145   {
146     int result = RunTestCaseInChildProcess( tc_array[i], true );
147     if( result == 0 )
148     {
149       numPasses++;
150     }
151     else
152     {
153       numFailures++;
154     }
155   }
156
157   OutputStatistics(numPasses, numFailures);
158
159   return numFailures;
160 }
161
162
163
164 // Constantly runs up to MAX_NUM_CHILDREN processes
165 int RunAllInParallel(  const char* processName, ::testcase tc_array[], bool reRunFailed)
166 {
167   int numFailures = 0;
168   int numPasses = 0;
169
170   RunningTestCases children;
171   std::vector<int> failedTestCases;
172
173   // Fork up to MAX_NUM_CHILDREN processes, then
174   // wait. As soon as a proc completes, fork the next.
175
176   int nextTestCase = 0;
177   int numRunningChildren = 0;
178
179   while( tc_array[nextTestCase].name || numRunningChildren > 0)
180   {
181     // Create more children (up to the max number or til the end of the array)
182     while( numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name )
183     {
184       int pid = fork();
185       if( pid == 0 ) // Child process
186       {
187         close(STDOUT_FILENO);
188         close(STDERR_FILENO);
189         exit( RunTestCase( tc_array[nextTestCase] ) );
190       }
191       else if(pid == -1)
192       {
193         perror("fork");
194         exit(EXIT_STATUS_FORK_FAILED);
195       }
196       else // Parent process
197       {
198         TestCase tc(nextTestCase, tc_array[nextTestCase].name);
199         children[pid] = tc;
200         nextTestCase++;
201         numRunningChildren++;
202       }
203     }
204
205     // Wait for the next child to finish
206
207     int status=0;
208     int childPid = waitpid(-1, &status, 0);
209     if( childPid == -1 )
210     {
211       perror("waitpid");
212       exit(EXIT_STATUS_WAITPID_FAILED);
213     }
214
215     if( WIFEXITED(status) )
216     {
217       if( childPid > 0 )
218       {
219         int testResult = WEXITSTATUS(status);
220         if( testResult )
221         {
222           printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult);
223           failedTestCases.push_back(children[childPid].testCase);
224           numFailures++;
225         }
226         else
227         {
228           numPasses++;
229         }
230         numRunningChildren--;
231       }
232     }
233
234     else if( WIFSIGNALED(status) || WIFSTOPPED(status))
235     {
236       status = WIFSIGNALED(status)?WTERMSIG(status):WSTOPSIG(status);
237
238       if( childPid > 0 )
239       {
240         RunningTestCases::iterator iter = children.find(childPid);
241         if( iter != children.end() )
242         {
243           printf("Test case %s exited with signal %s\n", iter->second.testCaseName, strsignal(status));
244           failedTestCases.push_back(iter->second.testCase);
245         }
246         else
247         {
248           printf("Unknown child process: %d signaled %s\n", childPid, strsignal(status));
249         }
250
251         numFailures++;
252         numRunningChildren--;
253       }
254     }
255   }
256
257   OutputStatistics( numPasses, numFailures );
258
259   if( reRunFailed )
260   {
261     for( unsigned int i=0; i<failedTestCases.size(); i++)
262     {
263       char* testCaseStrapline;
264       int numChars = asprintf(&testCaseStrapline, "Test case %s", tc_array[failedTestCases[i]].name );
265       printf("\n%s\n", testCaseStrapline);
266       for(int j=0; j<numChars; j++)
267       {
268         printf("=");
269       }
270       printf("\n");
271       RunTestCaseInChildProcess( tc_array[failedTestCases[i] ], false );
272     }
273   }
274
275   return numFailures;
276 }
277
278
279
280 int FindAndRunTestCase(::testcase tc_array[], const char* testCaseName)
281 {
282   int result = EXIT_STATUS_TESTCASE_NOT_FOUND;
283
284   for( int i = 0; tc_array[i].name; i++ )
285   {
286     if( !strcmp(testCaseName, tc_array[i].name) )
287     {
288       return RunTestCase( tc_array[i] );
289     }
290   }
291
292   printf("Unknown testcase name: \"%s\"\n", testCaseName);
293   return result;
294 }
295
296 void Usage(const char* program)
297 {
298   printf("Usage: \n"
299          "   %s <testcase name>\t\t Execute a test case\n"
300          "   %s \t\t Execute all test cases in parallel\n"
301          "   %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n",
302          program, program, program);
303 }
304
305 } // namespace