Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f5e7f12
MDEV-30281 Comment cleanup for my_load_defaults()
midenok Jul 21, 2026
5b160ac
MDEV-30281 MTR: cut server error log and shutdown warnings
midenok Jul 20, 2026
13e231a
MDEV-30281 MTR conf: document My::ConfigFactory config file handling
midenok Jul 20, 2026
4b6d002
MDEV-30281 MTR conf: options from .cnf file
midenok Jul 21, 2026
e4ba9d3
MDEV-30281 MTR conf: report config file errors as file:line without c…
midenok Jul 21, 2026
f95eb54
MDEV-30281 MTR conf: honor standard --defaults-* options
midenok Jul 21, 2026
0e28e01
MDEV-30281 MTR conf: add --mtr-config-only (-M)
midenok Jul 28, 2026
658610c
MDEV-30281 MTR: allow excluding suites with --suites=!NAME
midenok Jul 22, 2026
6035d7e
MDEV-30281 MTR: add --combination-select to run one combination
midenok Jul 23, 2026
28f1112
MDEV-30281 MTR: add --list-combinations to list a test's combinations
midenok Jul 23, 2026
faf7de4
MDEV-30281 MTR: add --strip-hints to omit crash-report hints
midenok Jul 24, 2026
eb36cf8
MDEV-30281 MTR: add --strip-limits to omit the resource-limits table
midenok Jul 24, 2026
47b6ffe
MDEV-30281 MTR: add --strip-backtrace and the --strip-log alias
midenok Jul 24, 2026
8d29bdf
MDEV-30281 MTR: test --tail-log / --tail-warnings in suite/mtr
midenok Jul 21, 2026
907eac3
MDEV-30281 MTR: add --exec-rr / --exec-gdb to wrap every --exec
midenok Jul 27, 2026
f903210
MDEV-30281 MTR: speed up test collection by caching result-dir listings
midenok Jul 28, 2026
540c580
MDEV-30281 MTR: make the debugger terminal configurable
midenok Jul 28, 2026
6ff0330
MDEV-30281 MTR: add --exit-line to stop a test at a given line
midenok Jul 28, 2026
4844876
MDEV-30281 MTR: fix --server-arg typo in embedded-server comments
midenok Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions client/mysqltest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ static uint my_end_arg= 0;
/* Number of lines of the result to include in failure report */
static uint opt_tail_lines= 0;

/* Stop the test before the command at this line of the test file (0 = off) */
static uint opt_exit_line= 0;

static uint opt_connect_timeout= 0;
static uint opt_wait_for_pos_timeout= 0;
static const uint default_wait_for_pos_timeout= 300;
Expand Down Expand Up @@ -3775,6 +3778,58 @@ static int execute_in_background(char *cmd)
mysqltest command(s) like "remove_file" for that
*/

/*
Find where a wrapper command (e.g. "rr record", "gdb --args") must be
injected into an --exec command line. Leading shell environment-variable
assignments (NAME=VALUE ...) must stay in front of the wrapper so that the
wrapped tool - not the assignment word - is what gets traced/debugged.
Returns the byte offset of the first non-assignment token.
*/

static size_t exec_wrap_offset(const char *cmd)
{
const char *p= cmd;
for (;;)
{
const char *tok;
while (*p == ' ' || *p == '\t')
p++;
tok= p;
/* A leading token counts as an assignment only if it is NAME= */
if (!(my_isalpha(charset_info, (uchar) *p) || *p == '_'))
return (size_t) (tok - cmd);
while (my_isalnum(charset_info, (uchar) *p) || *p == '_')
p++;
if (*p != '=')
return (size_t) (tok - cmd);
/* Skip the VALUE up to unquoted whitespace, honouring quotes/backslash */
for (p++; *p && *p != ' ' && *p != '\t'; )
{
if (*p == '\'')
{
for (p++; *p && *p != '\''; p++)
;
if (*p)
p++;
}
else if (*p == '"')
{
for (p++; *p && *p != '"'; p++)
{
if (*p == '\\' && p[1])
p++;
}
if (*p)
p++;
}
else if (*p == '\\' && p[1])
p+= 2;
else
p++;
}
}
}

void do_exec(struct st_command *command)
{
int error;
Expand Down Expand Up @@ -3824,6 +3879,28 @@ void do_exec(struct st_command *command)
dynstr_append_mem(&ds_cmd, STRING_WITH_LEN(" 2>&1"));
}

/*
--exec-rr / --exec-gdb: mariadb-test-run.pl sets MYSQLTEST_EXEC_WRAP to a
wrapper command (e.g. "rr record") that every --exec is run under. It is
injected after any leading NAME=VALUE assignments so the tool is wrapped,
not the assignment.
*/
{
const char *wrap= getenv("MYSQLTEST_EXEC_WRAP");
if (wrap && *wrap)
{
size_t off= exec_wrap_offset(ds_cmd.str);
DYNAMIC_STRING ds_wrap;
init_dynamic_string(&ds_wrap, "", ds_cmd.length + 64, 256);
dynstr_append_mem(&ds_wrap, ds_cmd.str, off);
dynstr_append_mem(&ds_wrap, wrap, strlen(wrap));
dynstr_append_mem(&ds_wrap, STRING_WITH_LEN(" "));
dynstr_append_mem(&ds_wrap, ds_cmd.str + off, ds_cmd.length - off);
dynstr_set(&ds_cmd, ds_wrap.str);
dynstr_free(&ds_wrap);
}
}

DBUG_PRINT("info", ("Executing '%s' as '%s'",
command->first_argument, ds_cmd.str));

Expand Down Expand Up @@ -10060,6 +10137,11 @@ static struct my_option my_long_options[] =
{"debug-info", 0, "Print some debug info at exit.",
&debug_info_flag, &debug_info_flag,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"exit-line", 'l',
"Stop the test before executing the command at this line of the test "
"file, as if an --exit directive were placed there.",
&opt_exit_line, &opt_exit_line, 0,
GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Connect to host.", &opt_host, &opt_host, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"prologue", 0, "Include SQL before each test case.", &opt_prologue,
Expand Down Expand Up @@ -13099,6 +13181,19 @@ int main(int argc, char **argv)
{
my_bool ok_to_do;
int current_line_inc = 1, processed = 0;

/*
--exit-line: stop before the command at this line of the top-level test
file, exactly as if an --exit directive were placed there. Gated to the
main file so that line numbers of sourced files do not trigger it.
*/
if (opt_exit_line && cur_file == file_stack &&
start_lineno >= opt_exit_line)
{
abort_flag= 1;
break;
}

if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
get_command_type(command);

Expand Down
53 changes: 43 additions & 10 deletions mysql-test/lib/My/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ sub new {
My::Config::Group::OPT->new('OPT'),
] }, $class;
my $F= IO::File->new($path, "<")
or croak "Could not open '$path': $!";
or die "Can't open config file '$path': $!\n";

while ( my $line= <$F> ) {
chomp($line);
Expand All @@ -282,39 +282,72 @@ sub new {
# Magic #! comments
elsif ( $line =~ /^(#\!\S+)(?:\s*(.*?)\s*)?$/) {
my ($magic, $arg)= ($1, $2);
croak "Found magic comment '$magic' outside of group"
die "$path:$.: magic comment '$magic' outside of any group\n"
unless $group_name;

#print "$magic\n";
$self->insert($group_name, $magic, $arg);
}

# Empty lines
elsif ( $line =~ /^$/ ) {
# Empty lines (including whitespace-only)
elsif ( $line =~ /^\s*$/ ) {
# Skip empty lines
next;
}

# !includedir <dir> - include all .cnf files in the directory
elsif ( $line =~ /^\!includedir\s+(.*?)\s*$/ ) {
# Resolve relative to the including file first, like !include; fall back
# to the raw path only if that directory does not exist.
my $dir= dirname($path)."/".$1;
$dir= $1 unless -d $dir;

# A missing includedir is silently ignored, as libmariadb does
if ( opendir(my $dh, $dir) ) {
foreach my $name ( sort grep { /\.cnf$/ } readdir($dh) ) {
my $file= "$dir/$name";
# Skip subdirectories or symlinks named *.cnf, as libmariadb does,
# so a stray non-file entry does not turn into a hard open failure.
next unless -f $file;
$self->append(My::Config->new($file));
}
closedir($dh);
}
}

# !include <filename>
elsif ( $line =~ /^\!include\s*(.*?)\s*$/ ) {
elsif ( $line =~ /^\!include\s+(.*?)\s*$/ ) {
my $include_file_name= dirname($path)."/".$1;

# Check that the file exists relative to path of first config file
if (! -f $include_file_name){
# Try to include file relativ to current dir
$include_file_name= $1;
}
croak "The include file '$include_file_name' does not exist"
die "$path:$.: include file '$include_file_name' does not exist\n"
unless -f $include_file_name;

$self->append(My::Config->new($include_file_name));
}

# <option>
# Any other !directive is ignored
elsif ( $line =~ /^\!/ ) {
next;
}

# A '#' or ';' line before any group is a header/comment (e.g. "#MySQL
# option file"), as libmariadb treats it - not an option. In-group '#foo'
# pseudo-options (below) are unaffected.
elsif ( !$group_name && $line =~ /^\s*[#;]/ ) {
next;
}

# <option> (a leading '#' makes it a pseudo-option, e.g. #galera_port,
# used in .cnf templates as an addressable name - not a comment)
elsif ( $line =~ /^(#?[\w-]+)\s*$/ ) {
my $option= $1;

croak "Found option '$option' outside of group"
die "$path:$.: option '$option' outside of any group\n"
unless $group_name;

#print "$option\n";
Expand All @@ -326,7 +359,7 @@ sub new {
my $option= $1;
my $value= $2;

croak "Found option '$option=$value' outside of group"
die "$path:$.: option '$option=$value' outside of any group\n"
unless $group_name;

#print "$option=$value\n";
Expand All @@ -346,7 +379,7 @@ sub new {
$self->insert($group_name, $option, $value);
}
else {
croak "Unexpected line '$line' found in '$path'";
die "$path:$.: unexpected line '$line'\n";
}
}
undef $F; # Close the file
Expand Down
Loading