remove extra var declaration
[gitmo/moose-presentations.git] / moose-class / exercises / t / lib / MooseClass / Tests.pm
CommitLineData
ddd87d75 1package MooseClass::Tests;
2
3use strict;
4use warnings;
5
5cab7e05 6use Lingua::EN::Inflect qw( A PL_N );
ddd87d75 7use Test::More 'no_plan';
8
9sub tests01 {
10 local $Test::Builder::Level = $Test::Builder::Level + 1;
11
12 has_meta('Person');
13
14 check_isa( 'Person', ['Moose::Object'] );
15
ddd87d75 16 has_rw_attr( 'Person', $_ ) for qw( first_name last_name );
17
18 has_method( 'Person', 'full_name' );
19
20 no_droppings('Person');
21 is_immutable('Person');
22
23 person01();
24
25 has_meta('Employee');
26
27 check_isa( 'Employee', [ 'Person', 'Moose::Object' ] );
28
8d1ce1d7 29 has_rw_attr( 'Employee', $_ ) for qw( title salary );
ddd87d75 30 has_ro_attr( 'Employee', 'ssn' );
31
32 has_overridden_method( 'Employee', 'full_name' );
33
34 employee01();
35}
36
5cab7e05 37sub tests02 {
a8c37857 38 tests01();
5cab7e05 39
40 local $Test::Builder::Level = $Test::Builder::Level + 1;
41
42 no_droppings($_) for qw( Printable HasAccount );
43
44 does_role( 'Person', $_ ) for qw( Printable HasAccount );
45 has_method( 'Person', $_ ) for qw( as_string deposit withdraw );
46 has_rw_attr( 'Person', 'balance' );
47
48 does_role( 'Employee', $_ ) for qw( Printable HasAccount );
49
50 person02();
51 employee02();
52}
53
8d1ce1d7 54sub tests03 {
55 {
56 local $Test::Builder::Level = $Test::Builder::Level + 1;
57
58 has_meta('Person');
59 has_meta('Employee');
60
61 has_rw_attr( 'Person', 'title' );
62
605c1144 63 has_rw_attr( 'Employee', 'title', 'overridden' );
8d1ce1d7 64 has_rw_attr( 'Employee', 'salary_level' );
65 has_ro_attr( 'Employee', 'salary' );
66
67 has_method( 'Employee', '_build_salary' );
68 }
69
70 ok( ! Employee->meta->has_method('full_name'),
71 'Employee no longer implements a full_name method' );
72
73 my $person_title_attr = Person->meta->get_attribute('title');
74 ok( !$person_title_attr->is_required, 'title is not required in Person' );
75 is( $person_title_attr->predicate, 'has_title',
76 'Person title attr has a has_title predicate' );
77 is( $person_title_attr->clearer, 'clear_title',
78 'Person title attr has a clear_title clearer' );
79
80 my $balance_attr = Person->meta->get_attribute('balance');
81 is( $balance_attr->default, 100, 'balance defaults to 100' );
82
83 my $employee_title_attr = Employee->meta->get_attribute('title');
84 is( $employee_title_attr->default, 'Worker',
85 'title defaults to Worker in Employee' );
86
87 my $salary_level_attr = Employee->meta->get_attribute('salary_level');
88 is( $salary_level_attr->default, 1, 'salary_level defaults to 1' );
89
90 my $salary_attr = Employee->meta->get_attribute('salary');
91 ok( !$salary_attr->init_arg, 'no init_arg for salary attribute' );
92 ok( $salary_attr->has_builder, 'salary attr has a builder' );
93
94 person03();
95 employee03();
96}
97
26164c8d 98sub tests04 {
99 {
100 local $Test::Builder::Level = $Test::Builder::Level + 1;
101
538499df 102 has_meta('Document');
103 has_meta('Report');
104 has_meta('TPSReport');
26164c8d 105
538499df 106 no_droppings('Document');
107 no_droppings('Report');
108 no_droppings('TPSReport');
109
110 has_ro_attr( 'Document', $_ ) for qw( title author );
111 has_ro_attr( 'Report', 'summary' );
112 has_ro_attr( 'TPSReport', $_ ) for qw( t p s );
113
114 has_method( 'Document', 'output' );
115 has_augmented_method( 'Report', 'output' );
116 has_augmented_method( 'TPSReport', 'output' );
26164c8d 117 }
118
538499df 119 my $tps = TPSReport->new(
120 title => 'That TPS Report',
121 author => 'Peter Gibbons (for Bill Lumberg)',
122 summary => 'I celebrate his whole collection!',
123 t => 'PC Load Letter',
124 p => 'Swingline',
125 s => 'flair!',
126 );
26164c8d 127
538499df 128 my $output = $tps->output;
129 $output =~ s/\n\n+/\n/g;
26164c8d 130
538499df 131 is( $output, <<'EOF', 'output returns expected report' );
132That TPS Report
133I celebrate his whole collection!
134t: PC Load Letter
135p: Swingline
136s: flair!
137Written by Peter Gibbons (for Bill Lumberg)
138EOF
26164c8d 139}
140
ad648c43 141sub tests05 {
142 {
143 local $Test::Builder::Level = $Test::Builder::Level + 1;
144
145 has_meta('Person');
146 has_meta('Employee');
147 no_droppings('Employee');
148 }
149
150 for my $attr_name ( qw( first_name last_name title ) ) {
151 my $attr = Person->meta->get_attribute($attr_name);
152
153 ok( $attr->has_type_constraint,
154 "Person $attr_name has a type constraint" );
155 is( $attr->type_constraint->name, 'Str',
156 "Person $attr_name type is Str" );
157 }
158
159 {
160 my $salary_level_attr = Employee->meta->get_attribute('salary_level');
161 ok( $salary_level_attr->has_type_constraint,
162 'Employee salary_level has a type constraint' );
163
164 my $tc = $salary_level_attr->type_constraint;
165
166 for my $invalid ( 0, 11, -14, 'foo', undef ) {
167 my $str = defined $invalid ? $invalid : 'undef';
168 ok( ! $tc->check($invalid),
169 "salary_level type rejects invalid value - $str" );
170 }
171
172 for my $valid ( 1..10 ) {
173 ok( $tc->check($valid),
174 "salary_level type accepts valid value - $valid" );
175 }
176 }
177
178 {
179 my $salary_attr = Employee->meta->get_attribute('salary');
180
181 ok( $salary_attr->has_type_constraint,
182 'Employee salary has a type constraint' );
183
184 my $tc = $salary_attr->type_constraint;
185
186 for my $invalid ( 0, -14, 'foo', undef ) {
187 my $str = defined $invalid ? $invalid : 'undef';
188 ok( ! $tc->check($invalid),
189 "salary type rejects invalid value - $str" );
190 }
191
192 for my $valid ( 1, 100_000, 10**10 ) {
193 ok( $tc->check($valid),
194 "salary type accepts valid value - $valid" );
195 }
196 }
197
198 {
199 my $ssn_attr = Employee->meta->get_attribute('ssn');
200
201 ok( $ssn_attr->has_type_constraint,
202 'Employee ssn has a type constraint' );
203
204 my $tc = $ssn_attr->type_constraint;
205
206 for my $invalid ( 0, -14, 'foo', undef, '123-ab-1241', '123456789' ) {
207 my $str = defined $invalid ? $invalid : 'undef';
208 ok( ! $tc->check($invalid),
209 "ssn type rejects invalid value - $str" );
210 }
211
212 for my $valid ( '041-12-1251', '123-45-6789', '926-41-5820' ) {
213 ok( $tc->check($valid),
214 "ssn type accepts valid value - $valid" );
215 }
216 }
217}
218
66b226e5 219sub tests06 {
220 {
221 local $Test::Builder::Level = $Test::Builder::Level + 1;
222
223 has_meta('BankAccount');
224 no_droppings('BankAccount');
225
226 has_rw_attr( 'BankAccount', 'balance' );
227 has_rw_attr( 'BankAccount', 'owner' );
228 has_ro_attr( 'BankAccount', 'history' );
229 }
230
36e1e336 231 my $ba_meta = BankAccount->meta;
232 ok( $ba_meta->has_attribute('balance'),
233 'BankAccount class has a balance attribute' );
234
235 ok( $ba_meta->get_attribute('balance')->has_trigger,
236 'BankAccount balance attribute has a trigger' );
237
66b226e5 238 my $person_meta = Person->meta;
239 ok( ! $person_meta->has_attribute('balance'),
240 'Person class does not have a balance attribute' );
241
242 my $deposit_meth = $person_meta->get_method('deposit');
243 isa_ok( $deposit_meth, 'Moose::Meta::Method::Delegation' );
244
245 my $withdraw_meth = $person_meta->get_method('withdraw');
246 isa_ok( $withdraw_meth, 'Moose::Meta::Method::Delegation' );
247
66b226e5 248 ok( $ba_meta->get_attribute('owner')->is_weak_ref,
249 'owner attribute is a weak ref' );
250
251 person06();
252}
253
254
ddd87d75 255sub has_meta {
256 my $class = shift;
257
258 ok( $class->can('meta'), "$class has a meta() method" )
29ae6919 259 or BAIL_OUT("$class does not have a meta() method (did you forget to 'use Moose'?)");
ddd87d75 260}
261
262sub check_isa {
263 my $class = shift;
264 my $parents = shift;
265
266 my @isa = $class->meta->linearized_isa;
267 shift @isa; # returns $class as the first entry
268
269 my $count = scalar @{$parents};
270 my $noun = PL_N( 'parent', $count );
271
272 is( scalar @isa, $count, "$class has $count $noun" );
273
274 for ( my $i = 0; $i < @{$parents}; $i++ ) {
275 is( $isa[$i], $parents->[$i], "parent[$i] is $parents->[$i]" );
276 }
277}
278
ddd87d75 279sub has_rw_attr {
605c1144 280 my $class = shift;
281 my $name = shift;
282 my $overridden = shift;
ddd87d75 283
605c1144 284 my $articled = $overridden ? "an overridden $name" : A($name);
5cab7e05 285 ok( $class->meta->has_attribute($name),
8d1ce1d7 286 "$class has $articled attribute" );
ddd87d75 287
288 my $attr = $class->meta->get_attribute($name);
289
8d1ce1d7 290 is( $attr->get_read_method, $name,
291 "$name attribute has a reader accessor - $name()" );
292 is( $attr->get_write_method, $name,
293 "$name attribute has a writer accessor - $name()" );
ddd87d75 294}
295
296sub has_ro_attr {
297 my $class = shift;
298 my $name = shift;
299
8d1ce1d7 300 my $articled = A($name);
5cab7e05 301 ok( $class->meta->has_attribute($name),
8d1ce1d7 302 "$class has $articled attribute" );
ddd87d75 303
304 my $attr = $class->meta->get_attribute($name);
305
8d1ce1d7 306 is( $attr->get_read_method, $name,
307 "$name attribute has a reader accessor - $name()" );
308 is( $attr->get_write_method, undef,
309 "$name attribute does not have a writer" );
ddd87d75 310}
311
312sub has_method {
313 my $class = shift;
314 my $name = shift;
315
8d1ce1d7 316 my $articled = A($name);
317 ok( $class->meta->has_method($name), "$class has $articled method" );
ddd87d75 318}
319
320sub has_overridden_method {
321 my $class = shift;
322 my $name = shift;
323
8d1ce1d7 324 my $articled = A($name);
325 ok( $class->meta->has_method($name), "$class has $articled method" );
ddd87d75 326
327 my $meth = $class->meta->get_method($name);
328 isa_ok( $meth, 'Moose::Meta::Method::Overridden' );
329}
330
538499df 331sub has_augmented_method {
332 my $class = shift;
333 my $name = shift;
334
335 my $articled = A($name);
336 ok( $class->meta->has_method($name), "$class has $articled method" );
337
338 my $meth = $class->meta->get_method($name);
339 isa_ok( $meth, 'Moose::Meta::Method::Augmented' );
340}
341
ddd87d75 342sub no_droppings {
343 my $class = shift;
344
345 ok( !$class->can('has'), "no Moose droppings in $class" );
ad648c43 346 ok( !$class->can('subtype'), "no Moose::Util::TypeConstraints droppings in $class" );
ddd87d75 347}
348
349sub is_immutable {
350 my $class = shift;
351
352 ok( $class->meta->is_immutable, "$class has been made immutable" );
353}
354
5cab7e05 355sub does_role {
356 my $class = shift;
357 my $role = shift;
358
359 ok( $class->meta->does_role($role), "$class does the $role role" );
360}
361
ddd87d75 362sub person01 {
363 my $person = Person->new(
364 first_name => 'Bilbo',
365 last_name => 'Baggins',
366 );
367
8d1ce1d7 368 is( $person->full_name, 'Bilbo Baggins',
369 'full_name() is correctly implemented' );
f7da468c 370
371 $person = Person->new( [ qw( Lisa Smith ) ] );
372 is( $person->first_name, 'Lisa', 'set first_name from two-arg arrayref' );
373 is( $person->last_name, 'Smith', 'set last_name from two-arg arrayref' );
374
375 eval { Person->new( sub { 'foo' } ) };
376 like( $@, qr/\QSingle parameters to new() must be a HASH ref/,
377 'Person constructor still rejects bad parameters' );
ddd87d75 378}
379
380sub employee01 {
381 my $employee = Employee->new(
382 first_name => 'Amanda',
383 last_name => 'Palmer',
8d1ce1d7 384 title => 'Singer',
ddd87d75 385 );
386
54b470f5 387 my $called = 0;
388 my $orig_super = \&Employee::super;
389 no warnings 'redefine';
390 local *Employee::super = sub { $called++; goto &$orig_super };
391
f555d2ec 392 is( $employee->full_name, 'Amanda Palmer (Singer)',
393 'full_name() is properly overriden in Employee' );
54b470f5 394 ok( $called, 'Employee->full_name calls super()' );
ddd87d75 395}
396
5cab7e05 397sub person02 {
398 my $person = Person->new(
399 first_name => 'Bilbo',
400 last_name => 'Baggins',
401 balance => 0,
402 );
403
8d1ce1d7 404 is( $person->as_string, 'Bilbo Baggins',
405 'as_string() is correctly implemented' );
5cab7e05 406
407 account_tests($person);
408}
409
410sub employee02 {
411 my $employee = Employee->new(
412 first_name => 'Amanda',
413 last_name => 'Palmer',
8d1ce1d7 414 title => 'Singer',
5cab7e05 415 balance => 0,
416 );
417
8d1ce1d7 418 is( $employee->as_string, 'Amanda Palmer (Singer)',
419 'as_string() uses overridden full_name method in Employee' );
5cab7e05 420
421 account_tests($employee);
422}
423
8d1ce1d7 424sub person03 {
425 my $person = Person->new(
426 first_name => 'Bilbo',
427 last_name => 'Baggins',
428 );
429
430 is( $person->full_name, 'Bilbo Baggins',
431 'full_name() is correctly implemented for a Person without a title' );
432 ok( !$person->has_title,
3647da1b 433 'Person has_title predicate is working correctly (returns false)' );
8d1ce1d7 434
435 $person->title('Ringbearer');
3647da1b 436 ok( $person->has_title, 'Person has_title predicate is working correctly (returns true)' );
437
3647da1b 438 my $called = 0;
54b470f5 439 my $orig_pred = \&Person::has_title;
440 no warnings 'redefine';
441 local *Person::has_title = sub { $called++; goto &$orig_pred };
442
8d1ce1d7 443 is( $person->full_name, 'Bilbo Baggins (Ringbearer)',
444 'full_name() is correctly implemented for a Person with a title' );
3647da1b 445 ok( $called, 'full_name in person uses the predicate for the title attribute' );
8d1ce1d7 446
447 $person->clear_title;
448 ok( !$person->has_title, 'Person clear_title method cleared the title' );
449
450 account_tests( $person, 100 );
451}
452
453sub employee03 {
454 my $employee = Employee->new(
455 first_name => 'Jimmy',
456 last_name => 'Foo',
457 salary_level => 3,
458 salary => 42,
459 );
460
461 is( $employee->salary, 30000,
462 'salary is calculated from salary_level, and salary passed to constructor is ignored' );
463}
464
66b226e5 465sub person06 {
466 my $person = Person->new(
467 first_name => 'Bilbo',
468 last_name => 'Baggins',
469 );
470
471 isa_ok( $person->account, 'BankAccount' );
472 is( $person->account->owner, $person,
473 'owner of bank account is person that created account' );
474
475 $person->deposit(10);
648519ab 476 is_deeply( $person->account->history, [ 100 ],
66b226e5 477 'deposit was recorded in account history' );
478
479 $person->withdraw(15);
648519ab 480 is_deeply( $person->account->history, [ 100, 110 ],
481 'withdrawal was recorded in account history' );
482
483 $person->withdraw(45);
484 is_deeply( $person->account->history, [ 100, 110, 95 ],
66b226e5 485 'withdrawal was recorded in account history' );
486}
487
5cab7e05 488sub account_tests {
489 local $Test::Builder::Level = $Test::Builder::Level + 1;
490
491 my $person = shift;
8d1ce1d7 492 my $base_amount = shift || 0;
5cab7e05 493
494 $person->deposit(50);
8d1ce1d7 495 eval { $person->withdraw( 75 + $base_amount ) };
496 like( $@, qr/\QBalance cannot be negative/,
497 'cannot withdraw more than is in our balance' );
5cab7e05 498
8d1ce1d7 499 $person->withdraw( 23 );
5cab7e05 500
8d1ce1d7 501 is( $person->balance, 27 + $base_amount,
502 'balance is 27 (+ starting balance) after deposit of 50 and withdrawal of 23' );
5cab7e05 503}
ddd87d75 504
5051;