summaryrefslogtreecommitdiffstats
path: root/mysql-test/mariadb-test-run.pl
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-test/mariadb-test-run.pl')
-rwxr-xr-xmysql-test/mariadb-test-run.pl6051
1 files changed, 6051 insertions, 0 deletions
diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl
new file mode 100755
index 00000000..1e868efa
--- /dev/null
+++ b/mysql-test/mariadb-test-run.pl
@@ -0,0 +1,6051 @@
+#!/usr/bin/env perl
+# -*- cperl -*-
+
+# Copyright (c) 2004, 2014, Oracle and/or its affiliates.
+# Copyright (c) 2009, 2022, MariaDB Corporation
+#
+# 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-1335 USA
+
+#
+##############################################################################
+#
+# mysql-test-run.pl
+#
+# Tool used for executing a suite of .test files
+#
+# See the "MySQL Test framework manual" for more information
+# https://mariadb.com/kb/en/library/mysqltest/
+#
+#
+##############################################################################
+
+use strict;
+use warnings;
+
+BEGIN {
+ # Check that mysql-test-run.pl is started from mysql-test/
+ unless ( -f "mariadb-test-run.pl" )
+ {
+ print "**** ERROR **** ",
+ "You must start mysql-test-run from the mysql-test/ directory\n";
+ exit(1);
+ }
+ # Check that lib exist
+ unless ( -d "lib/" )
+ {
+ print "**** ERROR **** ",
+ "Could not find the lib/ directory \n";
+ exit(1);
+ }
+
+ # Check backward compatibility support
+ # By setting the environment variable MTR_VERSION
+ # it's possible to use a previous version of
+ # mysql-test-run.pl
+ my $version= $ENV{MTR_VERSION} || 2;
+ if ( $version == 1 )
+ {
+ print "=======================================================\n";
+ print " WARNING: Using mariadb-test-run.pl version 1! \n";
+ print "=======================================================\n";
+ # Should use exec() here on *nix but this appears not to work on Windows
+ exit(system($^X, "lib/v1/mariadb-test-run.pl", @ARGV) >> 8);
+ }
+ elsif ( $version == 2 )
+ {
+ # This is the current version, just continue
+ ;
+ }
+ else
+ {
+ print "ERROR: Version $version of mariadb-test-run does not exist!\n";
+ exit(1);
+ }
+}
+
+use lib "lib";
+
+use Cwd ;
+use POSIX ":sys_wait_h";
+use Getopt::Long qw(:config bundling);
+use My::File::Path; # Patched version of File::Path
+use File::Basename;
+use File::Copy;
+use File::Find;
+use File::Temp qw/tempdir/;
+use File::Spec::Functions qw/splitdir rel2abs/;
+use My::Platform;
+use My::SafeProcess;
+use My::ConfigFactory;
+use My::Options;
+use My::Tee;
+use My::Find;
+use My::SysInfo;
+use My::CoreDump;
+use My::Debugger;
+use mtr_cases;
+use mtr_report;
+use mtr_match;
+use mtr_unique;
+use mtr_results;
+use IO::Socket::INET;
+use IO::Select;
+use Time::HiRes qw(gettimeofday);
+
+sub realpath($) { (IS_WINDOWS) ? $_[0] : Cwd::realpath($_[0]) }
+
+require "mtr_process.pl";
+require "mtr_io.pl";
+require "mtr_gprof.pl";
+require "mtr_misc.pl";
+
+my $opt_valgrind;
+my $valgrind_reports= 0;
+
+$SIG{INT}= sub { mtr_error("Got ^C signal"); };
+$SIG{HUP}= sub { mtr_error("Hangup detected on controlling terminal"); };
+
+our $mysql_version_id;
+my $mysql_version_extra;
+our $glob_mysql_test_dir;
+our $basedir;
+our $bindir;
+
+our $path_charsetsdir;
+our $path_client_bindir;
+our $path_client_libdir;
+our $path_language;
+
+our $path_current_testlog;
+our $path_testlog;
+
+our $default_vardir;
+our $opt_vardir; # Path to use for var/ dir
+our $plugindir;
+our $opt_xml_report; # XML output
+our $client_plugindir;
+my $path_vardir_trace; # unix formatted opt_vardir for trace files
+my $opt_tmpdir; # Path to use for tmp/ dir
+my $opt_tmpdir_pid;
+
+my $opt_start;
+my $opt_start_dirty;
+my $opt_start_exit;
+my $start_only;
+my $file_wsrep_provider;
+my $num_saved_cores= 0; # Number of core files saved in vardir/log/ so far.
+
+our @global_suppressions;
+
+END {
+ if ( defined $opt_tmpdir_pid and $opt_tmpdir_pid == $$ )
+ {
+ if (!$opt_start_exit)
+ {
+ # Remove the tempdir this process has created
+ mtr_verbose("Removing tmpdir $opt_tmpdir");
+ rmtree($opt_tmpdir);
+ }
+ else
+ {
+ mtr_warning("tmpdir $opt_tmpdir should be removed after the server has finished");
+ }
+ }
+}
+
+sub env_or_val($$) { defined $ENV{$_[0]} ? $ENV{$_[0]} : $_[1] }
+
+my $path_config_file; # The generated config file, var/my.cnf
+
+# Visual Studio produces executables in different sub-directories based on the
+# configuration used to build them. To make life easier, an environment
+# variable or command-line option may be specified to control which set of
+# executables will be used by the test suite.
+our $multiconfig = $ENV{'MTR_VS_CONFIG'};
+
+my @DEFAULT_SUITES= qw(
+ main-
+ archive-
+ atomic-
+ binlog-
+ binlog_encryption-
+ client-
+ csv-
+ compat/oracle-
+ compat/mssql-
+ compat/maxdb-
+ encryption-
+ federated-
+ funcs_1-
+ funcs_2-
+ gcol-
+ handler-
+ heap-
+ innodb-
+ innodb_fts-
+ innodb_gis-
+ innodb_i_s-
+ innodb_zip-
+ json-
+ maria-
+ mariabackup-
+ multi_source-
+ optimizer_unfixed_bugs-
+ parts-
+ perfschema-
+ plugins-
+ roles-
+ rpl-
+ stress-
+ sys_vars-
+ sql_sequence-
+ unit-
+ vcol-
+ versioning-
+ period-
+ sysschema-
+ );
+my $opt_suites;
+
+our $opt_verbose= 0; # Verbose output, enable with --verbose
+our $exe_patch;
+our $exe_mysql;
+our $exe_mysql_plugin;
+our $exe_mysqladmin;
+our $exe_mysqltest;
+our $exe_libtool;
+our $exe_mysql_embedded;
+our $exe_mariadb_conv;
+
+our $opt_big_test= 0;
+our $opt_staging_run= 0;
+
+our @opt_combinations;
+
+our @opt_extra_mysqld_opt;
+our @opt_mysqld_envs;
+
+my $opt_stress;
+my $opt_tail_lines= 20;
+
+my $opt_dry_run;
+
+my $opt_compress;
+my $opt_ssl;
+my $opt_skip_ssl;
+my @opt_skip_test_list;
+our $opt_ssl_supported;
+our $opt_ps_protocol;
+my $opt_sp_protocol;
+my $opt_cursor_protocol;
+my $opt_view_protocol;
+my $opt_non_blocking_api;
+
+our $opt_debug;
+my $debug_d= "d,*";
+my $opt_debug_common;
+our $opt_debug_server;
+our @opt_cases; # The test cases names in argv
+our $opt_embedded_server;
+
+# Options used when connecting to an already running server
+my %opts_extern;
+sub using_extern { return (keys %opts_extern > 0);};
+
+our $opt_fast= 0;
+our $opt_force= 0;
+our $opt_skip_not_found= 0;
+our $opt_mem= $ENV{'MTR_MEM'};
+our $opt_clean_vardir= $ENV{'MTR_CLEAN_VARDIR'};
+
+our $opt_gcov;
+our $opt_gprof;
+our %gprof_dirs;
+
+my $config; # The currently running config
+my $current_config_name; # The currently running config file template
+
+our @opt_experimentals;
+our $experimental_test_cases= [];
+
+our $baseport;
+# $opt_build_thread may later be set from $opt_port_base
+my $opt_build_thread= $ENV{'MTR_BUILD_THREAD'} || "auto";
+my $opt_port_base= $ENV{'MTR_PORT_BASE'} || "auto";
+my $build_thread= 0;
+
+my $opt_record;
+
+our $opt_resfile= $ENV{'MTR_RESULT_FILE'} || 0;
+
+my $opt_skip_core;
+
+our $opt_check_testcases= 1;
+my $opt_mark_progress;
+my $opt_max_connections;
+our $opt_report_times= 0;
+
+my $opt_sleep;
+
+our $opt_retry= 1;
+our $opt_retry_failure= env_or_val(MTR_RETRY_FAILURE => 2);
+our $opt_testcase_timeout= $ENV{MTR_TESTCASE_TIMEOUT} || 15; # minutes
+our $opt_suite_timeout = $ENV{MTR_SUITE_TIMEOUT} || 360; # minutes
+our $opt_shutdown_timeout= $ENV{MTR_SHUTDOWN_TIMEOUT} || 10; # seconds
+our $opt_start_timeout = $ENV{MTR_START_TIMEOUT} || 180; # seconds
+
+sub suite_timeout { return $opt_suite_timeout * 60; };
+
+my $opt_wait_all;
+my $opt_user_args;
+my $opt_repeat= 1;
+my $opt_reorder= 1;
+my $opt_force_restart= 0;
+
+our $opt_user = "root";
+
+my %mysqld_logs;
+my $opt_debug_sync_timeout= 300; # Default timeout for WAIT_FOR actions.
+my $warn_seconds = 60;
+
+my $rebootstrap_re= '--innodb[-_](?:page[-_]size|checksum[-_]algorithm|undo[-_]tablespaces|log[-_]group[-_]home[-_]dir|data[-_]home[-_]dir)|data[-_]file[-_]path|force_rebootstrap';
+
+sub testcase_timeout ($) { return $opt_testcase_timeout * 60; }
+sub check_timeout ($) { return testcase_timeout($_[0]); }
+
+our $opt_warnings= 1;
+
+our %mysqld_variables;
+our @optional_plugins;
+
+my $source_dist= -d "../sql";
+
+my $opt_max_save_core= env_or_val(MTR_MAX_SAVE_CORE => 5);
+my $opt_max_save_datadir= env_or_val(MTR_MAX_SAVE_DATADIR => 20);
+my $opt_max_test_fail= env_or_val(MTR_MAX_TEST_FAIL => 10);
+my $opt_core_on_failure= 0;
+
+my $opt_parallel= $ENV{MTR_PARALLEL} || 1;
+my $opt_port_group_size = $ENV{MTR_PORT_GROUP_SIZE} || 20;
+
+# lock file to stop tests
+my $opt_stop_file= $ENV{MTR_STOP_FILE};
+# print messages when test suite is stopped (for buildbot)
+my $opt_stop_keep_alive= $ENV{MTR_STOP_KEEP_ALIVE};
+
+select(STDOUT);
+$| = 1; # Automatically flush STDOUT
+
+main();
+
+sub main {
+ $ENV{MTR_PERL}= mixed_path($^X);
+
+ # Default, verbosity on
+ report_option('verbose', 0);
+
+ # 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));
+
+ command_line_setup();
+
+ # --help will not reach here, so now it's safe to assume we have binaries
+ My::SafeProcess::find_bin();
+
+ print "vardir: $opt_vardir\n";
+ initialize_servers();
+ init_timers();
+
+ unless (IS_WINDOWS) {
+ binmode(STDOUT,":via(My::Tee)") or die "binmode(STDOUT, :via(My::Tee)):$!";
+ binmode(STDERR,":via(My::Tee)") or die "binmode(STDERR, :via(My::Tee)):$!";
+ }
+
+ mtr_report("Checking supported features...");
+
+ executable_setup();
+
+ # --debug[-common] implies we run debug server
+ $opt_debug_server= 1 if $opt_debug || $opt_debug_common;
+
+ if (using_extern())
+ {
+ # Connect to the running mysqld and find out what it supports
+ collect_mysqld_features_from_running_server();
+ }
+ else
+ {
+ # Run the mysqld to find out what features are available
+ collect_mysqld_features();
+ }
+ check_ssl_support();
+ check_debug_support();
+ environment_setup();
+
+ if (!$opt_suites) {
+ $opt_suites= join ',', collect_default_suites(@DEFAULT_SUITES);
+ }
+ mtr_report("Using suites: $opt_suites") unless @opt_cases;
+
+ mtr_report("Collecting tests...");
+ my $tests= collect_test_cases($opt_reorder, $opt_suites, \@opt_cases, \@opt_skip_test_list);
+ if (@$tests == 0) {
+ mtr_report("No tests to run...");
+ exit 0;
+ }
+
+ mark_time_used('collect');
+
+ if (!using_extern())
+ {
+ mysql_install_db(default_mysqld(), "$opt_vardir/install.db");
+ make_readonly("$opt_vardir/install.db");
+ }
+ if ($opt_dry_run)
+ {
+ for (@$tests) {
+ print $_->fullname(), "\n";
+ }
+ exit 0;
+ }
+
+ if ($opt_gcov) {
+ system './dgcov.pl --purge';
+ }
+
+ #######################################################################
+ my $num_tests= $mtr_report::tests_total= @$tests;
+ if ( $opt_parallel eq "auto" ) {
+ # Try to find a suitable value for number of workers
+ if (IS_WINDOWS)
+ {
+ $opt_parallel= $ENV{NUMBER_OF_PROCESSORS} || 1;
+ }
+ else
+ {
+ my $sys_info= My::SysInfo->new();
+ $opt_parallel= $sys_info->num_cpus()+int($sys_info->min_bogomips()/500)-4;
+ }
+ my $max_par= $ENV{MTR_MAX_PARALLEL} || 8;
+ $opt_parallel= $max_par if ($opt_parallel > $max_par);
+ $opt_parallel= $num_tests if ($opt_parallel > $num_tests);
+ $opt_parallel= 1 if ($opt_parallel < 1);
+ mtr_report("Using parallel: $opt_parallel");
+ }
+ $ENV{MTR_PARALLEL} = $opt_parallel;
+
+ if ($opt_parallel > 1 && ($opt_start_exit || $opt_stress)) {
+ mtr_warning("Parallel cannot be used with --start-and-exit or --stress\n" .
+ "Setting parallel to 1");
+ $opt_parallel= 1;
+ }
+
+ # Create server socket on any free port
+ my $server = new IO::Socket::INET
+ (
+ LocalAddr => 'localhost',
+ Proto => 'tcp',
+ Listen => $opt_parallel,
+ );
+ mtr_error("Could not create testcase server port: $!") unless $server;
+ my $server_port = $server->sockport();
+
+ if ($opt_resfile) {
+ resfile_init("$opt_vardir/mtr-results.txt");
+ print_global_resfile();
+ }
+
+ # 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
+ $tests= {}; # Don't need the tests list in child
+
+ # Use subdir of var and tmp unless only one worker
+ if ($opt_parallel > 1) {
+ set_vardir("$opt_vardir/$child_num");
+ $opt_tmpdir= "$opt_tmpdir/$child_num";
+ }
+
+ init_timers();
+ run_worker($server_port, $child_num);
+ exit(1);
+ }
+
+ $children{$child_pid}= 1;
+ }
+ #######################################################################
+
+ mtr_report();
+ mtr_print_thick_line();
+ mtr_print_header($opt_parallel > 1);
+
+ mark_time_used('init');
+
+ my ($prefix, $fail, $completed, $extra_warnings)=
+ Manager::run($server, $tests, \%children);
+
+ exit(0) if $opt_start_exit;
+
+ # Send Ctrl-C to any children still running
+ kill("INT", keys(%children));
+
+ if (!IS_WINDOWS) {
+ # Wait for children to exit
+ foreach my $pid (keys %children)
+ {
+ my $ret_pid= waitpid($pid, 0);
+ if ($ret_pid == -1) {
+ # Child was automatically reaped. Probably not possible
+ # unless you $SIG{CHLD}= 'IGNORE'
+ mtr_warning("Child ${pid} was automatically reaped (this should never happen)");
+ } elsif ($ret_pid != $pid) {
+ confess("Unexpected PID ${ret_pid} instead of expected ${pid}");
+ }
+ my $exit_status= ($? >> 8);
+ mtr_verbose2("Child ${pid} exited with status ${exit_status}");
+ delete $children{$ret_pid};
+ }
+ }
+
+ if ( not @$completed ) {
+ my $test_name= mtr_grab_file($path_testlog);
+ $test_name =~ s/^CURRENT_TEST:\s//;
+ chomp($test_name);
+ my $tinfo = My::Test->new(name => $test_name);
+ $tinfo->{result}= 'MTR_RES_FAILED';
+ $tinfo->{comment}=' ';
+ mtr_report_test($tinfo);
+ mtr_error("Test suite aborted");
+ }
+
+ if ( @$completed != $num_tests){
+
+ # Not all tests completed, failure
+ mtr_report();
+ mtr_report("Only ", int(@$completed), " of $num_tests completed.");
+ }
+
+ if ($opt_valgrind) {
+ # Create minimalistic "test" for the reporting
+ my $tinfo = My::Test->new
+ (
+ suite => { name => 'valgrind', },
+ name => 'valgrind_report',
+ );
+ # Set dummy worker id to align report with normal tests
+ $tinfo->{worker} = 0 if $opt_parallel > 1;
+ if ($valgrind_reports) {
+ $tinfo->{result}= 'MTR_RES_FAILED';
+ $tinfo->{comment}= "Valgrind reported failures at shutdown, see above";
+ $tinfo->{failures}= 1;
+ } else {
+ $tinfo->{result}= 'MTR_RES_PASSED';
+ }
+ mtr_report_test($tinfo);
+ push @$completed, $tinfo;
+ ++$num_tests
+ }
+
+ mtr_print_line();
+
+ print_total_times($opt_parallel) if $opt_report_times;
+ mtr_report_stats($prefix, $fail, $completed, $extra_warnings);
+
+ if ($opt_gcov) {
+ mtr_report("Running dgcov");
+ system "./dgcov.pl --generate > $opt_vardir/last_changes.dgcov";
+ }
+
+ if ( @$completed != $num_tests)
+ {
+ mtr_error("Not all tests completed (only ". scalar(@$completed) .
+ " of $num_tests)");
+ }
+
+ remove_vardir_subs() if $opt_clean_vardir;
+
+ exit(0);
+}
+
+
+package Manager;
+use POSIX ":sys_wait_h";
+use File::Basename;
+use File::Find;
+use IO::Socket::INET;
+use IO::Select;
+use mtr_report;
+use My::Platform;
+
+my $num_saved_datadir; # Number of datadirs saved in vardir/log/ so far.
+my $num_failed_test; # Number of tests failed so far
+my $test_failure; # Set true if test suite failed
+my $extra_warnings; # Warnings found during server shutdowns
+
+my $completed;
+my %running;
+my $result;
+my $exe_mysqld; # Used as hint to CoreDump
+my %names;
+
+sub parse_protocol($$) {
+ my $sock= shift;
+ my $line= shift;
+
+ if ($line eq 'TESTRESULT'){
+ mtr_verbose2("Got TESTRESULT from ". $names{$sock});
+ $result= My::Test::read_test($sock);
+
+ # Report test status
+ mtr_report_test($result);
+
+ if ( $result->is_failed() ) {
+
+ # Save the workers "savedir" in var/log
+ my $worker_savedir= $result->{savedir};
+ my $worker_savename= basename($worker_savedir);
+ my $savedir= "$opt_vardir/log/$worker_savename";
+
+ # Move any core files from e.g. mysqltest
+ foreach my $coref (glob("core*"), glob("*.dmp"))
+ {
+ mtr_report(" - found '$coref', moving it to '$worker_savedir'");
+ ::move($coref, $worker_savedir);
+ }
+
+ find(
+ {
+ no_chdir => 1,
+ wanted => sub
+ {
+ My::CoreDump::core_wanted(\$num_saved_cores,
+ $opt_max_save_core,
+ @opt_cases == 0,
+ $exe_mysqld, $opt_parallel);
+ }
+ },
+ $worker_savedir);
+
+ if ($num_saved_datadir >= $opt_max_save_datadir)
+ {
+ mtr_report(" - skipping '$worker_savedir/'");
+ main::rmtree($worker_savedir);
+ }
+ else
+ {
+ mtr_report(" - saving '$worker_savedir/' to '$savedir/'");
+ rename($worker_savedir, $savedir);
+ $num_saved_datadir++;
+ }
+ main::resfile_print_test();
+ $num_failed_test++ unless ($result->{retries} ||
+ $result->{exp_fail});
+
+ $test_failure= 1;
+ if ( !$opt_force ) {
+ # Test has failed, force is off
+ push(@$completed, $result);
+ if ($result->{'dont_kill_server'})
+ {
+ mtr_verbose2("${line}: saying BYE to ". $names{$sock});
+ print $sock "BYE\n";
+ return 2;
+ }
+ return ["Failure", 1, $completed, $extra_warnings];
+ }
+ elsif ($opt_max_test_fail > 0 and
+ $num_failed_test >= $opt_max_test_fail) {
+ push(@$completed, $result);
+ mtr_report("Too many tests($num_failed_test) failed!",
+ "Terminating...");
+ return ["Too many failed", 1, $completed, $extra_warnings];
+ }
+ }
+
+ main::resfile_print_test();
+ # Retry test run after test failure
+ my $retries= $result->{retries} || 2;
+ 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 $tname= $result->{name};
+ my $failures= $result->{failures};
+ if ($opt_retry > 1 and $failures >= $opt_retry_failure){
+ mtr_report("\nTest $tname has failed $failures times,",
+ "no more retries!\n");
+ }
+ else {
+ mtr_report("\nRetrying test $tname, ".
+ "attempt($retries/$opt_retry)...\n");
+ #saving the log file as filename.failed in case of retry
+ if ( $result->is_failed() ) {
+ my $worker_logdir= $result->{savedir};
+ my $log_file_name=dirname($worker_logdir)."/".$result->{shortname}.".log";
+
+ if (-e $log_file_name) {
+ $result->{'logfile-failed'} = ::mtr_lastlinesfromfile($log_file_name, 20);
+ } else {
+ $result->{'logfile-failed'} = "";
+ }
+
+ rename $log_file_name, $log_file_name.".failed";
+ }
+ {
+ local @$result{'retries', 'result'};
+ delete $result->{result};
+ $result->{retries}= $retries+1;
+ $result->write_test($sock, 'TESTCASE');
+ }
+ push(@$completed, $result);
+ return 2;
+ }
+ }
+
+ # Repeat test $opt_repeat number of times
+ my $repeat= $result->{repeat} || 1;
+ if ($repeat < $opt_repeat)
+ {
+ $result->{retries}= 0;
+ $result->{rep_failures}++ if $result->{failures};
+ $result->{failures}= 0;
+ delete($result->{result});
+ $result->{repeat}= $repeat+1;
+ $result->write_test($sock, 'TESTCASE');
+ return 2;
+ }
+
+ # Remove from list of running
+ mtr_error("'", $result->{name},"' is not known to be running")
+ unless delete $running{$result->key()};
+
+ # Save result in completed list
+ push(@$completed, $result);
+
+ } # if ($line eq 'TESTRESULT')
+ elsif ($line=~ /^START (.*)$/){
+ # Send first test
+ $names{$sock}= $1;
+ }
+ elsif ($line eq 'WARNINGS'){
+ my $fake_test= My::Test::read_test($sock);
+ my $test_list= join (" ", @{$fake_test->{testnames}});
+ push @$extra_warnings, $test_list;
+ my $report= $fake_test->{'warnings'};
+ mtr_report("***Warnings generated in error logs during shutdown ".
+ "after running tests: $test_list\n\n$report");
+ $test_failure= 1;
+ if ( !$opt_force ) {
+ # Test failure due to warnings, force is off
+ mtr_verbose2("Socket loop exiting 3");
+ return ["Warnings in log", 1, $completed, $extra_warnings];
+ }
+ return 1;
+ }
+ elsif ($line =~ /^SPENT/) {
+ main::add_total_times($line);
+ }
+ elsif ($line eq 'VALGREP' && $opt_valgrind) {
+ $valgrind_reports= 1;
+ }
+ else {
+ mtr_error("Unknown response: '$line' from client");
+ }
+ return 0;
+}
+
+sub run ($$$) {
+ my ($server, $tests, $children) = @_;
+ my $suite_timeout= main::start_timer(main::suite_timeout());
+ $exe_mysqld= main::find_mysqld($bindir) || ""; # Used as hint to CoreDump
+ $num_saved_datadir= 0; # Number of datadirs saved in vardir/log/ so far.
+ $num_failed_test= 0; # Number of tests failed so far
+ $test_failure= 0; # Set true if test suite failed
+ $extra_warnings= []; # Warnings found during server shutdowns
+ $completed= [];
+
+ my $s= IO::Select->new();
+ my $childs= 0;
+
+ $s->add($server);
+ while (1) {
+ if ($opt_stop_file)
+ {
+ if (mtr_wait_lock_file($opt_stop_file, $opt_stop_keep_alive))
+ {
+ # We were waiting so restart timer process
+ my $suite_timeout= main::start_timer(main::suite_timeout());
+ }
+ }
+
+ main::mark_time_used('admin');
+ my @ready = $s->can_read(1); # Wake up once every second
+ if (@ready > 0) {
+ mtr_verbose2("Got ". (0 + @ready). " connection(s)");
+ }
+ main::mark_time_idle();
+ my $i= 0;
+ sock_loop: foreach my $sock (@ready) {
+ ++$i;
+ if ($sock == $server) {
+ # New client connected
+ ++$childs;
+ my $child= $sock->accept();
+ mtr_verbose2("Connection ${i}: Worker connected (got ${childs} childs)");
+ $s->add($child);
+ print $child "HELLO\n";
+ }
+ else {
+ my $j= 0;
+ $sock->blocking(0);
+ while (my $line= <$sock>) {
+ ++$j;
+ chomp($line);
+ mtr_verbose2("Connection ${i}.${j}". (exists $names{$sock} ? " from $names{$sock}" : "") .": $line");
+
+ $sock->blocking(1);
+ my $res= parse_protocol($sock, $line);
+ $sock->blocking(0);
+ if (ref $res eq 'ARRAY') {
+ return @$res;
+ } elsif ($res == 1) {
+ next;
+ } elsif ($res == 2) {
+ next sock_loop;
+ }
+ if (IS_WINDOWS and !IS_CYGWIN) {
+ # Strawberry and ActiveState don't support blocking(0), the next iteration will be blocked!
+ # If there is next response now in the buffer and it is TESTRESULT we are affected by MDEV-30836 and the manager will hang.
+ last;
+ }
+ }
+ $sock->blocking(1);
+ if ($j == 0) {
+ # Client disconnected
+ --$childs;
+ mtr_verbose2((exists $names{$sock} ? $names{$sock} : "Worker"). " closed socket (left ${childs} childs)");
+ $s->remove($sock);
+ $sock->close;
+ next;
+ }
+
+ # Find next test to schedule
+ # - Try to use same configuration as worker used last time
+
+ my $next;
+ my $second_best;
+ for(my $i= 0; $i <= @$tests; $i++)
+ {
+ my $t= $tests->[$i];
+
+ last unless defined $t;
+
+ if (main::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));
+
+ # Since the test at pos $i was taken away, next
+ # test will also be at $i -> redo
+ redo;
+ }
+
+ # From secondary choices, we prefer to pick a 'long-running' test if
+ # possible; this helps avoid getting stuck with a few of those at the
+ # end of high --parallel runs, with most workers being idle.
+ if (!defined $second_best ||
+ ($t->{'long_test'} && !($tests->[$second_best]{'long_test'}))){
+ #mtr_report("Setting second_best to $i");
+ $second_best= $i;
+ }
+
+ # Smart allocation of next test within this thread.
+
+ if ($opt_reorder and $opt_parallel > 1 and defined $result)
+ {
+ my $wid= $result->{worker};
+ # Reserved for other thread, try next
+ next if (defined $t->{reserved} and $t->{reserved} != $wid);
+ if (! defined $t->{reserved})
+ {
+ # Force-restart not relevant when comparing *next* test
+ $t->{criteria} =~ s/force-restart$/no-restart/;
+ my $criteria= $t->{criteria};
+ # Reserve similar tests for this worker, but not too many
+ my $maxres= (@$tests - $i) / $opt_parallel + 1;
+ for (my $j= $i+1; $j <= $i + $maxres; $j++)
+ {
+ my $tt= $tests->[$j];
+ last unless defined $tt;
+ last if $tt->{criteria} ne $criteria;
+ $tt->{reserved}= $wid;
+ }
+ }
+ }
+
+ # At this point we have found next suitable test
+ $next= splice(@$tests, $i, 1);
+ last;
+ } # for(my $i= 0; $i <= @$tests; $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($second_best)")
+ if $second_best > $#$tests;
+ $next= splice(@$tests, $second_best, 1);
+ delete $next->{reserved};
+ }
+
+ if ($next) {
+ # We don't need this any more
+ delete $next->{criteria};
+ $next->write_test($sock, 'TESTCASE');
+ $running{$next->key()}= $next;
+ }
+ else {
+ # No more test, tell child to exit
+ mtr_verbose2("Saying BYE to ". $names{$sock});
+ print $sock "BYE\n";
+ } # else (!$next)
+ } # else ($sock != $server)
+ } # foreach my $sock (@ready)
+
+ if (!IS_WINDOWS) {
+ foreach my $pid (keys %$children)
+ {
+ my $res= waitpid($pid, WNOHANG);
+ if ($res == $pid || $res == -1) {
+ if ($res == -1) {
+ # Child was automatically reaped. Probably not possible
+ # unless you $SIG{CHLD}= 'IGNORE'
+ mtr_warning("Child ${pid} was automatically reaped (this should never happen)");
+ }
+ my $exit_status= ($? >> 8);
+ mtr_verbose2("Child ${pid} exited with status ${exit_status}");
+ delete $children->{$pid};
+ if (!%$children && $childs) {
+ mtr_verbose2("${childs} children didn't close socket before dying!");
+ $childs= 0;
+ }
+ } elsif ($res != 0) {
+ confess("Unexpected result ${res} on waitpid(${pid}, WNOHANG)");
+ }
+ }
+ }
+
+ if ($childs == 0){
+ return ("Completed", $test_failure, $completed, $extra_warnings);
+ }
+
+ # ----------------------------------------------------
+ # Check if test suite timer expired
+ # ----------------------------------------------------
+ if ( main::has_expired($suite_timeout) )
+ {
+ mtr_report("Test suite timeout! Terminating...");
+ return ("Timeout", 1, $completed, $extra_warnings);
+ }
+ }
+}
+
+1;
+
+package main;
+
+sub run_worker ($) {
+ my ($server_port, $thread_num)= @_;
+
+ $SIG{INT}= sub { exit(1); };
+ $SIG{HUP}= sub { exit(1); };
+
+ # 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;
+
+ # --------------------------------------------------------------------------
+ # Set worker name
+ # --------------------------------------------------------------------------
+ report_option('name',"worker[". sprintf("%02d", $thread_num). "]");
+ my $proc_title= basename($0). " ${mtr_report::name} :". $server->sockport(). " -> :${server_port}";
+ $0= $proc_title;
+ mtr_verbose2("Running at PID $$");
+
+ # --------------------------------------------------------------------------
+ # Set different ports per thread
+ # --------------------------------------------------------------------------
+ set_build_thread_ports($thread_num);
+
+ # --------------------------------------------------------------------------
+ # Turn off verbosity in workers, unless explicitly specified
+ # --------------------------------------------------------------------------
+ report_option('verbose', undef) if ($opt_verbose == 0);
+
+ environment_setup();
+
+ # Read hello from server which it will send when shared
+ # resources have been setup
+ my $hello= <$server>;
+
+ setup_vardir();
+ check_running_as_root();
+
+ if ( using_extern() ) {
+ create_config_file_for_extern(%opts_extern);
+ }
+
+ # Ask server for first test
+ print $server "START ${mtr_report::name}\n";
+
+ mark_time_used('init');
+
+ while (my $line= <$server>){
+ chomp($line);
+ if ($line eq 'TESTCASE'){
+ my $test= My::Test::read_test($server);
+ $0= $proc_title. " ". $test->{name};
+
+ # Clear comment and logfile, to avoid
+ # reusing them from previous test
+ delete($test->{'comment'});
+ delete($test->{'logfile'});
+
+ # A sanity check. Should this happen often we need to look at it.
+ if (defined $test->{reserved} && $test->{reserved} != $thread_num) {
+ my $tres= $test->{reserved};
+ mtr_warning("Test reserved for w$tres picked up by w$thread_num");
+ }
+ $test->{worker} = $thread_num if $opt_parallel > 1;
+
+ run_testcase($test, $server);
+ #$test->{result}= 'MTR_RES_PASSED';
+ # Send it back, now with results set
+ mtr_verbose2('Writing TESTRESULT');
+ $test->write_test($server, 'TESTRESULT');
+ mark_time_used('restart');
+ }
+ elsif ($line eq 'BYE'){
+ mtr_verbose2("Manager said BYE");
+ # We need to gracefully shut down the servers to see any
+ # Valgrind memory leak errors etc. since last server restart.
+ if ($opt_warnings) {
+ stop_servers(reverse all_servers());
+ if(check_warnings_post_shutdown($server)) {
+ # Warnings appeared in log file(s) during final server shutdown.
+ exit(1);
+ }
+ }
+ else {
+ stop_all_servers($opt_shutdown_timeout);
+ }
+ mark_time_used('restart');
+ my $valgrind_reports= 0;
+ if ($opt_valgrind) {
+ $valgrind_reports= valgrind_exit_reports();
+ print $server "VALGREP\n" if $valgrind_reports;
+ }
+ if ( $opt_gprof ) {
+ gprof_collect (find_mysqld($bindir), keys %gprof_dirs);
+ }
+ mark_time_used('admin');
+ print_times_used($server, $thread_num);
+ exit($valgrind_reports);
+ }
+ else {
+ mtr_error("Could not understand server, '$line'");
+ }
+ }
+
+ stop_all_servers();
+
+ exit(1);
+}
+
+
+# Setup any paths that are $opt_vardir related
+sub set_vardir {
+ ($opt_vardir)= @_;
+
+ $path_vardir_trace= $opt_vardir;
+ # Chop off any "c:", DBUG likes a unix path ex: c:/src/... => /src/...
+ $path_vardir_trace=~ s/^\w://;
+
+ # Location of my.cnf that all clients use
+ $path_config_file= "$opt_vardir/my.cnf";
+
+ $path_testlog= "$opt_vardir/log/mysqltest.log";
+ $path_current_testlog= "$opt_vardir/log/current_test";
+
+}
+
+
+sub print_global_resfile {
+ resfile_global("start_time", isotime $^T);
+ resfile_global("user_id", $<);
+ resfile_global("embedded-server", $opt_embedded_server ? 1 : 0);
+ resfile_global("ps-protocol", $opt_ps_protocol ? 1 : 0);
+ resfile_global("sp-protocol", $opt_sp_protocol ? 1 : 0);
+ resfile_global("view-protocol", $opt_view_protocol ? 1 : 0);
+ resfile_global("cursor-protocol", $opt_cursor_protocol ? 1 : 0);
+ resfile_global("ssl", $opt_ssl ? 1 : 0);
+ resfile_global("compress", $opt_compress ? 1 : 0);
+ resfile_global("parallel", $opt_parallel);
+ resfile_global("check-testcases", $opt_check_testcases ? 1 : 0);
+ resfile_global("mariadbd", \@opt_extra_mysqld_opt);
+ resfile_global("debug", $opt_debug ? 1 : 0);
+ resfile_global("gcov", $opt_gcov ? 1 : 0);
+ resfile_global("gprof", $opt_gprof ? 1 : 0);
+ resfile_global("mem", $opt_mem);
+ resfile_global("tmpdir", $opt_tmpdir);
+ resfile_global("vardir", $opt_vardir);
+ resfile_global("fast", $opt_fast ? 1 : 0);
+ resfile_global("force-restart", $opt_force_restart ? 1 : 0);
+ resfile_global("reorder", $opt_reorder ? 1 : 0);
+ resfile_global("sleep", $opt_sleep);
+ resfile_global("repeat", $opt_repeat);
+ resfile_global("user", $opt_user);
+ resfile_global("testcase-timeout", $opt_testcase_timeout);
+ resfile_global("suite-timeout", $opt_suite_timeout);
+ resfile_global("shutdown-timeout", $opt_shutdown_timeout ? 1 : 0);
+ resfile_global("warnings", $opt_warnings ? 1 : 0);
+ resfile_global("max-connections", $opt_max_connections);
+ resfile_global("product", "MariaDB");
+ resfile_global("xml-report", $opt_xml_report);
+ # Somewhat hacky code to convert numeric version back to dot notation
+ my $v1= int($mysql_version_id / 10000);
+ my $v2= int(($mysql_version_id % 10000)/100);
+ my $v3= $mysql_version_id % 100;
+ resfile_global("version", "$v1.$v2.$v3");
+}
+
+
+
+sub command_line_setup {
+ my $opt_comment;
+ my $opt_usage;
+ my $opt_list_options;
+
+ # Read the command line options
+ # Note: Keep list in sync with usage at end of this file
+ Getopt::Long::Configure("pass_through");
+ my %options=(
+ # Control what engine/variation to run
+ 'embedded-server' => \$opt_embedded_server,
+ 'ps-protocol' => \$opt_ps_protocol,
+ 'sp-protocol' => \$opt_sp_protocol,
+ 'view-protocol' => \$opt_view_protocol,
+ 'cursor-protocol' => \$opt_cursor_protocol,
+ 'non-blocking-api' => \$opt_non_blocking_api,
+ 'ssl|with-openssl' => \$opt_ssl,
+ 'skip-ssl' => \$opt_skip_ssl,
+ 'compress' => \$opt_compress,
+ 'vs-config=s' => \$multiconfig,
+
+ # Max number of parallel threads to use
+ 'parallel=s' => \$opt_parallel,
+
+ # Config file to use as template for all tests
+ 'defaults-file=s' => \&collect_option,
+ # Extra config file to append to all generated configs
+ 'defaults-extra-file=s' => \&collect_option,
+
+ # Control what test suites or cases to run
+ 'force+' => \$opt_force,
+ 'skip-not-found' => \$opt_skip_not_found,
+ 'suite|suites=s' => \$opt_suites,
+ 'skip-rpl' => \&collect_option,
+ 'skip-test=s' => \&collect_option,
+ 'do-test=s' => \&collect_option,
+ 'start-from=s' => \&collect_option,
+ 'big-test+' => \$opt_big_test,
+ 'combination=s' => \@opt_combinations,
+ 'experimental=s' => \@opt_experimentals,
+ 'staging-run' => \$opt_staging_run,
+
+ # Specify ports
+ 'build-thread|mtr-build-thread=i' => \$opt_build_thread,
+ 'port-base|mtr-port-base=i' => \$opt_port_base,
+ 'port-group-size=s' => \$opt_port_group_size,
+
+ # Test case authoring
+ 'record' => \$opt_record,
+ 'check-testcases!' => \$opt_check_testcases,
+ 'mark-progress' => \$opt_mark_progress,
+
+ # Extra options used when starting mariadbd
+ 'mariadbd=s' => \@opt_extra_mysqld_opt,
+ 'mariadbd-env=s' => \@opt_mysqld_envs,
+ # mysqld is an alias for mariadbd
+ 'mysqld=s' => \@opt_extra_mysqld_opt,
+ 'mysqld-env=s' => \@opt_mysqld_envs,
+
+ # Run test on running server
+ 'extern=s' => \%opts_extern, # Append to hash
+
+ # Debugging
+ 'debug' => \$opt_debug,
+ 'debug-common' => \$opt_debug_common,
+ 'debug-server' => \$opt_debug_server,
+ 'max-save-core=i' => \$opt_max_save_core,
+ 'max-save-datadir=i' => \$opt_max_save_datadir,
+ 'max-test-fail=i' => \$opt_max_test_fail,
+ 'core-on-failure' => \$opt_core_on_failure,
+
+ # Coverage, profiling etc
+ 'gcov' => \$opt_gcov,
+ 'gprof' => \$opt_gprof,
+ 'debug-sync-timeout=i' => \$opt_debug_sync_timeout,
+
+ # Directories
+ 'tmpdir=s' => \$opt_tmpdir,
+ 'vardir=s' => \$opt_vardir,
+ 'mem' => \$opt_mem,
+ 'clean-vardir' => \$opt_clean_vardir,
+ 'client-bindir=s' => \$path_client_bindir,
+ 'client-libdir=s' => \$path_client_libdir,
+
+ # Misc
+ 'comment=s' => \$opt_comment,
+ 'fast' => \$opt_fast,
+ 'force-restart' => \$opt_force_restart,
+ 'reorder!' => \$opt_reorder,
+ 'enable-disabled' => \&collect_option,
+ 'verbose|v+' => \$opt_verbose,
+ 'verbose-restart' => \&report_option,
+ 'sleep=i' => \$opt_sleep,
+ 'start-dirty' => \$opt_start_dirty,
+ 'start-and-exit' => \$opt_start_exit,
+ 'start' => \$opt_start,
+ 'user-args' => \$opt_user_args,
+ 'wait-all' => \$opt_wait_all,
+ 'print-testcases' => \&collect_option,
+ 'repeat=i' => \$opt_repeat,
+ 'retry=i' => \$opt_retry,
+ 'retry-failure=i' => \$opt_retry_failure,
+ 'timer!' => \&report_option,
+ 'user=s' => \$opt_user,
+ 'testcase-timeout=i' => \$opt_testcase_timeout,
+ 'suite-timeout=i' => \$opt_suite_timeout,
+ 'shutdown-timeout=i' => \$opt_shutdown_timeout,
+ 'warnings!' => \$opt_warnings,
+ 'timestamp' => \&report_option,
+ 'timediff' => \&report_option,
+ 'stop-file=s' => \$opt_stop_file,
+ 'stop-keep-alive=i' => \$opt_stop_keep_alive,
+ 'max-connections=i' => \$opt_max_connections,
+ 'report-times' => \$opt_report_times,
+ 'result-file' => \$opt_resfile,
+ 'stress=s' => \$opt_stress,
+ 'tail-lines=i' => \$opt_tail_lines,
+ 'dry-run' => \$opt_dry_run,
+
+ 'help|h' => \$opt_usage,
+ # list-options is internal, not listed in help
+ 'list-options' => \$opt_list_options,
+ 'skip-test-list=s' => \@opt_skip_test_list,
+ 'xml-report=s' => \$opt_xml_report,
+
+ My::Debugger::options(),
+ My::CoreDump::options(),
+ My::Platform::options()
+ );
+
+ # fix options (that take an optional argument and *only* after = sign
+ @ARGV = My::Debugger::fix_options(@ARGV);
+ GetOptions(%options) or usage("Can't read options");
+ usage("") if $opt_usage;
+ list_options(\%options) if $opt_list_options;
+
+ # --------------------------------------------------------------------------
+ # Setup verbosity
+ # --------------------------------------------------------------------------
+ if ($opt_verbose != 0){
+ report_option('verbose', $opt_verbose);
+ }
+
+ # Negative values aren't meaningful on integer options
+ foreach(grep(/=i$/, keys %options))
+ {
+ if (defined ${$options{$_}} &&
+ do { no warnings "numeric"; int ${$options{$_}} < 0})
+ {
+ my $v= (split /=/)[0];
+ die("$v doesn't accept a negative value:");
+ }
+ }
+
+ # Find the absolute path to the test directory
+ $glob_mysql_test_dir= cwd();
+ if ($glob_mysql_test_dir =~ / /)
+ {
+ die("Working directory \"$glob_mysql_test_dir\" contains space\n".
+ "Bailing out, cannot function properly with space in path");
+ }
+ if (IS_CYGWIN)
+ {
+ if (My::Platform::check_cygwin_subshell()) {
+ die("Cygwin /bin/sh subshell requires fix with --cygwin-subshell-fix=do\n");
+ }
+ # Use mixed path format i.e c:/path/to/
+ $glob_mysql_test_dir= mixed_path($glob_mysql_test_dir);
+ }
+
+ # In most cases, the base directory we find everything relative to,
+ # is the parent directory of the "mysql-test" directory. For source
+ # distributions, TAR binary distributions and some other packages.
+ $basedir= dirname($glob_mysql_test_dir);
+
+ # In the RPM case, binaries and libraries are installed in the
+ # default system locations, instead of having our own private base
+ # directory. And we install "/usr/share/mysql-test". Moving up one
+ # more directory relative to "mysql-test" gives us a usable base
+ # directory for RPM installs.
+ if ( ! $source_dist and ! -d "$basedir/bin" )
+ {
+ $basedir= dirname($basedir);
+ }
+ # For .deb, it's like RPM, but installed in /usr/share/mysql/mysql-test.
+ # So move up one more directory level yet.
+ if ( ! $source_dist and ! -d "$basedir/bin" )
+ {
+ $basedir= dirname($basedir);
+ }
+
+ # Respect MTR_BINDIR variable, which is typically set in to the
+ # build directory in out-of-source builds.
+ $bindir=$ENV{MTR_BINDIR}||$basedir;
+
+ fix_vs_config_dir();
+
+ # Look for the client binaries directory
+ if ($path_client_bindir)
+ {
+ # --client-bindir=path set on command line, check that the path exists
+ $path_client_bindir= mtr_path_exists($path_client_bindir);
+ }
+ else
+ {
+ $path_client_bindir= mtr_path_exists("$bindir/client_release",
+ "$bindir/client_debug",
+ "$bindir/client/$multiconfig",
+ "$bindir/client$multiconfig",
+ "$bindir/client",
+ "$bindir/bin");
+ }
+
+ # Look for language files and charsetsdir, use same share
+ $path_language= mtr_path_exists("$bindir/share/mariadb",
+ "$bindir/share/mysql",
+ "$bindir/sql/share",
+ "$bindir/share");
+ my $path_share= $path_language;
+ $path_charsetsdir = mtr_path_exists("$basedir/share/mariadb/charsets",
+ "$basedir/share/mysql/charsets",
+ "$basedir/sql/share/charsets",
+ "$basedir/share/charsets");
+ if ( $opt_comment )
+ {
+ mtr_report();
+ mtr_print_thick_line('#');
+ mtr_report("# $opt_comment");
+ mtr_print_thick_line('#');
+ }
+
+ if ( @opt_experimentals )
+ {
+ # $^O on Windows considered not generic enough
+ my $plat= (IS_WINDOWS) ? 'windows' : $^O;
+
+ # read the list of experimental test cases from the files specified on
+ # the command line
+ $experimental_test_cases = [];
+ foreach my $exp_file (@opt_experimentals)
+ {
+ open(FILE, "<", $exp_file)
+ or mtr_error("Can't read experimental file: $exp_file");
+ mtr_report("Using experimental file: $exp_file");
+ while(<FILE>) {
+ chomp;
+ # remove comments (# foo) at the beginning of the line, or after a
+ # blank at the end of the line
+ s/(\s+|^)#.*$//;
+ # If @ platform specifier given, use this entry only if it contains
+ # @<platform> or @!<xxx> where xxx != platform
+ if (/\@.*/)
+ {
+ next if (/\@!$plat/);
+ next unless (/\@$plat/ or /\@!/);
+ # Then remove @ and everything after it
+ s/\@.*$//;
+ }
+ # remove whitespace
+ s/^\s+//;
+ s/\s+$//;
+ # if nothing left, don't need to remember this line
+ if ( $_ eq "" ) {
+ next;
+ }
+ # remember what is left as the name of another test case that should be
+ # treated as experimental
+ print " - $_\n";
+ push @$experimental_test_cases, $_;
+ }
+ close FILE;
+ }
+ }
+
+ foreach my $arg ( @ARGV )
+ {
+ if ( $arg =~ /^--skip-/ )
+ {
+ push(@opt_extra_mysqld_opt, $arg);
+ }
+ elsif ( $arg =~ /^--$/ )
+ {
+ # It is an effect of setting 'pass_through' in option processing
+ # that the lone '--' separating options from arguments survives,
+ # simply ignore it.
+ }
+ elsif ( $arg =~ /^-/ )
+ {
+ usage("Invalid option \"$arg\"");
+ }
+ else
+ {
+ push(@opt_cases, $arg);
+ }
+ }
+
+ if ( @opt_cases )
+ {
+ # Run big tests if explicitly specified on command line
+ $opt_big_test= 1;
+ }
+
+ # --------------------------------------------------------------------------
+ # Find out type of logging that are being used
+ # --------------------------------------------------------------------------
+ foreach my $arg ( @opt_extra_mysqld_opt )
+ {
+ if ( $arg =~ /binlog[-_]format=(\S+)/ )
+ {
+ # Save this for collect phase
+ collect_option('binlog-format', $1);
+ mtr_report("Using binlog format '$1'");
+ }
+ }
+
+
+ # --------------------------------------------------------------------------
+ # Find out default storage engine being used(if any)
+ # --------------------------------------------------------------------------
+ foreach my $arg ( @opt_extra_mysqld_opt )
+ {
+ if ( $arg =~ /default-storage-engine=(\S+)/ )
+ {
+ # Save this for collect phase
+ collect_option('default-storage-engine', $1);
+ mtr_report("Using default engine '$1'")
+ }
+ }
+
+ if (IS_WINDOWS and defined $opt_mem) {
+ mtr_report("--mem not supported on Windows, ignored");
+ $opt_mem= undef;
+ }
+
+ if ($opt_port_base ne "auto")
+ {
+ if (my $rem= $opt_port_base % 10)
+ {
+ mtr_warning ("Port base $opt_port_base rounded down to multiple of 10");
+ $opt_port_base-= $rem;
+ }
+ $opt_build_thread= $opt_port_base / 10 - 1000;
+ }
+
+ # --------------------------------------------------------------------------
+ # Check if we should speed up tests by trying to run on tmpfs
+ # --------------------------------------------------------------------------
+ if ( defined $opt_mem)
+ {
+ mtr_error("Can't use --mem and --vardir at the same time ")
+ if $opt_vardir;
+ mtr_error("Can't use --mem and --tmpdir at the same time ")
+ if $opt_tmpdir;
+
+ # Search through list of locations that are known
+ # to be "fast disks" to find a suitable location
+ my @tmpfs_locations= ("/run/shm", "/dev/shm", "/tmp");
+
+ # Use $ENV{'MTR_MEM'} as first location to look (if defined)
+ unshift(@tmpfs_locations, $ENV{'MTR_MEM'}) if defined $ENV{'MTR_MEM'};
+
+ foreach my $fs (@tmpfs_locations)
+ {
+ if ( -d $fs && ! -l $fs && -w $fs )
+ {
+ my $template= "var_${opt_build_thread}_XXXX";
+ $opt_mem= tempdir( $template, DIR => $fs, CLEANUP => 0);
+ last;
+ }
+ }
+ }
+
+ # --------------------------------------------------------------------------
+ # Set the "var/" directory, the base for everything else
+ # --------------------------------------------------------------------------
+ my $vardir_location= (defined $ENV{MTR_BINDIR}
+ ? "$ENV{MTR_BINDIR}/mysql-test"
+ : $glob_mysql_test_dir);
+ $vardir_location= realpath $vardir_location;
+ $default_vardir= "$vardir_location/var";
+
+ if ( ! $opt_vardir )
+ {
+ $opt_vardir= $default_vardir;
+ }
+
+ # We make the path absolute, as the server will do a chdir() before usage
+ unless ( $opt_vardir =~ m,^/, or
+ (IS_WINDOWS and $opt_vardir =~ m,^[a-z]:[/\\],i) )
+ {
+ # Make absolute path, relative test dir
+ $opt_vardir= "$glob_mysql_test_dir/$opt_vardir";
+ }
+
+ set_vardir($opt_vardir);
+
+ # --------------------------------------------------------------------------
+ # Set the "tmp" directory
+ # --------------------------------------------------------------------------
+ if ( ! $opt_tmpdir )
+ {
+ $opt_tmpdir= "$opt_vardir/tmp" unless $opt_tmpdir;
+
+ if (check_socket_path_length("$opt_tmpdir/mysql_testsocket.sock"))
+ {
+ mtr_report("Too long tmpdir path '$opt_tmpdir'",
+ " creating a shorter one...");
+
+ # Create temporary directory in standard location for temporary files
+ $opt_tmpdir= tempdir( TMPDIR => 1, CLEANUP => 0 );
+ mtr_report(" - using tmpdir: '$opt_tmpdir'\n");
+
+ # Remember pid that created dir so it's removed by correct process
+ $opt_tmpdir_pid= $$;
+ }
+ }
+ $opt_tmpdir =~ s,/+$,,; # Remove ending slash if any
+
+ # --------------------------------------------------------------------------
+ # fast option
+ # --------------------------------------------------------------------------
+ if ($opt_fast){
+ $opt_shutdown_timeout= 0; # Kill processes instead of nice shutdown
+ }
+
+ # --------------------------------------------------------------------------
+ # Check parallel value
+ # --------------------------------------------------------------------------
+ if ($opt_parallel ne "auto" && $opt_parallel < 1)
+ {
+ mtr_error("0 or negative parallel value makes no sense, use 'auto' or positive number");
+ }
+
+ # --------------------------------------------------------------------------
+ # Record flag
+ # --------------------------------------------------------------------------
+ if ( $opt_record and ! @opt_cases )
+ {
+ mtr_error("Will not run in record mode without a specific test case");
+ }
+
+ if ( $opt_record ) {
+ # Use only one worker with --record
+ $opt_parallel= 1;
+ }
+
+ # --------------------------------------------------------------------------
+ # Embedded server flag
+ # --------------------------------------------------------------------------
+ if ( $opt_embedded_server )
+ {
+ $opt_skip_ssl= 1; # Turn off use of SSL
+
+ # Turn off use of bin log
+ push(@opt_extra_mysqld_opt, "--skip-log-bin");
+
+ if ( using_extern() )
+ {
+ mtr_error("Can't use --extern with --embedded-server");
+ }
+ }
+
+ # --------------------------------------------------------------------------
+ # Big test and staging_run flags
+ # --------------------------------------------------------------------------
+ if ( $opt_big_test )
+ {
+ $ENV{'BIG_TEST'}= 1;
+ }
+ $ENV{'STAGING_RUN'}= $opt_staging_run;
+
+ # --------------------------------------------------------------------------
+ # Gcov flag
+ # --------------------------------------------------------------------------
+ if ( ($opt_gcov or $opt_gprof) and ! $source_dist )
+ {
+ mtr_error("Coverage test needs the source - please use source dist");
+ }
+
+ $ENV{ASAN_OPTIONS}= "abort_on_error=1:" . ($ENV{ASAN_OPTIONS} || '');
+ $ENV{ASAN_OPTIONS}= "suppressions=${glob_mysql_test_dir}/asan.supp:" .
+ $ENV{ASAN_OPTIONS}
+ if -f "$glob_mysql_test_dir/asan.supp" and not IS_WINDOWS;
+ # The following can be useful when a test fails without any asan report
+ # on stderr like with openssl_1.test
+ # $ENV{ASAN_OPTIONS}= "log_path=${opt_vardir}/log/asan:" . $ENV{ASAN_OPTIONS};
+
+ # Add leak suppressions
+ $ENV{LSAN_OPTIONS}= "suppressions=${glob_mysql_test_dir}/lsan.supp:print_suppressions=0"
+ if -f "$glob_mysql_test_dir/lsan.supp" and not IS_WINDOWS;
+
+ mtr_verbose("ASAN_OPTIONS=$ENV{ASAN_OPTIONS}");
+
+ # --------------------------------------------------------------------------
+ # Modified behavior with --start options
+ # --------------------------------------------------------------------------
+ if ($opt_start or $opt_start_dirty or $opt_start_exit) {
+ collect_option ('quick-collect', 1);
+ $start_only= 1;
+ }
+ if ($opt_debug)
+ {
+ $opt_testcase_timeout= 7 * 24 * 60;
+ $opt_suite_timeout= 7 * 24 * 60;
+ $opt_retry= 1;
+ $opt_retry_failure= 1;
+ }
+
+ # --------------------------------------------------------------------------
+ # Check use of user-args
+ # --------------------------------------------------------------------------
+
+ if ($opt_user_args) {
+ mtr_error("--user-args only valid with --start options")
+ unless $start_only;
+ mtr_error("--user-args cannot be combined with named suites or tests")
+ if $opt_suites || @opt_cases;
+ }
+
+ # --------------------------------------------------------------------------
+ # Check use of wait-all
+ # --------------------------------------------------------------------------
+
+ if ($opt_wait_all && ! $start_only)
+ {
+ mtr_error("--wait-all can only be used with --start options");
+ }
+
+ # --------------------------------------------------------------------------
+ # Gather stress-test options and modify behavior
+ # --------------------------------------------------------------------------
+
+ if ($opt_stress)
+ {
+ $opt_stress=~ s/,/ /g;
+ $opt_user_args= 1;
+ mtr_error("--stress cannot be combined with named ordinary suites or tests")
+ if $opt_suites || @opt_cases;
+ $opt_suites="stress";
+ @opt_cases= ("wrapper");
+ $ENV{MST_OPTIONS}= $opt_stress;
+ }
+
+ # --------------------------------------------------------------------------
+ # Check timeout arguments
+ # --------------------------------------------------------------------------
+
+ mtr_error("Invalid value '$opt_testcase_timeout' supplied ".
+ "for option --testcase-timeout")
+ if ($opt_testcase_timeout <= 0);
+ mtr_error("Invalid value '$opt_suite_timeout' supplied ".
+ "for option --testsuite-timeout")
+ if ($opt_suite_timeout <= 0);
+
+ if ($opt_debug_common)
+ {
+ $opt_debug= 1;
+ $debug_d= "d,query,info,error,enter,exit";
+ }
+
+ My::CoreDump::pre_setup();
+}
+
+
+#
+# To make it easier for different devs to work on the same host,
+# an environment variable can be used to control all ports. A small
+# number is to be used, 0 - 16 or similar.
+#
+# Note the MASTER_MYPORT has to be set the same in all 4.x and 5.x
+# versions of this script, else a 4.0 test run might conflict with a
+# 5.1 test run, even if different MTR_BUILD_THREAD is used. This means
+# all port numbers might not be used in this version of the script.
+#
+# Also note the limitation of ports we are allowed to hand out. This
+# differs between operating systems and configuration, see
+# http://www.ncftp.com/ncftpd/doc/misc/ephemeral_ports.html
+# But a fairly safe range seems to be 5001 - 32767
+#
+sub set_build_thread_ports($) {
+ my $thread= shift || 0;
+
+ if ( lc($opt_build_thread) eq 'auto' ) {
+ my $found_free = 0;
+ $build_thread = 300; # Start attempts from here
+ my $build_thread_upper = $build_thread + ($opt_parallel > 1500
+ ? 3000
+ : 2 * $opt_parallel) + 300;
+ while (! $found_free)
+ {
+ $build_thread= mtr_get_unique_id($build_thread, $build_thread_upper);
+ if ( !defined $build_thread ) {
+ mtr_error("Could not get a unique build thread id");
+ }
+ $found_free= check_ports_free($build_thread);
+ # If not free, release and try from next number
+ if (! $found_free) {
+ mtr_release_unique_id();
+ $build_thread++;
+ }
+ }
+ }
+ else
+ {
+ $build_thread = $opt_build_thread + $thread - 1;
+ if (! check_ports_free($build_thread)) {
+ # Some port was not free(which one has already been printed)
+ mtr_error("Some port(s) was not free")
+ }
+ }
+ $ENV{MTR_BUILD_THREAD}= $build_thread;
+
+ # Calculate baseport
+ $baseport= $build_thread * $opt_port_group_size + 10000;
+ if ( $baseport < 5001 or $baseport + $opt_port_group_size >= 32767 )
+ {
+ mtr_error("MTR_BUILD_THREAD number results in a port",
+ "outside 5001 - 32767",
+ "($baseport - $baseport + $opt_port_group_size)");
+ }
+
+ mtr_report("Using MTR_BUILD_THREAD $build_thread,",
+ "with reserved ports $baseport..".($baseport+($opt_port_group_size-1)));
+
+}
+
+
+sub collect_mysqld_features {
+ #
+ # Execute "mysqld --no-defaults --help --verbose" to get a
+ # list of all features and settings
+ #
+ # --no-defaults and --skip-grant-tables are to avoid loading
+ # system-wide configs and plugins
+ #
+ # --datadir must exist, mysqld will chdir into it
+ #
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--no-defaults");
+ mtr_add_arg($args, "--datadir=.");
+ mtr_add_arg($args, "--basedir=%s", $basedir);
+ mtr_add_arg($args, "--lc-messages-dir=%s", $path_language);
+ mtr_add_arg($args, "--skip-grant-tables");
+ mtr_add_arg($args, "--log-warnings=0");
+ mtr_add_arg($args, "--log-slow-admin-statements=0");
+ mtr_add_arg($args, "--log-queries-not-using-indexes=0");
+ mtr_add_arg($args, "--log-slow-slave-statements=0");
+ mtr_add_arg($args, "--verbose");
+ mtr_add_arg($args, "--help");
+
+ my $exe_mysqld= find_mysqld($bindir);
+ my $cmd= join(" ", $exe_mysqld, @$args);
+
+ mtr_verbose("cmd: $cmd");
+
+ my $list= `$cmd`;
+
+ # to simplify the parsing, we'll merge all nicely formatted --help texts
+ $list =~ s/\n {22}(\S)/ $1/g;
+
+ my @list= split '\R', $list;
+
+ $mysql_version_id= 0;
+ my $exe= basename($exe_mysqld);
+ while (defined(my $line = shift @list)){
+ if ($line =~ /\W\Q$exe\E\s+Ver\s(\d+)\.(\d+)\.(\d+)(\S*)/ ) {
+ $mysql_version_id= $1*10000 + $2*100 + $3;
+ mtr_report("MariaDB Version $1.$2.$3$4");
+ last;
+ }
+ }
+ mtr_error("Could not find version of MariaDB")
+ unless $mysql_version_id > 0;
+
+ for (@list)
+ {
+ # first part of the help - command-line options.
+ if (/Copyright/ .. /^-{30,}/) {
+ # here we want to detect all not mandatory plugins
+ # they are listed in the --help output as
+ # --archive[=name]
+ # Enable or disable ARCHIVE plugin. Possible values are ON, OFF,
+ # FORCE (don't start if the plugin fails to load),
+ # FORCE_PLUS_PERMANENT (like FORCE, but the plugin can not be uninstalled).
+ # For Innodb I_S plugins that are referenced in sys schema
+ # do not make them optional, to prevent diffs in tests.
+ push @optional_plugins, $1
+ if /^ --([-a-z0-9]+)\[=name\] +Enable or disable \w+ plugin. One of: ON, OFF, FORCE/
+ and $1 ne "innodb-metrics"
+ and $1 ne "innodb-buffer-page"
+ and $1 ne "innodb-lock-waits"
+ and $1 ne "innodb-locks"
+ and $1 ne "innodb-trx"
+ and $1 ne "gssapi";
+ next;
+ }
+
+ last if /^\r?$/; # then goes a list of variables, it ends with an empty line
+
+ # Put a variable into hash
+ /^([\S]+)[ \t]+(.*?)\r?$/ or die "Could not parse mysqld --help: $_\n";
+ $mysqld_variables{$1}= $2;
+ }
+ mtr_error("Could not find variabes list") unless %mysqld_variables;
+}
+
+
+sub collect_mysqld_features_from_running_server ()
+{
+ my $mysql= mtr_exe_exists("$path_client_bindir/mariadb");
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--no-defaults");
+ mtr_add_arg($args, "--user=%s", $opt_user);
+
+ while (my ($option, $value)= each( %opts_extern )) {
+ mtr_add_arg($args, "--$option=$value");
+ }
+
+ mtr_add_arg($args, "--silent"); # Tab separated output
+ mtr_add_arg($args, "-e \"use mysql; SHOW VARIABLES\"");
+ my $cmd= "$mysql " . join(' ', @$args);
+ mtr_verbose("cmd: $cmd");
+
+ my $list = `$cmd` or
+ mtr_error("Could not connect to extern server using command: '$cmd'");
+ foreach my $line (split('\R', $list ))
+ {
+ # Put variables into hash
+ if ( $line =~ /^([\S]+)[ \t]+(.*?)\r?$/ )
+ {
+ my $name= $1;
+ my $value=$2;
+ $name =~ s/_/-/g;
+ # print "$name=\"$value\"\n";
+ $mysqld_variables{$name}= $value;
+ }
+ }
+
+ # Parse version
+ my $version_str= $mysqld_variables{'version'};
+ if ( $version_str =~ /^([0-9]*)\.([0-9]*)\.([0-9]*)([^\s]*)/ )
+ {
+ #print "Major: $1 Minor: $2 Build: $3\n";
+ $mysql_version_id= $1*10000 + $2*100 + $3;
+ #print "mysql_version_id: $mysql_version_id\n";
+ mtr_report("MariaDB Version $1.$2.$3");
+ $mysql_version_extra= $4;
+ }
+ mtr_error("Could not find version of MariaDBL") unless $mysql_version_id;
+}
+
+sub find_mysqld {
+
+ my ($mysqld_basedir)= $ENV{MTR_BINDIR_FORCED} || $ENV{MTR_BINDIR} || @_;
+
+ my @mysqld_names= ("mariadbd", "mysqld", "mysqld-max-nt", "mysqld-max",
+ "mysqld-nt");
+
+ if ( $opt_debug_server ){
+ # Put mysqld-debug first in the list of binaries to look for
+ mtr_verbose("Adding mysqld-debug first in list of binaries to look for");
+ unshift(@mysqld_names, "mysqld-debug");
+ }
+
+ return my_find_bin($mysqld_basedir,
+ ["sql", "libexec", "sbin", "bin"],
+ [@mysqld_names]);
+}
+
+
+sub executable_setup () {
+
+ $exe_patch='patch' if `patch -v`;
+
+ # Look for the client binaries
+ $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mariadb-admin");
+ $exe_mysql= mtr_exe_exists("$path_client_bindir/mariadb");
+ $exe_mysql_plugin= mtr_exe_exists("$path_client_bindir/mariadb-plugin");
+ $exe_mariadb_conv= mtr_exe_exists("$path_client_bindir/mariadb-conv");
+
+ $exe_mysql_embedded= mtr_exe_maybe_exists("$bindir/libmysqld/examples/mysql_embedded");
+
+ # Look for mysqltest executable
+ if ( $opt_embedded_server )
+ {
+ $exe_mysqltest=
+ mtr_exe_exists("$bindir/libmysqld/examples$multiconfig/mysqltest_embedded",
+ "$path_client_bindir/mysqltest_embedded");
+ }
+ else
+ {
+ if ( defined $ENV{'MYSQL_TEST'} )
+ {
+ $exe_mysqltest=$ENV{'MYSQL_TEST'};
+ print "===========================================================\n";
+ print "WARNING:The mysqltest binary is fetched from $exe_mysqltest\n";
+ print "===========================================================\n";
+ }
+ else
+ {
+ $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mariadb-test");
+ }
+ }
+
+}
+
+
+sub client_debug_arg($$) {
+ my ($args, $client_name)= @_;
+
+ # Workaround for Bug #50627: drop any debug opt
+ return if $client_name =~ /^mariadb-binlog/;
+
+ if ( $opt_debug ) {
+ mtr_add_arg($args,
+ "--loose-debug=d,info,warning,warnings:t:A,%s/log/%s.trace",
+ $path_vardir_trace, $client_name)
+ }
+}
+
+
+sub client_arguments ($;$) {
+ my $client_name= shift;
+ my $group_suffix= shift;
+ my $client_exe= mtr_exe_exists("$path_client_bindir/$client_name");
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ if (defined($group_suffix)) {
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $group_suffix);
+ client_debug_arg($args, "$client_name-$group_suffix");
+ }
+ else
+ {
+ client_debug_arg($args, $client_name);
+ }
+ return mtr_args2str($client_exe, @$args);
+}
+
+
+sub mysqlbinlog_arguments () {
+ my $exe= mtr_exe_exists("$path_client_bindir/mariadb-binlog");
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--local-load=%s", $opt_tmpdir);
+ client_debug_arg($args, "mysqlbinlog");
+ return mtr_args2str($exe, @$args);
+}
+
+
+sub mysqlslap_arguments () {
+ my $exe= mtr_exe_maybe_exists("$path_client_bindir/mariadb-slap");
+ if ( $exe eq "" ) {
+ # mysqlap was not found
+
+ if (defined $mysql_version_id and $mysql_version_id >= 50100 ) {
+ mtr_error("Could not find the mariadb-slap binary");
+ }
+ return ""; # Don't care about mariadb-slap
+ }
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ client_debug_arg($args, "mysqlslap");
+ return mtr_args2str($exe, @$args);
+}
+
+
+sub mysqldump_arguments ($) {
+ my($group_suffix) = @_;
+ my $exe= mtr_exe_exists("$path_client_bindir/mariadb-dump");
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $group_suffix);
+ client_debug_arg($args, "mysqldump-$group_suffix");
+ return mtr_args2str($exe, @$args);
+}
+
+
+sub mysql_client_test_arguments(){
+ my $exe;
+ # mysql_client_test executable may _not_ exist
+ if ( $opt_embedded_server ) {
+ $exe= mtr_exe_maybe_exists(
+ "$bindir/libmysqld/examples$multiconfig/mysql_client_test_embedded",
+ "$bindir/bin/mysql_client_test_embedded");
+ } else {
+ $exe= mtr_exe_maybe_exists("$bindir/tests$multiconfig/mysql_client_test",
+ "$bindir/bin/mysql_client_test");
+ }
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--testcase");
+ mtr_add_arg($args, "--vardir=$opt_vardir");
+ client_debug_arg($args,"mysql_client_test");
+ my $ret=mtr_args2str($exe, @$args);
+ return $ret;
+}
+
+sub tool_arguments ($$) {
+ my($sedir, $tool_name) = @_;
+ my $exe= my_find_bin($bindir,
+ [$sedir, "bin"],
+ $tool_name);
+
+ my $args;
+ mtr_init_args(\$args);
+ client_debug_arg($args, $tool_name);
+ return mtr_args2str($exe, @$args);
+}
+
+# This is not used to actually start a mysqld server, just to allow test
+# scripts to run the mysqld binary to test invalid server startup options.
+sub mysqld_client_arguments () {
+ my $default_mysqld= default_mysqld();
+ my $exe = find_mysqld($bindir);
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--no-defaults");
+ mtr_add_arg($args, "--basedir=%s", $basedir);
+ mtr_add_arg($args, "--character-sets-dir=%s", $default_mysqld->value("character-sets-dir"));
+ mtr_add_arg($args, "--language=%s", $default_mysqld->value("language"));
+ return mtr_args2str($exe, @$args);
+}
+
+
+sub have_maria_support () {
+ my $maria_var= $mysqld_variables{'aria-recover-options'};
+ return defined $maria_var;
+}
+
+
+sub environment_setup {
+
+ umask(022);
+
+ $ENV{'USE_RUNNING_SERVER'}= using_extern();
+
+ my @ld_library_paths;
+
+ if ($path_client_libdir)
+ {
+ # Use the --client-libdir passed on commandline
+ push(@ld_library_paths, "$path_client_libdir");
+ }
+ else
+ {
+ # Setup LD_LIBRARY_PATH so the libraries from this distro/clone
+ # are used in favor of the system installed ones
+ if ( $source_dist )
+ {
+ push(@ld_library_paths, "$basedir/libmysql/.libs/",
+ "$basedir/libmysql_r/.libs/",
+ "$basedir/zlib/.libs/");
+ if ($^O eq "darwin")
+ {
+ # it is MAC OS and we have to add dynamic libraries paths
+ push @ld_library_paths, grep {<$_/*.dylib>}
+ (<$bindir/storage/*/.libs/>,<$bindir/plugin/*/.libs/>,
+ <$bindir/plugin/*/*/.libs/>,<$bindir/storage/*/*/.libs>);
+ }
+ }
+ else
+ {
+ push(@ld_library_paths, "$basedir/lib", "$basedir/lib/mysql");
+ }
+ }
+
+ $ENV{'LD_LIBRARY_PATH'}= join(":", @ld_library_paths,
+ $ENV{'LD_LIBRARY_PATH'} ?
+ split(':', $ENV{'LD_LIBRARY_PATH'}) : ());
+
+ My::Debugger::pre_setup();
+
+ mtr_debug("LD_LIBRARY_PATH: $ENV{'LD_LIBRARY_PATH'}");
+
+ $ENV{'DYLD_LIBRARY_PATH'}= join(":", @ld_library_paths,
+ $ENV{'DYLD_LIBRARY_PATH'} ?
+ split(':', $ENV{'DYLD_LIBRARY_PATH'}) : ());
+ mtr_debug("DYLD_LIBRARY_PATH: $ENV{'DYLD_LIBRARY_PATH'}");
+
+ # The environment variable used for shared libs on AIX
+ $ENV{'SHLIB_PATH'}= join(":", @ld_library_paths,
+ $ENV{'SHLIB_PATH'} ?
+ split(':', $ENV{'SHLIB_PATH'}) : ());
+ mtr_debug("SHLIB_PATH: $ENV{'SHLIB_PATH'}");
+
+ # The environment variable used for shared libs on hp-ux
+ $ENV{'LIBPATH'}= join(":", @ld_library_paths,
+ $ENV{'LIBPATH'} ?
+ split(':', $ENV{'LIBPATH'}) : ());
+ mtr_debug("LIBPATH: $ENV{'LIBPATH'}");
+
+ $ENV{'UMASK'}= "0660"; # The octal *string*
+ $ENV{'UMASK_DIR'}= "0770"; # The octal *string*
+
+ #
+ # MariaDB tests can produce output in various character sets
+ # (especially, ctype_xxx.test). To avoid confusing Perl
+ # with output which is incompatible with the current locale
+ # settings, we reset the current values of LC_ALL and LC_CTYPE to "C".
+ # For details, please see
+ # Bug#27636 tests fails if LC_* variables set to *_*.UTF-8
+ #
+ $ENV{'LC_ALL'}= "C";
+ $ENV{'LC_CTYPE'}= "C";
+ $ENV{'LC_COLLATE'}= "C";
+
+ $ENV{'OPENSSL_CONF'}= $mysqld_variables{'version-ssl-library'} gt 'OpenSSL 1.1.1'
+ ? "$glob_mysql_test_dir/lib/openssl.cnf" : '/dev/null';
+
+ $ENV{'MYSQL_TEST_DIR'}= $glob_mysql_test_dir;
+ $ENV{'DEFAULT_MASTER_PORT'}= $mysqld_variables{'port'};
+ $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir;
+ $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir;
+ $ENV{'MYSQLTEST_REAL_VARDIR'}= realpath $opt_vardir;
+ $ENV{'MYSQL_BINDIR'}= $bindir;
+ $ENV{'MYSQL_SHAREDIR'}= $path_language;
+ $ENV{'MYSQL_CHARSETSDIR'}= $path_charsetsdir;
+
+ if (IS_WINDOWS)
+ {
+ $ENV{'SECURE_LOAD_PATH'}= $glob_mysql_test_dir."\\std_data";
+ }
+ else
+ {
+ $ENV{'SECURE_LOAD_PATH'}= $glob_mysql_test_dir."/std_data";
+ }
+
+ #
+ # Some stupid^H^H^H^H^H^Hignorant network providers set up "wildcard DNS"
+ # servers that return some given web server address for any lookup of a
+ # non-existent host name. This confuses test cases that want to test the
+ # behaviour when connecting to a non-existing host, so we need to be able
+ # to disable those tests when DNS is broken.
+ #
+ $ENV{HAVE_BROKEN_DNS}= defined(gethostbyname('invalid_hostname'));
+
+ # ----------------------------------------------------
+ # mysql clients
+ # ----------------------------------------------------
+ $ENV{'MYSQL_CHECK'}= client_arguments("mariadb-check");
+ $ENV{'MYSQL_DUMP'}= mysqldump_arguments(".1");
+ $ENV{'MYSQL_DUMP_SLAVE'}= mysqldump_arguments(".2");
+ $ENV{'MYSQL_SLAP'}= mysqlslap_arguments();
+ $ENV{'MYSQL_IMPORT'}= client_arguments("mariadb-import");
+ $ENV{'MYSQL_SHOW'}= client_arguments("mariadb-show");
+ $ENV{'MYSQL_BINLOG'}= mysqlbinlog_arguments();
+ $ENV{'MYSQL'}= client_arguments("mariadb");
+ $ENV{'MYSQL_SLAVE'}= client_arguments("mariadb", ".2");
+ $ENV{'MYSQL_UPGRADE'}= client_arguments("mariadb-upgrade");
+ $ENV{'MYSQLADMIN'}= client_arguments("mariadb-admin");
+ $ENV{'MYSQL_CLIENT_TEST'}= mysql_client_test_arguments();
+ $ENV{'EXE_MYSQL'}= $exe_mysql;
+ $ENV{'MYSQL_PLUGIN'}= $exe_mysql_plugin;
+ $ENV{'MYSQL_EMBEDDED'}= $exe_mysql_embedded;
+ $ENV{'MARIADB_CONV'}= "$exe_mariadb_conv --character-sets-dir=$path_charsetsdir";
+ if(IS_WINDOWS)
+ {
+ $ENV{'MYSQL_INSTALL_DB_EXE'}= mtr_exe_exists("$bindir/sql$multiconfig/mariadb-install-db",
+ "$bindir/bin/mariadb-install-db");
+ }
+
+ my $client_config_exe=
+ mtr_exe_maybe_exists(
+ "$bindir/libmariadb/mariadb_config$multiconfig/mariadb_config",
+ "$bindir/bin/mariadb_config");
+ if ($client_config_exe)
+ {
+ my $tls_info= `$client_config_exe --tlsinfo`;
+ ($ENV{CLIENT_TLS_LIBRARY},$ENV{CLIENT_TLS_LIBRARY_VERSION})=
+ split(/ /, $tls_info, 2);
+ }
+ my $exe_mysqld= find_mysqld($basedir);
+ $ENV{'MYSQLD'}= $exe_mysqld;
+ my $extra_opts= join (" ", @opt_extra_mysqld_opt);
+ $ENV{'MYSQLD_CMD'}= "$exe_mysqld --defaults-group-suffix=.1 ".
+ "--defaults-file=$path_config_file $extra_opts";
+
+ # ----------------------------------------------------
+ # bug25714 executable may _not_ exist in
+ # some versions, test using it should be skipped
+ # ----------------------------------------------------
+ my $exe_bug25714=
+ mtr_exe_maybe_exists("$bindir/tests$multiconfig/bug25714");
+ $ENV{'MYSQL_BUG25714'}= native_path($exe_bug25714);
+
+ # ----------------------------------------------------
+ # mysql_fix_privilege_tables.sql
+ # ----------------------------------------------------
+ my $file_mysql_fix_privilege_tables=
+ mtr_file_exists("$bindir/scripts/mysql_fix_privilege_tables.sql",
+ "$bindir/share/mysql_fix_privilege_tables.sql",
+ "$bindir/share/mariadb/mysql_fix_privilege_tables.sql",
+ "$bindir/share/mysql/mysql_fix_privilege_tables.sql");
+ $ENV{'MYSQL_FIX_PRIVILEGE_TABLES'}= $file_mysql_fix_privilege_tables;
+
+ # ----------------------------------------------------
+ # my_print_defaults
+ # ----------------------------------------------------
+ my $exe_my_print_defaults=
+ mtr_exe_exists("$bindir/extra$multiconfig/my_print_defaults",
+ "$path_client_bindir/my_print_defaults");
+ $ENV{'MYSQL_MY_PRINT_DEFAULTS'}= native_path($exe_my_print_defaults);
+
+ # ----------------------------------------------------
+ # myisam tools
+ # ----------------------------------------------------
+ $ENV{'MYISAMLOG'}= tool_arguments("storage/myisam", "myisamlog", );
+ $ENV{'MYISAMCHK'}= tool_arguments("storage/myisam", "myisamchk");
+ $ENV{'MYISAMPACK'}= tool_arguments("storage/myisam", "myisampack");
+ $ENV{'MYISAM_FTDUMP'}= tool_arguments("storage/myisam", "myisam_ftdump");
+
+ # ----------------------------------------------------
+ # aria tools
+ # ----------------------------------------------------
+ if (have_maria_support())
+ {
+ $ENV{'MARIA_CHK'}= tool_arguments("storage/maria", "aria_chk");
+ $ENV{'MARIA_PACK'}= tool_arguments("storage/maria", "aria_pack");
+ }
+
+ # ----------------------------------------------------
+ # mysqlhotcopy
+ # ----------------------------------------------------
+ my $mysqlhotcopy=
+ mtr_pl_maybe_exists("$bindir/scripts/mysqlhotcopy") ||
+ mtr_pl_maybe_exists("$path_client_bindir/mysqlhotcopy");
+ if ($mysqlhotcopy)
+ {
+ $ENV{'MYSQLHOTCOPY'}= $mysqlhotcopy;
+ }
+
+ # ----------------------------------------------------
+ # perror
+ # ----------------------------------------------------
+ my $exe_perror= mtr_exe_exists("$bindir/extra$multiconfig/perror",
+ "$path_client_bindir/perror");
+ $ENV{'MY_PERROR'}= native_path($exe_perror);
+
+ # ----------------------------------------------------
+ # mysql_tzinfo_to_sql
+ # ----------------------------------------------------
+ my $exe_mysql_tzinfo_to_sql= mtr_exe_exists("$basedir/sql$multiconfig/mariadb-tzinfo-to-sql",
+ "$path_client_bindir/mariadb-tzinfo-to-sql",
+ "$bindir/sql$multiconfig/mariadb-tzinfo-to-sql");
+ $ENV{'MYSQL_TZINFO_TO_SQL'}= native_path($exe_mysql_tzinfo_to_sql);
+
+ # ----------------------------------------------------
+ # replace
+ # ----------------------------------------------------
+ my $exe_replace= mtr_exe_exists(vs_config_dirs('extra', 'replace'),
+ "$basedir/extra/replace",
+ "$bindir/extra$multiconfig/replace",
+ "$path_client_bindir/replace");
+ $ENV{'REPLACE'}= native_path($exe_replace);
+
+ # ----------------------------------------------------
+ # innochecksum
+ # ----------------------------------------------------
+ my $exe_innochecksum=
+ mtr_exe_maybe_exists("$bindir/extra$multiconfig/innochecksum",
+ "$path_client_bindir/innochecksum");
+ $ENV{'INNOCHECKSUM'}= native_path($exe_innochecksum) if $exe_innochecksum;
+
+ # ----------------------------------------------------
+ # mariabackup
+ # ----------------------------------------------------
+ my $exe_mariabackup= mtr_exe_maybe_exists(
+ "$bindir/extra/mariabackup$multiconfig/mariabackup",
+ "$path_client_bindir/mariabackup");
+
+ $ENV{XTRABACKUP}= native_path($exe_mariabackup) if $exe_mariabackup;
+
+ my $exe_xbstream= mtr_exe_maybe_exists(
+ "$bindir/extra/mariabackup/$multiconfig/mbstream",
+ "$path_client_bindir/mbstream");
+ $ENV{XBSTREAM}= native_path($exe_xbstream) if $exe_xbstream;
+
+ $ENV{INNOBACKUPEX}= "$exe_mariabackup --innobackupex";
+
+ # Add dir of this perl to aid mysqltest in finding perl
+ my $perldir= dirname($^X);
+ my $pathsep= ":";
+ $pathsep= ";" if IS_WINDOWS && ! IS_CYGWIN;
+ $ENV{'PATH'}= "$ENV{'PATH'}".$pathsep.$perldir;
+}
+
+
+sub remove_vardir_subs() {
+ foreach my $sdir ( glob("$opt_vardir/*") ) {
+ mtr_verbose("Removing subdir $sdir");
+ rmtree($sdir);
+ }
+}
+
+#
+# Remove var and any directories in var/ created by previous
+# tests
+#
+sub remove_stale_vardir () {
+
+ mtr_report("Removing old var directory...");
+
+ # Safety!
+ mtr_error("No, don't remove the vardir when running with --extern")
+ if using_extern();
+
+ mtr_verbose("opt_vardir: $opt_vardir");
+ if ( $opt_vardir eq $default_vardir )
+ {
+ #
+ # Running with "var" in mysql-test dir
+ #
+ if ( -l $opt_vardir)
+ {
+ # var is a symlink
+
+ if ( $opt_mem )
+ {
+ # Remove the directory which the link points at
+ mtr_verbose("Removing " . readlink($opt_vardir));
+ rmtree(readlink($opt_vardir));
+
+ # Remove the "var" symlink
+ mtr_verbose("unlink($opt_vardir)");
+ unlink($opt_vardir);
+ }
+ else
+ {
+ # Some users creates a soft link in mysql-test/var to another area
+ # - allow it, but remove all files in it
+
+ mtr_report(" - WARNING: Using the 'mysql-test/var' symlink");
+
+ # Make sure the directory where it points exist
+ 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);
+ }
+ remove_vardir_subs();
+ }
+ }
+ else
+ {
+ # Remove the entire "var" dir
+ mtr_verbose("Removing $opt_vardir/");
+ rmtree("$opt_vardir/");
+ }
+
+ if ( $opt_mem )
+ {
+ # A symlink from var/ to $opt_mem will be set up
+ # remove the $opt_mem dir to assure the symlink
+ # won't point at an old directory
+ mtr_verbose("Removing $opt_mem");
+ rmtree($opt_mem);
+ }
+
+ }
+ else
+ {
+ #
+ # Running with "var" in some other place
+ #
+
+ # Don't remove the var/ dir in mysql-test dir as it may be in
+ # use by another mysql-test-run run with --vardir
+ # mtr_verbose("Removing $default_vardir");
+ # rmtree($default_vardir);
+
+ # Remove the "var" dir
+ mtr_verbose("Removing $opt_vardir/");
+ rmtree("$opt_vardir/");
+ }
+ # Remove the "tmp" dir
+ mtr_verbose("Removing $opt_tmpdir/");
+ rmtree("$opt_tmpdir/");
+}
+
+sub set_plugin_var($) {
+ local $_ = $_[0];
+ s/\.\w+$//;
+ $ENV{"\U${_}_SO"} = $_[0];
+}
+
+#
+# Create var and the directories needed in var
+#
+sub setup_vardir() {
+ mtr_report("Creating var directory '$opt_vardir'...");
+
+ if ( $opt_vardir eq $default_vardir )
+ {
+ #
+ # Running with "var" in mysql-test dir
+ #
+ if ( -l $opt_vardir )
+ {
+ # it's a symlink
+
+ # Make sure the directory where it points exist
+ 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 )
+ {
+ # Runinng with "var" as a link to some "memory" location, normally tmpfs
+ mtr_verbose("Creating $opt_mem");
+ mkpath($opt_mem);
+
+ mtr_report(" - symlinking 'var' to '$opt_mem'");
+ symlink($opt_mem, $opt_vardir);
+ }
+ }
+
+ if ( ! -d $opt_vardir )
+ {
+ mtr_verbose("Creating $opt_vardir");
+ mkpath($opt_vardir);
+ }
+
+ # Ensure a proper error message if vardir couldn't be created
+ unless ( -d $opt_vardir and -w $opt_vardir )
+ {
+ mtr_error("Writable 'var' directory is needed, use the " .
+ "'--vardir=<path>' option");
+ }
+
+ mkpath("$opt_vardir/log");
+ mkpath("$opt_vardir/run");
+
+ # Create var/tmp and tmp - they might be different
+ mkpath("$opt_vardir/tmp");
+ mkpath($opt_tmpdir) if ($opt_tmpdir ne "$opt_vardir/tmp");
+
+ # On some operating systems, there is a limit to the length of a
+ # UNIX domain socket's path far below PATH_MAX.
+ # Don't allow that to happen
+ if (check_socket_path_length("$opt_tmpdir/testsocket.sock")){
+ mtr_error("Socket path '$opt_tmpdir' too long, it would be ",
+ "truncated and thus not possible to use for connection to ",
+ "MariaDB Server. Set a shorter with --tmpdir=<path> option");
+ }
+
+ # copy all files from std_data into var/std_data
+ # and make them world readable
+ copytree("$glob_mysql_test_dir/std_data", "$opt_vardir/std_data", "0022");
+
+ unless($plugindir)
+ {
+ # create a plugin dir and copy or symlink plugins into it
+ if ($source_dist)
+ {
+ $plugindir="$opt_vardir/plugins";
+ # Source builds collect both client plugins and server plugins in the
+ # same directory.
+ $client_plugindir= $plugindir;
+ mkpath($plugindir);
+ if (IS_WINDOWS)
+ {
+ if (!$opt_embedded_server)
+ {
+ for (<$bindir/storage/*$multiconfig/*.dll>,
+ <$bindir/plugin/*$multiconfig/*.dll>,
+ <$bindir/libmariadb$multiconfig/*.dll>,
+ <$bindir/sql$multiconfig/*.dll>)
+ {
+ my $pname=basename($_);
+ copy rel2abs($_), "$plugindir/$pname";
+ set_plugin_var($pname);
+ }
+ }
+ }
+ else
+ {
+ my $opt_use_copy= 1;
+ if (symlink "$opt_vardir/run", "$plugindir/symlink_test")
+ {
+ $opt_use_copy= 0;
+ unlink "$plugindir/symlink_test";
+ }
+
+ for (<$bindir/storage/*$multiconfig/*.so>,
+ <$bindir/plugin/*$multiconfig/*.so>,
+ <$bindir/libmariadb/plugins/*/*.so>,
+ <$bindir/libmariadb/$multiconfig/*.so>,
+ <$bindir/sql$multiconfig/*.so>)
+ {
+ my $pname=basename($_);
+ if ($opt_use_copy)
+ {
+ copy rel2abs($_), "$plugindir/$pname";
+ }
+ else
+ {
+ symlink rel2abs($_), "$plugindir/$pname";
+ }
+ set_plugin_var($pname);
+ }
+ }
+ }
+ else
+ {
+ # hm, what paths work for debs and for rpms ?
+ for (<$bindir/lib64/mysql/plugin/*.so>,
+ <$bindir/lib/mysql/plugin/*.so>,
+ <$bindir/lib64/mariadb/plugin/*.so>,
+ <$bindir/lib/mariadb/plugin/*.so>,
+ <$bindir/lib/plugin/*.so>, # bintar
+ <$bindir/lib/plugin/*.dll>)
+ {
+ my $pname= basename($_);
+ set_plugin_var($pname);
+ $plugindir= dirname($_) unless $plugindir;
+ }
+
+ # Note: client plugins can be installed separately from server plugins,
+ # as is the case for Debian packaging.
+ for (<$bindir/lib/*/libmariadb3/plugin>)
+ {
+ $client_plugindir= $_ if <$_/*.so>;
+ }
+ $client_plugindir= $plugindir unless $client_plugindir;
+ }
+ }
+
+ # Remove old log files
+ foreach my $name (glob("r/*.progress r/*.log r/*.warnings"))
+ {
+ unlink($name);
+ }
+}
+
+
+#
+# Check if running as root
+# i.e a file can be read regardless what mode we set it to
+#
+sub check_running_as_root () {
+ my $test_file= "$opt_vardir/test_running_as_root.txt";
+ mtr_tofile($test_file, "MySQL");
+ chmod(oct("0000"), $test_file);
+
+ my $result="";
+ if (open(FILE,"<",$test_file))
+ {
+ $result= join('', <FILE>);
+ close FILE;
+ }
+
+ # Some filesystems( for example CIFS) allows reading a file
+ # although mode was set to 0000, but in that case a stat on
+ # the file will not return 0000
+ my $file_mode= (stat($test_file))[2] & 07777;
+
+ mtr_verbose("result: $result, file_mode: $file_mode");
+ if ($result eq "MySQL" && $file_mode == 0)
+ {
+ mtr_warning("running this script as _root_ will cause some " .
+ "tests to be skipped");
+ $ENV{'MYSQL_TEST_ROOT'}= "1";
+ }
+
+ chmod(oct("0755"), $test_file);
+ unlink($test_file);
+}
+
+
+sub check_ssl_support {
+ if ($opt_skip_ssl)
+ {
+ mtr_report(" - skipping SSL");
+ $opt_ssl_supported= 0;
+ $opt_ssl= 0;
+ return;
+ }
+
+ if ( ! $mysqld_variables{'ssl'} )
+ {
+ if ( $opt_ssl)
+ {
+ mtr_error("Couldn't find support for SSL");
+ return;
+ }
+ mtr_report(" - skipping SSL, mysqld not compiled with SSL");
+ $opt_ssl_supported= 0;
+ $opt_ssl= 0;
+ return;
+ }
+ mtr_report(" - SSL connections supported");
+ $opt_ssl_supported= 1;
+}
+
+sub check_debug_support {
+ if (defined $mysqld_variables{'debug-dbug'})
+ {
+ mtr_report(" - binaries are debug compiled");
+ }
+ elsif ($opt_debug_server)
+ {
+ mtr_error("Can't use --debug[-server], binary does not support it");
+ }
+}
+
+
+#
+# Helper function to find the correct value for the multiconfig
+# if it was not set explicitly.
+#
+# the configuration with the most recent build dir in sql/ is selected.
+#
+# note: looking for all BuildLog.htm files everywhere in the tree with the
+# help of File::Find would be possibly more precise, but it is also
+# many times slower. Thus we are only looking at the server, client
+# executables, and plugins - that is, something that can affect the test suite
+#
+sub fix_vs_config_dir () {
+ return $multiconfig="/$multiconfig" if $multiconfig;
+
+ my $modified = 1e30;
+ $multiconfig="";
+
+
+ for (<$bindir/sql/*/mysqld.exe>,
+ <$bindir/sql/*/mysqld>
+ ) { #/
+ if (-M $_ < $modified)
+ {
+ $modified = -M _;
+ $multiconfig = basename(dirname($_));
+ }
+ }
+
+ mtr_report("VS config: $multiconfig");
+ $multiconfig="/$multiconfig" if $multiconfig;
+}
+
+
+#
+# Helper function to handle configuration-based subdirectories which Visual
+# Studio uses for storing binaries. If multiconfig is set, this returns
+# a path based on that setting; if not, it returns paths for the default
+# /release/ and /debug/ subdirectories.
+#
+# $exe can be undefined, if the directory itself will be used
+#
+sub vs_config_dirs ($$) {
+ my ($path_part, $exe) = @_;
+
+ $exe = "" if not defined $exe;
+
+ # Don't look in these dirs when not on windows
+ return () unless IS_WINDOWS;
+
+ if ($multiconfig)
+ {
+ return ("$basedir/$path_part/$multiconfig/$exe");
+ }
+
+ return ("$basedir/$path_part/release/$exe",
+ "$basedir/$path_part/relwithdebinfo/$exe",
+ "$basedir/$path_part/debug/$exe");
+}
+
+sub mysql_server_start($) {
+ my ($mysqld, $tinfo) = @_;
+
+ if ( $mysqld->{proc} )
+ {
+ # Already started
+
+ # Write start of testcase to log file
+ mark_log($mysqld->value('log-error'), $tinfo);
+
+ return;
+ }
+
+ my $datadir= $mysqld->value('datadir');
+ if (not $opt_start_dirty)
+ {
+
+ my @options= ('log-bin', 'relay-log');
+ foreach my $option_name ( @options ) {
+ next unless $mysqld->option($option_name);
+
+ my $file_name= $mysqld->value($option_name);
+ next unless
+ defined $file_name and
+ -e $file_name;
+
+ mtr_debug(" -removing '$file_name'");
+ unlink($file_name) or die ("unable to remove file '$file_name'");
+ }
+
+ if (-d $datadir ) {
+ mtr_verbose(" - removing '$datadir'");
+ rmtree($datadir);
+ }
+ }
+
+ my $mysqld_basedir= $mysqld->value('basedir');
+ my $extra_opts= get_extra_opts($mysqld, $tinfo);
+
+ if ( $basedir eq $mysqld_basedir )
+ {
+ if (! $opt_start_dirty) # If dirty, keep possibly grown system db
+ {
+ # Some InnoDB options are incompatible with the default bootstrap.
+ # If they are used, re-bootstrap
+ my @rebootstrap_opts;
+ @rebootstrap_opts = grep {/$rebootstrap_re/o} @$extra_opts if $extra_opts;
+ if (@rebootstrap_opts)
+ {
+ mtr_verbose("Re-bootstrap with @rebootstrap_opts");
+ mysql_install_db($mysqld, undef, \@rebootstrap_opts);
+ }
+ else {
+ # Copy datadir from installed system db
+ my $path= ($opt_parallel == 1) ? "$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;
+ }
+ }
+ }
+ else
+ {
+ mysql_install_db($mysqld); # For versional testing
+
+ mtr_error("Failed to install system db to '$datadir'")
+ unless -d $datadir;
+
+ }
+
+ # Create the servers tmpdir
+ my $tmpdir= $mysqld->value('tmpdir');
+ mkpath($tmpdir) unless -d $tmpdir;
+
+ # Write start of testcase to log file
+ mark_log($mysqld->value('log-error'), $tinfo);
+
+ # Run <tname>-master.sh
+ if ($mysqld->option('#!run-master-sh') and
+ defined $tinfo->{master_sh} and
+ run_system('/bin/sh ' . $tinfo->{master_sh}) )
+ {
+ $tinfo->{'comment'}= "Failed to execute '$tinfo->{master_sh}'";
+ return 1;
+ }
+
+ # Run <tname>-slave.sh
+ if ($mysqld->option('#!run-slave-sh') and
+ defined $tinfo->{slave_sh} and
+ run_system('/bin/sh ' . $tinfo->{slave_sh}))
+ {
+ $tinfo->{'comment'}= "Failed to execute '$tinfo->{slave_sh}'";
+ return 1;
+ }
+
+ if (!$opt_embedded_server)
+ {
+ mysqld_start($mysqld, $extra_opts) or
+ mtr_error("Failed to start mysqld ".$mysqld->name()." with command "
+ . $ENV{MYSQLD_LAST_CMD});
+
+ # Save this test case information, so next can examine it
+ $mysqld->{'started_tinfo'}= $tinfo;
+ }
+
+ # If wsrep is on, we need to wait until the first
+ # server starts and bootstraps the cluster before
+ # starting other servers. The bootsrap server in the
+ # configuration should always be the first which has
+ # wsrep_on=ON
+ if (wsrep_on($mysqld) && wsrep_is_bootstrap_server($mysqld))
+ {
+ mtr_verbose("Waiting for wsrep bootstrap server to start");
+ if ($mysqld->{WAIT}->($mysqld))
+ {
+ return 1;
+ }
+ }
+}
+
+sub mysql_server_wait {
+ my ($mysqld, $tinfo) = @_;
+ my $expect_file= "$opt_vardir/tmp/".$mysqld->name().".expect";
+
+ if (!sleep_until_file_created($mysqld->value('pid-file'), $expect_file,
+ $opt_start_timeout, $mysqld->{'proc'},
+ $warn_seconds))
+ {
+ $tinfo->{comment}= "Failed to start ".$mysqld->name() . "\n";
+ return 1;
+ }
+
+ if (wsrep_on($mysqld))
+ {
+ mtr_verbose("Waiting for wsrep server " . $mysqld->name() . " to be ready");
+ if (!wait_wsrep_ready($tinfo, $mysqld))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+sub create_config_file_for_extern {
+ my %opts=
+ (
+ socket => '/tmp/mysqld.sock',
+ port => 3306,
+ user => $opt_user,
+ password => '',
+ @_
+ );
+
+ mtr_report("Creating my.cnf file for extern server...");
+ my $F= IO::File->new($path_config_file, "w")
+ or mtr_error("Can't write to $path_config_file: $!");
+
+ print $F "[client]\n";
+ while (my ($option, $value)= each( %opts )) {
+ print $F "$option= $value\n";
+ mtr_report(" $option= $value");
+ }
+
+ print $F <<EOF
+
+# binlog reads from [client] and [mysqlbinlog]
+[mysqlbinlog]
+character-sets-dir= $path_charsetsdir
+local-load= $opt_tmpdir
+
+EOF
+;
+
+ $F= undef; # Close file
+}
+
+
+#
+# Kill processes left from previous runs, normally
+# there should be none so make sure to warn
+# if there is one
+#
+sub kill_leftovers ($) {
+ my $rundir= shift;
+ return unless ( -d $rundir );
+
+ mtr_report("Checking leftover processes...");
+
+ # Scan the "run" directory for process id's to kill
+ opendir(RUNDIR, $rundir)
+ or mtr_error("kill_leftovers, can't open dir \"$rundir\": $!");
+ while ( my $elem= readdir(RUNDIR) )
+ {
+ # Only read pid from files that end with .pid
+ if ( $elem =~ /.*[.]pid$/ )
+ {
+ my $pidfile= "$rundir/$elem";
+ next unless -f $pidfile;
+ my $pid= mtr_fromfile($pidfile);
+ unlink($pidfile);
+ unless ($pid=~ /^(\d+)/){
+ # The pid was not a valid number
+ mtr_warning("Got invalid pid '$pid' from '$elem'");
+ next;
+ }
+ mtr_report(" - found old pid $pid in '$elem', killing it...");
+
+ my $ret= kill("KILL", $pid);
+ if ($ret == 0) {
+ mtr_report(" process did not exist!");
+ next;
+ }
+
+ my $check_counter= 100;
+ while ($ret > 0 and $check_counter--) {
+ mtr_milli_sleep(100);
+ $ret= kill(0, $pid);
+ }
+ mtr_report($check_counter ? " ok!" : " failed!");
+ }
+ else
+ {
+ mtr_warning("Found non pid file '$elem' in '$rundir'")
+ if -f "$rundir/$elem";
+ }
+ }
+ closedir(RUNDIR);
+}
+
+#
+# Check that all the ports that are going to
+# be used are free
+#
+sub check_ports_free ($)
+{
+ my $bthread= shift;
+ my $portbase = $bthread * $opt_port_group_size + 10000;
+ for ($portbase..$portbase+($opt_port_group_size-1)){
+ if (mtr_ping_port($_)){
+ mtr_report(" - 'localhost:$_' was not free");
+ return 0; # One port was not free
+ }
+ }
+
+ return 1; # All ports free
+}
+
+
+sub initialize_servers {
+
+ if ( using_extern() )
+ {
+ # Running against an already started server, if the specified
+ # vardir does not already exist it should be created
+ if ( ! -d $opt_vardir )
+ {
+ setup_vardir();
+ }
+ else
+ {
+ mtr_verbose("No need to create '$opt_vardir' it already exists");
+ }
+ }
+ else
+ {
+ # Kill leftovers from previous run
+ # using any pidfiles found in var/run
+ kill_leftovers("$opt_vardir/run");
+
+ if ( ! $opt_start_dirty )
+ {
+ remove_stale_vardir();
+ setup_vardir();
+ }
+ }
+}
+
+
+#
+# Remove all newline characters expect after semicolon
+#
+sub sql_to_bootstrap {
+ my ($sql) = @_;
+ my @lines= split(/\R/, $sql);
+ my $result= "\n";
+ my $delimiter= ';';
+
+ foreach my $line (@lines) {
+
+ # Change current delimiter if line starts with "delimiter"
+ if ( $line =~ /^delimiter (.*)/ ) {
+ my $new= $1;
+ # Remove old delimiter from end of new
+ $new=~ s/\Q$delimiter\E$//;
+ $delimiter = $new;
+ mtr_debug("changed delimiter to $delimiter");
+ # No need to add the delimiter to result
+ next;
+ }
+
+ # Add newline if line ends with $delimiter
+ # and convert the current delimiter to semicolon
+ if ( $line =~ /\Q$delimiter\E$/ ){
+ $line =~ s/\Q$delimiter\E$/;/;
+ $result.= "$line\n";
+ mtr_debug("Added default delimiter");
+ next;
+ }
+
+ # Remove comments starting with --
+ if ( $line =~ /^\s*--/ ) {
+ mtr_debug("Discarded $line");
+ next;
+ }
+
+ # Replace @HOSTNAME with localhost
+ $line=~ s/\'\@HOSTNAME\@\'/localhost/;
+
+ # Default, just add the line without newline
+ # but with a space as separator
+ $result.= "$line ";
+
+ }
+ return $result;
+}
+
+
+sub default_mysqld {
+ # Generate new config file from template
+ environment_setup();
+ my $config= My::ConfigFactory->new_config
+ ( {
+ basedir => $basedir,
+ testdir => $glob_mysql_test_dir,
+ template_path => "include/default_my.cnf",
+ vardir => $opt_vardir,
+ tmpdir => $opt_tmpdir,
+ baseport => 0,
+ user => $opt_user,
+ password => '',
+ }
+ );
+
+ my $mysqld= $config->group('mysqld.1')
+ or mtr_error("Couldn't find mysqld.1 in default config");
+ return $mysqld;
+}
+
+
+sub mysql_install_db {
+ my ($mysqld, $datadir, $extra_opts)= @_;
+
+ my $install_datadir= $datadir || $mysqld->value('datadir');
+ my $install_basedir= $mysqld->value('basedir');
+ my $install_lang= $mysqld->value('lc-messages-dir');
+ my $install_chsdir= $mysqld->value('character-sets-dir');
+
+ mtr_report("Installing system database...");
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--no-defaults");
+ mtr_add_arg($args, "--disable-getopt-prefix-matching");
+ mtr_add_arg($args, "--bootstrap");
+ mtr_add_arg($args, "--basedir=%s", $install_basedir);
+ mtr_add_arg($args, "--datadir=%s", $install_datadir);
+ mtr_add_arg($args, "--plugin-dir=%s", $plugindir);
+ mtr_add_arg($args, "--default-storage-engine=myisam");
+ mtr_add_arg($args, "--loose-skip-plugin-$_") for @optional_plugins;
+ # starting from 10.0 bootstrap scripts require InnoDB
+ mtr_add_arg($args, "--loose-innodb");
+ mtr_add_arg($args, "--loose-innodb-log-file-size=10M");
+ mtr_add_arg($args, "--disable-sync-frm");
+ mtr_add_arg($args, "--tmpdir=%s", "$opt_vardir/tmp/");
+ mtr_add_arg($args, "--core-file");
+ mtr_add_arg($args, "--console");
+ mtr_add_arg($args, "--character-set-server=latin1");
+
+ if ( $opt_debug )
+ {
+ mtr_add_arg($args, "--debug-dbug=$debug_d:t:i:A,%s/log/bootstrap.trace",
+ $path_vardir_trace);
+ }
+
+ mtr_add_arg($args, "--lc-messages-dir=%s", $install_lang);
+ mtr_add_arg($args, "--character-sets-dir=%s", $install_chsdir);
+
+ # InnoDB arguments that affect file location and sizes may
+ # need to be given to the bootstrap process as well as the
+ # server process.
+ foreach my $extra_opt ( @opt_extra_mysqld_opt ) {
+ if ($extra_opt =~ /--innodb/) {
+ mtr_add_arg($args, $extra_opt);
+ }
+ }
+
+ # If DISABLE_GRANT_OPTIONS is defined when the server is compiled (e.g.,
+ # configure --disable-grant-options), mysqld will not recognize the
+ # --bootstrap or --skip-grant-tables options. The user can set
+ # MYSQLD_BOOTSTRAP to the full path to a mysqld which does accept
+ # --bootstrap, to accommodate this.
+ my $exe_mysqld_bootstrap =
+ $ENV{'MYSQLD_BOOTSTRAP'} || find_mysqld($install_basedir);
+
+ # ----------------------------------------------------------------------
+ # export MYSQLD_BOOTSTRAP_CMD variable containing <path>/mysqld <args>
+ # ----------------------------------------------------------------------
+ $ENV{'MYSQLD_BOOTSTRAP_CMD'}= "$exe_mysqld_bootstrap " . join(" ", @$args)
+ unless defined $ENV{'MYSQLD_BOOTSTRAP_CMD'};
+
+ # Extra options can come not only from the command line, but also
+ # from option files or combinations. We want them on a command line
+ # that is executed now, because otherwise the datadir might be
+ # incompatible with the test settings, but not on the general
+ # $MYSQLD_BOOTSTRAP_CMD line
+ foreach my $extra_opt ( @$extra_opts ) {
+ mtr_add_arg($args, $extra_opt);
+ }
+
+ # ----------------------------------------------------------------------
+ # Create the bootstrap.sql file
+ # ----------------------------------------------------------------------
+ my $bootstrap_sql_file= "$opt_vardir/log/bootstrap.sql";
+ $ENV{'MYSQL_BOOTSTRAP_SQL_FILE'}= $bootstrap_sql_file;
+
+ if (! -e $bootstrap_sql_file)
+ {
+ My::Debugger::setup_boot_args(\$args, \$exe_mysqld_bootstrap, $bootstrap_sql_file);
+
+ my $path_sql= my_find_file($install_basedir,
+ ["mysql", "sql/share", "share/mariadb",
+ "share/mysql", "share", "scripts"],
+ "mysql_system_tables.sql",
+ NOT_REQUIRED);
+
+ if (-f $path_sql )
+ {
+ my $sql_dir= dirname($path_sql);
+ # Use the mysql database for system tables
+ mtr_tofile($bootstrap_sql_file, "use mysql;\n");
+
+ # Add the offical mysql system tables
+ # for a production system
+ mtr_appendfile_to_file("$sql_dir/mysql_system_tables.sql",
+ $bootstrap_sql_file);
+
+ my $gis_sp_path = $source_dist ? "$bindir/scripts" : $sql_dir;
+ mtr_appendfile_to_file("$gis_sp_path/maria_add_gis_sp_bootstrap.sql",
+ $bootstrap_sql_file);
+
+ # Add the performance tables
+ # for a production system
+ mtr_appendfile_to_file("$sql_dir/mysql_performance_tables.sql",
+ $bootstrap_sql_file);
+
+ # Add the mysql system tables initial data
+ # for a production system
+ mtr_appendfile_to_file("$sql_dir/mysql_system_tables_data.sql",
+ $bootstrap_sql_file);
+
+ # Add test data for timezone - this is just a subset, on a real
+ # system these tables will be populated either by mysql_tzinfo_to_sql
+ # or by downloading the timezone table package from our website
+ mtr_appendfile_to_file("$sql_dir/mysql_test_data_timezone.sql",
+ $bootstrap_sql_file);
+
+ # Fill help tables, just an empty file when running from bk repo
+ # but will be replaced by a real fill_help_tables.sql when
+ # building the source dist
+ mtr_appendfile_to_file("$sql_dir/fill_help_tables.sql",
+ $bootstrap_sql_file);
+
+ # Append sys schema
+ mtr_appendfile_to_file("$gis_sp_path/mysql_sys_schema.sql",
+ $bootstrap_sql_file);
+
+ mtr_tofile($bootstrap_sql_file, "CREATE DATABASE IF NOT EXISTS test CHARACTER SET latin1 COLLATE latin1_swedish_ci;\n");
+
+ # mysql.gtid_slave_pos was created in InnoDB, but many tests
+ # run without InnoDB. Alter it to Aria now
+ mtr_tofile($bootstrap_sql_file, "ALTER TABLE gtid_slave_pos ENGINE=Aria transactional=0;\n");
+ }
+ else
+ {
+ # Install db from init_db.sql that exist in early 5.1 and 5.0
+ # versions of MySQL
+ my $init_file= "$install_basedir/mysql-test/lib/init_db.sql";
+ mtr_report(" - from '$init_file'");
+ my $text= mtr_grab_file($init_file) or
+ mtr_error("Can't open '$init_file': $!");
+
+ mtr_tofile($bootstrap_sql_file,
+ sql_to_bootstrap($text));
+ }
+
+ # Remove anonymous users
+ mtr_tofile($bootstrap_sql_file,
+ "DELETE FROM mysql.global_priv where user= '';\n");
+
+ # Create mtr database
+ mtr_tofile($bootstrap_sql_file,
+ "CREATE DATABASE mtr CHARSET=latin1;\n");
+
+ # Add help tables and data for warning detection and supression
+ mtr_tofile($bootstrap_sql_file,
+ sql_to_bootstrap(mtr_grab_file("include/mtr_warnings.sql")));
+
+ # Add procedures for checking server is restored after testcase
+ mtr_tofile($bootstrap_sql_file,
+ sql_to_bootstrap(mtr_grab_file("include/mtr_check.sql")));
+ }
+
+ # Log bootstrap command
+ my $path_bootstrap_log= "$opt_vardir/log/bootstrap.log";
+ mtr_tofile($path_bootstrap_log,
+ "$exe_mysqld_bootstrap " . join(" ", @$args) . "\n");
+
+ # Create directories mysql
+ mkpath("$install_datadir/mysql");
+
+ my $realtime= gettimeofday();
+ if ( My::SafeProcess->run
+ (
+ name => "bootstrap",
+ path => $exe_mysqld_bootstrap,
+ args => \$args,
+ input => $bootstrap_sql_file,
+ output => $path_bootstrap_log,
+ error => $path_bootstrap_log,
+ append => 1,
+ verbose => $opt_verbose,
+ ) != 0)
+ {
+ find(
+ {
+ no_chdir => 1,
+ wanted => sub
+ {
+ My::CoreDump::core_wanted(\$num_saved_cores,
+ $opt_max_save_core,
+ @opt_cases == 0,
+ $exe_mysqld_bootstrap, $opt_parallel);
+ }
+ },
+ $install_datadir);
+
+ my $data= mtr_grab_file($path_bootstrap_log);
+ mtr_error("Error executing mariadbd --bootstrap\n" .
+ "Could not install system database from $bootstrap_sql_file\n" .
+ "The $path_bootstrap_log file contains:\n$data\n");
+ }
+ else
+ {
+ mtr_verbose("Spent " . sprintf("%.3f", (gettimeofday() - $realtime)) . " seconds in bootstrap");
+ }
+}
+
+
+sub run_testcase_check_skip_test($)
+{
+ my ($tinfo)= @_;
+
+ # ----------------------------------------------------------------------
+ # Skip some tests silently
+ # ----------------------------------------------------------------------
+
+ my $start_from= $mtr_cases::start_from;
+ if ( $start_from )
+ {
+ if ($tinfo->{'name'} eq $start_from ||
+ $tinfo->{'shortname'} eq $start_from)
+ {
+ ## Found parting test. Run this test and all tests after this one
+ $mtr_cases::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
+ # with the mysqltest program tells us so.
+ # ----------------------------------------------------------------------
+
+ if ( $tinfo->{'skip'} )
+ {
+ mtr_report_test_skipped($tinfo) unless $start_only;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+sub run_query {
+ my ($tinfo, $mysqld, $query)= @_;
+
+ my $args;
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+
+ mtr_add_arg($args, "-e %s", $query);
+
+ my $res= My::SafeProcess->run
+ (
+ name => "run_query -> ".$mysqld->name(),
+ path => $exe_mysql,
+ args => \$args,
+ output => '/dev/null',
+ error => '/dev/null'
+ );
+
+ return $res
+}
+
+
+sub do_before_run_mysqltest($)
+{
+ my $tinfo= shift;
+ my $resfile= $tinfo->{result_file};
+ return unless defined $resfile;
+
+ # Remove old files produced by mysqltest
+ die "unsupported result file name $resfile, stoping" unless
+ $resfile =~ /^(.*?)((?:,\w+)*)\.(rdiff|result|result~)$/;
+ my ($base_file, $suites, $ext)= ($1, $2, $3);
+ # if the result file is a diff, make a proper result file
+ if ($ext eq 'rdiff') {
+ my $base_result = $tinfo->{base_result};
+ my $resdir= dirname($resfile);
+ # we'll use a separate extension for generated result files
+ # to be able to distinguish them from manually created
+ # version-controlled results, and to ignore them in git.
+ my $dest = "$base_file$suites.result~";
+ my @cmd = ($exe_patch);
+ if ($^O eq "MSWin32") {
+ push @cmd, '--binary';
+ }
+ push @cmd, (qw/-r - -f -s -o/, $dest . $$, $base_result, $resfile);
+ if (-w $resdir) {
+ # don't rebuild a file if it's up to date
+ unless (-e $dest and -M $dest < -M $resfile
+ and -M $dest < -M $base_result) {
+ run_system(@cmd);
+ rename $cmd[-3], $dest or unlink $cmd[-3];
+ }
+ } else {
+ $dest = $opt_tmpdir . '/' . basename($dest);
+ $cmd[-3] = $dest . $$;
+ run_system(@cmd);
+ rename $cmd[-3], $dest or unlink $cmd[-3];
+ }
+
+ $tinfo->{result_file} = $dest;
+ }
+
+ unlink("$base_file.reject");
+ unlink("$base_file.progress");
+ unlink("$base_file.log");
+ unlink("$base_file.warnings");
+}
+
+
+#
+# Check all server for sideffects
+#
+# RETURN VALUE
+# 0 ok
+# 1 Check failed
+# >1 Fatal errro
+
+sub check_testcase($$)
+{
+ my ($tinfo, $mode)= @_;
+ my $tname= $tinfo->{name};
+
+ # Start the mysqltest processes in parallel to save time
+ # also makes it possible to wait for any process to exit during the check
+ my %started;
+ foreach my $mysqld ( mysqlds() )
+ {
+ # Skip if server has been restarted with additional options
+ if ( defined $mysqld->{'proc'} && ! exists $mysqld->{'restart_opts'} )
+ {
+ my $proc= start_check_testcase($tinfo, $mode, $mysqld);
+ $started{$proc->pid()}= $proc;
+ }
+ }
+
+ # Return immediately if no check proceess was started
+ return 0 unless ( keys %started );
+
+ my $timeout= start_timer(check_timeout($tinfo));
+
+ while (1){
+ my $result;
+ my $proc= My::SafeProcess->wait_any_timeout($timeout);
+ mtr_report("Got $proc");
+
+ if ( delete $started{$proc->pid()} ) {
+
+ my $err_file= $proc->user_data();
+ my $base_file= mtr_match_extension($err_file, "err"); # Trim extension
+
+ # One check testcase process returned
+ my $res= $proc->exit_status();
+
+ if ( $res == 0){
+ # Check completed without problem
+
+ # Remove the .err file the check generated
+ unlink($err_file);
+
+ # Remove the .result file the check generated
+ if ( $mode eq 'after' ){
+ unlink("$base_file.result");
+ }
+
+ if ( keys(%started) == 0){
+ # All checks completed
+ mark_time_used('check');
+ return 0;
+ }
+ # Wait for next process to exit
+ next;
+ }
+ else
+ {
+ if ( $mode eq "after" and $res == 1 )
+ {
+ # Test failed, grab the report mysqltest has created
+ my $report= mtr_grab_file($err_file);
+ $tinfo->{check}.=
+ "\nMTR's internal check of the test case '$tname' failed.
+This means that the test case does not preserve the state that existed
+before the test case was executed. Most likely the test case did not
+do a proper clean-up. It could also be caused by the previous test run
+by this thread, if the server wasn't restarted.
+This is the diff of the states of the servers before and after the
+test case was executed:\n";
+ $tinfo->{check}.= $report;
+
+ # Check failed, mark the test case with that info
+ $tinfo->{'check_testcase_failed'}= 1;
+ $result= 1;
+ }
+ elsif ( $res )
+ {
+ my $report= mtr_grab_file($err_file);
+ $tinfo->{comment}.=
+ "Could not execute 'check-testcase' $mode ".
+ "testcase '$tname' (res: $res):\n";
+ $tinfo->{comment}.= $report;
+
+ $result= 2;
+ }
+ else
+ {
+ # Remove the .result file the check generated
+ unlink("$base_file.result");
+ }
+ # Remove the .err file the check generated
+ unlink($err_file);
+
+ }
+ }
+ elsif ( $proc->{timeout} ) {
+ $tinfo->{comment}.= "Timeout for 'check-testcase' expired after "
+ .check_timeout($tinfo)." seconds";
+ $result= 4;
+ }
+ else {
+ # Unknown process returned, most likley a crash, abort everything
+ $tinfo->{comment}=
+ "The server $proc crashed while running ".
+ "'check testcase $mode test'".
+ get_log_from_proc($proc, $tinfo->{name});
+ $result= 3;
+ }
+
+ # Kill any check processes still running
+ map($_->kill(), values(%started));
+
+ mtr_warning("Check-testcase failed, this could also be caused by the" .
+ " previous test run by this worker thread")
+ if $result > 1 && $mode eq "before";
+ mark_time_used('check');
+
+ return $result;
+ }
+
+ mtr_error("INTERNAL_ERROR: check_testcase");
+}
+
+
+# Start run mysqltest on one server
+#
+# RETURN VALUE
+# 0 OK
+# 1 Check failed
+#
+sub start_run_one ($$) {
+ my ($mysqld, $run)= @_;
+
+ my $name= "$run-".$mysqld->name();
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+
+ mtr_add_arg($args, "--silent");
+ mtr_add_arg($args, "--test-file=%s", "include/$run.test");
+
+ my $errfile= "$opt_vardir/tmp/$name.err";
+ my $proc= My::SafeProcess->new
+ (
+ name => $name,
+ path => $exe_mysqltest,
+ error => $errfile,
+ output => $errfile,
+ args => \$args,
+ user_data => $errfile,
+ verbose => $opt_verbose,
+ );
+ mtr_verbose("Started $proc");
+ return $proc;
+}
+
+
+#
+# Run script on all servers, collect results
+#
+# RETURN VALUE
+# 0 ok
+# 1 Failure
+
+sub run_on_all($$)
+{
+ my ($tinfo, $run)= @_;
+
+ # Start the mysqltest processes in parallel to save time
+ # also makes it possible to wait for any process to exit during the check
+ # and to have a timeout process
+ my %started;
+ foreach my $mysqld ( mysqlds() )
+ {
+ if ( defined $mysqld->{'proc'} )
+ {
+ my $proc= start_run_one($mysqld, $run);
+ $started{$proc->pid()}= $proc;
+ }
+ }
+
+ # Return immediately if no check proceess was started
+ return 0 unless ( keys %started );
+
+ my $timeout= start_timer(check_timeout($tinfo));
+
+ while (1){
+ my $result;
+ my $proc= My::SafeProcess->wait_any_timeout($timeout);
+ mtr_report("Got $proc");
+
+ if ( delete $started{$proc->pid()} ) {
+
+ # One mysqltest process returned
+ my $err_file= $proc->user_data();
+ my $res= $proc->exit_status();
+
+ # Append the report from .err file
+ $tinfo->{comment}.= " == $err_file ==\n";
+ $tinfo->{comment}.= mtr_grab_file($err_file);
+ $tinfo->{comment}.= "\n";
+
+ # Remove the .err file
+ unlink($err_file);
+
+ if ( keys(%started) == 0){
+ # All completed
+ return 0;
+ }
+
+ # Wait for next process to exit
+ next;
+ }
+ elsif ($proc->{timeout}) {
+ $tinfo->{comment}.= "Timeout for '$run' expired after "
+ .check_timeout($tinfo)." seconds";
+ }
+ else {
+ # Unknown process returned, most likley a crash, abort everything
+ $tinfo->{comment}.=
+ "The server $proc crashed while running '$run'".
+ get_log_from_proc($proc, $tinfo->{name});
+ }
+
+ # Kill any check processes still running
+ map($_->kill(), values(%started));
+
+ return 1;
+ }
+ mtr_error("INTERNAL_ERROR: run_on_all");
+}
+
+
+sub mark_log {
+ my ($log, $tinfo)= @_;
+ my $log_msg= "CURRENT_TEST: $tinfo->{name}\n";
+ pre_write_errorlog($log, $tinfo->{name});
+ mtr_tofile($log, $log_msg);
+}
+
+
+sub find_testcase_skipped_reason($)
+{
+ my ($tinfo)= @_;
+
+ # Set default message
+ $tinfo->{'comment'}= "Detected by testcase(no log file)";
+
+ # Open the test log file
+ my $F= IO::File->new($path_current_testlog)
+ or return;
+ my $reason;
+
+ while ( my $line= <$F> )
+ {
+ # Look for "reason: <reason for skipping test>"
+ if ( $line =~ /reason: (.*)/ )
+ {
+ $reason= $1;
+ }
+ }
+
+ if ( ! $reason )
+ {
+ mtr_warning("Could not find reason for skipping test in $path_current_testlog");
+ $reason= "Detected by testcase(reason unknown) ";
+ }
+ $tinfo->{'comment'}= $reason;
+}
+
+
+sub find_analyze_request
+{
+ # Open the test log file
+ my $F= IO::File->new($path_current_testlog)
+ or return;
+ my $analyze;
+
+ while ( my $line= <$F> )
+ {
+ # Look for "reason: <reason for skipping test>"
+ if ( $line =~ /analyze: (.*)/ )
+ {
+ $analyze= $1;
+ }
+ }
+
+ return $analyze;
+}
+
+# The test can leave a file in var/tmp/ to signal
+# that all servers should be restarted
+sub restart_forced_by_test($)
+{
+ my $file = shift;
+ my $restart = 0;
+ foreach my $mysqld ( mysqlds() )
+ {
+ my $datadir = $mysqld->value('datadir');
+ my $force_restart_file = "$datadir/mtr/$file";
+ if ( -f $force_restart_file )
+ {
+ mtr_verbose("Restart of servers forced by test");
+ $restart = 1;
+ last;
+ }
+ }
+ return $restart;
+}
+
+# Return timezone value of tinfo or default value
+sub timezone {
+ my ($tinfo)= @_;
+ local $_ = $tinfo->{timezone};
+ return 'DEFAULT' unless defined $_;
+ no warnings 'uninitialized';
+ s/\$\{(\w+)\}/$ENV{$1}/ge;
+ s/\$(\w+)/$ENV{$1}/ge;
+ $_;
+}
+
+sub mycnf_create {
+ my ($config) = @_;
+ my $res;
+
+ foreach my $group ($config->option_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";
+ }
+ $res;
+}
+
+sub config_files($) {
+ my ($tinfo) = @_;
+ (
+ 'my.cnf' => \&mycnf_create,
+ $tinfo->{suite}->config_files()
+ );
+}
+
+sub _like { return $config ? $config->like($_[0]) : (); }
+sub mysqlds { return _like('mysqld\.'); }
+
+sub fix_servers($) {
+ my ($tinfo) = @_;
+ return () unless $config;
+ my %servers = (
+ qr/mysqld\./ => {
+ SORT => 300,
+ START => \&mysql_server_start,
+ WAIT => \&mysql_server_wait,
+ },
+ $tinfo->{suite}->servers()
+ );
+ for ($config->groups()) {
+ while (my ($re,$prop) = each %servers) {
+ @$_{keys %$prop} = values %$prop if $_->{name} =~ /^$re/;
+ }
+ }
+}
+
+sub all_servers {
+ return unless $config;
+ ( sort { $a->{SORT} <=> $b->{SORT} }
+ grep { defined $_->{SORT} } $config->groups() );
+}
+
+# Storage for changed environment variables
+our %old_env;
+
+sub resfile_report_test ($) {
+ my $tinfo= shift;
+
+ resfile_new_test();
+
+ resfile_test_info("name", $tinfo->{name});
+ resfile_test_info("variation", $tinfo->{combination})
+ if $tinfo->{combination};
+ resfile_test_info("start_time", isotime time);
+}
+
+
+#
+# Run a single test case
+#
+# RETURN VALUE
+# 0 OK
+# > 0 failure
+#
+
+sub run_testcase ($$) {
+ my ($tinfo, $server_socket)= @_;
+ my $print_freq=20;
+
+ mtr_verbose("Running test:", $tinfo->{name});
+ $ENV{'MTR_TEST_NAME'} = $tinfo->{name};
+ resfile_report_test($tinfo) if $opt_resfile;
+
+ for my $key (grep { /^MTR_COMBINATION/ } keys %ENV)
+ {
+ delete $ENV{$key};
+ }
+
+ if (ref $tinfo->{combinations} eq 'ARRAY')
+ {
+ for (my $i = 0; $i < @{$tinfo->{combinations}}; ++$i )
+ {
+ my $combination = $tinfo->{combinations}->[$i];
+ # Allow only alphanumerics plus _ - + . in combination names,
+ # or anything beginning with -- (the latter comes from --combination)
+ if ($combination && $combination !~ /^\w[-\w\.\+]*$/
+ && $combination !~ /^--/)
+ {
+ mtr_error("Combination '$combination' contains illegal characters");
+ }
+ $ENV{"MTR_COMBINATION_". uc(${combination})} = 1;
+ }
+ $ENV{"MTR_COMBINATIONS"} = join(',', @{$tinfo->{combinations}});
+ }
+ elsif (exists $tinfo->{combinations})
+ {
+ die 'Unexpected type of $tinfo->{combinations}';
+ }
+
+ # -------------------------------------------------------
+ # Init variables that can change between each test case
+ # -------------------------------------------------------
+ my $timezone= timezone($tinfo);
+ if ($timezone ne 'DEFAULT') {
+ $ENV{'TZ'}= $timezone;
+ } else {
+ delete($ENV{'TZ'});
+ }
+ $ENV{MTR_SUITE_DIR} = $tinfo->{suite}->{dir};
+ mtr_verbose("Setting timezone: $timezone");
+
+ if ( ! using_extern() )
+ {
+ my @restart= servers_need_restart($tinfo);
+ if ( @restart != 0) {
+ # Remember that we restarted for this test case (count restarts)
+ $tinfo->{'restarted'}= 1;
+ stop_servers(reverse @restart);
+ if ($opt_warnings) {
+ check_warnings_post_shutdown($server_socket);
+ }
+ }
+
+ if ( started(all_servers()) == 0 )
+ {
+
+ # Remove old datadirs
+ clean_datadir() unless $opt_start_dirty;
+
+ # Restore old ENV
+ while (my ($option, $value)= each( %old_env )) {
+ if (defined $value){
+ mtr_verbose("Restoring $option to $value");
+ $ENV{$option}= $value;
+
+ } else {
+ mtr_verbose("Removing $option");
+ delete($ENV{$option});
+ }
+ }
+ %old_env= ();
+
+ mtr_verbose("Generating my.cnf from '$tinfo->{template_path}'");
+
+ # Generate new config file from template
+ $config= My::ConfigFactory->new_config
+ ( {
+ testname => $tinfo->{name},
+ basedir => $basedir,
+ testdir => $glob_mysql_test_dir,
+ template_path => $tinfo->{template_path},
+ extra_template_path => $tinfo->{extra_template_path},
+ vardir => $opt_vardir,
+ tmpdir => $opt_tmpdir,
+ baseport => $baseport,
+ user => $opt_user,
+ password => '',
+ ssl => $opt_ssl_supported,
+ embedded => $opt_embedded_server,
+ }
+ );
+
+ fix_servers($tinfo);
+
+ # Write config files:
+ my %config_files = config_files($tinfo);
+ while (my ($file, $generate) = each %config_files) {
+ next unless $generate;
+ my ($path) = "$opt_vardir/$file";
+ open (F, '>', $path) or die "Could not open '$path': $!";
+ print F &$generate($config);
+ close F;
+ }
+
+ # Remember current config so a restart can occur when a test need
+ # to use a different one
+ $current_config_name= $tinfo->{template_path};
+
+ #
+ # Set variables in the ENV section
+ #
+ foreach my $option ($config->options_in_group("ENV"))
+ {
+ my ($name, $val)= ($option->name(), $option->value());
+
+ # Save old value to restore it before next time
+ $old_env{$name}= $ENV{$name};
+
+ unless (defined $val) {
+ mtr_warning("Uninitialized value for ", $name,
+ ", group [ENV], file ", $current_config_name);
+ } else {
+ mtr_verbose($name, "=", $val);
+ $ENV{$name}= $val;
+ }
+ }
+ }
+
+ # Write start of testcase to log
+ mark_log($path_current_testlog, $tinfo);
+
+ # Make sure the safe_process also exits from now on
+ if ($opt_start_exit) {
+ My::SafeProcess->start_exit();
+ }
+
+ if (start_servers($tinfo))
+ {
+ report_failure_and_restart($tinfo);
+ unlink $path_current_testlog;
+ return 1;
+ }
+ }
+ mark_time_used('restart');
+
+ # --------------------------------------------------------------------
+ # If --start or --start-dirty given, stop here to let user manually
+ # run tests
+ # If --wait-all is also given, do the same, but don't die if one
+ # server exits
+ # ----------------------------------------------------------------------
+
+ if ( $start_only )
+ {
+ mtr_print("\nStarted", started(all_servers()));
+ mtr_print("Using config for test", $tinfo->{name});
+ mtr_print("Port and socket path for server(s):");
+ foreach my $mysqld ( mysqlds() )
+ {
+ mtr_print ($mysqld->name() . " " . $mysqld->value('port') .
+ " " . $mysqld->value('socket'));
+ }
+ if ( $opt_start_exit )
+ {
+ mtr_print("Server(s) started, not waiting for them to finish");
+ if (IS_WINDOWS)
+ {
+ POSIX::_exit(0); # exit hangs here in ActiveState Perl
+ }
+ else
+ {
+ exit(0);
+ }
+ }
+ mtr_print("Waiting for server(s) to exit...");
+ if ( $opt_wait_all ) {
+ My::SafeProcess->wait_all();
+ mtr_print( "All servers exited" );
+ exit(1);
+ }
+ else {
+ my $proc= My::SafeProcess->wait_any();
+ if ( grep($proc eq $_, started(all_servers())) )
+ {
+ mtr_print("Server $proc died");
+ exit(1);
+ }
+ mtr_print("Unknown process $proc died");
+ exit(1);
+ }
+ }
+
+ my $test_timeout= start_timer(testcase_timeout($tinfo));
+
+ do_before_run_mysqltest($tinfo);
+
+ mark_time_used('admin');
+
+ if ( $opt_check_testcases and check_testcase($tinfo, "before") ){
+ # Failed to record state of server or server crashed
+ report_failure_and_restart($tinfo);
+
+ return 1;
+ }
+
+ my $test= $tinfo->{suite}->start_test($tinfo);
+ # Set to a list of processes we have to keep waiting (expectedly died servers)
+ my %keep_waiting_proc = ();
+ my $print_timeout= start_timer($print_freq * 60);
+
+ while (1)
+ {
+ my $proc;
+ if (%keep_waiting_proc)
+ {
+ # Any other process exited?
+ $proc = My::SafeProcess->check_any();
+ if ($proc)
+ {
+ mtr_verbose ("Found exited process $proc");
+ }
+ else
+ {
+ # Also check if timer has expired, if so cancel waiting
+ if ( has_expired($test_timeout) )
+ {
+ %keep_waiting_proc = ();
+ }
+ }
+ }
+ if (!%keep_waiting_proc && !$proc)
+ {
+ if ($test_timeout > $print_timeout)
+ {
+ $proc= My::SafeProcess->wait_any_timeout($print_timeout);
+ if ($proc->{timeout})
+ {
+ #print out that the test is still on
+ mtr_print("Test still running: $tinfo->{name}");
+ #reset the timer
+ $print_timeout= start_timer($print_freq * 60);
+ next;
+ }
+ }
+ else
+ {
+ $proc= My::SafeProcess->wait_any_timeout($test_timeout);
+ }
+ }
+
+ if ($proc and $proc eq $test) # mysqltest itself exited
+ {
+ my $res= $test->exit_status();
+
+ if (($res == 0 or $res == 62) and $opt_warnings and check_warnings($tinfo) )
+ {
+ # If test case suceeded, but it has produced unexpected
+ # warnings, continue with $res == 1;
+ # but if the test was skipped, it should remain skipped
+ $res= 1 if $res == 0;
+ resfile_output($tinfo->{'warnings'}) if $opt_resfile;
+ }
+
+ if ( $res == 0 )
+ {
+ my $check_res;
+ if ( restart_forced_by_test('force_restart') )
+ {
+ stop_all_servers($opt_shutdown_timeout);
+ }
+ elsif ( $opt_check_testcases and
+ $check_res= check_testcase($tinfo, "after"))
+ {
+ if ($check_res == 1) {
+ # Test case had sideeffects, not fatal error, just continue
+ if ($opt_warnings) {
+ # Checking error logs for warnings, so need to stop server
+ # gracefully so that memory leaks etc. can be properly detected.
+ stop_servers(reverse all_servers());
+ check_warnings_post_shutdown($server_socket);
+ # Even if we got warnings here, we should not fail this
+ # particular test, as the warnings may be caused by an earlier
+ # test.
+ } else {
+ # Not checking warnings, so can do a hard shutdown.
+ stop_all_servers($opt_shutdown_timeout);
+ }
+ mtr_report("Resuming tests...\n");
+ resfile_output($tinfo->{'check'}) if $opt_resfile;
+ }
+ else {
+ # Test case check failed fatally, probably a server crashed
+ report_failure_and_restart($tinfo);
+ return 1;
+ }
+ }
+ mtr_report_test_passed($tinfo);
+ }
+ elsif ( $res == 62 )
+ {
+ # Testcase itself tell us to skip this one
+ $tinfo->{skip_detected_by_test}= 1;
+ # Try to get reason from test log file
+ find_testcase_skipped_reason($tinfo);
+ mtr_report_test_skipped($tinfo);
+ # Restart if skipped due to missing perl, it may have had side effects
+ if ( restart_forced_by_test('force_restart_if_skipped') ||
+ $tinfo->{'comment'} =~ /^perl not found/ )
+ {
+ stop_all_servers($opt_shutdown_timeout);
+ }
+ }
+ elsif ( $res == 65 )
+ {
+ # Testprogram killed by signal
+ $tinfo->{comment}=
+ "testprogram crashed(returned code $res)";
+ report_failure_and_restart($tinfo);
+ }
+ elsif ( $res == 1 )
+ {
+ # Check if the test tool requests that
+ # an analyze script should be run
+ my $analyze= find_analyze_request();
+ if ($analyze){
+ run_on_all($tinfo, "analyze-$analyze");
+ }
+
+ # Wait a bit and see if a server died, if so report that instead
+ mtr_milli_sleep(100);
+ my $srvproc= My::SafeProcess::check_any();
+ if ($srvproc && grep($srvproc eq $_, started(all_servers()))) {
+ $proc= $srvproc;
+ goto SRVDIED;
+ }
+
+ # Test case failure reported by mysqltest
+ report_failure_and_restart($tinfo);
+ }
+ else
+ {
+ # mysqltest failed, probably crashed
+ $tinfo->{comment}=
+ "mysqltest failed with unexpected return code $res\n";
+ report_failure_and_restart($tinfo);
+ }
+
+ # Save info from this testcase run to mysqltest.log
+ if( -f $path_current_testlog)
+ {
+ if ($opt_resfile && $res && $res != 62) {
+ resfile_output_file($path_current_testlog);
+ }
+ mtr_appendfile_to_file($path_current_testlog, $path_testlog);
+ unlink($path_current_testlog);
+ }
+
+ return ($res == 62) ? 0 : $res;
+ } # if ($proc and $proc eq $test)
+ elsif ($proc)
+ {
+ # It was not mysqltest that exited, add to a wait-to-be-started-again list.
+ $keep_waiting_proc{$proc} = 1;
+ }
+
+ mtr_verbose("Got " . join(",", keys(%keep_waiting_proc)));
+
+ mark_time_used('test');
+ foreach my $wait_for_proc (keys(%keep_waiting_proc)) {
+ # ----------------------------------------------------
+ # Check if it was an expected crash
+ # ----------------------------------------------------
+ my @mysqld = grep($wait_for_proc eq $_->{proc}, mysqlds());
+ goto SRVDIED unless @mysqld;
+ my $check_crash = check_expected_crash_and_restart($mysqld[0]);
+ if ($check_crash == 0) # unexpected exit/crash of $wait_for_proc
+ {
+ $proc= $mysqld[0]->{proc};
+ goto SRVDIED;
+ }
+ elsif ($check_crash == 1) # $wait_for_proc was started again by check_expected_crash_and_restart()
+ {
+ delete $keep_waiting_proc{$wait_for_proc};
+ }
+ elsif ($check_crash == 2) # we must keep waiting
+ {
+ # do nothing
+ }
+ } # foreach my $wait_for_proc
+
+ next;
+
+ SRVDIED:
+ # ----------------------------------------------------
+ # Stop the test case timer
+ # ----------------------------------------------------
+ $test_timeout= 0;
+
+ # ----------------------------------------------------
+ # Check if it was a server that died
+ # ----------------------------------------------------
+ if ( grep($proc eq $_, started(all_servers())) )
+ {
+ # Server failed, probably crashed
+ $tinfo->{comment}=
+ "Server $proc failed during test run" .
+ get_log_from_proc($proc, $tinfo->{name});
+
+ # ----------------------------------------------------
+ # It's not mysqltest that has exited, kill it
+ # ----------------------------------------------------
+ $test->kill();
+
+ report_failure_and_restart($tinfo);
+ return 1;
+ }
+
+ # Try to dump core for mysqltest and all servers
+ foreach my $proc ($test, started(all_servers()))
+ {
+ mtr_print("Trying to dump core for $proc");
+ if ($proc->dump_core())
+ {
+ $proc->wait_one(20);
+ }
+ }
+
+ # ----------------------------------------------------
+ # It's not mysqltest that has exited, kill it
+ # ----------------------------------------------------
+ $test->kill();
+
+ # ----------------------------------------------------
+ # Check if testcase timer expired
+ # ----------------------------------------------------
+ if ( $proc->{timeout} )
+ {
+ my $log_file_name= $opt_vardir."/log/".$tinfo->{shortname}.".log";
+ $tinfo->{comment}=
+ "Test case timeout after ".testcase_timeout($tinfo).
+ " seconds\n\n";
+ # Add 20 last executed commands from test case log file
+ if (-e $log_file_name)
+ {
+ $tinfo->{comment}.=
+ "== $log_file_name == \n".
+ mtr_lastlinesfromfile($log_file_name, 20)."\n";
+ }
+ $tinfo->{'timeout'}= testcase_timeout($tinfo); # Mark as timeout
+ run_on_all($tinfo, 'analyze-timeout');
+
+ report_failure_and_restart($tinfo);
+ return 1;
+ }
+
+ mtr_error("Unhandled process $proc exited");
+ }
+ mtr_error("Should never come here");
+}
+
+
+# Keep track of last position in mysqld error log where we scanned for
+# warnings, so we can attribute any warnings found to the correct test
+# suite or server restart.
+our $last_warning_position= { };
+
+# Called just before a mysqld server is started or a testcase is run,
+# to keep track of which tests have been run since last restart, and
+# of when the error log is reset.
+#
+# Second argument $test_name is test name, or undef for server restart.
+sub pre_write_errorlog {
+ my ($error_log, $test_name)= @_;
+
+ if (! -e $error_log) {
+ # If the error log is moved away, reset the warning parse position.
+ delete $last_warning_position->{$error_log};
+ }
+
+ if (defined($test_name)) {
+ $last_warning_position->{$error_log}{test_names}= []
+ unless exists($last_warning_position->{$error_log}{test_names});
+ push @{$last_warning_position->{$error_log}{test_names}}, $test_name;
+ } else {
+ # Server restart, so clear the list of tests run since last restart.
+ # (except the last one (if any), which is the test about to be run).
+ if (defined($last_warning_position->{$error_log}{test_names}) &&
+ @{$last_warning_position->{$error_log}{test_names}}) {
+ $last_warning_position->{$error_log}{test_names}=
+ [$last_warning_position->{$error_log}{test_names}[-1]];
+ } else {
+ $last_warning_position->{$error_log}{test_names}= [];
+ }
+ }
+}
+
+# Extract server log from after the last occurrence of named test
+# Return as an array of lines
+#
+
+sub extract_server_log ($$) {
+ my ($error_log, $tname) = @_;
+
+ return unless $error_log;
+
+ # Open the servers .err log file and read all lines
+ # belonging to current test into @lines
+ my $Ferr = IO::File->new($error_log)
+ or mtr_error("Could not open file '$error_log' for reading: $!");
+
+ my @lines;
+ my $found_test= 0; # Set once we've found the log of this test
+ while ( my $line = <$Ferr> )
+ {
+ if ($found_test)
+ {
+ # If test wasn't last after all, discard what we found, test again.
+ if ( $line =~ /^CURRENT_TEST:/)
+ {
+ @lines= ();
+ $found_test= $line =~ /^CURRENT_TEST: $tname/;
+ }
+ else
+ {
+ push(@lines, $line);
+ if (scalar(@lines) > 1000000) {
+ $Ferr = undef;
+ mtr_warning("Too much log in $error_log, bailing out from extracting");
+ return ();
+ }
+ }
+ }
+ else
+ {
+ # Search for beginning of test, until found
+ $found_test= 1 if ($line =~ /^CURRENT_TEST: $tname/);
+ }
+ }
+ $Ferr = undef; # Close error log file
+
+ return @lines;
+}
+
+# Get log from server identified from its $proc object, from named test
+# Return as a single string
+#
+
+sub get_log_from_proc ($$) {
+ my ($proc, $name)= @_;
+ my $srv_log= "";
+
+ foreach my $mysqld (all_servers()) {
+ if ($mysqld->{proc} eq $proc) {
+ my @srv_lines= extract_server_log($mysqld->if_exist('log-error'), $name);
+ $srv_log= "\nServer log from this test:\n" .
+ "----------SERVER LOG START-----------\n". join ("", @srv_lines) .
+ "----------SERVER LOG END-------------\n";
+ last;
+ }
+ }
+ return $srv_log;
+}
+
+#
+# Perform a rough examination of the servers
+# error log and write all lines that look
+# suspicious into $error_log.warnings
+#
+
+sub extract_warning_lines ($$) {
+ my ($error_log, $append) = @_;
+
+ # Open the servers .err log file and read all lines
+ # belonging to current tets into @lines
+ my $Ferr = IO::File->new($error_log)
+ or return [];
+ my $last_pos= $last_warning_position->{$error_log}{seek_pos};
+ $Ferr->seek($last_pos, 0) if defined($last_pos);
+ # If the seek fails, we will parse the whole error log, at least we will not
+ # miss any warnings.
+
+ my @lines= <$Ferr>;
+ $last_warning_position->{$error_log}{seek_pos}= $Ferr->tell();
+ $Ferr = undef; # Close error log file
+
+ # mysql_client_test.test sends a COM_DEBUG packet to the server
+ # to provoke a safemalloc leak report, ignore any warnings
+ # between "Begin/end safemalloc memory dump"
+ if ( grep(/Begin safemalloc memory dump:/, @lines) > 0)
+ {
+ my $discard_lines= 1;
+ foreach my $line ( @lines )
+ {
+ if ($line =~ /Begin safemalloc memory dump:/){
+ $discard_lines = 1;
+ } elsif ($line =~ /End safemalloc memory dump./){
+ $discard_lines = 0;
+ }
+
+ if ($discard_lines){
+ $line = "ignored";
+ }
+ }
+ }
+
+ # Write all suspicious lines to $error_log.warnings file
+ my $warning_log = "$error_log.warnings";
+ my $Fwarn = IO::File->new($warning_log, $append ? "a+" : "w")
+ or die("Could not open file '$warning_log' for writing: $!");
+ if (!$append)
+ {
+ print $Fwarn "Suspicious lines from $error_log\n";
+ }
+
+ my @patterns =
+ (
+ qr/^Warning|(mysqld|mariadbd): Warning|\[Warning\]/,
+ qr/^Error:|\[ERROR\]/,
+ qr/^==\d+==\s+\S/, # valgrind errors
+ qr/InnoDB: Warning|InnoDB: Error/,
+ qr/^safe_mutex:|allocated at line/,
+ qr/missing DBUG_RETURN/,
+ qr/Attempting backtrace/,
+ qr/Assertion .* failed/,
+ qr/Sanitizer/,
+ qr/runtime error:/,
+ );
+ # These are taken from the include/mtr_warnings.sql global suppression
+ # list. They occur delayed, so they can be parsed during shutdown rather
+ # than during the per-test check.
+ #
+ # ToDo: having the warning suppressions inside the mysqld we are trying to
+ # check is in any case horrible. We should change it to all be done here
+ # within the Perl code, which is both simpler, easier, faster, and more
+ # robust. We could still have individual test cases put in suppressions by
+ # parsing statically or by writing dynamically to a CSV table read by the
+ # Perl code.
+ my @antipatterns =
+ (
+ @global_suppressions,
+ qr/error .*connecting to master/,
+ qr/InnoDB: Error: in ALTER TABLE `test`.`t[12]`/,
+ qr/InnoDB: Error: table `test`.`t[12]` .*does not exist in the InnoDB internal/,
+ qr/InnoDB: Warning: a long semaphore wait:/,
+ qr/InnoDB: Dumping buffer pool.*/,
+ qr/InnoDB: Buffer pool.*/,
+ qr/InnoDB: Warning: Writer thread is waiting this semaphore:/,
+ qr/InnoDB: innodb_open_files .* should not be greater than/,
+ qr/Slave: Unknown table 't1' .* 1051/,
+ qr/Slave SQL:.*(Internal MariaDB error code: [[:digit:]]+|Query:.*)/,
+ qr/slave SQL thread aborted/,
+ qr/unknown option '--loose[-_]/,
+ qr/unknown variable 'loose[-_]/,
+ #qr/Invalid .*old.* table or database name/,
+ qr/Now setting lower_case_table_names to [02]/,
+ qr/Setting lower_case_table_names=2/,
+ qr/You have forced lower_case_table_names to 0/,
+ qr/deprecated/,
+ qr/Slave SQL thread retried transaction/,
+ qr/Slave \(additional info\)/,
+ qr/Incorrect information in file/,
+ qr/Simulating error for/,
+ qr/Slave I\/O: Get master SERVER_ID failed with error:.*/,
+ qr/Slave I\/O: Get master clock failed with error:.*/,
+ qr/Slave I\/O: Get master COLLATION_SERVER failed with error:.*/,
+ qr/Slave I\/O: Get master TIME_ZONE failed with error:.*/,
+ qr/Slave I\/O: Get master \@\@GLOBAL.gtid_domain_id failed with error:.*/,
+ qr/Slave I\/O: Setting \@slave_connect_state failed with error:.*/,
+ qr/Slave I\/O: Setting \@slave_gtid_strict_mode failed with error:.*/,
+ qr/Slave I\/O: Setting \@slave_gtid_ignore_duplicates failed with error:.*/,
+ qr/Slave I\/O: Setting \@slave_until_gtid failed with error:.*/,
+ qr/Slave I\/O: Get master GTID position failed with error:.*/,
+ qr/Slave I\/O: error reconnecting to master '.*' - retry-time: [1-3] retries/,
+ qr/Slave I\/0: Master command COM_BINLOG_DUMP failed/,
+ qr/Error reading packet/,
+ qr/Lost connection to MariaDB server at 'reading initial communication packet'/,
+ qr/Failed on request_dump/,
+ qr/Slave: Can't drop database.* database doesn't exist/,
+ qr/Slave: Operation DROP USER failed for 'create_rout_db'/,
+ qr|Checking table: '\..mtr.test_suppressions'|,
+ qr|Table \./test/bug53592 has a primary key in InnoDB data dictionary, but not in|,
+ qr|Table '\..mtr.test_suppressions' is marked as crashed and should be repaired|,
+ qr|Table 'test_suppressions' is marked as crashed and should be repaired|,
+ qr|Can't open shared library|,
+ qr|Couldn't load plugin named .*EXAMPLE.*|,
+ qr|InnoDB: Error: table 'test/bug39438'|,
+ qr| entry '.*' ignored in --skip-name-resolve mode|,
+ qr|mysqld got signal 6|,
+ qr|Error while setting value 'pool-of-threads' to 'thread_handling'|,
+ qr|Access denied for user|,
+ qr|Aborted connection|,
+ qr|table.*is full|,
+ qr/\[ERROR\] (mysqld|mariadbd): \Z/, # Warning from Aria recovery
+ qr|Linux Native AIO|, # warning that aio does not work on /dev/shm
+ qr|InnoDB: io_setup\(\) attempt|,
+ qr|InnoDB: io_setup\(\) failed with EAGAIN|,
+ qr|io_uring_queue_init\(\) failed with|,
+ qr|InnoDB: liburing disabled|,
+ qr/InnoDB: Failed to set (O_DIRECT|DIRECTIO_ON) on file/,
+ qr|setrlimit could not change the size of core files to 'infinity';|,
+ qr|feedback plugin: failed to retrieve the MAC address|,
+ qr|Plugin 'FEEDBACK' init function returned error|,
+ qr|Plugin 'FEEDBACK' registration as a INFORMATION SCHEMA failed|,
+ qr|'log-bin-use-v1-row-events' is MySQL .* compatible option|,
+ qr|InnoDB: Setting thread \d+ nice to \d+ failed, current nice \d+, errno 13|, # setpriority() fails under valgrind
+ qr|Failed to setup SSL|,
+ qr|SSL error: Failed to set ciphers to use|,
+ qr/Plugin 'InnoDB' will be forced to shutdown/,
+ qr|Could not increase number of max_open_files to more than|,
+ qr|Changed limits: max_open_files|,
+ qr/InnoDB: Error table encrypted but encryption service not available.*/,
+ qr/InnoDB: Could not find a valid tablespace file for*/,
+ qr/InnoDB: Tablespace open failed for*/,
+ qr/InnoDB: Failed to find tablespace for table*/,
+ qr/InnoDB: Space */,
+ qr|InnoDB: You may have to recover from a backup|,
+ qr|InnoDB: It is also possible that your operatingsystem has corrupted its own file cache|,
+ qr|InnoDB: and rebooting your computer removes the error|,
+ qr|InnoDB: If the corrupt page is an index page you can also try to|,
+ qr|nnoDB: fix the corruption by dumping, dropping, and reimporting|,
+ qr|InnoDB: the corrupt table. You can use CHECK|,
+ qr|InnoDB: TABLE to scan your table for corruption|,
+ qr/InnoDB: See also */,
+ qr/InnoDB: Cannot open .*ib_buffer_pool.* for reading: No such file or directory*/,
+ qr/InnoDB: Table .*mysql.*innodb_table_stats.* not found./,
+ qr/InnoDB: User stopword table .* does not exist./,
+ qr/Dump thread [0-9]+ last sent to server [0-9]+ binlog file:pos .+/,
+ qr/Detected table cache mutex contention at instance .* waits. Additional table cache instance cannot be activated: consider raising table_open_cache_instances. Number of active instances/,
+ qr/WSREP: Failed to guess base node address/,
+ qr/WSREP: Guessing address for incoming client/,
+
+ # for UBSAN
+ qr/decimal\.c.*: runtime error: signed integer overflow/,
+ # Disable test for UBSAN on dynamically loaded objects
+ qr/runtime error: member call.*object.*'Handler_share'/,
+ qr/sql_type\.cc.* runtime error: member call.*object.* 'Type_collection'/,
+ );
+
+ my $matched_lines= [];
+ LINE: foreach my $line ( @lines )
+ {
+ PAT: foreach my $pat ( @patterns )
+ {
+ if ( $line =~ /$pat/ )
+ {
+ foreach my $apat (@antipatterns)
+ {
+ next LINE if $line =~ $apat;
+ }
+ print $Fwarn $line;
+ push @$matched_lines, $line;
+ last PAT;
+ }
+ }
+ }
+ $Fwarn = undef; # Close file
+
+ if (scalar(@$matched_lines) > 0 &&
+ defined($last_warning_position->{$error_log}{test_names})) {
+ return ($last_warning_position->{$error_log}{test_names}, $matched_lines);
+ } else {
+ return ([], $matched_lines);
+ }
+}
+
+
+# Run include/check-warnings.test
+#
+# RETURN VALUE
+# 0 OK
+# 1 Check failed
+#
+sub start_check_warnings ($$) {
+ my $tinfo= shift;
+ my $mysqld= shift;
+
+ my $name= "warnings-".$mysqld->name();
+
+ my $log_error= $mysqld->value('log-error');
+ # To be communicated to the test
+ $ENV{MTR_LOG_ERROR}= $log_error;
+ extract_warning_lines($log_error, 0);
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+ mtr_add_arg($args, "--test-file=%s", "include/check-warnings.test");
+
+ if ( $opt_embedded_server )
+ {
+
+ # Get the args needed for the embedded server
+ # and append them to args prefixed
+ # with --sever-arg=
+
+ my $mysqld= $config->group('embedded')
+ or mtr_error("Could not get [embedded] section");
+
+ my $mysqld_args;
+ mtr_init_args(\$mysqld_args);
+ my $extra_opts= get_extra_opts($mysqld, $tinfo);
+ mysqld_arguments($mysqld_args, $mysqld, $extra_opts);
+ mtr_add_arg($args, "--server-arg=%s", $_) for @$mysqld_args;
+ }
+
+ my $errfile= "$opt_vardir/tmp/$name.err";
+ my $proc= My::SafeProcess->new
+ (
+ name => $name,
+ path => $exe_mysqltest,
+ error => $errfile,
+ output => $errfile,
+ args => \$args,
+ user_data => [$errfile, $mysqld],
+ verbose => $opt_verbose,
+ );
+ mtr_verbose("Started $proc");
+ return $proc;
+}
+
+
+#
+# Loop through our list of processes and check the error log
+# for unexpected errors and warnings
+#
+sub check_warnings ($) {
+ my ($tinfo)= @_;
+ my $res= 0;
+
+ my $tname= $tinfo->{name};
+
+ # Clear previous warnings
+ delete($tinfo->{warnings});
+
+ # Start the mysqltest processes in parallel to save time
+ # also makes it possible to wait for any process to exit during the check
+ my %started;
+ foreach my $mysqld ( mysqlds() )
+ {
+ if ( defined $mysqld->{'proc'} )
+ {
+ my $proc= start_check_warnings($tinfo, $mysqld);
+ $started{$proc->pid()}= $proc;
+ }
+ }
+
+ # Return immediately if no check proceess was started
+ return 0 unless ( keys %started );
+
+ my $timeout= start_timer(check_timeout($tinfo));
+
+ my $result= 0;
+ while (1){
+ my $proc= My::SafeProcess->wait_any_timeout($timeout);
+ mtr_report("Got $proc");
+
+ if ( delete $started{$proc->pid()} ) {
+ # One check warning process returned
+ my $res= $proc->exit_status();
+ my ($err_file, $mysqld)= @{$proc->user_data()};
+
+ if ( $res == 0 or $res == 62 ){
+
+ if ( $res == 0 ) {
+ # Check completed with problem
+ my $report= mtr_grab_file($err_file);
+ # Log to var/log/warnings file
+ mtr_tofile("$opt_vardir/log/warnings",
+ $tname."\n".$report);
+
+ $tinfo->{'warnings'}.= $report;
+ $result= 1;
+ }
+
+ if ( $res == 62 ) {
+ # Test case was ok and called "skip"
+ # Remove the .err file the check generated
+ unlink($err_file);
+ }
+
+ if ( keys(%started) == 0){
+ # All checks completed
+ mark_time_used('ch-warn');
+ return $result;
+ }
+ # Wait for next process to exit
+ next;
+ }
+ else
+ {
+ my $report= mtr_grab_file($err_file);
+ $tinfo->{comment}.=
+ "Could not execute 'check-warnings' for ".
+ "testcase '$tname' (res: $res) server: '".
+ $mysqld->name() ."':\n";
+ $tinfo->{comment}.= $report;
+
+ $result= 2;
+ }
+ }
+ elsif ( $proc->{timeout} ) {
+ $tinfo->{comment}.= "Timeout for 'check warnings' expired after "
+ .check_timeout($tinfo)." seconds";
+ $result= 4;
+ }
+ else {
+ # Unknown process returned, most likley a crash, abort everything
+ $tinfo->{comment}=
+ "The server $proc crashed while running 'check warnings'".
+ get_log_from_proc($proc, $tinfo->{name});
+ $result= 3;
+ }
+
+ # Kill any check processes still running
+ map($_->kill(), values(%started));
+
+ mark_time_used('ch-warn');
+ return $result;
+ }
+
+ mtr_error("INTERNAL_ERROR: check_warnings");
+}
+
+# Check for warnings generated during shutdown of a mysqld server.
+# If any, report them to master server, and return true; else just return
+# false.
+
+sub check_warnings_post_shutdown {
+ my ($server_socket)= @_;
+ my $testname_hash= { };
+ my $report= '';
+ foreach my $mysqld ( mysqlds())
+ {
+ my ($testlist, $match_lines)=
+ extract_warning_lines($mysqld->value('log-error'), 1);
+ $testname_hash->{$_}= 1 for @$testlist;
+ $report.= join('', @$match_lines);
+ }
+ my @warning_tests= keys(%$testname_hash);
+ if (@warning_tests) {
+ my $fake_test= My::Test->new(testnames => \@warning_tests);
+ $fake_test->{'warnings'}= $report;
+ $fake_test->write_test($server_socket, 'WARNINGS');
+ }
+}
+
+#
+# Check for the file indicating expected crash and restart it.
+#
+sub check_expected_crash_and_restart {
+ my $mysqld = shift;
+
+ # Check if crash expected by looking at the .expect file
+ # in var/tmp
+ my $expect_file= "$opt_vardir/tmp/".$mysqld->name().".expect";
+ if ( -f $expect_file )
+ {
+ mtr_verbose("Crash was expected, file '$expect_file' exists");
+
+ for (my $waits = 0; $waits < 50; mtr_milli_sleep(100), $waits++)
+ {
+ # Race condition seen on Windows: try again until file not empty
+ next if -z $expect_file;
+ # If last line in expect file starts with "wait"
+ # sleep a little and try again, thus allowing the
+ # test script to control when the server should start
+ # up again. Keep trying for up to 5s at a time.
+ my $last_line= mtr_lastlinesfromfile($expect_file, 1);
+ if ($last_line =~ /^wait/ )
+ {
+ mtr_verbose("Test says wait before restart") if $waits == 0;
+ next;
+ }
+ delete $ENV{MTR_BINDIR_FORCED};
+
+ # Ignore any partial or unknown command
+ next unless $last_line =~ /^restart/;
+ # If last line begins "restart:", the rest of the line is read as
+ # extra command line options to add to the restarted mysqld.
+ # Anything other than 'wait' or 'restart:' (with a colon) will
+ # result in a restart with original mysqld options.
+ if ($last_line =~ /restart_bindir\s+(\S+)(:.+)?/) {
+ $ENV{MTR_BINDIR_FORCED}= $1;
+ if ($2) {
+ my @rest_opt= split(' ', $2);
+ $mysqld->{'restart_opts'}= \@rest_opt;
+ }
+ } elsif ($last_line =~ /restart:(.+)/) {
+ my @rest_opt= split(' ', $1);
+ $mysqld->{'restart_opts'}= \@rest_opt;
+ } else {
+ delete $mysqld->{'restart_opts'};
+ }
+ unlink($expect_file);
+
+ # Start server with same settings as last time
+ return mysqld_start($mysqld, $mysqld->{'started_opts'});
+ }
+ # Loop ran through: we should keep waiting after a re-check
+ return 2;
+ }
+
+ # Not an expected crash
+ return 0;
+}
+
+
+# Remove all files and subdirectories of a directory
+sub clean_dir {
+ my ($dir)= @_;
+ mtr_verbose("clean_dir: $dir");
+ finddepth(
+ { no_chdir => 1,
+ wanted => sub {
+ if (-d $_){
+ # A dir
+ if ($_ eq $dir){
+ # The dir to clean
+ return;
+ } else {
+ mtr_verbose("rmdir: '$_'");
+ rmdir($_) or mtr_warning("rmdir($_) failed: $!");
+ }
+ } else {
+ # Hopefully a file
+ mtr_verbose("unlink: '$_'");
+ unlink($_) or mtr_warning("unlink($_) failed: $!");
+ }
+ }
+ },
+ $dir);
+}
+
+
+sub clean_datadir {
+ mtr_verbose("Cleaning datadirs...");
+
+ if (started(all_servers()) != 0){
+ mtr_error("Trying to clean datadir before all servers stopped");
+ }
+
+ for (all_servers())
+ {
+ my $dir= "$opt_vardir/".$_->{name};
+ mtr_verbose(" - removing '$dir'");
+ rmtree($dir);
+ }
+
+ # Remove all files in tmp and var/tmp
+ clean_dir("$opt_vardir/tmp");
+ if ($opt_tmpdir ne "$opt_vardir/tmp"){
+ clean_dir($opt_tmpdir);
+ }
+}
+
+
+#
+# Save datadir before it's removed
+#
+sub save_datadir_after_failure($$) {
+ my ($dir, $savedir)= @_;
+
+ mtr_report(" - saving '$dir'");
+ my $dir_name= basename($dir);
+ rename("$dir", "$savedir/$dir_name");
+}
+
+
+sub after_failure ($) {
+ my ($tinfo)= @_;
+
+ mtr_report("Saving datadirs...");
+
+ my $save_dir= "$opt_vardir/log/";
+ $save_dir.= $tinfo->{name};
+ # Add combination name if any
+ $save_dir.= '-' . join(',', sort @{$tinfo->{combinations}})
+ if defined $tinfo->{combinations};
+
+ # Save savedir path for server
+ $tinfo->{savedir}= $save_dir;
+
+ mkpath($save_dir) if ! -d $save_dir;
+
+ #
+ # Create a log of files in vardir (good for buildbot)
+ #
+ if (!IS_WINDOWS)
+ {
+ my $Flog= IO::File->new("$opt_vardir/log/files.log", "w");
+ if ($Flog)
+ {
+ print $Flog scalar(`/bin/ls -Rl $opt_vardir/*`);
+ close($Flog);
+ }
+ }
+
+ # Save the used config files
+ my %config_files = config_files($tinfo);
+ while (my ($file, $generate) = each %config_files) {
+ copy("$opt_vardir/$file", $save_dir);
+ }
+
+ # Copy the tmp dir
+ copytree("$opt_vardir/tmp/", "$save_dir/tmp/");
+
+ foreach (all_servers()) {
+ my $dir= "$opt_vardir/".$_->{name};
+ save_datadir_after_failure($dir, $save_dir);
+ }
+}
+
+
+sub report_failure_and_restart ($) {
+ my $tinfo= shift;
+
+ if ($opt_valgrind && ($tinfo->{'warnings'} || $tinfo->{'timeout'}) &&
+ $opt_core_on_failure == 0)
+ {
+ # In these cases we may want valgrind report from normal termination
+ $tinfo->{'dont_kill_server'}= 1;
+ }
+ # Shutdown properly if not to be killed (for valgrind)
+ stop_all_servers($tinfo->{'dont_kill_server'} ? $opt_shutdown_timeout : 0);
+
+ $tinfo->{'result'}= 'MTR_RES_FAILED';
+
+ my $test_failures= $tinfo->{'failures'} || 0;
+ $tinfo->{'failures'}= $test_failures + 1;
+
+
+ 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"
+ ;
+ }
+
+ if ( !defined $tinfo->{logfile} )
+ {
+ my $logfile= $path_current_testlog;
+ if ( defined $logfile )
+ {
+ if ( -f $logfile )
+ {
+ # Test failure was detected by test tool and its report
+ # about what failed has been saved to file. Save the report
+ # in tinfo
+ $tinfo->{logfile}= mtr_fromfile($logfile);
+ # If no newlines in the test log:
+ # (it will contain the CURRENT_TEST written by mtr, so is not empty)
+ if ($tinfo->{logfile} !~ /\n/)
+ {
+ # Show how far it got before suddenly failing
+ $tinfo->{comment}.= "mysqltest failed but provided no output\n";
+ my $log_file_name= $opt_vardir."/log/".$tinfo->{shortname}.".log";
+ if (-e $log_file_name) {
+ $tinfo->{comment}.=
+ "The result from queries just before the failure was:".
+ "\n< snip >\n".
+ mtr_lastlinesfromfile($log_file_name, $opt_tail_lines)."\n";
+ }
+ }
+ }
+ else
+ {
+ # The test tool report didn't exist, display an
+ # error message
+ $tinfo->{logfile}= "Could not open test tool report '$logfile'";
+ }
+ }
+ }
+
+ after_failure($tinfo);
+
+ mtr_report_test($tinfo);
+
+}
+
+
+sub run_system(@) {
+ mtr_verbose("Running '$_[0]'");
+ my $ret= system(@_) >> 8;
+ return $ret;
+}
+
+
+sub mysqld_stop {
+ my $mysqld= shift or die "usage: mysqld_stop(<mysqld>)";
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--no-defaults");
+ mtr_add_arg($args, "--character-sets-dir=%s", $mysqld->value('character-sets-dir'));
+ mtr_add_arg($args, "--user=%s", $opt_user);
+ mtr_add_arg($args, "--password=");
+ mtr_add_arg($args, "--port=%d", $mysqld->value('port'));
+ mtr_add_arg($args, "--host=%s", $mysqld->value('#host'));
+ mtr_add_arg($args, "--connect_timeout=20");
+ mtr_add_arg($args, "--protocol=tcp");
+
+ mtr_add_arg($args, "shutdown");
+
+ My::SafeProcess->run
+ (
+ name => "mysqladmin shutdown ".$mysqld->name(),
+ path => $exe_mysqladmin,
+ args => \$args,
+ error => "$opt_vardir/log/mysqladmin.err",
+
+ );
+}
+
+
+sub mysqld_arguments ($$$) {
+ my $args= shift;
+ my $mysqld= shift;
+ my $extra_opts= shift;
+
+ my @defaults = grep(/^--defaults-file=/, @$extra_opts);
+ if (@defaults > 0) {
+ mtr_add_arg($args, pop(@defaults))
+ }
+ else {
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ }
+
+ # When mysqld is run by a root user(euid is 0), it will fail
+ # to start unless we specify what user to run as, see BUG#30630
+ my $euid= $>;
+ if (!IS_WINDOWS and $euid == 0 and
+ (grep(/^--user/, @$extra_opts)) == 0) {
+ mtr_add_arg($args, "--user=root");
+ }
+
+ if (!using_extern() and !$opt_user_args)
+ {
+ # Turn on logging to file
+ mtr_add_arg($args, "%s--log-output=file");
+ }
+
+ # Check if "extra_opt" contains --log-bin
+ my $skip_binlog= not grep /^--(loose-)?log-bin/, @$extra_opts;
+
+ my $found_skip_core= 0;
+ foreach my $arg ( @$extra_opts )
+ {
+ # Skip --defaults-file option since it's handled above.
+ next if $arg =~ /^--defaults-file/;
+
+ # Allow --skip-core-file to be set in <testname>-[master|slave].opt file
+ if ($arg eq "--skip-core-file")
+ {
+ $found_skip_core= 1;
+ }
+ elsif ($skip_binlog and mtr_match_prefix($arg, "--binlog-format"))
+ {
+ ; # Dont add --binlog-format when running without binlog
+ }
+ elsif ($arg eq "--loose-skip-log-bin" and
+ $mysqld->option("log-slave-updates"))
+ {
+ ; # Dont add --skip-log-bin when mysqld have --log-slave-updates in config
+ }
+ else
+ {
+ mtr_add_arg($args, "%s", $arg);
+ }
+ }
+ $opt_skip_core = $found_skip_core;
+ if ( !$found_skip_core && !$opt_user_args )
+ {
+ mtr_add_arg($args, "%s", "--core-file");
+ }
+
+ # Enable the debug sync facility, set default wait timeout.
+ # Facility stays disabled if timeout value is zero.
+ mtr_add_arg($args, "--loose-debug-sync-timeout=%s",
+ $opt_debug_sync_timeout) unless $opt_user_args;
+
+ return $args;
+}
+
+
+
+sub mysqld_start ($$) {
+ my $mysqld= shift;
+ my $extra_opts= shift;
+
+ mtr_verbose(My::Options::toStr("mysqld_start", @$extra_opts));
+
+ my $exe= find_mysqld($mysqld->value('basedir'));
+
+ mtr_error("Internal error: mysqld should never be started for embedded")
+ if $opt_embedded_server;
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+
+ # Add any additional options from an in-test restart
+ my @all_opts= @$extra_opts;
+ if (exists $mysqld->{'restart_opts'}) {
+ push (@all_opts, @{$mysqld->{'restart_opts'}});
+ mtr_verbose(My::Options::toStr("mysqld_start restart",
+ @{$mysqld->{'restart_opts'}}));
+ }
+ mysqld_arguments($args,$mysqld,\@all_opts);
+
+ if ( $opt_debug )
+ {
+ mtr_add_arg($args, "--debug-dbug=$debug_d:t:i:A,%s/log/%s.trace",
+ $path_vardir_trace, $mysqld->name());
+ }
+
+
+ # Command line for mysqld started for *this particular test*.
+ # Differs from "generic" MYSQLD_CMD by including all command line
+ # options from *.opt and *.combination files.
+ $ENV{'MYSQLD_LAST_CMD'}= "$exe @$args";
+ my $oldexe= $exe;
+
+ My::Debugger::setup_args(\$args, \$exe, $mysqld->name());
+ $ENV{'VALGRIND_TEST'}= $opt_valgrind = int(($exe || '') eq 'valgrind');
+
+ # Remove the old pidfile if any
+ unlink($mysqld->value('pid-file'));
+
+ my $output= $mysqld->value('log-error');
+
+ 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
+
+ # Write a message about this to the normal log file
+ my $trace_name= "$opt_vardir/log/".$mysqld->name().".trace";
+ mtr_tofile($output,
+ "NOTE: When running with --valgrind --debug the output from ",
+ "mysqld (where the valgrind messages shows up) is stored ",
+ "together with the trace file to make it ",
+ "easier to find the exact position of valgrind errors.",
+ "See trace file $trace_name.\n");
+ $output= $trace_name;
+
+ }
+ # Remember this log file for valgrind error report search
+ $mysqld_logs{$output}= 1 if $opt_valgrind;
+ # Remember data dir for gmon.out files if using gprof
+ $gprof_dirs{$mysqld->value('datadir')}= 1 if $opt_gprof;
+
+ if ( defined $exe )
+ {
+ mtr_tofile($output, "\$ $exe @$args\n");
+ pre_write_errorlog($output);
+ $mysqld->{'proc'}= My::SafeProcess->new
+ (
+ name => $mysqld->name(),
+ path => $exe,
+ args => \$args,
+ output => $output,
+ error => $output,
+ append => 1,
+ verbose => $opt_verbose,
+ nocore => $opt_skip_core,
+ host => undef,
+ shutdown => sub { mysqld_stop($mysqld) },
+ envs => \@opt_mysqld_envs,
+ );
+ mtr_verbose("Started $mysqld->{proc}");
+ }
+
+ $mysqld->{'started_opts'}= $extra_opts;
+
+ my $expect_file= "$opt_vardir/tmp/".$mysqld->name().".expect";
+ my $rc= $oldexe eq ($exe || '') ||
+ sleep_until_file_created($mysqld->value('pid-file'), $expect_file,
+ $opt_start_timeout, $mysqld->{'proc'}, $warn_seconds);
+ if (!$rc)
+ {
+ # Report failure about the last test case before exit
+ my $test_name= mtr_grab_file($path_current_testlog);
+ $test_name =~ s/^CURRENT_TEST:\s//;
+ my $tinfo = My::Test->new(name => $test_name);
+ $tinfo->{result}= 'MTR_RES_FAILED';
+ $tinfo->{failures}= 1;
+ $tinfo->{logfile}=get_log_from_proc($mysqld->{'proc'}, $tinfo->{name});
+ report_option('verbose', 1);
+ mtr_report_test($tinfo);
+ }
+ return $rc;
+}
+
+
+sub stop_all_servers () {
+ my $shutdown_timeout = $_[0] or 0;
+
+ mtr_verbose("Stopping all servers...");
+
+ # Kill all started servers
+ My::SafeProcess::shutdown($shutdown_timeout,
+ started(all_servers()));
+
+ # Remove pidfiles
+ foreach my $server ( all_servers() )
+ {
+ my $pid_file= $server->if_exist('pid-file');
+ unlink($pid_file) if defined $pid_file;
+ }
+
+ # Mark servers as stopped
+ map($_->{proc}= undef, all_servers());
+
+}
+
+
+# Find out if server should be restarted for this test
+sub server_need_restart {
+ my ($tinfo, $server)= @_;
+
+ if ( using_extern() )
+ {
+ mtr_verbose_restart($server, "no restart for --extern server");
+ return 0;
+ }
+
+ if ( $tinfo->{'force_restart'} ) {
+ mtr_verbose_restart($server, "forced in .opt file");
+ return 1;
+ }
+
+ if ( $opt_force_restart ) {
+ mtr_verbose_restart($server, "forced restart turned on");
+ return 1;
+ }
+
+ if ( $tinfo->{template_path} ne $current_config_name)
+ {
+ mtr_verbose_restart($server, "using different config file");
+ return 1;
+ }
+
+ if ( $tinfo->{'master_sh'} || $tinfo->{'slave_sh'} )
+ {
+ mtr_verbose_restart($server, "sh script to run");
+ return 1;
+ }
+
+ if ( ! started($server) )
+ {
+ mtr_verbose_restart($server, "not started");
+ return 1;
+ }
+
+ my $started_tinfo= $server->{'started_tinfo'};
+ if ( defined $started_tinfo )
+ {
+
+ # Check if timezone of test that server was started
+ # with differs from timezone of next test
+ if ( timezone($started_tinfo) ne timezone($tinfo) )
+ {
+ mtr_verbose_restart($server, "different timezone");
+ return 1;
+ }
+ }
+
+ if ($server->name() =~ /^mysqld\./)
+ {
+
+ # Check that running process was started with same options
+ # as the current test requires
+ my $extra_opts= get_extra_opts($server, $tinfo);
+ my $started_opts= $server->{'started_opts'};
+
+ # Also, always restart if server had been restarted with additional
+ # options within test.
+ if (!My::Options::same($started_opts, $extra_opts) ||
+ exists $server->{'restart_opts'})
+ {
+ delete $server->{'restart_opts'};
+ my $use_dynamic_option_switch= 0;
+ my $restart_opts = delete $server->{'restart_opts'} || [];
+ if (!$use_dynamic_option_switch)
+ {
+ mtr_verbose_restart($server, "running with different options '" .
+ join(" ", @{$extra_opts}) . "' != '" .
+ join(" ", @{$started_opts}, @{$restart_opts}) . "'" );
+ return 1;
+ }
+
+ mtr_verbose(My::Options::toStr("started_opts", @$started_opts));
+ mtr_verbose(My::Options::toStr("extra_opts", @$extra_opts));
+
+ # Get diff and check if dynamic switch is possible
+ my @diff_opts= My::Options::diff($started_opts, $extra_opts);
+ mtr_verbose(My::Options::toStr("diff_opts", @diff_opts));
+
+ my $query= My::Options::toSQL(@diff_opts);
+ mtr_verbose("Attempting dynamic switch '$query'");
+ if (run_query($tinfo, $server, $query)){
+ mtr_verbose("Restart: running with different options '" .
+ join(" ", @{$extra_opts}) . "' != '" .
+ join(" ", @{$started_opts}) . "'" );
+ return 1;
+ }
+
+ # Remember the dynamically set options
+ $server->{'started_opts'}= $extra_opts;
+ }
+ }
+
+ # Default, no restart
+ return 0;
+}
+
+
+sub servers_need_restart($) {
+ my ($tinfo)= @_;
+ return grep { server_need_restart($tinfo, $_); } all_servers();
+}
+
+
+
+############################################
+
+############################################
+
+#
+# Filter a list of servers and return the SafeProcess
+# for only those that are started or stopped
+#
+sub started { return grep(defined $_, map($_->{proc}, @_)); }
+sub stopped { return grep(!defined $_, map($_->{proc}, @_)); }
+
+
+sub get_extra_opts {
+ # No extra options if --user-args
+ return \@opt_extra_mysqld_opt if $opt_user_args;
+
+ my ($mysqld, $tinfo)= @_;
+
+ my $opts=
+ $mysqld->option("#!use-slave-opt") ?
+ $tinfo->{slave_opt} : $tinfo->{master_opt};
+
+ # Expand environment variables
+ foreach my $opt ( @$opts )
+ {
+ no warnings 'uninitialized';
+ $opt =~ s/\$\{(\w+)\}/$ENV{$1}/ge;
+ $opt =~ s/\$(\w+)/$ENV{$1}/ge;
+ }
+ return $opts;
+}
+
+
+sub stop_servers($$) {
+ my (@servers)= @_;
+
+ mtr_report("Stopping ", started(@servers));
+
+ My::SafeProcess::shutdown($opt_shutdown_timeout,
+ started(@servers));
+
+ foreach my $server (@servers)
+ {
+ # Mark server as stopped
+ $server->{proc}= undef;
+
+ # Forget history
+ delete $server->{'started_tinfo'};
+ delete $server->{'started_opts'};
+ delete $server->{'started_cnf'};
+ }
+}
+
+#
+# run_query_output
+#
+# Run a query against a server using mysql client. The output of
+# the query will be written into outfile.
+#
+sub run_query_output {
+ my ($mysqld, $query, $outfile)= @_;
+ my $args;
+
+ mtr_init_args(\$args);
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+ mtr_add_arg($args, "--silent");
+ mtr_add_arg($args, "--execute=%s", $query);
+
+ my $res= My::SafeProcess->run
+ (
+ name => "run_query_output -> ".$mysqld->name(),
+ path => $exe_mysql,
+ args => \$args,
+ output => $outfile,
+ error => $outfile
+ );
+
+ return $res
+}
+
+
+#
+# wsrep_wait_ready
+#
+# Wait until the server has been joined to the cluster and is
+# ready for operation.
+#
+# RETURN
+# 1 Server is ready
+# 0 Server didn't transition to ready state within start timeout
+#
+sub wait_wsrep_ready($$) {
+ my ($tinfo, $mysqld)= @_;
+
+ my $sleeptime= 100; # Milliseconds
+ my $loops= ($opt_start_timeout * 1000) / $sleeptime;
+
+ my $name= $mysqld->name();
+ my $outfile= "$opt_vardir/tmp/$name.wsrep_ready";
+ my $query= "SET SESSION wsrep_sync_wait = 0;
+ SELECT VARIABLE_NAME, VARIABLE_VALUE
+ FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+ WHERE VARIABLE_NAME = 'wsrep_ready'";
+
+ for (my $loop= 1; $loop <= $loops; $loop++)
+ {
+ # Careful... if MTR runs with option 'verbose' then the
+ # file contains also SafeProcess verbose output
+ if (run_query_output($mysqld, $query, $outfile) == 0 &&
+ mtr_grab_file($outfile) =~ /WSREP_READY\s+ON/)
+ {
+ unlink($outfile);
+ return 1;
+ }
+ mtr_milli_sleep($sleeptime);
+ }
+
+ $tinfo->{logfile}= "WSREP did not transition to state READY";
+ return 0;
+}
+
+#
+# wsrep_is_bootstrap_server
+#
+# Check if the server is the first one to be started in the
+# cluster.
+#
+# RETURN
+# 1 The server is a bootstrap server
+# 0 The server is not a bootstrap server
+#
+sub wsrep_is_bootstrap_server($) {
+ my $mysqld= shift;
+
+ my $cluster_address= $mysqld->if_exist('wsrep-cluster-address') ||
+ $mysqld->if_exist('wsrep_cluster_address');
+ if (defined $cluster_address)
+ {
+ return $cluster_address eq "gcomm://" || $cluster_address eq "'gcomm://'";
+ }
+ return 0;
+}
+
+#
+# wsrep_on
+#
+# Check if wsrep has been enabled for a server.
+#
+# RETURN
+# 1 Wsrep has been enabled
+# 0 Wsrep is not enabled
+#
+sub wsrep_on($) {
+ my $mysqld= shift;
+ #check if wsrep_on= is set in configuration
+ if ($mysqld->if_exist('wsrep-on')) {
+ my $on= "".$mysqld->value('wsrep-on');
+ if ($on eq "1" || $on eq "ON") {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+#
+# start_servers
+#
+# Start servers not already started
+#
+# RETURN
+# 0 OK
+# 1 Start failed
+#
+sub start_servers($) {
+ my ($tinfo)= @_;
+
+ for (all_servers()) {
+ $_->{START}->($_, $tinfo) if $_->{START};
+ }
+
+ for (all_servers()) {
+ next unless $_->{WAIT} and started($_);
+ if ($_->{WAIT}->($_, $tinfo)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+#
+# Run include/check-testcase.test
+# Before a testcase, run in record mode and save result file to var/tmp
+# After testcase, run and compare with the recorded file, they should be equal!
+#
+# RETURN VALUE
+# The newly started process
+#
+sub start_check_testcase ($$$) {
+ my $tinfo= shift;
+ my $mode= shift;
+ my $mysqld= shift;
+
+ my $name= "check-".$mysqld->name();
+ # Replace dots in name with underscore to avoid that mysqltest
+ # misinterpret's what the filename extension is :(
+ $name=~ s/\./_/g;
+
+ my $args;
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ mtr_add_arg($args, "--defaults-group-suffix=%s", $mysqld->after('mysqld'));
+ mtr_add_arg($args, "--result-file=%s", "$opt_vardir/tmp/$name.result");
+ mtr_add_arg($args, "--test-file=%s", "include/check-testcase.test");
+ mtr_add_arg($args, "--verbose");
+
+ if ( $mode eq "before" )
+ {
+ mtr_add_arg($args, "--record");
+ }
+ my $errfile= "$opt_vardir/tmp/$name.err";
+ my $proc= My::SafeProcess->new
+ (
+ name => $name,
+ path => $exe_mysqltest,
+ error => $errfile,
+ output => $errfile,
+ args => \$args,
+ user_data => $errfile,
+ verbose => $opt_verbose,
+ );
+
+ mtr_report("Started $proc");
+ return $proc;
+}
+
+
+sub run_mysqltest ($) {
+ my $proc= start_mysqltest(@_);
+ $proc->wait();
+}
+
+
+sub start_mysqltest ($) {
+ my ($tinfo)= @_;
+ my $exe= $exe_mysqltest;
+ my $args;
+
+ mark_time_used('admin');
+
+ mtr_init_args(\$args);
+
+ mtr_add_arg($args, "--defaults-file=%s", $path_config_file);
+ if ($opt_verbose > 1) {
+ mtr_add_arg($args, "--verbose");
+ } else {
+ mtr_add_arg($args, "--silent");
+ }
+ mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir);
+ mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir);
+ mtr_add_arg($args, "--logdir=%s/log", $opt_vardir);
+
+ # Log line number and time for each line in .test file
+ mtr_add_arg($args, "--mark-progress")
+ if $opt_mark_progress;
+
+ mtr_add_arg($args, "--database=test");
+
+ if ( $opt_ps_protocol )
+ {
+ mtr_add_arg($args, "--ps-protocol");
+ }
+
+ if ( $opt_sp_protocol )
+ {
+ mtr_add_arg($args, "--sp-protocol");
+ }
+
+ if ( $opt_view_protocol )
+ {
+ mtr_add_arg($args, "--view-protocol");
+ }
+
+ if ( $opt_cursor_protocol )
+ {
+ mtr_add_arg($args, "--cursor-protocol");
+ }
+
+ if ( $opt_non_blocking_api )
+ {
+ mtr_add_arg($args, "--non-blocking-api");
+ }
+
+ mtr_add_arg($args, "--timer-file=%s/log/timer", $opt_vardir);
+
+ if ( $opt_compress )
+ {
+ mtr_add_arg($args, "--compress");
+ }
+
+ if ( $opt_sleep )
+ {
+ mtr_add_arg($args, "--sleep=%d", $opt_sleep);
+ }
+
+ if ( $opt_ssl )
+ {
+ # Turn on SSL for _all_ test cases if option --ssl was used
+ mtr_add_arg($args, "--ssl");
+ }
+
+ if ( $opt_max_connections ) {
+ mtr_add_arg($args, "--max-connections=%d", $opt_max_connections);
+ }
+
+ if ( $opt_embedded_server )
+ {
+
+ # Get the args needed for the embedded server
+ # and append them to args prefixed
+ # with --sever-arg=
+
+ my $mysqld= $config->group('embedded')
+ or mtr_error("Could not get [embedded] section");
+
+ my $mysqld_args;
+ mtr_init_args(\$mysqld_args);
+ my $extra_opts= get_extra_opts($mysqld, $tinfo);
+ mysqld_arguments($mysqld_args, $mysqld, $extra_opts);
+ mtr_add_arg($args, "--server-arg=%s", $_) for @$mysqld_args;
+ }
+
+ # ----------------------------------------------------------------------
+ # export MYSQL_TEST variable containing <path>/mysqltest <args>
+ # ----------------------------------------------------------------------
+ $ENV{'MYSQL_TEST'}= mtr_args2str($exe_mysqltest, @$args);
+
+ if ($opt_force > 1)
+ {
+ mtr_add_arg($args, "--continue-on-error");
+ }
+
+ my $suite = $tinfo->{suite};
+ if ($suite->{parent}) {
+ mtr_add_arg($args, "--overlay-dir=%s/", $suite->{dir});
+ mtr_add_arg($args, "--suite-dir=%s/", $suite->{parent}->{dir});
+ } else {
+ mtr_add_arg($args, "--suite-dir=%s/", $suite->{dir});
+ }
+
+ mtr_add_arg($args, "--test-file=%s", $tinfo->{'path'});
+
+ # Number of lines of resut to include in failure report
+ mtr_add_arg($args, "--tail-lines=%d", $opt_tail_lines);
+
+ if ( defined $tinfo->{'result_file'} ) {
+ mtr_add_arg($args, "--result-file=%s", $tinfo->{'result_file'});
+ }
+
+ client_debug_arg($args, "mysqltest");
+
+ if ( $opt_record )
+ {
+ mtr_add_arg($args, "--record");
+
+ # When recording to a non existing result file
+ # the name of that file is in "record_file"
+ if ( defined $tinfo->{'record_file'} ) {
+ mtr_add_arg($args, "--result-file=%s", $tinfo->{record_file});
+ }
+ }
+
+ My::Debugger::setup_client_args(\$args, \$exe);
+
+ my $proc= My::SafeProcess->new
+ (
+ name => "mysqltest",
+ path => $exe,
+ args => \$args,
+ append => 1,
+ error => $path_current_testlog,
+ verbose => $opt_verbose,
+ );
+ mtr_verbose("Started $proc");
+ return $proc;
+}
+
+#
+# Search server logs for valgrind reports printed at mysqld termination
+#
+sub valgrind_exit_reports() {
+ my $found_err= 0;
+
+ foreach my $log_file (keys %mysqld_logs)
+ {
+ my @culprits= ();
+ my $valgrind_rep= "";
+ my $found_report= 0;
+ my $err_in_report= 0;
+
+ my $LOGF = IO::File->new($log_file)
+ or mtr_error("Could not open file '$log_file' for reading: $!");
+
+ while ( my $line = <$LOGF> )
+ {
+ if ($line =~ /^CURRENT_TEST: (.+)$/)
+ {
+ my $testname= $1;
+ # If we have a report, report it if needed and start new list of tests
+ if ($found_report)
+ {
+ if ($err_in_report)
+ {
+ mtr_print ("Valgrind report from $log_file after tests:\n",
+ @culprits);
+ mtr_print_line();
+ print ("$valgrind_rep\n");
+ $found_err= 1;
+ $err_in_report= 0;
+ }
+ # Make ready to collect new report
+ @culprits= ();
+ $found_report= 0;
+ $valgrind_rep= "";
+ }
+ push (@culprits, $testname);
+ next;
+ }
+ # This line marks the start of a valgrind report
+ $found_report= 1 if $line =~ /^==\d+== .* SUMMARY:/;
+
+ if ($found_report) {
+ $line=~ s/^==\d+== //;
+ $valgrind_rep .= $line;
+ $err_in_report= 1 if $line =~ /ERROR SUMMARY: [1-9]/;
+ $err_in_report= 1 if $line =~ /definitely lost: [1-9]/;
+ $err_in_report= 1 if $line =~ /possibly lost: [1-9]/;
+ }
+ }
+
+ $LOGF= undef;
+
+ if ($err_in_report) {
+ mtr_print ("Valgrind report from $log_file after tests:\n", @culprits);
+ mtr_print_line();
+ print ("$valgrind_rep\n");
+ $found_err= 1;
+ }
+ }
+
+ return $found_err;
+}
+
+#
+# Usage
+#
+sub usage ($) {
+ my ($message)= @_;
+
+ if ( $message )
+ {
+ print STDERR "$message\n";
+ print STDERR "For full list of options, use $0 --help\n";
+ exit(1);
+ }
+
+ local $"= ','; # for @DEFAULT_SUITES below
+
+ print <<HERE . My::Debugger::help() . My::CoreDump::help() . <<HERE;
+
+$0 [ OPTIONS ] [ TESTCASE ]
+
+Where test case can be specified as:
+
+testcase[.test] Runs the test case named 'testcase' from all suits
+path-to-testcase
+[suite.]testcase[,combination]
+
+Examples:
+
+alias
+main.alias 'main' is the name of the main test suite
+rpl.rpl_invoked_features,mix,innodb
+suite/rpl/t/rpl.rpl_invoked_features
+
+Options to control what engine/variation to run:
+
+ embedded-server Use the embedded server, i.e. no mysqld daemons
+ ps-protocol Use the binary protocol between client and server
+ cursor-protocol Use the cursor protocol between client and server
+ (implies --ps-protocol)
+ view-protocol Create a view to execute all non updating queries
+ sp-protocol Create a stored procedure to execute all queries
+ non-blocking-api Use the non-blocking client API
+ compress Use the compressed protocol between client and server
+ ssl Use ssl protocol between client and server
+ skip-ssl Don't start server with support for ssl connections
+ vs-config Visual Studio configuration used to create executables
+ (default: MTR_VS_CONFIG environment variable)
+ parallel=# How many parallel test should be run
+ defaults-file=<config template> Use fixed config template for all
+ tests
+ defaults-extra-file=<config template> Extra config template to add to
+ all generated configs
+ combination=<opt> Use at least twice to run tests with specified
+ options to mysqld
+ dry-run Don't run any tests, print the list of tests
+ that were selected for execution
+
+Options to control directories to use
+ tmpdir=DIR The directory where temporary files are stored
+ (default: ./var/tmp).
+ vardir=DIR The directory where files generated from the test run
+ is stored (default: ./var). Specifying a ramdisk or
+ tmpfs will speed up tests.
+ mem[=DIR] Run testsuite in "memory" using tmpfs or ramdisk
+ Attempts to use DIR first if specified else
+ uses a builtin list of standard locations
+ for tmpfs (/run/shm, /dev/shm, /tmp)
+ The option can also be set using environment
+ variable MTR_MEM=[DIR]
+ clean-vardir Clean vardir if tests were successful and if
+ running in "memory". Otherwise this option is ignored
+ client-bindir=PATH Path to the directory where client binaries are located
+ client-libdir=PATH Path to the directory where client libraries are located
+
+
+Options to control what test suites or cases to run
+
+ force Continue after a failure. When specified once, a
+ failure in a test file will abort this test file, and
+ the execution will continue from the next test file.
+ When specified twice, execution will continue executing
+ the failed test file from the next command.
+ skip-not-found It is not an error if a test was not found in a
+ specified test suite. Test will be marked as skipped.
+ do-test=PREFIX or REGEX
+ Run test cases which name are prefixed with PREFIX
+ or fulfills REGEX
+ skip-test=PREFIX or REGEX
+ Skip test cases which name are prefixed with PREFIX
+ or fulfills REGEX
+ start-from=PREFIX Run test cases starting test prefixed with PREFIX where
+ prefix may be suite.testname or just testname
+ suite[s]=NAME1,..,NAMEN
+ Collect tests in suites from the comma separated
+ list of suite names.
+ The default is: "@DEFAULT_SUITES"
+ skip-rpl Skip the replication test cases.
+ big-test Also run tests marked as "big". Repeat this option
+ twice to run only "big" tests.
+ staging-run Run a limited number of tests (no slow tests). Used
+ for running staging trees with valgrind.
+ enable-disabled Run also tests marked as disabled
+ print-testcases Don't run the tests but print details about all the
+ selected tests, in the order they would be run.
+ skip-test-list=FILE Skip the tests listed in FILE. Each line in the file
+ is an entry and should be formatted as:
+ <TESTNAME> : <COMMENT>
+ force-restart Always restart servers between tests. This makes it
+ easier to see from which test warnings may come from.
+
+Options that specify ports
+
+ mtr-port-base=# Base for port numbers, ports from this number to
+ port-base=# number+9 are reserved. Should be divisible by 10;
+ if not it will be rounded down. May be set with
+ environment variable MTR_PORT_BASE. If this value is
+ set and is not "auto", it overrides build-thread.
+ mtr-build-thread=# Specify unique number to calculate port number(s) from.
+ build-thread=# Can be set in environment variable MTR_BUILD_THREAD.
+ Set MTR_BUILD_THREAD="auto" to automatically aquire
+ a build thread id that is unique to current host
+ port-group-size=N Reserve groups of TCP ports of size N for each MTR thread
+
+
+Options for test case authoring
+
+ record TESTNAME (Re)genereate the result file for TESTNAME
+ check-testcases Check testcases for sideeffects
+ mark-progress Log line number and elapsed time to <testname>.progress
+
+Options that pass on options (these may be repeated)
+
+ mariadbd=ARGS Specify additional arguments to "mariadbd"
+ mysqld Alias for mariadbd.
+ mariadbd-env=VAR=VAL Specify additional environment settings for "mariadbd"
+ mysqld-env Alias for mariadbd-env.
+
+Options to run test on running server
+
+ extern option=value Run only the tests against an already started server
+ the options to use for connection to the extern server
+ must be specified using name-value pair notation
+ For example:
+ ./$0 --extern socket=/tmp/mysqld.sock
+
+Options for debugging the product
+
+ debug Dump trace output for all servers and client programs
+ debug-common Same as debug, but sets 'd' debug flags to
+ "query,info,error,enter,exit"
+ debug-server Use debug version of server, but without turning on
+ tracing
+ max-save-core Limit the number of core files saved (to avoid filling
+ up disks for heavily crashing server). Defaults to
+ $opt_max_save_core. Set its default with
+ MTR_MAX_SAVE_CORE
+ max-save-datadir Limit the number of datadir saved (to avoid filling
+ up disks for heavily crashing server). Defaults to
+ $opt_max_save_datadir. Set its default with
+ MTR_MAX_SAVE_DATADIR
+ max-test-fail Limit the number of test failures before aborting
+ the current test run. Defaults to
+ $opt_max_test_fail, set to 0 for no limit. Set
+ it's default with MTR_MAX_TEST_FAIL
+ core-in-failure Generate a core even if run server is run with valgrind
+HERE
+
+Misc options
+ user=USER User for connecting to mysqld(default: $opt_user)
+ comment=STR Write STR to the output
+ timer Show test case execution time.
+ verbose More verbose output(use multiple times for even more)
+ verbose-restart Write when and why servers are restarted
+ start Only initialize and start the servers, using the
+ startup settings for the first specified test case
+ Example:
+ $0 --start alias &
+ start-and-exit Same as --start, but mysql-test-run terminates and
+ leaves just the server running
+ start-dirty Only start the servers (without initialization) for
+ the first specified test case
+ user-args In combination with start* and no test name, drops
+ arguments to mariadbd except those specified with
+ --mariadbd (if any)
+ wait-all If --start or --start-dirty option is used, wait for all
+ servers to exit before finishing the process
+ fast Run as fast as possible, don't wait for servers
+ to shutdown etc.
+ force-restart Always restart servers between tests
+ parallel=N Run tests in N parallel threads (default 1)
+ Use parallel=auto for auto-setting of N
+ repeat=N Run each test N number of times
+ retry=N Retry tests that fail up to N times (default $opt_retry).
+ Retries are also limited by the maximum number of
+ failures before stopping, set with the --retry-failure
+ option
+ retry-failure=N When using the --retry option to retry failed tests,
+ stop when N failures have occurred (default $opt_retry_failure)
+ reorder Reorder tests to get fewer server restarts
+ help Get this help text
+
+ testcase-timeout=MINUTES Max test case run time (default $opt_testcase_timeout)
+ suite-timeout=MINUTES Max test suite run time (default $opt_suite_timeout)
+ shutdown-timeout=SECONDS Max number of seconds to wait for server shutdown
+ before killing servers (default $opt_shutdown_timeout)
+ warnings Scan the log files for warnings. Use --nowarnings
+ to turn off.
+
+ stop-file=file (also MTR_STOP_FILE environment variable) if this
+ file detected mysql test will not start new tests
+ until the file will be removed.
+ stop-keep-alive=sec (also MTR_STOP_KEEP_ALIVE environment variable)
+ works with stop-file, print messages every sec
+ seconds when mysql test is waiting to removing
+ the file (for buildbot)
+
+ sleep=SECONDS Passed to mysqltest, will be used as fixed sleep time
+ debug-sync-timeout=NUM Set default timeout for WAIT_FOR debug sync
+ actions. Disable facility with NUM=0.
+ gcov Collect coverage information after the test.
+ The result is a gcov file per source and header file.
+ gprof Collect profiling information using gprof.
+ experimental=<file> Refer to list of tests considered experimental;
+ failures will be marked exp-fail instead of fail.
+ timestamp Print timestamp before each test report line
+ timediff With --timestamp, also print time passed since
+ *previous* test started
+ max-connections=N Max number of open connection to server in mysqltest
+ report-times Report how much time has been spent on different
+ phases of test execution.
+ stress=ARGS Run stress test, providing options to
+ mysql-stress-test.pl. Options are separated by comma.
+ xml-report=<file> Output jUnit xml file of the results.
+ tail-lines=N Number of lines of the result to include in a failure
+ report.
+
+Some options that control enabling a feature for normal test runs,
+can be turned off by prepending 'no' to the option, e.g. --notimer.
+This applies to reorder, timer, check-testcases and warnings.
+
+HERE
+ exit(1);
+
+}
+
+sub list_options ($) {
+ my $hash= shift;
+
+ for (keys %$hash) {
+ s/([:=].*|[+!])$//;
+ s/\|/\n--/g;
+ print "--$_\n";
+ }
+
+ exit(1);
+}