Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package os-autoinst for openSUSE:Factory checked in at 2026-04-30 20:32:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/os-autoinst (Old) and /work/SRC/openSUSE:Factory/.os-autoinst.new.30200 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "os-autoinst" Thu Apr 30 20:32:18 2026 rev:592 rq:1350258 version:5.1777537682.913fce0 Changes: -------- --- /work/SRC/openSUSE:Factory/os-autoinst/os-autoinst.changes 2026-04-28 11:55:44.903951355 +0200 +++ /work/SRC/openSUSE:Factory/.os-autoinst.new.30200/os-autoinst.changes 2026-04-30 20:33:11.257654654 +0200 @@ -1,0 +2,11 @@ +Thu Apr 30 08:28:11 UTC 2026 - [email protected] + +- Update to version 5.1777537682.913fce0: + * fix: re-install serial marker hook after console reset + * fix: support pretty_serial_markers in script_output regex + * feat: disable storage check in CI environments + * fix(test): handle D-Bus AccessDenied in Open vSwitch test + * fix: Avoid restarting openvswitch/NM after OVS bridge setup + * refactor: import IO::Socket::UNIX explicitly wherever is used + +------------------------------------------------------------------- Old: ---- os-autoinst-5.1776943886.0619ca6.obscpio New: ---- os-autoinst-5.1777537682.913fce0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ os-autoinst-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.O7PwaS/_old 2026-04-30 20:33:12.529706989 +0200 +++ /var/tmp/diff_new_pack.O7PwaS/_new 2026-04-30 20:33:12.529706989 +0200 @@ -18,7 +18,7 @@ %define short_name os-autoinst-devel Name: %{short_name}-test -Version: 5.1776943886.0619ca6 +Version: 5.1777537682.913fce0 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-openvswitch-test.spec ++++++ --- /var/tmp/diff_new_pack.O7PwaS/_old 2026-04-30 20:33:12.585709293 +0200 +++ /var/tmp/diff_new_pack.O7PwaS/_new 2026-04-30 20:33:12.589709457 +0200 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst-openvswitch Name: %{short_name}%{?name_ext} -Version: 5.1776943886.0619ca6 +Version: 5.1777537682.913fce0 Release: 0 Summary: test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-test.spec ++++++ --- /var/tmp/diff_new_pack.O7PwaS/_old 2026-04-30 20:33:12.641711597 +0200 +++ /var/tmp/diff_new_pack.O7PwaS/_new 2026-04-30 20:33:12.641711597 +0200 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst Name: %{short_name}%{?name_ext} -Version: 5.1776943886.0619ca6 +Version: 5.1777537682.913fce0 Release: 0 Summary: test package for os-autoinst License: GPL-2.0-or-later ++++++ os-autoinst.spec ++++++ --- /var/tmp/diff_new_pack.O7PwaS/_old 2026-04-30 20:33:12.689713571 +0200 +++ /var/tmp/diff_new_pack.O7PwaS/_new 2026-04-30 20:33:12.689713571 +0200 @@ -17,7 +17,7 @@ Name: os-autoinst -Version: 5.1776943886.0619ca6 +Version: 5.1777537682.913fce0 Release: 0 Summary: OS-level test automation License: GPL-2.0-or-later @@ -325,6 +325,7 @@ %files -f %{name}.files %defattr(-,root,root) +%doc README.md doc/architecture.md doc/backend_vars.md doc/backends.md doc/memorydumps.md doc/networking.md %{_docdir}/os-autoinst %dir %{_prefix}/lib/os-autoinst %{_prefix}/lib/os-autoinst/debugviewer ++++++ os-autoinst-5.1776943886.0619ca6.obscpio -> os-autoinst-5.1777537682.913fce0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/.mergify.yml new/os-autoinst-5.1777537682.913fce0/.mergify.yml --- old/os-autoinst-5.1776943886.0619ca6/.mergify.yml 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/.mergify.yml 2026-04-30 10:28:02.000000000 +0200 @@ -14,7 +14,7 @@ # "unresolvable" is not reported in general: # https://trello.com/c/0N3jHq5M/2257-report-back-to-scm-when-build-results-arent-failed-and-succeeded # So we need to require the number of tests explicitly: - - "#check-success>=29" + - "#check-success>=28" - "#check-failure=0" - "#check-pending=0" - linear-history diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/.perlcriticrc new/os-autoinst-5.1777537682.913fce0/.perlcriticrc --- old/os-autoinst-5.1776943886.0619ca6/.perlcriticrc 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/.perlcriticrc 2026-04-30 10:28:02.000000000 +0200 @@ -1,6 +1,6 @@ theme = community + openqa severity = 4 -include = strict ValuesAndExpressions::ProhibitInterpolationOfLiterals CodeLayout::ProhibitParensWithBuiltins BuiltinFunctions::RequireBlockMap BuiltinFunctions::ProhibitStringySplit ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions +include = strict ValuesAndExpressions::ProhibitInterpolationOfLiterals CodeLayout::ProhibitParensWithBuiltins BuiltinFunctions::RequireBlockMap BuiltinFunctions::ProhibitStringySplit ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions ValuesAndExpressions::RequireQuotedHeredocTerminator verbose = ::warning file=%f,line=%l,col=%c,title=%m - severity %s::[%p] %e\n diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/OpenQA/Isotovideo/Interface.pm new/os-autoinst-5.1777537682.913fce0/OpenQA/Isotovideo/Interface.pm --- old/os-autoinst-5.1776943886.0619ca6/OpenQA/Isotovideo/Interface.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/OpenQA/Isotovideo/Interface.pm 2026-04-30 10:28:02.000000000 +0200 @@ -9,7 +9,7 @@ # -> increment on every change of such APIs # -> never move that variable to another place (when refactoring) # because it may be accessed by the tests itself -our $version = 52; +our $version = 55; # major version of the (web socket) API relevant to the developer mode # -> increment when making non-backward compatible changes to that API diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/OpenQA/Qemu/Proc.pm new/os-autoinst-5.1777537682.913fce0/OpenQA/Qemu/Proc.pm --- old/os-autoinst-5.1776943886.0619ca6/OpenQA/Qemu/Proc.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/OpenQA/Qemu/Proc.pm 2026-04-30 10:28:02.000000000 +0200 @@ -27,6 +27,7 @@ use File::Which; use Mojo::JSON qw(encode_json decode_json); use Mojo::File 'path'; +use IO::Socket::UNIX; use OpenQA::Qemu::BlockDevConf; use OpenQA::Qemu::ControllerConf; use OpenQA::Qemu::DriveDevice 'QEMU_IMAGE_FORMAT'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/autotest.pm new/os-autoinst-5.1777537682.913fce0/autotest.pm --- old/os-autoinst-5.1776943886.0619ca6/autotest.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/autotest.pm 2026-04-30 10:28:02.000000000 +0200 @@ -301,7 +301,11 @@ } my $msg = "error on $script: $err"; bmwqemu::fctwarn($msg); - bmwqemu::serialize_state(component => 'tests', msg => "unable to load $script, check the log for the cause (e.g. syntax error)"); + my ($is_missing_module) = $err =~ /(Can't locate .+ in \@INC)/; + my ($state_msg) = $is_missing_module + ? "Missing Perl module while loading $script: $is_missing_module" + : "unable to load $script, check the log for the cause (e.g. syntax error)"; + bmwqemu::serialize_state(component => 'tests', msg => $state_msg, result => 'incomplete'); die $msg; } $test = $name->new($category); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/backend/qemu.pm new/os-autoinst-5.1777537682.913fce0/backend/qemu.pm --- old/os-autoinst-5.1776943886.0619ca6/backend/qemu.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/backend/qemu.pm 2026-04-30 10:28:02.000000000 +0200 @@ -11,7 +11,6 @@ use File::Which; use Time::HiRes qw(sleep gettimeofday); use Time::Seconds; -use IO::Socket::UNIX 'SOCK_STREAM'; use IO::Handle; use POSIX qw(strftime :sys_wait_h mkfifo); use Mojo::File 'path'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/backend/vagrant.pm new/os-autoinst-5.1777537682.913fce0/backend/vagrant.pm --- old/os-autoinst-5.1776943886.0619ca6/backend/vagrant.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/backend/vagrant.pm 2026-04-30 10:28:02.000000000 +0200 @@ -39,22 +39,22 @@ $self->{vagrantfile} = $path->child('Vagrantfile'); bmwqemu::diag("Writing Vagrantfile to $self->{vagrantfile}"); - my $vagrant_file_contents = <<END; + my $vagrant_file_contents = <<"END"; Vagrant.configure("2") do |config| config.vm.box = "$self->{box_name}" END if (defined($self->{box_url})) { - $vagrant_file_contents .= <<END; + $vagrant_file_contents .= <<"END"; config.vm.box_url = "$self->{box_url}" END } - $vagrant_file_contents .= <<END; + $vagrant_file_contents .= <<'END'; config.vm.synced_folder ".", "/vagrant", disabled: true END if ($self->{provider} eq 'virtualbox') { - $vagrant_file_contents .= <<END; + $vagrant_file_contents .= <<"END"; config.vm.provider "virtualbox" do |v| v.memory = $vars->{QEMURAM} v.cpus = $vars->{QEMUCPUS} @@ -65,7 +65,7 @@ mkdir $self->{libvirt_storage_pool_path}; $self->{libvirt_pool_name} = 'vagrant' . int rand 100_000; - $vagrant_file_contents .= <<END; + $vagrant_file_contents .= <<"END"; config.vm.provider :libvirt do |libvirt| libvirt.cpus = $vars->{QEMUCPUS} libvirt.memory = $vars->{QEMURAM} @@ -76,7 +76,7 @@ die "got an unknown vagrant provider $self->{provider}"; } - $vagrant_file_contents .= <<END; + $vagrant_file_contents .= <<'END'; end END diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/basetest.pm new/os-autoinst-5.1777537682.913fce0/basetest.pm --- old/os-autoinst-5.1776943886.0619ca6/basetest.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/basetest.pm 2026-04-30 10:28:02.000000000 +0200 @@ -334,6 +334,14 @@ if (!@{$self->{details}} || ($self->{details}->[-1]->{result} || '') ne 'fail') { $self->take_screenshot(); } + if (!$internal && $e =~ /Can't locate .+ in \@INC/) { + my $msg = "# Test died with missing dependency: $e"; + bmwqemu::fctinfo($msg); + $self->record_resultfile('Failed', $msg, result => 'fail'); + $self->{fatal_failure} = 1; + bmwqemu::serialize_state(component => 'tests', msg => "Missing Perl module: $e", result => 'incomplete'); + $died = 1; + } # show a text result with the die message unless the die was internally generated if (!$internal) { my $msg = "# Test died: $e"; @@ -429,11 +437,30 @@ # take screenshot for documentation (screenshot does not represent fail itself) $self->take_screenshot() unless (testapi::is_serial_terminal); + my $pretty = testapi::get_var('PRETTY_SERIAL_MARKER') || testapi::get_var('HIDE_MARKER_EVALUATION'); + my $internal = $args{internal_marker}; + my $output_string = $string; + my $captured_val; + + if ($internal && $args{marker_pattern}) { + my $pattern = $args{marker_pattern}; + my $is_regex = ref $pattern eq 'Regexp'; + $captured_val = $1 if $is_regex && $string =~ /$pattern/; + + if ($pretty) { + my $search = $is_regex ? $pattern : qr/\Q$pattern\E/; + $output_string =~ s/$search\s*\z//m; + } + } + my $output = ''; $output .= "# Command: $args{command}\n" if defined $args{command}; - $output .= "# wait_serial expected: $ref\n"; + $output .= "# wait_serial expected: $ref\n" unless $internal && $pretty; $output .= "# Result:\n"; - $output .= "$string\n"; + $output .= "$output_string\n"; + if (defined $captured_val && $args{capture_name}) { + $output .= "# $args{capture_name}: $captured_val\n"; + } $self->record_resultfile('wait_serial', $output, result => $res); return undef; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/bmwqemu.pm new/os-autoinst-5.1777537682.913fce0/bmwqemu.pm --- old/os-autoinst-5.1776943886.0619ca6/bmwqemu.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/bmwqemu.pm 2026-04-30 10:28:02.000000000 +0200 @@ -170,6 +170,7 @@ } sub _abort_if_storage_limit_exceeded () { + return undef if $ENV{CI} || $ENV{GITHUB_ACTIONS}; my $keep_free = $vars{STORAGE_KEEP_FREE_RATIO} // STORAGE_KEEP_FREE_RATIO; return undef if $keep_free <= 0; my $numdisks = $vars{NUMDISKS} // default_numdisks(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/dist/rpm/os-autoinst.spec new/os-autoinst-5.1777537682.913fce0/dist/rpm/os-autoinst.spec --- old/os-autoinst-5.1776943886.0619ca6/dist/rpm/os-autoinst.spec 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/dist/rpm/os-autoinst.spec 2026-04-30 10:28:02.000000000 +0200 @@ -325,6 +325,7 @@ %files -f %{name}.files %defattr(-,root,root) +%doc README.md doc/architecture.md doc/backend_vars.md doc/backends.md doc/memorydumps.md doc/networking.md %{_docdir}/os-autoinst %dir %{_prefix}/lib/os-autoinst %{_prefix}/lib/os-autoinst/debugviewer diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/distribution.pm new/os-autoinst-5.1777537682.913fce0/distribution.pm --- old/os-autoinst-5.1776943886.0619ca6/distribution.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/distribution.pm 2026-04-30 10:28:02.000000000 +0200 @@ -15,6 +15,7 @@ $self->{serial_failures} = []; $self->{autoinst_failures} = []; $self->{_serial_marker_level} = {}; + $self->{_serial_marker_hook_installed} = {}; =head2 serial_term_prompt @@ -155,7 +156,7 @@ if ($level == 3) { testapi::query_isotovideo('backend_clear_serial_buffer', {}); testapi::type_string "$cmd\n", max_interval => $args{max_interval}; - my $res = testapi::wait_serial(qr/OA:DONE-[0-9a-f]{4}-(\d+)-/, timeout => $args{timeout}, quiet => $args{quiet}, record_command => $cmd); + my $res = testapi::wait_serial(qr/OA:DONE-[0-9a-f]{4}-(\d+)-/, timeout => $args{timeout}, quiet => $args{quiet}, record_command => $cmd, internal_marker => 1, capture_name => 'Exit code'); return unless $res; return ($res =~ /OA:DONE-[0-9a-f]{4}-(\d+)-/)[0]; } @@ -169,7 +170,7 @@ if (testapi::is_serial_terminal) { testapi::type_string "$cmd", max_interval => $args{max_interval}; testapi::type_string $marker, max_interval => $args{max_interval}; - testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet}, buffer_size => (length $cmd) + 128) + testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet}, buffer_size => (length $cmd) + 128, internal_marker => 1) or _handle_cmd_typing_error($cmd, \%args); testapi::type_string "\n", max_interval => $args{max_interval}; } @@ -178,7 +179,7 @@ testapi::type_string "$marker > /dev/$testapi::serialdev\n", max_interval => $args{max_interval}; } } - my $res = testapi::wait_serial($wait_pattern, timeout => $args{timeout}, quiet => $args{quiet}, record_command => $cmd); + my $res = testapi::wait_serial($wait_pattern, timeout => $args{timeout}, quiet => $args{quiet}, record_command => $cmd, internal_marker => 1, capture_name => 'Exit code'); return unless $res; return ($res =~ $wait_pattern)[0]; } @@ -215,13 +216,13 @@ my $marker = "& echo $str-\$!-" . ($args{output} ? "Comment: $args{output}" : ''); if (testapi::is_serial_terminal) { testapi::type_string $marker; - testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet}) or _handle_cmd_typing_error($cmd, \%args); + testapi::wait_serial($cmd . $marker, no_regex => 1, quiet => $args{quiet}, internal_marker => 1) or _handle_cmd_typing_error($cmd, \%args); testapi::type_string "\n"; } else { testapi::type_string "$marker > /dev/$testapi::serialdev\n"; } - my $res = testapi::wait_serial(qr/$str-\d+-/, quiet => $args{quiet}); + my $res = testapi::wait_serial(qr/$str-\d+-/, quiet => $args{quiet}, internal_marker => 1); die 'PID marker not found' unless ($res =~ m/$str-(\d+)-/); return $1; } @@ -248,7 +249,7 @@ testapi::send_key 'ret'; } if ($str) { - return testapi::wait_serial($str, $wait); + return testapi::wait_serial($str, $wait, internal_marker => 1); } return; } @@ -307,7 +308,7 @@ testapi::wait_serial('> ', no_regex => 1, quiet => $args{quiet}); testapi::type_string "$script\n$heretag\n"; testapi::wait_serial("> $heretag", no_regex => 1, quiet => $args{quiet}); - testapi::wait_serial("$marker-0-", quiet => $args{quiet}); + testapi::wait_serial(qr/$marker-(0)-/, capture_name => 'Exit code', quiet => $args{quiet}, internal_marker => 1); } elsif ($args{type_command}) { my $cat = "cat - > $script_path;"; @@ -339,7 +340,7 @@ else { testapi::type_string "($run_script) | tee /dev/$testapi::serialdev\n"; } - my $output = testapi::wait_serial("SCRIPT_FINISHED$marker-\\d+-", timeout => $args{timeout}, record_output => 1, quiet => $args{quiet}) + my $output = testapi::wait_serial(qr/SCRIPT_FINISHED$marker-(\d+)-/, capture_name => 'Exit code', timeout => $args{timeout}, record_output => 1, quiet => $args{quiet}, internal_marker => 1) || croak "script timeout: $script"; if ($output !~ "SCRIPT_FINISHED$marker-0-") { @@ -353,7 +354,7 @@ } # and the markers including internal exit catcher - my $out = $output =~ /$marker(?<expected_output>.+)SCRIPT_FINISHED$marker-\d+-/s ? $+ : ''; + my $out = $output =~ /(?:^|\r?\n)$marker\r?\n(?<expected_output>.*?)SCRIPT_FINISHED$marker-\d+-/s ? $+ : ''; # trim whitespaces $out =~ s/^\s+|\s+$//g; return $out; @@ -452,6 +453,11 @@ $self->{_serial_marker_hook_installed}->{$console} = 1; } +sub reset_console_cache ($self, $console) { + delete $self->{_serial_marker_level}->{$console}; + delete $self->{_serial_marker_hook_installed}->{$console}; +} + =head2 _detect_serial_marker_capability _detect_serial_marker_capability() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/doc/backend_vars.md new/os-autoinst-5.1777537682.913fce0/doc/backend_vars.md --- old/os-autoinst-5.1776943886.0619ca6/doc/backend_vars.md 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/doc/backend_vars.md 2026-04-30 10:28:02.000000000 +0200 @@ -60,8 +60,8 @@ | GIT_CACHE_DIR | string | | If set enables locally caching Git repositories in the specified directory when handling Git URLs in variables like `CASEDIR` and wheels | | ENABLE_MODERN_PERL_FEATURES | boolean | 0 | Enables use of modern Perl features in test modules avoiding the need to use e.g. `use Mojo::Base 'basetest', -signatures;` in all test modules. This variable must be set before invoking `autotest::loadtest`. It only applies to the test modules themselves. It does *not* apply to e.g. `main.pm` and other Perl modules used via e.g. `use some::module`. | | _HIDE_SECRETS_REGEX | string | | If set, any test variables whose **NAME** (key) matches the specified regular expression, in addition to the default '^_SECRET_' and '_PASSWORD', are excluded from being saved into vars.json or further processing. For example, to hide all variables starting with 'SCC_REGCODE', use '^SCC_REGCODE'. | -| STORAGE_KEEP_FREE_RATIO | float | 0.2 | Ratio of total storage space to keep free (e.g. 0.2 for 20%). If the total requested HDD size exceeds the available space while keeping this ratio of total storage size free, the job is aborted early with "incomplete" status. Set to 0 to disable this check. | -| STORAGE_KEEP_FREE_GB | integer | 50 | Absolute free space threshold in GiB. If the total requested HDD size leaves less than this amount of free space, the job can only run if the relative check (`STORAGE_KEEP_FREE_RATIO`) is also satisfied. Set to 0 to disable the absolute threshold. | +| STORAGE_KEEP_FREE_RATIO | float | 0.2 | Ratio of total storage space to keep free (e.g. 0.2 for 20%). If the total requested HDD size exceeds the available space while keeping this ratio of total storage size free, the job is aborted early with "incomplete" status. Set to 0 to disable this check. Automatically disabled in CI environments (if `CI` or `GITHUB_ACTIONS` is set in the environment). | +| STORAGE_KEEP_FREE_GB | integer | 50 | Absolute free space threshold in GiB. If the total requested HDD size leaves less than this amount of free space, the job can only run if the relative check (`STORAGE_KEEP_FREE_RATIO`) is also satisfied. Set to 0 to disable the absolute threshold. Automatically disabled in CI environments (if `CI` or `GITHUB_ACTIONS` is set in the environment). | | FAIL_ON_ALWAYS_ROLLBACK_NOT_SUPPORTED | boolean | 0 | Fail explicitly if a test module is scheduled with the `always_rollback` flag but snapshots are not supported by the backend. | | | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/script/os-autoinst-setup-multi-machine new/os-autoinst-5.1777537682.913fce0/script/os-autoinst-setup-multi-machine --- old/os-autoinst-5.1776943886.0619ca6/script/os-autoinst-setup-multi-machine 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/script/os-autoinst-setup-multi-machine 2026-04-30 10:28:02.000000000 +0200 @@ -44,6 +44,7 @@ pkgs=(openvswitch os-autoinst-openvswitch firewalld libcap-progs) [[ $network == NetworkManager ]] && pkgs+=(NetworkManager-ovs) rpm -q ${pkgs[*]} > /dev/null || retry -e -s 30 -r 7 -- sh -c "zypper ref && zypper -n in ${pkgs[*]}" + systemctl enable --now openvswitch os-autoinst-openvswitch } configure_firewalld() { @@ -155,14 +156,17 @@ } setup_multi_machine_with_networkmanager() { - # Restart NM to load ovs plugin - systemctl restart NetworkManager # Delete any previous connections nmcli con | grep -oP 'ovs-(interface|port|bridge|slave)-[\w-]+' | xargs -r nmcli con del || true + # Clear NM state and timestamps + systemctl stop NetworkManager + [[ -d /var/lib/NetworkManager ]] && rm -vf /var/lib/NetworkManager/{timestamps,NetworkManager.state} + # Restart NM to load ovs plugin + systemctl start NetworkManager # Create bridge, port and interface connection - nmcli con add type ovs-bridge con.int "$bridge" + nmcli con add type ovs-bridge con.int "$bridge" ipv4.dad-timeout 0 nmcli con add type ovs-port con.int "$bridge" con.master "$bridge" - nmcli con add type ovs-interface con.int "$bridge" con.master "$bridge" ipv4.method manual ipv4.address 10.0.2.2/15 ipv6.method manual ipv6.address fec0::2/63 ethernet.mtu "$mtu" con.zone "$zone" + nmcli con add type ovs-interface con.int "$bridge" con.master "$bridge" ipv4.method manual ipv4.address 10.0.2.2/15 ipv4.dad-timeout 0 ipv6.method manual ipv6.address fec0::2/63 ethernet.mtu "$mtu" con.zone "$zone" # Create tap interfaces for i in 0 $( seq 1 "$instances" @@ -211,10 +215,23 @@ wicked ifup "$ethernet" "$bridge" } +delete_tap_ifaces() { + ifaces=($(ls /sys/class/net/ | xargs -I {} sh -c 'if [ -f /sys/class/net/{}/tun_flags ]; then echo {}; fi' | sort -V)) + for iface in "${ifaces[@]}"; do ip link delete $iface; done +} + +cleanup_openvswitch_db() { + # Delete existing ovs db and its lock but do not delete any ovs db backup + systemctl stop openvswitch os-autoinst-openvswitch + [[ -d /var/lib/openvswitch/ ]] && rm -f /var/lib/openvswitch/{conf.db,.conf.db.~lock~} + systemctl is-enabled openvswitch && systemctl start openvswitch + systemctl is-enabled os-autoinst-openvswitch && systemctl start os-autoinst-openvswitch +} + configure_openvswitch() { echo "OS_AUTOINST_USE_BRIDGE=$bridge" > /etc/sysconfig/os-autoinst-openvswitch systemctl enable os-autoinst-openvswitch - systemctl restart openvswitch os-autoinst-openvswitch + systemctl restart os-autoinst-openvswitch } determine_ethernet_interface() { @@ -239,7 +256,8 @@ else configure_nftables fi - systemctl enable --now openvswitch + cleanup_openvswitch_db + delete_tap_ifaces case "$network" in wicked) setup_multi_machine_with_wicked diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/03-testapi.t new/os-autoinst-5.1777537682.913fce0/t/03-testapi.t --- old/os-autoinst-5.1776943886.0619ca6/t/03-testapi.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/03-testapi.t 2026-04-30 10:28:02.000000000 +0200 @@ -791,46 +791,46 @@ $mock_testapi->redefine(hashed_string => 'XXX'); $mock_testapi->redefine(is_serial_terminal => sub { return $is_serial_terminal }); - $mock_testapi->redefine(wait_serial => "XXXfoo\nSCRIPT_FINISHEDXXX-0-"); + $mock_testapi->redefine(wait_serial => "XXX\nfoo\nSCRIPT_FINISHEDXXX-0-"); $bmwqemu::vars{OFFLINE_SUT} = 1; is(script_output('echo foo'), 'foo', 'sucessfull retrieves output of script'); $bmwqemu::vars{OFFLINE_SUT} = 0; - $mock_testapi->redefine(wait_serial => 'SCRIPT_FINISHEDXXX-0-'); + $mock_testapi->redefine(wait_serial => "XXX\nSCRIPT_FINISHEDXXX-0-"); is(script_output('foo'), '', 'calling script_output does not fail if script returns with success'); - $mock_testapi->redefine(wait_serial => "This is simulated output on the serial device\nXXXfoo\nSCRIPT_FINISHEDXXX-0-\nand more here"); + $mock_testapi->redefine(wait_serial => "This is simulated output on the serial device\nXXX\nfoo\nSCRIPT_FINISHEDXXX-0-\nand more here"); is(script_output('echo foo'), 'foo', 'script_output return only the actual output of the script'); - $mock_testapi->redefine(wait_serial => "XXXfoo\nSCRIPT_FINISHEDXXX-1-"); + $mock_testapi->redefine(wait_serial => "XXX\nfoo\nSCRIPT_FINISHEDXXX-1-"); is(script_output('echo foo', undef, proceed_on_failure => 1), 'foo', 'proceed_on_failure=1 retrieves retrieves output of script and do not die'); - $mock_testapi->redefine(wait_serial => sub { return 'none' if (shift !~ m/SCRIPT_FINISHEDXXX-\\d\+-/) }); + $mock_testapi->redefine(wait_serial => sub { return 'none' unless $_[0] =~ /SCRIPT_FINISHEDXXX/; return; }); throws_ok { script_output('timeout'); } qr/timeout/, 'die expected with timeout'; subtest 'script_output check error codes' => sub { for my $ret ((1, 10, 100, 255)) { - $mock_testapi->redefine(wait_serial => "XXXfoo\nSCRIPT_FINISHEDXXX-$ret-"); + $mock_testapi->redefine(wait_serial => "XXX\nfoo\nSCRIPT_FINISHEDXXX-$ret-"); throws_ok { script_output('false'); } qr/script failed/, "script_output die expected on exitcode $ret"; } }; + my @wait_serial_args; $mock_testapi->redefine(wait_serial => sub ($regex, %args) { - is($args{quiet}, undef, 'Check default quiet argument'); - if ($regex =~ m/SCRIPT_FINISHEDXXX-\\d\+-/) { - is($args{timeout}, 30, 'pass $wait value to wait_serial'); - } - return "XXXfoo\nSCRIPT_FINISHEDXXX-0-"; + push @wait_serial_args, \%args; + return "XXX\nfoo\nSCRIPT_FINISHEDXXX-0-"; }); - is(script_output('echo foo', 30), 'foo', ''); - is(script_output('echo foo', timeout => 30), 'foo', ''); - - $mock_testapi->redefine(wait_serial => sub ($regex, %args) { - is($args{quiet}, 1, 'Check quiet argument'); - return "XXXfoo\nSCRIPT_FINISHEDXXX-0-"; - }); - is(script_output('echo foo', quiet => 1), 'foo', ''); - $mock_testapi->redefine(wait_serial => "This is a simulated output on the serial dev\nXXXfoo\nSCRIPT_FINISHEDXXX-0-\nand more here"); + for my $t ( + {args => [30], timeout => 30, quiet => undef, msg => 'positional wait'}, + {args => [timeout => 30], timeout => 30, quiet => undef, msg => 'keyword timeout'}, + {args => [quiet => 1], timeout => undef, quiet => 1, msg => 'keyword quiet'}) { + @wait_serial_args = (); + is script_output('echo foo', $t->{args}->@*), 'foo', "$t->{msg}: returns expected output"; + is $wait_serial_args[-1]->{timeout}, $t->{timeout}, "$t->{msg}: correct timeout passed to final wait_serial call"; + my @inconsistent_quiet = grep { ($_->{quiet} // 0) != ($t->{quiet} // 0) } @wait_serial_args; + is_deeply \@inconsistent_quiet, [], "$t->{msg}: quiet argument is consistent across all calls"; + } + $mock_testapi->redefine(wait_serial => "This is a simulated output on the serial dev\nXXX\nfoo\nSCRIPT_FINISHEDXXX-0-\nand more here"); is script_output('echo foo', type_command => 0), 'foo', 'script_output with type_command => 0 output in a file'; } @@ -990,7 +990,7 @@ $mock_testapi->redefine(script_output => 'output'); $mock_testapi->redefine(wait_serial => sub ($regex, %args) { is($args{quiet}, 1, 'Check default quiet argument'); - return "XXXfoo\nSCRIPT_FINISHEDXXX-0-"; + return "XXX\nfoo\nSCRIPT_FINISHEDXXX-0-"; }); is(script_output('echo foo', 30), 'foo', 'script_output with _QUIET_SCRIPT_CALLS=1 expects command output'); is(script_run('true'), '0', 'script_run with _QUIET_SCRIPT_CALLS=1'); @@ -999,7 +999,7 @@ $mock_testapi->redefine(wait_serial => sub ($regex, %args) { is($args{quiet}, 0, 'Check default quiet argument'); - return "XXXfoo\nSCRIPT_FINISHEDXXX-0-"; + return "XXX\nfoo\nSCRIPT_FINISHEDXXX-0-"; }); is(script_output('echo foo', quiet => 0), 'foo', 'script_output with _QUIET_SCRIPT_CALLS=1 and quiet=>0'); is(script_run('true', quiet => 0), '0', 'script_run with _QUIET_SCRIPT_CALLS=1 and quiet=>0'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/04-check_vars_docu.t new/os-autoinst-5.1777537682.913fce0/t/04-check_vars_docu.t --- old/os-autoinst-5.1776943886.0619ca6/t/04-check_vars_docu.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/04-check_vars_docu.t 2026-04-30 10:28:02.000000000 +0200 @@ -63,7 +63,7 @@ } sub write_doc () { - my $data = <<EO_HEADER; + my $data = <<'EO_HEADER'; Supported variables per backend =============================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/05-distribution.t new/os-autoinst-5.1777537682.913fce0/t/05-distribution.t --- old/os-autoinst-5.1776943886.0619ca6/t/05-distribution.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/05-distribution.t 2026-04-30 10:28:02.000000000 +0200 @@ -137,6 +137,21 @@ throws_ok { $d->script_run('foo') } qr/typing command 'foo' timed out/, 'typing error handled in Level 1'; }; +subtest 'serial_marker_reinstall_cached_level' => sub { + my $d = distribution->new; + my $mock_testapi = Test::MockModule->new('testapi'); + $mock_testapi->redefine(current_console => sub { 'test-console' }); + my $typed = ''; + $mock_testapi->redefine(type_string => sub { $typed .= $_[0] }); + + $d->{_serial_marker_level}->{'test-console'} = 2; + delete $d->{_serial_marker_hook_installed}->{'test-console'}; + + is $d->_detect_serial_marker_capability(), 2, 'Returns cached level 2'; + like $typed, qr/PROMPT_COMMAND=/, 'Calls install_serial_marker_hook (types PROMPT_COMMAND)'; + ok $d->{_serial_marker_hook_installed}->{'test-console'}, 'Hook marked as installed'; +}; + subtest 'reboot_safety' => sub { my $d = distribution->new; my $mock_testapi = Test::MockModule->new('testapi'); @@ -174,11 +189,21 @@ like $typed_string, qr/bar\n/, 'Command typed'; # Case 2: manual clear (e.g. if we know it was lost) - delete $d->{_serial_marker_hook_installed}->{'test-console'}; + $d->reset_console_cache('test-console'); $typed_string = ''; $d->script_run('baz'); - like $typed_string, qr/PROMPT_COMMAND=.*OA:DONE/, 'Re-install if missing'; - like $typed_string, qr/baz\n/, 'Command typed after re-install'; + like $typed_string, qr/PROMPT_COMMAND=.*OA:DONE/, 'Re-detect and re-install after resetting the console cache'; + like $typed_string, qr/baz\n/, 'Command typed after re-installation'; + + # Case 3: select_console triggers reset + $d->{_serial_marker_hook_installed}->{'test-console'} = 1; + $typed_string = ''; + $mock_testapi->redefine(query_isotovideo => sub { return {activated => 1} }); + $testapi::distri = $d; + + testapi::select_console('test-console'); + $d->script_run('qux'); + like $typed_string, qr/BASH:/, 'Re-detect after select_console re-activates the console'; }; subtest 'sut_marker' => sub { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/12-bmwqemu.t new/os-autoinst-5.1777537682.913fce0/t/12-bmwqemu.t --- old/os-autoinst-5.1776943886.0619ca6/t/12-bmwqemu.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/12-bmwqemu.t 2026-04-30 10:28:02.000000000 +0200 @@ -34,31 +34,30 @@ subtest 'log_call' => sub { require bmwqemu; - sub log_call_test { + sub log_call_test () { bmwqemu::log_call(foo => "bar\tbaz\rboo\n"); } stderr_like(\&log_call_test, qr{\Q<<< main::log_call_test(foo="bar\tbaz\rboo\n")}, 'log_call escapes special characters'); - sub log_call_test_escape_key { + sub log_call_test_escape_key () { bmwqemu::log_call("foo\nbar" => "bar\tbaz\rboo\n"); } stderr_like(\&log_call_test_escape_key, qr{\Q<<< main::log_call_test_escape_key("foo\nbar"="bar\tbaz\rboo\n")}, 'log_call escapes special characters'); - sub log_call_test_single { + sub log_call_test_single () { bmwqemu::log_call("bar\tbaz\rboo\n"); } stderr_like(\&log_call_test_single, qr{\Q<<< main::log_call_test_single("bar\tbaz\rboo\n")}, 'log_call escapes special characters'); - sub log_call_indent { + sub log_call_indent () { my $lines = ['a', ['b']]; bmwqemu::log_call(test => $lines); } stderr_like(\&log_call_indent, qr{\Q<<< main::log_call_indent(test=[\E\n\Q "a",\E\n\Q [\E\n\Q "b"\E\n\Q ]\E\n\Q ])}, 'log_call auto indentation'); - sub log_call_test_secret { - my (%args) = @_; - # Use @_ instead of %args to keep the order - bmwqemu::log_call(@_, ($args{secret} ? (-masked => $args{text}) : ())); + sub log_call_test_secret (@args) { + my %args = @args; + bmwqemu::log_call(@args, ($args{secret} ? (-masked => $args{text}) : ())); return; } stderr_like { log_call_test_secret(text => "password\n", secret => 1) } qr{\Q<<< main::log_call_test_secret(text="[masked]", secret=1)}, 'log_call hides sensitive info'; @@ -207,6 +206,10 @@ desc => 'fails if both relative and absolute thresholds exceeded (e.g. 50GB requested on 100GB disk with 60GB avail leaves 10GB free)'}, {hdd_size_gb => 50, total_storage => 100, avail_storage => 60, expect => 'pass', extra_vars => {STORAGE_KEEP_FREE_GB => 0}, desc => 'passes if both thresholds would be exceeded but absolute threshold is disabled via 0'}, + {hdd_size_gb => 1, total_storage => 100, avail_storage => 1, expect => 'pass', ci => 1, + desc => 'succeed if requested HDDSIZEGB exceeds available space but CI environment variable is set'}, + {hdd_size_gb => 1, total_storage => 100, avail_storage => 1, expect => 'pass', github_actions => 1, + desc => 'succeed if requested HDDSIZEGB exceeds available space but GITHUB_ACTIONS environment variable is set'}, ); for my $case (@cases) { unlink bmwqemu::STATE_FILE; @@ -214,6 +217,8 @@ $vars{HDDSIZEGB} = $case->{hdd_size_gb} if defined $case->{hdd_size_gb}; create_vars(\%vars); $bmw_mock->mock(_get_storage_stats => sub (@) { ($case->{total_storage} * 1024**3, $case->{avail_storage} * 1024**3) }); + local $ENV{CI} = $case->{ci} // 0; + local $ENV{GITHUB_ACTIONS} = $case->{github_actions} // 0; if ($case->{expect} eq 'pass') { lives_ok { bmwqemu::init(); bmwqemu::ensure_valid_vars(); } $case->{desc}; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/14-isotovideo.t new/os-autoinst-5.1777537682.913fce0/t/14-isotovideo.t --- old/os-autoinst-5.1776943886.0619ca6/t/14-isotovideo.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/14-isotovideo.t 2026-04-30 10:28:02.000000000 +0200 @@ -84,7 +84,7 @@ subtest 'standard tests based on simple vars.json file' => sub { chdir $pool_dir; open my $var, '>', 'vars.json'; - print $var <<EOV; + print $var <<"EOV"; { "CASEDIR" : "$data_dir/tests", "_EXIT_AFTER_SCHEDULE" : 1, @@ -199,7 +199,7 @@ package Copy::Writer::Content$i; use Mojo::Base 'Exporter'; our \@EXPORT_OK = qw(write); - sub write { "val$i"}; + sub write () { "val$i"}; 1; EOM path($wheels_dir, 'writer', 'tests', 'pen')->make_path->child("ink$i.pm")->spew("use Mojo::Base 'basetest'; use Copy::Writer::Content$i 'write'; sub run {}; 1"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/17-basetest.t new/os-autoinst-5.1777537682.913fce0/t/17-basetest.t --- old/os-autoinst-5.1776943886.0619ca6/t/17-basetest.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/17-basetest.t 2026-04-30 10:28:02.000000000 +0200 @@ -282,6 +282,28 @@ ok $basetest->{fatal_failure}, 'failure considered fatal'; $basetest->remove_last_result; is_deeply $basetest->record_testresult('ok'), {result => 'ok'}, 'can add one test result again'; + path(bmwqemu::STATE_FILE)->remove; +}; + +subtest 'missing Perl module triggers incomplete' => sub { + Test::MockModule->new('autotest')->noop('set_current_test'); + my $mock = Test::MockModule->new('basetest'); + $mock->noop('take_screenshot'); + $mock->mock(run => sub { + die "Can't locate HTTP/Request/Common.pm in \@INC (you may need to install the HTTP::Request::Common module)\n"; + }); + local $bmwqemu::vars{MAX_TEST_STEPS} = 100; + my $basetest = bless {details => [], name => 'foo', fullname => 'foo', category => 'category1'}, 'basetest'; + my $logs = combined_from { dies_ok { $basetest->runtest } 'runtest dies due to missing module' }; + like $logs, qr/Can't locate.*\@INC/, 'catch expected error message'; + + # Verify state file has incomplete result + ok -e bmwqemu::STATE_FILE, 'state file was written'; + my $state = decode_json(path(bmwqemu::STATE_FILE)->slurp); + is $state->{result}, 'incomplete', 'result is incomplete for missing Perl module'; + # like $state->{msg}, qr/Can't locate/, 'message mentions the missing module'; + ok $basetest->{fatal_failure}, 'failure is fatal'; + path(bmwqemu::STATE_FILE)->remove if -e bmwqemu::STATE_FILE; }; delete $bmwqemu::vars{MAX_TEST_STEPS}; @@ -496,7 +518,7 @@ $suppress_match = 'yes'; my $details; my $mock_test = Test::MockModule->new('basetest'); - $mock_test->mock(record_screenfail => sub { my ($self, %args) = @_; $details = \%args; }); + $mock_test->mock(record_screenfail => sub ($self, %args) { $details = \%args; }); $res = $test->verify_sound_image("$FindBin::Bin/data/frame1.ppm", "$FindBin::Bin/data/frame2.ppm", 1); is($res, undef, 'res is undef as expected') or always_explain $res; is($details->{result}, 'unk', 'no needle match: unknown status correct') or always_explain $details; @@ -529,4 +551,71 @@ like($recorded_output, qr/# wait_serial expected: regex/, 'expected regex is in output'); }; +subtest record_serialresult_hiding => sub { + my $basetest = basetest->new(); + $basetest->{name} = 'test_hiding'; + my $recorded_output; + my $mock_basetest_local = Test::MockModule->new('basetest'); + my $mock_testapi = Test::MockModule->new('testapi'); + $mock_basetest_local->redefine(record_resultfile => sub ($self, $title, $output, %nargs) { $recorded_output = $output }); + + my @test_cases = ( + { + name => 'expected regex is visible by default (no pretty vars set)', + vars => {}, + params => ['regex', 'ok', 'some output marker', internal_marker => 1, marker_pattern => 'marker'], + expected => [qr/# wait_serial expected: regex/], + not_expected => [], + }, + { + name => 'expected regex is hidden and literal marker is stripped when PRETTY_SERIAL_MARKER is set', + vars => {PRETTY_SERIAL_MARKER => 1}, + params => ['regex', 'ok', "some output\nmarker\n", internal_marker => 1, marker_pattern => 'marker'], + expected => [qr/some output\n\s*\n/], + not_expected => [qr/# wait_serial expected: regex/], + }, + { + name => 'expected regex is hidden and regex marker is stripped when HIDE_MARKER_EVALUATION is set', + vars => {HIDE_MARKER_EVALUATION => 1}, + params => ['regex', 'ok', "command output\nOA:DONE-1234-0-\n", internal_marker => 1, marker_pattern => qr/OA:DONE-[0-9a-f]{4}-(\d+)-/], + expected => [qr/command output\n\s*\n/], + not_expected => [qr/# wait_serial expected: regex/, qr/OA:DONE-1234-0-/], + }, + { + name => 'Exit code is displayed when capture_name is provided', + vars => {PRETTY_SERIAL_MARKER => 1}, + params => ['regex', 'ok', "command output\nOA:DONE-1234-0-\n", internal_marker => 1, marker_pattern => qr/OA:DONE-[0-9a-f]{4}-(\d+)-/, capture_name => 'Exit code'], + expected => [qr/# Exit code: 0/, qr/command output\n\s*\n/], + not_expected => [qr/# wait_serial expected: regex/], + }, + { + name => 'PID is displayed for background commands', + vars => {HIDE_MARKER_EVALUATION => 1}, + params => ['regex', 'ok', "background output\nMARKER-1234-\n", internal_marker => 1, marker_pattern => qr/MARKER-(\d+)-/, capture_name => 'PID'], + expected => [qr/# PID: 1234/, qr/background output\n\s*\n/], + not_expected => [qr/MARKER-1234-/], + }, + { + name => 'no hiding occurs if it is not an internal marker even if pretty vars are set', + vars => {PRETTY_SERIAL_MARKER => 1, HIDE_MARKER_EVALUATION => 1}, + params => ['regex', 'ok', 'some output marker', internal_marker => 0, marker_pattern => 'marker'], + expected => [qr/# wait_serial expected: regex/], + not_expected => [], + }, + { + name => 'regex marker is provided but string does not match (e.g. on timeout)', + vars => {PRETTY_SERIAL_MARKER => 1}, + params => ['regex', 'fail', "some output that did not hit the marker\n", internal_marker => 1, marker_pattern => qr/OA:DONE-[0-9a-f]{4}-(\d+)-/, capture_name => 'Exit code'], + expected => [qr/some output that did not hit the marker\n/], + not_expected => [qr/# Exit code:/], + }, + ); + + foreach my $case (@test_cases) { + $mock_testapi->mock(get_var => sub ($var) { return $case->{vars}->{$var} }); + $basetest->record_serialresult(@{$case->{params}}); + like($recorded_output, $_, "$case->{name} (contains expected)") for @{$case->{expected}}; + unlike($recorded_output, $_, "$case->{name} (does not contain unexpected)") for @{$case->{not_expected}}; + } +}; done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/18-backend-qemu.t new/os-autoinst-5.1777537682.913fce0/t/18-backend-qemu.t --- old/os-autoinst-5.1776943886.0619ca6/t/18-backend-qemu.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/18-backend-qemu.t 2026-04-30 10:28:02.000000000 +0200 @@ -62,7 +62,7 @@ is($called{add_console}, 1, 'one console has been added'); subtest 'using Open vSwitch D-Bus service' => sub { - my $expected = qr/Open vSwitch command.*show.*arguments 'foo bar'.*(The name.*not (provided|activatable)|Failed to connect)/; + my $expected = qr/Open vSwitch command.*show.*arguments 'foo bar'.*(The name.*not (provided|activatable)|Failed to connect|AccessDenied)/; my $msg = 'error about missing service'; throws_ok { $backend->_dbus_call('show', 'foo', 'bar') } $expected, $msg . ' in exception'; $bmwqemu::vars{QEMU_NON_FATAL_DBUS_CALL} = 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/19-isotovideo-command-processing.t new/os-autoinst-5.1777537682.913fce0/t/19-isotovideo-command-processing.t --- old/os-autoinst-5.1776943886.0619ca6/t/19-isotovideo-command-processing.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/19-isotovideo-command-processing.t 2026-04-30 10:28:02.000000000 +0200 @@ -48,7 +48,7 @@ push @{$self->{messages}}, $cmd; return $cmd->{cmd} eq 'is_shutdown' ? 'down' : {tags => [qw(some fake tags)]}; } - sub stop { die "faking stop\n" } + sub stop ($self) { die "faking stop\n" } } # uncoverable statement package bmwqemu { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/20-openqa-isotovideo-utils.t new/os-autoinst-5.1777537682.913fce0/t/20-openqa-isotovideo-utils.t --- old/os-autoinst-5.1776943886.0619ca6/t/20-openqa-isotovideo-utils.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/20-openqa-isotovideo-utils.t 2026-04-30 10:28:02.000000000 +0200 @@ -59,7 +59,7 @@ my $state = decode_json($base_state->slurp); if (is(ref $state, 'HASH', 'state file contains object')) { is($state->{component}, 'tests', 'state file contains component'); - like($state->{msg}, qr/unable to load foo\/bar\.pm/, 'state file contains error message'); + like($state->{msg}, qr/Missing Perl module while loading foo\/bar\.pm/, 'state file contains error message'); } }; subtest 'invalid productdir' => sub { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/23-baseclass.t new/os-autoinst-5.1777537682.913fce0/t/23-baseclass.t --- old/os-autoinst-5.1776943886.0619ca6/t/23-baseclass.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/23-baseclass.t 2026-04-30 10:28:02.000000000 +0200 @@ -185,7 +185,7 @@ $mock_channel->mock(pty => sub { return 1; }); $mock_channel->mock(send_eof => sub { return 1; }); $mock_channel->mock(exit_status => sub { shift->{exit_status}; }); - $mock_channel->mock(ext_data => sub { my ($self, $v) = @_; $self->{ext_data} = $v; }); + $mock_channel->mock(ext_data => sub ($self, $v) { $self->{ext_data} = $v; }); $mock_channel->mock(close => sub { return 1; }); return $mock_channel; }); @@ -433,7 +433,7 @@ $baseclass->{current_console} = $current_console; #mock content of serial0.txt - path($baseclass->{serialfile})->spew(<<EOT); + path($baseclass->{serialfile})->spew(<<'EOT'); Just a simple text Just a simple another text that will disappear Welcome to GRUB2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/27-consoles-vmware.t new/os-autoinst-5.1777537682.913fce0/t/27-consoles-vmware.t --- old/os-autoinst-5.1776943886.0619ca6/t/27-consoles-vmware.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/27-consoles-vmware.t 2026-04-30 10:28:02.000000000 +0200 @@ -148,7 +148,7 @@ $self->send({binary => 'binary sent from WebSocket'}, sub { $self->send({text => 'text message sent from WebSocket'}, sub { $sent_everything = 1; - $self->finish if length $self->app->received_everything; + $self->finish if $self->app->received_everything; }); }); $self->on( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/33-vagrant.t new/os-autoinst-5.1777537682.913fce0/t/33-vagrant.t --- old/os-autoinst-5.1776943886.0619ca6/t/33-vagrant.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/33-vagrant.t 2026-04-30 10:28:02.000000000 +0200 @@ -216,7 +216,7 @@ $backend_vars->{QEMUCPUS} = 1; $backend_vars->{QEMURAM} = 1024; - $expected_spew_str = <<END; + $expected_spew_str = <<"END"; Vagrant.configure("2") do |config| config.vm.box = "$box_file" config.vm.synced_folder ".", "/vagrant", disabled: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/39-dewebsockify.t new/os-autoinst-5.1777537682.913fce0/t/39-dewebsockify.t --- old/os-autoinst-5.1776943886.0619ca6/t/39-dewebsockify.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/39-dewebsockify.t 2026-04-30 10:28:02.000000000 +0200 @@ -59,60 +59,60 @@ use Test::Most; use Mojo::Base -signatures; - sub is_websocket { 1 } - sub max_websocket_size { } - sub error { } - sub req { bless {}, 'MockTxGenericReq' } + sub is_websocket ($self, @args) { 1 } + sub max_websocket_size ($self, @args) { } + sub error ($self, @args) { } + sub req ($self, @args) { bless {}, 'MockTxGenericReq' } sub on ($self, $event, $cb) { if ($event eq 'text') { $main::ws_on_text = $cb } elsif ($event eq 'binary') { $main::ws_on_binary = $cb } elsif ($event eq 'finish') { $main::ws_on_finish = $cb } } - sub send { } - sub finish { } + sub send ($self, @args) { } + sub finish ($self) { } package MockTxGenericReq; - sub cookies { } - sub headers { bless {}, 'MockTxGenericHeaders' } + sub cookies ($self, @args) { } + sub headers ($self, @args) { bless {}, 'MockTxGenericHeaders' } package MockTxGenericHeaders; - sub add { } + sub add ($self, @args) { } package MockTxFailNoCode; - sub is_websocket { 0 } - sub error { undef } # No code, no message - sub req { bless {}, 'MockTxFailNoCodeReq' } - sub res { bless {}, 'MockTxFailNoCodeRes' } + sub is_websocket ($self, @args) { 0 } + sub error ($self, @args) { undef } # No code, no message + sub req ($self, @args) { bless {}, 'MockTxFailNoCodeReq' } + sub res ($self, @args) { bless {}, 'MockTxFailNoCodeRes' } package MockTxFailNoCodeReq; - sub cookies { } - sub headers { bless {}, 'MockTxFailNoCodeHeaders' } + sub cookies ($self, @args) { } + sub headers ($self, @args) { bless {}, 'MockTxFailNoCodeHeaders' } package MockTxFailNoCodeHeaders; - sub add { } + sub add ($self, @args) { } package MockTxFailNoCodeRes; - sub body { 'dummy body' } + sub body ($self, @args) { 'dummy body' } package MockTxFailWithCode; - sub is_websocket { 0 } + sub is_websocket ($self, @args) { 0 } - sub error { + sub error ($self, @args) { return {code => 403, message => 'Forbidden'}; } - sub req { bless {}, 'MockTxFailWithCodeReq' } - sub res { bless {}, 'MockTxFailWithCodeRes' } + sub req ($self, @args) { bless {}, 'MockTxFailWithCodeReq' } + sub res ($self, @args) { bless {}, 'MockTxFailWithCodeRes' } package MockTxFailWithCodeReq; - sub cookies { } - sub headers { bless {}, 'MockTxFailWithCodeHeaders' } + sub cookies ($self, @args) { } + sub headers ($self, @args) { bless {}, 'MockTxFailWithCodeHeaders' } package MockTxFailWithCodeHeaders; - sub add { } + sub add ($self, @args) { } package MockTxFailWithCodeRes; - sub body { 'dummy body' } + sub body ($self, @args) { 'dummy body' } } sub mock_build_ws_tx ($mock_ua, $tx_class) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/t/99-full-stack.t new/os-autoinst-5.1777537682.913fce0/t/99-full-stack.t --- old/os-autoinst-5.1776943886.0619ca6/t/99-full-stack.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/t/99-full-stack.t 2026-04-30 10:28:02.000000000 +0200 @@ -31,7 +31,7 @@ my $cleanup = scope_guard sub { chdir $Bin; undef $dir }; my $casedir = path($data_dir, 'tests'); -path('vars.json')->spew(<<EOV); +path('vars.json')->spew(<<"EOV"); { "ARCH" : "i386", "BACKEND" : "qemu", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/testapi.pm new/os-autoinst-5.1777537682.913fce0/testapi.pm --- old/os-autoinst-5.1776943886.0619ca6/testapi.pm 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/testapi.pm 2026-04-30 10:28:02.000000000 +0200 @@ -185,7 +185,7 @@ =cut -sub save_screenshot () { $autotest::current_test->take_screenshot unless is_serial_terminal } +sub save_screenshot () { $autotest::current_test->take_screenshot unless is_serial_terminal() } =head2 record_soft_failure @@ -858,8 +858,7 @@ =cut -sub wait_serial { # no:style:signatures - my $regexp = shift; +sub wait_serial ($regexp, @args) { my %args = compat_args( { regexp => $regexp, @@ -870,7 +869,9 @@ buffer_size => undef, record_output => undef, record_command => undef, - }, ['timeout', 'expect_not_found'], @_); + internal_marker => 0, + capture_name => undef, + }, ['timeout', 'expect_not_found'], @args); bmwqemu::log_call(%args); $args{timeout} = bmwqemu::scale_timeout($args{timeout}); @@ -886,7 +887,7 @@ # hyperv and vmware (backend/svirt.pm) connect serial line over TCP/IP (socat) # convert CRLF to LF only $ret->{string} =~ s,\r\n,\n,g; - $autotest::current_test->record_serialresult(bmwqemu::pp($regexp), $matched, $ret->{string}, command => $args{record_command}) unless ($args{quiet}); + $autotest::current_test->record_serialresult(bmwqemu::pp($regexp), $matched, $ret->{string}, command => $args{record_command}, internal_marker => $args{internal_marker}, marker_pattern => $regexp, capture_name => $args{capture_name}) unless ($args{quiet}); bmwqemu::fctres("$regexp: $matched"); return $ret->{string} if ($matched eq 'ok'); return; # false @@ -1705,6 +1706,7 @@ $autotest::selected_console = $testapi_console; if ($ret->{activated}) { push @$autotest::activated_consoles, $testapi_console; + $testapi::distri->reset_console_cache($testapi_console); $testapi::distri->activate_console($testapi_console, @args); } $testapi::distri->console_selected($testapi_console, @args); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1776943886.0619ca6/xt/01-style.t new/os-autoinst-5.1777537682.913fce0/xt/01-style.t --- old/os-autoinst-5.1776943886.0619ca6/xt/01-style.t 2026-04-23 13:31:26.000000000 +0200 +++ new/os-autoinst-5.1777537682.913fce0/xt/01-style.t 2026-04-30 10:28:02.000000000 +0200 @@ -19,7 +19,8 @@ is qx{git grep -I -l '^use \\(Try::Tiny\\|TryCatch\\)'}, '', 'No Try::Tiny or TryCatch necessary, use Feature::Compat::Try and later native Perl'; is qx{git grep -I -l '\<spurt\>'}, '', 'No deprecated "Mojo::File::spurt", use "spew" instead'; is qx{git grep -I -l '^use testapi' backend/ consoles/}, '', 'No backend or console files use external facing testapi'; -is qx{git grep -l -e '^sub \\S\\+ [^(]\\+' --and --not -e 'sub [(\{]' --and --not -e 'sub \\S\\+(' --and --not -e 'sub \\S\\+;' --and --not -e '# no:style:signatures' ':!external/'}, '', 'All files use sub signatures everywhere (nameless and in-place definitions still allowed)'; +is qx[git grep -l -e '^\\s*sub \\S\\+ [^(]\\+' --and --not -e 'sub [(\{]' --and --not -e 'sub \\S\\+\\s*[:(]' --and --not -e 'sub \\S\\+;' --and --not -e '# no:style:signatures' ':!external/' ':!t/48-testmodules-style.t'], '', 'All files use sub signatures everywhere (nameless and in-place definitions still allowed)'; +is qx[git grep -l -P 'sub\\s*\\{\\s*my\\s*\\(?\\\$' t/], '', 'Anonymous subs in tests should use signatures instead of manual unpacking of @_'; is qx{git grep -L '^#!.*perl' t/**.t}, '', 'All test files have shebang'; is qx{git ls-files -s t/**.t | grep -v ^1007}, '', 'All test modules are executable'; is qx{git grep -l '^use POSIX;'}, '', 'Use of bare POSIX import is discouraged, see https://perldoc.perl.org/POSIX'; ++++++ os-autoinst.obsinfo ++++++ --- /var/tmp/diff_new_pack.O7PwaS/_old 2026-04-30 20:33:15.165815444 +0200 +++ /var/tmp/diff_new_pack.O7PwaS/_new 2026-04-30 20:33:15.177815937 +0200 @@ -1,5 +1,5 @@ name: os-autoinst -version: 5.1776943886.0619ca6 -mtime: 1776943886 -commit: 0619ca6d84bded836782df071cfa1de4a96c6f3c +version: 5.1777537682.913fce0 +mtime: 1777537682 +commit: 913fce00a14c6d0b215fec654944b9f6f3954a62
