this error message changed
[gitmo/Moose.git] / t / immutable / inline_close_over.t
CommitLineData
4d9b02de 1#!/usr/bin/env perl
2use strict;
3use warnings;
4use Test::More;
5
6use Test::Requires 'Data::Visitor';
7use Test::Requires 'PadWalker';
8use Try::Tiny;
9my $can_partialdump = try {
10 Class::MOP::load_class('Devel::PartialDump', { -version => 0.14 }); 1;
11};
12
13{
14 package Test::Visitor;
15 use Moose;
16 use Moose::Util::TypeConstraints;
17 extends 'Data::Visitor';
18
19 has closed_over => (
20 traits => ['Array'],
21 isa => 'ArrayRef',
22 default => sub { [] },
23 handles => {
24 add_closed_over => 'push',
25 closed_over => 'elements',
26 pass => 'is_empty',
27 },
28 );
29
30 before visit_code => sub {
31 my $self = shift;
32 my ($code) = @_;
33 my $closed_over = PadWalker::closed_over($code);
34 $self->visit_ref($closed_over);
35 };
36
37 after visit => sub {
38 my $self = shift;
39 my ($thing) = @_;
40
41 $self->add_closed_over($thing)
42 unless $self->_is_okay_to_close_over($thing);
43 };
44
45 sub _is_okay_to_close_over {
46 my $self = shift;
47 my ($thing) = @_;
48
49 match_on_type $thing => (
50 'RegexpRef' => sub { 1 },
51 'Object' => sub { 0 },
52 'GlobRef' => sub { 0 },
53 'FileHandle' => sub { 0 },
54 'Any' => sub { 1 },
55 );
56 }
57}
58
59sub close_over_ok {
60 local $Test::Builder::Level = $Test::Builder::Level + 1;
61 my ($package, $method) = @_;
62 my $visitor = Test::Visitor->new;
63 my $code = $package->meta->find_method_by_name($method)->body;
64 $visitor->visit($code);
65 if ($visitor->pass) {
66 pass("${package}::${method} didn't close over anything complicated");
67 }
68 else {
69 fail("${package}::${method} closed over some stuff:");
70 my @closed_over = $visitor->closed_over;
71 for my $i (1..10) {
72 last unless @closed_over;
73 my $closed_over = shift @closed_over;
74 if ($can_partialdump) {
75 $closed_over = Devel::PartialDump->new->dump($closed_over);
76 }
77 diag($closed_over);
78 }
79 diag("... and " . scalar(@closed_over) . " more")
80 if @closed_over;
81 }
82}
83
84{
85 package Foo;
86 use Moose;
87 use Moose::Util::TypeConstraints;
88
89 has foo => (
90 is => 'ro',
91 isa => 'Str',
92 );
93
94 has bar => (
95 is => 'ro',
96 isa => 'Int',
97 default => 1,
98 );
99
100 has baz => (
101 is => 'rw',
102 isa => 'ArrayRef[Num]',
103 default => sub { [ 1.2 ] },
104 trigger => sub { warn "blah" },
105 );
106
107 subtype 'Thing',
108 as 'Int',
109 where { $_ < 5 },
110 message { "must be less than 5" };
111 has quux => (
112 is => 'rw',
113 isa => 'Thing',
114 predicate => 'has_quux',
115 clearer => 'clear_quux',
116 );
117
118 __PACKAGE__->meta->make_immutable;
119}
120
121close_over_ok('Foo', $_) for qw(new foo bar baz quux has_quux clear_quux);
122
123{
124 package Foo::Sub;
125 use Moose;
126 extends 'Foo';
127
128 around foo => sub {
129 my $orig = shift;
130 my $self = shift;
131 $self->$orig(@_);
132 };
133
134 after bar => sub { };
135 before baz => sub { };
136 override quux => sub { super };
137
138 sub blah { inner }
139
140 __PACKAGE__->meta->make_immutable;
141}
142
143close_over_ok('Foo::Sub', $_) for qw(new foo bar baz quux blah);
144
145{
146 package Foo::Sub::Sub;
147 use Moose;
148 extends 'Foo::Sub';
149
150 augment blah => { inner };
151
152 __PACKAGE__->meta->make_immutable;
153}
154
155close_over_ok('Foo::Sub::Sub', $_) for qw(new blah);
156
157{
158 my %handles = (
159 Array => {
160 count => 'count',
161 elements => 'elements',
162 is_empty => 'is_empty',
163 push => 'push',
164 push_curried => [ push => 42, 84 ],
165 unshift => 'unshift',
166 unshift_curried => [ unshift => 42, 84 ],
167 pop => 'pop',
168 shift => 'shift',
169 get => 'get',
170 get_curried => [ get => 1 ],
171 set => 'set',
172 set_curried_1 => [ set => 1 ],
173 set_curried_2 => [ set => ( 1, 98 ) ],
174 accessor => 'accessor',
175 accessor_curried_1 => [ accessor => 1 ],
176 accessor_curried_2 => [ accessor => ( 1, 90 ) ],
177 clear => 'clear',
178 delete => 'delete',
179 delete_curried => [ delete => 1 ],
180 insert => 'insert',
181 insert_curried => [ insert => ( 1, 101 ) ],
182 splice => 'splice',
183 splice_curried_1 => [ splice => 1 ],
184 splice_curried_2 => [ splice => 1, 2 ],
185 splice_curried_all => [ splice => 1, 2, ( 3, 4, 5 ) ],
186 sort => 'sort',
187 sort_curried => [ sort => ( sub { $_[1] <=> $_[0] } ) ],
188 sort_in_place => 'sort_in_place',
189 sort_in_place_curried =>
190 [ sort_in_place => ( sub { $_[1] <=> $_[0] } ) ],
191 map => 'map',
192 map_curried => [ map => ( sub { $_ + 1 } ) ],
193 grep => 'grep',
194 grep_curried => [ grep => ( sub { $_ < 5 } ) ],
195 first => 'first',
196 first_curried => [ first => ( sub { $_ % 2 } ) ],
197 join => 'join',
198 join_curried => [ join => '-' ],
199 shuffle => 'shuffle',
200 uniq => 'uniq',
201 reduce => 'reduce',
202 reduce_curried => [ reduce => ( sub { $_[0] * $_[1] } ) ],
203 natatime => 'natatime',
204 natatime_curried => [ natatime => 2 ],
205 },
206 Hash => {
207 option_accessor => 'accessor',
208 quantity => [ accessor => 'quantity' ],
209 clear_options => 'clear',
210 num_options => 'count',
211 delete_option => 'delete',
212 is_defined => 'defined',
213 options_elements => 'elements',
214 has_option => 'exists',
215 get_option => 'get',
216 has_no_options => 'is_empty',
217 keys => 'keys',
218 values => 'values',
219 key_value => 'kv',
220 set_option => 'set',
221 },
222 Counter => {
223 inc_counter => 'inc',
224 inc_counter_2 => [ inc => 2 ],
225 dec_counter => 'dec',
226 dec_counter_2 => [ dec => 2 ],
227 reset_counter => 'reset',
228 set_counter => 'set',
229 set_counter_42 => [ set => 42 ],
230 },
231 Number => {
232 abs => 'abs',
233 add => 'add',
234 inc => [ add => 1 ],
235 div => 'div',
236 cut_in_half => [ div => 2 ],
237 mod => 'mod',
238 odd => [ mod => 2 ],
239 mul => 'mul',
240 set => 'set',
241 sub => 'sub',
242 dec => [ sub => 1 ],
243 },
244 Bool => {
245 illuminate => 'set',
246 darken => 'unset',
247 flip_switch => 'toggle',
248 is_dark => 'not',
249 },
250 String => {
251 inc => 'inc',
252 append => 'append',
253 append_curried => [ append => '!' ],
254 prepend => 'prepend',
255 prepend_curried => [ prepend => '-' ],
256 replace => 'replace',
257 replace_curried => [ replace => qr/(.)$/, sub { uc $1 } ],
258 chop => 'chop',
259 chomp => 'chomp',
260 clear => 'clear',
261 match => 'match',
262 match_curried => [ match => qr/\D/ ],
263 length => 'length',
264 substr => 'substr',
265 substr_curried_1 => [ substr => (1) ],
266 substr_curried_2 => [ substr => ( 1, 3 ) ],
267 substr_curried_3 => [ substr => ( 1, 3, 'ong' ) ],
268 },
269 Code => {
270 execute => 'execute',
271 execute_method => 'execute_method',
272 },
273 );
274
275 my %isa = (
276 Array => 'ArrayRef[Str]',
277 Hash => 'HashRef[Int]',
278 Counter => 'Int',
279 Number => 'Num',
280 Bool => 'Bool',
281 String => 'Str',
282 Code => 'CodeRef',
283 );
284
285 my %default = (
286 Array => [],
287 Hash => {},
288 Counter => 0,
289 Number => 0.0,
290 Bool => 1,
291 String => '',
292 Code => sub { },
293 );
294
295 for my $trait (keys %default) {
296 my $class_name = "Native::$trait";
297 my $handles = $handles{$trait};
298 my $attr_class = Moose::Util::with_traits(
299 'Moose::Meta::Attribute',
300 "Moose::Meta::Attribute::Native::Trait::$trait",
301 );
302 Moose::Meta::Class->create(
303 $class_name,
304 superclasses => ['Moose::Object'],
305 attributes => [
306 $attr_class->new(
307 'nonlazy',
308 is => 'ro',
309 isa => $isa{$trait},
310 default => sub { $default{$trait} },
311 handles => {
312 map {; "nonlazy_$_" => $handles->{$_} } keys %$handles
313 },
314 ),
315 $attr_class->new(
316 'lazy',
317 is => 'ro',
318 isa => $isa{$trait},
319 lazy => 1,
320 default => sub { $default{$trait} },
321 handles => {
322 map {; "lazy_$_" => $handles->{$_} } keys %$handles
323 },
324 ),
325 ],
326 );
327 close_over_ok($class_name, $_) for (
328 'new',
329 map {; "nonlazy_$_", "lazy_$_" } keys %$handles
330 );
331 }
332}
333
334{
335 package WithInitializer;
336 use Moose;
337
338 has foo => (
339 is => 'ro',
340 isa => 'Str',
341 initializer => sub { },
342 );
343
344 has bar => (
345 is => 'ro',
346 isa => 'Str',
347 lazy => 1,
348 default => sub { 'a' },
349 initializer => sub { },
350 );
351
352 __PACKAGE__->meta->make_immutable;
353}
354
355close_over_ok('WithInitializer', 'foo');
356{ local $TODO = "initializer still closes over things";
357close_over_ok('WithInitializer', $_) for qw(new bar);
358}
359
360BEGIN {
361 package CustomErrorClass;
362 use Moose;
363 extends 'Moose::Error::Default';
364}
365
366{
367 package WithCustomErrorClass;
368 use metaclass (
369 metaclass => 'Moose::Meta::Class',
370 error_class => 'CustomErrorClass',
371 );
372 use Moose;
373
374 has foo => (
375 is => 'ro',
376 isa => 'Str',
377 );
378
379 __PACKAGE__->meta->make_immutable;
380}
381
382{ local $TODO = "custom error classes still close over things";
383close_over_ok('WithCustomErrorClass', $_) for qw(new foo);
384}
385
386done_testing;