diff options
Diffstat (limited to 'mysql-test/lib')
-rw-r--r-- | mysql-test/lib/My/Config.pm | 12 | ||||
-rw-r--r-- | mysql-test/lib/My/ConfigFactory.pm | 43 | ||||
-rw-r--r-- | mysql-test/lib/My/Find.pm | 31 | ||||
-rw-r--r-- | mysql-test/lib/My/Suite.pm | 5 | ||||
-rw-r--r-- | mysql-test/lib/My/Test.pm | 17 | ||||
-rw-r--r-- | mysql-test/lib/mtr_cases.pm | 1178 | ||||
-rw-r--r-- | mysql-test/lib/mtr_misc.pl | 5 |
7 files changed, 594 insertions, 697 deletions
diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm index 1823ed59e02..1d8d1617fce 100644 --- a/mysql-test/lib/My/Config.pm +++ b/mysql-test/lib/My/Config.pm @@ -164,7 +164,6 @@ sub value { return $option->value(); } - # # Return value for an option if it exist # @@ -437,6 +436,17 @@ sub groups { # +# Return a list with "real" groups in config, those +# that should be written to a my.cnf file, those that contain options. +# Same as groups() but without auto-generated groups like ENV or OPT. +# +sub option_groups { + my ($self)= @_; + return ( grep { ref $_ eq 'My::Config::Group' } @{$self->{groups}} ); +} + + +# # Return a list of all the groups in config # starting with the given string # diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index 73bad5bd81d..ca93cbc543a 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -31,8 +31,19 @@ use File::Basename; # # Rules to run first of all # + +sub add_opt_values { + my ($self, $config)= @_; + + # add auto-options + $config->insert('OPT', 'port' => sub { fix_port($self, $config) }); + $config->insert('OPT', 'vardir' => sub { $self->{ARGS}->{vardir} }); + $config->insert('mysqld', "loose-skip-$_" => undef) for (@::optional_plugins); +} + my @pre_rules= ( + \&add_opt_values, ); @@ -165,13 +176,6 @@ sub fix_log_slow_queries { return "$dir/mysqld-slow.log"; } -sub fix_secure_file_priv { - my ($self)= @_; - my $vardir= $self->{ARGS}->{vardir}; - # By default, prevent the started mysqld to access files outside of vardir - return $vardir; -} - sub fix_std_data { my ($self, $config, $group_name, $group)= @_; my $testdir= $self->get_testdir($group); @@ -237,15 +241,14 @@ my @mysqld_rules= { 'port' => \&fix_port }, { 'socket' => \&fix_socket }, { '#log-error' => \&fix_log_error }, - { 'general-log' => sub { return 1; } }, + { 'general-log' => 1 }, + { 'plugin-dir' => sub { $::plugindir } }, { 'general-log-file' => \&fix_log }, - { 'slow-query-log' => sub { return 1; } }, + { 'slow-query-log' => 1 }, { 'slow-query-log-file' => \&fix_log_slow_queries }, { '#user' => sub { return shift->{ARGS}->{user} || ""; } }, { '#password' => sub { return shift->{ARGS}->{password} || ""; } }, { 'server-id' => \&fix_server_id, }, - # By default, prevent the started mysqld to access files outside of vardir - { 'secure-file-priv' => sub { return shift->{ARGS}->{vardir}; } }, { 'ssl-ca' => \&fix_ssl_ca }, { 'ssl-cert' => \&fix_ssl_server_cert }, { 'ssl-key' => \&fix_ssl_server_key }, @@ -441,7 +444,7 @@ sub post_check_embedded_group { my $first_mysqld= $config->first_like('mysqld.') or croak "Can't run with embedded, config has no mysqld"; - my @no_copy = + my %no_copy = map { $_ => 1 } ( '#log-error', # Embedded server writes stderr to mysqltest's log file 'slave-net-timeout', # Embedded server are not build with replication @@ -450,7 +453,7 @@ sub post_check_embedded_group { foreach my $option ( $mysqld->options(), $first_mysqld->options() ) { # Don't copy options whose name is in "no_copy" list - next if grep ( $option->name() eq $_, @no_copy); + next if $no_copy{$option->name()}; $config->insert('embedded', $option->name(), $option->value()) } @@ -641,19 +644,11 @@ sub new_config { testname => $args->{testname}, }, $class; - # add auto-options - $config->insert('OPT', 'port' => sub { fix_port($self, $config) }); - $config->insert('OPT', 'vardir' => sub { $self->{ARGS}->{vardir} }); - $config->insert('OPT', 'plugindir' => sub { $::plugindir }); - - { - # Run pre rules - foreach my $rule ( @pre_rules ) { - &$rule($self, $config); - } + # Run pre rules + foreach my $rule ( @pre_rules ) { + &$rule($self, $config); } - $self->run_section_rules($config, 'cluster_config\.\w*$', @cluster_config_rules); diff --git a/mysql-test/lib/My/Find.pm b/mysql-test/lib/My/Find.pm index bd153becaf7..1d5e0f368bc 100644 --- a/mysql-test/lib/My/Find.pm +++ b/mysql-test/lib/My/Find.pm @@ -64,7 +64,7 @@ sub my_find_bin { # ------------------------------------------------------- # Find and return the first executable # ------------------------------------------------------- - foreach my $path (my_find_paths($base, $paths, $names, $bin_extension)) { + foreach my $path (my_build_path_list($base, $paths, $names, $bin_extension)) { return $path if ( -x $path or (IS_WINDOWS and -f $path) ); } if (defined $required and $required == NOT_REQUIRED){ @@ -98,7 +98,7 @@ sub my_find_file { # ------------------------------------------------------- # Find and return the first executable # ------------------------------------------------------- - foreach my $path (my_find_paths($base, $paths, $names, $bin_extension)) { + foreach my $path (my_build_path_list($base, $paths, $names, $bin_extension)) { return $path if ( -f $path ); } if (defined $required and $required == NOT_REQUIRED){ @@ -110,8 +110,9 @@ sub my_find_file { # -# my_find_dir - find the first existing directory in one of -# the given paths +# my_find_dir - find the existing directories in one of +# the given paths. Returns the first found in the scalar context +# and all of them in the list context. # # Example: # my $charset_set= my_find_dir($basedir, @@ -127,20 +128,22 @@ sub my_find_file { # sub my_find_dir { my ($base, $paths, $dirs, $required)= @_; - croak "usage: my_find_dir(<base>, <paths>[, <dirs>])" - unless (@_ == 3 or @_ == 2); - - # ------------------------------------------------------- - # Find and return the first directory - # ------------------------------------------------------- - foreach my $path (my_find_paths($base, $paths, $dirs)) { - return $path if ( -d $path ); + croak "usage: my_find_dir(<base>, <paths>[, <dirs>[, <required>]])" + unless (@_ >= 2 and @_ <= 4); + + my @all; + foreach my $path (my_build_path_list($base, $paths, $dirs)) { + next unless -d $path; + return $path unless wantarray; + push @all, $path; } + return @all if @all; + return wantarray ? () : "" if defined $required and $required == NOT_REQUIRED; find_error($base, $paths, $dirs); } -sub my_find_paths { +sub my_build_path_list { my ($base, $paths, $names, $extension)= @_; # Convert the arguments into two normal arrays to ease @@ -237,7 +240,7 @@ sub find_error { croak "** ERROR: Could not find ", commify(fnuttify(@names)), " in ", - commify(fnuttify(my_find_paths($base, $paths, $names))), "\n"; + commify(fnuttify(my_build_path_list($base, $paths, $names))), "\n"; } 1; diff --git a/mysql-test/lib/My/Suite.pm b/mysql-test/lib/My/Suite.pm index c63e633ec3b..b5870ea1e16 100644 --- a/mysql-test/lib/My/Suite.pm +++ b/mysql-test/lib/My/Suite.pm @@ -5,10 +5,13 @@ package My::Suite; sub config_files { () } sub servers { () } +sub skip_combinations { () } + +sub new { bless { } } sub list_cases { my ($self, $testdir) = @_; - opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); + opendir(TESTDIR, $testdir) or return (); my (@cases) = grep { s/\.test$// } readdir TESTDIR; closedir TESTDIR; @cases; diff --git a/mysql-test/lib/My/Test.pm b/mysql-test/lib/My/Test.pm index 04dccfdaa4a..2d2bac3be03 100644 --- a/mysql-test/lib/My/Test.pm +++ b/mysql-test/lib/My/Test.pm @@ -35,10 +35,23 @@ sub new { return $self; } +sub copy { + my $self= shift; + my $copy= My::Test->new(); + while (my ($key, $value) = each(%$self)) { + if (ref $value eq "ARRAY") { + $copy->{$key} = [ @$value ]; + } else { + $copy->{$key}= $value; + } + } + $copy; +} + sub fullname { my ($self)= @_; - $self->{name} . (defined $self->{combination} - ? " '$self->{combination}'" + $self->{name} . (defined $self->{combinations} + ? " '" . join(',', sort @{$self->{combinations}}) . "'" : "") } diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index 3c9afadfe2f..beb0716d4a7 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -29,18 +29,13 @@ use mtr_report; use mtr_match; # Options used for the collect phase -our $start_from; -our $print_testcases; our $skip_rpl; our $do_test; our $skip_test; -our $skip_combinations; our $binlog_format; our $enable_disabled; our $default_storage_engine; our $opt_with_ndbcluster_only; -our $defaults_file; -our $quick_collect; sub collect_option { my ($opt, $value)= @_; @@ -61,6 +56,7 @@ use My::Config; use My::Platform; use My::Test; use My::Find; +use My::Suite; require "mtr_misc.pl"; @@ -68,10 +64,7 @@ require "mtr_misc.pl"; my $do_test_reg; my $skip_test_reg; -# If "Quick collect", set to 1 once a test to run has been found. -my $some_test_found; - -my $default_suite_object = do 'My/Suite.pm'; +my %suites; sub init_pattern { my ($from, $what)= @_; @@ -90,21 +83,6 @@ sub init_pattern { } -sub testcase_sort_order { - my ($a, $b, $sort_criteria)= @_; - # Run slow tests first, trying to avoid getting stuck at the end - # with a slow test in one worker and the other workers idle. - return -1 if $a->{'long_test'} && !$b->{'long_test'}; - return 1 if !$a->{'long_test'} && $b->{'long_test'}; - - my $a_sort_criteria= $sort_criteria->{$a->fullname()}; - my $b_sort_criteria= $sort_criteria->{$b->fullname()}; - my $res= $a_sort_criteria cmp $b_sort_criteria; - return $res if $res; - - return $a->fullname() cmp $b->fullname(); -} - ############################################################################## # # Collect information about test cases to be run @@ -121,6 +99,8 @@ sub collect_test_cases ($$$$) { $do_test_reg= init_pattern($do_test, "--do-test"); $skip_test_reg= init_pattern($skip_test, "--skip-test"); + parse_disabled($_) for @$opt_skip_test_list; + # If not reordering, we also shouldn't group by suites, unless # no test cases were named. # This also effects some logic in the loop following this. @@ -128,8 +108,7 @@ sub collect_test_cases ($$$$) { { foreach my $suite (split(",", $suites)) { - push(@$cases, collect_one_suite($suite, $opt_cases, $opt_skip_test_list)); - last if $some_test_found; + push(@$cases, collect_one_suite($suite, $opt_cases)); } } @@ -157,7 +136,7 @@ sub collect_test_cases ($$$$) { $sname= "main" if !$opt_reorder and !$sname; mtr_error("Could not find '$tname' in '$suites' suite(s)") unless $sname; # If suite was part of name, find it there, may come with combinations - my @this_case = collect_one_suite($sname, [ $tname ]); + my @this_case = collect_one_suite($sname, [ $test_name_spec ]); if (@this_case) { push (@$cases, @this_case); @@ -170,11 +149,8 @@ sub collect_test_cases ($$$$) { } } - if ( $opt_reorder && !$quick_collect) + if ( $opt_reorder ) { - # Reorder the test cases in an order that will make them faster to run - my %sort_criteria; - # Make a mapping of test name to a string that represents how that test # should be sorted among the other tests. Put the most important criterion # first, then a sub-criterion, then sub-sub-criterion, etc. @@ -183,44 +159,31 @@ sub collect_test_cases ($$$$) { my @criteria = (); # - # Append the criteria for sorting, in order of importance. + # Collect the criteria for sorting, in order of importance. + # Note that criteria are also used in mysql-test-run.pl to + # schedule tests to workers, and it preferres tests that have + # *identical* criteria. That is, test name is *not* part of + # the criteria, but it's part of the sorting function below. # - push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "A" : "B")); push(@criteria, $tinfo->{template_path}); - # Group test with equal options together. - # Ending with "~" makes empty sort later than filled - my $opts= $tinfo->{'master_opt'} ? $tinfo->{'master_opt'} : []; - push(@criteria, join("!", sort @{$opts}) . "~"); - # Add slave opts if any - if ($tinfo->{'slave_opt'}) - { - push(@criteria, join("!", sort @{$tinfo->{'slave_opt'}})); + for (qw(master_opt slave_opt)) { + # Group test with equal options together. + # Ending with "~" makes empty sort later than filled + my $opts= $tinfo->{$_} ? $tinfo->{$_} : []; + push(@criteria, join("!", sort @{$opts}) . "~"); } - # This sorts tests with force-restart *before* identical tests - push(@criteria, $tinfo->{force_restart} ? "force-restart" : "no-restart"); - $tinfo->{criteria}= join(" ", @criteria); - $sort_criteria{$tinfo->fullname()} = $tinfo->{criteria}; } - @$cases = sort { testcase_sort_order($a, $b, \%sort_criteria) } @$cases; - - # For debugging the sort-order - # foreach my $tinfo (@$cases) - # { - # my $tname= $tinfo->{name} . ' ' . $tinfo->{combination}; - # my $crit= $tinfo->{criteria}; - # print("$tname\n\t$crit\n"); - # } - } - - if (defined $print_testcases){ - print_testcases(@$cases); - exit(1); + @$cases = sort { # ORDER BY + $b->{skip} <=> $a->{skip} || # skipped DESC, + $a->{criteria} cmp $b->{criteria} || # criteria ASC, + $b->{long_test} <=> $a->{long_test} || # long_test DESC, + $a->{name} cmp $b->{name} # name ASC + } @$cases; } return $cases; - } @@ -254,189 +217,226 @@ sub split_testname { mtr_error("Illegal format of test name: $test_name"); } +our %file_to_tags; +our %file_to_master_opts; +our %file_to_slave_opts; +our %file_combinations; +our %skip_combinations; +our %file_in_overlay; + +sub load_suite_object { + my ($suitename, $suitedir) = @_; + my $suite; + unless (defined $suites{$suitename}) { + if (-f "$suitedir/suite.pm") { + $suite= do "$suitedir/suite.pm"; + unless (ref $suite) { + my $comment = $suite; + $suite = My::Suite->new(); + $suite->{skip} = $comment; + } + } else { + $suite = My::Suite->new(); + } + + $suites{$suitename} = $suite; + + # add suite skiplist to a global hash, so that we can check it + # with only one lookup + my %suite_skiplist = $suite->skip_combinations(); + while (my ($file, $skiplist) = each %suite_skiplist) { + $file =~ s/\.\w+$/\.combinations/; + if (ref $skiplist) { + $skip_combinations{"$suitedir/$file => $_"} = 1 for (@$skiplist); + } else { + $skip_combinations{"$suitedir/$file"} = $skiplist; + } + } + } + return $suites{$suitename}; +} + + +# returns a pair of (suite, suitedir) +sub load_suite_for_file($) { + my ($file) = @_; + return load_suite_object($2, $1) + if $file =~ m@^(.*/(?:storage|plugin)/\w+/mysql-test/(\w+))/@; + return load_suite_object($2, $1) if $file =~ m@^(.*/mysql-test/suite/(\w+))/@; + return load_suite_object('main', $1) if $file =~ m@^(.*/mysql-test)/@; + mtr_error("Cannot determine suite for $file"); +} + +sub combinations_from_file($$) +{ + my ($in_overlay, $filename) = @_; + my @combs; + if ($skip_combinations{$filename}) { + @combs = ({ skip => $skip_combinations{$filename} }); + } else { + return () if @::opt_combinations or not -f $filename; + # Read combinations file in my.cnf format + mtr_verbose("Read combinations file"); + my $config= My::Config->new($filename); + foreach my $group ($config->option_groups()) { + my $comb= { name => $group->name(), comb_opt => [] }; + next if $skip_combinations{"$filename => $comb->{name}"}; + foreach my $option ( $group->options() ) { + push(@{$comb->{comb_opt}}, $option->option()); + } + $comb->{in_overlay} = 1 if $in_overlay; + push @combs, $comb; + } + @combs = ({ skip => 'Requires: ' . basename($filename, '.combinations') }) unless @combs; + } + @combs; +} + +our %disabled; +sub parse_disabled { + my ($filename, $suitename) = @_; + + if (open(DISABLED, $filename)) { + while (<DISABLED>) { + chomp; + next if /^\s*#/ or /^\s*$/; + mtr_error("Syntax error in $filename line $.") + unless /^\s*(?:([-0-9A-Za-z_]+)\.)?([-0-9A-Za-z_]+)\s*:\s*(.*?)\s*$/; + mtr_error("Wrong suite name in $filename line $.") + if defined $1 and defined $suitename and $1 ne $suitename; + $disabled{($1 || $suitename || '') . ".$2"} = $3; + } + close DISABLED; + } +} sub collect_one_suite { - my $suite= shift; # Test suite name + my $suitename= shift; # Test suite name my $opt_cases= shift; - my $opt_skip_test_list= shift; - my @cases; # Array of hash + my $over; - mtr_verbose("Collecting: $suite"); + ($suitename, $over) = split '-', $suitename; - my $suitedir= "$::glob_mysql_test_dir"; # Default - if ( $suite ne "main" ) + mtr_verbose("Collecting: $suitename"); + + my $suitedir= $::glob_mysql_test_dir; # Default + my @overlays = (); + if ( $suitename ne "main" ) { - # Allow suite to be path to "some dir" if $suite has at least + # Allow suite to be path to "some dir" if $suitename has at least # one directory part - if ( -d $suite and splitdir($suite) > 1 ){ - $suitedir= $suite; + if ( -d $suitename and splitdir($suitename) > 1 ){ + $suitedir= $suitename; mtr_report(" - from '$suitedir'"); } else { - $suitedir= my_find_dir($::basedir, - ["share/mysql-test/suite", - "share/mysql/mysql-test/suite", - "mysql-test/suite", - "mysql-test", - # Look in storage engine specific suite dirs - "storage/*/mtr", - # Look in plugin specific suite dir - "plugin/$suite/tests", - ], - [$suite, "mtr"]); - } - mtr_verbose("suitedir: $suitedir"); - } - - my $testdir= "$suitedir/t"; - my $resdir= "$suitedir/r"; - - # Check if t/ exists - if (-d $testdir){ - # t/ exists + @overlays = my_find_dir(dirname($::glob_mysql_test_dir), + ["mysql-test/suite", + "storage/*/mysql-test", + "plugin/*/mysql-test"], + [$suitename]); + # + # XXX at the moment, for simplicity, we will not fully support one plugin + # overlaying a suite of another plugin. Only suites in the main + # mysql-test directory can be safely overlayed. To be fixed, when needed. + # To fix it we'll need a smarter overlay detection (that is, detection of + # what is an overlay and what is the "original" suite) than simply + # "prefer directories with more files". + # - if ( -d $resdir ) - { - # r/exists - } - else - { - # No r/, use t/ as result dir - $resdir= $testdir; + if ($overlays[0] !~ m@/mysql-test/suite/$suitename$@) { + # prefer directories with more files + @overlays = sort { scalar(<$a/*>) <=> scalar(<$b/*>) } @overlays; + } + $suitedir = shift @overlays; } - + } else { + @overlays = my_find_dir(dirname($::glob_mysql_test_dir), + ["storage/*/mysql-test", + "plugin/*/mysql-test"], + ['main'], NOT_REQUIRED); + } + mtr_verbose("suitedir: $suitedir"); + mtr_verbose("overlays: @overlays") if @overlays; + + # we always need to process the parent suite, even if we won't use any + # test from it. + my @cases= process_suite($suitename, undef, $suitedir, + $over ? [ '*BOGUS*' ] : $opt_cases); + + # when working with overlays we cannot use global caches like + # %file_to_tags. Because the same file may have different tags + # with and without overlays. For example, when a.test includes + # b.inc, which includes c.inc, and an overlay replaces c.inc. + # In this case b.inc may have different tags in the overlay, + # despite the fact that b.inc itself is not replaced. + for (@overlays) { + local %file_to_tags = (); + local %file_to_master_opts = (); + local %file_to_slave_opts = (); + local %file_combinations = (); + local %file_in_overlay = (); + + die unless m@/(?:storage|plugin)/(\w+)/mysql-test/\w+$@; + next unless defined $over and ($over eq '' or $over eq $1); + push @cases, + # don't add cases that take *all* data from the parent suite + grep { $_->{in_overlay} } process_suite($suitename, $1, $_, $opt_cases); } - else { - # No t/ dir => there can' be any r/ dir - mtr_error("Can't have r/ dir without t/") if -d $resdir; + return @cases; +} - # No t/ or r/ => use suitedir - $resdir= $testdir= $suitedir; +sub process_suite { + my ($basename, $overname, $suitedir, $opt_cases) = @_; + my $suitename; + my $parent; + + if ($overname) { + $parent = $suites{$basename}; + die unless $parent; + $suitename = $basename . '-' . $overname; + } else { + $suitename = $basename; } - mtr_verbose("testdir: $testdir"); - mtr_verbose("resdir: $resdir"); + my $suite = load_suite_object($suitename, $suitedir); # - # Load the Suite object + # Read suite config files, unless it was done aleady # - unless ($::suites{$suite}) { - if (-f "$suitedir/suite.pm") { - $::suites{$suite} = do "$suitedir/suite.pm"; + unless (defined $suite->{name}) { + $suite->{name} = $suitename; + $suite->{dir} = $suitedir; + + # First, we need to find where the test files and result files are. + # test files are usually in a t/ dir inside suite dir. Or directly in the + # suite dir. result files are in a r/ dir or in the suite dir. + # Overlay uses t/ and r/ if and only if its parent does. + if ($parent) { + $suite->{parent} = $parent; + my $tdir = $parent->{tdir}; + my $rdir = $parent->{rdir}; + substr($tdir, 0, length $parent->{dir}) = $suitedir; + substr($rdir, 0, length $parent->{dir}) = $suitedir; + $suite->{tdir} = $tdir if -d $tdir; + $suite->{rdir} = $rdir if -d $rdir; } else { - $::suites{$suite} = $default_suite_object; + my $tdir= "$suitedir/t"; + my $rdir= "$suitedir/r"; + $suite->{tdir} = -d $tdir ? $tdir : $suitedir; + $suite->{rdir} = -d $rdir ? $rdir : $suite->{tdir}; } - } - # ---------------------------------------------------------------------- - # Build a hash of disabled testcases for this suite - # ---------------------------------------------------------------------- - my %disabled; - my @disabled_collection= @{$opt_skip_test_list} if defined @{$opt_skip_test_list}; - unshift (@disabled_collection, "$testdir/disabled.def"); - for my $skip (@disabled_collection) - { - if ( open(DISABLED, $skip ) ) - { - # $^O on Windows considered not generic enough - my $plat= (IS_WINDOWS) ? 'windows' : $^O; - - while ( <DISABLED> ) - { - chomp; - #diasble the test case if platform matches - if ( /\@/ ) - { - if ( /\@$plat/ ) - { - /^\s*(\S+)\s*\@$plat.*:\s*(.*?)\s*$/ ; - $disabled{$1}= $2 if not exists $disabled{$1}; - } - elsif ( /\@!(\S*)/ ) - { - if ( $1 ne $plat) - { - /^\s*(\S+)\s*\@!.*:\s*(.*?)\s*$/ ; - $disabled{$1}= $2 if not exists $disabled{$1}; - } - } - } - elsif ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) - { - chomp; - if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) - { - $disabled{$1}= $2 if not exists $disabled{$1}; - } - } - } - close DISABLED; - } - } - - # Read suite.opt file - my $suite_opts= [ opts_from_file("$testdir/suite.opt") ]; - $suite_opts = [ opts_from_file("$suitedir/suite.opt") ] unless @$suite_opts; + mtr_verbose("testdir: " . $suite->{tdir}); + mtr_verbose( "resdir: " . $suite->{rdir}); - my @case_names; - { - my $s= $::suites{$suite}; - $s = 'My::Suite' unless ref $s; - @case_names= $s->list_cases($testdir); - } - - if ( @$opt_cases ) - { - my (%case_names)= map { $_ => 1 } @case_names; - @case_names= (); - - # Collect in specified order - foreach my $test_name_spec ( @$opt_cases ) - { - my ($sname, $tname)= split_testname($test_name_spec); + # disabled.def + parse_disabled($suite->{dir} .'/disabled.def', $suitename); - # Check correct suite if suitename is defined - next if (defined $sname and $suite ne $sname); - - # Extension was specified, check if the test exists - if ( ! $case_names{$tname}) - { - # This is only an error if suite was specified, otherwise it - # could exist in another suite - mtr_error("Test '$tname' was not found in suite '$sname'") - if $sname; - - next; - } - push @case_names, $tname; - } - } - - foreach (@case_names) - { - # Skip tests that do not match the --do-test= filter - next if ($do_test_reg and not $_ =~ /$do_test_reg/o); - - push(@cases, collect_one_test_case($suitedir, $testdir, $resdir, - $suite, $_, "$_.test", \%disabled, - $suite_opts)); - } - - # Return empty list if no testcases found - return if (@cases == 0); - - # ---------------------------------------------------------------------- - # Read combinations for this suite and build testcases x combinations - # if any combinations exists - # ---------------------------------------------------------------------- - if ( ! $skip_combinations && ! $quick_collect ) - { - my @combinations; - my $combination_file= "$suitedir/combinations"; - #print "combination_file: $combination_file\n"; + # combinations if (@::opt_combinations) { # take the combination from command-line @@ -445,195 +445,57 @@ sub collect_one_suite my $comb= {}; $comb->{name}= $combination; push(@{$comb->{comb_opt}}, $combination); - push(@combinations, $comb); + push @{$suite->{combinations}}, $comb; } } - elsif (-f $combination_file ) + else { - # Read combinations file in my.cnf format - mtr_verbose("Read combinations file"); - my %env_filter = map { $_ => 1 } split /:/, $ENV{"\U${suite}_COMBINATIONS"}; - my $config= My::Config->new($combination_file); - foreach my $group ($config->groups()) { - my $comb= {}; - $comb->{name}= $group->name(); - next if %env_filter and not $env_filter{$comb->{name}}; - foreach my $option ( $group->options() ) { - push(@{$comb->{comb_opt}}, $option->option()); - } - push(@combinations, $comb) if $comb->{comb_opt}; - } + my @combs; + @combs = combinations_from_file($parent, "$suitedir/combinations") + unless $suite->{skip}; + $suite->{combinations} = [ @combs ]; + # in overlays it's a union of parent's and overlay's files. + unshift @{$suite->{combinations}}, @{$parent->{combinations}} if $parent; } - if (@combinations) - { - print " - adding combinations for $suite\n"; - #print_testcases(@cases); + # suite.opt + # in overlays it's a union of parent's and overlay's files. + $suite->{opts} = [ opts_from_file("$suitedir/suite.opt") ]; + $suite->{in_overlay} = 1 if $parent and @{$suite->{opts}}; + unshift @{$suite->{opts}}, @{$parent->{opts}} if $parent; - my @new_cases; - TEST: foreach my $test (@cases) - { - if ( $test->{'skip'} ) - { - push(@new_cases, $test); - next; - } - - foreach my $comb (@combinations) - { - # Skip all other combinations if the values they change - # are already fixed in master_opt or slave_opt - if (My::Options::is_set($test->{master_opt}, $comb->{comb_opt}) && - My::Options::is_set($test->{slave_opt}, $comb->{comb_opt}) ){ - - # Add combination name short name - $test->{combination}= $comb->{name}; - - # Add the test to new test cases list - push(@new_cases, $test); - next TEST; - } - } - - foreach my $comb (@combinations) - { - # Copy test options - my $new_test= My::Test->new(); - while (my ($key, $value) = each(%$test)) { - if (ref $value eq "ARRAY") { - push(@{$new_test->{$key}}, @$value); - } else { - $new_test->{$key}= $value; - } - } - - # Append the combination options to master_opt and slave_opt - push(@{$new_test->{master_opt}}, @{$comb->{comb_opt}}); - push(@{$new_test->{slave_opt}}, @{$comb->{comb_opt}}); - - # Add combination name short name - $new_test->{combination}= $comb->{name}; - - # Add the new test to new test cases list - push(@new_cases, $new_test); - } - } - - #print_testcases(@new_cases); - @cases= @new_cases; - #print_testcases(@cases); - } + $suite->{cases} = [ $suite->list_cases($suite->{tdir}) ]; } - optimize_cases(\@cases); - #print_testcases(@cases); - - return @cases; -} - - + my %all_cases; + %all_cases = map { $_ => $parent->{tdir} } @{$parent->{cases}} if $parent; + $all_cases{$_} = $suite->{tdir} for @{$suite->{cases}}; -# -# Loop through all test cases -# - optimize which test to run by skipping unnecessary ones -# - update settings if necessary -# -sub optimize_cases { - my ($cases)= @_; - - my @new_cases= (); - - foreach my $tinfo ( @$cases ) - { - push @new_cases, $tinfo; - - # Skip processing if already marked as skipped - next if $tinfo->{skip}; - - # ======================================================= - # If a special binlog format was selected with - # --mysqld=--binlog-format=x, skip all test that does not - # support it - # ======================================================= - #print "binlog_format: $binlog_format\n"; - if (not defined $binlog_format ) + my @cases; + if (@$opt_cases) { + # Collect in specified order + foreach my $test_name_spec ( @$opt_cases ) { - # ======================================================= - # Use dynamic switching of binlog format - # ======================================================= - - # Get binlog-format used by this test from master_opt - my $test_binlog_format; - foreach my $opt ( @{$tinfo->{master_opt}} ) { - $test_binlog_format= - mtr_match_prefix($opt, "--binlog-format=") || $test_binlog_format; - } - - if (defined $test_binlog_format and - defined $tinfo->{binlog_formats} ) - { - my $supported= - grep { $_ eq $test_binlog_format } @{$tinfo->{'binlog_formats'}}; - if ( !$supported ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= - "Doesn't support --binlog-format='$test_binlog_format'"; - # This test was added as a replication combination, but it is not - # actually ever possible to run it, as it is not made for this - # combination. - # So delete it from the list, rather than confuse the user with a - # message that this test is skipped (it is not really, just run - # with other combinations). - pop(@new_cases); - next; - } - } - } - - # ======================================================= - # Check that engine selected by - # --default-storage-engine=<engine> is supported - # ======================================================= - my %builtin_engines = ('myisam' => 1, 'memory' => 1, 'csv' => 1); - - foreach my $opt ( @{$tinfo->{master_opt}} ) { - my $default_engine= - mtr_match_prefix($opt, "--default-storage-engine="); - - # Allow use of uppercase, convert to all lower case - $default_engine =~ tr/A-Z/a-z/; - - if (defined $default_engine){ - - #print " $tinfo->{name}\n"; - #print " - The test asked to use '$default_engine'\n"; - - #my $engine_value= $::mysqld_variables{$default_engine}; - #print " - The mysqld_variables says '$engine_value'\n"; + my ($sname, $tname)= split_testname($test_name_spec); - if ( ! exists $::mysqld_variables{$default_engine} and - ! exists $builtin_engines{$default_engine} ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= - "'$default_engine' not supported"; - } + # Check correct suite if suitename is defined + next if defined $sname and $sname ne $suitename + and $sname ne "$basename-"; - $tinfo->{'ndb_test'}= 1 - if ( $default_engine =~ /^ndb/i ); - } + next unless $all_cases{$tname}; + push @cases, collect_one_test_case($suite, $all_cases{$tname}, $tname); } - - if ($quick_collect && ! $tinfo->{'skip'}) + } else { + for (sort keys %all_cases) { - $some_test_found= 1; - return; + # Skip tests that do not match the --do-test= filter + next if $do_test_reg and not /$do_test_reg/o; + push @cases, collect_one_test_case($suite, $all_cases{$_}, $_); } } - @$cases= @new_cases; -} + @cases; +} # # Read options from the given opt file and append them as an array @@ -660,23 +522,6 @@ sub process_opts { next; } - $value= mtr_match_prefix($opt, "--result-file="); - if ( defined $value ) - { - # Specifies the file mysqltest should compare - # output against - $tinfo->{'result_file'}= "r/$value.result"; - next; - } - - $value= mtr_match_prefix($opt, "--config-file-template="); - if ( defined $value) - { - # Specifies the configuration file to use for this test - $tinfo->{'template_path'}= dirname($tinfo->{path})."/$value"; - next; - } - # If we set default time zone, remove the one we have $value= mtr_match_prefix($opt, "--default-time-zone="); if ( defined $value ) @@ -686,29 +531,77 @@ sub process_opts { # Fallthrough, add the --default-time-zone option } - # The --restart option forces a restart even if no special - # option is set. If the options are the same as next testcase - # there is no need to restart after the testcase - # has completed - if ( $opt eq "--force-restart" ) - { - $tinfo->{'force_restart'}= 1; - next; - } + # Ok, this was a real option, add it + push(@{$tinfo->{$opt_name}}, $opt); + } +} - $value= mtr_match_prefix($opt, "--testcase-timeout="); - if ( defined $value ) { - # Overrides test case timeout for this test - $tinfo->{'case-timeout'}= $value; - next; +sub make_combinations($@) +{ + my ($test, @combinations) = @_; + + return ($test) if $test->{'skip'} or not @combinations; + if ($combinations[0]->{skip}) { + $test->{skip} = 1; + $test->{comment} = $combinations[0]->{skip} unless $test->{comment}; + die unless @combinations == 1; + return ($test); + } + + foreach my $comb (@combinations) + { + # Skip all other combinations if the values they change + # are already fixed in master_opt or slave_opt + if (My::Options::is_set($test->{master_opt}, $comb->{comb_opt}) && + My::Options::is_set($test->{slave_opt}, $comb->{comb_opt}) ){ + + # Add combination name short name + push @{$test->{combinations}}, $comb->{name}; + + return ($test); } + } - # Ok, this was a real option, add it - push(@{$tinfo->{$opt_name}}, $opt); + my @cases; + foreach my $comb (@combinations) + { + # Copy test options + my $new_test= $test->copy(); + + # Prepend the combination options to master_opt and slave_opt + # (on the command line combinations go *before* .opt files) + unshift @{$new_test->{master_opt}}, @{$comb->{comb_opt}}; + unshift @{$new_test->{slave_opt}}, @{$comb->{comb_opt}}; + + # Add combination name short name + push @{$new_test->{combinations}}, $comb->{name}; + + $new_test->{in_overlay} = 1 if $comb->{in_overlay}; + + # Add the new test to new test cases list + push(@cases, $new_test); } + return @cases; } +sub find_file_in_dirs +{ + my ($tinfo, $slot, $filename) = @_; + my $parent = $tinfo->{suite}->{parent}; + my $f = $tinfo->{suite}->{$slot} . '/' . $filename; + + if (-f $f) { + $tinfo->{in_overlay} = 1 if $parent; + return $f; + } + + return undef unless $parent; + + $f = $parent->{$slot} . '/' . $filename; + return -f $f ? $f : undef; +} + ############################################################################## # # Collect information about a single test case @@ -716,72 +609,33 @@ sub process_opts { ############################################################################## sub collect_one_test_case { - my $suitedir= shift; - my $testdir= shift; - my $resdir= shift; - my $suitename= shift; - my $tname= shift; - my $filename= shift; - my $disabled= shift; - my $suite_opts= shift; - - my $local_default_storage_engine= $default_storage_engine; - - #print "collect_one_test_case\n"; - #print " suitedir: $suitedir\n"; - #print " testdir: $testdir\n"; - #print " resdir: $resdir\n"; - #print " suitename: $suitename\n"; - #print " tname: $tname\n"; - #print " filename: $filename\n"; + my $suite = shift; + my $tpath = shift; + my $tname = shift; - # ---------------------------------------------------------------------- - # Check --start-from - # ---------------------------------------------------------------------- - if ( $start_from && 0) - { - # start_from can be specified as [suite.].testname_prefix - my ($suite, $test)= split_testname($start_from); - - if ( $suite and $suitename lt $suite){ - return; # Skip silently - } - if ((!$suite || $suitename == $suite) && $tname lt $test ){ - return; # Skip silently - } - } + my $suitename = $suite->{name}; + my $name = "$suitename.$tname"; + my $filename = "$tpath/${tname}.test"; # ---------------------------------------------------------------------- # Set defaults # ---------------------------------------------------------------------- my $tinfo= My::Test->new ( - name => "$suitename.$tname", + name => $name, shortname => $tname, - path => "$testdir/$filename", - suite => $suitename, + path => $filename, + suite => $suite, + in_overlay => $suite->{in_overlay}, + master_opt => [ @{$suite->{opts}} ], + slave_opt => [ @{$suite->{opts}} ], ); - my $result_file= "$resdir/$tname.result"; - if (-f $result_file) { - # Allow nonexistsing result file - # in that case .test must issue "exit" otherwise test - # should fail by default - $tinfo->{result_file}= $result_file; - } - else { - # No .result file exist - # Remember the path where it should be - # saved in case of --record - $tinfo->{record_file}= $result_file; - } - # ---------------------------------------------------------------------- # Skip some tests but include in list, just mark them as skipped # ---------------------------------------------------------------------- - my $name= basename($suitename) . ".$tname"; - if ( $skip_test_reg and ($tname =~ /$skip_test_reg/o || - $name =~ /$skip_test/o)) + if ( $skip_test_reg and ($tname =~ /$skip_test_reg/o or + $name =~ /$skip_test_reg/o)) { $tinfo->{'skip'}= 1; return $tinfo; @@ -790,50 +644,42 @@ sub collect_one_test_case { # ---------------------------------------------------------------------- # Check for disabled tests # ---------------------------------------------------------------------- - my $marked_as_disabled= 0; - if ( $disabled->{$tname} or $disabled->{"$suitename.$tname"} ) - { - # Test was marked as disabled in suites disabled.def file - $marked_as_disabled= 1; - # Test name may have been disabled with or without suite name part - $tinfo->{'comment'}= $disabled->{$tname} || - $disabled->{"$suitename.$tname"}; + my $disable = $disabled{".$tname"} || $disabled{$name}; + if (not defined $disable and $suite->{parent}) { + $disable = $disabled{$suite->{parent}->{name} . ".$tname"}; } - - my $disabled_file= "$testdir/$tname.disabled"; - if ( -f $disabled_file ) - { - $marked_as_disabled= 1; - $tinfo->{'comment'}= mtr_fromfile($disabled_file); - } - - if ( $marked_as_disabled ) + if (defined $disable) { + $tinfo->{comment}= $disable; if ( $enable_disabled ) { # User has selected to run all disabled tests mtr_report(" - $tinfo->{name} wil be run although it's been disabled\n", - " due to '$tinfo->{comment}'"); + " due to '$disable'"); } else { $tinfo->{'skip'}= 1; $tinfo->{'disable'}= 1; # Sub type of 'skip' - return $tinfo; + + # we can stop test file processing early if the test if disabled, but + # only if we're not in the overlay. for overlays we want to know exactly + # whether the test is ignored (in_overlay=0) or disabled. + return $tinfo unless $suite->{parent}; } } - # ---------------------------------------------------------------------- - # Append suite extra options to both master and slave - # ---------------------------------------------------------------------- - push(@{$tinfo->{'master_opt'}}, @$suite_opts); - push(@{$tinfo->{'slave_opt'}}, @$suite_opts); + if ($suite->{skip}) { + $tinfo->{skip}= 1; + $tinfo->{comment}= $suite->{skip} unless $tinfo->{comment}; + return $tinfo unless $suite->{parent}; + } # ---------------------------------------------------------------------- # Check for test specific config file # ---------------------------------------------------------------------- - my $test_cnf_file= "$testdir/$tname.cnf"; - if ( -f $test_cnf_file ) { + my $test_cnf_file= find_file_in_dirs($tinfo, tdir => "$tname.cnf"); + if ($test_cnf_file ) { # Specifies the configuration file to use for this test $tinfo->{'template_path'}= $test_cnf_file; } @@ -841,8 +687,8 @@ sub collect_one_test_case { # ---------------------------------------------------------------------- # master sh # ---------------------------------------------------------------------- - my $master_sh= "$testdir/$tname-master.sh"; - if ( -f $master_sh ) + my $master_sh= find_file_in_dirs($tinfo, tdir => "$tname-master.sh"); + if ($master_sh) { if ( IS_WIN32PERL ) { @@ -859,8 +705,8 @@ sub collect_one_test_case { # ---------------------------------------------------------------------- # slave sh # ---------------------------------------------------------------------- - my $slave_sh= "$testdir/$tname-slave.sh"; - if ( -f $slave_sh ) + my $slave_sh= find_file_in_dirs($tinfo, tdir => "$tname-slave.sh"); + if ($slave_sh) { if ( IS_WIN32PERL ) { @@ -874,32 +720,8 @@ sub collect_one_test_case { } } - # ---------------------------------------------------------------------- - # <tname>.slave-mi - # ---------------------------------------------------------------------- - mtr_error("$tname: slave-mi not supported anymore") - if ( -f "$testdir/$tname.slave-mi"); - - - my ($master_opts, $slave_opts)= - tags_from_test_file($tinfo, "$testdir/${tname}.test", $suitedir); - - # Get default storage engine from suite.opt file - - if (defined $suite_opts && - "@$suite_opts" =~ "default-storage-engine=\s*([^\s]*)") - { - $local_default_storage_engine= $1; - } - - if ( defined $local_default_storage_engine ) - { - # Different default engine is used - # tag test to require that engine - $tinfo->{'ndb_test'}= 1 - if ( $local_default_storage_engine =~ /^ndb/i ); - - } + my ($master_opts, $slave_opts)= tags_from_test_file($tinfo); + $tinfo->{in_overlay} = 1 if $file_in_overlay{$filename}; if ( $tinfo->{'big_test'} and ! $::opt_big_test ) { @@ -907,6 +729,7 @@ sub collect_one_test_case { $tinfo->{'comment'}= "Test needs --big-test"; return $tinfo } + if ( $tinfo->{'big_test'} ) { # All 'big_test' takes a long time to run @@ -920,13 +743,6 @@ sub collect_one_test_case { return $tinfo } - if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Test needs debug binaries"; - return $tinfo - } - if ( $tinfo->{'ndb_test'} ) { # This is a NDB test @@ -967,84 +783,24 @@ sub collect_one_test_case { } } - if ( $::opt_embedded_server ) - { - if ( $tinfo->{'not_embedded'} ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Not run for embedded server"; - return $tinfo; - } - } - - if ( $tinfo->{'not_valgrind'} ) - { - if ( $::opt_valgrind_mysqld ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Not compatible with Valgrind testing"; - return $tinfo; - } - } - - if ( $tinfo->{'need_ssl'} ) - { - # This is a test that needs ssl - if ( ! $::opt_ssl_supported ) { - # SSL is not supported, skip it - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "No SSL support"; - return $tinfo; - } - } - - if ( $tinfo->{'need_ipv6'} ) - { - # This is a test that needs ssl - if ( ! $::have_ipv6 ) { - # IPv6 is not supported, skip it - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "No IPv6"; - return $tinfo; - } - } - # ---------------------------------------------------------------------- # Find config file to use if not already selected in <testname>.opt file # ---------------------------------------------------------------------- - if (defined $defaults_file) { - # Using same config file for all tests - $tinfo->{template_path}= $defaults_file; - } - elsif (! $tinfo->{template_path} ) + if (not $tinfo->{template_path} ) { - my $config= "$suitedir/my.cnf"; - if (! -f $config ) + my $config= find_file_in_dirs($tinfo, dir => 'my.cnf'); + if (not $config) { - # assume default.cnf will be used - $config= "include/default_my.cnf"; - # Suite has no config, autodetect which one to use - if ( $tinfo->{rpl_test} ){ - $config= "suite/rpl/my.cnf"; - if ( $tinfo->{ndb_test} ){ - $config= "suite/rpl_ndb/my.cnf"; - } - } - elsif ( $tinfo->{ndb_test} ){ - $config= "suite/ndb/my.cnf"; + if ($tinfo->{rpl_test}) { + $config= "suite/rpl/my.cnf"; + } else { + $config= "include/default_my.cnf"; } } $tinfo->{template_path}= $config; } - if (not ref $::suites{$tinfo->{suite}}) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= $::suites{$tinfo->{suite}}; - return $tinfo; - } - # ---------------------------------------------------------------------- # Append mysqld extra options to master and slave, as appropriate # ---------------------------------------------------------------------- @@ -1054,29 +810,110 @@ sub collect_one_test_case { process_opts($tinfo, 'master_opt'); process_opts($tinfo, 'slave_opt'); - return $tinfo; + my @cases = ($tinfo); + for my $comb ($suite->{combinations}, @{$file_combinations{$filename}}) + { + @cases = map make_combinations($_, @{$comb}), @cases; + } + + for $tinfo (@cases) { + # + # Now we find a result file for every test file. It's a bit complicated. + # For a test foobar.test in the combination pair {aa,bb}, and in the + # overlay "rty" to the suite "qwe", in other words, for the + # that that mtr prints as + # ... + # qwe-rty.foobar 'aa,bb' [ pass ] + # ... + # the result can be expected in + # * either .rdiff or .result file + # * either in the overlay or in the original suite + # * with or without combinations in the file name. + # which means any of the following 15 file names can be used: + # + # 1 rty/r/foo,aa,bb.result + # 2 rty/r/foo,aa,bb.rdiff + # 3 qwe/r/foo,aa,bb.result + # 4 qwe/r/foo,aa,bb.rdiff + # 5 rty/r/foo,aa.result + # 6 rty/r/foo,aa.rdiff + # 7 qwe/r/foo,aa.result + # 8 qwe/r/foo,aa.rdiff + # 9 rty/r/foo,bb.result + # 10 rty/r/foo,bb.rdiff + # 11 qwe/r/foo,bb.result + # 12 qwe/r/foo,bb.rdiff + # 13 rty/r/foo.result + # 14 rty/r/foo.rdiff + # 15 qwe/r/foo.result + # + # They are listed, precisely, in the order of preference. + # mtr will walk that list from top to bottom and the first file that + # is found will be used. + # + # If this found file is a .rdiff, mtr continues walking down the list + # until the first .result file is found. + # A .rdiff is applied to that .result. + # + my $re =''; + + if ($tinfo->{combinations}) { + $re = '(?:' . join('|', @{$tinfo->{combinations}}) . ')'; + } + my $resdirglob = $suite->{rdir}; + $resdirglob.= ',' . $suite->{parent}->{rdir} if $suite->{parent}; + + my %files; + for (<{$resdirglob}/$tname*.{rdiff,result}>) { + my ($path, $combs, $ext) = + m@^(.*)/$tname((?:,$re)*)\.(rdiff|result)$@ or next; + my @combs = sort split /,/, $combs; + $files{$_} = join '~', ( # sort files by + 99 - scalar(@combs), # number of combinations DESC + join(',', sort @combs), # combination names ASC + $path eq $suite->{rdir} ? 1 : 2, # overlay first + $ext eq 'result' ? 1 : 2 # result before rdiff + ); + } + my @results = sort { $files{$a} cmp $files{$b} } keys %files; + + if (@results) { + my $result_file = shift @results; + $tinfo->{result_file} = $result_file; + + if ($result_file =~ /\.rdiff$/) { + shift @results while $results[0] =~ /\.rdiff$/; + mtr_error ("$result_file has no corresponding .result file") + unless @results; + $tinfo->{base_result} = $results[0]; + + if (not $::exe_patch) { + $tinfo->{skip} = 1; + $tinfo->{comment} = "requires patch executable"; + } + } + } else { + # No .result file exist + # Remember the path where it should be + # saved in case of --record + $tinfo->{record_file}= $suite->{rdir} . "/$tname.result"; + } + } + + return @cases; } my $tags_map= {'big_test' => ['big_test', 1], - 'have_debug' => ['need_debug', 1], 'have_ndb' => ['ndb_test', 1], 'have_multi_ndb' => ['ndb_test', 1], 'master-slave' => ['rpl_test', 1], 'ndb_master-slave' => ['rpl_test', 1, 'ndb_test', 1], - 'not_embedded' => ['not_embedded', 1], - 'not_valgrind' => ['not_valgrind', 1], - 'have_ssl' => ['need_ssl', 1], - 'check_ipv6' => ['need_ipv6', 1], 'long_test' => ['long_test', 1], }; my $tags_regex_string= join('|', keys %$tags_map); my $tags_regex= qr:include/($tags_regex_string)\.inc:o; -my $file_to_tags= { }; -my $file_to_master_opts= { }; -my $file_to_slave_opts= { }; - # Get various tags from a file, recursively scanning also included files. # And get options from .opt file, also recursively for included files. # Return a list of [TAG_TO_SET, VALUE_TO_SET_TO] of found tags. @@ -1085,14 +922,10 @@ my $file_to_slave_opts= { }; # cached result. # We need to be a bit careful about speed here; previous version of this code # took forever to scan the full test suite. -sub get_tags_from_file { - my ($file, $suitedir)= @_; +sub get_tags_from_file($$) { + my ($file, $suite)= @_; - return ([], [], []) unless -f $file; - - return ($file_to_tags->{$file}, $file_to_master_opts->{$file}, - $file_to_slave_opts->{$file}) - if exists($file_to_tags->{$file}); + return @{$file_to_tags{$file}} if exists $file_to_tags{$file}; my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); @@ -1100,6 +933,26 @@ sub get_tags_from_file { my $tags= []; my $master_opts= []; my $slave_opts= []; + my @combinations; + + my $over = defined $suite->{parent}; + my $sdir = $suite->{dir}; + my $pdir = $suite->{parent}->{dir} if $over; + my $in_overlay = 0; + my $suffix = $file; + my @prefix = (''); + + # to be able to look up all auxillary files in the overlay + # we split the file path in a prefix and a suffix + if ($file =~ m@^$sdir/(.*)$@) { + $suffix = $1; + @prefix = ("$sdir/"); + push @prefix, "$pdir/" if $over; + $in_overlay = $over; + } elsif ($over and $file =~ m@^$pdir/(.*)$@) { + $suffix = $1; + @prefix = map { "$_/" } $sdir, $pdir; + } while (my $line= <$F>) { @@ -1120,21 +973,23 @@ sub get_tags_from_file { if ($line =~ /^(--)?[[:space:]]*source[[:space:]]+([^;[:space:]]+)/) { my $include= $2; - # Sourced file may exist relative to test file, or in global location. + # The rules below must match open_file() function of mysqltest.cc # Note that for the purpose of tag collection we ignore # non-existing files, and let mysqltest handle the error # (e.g. mysqltest.test needs this) - for my $sourced_file (dirname($file) . '/' . $include, - $suitedir . '/' . $include, - $::glob_mysql_test_dir . '/' . $include) + for ((map { dirname("$_$suffix") } @prefix), + $sdir, $pdir, $::glob_mysql_test_dir) { + next unless defined $_; + my $sourced_file = "$_/$include"; + next if $sourced_file eq $file; if (-e $sourced_file) { - my ($sub_tags, $sub_master_opts, $sub_slave_opts)= - get_tags_from_file($sourced_file, $suitedir); - push @$tags, @$sub_tags; - push @$master_opts, @$sub_master_opts; - push @$slave_opts, @$sub_slave_opts; + push @$tags, get_tags_from_file($sourced_file, $suite); + push @$master_opts, @{$file_to_master_opts{$sourced_file}}; + push @$slave_opts, @{$file_to_slave_opts{$sourced_file}}; + push @combinations, @{$file_combinations{$sourced_file}}; + $file_in_overlay{$file} ||= $file_in_overlay{$sourced_file}; last; } } @@ -1144,28 +999,52 @@ sub get_tags_from_file { # Add options from main file _after_ those of any includes; this allows a # test file to override options set by includes (eg. rpl.rpl_ddl uses this # to enable innodb, then disable innodb in the slave. - my $file_no_ext= $file; - $file_no_ext =~ s/\.\w+$//; - my @common_opts= opts_from_file("$file_no_ext.opt"); - push @$master_opts, @common_opts, opts_from_file("$file_no_ext-master.opt"); - push @$slave_opts, @common_opts, opts_from_file("$file_no_ext-slave.opt"); + $suffix =~ s/\.\w+$//; + + for (qw(.opt -master.opt -slave.opt)) { + my @res; + push @res, opts_from_file("$prefix[1]$suffix$_") if $over; + if (-f "$prefix[0]$suffix$_") { + $in_overlay = $over; + push @res, opts_from_file("$prefix[0]$suffix$_"); + } + push @$master_opts, @res unless /slave/; + push @$slave_opts, @res unless /master/; + } + + # for combinations we need to make sure that its suite object is loaded, + # even if this file does not belong to a current suite! + my $comb_file = "$suffix.combinations"; + $suite = load_suite_for_file($comb_file) if $prefix[0] eq ''; + my @comb; + unless ($suite->{skip}) { + @comb = combinations_from_file($over, "$prefix[0]$comb_file"); + push @comb, combinations_from_file(undef, "$prefix[1]$comb_file") if $over; + } + push @combinations, [ @comb ]; # Save results so we can reuse without parsing if seen again. - $file_to_tags->{$file}= $tags; - $file_to_master_opts->{$file}= $master_opts; - $file_to_slave_opts->{$file}= $slave_opts; - return ($tags, $master_opts, $slave_opts); + $file_to_tags{$file}= $tags; + $file_to_master_opts{$file}= $master_opts; + $file_to_slave_opts{$file}= $slave_opts; + $file_combinations{$file}= [ uniq(@combinations) ]; + $file_in_overlay{$file} = 1 if $in_overlay; + return @{$tags}; } sub tags_from_test_file { - my ($tinfo, $file, $suitedir)= @_; + my ($tinfo)= @_; + my $file = $tinfo->{path}; - my ($tags, $master_opts, $slave_opts)= get_tags_from_file($file, $suitedir); - for (@$tags) + # a suite may generate tests that don't map to real *.test files + # see unit suite for an example. + return ([], []) unless -f $file; + + for (get_tags_from_file($file, $tinfo->{suite})) { $tinfo->{$_->[0]}= $_->[1]; } - return ($master_opts, $slave_opts); + return ($file_to_master_opts{$file}, $file_to_slave_opts{$file}); } sub unspace { @@ -1182,7 +1061,7 @@ sub opts_from_file ($) { return () unless -f $file; - open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); + open(FILE, '<', $file) or mtr_error("can't open file \"$file\": $!"); my @args; while ( <FILE> ) { @@ -1225,16 +1104,5 @@ sub opts_from_file ($) { return @args; } -sub print_testcases { - my (@cases)= @_; - - print "=" x 60, "\n"; - foreach my $test (@cases){ - $test->print_test(); - } - print "=" x 60, "\n"; -} - - 1; diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index 0377047c4eb..98f2c27ebce 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -263,6 +263,11 @@ sub mtr_wait_lock_file { return ($waited); } +sub uniq(@) { + my %seen = map { $_ => $_ } @_; + values %seen; +} + # Simple functions to start and check timers (have to be actively polled) # Timer can be "killed" by setting it to 0 |