X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=automated-tests%2Fsrc%2Fdali-toolkit%2Fdali-toolkit-test-utils%2Ftest-harness.cpp;h=b53630d48ebb339e098fd55a9a76be3884c33a70;hp=76b391b6dc163c906d12a036b83fee748edc6cec;hb=6b38c6c213808ada74270118045e7e23d8b4910d;hpb=a2519d330569f4898996dfcc74b8d30433bace42 diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-harness.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-harness.cpp index 76b391b..b53630d 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-harness.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-harness.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,45 +15,73 @@ */ #include "test-harness.h" + +#include #include #include #include +#include + +#include #include -#include -#include + +#include #include -#include +#include +#include namespace TestHarness { +typedef std::map RunningTestCases; -typedef std::map RunningTestCases; +const double MAXIMUM_CHILD_LIFETIME(60.0f); // 1 minute const char* basename(const char* path) { - const char* ptr=path; - const char* slash=NULL; - for( ; *ptr != '\0' ; ++ptr ) + const char* ptr = path; + const char* slash = NULL; + for(; *ptr != '\0'; ++ptr) { - if(*ptr == '/') slash=ptr; + if(*ptr == '/') slash = ptr; } if(slash != NULL) ++slash; return slash; } -int RunTestCase( struct ::testcase_s& testCase ) +void SuppressLogOutput() +{ + // Close stdout and stderr to suppress the log output + close(STDOUT_FILENO); // File descriptor number for stdout is 1 + close(STDERR_FILENO); // File descriptor number for stderr is 2 + + // The POSIX specification requires that /dev/null must be provided, + // The open function always chooses the lowest unused file descriptor + // It is sufficient for stdout to be writable. + open("/dev/null", O_WRONLY); // Redirect file descriptor number 1 (i.e. stdout) to /dev/null + // When stderr is opened it must be both readable and writable. + open("/dev/null", O_RDWR); // Redirect file descriptor number 2 (i.e. stderr) to /dev/null +} + +int32_t RunTestCase(struct ::testcase_s& testCase) { - int result = EXIT_STATUS_TESTCASE_FAILED; + int32_t result = EXIT_STATUS_TESTCASE_FAILED; -// dont want to catch exception as we want to be able to get -// gdb stack trace from the first error -// by default tests should all always pass with no exceptions - if( testCase.startup ) + // dont want to catch exception as we want to be able to get + // gdb stack trace from the first error + // by default tests should all always pass with no exceptions + if(testCase.startup) { testCase.startup(); } - result = testCase.function(); - if( testCase.cleanup ) + try + { + result = testCase.function(); + } + catch(const char*) + { + // just catch test fail exception, return is already set to EXIT_STATUS_TESTCASE_FAILED + } + if(testCase.cleanup) { testCase.cleanup(); } @@ -61,37 +89,35 @@ int RunTestCase( struct ::testcase_s& testCase ) return result; } - -int RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutput ) +int32_t RunTestCaseInChildProcess(struct ::testcase_s& testCase, bool suppressOutput) { - int testResult = EXIT_STATUS_TESTCASE_FAILED; + int32_t testResult = EXIT_STATUS_TESTCASE_FAILED; - int pid = fork(); - if( pid == 0 ) // Child process + int32_t pid = fork(); + if(pid == 0) // Child process { - if( suppressOutput ) + if(suppressOutput) { - close(STDOUT_FILENO); - close(STDERR_FILENO); + SuppressLogOutput(); } else { printf("\n"); - for(int i=0; i<80; ++i) printf("#"); + for(int32_t i = 0; i < 80; ++i) printf("#"); printf("\nTC: %s\n", testCase.name); fflush(stdout); } - int status = RunTestCase( testCase ); + int32_t status = RunTestCase(testCase); - if( ! suppressOutput ) + if(!suppressOutput) { fflush(stdout); fflush(stderr); fclose(stdout); fclose(stderr); } - exit( status ); + exit(status); } else if(pid == -1) { @@ -100,31 +126,31 @@ int RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutpu } else // Parent process { - int status = 0; - int childPid = waitpid(pid, &status, 0); - if( childPid == -1 ) + int32_t status = 0; + int32_t childPid = waitpid(pid, &status, 0); + if(childPid == -1) { perror("waitpid"); exit(EXIT_STATUS_WAITPID_FAILED); } - if( WIFEXITED(status) ) + if(WIFEXITED(status)) { - if( childPid > 0 ) + if(childPid > 0) { testResult = WEXITSTATUS(status); - if( testResult ) + if(testResult) { printf("Test case %s failed: %d\n", testCase.name, testResult); } } } - else if(WIFSIGNALED(status) ) + else if(WIFSIGNALED(status)) { - int signal = WTERMSIG(status); - testResult = EXIT_STATUS_TESTCASE_ABORTED; - if( signal == SIGABRT ) + int32_t signal = WTERMSIG(status); + testResult = EXIT_STATUS_TESTCASE_ABORTED; + if(signal == SIGABRT) { - printf("Test case %s failed: test case asserted\n", testCase.name ); + printf("Test case %s failed: test case asserted\n", testCase.name); } else { @@ -141,43 +167,43 @@ int RunTestCaseInChildProcess( struct ::testcase_s& testCase, bool suppressOutpu return testResult; } -void OutputStatistics( const char* processName, int numPasses, int numFailures ) +void OutputStatistics(const char* processName, int32_t numPasses, int32_t numFailures) { - FILE* fp=fopen("summary.xml", "a"); - if( fp != NULL ) + FILE* fp = fopen("summary.xml", "a"); + if(fp != NULL) { - fprintf( fp, - " \n" - " %d\n" - " %d\n" - " %5.2f\n" - " %d\n" - " %5.2f\n" - " 0\n" - " 0.00\n" - " 0\n" - " 0.00\n" - " \n", - basename(processName), - numPasses+numFailures, - numPasses, - (float)numPasses/(numPasses+numFailures), - numFailures, - (float)numFailures/(numPasses+numFailures) ); + fprintf(fp, + " \n" + " %d\n" + " %d\n" + " %5.2f\n" + " %d\n" + " %5.2f\n" + " 0\n" + " 0.00\n" + " 0\n" + " 0.00\n" + " \n", + basename(processName), + numPasses + numFailures, + numPasses, + (float)numPasses / (numPasses + numFailures), + numFailures, + (float)numFailures / (numPasses + numFailures)); fclose(fp); } } -int RunAll( const char* processName, ::testcase tc_array[] ) +int32_t RunAll(const char* processName, ::testcase tc_array[]) { - int numFailures = 0; - int numPasses = 0; + int32_t numFailures = 0; + int32_t numPasses = 0; // Run test cases in child process( to kill output/handle signals ), but run serially. - for( unsigned int i=0; tc_array[i].name; i++) + for(uint32_t i = 0; tc_array[i].name; i++) { - int result = RunTestCaseInChildProcess( tc_array[i], false ); - if( result == 0 ) + int32_t result = RunTestCaseInChildProcess(tc_array[i], false); + if(result == 0) { numPasses++; } @@ -187,37 +213,36 @@ int RunAll( const char* processName, ::testcase tc_array[] ) } } - OutputStatistics( processName, numPasses, numFailures); + OutputStatistics(processName, numPasses, numFailures); return numFailures; } // Constantly runs up to MAX_NUM_CHILDREN processes -int RunAllInParallel( const char* processName, ::testcase tc_array[], bool reRunFailed) +int32_t RunAllInParallel(const char* processName, ::testcase tc_array[], bool reRunFailed) { - int numFailures = 0; - int numPasses = 0; + int32_t numFailures = 0; + int32_t numPasses = 0; - RunningTestCases children; - std::vector failedTestCases; + RunningTestCases children; + std::vector failedTestCases; // Fork up to MAX_NUM_CHILDREN processes, then // wait. As soon as a proc completes, fork the next. - int nextTestCase = 0; - int numRunningChildren = 0; + int32_t nextTestCase = 0; + int32_t numRunningChildren = 0; - while( tc_array[nextTestCase].name || numRunningChildren > 0) + while(tc_array[nextTestCase].name || numRunningChildren > 0) { // Create more children (up to the max number or til the end of the array) - while( numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name ) + while(numRunningChildren < MAX_NUM_CHILDREN && tc_array[nextTestCase].name) { - int pid = fork(); - if( pid == 0 ) // Child process + int32_t pid = fork(); + if(pid == 0) // Child process { - close(STDOUT_FILENO); - close(STDERR_FILENO); - exit( RunTestCase( tc_array[nextTestCase] ) ); + SuppressLogOutput(); + exit(RunTestCase(tc_array[nextTestCase])); } else if(pid == -1) { @@ -227,28 +252,45 @@ int RunAllInParallel( const char* processName, ::testcase tc_array[], bool reRu else // Parent process { TestCase tc(nextTestCase, tc_array[nextTestCase].name); + tc.startTime = std::chrono::steady_clock::now(); + children[pid] = tc; nextTestCase++; numRunningChildren++; } } - // Wait for the next child to finish + // Check to see if any children have finished yet + int32_t status = 0; + int32_t childPid = waitpid(-1, &status, WNOHANG); + if(childPid == 0) + { + // No children have finished. + // Check if any have exceeded execution time + auto endTime = std::chrono::steady_clock::now(); - int status=0; - int childPid = waitpid(-1, &status, 0); - if( childPid == -1 ) + for(auto& tc : children) + { + std::chrono::steady_clock::duration timeSpan = endTime - tc.second.startTime; + std::chrono::duration seconds = std::chrono::duration_cast>(timeSpan); + if(seconds.count() > MAXIMUM_CHILD_LIFETIME) + { + // Kill the child process. A subsequent call to waitpid will process signal result below. + kill(tc.first, SIGKILL); + } + } + } + else if(childPid == -1) // waitpid errored { perror("waitpid"); exit(EXIT_STATUS_WAITPID_FAILED); } - - if( WIFEXITED(status) ) + else // a child has finished { - if( childPid > 0 ) + if(WIFEXITED(status)) { - int testResult = WEXITSTATUS(status); - if( testResult ) + int32_t testResult = WEXITSTATUS(status); + if(testResult) { printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult); failedTestCases.push_back(children[childPid].testCase); @@ -260,16 +302,13 @@ int RunAllInParallel( const char* processName, ::testcase tc_array[], bool reRu } numRunningChildren--; } - } - - else if( WIFSIGNALED(status) || WIFSTOPPED(status)) - { - status = WIFSIGNALED(status)?WTERMSIG(status):WSTOPSIG(status); - if( childPid > 0 ) + else if(WIFSIGNALED(status) || WIFSTOPPED(status)) { + status = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status); + RunningTestCases::iterator iter = children.find(childPid); - if( iter != children.end() ) + if(iter != children.end()) { printf("Test case %s exited with signal %s\n", iter->second.testCaseName, strsignal(status)); failedTestCases.push_back(iter->second.testCase); @@ -285,38 +324,36 @@ int RunAllInParallel( const char* processName, ::testcase tc_array[], bool reRu } } - OutputStatistics( processName, numPasses, numFailures ); + OutputStatistics(processName, numPasses, numFailures); - if( reRunFailed ) + if(reRunFailed) { - for( unsigned int i=0; i\t\t Execute a test case\n" - " %s \t\t Execute all test cases in parallel\n" - " %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n" - " %s -s\t\t Execute all test cases serially\n", - program, program, program, program); + printf( + "Usage: \n" + " %s \t\t Execute a test case\n" + " %s \t\t Execute all test cases in parallel\n" + " %s -r\t\t Execute all test cases in parallel, rerunning failed test cases\n" + " %s -s\t\t Execute all test cases serially\n", + program, + program, + program, + program); } -} // namespace +} // namespace TestHarness