More tests for setup_components, some for locate_components
[catagits/Catalyst-Runtime.git] / t / aggregate / unit_core_component_loading.t
CommitLineData
89b6b254 1# 3 initial tests, and 6 per component in the loop below
0756fe3b 2# (do not forget to update the number of components in test 3 as well)
b94b200c 3# 5 extra tests for the loading options
e1dd56e6 4# One test for components in inner packages
89b6b254 5use Test::More tests => 3 + 6 * 24 + 9 + 1;
0756fe3b 6
7use strict;
8use warnings;
9
10use File::Spec;
11use File::Path;
12
13my $libdir = 'test_trash';
06400669 14local @INC = @INC;
0756fe3b 15unshift(@INC, $libdir);
16
17my $appclass = 'TestComponents';
18my @components = (
19 { type => 'Controller', prefix => 'C', name => 'Bar' },
20 { type => 'Controller', prefix => 'C', name => 'Foo::Bar' },
21 { type => 'Controller', prefix => 'C', name => 'Foo::Foo::Bar' },
22 { type => 'Controller', prefix => 'C', name => 'Foo::Foo::Foo::Bar' },
23 { type => 'Controller', prefix => 'Controller', name => 'Bar::Bar::Bar::Foo' },
24 { type => 'Controller', prefix => 'Controller', name => 'Bar::Bar::Foo' },
25 { type => 'Controller', prefix => 'Controller', name => 'Bar::Foo' },
26 { type => 'Controller', prefix => 'Controller', name => 'Foo' },
27 { type => 'Model', prefix => 'M', name => 'Bar' },
28 { type => 'Model', prefix => 'M', name => 'Foo::Bar' },
29 { type => 'Model', prefix => 'M', name => 'Foo::Foo::Bar' },
30 { type => 'Model', prefix => 'M', name => 'Foo::Foo::Foo::Bar' },
31 { type => 'Model', prefix => 'Model', name => 'Bar::Bar::Bar::Foo' },
32 { type => 'Model', prefix => 'Model', name => 'Bar::Bar::Foo' },
33 { type => 'Model', prefix => 'Model', name => 'Bar::Foo' },
34 { type => 'Model', prefix => 'Model', name => 'Foo' },
35 { type => 'View', prefix => 'V', name => 'Bar' },
36 { type => 'View', prefix => 'V', name => 'Foo::Bar' },
37 { type => 'View', prefix => 'V', name => 'Foo::Foo::Bar' },
38 { type => 'View', prefix => 'V', name => 'Foo::Foo::Foo::Bar' },
39 { type => 'View', prefix => 'View', name => 'Bar::Bar::Bar::Foo' },
40 { type => 'View', prefix => 'View', name => 'Bar::Bar::Foo' },
41 { type => 'View', prefix => 'View', name => 'Bar::Foo' },
42 { type => 'View', prefix => 'View', name => 'Foo' },
43);
44
5d50f369 45sub write_component_file {
b94b200c 46 my ($dir_list, $module_name, $content) = @_;
47
48 my $dir = File::Spec->catdir(@$dir_list);
49 my $file = File::Spec->catfile($dir, $module_name . '.pm');
50
51 mkpath(join(q{/}, @$dir_list) );
52 open(my $fh, '>', $file) or die "Could not open file $file for writing: $!";
53 print $fh $content;
54 close $fh;
55}
56
0756fe3b 57sub make_component_file {
06400669 58 my ($libdir, $appclass, $type, $prefix, $name) = @_;
0756fe3b 59
60 my $compbase = "Catalyst::${type}";
61 my $fullname = "${appclass}::${prefix}::${name}";
62 my @namedirs = split(/::/, $name);
63 my $name_final = pop(@namedirs);
64 my @dir_list = ($libdir, $appclass, $prefix, @namedirs);
0756fe3b 65
b94b200c 66 write_component_file(\@dir_list, $name_final, <<EOF);
0756fe3b 67package $fullname;
f27f2c6d 68use MRO::Compat;
0756fe3b 69use base '$compbase';
70sub COMPONENT {
dbb2d5cd 71 my \$self = shift->next::method(\@_);
0756fe3b 72 no strict 'refs';
73 *{\__PACKAGE__ . "::whoami"} = sub { return \__PACKAGE__; };
74 \$self;
75}
761;
77
78EOF
0756fe3b 79}
80
81foreach my $component (@components) {
06400669 82 make_component_file(
83 $libdir,
84 $appclass,
85 $component->{type},
86 $component->{prefix},
87 $component->{name},
88 );
0756fe3b 89}
90
f27f2c6d 91my $shut_up_deprecated_warnings = q{
0f52a840 92 __PACKAGE__->log(Catalyst::Log->new('fatal'));
f27f2c6d 93};
94
95eval "package $appclass; use Catalyst; $shut_up_deprecated_warnings __PACKAGE__->setup";
0756fe3b 96
89b6b254 97is_deeply(
98 [ sort $appclass->locate_components ],
99 [ map { $appclass . '::' . $_->{prefix} . '::' . $_->{name} } @components ], 'locate_components finds the components correctly'
100);
101
0756fe3b 102can_ok( $appclass, 'components');
103
104my $complist = $appclass->components;
105
106# the +1 below is for the app class itself
107is(scalar keys %$complist, 24+1, "Correct number of components loaded");
108
109foreach (keys %$complist) {
110
111 # Skip the component which happens to be the app itself
112 next if $_ eq $appclass;
113
114 my $instance = $appclass->component($_);
115 isa_ok($instance, $_);
116 can_ok($instance, 'whoami');
117 is($instance->whoami, $_);
118
119 if($_ =~ /^${appclass}::(?:V|View)::(.*)/) {
120 my $moniker = $1;
121 isa_ok($instance, 'Catalyst::View');
122 can_ok($appclass->view($moniker), 'whoami');
123 is($appclass->view($moniker)->whoami, $_);
124 }
125 elsif($_ =~ /^${appclass}::(?:M|Model)::(.*)/) {
126 my $moniker = $1;
127 isa_ok($instance, 'Catalyst::Model');
128 can_ok($appclass->model($moniker), 'whoami');
129 is($appclass->model($moniker)->whoami, $_);
130 }
131 elsif($_ =~ /^${appclass}::(?:C|Controller)::(.*)/) {
132 my $moniker = $1;
133 isa_ok($instance, 'Catalyst::Controller');
134 can_ok($appclass->controller($moniker), 'whoami');
135 is($appclass->controller($moniker)->whoami, $_);
136 }
137 else {
138 die "Something is wrong with this test, this should"
139 . " have been unreachable";
140 }
141}
142
143rmtree($libdir);
18de900e 144
145# test extra component loading options
146
147$appclass = 'ExtraOptions';
148push @components, { type => 'View', prefix => 'Extra', name => 'Foo' };
149
150foreach my $component (@components) {
06400669 151 make_component_file(
152 $libdir,
153 $appclass,
154 $component->{type},
155 $component->{prefix},
156 $component->{name},
157 );
18de900e 158}
159
160eval qq(
161package $appclass;
162use Catalyst;
f27f2c6d 163$shut_up_deprecated_warnings
18de900e 164__PACKAGE__->config->{ setup_components } = {
165 search_extra => [ '::Extra' ],
166 except => [ "${appclass}::Controller::Foo" ]
167};
168__PACKAGE__->setup;
169);
170
89b6b254 171{
172 my $config = {
173 search_extra => [ '::Extra' ],
174 except => [ "${appclass}::Controller::Foo" ]
175 };
176 my @components_located = $appclass->locate_components($config);
177 my @components_expected;
178 for (@components) {
179 my $name = $appclass . '::' . $_->{prefix} . '::' . $_->{name};
180 push @components_expected, $name if $name ne "${appclass}::Controller::Foo";
181 }
182 is_deeply(
183 [ sort @components_located ],
184 [ sort @components_expected ],
185 'locate_components finds the components correctly'
186 );
187}
188
18de900e 189can_ok( $appclass, 'components');
190
191$complist = $appclass->components;
192
193is(scalar keys %$complist, 24+1, "Correct number of components loaded");
194
2d486591 195ok( !exists $complist->{ "${appclass}::Controller::Foo" }, 'Controller::Foo was skipped' );
196ok( exists $complist->{ "${appclass}::Extra::Foo" }, 'Extra::Foo was loaded' );
18de900e 197
b94b200c 198rmtree($libdir);
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
e7e4c469 241ok($appclass->model('TopLevel::Generated'), 'Have generated model');
242is(ref($appclass->model('TopLevel::Generated')), 'FooBarBazQuux',
243 'ACCEPT_CONTEXT in generated inner package fired as expected');
244
e1dd56e6 245$appclass = "InnerComponent";
246
247{
248 package InnerComponent::Controller::Test;
249 use base 'Catalyst::Controller';
250}
251
252$INC{'InnerComponent/Controller/Test.pm'} = 1;
253
254eval "package $appclass; use Catalyst; __PACKAGE__->setup";
255
256isa_ok($appclass->controller('Test'), 'Catalyst::Controller');
257
b94b200c 258rmtree($libdir);