Merge branch 'master' into gsoc_breadboard
[catagits/Catalyst-Runtime.git] / t / aggregate / unit_core_component_loading.t
CommitLineData
5e3121a8 1# way too many tests to count
2use Test::More;
0756fe3b 3
4use strict;
5use warnings;
6
7use File::Spec;
8use File::Path;
9
10my $libdir = 'test_trash';
06400669 11local @INC = @INC;
0756fe3b 12unshift(@INC, $libdir);
13
14my $appclass = 'TestComponents';
15my @components = (
16 { type => 'Controller', prefix => 'C', name => 'Bar' },
17 { type => 'Controller', prefix => 'C', name => 'Foo::Bar' },
18 { type => 'Controller', prefix => 'C', name => 'Foo::Foo::Bar' },
19 { type => 'Controller', prefix => 'C', name => 'Foo::Foo::Foo::Bar' },
20 { type => 'Controller', prefix => 'Controller', name => 'Bar::Bar::Bar::Foo' },
21 { type => 'Controller', prefix => 'Controller', name => 'Bar::Bar::Foo' },
22 { type => 'Controller', prefix => 'Controller', name => 'Bar::Foo' },
23 { type => 'Controller', prefix => 'Controller', name => 'Foo' },
24 { type => 'Model', prefix => 'M', name => 'Bar' },
25 { type => 'Model', prefix => 'M', name => 'Foo::Bar' },
26 { type => 'Model', prefix => 'M', name => 'Foo::Foo::Bar' },
27 { type => 'Model', prefix => 'M', name => 'Foo::Foo::Foo::Bar' },
28 { type => 'Model', prefix => 'Model', name => 'Bar::Bar::Bar::Foo' },
29 { type => 'Model', prefix => 'Model', name => 'Bar::Bar::Foo' },
30 { type => 'Model', prefix => 'Model', name => 'Bar::Foo' },
31 { type => 'Model', prefix => 'Model', name => 'Foo' },
32 { type => 'View', prefix => 'V', name => 'Bar' },
33 { type => 'View', prefix => 'V', name => 'Foo::Bar' },
34 { type => 'View', prefix => 'V', name => 'Foo::Foo::Bar' },
35 { type => 'View', prefix => 'V', name => 'Foo::Foo::Foo::Bar' },
36 { type => 'View', prefix => 'View', name => 'Bar::Bar::Bar::Foo' },
37 { type => 'View', prefix => 'View', name => 'Bar::Bar::Foo' },
38 { type => 'View', prefix => 'View', name => 'Bar::Foo' },
39 { type => 'View', prefix => 'View', name => 'Foo' },
40);
41
5d50f369 42sub write_component_file {
b94b200c 43 my ($dir_list, $module_name, $content) = @_;
44
45 my $dir = File::Spec->catdir(@$dir_list);
46 my $file = File::Spec->catfile($dir, $module_name . '.pm');
47
48 mkpath(join(q{/}, @$dir_list) );
49 open(my $fh, '>', $file) or die "Could not open file $file for writing: $!";
50 print $fh $content;
51 close $fh;
52}
53
0756fe3b 54sub make_component_file {
06400669 55 my ($libdir, $appclass, $type, $prefix, $name) = @_;
0756fe3b 56
57 my $compbase = "Catalyst::${type}";
58 my $fullname = "${appclass}::${prefix}::${name}";
59 my @namedirs = split(/::/, $name);
60 my $name_final = pop(@namedirs);
61 my @dir_list = ($libdir, $appclass, $prefix, @namedirs);
0756fe3b 62
b94b200c 63 write_component_file(\@dir_list, $name_final, <<EOF);
0756fe3b 64package $fullname;
f27f2c6d 65use MRO::Compat;
0756fe3b 66use base '$compbase';
67sub COMPONENT {
dbb2d5cd 68 my \$self = shift->next::method(\@_);
0756fe3b 69 no strict 'refs';
70 *{\__PACKAGE__ . "::whoami"} = sub { return \__PACKAGE__; };
71 \$self;
72}
731;
74
75EOF
0756fe3b 76}
77
78foreach my $component (@components) {
06400669 79 make_component_file(
80 $libdir,
81 $appclass,
82 $component->{type},
83 $component->{prefix},
84 $component->{name},
85 );
0756fe3b 86}
87
f27f2c6d 88my $shut_up_deprecated_warnings = q{
0f52a840 89 __PACKAGE__->log(Catalyst::Log->new('fatal'));
f27f2c6d 90};
91
92eval "package $appclass; use Catalyst; $shut_up_deprecated_warnings __PACKAGE__->setup";
0756fe3b 93
89b6b254 94is_deeply(
95 [ sort $appclass->locate_components ],
96 [ map { $appclass . '::' . $_->{prefix} . '::' . $_->{name} } @components ], 'locate_components finds the components correctly'
97);
98
0756fe3b 99can_ok( $appclass, 'components');
100
101my $complist = $appclass->components;
102
96efcd28 103is(scalar keys %$complist, 24, "Correct number of components loaded");
0756fe3b 104
105foreach (keys %$complist) {
106
107 # Skip the component which happens to be the app itself
108 next if $_ eq $appclass;
109
110 my $instance = $appclass->component($_);
111 isa_ok($instance, $_);
112 can_ok($instance, 'whoami');
113 is($instance->whoami, $_);
114
115 if($_ =~ /^${appclass}::(?:V|View)::(.*)/) {
116 my $moniker = $1;
117 isa_ok($instance, 'Catalyst::View');
118 can_ok($appclass->view($moniker), 'whoami');
119 is($appclass->view($moniker)->whoami, $_);
120 }
121 elsif($_ =~ /^${appclass}::(?:M|Model)::(.*)/) {
122 my $moniker = $1;
123 isa_ok($instance, 'Catalyst::Model');
124 can_ok($appclass->model($moniker), 'whoami');
125 is($appclass->model($moniker)->whoami, $_);
126 }
127 elsif($_ =~ /^${appclass}::(?:C|Controller)::(.*)/) {
128 my $moniker = $1;
129 isa_ok($instance, 'Catalyst::Controller');
130 can_ok($appclass->controller($moniker), 'whoami');
131 is($appclass->controller($moniker)->whoami, $_);
132 }
133 else {
134 die "Something is wrong with this test, this should"
135 . " have been unreachable";
136 }
137}
138
139rmtree($libdir);
18de900e 140
141# test extra component loading options
142
143$appclass = 'ExtraOptions';
144push @components, { type => 'View', prefix => 'Extra', name => 'Foo' };
145
146foreach my $component (@components) {
06400669 147 make_component_file(
148 $libdir,
149 $appclass,
150 $component->{type},
151 $component->{prefix},
152 $component->{name},
153 );
18de900e 154}
155
96efcd28 156SKIP: {
157 # FIXME - any backcompat planned?
158 skip "search_extra has been removed", 5;
159 eval qq(
160 package $appclass;
161 use Catalyst;
162 $shut_up_deprecated_warnings
163 __PACKAGE__->config->{ setup_components } = {
89b6b254 164 search_extra => [ '::Extra' ],
165 except => [ "${appclass}::Controller::Foo" ]
166 };
96efcd28 167 __PACKAGE__->setup;
89b6b254 168 );
89b6b254 169
96efcd28 170 {
171 my $config = {
172 search_extra => [ '::Extra' ],
173 except => [ "${appclass}::Controller::Foo" ]
174 };
175 my @components_located = $appclass->locate_components($config);
176 my @components_expected;
177 for (@components) {
178 my $name = $appclass . '::' . $_->{prefix} . '::' . $_->{name};
179 push @components_expected, $name if $name ne "${appclass}::Controller::Foo";
180 }
181 is_deeply(
182 [ sort @components_located ],
183 [ sort @components_expected ],
184 'locate_components finds the components correctly'
185 );
186 }
18de900e 187
96efcd28 188 can_ok( $appclass, 'components');
18de900e 189
96efcd28 190 $complist = $appclass->components;
18de900e 191
96efcd28 192 is(scalar keys %$complist, 24+1, "Correct number of components loaded");
18de900e 193
96efcd28 194 ok( !exists $complist->{ "${appclass}::Controller::Foo" }, 'Controller::Foo was skipped' );
195 ok( exists $complist->{ "${appclass}::Extra::Foo" }, 'Extra::Foo was loaded' );
196
197 rmtree($libdir);
198}
b94b200c 199
200$appclass = "ComponentOnce";
201
202write_component_file([$libdir, $appclass, 'Model'], 'TopLevel', <<EOF);
203package ${appclass}::Model::TopLevel;
204use base 'Catalyst::Model';
205sub COMPONENT {
5d50f369 206
dbb2d5cd 207 my \$self = shift->next::method(\@_);
b94b200c 208 no strict 'refs';
209 *{\__PACKAGE__ . "::whoami"} = sub { return \__PACKAGE__; };
e7e4c469 210 *${appclass}::Model::TopLevel::GENERATED::ACCEPT_CONTEXT = sub {
211 return bless {}, 'FooBarBazQuux';
212 };
b94b200c 213 \$self;
214}
215
216package ${appclass}::Model::TopLevel::Nested;
217
218sub COMPONENT { die "COMPONENT called in the wrong order!"; }
219
2201;
221
222EOF
223
224write_component_file([$libdir, $appclass, 'Model', 'TopLevel'], 'Nested', <<EOF);
225package ${appclass}::Model::TopLevel::Nested;
226use base 'Catalyst::Model';
227
ac424253 228my \$called=0;
b94b200c 229no warnings 'redefine';
ac424253 230sub COMPONENT { \$called++;return shift->next::method(\@_); }
231sub called { return \$called };
b94b200c 2321;
233
234EOF
235
236eval "package $appclass; use Catalyst; __PACKAGE__->setup";
237
238is($@, '', "Didn't load component twice");
ac424253 239is($appclass->model('TopLevel::Nested')->called,1, 'COMPONENT called once');
b94b200c 240
a987f40a 241# FIXME we need a much better way of components being able to generate
242# sub-components of themselves (e.g. bring back expand_component_modules?)
243# as otherwise we _have_ to construct / call the COMPONENT method
244# explicitly to get all the sub-components built for Devel::InnerPackage
245# to find them. See FIXME in Catalyst::IOC::Container
5a53ef3d 246ok($appclass->model('TopLevel::GENERATED'), 'Have generated model');
247is(ref($appclass->model('TopLevel::GENERATED')), 'FooBarBazQuux',
e7e4c469 248 'ACCEPT_CONTEXT in generated inner package fired as expected');
249
e1dd56e6 250$appclass = "InnerComponent";
251
252{
253 package InnerComponent::Controller::Test;
254 use base 'Catalyst::Controller';
255}
256
257$INC{'InnerComponent/Controller/Test.pm'} = 1;
258
259eval "package $appclass; use Catalyst; __PACKAGE__->setup";
260
261isa_ok($appclass->controller('Test'), 'Catalyst::Controller');
262
b94b200c 263rmtree($libdir);
5e3121a8 264
265done_testing;