X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=t%2F53lean_startup.t;h=b6c8be9d4d9eae0f92448a0f94122adaf6cb00a8;hb=cf9ba393c6309a66fe31b2decb7574fdf068a759;hp=81f9bca05d4c1a7bc2dcacaadaa57e5f8b938905;hpb=f873b733054ccaac282fa056adfe41d5fd987840;p=dbsrgits%2FDBIx-Class.git diff --git a/t/53lean_startup.t b/t/53lean_startup.t index 81f9bca..b6c8be9 100644 --- a/t/53lean_startup.t +++ b/t/53lean_startup.t @@ -1,78 +1,48 @@ # Use a require override instead of @INC munging (less common) # Do the override as early as possible so that CORE::require doesn't get compiled away -# We will replace $req_override in a bit -my $test_hook; +my ($initial_inc_contents, $expected_dbic_deps, $require_sites); BEGIN { - $test_hook = sub {}; # noop at first - *CORE::GLOBAL::require = sub { - $test_hook->(@_); - CORE::require($_[0]); - }; -} - -use strict; -use warnings; -use Test::More; -use Data::Dumper; - -my $expected_core_modules; - -BEGIN { - $expected_core_modules = { map { $_ => 1 } qw/ - strict - warnings - - base - mro - overload - - B - locale - - namespace::clean - Try::Tiny - Sub::Name - - Scalar::Util - List::Util - Hash::Merge - Data::Compare + # these envvars *will* bring in more stuff than the baseline + delete @ENV{qw(DBICTEST_SQLT_DEPLOY DBIC_TRACE)}; - DBI - SQL::Abstract + # make sure extras do not load even when this is set + $ENV{PERL_STRICTURES_EXTRA} = 1; - Carp + unshift @INC, 't/lib'; + require DBICTest::Util::OverrideRequire; - Class::Accessor::Grouped - Class::C3::Componentised - /, $] < 5.010 ? ( 'Class::C3', 'MRO::Compat' ) : () }; # this is special-cased in DBIx/Class.pm + DBICTest::Util::OverrideRequire::override_global_require( sub { + my $res = $_[0]->(); - $test_hook = sub { - - my $req = $_[0]; + my $req = $_[1]; $req =~ s/\.pm$//; $req =~ s/\//::/g; - return if $req =~ /^DBIx::Class|^DBICTest::/; - - my $up = 1; + my $up = 0; my @caller; do { @caller = caller($up++) } while ( @caller and ( # exclude our test suite, known "module require-rs" and eval frames $caller[1] =~ /^ t [\/\\] /x or - $caller[0] =~ /^ (?: base | parent | Class::C3::Componentised | Module::Inspector) $/x + $caller[0] =~ /^ (?: base | parent | Class::C3::Componentised | Module::Inspector | Module::Runtime ) $/x or $caller[3] eq '(eval)', ) ); + push @{$require_sites->{$req}}, "$caller[1] line $caller[2]" + if @caller; + + return $res if $req =~ /^DBIx::Class|^DBICTest::/; + # exclude everything where the current namespace does not match the called function # (this works around very weird XS-induced require callstack corruption) if ( - !$expected_core_modules->{$req} + !$initial_inc_contents->{$req} + and + !$expected_dbic_deps->{$req} and @caller and @@ -80,46 +50,171 @@ BEGIN { and (caller($up))[3] =~ /\Q$caller[0]/ ) { - fail ("Unexpected require of '$req' by $caller[0] ($caller[1] line $caller[2])"); - - if ($ENV{TEST_VERBOSE}) { - my ($i, @stack) = 1; - while (my @f = caller($i++) ) { - push @stack, \@f; - } - diag Dumper(\@stack); + CORE::require('Test/More.pm'); + Test::More::fail ("Unexpected require of '$req' by $caller[0] ($caller[1] line $caller[2])"); + + if ( $ENV{TEST_VERBOSE} or ! DBICTest::RunMode->is_plain ) { + CORE::require('DBICTest/Util.pm'); + Test::More::diag( 'Require invoked' . DBICTest::Util::stacktrace() ); } } - }; + + return $res; + }); } -use lib 't/lib'; -use DBICTest; +use strict; +use warnings; +use Test::More; -# these envvars bring in more stuff -delete $ENV{$_} for qw/ - DBICTEST_SQLT_DEPLOY - DBIC_TRACE -/; +BEGIN { + plan skip_all => 'A defined PERL5OPT may inject extra deps crashing this test' + if $ENV{PERL5OPT}; + + plan skip_all => 'Dependency load patterns are radically different before perl 5.10' + if $] < 5.010; + + # add what we loaded so far + for (keys %INC) { + my $mod = $_; + $mod =~ s/\.pm$//; + $mod =~ s!\/!::!g; + $initial_inc_contents->{$mod} = 1; + } +} -my $schema = DBICTest->init_schema; -is ($schema->resultset('Artist')->next->name, 'Caterwauler McCrae'); +####### +### This is where the test starts +####### -# check if anything we were expecting didn't actually load -my $nl; -for (keys %$expected_core_modules) { - my $mod = "$_.pm"; - $mod =~ s/::/\//g; - unless ($INC{$mod}) { - my $err = sprintf "Expected DBIC core module %s never loaded - %s needs adjustment", $_, __FILE__; - if (DBICTest::RunMode->is_smoker) { - fail ($err) - } - else { - diag "\n" unless $nl++; - diag $err; - } +# checking base schema load, no storage no connection +{ + register_lazy_loadable_requires(qw( + B + constant + overload + + base + Devel::GlobalDestruction + mro + + Carp + namespace::clean + Try::Tiny + Sub::Name + strictures + Sub::Defer + Sub::Quote + + Scalar::Util + List::Util + + Class::Accessor::Grouped + Class::C3::Componentised + SQL::Abstract + + Module::Runtime + File::Spec + )); + + require DBICTest::Schema; + assert_no_missing_expected_requires(); +} + +# check schema/storage instantiation with no connect +{ + register_lazy_loadable_requires(qw( + Moo + Moo::Object + Method::Generate::Accessor + Method::Generate::Constructor + Context::Preserve + )); + + my $s = DBICTest::Schema->connect('dbi:SQLite::memory:'); + ok (! $s->storage->connected, 'no connection'); + assert_no_missing_expected_requires(); +} + +# do something (deploy, insert) +{ + register_lazy_loadable_requires(qw( + DBI + Hash::Merge + )); + + my $s = DBICTest::Schema->connect('dbi:SQLite::memory:'); + $s->storage->dbh_do(sub { + $_[1]->do('CREATE TABLE artist ( + "artistid" INTEGER PRIMARY KEY NOT NULL, + "name" varchar(100), + "rank" integer NOT NULL DEFAULT 13, + "charfield" char(10) + )'); + }); + + my $art = $s->resultset('Artist')->create({ name => \[ '?' => 'foo'], rank => 42 }); + $art->discard_changes; + $art->update({ rank => 69, name => 'foo' }); + assert_no_missing_expected_requires(); +} + +# and do full populate() as well, just in case - shouldn't add new stuff +{ + local $ENV{DBICTEST_SQLITE_REVERSE_DEFAULT_ORDER}; + { + # in general we do not want DBICTest to load before sqla, but it is + # ok to cheat here + local $INC{'SQL/Abstract.pm'}; + require DBICTest; } + my $s = DBICTest->init_schema; + is ($s->resultset('Artist')->find(1)->name, 'Caterwauler McCrae'); + assert_no_missing_expected_requires(); +} + +# make sure we never loaded any of the strictures XS bullshit +{ + ok( ! exists $INC{ Module::Runtime::module_notional_filename($_) }, "$_ load never attempted" ) + for qw(indirect multidimensional bareword::filehandles); } done_testing; + +sub register_lazy_loadable_requires { + local $Test::Builder::Level = $Test::Builder::Level + 1; + + for my $mod (@_) { + (my $modfn = "$mod.pm") =~ s!::!\/!g; + fail(join "\n", + "Module $mod already loaded by require site(s):", + (map { "\t$_" } @{$require_sites->{$mod}}), + '', + ) if $INC{$modfn} and !$initial_inc_contents->{$mod}; + + $expected_dbic_deps->{$mod}++ + } +} + +# check if anything we were expecting didn't actually load +sub assert_no_missing_expected_requires { + my $nl; + for my $mod (keys %$expected_dbic_deps) { + (my $modfn = "$mod.pm") =~ s/::/\//g; + unless ($INC{$modfn}) { + my $err = sprintf "Expected DBIC core dependency '%s' never loaded - %s needs adjustment", $mod, __FILE__; + if (DBICTest::RunMode->is_smoker or DBICTest::RunMode->is_author) { + fail ($err) + } + else { + diag "\n" unless $nl->{$mod}++; + diag $err; + } + } + } + pass(sprintf 'All modules expected at %s line %s loaded by DBIC: %s', + __FILE__, + (caller(0))[2], + join (', ', sort keys %$expected_dbic_deps ), + ) unless $nl; +}