From f7ca12bc6666c6cbc4068b24495bc47d761bacf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Sat, 25 Apr 2026 21:35:41 +0200 Subject: [PATCH 1/5] resurrect(phase-4): remove skip guards for output-alignment tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit projects.t, includes.t, and signals.t had plan skip_all guards that overstated the required work — their underlying commands and flags are already implemented. Remove the guards so these tests run: - projects.t: yath projects with --ext and multi-project sub-dirs; assertions use qr{PASSED/FAILED.*project.*file} - includes.t: -I/--unsafe-inc/T2_HARNESS_INCLUDES flag wiring; chdir into the test dir and validates include ordering - signals.t: ABRT/IOT signal handling under AUTHOR_TESTING; loops 10 times to catch flapping signal delivery smoke.t, stamps.t, and concurrency.t remain skipped but with updated messages that identify the real blocker: all three use $log->poll() (the old flat-JSONL polling API) but Tester.pm's log=>1 now returns a workdir directory after the Phase-1 fix. These tests need a rewrite to iterate events via App::Yath2::LogArchive before they can run. Co-Authored-By: Claude Sonnet 4.6 --- t/Yath/integration/concurrency.t | 2 +- t/Yath/integration/includes.t | 4 ---- t/Yath/integration/projects.t | 4 ---- t/Yath/integration/signals.t | 4 ---- t/Yath/integration/smoke.t | 2 +- t/Yath/integration/stamps.t | 2 +- 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/t/Yath/integration/concurrency.t b/t/Yath/integration/concurrency.t index 28a494e9d..327d02f9a 100644 --- a/t/Yath/integration/concurrency.t +++ b/t/Yath/integration/concurrency.t @@ -1,6 +1,6 @@ # HARNESS-CONFLICTS YATH use Test2::V0; -plan skip_all => "TODO: job concurrency scheduling not validated yet against current scheduler"; +plan skip_all => "TODO: test uses \$log->poll() (JSONL API) but log=>1 now returns a workdir; needs rewrite to use App::Yath2::LogArchive for job event ordering assertions"; __END__ use Test2::V0; diff --git a/t/Yath/integration/includes.t b/t/Yath/integration/includes.t index 9cdba6eb9..d1d17ae8f 100644 --- a/t/Yath/integration/includes.t +++ b/t/Yath/integration/includes.t @@ -1,8 +1,4 @@ # HARNESS-CONFLICTS YATH -use Test2::V0; -plan skip_all => "TODO: -I/--unsafe-inc include handling not aligned with current command::test"; -__END__ - use Test2::V0; use IPC::Cmd qw/can_run/; diff --git a/t/Yath/integration/projects.t b/t/Yath/integration/projects.t index 3d233bbd4..cf1d15aca 100644 --- a/t/Yath/integration/projects.t +++ b/t/Yath/integration/projects.t @@ -1,8 +1,4 @@ # HARNESS-CONFLICTS YATH -use Test2::V0; -plan skip_all => "TODO: projects command output not aligned with current renderer"; -__END__ - use Test2::V0; use File::Temp qw/tempdir/; diff --git a/t/Yath/integration/signals.t b/t/Yath/integration/signals.t index b059c5404..c4b0c8413 100644 --- a/t/Yath/integration/signals.t +++ b/t/Yath/integration/signals.t @@ -1,8 +1,4 @@ # HARNESS-CONFLICTS YATH -use Test2::V0; -plan skip_all => "TODO: signal handling under nested yath not aligned with current Streamer (AUTHOR_TESTING bypasses old gate)"; -__END__ - use Test2::V0; use Test2::Require::AuthorTesting; diff --git a/t/Yath/integration/smoke.t b/t/Yath/integration/smoke.t index 3db10035b..be6f77d13 100644 --- a/t/Yath/integration/smoke.t +++ b/t/Yath/integration/smoke.t @@ -1,6 +1,6 @@ # HARNESS-CONFLICTS YATH use Test2::V0; -plan skip_all => "TODO: plugin loading (-p+SmokePlugin) and -D dev-libs not aligned"; +plan skip_all => "TODO: test uses \$log->poll() (JSONL API) but log=>1 now returns a workdir; needs rewrite to use App::Yath2::LogArchive for event iteration"; __END__ use Test2::V0; diff --git a/t/Yath/integration/stamps.t b/t/Yath/integration/stamps.t index 3c6164634..1031f4243 100644 --- a/t/Yath/integration/stamps.t +++ b/t/Yath/integration/stamps.t @@ -1,6 +1,6 @@ # HARNESS-CONFLICTS YATH use Test2::V0; -plan skip_all => "TODO: plugin loading and event timestamp rendering not aligned"; +plan skip_all => "TODO: test uses \$log->poll() (JSONL API) but log=>1 now returns a workdir; needs rewrite to App::Yath2::LogArchive; also --no-plugins flag not yet implemented"; __END__ use Test2::V0; From 6a7713a0c7707f8318aa7af93975605124647ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Sun, 26 Apr 2026 03:54:45 -0500 Subject: [PATCH 2/5] Fix CI failures in includes.t and projects.t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit includes.t: rename test fixture .yath.rc to .yath.v2.rc so that child yath processes select V2 instead of V1. The system App::Yath::Script defaults unversioned .yath.rc to V1, which is not installed in the 2.0 blib — causing "Could not load App::Yath::Script::V1" in all six subtests. projects.t: restore skip_all with accurate message. The projects command inherits test::run() which requires explicit file/dir args and dies with "No test files supplied" on CWD-based invocations. A proper run() override using the multi_project Finder logic is needed before this test can run. Co-Authored-By: Claude Sonnet 4.6 --- t/Yath/integration/includes/{.yath.rc => .yath.v2.rc} | 0 t/Yath/integration/projects.t | 4 ++++ 2 files changed, 4 insertions(+) rename t/Yath/integration/includes/{.yath.rc => .yath.v2.rc} (100%) diff --git a/t/Yath/integration/includes/.yath.rc b/t/Yath/integration/includes/.yath.v2.rc similarity index 100% rename from t/Yath/integration/includes/.yath.rc rename to t/Yath/integration/includes/.yath.v2.rc diff --git a/t/Yath/integration/projects.t b/t/Yath/integration/projects.t index cf1d15aca..7f227f470 100644 --- a/t/Yath/integration/projects.t +++ b/t/Yath/integration/projects.t @@ -1,4 +1,8 @@ # HARNESS-CONFLICTS YATH +use Test2::V0; +plan skip_all => "TODO: App::Yath2::Command::projects needs run() override — inherits test's guard that requires explicit args, no CWD fallback"; +__END__ + use Test2::V0; use File::Temp qw/tempdir/; From 474acb39e0e492900e0dde0d28a997d7da8c3216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Sun, 26 Apr 2026 04:32:01 -0500 Subject: [PATCH 3/5] Fix include path propagation and non-perl script handling in test command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Command::test now builds T2_HARNESS_INCLUDES correctly: app path first, then user -I paths, then lib/blib/lib/blib/arch by default (matching the old TestSettings.pm defaults). Caller-injected paths (e.g. from nested harness runs) are preserved by appending rather than replacing. RunService now detects non-perl test files (shell scripts via shebang) and routes include paths correctly: -I flags for perl scripts, PERL5LIB for shell wrappers (which re-exec perl — passing -I to bash causes a fatal flag error). Removed the hardcoded -Ilib that was the only include path for test children. Fixes includes.t: all 6 subtests now pass including not-perl.sh. Co-Authored-By: Claude Sonnet 4.6 --- lib/App/Yath2/Command/test.pm | 46 ++++++++++++++++++++++++++++++ lib/Test2/Harness2/RunService.pm | 48 ++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/lib/App/Yath2/Command/test.pm b/lib/App/Yath2/Command/test.pm index 14578da91..16078c635 100644 --- a/lib/App/Yath2/Command/test.pm +++ b/lib/App/Yath2/Command/test.pm @@ -12,11 +12,13 @@ use Object::HashBase qw{ tests->includes, already resolved), then the + # default lib/blib directories (on by default, disabled by --no-lib / + # --no-blib). Paths are absolutised relative to the current working + # directory so they remain valid regardless of the RunService's CWD. + # + # We PREPEND to any T2_HARNESS_INCLUDES value already set in the + # environment (e.g. /foo;/bar;/baz from nested_includes.t) so caller- + # injected paths survive but the tester's full @INC dump (added by + # App::Yath2::Tester for its own yath-subprocess setup) ends up after + # our ordered prefix. + { + my $cwd = Cwd::getcwd(); + my @new_inc; + + push @new_inc, App::Yath2->app_path; + + if (eval { $settings->can('check_group') && $settings->check_group('tests') }) { + my $ts = $settings->tests; + for my $path (@{$ts->includes // []}) { + push @new_inc, File::Spec->rel2abs($path, $cwd); + } + unless (defined($ts->lib) && !$ts->lib) { + push @new_inc, File::Spec->catdir($cwd, 'lib'); + } + unless (defined($ts->blib) && !$ts->blib) { + push @new_inc, File::Spec->catdir($cwd, 'blib', 'lib'); + push @new_inc, File::Spec->catdir($cwd, 'blib', 'arch'); + } + } + + my %seen; + my @all_inc; + for my $p (@new_inc) { + push @all_inc, $p unless $seen{$p}++; + } + if (my $existing = $ENV{T2_HARNESS_INCLUDES}) { + for my $p (grep { length && $_ ne '.' } split /;/, $existing) { + push @all_inc, $p unless $seen{$p}++; + } + } + $ENV{T2_HARNESS_INCLUDES} = join ';', @all_inc; + } + my $workdir = $settings->workspace->workdir; my $spawn = Test2::Harness2->spawn( diff --git a/lib/Test2/Harness2/RunService.pm b/lib/Test2/Harness2/RunService.pm index bbe1f15ab..2284547bf 100644 --- a/lib/Test2/Harness2/RunService.pm +++ b/lib/Test2/Harness2/RunService.pm @@ -189,13 +189,23 @@ sub request_handler_launch_job { return {ok => 0, error => "'test_file' must be absolute"} unless $test_file_abs =~ m{^/}; + # Parse the test file to detect non-perl scripts (shell scripts, etc.) + # via their shebang line. We need this before building the launch command + # so we can decide whether to use -I flags or PERL5LIB. + require Test2::Harness2::TestFile; + my $test_file_spec = Test2::Harness2::TestFile->new(file => $test_file_abs); + $test_file_spec->scan; + # The harness's synthetic-skip / synthetic-fail paths hand us an # explicit launch command (perl -e '...'). Default to running the - # real test file when no override is present. When the launch - # payload's env carries T2_HARNESS_INCLUDES (forwarded by - # Test2::Harness2::_launch_job), turn it into -I flags so the - # child's @INC actually picks the paths up -- the env var alone - # is not enough since exec(perl ...) starts a fresh interpreter. + # real test file when no override is present. + # + # T2_HARNESS_INCLUDES (forwarded by Test2::Harness2::_launch_job and + # populated by Command::test from the user's -I/--lib/--blib options) + # carries the correct include paths for test children. We convert these + # to -I flags for perl scripts, or to PERL5LIB for non-perl scripts + # (shell wrappers that re-exec perl). + my %extra_env; if (!defined $launch_cmd) { my @extra_inc; if (my $payload_env = $payload->{env}) { @@ -203,7 +213,29 @@ sub request_handler_launch_job { @extra_inc = grep { length && $_ ne '.' } split /;/, $inc if defined $inc && length $inc; } - $launch_cmd = [$^X, (map { "-I$_" } @extra_inc), '-Ilib', $test_file_abs]; + + if ($test_file_spec->non_perl) { + # Non-perl scripts (e.g. shell wrappers that exec perl): pass + # include paths via PERL5LIB so the re-execed perl sees them. + # Do not use -I flags — they would be forwarded by perl to the + # shebang interpreter (e.g. bash), which does not understand them. + if (@extra_inc) { + my $sep = $^O eq 'MSWin32' ? ';' : ':'; + my $new_perl5lib = join $sep, @extra_inc; + if (my $existing = $ENV{PERL5LIB}) { + $new_perl5lib .= "$sep$existing"; + } + $extra_env{PERL5LIB} = $new_perl5lib; + } + # Run the script directly; the OS kernel handles the shebang. + $launch_cmd = [$test_file_abs]; + } + else { + # Perl scripts: inject include paths as -I flags on the command + # line. This sets @INC before any module is loaded, which is + # required because exec() starts a fresh interpreter. + $launch_cmd = [$^X, (map { "-I$_" } @extra_inc), $test_file_abs]; + } } # Default the payload-level log_file only if the caller wants one; @@ -218,8 +250,6 @@ sub request_handler_launch_job { # test_file path so the resulting .json includes the relative # and absolute test-file paths. Callers that supply their own # spec are left alone. - require Test2::Harness2::TestFile; - my $test_file_spec = Test2::Harness2::TestFile->new(file => $test_file_abs); my @logger_specs = map { _maybe_inject_test_file_spec($_, $test_file_spec) } @$payload_loggers; @@ -230,7 +260,7 @@ sub request_handler_launch_job { launch => $launch_cmd, new_pgroup => 1, parent_pids => [$$], - env_vars => {T2_FORMATTER => 'Stream2', %$env}, + env_vars => {T2_FORMATTER => 'Stream2', %$env, %extra_env}, logdir => $self->{+LOGDIR}, run_id => $run_id, job_id => $job_id, From bb69e1051316a45bba9c9c11f510837459bb5a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Sun, 26 Apr 2026 04:53:09 -0500 Subject: [PATCH 4/5] fix(RunService): restore -Ilib for test children When T2_HARNESS_INCLUDES is unset (e.g., integration tests that create a harness directly without going through Command::test), test children need lib/ in their @INC to load Test2::Formatter::Stream2 and other harness internals. The include-path propagation refactor inadvertently removed the hardcoded -Ilib that was present since before Command::test started injecting T2_HARNESS_INCLUDES. Restore it as a permanent fallback for both perl and non-perl script paths. Co-Authored-By: Claude Sonnet 4.6 --- lib/Test2/Harness2/RunService.pm | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Test2/Harness2/RunService.pm b/lib/Test2/Harness2/RunService.pm index 2284547bf..f44c8d0b8 100644 --- a/lib/Test2/Harness2/RunService.pm +++ b/lib/Test2/Harness2/RunService.pm @@ -219,14 +219,14 @@ sub request_handler_launch_job { # include paths via PERL5LIB so the re-execed perl sees them. # Do not use -I flags — they would be forwarded by perl to the # shebang interpreter (e.g. bash), which does not understand them. - if (@extra_inc) { - my $sep = $^O eq 'MSWin32' ? ';' : ':'; - my $new_perl5lib = join $sep, @extra_inc; - if (my $existing = $ENV{PERL5LIB}) { - $new_perl5lib .= "$sep$existing"; - } - $extra_env{PERL5LIB} = $new_perl5lib; + # Always include 'lib' so harness internals (e.g. Stream2 + # formatter) are reachable even when T2_HARNESS_INCLUDES is unset. + my $sep = $^O eq 'MSWin32' ? ';' : ':'; + my $new_perl5lib = join $sep, @extra_inc, 'lib'; + if (my $existing = $ENV{PERL5LIB}) { + $new_perl5lib .= "$sep$existing"; } + $extra_env{PERL5LIB} = $new_perl5lib; # Run the script directly; the OS kernel handles the shebang. $launch_cmd = [$test_file_abs]; } @@ -234,7 +234,9 @@ sub request_handler_launch_job { # Perl scripts: inject include paths as -I flags on the command # line. This sets @INC before any module is loaded, which is # required because exec() starts a fresh interpreter. - $launch_cmd = [$^X, (map { "-I$_" } @extra_inc), $test_file_abs]; + # Always include -Ilib so harness internals (e.g. Stream2 + # formatter) are reachable even when T2_HARNESS_INCLUDES is unset. + $launch_cmd = [$^X, (map { "-I$_" } @extra_inc), '-Ilib', $test_file_abs]; } } From 6d652674783a1ed66e323389266ea2950092b947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Sun, 26 Apr 2026 05:03:34 -0500 Subject: [PATCH 5/5] ci: trigger CI rerun after -Ilib fix