1# -*- cperl -*- 2# Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. 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_cases; 29use strict; 30 31use base qw(Exporter); 32our @EXPORT= qw(collect_option collect_test_cases); 33 34use mtr_report; 35use mtr_match; 36 37# Options used for the collect phase 38our $start_from; 39our $print_testcases; 40our $skip_rpl; 41our $do_test; 42our $skip_test; 43our $skip_combinations; 44our $binlog_format; 45our $enable_disabled; 46our $default_storage_engine; 47our $opt_with_ndbcluster_only; 48our $defaults_file; 49our $defaults_extra_file; 50our $quick_collect; 51# Set to 1 if you want the tests to override 52# default storage engine settings, and use MyISAM 53# as default. (temporary option used in connection 54# with the change of default storage engine to InnoDB) 55our $default_myisam= 0; 56 57 58sub collect_option { 59 my ($opt, $value)= @_; 60 61 # Evaluate $opt as string to use "Getopt::Long::Callback legacy API" 62 my $opt_name = "$opt"; 63 64 # Convert - to _ in option name 65 $opt_name =~ s/-/_/g; 66 no strict 'refs'; 67 ${$opt_name}= $value; 68} 69 70use File::Basename; 71use File::Spec::Functions qw / splitdir /; 72use IO::File(); 73use My::Config; 74use My::Platform; 75use My::Test; 76use My::Find; 77 78require "mtr_misc.pl"; 79 80# Precompiled regex's for tests to do or skip 81my $do_test_reg; 82my $skip_test_reg; 83 84# Related to adding InnoDB plugin combinations 85my $lib_innodb_plugin; 86my $do_innodb_plugin; 87 88# If "Quick collect", set to 1 once a test to run has been found. 89my $some_test_found; 90 91sub init_pattern { 92 my ($from, $what)= @_; 93 return undef unless defined $from; 94 if ( $from =~ /^[a-z0-9\.]*$/ ) { 95 # Does not contain any regex (except . that we allow as 96 # separator betwen suite and testname), make the pattern match 97 # beginning of string 98 $from= "^$from"; 99 mtr_verbose("$what='$from'"); 100 } 101 # Check that pattern is a valid regex 102 eval { "" =~/$from/; 1 } or 103 mtr_error("Invalid regex '$from' passed to $what\nPerl says: $@"); 104 return $from; 105} 106 107 108############################################################################## 109# 110# Collect information about test cases to be run 111# 112############################################################################## 113 114sub collect_test_cases ($$$$) { 115 my $opt_reorder= shift; # True if we're reordering tests 116 my $suites= shift; # Semicolon separated list of test suites 117 my $opt_cases= shift; 118 my $opt_skip_test_list= shift; 119 my $cases= []; # Array of hash(one hash for each testcase) 120 121 # Unit tests off by default also if using --do-test or --start-from 122 $::opt_ctest= 0 if $::opt_ctest == -1 && ($do_test || $start_from); 123 124 $do_test_reg= init_pattern($do_test, "--do-test"); 125 $skip_test_reg= init_pattern($skip_test, "--skip-test"); 126 127 $lib_innodb_plugin= 128 my_find_file($::basedir, 129 ["storage/innodb_plugin", "storage/innodb_plugin/.libs", 130 "lib/mysql/plugin", "lib/plugin"], 131 ["ha_innodb_plugin.dll", "ha_innodb_plugin.so", 132 "ha_innodb_plugin.sl"], 133 NOT_REQUIRED); 134 $do_innodb_plugin= ($::mysql_version_id >= 50100 && 135 !(IS_WINDOWS && $::opt_embedded_server) && 136 $lib_innodb_plugin); 137 138 # If not reordering, we also shouldn't group by suites, unless 139 # no test cases were named. 140 # This also effects some logic in the loop following this. 141 if ($opt_reorder or !@$opt_cases) 142 { 143 foreach my $suite (split(",", $suites)) 144 { 145 push(@$cases, collect_one_suite($suite, $opt_cases, $opt_skip_test_list)); 146 last if $some_test_found; 147 push(@$cases, collect_one_suite("i_".$suite, $opt_cases, $opt_skip_test_list)); 148 } 149 } 150 151 if ( @$opt_cases ) 152 { 153 # A list of tests was specified on the command line. 154 # Among those, the tests which are not already collected will be 155 # collected and stored temporarily in an array of hashes pointed 156 # by the below reference. This array is eventually appeneded to 157 # the one having all collected test cases. 158 my $cmdline_cases; 159 160 # Check that the tests specified was found 161 # in at least one suite 162 foreach my $test_name_spec ( @$opt_cases ) 163 { 164 my $found= 0; 165 my ($sname, $tname, $extension)= split_testname($test_name_spec); 166 foreach my $test ( @$cases ) 167 { 168 last unless $opt_reorder; 169 # test->{name} is always in suite.name format 170 if ( $test->{name} =~ /^$sname.*\.$tname$/ ) 171 { 172 $found= 1; 173 last; 174 } 175 } 176 if ( not $found ) 177 { 178 if ( $sname ) 179 { 180 # If suite was part of name, find it there, may come with combinations 181 my @this_case = collect_one_suite($sname, [ $tname ]); 182 183 # If a test is specified multiple times on the command line, all 184 # instances of the test need to be picked. Hence, such tests are 185 # stored in the temporary array instead of adding them to $cases 186 # directly so that repeated tests are not run only once 187 if (@this_case) 188 { 189 push (@$cmdline_cases, @this_case); 190 } 191 else 192 { 193 mtr_error("Could not find '$tname' in '$sname' suite"); 194 } 195 } 196 else 197 { 198 if ( !$opt_reorder ) 199 { 200 # If --no-reorder is passed and if suite was not part of name, 201 # search in all the suites 202 foreach my $suite (split(",", $suites)) 203 { 204 my @this_case = collect_one_suite($suite, [ $tname ]); 205 if ( @this_case ) 206 { 207 push (@$cmdline_cases, @this_case); 208 $found= 1; 209 } 210 @this_case= collect_one_suite("i_".$suite, [ $tname ]); 211 if ( @this_case ) 212 { 213 push (@$cmdline_cases, @this_case); 214 $found= 1; 215 } 216 } 217 } 218 if ( !$found ) 219 { 220 mtr_error("Could not find '$tname' in '$suites' suite(s)"); 221 } 222 } 223 } 224 } 225 # Add test cases collected in the temporary array to the one 226 # containing all previously collected test cases 227 push (@$cases, @$cmdline_cases) if $cmdline_cases; 228 } 229 230 if ( $opt_reorder && !$quick_collect) 231 { 232 # Reorder the test cases in an order that will make them faster to run 233 # Make a mapping of test name to a string that represents how that test 234 # should be sorted among the other tests. Put the most important criterion 235 # first, then a sub-criterion, then sub-sub-criterion, etc. 236 foreach my $tinfo (@$cases) 237 { 238 my @criteria = (); 239 240 # 241 # Append the criteria for sorting, in order of importance. 242 # 243 push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "A" : "B")); 244 push(@criteria, $tinfo->{template_path}); 245 # Group test with equal options together. 246 # Ending with "~" makes empty sort later than filled 247 my $opts= $tinfo->{'master_opt'} ? $tinfo->{'master_opt'} : []; 248 push(@criteria, join("!", sort @{$opts}) . "~"); 249 # Add slave opts if any 250 if ($tinfo->{'slave_opt'}) 251 { 252 push(@criteria, join("!", sort @{$tinfo->{'slave_opt'}})); 253 } 254 # This sorts tests with force-restart *before* identical tests 255 push(@criteria, $tinfo->{force_restart} ? "force-restart" : "no-restart"); 256 257 $tinfo->{criteria}= join(" ", @criteria); 258 } 259 260 @$cases = sort {$a->{criteria} cmp $b->{criteria}; } @$cases; 261 262 # For debugging the sort-order 263 # foreach my $tinfo (@$cases) 264 # { 265 # my $tname= $tinfo->{name} . ' ' . $tinfo->{combination}; 266 # my $crit= $tinfo->{criteria}; 267 # print("$tname\n\t$crit\n"); 268 # } 269 } 270 271 if (defined $print_testcases){ 272 print_testcases(@$cases); 273 exit(1); 274 } 275 276 return $cases; 277 278} 279 280 281# Returns (suitename, testname, extension) 282sub split_testname { 283 my ($test_name)= @_; 284 285 # If .test file name is used, get rid of directory part 286 $test_name= basename($test_name) if $test_name =~ /\.test$/; 287 288 # Now split name on .'s 289 my @parts= split(/\./, $test_name); 290 291 if (@parts == 1){ 292 # Only testname given, ex: alias 293 return (undef , $parts[0], undef); 294 } elsif (@parts == 2) { 295 # Either testname.test or suite.testname given 296 # Ex. main.alias or alias.test 297 298 if ($parts[1] eq "test") 299 { 300 return (undef , $parts[0], $parts[1]); 301 } 302 else 303 { 304 return ($parts[0], $parts[1], undef); 305 } 306 307 } elsif (@parts == 3) { 308 # Fully specified suitename.testname.test 309 # ex main.alias.test 310 return ( $parts[0], $parts[1], $parts[2]); 311 } 312 313 mtr_error("Illegal format of test name: $test_name"); 314} 315 316 317sub collect_one_suite($) 318{ 319 my $suite= shift; # Test suite name 320 my $opt_cases= shift; 321 my $opt_skip_test_list= shift; 322 my @cases; # Array of hash 323 324 mtr_verbose("Collecting: $suite"); 325 326 my $suitedir= "$::glob_mysql_test_dir"; # Default 327 if ( $suite ne "main" ) 328 { 329 # Allow suite to be path to "some dir" if $suite has at least 330 # one directory part 331 if ( -d $suite and splitdir($suite) > 1 ){ 332 $suitedir= $suite; 333 mtr_report(" - from '$suitedir'"); 334 335 } 336 else 337 { 338 $suitedir= my_find_dir($::basedir, 339 ["share/mysql-test/suite", 340 "mysql-test/suite", 341 "lib/mysql-test/suite", 342 "internal/mysql-test/suite", 343 "mysql-test", 344 # Look in storage engine specific suite dirs 345 "storage/*/mtr", 346 # Look in plugin specific suite dir 347 "plugin/$suite/tests", 348 "internal/plugin/$suite/tests", 349 "rapid/plugin/$suite/tests", 350 "rapid/mysql-test/suite", 351 ], 352 [$suite, "mtr"], ($suite =~ /^i_/)); 353 return unless $suitedir; 354 } 355 mtr_verbose("suitedir: $suitedir"); 356 } 357 358 my $testdir= "$suitedir/t"; 359 my $resdir= "$suitedir/r"; 360 361 # Check if t/ exists 362 if (-d $testdir){ 363 # t/ exists 364 365 if ( -d $resdir ) 366 { 367 # r/exists 368 } 369 else 370 { 371 # No r/, use t/ as result dir 372 $resdir= $testdir; 373 } 374 375 } 376 else { 377 # No t/ dir => there can' be any r/ dir 378 mtr_error("Can't have r/ dir without t/") if -d $resdir; 379 380 # No t/ or r/ => use suitedir 381 $resdir= $testdir= $suitedir; 382 } 383 384 mtr_verbose("testdir: $testdir"); 385 mtr_verbose("resdir: $resdir"); 386 387 # ---------------------------------------------------------------------- 388 # Build a hash of disabled testcases for this suite 389 # ---------------------------------------------------------------------- 390 my %disabled; 391 my @disabled_collection= @{$opt_skip_test_list} if $opt_skip_test_list; 392 unshift (@disabled_collection, "$testdir/disabled.def"); 393 394 # Check for the tests to be skipped in a sanitizer which are listed 395 # in "mysql-test/collections/disabled-<sanitizer>.list" file. 396 if ($::opt_sanitize) { 397 # Check for disabled-asan.list 398 if ($::mysql_version_extra =~ /asan/i && 399 !grep (/disabled-asan\.list$/, @{$opt_skip_test_list})) { 400 push(@disabled_collection, "collections/disabled-asan.list"); 401 } 402 } 403 404 for my $skip (@disabled_collection) 405 { 406 if ( open(DISABLED, $skip ) ) 407 { 408 # $^O on Windows considered not generic enough 409 my $plat= (IS_WINDOWS) ? 'windows' : $^O; 410 411 while ( <DISABLED> ) 412 { 413 chomp; 414 #diasble the test case if platform matches 415 if ( /\@/ ) 416 { 417 if ( /\@$plat/ ) 418 { 419 /^\s*(\S+)\s*\@$plat.*:\s*(.*?)\s*$/ ; 420 $disabled{$1}= $2 if not exists $disabled{$1}; 421 } 422 elsif ( /\@!(\S*)/ ) 423 { 424 if ( $1 ne $plat) 425 { 426 /^\s*(\S+)\s*\@!.*:\s*(.*?)\s*$/ ; 427 $disabled{$1}= $2 if not exists $disabled{$1}; 428 } 429 } 430 } 431 elsif ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) 432 { 433 chomp; 434 if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) 435 { 436 $disabled{$1}= $2 if not exists $disabled{$1}; 437 } 438 } 439 } 440 close DISABLED; 441 } 442 } 443 444 # Read suite.opt file 445 my $suite_opt_file= "$testdir/suite.opt"; 446 447 if ( $::opt_suite_opt ) 448 { 449 $suite_opt_file= "$testdir/$::opt_suite_opt"; 450 } 451 452 my $suite_opts= []; 453 if ( -f $suite_opt_file ) 454 { 455 $suite_opts= opts_from_file($suite_opt_file); 456 } 457 458 if ( @$opt_cases ) 459 { 460 # Collect in specified order 461 foreach my $test_name_spec ( @$opt_cases ) 462 { 463 my ($sname, $tname, $extension)= split_testname($test_name_spec); 464 465 # The test name parts have now been defined 466 #print " suite_name: $sname\n"; 467 #print " tname: $tname\n"; 468 #print " extension: $extension\n"; 469 470 # Check cirrect suite if suitename is defined 471 next if (defined $sname and $suite ne $sname); 472 473 if ( defined $extension ) 474 { 475 my $full_name= "$testdir/$tname.$extension"; 476 # Extension was specified, check if the test exists 477 if ( ! -f $full_name) 478 { 479 # This is only an error if suite was specified, otherwise it 480 # could exist in another suite 481 mtr_error("Test '$full_name' was not found in suite '$sname'") 482 if $sname; 483 484 next; 485 } 486 } 487 else 488 { 489 # No extension was specified, use default 490 $extension= "test"; 491 my $full_name= "$testdir/$tname.$extension"; 492 493 # Test not found here, could exist in other suite 494 next if ( ! -f $full_name ); 495 } 496 497 push(@cases, 498 collect_one_test_case($suitedir, 499 $testdir, 500 $resdir, 501 $suite, 502 $tname, 503 "$tname.$extension", 504 \%disabled, 505 $suite_opts)); 506 } 507 } 508 else 509 { 510 opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); 511 512 foreach my $elem ( sort readdir(TESTDIR) ) 513 { 514 my $tname= mtr_match_extension($elem, 'test'); 515 516 next unless defined $tname; 517 518 # Skip tests that does not match the --do-test= filter 519 next if ($do_test_reg and not $tname =~ /$do_test_reg/o); 520 521 push(@cases, 522 collect_one_test_case($suitedir, 523 $testdir, 524 $resdir, 525 $suite, 526 $tname, 527 $elem, 528 \%disabled, 529 $suite_opts)); 530 } 531 closedir TESTDIR; 532 } 533 534 # Return empty list if no testcases found 535 return if (@cases == 0); 536 537 # ---------------------------------------------------------------------- 538 # Read combinations for this suite and build testcases x combinations 539 # if any combinations exists 540 # ---------------------------------------------------------------------- 541 if ( ! $skip_combinations && ! $quick_collect ) 542 { 543 my @combinations; 544 my $combination_file= "$suitedir/combinations"; 545 #print "combination_file: $combination_file\n"; 546 if (@::opt_combinations) 547 { 548 # take the combination from command-line 549 mtr_verbose("Take the combination from command line"); 550 foreach my $combination (@::opt_combinations) { 551 my $comb= {}; 552 $comb->{name}= $combination; 553 push(@{$comb->{comb_opt}}, $combination); 554 push(@combinations, $comb); 555 } 556 } 557 elsif (-f $combination_file ) 558 { 559 # Read combinations file in my.cnf format 560 mtr_verbose("Read combinations file"); 561 my $config= My::Config->new($combination_file); 562 foreach my $group ($config->groups()) { 563 my $comb= {}; 564 $comb->{name}= $group->name(); 565 foreach my $option ( $group->options() ) { 566 push(@{$comb->{comb_opt}}, $option->option()); 567 } 568 push(@combinations, $comb); 569 } 570 } 571 572 if (@combinations) 573 { 574 print " - adding combinations for $suite\n"; 575 #print_testcases(@cases); 576 577 my @new_cases; 578 foreach my $comb (@combinations) 579 { 580 foreach my $test (@cases) 581 { 582 583 next if ( $test->{'skip'} ); 584 585 # Skip this combination if the values it provides 586 # already are set in master_opt or slave_opt 587 if (My::Options::is_set($test->{master_opt}, $comb->{comb_opt}) && 588 My::Options::is_set($test->{slave_opt}, $comb->{comb_opt}) ){ 589 next; 590 } 591 592 # Copy test options 593 my $new_test= My::Test->new(); 594 while (my ($key, $value) = each(%$test)) { 595 if (ref $value eq "ARRAY") { 596 push(@{$new_test->{$key}}, @$value); 597 } else { 598 $new_test->{$key}= $value; 599 } 600 } 601 602 # Append the combination options to master_opt and slave_opt 603 push(@{$new_test->{master_opt}}, @{$comb->{comb_opt}}); 604 push(@{$new_test->{slave_opt}}, @{$comb->{comb_opt}}); 605 606 # Add combination name short name 607 $new_test->{combination}= $comb->{name}; 608 609 # Add the new test to new test cases list 610 push(@new_cases, $new_test); 611 } 612 } 613 614 # Add the plain test if it was not already added 615 # as part of a combination 616 my %added; 617 foreach my $new_test (@new_cases){ 618 $added{$new_test->{name}}= 1; 619 } 620 foreach my $test (@cases){ 621 push(@new_cases, $test) unless $added{$test->{name}}; 622 } 623 624 625 #print_testcases(@new_cases); 626 @cases= @new_cases; 627 #print_testcases(@cases); 628 } 629 } 630 631 optimize_cases(\@cases); 632 #print_testcases(@cases); 633 634 return @cases; 635} 636 637 638 639# 640# Loop through all test cases 641# - optimize which test to run by skipping unnecessary ones 642# - update settings if necessary 643# 644sub optimize_cases { 645 my ($cases)= @_; 646 647 foreach my $tinfo ( @$cases ) 648 { 649 # Skip processing if already marked as skipped 650 next if $tinfo->{skip}; 651 652 # ======================================================= 653 # If a special binlog format was selected with 654 # --mysqld=--binlog-format=x, skip all test that does not 655 # support it 656 # ======================================================= 657 #print "binlog_format: $binlog_format\n"; 658 if (defined $binlog_format ) 659 { 660 # ======================================================= 661 # Fixed --binlog-format=x specified on command line 662 # ======================================================= 663 if ( defined $tinfo->{'binlog_formats'} ) 664 { 665 #print "binlog_formats: ". join(", ", @{$tinfo->{binlog_formats}})."\n"; 666 667 # The test supports different binlog formats 668 # check if the selected one is ok 669 my $supported= 670 grep { $_ eq lc $binlog_format } @{$tinfo->{'binlog_formats'}}; 671 if ( !$supported ) 672 { 673 $tinfo->{'skip'}= 1; 674 $tinfo->{'comment'}= 675 "Doesn't support --binlog-format='$binlog_format'"; 676 } 677 } 678 } 679 else 680 { 681 # ======================================================= 682 # Use dynamic switching of binlog format 683 # ======================================================= 684 685 # Get binlog-format used by this test from master_opt 686 my $test_binlog_format; 687 foreach my $opt ( @{$tinfo->{master_opt}} ) { 688 (my $dash_opt = $opt) =~ s/_/-/g; 689 $test_binlog_format= 690 mtr_match_prefix($dash_opt, "--binlog-format=") || $test_binlog_format; 691 } 692 693 if (defined $test_binlog_format and 694 defined $tinfo->{binlog_formats} ) 695 { 696 my $supported= 697 grep { My::Options::option_equals($_, lc $test_binlog_format) } 698 @{$tinfo->{'binlog_formats'}}; 699 if ( !$supported ) 700 { 701 $tinfo->{'skip'}= 1; 702 $tinfo->{'comment'}= 703 "Doesn't support --binlog-format='$test_binlog_format'"; 704 next; 705 } 706 } 707 } 708 709 # ======================================================= 710 # Check that engine selected by 711 # --default-storage-engine=<engine> is supported 712 # ======================================================= 713 my %builtin_engines = ('myisam' => 1, 'memory' => 1, 'csv' => 1); 714 715 foreach my $opt ( @{$tinfo->{master_opt}} ) { 716 (my $dash_opt = $opt) =~ s/_/-/g; 717 my $default_engine= 718 mtr_match_prefix($dash_opt, "--default-storage-engine="); 719 my $default_tmp_engine= 720 mtr_match_prefix($dash_opt, "--default-tmp-storage-engine="); 721 722 # Allow use of uppercase, convert to all lower case 723 $default_engine =~ tr/A-Z/a-z/; 724 $default_tmp_engine =~ tr/A-Z/a-z/; 725 726 if (defined $default_engine){ 727 728 #print " $tinfo->{name}\n"; 729 #print " - The test asked to use '$default_engine'\n"; 730 731 #my $engine_value= $::mysqld_variables{$default_engine}; 732 #print " - The mysqld_variables says '$engine_value'\n"; 733 734 if ( ! exists $::mysqld_variables{$default_engine} and 735 ! exists $builtin_engines{$default_engine} ) 736 { 737 $tinfo->{'skip'}= 1; 738 $tinfo->{'comment'}= 739 "'$default_engine' not supported"; 740 } 741 742 $tinfo->{'ndb_test'}= 1 743 if ( $default_engine =~ /^ndb/i ); 744 $tinfo->{'myisam_test'}= 1 745 if ( $default_engine =~ /^myisam/i ); 746 } 747 if (defined $default_tmp_engine){ 748 749 #print " $tinfo->{name}\n"; 750 #print " - The test asked to use '$default_tmp_engine' as temp engine\n"; 751 752 #my $engine_value= $::mysqld_variables{$default_tmp_engine}; 753 #print " - The mysqld_variables says '$engine_value'\n"; 754 755 if ( ! exists $::mysqld_variables{$default_tmp_engine} and 756 ! exists $builtin_engines{$default_tmp_engine} ) 757 { 758 $tinfo->{'skip'}= 1; 759 $tinfo->{'comment'}= 760 "'$default_tmp_engine' not supported"; 761 } 762 763 $tinfo->{'ndb_test'}= 1 764 if ( $default_tmp_engine =~ /^ndb/i ); 765 $tinfo->{'myisam_test'}= 1 766 if ( $default_tmp_engine =~ /^myisam/i ); 767 } 768 } 769 770 if ($quick_collect && ! $tinfo->{'skip'}) 771 { 772 $some_test_found= 1; 773 return; 774 } 775 } 776} 777 778 779# 780# Read options from the given opt file and append them as an array 781# to $tinfo->{$opt_name} 782# 783sub process_opts_file { 784 my ($tinfo, $opt_file, $opt_name)= @_; 785 786 if ( -f $opt_file ) 787 { 788 my $opts= opts_from_file($opt_file); 789 790 foreach my $opt ( @$opts ) 791 { 792 my $value; 793 794 # The opt file is used both to send special options to the mysqld 795 # as well as pass special test case specific options to this 796 # script 797 798 $value= mtr_match_prefix($opt, "--timezone="); 799 if ( defined $value ) 800 { 801 $tinfo->{'timezone'}= $value; 802 next; 803 } 804 805 $value= mtr_match_prefix($opt, "--result-file="); 806 if ( defined $value ) 807 { 808 # Specifies the file mysqltest should compare 809 # output against 810 $tinfo->{'result_file'}= "r/$value.result"; 811 next; 812 } 813 814 $value= mtr_match_prefix($opt, "--config-file-template="); 815 if ( defined $value) 816 { 817 # Specifies the configuration file to use for this test 818 $tinfo->{'template_path'}= dirname($tinfo->{path})."/$value"; 819 next; 820 } 821 822 # If we set default time zone, remove the one we have 823 $value= mtr_match_prefix($opt, "--default-time-zone="); 824 if ( defined $value ) 825 { 826 # Set timezone for this test case to something different 827 $tinfo->{'timezone'}= "GMT-8"; 828 # Fallthrough, add the --default-time-zone option 829 } 830 831 # The --restart option forces a restart even if no special 832 # option is set. If the options are the same as next testcase 833 # there is no need to restart after the testcase 834 # has completed 835 if ( $opt eq "--force-restart" ) 836 { 837 $tinfo->{'force_restart'}= 1; 838 next; 839 } 840 841 $value= mtr_match_prefix($opt, "--testcase-timeout="); 842 if ( defined $value ) { 843 # Overrides test case timeout for this test 844 $tinfo->{'case-timeout'}= $value; 845 next; 846 } 847 848 # Ok, this was a real option, add it 849 push(@{$tinfo->{$opt_name}}, $opt); 850 } 851 } 852} 853 854############################################################################## 855# 856# Collect information about a single test case 857# 858############################################################################## 859 860sub collect_one_test_case { 861 my $suitedir= shift; 862 my $testdir= shift; 863 my $resdir= shift; 864 my $suitename= shift; 865 my $tname= shift; 866 my $filename= shift; 867 my $disabled= shift; 868 my $suite_opts= shift; 869 870 #print "collect_one_test_case\n"; 871 #print " suitedir: $suitedir\n"; 872 #print " testdir: $testdir\n"; 873 #print " resdir: $resdir\n"; 874 #print " suitename: $suitename\n"; 875 #print " tname: $tname\n"; 876 #print " filename: $filename\n"; 877 878 # ---------------------------------------------------------------------- 879 # Check --start-from 880 # ---------------------------------------------------------------------- 881 if ( $start_from ) 882 { 883 # start_from can be specified as [suite.].testname_prefix 884 my ($suite, $test, $ext)= split_testname($start_from); 885 886 if ( $suite and $suitename lt $suite){ 887 return; # Skip silently 888 } 889 if ( $tname lt $test ){ 890 return; # Skip silently 891 } 892 } 893 894 # ---------------------------------------------------------------------- 895 # Set defaults 896 # ---------------------------------------------------------------------- 897 my $tinfo= My::Test->new 898 ( 899 name => "$suitename.$tname", 900 shortname => $tname, 901 path => "$testdir/$filename", 902 903 ); 904 905 my $result_file= "$resdir/$tname.result"; 906 if (-f $result_file) { 907 # Allow nonexistsing result file 908 # in that case .test must issue "exit" otherwise test 909 # should fail by default 910 $tinfo->{result_file}= $result_file; 911 } 912 else { 913 # No .result file exist 914 # Remember the path where it should be 915 # saved in case of --record 916 $tinfo->{record_file}= $result_file; 917 } 918 919 # ---------------------------------------------------------------------- 920 # Skip some tests but include in list, just mark them as skipped 921 # ---------------------------------------------------------------------- 922 if ( $skip_test_reg and $tname =~ /$skip_test_reg/o ) 923 { 924 $tinfo->{'skip'}= 1; 925 return $tinfo; 926 } 927 928 # ---------------------------------------------------------------------- 929 # Check for replicaton tests 930 # ---------------------------------------------------------------------- 931 $tinfo->{'rpl_test'}= 1 if ($suitename =~ 'rpl'); 932 $tinfo->{'grp_rpl_test'}= 1 if ($suitename =~ 'group_replication'); 933 934 # ---------------------------------------------------------------------- 935 # Check for disabled tests 936 # ---------------------------------------------------------------------- 937 my $marked_as_disabled= 0; 938 if ( $disabled->{$tname} or $disabled->{"$suitename.$tname"} ) 939 { 940 # Test was marked as disabled in suites disabled.def file 941 $marked_as_disabled= 1; 942 # Test name may have been disabled with or without suite name part 943 $tinfo->{'comment'}= $disabled->{$tname} || 944 $disabled->{"$suitename.$tname"}; 945 } 946 947 my $disabled_file= "$testdir/$tname.disabled"; 948 if ( -f $disabled_file ) 949 { 950 $marked_as_disabled= 1; 951 $tinfo->{'comment'}= mtr_fromfile($disabled_file); 952 } 953 954 if ( $marked_as_disabled ) 955 { 956 if ( $enable_disabled ) 957 { 958 # User has selected to run all disabled tests 959 mtr_report(" - $tinfo->{name} wil be run although it's been disabled\n", 960 " due to '$tinfo->{comment}'"); 961 } 962 else 963 { 964 $tinfo->{'skip'}= 1; 965 $tinfo->{'disable'}= 1; # Sub type of 'skip' 966 return $tinfo; 967 } 968 } 969 970 # ---------------------------------------------------------------------- 971 # Append suite extra options to both master and slave 972 # ---------------------------------------------------------------------- 973 push(@{$tinfo->{'master_opt'}}, @$suite_opts); 974 push(@{$tinfo->{'slave_opt'}}, @$suite_opts); 975 976 #----------------------------------------------------------------------- 977 # Check for test specific config file 978 #----------------------------------------------------------------------- 979 my $test_cnf_file= "$testdir/$tname.cnf"; 980 if ( -f $test_cnf_file) { 981 # Specifies the configuration file to use for this test 982 $tinfo->{'template_path'}= $test_cnf_file; 983 } 984 985 # ---------------------------------------------------------------------- 986 # Check for test specific config file 987 # ---------------------------------------------------------------------- 988 my $test_cnf_file= "$testdir/$tname.cnf"; 989 if ( -f $test_cnf_file ) { 990 # Specifies the configuration file to use for this test 991 $tinfo->{'template_path'}= $test_cnf_file; 992 } 993 994 # ---------------------------------------------------------------------- 995 # master sh 996 # ---------------------------------------------------------------------- 997 my $master_sh= "$testdir/$tname-master.sh"; 998 if ( -f $master_sh ) 999 { 1000 if ( IS_WIN32PERL ) 1001 { 1002 $tinfo->{'skip'}= 1; 1003 $tinfo->{'comment'}= "No tests with sh scripts on Windows"; 1004 return $tinfo; 1005 } 1006 else 1007 { 1008 $tinfo->{'master_sh'}= $master_sh; 1009 } 1010 } 1011 1012 # ---------------------------------------------------------------------- 1013 # slave sh 1014 # ---------------------------------------------------------------------- 1015 my $slave_sh= "$testdir/$tname-slave.sh"; 1016 if ( -f $slave_sh ) 1017 { 1018 if ( IS_WIN32PERL ) 1019 { 1020 $tinfo->{'skip'}= 1; 1021 $tinfo->{'comment'}= "No tests with sh scripts on Windows"; 1022 return $tinfo; 1023 } 1024 else 1025 { 1026 $tinfo->{'slave_sh'}= $slave_sh; 1027 } 1028 } 1029 1030 # ---------------------------------------------------------------------- 1031 # <tname>.slave-mi 1032 # ---------------------------------------------------------------------- 1033 mtr_error("$tname: slave-mi not supported anymore") 1034 if ( -f "$testdir/$tname.slave-mi"); 1035 1036 1037 tags_from_test_file($tinfo,"$testdir/${tname}.test"); 1038 1039 if ( defined $default_storage_engine ) 1040 { 1041 # Different default engine is used 1042 # tag test to require that engine 1043 $tinfo->{'ndb_test'}= 1 1044 if ( $default_storage_engine =~ /^ndb/i ); 1045 1046 $tinfo->{'mysiam_test'}= 1 1047 if ( $default_storage_engine =~ /^mysiam/i ); 1048 1049 } 1050 1051 if ( $tinfo->{'big_test'} and ! $::opt_big_test ) 1052 { 1053 $tinfo->{'skip'}= 1; 1054 $tinfo->{'comment'}= "Test needs 'big-test' option"; 1055 return $tinfo 1056 } 1057 1058 if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) 1059 { 1060 $tinfo->{'skip'}= 1; 1061 $tinfo->{'comment'}= "Test needs debug binaries"; 1062 return $tinfo 1063 } 1064 1065 if ( $tinfo->{'ndb_test'} ) 1066 { 1067 # This is a NDB test 1068 if ( $::ndbcluster_enabled == 0) 1069 { 1070 # ndbcluster is disabled 1071 $tinfo->{'skip'}= 1; 1072 $tinfo->{'comment'}= "ndbcluster disabled"; 1073 return $tinfo; 1074 } 1075 } 1076 else 1077 { 1078 # This is not a ndb test 1079 if ( $opt_with_ndbcluster_only ) 1080 { 1081 # Only the ndb test should be run, all other should be skipped 1082 $tinfo->{'skip'}= 1; 1083 $tinfo->{'comment'}= "Only ndbcluster tests"; 1084 return $tinfo; 1085 } 1086 } 1087 1088 if ($tinfo->{'federated_test'}) 1089 { 1090 # This is a test that needs federated, enable it 1091 push(@{$tinfo->{'master_opt'}}, "--loose-federated"); 1092 push(@{$tinfo->{'slave_opt'}}, "--loose-federated"); 1093 } 1094 if ( $tinfo->{'myisam_test'}) 1095 { 1096 # This is a temporary fix to allow non-innodb tests to run even if 1097 # the default storage engine is innodb. 1098 push(@{$tinfo->{'master_opt'}}, "--default-storage-engine=MyISAM"); 1099 push(@{$tinfo->{'slave_opt'}}, "--default-storage-engine=MyISAM"); 1100 push(@{$tinfo->{'master_opt'}}, "--default-tmp-storage-engine=MyISAM"); 1101 push(@{$tinfo->{'slave_opt'}}, "--default-tmp-storage-engine=MyISAM"); 1102 } 1103 if ( $tinfo->{'need_binlog'} ) 1104 { 1105 if (grep(/^--skip[-_]log[-_]bin/, @::opt_extra_mysqld_opt) ) 1106 { 1107 $tinfo->{'skip'}= 1; 1108 $tinfo->{'comment'}= "Test needs binlog"; 1109 return $tinfo; 1110 } 1111 } 1112 else 1113 { 1114 # Test does not need binlog, add --skip-binlog to 1115 # the options used when starting 1116 push(@{$tinfo->{'master_opt'}}, "--loose-skip-log-bin"); 1117 push(@{$tinfo->{'slave_opt'}}, "--loose-skip-log-bin"); 1118 } 1119 1120 if ( $tinfo->{'rpl_test'} or $tinfo->{'grp_rpl_test'} ) 1121 { 1122 if ( $skip_rpl ) 1123 { 1124 $tinfo->{'skip'}= 1; 1125 $tinfo->{'comment'}= "No replication tests(--skip-rpl)"; 1126 return $tinfo; 1127 } 1128 } 1129 1130 if ( $::opt_embedded_server ) 1131 { 1132 if ( $tinfo->{'not_embedded'} ) 1133 { 1134 $tinfo->{'skip'}= 1; 1135 $tinfo->{'comment'}= "Not run for embedded server"; 1136 return $tinfo; 1137 } 1138#Setting the default storage engine to InnoDB for embedded tests as the default 1139#storage engine for mysqld in embedded mode is still MyISAM. 1140#To be removed after completion of WL #6911. 1141 if ( !$tinfo->{'myisam_test'} && !defined $default_storage_engine) 1142 { 1143 push(@{$tinfo->{'master_opt'}}, "--default-storage-engine=InnoDB"); 1144 push(@{$tinfo->{'master_opt'}}, "--default-tmp-storage-engine=InnoDB"); 1145 } 1146 } 1147 1148 if ( $tinfo->{'need_ssl'} ) 1149 { 1150 # This is a test that needs ssl 1151 if ( ! $::opt_ssl_supported ) { 1152 # SSL is not supported, skip it 1153 $tinfo->{'skip'}= 1; 1154 $tinfo->{'comment'}= "No SSL support"; 1155 return $tinfo; 1156 } 1157 } 1158 1159 # Check for group replication tests 1160 if ( $tinfo->{'grp_rpl_test'} ) 1161 { 1162 $::group_replication= 1; 1163 } 1164 1165 # Check for xplugin tests 1166 if ( $tinfo->{'xplugin_test'} ) 1167 { 1168 $::xplugin= 1; 1169 } 1170 1171 if ( $tinfo->{'not_windows'} && IS_WINDOWS ) 1172 { 1173 $tinfo->{'skip'}= 1; 1174 $tinfo->{'comment'}= "Test not supported on Windows"; 1175 return $tinfo; 1176 } 1177 1178 # ---------------------------------------------------------------------- 1179 # Find config file to use if not already selected in <testname>.opt file 1180 # ---------------------------------------------------------------------- 1181 if (defined $defaults_file) { 1182 # Using same config file for all tests 1183 $tinfo->{template_path}= $defaults_file; 1184 } 1185 elsif (! $tinfo->{template_path} ) 1186 { 1187 my $config= "$suitedir/my.cnf"; 1188 if (! -f $config ) 1189 { 1190 # assume default.cnf will be used 1191 $config= "include/default_my.cnf"; 1192 1193 # Suite has no config, autodetect which one to use 1194 if ( $tinfo->{rpl_test} ){ 1195 $config= "suite/rpl/my.cnf"; 1196 if ( $tinfo->{ndb_test} ){ 1197 $config= "suite/rpl_ndb/my.cnf"; 1198 } 1199 } 1200 elsif ( $tinfo->{ndb_test} ){ 1201 $config= "suite/ndb/my.cnf"; 1202 } 1203 } 1204 $tinfo->{template_path}= $config; 1205 } 1206 1207 # Set extra config file to use 1208 if (defined $defaults_extra_file) { 1209 $tinfo->{extra_template_path}= $defaults_extra_file; 1210 } 1211 1212 # ---------------------------------------------------------------------- 1213 # Append mysqld extra options to both master and slave 1214 # ---------------------------------------------------------------------- 1215 push(@{$tinfo->{'master_opt'}}, @::opt_extra_mysqld_opt); 1216 push(@{$tinfo->{'slave_opt'}}, @::opt_extra_mysqld_opt); 1217 1218 # ---------------------------------------------------------------------- 1219 # Add master opts, extra options only for master 1220 # ---------------------------------------------------------------------- 1221 process_opts_file($tinfo, "$testdir/$tname-master.opt", 'master_opt'); 1222 1223 # ---------------------------------------------------------------------- 1224 # Add slave opts, list of extra option only for slave 1225 # ---------------------------------------------------------------------- 1226 process_opts_file($tinfo, "$testdir/$tname-slave.opt", 'slave_opt'); 1227 1228 return $tinfo; 1229} 1230 1231 1232# List of tags in the .test files that if found should set 1233# the specified value in "tinfo" 1234my @tags= 1235( 1236 ["include/have_binlog_format_row.inc", "binlog_formats", ["row"]], 1237 ["include/have_binlog_format_statement.inc", "binlog_formats", ["statement"]], 1238 ["include/have_binlog_format_mixed.inc", "binlog_formats", ["mixed", "mix"]], 1239 ["include/have_binlog_format_mixed_or_row.inc", 1240 "binlog_formats", ["mixed", "mix", "row"]], 1241 ["include/have_binlog_format_mixed_or_statement.inc", 1242 "binlog_formats", ["mixed", "mix", "statement"]], 1243 ["include/have_binlog_format_row_or_statement.inc", 1244 "binlog_formats", ["row", "statement"]], 1245 1246 ["include/have_log_bin.inc", "need_binlog", 1], 1247# an empty file to use test that needs myisam engine. 1248 ["include/force_myisam_default.inc", "myisam_test", 1], 1249 ["include/big_test.inc", "big_test", 1], 1250 ["include/have_debug.inc", "need_debug", 1], 1251 ["include/have_ndb.inc", "ndb_test", 1], 1252 ["include/have_multi_ndb.inc", "ndb_test", 1], 1253 ["include/master-slave.inc", "rpl_test", 1], 1254 ["include/ndb_master-slave.inc", "rpl_test", 1], 1255 ["include/ndb_master-slave.inc", "ndb_test", 1], 1256 ["federated.inc", "federated_test", 1], 1257 ["include/not_embedded.inc", "not_embedded", 1], 1258 ["include/have_ssl.inc", "need_ssl", 1], 1259 ["include/have_ssl_communication.inc", "need_ssl", 1], 1260 ["include/not_windows.inc", "not_windows", 1], 1261 1262 # Tests with below .inc file are considered to be group replication tests 1263 ["have_group_replication_plugin_base.inc", "grp_rpl_test", 1], 1264 1265 # Tests with below .inc file are considered to be xplugin tests 1266 ["include/have_mysqlx_plugin.inc", "xplugin_test", 1], 1267); 1268 1269 1270sub tags_from_test_file { 1271 my $tinfo= shift; 1272 my $file= shift; 1273 #mtr_verbose("$file"); 1274 my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); 1275 1276 while ( my $line= <$F> ) 1277 { 1278 1279 # Skip line if it start's with # 1280 next if ( $line =~ /^#/ ); 1281 1282 # Match this line against tag in "tags" array 1283 foreach my $tag (@tags) 1284 { 1285 if ( index($line, $tag->[0]) >= 0 ) 1286 { 1287 # Tag matched, assign value to "tinfo" 1288 $tinfo->{"$tag->[1]"}= $tag->[2]; 1289 } 1290 } 1291 1292 # If test sources another file, open it as well 1293 if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ or 1294 $line =~ /^([[:space:]]*)source(.*);$/ ) 1295 { 1296 my $value= $2; 1297 $value =~ s/^\s+//; # Remove leading space 1298 $value =~ s/[[:space:]]+$//; # Remove ending space 1299 1300 # Sourced file may exist relative to test or 1301 # in global location 1302 foreach my $sourced_file (dirname($file). "/$value", 1303 "$::glob_mysql_test_dir/$value") 1304 { 1305 if ( -f $sourced_file ) 1306 { 1307 # Only source the file if it exists, we may get 1308 # false positives in the regexes above if someone 1309 # writes "source nnnn;" in a test case(such as mysqltest.test) 1310 tags_from_test_file($tinfo, $sourced_file); 1311 last; 1312 } 1313 } 1314 } 1315 1316 } 1317} 1318 1319sub unspace { 1320 my $string= shift; 1321 my $quote= shift; 1322 $string =~ s/[ \t]/\x11/g; 1323 return "$quote$string$quote"; 1324} 1325 1326 1327sub opts_from_file ($) { 1328 my $file= shift; 1329 1330 open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); 1331 my @args; 1332 while ( <FILE> ) 1333 { 1334 chomp; 1335 1336 # --init_connect=set @a='a\\0c' 1337 s/^\s+//; # Remove leading space 1338 s/\s+$//; # Remove ending space 1339 1340 # This is strange, but we need to fill whitespace inside 1341 # quotes with something, to remove later. We do this to 1342 # be able to split on space. Else, we have trouble with 1343 # options like 1344 # 1345 # --someopt="--insideopt1 --insideopt2" 1346 # 1347 # But still with this, we are not 100% sure it is right, 1348 # we need a shell to do it right. 1349 1350 s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; 1351 s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; 1352 s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; 1353 s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; 1354 1355 foreach my $arg (split(/[ \t]+/)) 1356 { 1357 $arg =~ tr/\x11\x0a\x0b/ \'\"/; # Put back real chars 1358 # The outermost quotes has to go 1359 $arg =~ s/^([^\'\"]*)\'(.*)\'([^\'\"]*)$/$1$2$3/ 1360 or $arg =~ s/^([^\'\"]*)\"(.*)\"([^\'\"]*)$/$1$2$3/; 1361 $arg =~ s/\\\\/\\/g; 1362 1363 # Do not pass empty string since my_getopt is not capable to handle it. 1364 if (length($arg)) { 1365 push(@args, $arg); 1366 } 1367 } 1368 } 1369 close FILE; 1370 return \@args; 1371} 1372 1373sub print_testcases { 1374 my (@cases)= @_; 1375 1376 print "=" x 60, "\n"; 1377 foreach my $test (@cases){ 1378 $test->print_test(); 1379 } 1380 print "=" x 60, "\n"; 1381} 1382 1383 13841; 1385