summaryrefslogtreecommitdiff
path: root/mysql-test/lib
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-test/lib')
-rw-r--r--mysql-test/lib/My/Config.pm12
-rw-r--r--mysql-test/lib/My/ConfigFactory.pm43
-rw-r--r--mysql-test/lib/My/Find.pm31
-rw-r--r--mysql-test/lib/My/Suite.pm5
-rw-r--r--mysql-test/lib/My/Test.pm17
-rw-r--r--mysql-test/lib/mtr_cases.pm1178
-rw-r--r--mysql-test/lib/mtr_misc.pl5
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