Esempio n. 1
0
function testAssessmentTests(array $files, $validate = false)
{
    $loaded = 0;
    $totalSpent = 0;
    foreach ($files as $f) {
        $start = microtime();
        $testDoc = new XmlDocument();
        $testDoc->load($f, $validate);
        $end = microtime();
        $spent = spentTime($start, $end);
        $totalSpent += $spent;
        output("Test '" . pathinfo($f, PATHINFO_BASENAME) . "' loaded in " . sprintf("%.8f", $spent) . " seconds.");
        $partCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('testPart'));
        $sectionCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('assessmentSection'));
        $itemCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('assessmentItemRef'));
        outputDescription("{$partCount} testPart(s), {$sectionCount} assessmentSection(s), {$itemCount} assessmentItemRef(s)");
        outputDescription("Memory usage is " . memory_get_usage() / pow(1024, 2) . " MB");
        output('');
        $loaded++;
    }
    outputAverage($totalSpent / $loaded);
}
function doGrading($usercode, $TC)
{
    $files = array();
    if ($TC['showonly'] !== FALSE) {
        $desired = explode(" ", $TC['showonly']);
        foreach ($TC as $name => $value) {
            if (substr($name, 0, 4) == "show") {
                $TC[$name] = in_array(substr($name, 4), $desired) ? "Y" : "N";
            }
        }
    }
    if ($TC['answer'] !== FALSE) {
        $TC['answer'] = ensureNewlineTerminated($TC['answer']);
    }
    $TC["inplace"] = booleanize($TC["inplace"]);
    extract($TC);
    // same as $showinput = $TC["showinput"], etc
    $mainFile = "";
    $er = FALSE;
    $mainFile .= "from _UTILITIES import *\n";
    $lang = 'en_US';
    if (array_key_exists("lang", $_REQUEST) && preg_match("~^[a-zA-Z_]*\$~", $_REQUEST["lang"])) {
        $lang = $_REQUEST["lang"];
    }
    $mainFile .= "_setLanguage('{$lang}')\n";
    $inputMaker = inputMaker($TC);
    $noInput = $inputMaker === FALSE;
    $mainFile .= ($inputMaker === FALSE ? "_stdin=''" : $inputMaker) . "\n_stdincopy = open('stdincopy', 'w', encoding='utf-8')\nprint(_stdin, file=_stdincopy, end='')\n_stdincopy.close()\n";
    if ($precode !== FALSE) {
        $mainFile .= softSafeDereference($precode) . "\n";
    }
    $files['stdincopy'] = NULL;
    $mainFile .= "import _GRADER\n";
    $mainFile .= "_G = _GRADER\n";
    global $inputInUse, $facultative;
    if (!$inputInUse && ($inplace || $solver !== FALSE)) {
        if ($solver !== FALSE) {
            $files['solver'] = $solver;
        }
        if ($solver !== FALSE) {
            $mainFile .= "_GRADER.globalsInitAndEcho(globals())\n";
        } else {
            $mainFile .= "_GRADER.globalsInitAndEcho(globals(), False)\n";
        }
        $files['graderreply'] = NULL;
        $files['graderpre'] = NULL;
        $files['solverstdout'] = NULL;
        // run the solver before usercode, lest they mess up our globals.
        $mainFile .= "_GRADER.runSolverWithTests()\n";
        $testcode = "";
        if ($rawtests !== FALSE) {
            $testcode .= $rawtests . "\n";
        }
        if ($autotests != FALSE) {
            $autotests = softSafeDereference($autotests);
            $python_ident_regex = '(\\p{L}|\\p{Nl}|_)(\\p{L}|\\p{N}|\\p{Mn}|\\p{Mc}|\\p{Pc})*';
            // note: this is close to, but not technically, 100% the
            // same as the formal Python definition of an identifier
            $py_regex_parens = 2;
            foreach (explode("\n", $autotests) as $autotestline) {
                if (preg_match('|^(\\s*)(\\S.*)$|', $autotestline, $matches) === 0) {
                    continue;
                }
                //skip blank lines
                $indentation = $matches[1];
                $command = trim($matches[2]);
                if (1 == preg_match('@^' . $python_ident_regex . '$@u', $command)) {
                    //varname
                    $testcode .= $indentation . "_G.checkVar('{$command}')\n";
                } elseif (1 == preg_match('@^(' . $python_ident_regex . ')\\s*\\((.*)\\)$@u', $command, $pieces)) {
                    if (strpos($pieces[2], $pieces[1]) === FALSE) {
                        // looks like a non-self-nested function call
                        $testcode .= $indentation . "_G.autotestCall('" . $pieces[1] . "',[" . $pieces[2 + $py_regex_parens] . "])\n";
                    } else {
                        // something more complex
                        $testcode .= $indentation . "_G.sayRunning(\"" . $command . "\")\n";
                        $testcode .= $indentation . "_G.autotestCompare(\"" . $command . "\", {$command})\n";
                    }
                } else {
                    $testcode .= $autotestline . "\n";
                }
                // just leave it alone
            }
        }
        $files['testcode'] = $testcode === FALSE ? "" : softSafeDereference($testcode) . "\n";
    }
    $mainFile .= '
_orig_std = (_sys.stdin, _sys.stdout)
_user_stdout = _StringIO()
_sys.stdout = _TeeOut(_user_stdout, _orig_std[1])
_sys.stdin = _StringIO(_stdin)
exec(compile(open(\'usercode\', encoding="utf-8").read(), ' . '\'usercode\', \'exec\'))
';
    if (!$inputInUse) {
        // lesson 18, part 2: may do this even if facultative
        if ($inplace) {
            $mainFile .= "exec(compile(open('testcode', encoding='utf-8').read()," . " 'testcode', 'exec'))\n";
            $mainFile .= "_G.say('Y', 'noend')\n";
            // success if none of the tests crash
        }
    }
    // we've got all the user stdout necessary for testing
    $mainFile .= '
__user_stdout = _user_stdout.getvalue()
_user_stdout.close()
(_sys.stdin, _sys.stdout) = _orig_std
';
    if (!$facultative && !$inputInUse) {
        if ($answer !== FALSE) {
            $mainFile .= "_G._solver_stdout = " . pythonEscape(softSafeDereference($answer)) . "\n";
        }
        if ($grader !== '*nograder*' && ($answer !== FALSE || $solver !== FALSE)) {
            $mainFile .= "_G.stdoutGrading(_stdin,__user_stdout,_G._solver_stdout, " . pythonEscape(softSafeDereference($grader)) . " )\n";
            $files['stdoutgraderreply'] = NULL;
        }
    }
    $testDescription = FALSE;
    $files["usercode"] = $usercode;
    global $usertni;
    if ($inputInUse && $usertni) {
        $mainFile .= "\n" . "exec(compile(open('usertests', encoding='utf-8').read(), " . "'usertests', 'exec'))\n";
        global $userinput;
        $files['usertests'] = $userinput;
    }
    $files["mainfile"] = $mainFile;
    $userResult = safepython($files, "mainfile", "", $cpulimit);
    extract($userResult);
    // start printing stuff out now.
    $m = '';
    if ($testDescription != FALSE) {
        $m .= $testDescription;
    }
    if (!$inputInUse && $inplace && trim($outdata['graderpre']) != '') {
        $m .= '<i>' . __t('Before running your code:') . '</i> ' . $outdata['graderpre'] . '<br/>';
    }
    if ($showinput == "Y" && !$inputInUse && !$noInput && ($hideemptyinput == "N" || $outdata['stdincopy'] != "")) {
        $m .= __t("Input:") . preBox($outdata['stdincopy']);
    }
    global $submit_code_stderr, $submit_code_errnice;
    $submit_code_stderr = $stderr;
    $submit_code_errnice = stderrNiceify($stderr);
    if (userIsAdmin() && $stderrlen > 0) {
        $m .= JQpopUp("Debug: view unsanitized", preBox($stderr, $stderrlen));
    }
    if ($stderr == '') {
        $errnice = '';
    } else {
        $errnice = '<p>' . __t('Error messages: ') . preBoxHinted(stderrNiceify($stderr), $stderrlen) . '</p>';
    }
    if ($ok) {
        $m .= "<p>" . __t('Program executed without crashing.') . "</p>";
    } elseif (firstLine($safeexecOut) == 'Command exited with non-zero status (1)') {
        $m .= "<p>" . __t("Program crashed.") . "</p>";
    } else {
        $m .= "<p>" . __t("Program crashed &mdash; ") . firstLine($safeexecOut) . ".</p>";
    }
    if (1 === 2) {
        // these lines are just to trick gettext
        __t("Memory Limit Exceeded") . __t("Time Limit Exceeded") . __t("Command exited with non-zero status") . __t("Command terminated by signal") . __t("Output Limit Exceeded") . __t("Invalid Function") . __t("Internal Error");
    }
    if ($showsafeexec == "Y") {
        $m .= "Sandbox messages:" . preBox($safeexecOut);
    }
    $simpleOutputDescription = outputDescription(NULL, array('showoutput' => $showoutput, 'stdoutlen' => $stdoutlen, 'hideemptyoutput' => $hideemptyoutput, 'stdout' => $stdout, 'ok' => $ok));
    if ($desirederror !== FALSE) {
        $m .= $simpleOutputDescription;
        $lines = explode("\n", trim($userResult["stderr"]));
        $goodFail = count($lines) > 0 && $lines[count($lines) - 1] == $desirederror;
        $m .= $errnice;
        return $goodFail ? tcpass($m) : tcfail($m);
    }
    if (!$ok || $facultative) {
        // we don't care what's in stdout
        $graderreply = trim(getSoft($outdata, 'graderreply', ''));
        if (!$ok && !$inputInUse && $graderreply != '') {
            $m .= "<i>" . __t("The grader said:") . "</i>" . "<div>" . $graderreply . "</div>";
        } elseif ($inplace && $solver === FALSE & $graderreply != '') {
            $m .= "<i>" . __t("Automatic tests:") . "</i>" . "<div>" . substr($graderreply, 0, -1) . "</div>";
        }
        $m .= $errnice . $simpleOutputDescription;
        return $ok ? tcpass($m) : tcfail($m);
    }
    if ($inplace) {
        // don't care what's in stdout, unless solverstdout != ''
        $GR = $outdata['graderreply'];
        $inplaceresult = substr($GR, -1);
        $inplacereply = substr($GR, 0, -1);
        if ($inplacereply != '') {
            $inplacereply = "<i>" . __t("The grader said:") . "</i>" . "<div>{$inplacereply}</div>";
        }
        if ($inplaceresult == 'Y') {
            if ($outdata['solverstdout'] == '') {
                return tcpass($m . $inplacereply . $errnice . $simpleOutputDescription);
            }
        } elseif ($inplaceresult == 'N') {
            return tcfail($m . $inplacereply . $errnice . $simpleOutputDescription);
        }
        $m .= $inplacereply;
        // carry on and let the stdout grader do its thing
    }
    // the user's code did not crash. what did the stdout grader say?
    $outGraderReply = $outdata['stdoutgraderreply'];
    if ($outGraderReply == "" || !($outGraderReply[0] == "Y" || $outGraderReply[0] == "N")) {
        throw new PyboxException("Grader error 2 [" . $outGraderReply . '|' . $outdata['graderreply'] . "|" . ord(substr($outdata['graderreply'], -1)) . "| {$m} ]");
    }
    $outinfo = array('stdout' => $stdout, 'stdoutlen' => $stdoutlen, 'requiredStdout' => getSoft($outdata, 'solverstdout', $answer), 'showoutput' => $showoutput, 'showexpected' => $showexpected, 'grader' => $grader);
    $m .= outputDescription($outGraderReply[0] == "Y", $outinfo) . $errnice;
    if (strlen(trim($outGraderReply)) > 1) {
        $m .= "<p>" . __t("Result of grading: ") . substr($outGraderReply, 1) . "</p>";
    }
    return $outGraderReply[0] == "Y" ? tcpass($m) : tcfail($m);
}