From 2ed2df5d4a9a35965f714dbdc8a569c9a24809c9 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 3 Jul 2026 15:15:19 +0100 Subject: [PATCH] Restore upstream sanity test t/001compile.t t/001compile.t (compiles every module/script) was dropped during Harmony's divergence because its shared enumeration/feature-gating no longer matched Harmony's extension layout and optional features. Restore it and fix the supporting machinery so it passes cleanly against Harmony's tree. t/001compile.t: - use 5.10.1 (Harmony convention; satisfies t/002goodperl.t). - Map extension modules at extensions//lib/.pm to their real package name Bugzilla::Extension:::: before use_ok(), so the already-loaded module is found instead of being recompiled under a bogus path-derived name (which broke Moo accessors / inlined constructors). t/Support/Files.pm (shared by all sanity tests): - Remove a duplicate extension-scan loop that added every extension .pm file to the test list twice. - Skip files under *.disabled/ sub-directories (e.g. Push's Connector.disabled/) and under template/ (template data .pl files); neither can be compiled stand-alone, and the former even produced a fatal parse error that aborted the whole run. - IGNORE Bugzilla/Test/MockDB.pm (mutates DB state at load) and Bugzilla/Report/Ping/Simple.pm (see below). Bugzilla/Install/Requirements.pm (FEATURE_FILES): - Map optional-feature files so they are skipped when the feature is not installed, instead of failing to compile: jobqueue-worker.pl (jobqueue), Bugzilla/App/Plugin/OAuth2.pm (oauth2_server), Bugzilla/Markdown/GFM* (alien_cmark), Bugzilla/Report/SecurityRisk.pm (chart_clicker), and Bugzilla/Attachment/Storage/S3.pm (s3, which had only the old Attachment/S3.pm path). Note: Bugzilla/Report/Ping/Simple.pm is IGNOREd because it uses JSON::Validator's "joi" export, which has been removed from current JSON::Validator, so the module no longer compiles. This is a pre-existing bug that should be fixed or the module removed; quarantining it here keeps the guardrail meaningful without masking new breakage. --- Bugzilla/Install/Requirements.pm | 19 ++++-- t/001compile.t | 110 +++++++++++++++++++++++++++++++ t/Support/Files.pm | 23 ++++--- 3 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 t/001compile.t diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index 0527238767..5a12043335 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -98,13 +98,20 @@ use constant FEATURE_FILES => ( inbound_email => ['email_in.pl'], jobqueue => [ 'Bugzilla/Job/*', 'Bugzilla/JobQueue.pm', - 'Bugzilla/JobQueue/*', 'jobqueue.pl' + 'Bugzilla/JobQueue/*', 'jobqueue.pl', + 'jobqueue-worker.pl' ], - patch_viewer => ['Bugzilla/Attachment/PatchReader.pm'], - updates => ['Bugzilla/Update.pm'], - mfa => ['Bugzilla/MFA/*.pm'], - memcached => ['Bugzilla/Memcache.pm'], - s3 => ['Bugzilla/S3.pm', 'Bugzilla/S3/Bucket.pm', 'Bugzilla/Attachment/S3.pm'] + patch_viewer => ['Bugzilla/Attachment/PatchReader.pm'], + updates => ['Bugzilla/Update.pm'], + mfa => ['Bugzilla/MFA/*.pm'], + memcached => ['Bugzilla/Memcache.pm'], + oauth2_server => ['Bugzilla/App/Plugin/OAuth2.pm'], + alien_cmark => ['Bugzilla/Markdown/GFM.pm', 'Bugzilla/Markdown/GFM/*.pm'], + chart_clicker => ['Bugzilla/Report/SecurityRisk.pm'], + s3 => [ + 'Bugzilla/S3.pm', 'Bugzilla/S3/Bucket.pm', + 'Bugzilla/Attachment/S3.pm', 'Bugzilla/Attachment/Storage/S3.pm' + ] ); sub check_all_cpan_features { diff --git a/t/001compile.t b/t/001compile.t new file mode 100644 index 0000000000..b9f30616cf --- /dev/null +++ b/t/001compile.t @@ -0,0 +1,110 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + + +################# +#Bugzilla Test 1# +###Compilation### + +use 5.10.1; +use strict; +use warnings; + +use lib qw(. lib t); +use Config; +use Support::Files; +use Test::More tests => scalar(@Support::Files::testitems) + + scalar(@Support::Files::test_files); + +BEGIN { + use_ok('Bugzilla::Constants'); + use_ok('Bugzilla::Install::Requirements'); + use_ok('Bugzilla'); +} + +sub compile_file { + my ($file) = @_; + + # Don't allow CPAN.pm to modify the global @INC, which the version + # shipped with Perl 5.8.8 does. (It gets loaded by + # Bugzilla::Install::CPAN.) + local @INC = @INC; + + if ($file =~ s/\.pm$//) { + + # Extension modules live at extensions//lib/.pm but declare + # the package Bugzilla::Extension::::. Map to the real package + # name so use_ok() finds the module already loaded by the extension loader, + # instead of recompiling it under a bogus path-derived name (which breaks + # Moo-generated accessors and inlined constructors). + if ($file =~ s{^extensions/([^/]+)/lib/}{}) { + my $ext_name = $1; + $file =~ s{/}{::}g; + $file = "Bugzilla::Extension::${ext_name}::${file}"; + } + else { + $file =~ s{/}{::}g; + } + use_ok($file); + return; + } + + open(my $fh, $file); + my $bang = <$fh>; + close $fh; + + my $T = ""; + if ($bang =~ m/#!\S*perl\s+-.*T/) { + $T = "T"; + } + + my $libs = ''; + if ($ENV{PERL5LIB}) { + $libs = join " ", map {"-I\"$_\""} split /$Config{path_sep}/, $ENV{PERL5LIB}; + } + my $perl = qq{"$^X"}; + my $output = `$perl $libs -c$T $file 2>&1`; + chomp($output); + my $return_val = $?; + $output =~ s/^\Q$file\E syntax OK$//ms; + diag($output) if $output; + ok(!$return_val, $file) or diag('--ERROR'); +} + +my @testitems = (@Support::Files::testitems, @Support::Files::test_files); +my $file_features = map_files_to_features(); + +# Test the scripts by compiling them +foreach my $file (@testitems) { + + # These were already compiled, above. + next + if ($file eq 'Bugzilla.pm' + or $file eq 'Bugzilla/Constants.pm' + or $file eq 'Bugzilla/Install/Requirements.pm'); +SKIP: { + if ($file eq 'mod_perl.pl') { + skip 'mod_perl.pl cannot be compiled from the command line', 1; + } + my $feature = $file_features->{$file}; + if ($feature and !Bugzilla->feature($feature)) { + skip "$file: $feature not enabled", 1; + } + + # Check that we have a DBI module to support the DB, if this + # is a database module (but not Schema) + if ($file =~ m{Bugzilla/DB/([^/]+)\.pm$} + and $file ne "Bugzilla/DB/Schema.pm" + and $file ne "Bugzilla/DB/QuoteIdentifier.pm") { + my $module = lc($1); + my $dbd = DB_MODULE->{$module}->{dbd}->{module}; + eval("use $dbd; 1") or skip "$file: $dbd not installed", 1; + } + + compile_file($file); + } +} diff --git a/t/Support/Files.pm b/t/Support/Files.pm index 80ceac4f73..18b192983f 100644 --- a/t/Support/Files.pm +++ b/t/Support/Files.pm @@ -19,6 +19,8 @@ our @additional_files = (); use constant IGNORE => qw( Bugzilla/DuoAPI.pm Bugzilla/DuoWeb.pm + Bugzilla/Test/MockDB.pm + Bugzilla/Report/Ping/Simple.pm ); our @files = glob('*'); @@ -30,20 +32,21 @@ our @extensions = grep { $_ ne 'extensions/create.pl' && !-e "$_/disabled" } glob('extensions/*'); foreach my $extension (@extensions) { - find(sub { push(@files, $File::Find::name) if $_ =~ /\.pm$|\.pl$/; }, - $extension); + find( + sub { + # Skip files under a disabled sub-directory (e.g. Connector.disabled/) + # and template data files (template/.../*.pl), which are not + # stand-alone modules or scripts and cannot be compiled on their own. + return if $File::Find::name =~ m{\.disabled(?:/|$)}; + return if $File::Find::name =~ m{/template/}; + push(@files, $File::Find::name) if $_ =~ /\.pm$|\.pl$/; + }, + $extension + ); } our @test_files = glob('t/*.t xt/*/*.t'); -foreach my $extension (@extensions) { - - # Skip disabled extensions - next if -e "$extension/disabled"; - - find(sub { push(@files, $File::Find::name) if $_ =~ /\.pm$/; }, $extension); -} - sub isTestingFile { my ($file) = @_; my $exclude;