test262/Runner.pm: minor fixes
[WebKit-https.git] / Tools / Scripts / test262 / Runner.pm
index df6b506..67cd8ba 100755 (executable)
@@ -61,10 +61,9 @@ use YAML qw(Load LoadFile Dump DumpFile Bless);
 use Parallel::ForkManager;
 use Getopt::Long qw(GetOptions);
 use Pod::Usage;
-use Term::ANSIColor;
 
-# Commandline args
-my $cliProcesses;
+# Commandline settings
+my $max_process;
 my @cliTestDirs;
 my $verbose;
 my $JSC;
@@ -75,16 +74,22 @@ my $ignoreConfig;
 my $config;
 my %configSkipHash;
 my $expect;
-my $saveCurrentResults;
+my $saveExpectations;
 my $failingOnly;
 my $latestImport;
-
-my $expectationsFile = abs_path("$Bin/test262-expectations.yaml");
-my $configFile = abs_path("$Bin/test262-config.yaml");
-my $resultsFile = abs_path("$Bin/test262-results.yaml");
+my $runningAllTests;
 
 processCLI();
 
+my @results;
+my @files;
+
+my $expectationsFile = abs_path("$Bin/expectations.yaml");
+my $configFile = abs_path("$Bin/config.yaml");
+my $resultsFile = abs_path("$Bin/results.yaml");
+my $summaryTxtFile = abs_path("$Bin/results-summary.txt");
+my $summaryFile = abs_path("$Bin/results-summary.yaml");
+
 my $tempdir = tempdir();
 
 my @default_harnesses = (
@@ -94,9 +99,6 @@ my @default_harnesses = (
     "$Bin/agent.js"
 );
 
-my @files;
-my ($resfh, $resfilename) = getTempFile();
-
 my ($deffh, $deffile) = getTempFile();
 print $deffh getHarness(<@default_harnesses>);
 
@@ -117,14 +119,14 @@ sub processCLI {
         'j|jsc=s' => \$JSC,
         't|t262=s' => \$test262Dir,
         'o|test-only=s@' => \@cliTestDirs,
-        'p|child-processes=i' => \$cliProcesses,
+        'p|child-processes=i' => \$max_process,
         'h|help' => \$help,
         'd|debug' => \$debug,
         'v|verbose' => \$verbose,
         'f|features=s@' => \@features,
         'c|config=s' => \$configFile,
         'i|ignore-config' => \$ignoreConfig,
-        's|save' => \$saveCurrentResults,
+        's|save' => \$saveExpectations,
         'x|ignore-expectations' => \$ignoreExpectations,
         'failing-files' => \$failingOnly,
         'l|latest-import' => \$latestImport,
@@ -136,7 +138,6 @@ sub processCLI {
     }
 
     if ($stats) {
-        print "Summarizing results...\n\n";
         summarizeResults();
         exit;
     }
@@ -154,8 +155,6 @@ sub processCLI {
         }
     } else {
         $JSC = getBuildPath($debug);
-
-        print("Using the following jsc path: $JSC\n");
     }
 
     if ($latestImport) {
@@ -196,13 +195,13 @@ sub processCLI {
         %filterFeatures = map { $_ => 1 } @features;
     }
 
-    $cliProcesses ||= getProcesses();
+    $max_process ||= getProcesses();
 
     print "\n-------------------------Settings------------------------\n"
         . "Test262 Dir: $test262Dir\n"
         . "JSC: $JSC\n"
         . "DYLD_FRAMEWORK_PATH: $DYLD_FRAMEWORK_PATH\n"
-        . "Child Processes: $cliProcesses\n";
+        . "Child Processes: $max_process\n";
 
     print "Features to include: " . join(', ', @features) . "\n" if @features;
     print "Paths:  " . join(', ', @cliTestDirs) . "\n" if @cliTestDirs;
@@ -217,10 +216,9 @@ sub processCLI {
 }
 
 sub main {
-    push(@cliTestDirs, 'test') if not @cliTestDirs;
 
-    my $max_process = $cliProcesses;
-    my $pm = Parallel::ForkManager->new($max_process);
+    # If not commandline test path supplied, use the root directory of all tests.
+    push(@cliTestDirs, 'test') if not @cliTestDirs;
 
     if ($latestImport) {
         @files = loadImportFile();
@@ -228,6 +226,7 @@ sub main {
         # If we only want to re-run failure, only run tests in expectation file
         @files = map { qq($test262Dir/$_) } keys %{$expect};
     } else {
+        $runningAllTests = 1;
         # Otherwise, get all files from directory
         foreach my $testsDir (@cliTestDirs) {
             find(
@@ -240,24 +239,58 @@ sub main {
         }
     }
 
-    FILES:
-    foreach my $file (@files) {
-        $pm->start and next FILES; # do the fork
-        srand(time ^ $$); # Creates a new seed for each fork
-        processFile($file);
 
-        $pm->finish; # do the exit in the child process
-    };
+    # If we are processing many files, fork process
+    if (scalar @files > $max_process * 5) {
 
-    $pm->wait_all_children;
+        # Make temporary files to record results
+        my @resultsfhs;
+        for (my $i = 0; $i <= $max_process-1; $i++) {
+            my ($fh, $filename) = getTempFile();
+            $resultsfhs[$i] = $fh;
+        }
 
-    close $deffh;
+        my $pm = Parallel::ForkManager->new($max_process);
+        my $filesperprocess = int(scalar @files / $max_process);
+
+        FILES:
+        for (my $i = 0; $i <= $max_process-1; $i++) {
+            $pm->start and next FILES; # do the fork
+            srand(time ^ $$); # Creates a new seed for each fork
+
+            my $first = $filesperprocess * $i;
+            my $last = $i == $max_process-1 ? scalar @files : $filesperprocess * ($i+1);
 
-    seek($resfh, 0, 0);
-    my @res = LoadFile($resfh);
-    close $resfh;
+            for (my $j = $first; $j < $last; $j++) {
+                processFile($files[$j], $resultsfhs[$i]);
+            };
 
-    @res = sort { "$a->{path} . $a->{mode}" cmp "$b->{path} . $b->{mode}" } @res;
+            $pm->finish; # do the exit in the child process
+        };
+
+        $pm->wait_all_children;
+
+        # Read results from file into @results and close
+        for (my $i = 0; $i <= $max_process-1; $i++) {
+            seek($resultsfhs[$i], 0, 0);
+            push @results, LoadFile($resultsfhs[$i]);
+            close $resultsfhs[$i];
+        }
+    }
+    # Otherwising, running sequentially is fine
+    else {
+        my ($resfh, $resfilename) = getTempFile();
+        foreach my $file (@files) {
+            processFile($file, $resfh);
+        };
+        seek($resfh, 0, 0);
+        @results = LoadFile($resfh);
+        close $resfh;
+    }
+
+    close $deffh;
+
+    @results = sort { "$a->{path} . $a->{mode}" cmp "$b->{path} . $b->{mode}" } @results;
 
     my %failed;
     my $failcount = 0;
@@ -266,7 +299,7 @@ sub main {
     my $skipfilecount = 0;
 
     # Create expectation file and calculate results
-    foreach my $test (@res) {
+    foreach my $test (@results) {
 
         my $expectedFailure = 0;
         if ($expect && $expect->{$test->{path}}) {
@@ -299,33 +332,37 @@ sub main {
         }
     }
 
-    if ($saveCurrentResults) {
-        DumpFile($resultsFile, \@res);
+    if ($saveExpectations) {
         DumpFile($expectationsFile, \%failed);
+        print "\nSaved results in: $expectationsFile\n";
+    } else {
+        print "\nRun with --save to save a new expectations file\n";
     }
 
-    my $endTime = time();
-    my $totalTime = $endTime - $startTime;
-    my $total = scalar @res - $skipfilecount;
+    if ($runningAllTests) {
+        DumpFile($resultsFile, \@results);
+        print "Saved all the results in $resultsFile\n";
+        summarizeResults();
+    }
+
+    my $total = scalar @results - $skipfilecount;
     print "\n" . $total . " tests ran\n";
 
     if ( !$expect ) {
         print $failcount . " tests failed\n";
-    }
-    else {
+    } else {
         print $failcount . " expected tests failed\n";
         print $newfailcount . " tests newly fail\n";
         print $newpasscount . " tests newly pass\n";
     }
 
     print $skipfilecount . " test files skipped\n";
+
+    my $endTime = time();
+    my $totalTime = $endTime - $startTime;
     print "Done in $totalTime seconds!\n";
-    if ($saveCurrentResults) {
-        print "\nSaved results in:\n$expectationsFile\n$resultsFile\n";
-    }
-    else {
-        print "\nRun with --save to saved new test262-expectation.yaml and test262-results.yaml files\n";
-    }
+
+    exit $newfailcount ? 1 : 0;
 }
 
 sub loadImportFile {
@@ -368,8 +405,7 @@ sub parseError {
 
     if ($error =~ /^Exception: ([\w\d]+: .*)/m) {
         return $1;
-    }
-    else {
+    } else {
         # Unusual error format. Save the first line instead.
         my @errors = split("\n", $error);
         return $errors[0];
@@ -410,14 +446,16 @@ sub getBuildPath {
 }
 
 sub processFile {
-    my $filename = shift;
+    my ($filename, $resultsfh) = @_;
     my $contents = getContents($filename);
     my $data = parseData($contents, $filename);
+    my $resultsdata;
 
     # Check test against filters in config file
     my $file = abs2rel( $filename, $test262Dir );
     if (shouldSkip($file, $data)) {
-        processResult($filename, $data, "skip");
+        $resultsdata = processResult($filename, $data, "skip");
+        DumpFile($resultsfh, $resultsdata);
         return;
     }
 
@@ -431,7 +469,8 @@ sub processFile {
     foreach my $scenario (@scenarios) {
         my $result = runTest($includesfile, $filename, $scenario, $data);
 
-        processResult($filename, $data, $scenario, $result);
+        $resultsdata = processResult($filename, $data, $scenario, $result);
+        DumpFile($resultsfh, $resultsdata);
     }
 
     close $includesfh if defined $includesfh;
@@ -538,7 +577,7 @@ sub runTest {
     my $defaultHarness = '';
     $defaultHarness = $deffile if $scenario ne 'raw';
 
-    my $result = qx/$JSC $args $defaultHarness $includesfile $prefixFile$filename/;
+    my $result = qx/$JSC $args $defaultHarness $includesfile '$prefixFile$filename'/;
 
     chomp $result;
 
@@ -580,22 +619,20 @@ sub processResult {
 
         $resultdata{result} = 'FAIL';
         $resultdata{error} = $currentfailure;
-    }
-    elsif ($scenario ne 'skip' && !$currentfailure) {
+    } elsif ($scenario ne 'skip' && !$currentfailure) {
         if ($expectedfailure) {
             print "NEW PASS $file ($scenario)\n";
             print "\n" if $verbose;
         }
 
         $resultdata{result} = 'PASS';
-    }
-    else {
+    } else {
         $resultdata{result} = 'SKIP';
     }
 
     $resultdata{features} = $data->{features} if $data->{features};
 
-    DumpFile($resfh, \%resultdata);
+    return \%resultdata;
 }
 
 sub getTempFile {
@@ -650,15 +687,17 @@ sub getHarness {
     return $content;
 }
 
-
 sub summarizeResults {
-    my @rawresults = LoadFile($resultsFile) or die $!;
+    print "Summarizing results...\n";
 
+    if (not @results) {
+        my @rawresults = LoadFile($resultsFile) or die $!;
+        @results = @{$rawresults[0]};
+    }
     my %byfeature;
     my %bypath;
 
-    foreach my $test (@{$rawresults[0]}) {
-
+    foreach my $test (@results) {
         my $result = $test->{result};
 
         if ($test->{features}) {
@@ -701,12 +740,10 @@ sub summarizeResults {
 
     }
 
+    open(my $sfh, '>', $summaryTxtFile) or die $!;
 
-    print sprintf("%-6s %-6s %-6s %-6s %s\n", '% PASS', 'PASS', 'FAIL', 'SKIP', 'FOLDER');
+    print $sfh sprintf("%-6s %-6s %-6s %-6s %s\n", '%PASS', 'PASS', 'FAIL', 'SKIP', 'FOLDER');
     foreach my $key (sort keys %bypath) {
-        my $c = 'black';
-        $c = 'red' if $bypath{$key}->[1];
-
         my $per = ($bypath{$key}->[0] / (
             $bypath{$key}->[0]
             + $bypath{$key}->[1]
@@ -714,19 +751,16 @@ sub summarizeResults {
 
         $per = sprintf("%.0f", $per) . "%";
 
-        print colored([$c], sprintf("%-6s %-6d %-6d %-6d %s \n", $per,
-                      $bypath{$key}->[0],
-                      $bypath{$key}->[1],
-                      $bypath{$key}->[2], $key,));
+        print $sfh sprintf("%-6s %-6d %-6d %-6d %s \n", $per,
+                           $bypath{$key}->[0],
+                           $bypath{$key}->[1],
+                           $bypath{$key}->[2], $key,);
     }
 
-    print "\n\n";
-    print sprintf("%-6s %-6s %-6s %-6s %s\n", '% PASS', 'PASS', 'FAIL', 'SKIP', 'FEATURE');
+    print $sfh "\n\n";
+    print $sfh sprintf("%-6s %-6s %-6s %-6s %s\n", '%PASS', 'PASS', 'FAIL', 'SKIP', 'FEATURE');
 
     foreach my $key (sort keys %byfeature) {
-        my $c = 'black';
-        $c = 'red' if $byfeature{$key}->[1];
-
         my $per = ($byfeature{$key}->[0] / (
             $byfeature{$key}->[0]
             + $byfeature{$key}->[1]
@@ -734,13 +768,22 @@ sub summarizeResults {
 
         $per = sprintf("%.0f", $per) . "%";
 
-        print colored([$c], sprintf("%-6s %-6d %-6d %-6d %s\n", $per,
-                      $byfeature{$key}->[0],
-                      $byfeature{$key}->[1],
-                      $byfeature{$key}->[2], $key));
+        print $sfh sprintf("%-6s %-6d %-6d %-6d %s\n", $per,
+                           $byfeature{$key}->[0],
+                           $byfeature{$key}->[1],
+                           $byfeature{$key}->[2], $key);
     }
 
+    close($sfh);
+
+    my %resultsyaml = (
+        byFolder => \%bypath,
+        byFeature => \%byfeature,
+    );
+
+    DumpFile($summaryFile, \%resultsyaml);
 
+    print "See summarized results in $summaryTxtFile\n";
 }
 
 __END__
@@ -813,7 +856,7 @@ Specify one or more specific test262 directory of test to run, relative to the r
 
 =item B<--save, -s>
 
-Overwrites the test262-expectations.yaml and test262-results.yaml file with the current list of test262 files and test results.
+Overwrites the test262-expectations.yaml file with the current list of test262 files and test results.
 
 =item B<--ignore-expectations, -x>
 
@@ -829,7 +872,7 @@ Runs the test files listed in the last import (./JSTests/test262/latest-changes-
 
 =item B<--stats>
 
-Calculate conformance statistics from test262-results.yaml file.
+Calculate conformance statistics from JSTests/test262-results.yaml file. Saves results in JSTests/test262/results-summary.txt and JSTests/test262/results-summary.yaml.
 
 =back