include test for failure mode
[catagits/Catalyst-Runtime.git] / t / unit_core_component_loading.t
1 # 2 initial tests, and 6 per component in the loop below
2 # (do not forget to update the number of components in test 3 as well)
3 # 5 extra tests for the loading options
4 use Test::More tests => 2 + 6 * 24 + 5;
5
6 use strict;
7 use warnings;
8
9 use File::Spec;
10 use File::Path;
11
12 use Test::MockObject;
13
14 my $libdir = 'test_trash';
15 unshift(@INC, $libdir);
16
17 my $appclass = 'TestComponents';
18 my @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
45 sub write_component_file { 
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
57 sub make_component_file {
58     my ($type, $prefix, $name) = @_;
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);
65
66     write_component_file(\@dir_list, $name_final, <<EOF);
67 package $fullname;
68 use MRO::Compat;
69 use base '$compbase';
70 sub COMPONENT {
71     my \$self = shift->next::method(\@_);
72     no strict 'refs';
73     *{\__PACKAGE__ . "::whoami"} = sub { return \__PACKAGE__; };
74     \$self;
75 }
76 1;
77
78 EOF
79 }
80
81 foreach my $component (@components) {
82     make_component_file($component->{type},
83                         $component->{prefix},
84                         $component->{name});
85 }
86
87 my $shut_up_deprecated_warnings = q{
88     use Test::MockObject;
89     my $old_logger = __PACKAGE__->log;
90     my $logger = Test::MockObject->new;
91     $logger->mock('warn', sub { 
92         my $self = shift;
93         return if $_[0] =~ /deprecated/;
94         $old_logger->warn(@_);
95     });
96     __PACKAGE__->log($logger);
97 };
98
99 eval "package $appclass; use Catalyst; $shut_up_deprecated_warnings __PACKAGE__->setup";
100
101 can_ok( $appclass, 'components');
102
103 my $complist = $appclass->components;
104
105 # the +1 below is for the app class itself
106 is(scalar keys %$complist, 24+1, "Correct number of components loaded");
107
108 foreach (keys %$complist) {
109
110     # Skip the component which happens to be the app itself
111     next if $_ eq $appclass;
112
113     my $instance = $appclass->component($_);
114     isa_ok($instance, $_);
115     can_ok($instance, 'whoami');
116     is($instance->whoami, $_);
117
118     if($_ =~ /^${appclass}::(?:V|View)::(.*)/) {
119         my $moniker = $1;
120         isa_ok($instance, 'Catalyst::View');
121         can_ok($appclass->view($moniker), 'whoami');
122         is($appclass->view($moniker)->whoami, $_);
123     }
124     elsif($_ =~ /^${appclass}::(?:M|Model)::(.*)/) {
125         my $moniker = $1;
126         isa_ok($instance, 'Catalyst::Model');
127         can_ok($appclass->model($moniker), 'whoami');
128         is($appclass->model($moniker)->whoami, $_);
129     }
130     elsif($_ =~ /^${appclass}::(?:C|Controller)::(.*)/) {
131         my $moniker = $1;
132         isa_ok($instance, 'Catalyst::Controller');
133         can_ok($appclass->controller($moniker), 'whoami');
134         is($appclass->controller($moniker)->whoami, $_);
135     }
136     else {
137         die "Something is wrong with this test, this should"
138             . " have been unreachable";
139     }
140 }
141
142 rmtree($libdir);
143
144 # test extra component loading options
145
146 $appclass = 'ExtraOptions';
147 push @components, { type => 'View', prefix => 'Extra', name => 'Foo' };
148
149 foreach my $component (@components) {
150     make_component_file($component->{type},
151                         $component->{prefix},
152                         $component->{name});
153 }
154
155 eval qq(
156 package $appclass;
157 use Catalyst;
158 $shut_up_deprecated_warnings
159 __PACKAGE__->config->{ setup_components } = {
160     search_extra => [ '::Extra' ],
161     except       => [ "${appclass}::Controller::Foo" ]
162 };
163 __PACKAGE__->setup;
164 );
165
166 can_ok( $appclass, 'components');
167
168 $complist = $appclass->components;
169
170 is(scalar keys %$complist, 24+1, "Correct number of components loaded");
171
172 ok( !exists $complist->{ "${appclass}::Controller::Foo" }, 'Controller::Foo was skipped' );
173 ok( exists $complist->{ "${appclass}::Extra::Foo" }, 'Extra::Foo was loaded' );
174
175 rmtree($libdir);
176
177 $appclass = "ComponentOnce";
178
179 write_component_file([$libdir, $appclass, 'Model'], 'TopLevel', <<EOF);
180 package ${appclass}::Model::TopLevel;
181 use base 'Catalyst::Model';
182 sub COMPONENT {
183  
184     my \$self = shift->next::method(\@_);
185     no strict 'refs';
186     *{\__PACKAGE__ . "::whoami"} = sub { return \__PACKAGE__; };
187     \$self;
188 }
189
190 package ${appclass}::Model::TopLevel::Nested;
191
192 sub COMPONENT { die "COMPONENT called in the wrong order!"; }
193
194 1;
195
196 EOF
197
198 write_component_file([$libdir, $appclass, 'Model', 'TopLevel'], 'Nested', <<EOF);
199 package ${appclass}::Model::TopLevel::Nested;
200 use base 'Catalyst::Model';
201
202 no warnings 'redefine';
203 sub COMPONENT { return shift->next::method(\@_); }
204 1;
205
206 EOF
207
208 eval "package $appclass; use Catalyst; __PACKAGE__->setup";
209
210 is($@, '', "Didn't load component twice");
211
212 rmtree($libdir);