1 #!/usr/bin/perl 2 # 3 # The contents of this file are subject to the Netscape Public 4 # License Version 1.1 (the "License"); you may not use this file 5 # except in compliance with the License. You may obtain a copy of 6 # the License at http://www.mozilla.org/NPL/ 7 # 8 # Software distributed under the License is distributed on an "AS 9 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 10 # implied. See the License for the specific language governing 11 # rights and limitations under the License. 12 # 13 # The Original Code is JavaScript Core Tests. 14 # 15 # The Initial Developer of the Original Code is Netscape 16 # Communications Corporation. Portions created by Netscape are 17 # Copyright (C) 1997-1999 Netscape Communications Corporation. All 18 # Rights Reserved. 19 # 20 # Alternatively, the contents of this file may be used under the 21 # terms of the GNU Public License (the "GPL"), in which case the 22 # provisions of the GPL are applicable instead of those above. 23 # If you wish to allow use of your version of this file only 24 # under the terms of the GPL and not to allow others to use your 25 # version of this file under the NPL, indicate your decision by 26 # deleting the provisions above and replace them with the notice 27 # and other provisions required by the GPL. If you do not delete 28 # the provisions above, a recipient may use your version of this 29 # file under either the NPL or the GPL. 30 # 31 # Contributers: 32 # Robert Ginda <rginda (at] netscape.com> 33 # 34 # Second cut at runtests.pl script originally by 35 # Christine Begle (cbegle (at] netscape.com) 36 # Branched 11/01/99 37 # 38 39 use strict; 40 use Getopt::Mixed "nextOption"; 41 42 my $os_type = &get_os_type; 43 my $unixish = (($os_type ne "WIN") && ($os_type ne "MAC")); 44 my $path_sep = ($os_type eq "MAC") ? ":" : "/"; 45 my $win_sep = ($os_type eq "WIN")? &get_win_sep : ""; 46 my $redirect_command = ($os_type ne "MAC") ? " 2>&1" : ""; 47 48 # command line option defaults 49 my $opt_suite_path; 50 my $opt_trace = 0; 51 my $opt_classpath = ""; 52 my $opt_rhino_opt = 0; 53 my $opt_rhino_ms = 0; 54 my @opt_engine_list; 55 my $opt_engine_type = ""; 56 my $opt_engine_params = ""; 57 my $opt_user_output_file = 0; 58 my $opt_output_file = ""; 59 my @opt_test_list_files; 60 my @opt_neg_list_files; 61 my $opt_shell_path = ""; 62 my $opt_java_path = ""; 63 my $opt_bug_url = "http://bugzilla.mozilla.org/show_bug.cgi?id="; 64 my $opt_console_failures = 0; 65 my $opt_lxr_url = "./"; # "http://lxr.mozilla.org/mozilla/source/js/tests/"; 66 my $opt_exit_munge = ($os_type ne "MAC") ? 1 : 0; 67 my $opt_arch= ""; 68 69 # command line option definition 70 my $options = "a=s arch>a b=s bugurl>b c=s classpath>c e=s engine>e f=s file>f " . 71 "h help>h i j=s javapath>j k confail>k l=s list>l L=s neglist>L " . 72 "o=s opt>o p=s testpath>p s=s shellpath>s t trace>t u=s lxrurl>u " . 73 "x noexitmunge>x"; 74 75 if ($os_type eq "MAC") { 76 $opt_suite_path = `directory`; 77 $opt_suite_path =~ s/[\n\r]//g; 78 $opt_suite_path .= ":"; 79 } else { 80 $opt_suite_path = "./"; 81 } 82 83 &parse_args; 84 85 my $user_exit = 0; 86 my ($engine_command, $html, $failures_reported, $tests_completed, 87 $exec_time_string); 88 my @failed_tests; 89 my @test_list = &get_test_list; 90 91 if ($#test_list == -1) { 92 die ("Nothing to test.\n"); 93 } 94 95 if ($unixish) { 96 # on unix, ^C pauses the tests, and gives the user a chance to quit but 97 # report on what has been done, to just quit, or to continue (the 98 # interrupted test will still be skipped.) 99 # windows doesn't handle the int handler they way we want it to, 100 # so don't even pretend to let the user continue. 101 $SIG{INT} = 'int_handler'; 102 } 103 104 &main; 105 106 #End. 107 108 sub main { 109 my $start_time; 110 111 while ($opt_engine_type = pop (@opt_engine_list)) { 112 dd ("Testing engine '$opt_engine_type'"); 113 114 $engine_command = &get_engine_command; 115 $html = ""; 116 @failed_tests = (); 117 $failures_reported = 0; 118 $tests_completed = 0; 119 $start_time = time; 120 121 122 &execute_tests (@test_list); 123 124 my $exec_time = (time - $start_time); 125 my $exec_hours = int($exec_time / 60 / 60); 126 $exec_time -= $exec_hours * 60 * 60; 127 my $exec_mins = int($exec_time / 60); 128 $exec_time -= $exec_mins * 60; 129 my $exec_secs = ($exec_time % 60); 130 131 if ($exec_hours > 0) { 132 $exec_time_string = "$exec_hours hours, $exec_mins minutes, " . 133 "$exec_secs seconds"; 134 } elsif ($exec_mins > 0) { 135 $exec_time_string = "$exec_mins minutes, $exec_secs seconds"; 136 } else { 137 $exec_time_string = "$exec_secs seconds"; 138 } 139 140 if (!$opt_user_output_file) { 141 $opt_output_file = &get_tempfile_name; 142 } 143 144 &write_results; 145 146 } 147 } 148 149 sub execute_tests { 150 my (@test_list) = @_; 151 my ($test, $shell_command, $line, @output, $path); 152 my $file_param = " -f "; 153 my ($last_suite, $last_test_dir); 154 155 # Don't run any shell.js files as tests; they are only utility files 156 @test_list = grep (!/shell\.js$/, @test_list); 157 158 &status ("Executing " . ($#test_list + 1) . " test(s)."); 159 foreach $test (@test_list) { 160 my ($suite, $test_dir, $test_file) = split($path_sep, $test); 161 # *-n.js is a negative test, expect exit code 3 (runtime error) 162 my $expected_exit = ($test =~ /\-n\.js$/) ? 3 : 0; 163 my ($got_exit, $exit_signal); 164 my $failure_lines; 165 my $bug_number; 166 my $status_lines; 167 168 # user selected [Q]uit from ^C handler. 169 if ($user_exit) { 170 return; 171 } 172 173 # Append the shell.js files to the shell_command if they're there. 174 # (only check for their existance if the suite or test_dir has changed 175 # since the last time we looked.) 176 if ($last_suite ne $suite || $last_test_dir ne $test_dir) { 177 $shell_command = $opt_arch . " "; 178 179 $shell_command .= &xp_path($engine_command) . " -s "; 180 181 $path = &xp_path($opt_suite_path . $suite . "/shell.js"); 182 if (-f $path) { 183 $shell_command .= $file_param . $path; 184 } 185 186 $path = &xp_path($opt_suite_path . $suite . "/" . 187 $test_dir . "/shell.js"); 188 if (-f $path) { 189 $shell_command .= $file_param . $path; 190 } 191 192 $last_suite = $suite; 193 $last_test_dir = $test_dir; 194 } 195 196 $path = &xp_path($opt_suite_path . $test); 197 198 print ($shell_command . $file_param . $path . "\n"); 199 &dd ("executing: " . $shell_command . $file_param . $path); 200 201 open (OUTPUT, $shell_command . $file_param . $path . 202 $redirect_command . " |"); 203 @output = <OUTPUT>; 204 close (OUTPUT); 205 206 @output = grep (!/js\>/, @output); 207 208 if ($opt_exit_munge == 1) { 209 # signal information in the lower 8 bits, exit code above that 210 $got_exit = ($? >> 8); 211 $exit_signal = ($? & 255); 212 } else { 213 # user says not to munge the exit code 214 $got_exit = $?; 215 $exit_signal = 0; 216 } 217 218 $failure_lines = ""; 219 $bug_number = ""; 220 $status_lines = ""; 221 222 foreach $line (@output) { 223 224 # watch for testcase to proclaim what exit code it expects to 225 # produce (0 by default) 226 if ($line =~ /expect(ed)?\s*exit\s*code\s*\:?\s*(\d+)/i) { 227 $expected_exit = $2; 228 &dd ("Test case expects exit code $expected_exit"); 229 } 230 231 # watch for failures 232 if ($line =~ /failed!/i) { 233 $failure_lines .= $line; 234 } 235 236 # and watch for bugnumbers 237 # XXX This only allows 1 bugnumber per testfile, should be 238 # XXX modified to allow for multiple. 239 if ($line =~ /bugnumber\s*\:?\s*(.*)/i) { 240 $1 =~ /(\n+)/; 241 $bug_number = $1; 242 } 243 244 # and watch for status 245 if ($line =~ /status/i) { 246 $status_lines .= $line; 247 } 248 249 } 250 251 if (!@output) { 252 @output = ("Testcase produced no output!"); 253 } 254 255 if ($got_exit != $expected_exit) { 256 # full testcase output dumped on mismatched exit codes, 257 &report_failure ($test, "Expected exit code " . 258 "$expected_exit, got $got_exit\n" . 259 "Testcase terminated with signal $exit_signal\n" . 260 "Complete testcase output was:\n" . 261 join ("\n",@output), $bug_number); 262 } elsif ($failure_lines) { 263 # only offending lines if exit codes matched 264 &report_failure ($test, "$status_lines\n". 265 "Failure messages were:\n$failure_lines", 266 $bug_number); 267 } 268 269 &dd ("exit code $got_exit, exit signal $exit_signal."); 270 271 $tests_completed++; 272 } 273 } 274 275 sub write_results { 276 my ($list_name, $neglist_name); 277 my $completion_date = localtime; 278 my $failure_pct = int(($failures_reported / $tests_completed) * 10000) / 279 100; 280 &dd ("Writing output to $opt_output_file."); 281 282 if ($#opt_test_list_files == -1) { 283 $list_name = "All tests"; 284 } elsif ($#opt_test_list_files < 10) { 285 $list_name = join (", ", @opt_test_list_files); 286 } else { 287 $list_name = "($#opt_test_list_files test files specified)"; 288 } 289 290 if ($#opt_neg_list_files == -1) { 291 $neglist_name = "(none)"; 292 } elsif ($#opt_test_list_files < 10) { 293 $neglist_name = join (", ", @opt_neg_list_files); 294 } else { 295 $neglist_name = "($#opt_neg_list_files skip files specified)"; 296 } 297 298 open (OUTPUT, "> $opt_output_file") || 299 die ("Could not create output file $opt_output_file"); 300 301 print OUTPUT 302 ("<html><head>\n" . 303 "<title>Test results, $opt_engine_type</title>\n" . 304 "</head>\n" . 305 "<body bgcolor='white'>\n" . 306 "<a name='tippy_top'></a>\n" . 307 "<h2>Test results, $opt_engine_type</h2><br>\n" . 308 "<p class='results_summary'>\n" . 309 "Test List: $list_name<br>\n" . 310 "Skip List: $neglist_name<br>\n" . 311 ($#test_list + 1) . " test(s) selected, $tests_completed test(s) " . 312 "completed, $failures_reported failures reported " . 313 "($failure_pct% failed)<br>\n" . 314 "Engine command line: $engine_command<br>\n" . 315 "OS type: $os_type<br>\n"); 316 317 if ($opt_engine_type =~ /^rhino/) { 318 open (JAVAOUTPUT, $opt_java_path . "java -fullversion " . 319 $redirect_command . " |"); 320 print OUTPUT <JAVAOUTPUT>; 321 print OUTPUT "<BR>"; 322 close (JAVAOUTPUT); 323 } 324 325 print OUTPUT 326 ("Testcase execution time: $exec_time_string.<br>\n" . 327 "Tests completed on $completion_date.<br><br>\n"); 328 329 if ($failures_reported > 0) { 330 print OUTPUT 331 ("[ <a href='#fail_detail'>Failure Details</a> | " . 332 "<a href='#retest_list'>Retest List</a> | " . 333 "<a href='menu.html'>Test Selection Page</a> ]<br>\n" . 334 "<hr>\n" . 335 "<a name='fail_detail'></a>\n" . 336 "<h2>Failure Details</h2><br>\n<dl>" . 337 $html . 338 "</dl>\n[ <a href='#tippy_top'>Top of Page</a> | " . 339 "<a href='#fail_detail'>Top of Failures</a> ]<br>\n" . 340 "<hr>\n<pre>\n" . 341 "<a name='retest_list'></a>\n" . 342 "<h2>Retest List</h2><br>\n" . 343 "# Retest List, $opt_engine_type, " . 344 "generated $completion_date.\n" . 345 "# Original test base was: $list_name.\n" . 346 "# $tests_completed of " . ($#test_list + 1) . 347 " test(s) were completed, " . 348 "$failures_reported failures reported.\n" . 349 join ("\n", @failed_tests) ); 350 #"</pre>\n" . 351 # "[ <a href='#tippy_top'>Top of Page</a> | " . 352 # "<a href='#retest_list'>Top of Retest List</a> ]<br>\n"); 353 } else { 354 print OUTPUT 355 ("<h1>Whoop-de-doo, nothing failed!</h1>\n"); 356 } 357 358 #print OUTPUT "</body>"; 359 360 close (OUTPUT); 361 362 &status ("Wrote results to '$opt_output_file'."); 363 364 if ($opt_console_failures) { 365 &status ("$failures_reported test(s) failed"); 366 } 367 368 } 369 370 sub parse_args { 371 my ($option, $value, $lastopt); 372 373 &dd ("checking command line options."); 374 375 Getopt::Mixed::init ($options); 376 $Getopt::Mixed::order = $Getopt::Mixed::RETURN_IN_ORDER; 377 378 while (($option, $value) = nextOption()) { 379 380 if ($option eq "a") { 381 &dd ("opt: running with architecture $value."); 382 $value =~ s/^ //; 383 $opt_arch = "arch -$value"; 384 385 } elsif ($option eq "b") { 386 &dd ("opt: setting bugurl to '$value'."); 387 $opt_bug_url = $value; 388 389 } elsif ($option eq "c") { 390 &dd ("opt: setting classpath to '$value'."); 391 $opt_classpath = $value; 392 393 } elsif (($option eq "e") || (($option eq "") && ($lastopt eq "e"))) { 394 &dd ("opt: adding engine $value."); 395 push (@opt_engine_list, $value); 396 397 } elsif ($option eq "f") { 398 if (!$value) { 399 die ("Output file cannot be null.\n"); 400 } 401 &dd ("opt: setting output file to '$value'."); 402 $opt_user_output_file = 1; 403 $opt_output_file = $value; 404 405 } elsif ($option eq "h") { 406 &usage; 407 408 } elsif ($option eq "j") { 409 if (!($value =~ /[\/\\]$/)) { 410 $value .= "/"; 411 } 412 &dd ("opt: setting java path to '$value'."); 413 $opt_java_path = $value; 414 415 } elsif ($option eq "k") { 416 &dd ("opt: displaying failures on console."); 417 $opt_console_failures=1; 418 419 } elsif ($option eq "l" || (($option eq "") && ($lastopt eq "l"))) { 420 $option = "l"; 421 &dd ("opt: adding test list '$value'."); 422 push (@opt_test_list_files, $value); 423 424 } elsif ($option eq "L" || (($option eq "") && ($lastopt eq "L"))) { 425 $option = "L"; 426 &dd ("opt: adding negative list '$value'."); 427 push (@opt_neg_list_files, $value); 428 429 } elsif ($option eq "o") { 430 $opt_engine_params = $value; 431 &dd ("opt: setting engine params to '$opt_engine_params'."); 432 433 } elsif ($option eq "p") { 434 $opt_suite_path = $value; 435 436 if ($os_type eq "MAC") { 437 if (!($opt_suite_path =~ /\:$/)) { 438 $opt_suite_path .= ":"; 439 } 440 } else { 441 if (!($opt_suite_path =~ /[\/\\]$/)) { 442 $opt_suite_path .= "/"; 443 } 444 } 445 446 &dd ("opt: setting suite path to '$opt_suite_path'."); 447 448 } elsif ($option eq "s") { 449 $opt_shell_path = $value; 450 &dd ("opt: setting shell path to '$opt_shell_path'."); 451 452 } elsif ($option eq "t") { 453 &dd ("opt: tracing output. (console failures at no extra charge.)"); 454 $opt_console_failures = 1; 455 $opt_trace = 1; 456 457 } elsif ($option eq "u") { 458 &dd ("opt: setting lxr url to '$value'."); 459 $opt_lxr_url = $value; 460 461 } elsif ($option eq "x") { 462 &dd ("opt: turning off exit munging."); 463 $opt_exit_munge = 0; 464 465 } else { 466 &usage; 467 } 468 469 $lastopt = $option; 470 471 } 472 473 Getopt::Mixed::cleanup(); 474 475 if ($#opt_engine_list == -1) { 476 die "You must select a shell to test in.\n"; 477 } 478 479 } 480 481 # 482 # print the arguments that this script expects 483 # 484 sub usage { 485 print STDERR 486 ("\nusage: $0 [<options>] \n" . 487 "(-a|--arch) <arch> run with a specific architecture on mac\n" . 488 "(-b|--bugurl) Bugzilla URL.\n" . 489 " (default is $opt_bug_url)\n" . 490 "(-c|--classpath) Classpath (Rhino only.)\n" . 491 "(-e|--engine) <type> ... Specify the type of engine(s) to test.\n" . 492 " <type> is one or more of\n" . 493 " (squirrelfish|smopt|smdebug|lcopt|lcdebug|xpcshell|" . 494 "rhino|rhinoi|rhinoms|rhinomsi|rhino9|rhinoms9).\n" . 495 "(-f|--file) <file> Redirect output to file named <file>.\n" . 496 " (default is " . 497 "results-<engine-type>-<date-stamp>.html)\n" . 498 "(-h|--help) Print this message.\n" . 499 "(-j|--javapath) Location of java executable.\n" . 500 "(-k|--confail) Log failures to console (also.)\n" . 501 "(-l|--list) <file> ... List of tests to execute.\n" . 502 "(-L|--neglist) <file> ... List of tests to skip.\n" . 503 "(-o|--opt) <options> Options to pass to the JavaScript engine.\n" . 504 " (Make sure to quote them!)\n" . 505 "(-p|--testpath) <path> Root of the test suite. (default is ./)\n" . 506 "(-s|--shellpath) <path> Location of JavaScript shell.\n" . 507 "(-t|--trace) Trace script execution.\n" . 508 "(-u|--lxrurl) <url> Complete URL to tests subdirectory on lxr.\n" . 509 " (default is $opt_lxr_url)\n" . 510 "(-x|--noexitmunge) Don't do exit code munging (try this if it\n" . 511 " seems like your exit codes are turning up\n" . 512 " as exit signals.)\n"); 513 exit (1); 514 515 } 516 517 # 518 # get the shell command used to start the (either) engine 519 # 520 sub get_engine_command { 521 522 my $retval; 523 524 if ($opt_engine_type eq "rhino") { 525 &dd ("getting rhino engine command."); 526 $opt_rhino_opt = 0; 527 $opt_rhino_ms = 0; 528 $retval = &get_rhino_engine_command; 529 } elsif ($opt_engine_type eq "rhinoi") { 530 &dd ("getting rhinoi engine command."); 531 $opt_rhino_opt = -1; 532 $opt_rhino_ms = 0; 533 $retval = &get_rhino_engine_command; 534 } elsif ($opt_engine_type eq "rhino9") { 535 &dd ("getting rhino engine command."); 536 $opt_rhino_opt = 9; 537 $opt_rhino_ms = 0; 538 $retval = &get_rhino_engine_command; 539 } elsif ($opt_engine_type eq "rhinoms") { 540 &dd ("getting rhinoms engine command."); 541 $opt_rhino_opt = 0; 542 $opt_rhino_ms = 1; 543 $retval = &get_rhino_engine_command; 544 } elsif ($opt_engine_type eq "rhinomsi") { 545 &dd ("getting rhinomsi engine command."); 546 $opt_rhino_opt = -1; 547 $opt_rhino_ms = 1; 548 $retval = &get_rhino_engine_command; 549 } elsif ($opt_engine_type eq "rhinoms9") { 550 &dd ("getting rhinomsi engine command."); 551 $opt_rhino_opt = 9; 552 $opt_rhino_ms = 1; 553 $retval = &get_rhino_engine_command; 554 } elsif ($opt_engine_type eq "xpcshell") { 555 &dd ("getting xpcshell engine command."); 556 $retval = &get_xpc_engine_command; 557 } elsif ($opt_engine_type =~ /^lc(opt|debug)$/) { 558 &dd ("getting liveconnect engine command."); 559 $retval = &get_lc_engine_command; 560 } elsif ($opt_engine_type =~ /^sm(opt|debug)$/) { 561 &dd ("getting spidermonkey engine command."); 562 $retval = &get_sm_engine_command; 563 } elsif ($opt_engine_type =~ /^ep(opt|debug)$/) { 564 &dd ("getting epimetheus engine command."); 565 $retval = &get_ep_engine_command; 566 } elsif ($opt_engine_type eq "squirrelfish") { 567 &dd ("getting squirrelfish engine command."); 568 $retval = &get_squirrelfish_engine_command; 569 } else { 570 die ("Unknown engine type selected, '$opt_engine_type'.\n"); 571 } 572 573 $retval .= " $opt_engine_params"; 574 575 &dd ("got '$retval'"); 576 577 return $retval; 578 579 } 580 581 # 582 # get the shell command used to run rhino 583 # 584 sub get_rhino_engine_command { 585 my $retval = $opt_java_path . ($opt_rhino_ms ? "jview " : "java "); 586 587 if ($opt_shell_path) { 588 $opt_classpath = ($opt_classpath) ? 589 $opt_classpath . ":" . $opt_shell_path : 590 $opt_shell_path; 591 } 592 593 if ($opt_classpath) { 594 $retval .= ($opt_rhino_ms ? "/cp:p" : "-classpath") . " $opt_classpath "; 595 } 596 597 $retval .= "org.mozilla.javascript.tools.shell.Main"; 598 599 if ($opt_rhino_opt) { 600 $retval .= " -opt $opt_rhino_opt"; 601 } 602 603 return $retval; 604 605 } 606 607 # 608 # get the shell command used to run xpcshell 609 # 610 sub get_xpc_engine_command { 611 my $retval; 612 my $m5_home = @ENV{"MOZILLA_FIVE_HOME"} || 613 die ("You must set MOZILLA_FIVE_HOME to use the xpcshell" , 614 (!$unixish) ? "." : ", also " . 615 "setting LD_LIBRARY_PATH to the same directory may get rid of " . 616 "any 'library not found' errors.\n"); 617 618 if (($unixish) && (!@ENV{"LD_LIBRARY_PATH"})) { 619 print STDERR "-#- WARNING: LD_LIBRARY_PATH is not set, xpcshell may " . 620 "not be able to find the required components.\n"; 621 } 622 623 if (!($m5_home =~ /[\/\\]$/)) { 624 $m5_home .= "/"; 625 } 626 627 $retval = $m5_home . "xpcshell"; 628 629 if ($os_type eq "WIN") { 630 $retval .= ".exe"; 631 } 632 633 $retval = &xp_path($retval); 634 635 if (($os_type ne "MAC") && !(-x $retval)) { 636 # mac doesn't seem to deal with -x correctly 637 die ($retval . " is not a valid executable on this system.\n"); 638 } 639 640 return $retval; 641 642 } 643 644 # 645 # get the shell command used to run squirrelfish 646 # 647 sub get_squirrelfish_engine_command { 648 my $retval; 649 650 if ($opt_shell_path) { 651 # FIXME: Quoting the path this way won't work with paths with quotes in 652 # them. A better fix would be to use the multi-parameter version of 653 # open(), but that doesn't work on ActiveState Perl. 654 $retval = "\"" . $opt_shell_path . "\""; 655 } else { 656 die "Please specify a full path to the squirrelfish testing engine"; 657 } 658 659 return $retval; 660 } 661 662 # 663 # get the shell command used to run spidermonkey 664 # 665 sub get_sm_engine_command { 666 my $retval; 667 668 # Look for Makefile.ref style make first. 669 # (On Windows, spidermonkey can be made by two makefiles, each putting the 670 # executable in a diferent directory, under a different name.) 671 672 if ($opt_shell_path) { 673 # if the user provided a path to the shell, return that. 674 $retval = $opt_shell_path; 675 676 } else { 677 678 if ($os_type eq "MAC") { 679 $retval = $opt_suite_path . ":src:macbuild:JS"; 680 } else { 681 $retval = $opt_suite_path . "../src/"; 682 opendir (SRC_DIR_FILES, $retval); 683 my @src_dir_files = readdir(SRC_DIR_FILES); 684 closedir (SRC_DIR_FILES); 685 686 my ($dir, $object_dir); 687 my $pattern = ($opt_engine_type eq "smdebug") ? 688 'DBG.OBJ' : 'OPT.OBJ'; 689 690 # scan for the first directory matching 691 # the pattern expected to hold this type (debug or opt) of engine 692 foreach $dir (@src_dir_files) { 693 if ($dir =~ $pattern) { 694 $object_dir = $dir; 695 last; 696 } 697 } 698 699 if (!$object_dir && $os_type ne "WIN") { 700 die ("Could not locate an object directory in $retval " . 701 "matching the pattern *$pattern. Have you built the " . 702 "engine?\n"); 703 } 704 705 if (!(-x $retval . $object_dir . "/js.exe") && ($os_type eq "WIN")) { 706 # On windows, you can build with js.mak as well as Makefile.ref 707 # (Can you say WTF boys and girls? I knew you could.) 708 # So, if the exe the would have been built by Makefile.ref isn't 709 # here, check for the js.mak version before dying. 710 if ($opt_shell_path) { 711 $retval = $opt_shell_path; 712 if (!($retval =~ /[\/\\]$/)) { 713 $retval .= "/"; 714 } 715 } else { 716 if ($opt_engine_type eq "smopt") { 717 $retval = "../src/Release/"; 718 } else { 719 $retval = "../src/Debug/"; 720 } 721 } 722 723 $retval .= "jsshell.exe"; 724 725 } else { 726 $retval .= $object_dir . "/js"; 727 if ($os_type eq "WIN") { 728 $retval .= ".exe"; 729 } 730 } 731 } # mac/ not mac 732 733 $retval = &xp_path($retval); 734 735 } # (user provided a path) 736 737 738 if (($os_type ne "MAC") && !(-x $retval)) { 739 # mac doesn't seem to deal with -x correctly 740 die ($retval . " is not a valid executable on this system.\n"); 741 } 742 743 return $retval; 744 745 } 746 747 # 748 # get the shell command used to run epimetheus 749 # 750 sub get_ep_engine_command { 751 my $retval; 752 753 if ($opt_shell_path) { 754 # if the user provided a path to the shell, return that - 755 $retval = $opt_shell_path; 756 757 } else { 758 my $dir; 759 my $os; 760 my $debug; 761 my $opt; 762 my $exe; 763 764 $dir = $opt_suite_path . "../../js2/src/"; 765 766 if ($os_type eq "MAC") { 767 # 768 # On the Mac, the debug and opt builds lie in the same directory - 769 # 770 $os = "macbuild:"; 771 $debug = ""; 772 $opt = ""; 773 $exe = "JS2"; 774 } elsif ($os_type eq "WIN") { 775 $os = "winbuild/Epimetheus/"; 776 $debug = "Debug/"; 777 $opt = "Release/"; 778 $exe = "Epimetheus.exe"; 779 } else { 780 $os = ""; 781 $debug = ""; 782 $opt = ""; # <<<----- XXX THIS IS NOT RIGHT! CHANGE IT! 783 $exe = "epimetheus"; 784 } 785 786 787 if ($opt_engine_type eq "epdebug") { 788 $retval = $dir . $os . $debug . $exe; 789 } else { 790 $retval = $dir . $os . $opt . $exe; 791 } 792 793 $retval = &xp_path($retval); 794 795 }# (user provided a path) 796 797 798 if (($os_type ne "MAC") && !(-x $retval)) { 799 # mac doesn't seem to deal with -x correctly 800 die ($retval . " is not a valid executable on this system.\n"); 801 } 802 803 return $retval; 804 } 805 806 # 807 # get the shell command used to run the liveconnect shell 808 # 809 sub get_lc_engine_command { 810 my $retval; 811 812 if ($opt_shell_path) { 813 $retval = $opt_shell_path; 814 } else { 815 if ($os_type eq "MAC") { 816 die "Don't know how to run the lc shell on the mac yet.\n"; 817 } else { 818 $retval = $opt_suite_path . "../src/liveconnect/"; 819 opendir (SRC_DIR_FILES, $retval); 820 my @src_dir_files = readdir(SRC_DIR_FILES); 821 closedir (SRC_DIR_FILES); 822 823 my ($dir, $object_dir); 824 my $pattern = ($opt_engine_type eq "lcdebug") ? 825 'DBG.OBJ' : 'OPT.OBJ'; 826 827 foreach $dir (@src_dir_files) { 828 if ($dir =~ $pattern) { 829 $object_dir = $dir; 830 last; 831 } 832 } 833 834 if (!$object_dir) { 835 die ("Could not locate an object directory in $retval " . 836 "matching the pattern *$pattern. Have you built the " . 837 "engine?\n"); 838 } 839 840 $retval .= $object_dir . "/"; 841 842 if ($os_type eq "WIN") { 843 $retval .= "lcshell.exe"; 844 } else { 845 $retval .= "lcshell"; 846 } 847 } # mac/ not mac 848 849 $retval = &xp_path($retval); 850 851 } # (user provided a path) 852 853 854 if (($os_type ne "MAC") && !(-x $retval)) { 855 # mac doesn't seem to deal with -x correctly 856 die ("$retval is not a valid executable on this system.\n"); 857 } 858 859 return $retval; 860 861 } 862 863 sub get_os_type { 864 865 if ("\n" eq "\015") { 866 return "MAC"; 867 } 868 869 my $uname = `uname -a`; 870 871 if ($uname =~ /WIN/) { 872 $uname = "WIN"; 873 } else { 874 chop $uname; 875 } 876 877 &dd ("get_os_type returning '$uname'."); 878 return $uname; 879 880 } 881 882 sub get_test_list { 883 my @test_list; 884 my @neg_list; 885 886 if ($#opt_test_list_files > -1) { 887 my $list_file; 888 889 &dd ("getting test list from user specified source."); 890 891 foreach $list_file (@opt_test_list_files) { 892 push (@test_list, &expand_user_test_list($list_file)); 893 } 894 } else { 895 &dd ("no list file, groveling in '$opt_suite_path'."); 896 897 @test_list = &get_default_test_list($opt_suite_path); 898 } 899 900 if ($#opt_neg_list_files > -1) { 901 my $list_file; 902 my $orig_size = $#test_list + 1; 903 my $actually_skipped; 904 905 &dd ("getting negative list from user specified source."); 906 907 foreach $list_file (@opt_neg_list_files) { 908 push (@neg_list, &expand_user_test_list($list_file)); 909 } 910 911 @test_list = &subtract_arrays (\@test_list, \@neg_list); 912 913 $actually_skipped = $orig_size - ($#test_list + 1); 914 915 &dd ($actually_skipped . " of " . $orig_size . 916 " tests will be skipped."); 917 &dd ((($#neg_list + 1) - $actually_skipped) . " skip tests were " . 918 "not actually part of the test list."); 919 920 921 } 922 923 return @test_list; 924 925 } 926 927 # 928 # reads $list_file, storing non-comment lines into an array. 929 # lines in the form suite_dir/[*] or suite_dir/test_dir/[*] are expanded 930 # to include all test files under the specified directory 931 # 932 sub expand_user_test_list { 933 my ($list_file) = @_; 934 my @retval = (); 935 936 # 937 # Trim off the leading path separator that begins relative paths on the Mac. 938 # Each path will get concatenated with $opt_suite_path, which ends in one. 939 # 940 # Also note: 941 # 942 # We will call expand_test_list_entry(), which does pattern-matching on $list_file. 943 # This will make the pattern-matching the same as it would be on Linux/Windows - 944 # 945 if ($os_type eq "MAC") { 946 $list_file =~ s/^$path_sep//; 947 } 948 949 if ($list_file =~ /\.js$/ || -d $opt_suite_path . $list_file) { 950 951 push (@retval, &expand_test_list_entry($list_file)); 952 953 } else { 954 955 open (TESTLIST, $list_file) || 956 die("Error opening test list file '$list_file': $!\n"); 957 958 while (<TESTLIST>) { 959 s/\r*\n*$//; 960 if (!(/\s*\#/)) { 961 # It's not a comment, so process it 962 push (@retval, &expand_test_list_entry($_)); 963 } 964 } 965 966 close (TESTLIST); 967 968 } 969 970 return @retval; 971 972 } 973 974 975 # 976 # Currently expect all paths to be RELATIVE to the top-level tests directory. 977 # One day, this should be improved to allow absolute paths as well - 978 # 979 sub expand_test_list_entry { 980 my ($entry) = @_; 981 my @retval; 982 983 if ($entry =~ /\.js$/) { 984 # it's a regular entry, add it to the list 985 if (-f $opt_suite_path . $entry) { 986 push (@retval, $entry); 987 } else { 988 status ("testcase '$entry' not found."); 989 } 990 } elsif ($entry =~ /(.*$path_sep[^\*][^$path_sep]*)$path_sep?\*?$/) { 991 # Entry is in the form suite_dir/test_dir[/*] 992 # so iterate all tests under it 993 my $suite_and_test_dir = $1; 994 my @test_files = &get_js_files ($opt_suite_path . 995 $suite_and_test_dir); 996 my $i; 997 998 foreach $i (0 .. $#test_files) { 999 $test_files[$i] = $suite_and_test_dir . $path_sep . 1000 $test_files[$i]; 1001 } 1002 1003 splice (@retval, $#retval + 1, 0, @test_files); 1004 1005 } elsif ($entry =~ /([^\*][^$path_sep]*)$path_sep?\*?$/) { 1006 # Entry is in the form suite_dir[/*] 1007 # so iterate all test dirs and tests under it 1008 my $suite = $1; 1009 my @test_dirs = &get_subdirs ($opt_suite_path . $suite); 1010 my $test_dir; 1011 1012 foreach $test_dir (@test_dirs) { 1013 my @test_files = &get_js_files ($opt_suite_path . $suite . 1014 $path_sep . $test_dir); 1015 my $i; 1016 1017 foreach $i (0 .. $#test_files) { 1018 $test_files[$i] = $suite . $path_sep . $test_dir . $path_sep . 1019 $test_files[$i]; 1020 } 1021 1022 splice (@retval, $#retval + 1, 0, @test_files); 1023 } 1024 1025 } else { 1026 die ("Dont know what to do with list entry '$entry'.\n"); 1027 } 1028 1029 return @retval; 1030 1031 } 1032 1033 # 1034 # Grovels through $suite_path, searching for *all* test files. Used when the 1035 # user doesn't supply a test list. 1036 # 1037 sub get_default_test_list { 1038 my ($suite_path) = @_; 1039 my @suite_list = &get_subdirs($suite_path); 1040 my $suite; 1041 my @retval; 1042 1043 foreach $suite (@suite_list) { 1044 my @test_dir_list = get_subdirs ($suite_path . $suite); 1045 my $test_dir; 1046 1047 foreach $test_dir (@test_dir_list) { 1048 my @test_list = get_js_files ($suite_path . $suite . $path_sep . 1049 $test_dir); 1050 my $test; 1051 1052 foreach $test (@test_list) { 1053 $retval[$#retval + 1] = $suite . $path_sep . $test_dir . 1054 $path_sep . $test; 1055 } 1056 } 1057 } 1058 1059 return @retval; 1060 1061 } 1062 1063 # 1064 # generate an output file name based on the date 1065 # 1066 sub get_tempfile_name { 1067 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = 1068 &get_padded_time (localtime); 1069 my $rv; 1070 1071 if ($os_type ne "MAC") { 1072 $rv = "results-" . $year . "-" . $mon . "-" . $mday . "-" . $hour . 1073 $min . $sec . "-" . $opt_engine_type; 1074 } else { 1075 $rv = "res-" . $year . $mon . $mday . $hour . $min . $sec . "-" . 1076 $opt_engine_type 1077 } 1078 1079 return $rv . ".html"; 1080 } 1081 1082 sub get_padded_time { 1083 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = @_; 1084 1085 $mon++; 1086 $mon = &zero_pad($mon); 1087 $year += 1900; 1088 $mday= &zero_pad($mday); 1089 $sec = &zero_pad($sec); 1090 $min = &zero_pad($min); 1091 $hour = &zero_pad($hour); 1092 1093 return ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); 1094 1095 } 1096 1097 sub zero_pad { 1098 my ($string) = @_; 1099 1100 $string = ($string < 10) ? "0" . $string : $string; 1101 return $string; 1102 } 1103 1104 sub subtract_arrays { 1105 my ($whole_ref, $part_ref) = @_; 1106 my @whole = @$whole_ref; 1107 my @part = @$part_ref; 1108 my $line; 1109 1110 foreach $line (@part) { 1111 @whole = grep (!/$line/, @whole); 1112 } 1113 1114 return @whole; 1115 1116 } 1117 1118 # 1119 # Convert unix path to mac style. 1120 # 1121 sub unix_to_mac { 1122 my ($path) = @_; 1123 my @path_elements = split ("/", $path); 1124 my $rv = ""; 1125 my $i; 1126 1127 foreach $i (0 .. $#path_elements) { 1128 if ($path_elements[$i] eq ".") { 1129 if (!($rv =~ /\:$/)) { 1130 $rv .= ":"; 1131 } 1132 } elsif ($path_elements[$i] eq "..") { 1133 if (!($rv =~ /\:$/)) { 1134 $rv .= "::"; 1135 } else { 1136 $rv .= ":"; 1137 } 1138 } elsif ($path_elements[$i] ne "") { 1139 $rv .= $path_elements[$i] . ":"; 1140 } 1141 1142 } 1143 1144 $rv =~ s/\:$//; 1145 1146 return $rv; 1147 } 1148 1149 # 1150 # Convert unix path to win style. 1151 # 1152 sub unix_to_win { 1153 my ($path) = @_; 1154 1155 if ($path_sep ne $win_sep) { 1156 $path =~ s/$path_sep/$win_sep/g; 1157 } 1158 1159 return $path; 1160 } 1161 1162 # 1163 # Windows shells require "/" or "\" as path separator. 1164 # Find out the one used in the current Windows shell. 1165 # 1166 sub get_win_sep { 1167 my $path = $ENV{"PATH"} || $ENV{"Path"} || $ENV{"path"}; 1168 $path =~ /\\|\//; 1169 return $&; 1170 } 1171 1172 # 1173 # Convert unix path to correct style based on platform. 1174 # 1175 sub xp_path { 1176 my ($path) = @_; 1177 1178 if ($os_type eq "MAC") { 1179 return &unix_to_mac($path); 1180 } elsif($os_type eq "WIN") { 1181 return &unix_to_win($path); 1182 } else { 1183 return $path; 1184 } 1185 } 1186 1187 sub numericcmp($$) 1188 { 1189 my ($aa, $bb) = @_; 1190 1191 my @a = split /(\d+)/, $aa; 1192 my @b = split /(\d+)/, $bb; 1193 1194 while (@a && @b) { 1195 my $a = shift @a; 1196 my $b = shift @b; 1197 return $a <=> $b if $a =~ /^\d/ && $b =~ /^\d/ && $a != $b; 1198 return $a cmp $b if $a ne $b; 1199 } 1200 1201 return @a <=> @b; 1202 } 1203 1204 # 1205 # given a directory, return an array of all subdirectories 1206 # 1207 sub get_subdirs { 1208 my ($dir) = @_; 1209 my @subdirs; 1210 1211 if ($os_type ne "MAC") { 1212 if (!($dir =~ /\/$/)) { 1213 $dir = $dir . "/"; 1214 } 1215 } else { 1216 if (!($dir =~ /\:$/)) { 1217 $dir = $dir . ":"; 1218 } 1219 } 1220 opendir (DIR, $dir) || die ("couldn't open directory $dir: $!"); 1221 my @testdir_contents = sort numericcmp readdir(DIR); 1222 closedir(DIR); 1223 1224 foreach (@testdir_contents) { 1225 if ((-d ($dir . $_)) && ($_ ne 'CVS') && ($_ ne '.') && ($_ ne '..')) { 1226 @subdirs[$#subdirs + 1] = $_; 1227 } 1228 } 1229 1230 return @subdirs; 1231 } 1232 1233 # 1234 # given a directory, return an array of all the js files that are in it. 1235 # 1236 sub get_js_files { 1237 my ($test_subdir) = @_; 1238 my (@js_file_array, @subdir_files); 1239 1240 opendir (TEST_SUBDIR, $test_subdir) || die ("couldn't open directory " . 1241 "$test_subdir: $!"); 1242 @subdir_files = sort numericcmp readdir(TEST_SUBDIR); 1243 closedir( TEST_SUBDIR ); 1244 1245 foreach (@subdir_files) { 1246 if ($_ =~ /\.js$/) { 1247 $js_file_array[$#js_file_array+1] = $_; 1248 } 1249 } 1250 1251 return @js_file_array; 1252 } 1253 1254 sub report_failure { 1255 my ($test, $message, $bug_number) = @_; 1256 my $bug_line = ""; 1257 1258 $failures_reported++; 1259 1260 $message =~ s/\n+/\n/g; 1261 $test =~ s/\:/\//g; 1262 1263 if ($opt_console_failures) { 1264 if($bug_number) { 1265 print STDERR ("*-* Testcase $test failed:\nBug Number $bug_number". 1266 "\n$message\n"); 1267 } else { 1268 print STDERR ("*-* Testcase $test failed:\n$message\n"); 1269 } 1270 } 1271 1272 $message =~ s/\n/<br>\n/g; 1273 $html .= "<a name='failure$failures_reported'></a>"; 1274 1275 if ($bug_number) { 1276 $bug_line = "<a href='$opt_bug_url$bug_number' target='other_window'>". 1277 "Bug Number $bug_number</a>"; 1278 } 1279 1280 if ($opt_lxr_url) { 1281 $test =~ /\/?([^\/]+\/[^\/]+\/[^\/]+)$/; 1282 $test = $1; 1283 $html .= "<dd><b>". 1284 "Testcase <a target='other_window' href='$opt_lxr_url$test'>$1</a> " . 1285 "failed</b> $bug_line<br>\n"; 1286 } else { 1287 $html .= "<dd><b>". 1288 "Testcase $test failed</b> $bug_line<br>\n"; 1289 } 1290 1291 $html .= " [ "; 1292 if ($failures_reported > 1) { 1293 $html .= "<a href='#failure" . ($failures_reported - 1) . "'>" . 1294 "Previous Failure</a> | "; 1295 } 1296 1297 $html .= "<a href='#failure" . ($failures_reported + 1) . "'>" . 1298 "Next Failure</a> | " . 1299 "<a href='#tippy_top'>Top of Page</a> ]<br>\n" . 1300 "<tt>$message</tt><br>\n"; 1301 1302 @failed_tests[$#failed_tests + 1] = $test; 1303 1304 } 1305 1306 sub dd { 1307 1308 if ($opt_trace) { 1309 print ("-*- ", @_ , "\n"); 1310 } 1311 1312 } 1313 1314 sub status { 1315 1316 print ("-#- ", @_ , "\n"); 1317 1318 } 1319 1320 sub int_handler { 1321 my $resp; 1322 1323 do { 1324 print ("\n*** User Break: Just [Q]uit, Quit and [R]eport, [C]ontinue ?"); 1325 $resp = <STDIN>; 1326 } until ($resp =~ /[QqRrCc]/); 1327 1328 if ($resp =~ /[Qq]/) { 1329 print ("User Exit. No results were generated.\n"); 1330 exit 1; 1331 } elsif ($resp =~ /[Rr]/) { 1332 $user_exit = 1; 1333 } 1334 1335 } 1336