From 99f4854be6276eae4a7c2367a6c30f223b1d8ddf Mon Sep 17 00:00:00 2001 From: cherierm Date: Tue, 19 Dec 2006 16:30:21 +0000 Subject: [PATCH] test suite concept implementation git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@3016 48e7efb5-ca39-0410-a469-dd3cf9ba447f --- win32_test_app/src/TTestSuite.c | 1059 +++++++++++++++++++++++++++++++ 1 file changed, 1059 insertions(+) create mode 100644 win32_test_app/src/TTestSuite.c diff --git a/win32_test_app/src/TTestSuite.c b/win32_test_app/src/TTestSuite.c new file mode 100644 index 0000000000..854b222f48 --- /dev/null +++ b/win32_test_app/src/TTestSuite.c @@ -0,0 +1,1059 @@ +#include + + +/* + * Create a new s_TestSuite an returns a pointer to self. + */ +TestSuite_t TestSuite_new(void) +{ + TestSuite_t testSuite = calloc(1,sizeof(s_TestSuite_t)); + + if(NULL == testSuite){ + setErrno(E_TEST_SUITE_ALLOCATION_FAILED); + TestSuite_free(testSuite); + return NULL; + } + + testSuite->stream = Stream_new(); + + if(NULL == testSuite->stream){ + TestSuite_free(testSuite); + return NULL; + } + + testSuite->test_case_context = TestCaseContext_new(); + + if(NULL == testSuite->test_case_context){ + TestSuite_free(testSuite); + return NULL; + } + + testSuite->test_case_context->hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + + testSuite->threads = ThreadDynarray_new(15); + + if(NULL == testSuite->threads){ + TestSuite_free(testSuite); + return NULL; + } + + testSuite->successCount = 0; + testSuite->failureCount = 0; + + return testSuite; +} + +/* + * Initialize the s_TestSuite structure. + */ +errno_t TestSuite_initialize(TestSuite_t ptr,int argc,char *argv[]) +{ + switch(argc) + { + case 1: + TestSuite_print("Run the test case from the console.\n"); + + ptr->stream->file = stdin; + return E_SUCCESS; + + case 2: + + if(E_SUCCESS != Stream_isValidFile(argv[1])) + return getErrno(); + + printf("\n\n Test runner : %s\n\n",argv[1]); + + if(E_SUCCESS != Stream_openFile(ptr->stream,argv[1])) + return getErrno(); + + return E_SUCCESS; + + default: + { + setErrno(E_BAD_USAGE); + return getErrno(); + } + } +} + +/* + * Launch the test suite. + */ +void TestSuite_run(TestSuite_t testSuite) +{ + Stream_t stream = testSuite->stream; + + /* Handle all lines in the testsuite file (or from stdin) */ + while((Stream_getLine(stream) != -1) && (E_SUCCESS == getErrno())) + { + /* Don't process blank lines. */ + if (Stream_lineIsBlank(stream)) + continue; + + /* Check if the current text line contains a invalid token. */ + if(Stream_lineContainsInvalidToken(stream)) + return; + + /* Check if the text line contains a meta command. */ + if(Stream_lineIsMetaCommand(stream)){ + /* Check if the current text line contains a unknown meta command. */ + if(Stream_lineIsUnknwnMetaCommand(stream)) + return; + + /* Check the meta command validity.*/ + if(Stream_lineIsInvalidMetaCommand(stream)) + return; + + /* We have a valid meta command, process it */ + if(E_SUCCESS != TestSuite_processMetaCommand(testSuite)) + return; + + continue; + } + + /* Handle the comment. */ + if(Stream_lineIsComment(stream)){ + Stream_printLine(testSuite->stream,comment_line_type); + continue; + } + + /* Handle expected child output. */ + if(Stream_lineIsExpectedChildOutput(stream)){ + if(E_SUCCESS != TestSuite_processExpectedChildOutput(testSuite)) + return; + + continue; + } + + /* Handle expected child input. */ + if(Stream_lineIsChildInput(stream)){ + if(E_SUCCESS != TestSuite_processChildInput(testSuite)) + return; + + continue; + } + + if(Stream_lineIsChangeDir(stream)){ + if(E_SUCCESS != TestSuite_changeDir(testSuite)) + return; + + continue; + } + + /* Handle synchrone synchrone test case. */ + if(Stream_lineIsSyncTestCase(stream)){ + TestCaseContext_setName(testSuite->test_case_context,stream->line + 2); + + TestSuite_runSyncTestCase(testSuite->test_case_context); + + + if(TestSuite_iSPostOutputCheckingEnabled(testSuite->test_case_context)){ + TestSuite_checkChildOutput(testSuite->test_case_context); + } + + if(TestSuite_iSExitCodeCheckingEnabled(testSuite->test_case_context)){ + if(E_SUCCESS != TestSuite_checkChildExitCode(testSuite->test_case_context)) + return; + } + + + if(E_SUCCESS != getErrno()) + return; + } + /* Handle asynchrone synchrone test case. */ + else if(Stream_lineIsAsyncTestCase(stream)) + { + TestCaseContext_setName(testSuite->test_case_context,stream->line + 2); + + if(E_SUCCESS != TestSuite_runAsyncTestCase(testSuite)) + return; + } + else + { + ASSERT(false); + } + + /* Clear the child input stream. */ + Buffer_clear(testSuite->test_case_context->inputBuffer); + /* Clear the command line buffer. */ + Buffer_clear(testSuite->test_case_context->commandLineBuffer); + } +} +/* + * Meta command processing. + */ +errno_t TestSuite_processMetaCommand(TestSuite_t testSuite) +{ + Stream_t stream = testSuite->stream; + + if(!strncmp("set timeout ",stream->line + 2,strlen("set timeout "))) + { + TestSuite_setTimeout(testSuite); + } + else if(!strncmp("command line ",stream->line + 2,strlen("command line"))) + { + TestSuite_setCommandLine(testSuite); + } + else if(!strncmp("enable output checking",stream->line + 2,strlen("enable output checking"))) + { + TestSuite_enableOutputChecking(testSuite); + } + else if(!strncmp("disable output checking",stream->line + 2,strlen("disable output checking"))) + { + TestSuite_disableOutputChecking(testSuite); + } + else if(!strncmp("enable post output checking",stream->line + 2,strlen("enable post output checking"))) + { + TestSuite_enablePostOutputChecking(testSuite); + } + else if(!strncmp("disable post output checking",stream->line + 2,strlen("disable post output checking"))) + { + TestSuite_disablePostOutputChecking(testSuite); + } + else if(!strncmp("expect exit code ",stream->line + 2,strlen("expect exit code "))) + { + TestSuite_setExpectedExitCode(testSuite); + } + else if(!strncmp("export ",stream->line + 2,strlen("export "))) + { + TestSuite_export(testSuite); + } + else if(!strncmp("unset ",stream->line + 2,strlen("unset "))) + { + TestSuite_unset(testSuite); + } + else if(!strncmp("create console",stream->line + 2,strlen("create console"))) + { + TestSuite_createConsole(testSuite); + } + else if(!strncmp("create no console",stream->line + 2,strlen("create no console"))) + { + TestSuite_createNoConsole(testSuite); + } + else if(!strncmp("enable exit code checking",stream->line + 2,strlen("enable exit code checking"))) + { + TestSuite_enableExitCodeChecking(testSuite); + } + else if(!strncmp("disable exit code checking",stream->line + 2,strlen("disable exit code checking"))) + { + TestSuite_disableExitCodeChecking(testSuite); + } + else + { + /* TODO */ + ASSERT(false); + } + + return E_SUCCESS; + +} + +/* + * Set the timeout of the test case context of the + * test suite. + */ +void TestSuite_setTimeout(TestSuite_t testSuite) +{ + + int timeout = atoi(testSuite->stream->line + 2 + strlen("set timeout ")); + TestCaseContext_setTimeout(testSuite->test_case_context,timeout); +} + +/* + * Enable output checking for the current test case context. + */ +void TestSuite_enableOutputChecking(TestSuite_t testSuite) +{ + TestCaseContext_enableOutputChecking(testSuite->test_case_context); +} + +void TestSuite_setCommandLine(TestSuite_t testSuite) +{ + TestCaseContext_setCommandLine(testSuite->test_case_context,testSuite->stream->line + 2 + strlen("command line ")); +} + +/* + * Disable output checking for the current test case context. + */ +void TestSuite_disableOutputChecking(TestSuite_t testSuite) +{ + TestCaseContext_disableOutputChecking(testSuite->test_case_context); +} + +/* + * Enable post output checking for the current test case context. + */ +void TestSuite_enablePostOutputChecking(TestSuite_t testSuite) +{ + TestCaseContext_enable_post_output_checking(testSuite->test_case_context); +} + +void TestSuite_createConsole(TestSuite_t testSuite) +{ + TestCaseContext_createConsole(testSuite->test_case_context); +} + +void TestSuite_createNoConsole(TestSuite_t testSuite) +{ + TestCaseContext_createNoConsole(testSuite->test_case_context); +} + +/* + * Disable post output checking for the current test case context. + */ +void TestSuite_disablePostOutputChecking(TestSuite_t testSuite) +{ + TestCaseContext_disablePostOutputChecking(testSuite->test_case_context); +} + +/* + * Set the expected exit code of the current test case context of the test suite. + */ +void TestSuite_setExpectedExitCode(TestSuite_t testSuite) +{ + int expectedExitCode = atoi(testSuite->stream->line + 2 + strlen("expect exit code ")); + TestCaseContext_setExpectedExitCode(testSuite->test_case_context,expectedExitCode); +} + +void TestSuite_enableExitCodeChecking(TestSuite_t testSuite) +{ + TestCaseContext_enableExitCodeChecking(testSuite->test_case_context); +} + +void TestSuite_disableExitCodeChecking(TestSuite_t testSuite) +{ + TestCaseContext_disableExitCodeChecking(testSuite->test_case_context); +} + + +/* + * Export a variable in the environment of the current test_runner.exe process. + */ +errno_t TestSuite_export(TestSuite_t testSuite) +{ + /* TODO trim */ + const char* ptr; + const char* pos; + char __buffer[50] = {0}; + char* line = testSuite->stream->line + strlen("export "); + + line[strlen(line) -1] = '\0'; + + ptr = strchr(line,' '); + pos= ++ptr; + ptr = strchr(line,'='); + strncpy(__buffer,pos,ptr - pos); + if(!SetEnvironmentVariable(__buffer,++ptr)) + { + setErrno(E_EXPORT_FAILED); + Stream_printLine(testSuite->stream,export_failed_line_type); + return getErrno(); + + } + + return E_SUCCESS; +} + +errno_t TestSuite_unset(TestSuite_t testSuite) +{ + char line[128] = {0}; + const char* ptr; + strcpy(line,testSuite->stream->line +2); + ptr = strchr(line,' '); + line[strlen(line) -1] = '\0'; + + if(!SetEnvironmentVariable(++ptr,NULL)) + { + + setErrno(E_UNSET_FAILED); + Stream_printLine(testSuite->stream,unset_failed_line_type); + return getErrno(); + } + + return E_SUCCESS; +} + +/* + * Expected child output processing. + */ +errno_t TestSuite_processExpectedChildOutput(TestSuite_t testSuite) +{ + /* TODO : logic error*/ + if(!TestCaseContext_isOutputCheckingEnabled(testSuite->test_case_context)) + return E_SUCCESS; + + /* TODO : trim */ + TestCaseContext_appendExpectedOutput(testSuite->test_case_context,testSuite->stream->line + 2); + + return E_SUCCESS; +} + +/* + * Child input processing. + */ +errno_t TestSuite_processChildInput(TestSuite_t testSuite) +{ + /* TODO : trim */ + TestCaseContext_appendChildInput(testSuite->test_case_context,testSuite->stream->line + 2); + + return E_SUCCESS; +} + +/* + * Free the s_TestSuite pointed to by ptr. + */ +void TestSuite_free(TestSuite_t testSuite) +{ + ThreadEntry_t entry; + unsigned long count; + unsigned long i; + DWORD dwWaitResult; + bool steel_running; + bool last_async_process_error = false; + DWORD ExitCode = 0; + errno_t e = getErrno(); + + if(NULL == testSuite) + return; + + count = ThreadDynarray_getCount(testSuite->threads); + + /* Wait for all asynchrone process */ + if(NULL != testSuite->threads && count) + { + while(true) + { + steel_running = false; + + for(i = 0;i < count ; i++) + { + entry = ThreadDynarray_at(testSuite->threads,i); + + GetExitCodeThread(entry->hThread,&ExitCode); + + if(STILL_ACTIVE == ExitCode) + { + Sleep(0); + steel_running = true; + } + } + + if(!steel_running) + break; + } + + for(i = 0;i threads,i); + + if(entry->context->pi.hProcess) + { + dwWaitResult=WaitForSingleObject(entry->hThread,INFINITE); + + if((WAIT_FAILED == dwWaitResult)) + TerminateThread(entry->hThread,0); + else + CloseHandle(entry->hThread); + } + + /*if(((E_SUCCESS == e) || (E_EXIT_CODE_DONT_MATCH == e) || (E_OUTPUT_DONT_MATCH == e)) && !last_async_process_error) + {*/ + /* Child output and exit code checking */ + if(TestSuite_iSPostOutputCheckingEnabled(entry->context)) + { + if(E_SUCCESS != TestSuite_checkChildOutput(entry->context)) + last_async_process_error = true; + } + + if(TestSuite_iSExitCodeCheckingEnabled(entry->context)) + { + if(E_SUCCESS != TestSuite_checkChildExitCode(entry->context)) + last_async_process_error = true; + } + } + + TestCaseContext_free(entry->context); + /*}*/ + + ThreadDynarray_destroy(testSuite->threads); + } + + if(NULL != testSuite->test_case_context) + TestCaseContext_free(testSuite->test_case_context); + + if(NULL != testSuite->stream) + Stream_free(testSuite->stream); + + free(testSuite); +} + +/* + * Check the child output. + */ +errno_t TestSuite_checkChildOutput(TestCaseContext_t context) +{ + bool are_equals = false; + char str[256] = {0}; + + + if (context->expectedOutputBuffer->size==0 && context->outputBuffer->size==0) + return E_SUCCESS; + + Buffer_chomp(context->outputBuffer); + Buffer_chomp(context->expectedOutputBuffer); + + + if (context->outputBuffer->size != context->expectedOutputBuffer->size || strcmp(context->outputBuffer->data, context->expectedOutputBuffer->data)) + { + strcpy(str," \n"); + TestSuite_print(str); + + } + else + { + are_equals = true; + strcpy(str," \n"); + TestSuite_print(str); + + + } + + memset(str,0,256); + + if(context->expectedOutputBuffer->size) + { + sprintf(str," SIZE (%4d) DATA (%s)\n",context->expectedOutputBuffer->size,context->expectedOutputBuffer->data); + TestSuite_print(str); + } + else + { + scanf(str," SIZE (%4d) DATA (%s)\n",context->expectedOutputBuffer->size,"empty"); + TestSuite_print(str); + } + + memset(str,0,256); + + if(context->outputBuffer->size) + { + sprintf(str," SIZE (%4d) DATA (%s)\n",context->outputBuffer->size,context->outputBuffer->data); + TestSuite_print(str); + } + else + { + sprintf(str," SIZE (%4d) DATA (%s)\n",context->outputBuffer->size,"empty"); + TestSuite_print(str); + } + + Buffer_clear(context->expectedOutputBuffer); + Buffer_clear(context->outputBuffer); + + if(!are_equals) + { + setErrno(E_OUTPUT_DONT_MATCH); + return getErrno(); + } + + return E_SUCCESS; +} + +/* + * Check the child process exit code. + */ +errno_t TestSuite_checkChildExitCode(TestCaseContext_t context) +{ + bool __success = false; + char str[256] = {0}; + + sprintf(str," %s %3ld\n",context->name,context->exitCode); + TestSuite_print(str); + + memset(str,0,256); + + /* if a expected exit code was signaled, compare it with the real. */ + if(context->expectedExitCode != INVALID_EXIT_CODE) + { + if(context->expectedExitCode != context->exitCode ) + { + + TestSuite_print("\n"); + } + else + { + __success = true; + TestSuite_print("\n"); + } + sprintf(str," (%3d)\n",context->expectedExitCode); + TestSuite_print(str); + + memset(str,0,256); + + sprintf(str," (%3d)\n",context->exitCode); + TestSuite_print(str); + + context->expectedExitCode = INVALID_EXIT_CODE; + } + + if(!__success) + { + setErrno(E_EXIT_CODE_DONT_MATCH); + return getErrno(); + } + + return E_SUCCESS; +} + +/* + * Terminate the test suite. + */ +void TestSuite_terminate(TestSuite_t testSuite) +{ + TestCaseContext_t context = testSuite->test_case_context; + + /* cleanup the child_input_stream/output buffers. */ + if(NULL != context->inputBuffer) + Buffer_free(context->inputBuffer); + + if(NULL != context->outputBuffer) + Buffer_free(context->outputBuffer); + + if(NULL != context->expectedOutputBuffer) + Buffer_free(context->expectedOutputBuffer); + + /* close the file stream. */ + if(NULL != testSuite->stream) + Stream_free(testSuite->stream); + + +} + + +/* + * Print message + */ +void TestSuite_print(const char* str) +{ + char* t = (char*)calloc(1,20); + + __time(t); + + EnterCriticalSection(&cs); + printf("%s %s",t,str); + LeaveCriticalSection(&cs); + + free(t); + +} + +unsigned long WINAPI TestSuite_asyncReadChildOutput(void* param) +{ + char str[1024] = {0}; + char __buffer[1024] = {0}; + + DWORD nBytesRead; + DWORD nCharsWritten; + TestCaseContext_t context = (TestCaseContext_t)param; + HANDLE hPipeRead = context->hOutputRead; + + + while (context->runThread) + { + if (!ReadFile(hPipeRead,str,sizeof(str),&nBytesRead,NULL) || !nBytesRead){ + if (GetLastError() == ERROR_BROKEN_PIPE){ + break; + } + else{ + /* TODO */ + context->threadExitCode = 1; + exit(1); + } + } + + if(nBytesRead){ + if(context->isOutputCheckingEnabled){ + if(!Buffer_empty(context->outputBuffer)) + Buffer_clear(context->outputBuffer); + + TestSuite_print(str); + + + Buffer_append(context->outputBuffer,str); + } + + memset(str,0,1024); + memset(__buffer,0,1024); + } + + } + context->threadExitCode = 0; + return 0; +} + +errno_t TestSuite_runAsyncTestCase(TestSuite_t testSuite) +{ + DWORD ThreadId; + s_ThreadEntry_t entry; + /* = (ThreadEntry_t)calloc(1,sizeof(s_ThreadEntry_t));*/ + + TestCaseContext_t context = testSuite->test_case_context; + memset(&entry,0,sizeof(s_ThreadEntry_t)); + entry.context = TestCaseContext_new(); + + Buffer_append(entry.context->inputBuffer,context->inputBuffer->data); + Buffer_append(entry.context->outputBuffer,context->outputBuffer->data); + Buffer_append(entry.context->expectedOutputBuffer,context->expectedOutputBuffer->data); + Buffer_append(entry.context->commandLineBuffer,context->commandLineBuffer->data); + entry.context->name = strdup(context->name); + entry.context->timeoutValue = context->timeoutValue; + entry.context->isOutputCheckingEnabled = context->isOutputCheckingEnabled; + entry.context->isPostOutputCheckingEnabled = context->isPostOutputCheckingEnabled; + entry.context->expectedExitCode = context->expectedExitCode; + entry.context->createConsole = context->createConsole; + entry.context->exitCodeCheckingEnabled = context->exitCodeCheckingEnabled; + entry.context->hConsole = context->hConsole; + Buffer_clear(context->inputBuffer); + Buffer_clear(context->outputBuffer); + Buffer_clear(context->expectedOutputBuffer); + memset(&(entry.context->pi),0,sizeof(PROCESS_INFORMATION)); + entry.context->runThread = true; + + entry.hThread = CreateThread(NULL,0,TestSuite_runSyncTestCase,(LPVOID)entry.context,CREATE_SUSPENDED,&ThreadId); + entry.threadId = ThreadId; + ThreadDynarray_pushBack(testSuite->threads,&entry); + ResumeThread(entry.hThread); + Sleep(0); + setErrno(E_SUCCESS); + + return getErrno(); +} + +unsigned long WINAPI TestSuite_runSyncTestCase( void* param) +{ + STARTUPINFO si = {0}; + SECURITY_ATTRIBUTES sa = {0}; + DWORD dwWaitResult = 0; + DWORD dwExitCode = 0; + DWORD ThreadId; + DWORD nBytes = 0; + DWORD dwCreationMode = CREATE_NO_WINDOW; + char cmdLine[4098] = {0}; + + TestCaseContext_t context = (TestCaseContext_t)param; + context->started = true; + + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + /* The pipe handes can be inherited by the child. */ + sa.bInheritHandle = TRUE; + + /* Create a write pipe handle for the child std output */ + if(!CreatePipe(&(context->hChildStdoutReadTmp),&(context->hChildStdOutWrite),&sa,0)) + { + setErrno(E_CANNOT_CREATE_CHILD_STDOUT_READ_HANDLE); + return getErrno(); + } + + /* + * Create a duplicate of the output write handle for the std error + * write handle. This is necessary in case the child application closes + * one of its std output handles. + */ + if(!DuplicateHandle(GetCurrentProcess(),(context->hChildStdOutWrite),GetCurrentProcess(),&(context->hChildStderr),0,TRUE,DUPLICATE_SAME_ACCESS)) + { + setErrno(E_CANNOT_CREATE_CHILD_STDERR_READ_HANDLE); + return getErrno(); + } + + /* Create a read pipe handle for the child std input */ + if(!CreatePipe(&(context->hChildStdInRead),&(context->hChildStdinWriteTmp),&sa,0)) + { + setErrno(E_CANNOT_CREATE_CHILD_STDIN_WRITE_HANDLE); + return getErrno(); + } + + + /* Create new output read handle and the input write handle use by + * the parent process to communicate with his child. Set the Properties + * to FALSE. Otherwise, the child inherits the properties and, as a + * result, non-closeable handles to the pipes are created. + */ + + /* Read handle for read operations on the child std output. */ + if(!DuplicateHandle(GetCurrentProcess(),(context->hChildStdoutReadTmp),GetCurrentProcess(),&(context->hOutputRead),0,FALSE, DUPLICATE_SAME_ACCESS)) + { + setErrno(E_CANNOT_CREATE_STDOUT_READ_HANDLE); + return getErrno(); + } + + + /* Write handle for write operations on the child std input. */ + if(!DuplicateHandle(GetCurrentProcess(),(context->hChildStdinWriteTmp),GetCurrentProcess(),&(context->hInputWrite), 0,FALSE,DUPLICATE_SAME_ACCESS)) + { + setErrno(E_CANNOT_CREATE_STDIN_WRITE_HANDLE); + return getErrno(); + } + + + /* Close inheritable copies of the handles you do not want to be inherited. */ + if(!CloseHandle((context->hChildStdoutReadTmp))) + { + setErrno(E_CANNOT_CLOSE_CHILD_STDIN_TEMPORY_HANDLE); + return getErrno(); + } + + context->hChildStdoutReadTmp = NULL; + + if(!CloseHandle((context->hChildStdinWriteTmp))) + { + setErrno(E_CANNOT_CLOSE_CHILD_STDOUT_TEMPORY_HANDLE); + return getErrno(); + } + + + context->hChildStdinWriteTmp = NULL; + + si.cb = sizeof(STARTUPINFO); + /* Set the child std handles. */ + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = context->hChildStdOutWrite; + si.hStdInput = context->hChildStdInRead; + si.hStdError = context->hChildStderr; + + if(context->createConsole) + dwCreationMode = CREATE_NEW_CONSOLE; + + if(!Buffer_empty(context->commandLineBuffer)){ + Buffer_chomp(context->commandLineBuffer); + sprintf(cmdLine,"%s %s",context->name,context->commandLineBuffer->data); + } + else + strcpy(cmdLine,context->name); + + + /* Create the child process. */ + if(!CreateProcess(NULL,cmdLine,NULL,NULL,TRUE,dwCreationMode,NULL,NULL,&si,&(context->pi))){ + setErrno(E_CANNOT_CREATE_CHILD_PROCESS); + return getErrno(); + } + + if(!CloseHandle(context->pi.hThread)){ + setErrno(E_CANNOT_CLOSE_PROCESS_THREAD_HANDLE); + return getErrno(); + } + + + context->pi.hThread = NULL; + + /* close unnessary pipe handles. */ + if(!CloseHandle(context->hChildStdOutWrite)){ + setErrno(E_CANNOT_CLOSE_CHILD_STDOUT_HANDLE); + return getErrno(); + } + + context->hChildStdOutWrite = NULL; + + if(!CloseHandle(context->hChildStdInRead)){ + setErrno(E_CANNOT_CLOSE_CHILD_STDIN_HANDLE); + return getErrno(); + } + + context->hChildStdInRead = NULL; + + if(!CloseHandle(context->hChildStderr)){ + setErrno(E_CANNOT_CLOSE_CHILD_STDERR_HANDLE); + return getErrno(); + } + + context->hChildStderr = NULL; + + if(!Buffer_empty(context->inputBuffer)){ + if(!WriteFile(context->hInputWrite,context->inputBuffer->data,context->inputBuffer->size,&nBytes,NULL)){ + setErrno(E_CANNOT_WRITE_ON_CHILD_STDIN); + return getErrno(); + } + } + + context->hThread = CreateThread(&sa,0,TestSuite_asyncReadChildOutput,(LPVOID)context,0,&ThreadId); + Sleep(0); + + if(NULL == context->hThread){ + setErrno(E_CANNOT_CREATE_READ_CHILD_OUTPUT_THREAD); + return getErrno(); + } + + + dwWaitResult = WaitForSingleObject(context->pi.hProcess,context->timeoutValue); + + if(WAIT_FAILED == dwWaitResult) + { + TerminateProcess(context->pi.hProcess,0); + context->pi.hProcess = NULL; + context->runThread = false; + + if(WAIT_FAILED == WaitForSingleObject(context->hThread,INFINITE)){ + setErrno(E_WAIT_THREAD_FAILED); + return getErrno(); + } + + if(!CloseHandle(context->hThread)){ + setErrno(E_CANNOT_CLOSE_THREAD_HANDLE); + return getErrno(); + } + + context->hThread = NULL; + + if(!CloseHandle(context->hOutputRead)){ + setErrno(E_CANNOT_CLOSE_READ_HANDLE); + return getErrno(); + } + + context->hOutputRead = NULL; + + if(!CloseHandle(context->hInputWrite)){ + setErrno(E_CANNOT_CLOSE_WRITE_HANDLE); + return getErrno(); + } + + context->hInputWrite = NULL; + setErrno(E_WAIT_FAILURE); + return getErrno(); + } + + if(WAIT_TIMEOUT == dwWaitResult) + { + TerminateProcess(context->pi.hProcess,0); + context->pi.hProcess = NULL; + context->runThread = false; + + if(WAIT_FAILED == WaitForSingleObject(context->hThread,INFINITE)){ + setErrno(E_WAIT_THREAD_FAILED); + return getErrno(); + } + + if(!CloseHandle(context->hThread)){ + setErrno(E_CANNOT_CLOSE_THREAD_HANDLE); + return getErrno(); + } + + context->hThread = NULL; + + if(!CloseHandle(context->hOutputRead)){ + setErrno(E_CANNOT_CLOSE_READ_HANDLE); + return getErrno(); + } + + + context->hOutputRead = NULL; + + if(!CloseHandle(context->hInputWrite)){ + setErrno(E_CANNOT_CLOSE_WRITE_HANDLE); + return getErrno(); + } + + context->hInputWrite = NULL; + setErrno(E_WAIT_TIMEOUT); + return getErrno(); + } + + /* all is ok . */ + + context->runThread = false; + + if(WAIT_FAILED == WaitForSingleObject(context->hThread,INFINITE)){ + setErrno(E_WAIT_THREAD_FAILED); + return getErrno(); + } + + if(!CloseHandle(context->hThread)){ + setErrno(E_CANNOT_CLOSE_THREAD_HANDLE); + return getErrno(); + } + + context->hThread = NULL; + + if(!CloseHandle(context->hOutputRead)){ + setErrno(E_CANNOT_CLOSE_READ_HANDLE); + return getErrno(); + } + + context->hOutputRead = NULL; + + if(!CloseHandle(context->hInputWrite)){ + setErrno(E_CANNOT_CLOSE_WRITE_HANDLE); + return getErrno(); + } + + context->hInputWrite = NULL; + + + /* Get the child exit code before close it. */ + GetExitCodeProcess(context->pi.hProcess,&dwExitCode); + + context->exitCode = (int)dwExitCode; + + if(!CloseHandle(context->pi.hProcess)){ + setErrno(E_CANNOT_CLOSE_PROCESS_HANDLE); + return getErrno(); + } + + context->runThread = true; + + if(TestSuite_iSPostOutputCheckingEnabled(context)){ + if (context->expectedOutputBuffer->size !=0 || context->outputBuffer->size !=0){ + Buffer_chomp(context->outputBuffer); + Buffer_chomp(context->expectedOutputBuffer); + + if (context->outputBuffer->size != context->expectedOutputBuffer->size || strcmp(context->outputBuffer->data, context->expectedOutputBuffer->data)){ + setErrno(E_OUTPUT_DONT_MATCH); + } + } + } + + if(TestSuite_iSExitCodeCheckingEnabled(context)){ + if(context->expectedExitCode != INVALID_EXIT_CODE){ + if(context->expectedExitCode != context->exitCode ){ + setErrno(E_EXIT_CODE_DONT_MATCH); + } + } + } + + context->pi.hProcess = NULL; + return getErrno(); +} + +bool TestSuite_iSPostOutputCheckingEnabled(TestCaseContext_t context) +{ + if(!context->isPostOutputCheckingEnabled && context->isOutputCheckingEnabled){ + return true; + } + + return false; +} + +bool TestSuite_iSExitCodeCheckingEnabled(TestCaseContext_t context) +{ + return context->exitCodeCheckingEnabled; +} + +errno_t TestSuite_changeDir(TestSuite_t testSuite) +{ + char* line = testSuite->stream->line + 5; + size_t size = strlen(line); + + while ((line[size-1] == '\n') || (line[size-1] == '\r')){ + line[size-1] = '\0'; + + if(size) + (size)--; + } + + if(!SetCurrentDirectory(line)){ + setErrno(E_CHANGE_DIRECTORY_FAILED); + return E_CHANGE_DIRECTORY_FAILED; + } + + Stream_printLine(testSuite->stream,change_directory_line_type); + + return E_SUCCESS; +} + -- 2.20.1