Revert "Remove dali-core dependency of GLES version."
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-toolkit / dali-toolkit-test-utils / test-harness.cpp
1 /*
2  * Copyright (c) 2017 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<int32_t, TestCase> RunningTestCases;
31
32 const char* basename(const char* path)
33 {
34   const char* ptr=path;
35   const char* slash=NULL;
36   for( ; *ptr != '\0' ; ++ptr )
37   {
38     if(*ptr == '/') slash=ptr;
39   }
40   if(slash != NULL) ++slash;
41   return slash;
42 }
43
44 int32_t RunTestCase( struct ::testcase_s& testCase )
45 {
46   int32_t result = EXIT_STATUS_TESTCASE_FAILED;
47
48 // dont want to catch exception as we want to be able to get
49 // gdb stack trace from the first error
50 // by default tests should all always pass with no exceptions
51   if( testCase.startup )
52   {
53     testCase.startup();
54   }
55   try
56   {
57     result = testCase.function();
58   }
59   catch( const char* )
60   {
61     // just catch test fail exception, return is already set to EXIT_STATUS_TESTCASE_FAILED
62   }
63   if( testCase.cleanup )
64   {
65     testCase.cleanup();
66   }
67
68   return result;
69 }
70
71
72 int32_t RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput )
73 {
74   int32_t testResult = EXIT_STATUS_TESTCASE_FAILED;
75
76   int32_t pid = fork();
77   if( pid == 0 ) // Child process
78   {
79     if( suppressOutput )
80     {
81       close(STDOUT_FILENO);
82       close(STDERR_FILENO);
83     }
84     else
85     {
86       printf("\n");
87       for(int32_t i=0; i<80; ++i) printf("#");
88       printf("\nTC: %s\n", testCase.name);
89       fflush(stdout);
90     }
91
92     int32_t status = RunTestCase( testCase );
93
94     if( ! suppressOutput )
95     {
96       fflush(stdout);
97       fflush(stderr);
98       fclose(stdout);
99       fclose(stderr);
100     }
101     exit( status );
102   }
103   else if(pid == -1)
104   {
105     perror("fork");
106     exit(EXIT_STATUS_FORK_FAILED);
107   }
108   else // Parent process
109   {
110     int32_t status = 0;
111     int32_t childPid = waitpid(pid, &status, 0);
112     if( childPid == -1 )
113     {
114       perror("waitpid");
115       exit(EXIT_STATUS_WAITPID_FAILED);
116     }
117     if( WIFEXITED(status) )
118     {
119       if( childPid > 0 )
120       {
121         testResult = WEXITSTATUS(status);
122         if( testResult )
123         {
124           printf("Test case %s failed: %d\n", testCase.name, testResult);
125         }
126       }
127     }
128     else if(WIFSIGNALED(status) )
129     {
130       int32_t signal = WTERMSIG(status);
131       testResult = EXIT_STATUS_TESTCASE_ABORTED;
132       if( signal == SIGABRT )
133       {
134         printf("Test case %s failed: test case asserted\n", testCase.name );
135       }
136       else
137       {
138         printf("Test case %s failed: exit with signal %s\n", testCase.name, strsignal(WTERMSIG(status)));
139       }
140     }
141     else if(WIFSTOPPED(status))
142     {
143       printf("Test case %s failed: stopped with signal %s\n", testCase.name, strsignal(WSTOPSIG(status)));
144     }
145   }
146   fflush(stdout);
147   fflush(stderr);
148   return testResult;
149 }
150
151 void OutputStatistics( const char* processName, int32_t numPasses, int32_t numFailures )
152 {
153   FILE* fp=fopen("summary.xml", "a");
154   if( fp != NULL )
155   {
156     fprintf( fp,
157              "  <suite name=\"%s\">\n"
158              "    <total_case>%d</total_case>\n"
159              "    <pass_case>%d</pass_case>\n"
160              "    <pass_rate>%5.2f</pass_rate>\n"
161              "    <fail_case>%d</fail_case>\n"
162              "    <fail_rate>%5.2f</fail_rate>\n"
163              "    <block_case>0</block_case>\n"
164              "    <block_rate>0.00</block_rate>\n"
165              "    <na_case>0</na_case>\n"
166              "    <na_rate>0.00</na_rate>\n"
167              "  </suite>\n",
168              basename(processName),
169              numPasses+numFailures,
170              numPasses,
171              (float)numPasses/(numPasses+numFailures),
172              numFailures,
173              (float)numFailures/(numPasses+numFailures) );
174     fclose(fp);
175   }
176 }
177
178 int32_t RunAll( const char* processName, ::testcase tc_array[] )
179 {
180   int32_t numFailures = 0;
181   int32_t numPasses = 0;
182
183   // Run test cases in child process( to kill output/handle signals ), but run serially.
184   for( uint32_t i=0; tc_array[i].name; i++)
185   {
186     int32_t result = RunTestCaseInChildProcess( tc_array[i], false );
187     if( result == 0 )
188     {
189       numPasses++;
190     }
191     else
192     {
193       numFailures++;
194     }
195   }
196
197   OutputStatistics( processName, numPasses, numFailures);
198
199   return numFailures;
200 }
201
202 // Constantly runs up to MAX_NUM_CHILDREN processes
203 int32_t RunAllInParallel(  const char* processName, ::testcase tc_array[], bool reRunFailed)
204 {
205   int32_t numFailures = 0;
206   int32_t numPasses = 0;
207
208   RunningTestCases children;
209   std::vector<int32_t> failedTestCases;
210
211   // Fork up to MAX_NUM_CHILDREN processes, then
212   // wait. As soon as a proc completes, fork the next.
213
214   int32_t nextTestCase = 0;
215   int32_t numRunningChildren = 0;
216
217   while( tc_array[nextTestCase].name || numRunningChildren > 0)
218   {
219     // Create more children (up to the max number or til the end of the array)
220     while( numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name )
221     {
222       int32_t pid = fork();
223       if( pid == 0 ) // Child process
224       {
225         close(STDOUT_FILENO);
226         close(STDERR_FILENO);
227         exit( RunTestCase( tc_array[nextTestCase] ) );
228       }
229       else if(pid == -1)
230       {
231         perror("fork");
232         exit(EXIT_STATUS_FORK_FAILED);
233       }
234       else // Parent process
235       {
236         TestCase tc(nextTestCase, tc_array[nextTestCase].name);
237         children[pid] = tc;
238         nextTestCase++;
239         numRunningChildren++;
240       }
241     }
242
243     // Wait for the next child to finish
244
245     int32_t status=0;
246     int32_t childPid = waitpid(-1, &status, 0);
247     if( childPid == -1 )
248     {
249       perror("waitpid");
250       exit(EXIT_STATUS_WAITPID_FAILED);
251     }
252
253     if( WIFEXITED(status) )
254     {
255       if( childPid > 0 )
256       {
257         int32_t testResult = WEXITSTATUS(status);
258         if( testResult )
259         {
260           printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult);
261           failedTestCases.push_back(children[childPid].testCase);
262           numFailures++;
263         }
264         else
265         {
266           numPasses++;
267         }
268         numRunningChildren--;
269       }
270     }
271
272     else if( WIFSIGNALED(status) || WIFSTOPPED(status))
273     {
274       status = WIFSIGNALED(status)?WTERMSIG(status):WSTOPSIG(status);
275
276       if( childPid > 0 )
277       {
278         RunningTestCases::iterator iter = children.find(childPid);
279         if( iter != children.end() )
280         {
281           printf("Test case %s exited with signal %s\n", iter->second.testCaseName, strsignal(status));
282           failedTestCases.push_back(iter->second.testCase);
283         }
284         else
285         {
286           printf("Unknown child process: %d signaled %s\n", childPid, strsignal(status));
287         }
288
289         numFailures++;
290         numRunningChildren--;
291       }
292     }
293   }
294
295   OutputStatistics( processName, numPasses, numFailures );
296
297   if( reRunFailed )
298   {
299     for( uint32_t i=0; i<failedTestCases.size(); i++)
300     {
301       char* testCaseStrapline;
302       int32_t numChars = asprintf(&testCaseStrapline, "Test case %s", tc_array[failedTestCases[i]].name );
303       printf("\n%s\n", testCaseStrapline);
304       for(int32_t j=0; j<numChars; j++)
305       {
306         printf("=");
307       }
308       printf("\n");
309       RunTestCaseInChildProcess( tc_array[failedTestCases[i] ], false );
310     }
311   }
312
313   return numFailures;
314 }
315
316
317
318 int32_t FindAndRunTestCase(::testcase tc_array[], const char* testCaseName)
319 {
320   int32_t result = EXIT_STATUS_TESTCASE_NOT_FOUND;
321
322   for( int32_t i = 0; tc_array[i].name; i++ )
323   {
324     if( !strcmp(testCaseName, tc_array[i].name) )
325     {
326       return RunTestCase( tc_array[i] );
327     }
328   }
329
330   printf("Unknown testcase name: \"%s\"\n", testCaseName);
331   return result;
332 }
333
334 void Usage(const char* program)
335 {
336   printf("Usage: \n"
337          "   %s <testcase name>\t\t Execute a test case\n"
338          "   %s \t\t Execute all test cases in parallel\n"
339          "   %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n"
340          "   %s -s\t\t Execute all test cases serially\n",
341          program, program, program, program);
342 }
343
344 } // namespace