Commit 4c2f1d9b authored by unknown's avatar unknown

Add support for running in parallel


mysql-test/lib/My/Options.pm:
  Allow $VAR as option
mysql-test/lib/My/SafeProcess.pm:
  Remove printouts
mysql-test/lib/My/Test.pm:
  New BitKeeper file ``mysql-test/lib/My/Test.pm''
parent 31452b10
...@@ -61,6 +61,9 @@ sub _split_option { ...@@ -61,6 +61,9 @@ sub _split_option {
elsif ($option=~ /^--(.*)$/){ elsif ($option=~ /^--(.*)$/){
return ($1, undef) return ($1, undef)
} }
elsif ($option=~ /^\$(.*)$/){ # $VAR
return ($1, undef)
}
elsif ($option=~ /^(.*)=(.*)$/){ elsif ($option=~ /^(.*)=(.*)$/){
return ($1, $2) return ($1, $2)
} }
......
...@@ -65,7 +65,7 @@ END { ...@@ -65,7 +65,7 @@ END {
# Kill any children still running # Kill any children still running
for my $proc (values %running){ for my $proc (values %running){
if ( $proc->is_child($$) ){ if ( $proc->is_child($$) ){
print "Killing: $proc\n"; #print "Killing: $proc\n";
$proc->kill(); $proc->kill();
} }
} }
...@@ -461,8 +461,8 @@ sub wait_one { ...@@ -461,8 +461,8 @@ sub wait_one {
return 1; return 1;
} }
warn "wait_one: expected pid $pid but got $retpid" #warn "wait_one: expected pid $pid but got $retpid"
unless( $retpid == $pid ); # unless( $retpid == $pid );
$self->_collect(); $self->_collect();
return 0; return 0;
......
# -*- cperl -*-
#
# One test
#
package My::Test;
use strict;
use warnings;
use Carp;
sub new {
my $class= shift;
my $self= bless {
@_,
}, $class;
return $self;
}
#
# Return a unique key that can be used to
# identify this test in a hash
#
sub key {
my ($self)= @_;
my $key= $self->{name};
$key.= "+".$self->{combination} if $self->{combination};
return $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};
croak "'is_failed' can't be called until test has been run!"
unless defined $result;
return ($result eq 'MTR_RES_FAILED');
}
sub write_test {
my ($test, $sock, $header)= @_;
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";
}
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);
}
}
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;
...@@ -40,7 +40,7 @@ our $default_storage_engine; ...@@ -40,7 +40,7 @@ our $default_storage_engine;
our $opt_with_ndbcluster_only; our $opt_with_ndbcluster_only;
our $defaults_file; our $defaults_file;
our $defaults_extra_file; our $defaults_extra_file;
our $reorder; our $reorder= 1;
sub collect_option { sub collect_option {
my ($opt, $value)= @_; my ($opt, $value)= @_;
...@@ -55,6 +55,7 @@ use File::Basename; ...@@ -55,6 +55,7 @@ use File::Basename;
use IO::File(); use IO::File();
use My::Config; use My::Config;
use My::Platform; use My::Platform;
use My::Test;
use My::Find; use My::Find;
require "mtr_misc.pl"; require "mtr_misc.pl";
...@@ -135,52 +136,16 @@ sub collect_test_cases ($$) { ...@@ -135,52 +136,16 @@ sub collect_test_cases ($$) {
{ {
my @criteria = (); my @criteria = ();
# Look for tests that must be run in a defined order - that is #
# defined by test having the same name except for the ending digit # Append the criteria for sorting, in order of importance.
#
push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "A" : "B"));
# 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}) . "~");
# Put variables into hash $sort_criteria{$tinfo->{name}} = join(" ", @criteria);
my $test_name= $tinfo->{'name'};
my $depend_on_test_name;
if ( $test_name =~ /^([\D]+)([0-9]{1})$/ )
{
my $base_name= $1;
my $idx= $2;
mtr_verbose("$test_name => $base_name idx=$idx");
if ( $idx > 1 )
{
$idx-= 1;
$base_name= "$base_name$idx";
mtr_verbose("New basename $base_name");
}
foreach my $tinfo2 (@$cases)
{
if ( $tinfo2->{'name'} eq $base_name )
{
mtr_verbose("found dependent test $tinfo2->{'name'}");
$depend_on_test_name=$base_name;
}
}
}
if ( defined $depend_on_test_name )
{
mtr_verbose("Giving $test_name same critera as $depend_on_test_name");
$sort_criteria{$test_name} = $sort_criteria{$depend_on_test_name};
}
else
{
#
# Append the criteria for sorting, in order of importance.
#
push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "1" : "0"));
# 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}) . "~");
$sort_criteria{$test_name} = join(" ", @criteria);
}
} }
@$cases = sort { @$cases = sort {
...@@ -454,7 +419,7 @@ sub collect_one_suite($) ...@@ -454,7 +419,7 @@ sub collect_one_suite($)
} }
# Copy test options # Copy test options
my $new_test= {}; my $new_test= My::Test->new();
while (my ($key, $value) = each(%$test)) { while (my ($key, $value) = each(%$test)) {
if (ref $value eq "ARRAY") { if (ref $value eq "ARRAY") {
push(@{$new_test->{$key}}, @$value); push(@{$new_test->{$key}}, @$value);
...@@ -682,13 +647,16 @@ sub collect_one_test_case { ...@@ -682,13 +647,16 @@ sub collect_one_test_case {
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Set defaults # Set defaults
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
my $tinfo= {}; my $tinfo= My::Test->new
$tinfo->{'name'}= $suitename . ".$tname"; (
$tinfo->{'path'}= "$testdir/$filename"; name => "$suitename.$tname",
path => "$testdir/$filename",
# TODO allow nonexistsing result file # TODO allow nonexistsing result file
# in that case .test must issue "exit" otherwise test should fail by default # in that case .test must issue "exit" otherwise test
$tinfo->{'result_file'}= "$resdir/$tname.result"; # should fail by default
result_file => "$resdir/$tname.result",
);
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Skip some tests but include in list, just mark them as skipped # Skip some tests but include in list, just mark them as skipped
...@@ -1034,19 +1002,6 @@ sub unspace { ...@@ -1034,19 +1002,6 @@ sub unspace {
} }
sub envsubst {
my $string= shift;
if ( ! defined $ENV{$string} )
{
mtr_error(".opt file references '$string' which is not set");
}
return $ENV{$string};
}
sub opts_from_file ($) { sub opts_from_file ($) {
my $file= shift; my $file= shift;
...@@ -1083,10 +1038,6 @@ sub opts_from_file ($) { ...@@ -1083,10 +1038,6 @@ sub opts_from_file ($) {
or $arg =~ s/^([^\'\"]*)\"(.*)\"([^\'\"]*)$/$1$2$3/; or $arg =~ s/^([^\'\"]*)\"(.*)\"([^\'\"]*)$/$1$2$3/;
$arg =~ s/\\\\/\\/g; $arg =~ s/\\\\/\\/g;
# Expand environment variables
$arg =~ s/\$\{(\w+)\}/envsubst($1)/ge;
$arg =~ s/\$(\w+)/envsubst($1)/ge;
# Do not pass empty string since my_getopt is not capable to handle it. # Do not pass empty string since my_getopt is not capable to handle it.
if (length($arg)) { if (length($arg)) {
push(@args, $arg); push(@args, $arg);
...@@ -1102,17 +1053,7 @@ sub print_testcases { ...@@ -1102,17 +1053,7 @@ sub print_testcases {
print "=" x 60, "\n"; print "=" x 60, "\n";
foreach my $test (@cases){ foreach my $test (@cases){
print "[", $test->{name}, "]", "\n"; $test->print_test();
while ((my ($key, $value)) = each(%$test)) {
print " ", $key, "= ";
if (ref $value eq "ARRAY") {
print "[", join(", ", @$value), "]";
} else {
print $value;
}
print "\n";
}
print "\n";
} }
print "=" x 60, "\n"; print "=" x 60, "\n";
} }
......
...@@ -27,7 +27,7 @@ our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line ...@@ -27,7 +27,7 @@ our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line
mtr_warning mtr_error mtr_debug mtr_verbose mtr_warning mtr_error mtr_debug mtr_verbose
mtr_verbose_restart mtr_report_test_passed mtr_verbose_restart mtr_report_test_passed
mtr_report_test_failed mtr_report_test_skipped mtr_report_test_failed mtr_report_test_skipped
mtr_report_stats); mtr_report_stats mtr_report_test);
use mtr_match; use mtr_match;
require "mtr_io.pl"; require "mtr_io.pl";
...@@ -35,6 +35,10 @@ require "mtr_io.pl"; ...@@ -35,6 +35,10 @@ require "mtr_io.pl";
my $tot_real_time= 0; my $tot_real_time= 0;
our $timestamp= 0; our $timestamp= 0;
our $name;
our $verbose;
our $verbose_restart= 0;
sub report_option { sub report_option {
my ($opt, $value)= @_; my ($opt, $value)= @_;
...@@ -43,6 +47,8 @@ sub report_option { ...@@ -43,6 +47,8 @@ sub report_option {
$opt =~ s/-/_/; $opt =~ s/-/_/;
no strict 'refs'; no strict 'refs';
${$opt}= $value; ${$opt}= $value;
#print $name, " setting $opt to ", (defined $value? $value : "undef") ,"\n";
} }
sub SHOW_SUITE_NAME() { return 1; }; sub SHOW_SUITE_NAME() { return 1; };
...@@ -51,6 +57,8 @@ sub _mtr_report_test_name ($) { ...@@ -51,6 +57,8 @@ sub _mtr_report_test_name ($) {
my $tinfo= shift; my $tinfo= shift;
my $tname= $tinfo->{name}; my $tname= $tinfo->{name};
return unless defined $verbose;
# Remove suite part of name # Remove suite part of name
$tname =~ s/.*\.// unless SHOW_SUITE_NAME; $tname =~ s/.*\.// unless SHOW_SUITE_NAME;
...@@ -58,7 +66,7 @@ sub _mtr_report_test_name ($) { ...@@ -58,7 +66,7 @@ sub _mtr_report_test_name ($) {
$tname.= " '$tinfo->{combination}'" $tname.= " '$tinfo->{combination}'"
if defined $tinfo->{combination}; if defined $tinfo->{combination};
print _timestamp(); print $name, _timestamp();
printf "%-30s ", $tname; printf "%-30s ", $tname;
} }
...@@ -100,12 +108,19 @@ sub mtr_report_test_passed ($$) { ...@@ -100,12 +108,19 @@ sub mtr_report_test_passed ($$) {
$timer= mtr_fromfile("$::opt_vardir/log/timer"); $timer= mtr_fromfile("$::opt_vardir/log/timer");
$tot_real_time += ($timer/1000); $tot_real_time += ($timer/1000);
$timer= sprintf "%12s", $timer; $timer= sprintf "%12s", $timer;
$tinfo->{timer}= $timer;
} }
# Set as passed unless already set # Set as passed unless already set
if ( not defined $tinfo->{'result'} ){ if ( not defined $tinfo->{'result'} ){
$tinfo->{'result'}= 'MTR_RES_PASSED'; $tinfo->{'result'}= 'MTR_RES_PASSED';
} }
mtr_report("[ pass ] $timer"); mtr_report("[ pass ] $timer");
# Show any problems check-testcase found
if ( defined $tinfo->{'check'} )
{
mtr_report($tinfo->{'check'});
}
} }
...@@ -143,9 +158,7 @@ sub mtr_report_test_failed ($$) { ...@@ -143,9 +158,7 @@ sub mtr_report_test_failed ($$) {
{ {
# Test failure was detected by test tool and its report # Test failure was detected by test tool and its report
# about what failed has been saved to file. Display the report. # about what failed has been saved to file. Display the report.
print "\n"; $tinfo->{logfile}= mtr_fromfile($logfile);
mtr_printfile($logfile);
print "\n";
} }
else else
{ {
...@@ -156,6 +169,83 @@ sub mtr_report_test_failed ($$) { ...@@ -156,6 +169,83 @@ sub mtr_report_test_failed ($$) {
} }
sub mtr_report_test ($) {
my ($tinfo)= @_;
_mtr_report_test_name($tinfo);
if ($tinfo->{'result'} eq 'MTR_RES_FAILED'){
#my $test_failures= $tinfo->{'failures'} || 0;
#$tinfo->{'failures'}= $test_failures + 1;
if ( defined $tinfo->{'warnings'} )
{
mtr_report("[ fail ] Found warnings in server log file!");
mtr_report($tinfo->{'warnings'});
return;
}
if ( defined $tinfo->{'timeout'} )
{
mtr_report("[ fail ] timeout");
return;
}
else
{
mtr_report("[ fail ]");
}
if ( $tinfo->{'comment'} )
{
# The test failure has been detected by mysql-test-run.pl
# when starting the servers or due to other error, the reason for
# failing the test is saved in "comment"
mtr_report("\nERROR: $tinfo->{'comment'}");
}
elsif ( $tinfo->{logfile} )
{
# Test failure was detected by test tool and its report
# about what failed has been saved to file. Display the report.
mtr_report("\n");
mtr_report($tinfo->{logfile}, "\n");
}
else
{
# Neither this script or the test tool has recorded info
# about why the test has failed. Should be debugged.
mtr_report("\nUnexpected termination, probably when starting mysqld");;
}
}
elsif ($tinfo->{'result'} eq 'MTR_RES_SKIPPED')
{
if ( $tinfo->{'disable'} )
{
mtr_report("[ disabled ] $tinfo->{'comment'}");
}
elsif ( $tinfo->{'comment'} )
{
if ( $tinfo->{skip_detected_by_test} )
{
mtr_report("[ skip ]. $tinfo->{'comment'}");
}
else
{
mtr_report("[ skip ] $tinfo->{'comment'}");
}
}
else
{
mtr_report("[ skip ]");
}
}
elsif ($tinfo->{'result'} eq 'MTR_RES_PASSED')
{
my $timer= $tinfo->{timer} || "";
mtr_report("[ pass ] $timer");
}
}
sub mtr_report_stats ($) { sub mtr_report_stats ($) {
my $tests= shift; my $tests= shift;
...@@ -342,35 +432,42 @@ sub _timestamp { ...@@ -342,35 +432,42 @@ sub _timestamp {
# Print message to screen # Print message to screen
sub mtr_report (@) { sub mtr_report (@) {
print join(" ", @_), "\n"; if (defined $verbose)
{
print join(" ", @_), "\n";
}
} }
# Print warning to screen # Print warning to screen
sub mtr_warning (@) { sub mtr_warning (@) {
print STDERR _timestamp(), "mysql-test-run: WARNING: ", join(" ", @_), "\n"; print STDERR $name, _timestamp(),
"mysql-test-run: WARNING: ", join(" ", @_), "\n";
} }
# Print error to screen and then exit # Print error to screen and then exit
sub mtr_error (@) { sub mtr_error (@) {
print STDERR _timestamp(), "mysql-test-run: *** ERROR: ", join(" ", @_), "\n"; print STDERR $name, _timestamp(),
"mysql-test-run: *** ERROR: ", join(" ", @_), "\n";
exit(1); exit(1);
} }
sub mtr_debug (@) { sub mtr_debug (@) {
if ( $::opt_verbose > 1 ) if ( $verbose > 2 )
{ {
print STDERR _timestamp(), "####: ", join(" ", @_), "\n"; print STDERR $name,
_timestamp(), "####: ", join(" ", @_), "\n";
} }
} }
sub mtr_verbose (@) { sub mtr_verbose (@) {
if ( $::opt_verbose ) if ( $verbose )
{ {
print STDERR _timestamp(), "> ",join(" ", @_),"\n"; print STDERR $name, _timestamp(),
"> ",join(" ", @_),"\n";
} }
} }
...@@ -378,9 +475,10 @@ sub mtr_verbose (@) { ...@@ -378,9 +475,10 @@ sub mtr_verbose (@) {
sub mtr_verbose_restart (@) { sub mtr_verbose_restart (@) {
my ($server, @args)= @_; my ($server, @args)= @_;
my $proc= $server->{proc}; my $proc= $server->{proc};
if ( $::opt_verbose_restart ) if ( $verbose_restart )
{ {
print STDERR _timestamp(), "> Restart $proc - ",join(" ", @args),"\n"; print STDERR $name,_timestamp(),
"> Restart $proc - ",join(" ", @args),"\n";
} }
} }
......
...@@ -51,6 +51,8 @@ use My::Find; ...@@ -51,6 +51,8 @@ use My::Find;
use mtr_cases; use mtr_cases;
use mtr_report; use mtr_report;
use mtr_match; use mtr_match;
use IO::Socket::INET;
use IO::Select;
require "lib/mtr_process.pl"; require "lib/mtr_process.pl";
require "lib/mtr_io.pl"; require "lib/mtr_io.pl";
...@@ -88,7 +90,6 @@ my $DEFAULT_SUITES= "main,binlog,federated,rpl,rpl_ndb,ndb"; ...@@ -88,7 +90,6 @@ my $DEFAULT_SUITES= "main,binlog,federated,rpl,rpl_ndb,ndb";
my $opt_suites; my $opt_suites;
our $opt_verbose= 0; # Verbose output, enable with --verbose our $opt_verbose= 0; # Verbose output, enable with --verbose
our $opt_verbose_restart= 0; # Verbose output for restarts
our $exe_mysql; our $exe_mysql;
our $exe_mysqladmin; our $exe_mysqladmin;
...@@ -141,7 +142,7 @@ my $config; # The currently running config ...@@ -141,7 +142,7 @@ my $config; # The currently running config
my $current_config_name; # The currently running config file template my $current_config_name; # The currently running config file template
my $opt_baseport; my $opt_baseport;
my $opt_mtr_build_thread= $ENV{'MTR_BUILD_THREAD'} || "auto"; my $opt_build_thread= $ENV{'MTR_BUILD_THREAD'} || "auto";
my $opt_record; my $opt_record;
my $opt_report_features; my $opt_report_features;
...@@ -161,6 +162,8 @@ my $opt_repeat= 1; ...@@ -161,6 +162,8 @@ my $opt_repeat= 1;
my $opt_retry= 3; my $opt_retry= 3;
my $opt_retry_failure= 2; my $opt_retry_failure= 2;
my $opt_parallel= 1;
my $opt_strace_client; my $opt_strace_client;
our $opt_timer= 1; our $opt_timer= 1;
...@@ -203,25 +206,46 @@ main(); ...@@ -203,25 +206,46 @@ main();
sub main { sub main {
command_line_setup(); # This is needed for test log evaluation in "gen-build-status-page"
# in all cases where the calling tool does not log the commands
# directly before it executes them, like "make test-force-pl" in RPM builds.
mtr_report("Logging: $0 ", join(" ", @ARGV));
mtr_report("Checking supported features..."); Getopt::Long::Configure("pass_through");
GetOptions('parallel=i' => \$opt_parallel) or usage("Can't read options");
check_ndbcluster_support(\%mysqld_variables); # Create server socket on any free port
check_ssl_support(\%mysqld_variables); my $server = new IO::Socket::INET
check_debug_support(\%mysqld_variables); (
LocalAddr => 'localhost',
Proto => 'tcp',
Listen => $opt_parallel,
);
mtr_error("Could not create testcase server port: $!") unless $server;
my $server_port = $server->sockport();
mtr_report("Using server port $server_port");
# Create child processes
my %children;
for my $child_num (1..$opt_parallel){
my $child_pid= My::SafeProcess::Base::_safe_fork();
if ($child_pid == 0){
$server= undef; # Close the server port in child
run_worker($server_port, $child_num);
exit(1);
}
executable_setup(); mtr_report("Started worker, pid: $child_pid");
$children{$child_pid}= 1;
}
environment_setup(); command_line_setup(0);
if ( $opt_gcov ) if ( $opt_gcov ) {
{
gcov_prepare(); gcov_prepare();
} }
if (!$opt_suites) if (!$opt_suites) {
{
$opt_suites= $DEFAULT_SUITES; $opt_suites= $DEFAULT_SUITES;
# Check for any extra suites to enable based on the path name # Check for any extra suites to enable based on the path name
...@@ -235,10 +259,9 @@ sub main { ...@@ -235,10 +259,9 @@ sub main {
"mysql-6.0-ndb" => "ndb_team", "mysql-6.0-ndb" => "ndb_team",
); );
foreach my $dir ( reverse splitdir($basedir) ) foreach my $dir ( reverse splitdir($basedir) ) {
{
my $extra_suite= $extra_suites{$dir}; my $extra_suite= $extra_suites{$dir};
if (defined $extra_suite){ if (defined $extra_suite) {
mtr_report("Found extra suite: $extra_suite"); mtr_report("Found extra suite: $extra_suite");
$opt_suites= "$extra_suite,$opt_suites"; $opt_suites= "$extra_suite,$opt_suites";
last; last;
...@@ -249,26 +272,289 @@ sub main { ...@@ -249,26 +272,289 @@ sub main {
mtr_report("Collecting tests..."); mtr_report("Collecting tests...");
my $tests= collect_test_cases($opt_suites, \@opt_cases); my $tests= collect_test_cases($opt_suites, \@opt_cases);
initialize_servers();
if ( $opt_report_features ) { if ( $opt_report_features ) {
# Put "report features" as the first test to run # Put "report features" as the first test to run
my $tinfo = {}; my $tinfo = My::Test->new
$tinfo->{'name'} = 'report_features'; (
$tinfo->{'result_file'} = undef; # Prints result name => 'report_features',
$tinfo->{'path'} = 'include/report-features.test'; result_file => undef, # Prints result
$tinfo->{'master_opt'} = []; path => 'include/report-features.test'.
$tinfo->{'slave_opt'} = []; master_opt => [],
slave_opt => [],
);
unshift(@$tests, $tinfo); unshift(@$tests, $tinfo);
} }
initialize_servers();
mtr_report();
mtr_print_thick_line();
mtr_print_header();
my $completed= run_test_server($server, $tests, $opt_parallel);
# Send Ctrl-C to any children still running
kill("INT", keys(%children));
# Wait for childs to exit
foreach my $pid (keys %children)
{
my $ret_pid= waitpid($pid, 0);
if ($ret_pid != $pid){
mtr_report("Unknown process $ret_pid exited");
}
else {
delete $children{$ret_pid};
}
}
mtr_verbose("Server exit\n");
mtr_print_line();
mtr_report_stats($completed);
exit(0);
}
sub run_test_server {
my ($server, $tests, $childs) = @_;
# Scheduler variables
my $max_ndb= $opt_parallel / 2;
$max_ndb = 4 if $max_ndb > 4;
$max_ndb = 1 if $max_ndb < 1;
my $num_ndb_tests= 0;
my $completed= [];
my %running;
my $result;
my $s= IO::Select->new();
$s->add($server);
while (1) {
my @ready = $s->can_read(1); # Wake up once every second
foreach my $sock (@ready) {
if ($sock == $server) {
# New client connected
my $child= $sock->accept();
mtr_verbose("Client connected");
$s->add($child);
print $child "HELLO\n";
}
else {
my $line= <$sock>;
if (!defined $line) {
# Client disconnected
mtr_verbose("Child closed socket");
$s->remove($sock);
if (--$childs == 0){
return $completed;
}
next;
}
chomp($line);
if ($line eq 'TESTRESULT'){
$result= My::Test::read_test($sock);
# $result->print_test();
# Report test status
mtr_report_test($result);
if ($result->is_failed() and !$opt_force){
# Test has failed, force is off
push(@$completed, $result);
return $completed;
}
# Retry test run after test failure
my $retries= $result->{retries} || 1;
my $test_has_failed= $result->{failures} || 0;
if ($test_has_failed and $retries < $opt_retry){
# Test should be run one more time unless it has failed
# too many times already
my $failures= $result->{failures};
if ($opt_retry > 1 and $failures >= $opt_retry_failure){
mtr_report("Test has failed $failures times,",
"no more retries!\n");
}
else {
mtr_report("\nRetrying test, attempt($retries/$opt_retry)...\n");
$result->{retries}= $retries+1;
$result->write_test($sock, 'TESTCASE');
next;
}
}
# Repeat test $opt_repeat number of times
my $repeat= $result->{repeat} || 1;
if ($repeat < $opt_repeat)
{
$result->{retries}= 0;
$result->{failures}= 0;
$result->{repeat}= $repeat+1;
$result->write_test($sock, 'TESTCASE');
next;
}
# Remove from list of running
mtr_error("'", $result->{name},"' is not known to be running")
unless delete $running{$result->key()};
# Update scheduler variables
$num_ndb_tests-- if ($result->{ndb_test});
# Save result in completed list
push(@$completed, $result);
}
elsif ($line eq 'START'){
; # Send first test
}
else {
mtr_error("Unknown response: '$line' from client");
}
# Find next test to schedule
# - Try to use same configuration as worker used last time
# - Limit number of parallel ndb tests
my $next;
my $second_best;
for(my $i= 0; $i <= $#$tests; $i++)
{
my $t= $tests->[$i];
if (run_testcase_check_skip_test($t)){
# Move the test to completed list
#mtr_report("skip - Moving test $i to completed");
push(@$completed, splice(@$tests, $i, 1));
redo; # Start over again
}
# Limit number of parallell NDB tests
if ($t->{ndb_test} and $num_ndb_tests >= $max_ndb){
#mtr_report("Skipping, num ndb is already at max, $num_ndb_tests");
next;
}
# Prefer same configuration
if (defined $result and
$result->{template_path} eq $t->{template_path})
{
#mtr_report("Test uses same config => good match");
# Test uses same config => good match
$next= splice(@$tests, $i, 1);
last;
}
# Second best choice is the first that does not fulfill
# any of the above conditions
if (!defined $second_best){
#mtr_report("Setting second_best to $i");
$second_best= $i;
}
}
# Use second best choice if no other test has been found
if (!$next and defined $second_best){
#mtr_report("Take second best choice $second_best");
mtr_error("Internal error, second best too large")
if $second_best > $#$tests;
$next= splice(@$tests, $second_best, 1);
}
if ($next) {
#$next->print_test();
$next->write_test($sock, 'TESTCASE');
$running{$next->key()}= $next;
$num_ndb_tests++ if ($next->{ndb_test});
}
else {
# No more test, tell child to exit
#mtr_report("Saying BYE to child");
print $sock "BYE\n";
}
}
}
}
}
sub run_worker ($) {
my ($server_port, $thread_num)= @_;
$SIG{INT}= sub { exit(1); };
report_option('name',"worker[$thread_num]");
# Connect to server
my $server = new IO::Socket::INET
(
PeerAddr => 'localhost',
PeerPort => $server_port,
Proto => 'tcp'
);
mtr_error("Could not connect to server at port $server_port: $!")
unless $server;
# Read hello from server which it will send when shared
# resources have been setup
my $hello= <$server>;
command_line_setup($thread_num);
if ( $opt_gcov )
{
gcov_prepare();
}
setup_vardir();
mysql_install_db($thread_num);
if ( using_extern() ) { if ( using_extern() ) {
create_config_file_for_extern(%opts_extern); create_config_file_for_extern(%opts_extern);
} }
run_tests($tests); # Ask server for first test
print $server "START\n";
exit(0); while(my $line= <$server>){
chomp($line);
if ($line eq 'TESTCASE'){
my $test= My::Test::read_test($server);
#$test->print_test();
run_testcase($test);
#$test->{result}= 'MTR_RES_PASSED';
# Send it back, now with results set
#$test->print_test();
$test->write_test($server, 'TESTRESULT');
}
elsif ($line eq 'BYE'){
mtr_report("Server said BYE");
exit(0);
}
else {
mtr_error("Could not understand server, '$line'");
}
}
stop_all_servers();
if ( $opt_gcov )
{
gcov_collect(); # collect coverage information
}
if ( $opt_gcov )
{
gcov_collect(); # collect coverage information
}
exit(1);
} }
...@@ -278,14 +564,11 @@ sub ignore_option { ...@@ -278,14 +564,11 @@ sub ignore_option {
} }
sub command_line_setup { sub command_line_setup {
my ($thread_num)= @_;
my $opt_comment; my $opt_comment;
my $opt_usage; my $opt_usage;
# This is needed for test log evaluation in "gen-build-status-page"
# in all cases where the calling tool does not log the commands
# directly before it executes them, like "make test-force-pl" in RPM builds.
mtr_report("Logging: $0 ", join(" ", @ARGV));
# Read the command line options # Read the command line options
# Note: Keep list, and the order, in sync with usage at end of this file # Note: Keep list, and the order, in sync with usage at end of this file
Getopt::Long::Configure("pass_through"); Getopt::Long::Configure("pass_through");
...@@ -325,7 +608,7 @@ sub command_line_setup { ...@@ -325,7 +608,7 @@ sub command_line_setup {
'skip-im' => \&ignore_option, 'skip-im' => \&ignore_option,
# Specify ports # Specify ports
'build-thread|mtr-build-thread=i' => \$opt_mtr_build_thread, 'build-thread|mtr-build-thread=i' => \$opt_build_thread,
# Test case authoring # Test case authoring
'record' => \$opt_record, 'record' => \$opt_record,
...@@ -383,10 +666,10 @@ sub command_line_setup { ...@@ -383,10 +666,10 @@ sub command_line_setup {
'report-features' => \$opt_report_features, 'report-features' => \$opt_report_features,
'comment=s' => \$opt_comment, 'comment=s' => \$opt_comment,
'fast' => \$opt_fast, 'fast' => \$opt_fast,
'reorder' => \&collect_option, 'reorder!' => \&collect_option,
'enable-disabled' => \&collect_option, 'enable-disabled' => \&collect_option,
'verbose+' => \$opt_verbose, 'verbose+' => \$opt_verbose,
'verbose-restart' => \$opt_verbose_restart, 'verbose-restart' => \&report_option,
'sleep=i' => \$opt_sleep, 'sleep=i' => \$opt_sleep,
'start-dirty' => \$opt_start_dirty, 'start-dirty' => \$opt_start_dirty,
'start' => \$opt_start, 'start' => \$opt_start,
...@@ -408,9 +691,21 @@ sub command_line_setup { ...@@ -408,9 +691,21 @@ sub command_line_setup {
usage("") if $opt_usage; usage("") if $opt_usage;
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Check mtr_build_thread and calculate baseport # Setup verbosity
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
set_mtr_build_thread_ports($opt_mtr_build_thread); if ($thread_num == 0){
# The server should by default have verbose on
report_option('verbose', $opt_verbose ? $opt_verbose : 0);
} else {
# Worker should by default have verbose off
report_option('verbose', $opt_verbose ? $opt_verbose : undef);
}
# --------------------------------------------------------------------------
# Check build_thread and calculate baseport
# Use auto build thread in all but first worker
# --------------------------------------------------------------------------
set_build_thread_ports($thread_num > 1 ? 'auto' : $opt_build_thread);
if ( -d "../sql" ) if ( -d "../sql" )
{ {
...@@ -537,8 +832,9 @@ sub command_line_setup { ...@@ -537,8 +832,9 @@ sub command_line_setup {
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Check if we should speed up tests by trying to run on tmpfs # Check if we should speed up tests by trying to run on tmpfs
# - Dont check in workers
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
if ( defined $opt_mem ) if ( defined $opt_mem and $thread_num == 0)
{ {
mtr_error("Can't use --mem and --vardir at the same time ") mtr_error("Can't use --mem and --vardir at the same time ")
if $opt_vardir; if $opt_vardir;
...@@ -554,7 +850,7 @@ sub command_line_setup { ...@@ -554,7 +850,7 @@ sub command_line_setup {
{ {
if ( -d $fs ) if ( -d $fs )
{ {
my $template= "var_${opt_mtr_build_thread}_XXXX"; my $template= "var_${opt_build_thread}_XXXX";
$opt_mem= tempdir( $template, DIR => $fs, CLEANUP => 0); $opt_mem= tempdir( $template, DIR => $fs, CLEANUP => 0);
last; last;
} }
...@@ -569,21 +865,11 @@ sub command_line_setup { ...@@ -569,21 +865,11 @@ sub command_line_setup {
{ {
$opt_vardir= $default_vardir; $opt_vardir= $default_vardir;
} }
elsif ( $mysql_version_id < 50000 and
$opt_vardir ne $default_vardir)
{
# Version 4.1 and --vardir was specified
# Only supported as a symlink from var/
# by setting up $opt_mem that symlink will be created
if ( ! IS_WINDOWS )
{
# Only platforms that have native symlinks can use the vardir trick
$opt_mem= $opt_vardir;
mtr_report("Using 4.1 vardir trick");
}
$opt_vardir= $default_vardir; # If more than one parallel run, use a subdir of the selected var
} if ($thread_num && $opt_parallel > 1) {
$opt_vardir.= "/".$thread_num;
}
$path_vardir_trace= $opt_vardir; $path_vardir_trace= $opt_vardir;
# Chop off any "c:", DBUG likes a unix path ex: c:/src/... => /src/... # Chop off any "c:", DBUG likes a unix path ex: c:/src/... => /src/...
...@@ -738,6 +1024,16 @@ sub command_line_setup { ...@@ -738,6 +1024,16 @@ sub command_line_setup {
$path_testlog= "$opt_vardir/log/mysqltest.log"; $path_testlog= "$opt_vardir/log/mysqltest.log";
$path_current_testlog= "$opt_vardir/log/current_test"; $path_current_testlog= "$opt_vardir/log/current_test";
mtr_report("Checking supported features...");
check_ndbcluster_support(\%mysqld_variables);
check_ssl_support(\%mysqld_variables);
check_debug_support(\%mysqld_variables);
executable_setup();
environment_setup();
} }
...@@ -756,19 +1052,20 @@ sub command_line_setup { ...@@ -756,19 +1052,20 @@ sub command_line_setup {
# http://www.ncftp.com/ncftpd/doc/misc/ephemeral_ports.html # http://www.ncftp.com/ncftpd/doc/misc/ephemeral_ports.html
# But a fairly safe range seems to be 5001 - 32767 # But a fairly safe range seems to be 5001 - 32767
# #
sub set_mtr_build_thread_ports($) { sub set_build_thread_ports($) {
my $mtr_build_thread= shift || 0; my $build_thread= shift || 0;
if ( lc($mtr_build_thread) eq 'auto' ) { if ( lc($build_thread) eq 'auto' ) {
print "Requesting build thread... "; mtr_report("Requesting build thread... ");
$mtr_build_thread= $build_thread=
mtr_require_unique_id_and_wait("/tmp/mysql-test-ports", 200, 299); mtr_require_unique_id_and_wait("/tmp/mysql-test-ports", 200, 299);
print "got ".$mtr_build_thread."\n"; mtr_report(" - got $build_thread");
} }
$ENV{MTR_BUILD_THREAD}= $mtr_build_thread; $ENV{MTR_BUILD_THREAD}= $build_thread;
$opt_build_thread= $build_thread;
# Calculate baseport # Calculate baseport
$opt_baseport= $mtr_build_thread * 10 + 10000; $opt_baseport= $build_thread * 10 + 10000;
if ( $opt_baseport < 5001 or $opt_baseport + 9 >= 32767 ) if ( $opt_baseport < 5001 or $opt_baseport + 9 >= 32767 )
{ {
mtr_error("MTR_BUILD_THREAD number results in a port", mtr_error("MTR_BUILD_THREAD number results in a port",
...@@ -776,7 +1073,7 @@ sub set_mtr_build_thread_ports($) { ...@@ -776,7 +1073,7 @@ sub set_mtr_build_thread_ports($) {
"($opt_baseport - $opt_baseport + 9)"); "($opt_baseport - $opt_baseport + 9)");
} }
mtr_report("Using MR_BUILD_THREAD $mtr_build_thread,", mtr_report("Using MTR_BUILD_THREAD $build_thread,",
"with reserved ports $opt_baseport..".($opt_baseport+9)); "with reserved ports $opt_baseport..".($opt_baseport+9));
} }
...@@ -1383,7 +1680,7 @@ sub remove_stale_vardir () { ...@@ -1383,7 +1680,7 @@ sub remove_stale_vardir () {
# Create var and the directories needed in var # Create var and the directories needed in var
# #
sub setup_vardir() { sub setup_vardir() {
mtr_report("Creating var directory..."); mtr_report("Creating var directory '$opt_vardir'...");
if ( $opt_vardir eq $default_vardir ) if ( $opt_vardir eq $default_vardir )
{ {
...@@ -1773,76 +2070,6 @@ sub ndbcluster_start ($) { ...@@ -1773,76 +2070,6 @@ sub ndbcluster_start ($) {
} }
#
# Run the collected tests
#
my $suite_timeout_proc;
sub run_tests {
my ($tests)= @_;
mtr_report();
mtr_print_thick_line();
mtr_print_header();
$suite_timeout_proc= My::SafeProcess->timer($opt_suite_timeout* 60);
foreach my $tinfo ( @$tests )
{
if (run_testcase_check_skip_test($tinfo))
{
next;
}
for my $repeat (1..$opt_repeat){
if (run_testcase($tinfo))
{
# Testcase failed, enter retry mode
my $retries= 1;
while ($retries < $opt_retry){
mtr_report("\nRetrying, attempt($retries/$opt_retry)...\n");
if (run_testcase($tinfo) <= 0)
{
# Testcase suceeded
my $test_has_failed= $tinfo->{failures} || 0;
if (!$test_has_failed){
last;
}
}
else
{
# Testcase failed
# Limit number of test failures
my $failures= $tinfo->{failures};
if ($opt_retry > 1 and $failures >= $opt_retry_failure){
mtr_report("Test has failed $failures times, no more retries!\n");
last;
}
}
$retries++;
}
}
}
}
# Kill the test suite timer
$suite_timeout_proc->kill();
mtr_print_line();
stop_all_servers();
if ( $opt_gcov )
{
gcov_collect(); # collect coverage information
}
mtr_report_stats($tests);
}
sub create_config_file_for_extern { sub create_config_file_for_extern {
my %opts= my %opts=
( (
...@@ -1990,7 +2217,7 @@ sub initialize_servers { ...@@ -1990,7 +2217,7 @@ sub initialize_servers {
remove_stale_vardir(); remove_stale_vardir();
setup_vardir(); setup_vardir();
mysql_install_db(); mysql_install_db(0);
} }
} }
check_running_as_root(); check_running_as_root();
...@@ -2044,6 +2271,7 @@ sub sql_to_bootstrap { ...@@ -2044,6 +2271,7 @@ sub sql_to_bootstrap {
sub mysql_install_db { sub mysql_install_db {
my ($thread_num)= @_;
my $data_dir= "$opt_vardir/install.db"; my $data_dir= "$opt_vardir/install.db";
mtr_report("Installing system database..."); mtr_report("Installing system database...");
...@@ -2080,6 +2308,8 @@ sub mysql_install_db { ...@@ -2080,6 +2308,8 @@ sub mysql_install_db {
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
$ENV{'MYSQLD_BOOTSTRAP_CMD'}= "$exe_mysqld_bootstrap " . join(" ", @$args); $ENV{'MYSQLD_BOOTSTRAP_CMD'}= "$exe_mysqld_bootstrap " . join(" ", @$args);
return if $thread_num > 0; # Only generate MYSQLD_BOOTSTRAP_CMD in workers
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Create the bootstrap.sql file # Create the bootstrap.sql file
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
...@@ -2387,7 +2617,7 @@ sub run_testcase ($) { ...@@ -2387,7 +2617,7 @@ sub run_testcase ($) {
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
if ( $opt_start or $opt_start_dirty ) if ( $opt_start or $opt_start_dirty )
{ {
$suite_timeout_proc->kill(); # MASV $suite_timeout_proc->kill();
mtr_report("\nStarted", started(all_servers())); mtr_report("\nStarted", started(all_servers()));
mtr_report("Waiting for server(s) to exit..."); mtr_report("Waiting for server(s) to exit...");
my $proc= My::SafeProcess->wait_any(); my $proc= My::SafeProcess->wait_any();
...@@ -2538,11 +2768,12 @@ sub run_testcase ($) { ...@@ -2538,11 +2768,12 @@ sub run_testcase ($) {
# ---------------------------------------------------- # ----------------------------------------------------
# Check if test suite timer expired # Check if test suite timer expired
# ---------------------------------------------------- # ----------------------------------------------------
if ( $proc eq $suite_timeout_proc ) # MASV
{ # if ( $proc eq $suite_timeout_proc )
mtr_report("Test suite timeout! Terminating..."); # {
exit(1); # mtr_report("Test suite timeout! Terminating...");
} # exit(1);
# }
mtr_error("Unhandled process $proc exited"); mtr_error("Unhandled process $proc exited");
} }
...@@ -2617,7 +2848,7 @@ sub check_warnings ($) { ...@@ -2617,7 +2848,7 @@ sub check_warnings ($) {
my $res= 0; my $res= 0;
# Clear previous warnings # Clear previous warnings
$tinfo->{warnings}= undef; delete($tinfo->{warnings});
# Parallell( mysqlds(), run_check_warning, check_warning_failed); # Parallell( mysqlds(), run_check_warning, check_warning_failed);
foreach my $mysqld ( mysqlds() ) foreach my $mysqld ( mysqlds() )
...@@ -2727,18 +2958,14 @@ sub report_failure_and_restart ($) { ...@@ -2727,18 +2958,14 @@ sub report_failure_and_restart ($) {
my $tinfo= shift; my $tinfo= shift;
mtr_report_test_failed($tinfo, $path_current_testlog); mtr_report_test_failed($tinfo, $path_current_testlog);
print "\n";
if ( $opt_force )
{
# Stop all servers that are known to be running
stop_all_servers();
after_test_failure($tinfo->{'name'}); # Stop all servers that are known to be running
mtr_report("Resuming tests...\n"); stop_all_servers();
return;
} # Collect and clean files
mtr_error("Test '$tinfo->{'name'}' failed.", after_test_failure($tinfo->{'name'});
"To continue, re-run with '--force'");
mtr_report("Resuming tests...\n");
} }
...@@ -3118,12 +3345,32 @@ sub started { return grep(defined $_, map($_->{proc}, @_)); } ...@@ -3118,12 +3345,32 @@ sub started { return grep(defined $_, map($_->{proc}, @_)); }
sub stopped { return grep(!defined $_, map($_->{proc}, @_)); } sub stopped { return grep(!defined $_, map($_->{proc}, @_)); }
sub envsubst {
my $string= shift;
if ( ! defined $ENV{$string} )
{
mtr_error(".opt file references '$string' which is not set");
}
return $ENV{$string};
}
sub get_extra_opts { sub get_extra_opts {
my ($mysqld, $tinfo)= @_; my ($mysqld, $tinfo)= @_;
return my $opts=
$mysqld->option("#!use-slave-opt") ? $mysqld->option("#!use-slave-opt") ?
$tinfo->{slave_opt} : $tinfo->{master_opt}; $tinfo->{slave_opt} : $tinfo->{master_opt};
# Expand environment variables
foreach my $opt ( @$opts )
{
$opt =~ s/\$\{(\w+)\}/envsubst($1)/ge;
$opt =~ s/\$(\w+)/envsubst($1)/ge;
}
return $opts;
} }
...@@ -3227,7 +3474,12 @@ sub start_servers($) { ...@@ -3227,7 +3474,12 @@ sub start_servers($) {
} }
# Copy datadir from installed system db # Copy datadir from installed system db
copytree("$opt_vardir/install.db", $datadir) for my $path ( "$opt_vardir", "$opt_vardir/..") {
my $install_db= "$path/install.db";
copytree($install_db, $datadir)
if -d $install_db;
}
mtr_error("Failed to copy system db to '$datadir'")
unless -d $datadir; unless -d $datadir;
# Write start of testcase to log file # Write start of testcase to log file
...@@ -3332,16 +3584,20 @@ sub run_check_testcase ($$$) { ...@@ -3332,16 +3584,20 @@ sub run_check_testcase ($$$) {
if ( $mode eq "after" and $res == 1 ) if ( $mode eq "after" and $res == 1 )
{ {
mtr_report("\nThe check of testcase '$tname' failed, this is the\n", # Test failed, grab the report mysqltest has created
"diff between before and after:\n"); my $report= mtr_grab_file($errfile);
# Test failed, display the report mysqltest has created $tinfo->{check}.=
mtr_printfile($errfile); "\nThe check of testcase '$tname' failed, this is the\n".
"diff between before and after:\n";
$tinfo->{check}.= $report;
} }
elsif ( $res ) elsif ( $res )
{ {
mtr_report("\nCould not execute 'check-testcase' $mode testcase '$tname':"); my $report= mtr_grab_file($errfile);
mtr_printfile($errfile); $tinfo->{'check'}.=
mtr_report(); "\nCould not execute 'check-testcase' $mode testcase '$tname':\n";
$tinfo->{check}.= $report;
} }
return $res; return $res;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment