More robust behavior of ANFANG.pm, also guard against sitecustomize.pl
[dbsrgits/DBIx-Class.git] / xt / extra / internals / optional_deps.t
1 my ($inc_before, $inc_after);
2 BEGIN {
3   $inc_before = [ keys %INC ];
4   require DBIx::Class::Optional::Dependencies;
5   $inc_after = [ keys %INC ];
6 }
7
8 use strict;
9 use warnings;
10 no warnings qw/once/;
11
12 use Test::More;
13 use Test::Exception;
14
15 # load before we break require()
16 use Scalar::Util();
17 use MRO::Compat();
18 use Carp 'confess';
19 use List::Util 'shuffle';
20
21 SKIP: {
22   skip 'Lean load pattern testing unsafe with $ENV{PERL5OPT}', 1
23     if $ENV{PERL5OPT};
24
25   skip 'Lean load pattern testing unsafe with sitecustomize.pl', 1
26     if grep { $_ =~ m| \/ sitecustomize\.pl $ |x } keys %INC;
27
28   skip 'Lean load pattern testing useless with $ENV{RELEASE_TESTING}', 1
29     if $ENV{RELEASE_TESTING};
30
31   is_deeply
32     $inc_before,
33     [],
34     'Nothing was loaded before inc-test'
35   ;
36   is_deeply
37     $inc_after,
38     [ 'DBIx/Class/Optional/Dependencies.pm' ],
39     'Nothing was loaded other than DBIx::Class::OptDeps'
40   ;
41 }
42
43 # check the project-local groups for sanity
44 lives_ok {
45   DBIx::Class::Optional::Dependencies->req_group_list
46 } "The entire optdep list is well formed";
47
48 is_deeply (
49   [ keys %{ DBIx::Class::Optional::Dependencies->req_list_for ('deploy') } ],
50   [ 'SQL::Translator' ],
51   'Correct deploy() dependency list',
52 );
53
54 # scope to break require()
55 {
56
57 # make module loading impossible, regardless of actual libpath contents
58   local @INC;
59
60 # basic test using the deploy target
61   for ('deploy', ['deploy']) {
62
63     # explicitly blow up cache
64     %DBIx::Class::Optional::Dependencies::req_unavailability_cache = ();
65
66     ok (
67       ! DBIx::Class::Optional::Dependencies->req_ok_for ($_),
68       'deploy() deps missing',
69     );
70
71     like (
72       DBIx::Class::Optional::Dependencies->modreq_missing_for ($_),
73       qr/
74         \A
75         SQL::Translator \~ [\d\.]+
76         \z
77       /x,
78       'expected modreq missing string contents',
79     );
80
81     like (
82       DBIx::Class::Optional::Dependencies->req_missing_for ($_),
83       qr/
84         \A
85         SQL::Translator \~ [\d\.]+
86         \Q (see DBIx::Class::Optional::Dependencies documentation for details)\E
87         \z
88       /x,
89       'expected missing string contents',
90     );
91
92     like (
93       DBIx::Class::Optional::Dependencies->modreq_errorlist_for ($_)->{'SQL::Translator'},
94       qr|\QCan't locate SQL/Translator.pm|,
95       'correct "unable to locate"  exception found in errorlist',
96     );
97
98     #make it so module appears loaded
99     local $INC{'SQL/Translator.pm'} = 1;
100     local $SQL::Translator::VERSION = 999;
101
102     ok (
103       ! DBIx::Class::Optional::Dependencies->req_ok_for ($_),
104       'deploy() deps missing cached properly from previous run',
105     );
106
107     # blow cache again
108     %DBIx::Class::Optional::Dependencies::req_unavailability_cache = ();
109
110     ok (
111       DBIx::Class::Optional::Dependencies->req_ok_for ($_),
112       'deploy() deps present',
113     );
114
115     is (
116       DBIx::Class::Optional::Dependencies->req_missing_for ($_),
117       '',
118       'expected null missing string',
119     );
120
121     is_deeply (
122       # use the deprecated method name
123       DBIx::Class::Optional::Dependencies->req_errorlist_for ($_),
124       undef,
125       'expected empty errorlist',
126     );
127   }
128
129 # test single-db text
130   local $ENV{DBICTEST_MYSQL_DSN};
131   is_deeply(
132     DBIx::Class::Optional::Dependencies->req_list_for('test_rdbms_mysql'),
133     undef,
134     'unknown optional dependencies list for testing MySQL without ENV var',
135   );
136   is_deeply(
137     DBIx::Class::Optional::Dependencies->modreq_list_for('test_rdbms_mysql'),
138     { 'DBD::mysql' => 0 },
139     'correct optional module dependencies list for testing MySQL without ENV var',
140   );
141
142   local $ENV{DBICTEST_MYSQL_DSN};
143   local $ENV{DBICTEST_PG_DSN};
144
145 # regular
146   is_deeply(
147     DBIx::Class::Optional::Dependencies->modreq_list_for([shuffle qw( test_rdbms_pg binary_data )]),
148     { 'DBD::Pg' => '2.009002' },
149     'optional dependencies list for testing Postgres without envvar',
150   );
151
152   is_deeply(
153     DBIx::Class::Optional::Dependencies->req_list_for([shuffle qw( test_rdbms_pg binary_data )]),
154     undef,
155     'optional dependencies list for testing Postgres without envvar',
156   );
157
158   is_deeply(
159     DBIx::Class::Optional::Dependencies->req_list_for('rdbms_pg'),
160     { 'DBD::Pg' => '0', },
161     'optional dependencies list for using Postgres matches',
162   );
163
164   is_deeply(
165     DBIx::Class::Optional::Dependencies->req_missing_for('rdbms_pg'),
166     'DBD::Pg (see DBIx::Class::Optional::Dependencies documentation for details)',
167     'optional dependencies missing list for using Postgres matches',
168   );
169
170 # test combination of different requirements on same module (pg's are relatively stable)
171   is_deeply (
172     DBIx::Class::Optional::Dependencies->req_list_for([shuffle qw( rdbms_pg test_rdbms_pg )]),
173     { 'DBD::Pg' => '0' },
174     'optional module dependencies list for testing Postgres matches without envvar',
175   );
176
177   is(
178     DBIx::Class::Optional::Dependencies->req_missing_for([shuffle qw( rdbms_pg test_rdbms_pg binary_data )]),
179     'DBD::Pg~2.009002 as well as the following group(s) of environment variables: DBICTEST_PG_DSN/..._USER/..._PASS',
180     'optional dependencies for testing Postgres without envvar'
181   );
182
183   is(
184     DBIx::Class::Optional::Dependencies->req_missing_for([shuffle qw( test_rdbms_mysql test_rdbms_pg binary_data)]),
185     'DBD::mysql DBD::Pg~2.009002 as well as the following group(s) of environment variables: DBICTEST_MYSQL_DSN/..._USER/..._PASS and DBICTEST_PG_DSN/..._USER/..._PASS',
186     'optional dependencies for testing Postgres+MySQL without envvars'
187   );
188
189   $ENV{DBICTEST_PG_DSN} = 'boo';
190   is_deeply (
191     DBIx::Class::Optional::Dependencies->modreq_list_for([shuffle qw( rdbms_pg test_rdbms_pg binary_data)]),
192     { 'DBD::Pg' => '2.009002' },
193     'optional module dependencies list for testing Postgres matches with envvar',
194   );
195
196   is(
197     DBIx::Class::Optional::Dependencies->req_missing_for([shuffle qw( rdbms_pg test_rdbms_pg binary_data )]),
198     'DBD::Pg~2.009002',
199     'optional dependencies error text for testing Postgres matches with evvar',
200   );
201
202 # ICDT augmentation
203   my %expected_icdt_base = ( DateTime => '0.55', 'DateTime::TimeZone::OlsonDB' => 0 );
204
205   my $mysql_icdt = [shuffle qw( test_rdbms_mysql ic_dt )];
206
207   is_deeply(
208     DBIx::Class::Optional::Dependencies->modreq_list_for($mysql_icdt),
209     {
210       %expected_icdt_base,
211       'DBD::mysql' => 0,
212       'DateTime::Format::MySQL' => 0,
213     },
214     'optional module dependencies list for testing ICDT MySQL without envvar',
215   );
216
217   is_deeply(
218     DBIx::Class::Optional::Dependencies->req_list_for($mysql_icdt),
219     \%expected_icdt_base,
220     'optional dependencies list for testing ICDT MySQL without envvar',
221   );
222
223   is(
224     DBIx::Class::Optional::Dependencies->req_missing_for($mysql_icdt),
225     "DateTime~0.55 DateTime::Format::MySQL DateTime::TimeZone::OlsonDB DBD::mysql as well as the following group(s) of environment variables: DBICTEST_MYSQL_DSN/..._USER/..._PASS",
226     'missing optional dependencies for testing ICDT MySQL without envvars'
227   );
228
229 # test multi-level include with a variable and mandatory part converging on same included dep
230   local $ENV{DBICTEST_MSACCESS_ODBC_DSN};
231   local $ENV{DBICTEST_MSSQL_ODBC_DSN} = 'foo';
232   my $msaccess_mssql_icdt = [ shuffle qw( test_rdbms_msaccess_odbc test_rdbms_mssql_odbc ic_dt ) ];
233   is_deeply(
234     DBIx::Class::Optional::Dependencies->req_missing_for($msaccess_mssql_icdt),
235     'Data::GUID DateTime~0.55 DateTime::Format::Strptime~1.2 DateTime::TimeZone::OlsonDB DBD::ODBC as well as the following group(s) of environment variables: DBICTEST_MSACCESS_ODBC_DSN/..._USER/..._PASS',
236     'Correct req_missing_for on multi-level converging include',
237   );
238
239   is_deeply(
240     DBIx::Class::Optional::Dependencies->modreq_missing_for($msaccess_mssql_icdt),
241     'Data::GUID DateTime~0.55 DateTime::Format::Strptime~1.2 DateTime::TimeZone::OlsonDB DBD::ODBC',
242     'Correct modreq_missing_for on multi-level converging include',
243   );
244
245   is_deeply(
246     DBIx::Class::Optional::Dependencies->req_list_for($msaccess_mssql_icdt),
247     {
248       'DBD::ODBC' => 0,
249       'DateTime::Format::Strptime' => '1.2',
250       %expected_icdt_base,
251     },
252     'Correct req_list_for on multi-level converging include',
253   );
254
255   is_deeply(
256     DBIx::Class::Optional::Dependencies->modreq_list_for($msaccess_mssql_icdt),
257     {
258       'DBD::ODBC' => 0,
259       'Data::GUID' => 0,
260       'DateTime::Format::Strptime' => '1.2',
261       %expected_icdt_base,
262     },
263     'Correct modreq_list_for on multi-level converging include',
264   );
265
266 }
267
268 # test multiple times to find autovivification bugs
269 for my $meth (qw(req_list_for modreq_list_for)) {
270   throws_ok {
271     DBIx::Class::Optional::Dependencies->$meth();
272   } qr/\Qreq_list_for() expects a requirement group name/,
273   "$meth without groupname throws exception";
274
275   throws_ok {
276     DBIx::Class::Optional::Dependencies->$meth('');
277   } qr/\Q$meth() expects a requirement group name/,
278   "$meth with empty groupname throws exception";
279
280   throws_ok {
281     DBIx::Class::Optional::Dependencies->$meth('invalid_groupname');
282   } qr/Requirement group 'invalid_groupname' is not defined/,
283   "$meth with invalid groupname throws exception";
284 }
285
286 done_testing;