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