#if (isset($setup_function)) { # if ($setup_function() == false) # return; #} if ($action == '') { do_frameset(); } elseif ($action == 'view_control_frame') { do_controls(); } elseif ($action == 'start') { do_start(); } elseif ($action == 'pause') { do_pause(); } elseif ($action == 'resume') { do_resume(); } elseif ($action == 'run') { do_run(); } elseif ($action == 'test') { echo "blah"; } elseif ($action == 'none') { echo ""; } return; ############################################################## function do_frameset() { ?> <html> <head><title>Mail Blast</title></head> <frameset cols="160,*"> <frame name="controls" src="<?php echo $_SERVER['PHP_SELF'];
// Compile if ($task['compile'] === "true") { $compile_result = do_compile($filelist, $exe_file, $compiler, $task['compiler_options'], $instance); if ($compile_result['status'] == COMPILE_SUCCESS) { update_status("STA002", "Compile succeeded"); } else { update_status("STA003", "Compile failed"); done(); } } else { $exe_file = $task['exe_file']; $debug_exe_file = $task['debug_exe_file']; } // Run if ($task['run'] === "true") { $run_result = do_run($filelist, $exe_file, $task['running_params'], $compiler, $task['compiler_options'], $instance); // Debug if ($run_result['status'] == EXECUTION_CRASH && $task['debug'] === "true" && $debugger) { update_status("STA004", "Program crashed"); // Recompile with debug compiler_options $compile_result_debug = do_compile($filelist, $debug_exe_file, $compiler, $task['compiler_options_debug'], $instance); // If compiler failed with compiler_options_debug but succeeded with compiler_options, // most likely options are bad... so we'll skip debugging if ($compile_result_debug['status'] === COMPILE_SUCCESS) { $debug_result = do_debug($debug_exe_file, $debugger, $run_result['core'], $filelist, $instance); update_status("STA006", "Program crashed - debugging finished"); unlink($run_result['core']); } } else { update_status("STA005", "Program executed successfully"); }
function do_test($filelist, $global_symbols, $test, $compiler, $debugger, $profiler, $task, $instance) { global $conf_max_program_output, $conf_verbosity; $test_result = array(); $test_result['status'] = TEST_SUCCESS; $test_result['run_result'] = array(); $test_result['debug_result'] = array(); $test_result['profile_result'] = array(); if ($conf_verbosity > 0) { print "Testing...\n"; } // Replacement strings $start_string = "====START_TEST_" . $test['id'] . "===="; $end_string = "====END_TEST_" . $test['id'] . "===="; $except_string = "====EXCEPTION_TEST_" . $test['id'] . "===="; // Find symbols that are required by this test $includes = array(); if (!empty($global_symbols)) { foreach ($test['require_symbols'] as $symbol) { $found = false; foreach ($global_symbols as $global => $file) { if ($symbol === $global) { array_push($includes, $file); $found = true; break; } } if (!$found) { $test_result['status'] = TEST_SYMBOL_NOT_FOUND; $test_result['status_object'] = $symbol; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - symbol not found ({$symbol})\n"; } return $test_result; } } } // TODO symbol renaming // Construct a new source file with embedded test $main_source_code = $main_filename = ""; if ($task['language'] == "C" || $task['language'] == "C++") { if (!array_key_exists("main", $global_symbols)) { // This is a bug in get_global_symbols, as a program without main wouldn't compile! $test_result['status'] = TEST_SYMBOL_NOT_FOUND; $test_result['status_object'] = "main"; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - main not found\n"; } return $test_result; } $main_filename = $global_symbols['main']; $main_source_code = file_get_contents($main_filename); // Rename main $newname = "_main"; while (array_key_exists($newname, $global_symbols)) { $newname = "_{$newname}"; } $newname = " {$newname}\${1}"; $main_source_code = preg_replace("/\\smain(\\W)/", $newname, $main_source_code); } // Include files containing symbols we need $includes_code = ""; foreach ($includes as $file) { if ($task['language'] == "C" || $task['language'] == "C++") { $includes_code .= "#include \"{$file}\"\n"; } if ($task['language'] == "Python") { $import_file = basename(preg_replace("/\\.py\$/", "", $file)); // Imported filenames must start with a letter in python if (!ctype_alpha($import_file[0])) { $import_file = "a" . $import_file; $new_file = dirname($file) . "/a" . basename($file); rename($file, $new_file); } $includes_code .= "from {$import_file} import *"; } } // Also include stdin/stdout libraries cause they will surely be used in test if ($task['language'] == "C") { $includes_code .= "#include <stdio.h>\n"; } if ($task['language'] == "C++") { $includes_code .= "#include <iostream>\nusing std::cin;\nusing std::cout;\nusing std::cerr;\nusing std::endl;\n"; } // Construct test code $test_code = ""; if ($task['language'] == "C") { $test_code .= "int main() {\nprintf(\"{$start_string}\");\n " . $test['code'] . "\n printf(\"{$end_string}\");\nreturn 0;\n}\n"; } else { if ($task['language'] == "C++") { $test_code .= "int main() {\ntry {\n std::cout<<\"{$start_string}\";\n " . $test['code'] . "\n std::cout<<\"{$end_string}\";\n } catch (...) {\n std::cout<<\"{$except_string}\";\n }\nreturn 0;\n}\n"; } else { if ($task['language'] == "Python") { $test_code .= "print(\"{$start_string}\")\n" . $test['code'] . "\nprint(\"{$end_string}\")\n"; } else { $test_code = $test['code']; } } } // Prevent cheating $main_source_code = str_replace($start_string, "====cheat_protection====", $main_source_code); $main_source_code = str_replace($end_string, "====cheat_protection====", $main_source_code); $main_source_code = str_replace($except_string, "====cheat_protection====", $main_source_code); // Construct whole file $main_length = substr_count($main_source_code, "\n"); $main_source_code = $includes_code . $test['global_top'] . "\n" . $main_source_code . "\n" . $test['global_above_main'] . "\n" . $test_code . "\n"; // Choose filename for test $test_filename = "bs_test_" . $test['id']; if ($task['language'] == "C") { $test_filename .= ".c"; } if ($task['language'] == "C++") { $test_filename .= ".cpp"; } if ($task['language'] == "Python") { $test_filename .= ".py"; } $test_path = instance_path($instance); if ($task['language'] == "C" || $task['language'] == "C++") { // Locate test file in the same path that mainfile used to be $test_path = dirname($main_filename); } while (in_array($test_path . "/" . $test_filename, $filelist)) { $test_filename = "_" . $test_filename; } $test_filename = $test_path . "/" . $test_filename; file_put_contents($test_filename, $main_source_code); // Add test file to filelist if ($task['language'] == "C" || $task['language'] == "C++") { for ($i = 0; $i < count($filelist); $i++) { if ($filelist[$i] === $global_symbols['main']) { $filelist[$i] = $test_filename; } } } else { if ($task['language'] == "Python") { // In python we execute just the test file and it includes everything else $filelist = array($test_filename); } else { array_push($filelist, $test_filename); } } // Calculate positions of original code and test code inside sources // that will be used to adjust output of compile/debug/profile to something user expects $adjustment_data = array(); $adjustment_data['orig_filename'] = $main_filename; $adjustment_data['new_filename'] = $test_filename; $adjustment_data['global_top_pos'] = substr_count($includes_code, "\n"); $adjustment_data['original_source_pos'] = $adjustment_data['global_top_pos'] + substr_count($test['global_top'], "\n") + 1; $adjustment_data['global_above_pos'] = $adjustment_data['original_source_pos'] + $main_length + 1; $adjustment_data['test_code_pos'] = $adjustment_data['global_above_pos'] + substr_count($test['global_above_main'], "\n") + 1; // number of lines added to main per language if ($task['language'] == "C") { $adjustment_data['test_code_pos'] += 2; } if ($task['language'] == "C++") { $adjustment_data['test_code_pos'] += 3; } if ($task['language'] == "Python") { $adjustment_data['test_code_pos'] += 1; } // === TESTING COMMENCE === // Compile test $test_exe_file = instance_path($instance) . "/bs_test_" . $test['id']; $compile_result = do_compile($filelist, $test_exe_file, $compiler, $task['compiler_options_debug'], $instance); $test_result['compile_result'] = $compile_result; if ($compile_result['status'] !== COMPILE_SUCCESS) { $test_result['status'] = TEST_COMPILE_FAILED; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - compile error\n"; } return $test_result; } // Execute test $run_result = do_run($filelist, $test_exe_file, $test['running_params'], $compiler, $task['compiler_options_debug'], $instance); $test_result['run_result'] = $run_result; $program_output = $run_result['output']; // Shortcut // Output was too long and it was cut off... let's pretend it finished ok if (strlen($program_output) >= $conf_max_program_output) { $program_output .= "\n{$end_string}\n"; } // Find marker strings in program output $start_pos = strpos($program_output, $start_string); if ($start_pos !== false) { $start_pos += strlen($start_string); } $end_pos = strpos($program_output, $end_string); $except_pos = strpos($program_output, $except_string); // Remove marker strings from output if ($end_pos !== false) { $program_output = substr($program_output, $start_pos, $end_pos - $start_pos); } else { if ($except_pos !== false) { $program_output = substr($program_output, $start_pos, $except_pos - $start_pos); } else { if ($start_pos !== false) { $program_output = substr($program_output, $start_pos); } } } $test_result['run_result']['output'] = $program_output; if ($run_result['status'] === EXECUTION_TIMEOUT) { $test_result['status'] = TEST_EXECUTION_TIMEOUT; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - execution timeout\n"; } // Profiler will simply execute even longer, so we don't go there return $test_result; } if ($run_result['status'] === EXECUTION_CRASH) { $test_result['status'] = TEST_EXECUTION_CRASH; // Use seldom: crashes are unreliable! if ($test['expected_crash'] === "true") { $test_result['status'] = TEST_SUCCESS; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " ok (crash)\n"; } } else { if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - crash\n"; } } // Debug in case of crash if ($debugger && $task['debug'] === "true") { $debug_result = do_debug($test_exe_file, $debugger, $run_result['core'], $filelist, $instance); // Adjust filenames and line numbers that were changed for the test foreach ($debug_result['parsed_output'] as &$msg) { if (instance_path($instance) . "/" . $msg['file'] === $adjustment_data['new_filename']) { test_adjust_lines($msg['file'], $msg['line'], $adjustment_data, $instance); } } $test_result['debug_result'] = $debug_result; } unlink($run_result['core']); // If crash is unexpected, we will go on to profiler cause it can give some more information if ($test['expected_crash'] === "true") { return $test_result; } } else { // === FINDING EXPECTED OUTPUT IN PROGRAM OUTPUT === // Remove invisible spaces in expected program output // Allow to specify newlines in expected output using \n foreach ($test['expected'] as &$ex) { $ex = str_replace("\r\n", "\n", $ex); $ex = str_replace("\\n", "\n", $ex); $ex = trim(preg_replace("/\\s+\n/", "\n", $ex)); } // Program finished normally if ($start_pos !== false && $end_pos !== false) { if ($test['expected_exception'] === "true") { $test_result['status'] = TEST_WRONG_OUTPUT; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - expected exception\n"; } return $test_result; } $test_result['run_result']['output'] = $program_output; // Don't fail test because of invisible spaces and empty lines $program_output = preg_replace("/\\s+\n/", "\n", $program_output); $program_output = trim(preg_replace("/\n+/", "\n", $program_output)); // Ignore whitespace if ($test['ignore_whitespace'] === "true") { $program_output = str_replace("\n", "", $program_output); $program_output = preg_replace("/\\s+/", "", $program_output); foreach ($test['expected'] as &$ex) { $ex = preg_replace("/\\s+/", "", $ex); } } // Look for expected outputs in program output $test_ok = false; $exnr = 1; foreach ($test['expected'] as &$ex) { // Why do we need a reference here??? if ($program_output == $ex) { if ($conf_verbosity > 0) { print "- test " . $test['id'] . " ok (exact match {$exnr})\n"; } $test_ok = true; break; } else { if ($test['substring'] === "true" && strstr($program_output, $ex)) { if ($conf_verbosity > 0) { print "- test " . $test['id'] . " ok (substring {$exnr})\n"; } $test_ok = true; break; } else { if ($test['regex'] === "true" && preg_match("/{$ex}/", $program_output)) { if ($conf_verbosity > 0) { print "- test " . $test['id'] . " ok (regex {$exnr})\n"; } $test_ok = true; break; } } } $exnr++; } if (!$test_ok) { $test_result['status'] = TEST_WRONG_OUTPUT; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed\n"; } if ($conf_verbosity > 2) { print "Output:\n{$program_output}\nExpected:\n" . $test['expected'][0]; } // We will continue to profiler as it may give us some explanation of result } } else { if ($start_pos !== false && $except_pos !== false) { // TODO check type of exception if ($test['expected_exception'] === "false") { $test_result['status'] = TEST_UNEXPECTED_EXCEPTION; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - exception not expected\n"; } // We won't continue to profiler because unexpected exception usually causes memleaks // which won't exist after the reason for this exception is removed. // Otherwise programmer might be confused that memleak caused the exception return $test_result; } if ($conf_verbosity > 0) { print "- test " . $test['id'] . " ok (exception)\n"; } } else { $test_result['status'] = TEST_OUTPUT_NOT_FOUND; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - output not found ({$start_pos}, {$end_pos}, {$except_pos})\n"; } } } } // if ($run_result['status'] === EXECUTION_CRASH) { ... } else { // Profile if ($profiler && $task['profile'] === "true") { $profile_result = do_profile($test_exe_file, $profiler, $filelist, $test['running_params'], $instance); // Adjust filenames and line numbers that were changed for the test foreach ($profile_result['parsed_output'] as &$msg) { // Valgrind always returns just the base file name if ($msg['file'] === basename($adjustment_data['new_filename'])) { test_adjust_lines($msg['file'], $msg['line'], $adjustment_data, $instance); } if (array_key_exists('file_alloced', $msg) && instance_path($instance) . "/" . $msg['file_alloced'] === $adjustment_data['new_filename']) { test_adjust_lines($msg['file_alloced'], $msg['line_alloced'], $adjustment_data, $instance); } } // If there are no errors, we will disregard profiler output if ($profile_result['status'] !== PROFILER_OK) { $test_result['profile_result'] = $profile_result; if ($test_result['status'] === TEST_SUCCESS) { $test_result['status'] = TEST_PROFILER_ERROR; if ($conf_verbosity > 0) { print "- test " . $test['id'] . " failed - profiler error\n"; } } } } return $test_result; }