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-07-21 23:09:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/os-autoinst (Old) and /work/SRC/openSUSE:Factory/.os-autoinst.new.24530 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "os-autoinst" Tue Jul 21 23:09:22 2026 rev:614 rq:1366815 version:5.1784551853.234e380 Changes: -------- --- /work/SRC/openSUSE:Factory/os-autoinst/os-autoinst.changes 2026-07-12 16:22:00.873743171 +0200 +++ /work/SRC/openSUSE:Factory/.os-autoinst.new.24530/os-autoinst.changes 2026-07-21 23:09:37.560708898 +0200 @@ -1,0 +2,12 @@ +Mon Jul 20 12:51:05 UTC 2026 - [email protected] + +- Update to version 5.1784551853.234e380: + * ci: Add tool to check line coverage + * git subrepo pull (merge) external/os-autoinst-common + * style(testapi): apply Modules::ProhibitAutomaticExportation on testapi + * style(testapi.pm): apply Subroutines::RequireArgUnpacking on testapi.pm + * style(perlcritic): RegularExpressions::ProhibitSingleCharAlternation + * build(deps): bump actions/checkout (#3018) + * fix(backend): bring back support for legacy BIOS + +------------------------------------------------------------------- Old: ---- os-autoinst-5.1783672666.346fbe5.obscpio New: ---- os-autoinst-5.1784551853.234e380.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ os-autoinst-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:42.560879851 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:42.560879851 +0200 @@ -18,7 +18,7 @@ %define short_name os-autoinst-devel Name: %{short_name}-test -Version: 5.1783672666.346fbe5 +Version: 5.1784551853.234e380 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-openvswitch-test.spec ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:42.596881082 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:42.596881082 +0200 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst-openvswitch Name: %{short_name}%{?name_ext} -Version: 5.1783672666.346fbe5 +Version: 5.1784551853.234e380 Release: 0 Summary: test package for %{short_name} License: GPL-2.0-or-later ++++++ os-autoinst-test.spec ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:42.644882723 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:42.648882860 +0200 @@ -19,7 +19,7 @@ %define name_ext -test %define short_name os-autoinst Name: %{short_name}%{?name_ext} -Version: 5.1783672666.346fbe5 +Version: 5.1784551853.234e380 Release: 0 Summary: test package for os-autoinst License: GPL-2.0-or-later ++++++ os-autoinst.spec ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:42.676883817 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:42.680883954 +0200 @@ -17,7 +17,7 @@ Name: os-autoinst -Version: 5.1783672666.346fbe5 +Version: 5.1784551853.234e380 Release: 0 Summary: OS-level test automation License: GPL-2.0-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:42.716885184 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:42.720885321 +0200 @@ -1,4 +1,4 @@ -<?xml version="1.0"?> +<?xml version='1.0' encoding='utf-8'?> <services> <service name="obs_scm" mode="disabled"> <param name="extract">dist/rpm/os-autoinst.spec</param> @@ -13,11 +13,12 @@ <param name="changesgenerate">enable</param> <param name="changesauthor">[email protected]</param> </service> - <service name="set_version" mode="disabled"/> - <service name="tar" mode="buildtime"/> + <service name="set_version" mode="disabled" /> + <service name="tar" mode="buildtime" /> <service name="recompress" mode="buildtime"> <param name="compression">xz</param> <param name="file">*.tar</param> </service> </services> +(No newline at EOF) ++++++ os-autoinst-5.1783672666.346fbe5.obscpio -> os-autoinst-5.1784551853.234e380.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/dependabot.yml new/os-autoinst-5.1784551853.234e380/.github/dependabot.yml --- old/os-autoinst-5.1783672666.346fbe5/.github/dependabot.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/os-autoinst-5.1784551853.234e380/.github/dependabot.yml 2026-07-20 14:50:53.000000000 +0200 @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' + day: 'monday' + open-pull-requests-limit: 5 + groups: + all-actions: + patterns: + - "*" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/author-tests.yaml new/os-autoinst-5.1784551853.234e380/.github/workflows/author-tests.yaml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/author-tests.yaml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/author-tests.yaml 2026-07-20 14:50:53.000000000 +0200 @@ -12,7 +12,7 @@ container: image: registry.opensuse.org/devel/openqa/containers/os-autoinst_dev steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: make test-local run: | git config --global --add safe.directory '*' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/checklist.yml new/os-autoinst-5.1784551853.234e380/.github/workflows/checklist.yml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/checklist.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/checklist.yml 2026-07-20 14:50:53.000000000 +0200 @@ -10,7 +10,7 @@ name: Checklist job steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checklist uses: wyozi/contextual-qa-checklist-action@master with: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/ci.yml new/os-autoinst-5.1784551853.234e380/.github/workflows/ci.yml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/ci.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/ci.yml 2026-07-20 14:50:53.000000000 +0200 @@ -11,7 +11,7 @@ contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Static checks, unit tests and integration tests run: tools/container_run_ci - name: Upload coverage to Codecov @@ -29,11 +29,12 @@ name: coverage path: coverage.tar retention-days: 15 + - run: ./tools/check-line-coverage build/cover_db/codecov.json static-check-containers: permissions: contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Test our container definitions run: tools/static_check_containers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/ci_extended.yml new/os-autoinst-5.1784551853.234e380/.github/workflows/ci_extended.yml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/ci_extended.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/ci_extended.yml 2026-07-20 14:50:53.000000000 +0200 @@ -14,6 +14,6 @@ contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Test our container definitions run: tools/test_containers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/obs-helper.yaml new/os-autoinst-5.1784551853.234e380/.github/workflows/obs-helper.yaml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/obs-helper.yaml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/obs-helper.yaml 2026-07-20 14:50:53.000000000 +0200 @@ -12,7 +12,7 @@ contents: read issues: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: debug run: | env | sort diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.github/workflows/openqa_fullstack.yml new/os-autoinst-5.1784551853.234e380/.github/workflows/openqa_fullstack.yml --- old/os-autoinst-5.1783672666.346fbe5/.github/workflows/openqa_fullstack.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.github/workflows/openqa_fullstack.yml 2026-07-20 14:50:53.000000000 +0200 @@ -22,7 +22,7 @@ container: image: registry.opensuse.org/devel/openqa/containers/opensuse/openqa_ci:latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Create additional user to avoid initdb error "cannot be run as root" run: useradd "$TEST_USER" - name: Make project folder user-writable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.mergify.yml new/os-autoinst-5.1784551853.234e380/.mergify.yml --- old/os-autoinst-5.1783672666.346fbe5/.mergify.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.mergify.yml 2026-07-20 14:50:53.000000000 +0200 @@ -39,3 +39,12 @@ - base=master - label=merge-fast actions: *merge + - name: automatic merge for dependabot updates + conditions: + - and: *base_checks + - and: + - base=master + - author=dependabot[bot] + actions: + merge: + method: squash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/.perlcriticrc new/os-autoinst-5.1784551853.234e380/.perlcriticrc --- old/os-autoinst-5.1783672666.346fbe5/.perlcriticrc 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/.perlcriticrc 2026-07-20 14:50:53.000000000 +0200 @@ -1,7 +1,7 @@ theme = community + openqa severity = 4 -include = strict ValuesAndExpressions::ProhibitInterpolationOfLiterals CodeLayout::ProhibitParensWithBuiltins BuiltinFunctions::RequireBlockMap BuiltinFunctions::ProhibitStringySplit ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions ValuesAndExpressions::RequireQuotedHeredocTerminator Variables::ProhibitPackageVars CodeLayout::RequireTidyCode Community::WarningsSwitch Community::EmptyReturn Variables::ProhibitUnusedVariables RegularExpressions::ProhibitUselessTopic BuiltinFunctions::ProhibitUselessTopic CodeLayout::ProhibitQuotedWordLists BuiltinFunctions::RequireBlockGrep Modules::ProhibitConditionalUseStatements +include = strict ValuesAndExpressions::ProhibitInterpolationOfLiterals CodeLayout::ProhibitParensWithBuiltins BuiltinFunctions::RequireBlockMap BuiltinFunctions::ProhibitStringySplit ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions ValuesAndExpressions::RequireQuotedHeredocTerminator Variables::ProhibitPackageVars CodeLayout::RequireTidyCode Community::WarningsSwitch Community::EmptyReturn Variables::ProhibitUnusedVariables RegularExpressions::ProhibitUselessTopic BuiltinFunctions::ProhibitUselessTopic CodeLayout::ProhibitQuotedWordLists BuiltinFunctions::RequireBlockGrep Modules::ProhibitConditionalUseStatements BuiltinFunctions::ProhibitLvalueSubstr Community::OpenArgs Plicease::ProhibitArrayAssignAref Community::Wantarray ClassHierarchies::ProhibitExplicitISA Community::ConditionalImplicitReturn Documentation::RequirePackageMatchesPodName BuiltinFunctions::ProhibitStringyEval InputOutput::ProhibitOneArgSelect ErrorHandling::RequireCheckingReturnValueOfEval Community::Each ProhibitQuotesAsQuotelikeOperatorDelimiter BuiltinFunctions::ProhibitSleepViaSelects ValuesAndExpressions::ProhibitInterpolationOfLiterals Miscellanea::ProhibitUselessNoCritic RegularExpressions::ProhibitSingleCharAlternation 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.1783672666.346fbe5/OpenQA/Benchmark/Stopwatch.pm new/os-autoinst-5.1784551853.234e380/OpenQA/Benchmark/Stopwatch.pm --- old/os-autoinst-5.1783672666.346fbe5/OpenQA/Benchmark/Stopwatch.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/OpenQA/Benchmark/Stopwatch.pm 2026-07-20 14:50:53.000000000 +0200 @@ -7,7 +7,7 @@ =head1 NAME -Benchmark::Stopwatch - simple timing of stages of your code. +OpenQA::Benchmark::Stopwatch - simple timing of stages of your code. =head1 SYNOPSIS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/OpenQA/Isotovideo/Interface.pm new/os-autoinst-5.1784551853.234e380/OpenQA/Isotovideo/Interface.pm --- old/os-autoinst-5.1783672666.346fbe5/OpenQA/Isotovideo/Interface.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/OpenQA/Isotovideo/Interface.pm 2026-07-20 14:50:53.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 = 56; ## no critic (Variables::ProhibitPackageVars) +our $version = 57; ## no critic (Variables::ProhibitPackageVars) # 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.1783672666.346fbe5/OpenQA/Isotovideo/Runner.pm new/os-autoinst-5.1784551853.234e380/OpenQA/Isotovideo/Runner.pm --- old/os-autoinst-5.1783672666.346fbe5/OpenQA/Isotovideo/Runner.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/OpenQA/Isotovideo/Runner.pm 2026-07-20 14:50:53.000000000 +0200 @@ -23,6 +23,7 @@ use needle (); use commands (); use distribution (); +use IO::Handle; has [qw(cmd_srv_process cmd_srv_fd cmd_srv_port)]; @@ -230,10 +231,8 @@ } sub _flush_std ($) { - select STDERR; - $| = 1; - select STDOUT; # default - $| = 1; + STDERR->autoflush(1); + STDOUT->autoflush(1); } sub _init_bmwqemu ($, @args) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/OpenQA/Qemu/Proc.pm new/os-autoinst-5.1784551853.234e380/OpenQA/Qemu/Proc.pm --- old/os-autoinst-5.1783672666.346fbe5/OpenQA/Qemu/Proc.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/OpenQA/Qemu/Proc.pm 2026-07-20 14:50:53.000000000 +0200 @@ -293,6 +293,16 @@ my $bdc = $self->blockdev_conf; return $self unless $vars->{UEFI}; + + if ($vars->{UEFI_PFLASH}) { + die 'Mixing old and new PFLASH variables' + if ($vars->{UEFI_PFLASH_CODE} || $vars->{UEFI_PFLASH_VARS}); + + my $file = $vars->{BIOS}; + $bdc->add_pflash_drive('pflash', $file, $self->get_img_size($file)); + return $self; + } + my $fw = $vars->{UEFI_PFLASH_CODE}; if ($fw) { $fw = path($vars->{UEFI_PFLASH_CODE})->to_abs; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/autotest.pm new/os-autoinst-5.1784551853.234e380/autotest.pm --- old/os-autoinst-5.1783672666.346fbe5/autotest.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/autotest.pm 2026-07-20 14:50:53.000000000 +0200 @@ -108,11 +108,10 @@ sub _debug_python_version () { state $python_loaded = 0; return if $python_loaded++; - my $code = <<~'EOM'; - use Inline::Python; - return Inline::Python::py_eval('__import__("sys").version', 0); - EOM - my $debug = eval $code; + my $debug = eval { + require Inline::Python; + return Inline::Python::py_eval('__import__("sys").version', 0); + }; bmwqemu::diag 'Using python version ' . $debug; } @@ -132,8 +131,8 @@ =cut sub _lua_use ($perlmod, $import = undef) { - eval "use $perlmod ()"; - if ($@) { die "Could not load '$perlmod': $@" } + my $result = eval "use $perlmod (); 1"; ## no critic (BuiltinFunctions::ProhibitStringyEval) + if (!$result) { die "Could not load '$perlmod': $@" } my %exports; no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict, TestingAndDebugging::ProhibitProlongedStrictureOverride) my @export = @{"${perlmod}::EXPORT"}; @@ -180,7 +179,7 @@ EOLUA return lua_eval('_VERSION', 0); EOM - my $debug = eval $code; + my $debug = eval $code; ## no critic (BuiltinFunctions::ProhibitStringyEval) bmwqemu::diag "Using Lua version $debug"; lua_set('use', \&_lua_use); } @@ -306,7 +305,7 @@ state %loaded; # keep track of loaded packages # note: Never load a test module that would result in the same package twice as this would only lead to warnings # like "Subroutine run redefined at …". - eval _make_test_code_to_eval($script_path, $script, $name, \$is_python) unless $loaded{$script_path}++; + eval _make_test_code_to_eval($script_path, $script, $name, \$is_python) unless $loaded{$script_path}++; ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval) if (my $err = $@) { if ($is_python) { try { require Inline; import Inline Python => 'sys.stderr.flush()'; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/backend/baseclass.pm new/os-autoinst-5.1784551853.234e380/backend/baseclass.pm --- old/os-autoinst-5.1783672666.346fbe5/backend/baseclass.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/backend/baseclass.pm 2026-07-20 14:50:53.000000000 +0200 @@ -364,7 +364,7 @@ sub _auto_detect_external_video_encoder ($self) { my $ffmpeg_banner = _ffmpeg_banner; - return DEFAULT_FFMPEG_CMD . ' -c:v libsvtav1 -crf 50 -preset 7' if $ffmpeg_banner =~ qr/--enable-libsvtav1(\s|$)/ and $ffmpeg_banner !~ qr/ffmpeg\sversion\s4\./; + return DEFAULT_FFMPEG_CMD . ' -c:v libsvtav1 -crf 50 -preset 7 -b:v 0' if $ffmpeg_banner =~ qr/--enable-libsvtav1(\s|$)/ and $ffmpeg_banner !~ qr/ffmpeg\sversion\s4\./; return DEFAULT_FFMPEG_CMD . ' -c:v libvpx-vp9 -crf 35 -b:v 1500k -cpu-used 1' if $ffmpeg_banner =~ qr/--enable-libvpx(\s|$)/; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/backend/console_proxy.pm new/os-autoinst-5.1784551853.234e380/backend/console_proxy.pm --- old/os-autoinst-5.1783672666.346fbe5/backend/console_proxy.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/backend/console_proxy.pm 2026-07-20 14:50:53.000000000 +0200 @@ -38,7 +38,7 @@ console => $self->{console}, function => $function, args => $args, - wantarray => wantarray, + wantarray => wantarray, ## no critic (Community::Wantarray) }; bmwqemu::log_call(wrapped_call => $wrapped_call); @@ -51,7 +51,7 @@ # get more screenshots from consoles, especially from x3270 on s390 $autotest::current_test->take_screenshot; - return wantarray ? @{$wrapped_retval->{result}} : $wrapped_retval->{result}; + return wantarray ? @{$wrapped_retval->{result}} : $wrapped_retval->{result}; ## no critic (Community::Wantarray) }; # this is why we can't use a signature for this function, goto diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/backend/qemu.pm new/os-autoinst-5.1784551853.234e380/backend/qemu.pm --- old/os-autoinst-5.1783672666.346fbe5/backend/qemu.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/backend/qemu.pm 2026-07-20 14:50:53.000000000 +0200 @@ -368,6 +368,7 @@ if ($compress_method eq 'bzip2') { runcmd('bzip2', "-v$compress_level", "ulogs/$filename"); } + return undef; } sub inflate_balloon ($self) { @@ -726,10 +727,10 @@ if ($vars->{UEFI} && ($arch eq 'x86_64')) { $vars->{UEFI_PFLASH_CODE} //= find_ovmf; $vars->{UEFI_PFLASH_VARS} //= $vars->{UEFI_PFLASH_CODE} =~ s/code/$&=~tr,CcOoDdEe,VvAaRrSs,r/eir; - die 'No UEFI firmware can be found! Please specify UEFI_PFLASH_CODE/UEFI_PFLASH_VARS or UEFI_BIOS or install an appropriate package' unless $vars->{UEFI_PFLASH_CODE}; + die 'No UEFI firmware can be found! Please specify UEFI_PFLASH_CODE/UEFI_PFLASH_VARS or BIOS or UEFI_BIOS or install an appropriate package' unless $vars->{UEFI_PFLASH_CODE}; } - foreach my $attribute (qw(KERNEL INITRD)) { + foreach my $attribute (qw(KERNEL INITRD BIOS)) { if ($vars->{$attribute} && $vars->{$attribute} !~ /^\//) { # Non-absolute paths are assumed relative to /usr/share/qemu $vars->{$attribute} = '/usr/share/qemu/' . $vars->{$attribute}; @@ -989,6 +990,10 @@ } sp('boot', join ',', @boot_args) if @boot_args; + if (!$vars->{UEFI} && $vars->{BIOS}) { + sp('bios', $vars->{BIOS}); + } + foreach my $attribute (qw(KERNEL INITRD APPEND)) { sp(lc($attribute), $vars->{$attribute}) if $vars->{$attribute}; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/bmwqemu.pm new/os-autoinst-5.1784551853.234e380/bmwqemu.pm --- old/os-autoinst-5.1783672666.346fbe5/bmwqemu.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/bmwqemu.pm 2026-07-20 14:50:53.000000000 +0200 @@ -372,12 +372,12 @@ sub FIRSTKEY ($self) { my $data = $self->{data}; my @k = keys %$data; # reset - my $next = each %$data; + my $next = each %$data; ## no critic (Community::Each) } sub NEXTKEY ($self, $last) { my $data = $self->{data}; - my $next = each %$data; + my $next = each %$data; ## no critic (Community::Each) } sub FETCH ($self, $key) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/VNC.pm new/os-autoinst-5.1784551853.234e380/consoles/VNC.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/VNC.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/VNC.pm 2026-07-20 14:50:53.000000000 +0200 @@ -492,8 +492,6 @@ sub send_key_event_up ($self, $key) { $self->_send_key_event(0, $key) } -## no critic (HashKeyQuotes) - my $keymap_x11 = { 'esc' => 0xff1b, 'down' => 0xff54, @@ -646,7 +644,8 @@ $keymap{"f$key"} = 0x3a + $key - 1,; } my %map = %{shift_keys()}; - while (my ($key, $shift) = each %map) { + foreach my $key (keys %map) { + my $shift = $map{$key}; die_on_invalid_mapping($key) unless $keymap{$shift}; $keymap{$key} = [$keymap{shift}, $keymap{$shift}]; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/localXvnc.pm new/os-autoinst-5.1784551853.234e380/consoles/localXvnc.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/localXvnc.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/localXvnc.pm 2026-07-20 14:50:53.000000000 +0200 @@ -54,7 +54,7 @@ die q{Missing 'xdotool'} unless $xdotool; # search for YaST Window and grab the id - my $window_id = qx"DISPLAY=$display $xdotool search --sync --onlyvisible --name $window_name"; + my $window_id = qx{DISPLAY=$display $xdotool search --sync --onlyvisible --name $window_name}; $window_id =~ s/\D//g; # resize and move window to fit in icewm diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/sshVirtsh.pm new/os-autoinst-5.1784551853.234e380/consoles/sshVirtsh.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/sshVirtsh.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/sshVirtsh.pm 2026-07-20 14:50:53.000000000 +0200 @@ -185,8 +185,8 @@ else { if (ref($tag) eq 'HASH') { # for hashes set the attributes - while (my ($key, $value) = each %$tag) { - $elem->setAttribute($key => $value); + foreach my $key (keys %$tag) { + $elem->setAttribute($key => $tag->{$key}); } } else { @@ -383,7 +383,7 @@ return $dest_image; } -sub _copy_image_vmware ($self, $name, $backingfile, $file_basename, $vmware_openqa_datastore, $vmware_disk_path, $vmware_disk_path_thinfile) { +sub _copy_image_vmware ($self, $name, $backingfile, $file_basename, $vmware_openqa_datastore, $vmware_disk_path, $vmware_disk_path_thinfile, $copy_timeout = 600) { # If the file exists, make sure someone else is not copying it there right now, # otherwise copy image from NFS datastore. my $nfs_dir = $backingfile ? 'hdd' : 'iso'; @@ -398,7 +398,7 @@ 'else ' . "cp /vmfs/volumes/$vmware_nfs_datastore/$nfs_dir/$file_basename $vmware_openqa_datastore;" . 'fi;'; - my $retval = $self->run_cmd($cmd, domain => 'sshVMwareServer'); + my $retval = $self->run_cmd($cmd, domain => 'sshVMwareServer', timeout => $copy_timeout); die "Can't copy VMware image $file_basename" if $retval; return unless $backingfile; # Power VM off, delete it's disk image, and create it again. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/ssh_screen.pm new/os-autoinst-5.1784551853.234e380/consoles/ssh_screen.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/ssh_screen.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/ssh_screen.pm 2026-07-20 14:50:53.000000000 +0200 @@ -7,6 +7,7 @@ use Carp 'croak'; use Net::SSH2 'LIBSSH2_ERROR_EAGAIN'; use Time::Seconds; +use Time::HiRes qw(sleep); has ssh_connection => undef; has ssh_channel => undef; @@ -52,7 +53,7 @@ unless $error_seen{$errcode}++; last if ($args{timeout} == 0); - select undef, undef, undef, 0.25; + sleep 0.25; } return undef; } @@ -80,12 +81,12 @@ croak "Lost SSH connection to SUT: $errcode $errstr" if $errcode != LIBSSH2_ERROR_EAGAIN; - select undef, undef, undef, 0.1; + sleep 0.1; } elsif ($chunk < 0) { # Old Net::SSH2 error signaling croak "Lost SSH connection to SUT: $chunk" if $chunk != LIBSSH2_ERROR_EAGAIN; - select undef, undef, undef, 0.1; + sleep 0.1; } else { $written += $chunk; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/video_stream.pm new/os-autoinst-5.1784551853.234e380/consoles/video_stream.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/video_stream.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/video_stream.pm 2026-07-20 14:50:53.000000000 +0200 @@ -389,7 +389,7 @@ # tell ustreamer we are reading, otherwise it won't write new frames my $clock = clock_gettime(CLOCK_MONOTONIC); - substr($ustreamer_map, $client_clock_offset, 16) = pack 'D', $clock; + substr $ustreamer_map, $client_clock_offset, 16, (pack 'D', $clock); # no new frame return undef if $self->{ustreamer_last_id} && $id == $self->{ustreamer_last_id}; $self->{ustreamer_last_id} = $id; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/consoles/virtio_terminal.pm new/os-autoinst-5.1784551853.234e380/consoles/virtio_terminal.pm --- old/os-autoinst-5.1783672666.346fbe5/consoles/virtio_terminal.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/consoles/virtio_terminal.pm 2026-07-20 14:50:53.000000000 +0200 @@ -96,13 +96,25 @@ This is a helper method for system which do not have F_GETPIPE_SZ in there Fcntl bindings. See https://perldoc.perl.org/Fcntl.html =cut -sub F_GETPIPE_SZ () { eval 'no warnings "all"; Fcntl::F_GETPIPE_SZ;' || 1032 } + +sub F_GETPIPE_SZ () { + eval { + no warnings 'all'; + Fcntl::F_GETPIPE_SZ; + } || 1032; +} =head2 F_SETPIPE_SZ This is a helper method for system which do not have F_SETPIPE_SZ in there Fcntl bindings. See: https://perldoc.perl.org/Fcntl.html =cut -sub F_SETPIPE_SZ () { eval 'no warnings "all"; Fcntl::F_SETPIPE_SZ;' || 1031 } + +sub F_SETPIPE_SZ () { + eval { + no warnings 'all'; + Fcntl::F_SETPIPE_SZ; + } || 1031; +} sub set_pipe_sz ($self, $fd, $newsize) { no autodie; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/cpanfile new/os-autoinst-5.1784551853.234e380/cpanfile --- old/os-autoinst-5.1783672666.346fbe5/cpanfile 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/cpanfile 2026-07-20 14:50:53.000000000 +0200 @@ -104,7 +104,7 @@ requires 'Devel::Cover'; requires 'Module::CPANfile'; requires 'PPI'; - requires 'Perl::Tidy', '== 20260204.0.0'; + requires 'Perl::Tidy', '== 20260705.0.0'; requires 'Template::Toolkit'; requires 'Test::CheckGitStatus'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/dependencies.yaml new/os-autoinst-5.1784551853.234e380/dependencies.yaml --- old/os-autoinst-5.1783672666.346fbe5/dependencies.yaml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/dependencies.yaml 2026-07-20 14:50:53.000000000 +0200 @@ -99,7 +99,7 @@ perl(Devel::Cover): perl(Module::CPANfile): perl(Template::Toolkit): - perl(Perl::Tidy): == 20260204.0.0 + perl(Perl::Tidy): == 20260705.0.0 perl(PPI): perl(Test::CheckGitStatus): shfmt: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/doc/backend_vars.md new/os-autoinst-5.1784551853.234e380/doc/backend_vars.md --- old/os-autoinst-5.1783672666.346fbe5/doc/backend_vars.md 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/doc/backend_vars.md 2026-07-20 14:50:53.000000000 +0200 @@ -17,7 +17,7 @@ | _SKIP_POST_FAIL_HOOKS | boolean | 0 | Skip the execution of post_fail_hook methods if set. This can be useful to save test execution time during test development when the post_fail_hook is not expected to provide any value as most likely the test developer already knows what needs to be done as a next step on a test fail. | | TEST_GIT_REFSPEC | string | | git refspec to check out within `CASEDIR` when `CASEDIR` is a git working copy. By default, does not change the content of `CASEDIR`. Overrides the optional git refspec in `CASEDIR`. Can be used to explicitly select a git commit within an existing git working copy and also to skip unnecessary git network transfers when `CASEDIR` is already providing the right git working copy. | | NEEDLES_GIT_REFSPEC | string | | git refspec to check out within `NEEDLES_DIR`. See `TEST_GIT_REFSPEC` for details. | -| EXTERNAL_VIDEO_ENCODER_CMD | string | | Specifies the command line for invoking a custom video encoder. It is supposed to accept a sequence of PPM images via stdin. The placeholder `%OUTPUT_FILE_NAME%` is replaced with the output file path. The output file path is appended if the placeholder is missing. Examples: `ffmpeg -y -hide_banner -nostats -r 24 -f image2pipe -vcodec ppm -i - -pix_fmt yuv420p -c:v libvpx-vp9 -crf 35 -b:v 1500k -cpu-used 1`, `podman run --rm --workdir /pool -i -v .:/pool ghcr.io/tamara-schmitz/ffmpeg-docker-container-free -y -hide_banner -nostats -r 24 -f image2pipe -vcodec ppm -i - -pix_fmt yuv420p -c:v libsvtav1 -preset 10 -crf 35` | +| EXTERNAL_VIDEO_ENCODER_CMD | string | | Specifies the command line for invoking a custom video encoder. It is supposed to accept a sequence of PPM images via stdin. The placeholder `%OUTPUT_FILE_NAME%` is replaced with the output file path. The output file path is appended if the placeholder is missing. Examples: `ffmpeg -y -hide_banner -nostats -r 24 -f image2pipe -vcodec ppm -i - -pix_fmt yuv420p -c:v libvpx-vp9 -crf 35 -b:v 1500k -cpu-used 1`, `podman run --rm --workdir /pool -i -v .:/pool ghcr.io/tamara-schmitz/ffmpeg-docker-container-free -y -hide_banner -nostats -r 24 -f image2pipe -vcodec ppm -i - -pix_fmt yuv420p -c:v libsvtav1 -preset 10 -crf 35 -b:v 0` | | EXTERNAL_VIDEO_ENCODER_OUTPUT_FILE_EXTENSION | string | webm | The extension of the output file when `EXTERNAL_VIDEO_ENCODER_CMD` is used. | | EXTERNAL_VIDEO_ENCODER_ADDITIONALLY | boolean | 0 | Whether `EXTERNAL_VIDEO_ENCODER_CMD` should only be invoked additionally to the built-in Theora encoder. This means two videos will be created and uploaded which can be useful for comparison. (Configuring an external video encoder disables the built-in one by default.) | | NOVIDEO | boolean | 0 | Whether the creation of the video should be disabled and also any `EXTERNAL_VIDEO_ENCODER_` variables be ignored. | @@ -111,6 +111,7 @@ | ARCH | x86_64\|i686\|aarch64\|... | depends on tested medium | Architecture of VM. | | APPEND | string | | Kernel options to pass when using the KERNEL variable, see https://qemu-project.gitlab.io/qemu/system/linuxboot.html | | ATACONTROLLER | see qemu -device ?, e. g. for SATA: ich9-ahci | | Controller for ATA devices, needed for connecting disks as SATA. | +| BIOS | string | | Set the filename for the BIOS | | BOOT_HDD_IMAGE | boolean | | enables boot from HDD_1 (BOOTFROM has higher priority) | | BOOT_MENU | boolean | undef | enables boot menu for selection of boot device | | BOOT_MENU_TIMEOUT | integer | 5000 | boot menu timeout in ms. Needs BOOT_MENU | @@ -206,6 +207,7 @@ | UEFI_PFLASH_SECURE_BOOT | boolean | | Enable or disable secure boot in the UEFI firmware variables file using `virt-fw-vars`. | | UEFI_PFLASH_RESOLUTION | string | | Specify the resolution to configure via UEFI firmware variables file using `virt-fw-vars`, e.g. `800x600` for a width of 800 pixels and a height of 600 pixels. | | PUBLISH_PFLASH_VARS | string | | Specify the file name to publish the UEFI vars file as | +| UEFI_PFLASH | boolean | 0 | (Deprecated, use UEFI_PFLASH_VARS) Enable the pflash mode to write the UEFI variables directly into the firmware file instead of NVvars in the EFI system partition | | UEFI_BIOS | | | Deprecated, use UEFI_PFLASH_CODE | | USBBOOT | boolean | 0 | Mount ISO as USB disk and boot VM from it | | USBSIZEGB | integer | size of ISO | Size of USB disk for USBBOOT | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/base-commit-message-checker.yml 2026-07-20 14:50:53.000000000 +0200 @@ -10,7 +10,7 @@ permissions: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - name: Install gitlint diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/perl-author-tests.yml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/perl-author-tests.yml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/perl-author-tests.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/perl-author-tests.yml 2026-07-20 14:50:53.000000000 +0200 @@ -12,7 +12,7 @@ container: image: registry.opensuse.org/devel/openqa/containers/os-autoinst_dev steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - run: | git config --system --add safe.directory "$GITHUB_WORKSPACE" make test-author diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/perl-lint-checks.yml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/perl-lint-checks.yml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/perl-lint-checks.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/perl-lint-checks.yml 2026-07-20 14:50:53.000000000 +0200 @@ -12,5 +12,5 @@ container: image: registry.opensuse.org/devel/openqa/containers/os-autoinst_dev steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - run: GITHUB_ACTIONS=1 make test-tidy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/test.yaml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/test.yaml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/test.yaml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/test.yaml 2026-07-20 14:50:53.000000000 +0200 @@ -11,5 +11,5 @@ container: image: registry.opensuse.org/devel/openqa/containers/os-autoinst_dev steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - run: make test-t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/yamllint.yml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/yamllint.yml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.github/workflows/yamllint.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.github/workflows/yamllint.yml 2026-07-20 14:50:53.000000000 +0200 @@ -10,7 +10,7 @@ contents: read name: "YAML-lint" steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: docker://registry.opensuse.org/home/okurz/container/containers/tumbleweed:yamllint with: entrypoint: make diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.gitlint new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.gitlint --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.gitlint 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.gitlint 2026-07-20 14:50:53.000000000 +0200 @@ -10,5 +10,5 @@ [ignore-by-title] # "git subrepo" creates automatic, non-conventional commits we need to accept # https://progress.opensuse.org/issues/198254 -regex=^git subrepo pull +regex=^(git subrepo pull|(chore|build)\(deps(-dev)?\): bump) ignore=all diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.gitrepo new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.gitrepo --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.gitrepo 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.gitrepo 2026-07-20 14:50:53.000000000 +0200 @@ -6,7 +6,7 @@ [subrepo] remote = [email protected]:os-autoinst/os-autoinst-common.git branch = master - commit = e9a35c74028cb8f1e64a4d787d5751dd7b0790c8 - parent = e988970b71bea5787b5158547b54a10c16959a0b + commit = 7e1f983c44858679f93e2046ed1aa82171bd7f4b + parent = e750fdd98b1e45e1b516447e46b8289b6d438fbd method = merge cmdver = 0.4.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.mergify.yml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.mergify.yml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/.mergify.yml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/.mergify.yml 2026-07-20 14:50:53.000000000 +0200 @@ -25,9 +25,14 @@ actions: merge: method: merge - - name: ask to resolve conflict + - name: automatic merge for dependabot updates conditions: - - conflict + - base=master + - author=dependabot[bot] + - -label~=^acceptance-tests-needed|not-ready + - "#check-failure=0" + - "#check-pending=0" + - linear-history actions: - comment: - message: This pull request is now in conflicts. Could you fix it? 🙏 + merge: + method: squash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/cpanfile new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/cpanfile --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/cpanfile 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/cpanfile 2026-07-20 14:50:53.000000000 +0200 @@ -18,7 +18,7 @@ requires 'Code::TidyAll'; requires 'Perl::Critic'; requires 'Perl::Critic::Community'; - requires 'Perl::Tidy', '== 20260204'; + requires 'Perl::Tidy', '== 20260705'; requires 'Test::Perl::Critic'; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/dependencies.yaml new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/dependencies.yaml --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/dependencies.yaml 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/dependencies.yaml 2026-07-20 14:50:53.000000000 +0200 @@ -17,7 +17,7 @@ perl(Module::CPANfile): develop_requires: - perl(Perl::Tidy): '== 20260204' + perl(Perl::Tidy): '== 20260705' perl(Code::TidyAll): perl(Perl::Critic): perl(Perl::Critic::Community): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/tools/check-line-coverage new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/tools/check-line-coverage --- old/os-autoinst-5.1783672666.346fbe5/external/os-autoinst-common/tools/check-line-coverage 1970-01-01 01:00:00.000000000 +0100 +++ new/os-autoinst-5.1784551853.234e380/external/os-autoinst-common/tools/check-line-coverage 2026-07-20 14:50:53.000000000 +0200 @@ -0,0 +1,44 @@ +#!/bin/bash +# This takes a JSON file generated by Devel::Cover::Report::Codecovbash +# and checks if every line is covered. + +if [[ $# -eq 0 ]]; then + echo "Usage: $0 build/cover_db/codecov.json" >&2 + exit 1 +fi + +input_file=$1 +if [[ ! -f $input_file ]]; then + echo "Input file $input_file does not exist" >&2 + exit 1 +fi + +limit=${2:-500} + +declare -i counter=0 + +echo "Checking for uncovered lines" >&2 + +while read -r file line; do + [[ $line -eq 0 ]] && continue + if [[ -f "$file" ]]; then + content=$(sed -n "${line}p" "$file") + printf "%-60s %s\n" "$file:$line" "| $content" + [[ $GITHUB_ACTIONS == true ]] && echo "::warning file=${file},line=${line},title=Uncovered Line::${content}" + counter+=1 + if [[ $counter -ge $limit ]]; then + echo "Reached limit of $limit printed lines. Stopping." >&2 + break + fi + else + echo "Warning: File '$file' not found." >&2 + fi +done < <(jq -r '.coverage | to_entries[] | .key as $file | .value | to_entries[] | select(.value == 0) | "\($file) \(.key)"' "$input_file") + +[[ $GITHUB_ACTIONS == true ]] && echo "::notice ::Found $counter uncovered lines." +if [[ $counter -gt 0 ]]; then + echo "$counter uncovered lines" >&2 + exit 1 +fi + +echo "All lines covered \\o/" >&2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/ppmclibs/tinycv.pm new/os-autoinst-5.1784551853.234e380/ppmclibs/tinycv.pm --- old/os-autoinst-5.1783672666.346fbe5/ppmclibs/tinycv.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/ppmclibs/tinycv.pm 2026-07-20 14:50:53.000000000 +0200 @@ -12,8 +12,7 @@ require Exporter; require DynaLoader; -our @ISA = qw(Exporter DynaLoader); -our @EXPORT = qw(); +use parent qw(Exporter DynaLoader); our $VERSION = '1.0'; @@ -160,7 +159,7 @@ if (@candidates && $candidates[0]->{ok}) { $best = shift @candidates; } - if (wantarray) { + if (wantarray) { ## no critic (Community::Wantarray) return ($best, \@candidates); } else { @@ -172,7 +171,7 @@ my $found = $self->search_($needle, $threshold, $search_ratio, $stopwatch); $stopwatch->lap("** search_: single needle: $needle->{name}") if $stopwatch; return undef unless $found; - if (wantarray) { + if (wantarray) { ## no critic (Community::Wantarray) return ($found, undef) if ($found->{ok}); return (undef, [$found]); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/script/os-autoinst-generate-needle-preview new/os-autoinst-5.1784551853.234e380/script/os-autoinst-generate-needle-preview --- old/os-autoinst-5.1783672666.346fbe5/script/os-autoinst-generate-needle-preview 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/script/os-autoinst-generate-needle-preview 2026-07-20 14:50:53.000000000 +0200 @@ -46,7 +46,7 @@ my $image_data = b64_encode($pngpath->slurp, ' '); open $SVGFILE, '>', path($pp->dirname, "${name}.svg"); - select $SVGFILE; + select $SVGFILE; ## no critic (InputOutput::ProhibitOneArgSelect) # The generated file should be identical to the svg file as saved with inkscape as 'Standard SVG' printf <<~"EOM"; @@ -101,7 +101,7 @@ print <<~"EOM"; </svg> EOM - select STDOUT; + select STDOUT; ## no critic (InputOutput::ProhibitOneArgSelect) close $SVGFILE; return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/08-autotest.t new/os-autoinst-5.1784551853.234e380/t/08-autotest.t --- old/os-autoinst-5.1783672666.346fbe5/t/08-autotest.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/08-autotest.t 2026-07-20 14:50:53.000000000 +0200 @@ -76,56 +76,60 @@ my $vm_stopped = 0; $mock_bmwqemu->noop('save_json_file'); $mock_bmwqemu->redefine(stop_vm => sub { $vm_stopped = 1 }); -my $mock_basetest = Test::MockModule->new('basetest'); -$mock_basetest->noop('_result_add_screenshot'); # stop `run_all` from calling `Devel::Cover::report()` and quitting at the end # note: We are not calling `run_all` from a sub process here so the extra coverage collection must *not* run. -my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); -$mock_autotest->noop('_terminate'); -my $died; -my $completed; -like warning { - stderr_like { autotest::run_all } qr/Sending tests_done/, 'tests_done sent'; -}, qr/ERROR: no tests loaded/, 'run_all outputs status on stderr'; - -($died, $completed) = get_tests_done; -is $died, 1, 'run_all with no tests should catch runalltests dying'; -is $completed, 0, 'run_all with no tests should not complete'; - -loadtest 'start'; -loadtest 'next'; -is keys %autotest::tests, 2, 'two tests have been scheduled'; -loadtest 'start', 'rescheduling same step later'; -is keys %autotest::tests, 3, 'three steps have been scheduled (one twice)' or always_explain %autotest::tests; -is $autotest::tests{'tests-start1'}->{name}, 'start#1', 'handle duplicate tests'; -is $autotest::tests{'tests-start1'}->{fullname}, 'tests-start#1', 'duplicate test has correct fullname'; -is $autotest::tests{'tests-start1'}->{$_}, $autotest::tests{'tests-start'}->{$_}, "duplicate tests point to the same $_" - for qw(script category class); - -like warning { - stderr_like { autotest::run_all } qr/Sending tests_done/, 'tests_done sent'; -}, qr/isotovideo.*not initialized/, 'autotest methods need a valid isotovideo socket'; - -@sent = (); -$autotest::isotovideo = 1; -stderr_like { autotest::run_all } qr/finished/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'start+next+start should not die'; -is $completed, 1, 'start+next+start should complete'; +subtest 'load_test && run_all' => sub { + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); + my ($died, $completed); + like warning { + stderr_like { autotest::run_all } qr/Sending tests_done/, 'tests_done sent'; + }, qr/ERROR: no tests loaded/, 'run_all outputs status on stderr'; + + ($died, $completed) = get_tests_done; + is $died, 1, 'run_all with no tests should catch runalltests dying'; + is $completed, 0, 'run_all with no tests should not complete'; + + loadtest 'start'; + loadtest 'next'; + is keys %autotest::tests, 2, 'two tests have been scheduled'; + loadtest 'start', 'rescheduling same step later'; + is keys %autotest::tests, 3, 'three steps have been scheduled (one twice)' or always_explain %autotest::tests; + is $autotest::tests{'tests-start1'}->{name}, 'start#1', 'handle duplicate tests'; + is $autotest::tests{'tests-start1'}->{fullname}, 'tests-start#1', 'duplicate test has correct fullname'; + is $autotest::tests{'tests-start1'}->{$_}, $autotest::tests{'tests-start'}->{$_}, "duplicate tests point to the same $_" + for qw(script category class); + + like warning { + stderr_like { autotest::run_all } qr/Sending tests_done/, 'tests_done sent'; + }, qr/isotovideo.*not initialized/, 'autotest methods need a valid isotovideo socket'; + + @sent = (); + $autotest::isotovideo = 1; + stderr_like { autotest::run_all } qr/finished/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'start+next+start should not die'; + is $completed, 1, 'start+next+start should complete'; +}; # Test loading snapshots with always_rollback flag. Have to put it here, before loading # runargs test module, as it fails. my ($reverts_done, $snapshots_made) = (0, 0); -# uncoverable statement count:2 -$mock_autotest->redefine(load_snapshot => sub { $reverts_done++ }); -$mock_autotest->redefine(make_snapshot => sub { $snapshots_made++ }); -$mock_autotest->redefine(query_isotovideo => 0); -$mock_basetest->redefine(test_flags => {milestone => 1}); -$mock_basetest->noop('record_resultfile'); sub snapshot_subtest ($name, $sub) { subtest $name, $sub; $reverts_done = $snapshots_made = 0; @sent = () } subtest 'test always_rollback flag' => sub { + my $mock_basetest = Test::MockModule->new('basetest'); + $mock_basetest->noop('_result_add_screenshot'); + $mock_basetest->redefine(test_flags => {milestone => 1}); + $mock_basetest->noop('record_resultfile'); + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); + # uncoverable statement count:2 + $mock_autotest->redefine(load_snapshot => sub { $reverts_done++ }); + $mock_autotest->redefine(make_snapshot => sub { $snapshots_made++ }); + $mock_autotest->redefine(query_isotovideo => 0); + my ($died, $completed); snapshot_subtest 'no rollback is triggered when flag is not explicitly set to true' => sub { stderr_like { autotest::run_all } qr/finished/, 'run_all outputs status on stderr'; ($died, $completed) = get_tests_done; @@ -241,117 +245,132 @@ stderr_like { $w = warning { autotest::run_all } } qr/Snapshots are not supported/, 'run_all outputs on stderr'; like $w, qr/always_rollback requested but snapshots are not supported by the backend/, 'fails with explicit error message'; }; - $mock_basetest->unmock($_) for qw(runtest test_flags); - $mock_autotest->unmock($_) for qw(load_snapshot make_snapshot query_isotovideo); + undef $mock_autotest; + undef $mock_basetest; }; -my $targs = OpenQA::Test::RunArgs->new(); -stderr_like { - autotest::loadtest('tests/run_args.pm', name => 'alt_name', run_args => $targs) -} -qr@scheduling alt_name tests/run_args.pm@; -stderr_like { autotest::run_all } qr/finished alt_name tests/, 'dynamic scheduled alt_name shows up'; -($died, $completed) = get_tests_done; -is $died, 0, 'run_args test should not die'; -is $completed, 1, 'run_args test should complete'; - -stderr_like { autotest::loadtest('tests/run_args.pm', name => 'alt_name') } qr@scheduling alt_name tests/run_args.pm@; -stderr_like { autotest::run_all } qr/Snapshots are not supported/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'run_args test should not die if there is no run_args'; -is $completed, 0, 'run_args test should not complete if there is no run_args'; -throws_ok { autotest::loadtest('tests/run_args.pm', name => 'alt_name', run_args => {foo => 'bar'}) } qr/The run_args must be a sub-class of OpenQA::Test::RunArgs/, 'error message mentions RunArgs'; +subtest run_args => sub { + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); + my ($died, $completed); + my $targs = OpenQA::Test::RunArgs->new(); + stderr_like { + autotest::loadtest('tests/run_args.pm', name => 'alt_name', run_args => $targs) + } + qr@scheduling alt_name tests/run_args.pm@; + stderr_like { autotest::run_all } qr/finished alt_name tests/, 'dynamic scheduled alt_name shows up'; + ($died, $completed) = get_tests_done; + is $died, 0, 'run_args test should not die'; + is $completed, 1, 'run_args test should complete'; + + stderr_like { autotest::loadtest('tests/run_args.pm', name => 'alt_name') } qr@scheduling alt_name tests/run_args.pm@; + stderr_like { autotest::run_all } qr/Snapshots are not supported/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'run_args test should not die if there is no run_args'; + is $completed, 0, 'run_args test should not complete if there is no run_args'; + + throws_ok { autotest::loadtest('tests/run_args.pm', name => 'alt_name', run_args => {foo => 'bar'}) } qr/The run_args must be a sub-class of OpenQA::Test::RunArgs/, 'error message mentions RunArgs'; +}; # now let's make the tests fail...but so far none is fatal. We also # have to mock query_isotovideo so we think snapshots are supported. # we cause the failure by mocking runtest rather than using a test # which dies, as runtest does a whole bunch of stuff when the test # dies that we may not want to run into here -$mock_basetest->redefine(runtest => sub { die 'oh noes!' }); my $enable_snapshots = 1; -$mock_autotest->redefine(query_isotovideo => sub ($command, $arguments) { - $command eq 'backend_can_handle' && $arguments->{function} eq 'snapshots' ? $enable_snapshots : 1; -}); - -my $record_resultfile_called; -$mock_basetest->redefine(record_resultfile => sub { ++$record_resultfile_called }); -stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'non-fatal test failure should not die'; -is $completed, 1, 'non-fatal test failure should complete'; -is $record_resultfile_called, 4, 'record_resultfile was called'; - -# now let's add an ignore_failure test -loadtest 'ignore_failure'; -stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'unimportant test failure should not die'; -is $completed, 1, 'unimportant test failure should complete'; - -# unmock runtest, to fail in search_for_expected_serial_failures -$mock_basetest->unmock('runtest'); -# mock reading of the serial output -$mock_basetest->redefine(search_for_expected_serial_failures => sub ($self) { - $self->{fatal_failure} = 1; - die 'Got serial hard failure'; -}); - -$bmwqemu::vars{MAKETESTSNAPSHOTS} = 1; -stderr_like { autotest::run_all } qr/Snapshots are supported/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'fatal serial failure test should not die'; -is $completed, 0, 'fatal serial failure test should not complete'; -$bmwqemu::vars{MAKETESTSNAPSHOTS} = 0; -# make the serial failure non-fatal -$mock_basetest->unmock('search_for_expected_serial_failures'); -$mock_basetest->redefine(search_for_expected_serial_failures => sub ($self) { - $self->{fatal_failure} = 0; - die 'Got serial hard failure'; -}); - -$autotest::current_test = Test::MockObject->new->set_true('record_resultfile'); -stderr_like { autotest::run_all } qr/Snapshots are supported/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'non-fatal serial failure test should not die'; -is $completed, 1, 'non-fatal serial failure test should complete'; - -# disable snapshots and clean last milestone from previous testrun (with had snapshots enabled) -$enable_snapshots = 0; -$autotest::last_milestone = undef; - -my $output = combined_from(sub { autotest::run_all }); -like $output, qr/Snapshots are not supported/, 'snapshots actually disabled'; -unlike $output, qr/Loading a VM snapshot/, 'no attempt to load VM snapshot'; -($died, $completed) = get_tests_done; -is $died, 0, 'non-fatal serial failure test should not die'; -is $completed, 0, 'non-fatal serial failure test should not complete by default without snapshot support'; - -$mock_basetest->redefine(test_flags => {fatal => 0}); -$output = combined_from(sub { autotest::run_all }); -like $output, qr/Snapshots are not supported/, 'snapshots actually disabled'; -unlike $output, qr/Loading a VM snapshot/, 'no attempt to load VM snapshot'; -($died, $completed) = get_tests_done; -is $died, 0, 'non-fatal serial failure test should not die'; -is $completed, 1, 'non-fatal serial failure test should complete with {fatal => 0} and not snapshot support'; - -# Revert mock for runtest and remove mock for search_for_expected_serial_failures -$mock_basetest->unmock('search_for_expected_serial_failures'); -$mock_basetest->unmock('test_flags'); -$mock_basetest->redefine(runtest => sub { die "oh noes!\n"; }); - -# now let's add a fatal test -loadtest 'fatal'; -stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; -($died, $completed) = get_tests_done; -is $died, 0, 'fatal test failure should not die'; -is $completed, 0, 'fatal test failure should not complete'; - -loadtest 'fatal', 'rescheduling same step later' for 1 .. 10; -my @opts = qw(script fullname category class); -is @{$autotest::tests{'tests-fatal'}}{@opts}, @{$autotest::tests{'tests-fatal' . $_}}{@opts}, "tests-fatal$_ share same options with tests-fatal" - and is $autotest::tests{'tests-fatal' . $_}->{name}, 'fatal#' . $_ - for 1 .. 10; + +subtest 'failing tests' => sub { + my $mock_basetest = Test::MockModule->new('basetest'); + $mock_basetest->noop('_result_add_screenshot'); + $mock_basetest->redefine(test_flags => {milestone => 1}); + $mock_basetest->noop('record_resultfile'); + $mock_basetest->redefine(runtest => sub { die 'oh noes!' }); + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); + $mock_autotest->redefine(query_isotovideo => sub ($command, $arguments) { + $command eq 'backend_can_handle' && $arguments->{function} eq 'snapshots' ? $enable_snapshots : 1; + }); + my ($died, $completed); + my $record_resultfile_called; + $mock_basetest->redefine(record_resultfile => sub { ++$record_resultfile_called }); + stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'non-fatal test failure should not die'; + is $completed, 1, 'non-fatal test failure should complete'; + is $record_resultfile_called, 4, 'record_resultfile was called'; + + # now let's add an ignore_failure test + loadtest 'ignore_failure'; + stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'unimportant test failure should not die'; + is $completed, 1, 'unimportant test failure should complete'; + + # unmock runtest, to fail in search_for_expected_serial_failures + $mock_basetest->unmock('runtest'); + # mock reading of the serial output + $mock_basetest->redefine(search_for_expected_serial_failures => sub ($self) { + $self->{fatal_failure} = 1; + die 'Got serial hard failure'; + }); + + $bmwqemu::vars{MAKETESTSNAPSHOTS} = 1; + stderr_like { autotest::run_all } qr/Snapshots are supported/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'fatal serial failure test should not die'; + is $completed, 0, 'fatal serial failure test should not complete'; + $bmwqemu::vars{MAKETESTSNAPSHOTS} = 0; + # make the serial failure non-fatal + $mock_basetest->unmock('search_for_expected_serial_failures'); + $mock_basetest->redefine(search_for_expected_serial_failures => sub ($self) { + $self->{fatal_failure} = 0; + die 'Got serial hard failure'; + }); + + $autotest::current_test = Test::MockObject->new->set_true('record_resultfile'); + stderr_like { autotest::run_all } qr/Snapshots are supported/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'non-fatal serial failure test should not die'; + is $completed, 1, 'non-fatal serial failure test should complete'; + + # disable snapshots and clean last milestone from previous testrun (with had snapshots enabled) + $enable_snapshots = 0; + $autotest::last_milestone = undef; + + my $output = combined_from(sub { autotest::run_all }); + like $output, qr/Snapshots are not supported/, 'snapshots actually disabled'; + unlike $output, qr/Loading a VM snapshot/, 'no attempt to load VM snapshot'; + ($died, $completed) = get_tests_done; + is $died, 0, 'non-fatal serial failure test should not die'; + is $completed, 0, 'non-fatal serial failure test should not complete by default without snapshot support'; + + $mock_basetest->redefine(test_flags => {fatal => 0}); + $output = combined_from(sub { autotest::run_all }); + like $output, qr/Snapshots are not supported/, 'snapshots actually disabled'; + unlike $output, qr/Loading a VM snapshot/, 'no attempt to load VM snapshot'; + ($died, $completed) = get_tests_done; + is $died, 0, 'non-fatal serial failure test should not die'; + is $completed, 1, 'non-fatal serial failure test should complete with {fatal => 0} and not snapshot support'; + + # Revert mock for runtest and remove mock for search_for_expected_serial_failures + $mock_basetest->unmock('search_for_expected_serial_failures'); + $mock_basetest->unmock('test_flags'); + $mock_basetest->redefine(runtest => sub { die "oh noes!\n"; }); + + # now let's add a fatal test + loadtest 'fatal'; + stderr_like { autotest::run_all } qr/oh noes/, 'run_all outputs status on stderr'; + ($died, $completed) = get_tests_done; + is $died, 0, 'fatal test failure should not die'; + is $completed, 0, 'fatal test failure should not complete'; + + loadtest 'fatal', 'rescheduling same step later' for 1 .. 10; + my @opts = qw(script fullname category class); + is @{$autotest::tests{'tests-fatal'}}{@opts}, @{$autotest::tests{'tests-fatal' . $_}}{@opts}, "tests-fatal$_ share same options with tests-fatal" + and is $autotest::tests{'tests-fatal' . $_}->{name}, 'fatal#' . $_ + for 1 .. 10; +}; subtest 'scheduling rules' => sub { %autotest::tests = (); @@ -386,6 +405,16 @@ subtest 'test scheduling test modules at test runtime' => sub { + my $mock_basetest = Test::MockModule->new('basetest'); + $mock_basetest->noop('_result_add_screenshot'); + $mock_basetest->noop('record_resultfile'); + $mock_basetest->redefine(runtest => sub { die 'oh noes!' }); + + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); + $mock_autotest->redefine(query_isotovideo => sub ($command, $arguments) { + $command eq 'backend_can_handle' && $arguments->{function} eq 'snapshots' ? $enable_snapshots : 1; + }); $autotest::tests_running = 0; @autotest::testorder = (); %autotest::tests = (); @@ -461,6 +490,9 @@ }; subtest 'python with bad run method' => sub { + my $mock_basetest = Test::MockModule->new('basetest'); + $mock_basetest->noop('record_resultfile'); + plan skip_all => 'Inline::Python is not available' unless $has_python; %autotest::tests = (); @@ -501,7 +533,6 @@ }; subtest rollback_activated_consoles => sub { - $mock_autotest->unmock('query_isotovideo'); $autotest::activated_consoles = ['activated_console']; $autotest::last_milestone_console = 'last_milestone_console'; @@ -600,6 +631,8 @@ }; subtest 'start_process' => sub { + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); my $fh = path(\'fake-file')->open('<'); $autotest::isotovideo = $fh; # Test the start_process subroutine @@ -616,6 +649,8 @@ }; subtest 'lua_use' => sub { + my $mock_autotest = Test::MockModule->new('autotest', no_auto => 1); + $mock_autotest->noop('_terminate'); my $lua_vars = {}; $mock_autotest->mock('lua_set' => sub ($k, $v) { $lua_vars->{$k} = $v; }); autotest::_lua_use('testapi'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/14-isotovideo.t new/os-autoinst-5.1784551853.234e380/t/14-isotovideo.t --- old/os-autoinst-5.1783672666.346fbe5/t/14-isotovideo.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/14-isotovideo.t 2026-07-20 14:50:53.000000000 +0200 @@ -220,7 +220,7 @@ is 0, checkout_wheels($case_dir, $wheels_dir), 'wheels checkout out'; load_test_schedule; like $diags[0], qr/scheduling ink$i/, 'module from the wheel scheduled'; - my $ret = eval "Copy::Writer::Content${i}::write()"; + my $ret = eval { "Copy::Writer::Content${i}"->can('write')->() }; is $ret, "val$i", 'called function in wheel library returns expected value'; rmtree "$wheels_dir/writer"; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/18-backend-qemu.t new/os-autoinst-5.1784551853.234e380/t/18-backend-qemu.t --- old/os-autoinst-5.1783672666.346fbe5/t/18-backend-qemu.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/18-backend-qemu.t 2026-07-20 14:50:53.000000000 +0200 @@ -784,6 +784,14 @@ unlike $qemu_params, qr{order=}, 'order parameter not present due to PXEBOOT'; like $qemu_params, qr{once=n}, 'once=n parameter present due to PXEBOOT'; + @qemu_params = (); + $bmwqemu::vars{PXEBOOT} = 0; + $bmwqemu::vars{UEFI} = 0; + $bmwqemu::vars{BIOS} = 'bios.bin'; + combined_like { $backend->start_qemu } qr{.+}s, 'invoked with legacy BIOS'; + $qemu_params = Mojo::Collection->new(\@qemu_params)->flatten->join(' '); + like $qemu_params, qr{bios /usr/share/qemu/bios\.bin}, 'bios parameter correctly configured'; + subtest 'various error cases' => sub { my %initial_vars = %bmwqemu::vars; $bmwqemu::vars{NICTYPE} = 'foo'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/18-qemu.t new/os-autoinst-5.1784551853.234e380/t/18-qemu.t --- old/os-autoinst-5.1783672666.346fbe5/t/18-qemu.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/18-qemu.t 2026-07-20 14:50:53.000000000 +0200 @@ -49,7 +49,7 @@ @gcmdl = $bdc->gen_cmdline(); is_deeply \@gcmdl, \@cmdl, 'Generate qemu command line for drive with 4k sector size'; -@cmdl = [qw(create -f qcow2 raid/hd1 10G)]; +@cmdl = ([qw(create -f qcow2 raid/hd1 10G)]); @gcmdl = $bdc->gen_qemu_img_cmdlines(); is_deeply \@gcmdl, \@cmdl, 'Generate qemu-img command line for single new drive'; @@ -581,6 +581,19 @@ %vars = (UEFI => 1, UEFI_PFLASH_VARS => 'vars'); throws_ok { $proc->configure_pflash(\%vars) } qr{Need UEFI_PFLASH_CODE with UEFI_PFLASH_VARS}, 'Fatal UEFI_PFLASH_VARS without UEFI_PFLASH_CODE'; + + @flash = (); + my $bios_file = tempfile->spew('bios'); + %vars = (UEFI => 1, UEFI_PFLASH => 1, BIOS => $bios_file->to_string); + my $res_pflash = $proc->configure_pflash(\%vars); + is_deeply \@flash, [['pflash', $bios_file->to_string, 4]], 'add_pflash_drive correctly called for UEFI_PFLASH'; + is $res_pflash, $proc, 'returns self'; + + %vars = (UEFI => 1, UEFI_PFLASH => 1, UEFI_PFLASH_CODE => 'code'); + throws_ok { $proc->configure_pflash(\%vars) } qr{Mixing old and new PFLASH variables}, 'Mixing old and new PFLASH variables (code)'; + + %vars = (UEFI => 1, UEFI_PFLASH => 1, UEFI_PFLASH_VARS => 'vars'); + throws_ok { $proc->configure_pflash(\%vars) } qr{Mixing old and new PFLASH variables}, 'Mixing old and new PFLASH variables (vars)'; }; subtest connect_qmp => sub { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/19-isotovideo-command-processing.t new/os-autoinst-5.1784551853.234e380/t/19-isotovideo-command-processing.t --- old/os-autoinst-5.1783672666.346fbe5/t/19-isotovideo-command-processing.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/19-isotovideo-command-processing.t 2026-07-20 14:50:53.000000000 +0200 @@ -502,7 +502,7 @@ my $runner = OpenQA::Isotovideo::Runner->new; $runner->command_handler($command_handler); # We need valid fd's so fileno works but they're never used - open my $readable, "$Bin"; + open my $readable, '<', $Bin; $runner->testfd($readable); $runner->cmd_srv_fd($readable); stderr_like { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/23-baseclass.t new/os-autoinst-5.1784551853.234e380/t/23-baseclass.t --- old/os-autoinst-5.1783672666.346fbe5/t/23-baseclass.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/23-baseclass.t 2026-07-20 14:50:53.000000000 +0200 @@ -142,7 +142,7 @@ shift->{connected} = 0; return 1; }); - $self->mock(error => sub { wantarray ? @net_ssh2_error : ($net_ssh2_error[0] // 0) }); + $self->mock(error => sub { wantarray ? @net_ssh2_error : ($net_ssh2_error[0] // 0) }); ## no critic (Community::Wantarray) $self->mock(sock => sub ($self) { unless ($self->{sock}) { my $mock_sock = Test::MockObject->new(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/26-video_stream.t new/os-autoinst-5.1784551853.234e380/t/26-video_stream.t --- old/os-autoinst-5.1783672666.346fbe5/t/26-video_stream.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/26-video_stream.t 2026-07-20 14:50:53.000000000 +0200 @@ -401,7 +401,7 @@ $console->mouse_hide({}); $console->disable; - ok open($cmds_fh, 'input-commands'), 'open input-commands'; + ok open($cmds_fh, '<', 'input-commands'), 'open input-commands'; @cmds = <$cmds_fh>; is_deeply \@cmds, [ "mouse_move 320 420\n", @@ -421,7 +421,7 @@ $console->send_key({key => 'ctrl-x'}); $console->type_string({text => "some test\n"}); $console->disable; - ok open($cmds_fh, 'input-commands'), 'open input-commands'; + ok open($cmds_fh, '<', 'input-commands'), 'open input-commands'; @cmds = <$cmds_fh>; is_deeply \@cmds, [ "a\n", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-s3270.t new/os-autoinst-5.1784551853.234e380/t/27-consoles-s3270.t --- old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-s3270.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/27-consoles-s3270.t 2026-07-20 14:50:53.000000000 +0200 @@ -24,7 +24,7 @@ my $socket_mock = Test::MockObject->new->set_true(qw(sockopt fileno print connected close blocking)); $localXvnc_mock->redefine(activate => sub ($self) { $self->{DISPLAY} = 'display' }); -$vnc_mock->redefine(_read_socket => sub { substr(${$_[1]}, $_[3], $_[2]) = $socket_mock->mocked_read; length ${$_[1]} }); +$vnc_mock->redefine(_read_socket => sub { substr ${$_[1]}, $_[3], $_[2], $socket_mock->mocked_read; length ${$_[1]} }); $vnc_mock->redefine(login => 1); $inet_mock->redefine(new => $socket_mock); $ipc_run_mock->redefine(pump => 1); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-sshXtermIPMI.t new/os-autoinst-5.1784551853.234e380/t/27-consoles-sshXtermIPMI.t --- old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-sshXtermIPMI.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/27-consoles-sshXtermIPMI.t 2026-07-20 14:50:53.000000000 +0200 @@ -44,7 +44,7 @@ $s->mock(read => sub { $_[1] = $s->mocked_read; length $_[1] }); $s->mock($_ => sub { push @printed, $_[1] }) for qw(print write); -$vnc_mock->redefine(_read_socket => sub { substr(${$_[1]}, $_[3], $_[2]) = $s->mocked_read; length ${$_[1]} }); +$vnc_mock->redefine(_read_socket => sub { substr ${$_[1]}, $_[3], $_[2], $s->mocked_read; length ${$_[1]} }); $inet_mock->redefine(new => $s); $backend_mock->redefine(do_mc_reset => sub { bmwqemu::diag 'IPMI mc reset success'; }); $testapi_console_mock->redefine(backend => $backend); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-vmware.t new/os-autoinst-5.1784551853.234e380/t/27-consoles-vmware.t --- old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-vmware.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/27-consoles-vmware.t 2026-07-20 14:50:53.000000000 +0200 @@ -240,12 +240,12 @@ close $dewebsockify_pipe; # might fail because dewebsockify has already exited but that's ok }; subtest 'handle error when WebSocket server is not reachable' => sub { - my $dewebsockify_pid = open my $dewebsockify_pipe, "$dewebsockify_cmd_start ws://127.0.0.:4 2>&1 |" or die "Unable to start dewebsockify: $!"; + my $dewebsockify_pid = open my $dewebsockify_pipe, '-|', "$dewebsockify_cmd_start ws://127.0.0.:4 2>&1" or die "Unable to start dewebsockify: $!"; $connect_to_dewebsockify_with_multiple_attempts->(close_immediately => 1, wait_pid => $dewebsockify_pid); $assert_log->($dewebsockify_pipe, qr/WebSocket connection error:/); }; subtest 'handle error when HTTP server is not upgrading to WebSockets' => sub { - my $dewebsockify_pid = open my $dewebsockify_pipe, "$dewebsockify_cmd_start ws://127.0.0.1:$ws_port/foo 2>&1 |" or die "Unable to start dewebsockify: $!"; + my $dewebsockify_pid = open my $dewebsockify_pipe, '-|', "$dewebsockify_cmd_start ws://127.0.0.1:$ws_port/foo 2>&1" or die "Unable to start dewebsockify: $!"; $connect_to_dewebsockify_with_multiple_attempts->(close_immediately => 0, wait_pid => $dewebsockify_pid); $assert_log->($dewebsockify_pipe, qr/WebSocket 404 response: Not Found/); }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-vnc.t new/os-autoinst-5.1784551853.234e380/t/27-consoles-vnc.t --- old/os-autoinst-5.1783672666.346fbe5/t/27-consoles-vnc.t 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/t/27-consoles-vnc.t 2026-07-20 14:50:53.000000000 +0200 @@ -34,7 +34,7 @@ sub _setup_rfb_magic () { $s->set_series('mocked_read', 'RFB 003.006', pack 'N', 1) } _setup_rfb_magic; $s->mock(read => sub { $_[1] = $s->mocked_read; length $_[1] }); -$vnc_mock->redefine(_read_socket => sub { substr(${$_[1]}, $_[3], $_[2]) = $s->mocked_read; length ${$_[1]} }); +$vnc_mock->redefine(_read_socket => sub { substr ${$_[1]}, $_[3], $_[2], $s->mocked_read; length ${$_[1]} }); $s->mock($_ => sub { push @printed, $_[1] }) for qw(print write); $inet_mock->redefine(new => $s); $vnc_mock->noop('_server_initialization'); @@ -454,8 +454,8 @@ my $port = 5900 + $display; note "running Xvnc for display $display (port $port) and connect via $bmwqemu::topdir/script/vnctest"; - my $xvnc_pid = open my $xvnc_pipe, "Xvnc -depth 16 -SecurityTypes None -ac :$display 2>&1 |"; - my $vnc_test_pid = open my $vnc_test_pipe, "$bmwqemu::topdir/script/vnctest --port $port --verbose 2>&1 |"; + my $xvnc_pid = open my $xvnc_pipe, '-|', "Xvnc -depth 16 -SecurityTypes None -ac :$display 2>&1"; + my $vnc_test_pid = open my $vnc_test_pipe, '-|', "$bmwqemu::topdir/script/vnctest --port $port --verbose 2>&1"; my ($sent_update_request, $has_framebuffer) = (0, 0); while (my $line = <$vnc_test_pipe>) { ++$sent_update_request if $line =~ qr/Send update request/; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/testapi.pm new/os-autoinst-5.1784551853.234e380/testapi.pm --- old/os-autoinst-5.1783672666.346fbe5/testapi.pm 2026-07-10 10:37:46.000000000 +0200 +++ new/os-autoinst-5.1784551853.234e380/testapi.pm 2026-07-20 14:50:53.000000000 +0200 @@ -71,6 +71,7 @@ save_tmp_file get_test_data ); +## use critic our @EXPORT_OK = qw(is_serial_terminal); our %cmd; ## no critic (Variables::ProhibitPackageVars) @@ -422,10 +423,10 @@ =cut sub assert_screen { # no:style:signatures - my ($mustmatch) = shift; + my ($mustmatch, @params) = @_; my $timeout; - $timeout = shift if (@_ % 2); - my %args = (timeout => $timeout // $bmwqemu::default_timeout, @_); + $timeout = shift @params if (@params % 2); + my %args = (timeout => $timeout // $bmwqemu::default_timeout, @params); bmwqemu::log_call(mustmatch => $mustmatch, %args); return _check_or_assert($mustmatch, 0, %args); } @@ -450,10 +451,10 @@ =cut sub check_screen { # no:style:signatures - my ($mustmatch) = shift; + my ($mustmatch, @params) = @_; my $timeout; - $timeout = shift if (@_ % 2); - my %args = (timeout => $timeout // 0, @_); + $timeout = shift @params if (@params % 2); + my %args = (timeout => $timeout // 0, @params); bmwqemu::log_call(mustmatch => $mustmatch, %args); return _check_or_assert($mustmatch, 1, %args); } @@ -663,9 +664,10 @@ =cut sub wait_still_screen { # no:style:signatures - my $stilltime = looks_like_number($_[0]) ? shift : 7; - my $timeout = (@_ % 2) ? shift : $bmwqemu::default_timeout; - my %args = (stilltime => $stilltime, timeout => $timeout, @_); + my @params = @_; + my $stilltime = looks_like_number($params[0]) ? shift @params : 7; + my $timeout = (@params % 2) ? shift @params : $bmwqemu::default_timeout; + my %args = (stilltime => $stilltime, timeout => $timeout, @params); $args{similarity_level} //= 47; bmwqemu::log_call(%args); $timeout = $args{timeout} = bmwqemu::scale_timeout($args{timeout}); @@ -776,8 +778,8 @@ =cut sub get_var_array ($var, $default = undef) { - my @vars = split /,|;/, $bmwqemu::vars{$var} || ''; - my @default = split /,|;/, $default || ''; + my @vars = split /[,;]/, $bmwqemu::vars{$var} || ''; + my @default = split /[,;]/, $default || ''; return \@default if !@vars; return \@vars; } @@ -944,7 +946,7 @@ =cut sub assert_script_run { # no:style:signatures - my $cmd = shift; + my ($cmd, @params) = @_; my %args = compat_args( { # assert_script_run originally had the implicit default timeout of @@ -954,7 +956,7 @@ fail_message => '', quiet => testapi::get_var('_QUIET_SCRIPT_CALLS'), max_interval => DEFAULT_MAX_INTERVAL - }, ['timeout', 'fail_message'], @_); + }, ['timeout', 'fail_message'], @params); bmwqemu::log_call(cmd => $cmd, %args); my $ret = $distri->script_run($cmd, timeout => $args{timeout}, quiet => $args{quiet}, max_interval => $args{max_interval}); @@ -994,14 +996,14 @@ =cut sub script_run { # no:style:signatures - my $cmd = shift; + my ($cmd, @params) = @_; my %args = compat_args( { timeout => $bmwqemu::default_timeout, output => '', quiet => testapi::get_var('_QUIET_SCRIPT_CALLS'), max_interval => DEFAULT_MAX_INTERVAL - }, ['timeout'], @_); + }, ['timeout'], @params); bmwqemu::log_call(cmd => $cmd, %args); my $ret = $distri->script_run($cmd, %args); @@ -1109,14 +1111,14 @@ =cut sub script_output { # no:style:signatures - my $script = shift; + my ($script, @params) = @_; my %args = testapi::compat_args( { timeout => undef, proceed_on_failure => undef, # fail on error by default quiet => testapi::get_var('_QUIET_SCRIPT_CALLS'), type_command => undef, - }, ['timeout'], @_); + }, ['timeout'], @params); return $distri->script_output($script, %args); } @@ -1400,11 +1402,9 @@ =cut sub type_string { # no:style:signatures - - # special argument handling for backward compat - my $string = shift; + my ($string, @params) = @_; # backward compat - my %args = (@_ == 1) ? (max_interval => $_[0]) : @_; + my %args = (@params == 1) ? (max_interval => $params[0]) : @params; $string .= "\n" if $args{lf}; if (is_serial_terminal) { @@ -1475,7 +1475,8 @@ =cut sub enter_cmd { # no:style:signatures - type_string shift, lf => 1, @_; + my ($cmd, @params) = @_; + type_string $cmd, lf => 1, @params; } =head1 mouse support diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-autoinst-5.1783672666.346fbe5/tools/check-line-coverage new/os-autoinst-5.1784551853.234e380/tools/check-line-coverage --- old/os-autoinst-5.1783672666.346fbe5/tools/check-line-coverage 1970-01-01 01:00:00.000000000 +0100 +++ new/os-autoinst-5.1784551853.234e380/tools/check-line-coverage 2026-07-21 23:09:44.732954113 +0200 @@ -0,0 +1 @@ +symbolic link to ../external/os-autoinst-common/tools/check-line-coverage ++++++ os-autoinst.obsinfo ++++++ --- /var/tmp/diff_new_pack.DVZKeE/_old 2026-07-21 23:09:45.060965327 +0200 +++ /var/tmp/diff_new_pack.DVZKeE/_new 2026-07-21 23:09:45.064965464 +0200 @@ -1,5 +1,5 @@ name: os-autoinst -version: 5.1783672666.346fbe5 -mtime: 1783672666 -commit: 346fbe526713586654b58f57623daf98bf546088 +version: 5.1784551853.234e380 +mtime: 1784551853 +commit: 234e3804e44685e831e3479f8b2564fd8e30f4f4
