From 7dc59a6098a724cdd0fcd79bf1b000f472d469cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janis=20Da=CC=88hne?= <janis.daehne@informatik.uni-halle.de> Date: Wed, 24 May 2023 11:18:38 +0200 Subject: [PATCH] - added support for adt structural induction checker (test) --- api.php | 40 +++- constants.php | 3 +- ...nalCheckerStructuralInductionTest_func.php | 212 ++++++++++++++++++ helpers.php | 2 +- readme.md | 5 + 5 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 do_externalCheckerStructuralInductionTest_func.php diff --git a/api.php b/api.php index 4be47da..86a0558 100644 --- a/api.php +++ b/api.php @@ -446,7 +446,8 @@ foreach ($testCases as $test) { $hasTriedToCompile = TRUE; } - } else if ($arg_command === $s_command_regexTest) { + } + else if ($arg_command === $s_command_regexTest) { require_once './do_regexTest_func.php'; @@ -479,7 +480,8 @@ foreach ($testCases as $test) { addToDebugTimings("after regex test"); - } else if ($arg_command === $s_command_blackBoxTest || $arg_command === $s_command_justRunTest || $arg_command === $s_command_compareFilesTest || $arg_command === $s_command_externalCheckerTest) { + } + else if ($arg_command === $s_command_blackBoxTest || $arg_command === $s_command_justRunTest || $arg_command === $s_command_compareFilesTest || $arg_command === $s_command_externalCheckerTest || $arg_command === $s_command_externalCheckerStructuralInductionTest) { if ($isDebug) { $time_pre = microtime(true); @@ -571,7 +573,12 @@ foreach ($testCases as $test) { $arg_DiskSpaceLimit = $test[$s_test_diskSpaceLimitInKb]; - if ($compileCmd === NULL || $execCmd === NULL) { + addToDebugTimings("needCompilation2 " . $compileCmd); + addToDebugTimings("execCmd " . $execCmd); + + # return error if we have no cmd to execute + # there is a special case when compile command is null and we are running a structural induction test (this does not need compile command) + if (($compileCmd === NULL && $arg_command !== $s_command_externalCheckerStructuralInductionTest) || $execCmd === NULL) { $result = create_test_result($status_code_DbIssue, "could not find compile command or execute command (unknown plang?)", $testId, FALSE, FALSE, NULL, NULL, NULL, NULL, @@ -622,6 +629,11 @@ foreach ($testCases as $test) { $needCompilation = !$hasTriedToCompile; + # this test does not compile, only run/check + if ($arg_command === $s_command_externalCheckerStructuralInductionTest) { + $needCompilation = FALSE; + } + if ($needCompilation === FALSE && $compileResult[$s_hasCompiled] === FALSE) { //if the compile fail ... all other tests would give use main file not found... @@ -684,6 +696,28 @@ foreach ($testCases as $test) { addToDebugTimings("after external checker test"); + } + else if ($arg_command === $s_command_externalCheckerStructuralInductionTest) { + require_once './do_externalCheckerStructuralInductionTest_func.php'; + + addToDebugTimings("before external checker structural induction test"); + + try { + $_result = do_externalCheckerStructualInductionTest($conn, $traceString, $allUserSourceCode, $arg_mainFileNameWithExtension, $test, $fullWorkingDirPath, + $min_timeout, $min_compileTimeout, $min_memoryLimit, $min_diskSpaceLimit, + $compileCmd, $execCmd, $sourceFileExtensions, False, + $requestDistinctionString, + $showTestRunnerDebugOutput, + $compilerOptions, + $arg_characterLimitProtocol, + $arg_maxPoints + ); + } catch (Exception $e) { + //critical error, abort execution (all other tests are likely to fail) + output($status_code_InternalServerError, "error executing external checker structural induction test: " . $e->getMessage()); + goto handleCleanup; + } + } else { require_once './do_blackBoxTest_func.php'; diff --git a/constants.php b/constants.php index fc15a83..75db990 100644 --- a/constants.php +++ b/constants.php @@ -101,10 +101,11 @@ $s_arg_maxNumberOfTestsWithOneRequest = 'maxNumberOfTestsWithOneRequest'; $s_command_compile = 'compileTest'; $s_command_blackBoxTest = 'blackBoxTest'; $s_command_externalCheckerTest = 'externalCheckerTest'; +$s_command_externalCheckerStructuralInductionTest= 'externalCheckerStructuralInductionTest'; $s_command_compareFilesTest = 'compareFilesTest'; $s_command_regexTest = 'regexTest'; $s_command_justRunTest = 'justRunTest'; -$s_supportedCommands = [$s_command_compile, $s_command_blackBoxTest, $s_command_externalCheckerTest, $s_command_compareFilesTest, $s_command_regexTest, $s_command_justRunTest]; +$s_supportedCommands = [$s_command_compile, $s_command_blackBoxTest, $s_command_externalCheckerTest, $s_command_externalCheckerStructuralInductionTest, $s_command_compareFilesTest, $s_command_regexTest, $s_command_justRunTest]; # db table columns diff --git a/do_externalCheckerStructuralInductionTest_func.php b/do_externalCheckerStructuralInductionTest_func.php new file mode 100644 index 0000000..daa9548 --- /dev/null +++ b/do_externalCheckerStructuralInductionTest_func.php @@ -0,0 +1,212 @@ +<?php + +require_once __DIR__ . '/protocolHeaderPartParser.php'; + +/** + * runs the given black box test against the given files + * @param $conn PDO + * @param $traceString string + * @param $sourceCode string the user program source code as one string + * @param string $mainFileNameWithExtension the main file to compile (with extension) + * @param $test + * @param $fullWorkingDirPath + * @param int|string $timeout the timeout in MS (can be string from db or int from default values) + * @param int|string $compileTimeout the compile timeout in MS (can be string from db or int from default values) + * @param int|string $memoryLimit the memory limit in kb (can be string from db or int from default values) + * @param int|string $diskSpaceLimit the max disk space the program can write to (can be string from db or int from default values) + * @param string $compileCmd the command to execute to compile the file(s) + * @param string $execCmd the command to execute to run the test + * @param $sourceFileExtensions array the list with the file extensions from the source files for the p language + * @param $needCompilation bool true: should compile sources, false: not (this assumes the sources are already compiled) + * @param $justRun true: just run test, false: normal back box test + * @param $uniqueSessionId string the unique session id for the test runner to handle sandbox file system management + * @param $showTestRunnerDebugOutput 1: show internal debug output from the test runner (included in the test response) + * @param $compilerOptions string the options for the compiler + * @param $arg_characterLimitProtocol + * @param $maxPoints int + * @return array the result + * @internal param array $allFiles all files to copy in teh dir to use + * Format: array[int] => {fileName: string, fileContent: string} + */ +function do_externalCheckerStructualInductionTest($conn, $traceString, $sourceCode, $mainFileNameWithExtension, $test, $fullWorkingDirPath, + $timeout, $compileTimeout, $memoryLimit, $diskSpaceLimit, + $compileCmd, $execCmd, $sourceFileExtensions, $needCompilation, + $uniqueSessionId, + $showTestRunnerDebugOutput, + $compilerOptions, + $arg_characterLimitProtocol, + $arg_maxPoints + + +) +{ + + global $isDebug; + global $config; + global $s_command_externalCheckerStructuralInductionTest; + global $s_test_content; + global $s_test_allAssets; + global $s_return_val; + global $s_output; + global $s_user_program_exit_code; + global $s_runnerVersion; + global $s_timeForCompiling; + global $s_timeForUserProgram; + global $s_timeoutInMsUsed; + global $s_compileTimeoutInMsUsed; + global $s_output_without_header_part; + global $s_characterLimitExceeded; + global $s_points; + + + $command_to_execute_string = $s_command_externalCheckerStructuralInductionTest; + + + # compile is done in the runner + $commandToExecute = $execCmd; + $output = []; + $return_var = -1; + + $testContent = $test[$s_test_content]; + $allAssets = $test[$s_test_allAssets]; + + addToDebugTimings("before creating test assets"); + + # create the assets for the current test + if (createTestAssets($allAssets, $fullWorkingDirPath) === FALSE) { + //error message is in createFiles function + return array( + $s_return_val => 100, + $s_output => 'could not create asset(s)', + $s_user_program_exit_code => NULL, + $s_runnerVersion => NULL, + $s_timeForCompiling => NULL, + $s_timeForUserProgram => NULL, + ); + } + addToDebugTimings("after creating test assets"); + + + $longCmd = $config['runner'] + . ' ' . escapeshellarg($command_to_execute_string) # arg[0] the test method + . ' ' . escapeshellarg($fullWorkingDirPath) # arg[1] dir path + . ' ' . escapeshellarg($mainFileNameWithExtension) # arg[2] file path + . ' ' . escapeshellarg($compileCmd) # arg[3] compile command + + . ' ' . escapeshellarg($commandToExecute) # arg[4] command to execute the test + . ' ' . escapeshellarg($timeout) # arg[5] the timeout in ms + . ' ' . escapeshellarg($memoryLimit) # arg[6] the memory limit + . ' ' . escapeshellarg($diskSpaceLimit) # arg[7] the disk limit + . ' ' . escapeshellarg(implode(',', $sourceFileExtensions)) # arg[8] the source file extensions + + + . ' ' . escapeshellarg($arg_characterLimitProtocol) # arg[9] character limit for the test protocol, output cut after the limit + . ' ' . escapeshellarg($compilerOptions) # arg[10] options for the compiler + . ' ' . escapeshellarg($arg_maxPoints) # arg[11] (int) the max points we expect (but checker can give mor or less than that) + + . ' ' . escapeshellarg(($showTestRunnerDebugOutput === TRUE ? 1 : 0)) # arg[12] showTestRunnerDebugOutput + . ' ' . escapeshellarg($uniqueSessionId) # arg[13] a unique session id + + ; + + + + addToDebugTimings("longCmd: " . $longCmd); + + require_once './transactionHelper.php'; + addTraceLogToDb($conn, $config, $traceString, $longCmd, $sourceCode); + + if ($isDebug) { + debug("using runner: " . $config['runner']); + debug("full command: " . $longCmd); + } + + # the test content is passed in as stdin (because might be too long for a console argument + # e.g. windows its 32bit ca. 2.000 kb... + + $pipesDescriptor = array( + 0 => array('pipe', 'r'), # stdin is a pipe that the child will read from + 1 => array('pipe', 'w'), # stdout is a pipe that the child will write to + 2 => array("pipe", "w") # stderr + ); + + + if ($isDebug) { + $time_pre = microtime(true); + } + + + $env = $config['environmentVarsParsed']; + + addToDebugTimings("starting runner"); + + # without bypass_shell it won't work on windows + $process = proc_open($longCmd, $pipesDescriptor, $pipes, $fullWorkingDirPath, $env, array('bypass_shell' => TRUE)); + +// $state = proc_get_status($process); +// warn('open pid: ' . $state['pid']); + + if (is_resource($process)) { + fwrite($pipes[0], $testContent); + fclose($pipes[0]); + + + $output = stream_get_contents($pipes[1]); + fclose($pipes[1]); + + $errorOutput = stream_get_contents($pipes[2]); + fclose($pipes[2]); + + $return_var = proc_close($process); + } + + # we cannot use exec because this does no let us write to the stdin of the process + #exec($longCmd, $output, $return_var); + + if ($isDebug) { + $time_post = microtime(true); + $exec_time = ($time_post - $time_pre) * 1000; //in ms + debug("time to run the black-box-tests: " . $exec_time); + } + + if ($isDebug && (isset($errorOutput) && trim($errorOutput) !== '')) { + debug("error during execution of the (blackbox) test runner: " . $errorOutput); + } + + addToDebugTimings("finished runner"); + + + # the test assets are removed when the dir gets removed + # also the test runner could renamed/added/deleted some files + + $headerPart = parseHeaderPart($output); + + //$output + + #file_put_contents($fullWorkingDirPath . 'xyz.txt', $output); + + # in case the test runner didn't even run we may need to cast (vals from db) + if (gettype($timeout) === 'string') { + $timeout = intval($timeout, 10); + } + + if (gettype($compileTimeout) === 'string') { + $compileTimeout = intval($compileTimeout, 10); + } + + addToDebugTimings("finished parsing runner header"); + addToDebugTimings("headerPart " . json_encode($headerPart)); + + return array( + $s_return_val => $return_var, + $s_output => $headerPart[$s_output_without_header_part], + $s_user_program_exit_code => $headerPart[$s_user_program_exit_code], + $s_runnerVersion => $headerPart[$s_runnerVersion], + $s_timeForCompiling => $headerPart[$s_timeForCompiling], + $s_timeForUserProgram => $headerPart[$s_timeForUserProgram], + $s_timeoutInMsUsed => $timeout, + $s_compileTimeoutInMsUsed => $compileTimeout, + $s_characterLimitExceeded => $headerPart[$s_characterLimitExceeded], + $s_points => $headerPart[$s_points] + ); +} diff --git a/helpers.php b/helpers.php index 3d4968f..d16d1cd 100644 --- a/helpers.php +++ b/helpers.php @@ -15,7 +15,7 @@ function formatOutput($output) { $formattedOutput = $output; # backend requires output to be a string... so convert it if needed - if (count($formattedOutput) === 0) { + if (strlen($formattedOutput) === 0) { $formattedOutput = ''; } else if (is_array($output)) { $formattedOutput = join($responseNewLineString, $formattedOutput); diff --git a/readme.md b/readme.md index e5955e7..5c18c9a 100644 --- a/readme.md +++ b/readme.md @@ -85,6 +85,11 @@ The commands will probably require the users files (code). This files can be acc - default is true, is specified in the test content as `evaluate:true/false` - only used for external checkers +* **49** - contains the compiler options (if any or empty string) + - can be used for external checkers + - compiler options are added automatically after all commands + - if this is used the compiler options are not appended (only inserted once) + * **#50** - contains the timeout in ms * **#51** - contains the memory limit in kb * **#52** - contains the disk space limit in kb (the program can write to) -- GitLab