Auto save the results for Test262
[WebKit-https.git] / Tools / Scripts / test262 / Runner.pm
1 #!/usr/bin/env perl
2
3 # Copyright (C) 2018 Bocoup LLC. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # 1. Redistributions of source code must retain the above
10 #    copyright notice, this list of conditions and the following
11 #    disclaimer.
12 # 2. Redistributions in binary form must reproduce the above
13 #    copyright notice, this list of conditions and the following
14 #    disclaimer in the documentation and/or other materials
15 #    provided with the distribution.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
18 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 # SUCH DAMAGE.
29
30 use strict;
31 use warnings;
32 use 5.8.8;
33 package Test262::Runner;
34
35 use File::Find;
36 use File::Temp qw(tempfile tempdir);
37 use File::Spec::Functions qw(abs2rel);
38 use File::Basename qw(dirname);
39 use Cwd qw(abs_path);
40 use FindBin;
41 use Env qw(DYLD_FRAMEWORK_PATH);
42 my $Bin;
43
44 use Config;
45 use Encode;
46
47 BEGIN {
48     $ENV{DBIC_OVERWRITE_HELPER_METHODS_OK} = 1;
49
50     $Bin = $ENV{T262_EXEC_BIN} || $FindBin::Bin;
51
52     unshift @INC, "$Bin";
53     unshift @INC, "$Bin/lib";
54     unshift @INC, "$Bin/local/lib/perl5";
55     unshift @INC, "$Bin/local/lib/perl5/$Config{archname}";
56
57     $ENV{LOAD_ROUTES} = 1;
58 }
59
60 use YAML qw(Load LoadFile Dump DumpFile Bless);
61 use Parallel::ForkManager;
62 use Getopt::Long qw(GetOptions);
63 use Pod::Usage;
64 use Term::ANSIColor;
65
66 # Commandline args
67 my $cliProcesses;
68 my @cliTestDirs;
69 my $verbose;
70 my $JSC;
71 my $test262Dir;
72 my $harnessDir;
73 my %filterFeatures;
74 my $ignoreConfig;
75 my $config;
76 my %configSkipHash;
77 my $expect;
78 my $saveExpectations;
79 my $failingOnly;
80 my $latestImport;
81 my $runningAllTests;
82
83 my $expectationsFile = abs_path("$Bin/expectations.yaml");
84 my $configFile = abs_path("$Bin/config.yaml");
85 my $resultsFile = abs_path("$Bin/results.yaml");
86
87 processCLI();
88
89 my $tempdir = tempdir();
90
91 my @default_harnesses = (
92     "$harnessDir/sta.js",
93     "$harnessDir/assert.js",
94     "$harnessDir/doneprintHandle.js",
95     "$Bin/agent.js"
96 );
97
98 my @files;
99 my ($resfh, $resfilename) = getTempFile();
100
101 my ($deffh, $deffile) = getTempFile();
102 print $deffh getHarness(<@default_harnesses>);
103
104 my $startTime = time();
105
106 main();
107
108 sub processCLI {
109     my $help = 0;
110     my $debug;
111     my $ignoreExpectations;
112     my @features;
113     my $stats;
114
115     # If adding a new commandline argument, you must update the POD
116     # documentation at the end of the file.
117     GetOptions(
118         'j|jsc=s' => \$JSC,
119         't|t262=s' => \$test262Dir,
120         'o|test-only=s@' => \@cliTestDirs,
121         'p|child-processes=i' => \$cliProcesses,
122         'h|help' => \$help,
123         'd|debug' => \$debug,
124         'v|verbose' => \$verbose,
125         'f|features=s@' => \@features,
126         'c|config=s' => \$configFile,
127         'i|ignore-config' => \$ignoreConfig,
128         's|save' => \$saveExpectations,
129         'x|ignore-expectations' => \$ignoreExpectations,
130         'failing-files' => \$failingOnly,
131         'l|latest-import' => \$latestImport,
132         'stats' => \$stats,
133     );
134
135     if ($help) {
136         pod2usage(-exitstatus => 0, -verbose => 2, -input => __FILE__);
137     }
138
139     if ($stats) {
140         print "Summarizing results...\n\n";
141         summarizeResults();
142         exit;
143     }
144
145     if ($JSC) {
146         $JSC = abs_path($JSC);
147         # Make sure the path and file jsc exist
148         if (! ($JSC && -e $JSC)) {
149             die "Error: --jsc path does not exist.";
150         }
151
152         # For custom JSC paths, Sets only if not yet defined
153         if (not defined $DYLD_FRAMEWORK_PATH) {
154             $DYLD_FRAMEWORK_PATH = dirname($JSC);
155         }
156     } else {
157         $JSC = getBuildPath($debug);
158
159         print("Using the following jsc path: $JSC\n");
160     }
161
162     if ($latestImport) {
163         # Does not allow custom $test262Dir
164         $test262Dir = '';
165     }
166
167     if (! $test262Dir) {
168         $test262Dir = abs_path("$Bin/../../../JSTests/test262");
169     } else {
170         $test262Dir = abs_path($test262Dir);
171     }
172     $harnessDir = "$test262Dir/harness";
173
174     if (! $ignoreConfig) {
175         if ($configFile and not -e $configFile) {
176             die "Config file $configFile does not exist!";
177         }
178
179         $config = LoadFile($configFile) or die $!;
180         if ($config->{skip} && $config->{skip}->{files}) {
181             %configSkipHash = map { $_ => 1 } @{$config->{skip}->{files}};
182         }
183     }
184
185     if (! $ignoreExpectations) {
186         # If expectations file doesn't exist yet, just run tests, UNLESS
187         # --failures-only option supplied.
188         if ( $failingOnly && ! -e $expectationsFile ) {
189             print "Error: Cannot run failing tests if test262-expectation.yaml file does not exist.\n";
190             die;
191         } elsif (-e $expectationsFile) {
192             $expect = LoadFile($expectationsFile) or die $!;
193         }
194     }
195
196     if (@features) {
197         %filterFeatures = map { $_ => 1 } @features;
198     }
199
200     $cliProcesses ||= getProcesses();
201
202     print "\n-------------------------Settings------------------------\n"
203         . "Test262 Dir: $test262Dir\n"
204         . "JSC: $JSC\n"
205         . "DYLD_FRAMEWORK_PATH: $DYLD_FRAMEWORK_PATH\n"
206         . "Child Processes: $cliProcesses\n";
207
208     print "Features to include: " . join(', ', @features) . "\n" if @features;
209     print "Paths:  " . join(', ', @cliTestDirs) . "\n" if @cliTestDirs;
210     print "Config file: $configFile\n" if $config;
211     print "Expectations file: $expectationsFile\n" if $expect;
212
213     print "Running only the latest imported files\n" if $latestImport;
214
215     print "Verbose mode\n" if $verbose;
216
217     print "--------------------------------------------------------\n\n";
218 }
219
220 sub main {
221     push(@cliTestDirs, 'test') if not @cliTestDirs;
222
223     my $max_process = $cliProcesses;
224     my $pm = Parallel::ForkManager->new($max_process);
225
226     if ($latestImport) {
227         @files = loadImportFile();
228     } elsif ($failingOnly) {
229         # If we only want to re-run failure, only run tests in expectation file
230         @files = map { qq($test262Dir/$_) } keys %{$expect};
231     } else {
232         $runningAllTests = 1;
233         # Otherwise, get all files from directory
234         foreach my $testsDir (@cliTestDirs) {
235             find(
236                 { wanted => \&wanted, bydepth => 1 },
237                 qq($test262Dir/$testsDir)
238                 );
239             sub wanted {
240                 /(?<!_FIXTURE)\.[jJ][sS]$/s && push(@files, $File::Find::name);
241             }
242         }
243     }
244
245     FILES:
246     foreach my $file (@files) {
247         $pm->start and next FILES; # do the fork
248         srand(time ^ $$); # Creates a new seed for each fork
249         processFile($file);
250
251         $pm->finish; # do the exit in the child process
252     };
253
254     $pm->wait_all_children;
255
256     close $deffh;
257
258     seek($resfh, 0, 0);
259     my @res = LoadFile($resfh);
260     close $resfh;
261
262     @res = sort { "$a->{path} . $a->{mode}" cmp "$b->{path} . $b->{mode}" } @res;
263
264     my %failed;
265     my $failcount = 0;
266     my $newfailcount = 0;
267     my $newpasscount = 0;
268     my $skipfilecount = 0;
269
270     # Create expectation file and calculate results
271     foreach my $test (@res) {
272
273         my $expectedFailure = 0;
274         if ($expect && $expect->{$test->{path}}) {
275             $expectedFailure = $expect->{$test->{path}}->{$test->{mode}}
276         }
277
278         if ($test->{result} eq 'FAIL') {
279             $failcount++;
280
281             # Record this round of failures
282             if ( $failed{$test->{path}} ) {
283                 $failed{$test->{path}}->{$test->{mode}} =  $test->{error};
284             }
285             else {
286                 $failed{$test->{path}} = {
287                     $test->{mode} => $test->{error}
288                 };
289             }
290
291             # If an unexpected failure
292             $newfailcount++ if !$expectedFailure || ($expectedFailure ne $test->{error});
293
294         }
295         elsif ($test->{result} eq 'PASS') {
296             # If this is an newly passing test
297             $newpasscount++ if $expectedFailure;
298         }
299         elsif ($test->{result} eq 'SKIP') {
300             $skipfilecount++;
301         }
302     }
303
304     if ($saveExpectations) {
305         DumpFile($expectationsFile, \%failed);
306         print "\nSaved results in: $expectationsFile\n";
307     } else {
308         print "\nRun with --save to save a new expectations file\n";
309     }
310
311     if ($runningAllTests) {
312         DumpFile($resultsFile, \@res);
313         print "Saved all the results in the $resultsFile.";
314         print "Summarizing results...\n\n";
315         summarizeResults();
316     }
317
318     my $total = scalar @res - $skipfilecount;
319     print "\n" . $total . " tests ran\n";
320
321     if ( !$expect ) {
322         print $failcount . " tests failed\n";
323     } else {
324         print $failcount . " expected tests failed\n";
325         print $newfailcount . " tests newly fail\n";
326         print $newpasscount . " tests newly pass\n";
327     }
328
329     print $skipfilecount . " test files skipped\n";
330
331     my $endTime = time();
332     my $totalTime = $endTime - $startTime;
333     print "Done in $totalTime seconds!\n";
334
335     exit $newfailcount ? 1 : 0;
336 }
337
338 sub loadImportFile {
339     my $importFile = abs_path("$Bin/../../../JSTests/test262/latest-changes-summary.txt");
340     die "Import file not found at $importFile.\n" if ! -e $importFile;
341
342     open(my $fh, "<", $importFile) or die $!;
343
344     my @files = grep { $_ =~ /^[AM]\s*test\// } <$fh>;
345
346     return map { $_ =~ s/^\w\s(\w*)/$test262Dir\/$1/; chomp $_; $_ } @files;
347 }
348
349 sub getProcesses {
350     my $cores;
351     my $uname = qx(which uname >> /dev/null && uname);
352     chomp $uname;
353
354     if ($uname eq 'Darwin') {
355         # sysctl should be available
356         $cores = qx/sysctl -n hw.ncpu/;
357     } elsif ($uname eq 'Linux') {
358         $cores = qx(which getconf >> /dev/null && getconf _NPROCESSORS_ONLN);
359         if (!$cores) {
360             $cores = qx(which lscpu >> /dev/null && lscpu -p | egrep -v '^#' | wc -l);
361         }
362     }
363
364     chomp $cores;
365
366     if (!$cores) {
367         $cores = 1;
368     }
369
370     return $cores * 8;
371 }
372
373 sub parseError {
374     my $error = shift;
375
376     if ($error =~ /^Exception: ([\w\d]+: .*)/m) {
377         return $1;
378     } else {
379         # Unusual error format. Save the first line instead.
380         my @errors = split("\n", $error);
381         return $errors[0];
382     }
383 }
384
385 sub getBuildPath {
386     my $debug = shift;
387
388     # Try to find JSC for user, if not supplied
389     my $cmd = abs_path("$Bin/../webkit-build-directory");
390     if (! -e $cmd) {
391         die 'Error: cannot find webkit-build-directory, specify with JSC with --jsc <path>.';
392     }
393
394     if ($debug) {
395         $cmd .= ' --debug';
396     } else {
397         $cmd .= ' --release';
398     }
399     $cmd .= ' --executablePath';
400     my $jscDir = qx($cmd);
401     chomp $jscDir;
402
403     my $jsc;
404     $jsc = $jscDir . '/jsc';
405
406     $jsc = $jscDir . '/JavaScriptCore.framework/Resources/jsc' if (! -e $jsc);
407     $jsc = $jscDir . '/bin/jsc' if (! -e $jsc);
408     if (! -e $jsc) {
409         die 'Error: cannot find jsc, specify with --jsc <path>.';
410     }
411
412     # Sets the Env DYLD_FRAMEWORK_PATH
413     $DYLD_FRAMEWORK_PATH = dirname($jsc);
414
415     return $jsc;
416 }
417
418 sub processFile {
419     my $filename = shift;
420     my $contents = getContents($filename);
421     my $data = parseData($contents, $filename);
422
423     # Check test against filters in config file
424     my $file = abs2rel( $filename, $test262Dir );
425     if (shouldSkip($file, $data)) {
426         processResult($filename, $data, "skip");
427         return;
428     }
429
430     my @scenarios = getScenarios(@{ $data->{flags} });
431
432     my $includes = $data->{includes};
433     my ($includesfh, $includesfile);
434
435     ($includesfh, $includesfile) = compileTest($includes) if defined $includes;
436
437     foreach my $scenario (@scenarios) {
438         my $result = runTest($includesfile, $filename, $scenario, $data);
439
440         processResult($filename, $data, $scenario, $result);
441     }
442
443     close $includesfh if defined $includesfh;
444 }
445
446 sub shouldSkip {
447     my ($filename, $data) = @_;
448
449     if (exists $config->{skip}) {
450         # Filter by file
451         if( $configSkipHash{$filename} ) {
452             return 1;
453         }
454
455         # Filter by paths
456         my @skipPaths;
457         @skipPaths = @{ $config->{skip}->{paths} } if defined $config->{skip}->{paths};
458         return 1 if (grep {$filename =~ $_} @skipPaths);
459
460         my @skipFeatures;
461         @skipFeatures = @{ $config->{skip}->{features} } if defined $config->{skip}->{features};
462
463         my $skip = 0;
464         my $keep = 0;
465         my @features = @{ $data->{features} } if $data->{features};
466         # Filter by features, loop over file features to for less iterations
467         foreach my $feature (@features) {
468             $skip = (grep {$_ eq $feature} @skipFeatures) ? 1 : 0;
469
470             # keep the test if the config skips the feature but it was also request
471             # through the CLI --features
472             return 1 if $skip && !$filterFeatures{$feature};
473
474             $keep = 1 if $filterFeatures{$feature};
475         }
476
477         # filter tests that do not contain the --features features
478         return 1 if (%filterFeatures and not $keep);
479     }
480
481     return 0;
482 }
483
484 sub getScenarios {
485     my @flags = @_;
486     my @scenarios;
487     my $nonStrict = 'default';
488     my $strictMode = 'strict mode';
489
490     if (grep $_ eq 'raw', @flags) {
491         push @scenarios, 'raw';
492     } elsif (grep $_ eq 'noStrict', @flags) {
493         push @scenarios, $nonStrict;
494     } elsif (grep $_ eq 'onlyStrict', @flags) {
495         push @scenarios, $strictMode;
496     } elsif (grep $_ eq 'module', @flags) {
497         push @scenarios, 'module';
498     } else {
499         # Add 2 default scenarios
500         push @scenarios, $strictMode;
501         push @scenarios, $nonStrict;
502     };
503
504     return @scenarios;
505 }
506
507 sub compileTest {
508     my $includes = shift;
509     my ($tfh, $tfname) = getTempFile();
510
511     my $includesContent = getHarness(map { "$harnessDir/$_" } @{ $includes });
512     print $tfh $includesContent;
513
514     return ($tfh, $tfname);
515 }
516
517 sub runTest {
518     my ($includesfile, $filename, $scenario, $data) = @_;
519     $includesfile ||= '';
520
521     my $args = '';
522
523     if (exists $data->{negative}) {
524         my $type = $data->{negative}->{type};
525         $args .=  " --exception=$type ";
526     }
527
528     if (exists $data->{flags}) {
529         my @flags = $data->{flags};
530         if (grep $_ eq 'async', @flags) {
531             $args .= ' --test262-async ';
532         }
533     }
534
535     my $prefixFile = '';
536
537     if ($scenario eq 'module') {
538         $prefixFile='--module-file=';
539     } elsif ($scenario eq 'strict mode') {
540         $prefixFile='--strict-file=';
541     }
542
543     # Raw tests should not include the default harness
544     my $defaultHarness = '';
545     $defaultHarness = $deffile if $scenario ne 'raw';
546
547     my $result = qx/$JSC $args $defaultHarness $includesfile $prefixFile$filename/;
548
549     chomp $result;
550
551     return $result if ($?);
552 }
553
554 sub processResult {
555     my ($path, $data, $scenario, $result) = @_;
556
557     # Report a relative path
558     my $file = abs2rel( $path, $test262Dir );
559     my %resultdata;
560     $resultdata{path} = $file;
561     $resultdata{mode} = $scenario;
562
563     my $currentfailure = parseError($result) if $result;
564     my $expectedfailure = $expect
565         && $expect->{$file}
566         && $expect->{$file}->{$scenario};
567
568     if ($scenario ne 'skip' && $currentfailure) {
569
570         # We have a new failure if we have loaded an expectation file
571         # AND (there is no expected failure OR the failure has changed).
572         my $isnewfailure = $expect
573             && (!$expectedfailure || $expectedfailure ne $currentfailure);
574
575         # Print the failure if we haven't loaded an expectation file
576         # or the failure is new.
577         my $printfailure = !$expect || $isnewfailure;
578
579         print "! NEW " if $isnewfailure;
580         print "FAIL $file ($scenario)\n" if $printfailure;
581         if ($verbose) {
582             print $result;
583             print "\nFeatures: " . join(', ', @{ $data->{features} }) if $data->{features};
584             print "\n\n";
585         }
586
587         $resultdata{result} = 'FAIL';
588         $resultdata{error} = $currentfailure;
589     } elsif ($scenario ne 'skip' && !$currentfailure) {
590         if ($expectedfailure) {
591             print "NEW PASS $file ($scenario)\n";
592             print "\n" if $verbose;
593         }
594
595         $resultdata{result} = 'PASS';
596     } else {
597         $resultdata{result} = 'SKIP';
598     }
599
600     $resultdata{features} = $data->{features} if $data->{features};
601
602     DumpFile($resfh, \%resultdata);
603 }
604
605 sub getTempFile {
606     my ($tfh, $tfname) = tempfile(DIR => $tempdir);
607
608     return ($tfh, $tfname);
609 }
610
611 sub getContents {
612     my $filename = shift;
613
614     open(my $fh, '<', $filename) or die $!;
615     my $contents = join('', <$fh>);
616     close $fh;
617
618     return $contents;
619 }
620
621 sub parseData {
622     my ($contents, $filename) = @_;
623
624     my $parsed;
625     my $found = '';
626     if ($contents =~ /\/\*(---\n[\S\s]*)\n---\*\//m) {
627         $found = $1;
628     };
629
630     eval {
631         $parsed = Load($found);
632     };
633     if ($@) {
634         print "\nError parsing YAML data on file $filename.\n";
635         print "$@\n";
636     };
637     return $parsed;
638 }
639
640 sub getHarness {
641     my @files = @_;
642     my $content;
643     for (@files) {
644         my $file = $_;
645
646         open(my $harness_file, '<', $file)
647             or die "$!, '$file'";
648
649         $content .= join('', <$harness_file>);
650
651         close $harness_file;
652     };
653
654     return $content;
655 }
656
657 sub summarizeResults {
658     my @rawresults = LoadFile($resultsFile) or die $!;
659
660     my %byfeature;
661     my %bypath;
662
663     foreach my $test (@{$rawresults[0]}) {
664         my $result = $test->{result};
665
666         if ($test->{features}) {
667             foreach my $feature (@{$test->{features}}) {
668
669                 if (not exists $byfeature{$feature}) {
670                     $byfeature{$feature} = [0, 0, 0]
671                 }
672
673                 if ($result eq 'PASS') {
674                     $byfeature{$feature}->[0]++;
675                 }
676                 if ($result eq 'FAIL') {
677                     $byfeature{$feature}->[1]++;
678                 }
679                 if ($result eq 'SKIP') {
680                     $byfeature{$feature}->[2]++;
681                 }
682             }
683         }
684         my @paths = split('/', $test->{path});
685         @paths = @paths[ 1 ... scalar @paths-2 ];
686         foreach my $i (0..scalar @paths-1) {
687             my $partialpath = join("/", @paths[0...$i]);
688
689             if (not exists $bypath{$partialpath}) {
690                 $bypath{$partialpath} = [0, 0, 0];
691             }
692
693             if ($result eq 'PASS') {
694                 $bypath{$partialpath}->[0]++;
695             }
696             if ($result eq 'FAIL') {
697                 $bypath{$partialpath}->[1]++;
698             }
699             if ($result eq 'SKIP') {
700                 $bypath{$partialpath}->[2]++;
701             }
702         }
703
704     }
705
706     print sprintf("%-6s %-6s %-6s %-6s %s\n", '% PASS', 'PASS', 'FAIL', 'SKIP', 'FOLDER');
707     foreach my $key (sort keys %bypath) {
708         my $c = 'bold';
709         $c = 'clear' if $bypath{$key}->[1];
710
711         my $per = ($bypath{$key}->[0] / (
712             $bypath{$key}->[0]
713             + $bypath{$key}->[1]
714             + $bypath{$key}->[2])) * 100;
715
716         $per = sprintf("%.0f", $per) . "%";
717
718         print colored([$c], sprintf("%-6s %-6d %-6d %-6d %s \n", $per,
719                       $bypath{$key}->[0],
720                       $bypath{$key}->[1],
721                       $bypath{$key}->[2], $key,));
722     }
723
724     print "\n\n";
725     print sprintf("%-6s %-6s %-6s %-6s %s\n", '% PASS', 'PASS', 'FAIL', 'SKIP', 'FEATURE');
726
727     foreach my $key (sort keys %byfeature) {
728         my $c = 'bold';
729         $c = 'clear' if $byfeature{$key}->[1];
730
731         my $per = ($byfeature{$key}->[0] / (
732             $byfeature{$key}->[0]
733             + $byfeature{$key}->[1]
734             + $byfeature{$key}->[2])) * 100;
735
736         $per = sprintf("%.0f", $per) . "%";
737
738         print colored([$c], sprintf("%-6s %-6d %-6d %-6d %s\n", $per,
739                       $byfeature{$key}->[0],
740                       $byfeature{$key}->[1],
741                       $byfeature{$key}->[2], $key));
742     }
743 }
744
745 __END__
746
747 =head1 DESCRIPTION
748
749 This program will run all Test262 tests. If you edit, make sure your changes are Perl 5.8.8 compatible.
750
751 =head1 SYNOPSIS
752
753 Run using native Perl:
754
755 =over 8
756
757 test262-runner -j $jsc-dir
758
759 =back
760
761 Run using carton (recommended for testing on Perl 5.8.8):
762
763 =over 8
764
765 carton exec 'test262-runner -j $jsc-dir'
766
767 =back
768
769 =head1 OPTIONS
770
771 =over 8
772
773 =item B<--help, -h>
774
775 Print a brief help message and exits.
776
777 =item B<--child-processes, -p>
778
779 Specify number of child processes.
780
781 =item B<--t262, -t>
782
783 Specify root test262 directory.
784
785 =item B<--jsc, -j>
786
787 Specify JSC location. If not provided, script will attempt to look up JSC.
788
789 =item B<--debug, -d>
790
791 Use debug build of JSC. Can only use if --jsc <path> is not provided. Release build of JSC is used by default.
792
793 =item B<--verbose, -v>
794
795 Verbose output for test results. Includes error message for test.
796
797 =item B<--config, -c>
798
799 Specify a config file. If not provided, script will load local test262-config.yaml
800
801 =item B<--ignore-config, -i>
802
803 Ignores config file if supplied or findable in directory. Will still filter based on commandline arguments.
804
805 =item B<--features, -f>
806
807 Filter test on list of features (only runs tests in feature list).
808
809 =item B<--test-only, -o>
810
811 Specify one or more specific test262 directory of test to run, relative to the root test262 directory. For example, --test-only 'test/built-ins/Number/prototype'
812
813 =item B<--save, -s>
814
815 Overwrites the test262-expectations.yaml and test262-results.yaml file with the current list of test262 files and test results.
816
817 =item B<--ignore-expectations, -x>
818
819 Ignores the test262-expectations.yaml file and outputs all failures, instead of only unexpected failures.
820
821 =item B<--failing-files>
822
823 Runs all test files that expect to fail according to the expectation file. This option will run the rests in both strict and non-strict modes, even if the test only fails in one of the two modes.
824
825 =item B<--latest-import, -l>
826
827 Runs the test files listed in the last import (./JSTests/test262/latest-changes-summary.txt).
828
829 =item B<--stats>
830
831 Calculate conformance statistics from test262-results.yaml file.
832
833 =back
834
835 =cut