diff options
Diffstat (limited to 'mysql-test/lib')
-rw-r--r-- | mysql-test/lib/My/Config.pm | 145 | ||||
-rw-r--r-- | mysql-test/lib/My/ConfigFactory.pm | 100 | ||||
-rw-r--r-- | mysql-test/lib/My/File/Path.pm | 2 | ||||
-rw-r--r-- | mysql-test/lib/My/Find.pm | 36 | ||||
-rw-r--r-- | mysql-test/lib/My/Options.pm | 2 | ||||
-rw-r--r-- | mysql-test/lib/My/SafeProcess.pm | 24 | ||||
-rw-r--r-- | mysql-test/lib/My/SafeProcess/safe_process.cc | 49 | ||||
-rw-r--r-- | mysql-test/lib/My/Suite.pm | 26 | ||||
-rw-r--r-- | mysql-test/lib/My/Test.pm | 96 | ||||
-rw-r--r-- | mysql-test/lib/mtr_cases.pm | 1431 | ||||
-rw-r--r-- | mysql-test/lib/mtr_gcov.pl | 5 | ||||
-rw-r--r-- | mysql-test/lib/mtr_gprof.pl | 2 | ||||
-rw-r--r-- | mysql-test/lib/mtr_misc.pl | 28 | ||||
-rw-r--r-- | mysql-test/lib/mtr_process.pl | 11 | ||||
-rw-r--r-- | mysql-test/lib/mtr_report.pm | 44 | ||||
-rwxr-xr-x | mysql-test/lib/process-purecov-annotations.pl | 63 | ||||
-rw-r--r-- | mysql-test/lib/v1/mtr_process.pl | 40 | ||||
-rw-r--r-- | mysql-test/lib/v1/mtr_report.pl | 25 | ||||
-rwxr-xr-x | mysql-test/lib/v1/mysql-test-run.pl | 191 |
19 files changed, 1205 insertions, 1115 deletions
diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm index 535df07871d..1d8d1617fce 100644 --- a/mysql-test/lib/My/Config.pm +++ b/mysql-test/lib/My/Config.pm @@ -1,6 +1,6 @@ # -*- cperl -*- -# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2010, Oracle and/or its affiliates # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ use strict; use warnings; use Carp; - sub new { my ($class, $option_name, $option_value)= @_; my $self= bless { name => $option_name, @@ -76,7 +75,7 @@ sub insert { $option->{value}= $value; } else { - my $option= My::Config::Option->new($option_name, $value); + $option= My::Config::Option->new($option_name, $value); # Insert option in list push(@{$self->{options}}, $option); # Insert option in hash @@ -165,7 +164,6 @@ sub value { return $option->value(); } - # # Return value for an option if it exist # @@ -178,6 +176,62 @@ sub if_exist { return $option->value(); } +package My::Config::Group::ENV; +our @ISA=qw(My::Config::Group); + +use strict; +use warnings; +use Carp; + +sub new { + my ($class, $group_name)= @_; + bless My::Config::Group->new($group_name), $class; +} + +# +# Return value for an option in the group, fail if it does not exist +# +sub value { + my ($self, $option_name)= @_; + my $option= $self->option($option_name); + + if (! defined($option) and defined $ENV{$option_name}) { + my $value= $ENV{$option_name}; + $option= My::Config::Option->new($option_name, $value); + } + + croak "No option named '$option_name' in group '$self->{name}'" + if ! defined($option); + + return $option->value(); +} + +package My::Config::Group::OPT; +our @ISA=qw(My::Config::Group); + +use strict; +use warnings; +use Carp; + +sub new { + my ($class, $group_name)= @_; + bless My::Config::Group->new($group_name), $class; +} + +sub options { + my ($self)= @_; + () +} + +sub value { + my ($self, $option_name)= @_; + my $option= $self->option($option_name); + + croak "No option named '$option_name' in group '$self->{name}'" + if ! defined($option); + + return $option->value()->(); +} package My::Config; @@ -197,7 +251,10 @@ sub new { my ($class, $path)= @_; my $group_name= undef; - my $self= bless { groups => [] }, $class; + my $self= bless { groups => [ + My::Config::Group::ENV->new('ENV'), + My::Config::Group::OPT->new('OPT'), + ] }, $class; my $F= IO::File->new($path, "<") or croak "Could not open '$path': $!"; @@ -216,19 +273,13 @@ sub new { } # Magic #! comments - elsif ( $line =~ /^#\!/) { - my $magic= $line; + elsif ( $line =~ /^(#\!\S+)(?:\s*(.*?)\s*)?$/) { + my ($magic, $arg)= ($1, $2); croak "Found magic comment '$magic' outside of group" unless $group_name; #print "$magic\n"; - $self->insert($group_name, $magic, undef); - } - - # Comments - elsif ( $line =~ /^#/ || $line =~ /^;/) { - # Skip comment - next; + $self->insert($group_name, $magic, $arg); } # Empty lines @@ -253,7 +304,7 @@ sub new { } # <option> - elsif ( $line =~ /^([\@\w-]+)\s*$/ ) { + elsif ( $line =~ /^(#?[\w-]+)\s*$/ ) { my $option= $1; croak "Found option '$option' outside of group" @@ -264,7 +315,7 @@ sub new { } # <option>=<value> - elsif ( $line =~ /^([\@\w-]+)\s*=\s*(.*?)\s*$/ ) { + elsif ( $line =~ /^(#?[\w-]+)\s*=\s*(.*?)\s*$/ ) { my $option= $1; my $value= $2; @@ -273,10 +324,17 @@ sub new { #print "$option=$value\n"; $self->insert($group_name, $option, $value); - } else { - croak "Unexpected line '$line' found in '$path'"; } + # Comments + elsif ( $line =~ /^#/ || $line =~ /^;/) { + # Skip comment + next; + } + + else { + croak "Unexpected line '$line' found in '$path'"; + } } undef $F; # Close the file @@ -378,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 # @@ -454,44 +523,4 @@ sub exists { return defined($option); } - -# Overload "to string"-operator with 'stringify' -use overload - '""' => \&stringify; - -# -# Return the config as a string in my.cnf file format -# -sub stringify { - my ($self)= @_; - my $res; - - foreach my $group ($self->groups()) { - $res .= "[$group->{name}]\n"; - - foreach my $option ($group->options()) { - $res .= $option->name(); - my $value= $option->value(); - if (defined $value) { - $res .= "=$value"; - } - $res .= "\n"; - } - $res .= "\n"; - } - return $res; -} - - -# -# Save the config to named file -# -sub save { - my ($self, $path)= @_; - my $F= IO::File->new($path, ">") - or croak "Could not open '$path': $!"; - print $F $self; - undef $F; # Close the file -} - 1; diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index a48ac12c549..e79d97e045e 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2011, Oracle and/or its affiliates # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -31,12 +31,22 @@ 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('mysqld', "loose-skip-$_" => undef) for (@::optional_plugins); +} + my @pre_rules= ( + \&add_opt_values, ); -my @share_locations= ("share/mysql", "sql/share", "share"); +my @share_locations= ("share/mariadb", "share/mysql", "sql/share", "share"); sub get_basedir { @@ -89,16 +99,12 @@ sub fix_pidfile { sub fix_port { my ($self, $config, $group_name, $group)= @_; - my $hostname= $group->value('#host'); - return $self->{HOSTS}->{$hostname}++; + return $self->{PORT}++; } sub fix_host { my ($self)= @_; - # Get next host from HOSTS array - my @hosts= keys(%{$self->{HOSTS}});; - my $host_no= $self->{NEXT_HOST}++ % @hosts; - return $hosts[$host_no]; + 'localhost' } sub is_unique { @@ -169,13 +175,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); @@ -241,15 +240,14 @@ my @mysqld_rules= { 'port' => \&fix_port }, { 'socket' => \&fix_socket }, { '#log-error' => \&fix_log_error }, - { 'general_log' => 1 }, - { 'general_log_file' => \&fix_log }, - { 'slow_query_log' => 1 }, - { 'slow_query_log_file' => \&fix_log_slow_queries }, + { 'general-log' => 1 }, + { 'plugin-dir' => sub { $::plugindir } }, + { 'general-log-file' => \&fix_log }, + { '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 }, @@ -265,7 +263,7 @@ if (IS_WINDOWS) sub fix_ndb_mgmd_port { my ($self, $config, $group_name, $group)= @_; my $hostname= $group->value('HostName'); - return $self->{HOSTS}->{$hostname}++; + return $self->{PORT}++; } @@ -386,7 +384,7 @@ sub post_check_client_group { if (! defined $option){ #print $config; - croak "Could not get value for '$name_from'"; + croak "Could not get value for '$name_from' for test $self->{testname}"; } $config->insert($client_group_name, $name_to, $option->value()) } @@ -409,7 +407,7 @@ sub post_check_client_group { sub post_check_client_groups { my ($self, $config)= @_; - my $first_mysqld= $config->first_like('mysqld.'); + my $first_mysqld= $config->first_like('mysqld\.'); return unless $first_mysqld; @@ -445,7 +443,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 @@ -454,7 +452,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()) } @@ -464,20 +462,24 @@ sub post_check_embedded_group { sub resolve_at_variable { my ($self, $config, $group, $option)= @_; + local $_ = $option->value(); + my ($res, $after); - # Split the options value on last . - my @parts= split(/\./, $option->value()); - my $option_name= pop(@parts); - my $group_name= join('.', @parts); - - $group_name =~ s/^\@//; # Remove at + while (m/(.*?)\@((?:\w+\.)+)(#?[-\w]+)/g) { + my ($before, $group_name, $option_name)= ($1, $2, $3); + $after = $'; + chop($group_name); my $from_group= $config->group($group_name) or croak "There is no group named '$group_name' that ", - "can be used to resolve '$option_name'"; + "can be used to resolve '$option_name' for test '$self->{testname}'"; - my $from= $from_group->value($option_name); - $config->insert($group->name(), $option->name(), $from) + my $value= $from_group->value($option_name); + $res .= $before.$value; + } + $res .= $after; + + $config->insert($group->name(), $option->name(), $res) } @@ -489,7 +491,7 @@ sub post_fix_resolve_at_variables { next unless defined $option->value(); $self->resolve_at_variable($config, $group, $option) - if ($option->value() =~ /^\@/); + if ($option->value() =~ /\@/); } } } @@ -631,37 +633,21 @@ sub new_config { croak "you must pass '$required'" unless defined $args->{$required}; } - # Fill in hosts/port hash - my $hosts= {}; - my $baseport= $args->{baseport}; - $args->{hosts}= [ 'localhost' ] unless exists($args->{hosts}); - foreach my $host ( @{$args->{hosts}} ) { - $hosts->{$host}= $baseport; - } - # Open the config template my $config= My::Config->new($args->{'template_path'}); - my $extra_template_path= $args->{'extra_template_path'}; - if ($extra_template_path){ - $config->append(My::Config->new($extra_template_path)); - } my $self= bless { CONFIG => $config, ARGS => $args, - HOSTS => $hosts, - NEXT_HOST => 0, + PORT => $args->{baseport}, SERVER_ID => 1, + testname => $args->{testname}, }, $class; - - { - # 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/File/Path.pm b/mysql-test/lib/My/File/Path.pm index 19abd95d824..1b7982d6b36 100644 --- a/mysql-test/lib/My/File/Path.pm +++ b/mysql-test/lib/My/File/Path.pm @@ -34,7 +34,7 @@ use strict; use Exporter; use base "Exporter"; -our @EXPORT= qw / rmtree mkpath copytree /; +our @EXPORT= qw /rmtree mkpath copytree/; use File::Find; use File::Copy; diff --git a/mysql-test/lib/My/Find.pm b/mysql-test/lib/My/Find.pm index 1f99c470e73..1d5e0f368bc 100644 --- a/mysql-test/lib/My/Find.pm +++ b/mysql-test/lib/My/Find.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2011, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -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, @@ -126,22 +127,23 @@ sub my_find_file { # # sub my_find_dir { - my ($base, $paths, $dirs, $optional)= @_; - croak "usage: my_find_dir(<base>, <paths>[, <dirs>[, <optional>]])" - unless (@_ == 3 or @_ == 2 or @_ == 4); - - # ------------------------------------------------------- - # Find and return the first directory - # ------------------------------------------------------- - foreach my $path (my_find_paths($base, $paths, $dirs)) { - return $path if ( -d $path ); + my ($base, $paths, $dirs, $required)= @_; + 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 "" if $optional; + 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 @@ -238,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/Options.pm b/mysql-test/lib/My/Options.pm index 55d1010aad8..ff3992ecb2e 100644 --- a/mysql-test/lib/My/Options.pm +++ b/mysql-test/lib/My/Options.pm @@ -150,7 +150,7 @@ sub is_set { foreach my $set_opt (@$set_opts){ my ($opt_name2, $value2)= _split_option($set_opt); - if ($opt_name1 eq $opt_name2){ + if ($opt_name1 eq $opt_name2 and $value1 eq $value2){ # Option already set return 1; } diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index 4298672e02d..e7917f8fb16 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -1,5 +1,6 @@ # -*- cperl -*- -# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2011, Oracle and/or its affiliates. +# Copyright (c) 2009, 2011 Monty Program Ab # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -86,6 +87,7 @@ sub is_child { my @safe_process_cmd; my $safe_kill; my $bindir; + if(defined $ENV{MTR_BINDIR}) { # This is an out-of-source build. Build directory @@ -94,10 +96,10 @@ if(defined $ENV{MTR_BINDIR}) } else { - $bindir = "."; + use Cwd; + $bindir = getcwd(); } - # Find the safe process binary or script sub find_bin { if (IS_WIN32PERL or IS_CYGWIN) @@ -134,7 +136,7 @@ sub new { my $input = delete($opts{'input'}); my $output = delete($opts{'output'}); my $error = delete($opts{'error'}); - my $verbose = delete($opts{'verbose'}); + my $verbose = delete($opts{'verbose'}) || $::opt_verbose; my $nocore = delete($opts{'nocore'}); my $host = delete($opts{'host'}); my $shutdown = delete($opts{'shutdown'}); @@ -358,9 +360,9 @@ sub kill { sub _collect { - my ($self)= @_; + my ($self, $exit_code)= @_; - $self->{EXIT_STATUS}= $?; + $self->{EXIT_STATUS}= $exit_code; _verbose("_collect: $self"); # Take the process out of running list @@ -427,6 +429,7 @@ sub wait_one { #_verbose("blocking: $blocking, use_alarm: $use_alarm"); my $retpid; + my $exit_code; eval { # alarm should break the wait @@ -435,6 +438,7 @@ sub wait_one { alarm($timeout) if $use_alarm; $retpid= waitpid($pid, $blocking ? 0 : &WNOHANG); + $exit_code= $?; alarm(0) if $use_alarm; }; @@ -466,7 +470,7 @@ sub wait_one { #warn "wait_one: expected pid $pid but got $retpid" # unless( $retpid == $pid ); - $self->_collect(); + $self->_collect($exit_code); return 0; } @@ -479,6 +483,8 @@ sub wait_one { # sub wait_any { my $ret_pid; + my $exit_code; + if (IS_WIN32PERL) { # Can't wait for -1 => use a polling loop do { @@ -488,6 +494,7 @@ sub wait_any { last if $pid == $ret_pid; } } while ($ret_pid == 0); + $exit_code= $?; } else { @@ -497,6 +504,7 @@ sub wait_any { print STDERR "wait_any, got invalid pid: $ret_pid\n"; return undef; } + $exit_code= $?; } # Look it up in "running" table @@ -506,7 +514,7 @@ sub wait_any { print STDERR "running: ". join(", ", keys(%running)). "\n"; return undef; } - $proc->_collect; + $proc->_collect($exit_code); return $proc; } diff --git a/mysql-test/lib/My/SafeProcess/safe_process.cc b/mysql-test/lib/My/SafeProcess/safe_process.cc index e7ac36fd00f..f41a77ff6ac 100644 --- a/mysql-test/lib/My/SafeProcess/safe_process.cc +++ b/mysql-test/lib/My/SafeProcess/safe_process.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 2011, Oracle and/or its affiliates This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -85,17 +85,18 @@ static void die(const char* fmt, ...) va_end(args); if (int last_err= errno) fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err)); - exit(1); + exit(6); } -static void kill_child(void) +static void kill_child(bool was_killed) { int status= 0; message("Killing child: %d", child_pid); // Terminate whole process group - kill(-child_pid, SIGKILL); + if (! was_killed) + kill(-child_pid, SIGKILL); pid_t ret_pid= waitpid(child_pid, &status, 0); if (ret_pid == child_pid) @@ -115,7 +116,7 @@ static void kill_child(void) exit(exit_code); } - exit(1); + exit(5); } @@ -135,7 +136,7 @@ extern "C" void handle_signal(int sig) terminated= 1; if (child_pid > 0) - kill_child(); + kill_child(sig == SIGCHLD); // Ignore further signals signal(SIGTERM, SIG_IGN); @@ -252,8 +253,8 @@ int main(int argc, char* const argv[] ) // Close write end close(pfd[1]); - if (execvp(child_argv[0], child_argv) < 0) - die("Failed to exec child"); + execvp(child_argv[0], child_argv); + die("Failed to exec child"); } close(pfd[1]); // Close unused write end @@ -269,39 +270,19 @@ int main(int argc, char* const argv[] ) /* Monitor loop */ message("Started child %d, terminated: %d", child_pid, terminated); - while(!terminated) + while (!terminated) { // Check if parent is still alive - if (kill(parent_pid, 0) != 0){ + if (kill(parent_pid, 0) != 0) + { message("Parent is not alive anymore"); break; } - - // Check if child has exited, normally this will be - // detected immediately with SIGCHLD handler - int status= 0; - pid_t ret_pid= waitpid(child_pid, &status, WNOHANG); - if (ret_pid == child_pid) - { - int ret_code= 2; - if (WIFEXITED(status)) - { - // Process has exited, collect return status - ret_code= WEXITSTATUS(status); - message("Child exit: %d", ret_code); - // Exit with exit status of the child - exit(ret_code); - } - - if (WIFSIGNALED(status)) - message("Child killed by signal: %d", WTERMSIG(status)); - - exit(ret_code); - } + /* Wait for parent or child to die */ sleep(1); } - kill_child(); + kill_child(0); - return 1; + return 4; } diff --git a/mysql-test/lib/My/Suite.pm b/mysql-test/lib/My/Suite.pm new file mode 100644 index 00000000000..b5870ea1e16 --- /dev/null +++ b/mysql-test/lib/My/Suite.pm @@ -0,0 +1,26 @@ +# A default suite class that is used for all suites without their owns suite.pm +# see README.suites for a description + +package My::Suite; + +sub config_files { () } +sub servers { () } +sub skip_combinations { () } + +sub new { bless { } } + +sub list_cases { + my ($self, $testdir) = @_; + opendir(TESTDIR, $testdir) or return (); + my (@cases) = grep { s/\.test$// } readdir TESTDIR; + closedir TESTDIR; + @cases; +} + +sub start_test { + my ($self, $tinfo)= @_; + &::start_mysqltest($tinfo); +} + +bless { }; + diff --git a/mysql-test/lib/My/Test.pm b/mysql-test/lib/My/Test.pm index 895afd210e7..4d017b3b42c 100644 --- a/mysql-test/lib/My/Test.pm +++ b/mysql-test/lib/My/Test.pm @@ -23,6 +23,7 @@ package My::Test; use strict; use warnings; use Carp; +use Storable(); use mtr_results; @@ -34,6 +35,25 @@ 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->{combinations} + ? " '" . join(',', sort @{$self->{combinations}}) . "'" + : "") +} # # Return a unique key that can be used to @@ -45,18 +65,6 @@ sub key { } -sub _encode { - my ($value)= @_; - $value =~ s/([|\\\x{0a}\x{0d}])/sprintf('\%02X', ord($1))/eg; - return $value; -} - -sub _decode { - my ($value)= @_; - $value =~ s/\\([0-9a-fA-F]{2})/chr(hex($1))/ge; - return $value; -} - sub is_failed { my ($self)= @_; my $result= $self->{result}; @@ -90,67 +98,23 @@ sub write_test { # Give the test a unique key before serializing it $test->{key}= "$test" unless defined $test->{key}; - print $sock $header, "\n"; - while ((my ($key, $value)) = each(%$test)) { - print $sock $key, "= "; - if (ref $value eq "ARRAY") { - print $sock "[", _encode(join(", ", @$value)), "]"; - } else { - print $sock _encode($value); - } - print $sock "\n"; - } - print $sock "\n"; + my $serialized= Storable::freeze($test); + $serialized =~ s/([\x0d\x0a\\])/sprintf("\\%02x", ord($1))/eg; + print $sock $header, "\n", $serialized, "\n"; } sub read_test { my ($sock)= @_; - my $test= My::Test->new(); - # Read the : separated key value pairs until a - # single newline on it's own line - my $line; - while (defined($line= <$sock>)) { - # List is terminated by newline on it's own - if ($line eq "\n") { - # Correctly terminated reply - # print "Got newline\n"; - last; - } - chomp($line); - - # Split key/value on the first "=" - my ($key, $value)= split("= ", $line, 2); - - if ($value =~ /^\[(.*)\]/){ - my @values= split(", ", _decode($1)); - push(@{$test->{$key}}, @values); - } - else - { - $test->{$key}= _decode($value); - } - } + my $serialized= <$sock>; + chomp($serialized); + $serialized =~ s/\\([0-9a-fA-F]{2})/chr(hex($1))/eg; + my $test= Storable::thaw($serialized); + use Data::Dumper; + die "wrong class (hack attempt?): ".ref($test)."\n".Dumper(\$test, $serialized) + unless ref($test) eq 'My::Test'; resfile_from_test($test) if $::opt_resfile; return $test; } - -sub print_test { - my ($self)= @_; - - print "[", $self->{name}, "]", "\n"; - while ((my ($key, $value)) = each(%$self)) { - print " ", $key, "= "; - if (ref $value eq "ARRAY") { - print "[", join(", ", @$value), "]"; - } else { - print $value; - } - print "\n"; - } - print "\n"; -} - - 1; diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index b3bc2a83b92..32ec24da492 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -1,5 +1,6 @@ # -*- cperl -*- -# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2011, Oracle and/or its affiliates. +# Copyright (c) 2010, 2011 Monty Program Ab # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,29 +25,19 @@ use strict; use base qw(Exporter); our @EXPORT= qw(collect_option collect_test_cases); +use Carp; + 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 $defaults_extra_file; -our $quick_collect; -# Set to 1 if you want the tests to override -# default storage engine settings, and use MyISAM -# as default. (temporary option used in connection -# with the change of default storage engine to InnoDB) -our $default_myisam= 1; - sub collect_option { my ($opt, $value)= @_; @@ -61,12 +52,13 @@ sub collect_option { } use File::Basename; -use File::Spec::Functions qw / splitdir /; +use File::Spec::Functions qw /splitdir/; use IO::File(); use My::Config; use My::Platform; use My::Test; use My::Find; +use My::Suite; require "mtr_misc.pl"; @@ -74,12 +66,7 @@ require "mtr_misc.pl"; my $do_test_reg; my $skip_test_reg; -# Related to adding InnoDB plugin combinations -my $lib_innodb_plugin; -my $do_innodb_plugin; - -# If "Quick collect", set to 1 once a test to run has been found. -my $some_test_found; +my %suites; sub init_pattern { my ($from, $what)= @_; @@ -111,33 +98,19 @@ sub collect_test_cases ($$$$) { my $opt_skip_test_list= shift; my $cases= []; # Array of hash(one hash for each testcase) - # Unit tests off by default also if using --do-test or --start-from - $::opt_ctest= 0 if $::opt_ctest == -1 && ($do_test || $start_from); - $do_test_reg= init_pattern($do_test, "--do-test"); $skip_test_reg= init_pattern($skip_test, "--skip-test"); - $lib_innodb_plugin= - my_find_file($::basedir, - ["storage/innodb_plugin", "storage/innodb_plugin/.libs", - "lib/mysql/plugin", "lib/plugin"], - ["ha_innodb_plugin.dll", "ha_innodb_plugin.so", - "ha_innodb_plugin.sl"], - NOT_REQUIRED); - $do_innodb_plugin= ($::mysql_version_id >= 50100 && - !(IS_WINDOWS && $::opt_embedded_server) && - $lib_innodb_plugin); + 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. + # This also affects some logic in the loop following this. if ($opt_reorder or !@$opt_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("i_".$suite, $opt_cases, $opt_skip_test_list)); + push(@$cases, collect_suite_name($suite, $opt_cases)); } } @@ -149,7 +122,7 @@ sub collect_test_cases ($$$$) { foreach my $test_name_spec ( @$opt_cases ) { my $found= 0; - my ($sname, $tname, $extension)= split_testname($test_name_spec); + my ($sname, $tname)= split_testname($test_name_spec); foreach my $test ( @$cases ) { last unless $opt_reorder; @@ -165,7 +138,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_suite_name($sname, [ $test_name_spec ]); if (@this_case) { push (@$cases, @this_case); @@ -178,9 +151,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 # 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. @@ -189,47 +161,35 @@ 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); } - @$cases = sort {$a->{criteria} cmp $b->{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; - } -# Returns (suitename, testname, extension) +# Returns (suitename, testname) sub split_testname { my ($test_name)= @_; @@ -241,239 +201,263 @@ sub split_testname { if (@parts == 1){ # Only testname given, ex: alias - return (undef , $parts[0], undef); + return (undef , $parts[0]); } elsif (@parts == 2) { # Either testname.test or suite.testname given # Ex. main.alias or alias.test if ($parts[1] eq "test") { - return (undef , $parts[0], $parts[1]); + return (undef , $parts[0]); } else { - return ($parts[0], $parts[1], undef); + return ($parts[0], $parts[1]); } - - } elsif (@parts == 3) { - # Fully specified suitename.testname.test - # ex main.alias.test - return ( $parts[0], $parts[1], $parts[2]); } 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(); + } -sub collect_one_suite($) -{ - my $suite= shift; # Test suite name - my $opt_cases= shift; - my $opt_skip_test_list= shift; - my @cases; # Array of hash + $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}; +} - mtr_verbose("Collecting: $suite"); - my $suitedir= "$::glob_mysql_test_dir"; # Default - if ( $suite ne "main" ) - { - # Allow suite to be path to "some dir" if $suite has at least - # one directory part - if ( -d $suite and splitdir($suite) > 1 ){ - $suitedir= $suite; - mtr_report(" - from '$suitedir'"); +# 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; } - else - { - $suitedir= my_find_dir($::basedir, - ["share/mysql-test/suite", - "mysql-test/suite", - "internal/mysql-test/suite", - "mysql-test", - # Look in storage engine specific suite dirs - "storage/*/mtr", - # Look in plugin specific suite dir - "plugin/$suite/tests", - "internal/plugin/$suite/tests", - ], - [$suite, "mtr"], ($suite =~ /^i_/)); - return unless $suitedir; + @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; } - mtr_verbose("suitedir: $suitedir"); + close DISABLED; } +} - my $testdir= "$suitedir/t"; - my $resdir= "$suitedir/r"; +# +# processes one user-specified suite name. +# it could contain wildcards, e.g engines/* +# +sub collect_suite_name +{ + my $suitename= shift; # Test suite name + my $opt_cases= shift; + my $over; + my %suites; - # Check if t/ exists - if (-d $testdir){ - # t/ exists + ($suitename, $over) = split '-', $suitename; - if ( -d $resdir ) - { - # r/exists + if ( $suitename ne "main" ) + { + # Allow suite to be path to "some dir" if $suitename has at least + # one directory part + if ( -d $suitename and splitdir($suitename) > 1 ) { + $suites{$suitename} = [ $suitename ]; + mtr_report(" - from '$suitename'"); } else { - # No r/, use t/ as result dir - $resdir= $testdir; + my @dirs = my_find_dir(dirname($::glob_mysql_test_dir), + ["mysql-test/suite", + "storage/*/mysql-test", + "plugin/*/mysql-test"], + [$suitename]); + # + # if $suitename contained wildcards, we'll have many suites and + # their overlays here. Let's group them appropriately. + # + for (@dirs) { + m@^.*/mysql-test/(?:suite/)?(.*)$@ or confess $_; + push @{$suites{$1}}, $_; + } } - + } else { + $suites{$suitename} = [ $::glob_mysql_test_dir, + my_find_dir(dirname($::glob_mysql_test_dir), + ["storage/*/mysql-test", + "plugin/*/mysql-test"], + ['main'], NOT_REQUIRED) ]; } - else { - # No t/ dir => there can' be any r/ dir - mtr_error("Can't have r/ dir without t/") if -d $resdir; - # No t/ or r/ => use suitedir - $resdir= $testdir= $suitedir; - } - - mtr_verbose("testdir: $testdir"); - mtr_verbose("resdir: $resdir"); - - # ---------------------------------------------------------------------- - # 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; - } + my @cases; + while (my ($name, $dirs) = each %suites) { + # + # 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 ($dirs->[0] !~ m@/mysql-test/suite/$name$@) { + # prefer directories with more files + @$dirs = sort { scalar(<$a/*>) <=> scalar(<$b/*>) } @$dirs; } - - # Read suite.opt file - my $suite_opt_file= "$testdir/suite.opt"; - my $suite_opts= []; - if ( -f $suite_opt_file ) - { - $suite_opts= opts_from_file($suite_opt_file); + push @cases, collect_one_suite($opt_cases, $name, $over, @$dirs); } + return @cases; +} - if ( @$opt_cases ) - { - # Collect in specified order - foreach my $test_name_spec ( @$opt_cases ) - { - my ($sname, $tname, $extension)= split_testname($test_name_spec); - - # The test name parts have now been defined - #print " suite_name: $sname\n"; - #print " tname: $tname\n"; - #print " extension: $extension\n"; - - # Check cirrect suite if suitename is defined - next if (defined $sname and $suite ne $sname); - - if ( defined $extension ) - { - my $full_name= "$testdir/$tname.$extension"; - # Extension was specified, check if the test exists - if ( ! -f $full_name) - { - # This is only an error if suite was specified, otherwise it - # could exist in another suite - mtr_error("Test '$full_name' was not found in suite '$sname'") - if $sname; - - next; - } - } - else - { - # No extension was specified, use default - $extension= "test"; - my $full_name= "$testdir/$tname.$extension"; - - # Test not found here, could exist in other suite - next if ( ! -f $full_name ); - } +sub collect_one_suite { + my ($opt_cases, $suitename, $over, $suitedir, @overlays) = @_; + + mtr_verbose("Collecting: $suitename"); + 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 = (); + + confess $_ unless m@/(?:storage|plugin)/(\w+)/mysql-test/[\w/]*\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); + } + return @cases; +} - push(@cases, - collect_one_test_case($suitedir, - $testdir, - $resdir, - $suite, - $tname, - "$tname.$extension", - \%disabled, - $suite_opts)); - } +sub process_suite { + my ($basename, $overname, $suitedir, $opt_cases) = @_; + my $suitename; + my $parent; + + if ($overname) { + $parent = $suites{$basename}; + confess unless $parent; + $suitename = $basename . '-' . $overname; + } else { + $suitename = $basename; } - else - { - opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); - foreach my $elem ( sort readdir(TESTDIR) ) - { - my $tname= mtr_match_extension($elem, 'test'); - - next unless defined $tname; - - # Skip tests that does not match the --do-test= filter - next if ($do_test_reg and not $tname =~ /$do_test_reg/o); - - push(@cases, - collect_one_test_case($suitedir, - $testdir, - $resdir, - $suite, - $tname, - $elem, - \%disabled, - $suite_opts)); + my $suite = load_suite_object($suitename, $suitedir); + + # + # Read suite config files, unless it was done aleady + # + 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 { + my $tdir= "$suitedir/t"; + my $rdir= "$suitedir/r"; + $suite->{tdir} = -d $tdir ? $tdir : $suitedir; + $suite->{rdir} = -d $rdir ? $rdir : $suite->{tdir}; } - closedir TESTDIR; - } - # Return empty list if no testcases found - return if (@cases == 0); + mtr_verbose("testdir: " . $suite->{tdir}); + mtr_verbose( "resdir: " . $suite->{rdir}); - # ---------------------------------------------------------------------- - # 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"; + # disabled.def + parse_disabled($suite->{dir} .'/disabled.def', $suitename); + + # combinations if (@::opt_combinations) { # take the combination from command-line @@ -482,277 +466,163 @@ 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 $config= My::Config->new($combination_file); - foreach my $group ($config->groups()) { - my $comb= {}; - $comb->{name}= $group->name(); - foreach my $option ( $group->options() ) { - push(@{$comb->{comb_opt}}, $option->option()); - } - push(@combinations, $comb); - } + my @combs; + my $from = "$suitedir/combinations"; + @combs = combinations_from_file($parent, $from) unless $suite->{skip}; + $suite->{combinations} = [ @combs ]; + # in overlays it's a union of parent's and overlay's files. + unshift @{$suite->{combinations}}, + grep { not $skip_combinations{"$from => $_->{name}"} } + @{$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; - foreach my $comb (@combinations) - { - foreach my $test (@cases) - { + $suite->{cases} = [ $suite->list_cases($suite->{tdir}) ]; + } - next if ( $test->{'skip'} ); - - # Skip this combination if the values it provides - # already are set 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}) ){ - next; - } - - # 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); - } - } + my %all_cases; + %all_cases = map { $_ => $parent->{tdir} } @{$parent->{cases}} if $parent; + $all_cases{$_} = $suite->{tdir} for @{$suite->{cases}}; - # Add the plain test if it was not already added - # as part of a combination - my %added; - foreach my $new_test (@new_cases){ - $added{$new_test->{name}}= 1; - } - foreach my $test (@cases){ - push(@new_cases, $test) unless $added{$test->{name}}; - } + my @cases; + if (@$opt_cases) { + # Collect in specified order + foreach my $test_name_spec ( @$opt_cases ) + { + my ($sname, $tname)= split_testname($test_name_spec); + # Check correct suite if suitename is defined + next if defined $sname and $sname ne $suitename + and $sname ne "$basename-"; - #print_testcases(@new_cases); - @cases= @new_cases; - #print_testcases(@cases); + next unless $all_cases{$tname}; + push @cases, collect_one_test_case($suite, $all_cases{$tname}, $tname); + } + } else { + for (sort keys %all_cases) + { + # 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{$_}, $_); } } - optimize_cases(\@cases); - #print_testcases(@cases); - - return @cases; + @cases; } - - # -# Loop through all test cases -# - optimize which test to run by skipping unnecessary ones -# - update settings if necessary +# Read options from the given opt file and append them as an array +# to $tinfo->{$opt_name} # -sub optimize_cases { - my ($cases)= @_; - - foreach my $tinfo ( @$cases ) - { - # 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 (defined $binlog_format ) - { - # ======================================================= - # Fixed --binlog-format=x specified on command line - # ======================================================= - if ( defined $tinfo->{'binlog_formats'} ) - { - #print "binlog_formats: ". join(", ", @{$tinfo->{binlog_formats}})."\n"; - - # The test supports different binlog formats - # check if the selected one is ok - my $supported= - grep { $_ eq $binlog_format } @{$tinfo->{'binlog_formats'}}; - if ( !$supported ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= - "Doesn't support --binlog-format='$binlog_format'"; - } - } - } - else - { - # ======================================================= - # 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'"; - 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="); +sub process_opts { + my ($tinfo, $opt_name)= @_; - # Allow use of uppercase, convert to all lower case - $default_engine =~ tr/A-Z/a-z/; + my @opts= @{$tinfo->{$opt_name}}; + $tinfo->{$opt_name} = []; - 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"; + foreach my $opt (@opts) + { + my $value; - if ( ! exists $::mysqld_variables{$default_engine} and - ! exists $builtin_engines{$default_engine} ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= - "'$default_engine' not supported"; - } + # The opt file is used both to send special options to the mysqld + # as well as pass special test case specific options to this + # script - $tinfo->{'ndb_test'}= 1 - if ( $default_engine =~ /^ndb/i ); - $tinfo->{'innodb_test'}= 1 - if ( $default_engine =~ /^innodb/i ); - } + $value= mtr_match_prefix($opt, "--timezone="); + if ( defined $value ) + { + $tinfo->{'timezone'}= $value; + next; } - if ($quick_collect && ! $tinfo->{'skip'}) + # If we set default time zone, remove the one we have + $value= mtr_match_prefix($opt, "--default-time-zone="); + if ( defined $value ) { - $some_test_found= 1; - return; + # Set timezone for this test case to something different + $tinfo->{'timezone'}= "GMT-8"; + # Fallthrough, add the --default-time-zone option } + + # Ok, this was a real option, add it + push(@{$tinfo->{$opt_name}}, $opt); } } +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}; + confess unless @combinations == 1; + return ($test); + } -# -# Read options from the given opt file and append them as an array -# to $tinfo->{$opt_name} -# -sub process_opts_file { - my ($tinfo, $opt_file, $opt_name)= @_; - - if ( -f $opt_file ) + foreach my $comb (@combinations) { - my $opts= opts_from_file($opt_file); + # 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}) ){ - foreach my $opt ( @$opts ) - { - my $value; + # Add combination name short name + push @{$test->{combinations}}, $comb->{name}; - # The opt file is used both to send special options to the mysqld - # as well as pass special test case specific options to this - # script + return ($test); + } + } - $value= mtr_match_prefix($opt, "--timezone="); - if ( defined $value ) - { - $tinfo->{'timezone'}= $value; - next; - } + 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}}; - $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; - } + # Add combination name short name + push @{$new_test->{combinations}}, $comb->{name}; - $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; - } + $new_test->{in_overlay} = 1 if $comb->{in_overlay}; - # If we set default time zone, remove the one we have - $value= mtr_match_prefix($opt, "--default-time-zone="); - if ( defined $value ) - { - # Set timezone for this test case to something different - $tinfo->{'timezone'}= "GMT-8"; - # Fallthrough, add the --default-time-zone option - } + # Add the new test to new test cases list + push(@cases, $new_test); + } + return @cases; +} - # 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; - } - $value= mtr_match_prefix($opt, "--testcase-timeout="); - if ( defined $value ) { - # Overrides test case timeout for this test - $tinfo->{'case-timeout'}= $value; - next; - } +sub find_file_in_dirs +{ + my ($tinfo, $slot, $filename) = @_; + my $parent = $tinfo->{suite}->{parent}; + my $f = $tinfo->{suite}->{$slot} . '/' . $filename; - # Ok, this was a real option, add it - push(@{$tinfo->{$opt_name}}, $opt); - } + if (-f $f) { + $tinfo->{in_overlay} = 1 if $parent; + return $f; } + + return undef unless $parent; + + $f = $parent->{$slot} . '/' . $filename; + return -f $f ? $f : undef; } ############################################################################## @@ -762,68 +632,33 @@ sub process_opts_file { ############################################################################## 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; - - #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 ) - { - # start_from can be specified as [suite.].testname_prefix - my ($suite, $test, $ext)= split_testname($start_from); - - if ( $suite and $suitename lt $suite){ - return; # Skip silently - } - if ( $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", - + 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 # ---------------------------------------------------------------------- - if ( $skip_test_reg and $tname =~ /$skip_test_reg/o ) + if ( $skip_test_reg and ($tname =~ /$skip_test_reg/o or + $name =~ /$skip_test_reg/o)) { $tinfo->{'skip'}= 1; return $tinfo; @@ -832,59 +667,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); - - #----------------------------------------------------------------------- - # Check for test specific config file - #----------------------------------------------------------------------- - my $test_cnf_file= "$testdir/$tname.cnf"; - if ( -f $test_cnf_file) { - # Specifies the configuration file to use for this test - $tinfo->{'template_path'}= $test_cnf_file; + 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; } @@ -892,8 +710,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 ) { @@ -910,8 +728,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 ) { @@ -925,38 +743,26 @@ sub collect_one_test_case { } } - # ---------------------------------------------------------------------- - # <tname>.slave-mi - # ---------------------------------------------------------------------- - mtr_error("$tname: slave-mi not supported anymore") - if ( -f "$testdir/$tname.slave-mi"); - - - tags_from_test_file($tinfo,"$testdir/${tname}.test"); - - if ( defined $default_storage_engine ) - { - # Different default engine is used - # tag test to require that engine - $tinfo->{'ndb_test'}= 1 - if ( $default_storage_engine =~ /^ndb/i ); - - $tinfo->{'innodb_test'}= 1 - if ( $default_storage_engine =~ /^innodb/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 ) { $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Test needs 'big-test' option"; + $tinfo->{'comment'}= "Test needs --big-test"; return $tinfo } - if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) + if ( $tinfo->{'big_test'} ) + { + # All 'big_test' takes a long time to run + $tinfo->{'long_test'}= 1; + } + + if ( ! $tinfo->{'big_test'} and $::opt_big_test > 1 ) { $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Test needs debug binaries"; + $tinfo->{'comment'}= "Small test"; return $tinfo } @@ -983,80 +789,12 @@ sub collect_one_test_case { } } - if ($tinfo->{'federated_test'}) - { - # This is a test that needs federated, enable it - push(@{$tinfo->{'master_opt'}}, "--loose-federated"); - push(@{$tinfo->{'slave_opt'}}, "--loose-federated"); - } - - if ( $tinfo->{'innodb_test'} ) - { - # This is a test that needs innodb - if ( $::mysqld_variables{'innodb'} eq "OFF" || - ! exists $::mysqld_variables{'innodb'} ) - { - # innodb is not supported, skip it - $tinfo->{'skip'}= 1; - # This comment is checked for running with innodb plugin (see above), - # please keep that in mind if changing the text. - $tinfo->{'comment'}= "No innodb support"; - # But continue processing if we may run it with innodb plugin - return $tinfo unless $do_innodb_plugin; - } - } - elsif ($default_myisam) - { - # This is a temporary fix to allow non-innodb tests to run even if - # the default storage engine is innodb. - push(@{$tinfo->{'master_opt'}}, "--default-storage-engine=MyISAM"); - push(@{$tinfo->{'slave_opt'}}, "--default-storage-engine=MyISAM"); - } - - if ( $tinfo->{'need_binlog'} ) - { - if (grep(/^--skip-log-bin/, @::opt_extra_mysqld_opt) ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Test needs binlog"; - return $tinfo; - } - } - else - { - # Test does not need binlog, add --skip-binlog to - # the options used when starting - push(@{$tinfo->{'master_opt'}}, "--loose-skip-log-bin"); - push(@{$tinfo->{'slave_opt'}}, "--loose-skip-log-bin"); - } - if ( $tinfo->{'rpl_test'} ) { if ( $skip_rpl ) { $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "No replication tests(--skip-rpl)"; - return $tinfo; - } - } - - if ( $::opt_embedded_server ) - { - if ( $tinfo->{'not_embedded'} ) - { - $tinfo->{'skip'}= 1; - $tinfo->{'comment'}= "Not run for embedded server"; - 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"; + $tinfo->{'comment'}= "No replication tests"; return $tinfo; } } @@ -1064,134 +802,268 @@ sub collect_one_test_case { # ---------------------------------------------------------------------- # 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; } - # Set extra config file to use - if (defined $defaults_extra_file) { - $tinfo->{extra_template_path}= $defaults_extra_file; - } - # ---------------------------------------------------------------------- - # Append mysqld extra options to both master and slave + # Append mysqld extra options to master and slave, as appropriate # ---------------------------------------------------------------------- - push(@{$tinfo->{'master_opt'}}, @::opt_extra_mysqld_opt); - push(@{$tinfo->{'slave_opt'}}, @::opt_extra_mysqld_opt); + push @{$tinfo->{'master_opt'}}, @$master_opts, @::opt_extra_mysqld_opt; + push @{$tinfo->{'slave_opt'}}, @$slave_opts, @::opt_extra_mysqld_opt; - # ---------------------------------------------------------------------- - # Add master opts, extra options only for master - # ---------------------------------------------------------------------- - process_opts_file($tinfo, "$testdir/$tname-master.opt", 'master_opt'); + process_opts($tinfo, 'master_opt'); + process_opts($tinfo, 'slave_opt'); - # ---------------------------------------------------------------------- - # Add slave opts, list of extra option only for slave - # ---------------------------------------------------------------------- - process_opts_file($tinfo, "$testdir/$tname-slave.opt", 'slave_opt'); + my @cases = ($tinfo); + for my $comb ($suite->{combinations}, @{$file_combinations{$filename}}) + { + @cases = map make_combinations($_, @{$comb}), @cases; + } - return $tinfo; -} + 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; -# List of tags in the .test files that if found should set -# the specified value in "tinfo" -my @tags= -( - ["include/have_binlog_format_row.inc", "binlog_formats", ["row"]], - ["include/have_binlog_format_statement.inc", "binlog_formats", ["statement"]], - ["include/have_binlog_format_mixed.inc", "binlog_formats", ["mixed"]], - ["include/have_binlog_format_mixed_or_row.inc", - "binlog_formats", ["mixed", "row"]], - ["include/have_binlog_format_mixed_or_statement.inc", - "binlog_formats", ["mixed", "statement"]], - ["include/have_binlog_format_row_or_statement.inc", - "binlog_formats", ["row", "statement"]], - - ["include/have_log_bin.inc", "need_binlog", 1], - - ["include/have_innodb.inc", "innodb_test", 1], - ["include/big_test.inc", "big_test", 1], - ["include/have_debug.inc", "need_debug", 1], - ["include/have_ndb.inc", "ndb_test", 1], - ["include/have_multi_ndb.inc", "ndb_test", 1], - ["include/master-slave.inc", "rpl_test", 1], - ["include/ndb_master-slave.inc", "rpl_test", 1], - ["include/ndb_master-slave.inc", "ndb_test", 1], - ["federated.inc", "federated_test", 1], - ["include/not_embedded.inc", "not_embedded", 1], - ["include/have_ssl.inc", "need_ssl", 1], -); + 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"; + } + } -sub tags_from_test_file { - my $tinfo= shift; - my $file= shift; - #mtr_verbose("$file"); - my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); + return @cases; +} - while ( my $line= <$F> ) - { - # Skip line if it start's with # - next if ( $line =~ /^#/ ); +my $tags_map= {'big_test' => ['big_test', 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], + 'long_test' => ['long_test', 1], +}; +my $tags_regex_string= join('|', keys %$tags_map); +my $tags_regex= qr:include/($tags_regex_string)\.inc:o; + +# 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. +# Also returns lists of options for master and slave found in .opt files. +# Each include file is scanned only once, and subsequent calls just look up the +# 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, $suite)= @_; + + 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\": $!"); + + 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; + } - # Match this line against tag in "tags" array - foreach my $tag (@tags) + while (my $line= <$F>) + { + # Ignore comments. + next if $line =~ /^\#/; + + # Add any tag we find. + if ($line =~ /$tags_regex/o) { - if ( index($line, $tag->[0]) >= 0 ) + my $to_set= $tags_map->{$1}; + for (my $i= 0; $i < @$to_set; $i+= 2) { - # Tag matched, assign value to "tinfo" - $tinfo->{"$tag->[1]"}= $tag->[2]; + push @$tags, [$to_set->[$i], $to_set->[$i+1]]; } } - # If test sources another file, open it as well - if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ or - $line =~ /^([[:space:]]*)source(.*);$/ ) + # Check for a sourced include file. + if ($line =~ /^(--)?[[:space:]]*source[[:space:]]+([^;[:space:]]+)/) { - my $value= $2; - $value =~ s/^\s+//; # Remove leading space - $value =~ s/[[:space:]]+$//; # Remove ending space - - # Sourced file may exist relative to test or - # in global location - foreach my $sourced_file (dirname($file). "/$value", - "$::glob_mysql_test_dir/$value") + my $include= $2; + # 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 ((map { dirname("$_$suffix") } @prefix), + $sdir, $pdir, $::glob_mysql_test_dir) { - if ( -f $sourced_file ) - { - # Only source the file if it exists, we may get - # false positives in the regexes above if someone - # writes "source nnnn;" in a test case(such as mysqltest.test) - tags_from_test_file($tinfo, $sourced_file); - last; - } + next unless defined $_; + my $sourced_file = "$_/$include"; + next if $sourced_file eq $file; + if (-e $sourced_file) + { + 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; + } } } + } + + # 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. + $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}) { + my $from = "$prefix[0]$comb_file"; + @comb = combinations_from_file($over, $from); + push @comb, + grep { not $skip_combinations{"$from => $_->{name}"} } + 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; + $file_combinations{$file}= [ uniq(@combinations) ]; + $file_in_overlay{$file} = 1 if $in_overlay; + return @{$tags}; +} + +sub tags_from_test_file { + my ($tinfo)= @_; + my $file = $tinfo->{path}; + # 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 ($file_to_master_opts{$file}, $file_to_slave_opts{$file}); } sub unspace { @@ -1204,8 +1076,11 @@ sub unspace { sub opts_from_file ($) { my $file= shift; + local $_; + + 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> ) { @@ -1245,18 +1120,8 @@ sub opts_from_file ($) { } } close FILE; - return \@args; + 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_gcov.pl b/mysql-test/lib/mtr_gcov.pl index 6f9e744a548..a6e1f8efd5f 100644 --- a/mysql-test/lib/mtr_gcov.pl +++ b/mysql-test/lib/mtr_gcov.pl @@ -20,6 +20,8 @@ use strict; +our $basedir; + sub gcov_prepare ($) { my ($dir)= @_; print "Purging gcov information from '$dir'...\n"; @@ -42,7 +44,7 @@ sub gcov_collect ($$$) { # Get current directory to return to later. my $start_dir= cwd(); - print "Collecting source coverage info using '$gcov'...\n"; + print "Collecting source coverage info using '$gcov'...$basedir\n"; -f "$dir/$gcov_msg" and unlink("$dir/$gcov_msg"); -f "$dir/$gcov_err" and unlink("$dir/$gcov_err"); @@ -58,6 +60,7 @@ sub gcov_collect ($$$) { foreach my $f (@flist) { system("$gcov $f 2>>$dir/$gcov_err >>$dir/$gcov_msg"); + system("perl", "$basedir/mysql-test/lib/process-purecov-annotations.pl", "$f.gcov"); } chdir($start_dir); } diff --git a/mysql-test/lib/mtr_gprof.pl b/mysql-test/lib/mtr_gprof.pl index 15aad17a3d4..34e722a251f 100644 --- a/mysql-test/lib/mtr_gprof.pl +++ b/mysql-test/lib/mtr_gprof.pl @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2011, Oracle and/or its affiliates # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index e50e8cc77df..98f2c27ebce 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -1,5 +1,6 @@ # -*- cperl -*- -# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. +# Copyright (c) 2009-2011, Monty Program Ab # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -242,6 +243,31 @@ sub mtr_milli_sleep ($) { select(undef, undef, undef, ($millis/1000)); } +sub mtr_wait_lock_file { + die "usage: mtr_wait_lock_file(path_to_file, keep_alive)" unless @_ == 2; + my ($file, $keep_alive)= @_; + my $waited= 0; + my $msg_counter= $keep_alive; + + while ( -e $file) + { + if ($keep_alive && !$msg_counter) + { + print "\n-STOPPED- [pass] ".$keep_alive."\n"; + $msg_counter= $keep_alive; + } + mtr_milli_sleep(1000); + $waited= 1; + $msg_counter--; + } + 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 diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 88e714f6f8d..4bb6be711f0 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2010, Oracle and/or its affiliates # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ BEGIN eval 'sub USE_NETPING { $use_netping }'; } -sub sleep_until_file_created ($$$); +sub sleep_until_file_created ($$$$); sub mtr_ping_port ($); sub mtr_ping_port ($) { @@ -102,10 +102,11 @@ sub mtr_ping_port ($) { # FIXME check that the pidfile contains the expected pid! -sub sleep_until_file_created ($$$) { +sub sleep_until_file_created ($$$$) { my $pidfile= shift; my $timeout= shift; my $proc= shift; + my $warn_seconds = shift; my $sleeptime= 100; # Milliseconds my $loops= ($timeout * 1000) / $sleeptime; @@ -128,8 +129,8 @@ sub sleep_until_file_created ($$$) { mtr_debug("Sleep $sleeptime milliseconds waiting for $pidfile"); - # Print extra message every 60 seconds - if ( $seconds > 1 && int($seconds * 10) % 600 == 0 && $seconds < $timeout ) + # Print extra message every $warn_seconds seconds + if ( $seconds > 1 && ($seconds*10) % ($warn_seconds*10) == 0 && $seconds < $timeout ) { my $left= $timeout - $seconds; mtr_warning("Waited $seconds seconds for $pidfile to be created, " . diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index af5b32ed939..9ab82c454ed 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -1,15 +1,16 @@ # -*- cperl -*- -# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. +# Copyright (c) 2009-2011, Monty Program Ab # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA @@ -63,14 +64,10 @@ sub _name { sub _mtr_report_test_name ($) { my $tinfo= shift; - my $tname= $tinfo->{name}; + my $tname= $tinfo->fullname(); return unless defined $verbose; - # Add combination name if any - $tname.= " '$tinfo->{combination}'" - if defined $tinfo->{combination}; - print _name(). _timestamp(); printf "%-40s ", $tname; my $worker = $tinfo->{worker}; @@ -228,8 +225,11 @@ sub mtr_report_test ($) { } -sub mtr_report_stats ($$;$) { - my ($prefix, $tests, $dont_error)= @_; +sub mtr_report_stats ($$$$) { + my $prefix= shift; + my $fail= shift; + my $tests= shift; + my $extra_warnings= shift; # ---------------------------------------------------------------------- # Find out how we where doing @@ -308,14 +308,13 @@ sub mtr_report_stats ($$;$) { resfile_global("duration", time - $BASETIME) if $::opt_resfile; my $warnlog= "$::opt_vardir/log/warnings"; - if ( -f $warnlog ) + if ( ! $::glob_use_running_server && !$::opt_extern && -f $warnlog) { mtr_warning("Got errors/warnings while running tests, please examine", "'$warnlog' for details."); - } + } print "\n"; - # Print a list of check_testcases that failed(if any) if ( $::opt_check_testcases ) { @@ -385,12 +384,29 @@ sub mtr_report_stats ($$;$) { print "All $tot_tests tests were successful.\n\n"; } + if (@$extra_warnings) + { + print <<MSG; +Errors/warnings were found in logfiles during server shutdown after running the +following sequence(s) of tests: +MSG + print " $_\n" for @$extra_warnings; + } + print "$tot_skipped tests were skipped, ". "$tot_skipdetect by the test itself.\n\n" if $tot_skipped; if ( $tot_failed != 0 || $found_problems) { - mtr_error("there were failing test cases") unless $dont_error; + mtr_error("there were failing test cases"); + } + elsif (@$extra_warnings) + { + mtr_error("There where errors/warnings in server logs after running test cases."); + } + elsif ($fail) + { + mtr_error("Test suite failure, see messages above for possible cause(s)."); } } diff --git a/mysql-test/lib/process-purecov-annotations.pl b/mysql-test/lib/process-purecov-annotations.pl new file mode 100755 index 00000000000..d533bd02fd6 --- /dev/null +++ b/mysql-test/lib/process-purecov-annotations.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl +# -*- cperl -*- + +# This script processes a .gcov coverage report to honor purecov +# annotations: lines marked as inspected or as deadcode are changed +# from looking like lines with code that was never executed to look +# like lines that have no executable code. + +use strict; +use warnings; + +foreach my $in_file_name ( @ARGV ) +{ + my $out_file_name=$in_file_name . ".tmp"; + my $skipping=0; + + open(IN, "<", $in_file_name) || next; + open(OUT, ">", $out_file_name); + while(<IN>) + { + my $line= $_; + my $check= $line; + + # process purecov: start/end multi-blocks + my $started=0; + my $ended= 0; + while (($started=($check =~ s/purecov: *begin *(deadcode|inspected)//)) || + ($ended=($check =~ s/purecov: *end//))) + { + $skipping= $skipping + $started - $ended; + } + if ($skipping < 0) + { + print OUT "WARNING: #####: incorrect order of purecov begin/end annotations\n"; + $skipping= 0; + } + + # Besides purecov annotations, also remove uncovered code mark from cases + # like the following: + # + # -: 211:*/ + # -: 212:class Field_value : public Value_dep + # #####: 213:{ + # -: 214:public: + # + # I have no idea why would gcov think there is uncovered code there + # + my @arr= split(/:/, $line); + if ($skipping || $line =~ /purecov: *(inspected|deadcode)/ || + $arr[2] =~ m/^{ *$/) + { + # Change '####' to '-'. + $arr[0] =~ s/#####/ -/g; + $line= join(":", @arr); + } + print OUT $line; + } + close(IN); + close(OUT); + system("mv", "-f", $out_file_name, $in_file_name); +} + + diff --git a/mysql-test/lib/v1/mtr_process.pl b/mysql-test/lib/v1/mtr_process.pl index 333f8bcc698..f86f9e5dec0 100644 --- a/mysql-test/lib/v1/mtr_process.pl +++ b/mysql-test/lib/v1/mtr_process.pl @@ -476,12 +476,6 @@ sub mtr_kill_leftovers () { } } } - else - { - mtr_warning("Found non pid file $elem in $rundir") - if -f "$rundir/$elem"; - next; - } } closedir(RUNDIR); @@ -888,15 +882,33 @@ sub check_expected_crash_and_restart($) mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); $mysqld->{'pid'}= 0; - # Check if crash expected and restart if it was + # Check if crash expected, and restart if it was my $expect_file= "$::opt_vardir/tmp/" . "$mysqld->{'type'}" . "$mysqld->{'idx'}" . ".expect"; - if ( -f $expect_file ) + while ( 1 ) { - mtr_verbose("Crash was expected, file $expect_file exists"); - mysqld_start($mysqld, $mysqld->{'start_opts'}, - $mysqld->{'start_slave_master_info'}); - unlink($expect_file); + if ( -f $expect_file ) + { + mtr_verbose("Crash was expected, file $expect_file exists"); + my $expect_file_handler; + open($expect_file_handler, "<$expect_file") or die; + my @expect_lines= <$expect_file_handler>; + close $expect_file_handler; + # look at most recent order by the test + my $expect_content= pop @expect_lines; + chomp $expect_content; + if ( $expect_content =~ /^wait/ ) + { + mtr_verbose("Test asks that we wait before restart"); + # Millisceond sleep emulated with select + select(undef, undef, undef, (0.1)); + next; + } + unlink($expect_file); + mysqld_start($mysqld, $mysqld->{'start_opts'}, + $mysqld->{'start_slave_master_info'}); + } + last; } return; @@ -916,8 +928,8 @@ sub check_expected_crash_and_restart($) if ( -f $expect_file ) { mtr_verbose("Crash was expected, file $expect_file exists"); - ndbmgmd_start($cluster); unlink($expect_file); + ndbmgmd_start($cluster); } return; } @@ -935,9 +947,9 @@ sub check_expected_crash_and_restart($) if ( -f $expect_file ) { mtr_verbose("Crash was expected, file $expect_file exists"); + unlink($expect_file); ndbd_start($cluster, $ndbd->{'idx'}, $ndbd->{'start_extra_args'}); - unlink($expect_file); } return; } diff --git a/mysql-test/lib/v1/mtr_report.pl b/mysql-test/lib/v1/mtr_report.pl index c34df9e9352..7166eb523f2 100644 --- a/mysql-test/lib/v1/mtr_report.pl +++ b/mysql-test/lib/v1/mtr_report.pl @@ -220,7 +220,7 @@ sub mtr_report_stats ($) { # the "var/log/*.err" files. We save this info in "var/log/warnings" # ---------------------------------------------------------------------- - if ( ! $::glob_use_running_server ) + if ( ! $::glob_use_running_server && !$::opt_extern) { # Save and report if there was any fatal warnings/errors in err logs @@ -387,11 +387,10 @@ sub mtr_report_stats ($) { (/Failed during slave.*thread initialization/ )) or - # rpl_temporary has an error on slave that can be ignored - ($testname eq 'rpl.rpl_temporary' and - (/Slave: Can\'t find record in \'user\' Error_code: 1032/ - )) or - + # rpl_temporary has an error on slave that can be ignored + ($testname eq 'rpl.rpl_temporary' and + (/Slave: Can\'t find record in \'user\' Error_code: 1032/ + )) or # Test case for Bug#31590 produces the following error: /Out of sort memory; increase server sort buffer size/ or @@ -404,8 +403,18 @@ sub mtr_report_stats ($) { # When trying to set lower_case_table_names = 2 # on a case sensitive file system. Bug#37402. - /lower_case_table_names was set to 2, even though your the file system '.*' is case sensitive. Now setting lower_case_table_names to 0 to avoid future problems./ - ) + /lower_case_table_names was set to 2, even though your the file system '.*' is case sensitive. Now setting lower_case_table_names to 0 to avoid future problems./ or + + # maria-recovery.test has warning about missing log file + /File '.*maria_log.000.*' not found \(Errcode: 2\)/ or + # and about marked-corrupted table + /Table '..mysqltest.t_corrupted1' is crashed, skipping it. Please repair it with maria_chk -r/ or + # maria-recover.test corrupts tables on purpose + /Checking table: '..mysqltest.t_corrupted2'/ or + /Recovering table: '..mysqltest.t_corrupted2'/ or + /Table '..mysqltest.t_corrupted2' is marked as crashed and should be repaired/ or + /Incorrect key file for table '..mysqltest.t_corrupted2.MAI'; try to repair it/ + ) { next; # Skip these lines } diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index c0d23b26fe2..4e1625db093 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -148,8 +148,9 @@ our $opt_vs_config = $ENV{'MTR_VS_CONFIG'}; our $default_vardir; our $opt_usage; +our $opt_list_options; our $opt_suites; -our $opt_suites_default= "main,binlog,rpl"; # Default suites to run +our $opt_suites_default= "main,binlog,rpl,maria"; # Default suites to run our $opt_script_debug= 0; # Script debugging, enable with --script-debug our $opt_verbose= 0; # Verbose output, enable with --verbose @@ -185,6 +186,7 @@ our @opt_combinations; our $opt_skip_combination; our @opt_extra_mysqld_opt; +our @opt_extra_mysqltest_opt; our $opt_compress; our $opt_ssl; @@ -444,7 +446,7 @@ sub main () { my $tests= collect_test_cases($opt_suites); # Turn off NDB and other similar options if no tests use it - my ($need_ndbcluster,$need_im); + my ($need_ndbcluster,$need_im, $need_debug); foreach my $test (@$tests) { next if $test->{skip}; @@ -452,6 +454,7 @@ sub main () { if (!$opt_extern) { $need_ndbcluster||= $test->{ndb_test}; + $need_debug||=$test->{need_debug}; $need_im||= $test->{component_id} eq 'im'; # Count max number of slaves used by a test case @@ -477,6 +480,11 @@ sub main () { $opt_skip_ndbcluster_slave= 1; } + if ( !$need_debug && !$opt_debug) + { + $opt_debug=0; + } + # Check if slave cluster can be skipped if ($max_slave_num == 0) { @@ -561,7 +569,7 @@ sub command_line_setup () { ); Getopt::Long::Configure("pass_through"); - GetOptions( + my %options=( # Control what engine/variation to run 'embedded-server' => \$opt_embedded_server, 'ps-protocol' => \$opt_ps_protocol, @@ -613,6 +621,9 @@ sub command_line_setup () { # Extra options used when starting mysqld 'mysqld=s' => \@opt_extra_mysqld_opt, + # Extra options used when starting mysqld + 'mysqltest=s' => \@opt_extra_mysqltest_opt, + # Run test on running server 'extern' => \$opt_extern, 'ndb-connectstring=s' => \$opt_ndbconnectstring, @@ -696,9 +707,13 @@ sub command_line_setup () { (map { $_ => \&warn_about_removed_option } @removed_options), 'help|h' => \$opt_usage, - ) or usage("Can't read options"); + 'list-options' => \$opt_list_options, + ); + + GetOptions(%options) or usage("Can't read options"); usage("") if $opt_usage; + list_options(\%options) if $opt_list_options; $glob_scriptname= basename($0); @@ -1004,7 +1019,7 @@ sub command_line_setup () { # -------------------------------------------------------------------------- if ($opt_extern) { - mtr_report("Disable instance manager when running with extern mysqld"); + # mtr_report("Disable instance manager when running with extern mysqld"); $opt_skip_im= 1; } elsif ( $mysql_version_id < 50000 ) @@ -1379,19 +1394,6 @@ sub command_line_setup () { $path_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; $path_snapshot= "$opt_tmpdir/snapshot_$opt_master_myport/"; - - if ( $opt_valgrind and $opt_debug ) - { - # When both --valgrind and --debug is selected, send - # all output to the trace file, making it possible to - # see the exact location where valgrind complains - foreach my $mysqld (@{$master}, @{$slave}) - { - my $sidx= $mysqld->{idx} ? "$mysqld->{idx}" : ""; - $mysqld->{path_myerr}= - "$opt_vardir/log/" . $mysqld->{type} . "$sidx.trace"; - } - } } # @@ -2150,7 +2152,10 @@ sub environment_setup () { $ENV{'MYSQL_UPGRADE'}= mysql_upgrade_arguments(); } - $ENV{'MYSQL_FIX_PRIVILEGE_TABLES'}= $file_mysql_fix_privilege_tables; + if ( !$opt_extern ) + { + $ENV{'MYSQL_FIX_PRIVILEGE_TABLES'}= $file_mysql_fix_privilege_tables; + } # ---------------------------------------------------- # Setup env so childs can execute my_print_defaults @@ -2200,6 +2205,22 @@ sub environment_setup () { "$glob_bindir/myisam/myisampack")); # ---------------------------------------------------- + # Setup env so childs can execute aria_pack and aria_chk + # ---------------------------------------------------- + $ENV{'ARIA_CHK'}= mtr_native_path(mtr_exe_maybe_exists( + vs_config_dirs('storage/maria', 'aria_chk'), + vs_config_dirs('maria', 'aria_chk'), + "$path_client_bindir/aria_chk", + "$glob_basedir/storage/maria/aria_chk", + "$glob_basedir/maria/aria_chk")); + $ENV{'ARIA_PACK'}= mtr_native_path(mtr_exe_maybe_exists( + vs_config_dirs('storage/maria', 'aria_pack'), + vs_config_dirs('maria', 'aria_pack'), + "$path_client_bindir/aria_pack", + "$glob_basedir/storage/maria/aria_pack", + "$glob_basedir/maria/aria_pack")); + + # ---------------------------------------------------- # We are nice and report a bit about our settings # ---------------------------------------------------- if (!$opt_extern) @@ -2328,8 +2349,11 @@ sub remove_stale_vardir () { mtr_report("WARNING: Using the 'mysql-test/var' symlink"); # Make sure the directory where it points exist - mtr_error("The destination for symlink $opt_vardir does not exist") - if ! -d readlink($opt_vardir); + if (! -d readlink($opt_vardir)) + { + mtr_report("The destination for symlink $opt_vardir does not exist; Removing it and creating a new var directory"); + unlink($opt_vardir); + } foreach my $bin ( glob("$opt_vardir/*") ) { @@ -2363,8 +2387,8 @@ sub remove_stale_vardir () { # Remove the var/ dir in mysql-test dir if any # this could be an old symlink that shouldn't be there - mtr_verbose("Removing $default_vardir"); - mtr_rmtree($default_vardir); + # mtr_verbose("Removing $default_vardir"); + # mtr_rmtree($default_vardir); # Remove the "var" dir mtr_verbose("Removing $opt_vardir/"); @@ -2388,8 +2412,11 @@ sub setup_vardir() { # it's a symlink # Make sure the directory where it points exist - mtr_error("The destination for symlink $opt_vardir does not exist") - if ! -d readlink($opt_vardir); + if (! -d readlink($opt_vardir)) + { + mtr_report("The destination for symlink $opt_vardir does not exist; Removing it and creating a new var directory"); + unlink($opt_vardir); + } } elsif ( $opt_mem ) { @@ -2444,6 +2471,25 @@ sub setup_vardir() { { unlink($name); } + if ( $opt_valgrind and $opt_debug ) + { + # When both --valgrind and --debug is selected, send + # all output to the trace file, making it possible to + # see the exact location where valgrind complains + foreach my $mysqld (@{$master}, @{$slave}) + { + my $sidx= $mysqld->{idx} ? "$mysqld->{idx}" : ""; + my $trace_name= "$opt_vardir/log/" . $mysqld->{type} . "$sidx.trace"; + open(LOG, ">$mysqld->{path_myerr}") or die "Can't create $mysqld->{path_myerr}\n"; + print LOG " +NOTE: When running with --valgrind --debug the output from the .err file is +stored together with the trace file to make it easier to find the exact +position for valgrind errors. +See trace file $trace_name.\n"; + close(LOG); + $mysqld->{path_myerr}= $trace_name; + } + } } @@ -2896,7 +2942,7 @@ sub run_benchmarks ($) { if ( ! $benchmark ) { - mtr_add_arg($args, "--log"); + mtr_add_arg($args, "--general-log"); mtr_run("$glob_mysql_bench_dir/run-all-tests", $args, "", "", "", ""); # FIXME check result code?! } @@ -3120,17 +3166,26 @@ sub install_db ($$) { mtr_report("Installing \u$type Database"); - my $args; + my $cmd_args; mtr_init_args(\$args); mtr_add_arg($args, "--no-defaults"); mtr_add_arg($args, "--bootstrap"); mtr_add_arg($args, "--basedir=%s", $path_my_basedir); mtr_add_arg($args, "--datadir=%s", $data_dir); mtr_add_arg($args, "--loose-skip-ndbcluster"); + mtr_add_arg($args, "--loose-skip-aria"); + mtr_add_arg($args, "--disable-sync-frm"); + mtr_add_arg($args, "--loose-disable-debug"); mtr_add_arg($args, "--tmpdir=."); mtr_add_arg($args, "--core-file"); + # + # Setup args for bootstrap.test + # + mtr_init_args(\$cmd_args); + mtr_add_arg($cmd_args, "--loose-skip-aria"); + if ( $opt_debug ) { mtr_add_arg($args, "--debug=d:t:i:A,%s/log/bootstrap_%s.trace", @@ -3159,7 +3214,8 @@ sub install_db ($$) { # ---------------------------------------------------------------------- # export MYSQLD_BOOTSTRAP_CMD variable containing <path>/mysqld <args> # ---------------------------------------------------------------------- - $ENV{'MYSQLD_BOOTSTRAP_CMD'}= "$exe_mysqld_bootstrap " . join(" ", @$args); + $ENV{'MYSQLD_BOOTSTRAP_CMD'}= "$exe_mysqld_bootstrap " . join(" ", @$args) . + " " . join(" ", @$cmd_args); # ---------------------------------------------------------------------- # Create the bootstrap.sql file @@ -3173,6 +3229,8 @@ sub install_db ($$) { # for a production system mtr_appendfile_to_file("$path_sql_dir/mysql_system_tables.sql", $bootstrap_sql_file); + mtr_appendfile_to_file("$path_sql_dir/mysql_performance_tables.sql", + $bootstrap_sql_file); # Add the mysql system tables initial data # for a production system @@ -3337,6 +3395,24 @@ sub run_testcase_check_skip_test($) my ($tinfo)= @_; # ---------------------------------------------------------------------- + # Skip some tests silently + # ---------------------------------------------------------------------- + + if ( $::opt_start_from ) + { + if ($tinfo->{'name'} eq $::opt_start_from ) + { + ## Found parting test. Run this test and all tests after this one + $::opt_start_from= ""; + } + else + { + $tinfo->{'result'}= 'MTR_RES_SKIPPED'; + return 1; + } + } + + # ---------------------------------------------------------------------- # If marked to skip, just print out and return. # Note that a test case not marked as 'skip' can still be # skipped later, because of the test case itself in cooperation @@ -3918,17 +3994,22 @@ sub mysqld_arguments ($$$$) { mtr_add_arg($args, "%s--datadir=%s", $prefix, $mysqld->{'path_myddir'}); + mtr_add_arg($args, "%s--disable-sync-frm", $prefix); # Faster test - if ( $mysql_version_id >= 50106 ) + if (!$opt_extern and $mysql_version_id >= 50106 ) { # Turn on logging to bothe tables and file mtr_add_arg($args, "%s--log-output=table,file", $prefix); } my $log_base_path= "$opt_vardir/log/$mysqld->{'type'}$sidx"; - mtr_add_arg($args, "%s--log=%s.log", $prefix, $log_base_path); + mtr_add_arg($args, "%s--general-log-file=%s.log", + $prefix, $log_base_path); + mtr_add_arg($args, "%s--general-log", $prefix); mtr_add_arg($args, - "%s--log-slow-queries=%s-slow.log", $prefix, $log_base_path); + "%s--slow-query-log-file=%s-slow.log", + $prefix, $log_base_path); + mtr_add_arg($args, "%s--slow-query-log", $prefix); # Check if "extra_opt" contains --skip-log-bin my $skip_binlog= grep(/^--skip-log-bin/, @$extra_opt, @opt_extra_mysqld_opt); @@ -4052,10 +4133,17 @@ sub mysqld_arguments ($$$$) { } # end slave - if ( $opt_debug ) + if ( $debug_compiled_binaries && defined $opt_debug ) { - mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/%s%s.trace", - $prefix, $path_vardir_trace, $mysqld->{'type'}, $sidx); + if ( $opt_debug ) + { + mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/%s%s.trace", + $prefix, $path_vardir_trace, $mysqld->{'type'}, $sidx); + } + else + { + mtr_add_arg($args, "--disable-debug"); + } } mtr_add_arg($args, "%s--key_buffer_size=1M", $prefix); @@ -4908,6 +4996,11 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--skip-ssl"); } + foreach my $arg ( @opt_extra_mysqltest_opt ) + { + mtr_add_arg($args, "%s", $arg); + } + # ---------------------------------------------------------------------- # If embedded server, we create server args to give mysqltest to pass on # ---------------------------------------------------------------------- @@ -5023,13 +5116,9 @@ sub gdb_arguments { else { # write init file for mysqld - mtr_tofile($gdb_init_file, - "set args $str\n" . - "break mysql_parse\n" . - "commands 1\n" . - "disable 1\n" . - "end\n" . - "run"); + mtr_tofile($gdb_init_file, <<EOGDB ); +set args $str +EOGDB } if ( $opt_manual_gdb ) @@ -5089,11 +5178,7 @@ sub ddd_arguments { # write init file for mysqld mtr_tofile($gdb_init_file, "file $$exe\n" . - "set args $str\n" . - "break mysql_parse\n" . - "commands 1\n" . - "disable 1\n" . - "end"); + "set args $str\n"); } if ( $opt_manual_ddd ) @@ -5189,6 +5274,7 @@ sub valgrind_arguments { { mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option mtr_add_arg($args, "--leak-check=yes"); + #mtr_add_arg($args, "--db-attach=yes"); mtr_add_arg($args, "--num-callers=16"); mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) if -f "$glob_mysql_test_dir/valgrind.supp"; @@ -5381,3 +5467,16 @@ HERE mtr_exit(1); } + +sub list_options ($) { + my $hash= shift; + + for (keys %$hash) { + s/(=.*|!)$//; + s/\|/\n--/g; + print "--$_\n"; + } + + mtr_exit(1); +} + |