1# -*- cperl -*- 2# Copyright (c) 2004, 2021, Oracle and/or its affiliates. 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License, version 2.0, 6# as published by the Free Software Foundation. 7# 8# This program is also distributed with certain software (including 9# but not limited to OpenSSL) that is licensed under separate terms, 10# as designated in a particular file or component or in included license 11# documentation. The authors of MySQL hereby grant you an additional 12# permission to link the program and your derivative works with the 13# separately licensed software that they have included with MySQL. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License, version 2.0, for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program; if not, write to the Free Software 22# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 24# This is a library file used by the Perl version of mysql-test-run, 25# and is part of the translation of the Bourne shell script with the 26# same name. 27 28package mtr_report; 29use strict; 30 31use base qw(Exporter); 32our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line 33 mtr_print_header mtr_report mtr_report_stats 34 mtr_warning mtr_error mtr_debug mtr_verbose 35 mtr_verbose_restart mtr_report_test_passed 36 mtr_report_test_skipped mtr_print 37 mtr_report_test isotime mtr_summary_file_init mtr_xml_init); 38 39use mtr_match; 40use File::Spec; 41use My::Constants; 42use My::Platform; 43use POSIX qw(_exit floor); 44use IO::Handle qw[ flush ]; 45require "mtr_io.pl"; 46use mtr_results; 47 48my $tot_real_time= 0; 49 50my $done_percentage= 0; 51my $tests_completed= 0; 52 53our $timestamp= 0; 54our $timediff= 0; 55our $name; 56our $verbose; 57our $verbose_restart= 0; 58our $timer= 1; 59 60our $xml_report_file; 61our $summary_report_file; 62 63sub report_option { 64 my ($opt, $value)= @_; 65 66 # Evaluate $opt as string to use "Getopt::Long::Callback legacy API" 67 my $opt_name = "$opt"; 68 69 # Convert - to _ in option name 70 $opt_name =~ s/-/_/g; 71 no strict 'refs'; 72 ${$opt_name}= $value; 73} 74 75sub _name { 76 return $name ? $name." " : undef; 77} 78 79sub _mtr_report_test_name ($$) 80{ 81 my $tinfo= shift; 82 my $done_percentage= shift; 83 84 my $tname= $tinfo->{name}; 85 86 return unless defined $verbose; 87 88 # Add combination name if any 89 $tname.= " '$tinfo->{combination}'" if defined $tinfo->{combination}; 90 91 print _name(). _timestamp(); 92 if($::opt_test_progress) 93 { 94 printf "[%3s%] %-40s ", $done_percentage, $tname ; 95 } 96 else 97 { 98 printf "%-40s ", $tname; 99 } 100 101 my $worker = $tinfo->{worker}; 102 print "w$worker " if defined $worker; 103 104 return $tname; 105} 106 107 108sub mtr_report_test_skipped ($) { 109 my ($tinfo)= @_; 110 $tinfo->{'result'}= 'MTR_RES_SKIPPED'; 111 112 mtr_report_test($tinfo); 113} 114 115 116sub mtr_report_test_passed ($) { 117 my ($tinfo)= @_; 118 119 # Save the timer value 120 my $timer_str= ""; 121 if ( $timer and -f "$::opt_vardir/log/timer" ) 122 { 123 $timer_str= mtr_fromfile("$::opt_vardir/log/timer"); 124 $tinfo->{timer}= $timer_str; 125 resfile_test_info('duration', $timer_str) if $::opt_resfile; 126 } 127 128 # Big warning if status already set 129 if ( $tinfo->{'result'} ){ 130 mtr_warning("mtr_report_test_passed: Test result", 131 "already set to '", $tinfo->{'result'}, ","); 132 } 133 134 $tinfo->{'result'}= 'MTR_RES_PASSED'; 135 136 mtr_report_test($tinfo); 137 138 resfile_global("endtime ", isotime (time)); 139} 140 141 142sub mtr_report_test ($) { 143 my ($tinfo)= @_; 144 145 my $comment= $tinfo->{'comment'}; 146 my $logfile= $tinfo->{'logfile'}; 147 my $warnings= $tinfo->{'warnings'}; 148 my $result= $tinfo->{'result'}; 149 my $retry= $tinfo->{'retries'} ? "retry-" : ""; 150 151 if ($::opt_test_progress) 152 { 153 if ($tinfo->{'name'} && !$retry) 154 { 155 $tests_completed= $tests_completed + 1; 156 $done_percentage= 157 floor(($tests_completed / $::num_tests_for_report) * 100); 158 } 159 } 160 161 my $test_name = _mtr_report_test_name($tinfo, $done_percentage); 162 163 if ($result eq 'MTR_RES_FAILED'){ 164 165 my $timest = format_time(); 166 my $fail = "fail"; 167 168 if ( @$::experimental_test_cases ) 169 { 170 # Find out if this test case is an experimental one, so we can treat 171 # the failure as an expected failure instead of a regression. 172 for my $exp ( @$::experimental_test_cases ) { 173 # Include pattern match for combinations 174 if ( $exp ne $test_name && $test_name !~ /^$exp / ) { 175 # if the expression is not the name of this test case, but has 176 # an asterisk at the end, determine if the characters up to 177 # but excluding the asterisk are the same 178 if ( $exp ne "" && substr($exp, -1, 1) eq "*" ) { 179 my $nexp = substr($exp, 0, length($exp) - 1); 180 if ( substr($test_name, 0, length($nexp)) ne $nexp ) { 181 # no match, try next entry 182 next; 183 } 184 # if yes, fall through to set the exp-fail status 185 } else { 186 # no match, try next entry 187 next; 188 } 189 } 190 $fail = "exp-fail"; 191 $tinfo->{exp_fail}= 1; 192 last; 193 } 194 } 195 196 if ( $warnings ) 197 { 198 mtr_report("[ $retry$fail ] Found warnings/errors in server log file!"); 199 mtr_report(" Test ended at $timest"); 200 mtr_report($warnings); 201 mtr_report("\n$tinfo->{'comment'}"); 202 return; 203 } 204 my $timeout= $tinfo->{'timeout'}; 205 if ( $timeout ) 206 { 207 mtr_report("[ $retry$fail ] timeout after $timeout seconds"); 208 mtr_report(" Test ended at $timest"); 209 mtr_report("\n$tinfo->{'comment'}"); 210 return; 211 } 212 else 213 { 214 mtr_report("[ $retry$fail ]\n Test ended at $timest"); 215 } 216 217 if ( $logfile ) 218 { 219 # Test failure was detected by test tool and its report 220 # about what failed has been saved to file. Display the report. 221 mtr_report("\n$logfile\n"); 222 } 223 if ( $comment ) 224 { 225 # The test failure has been detected by mysql-test-run.pl 226 # when starting the servers or due to other error, the reason for 227 # failing the test is saved in "comment" 228 mtr_report("\n$comment\n"); 229 } 230 231 if ( !$logfile and !$comment ) 232 { 233 # Neither this script or the test tool has recorded info 234 # about why the test has failed. Should be debugged. 235 mtr_report("\nUnknown result, neither 'comment' or 'logfile' set"); 236 } 237 } 238 elsif ($result eq 'MTR_RES_SKIPPED') 239 { 240 if ( $tinfo->{'disable'} ) 241 { 242 mtr_report("[ disabled ] $comment"); 243 } 244 elsif ( $comment ) 245 { 246 mtr_report("[ skipped ] $comment"); 247 } 248 else 249 { 250 mtr_report("[ skipped ]"); 251 } 252 } 253 elsif ($result eq 'MTR_RES_PASSED') 254 { 255 my $timer_str= $tinfo->{timer} || ""; 256 $tot_real_time += ($timer_str/1000); 257 mtr_report("[ ${retry}pass ] ", sprintf("%5s", $timer_str)); 258 259 # Show any problems check-testcase found 260 if ( defined $tinfo->{'check'} ) 261 { 262 mtr_report($tinfo->{'check'}); 263 } 264 } 265} 266 267 268sub mtr_generate_xml_report($) { 269 my ($tests)= @_; 270 my $suite_group; 271 272 # ------------------------------------ 273 # Calculate suitewise/total statistics 274 # ------------------------------------ 275 276 my %suite_stats; 277 my %total_stats; 278 my @stat_list= ('tests', 'failures', 'disabled', 'skipped', 279 'errors', 'unstable', 'time'); 280 281 # Initialize overall statistics 282 map { $total_stats{$_}= 0 } @stat_list; 283 284 # Sort in order to group tests suitewise 285 my @sorted_tests= sort { $a->{'name'} cmp $b->{'name'} } @$tests; 286 287 foreach my $tinfo (@sorted_tests) 288 { 289 my ($tsuite, $tname) = split /\./, $tinfo->{'name'}; 290 291 # Initialize suitewise statistics for new suites 292 if ($tsuite ne $suite_group) 293 { 294 map{ $suite_stats{$tsuite}{$_}= 0 } @stat_list; 295 $suite_group= $tsuite; 296 } 297 298 # Increment test counters 299 $suite_stats{$tsuite}{'tests'}++; 300 $total_stats{'tests'}++; 301 302 # Increment result counters based on outcome of the test 303 if ($tinfo->{'result'} eq 'MTR_RES_UNSTABLE') 304 { 305 $suite_stats{$tsuite}{'unstable'}++; 306 $total_stats{'unstable'}++; 307 } 308 elsif ($tinfo->{'result'} eq 'MTR_RES_FAILED' or $tinfo->{failures}) 309 { 310 $suite_stats{$tsuite}{'failures'}++; 311 $total_stats{'failures'}++; 312 } 313 elsif ($tinfo->{'result'} eq 'MTR_RES_SKIPPED') 314 { 315 if ($tinfo->{'disable'}) 316 { 317 $suite_stats{$tsuite}{'disabled'}++; 318 $total_stats{'disabled'}++; 319 } 320 else 321 { 322 $suite_stats{$tsuite}{'skipped'}++; 323 $total_stats{'skipped'}++; 324 } 325 } 326 327 # Update duration 328 $suite_stats{$tsuite}{'time'}+= $tinfo->{'timer'}; 329 $total_stats{'time'}+= $tinfo->{'timer'}; 330 } 331 332 # -------------------------------------- 333 # Generate xml report from obtained data 334 # -------------------------------------- 335 336 # Convert overall test duration from ms to s 337 $total_stats{'time'}/= 1000.0; 338 339 my $total_unstable_tests= $::opt_report_unstable_tests ? 340 "unstable-tests=\"$total_stats{'unstable'}\" " 341 : ""; 342 343 # Top level xml tag 344 print $xml_report_file "<testsuites tests=\"$total_stats{'tests'}\" ". 345 "failures=\"$total_stats{'failures'}\" ". 346 "$total_unstable_tests". 347 "disabled=\"$total_stats{'disabled'}\" ". 348 "skipped=\"$total_stats{'skipped'}\" ". 349 "errors=\"$total_stats{'errors'}\" ". 350 "time=\"$total_stats{'time'}\" ". 351 "name=\"AllTests\">\n"; 352 353 $suite_group= ""; 354 355 foreach my $tinfo (@sorted_tests) 356 { 357 my ($tsuite, $tname)= split /\./, $tinfo->{'name'}; 358 359 # Put reports in a suite-wise manner 360 if ($tsuite ne $suite_group) 361 { 362 if ($suite_group) 363 { 364 print $xml_report_file " " x 2; 365 print $xml_report_file "</testsuite>\n"; 366 } 367 368 $suite_group= $tsuite; 369 my $suite_time= $suite_stats{$tsuite}{'time'} / 1000.0; 370 my $suite_unstable_tests= $::opt_report_unstable_tests ? 371 "unstable-tests=\"". 372 $suite_stats{$tsuite}{'unstable'}."\" " 373 : ""; 374 375 print $xml_report_file " " x 2; 376 print $xml_report_file "<testsuite name=\"$tsuite\" ". 377 "tests=\"$suite_stats{$tsuite}{'tests'}\" ". 378 "failures=\"$suite_stats{$tsuite}{'failures'}\" ". 379 "$suite_unstable_tests". 380 "disabled=\"$suite_stats{$tsuite}{'disabled'}\" ". 381 "skipped=\"$suite_stats{$tsuite}{'skipped'}\" ". 382 "errors=\"$suite_stats{$tsuite}{'errors'}\" ". 383 "time=\"$suite_time\">\n"; 384 } 385 386 # Get the test's variant(if any) 387 my $combination= $tinfo->{combination} ? 388 " combination=\"".$tinfo->{combination}."\"" : ""; 389 390 my $test_time= $tinfo->{'timer'} / 1000.0; 391 392 # Print outcome-based tags 393 if ($tinfo->{'result'} eq 'MTR_RES_UNSTABLE') 394 { 395 print $xml_report_file " " x 4; 396 print $xml_report_file "<testcase name=\"$tname\"$combination ". 397 "status=\"unstable\" ". 398 "time=\"$test_time\" ". 399 "suitename=\"$tsuite\" >\n"; 400 401 print $xml_report_file " " x 7; 402 print $xml_report_file "<unstable message=\"Test unstable: It failed ". 403 "initially but passed on one or more retries\" ". 404 "passed=\"". 405 ($tinfo->{retries} - $tinfo->{failures} - 1). 406 "\" failed=\"$tinfo->{failures}\" />\n"; 407 408 print $xml_report_file " " x 4; 409 print $xml_report_file "</testcase>\n"; 410 } 411 elsif ($tinfo->{'result'} eq 'MTR_RES_FAILED' or $tinfo->{'failures'}) 412 { 413 print $xml_report_file " " x 4; 414 print $xml_report_file "<testcase name=\"$tname\"$combination ". 415 "status=\"fail\" ". 416 "time=\"$test_time\" ". 417 "suitename=\"$tsuite\" >\n"; 418 419 # Get information regarding the failure 420 my $failure_log= $tinfo->{'logfile'}; 421 my $failure_comment; 422 423 if ($tinfo->{'warnings'}) 424 { 425 $failure_comment= ": Found warnings/errors in server log file!"; 426 $failure_log= $failure_log."\n".$tinfo->{'warnings'}; 427 } 428 429 $failure_comment= ": ".$1 if ($failure_log =~ /mysqltest: (.*)/); 430 431 # For large comments from mysql-test-run.pl, display a brief message in 432 # the attribute 'message' and prepend details within the <failure> tag 433 my @comment_patterns= 434 ("MTR's internal check of the test case '.+' failed\\.", 435 "Test case timeout after \\d+ seconds", 436 "Could not execute 'check-testcase' [a-z]+ testcase '.+' \\(res: .*\\)", 437 "Could not execute 'check-warnings' for testcase '.+' \\(res: .*\\)", 438 "Server .+ failed during test run", 439 "The server .+ crashed while running 'check testcase [a-z]+ test'", 440 "The server .+ crashed while running 'check warnings'", 441 "mysqltest failed but provided no output"); 442 443 my $comment_regex= "(". join('|', @comment_patterns). ")(.*)"; 444 if ($tinfo->{'comment'}=~ /$comment_regex/s) 445 { 446 $failure_comment.= ", ".$1; 447 $failure_log= $2."\n".$failure_log; 448 } 449 else 450 { 451 $failure_comment= ", ".$tinfo->{'comment'} if ($tinfo->{'comment'}); 452 } 453 454 if (!$failure_log) 455 { 456 $failure_log= "Failure log not found"; 457 $failure_comment= ": Unknown result, no comment or log found" 458 if (!$failure_comment); 459 } 460 461 print $xml_report_file " " x 7; 462 print $xml_report_file "<failure message=\"Test failed". 463 "$failure_comment\">\n"; 464 465 # Embed failure log as a literal string so that the XML parser 466 # ignores any markup which might be included 467 my $failure_log_indent= " " x 10; 468 my $failure_log_message= ""; 469 map { $failure_log_message= $failure_log_message.$failure_log_indent.$_ } 470 (split /^/, $failure_log); 471 472 print $xml_report_file $failure_log_indent."<![CDATA[\n". 473 $failure_log_message."\n". 474 $failure_log_indent."]]>\n"; 475 476 print $xml_report_file " " x 7; 477 print $xml_report_file "</failure>\n"; 478 479 print $xml_report_file " " x 4; 480 print $xml_report_file "</testcase>\n"; 481 } 482 elsif ($tinfo->{'result'} eq 'MTR_RES_SKIPPED') 483 { 484 # Differentiate disabled and skipped tests as we return 485 # the same outcome code for both 486 my $not_run_status= $tinfo->{'disable'} ? "disabled" : "skipped"; 487 488 print $xml_report_file " " x 4; 489 print $xml_report_file "<testcase name=\"$tname\"$combination ". 490 "status=\"$not_run_status\" ". 491 "time=\"$test_time\" ". 492 "suitename=\"$tsuite\" ". 493 "comment=\"$tinfo->{'comment'}\" />\n"; 494 } 495 elsif ($tinfo->{'result'} eq 'MTR_RES_PASSED') 496 { 497 print $xml_report_file " " x 4; 498 print $xml_report_file "<testcase name=\"$tname\"$combination ". 499 "status=\"pass\" ". 500 "time=\"$test_time\" ". 501 "suitename=\"$tsuite\" />\n"; 502 } 503 } 504 if ($suite_group) 505 { 506 print $xml_report_file " " x 2; 507 print $xml_report_file "</testsuite>\n"; 508 } 509 print $xml_report_file "</testsuites>\n"; 510} 511 512 513sub mtr_report_stats ($$;$) { 514 my ($prefix, $tests, $dont_error)= @_; 515 516 # ---------------------------------------------------------------------- 517 # Find out how we where doing 518 # ---------------------------------------------------------------------- 519 520 my $tot_skipped= 0; 521 my $tot_skipdetect= 0; 522 my $tot_passed= 0; 523 my $tot_failed= 0; 524 my $tot_unstable= 0; 525 my $tot_tests= 0; 526 my $tot_restarts= 0; 527 my $found_problems= 0; 528 529 foreach my $tinfo (@$tests) 530 { 531 if ( $tinfo->{failures} ) 532 { 533 # Test has failed at least one time 534 $tot_tests++; 535 $tot_failed++; 536 if ($::opt_report_unstable_tests and defined $tinfo->{retries}) 537 { 538 my $num_passed= $tinfo->{retries} - $tinfo->{failures} - 1; 539 540 # Tests which exhibit both passing and failing behaviour 541 # with the same code are unstable tests. The level of in- 542 # stability is not restricted i.e., a failed test which is 543 # successful on at least one retry is marked unstable. 544 if ($num_passed > 0) 545 { 546 $tot_unstable++; 547 $tinfo->{'result'}= 'MTR_RES_UNSTABLE'; 548 } 549 } 550 } 551 elsif ( $tinfo->{'result'} eq 'MTR_RES_SKIPPED' ) 552 { 553 # Test was skipped (disabled not counted) 554 $tot_skipped++ unless $tinfo->{'disable'}; 555 $tot_skipdetect++ 556 if (defined $tinfo->{'skip_reason'} 557 and $tinfo->{skip_reason} eq MTR_SKIP_BY_TEST); 558 } 559 elsif ( $tinfo->{'result'} eq 'MTR_RES_PASSED' ) 560 { 561 # Test passed 562 $tot_tests++; 563 $tot_passed++; 564 } 565 566 if ( $tinfo->{'restarted'} ) 567 { 568 # Servers was restarted 569 $tot_restarts++; 570 } 571 572 # Add counts for repeated runs, if any. 573 # Note that the last run has already been counted above. 574 my $num_repeat = $tinfo->{'repeat'} - 1; 575 if ( $num_repeat > 0 ) 576 { 577 $tot_tests += $num_repeat; 578 my $rep_failed = $tinfo->{'rep_failures'} || 0; 579 $tot_failed += $rep_failed; 580 $tot_passed += $num_repeat - $rep_failed; 581 } 582 583 # Look for warnings produced by mysqltest 584 my $base_file= mtr_match_extension($tinfo->{'result_file'}, 585 "result"); # Trim extension 586 my $warning_file= "$base_file.warnings"; 587 if ( -f $warning_file ) 588 { 589 $found_problems= 1; 590 mtr_warning("Check myqltest warnings in '$warning_file'"); 591 } 592 } 593 594 # ---------------------------------------------------------------------- 595 # Print out a summary report to screen 596 # ---------------------------------------------------------------------- 597 print "The servers were restarted $tot_restarts times\n"; 598 599 if ( $timer ) 600 { 601 use English; 602 603 mtr_report("Spent", sprintf("%.3f", $tot_real_time),"of", 604 time - $BASETIME, "seconds executing testcases"); 605 } 606 607 resfile_global("duration", time - $BASETIME) if $::opt_resfile; 608 609 my $warnlog= "$::opt_vardir/log/warnings"; 610 if ( -f $warnlog ) 611 { 612 mtr_warning("Got errors/warnings while running tests, please examine", 613 "'$warnlog' for details."); 614 } 615 616 print "\n"; 617 618 # Print a list of check_testcases that failed(if any) 619 if ( $::opt_check_testcases ) 620 { 621 my %check_testcases; 622 623 foreach my $tinfo (@$tests) 624 { 625 if ( defined $tinfo->{'check_testcase_failed'} ) 626 { 627 $check_testcases{$tinfo->{'name'}}= 1; 628 } 629 } 630 631 if ( keys %check_testcases ) 632 { 633 print "Check of testcase failed for: "; 634 print join(" ", keys %check_testcases); 635 print "\n\n"; 636 } 637 } 638 639 # Print summary line prefix 640 summary_print("$prefix: "); 641 642 # Print a list of testcases that failed 643 if ( $tot_failed != 0 ) 644 { 645 646 # Print each failed test, again 647 #foreach my $test ( @$tests ){ 648 # if ( $test->{failures} ) { 649 # mtr_report_test($test); 650 # } 651 #} 652 653 my $ratio= $tot_passed * 100 / $tot_tests; 654 summary_print(sprintf("Failed $tot_failed/$tot_tests tests, %.2f%% were successful.\n\n", $ratio)); 655 656 # Hashes to keep track of reported failures 657 my %seen= (); 658 my %seen_unstable= (); 659 660 foreach my $tinfo (@$tests) 661 { 662 my $tname= $tinfo->{'name'}; 663 if (($tinfo->{failures} || $tinfo->{rep_failures})) 664 { 665 # Check for unstable tests 666 if ($tinfo->{result} eq "MTR_RES_UNSTABLE" and !$seen_unstable{$tname}) 667 { 668 # Report all unstable tests in the format :- 669 # <test_name>(<number of failures>/<total attempts>). 670 # 671 # Marking the test as 'seen' in case of unstable tests might cause 672 # hard-failures in other combination runs of the same test to 673 # not be reported. Separate hash used to avoid redundant mentions 674 # of an unstable test 675 $seen_unstable{$tname}= "(".$tinfo->{failures}."/". 676 ($tinfo->{retries} - 1).")"; 677 } 678 elsif (!$seen{$tname}) 679 { 680 $seen{$tname}= 1; 681 } 682 } 683 } 684 685 # Print the list of tests that failed in a format 686 # that can be copy pasted to rerun only failing tests 687 if (%seen) 688 { 689 summary_print("Failing test(s): ". 690 join(" ", keys %seen). "\n\n"); 691 } 692 693 # Print unstable tests, if any 694 if (%seen_unstable) 695 { 696 summary_print("Unstable test(s)(failures/attempts): ". 697 join(" ", map {$_.$seen_unstable{$_}} keys %seen_unstable). 698 "\n\n"); 699 } 700 701 # Print info about reporting the error 702 print 703 "The log files in var/log may give you some hint of what went wrong.\n\n", 704 "If you want to report this error, please read first ", 705 "the documentation\n", 706 "at http://dev.mysql.com/doc/mysql/en/mysql-test-suite.html\n\n"; 707 708 } 709 else 710 { 711 summary_print("All $tot_tests tests were successful.\n\n"); 712 } 713 close($summary_report_file) if defined($summary_report_file); 714 715 if ($xml_report_file) 716 { 717 mtr_generate_xml_report($tests); 718 $xml_report_file->flush(); 719 } 720 721 print "$tot_skipped tests were skipped, ". 722 "$tot_skipdetect by the test itself.\n\n" if $tot_skipped; 723 724 if ( $tot_failed != 0 || $found_problems) 725 { 726 if ($tot_failed == $tot_unstable) 727 { 728 # Print a warning if all failures are due to unstable tests 729 mtr_warning("There are failures due to unstable test cases.\n". 730 "However, the tests are not hard-failing."); 731 } 732 else 733 { 734 mtr_error("there were failing test cases") unless $dont_error; 735 } 736 } 737} 738 739 740############################################################################## 741# 742# Text formatting 743# 744############################################################################## 745 746sub mtr_print_line () { 747 print '-' x 74 . "\n"; 748} 749 750 751sub mtr_print_thick_line { 752 my $char= shift || '='; 753 print $char x 78 . "\n"; 754} 755 756 757sub mtr_print_header ($) { 758 my ($wid) = @_; 759 print "\n"; 760 printf "TEST"; 761 if ($wid) { 762 print " " x 34 . "WORKER "; 763 } else { 764 print " " x 38; 765 } 766 print "RESULT "; 767 print "TIME (ms) or " if $timer; 768 print "COMMENT\n"; 769 mtr_print_line(); 770 print "\n"; 771} 772 773############################################################################## 774# 775# XML Output 776# 777############################################################################## 778 779sub mtr_xml_init($) { 780 my ($fn)= @_; 781 # Write xml-report in the build directory for out-of-source builds or current 782 # path otherwise, unless an absolute path is already specified 783 $fn= File::Spec->rel2abs($fn, 784 $ENV{MTR_BINDIR} ? 785 File::Spec->catfile($ENV{MTR_BINDIR}, "mysql-test"): 786 ""); 787 788 unless(open $xml_report_file, '>', $fn) { 789 mtr_error("could not create xml_report file $fn"); 790 } 791 print "Writing XML report to $fn...\n"; 792 print $xml_report_file "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 793} 794 795############################################################################## 796# 797# Summary file output 798# 799############################################################################## 800 801sub mtr_summary_file_init($) { 802 my ($fn) = @_; 803 804 # For out-of-tree builds, the MTR wrapper script will cd to the source 805 # directory. Thus, let's make $summary_report_file local to the vardir 806 # instead, if it's not already absolute. 807 my $full_path = File::Spec->rel2abs($fn, $::opt_vardir); 808 unless(open $summary_report_file, '>', $full_path) { 809 mtr_error("could not create summary_report file $fn"); 810 } 811 print "Writing summary report to $fn...\n"; 812} 813 814sub summary_print($) { 815 my ($text) = @_; 816 print $text; 817 if (defined($summary_report_file)) { 818 print $summary_report_file $text; 819 } 820} 821 822############################################################################## 823# 824# Log and reporting functions 825# 826############################################################################## 827 828use Time::localtime; 829 830use Time::HiRes qw(gettimeofday); 831 832sub format_time { 833 my $tm= localtime(); 834 return sprintf("%4d-%02d-%02d %02d:%02d:%02d", 835 $tm->year + 1900, $tm->mon+1, $tm->mday, 836 $tm->hour, $tm->min, $tm->sec); 837} 838 839my $t0= gettimeofday(); 840 841sub _timestamp { 842 return "" unless $timestamp; 843 844 my $diff; 845 if ($timediff){ 846 my $t1= gettimeofday(); 847 my $elapsed= $t1 - $t0; 848 849 $diff= sprintf(" +%02.3f", $elapsed); 850 851 # Save current time for next lap 852 $t0= $t1; 853 854 } 855 856 my $tm= localtime(); 857 return sprintf("%02d%02d%02d %2d:%02d:%02d%s ", 858 $tm->year % 100, $tm->mon+1, $tm->mday, 859 $tm->hour, $tm->min, $tm->sec, $diff); 860} 861 862# Always print message to screen 863sub mtr_print (@) { 864 print _name(). join(" ", @_). "\n"; 865} 866 867 868# Print message to screen if verbose is defined 869sub mtr_report (@) { 870 if (defined $verbose) 871 { 872 print _name(). join(" ", @_). "\n"; 873 } 874} 875 876 877# Print warning to screen 878sub mtr_warning (@) { 879 print STDERR _name(). _timestamp(). 880 "mysql-test-run: WARNING: ". join(" ", @_). "\n"; 881} 882 883 884# Print error to screen and then exit 885sub mtr_error (@) { 886 IO::Handle::flush(\*STDOUT) if IS_WINDOWS; 887 print STDERR _name(). _timestamp(). 888 "mysql-test-run: *** ERROR: ". join(" ", @_). "\n"; 889 if (IS_WINDOWS) 890 { 891 POSIX::_exit(1); 892 } 893 else 894 { 895 exit(1); 896 } 897} 898 899 900sub mtr_debug (@) { 901 if ( $verbose > 2 ) 902 { 903 print STDERR _name(). 904 _timestamp(). "####: ". join(" ", @_). "\n"; 905 } 906} 907 908 909sub mtr_verbose (@) { 910 if ( $verbose ) 911 { 912 print STDERR _name(). _timestamp(). 913 "> ".join(" ", @_)."\n"; 914 } 915} 916 917 918sub mtr_verbose_restart (@) { 919 my ($server, @args)= @_; 920 my $proc= $server->{proc}; 921 if ( $verbose_restart ) 922 { 923 print STDERR _name()._timestamp(). 924 "> Restart $proc - ".join(" ", @args)."\n"; 925 } 926} 927 928 929# Used by --result-file for for formatting times 930 931sub isotime($) { 932 my ($sec,$min,$hr,$day,$mon,$yr)= gmtime($_[0]); 933 return sprintf "%d-%02d-%02dT%02d:%02d:%02dZ", 934 $yr+1900, $mon+1, $day, $hr, $min, $sec; 935} 936 9371; 938