rewrite test running in php and cleanup test output
I started cleaning up the perl script and then realized that it wasn't buying us much at all. So I changed the ##test/run## script to just do the executing of the ##hhvm## directly and printing the output.
While I was in there, I also made the ##verify_to_json.php## redundant by allowing different output formats, one of which fbmake can consume.
A good run looks like:
$ test/run test/slow/array
Running 68 tests in 20 threads
....................................................................
All tests passed. Ship it!
and a bad run looks like:
$ test/run test/zend/good/ext-bz2
Running 5 tests in 5 threads
....F
1 tests failed:
test/zend/good/ext-bz2/002.php
To run these by hand:
hphp/hhvm/hhvm --config test/zend/config.hdf -v Repo.Local.Mode=-- -v Repo.Central.Path=/data/users/ptarjan/other-git/fbcode/hphp/test/../../_bin/verify.hhbc -v Eval.Jit=true -v Eval.JitEnableRenameFunction=true -v Eval.EnableArgsInBacktraces=true --file test/zend/good/ext-bz2/002.php
I chose to not support the ##1..1## syntax since only 5 tests were using it.
Esse commit está contido em:
@@ -0,0 +1,7 @@
|
||||
1..6
|
||||
ok 1
|
||||
ok 2
|
||||
ok 3
|
||||
ok 4
|
||||
ok 5
|
||||
ok 6
|
||||
@@ -0,0 +1,3 @@
|
||||
1..2
|
||||
ok 1
|
||||
ok 2
|
||||
@@ -0,0 +1,3 @@
|
||||
1..2
|
||||
ok 1
|
||||
ok 2
|
||||
@@ -0,0 +1,5 @@
|
||||
1..4
|
||||
ok 1
|
||||
ok 2
|
||||
ok 3
|
||||
ok 4
|
||||
@@ -0,0 +1,2 @@
|
||||
1..1
|
||||
ok 1
|
||||
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,7 @@
|
||||
1..6
|
||||
ok 1
|
||||
ok 2
|
||||
ok 3
|
||||
ok 4
|
||||
ok 5
|
||||
ok 6
|
||||
@@ -0,0 +1,3 @@
|
||||
1..2
|
||||
ok 1
|
||||
ok 2
|
||||
@@ -0,0 +1,3 @@
|
||||
1..2
|
||||
ok 1
|
||||
ok 2
|
||||
+372
-121
@@ -49,12 +49,12 @@ EOT;
|
||||
}
|
||||
|
||||
function error($message) {
|
||||
echo "$message\n";
|
||||
print "$message\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function hphp_home() {
|
||||
return __DIR__.'/../..';
|
||||
return realpath(__DIR__.'/../..');
|
||||
}
|
||||
|
||||
function idx($array, $key, $default = null) {
|
||||
@@ -66,7 +66,7 @@ function idx_file($array, $key, $default = null) {
|
||||
if (!is_file($file)) {
|
||||
error("$file doesn't exist");
|
||||
}
|
||||
return $file;
|
||||
return rel_path($file);
|
||||
}
|
||||
|
||||
function bin_root() {
|
||||
@@ -80,14 +80,47 @@ function bin_root() {
|
||||
function verify_hhbc() {
|
||||
return idx($_ENV, 'VERIFY_HHBC', bin_root().'/verify.hhbc');
|
||||
}
|
||||
|
||||
function read_file($file) {
|
||||
return file_exists($file) ? preg_replace('/\s+/', ' ', (file_get_contents($file))) : "";
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/2637945/
|
||||
function rel_path($to) {
|
||||
$from = explode('/', getcwd().'/');
|
||||
$to = explode('/', $to);
|
||||
$relPath = $to;
|
||||
|
||||
foreach($from as $depth => $dir) {
|
||||
// find first non-matching dir
|
||||
if($dir === $to[$depth]) {
|
||||
// ignore this directory
|
||||
array_shift($relPath);
|
||||
} else {
|
||||
// get number of remaining dirs to $from
|
||||
$remaining = count($from) - $depth;
|
||||
if($remaining > 1) {
|
||||
// add traversals up to first matching dir
|
||||
$padLength = (count($relPath) + $remaining - 1) * -1;
|
||||
$relPath = array_pad($relPath, $padLength, '..');
|
||||
break;
|
||||
} else {
|
||||
$relPath[0] = './' . $relPath[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode('/', $relPath);
|
||||
}
|
||||
|
||||
function get_options($argv) {
|
||||
$parameters = array(
|
||||
'r' => 'repo',
|
||||
'm:' => 'mode:',
|
||||
'' => 'server',
|
||||
'h' => 'help',
|
||||
'' => 'commands',
|
||||
'repo' => 'r',
|
||||
'mode:' => 'm:',
|
||||
'server' => '',
|
||||
'help' => 'h',
|
||||
'verbose' => 'v',
|
||||
'fbmake' => '',
|
||||
'threads:' => '',
|
||||
);
|
||||
$options = array();
|
||||
$files = array();
|
||||
@@ -95,7 +128,7 @@ function get_options($argv) {
|
||||
$arg = $argv[$i];
|
||||
$found = false;
|
||||
if ($arg && $arg[0] == '-') {
|
||||
foreach ($parameters as $short => $long) {
|
||||
foreach ($parameters as $long => $short) {
|
||||
if ($arg == '-'.str_replace(':', '', $short) ||
|
||||
$arg == '--'.str_replace(':', '', $long)) {
|
||||
if (substr($long, -1, 1) == ':') {
|
||||
@@ -130,12 +163,11 @@ function map_convenience_filename($file) {
|
||||
);
|
||||
|
||||
$m = null;
|
||||
if (!preg_match('/([^\/]*)(.*)/', $file, $m) ||
|
||||
if (!preg_match('/([^\/]*)/', $file, $m) ||
|
||||
!isset($mappage[$m[1]])) {
|
||||
error("Not valid file or directory: '$file'");
|
||||
return $file;
|
||||
}
|
||||
if (!isset($m[2])) $m[2] = '';
|
||||
return hphp_home().'/'.$mappage[$m[1]].$m[2];
|
||||
return hphp_home().'/'.$mappage[$m[1]];
|
||||
}
|
||||
|
||||
function find_tests($files) {
|
||||
@@ -143,8 +175,9 @@ function find_tests($files) {
|
||||
$files = array(hphp_home().'/hphp/test/quick');
|
||||
}
|
||||
foreach ($files as &$file) {
|
||||
if (!stat($file)) {
|
||||
$file = map_convenience_filename($file);
|
||||
$file = map_convenience_filename($file);
|
||||
if (!@stat($file)) {
|
||||
error("Not valid file or directory: '$file'");
|
||||
}
|
||||
$file = preg_replace(',//+,', '/', realpath($file));
|
||||
$file = preg_replace(',^'.getcwd().'/,', '', $file);
|
||||
@@ -158,21 +191,8 @@ function find_tests($files) {
|
||||
return array_filter($tests);
|
||||
}
|
||||
|
||||
function find_config($tests, $name) {
|
||||
$dirs = array_map('dirname', $tests);
|
||||
$configs = array_map(function($test) use ($name) {
|
||||
return find_config_for_dir($test, $name);
|
||||
}, $tests);
|
||||
$configs = array_unique($configs);
|
||||
if (count($configs) != 1) {
|
||||
error(
|
||||
"These tests would use many different configs and we only support ".
|
||||
"using one for all the tests. Need these configs: ".
|
||||
implode(' ', $configs).""
|
||||
);
|
||||
}
|
||||
$ret = array_values($configs);
|
||||
return $ret[0];
|
||||
function find_config($test, $name) {
|
||||
return find_config_for_dir(dirname($test), $name);
|
||||
}
|
||||
|
||||
function find_config_for_dir($dir, $name) {
|
||||
@@ -183,24 +203,12 @@ function find_config_for_dir($dir, $name) {
|
||||
}
|
||||
$dir = substr($dir, 0, strrpos($dir, '/'));
|
||||
}
|
||||
return $name;
|
||||
return __DIR__.'/'.$name;
|
||||
}
|
||||
|
||||
function is_server($options) {
|
||||
return idx($options, 'server');
|
||||
}
|
||||
|
||||
function file_arg($options) {
|
||||
if (is_server($options)) {
|
||||
return '-m server -vServer.SourceRoot=%s -p %s';
|
||||
} else {
|
||||
return '--file %3\$s';
|
||||
}
|
||||
}
|
||||
|
||||
function mode_arg($options) {
|
||||
$repo_args = "-vRepo.Local.Mode=-- -vRepo.Central.Path=".verify_hhbc();
|
||||
$jit_args = "$repo_args -vEval.Jit=1 -vEval.JitEnableRenameFunction=1";
|
||||
function mode_cmd($options) {
|
||||
$repo_args = "-v Repo.Local.Mode=-- -v Repo.Central.Path=".verify_hhbc();
|
||||
$jit_args = "$repo_args -v Eval.Jit=true -v Eval.JitEnableRenameFunction=true";
|
||||
$mode = idx($options, 'mode');
|
||||
switch ($mode) {
|
||||
case '':
|
||||
@@ -213,95 +221,338 @@ function mode_arg($options) {
|
||||
}
|
||||
}
|
||||
|
||||
function command_arg($options, $tests) {
|
||||
return array(
|
||||
function hhvm_cmd($options, $test) {
|
||||
return implode(" ", array(
|
||||
idx_file($_ENV, 'HHVM', bin_root().'/hphp/hhvm/hhvm'),
|
||||
'--config',
|
||||
find_config($tests, 'config.hdf'),
|
||||
file_arg($options),
|
||||
mode_arg($options),
|
||||
'-vEval.EnableArgsInBacktraces=true',
|
||||
);
|
||||
find_config($test, 'config.hdf'),
|
||||
read_file("$test.opts"),
|
||||
mode_cmd($options),
|
||||
'-v Eval.EnableArgsInBacktraces=true',
|
||||
'--file',
|
||||
$test
|
||||
));
|
||||
}
|
||||
|
||||
function hphp_arg($options, $tests) {
|
||||
return array(
|
||||
function hphp_cmd($options, $test) {
|
||||
return implode(" ", array(
|
||||
idx_file($_ENV, 'HPHP', bin_root().'/hphp/hhvm/hphp'),
|
||||
'--config',
|
||||
find_config($tests, 'hphp_config.hdf'),
|
||||
);
|
||||
find_config($test, 'hphp_config.hdf'),
|
||||
read_file("$test.hphp_opts"),
|
||||
"-thhbc -l0 -k1 -o $test.repo $test",
|
||||
));
|
||||
}
|
||||
|
||||
function verify_args($options) {
|
||||
$args = array();
|
||||
if (is_server($options)) {
|
||||
$args[] = '--server --port 8080 --home .';
|
||||
class Status {
|
||||
private static $results = array();
|
||||
private static $mode = 0;
|
||||
|
||||
const MODE_NORMAL = 0;
|
||||
const MODE_VERBOSE = 1;
|
||||
const MODE_FBMAKE = 2;
|
||||
|
||||
public static function setMode($mode) {
|
||||
self::$mode = $mode;
|
||||
}
|
||||
$args[] = "--hhvm";
|
||||
$args[] = idx_file($_ENV, 'HHVM',bin_root().'/hphp/hhvm/hhvm');
|
||||
|
||||
public static function pass($test) {
|
||||
array_push(self::$results, array('name' => $test, 'status' => 'passed'));
|
||||
switch (self::$mode) {
|
||||
case self::MODE_NORMAL:
|
||||
if (self::hasColor()) {
|
||||
print "\033[1;32m.\033[0m";
|
||||
} else {
|
||||
print '.';
|
||||
}
|
||||
break;
|
||||
case self::MODE_VERBOSE:
|
||||
if (self::hasColor()) {
|
||||
print "$test \033[1;32mpassed\033[0m\n";
|
||||
} else {
|
||||
print "$test passed";
|
||||
}
|
||||
break;
|
||||
case self::MODE_FBMAKE:
|
||||
self::sayFBMake($test, 'passed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function fail($test) {
|
||||
array_push(self::$results, array('name' => $test, 'status' => 'failed'));
|
||||
switch (self::$mode) {
|
||||
case self::MODE_NORMAL:
|
||||
if (self::hasColor()) {
|
||||
print "\033[0;31mF\033[0m";
|
||||
} else {
|
||||
print 'F';
|
||||
}
|
||||
break;
|
||||
case self::MODE_VERBOSE:
|
||||
if (self::hasColor()) {
|
||||
print "$test \033[0;31mFAILED\033[0m\n";
|
||||
} else {
|
||||
print "FAILED";
|
||||
}
|
||||
break;
|
||||
case self::MODE_FBMAKE:
|
||||
self::sayFBMake($test, 'failed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static function sayFBMake($test, $status) {
|
||||
$start = array('op' => 'start', 'test' => $test);
|
||||
$end = array('op' => 'test_done', 'test' => $test, 'status' => $status);
|
||||
if ($status == 'failed') {
|
||||
$end['details'] = file_get_contents("$test.diff");
|
||||
}
|
||||
self::say($start, $end);
|
||||
}
|
||||
|
||||
public static function getResults() {
|
||||
return self::$results;
|
||||
}
|
||||
|
||||
/** Output is in the format expected by JsonTestRunner. */
|
||||
public static function say(/* ... */) {
|
||||
$data = array_map(function($row) {
|
||||
return json_encode($row, JSON_UNESCAPED_SLASHES) . "\n";
|
||||
}, func_get_args());
|
||||
fwrite(STDERR, implode("", $data));
|
||||
}
|
||||
|
||||
private static function hasColor() {
|
||||
return posix_isatty(STDOUT);
|
||||
}
|
||||
}
|
||||
|
||||
function run($options, $tests, $bad_test_file) {
|
||||
if (isset($options['verbose'])) {
|
||||
Status::setMode(Status::MODE_VERBOSE);
|
||||
}
|
||||
if (isset($options['fbmake'])) {
|
||||
Status::setMode(Status::MODE_FBMAKE);
|
||||
}
|
||||
foreach ($tests as $test) {
|
||||
$status = run_test($options, $test);
|
||||
if ($status) {
|
||||
Status::pass($test);
|
||||
} else {
|
||||
Status::fail($test);
|
||||
}
|
||||
}
|
||||
file_put_contents($bad_test_file, json_encode(Status::getResults()));
|
||||
foreach (Status::getResults() as $result) {
|
||||
if ($result['status'] == 'failed') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function run_test($options, $test) {
|
||||
$hhvm = hhvm_cmd($options, $test);
|
||||
if (isset($options['repo'])) {
|
||||
$args[] = '--repo 1';
|
||||
} else {
|
||||
$args[] = '--repo 0';
|
||||
if (strpos($test, '.hhas') !== false || is_file($test.'.norepo')) {
|
||||
# We don't have a way to skip, I guess run non-repo?
|
||||
} else {
|
||||
unlink("$test.repo/hhvm.hhbc");
|
||||
$hphp = hphp_cmd($options, $test);
|
||||
shell_exec($hphp);
|
||||
$opts .= " -v Repo.Authoritative=true -v Repo.Central.Path=$test.repo/hhvm.hhbc";
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
function run($cmd, $tests) {
|
||||
$descriptorspec = array(
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w"),
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w"),
|
||||
);
|
||||
$process = proc_open($cmd, $descriptorspec, $pipes);
|
||||
if (!is_resource($process)) {
|
||||
error("Can't run $cmd");
|
||||
}
|
||||
fwrite($pipes[0], implode(' ', $tests));
|
||||
fclose($pipes[0]);
|
||||
while(!feof($pipes[1])) {
|
||||
echo fgets($pipes[1], 1024);
|
||||
}
|
||||
fclose($pipes[1]);
|
||||
return proc_close($process);
|
||||
}
|
||||
|
||||
list($options, $files) = get_options($argv);
|
||||
if (isset($options['help'])) {
|
||||
error(help());
|
||||
}
|
||||
$tests = find_tests($files);
|
||||
|
||||
$verify = array(
|
||||
hphp_home().'/hphp/test/verify',
|
||||
'--command="'.implode(' ', command_arg($options, $tests)).'"',
|
||||
implode(' ', verify_args($options)),
|
||||
);
|
||||
if (isset($options['repo'])) {
|
||||
$verify[] = '--hphp="'.implode(' ', hphp_arg($options, $tests)).'"';
|
||||
}
|
||||
|
||||
$return_value = run(implode(' ', $verify), $tests);
|
||||
|
||||
if ($return_value || isset($options['commands'])) {
|
||||
$command = implode(' ', command_arg($options, $tests));
|
||||
$filename = 'FILENAME';
|
||||
$extra_help = ' change FILENAME to the test you want';
|
||||
if (count($tests) == 1) {
|
||||
$filename = current($tests);
|
||||
$extra_help = '';
|
||||
}
|
||||
$command = str_replace('%3\\$s', $filename, $command);
|
||||
|
||||
if (isset($options['repo'])) {
|
||||
$command .= " -vRepo.Authoritative=true ";
|
||||
$command = str_replace(verify_hhbc(), "$filename.repo/hhvm.hhbc", $command);
|
||||
$command = implode(' ', hphp_arg($options, $tests)).
|
||||
" -thhbc -l0 -k1 -o $filename.repo $filename\n".
|
||||
$command;
|
||||
$process = proc_open("$hhvm 2>&1", $descriptorspec, $pipes);
|
||||
if (is_resource($process)) {
|
||||
fclose($pipes[0]);
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
proc_close($process);
|
||||
}
|
||||
|
||||
print "\nTo run these by hand$extra_help:\n".
|
||||
"$command\n";
|
||||
file_put_contents("$test.out", $output);
|
||||
|
||||
if (file_exists("$test.expect")) {
|
||||
$diff_cmds = "--text -u";
|
||||
exec("diff --text -u $test.expect $test.out > $test.diff 2>&1", $_, $status);
|
||||
// unix 0 == success
|
||||
return !$status;
|
||||
|
||||
} else if (file_exists("$test.expectf")) {
|
||||
$wanted_re = file_get_contents("$test.expectf");
|
||||
|
||||
// do preg_quote, but miss out any %r delimited sections
|
||||
$temp = "";
|
||||
$r = "%r";
|
||||
$startOffset = 0;
|
||||
$length = strlen($wanted_re);
|
||||
while($startOffset < $length) {
|
||||
$start = strpos($wanted_re, $r, $startOffset);
|
||||
if ($start !== false) {
|
||||
// we have found a start tag
|
||||
$end = strpos($wanted_re, $r, $start+2);
|
||||
if ($end === false) {
|
||||
// unbalanced tag, ignore it.
|
||||
$end = $start = $length;
|
||||
}
|
||||
} else {
|
||||
// no more %r sections
|
||||
$start = $end = $length;
|
||||
}
|
||||
// quote a non re portion of the string
|
||||
$temp = $temp . preg_quote(substr($wanted_re, $startOffset, ($start - $startOffset)), '/');
|
||||
// add the re unquoted.
|
||||
if ($end > $start) {
|
||||
$temp = $temp . '(' . substr($wanted_re, $start+2, ($end - $start-2)). ')';
|
||||
}
|
||||
$startOffset = $end + 2;
|
||||
}
|
||||
$wanted_re = $temp;
|
||||
|
||||
$wanted_re = str_replace(
|
||||
array('%binary_string_optional%'),
|
||||
'string',
|
||||
$wanted_re
|
||||
);
|
||||
$wanted_re = str_replace(
|
||||
array('%unicode_string_optional%'),
|
||||
'string',
|
||||
$wanted_re
|
||||
);
|
||||
$wanted_re = str_replace(
|
||||
array('%unicode\|string%', '%string\|unicode%'),
|
||||
'string',
|
||||
$wanted_re
|
||||
);
|
||||
$wanted_re = str_replace(
|
||||
array('%u\|b%', '%b\|u%'),
|
||||
'',
|
||||
$wanted_re
|
||||
);
|
||||
// Stick to basics
|
||||
$wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re);
|
||||
$wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re);
|
||||
$wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re);
|
||||
$wanted_re = str_replace('%a', '.+', $wanted_re);
|
||||
$wanted_re = str_replace('%A', '.*', $wanted_re);
|
||||
$wanted_re = str_replace('%w', '\s*', $wanted_re);
|
||||
$wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re);
|
||||
$wanted_re = str_replace('%d', '\d+', $wanted_re);
|
||||
$wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re);
|
||||
$wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re);
|
||||
$wanted_re = str_replace('%c', '.', $wanted_re);
|
||||
// %f allows two points "-.0.0" but that is the best *simple* expression
|
||||
|
||||
# a poor mans aide for debugging
|
||||
shell_exec("diff --text -u $test.expectf $test.out > $test.diff 2>&1");
|
||||
|
||||
return preg_match("/^$wanted_re\$/s", $output);
|
||||
|
||||
} else if (file_exists("$test.expectregex")) {
|
||||
$wanted_re = file_get_contents("$test.expectregex");
|
||||
|
||||
# a poor mans aide for debugging
|
||||
shell_exec("diff --text -u $test.expectf $test.out > $test.diff 2>&1");
|
||||
|
||||
return preg_match("/^$wanted_re\$/s", $output);
|
||||
}
|
||||
}
|
||||
exit($return_value);
|
||||
|
||||
function main($argv) {
|
||||
|
||||
ini_set('pcre.backtrack_limit', PHP_INT_MAX);
|
||||
ini_set('pcre.recursion_limit', PHP_INT_MAX);
|
||||
|
||||
list($options, $files) = get_options($argv);
|
||||
if (isset($options['help'])) {
|
||||
error(help());
|
||||
}
|
||||
$tests = find_tests($files);
|
||||
|
||||
$threads = min(count($tests), idx($options, 'threads', 20));
|
||||
|
||||
print "Running ".count($tests)." tests in $threads threads\n";
|
||||
|
||||
# Try to construct the buckets so the test results are ready in approximately
|
||||
# alphabetical order
|
||||
$test_buckets = array();
|
||||
$i = 0;
|
||||
foreach ($tests as $test) {
|
||||
$test_buckets[$i][] = $test;
|
||||
$i = ($i + 1) % $threads;
|
||||
}
|
||||
|
||||
# Spawn off worker threads
|
||||
$children = array();
|
||||
# A poor man's shared memory
|
||||
$bad_test_files = array();
|
||||
for ($i = 0; $i < $threads; $i++) {
|
||||
$bad_test_file = tempnam('/tmp', 'test-run-');
|
||||
$bad_test_files[] = $bad_test_file;
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
error('could not fork');
|
||||
} else if ($pid) {
|
||||
$children[] = $pid;
|
||||
} else {
|
||||
exit(run($options, $test_buckets[$i], $bad_test_file));
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for the kids
|
||||
$return_value = 0;
|
||||
foreach ($children as $child) {
|
||||
pcntl_waitpid($child, $status);
|
||||
$return_value |= pcntl_wexitstatus($status);
|
||||
}
|
||||
|
||||
if (!$return_value) {
|
||||
print "\nAll tests passed. Ship it!\n";
|
||||
} else {
|
||||
$results = array();
|
||||
foreach ($bad_test_files as $bad_test_file) {
|
||||
$results = array_merge($results,
|
||||
json_decode(file_get_contents($bad_test_file), true));
|
||||
}
|
||||
if (isset($options['fbmake'])) {
|
||||
Status::say(array('op' => 'all_done', 'results' => $results));
|
||||
} else {
|
||||
$failed = array();
|
||||
foreach ($results as $result) {
|
||||
if ($result['status'] == 'failed') {
|
||||
$failed[] = $result['name'];
|
||||
}
|
||||
}
|
||||
asort($failed);
|
||||
print "\n\n".count($failed)." tests failed:\n".implode("\n", $failed)."\n";
|
||||
|
||||
print "\nSee the diffs:\n".implode("\n", array_map(
|
||||
function($test) { return 'cat '.$test.'.diff'; },
|
||||
$failed))."\n";
|
||||
|
||||
print "\nTo run these by hand:\n";
|
||||
|
||||
foreach ($failed as $test) {
|
||||
$command = hhvm_cmd($options, $test);
|
||||
if (isset($options['repo'])) {
|
||||
$command .= " -v Repo.Authoritative=true ";
|
||||
$command = str_replace(verify_hhbc(), "$test.repo/hhvm.hhbc", $command);
|
||||
$command = hphp_cmd($options, $test)."\n".$command."\n";
|
||||
}
|
||||
print "$command\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
exit(main($argv));
|
||||
|
||||
@@ -1,784 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
################################################################################
|
||||
#
|
||||
# Test harness.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
use threads;
|
||||
use threads::shared;
|
||||
use Thread::Semaphore;
|
||||
|
||||
# Shut off buffering.
|
||||
select(STDOUT);
|
||||
$| = 1;
|
||||
|
||||
#
|
||||
# Parse command-line arguments.
|
||||
#
|
||||
use Getopt::Long;
|
||||
Getopt::Long::config("bundling"); # Allow -hv rather than forcing -h -v.
|
||||
|
||||
# Set option defaults for optional arguments.
|
||||
$opt_help = 0;
|
||||
$opt_verbose = 0;
|
||||
$opt_quiet = 0;
|
||||
$opt_srcdir = ".";
|
||||
$opt_objdir = ".";
|
||||
$opt_command = '%1$s/%3$s';
|
||||
$opt_ustats = 0;
|
||||
$opt_zero = 0;
|
||||
$opt_server = 0;
|
||||
$opt_port = 8080;
|
||||
$opt_home = ".";
|
||||
$opt_threads = `cat /proc/cpuinfo | grep '^processor' | wc -l`;
|
||||
$opt_reduce = "";
|
||||
$opt_coverage = "";
|
||||
$opt_hhvm = "hhvm";
|
||||
$opt_hphp = "hphp";
|
||||
$opt_repo = 0;
|
||||
$opt_cpu_time_limit = 0;
|
||||
$opt_real_time_limit = 0;
|
||||
$opt_no_exp = 0;
|
||||
|
||||
$opt_retval =
|
||||
&GetOptions("h|help" => \$opt_help,
|
||||
"v|verbose" => \$opt_verbose,
|
||||
"q|quiet" => \$opt_quiet,
|
||||
"s|srcdir=s" => \$opt_srcdir,
|
||||
"o|objdir=s" => \$opt_objdir,
|
||||
"u|ustats" => \$opt_ustats,
|
||||
"c|command=s" => \$opt_command,
|
||||
"z|zero" => \$opt_zero,
|
||||
"server" => \$opt_server,
|
||||
"port:i" => \$opt_port,
|
||||
"home:s" => \$opt_home,
|
||||
"threads=i" => \$opt_threads,
|
||||
"r|reduce=s" => \$opt_reduce,
|
||||
"coverage=s" => \$opt_coverage,
|
||||
"no-exp" => \$opt_no_exp,
|
||||
"hhvm=s" => \$opt_hhvm,
|
||||
"hphp=s" => \$opt_hphp,
|
||||
"repo=i" => \$opt_repo,
|
||||
"cpu-time-limit=i" => \$opt_cpu_time_limit,
|
||||
"real-time-limit=i" => \$opt_real_time_limit,
|
||||
);
|
||||
|
||||
# Munge directory paths if necessary.
|
||||
if (defined($opt_srcdir) && $opt_srcdir eq "")
|
||||
{
|
||||
$opt_srcdir = ".";
|
||||
}
|
||||
if (defined($opt_objdir) && $opt_objdir eq "")
|
||||
{
|
||||
$opt_objdir = ".";
|
||||
}
|
||||
|
||||
if ($opt_help)
|
||||
{
|
||||
&usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($opt_retval == 0)
|
||||
{
|
||||
&usage();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if ($opt_verbose && $opt_quiet)
|
||||
{
|
||||
print STDERR "-v and -q are incompatible\n";
|
||||
&usage();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if ($#ARGV + 1 == 0)
|
||||
{
|
||||
while (<STDIN>) {
|
||||
push @ARGV, split /\s+/;
|
||||
}
|
||||
@ARGV = grep !/^$/, @ARGV;
|
||||
if (scalar @ARGV == 0) {
|
||||
print STDERR "no files passed in or on stdin\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($opt_verbose)
|
||||
{
|
||||
print STDERR "Option values: h:$opt_help, v:$opt_verbose, "
|
||||
. "s:\"$opt_srcdir\", o:\"$opt_objdir\" "
|
||||
. "q:$opt_quiet, u:$opt_ustats, z:$opt_zero\n";
|
||||
printf STDERR "Tests (%d total): @ARGV\n", $#ARGV + 1;
|
||||
}
|
||||
|
||||
if ($opt_server)
|
||||
{
|
||||
$command = sprintf ($opt_command, $opt_home, $opt_port);
|
||||
$server_pid = fork();
|
||||
if ($server_pid == 0)
|
||||
{
|
||||
`sudo $command 2>&1 > /dev/null`;
|
||||
}
|
||||
else
|
||||
{
|
||||
# wait for server to warm up
|
||||
`sleep 5`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$opt_threads) {
|
||||
$opt_threads = 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Create and print header.
|
||||
#
|
||||
@TSTATS =
|
||||
(
|
||||
"--------------------------------------------------------------------------\n",
|
||||
"Test c_user c_system c_total chng\n",
|
||||
" passed/FAILED h_user h_system h_total %% chng\n",
|
||||
"--------------------------------------------------------------------------\n",
|
||||
);
|
||||
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
foreach $line (@TSTATS)
|
||||
{
|
||||
printf STDOUT "$line";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Run sequence test(s).
|
||||
#
|
||||
$total_utime = 0.0; # Total user time.
|
||||
$total_stime = 0.0; # Total system time.
|
||||
$total_hutime = 0.0; # Total historical user time.
|
||||
$total_hstime = 0.0; # Total historical system time.
|
||||
$total_ntime = 0.0; # Total time for tests that have historical data.
|
||||
|
||||
@test_buckets = ();
|
||||
%tests_okay = ();
|
||||
%tests_num_failed_subtests = ();
|
||||
%tests_num_subtests = ();
|
||||
%tests_utime = ();
|
||||
%tests_stime = ();
|
||||
# Extra debugging info generated by reduce or coverage.
|
||||
# We store them in tests_extra_dbg instead of directly printing them out is
|
||||
# that the output might be interleaved when the tests are run concurrently.
|
||||
%tests_extra_dbg = ();
|
||||
share(%tests_okay);
|
||||
share(%tests_num_failed_subtests);
|
||||
share(%tests_num_subtests);
|
||||
share(%tests_utime);
|
||||
share(%tests_stime);
|
||||
share(%tests_extra_dbg);
|
||||
# Try to construct the buckets so the test results are ready in approximately
|
||||
# alphabetical order
|
||||
for ($i = 0; $i < $opt_threads; $i++)
|
||||
{
|
||||
push @test_buckets, [];
|
||||
}
|
||||
$i = 0;
|
||||
foreach $test (@ARGV)
|
||||
{
|
||||
push @{$test_buckets[$i]}, $test;
|
||||
$i = ($i + 1) % $opt_threads;
|
||||
}
|
||||
|
||||
# Spawn off worker threads
|
||||
for ($i = 0; $i < $opt_threads; $i++)
|
||||
{
|
||||
threads->create(\&run_tests, $test_buckets[$i]);
|
||||
}
|
||||
|
||||
foreach $test (@ARGV)
|
||||
{
|
||||
# Strip out any whitespace in $test.
|
||||
$test =~ s/^\s*(.*)\s*$/$1/;
|
||||
|
||||
$okay = 1;
|
||||
|
||||
my (@TSTATS);
|
||||
my ($t_str);
|
||||
|
||||
$t_str = sprintf ("%s%s", $test, ' ' x (40 - length($test)));
|
||||
@TSTATS = (@TSTATS, $t_str);
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
foreach $line (@TSTATS)
|
||||
{
|
||||
printf STDOUT "$line";
|
||||
}
|
||||
}
|
||||
|
||||
($okay, $num_failed_subtests, $num_subtests, $utime, $stime) = wait_for_test($test);
|
||||
# num_failed_subtests and num_subtests will be zero in the diff mode.
|
||||
($hutime, $hstime) = &print_stats($test, $okay, $num_failed_subtests, $num_subtests, $utime, $stime);
|
||||
{
|
||||
lock(%tests_okay);
|
||||
print $tests_extra_dbg{$test};
|
||||
}
|
||||
|
||||
# Print coverage on success, or run reduce on failure.
|
||||
|
||||
$total_hutime += $hutime;
|
||||
$total_hstime += $hstime;
|
||||
|
||||
if ($okay)
|
||||
{
|
||||
$total_utime += $utime;
|
||||
$total_stime += $stime;
|
||||
}
|
||||
else
|
||||
{
|
||||
@FAILED_TESTS = (@FAILED_TESTS, $test);
|
||||
}
|
||||
|
||||
# If there were historical data, add the run time to the total time to
|
||||
# compare against the historical run time.
|
||||
if (0 < ($hutime + $hstime))
|
||||
{
|
||||
$total_ntime += $utime + $stime;
|
||||
}
|
||||
}
|
||||
|
||||
# Print summary stats.
|
||||
$tt_str = sprintf ("%d / %d passed (%5.2f%%%%)",
|
||||
($#ARGV + 1) - ($#FAILED_TESTS + 1),
|
||||
$#ARGV + 1,
|
||||
(($#ARGV + 1) - ($#FAILED_TESTS + 1))
|
||||
/ ($#ARGV + 1) * 100);
|
||||
|
||||
$t_str = sprintf ("Totals %7.2f %7.2f %7.2f"
|
||||
. " %7.2f\n"
|
||||
. " %s %7.2f %7.2f %7.2f %7.2f%%%%\n",
|
||||
$total_utime, $total_stime, $total_utime + $total_stime,
|
||||
($total_ntime - ($total_hutime + $total_hstime)),
|
||||
$tt_str . ' ' x (40 - length($tt_str)),
|
||||
$total_hutime, $total_hstime, $total_hutime + $total_hstime,
|
||||
($total_hutime + $total_hstime == 0.0) ? 0.0 :
|
||||
(($total_ntime
|
||||
- ($total_hutime + $total_hstime))
|
||||
/ ($total_hutime + $total_hstime) * 100));
|
||||
|
||||
$thread_warning = "";
|
||||
if ($opt_threads > 1) {
|
||||
$thread_warning = "WARNING: Multiple threads were used. CPU times "
|
||||
. "are wildly inaccurate\n"
|
||||
}
|
||||
|
||||
@TSTATS = ("--------------------------------------------------------------------------\n",
|
||||
$thread_warning,
|
||||
$t_str,
|
||||
"--------------------------------------------------------------------------\n"
|
||||
);
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
foreach $line (@TSTATS)
|
||||
{
|
||||
printf STDOUT "$line";
|
||||
}
|
||||
if (@FAILED_TESTS) {
|
||||
printf STDOUT "failed tests:\n";
|
||||
}
|
||||
foreach $fail (@FAILED_TESTS)
|
||||
{
|
||||
printf STDOUT "%50s\n", $fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($opt_server)
|
||||
{
|
||||
kill 9, $server_pid;
|
||||
`ps aux | grep hphpi | grep -v perl | cut -c10-15 | sudo xargs kill -9 \\
|
||||
2>&1 >/dev/null`
|
||||
}
|
||||
|
||||
if ($#FAILED_TESTS >= 0)
|
||||
{
|
||||
# One or more tests failed, so return an error.
|
||||
exit 1;
|
||||
}
|
||||
# End of main execution.
|
||||
|
||||
sub run_tests
|
||||
{
|
||||
foreach my $test (@{$_[0]})
|
||||
{
|
||||
my ($okay, $num_failed_subtests, $num_subtests, $utime, $stime, $extra_dbg) = run_test($test);
|
||||
{
|
||||
lock(%tests_okay);
|
||||
$tests_okay{$test} = $okay;
|
||||
$tests_num_failed_subtests{$test} = $num_failed_subtests;
|
||||
$tests_num_subtests{$test} = $num_subtests;
|
||||
$tests_utime{$test} = $utime;
|
||||
$tests_stime{$test} = $stime;
|
||||
$tests_extra_dbg{$test} = $extra_dbg;
|
||||
cond_signal(%tests_okay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub wait_for_test
|
||||
{
|
||||
my ($test) = @_;
|
||||
while (1)
|
||||
{
|
||||
lock(%tests_okay);
|
||||
if (exists $tests_okay{$test}) {
|
||||
return ($tests_okay{$test},
|
||||
$tests_num_failed_subtests{$test},
|
||||
$tests_num_subtests{$test},
|
||||
$tests_utime{$test},
|
||||
$tests_stime{$test},
|
||||
$tests_extra_dbg{$test});
|
||||
}
|
||||
cond_wait(%tests_okay);
|
||||
}
|
||||
}
|
||||
|
||||
sub run_test
|
||||
{
|
||||
my ($test) = @_;
|
||||
my ($okay) = 1;
|
||||
my ($num_failed_subtests) = 0;
|
||||
my ($num_subtests) = 0;
|
||||
my ($tutime, $tstime);
|
||||
my ($extra_dbg) = "";
|
||||
my ($utime, $stime, $cutime, $cstime);
|
||||
my ($opts) = "";
|
||||
|
||||
if ($opt_server)
|
||||
{
|
||||
$command = "GET http://localhost:" . $opt_port . "/" . $test;
|
||||
}
|
||||
else
|
||||
{
|
||||
$command = sprintf ($opt_command, $opt_srcdir, $opt_objdir, $test);
|
||||
if ($opt_cpu_time_limit)
|
||||
{
|
||||
$command = "(ulimit -t " . $opt_cpu_time_limit . "; "
|
||||
. "timeout " . $opt_real_time_limit . " "
|
||||
. $command . ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (!$opt_no_exp && -e "$opt_objdir/$test.opts") {
|
||||
open (FILE, "<$opt_objdir/$test.opts");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
chomp(@lines);
|
||||
$opts = join(' ', @lines);
|
||||
}
|
||||
|
||||
|
||||
if ($opt_repo) {
|
||||
if ($test =~ m/\.hhas/ || -e $test . ".norepo") {
|
||||
# We don't have a way to skip, I guess run non-repo?
|
||||
} else {
|
||||
my ($hphp_opts) = "";
|
||||
if (-e "$opt_objdir/$test.hphp_opts") {
|
||||
open (FILE, "<$opt_objdir/$test.hphp_opts");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
chomp(@lines);
|
||||
$hphp_opts = join(' ', @lines);
|
||||
}
|
||||
unlink "$opt_objdir/$test.repo/hhvm.hhbc";
|
||||
`$opt_hphp -thhbc -l0 -k1 -o $opt_objdir/$test.repo $hphp_opts $opt_objdir/$test`;
|
||||
$opts .= " -v Repo.Authoritative=true -v Repo.Central.Path=$opt_objdir/$test.repo/hhvm.hhbc";
|
||||
}
|
||||
}
|
||||
|
||||
($utime, $stime, $cutime, $cstime) = times;
|
||||
`$command $opts > $opt_objdir/$test.out 2>&1`;
|
||||
($utime, $stime, $tutime, $tstime) = times;
|
||||
|
||||
# Subtract the before time from the after time.
|
||||
$tutime -= $cutime;
|
||||
$tstime -= $cstime;
|
||||
|
||||
if ($opt_zero)
|
||||
{
|
||||
if ($?)
|
||||
{
|
||||
$okay = 0;
|
||||
if ($opt_verbose)
|
||||
{
|
||||
print STDERR
|
||||
"\"$opt_objdir/$test > $opt_objdir/$test.out 2>&1\" returned $?\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Compare the results with the expected, and update "okay".
|
||||
if (! -e "$opt_objdir/$test.out") {
|
||||
$okay = 0;
|
||||
if ($opt_verbose)
|
||||
{
|
||||
print STDERR
|
||||
"Nonexistent output file \"$opt_objdir/$test.out\"\n";
|
||||
}
|
||||
}
|
||||
elsif (-e "$opt_srcdir/$test.exp")
|
||||
{
|
||||
$okay = 0;
|
||||
print STDERR "Rename your .exp file to .expect. See test/README.md";
|
||||
}
|
||||
elsif (!$opt_no_exp && -e "$opt_srcdir/$test.expect")
|
||||
{
|
||||
$diff_args = "--text -u";
|
||||
`diff $diff_args $opt_srcdir/$test.expect $opt_objdir/$test.out > $opt_objdir/$test.diff 2>&1`;
|
||||
if ($?)
|
||||
{
|
||||
# diff returns non-zero if there is a difference.
|
||||
$okay = 0;
|
||||
}
|
||||
}
|
||||
elsif (!$opt_no_exp && -e "$opt_srcdir/$test.expectf")
|
||||
{
|
||||
open (FILE, "<$opt_objdir/$test.expectf");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
$wanted_re = join('', @lines);
|
||||
|
||||
open (FILE, "<$opt_objdir/$test.out");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
$content = join('', @lines);
|
||||
|
||||
# from run-tests.php
|
||||
$temp = "";
|
||||
$r = "%r";
|
||||
$startOffset = 0;
|
||||
$length = length($wanted_re);
|
||||
while ($startOffset < $length)
|
||||
{
|
||||
$start = index($wanted_re, $r, $startOffset);
|
||||
if ($start != -1)
|
||||
{
|
||||
$end = index($wanted_re, $r, $start+2);
|
||||
if ($end == -1)
|
||||
{
|
||||
# unbalanced tag, ignore it.
|
||||
$end = $start = $length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$start = $end = $length;
|
||||
}
|
||||
|
||||
$temp = $temp . quotemeta(substr($wanted_re, $startOffset, $start - $startOffset));
|
||||
if ($end > $start)
|
||||
{
|
||||
$temp = $temp . '(' . substr($wanted_re, $start+2, $end - $start-2) . ')';
|
||||
}
|
||||
|
||||
$startOffset = $end + 2;
|
||||
}
|
||||
$wanted_re = $temp;
|
||||
|
||||
## perl escapes % but we want to use %s as our replacement
|
||||
$wanted_re =~ s/\\%/%/g;
|
||||
|
||||
$wanted_re =~ s/%binary_string_optional%/string/g;
|
||||
$wanted_re =~ s/%unicode_string_optional%/string/g;
|
||||
$wanted_re =~ s/%unicode\\\|string%/string/g;
|
||||
$wanted_re =~ s/%string\\\|unicode%/string/g;
|
||||
$wanted_re =~ s/%u\\\|b%//g;
|
||||
$wanted_re =~ s/%b\\\|u%//g;
|
||||
|
||||
# Stick to basics;
|
||||
$wanted_re =~ s/%e/\\\//g;
|
||||
$wanted_re =~ s/%s/[^\r\n]+/g;
|
||||
$wanted_re =~ s/%S/[^\r\n]*/g;
|
||||
$wanted_re =~ s/%a/.+/g;
|
||||
$wanted_re =~ s/%A/.*/g;
|
||||
$wanted_re =~ s/%w/\\s*/g;
|
||||
$wanted_re =~ s/%i/[+-]?\\d+/g;
|
||||
$wanted_re =~ s/%d/\\d+/g;
|
||||
$wanted_re =~ s/%x/[0-9a-fA-F]+/g;
|
||||
$wanted_re =~ s/%f/[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?/g;
|
||||
$wanted_re =~ s/%c/./g;
|
||||
|
||||
unless ($content =~ /^$wanted_re$/)
|
||||
{
|
||||
$okay = 0;
|
||||
# a poor mans aide for debugging
|
||||
$diff_args = "--text -u";
|
||||
`diff $diff_args $opt_srcdir/$test.expectf $opt_objdir/$test.out > $opt_objdir/$test.diff 2>&1`;
|
||||
}
|
||||
}
|
||||
elsif (!$opt_no_exp && -e "$opt_srcdir/$test.expectregex")
|
||||
{
|
||||
open (FILE, "<$opt_objdir/$test.expectregex");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
$regex = join('', @lines);
|
||||
|
||||
open (FILE, "<$opt_objdir/$test.out");
|
||||
@lines = <FILE>;
|
||||
close(FILE);
|
||||
$content = join('', @lines);
|
||||
|
||||
unless ($content =~ /$regex/)
|
||||
{
|
||||
$okay = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Sequence mode.
|
||||
if (open (STEST_OUT, "<$opt_objdir/$test.out"))
|
||||
{
|
||||
$num_subtests = 0;
|
||||
$num_failed_subtests = 0;
|
||||
|
||||
while (defined($line = <STEST_OUT>))
|
||||
{
|
||||
if ($line =~ /1\.\.(\d+)/)
|
||||
{
|
||||
$num_subtests = $1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($num_subtests == 0)
|
||||
{
|
||||
$okay = 0;
|
||||
if ($opt_verbose)
|
||||
{
|
||||
print STDERR "Malformed or missing 1..n line\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ($subtest = 1; $subtest <= $num_subtests; $subtest++)
|
||||
{
|
||||
while (defined($line = <STEST_OUT>))
|
||||
{
|
||||
if ($line =~ /^not\s+ok\s+(\d+)?/)
|
||||
{
|
||||
$not = 1;
|
||||
$test_num = $1;
|
||||
last;
|
||||
}
|
||||
elsif ($line =~ /^ok\s+(\d+)?/)
|
||||
{
|
||||
$not = 0;
|
||||
$test_num = $1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if (defined($line))
|
||||
{
|
||||
if (defined($test_num) && ($test_num != $subtest))
|
||||
{
|
||||
# There was no output printed for one or more tests.
|
||||
for (; $subtest < $test_num; $subtest++)
|
||||
{
|
||||
$num_failed_subtests++;
|
||||
}
|
||||
}
|
||||
if ($not)
|
||||
{
|
||||
$num_failed_subtests++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; $subtest <= $num_subtests; $subtest++)
|
||||
{
|
||||
$num_failed_subtests++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $num_failed_subtests)
|
||||
{
|
||||
$okay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
print STDERR "Cannot open output file \"$opt_objdir/$test.out\"\n";
|
||||
}
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($okay == 0)
|
||||
{
|
||||
# Run reduce to pinpoint the tracelet that causes the error.
|
||||
# For now, reduce has to be run sequentially, because it copies the input
|
||||
# file to the same temporary place.
|
||||
if ($opt_reduce ne "")
|
||||
{
|
||||
$cmd = $opt_reduce . " '" . $opt_hhvm . "' " . $test;
|
||||
if ($opt_cpu_time_limit)
|
||||
{
|
||||
$cmd = join(" ",
|
||||
$cmd,
|
||||
$opt_cpu_time_limit,
|
||||
$opt_real_time_limit);
|
||||
}
|
||||
$extra_dbg = `$cmd 2> /dev/null`;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Print the coverage.
|
||||
# Need run the command again in the tracing mode.
|
||||
if ($opt_coverage ne "")
|
||||
{
|
||||
$command = sprintf($opt_command, $opt_srcdir, $opt_objdir, $test);
|
||||
# Need calculate # of spills, reloads, and pushes/pops
|
||||
# around natives which are logged in level 3.
|
||||
$command = join("", "TRACE=tx64:3 ",
|
||||
"HPHP_TRACE_FILE=", $test, ".log ",
|
||||
$command);
|
||||
`$command 2> /dev/null`;
|
||||
$extra_dbg = `$opt_coverage < $test.log 2> /dev/null`;
|
||||
}
|
||||
}
|
||||
|
||||
return ($okay, $num_failed_subtests, $num_subtests, $tutime,
|
||||
$tstime, $extra_dbg);
|
||||
}
|
||||
|
||||
sub print_stats
|
||||
{
|
||||
my ($test, $okay, $failed_subtests, $subtests, $utime, $stime) = @_;
|
||||
my ($hutime, $hstime);
|
||||
# my (TEST_PERF);
|
||||
my (@TSTATS);
|
||||
my ($t_str, $pass_str);
|
||||
|
||||
$pass_str = $okay ? "" : "*** FAILED ***";
|
||||
if ((0 != $subtests) && (!$okay))
|
||||
{
|
||||
$pass_str = $pass_str . " ($failed_subtests/$subtests failed)";
|
||||
}
|
||||
if (length($pass_str) > 0)
|
||||
{
|
||||
$pass_str = $pass_str . ' ' x (39 - length($pass_str));
|
||||
}
|
||||
|
||||
if (-r "$test.perf")
|
||||
{
|
||||
if (!open (TEST_PERF, "<$opt_objdir/$test.perf"))
|
||||
{
|
||||
print STDERR "Unable to open \"$opt_objdir/$test.perf\"\n";
|
||||
exit 1;
|
||||
}
|
||||
$_ = <TEST_PERF>;
|
||||
|
||||
($hutime, $hstime) = split;
|
||||
close TEST_PERF;
|
||||
|
||||
$t_str = sprintf (" %7.2f %7.2f %7.2f %7.2f\n"
|
||||
. " %s %7.2f %7.2f %7.2f %7.2f%%%%\n",
|
||||
$utime, $stime, $utime + $stime,
|
||||
($utime + $stime) - ($hutime + $hstime),
|
||||
$pass_str,
|
||||
$hutime, $hstime, $hutime + $hstime,
|
||||
(($hutime + $hstime) == 0.0) ? 0.0 :
|
||||
((($utime + $stime) - ($hutime + $hstime))
|
||||
/ ($hutime + $hstime) * 100));
|
||||
}
|
||||
else
|
||||
{
|
||||
$hutime = 0.0;
|
||||
$hstime = 0.0;
|
||||
|
||||
$t_str = sprintf (" %7.2f %7.2f %7.2f \n"
|
||||
. "%s",
|
||||
$utime, $stime, $utime + $stime,
|
||||
length($pass_str) > 0 ? " $pass_str\n" : "");
|
||||
}
|
||||
@TSTATS = ($t_str);
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
foreach $line (@TSTATS)
|
||||
{
|
||||
printf STDOUT "$line";
|
||||
}
|
||||
}
|
||||
|
||||
if ($okay && $opt_ustats)
|
||||
{
|
||||
if (!open (TEST_PERF, ">$opt_objdir/$test.perf"))
|
||||
{
|
||||
if (!$opt_quiet)
|
||||
{
|
||||
print STDERR "Unable to update \"$opt_objdir/$test.perf\"\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print TEST_PERF "$utime $stime\n";
|
||||
close TEST_PERF;
|
||||
}
|
||||
}
|
||||
|
||||
return ($hutime, $hstime);
|
||||
}
|
||||
|
||||
sub usage
|
||||
{
|
||||
print <<EOF;
|
||||
$0 usage:
|
||||
$0 [<options>] <test>+
|
||||
|
||||
Option | Description
|
||||
--------------+-------------------------------------------------------------
|
||||
-h --help | Print usage and exit.
|
||||
-v --verbose | Verbose (incompatible with quiet).
|
||||
-q --quiet | Quiet (incompatible with verbose).
|
||||
-s --srcdir | Path to source tree (default is ".").
|
||||
-o --objdir | Path to object tree (default is ".").
|
||||
-c --command | Command template, where the following substitutions are
|
||||
| performed:
|
||||
| %1\$s : srcdir (see --srcdir)
|
||||
| %2\$s : objdir (see --objdir)
|
||||
| %3\$s : <test>
|
||||
| (default is '%1\$s/%3\$s').
|
||||
-r --reduce | The path to the reduce tool.
|
||||
| Do not run reduce if it is empty.
|
||||
| (default is empty)
|
||||
--coverage | The path to the coverage tool.
|
||||
| Do not run coverage if it is empty.
|
||||
| (default is empty)
|
||||
-t | Set the time limit for each test case
|
||||
| (default is unlimited).
|
||||
-u --ustats | Update historical statistics (stored in "<test>.perf".
|
||||
-z --zero | Consider non-zero exit code to be an error.
|
||||
--no-exp | Don't diff with .exp files
|
||||
--------------+-------------------------------------------------------------
|
||||
|
||||
If <test>.exp exists and --no-exp is not specified, <test>'s output is
|
||||
diff'ed with <test>.exp. Any difference is considered failure.
|
||||
|
||||
If <test>.exp does not exist or --no-exp is specified, output to stdout of
|
||||
the following form is expected:
|
||||
|
||||
1..<n>
|
||||
{not }ok[ 1]
|
||||
{not }ok[ 2]
|
||||
...
|
||||
{not }ok[ n]
|
||||
|
||||
1 <= <n> < 2^31
|
||||
|
||||
Lines which do not match the patterns shown above are ignored.
|
||||
EOF
|
||||
}
|
||||
# vim:filetype=perl:
|
||||
@@ -1,369 +0,0 @@
|
||||
<?php
|
||||
for ($jdk=0; $jdk<50; $jdk++) {
|
||||
?><html>
|
||||
<head>
|
||||
<?php /* the point of this file is to intensively test various aspects of the parser.
|
||||
* right now, each test focuses in one aspect only (e.g. variable aliasing, arithemtic operator,
|
||||
* various control structures), while trying to combine code from other parts of the parser as well.
|
||||
*/
|
||||
?>
|
||||
*** Testing assignments and variable aliasing: ***
|
||||
<?php
|
||||
/* This test tests assignments to variables using other variables as variable-names */
|
||||
$a = "b";
|
||||
$$a = "test";
|
||||
$$$a = "blah";
|
||||
${$$$a}["associative arrays work too"] = "this is nifty";
|
||||
?>
|
||||
This should read "blah": <?php echo "$test\n"; ?>
|
||||
This should read "this is nifty": <?php echo $blah[$test="associative arrays work too"]."\n"; ?>
|
||||
*************************************************
|
||||
|
||||
*** Testing integer operators ***
|
||||
<?php
|
||||
/* test just about any operator possible on $i and $j (ints) */
|
||||
$i = 5;
|
||||
$j = 3;
|
||||
?>
|
||||
Correct result - 8: <?php echo $i+$j; ?>
|
||||
|
||||
Correct result - 8: <?php echo $i+$j; ?>
|
||||
|
||||
Correct result - 2: <?php echo $i-$j; ?>
|
||||
|
||||
Correct result - -2: <?php echo $j-$i; ?>
|
||||
|
||||
Correct result - 15: <?php echo $i*$j; ?>
|
||||
|
||||
Correct result - 15: <?php echo $j*$i; ?>
|
||||
|
||||
Correct result - 2: <?php echo $i%$j; ?>
|
||||
|
||||
Correct result - 3: <?php echo $j%$i; ?>
|
||||
|
||||
*********************************
|
||||
|
||||
*** Testing real operators ***
|
||||
<?php
|
||||
/* test just about any operator possible on $i and $j (floats) */
|
||||
$i = 5.0;
|
||||
$j = 3.0;
|
||||
?>
|
||||
Correct result - 8: <?php echo $i+$j; ?>
|
||||
|
||||
Correct result - 8: <?php echo $i+$j; ?>
|
||||
|
||||
Correct result - 2: <?php echo $i-$j; ?>
|
||||
|
||||
Correct result - -2: <?php echo $j-$i; ?>
|
||||
|
||||
Correct result - 15: <?php echo $i*$j; ?>
|
||||
|
||||
Correct result - 15: <?php echo $j*$i; ?>
|
||||
|
||||
Correct result - 2: <?php echo $i%$j; ?>
|
||||
|
||||
Correct result - 3: <?php echo $j%$i; ?>
|
||||
|
||||
*********************************
|
||||
|
||||
*** Testing if/elseif/else control ***
|
||||
|
||||
<?php
|
||||
/* sick if/elseif/else test by Andi :) */
|
||||
$a = 5;
|
||||
if ($a == "4") {
|
||||
echo "This "." does "." not "." work\n";
|
||||
} elseif ($a == "5") {
|
||||
echo "This "." works\n";
|
||||
$a = 6;
|
||||
if ("andi" == ($test = "andi")) {
|
||||
echo "this_still_works\n";
|
||||
} elseif (1) {
|
||||
echo "should_not_print\n";
|
||||
} else {
|
||||
echo "should_not_print\n";
|
||||
}
|
||||
if (44 == 43) {
|
||||
echo "should_not_print\n";
|
||||
} else {
|
||||
echo "should_print\n";
|
||||
}
|
||||
} elseif ($a == 6) {
|
||||
echo "this "."broken\n";
|
||||
if (0) {
|
||||
echo "this_should_not_print\n";
|
||||
} else {
|
||||
echo "TestingDanglingElse_This_Should_not_print\n";
|
||||
}
|
||||
} else {
|
||||
echo "This "."does "." not"." work\n";
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
*** Seriously nested if's test ***
|
||||
** spelling correction by kluzz **
|
||||
<?php
|
||||
/* yet another sick if/elseif/else test by Zeev */
|
||||
$i=$j=0;
|
||||
echo "Only two lines of text should follow:\n";
|
||||
if (0) { /* this code is not supposed to be executed */
|
||||
echo "hmm, this shouldn't be displayed #1\n";
|
||||
$j++;
|
||||
if (1) {
|
||||
$i += $j;
|
||||
if (0) {
|
||||
$j = ++$i;
|
||||
if (1) {
|
||||
$j *= $i;
|
||||
echo "damn, this shouldn't be displayed\n";
|
||||
} else {
|
||||
$j /= $i;
|
||||
++$j;
|
||||
echo "this shouldn't be displayed either\n";
|
||||
}
|
||||
} elseif (1) {
|
||||
$i++; $j++;
|
||||
echo "this isn't supposed to be displayed\n";
|
||||
}
|
||||
} elseif (0) {
|
||||
$i++;
|
||||
echo "this definitely shouldn't be displayed\n";
|
||||
} else {
|
||||
--$j;
|
||||
echo "and this too shouldn't be displayed\n";
|
||||
while ($j>0) {
|
||||
$j--;
|
||||
}
|
||||
}
|
||||
} elseif (2-2) { /* as long as 2-2==0, this isn't supposed to be executed either */
|
||||
$i = ++$j;
|
||||
echo "hmm, this shouldn't be displayed #2\n";
|
||||
if (1) {
|
||||
$j = ++$i;
|
||||
if (0) {
|
||||
$j = $i*2+$j*($i++);
|
||||
if (1) {
|
||||
$i++;
|
||||
echo "damn, this shouldn't be displayed\n";
|
||||
} else {
|
||||
$j++;
|
||||
echo "this shouldn't be displayed either\n";
|
||||
}
|
||||
} else if (1) {
|
||||
++$j;
|
||||
echo "this isn't supposed to be displayed\n";
|
||||
}
|
||||
} elseif (0) {
|
||||
$j++;
|
||||
echo "this definitely shouldn't be displayed\n";
|
||||
} else {
|
||||
$i++;
|
||||
echo "and this too shouldn't be displayed\n";
|
||||
}
|
||||
} else {
|
||||
$j=$i++; /* this should set $i to 1, but shouldn't change $j (it's assigned $i's previous values, zero) */
|
||||
echo "this should be displayed. should be: \$i=1, \$j=0. is: \$i=$i, \$j=$j\n";
|
||||
if (1) {
|
||||
$j += ++$i; /* ++$i --> $i==2, $j += 2 --> $j==2 */
|
||||
if (0) {
|
||||
$j += 40;
|
||||
if (1) {
|
||||
$i += 50;
|
||||
echo "damn, this shouldn't be displayed\n";
|
||||
} else {
|
||||
$j += 20;
|
||||
echo "this shouldn't be displayed either\n";
|
||||
}
|
||||
} else if (1) {
|
||||
$j *= $i; /* $j *= 2 --> $j == 4 */
|
||||
echo "this is supposed to be displayed. should be: \$i=2, \$j=4. is: \$i=$i, \$j=$j\n";
|
||||
echo "3 loop iterations should follow:\n";
|
||||
while ($i<=$j) {
|
||||
echo $i++." $j\n";
|
||||
}
|
||||
}
|
||||
} elseif (0) {
|
||||
echo "this definitely shouldn't be displayed\n";
|
||||
} else {
|
||||
echo "and this too shouldn't be displayed\n";
|
||||
}
|
||||
echo "**********************************\n";
|
||||
}
|
||||
?>
|
||||
|
||||
*** C-style else-if's ***
|
||||
<?php
|
||||
/* looks like without we even tried, C-style else-if structure works fine! */
|
||||
if ($a=0) {
|
||||
echo "This shouldn't be displayed\n";
|
||||
} else if ($a++) {
|
||||
echo "This shouldn't be displayed either\n";
|
||||
} else if (--$a) {
|
||||
echo "No, this neither\n";
|
||||
} else if (++$a) {
|
||||
echo "This should be displayed\n";
|
||||
} else {
|
||||
echo "This shouldn't be displayed at all\n";
|
||||
}
|
||||
?>
|
||||
*************************
|
||||
|
||||
*** WHILE tests ***
|
||||
<?php
|
||||
$i=0;
|
||||
$j=20;
|
||||
while ($i<(2*$j)) {
|
||||
if ($i>$j) {
|
||||
echo "$i is greater than $j\n";
|
||||
} else if ($i==$j) {
|
||||
echo "$i equals $j\n";
|
||||
} else {
|
||||
echo "$i is smaller than $j\n";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
?>
|
||||
*******************
|
||||
|
||||
|
||||
*** Nested WHILEs ***
|
||||
<?php
|
||||
$arr_len=3;
|
||||
|
||||
$i=0;
|
||||
while ($i<$arr_len) {
|
||||
$j=0;
|
||||
while ($j<$arr_len) {
|
||||
$k=0;
|
||||
while ($k<$arr_len) {
|
||||
${"test$i$j"}[$k] = $i+$j+$k;
|
||||
$k++;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
echo "Each array variable should be equal to the sum of its indices:\n";
|
||||
|
||||
$i=0;
|
||||
while ($i<$arr_len) {
|
||||
$j=0;
|
||||
while ($j<$arr_len) {
|
||||
$k=0;
|
||||
while ($k<$arr_len) {
|
||||
echo "\${test$i$j}[$k] = ".${"test$i$j"}[$k]."\n";
|
||||
$k++;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
?>
|
||||
*********************
|
||||
|
||||
*** hash test... ***
|
||||
<?php
|
||||
/*
|
||||
$i=0;
|
||||
|
||||
while ($i<10000) {
|
||||
$arr[$i]=$i;
|
||||
$i++;
|
||||
}
|
||||
|
||||
$i=0;
|
||||
while ($i<10000) {
|
||||
echo $arr[$i++]."\n";
|
||||
}
|
||||
*/
|
||||
echo "commented out...";
|
||||
?>
|
||||
|
||||
**************************
|
||||
|
||||
*** Hash resizing test ***
|
||||
<?php
|
||||
$i = 10;
|
||||
$a = "b";
|
||||
while ($i > 0) {
|
||||
$a = $a . "a";
|
||||
echo "$a\n";
|
||||
$resize[$a] = $i;
|
||||
$i--;
|
||||
}
|
||||
$i = 10;
|
||||
$a = "b";
|
||||
while ($i > 0) {
|
||||
$a = $a . "a";
|
||||
echo "$a\n";
|
||||
echo $resize[$a]."\n";
|
||||
$i--;
|
||||
}
|
||||
?>
|
||||
**************************
|
||||
|
||||
|
||||
*** break/continue test ***
|
||||
<?php
|
||||
$i=0;
|
||||
|
||||
echo "\$i should go from 0 to 2\n";
|
||||
while ($i<5) {
|
||||
if ($i>2) {
|
||||
break;
|
||||
}
|
||||
$j=0;
|
||||
echo "\$j should go from 3 to 4, and \$q should go from 3 to 4\n";
|
||||
while ($j<5) {
|
||||
if ($j<=2) {
|
||||
$j++;
|
||||
continue;
|
||||
}
|
||||
echo " \$j=$j\n";
|
||||
for ($q=0; $q<=10; $q++) {
|
||||
if ($q<3) {
|
||||
continue;
|
||||
}
|
||||
if ($q>4) {
|
||||
break;
|
||||
}
|
||||
echo " \$q=$q\n";
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
$j=0;
|
||||
echo "\$j should go from 0 to 2\n";
|
||||
while ($j<5) {
|
||||
if ($j>2) {
|
||||
$k=0;
|
||||
echo "\$k should go from 0 to 2\n";
|
||||
while ($k<5) {
|
||||
if ($k>2) {
|
||||
break 2;
|
||||
}
|
||||
echo " \$k=$k\n";
|
||||
$k++;
|
||||
}
|
||||
}
|
||||
echo " \$j=$j\n";
|
||||
$j++;
|
||||
}
|
||||
echo "\$i=$i\n";
|
||||
$i++;
|
||||
}
|
||||
?>
|
||||
***********************
|
||||
|
||||
*** Nested file include test ***
|
||||
<?php include("023-2.inc"); ?>
|
||||
********************************
|
||||
|
||||
<?php
|
||||
{
|
||||
echo "Tests completed.\n"; # testing some PHP style comment...
|
||||
}
|
||||
|
||||
} ?>
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -63,6 +63,9 @@ no_import = (
|
||||
'/Zend/tests/002.phpt',
|
||||
'/Zend/tests/003.phpt',
|
||||
|
||||
# too large input
|
||||
'/tests/lang/024.phpt',
|
||||
|
||||
# spews files until they work
|
||||
'/ext/spl/tests/SplFileInfo_getExtension_basic.phpt',
|
||||
'/ext/spl/tests/SplFileObject_fgetcsv_basic.phpt',
|
||||
@@ -620,12 +623,11 @@ file('test/zend/config.hdf', 'w').write(
|
||||
|
||||
stdout = subprocess.Popen(
|
||||
[
|
||||
'tools/verify_to_json.php',
|
||||
'test/run',
|
||||
'test/zend/all',
|
||||
'-m',
|
||||
'interp',
|
||||
'',
|
||||
'../_bin',
|
||||
'--fbmake',
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Run verify checking only parsing works as expected.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# % fbconfig hphp/util/parser/test && fbmake dbg && \
|
||||
# ./hphp/tools/run_verify_parse.sh
|
||||
#
|
||||
HPHP_HOME=$(git rev-parse --show-toplevel)
|
||||
: ${FBMAKE_BIN_ROOT=_bin}
|
||||
cd $HPHP_HOME
|
||||
|
||||
VERIFY_SCRIPT=./hphp/test/verify
|
||||
PARSE_TEST=$FBMAKE_BIN_ROOT/hphp/util/parser/test/parse_tester
|
||||
|
||||
# some tests are expected not to parse
|
||||
PARSE_SKIP='dv_i0.php hh_bad_end.php hh_bad_start.php hh_numbers.php
|
||||
strict_bad_end.php strict_bad_start.php
|
||||
strict_numbers.php syntax-error.php xhp-malformed.php Xhp.php
|
||||
trailing_comma_bad1.php trailing_comma_bad2.php trailing_comma_bad3.php
|
||||
trailing_comma_bad4.php trailing_comma_bad5.php trailing_comma_bad6.php'
|
||||
PARSE_SKIP="$PARSE_SKIP $(cd hphp/test/quick && ls parse_fail_*.php)"
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
skip_list=
|
||||
for x in $PARSE_SKIP ; do
|
||||
skip_list="$skip_list hphp/test/quick/$x"
|
||||
done
|
||||
|
||||
qtests=$(comm -23 \
|
||||
<(find hphp/test/quick -maxdepth 1 -name \*.php | sort) \
|
||||
<(echo $skip_list|sed -e 's/ /\n/g'|sort))
|
||||
|
||||
cmd="$PARSE_TEST --verify %1\$s/%3\$s"
|
||||
exec $VERIFY_SCRIPT --no-exp --command="$cmd" $qtests
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/bin/env php
|
||||
<?php
|
||||
chdir(__DIR__.'/../../');
|
||||
|
||||
include_once 'hphp/test/fbmake_test_lib.php';
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Args to this script are:
|
||||
// $argv[0] test-script test-path interp|jit|hhir maybe-dash-r
|
||||
// FBMAKE_BIN_ROOT
|
||||
if (count($argv) != 6) {
|
||||
echo
|
||||
"This script is not intended for direct use. See hphp/hhvm/TARGETS.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$cmd = "FBMAKE_BIN_ROOT=$argv[5] $argv[1] $argv[2] -m $argv[3] $argv[4]";
|
||||
loop_tests($cmd, function ($line) {
|
||||
if (preg_match('/^(?:hphp\/)?(test[^\s]*).*/', $line, &$m)) {
|
||||
if (test_is_running()) {
|
||||
// Previous test made it! Just needs finishing.
|
||||
finish('passed');
|
||||
}
|
||||
start($m[1]);
|
||||
return;
|
||||
}
|
||||
if (preg_match('/^[\s\*]*FAILED.*/', $line)) {
|
||||
finish('failed');
|
||||
} else if (preg_match('/^\s*Totals.*/', $line) && test_is_running()) {
|
||||
// Account for last test if it was successful
|
||||
finish('passed');
|
||||
}
|
||||
});
|
||||
Referência em uma Nova Issue
Bloquear um usuário