summaryrefslogtreecommitdiff
path: root/mysql-test/lib
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-test/lib')
-rw-r--r--mysql-test/lib/My/Config.pm145
-rw-r--r--mysql-test/lib/My/ConfigFactory.pm100
-rw-r--r--mysql-test/lib/My/File/Path.pm2
-rw-r--r--mysql-test/lib/My/Find.pm36
-rw-r--r--mysql-test/lib/My/Options.pm2
-rw-r--r--mysql-test/lib/My/SafeProcess.pm24
-rw-r--r--mysql-test/lib/My/SafeProcess/safe_process.cc49
-rw-r--r--mysql-test/lib/My/Suite.pm26
-rw-r--r--mysql-test/lib/My/Test.pm96
-rw-r--r--mysql-test/lib/mtr_cases.pm1431
-rw-r--r--mysql-test/lib/mtr_gcov.pl5
-rw-r--r--mysql-test/lib/mtr_gprof.pl2
-rw-r--r--mysql-test/lib/mtr_misc.pl28
-rw-r--r--mysql-test/lib/mtr_process.pl11
-rw-r--r--mysql-test/lib/mtr_report.pm44
-rwxr-xr-xmysql-test/lib/process-purecov-annotations.pl63
-rw-r--r--mysql-test/lib/v1/mtr_process.pl40
-rw-r--r--mysql-test/lib/v1/mtr_report.pl25
-rwxr-xr-xmysql-test/lib/v1/mysql-test-run.pl191
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);
+}
+