refactor in progress, beware (still passing all my tests though :P)
[gitmo/Moose.git] / t / 030_roles / 005_role_conflict_detection.t
CommitLineData
db1ab48d 1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
fb1e11d5 6use Test::More tests => 89; # it's really 126 with kolibre's tests;
db1ab48d 7use Test::Exception;
8
9BEGIN {
10 use_ok('Moose');
11 use_ok('Moose::Role');
12}
13
14=pod
15
16Mutually recursive roles.
17
18=cut
19
20{
21 package Role::Foo;
db1ab48d 22 use Moose::Role;
23
24 requires 'foo';
25
26 sub bar { 'Role::Foo::bar' }
27
28 package Role::Bar;
db1ab48d 29 use Moose::Role;
30
31 requires 'bar';
32
33 sub foo { 'Role::Bar::foo' }
34}
35
36{
37 package My::Test1;
db1ab48d 38 use Moose;
39
40 ::lives_ok {
41 with 'Role::Foo', 'Role::Bar';
42 } '... our mutually recursive roles combine okay';
43
44 package My::Test2;
db1ab48d 45 use Moose;
46
47 ::lives_ok {
48 with 'Role::Bar', 'Role::Foo';
49 } '... our mutually recursive roles combine okay (no matter what order)';
50}
51
52my $test1 = My::Test1->new;
53isa_ok($test1, 'My::Test1');
54
55ok($test1->does('Role::Foo'), '... $test1 does Role::Foo');
56ok($test1->does('Role::Bar'), '... $test1 does Role::Bar');
57
58can_ok($test1, 'foo');
59can_ok($test1, 'bar');
60
61is($test1->foo, 'Role::Bar::foo', '... $test1->foo worked');
62is($test1->bar, 'Role::Foo::bar', '... $test1->bar worked');
63
64my $test2 = My::Test2->new;
65isa_ok($test2, 'My::Test2');
66
67ok($test2->does('Role::Foo'), '... $test2 does Role::Foo');
68ok($test2->does('Role::Bar'), '... $test2 does Role::Bar');
69
70can_ok($test2, 'foo');
71can_ok($test2, 'bar');
72
73is($test2->foo, 'Role::Bar::foo', '... $test2->foo worked');
74is($test2->bar, 'Role::Foo::bar', '... $test2->bar worked');
75
76# check some meta-stuff
77
78ok(Role::Foo->meta->has_method('bar'), '... it still has the bar method');
79ok(Role::Foo->meta->requires_method('foo'), '... it still has the required foo method');
80
81ok(Role::Bar->meta->has_method('foo'), '... it still has the foo method');
82ok(Role::Bar->meta->requires_method('bar'), '... it still has the required bar method');
83
84=pod
85
86Role method conflicts
87
88=cut
89
90{
91 package Role::Bling;
db1ab48d 92 use Moose::Role;
93
94 sub bling { 'Role::Bling::bling' }
95
96 package Role::Bling::Bling;
db1ab48d 97 use Moose::Role;
98
99 sub bling { 'Role::Bling::Bling::bling' }
100}
101
102{
103 package My::Test3;
db1ab48d 104 use Moose;
105
106 ::throws_ok {
107 with 'Role::Bling', 'Role::Bling::Bling';
108 } qr/requires the method \'bling\' to be implemented/, '... role methods conflicted and method was required';
109
110 package My::Test4;
db1ab48d 111 use Moose;
112
113 ::lives_ok {
114 with 'Role::Bling';
115 with 'Role::Bling::Bling';
116 } '... role methods didnt conflict when manually combined';
117
118 package My::Test5;
db1ab48d 119 use Moose;
120
121 ::lives_ok {
122 with 'Role::Bling::Bling';
123 with 'Role::Bling';
124 } '... role methods didnt conflict when manually combined (in opposite order)';
125
126 package My::Test6;
db1ab48d 127 use Moose;
128
129 ::lives_ok {
130 with 'Role::Bling::Bling', 'Role::Bling';
131 } '... role methods didnt conflict when manually resolved';
132
133 sub bling { 'My::Test6::bling' }
134}
135
136ok(!My::Test3->meta->has_method('bling'), '... we didnt get any methods in the conflict');
137ok(My::Test4->meta->has_method('bling'), '... we did get the method when manually dealt with');
138ok(My::Test5->meta->has_method('bling'), '... we did get the method when manually dealt with');
139ok(My::Test6->meta->has_method('bling'), '... we did get the method when manually dealt with');
140
d79e62fd 141ok(!My::Test3->does('Role::Bling'), '... our class does() the correct roles');
142ok(!My::Test3->does('Role::Bling::Bling'), '... our class does() the correct roles');
143ok(My::Test4->does('Role::Bling'), '... our class does() the correct roles');
144ok(My::Test4->does('Role::Bling::Bling'), '... our class does() the correct roles');
145ok(My::Test5->does('Role::Bling'), '... our class does() the correct roles');
146ok(My::Test5->does('Role::Bling::Bling'), '... our class does() the correct roles');
147ok(My::Test6->does('Role::Bling'), '... our class does() the correct roles');
148ok(My::Test6->does('Role::Bling::Bling'), '... our class does() the correct roles');
149
db1ab48d 150is(My::Test4->bling, 'Role::Bling::bling', '... and we got the first method that was added');
151is(My::Test5->bling, 'Role::Bling::Bling::bling', '... and we got the first method that was added');
152is(My::Test6->bling, 'My::Test6::bling', '... and we got the local method');
153
154# check how this affects role compostion
155
156{
157 package Role::Bling::Bling::Bling;
db1ab48d 158 use Moose::Role;
159
160 with 'Role::Bling::Bling';
161
162 sub bling { 'Role::Bling::Bling::Bling::bling' }
163}
164
165ok(Role::Bling::Bling->meta->has_method('bling'), '... still got the bling method in Role::Bling::Bling');
d79e62fd 166ok(Role::Bling::Bling->meta->does_role('Role::Bling::Bling'), '... our role correctly does() the other role');
fb1e11d5 167ok(Role::Bling::Bling::Bling->meta->has_method('bling'), '... dont have the bling method in Role::Bling::Bling::Bling');
d05cd563 168is(Role::Bling::Bling::Bling->meta->get_method('bling')->(),
169 'Role::Bling::Bling::Bling::bling',
170 '... still got the bling method in Role::Bling::Bling::Bling');
db1ab48d 171
fb1e11d5 172
db1ab48d 173=pod
174
175Role attribute conflicts
176
177=cut
178
179{
180 package Role::Boo;
db1ab48d 181 use Moose::Role;
182
183 has 'ghost' => (is => 'ro', default => 'Role::Boo::ghost');
184
185 package Role::Boo::Hoo;
db1ab48d 186 use Moose::Role;
187
188 has 'ghost' => (is => 'ro', default => 'Role::Boo::Hoo::ghost');
189}
190
191{
192 package My::Test7;
db1ab48d 193 use Moose;
194
195 ::throws_ok {
196 with 'Role::Boo', 'Role::Boo::Hoo';
fb1e11d5 197 } qr/We have encountered an attribute conflict/,
db1ab48d 198 '... role attrs conflicted and method was required';
199
200 package My::Test8;
db1ab48d 201 use Moose;
202
203 ::lives_ok {
204 with 'Role::Boo';
205 with 'Role::Boo::Hoo';
206 } '... role attrs didnt conflict when manually combined';
207
208 package My::Test9;
db1ab48d 209 use Moose;
210
211 ::lives_ok {
212 with 'Role::Boo::Hoo';
213 with 'Role::Boo';
214 } '... role attrs didnt conflict when manually combined';
215
216 package My::Test10;
db1ab48d 217 use Moose;
218
219 has 'ghost' => (is => 'ro', default => 'My::Test10::ghost');
220
221 ::throws_ok {
222 with 'Role::Boo', 'Role::Boo::Hoo';
fb1e11d5 223 } qr/We have encountered an attribute conflict/,
db1ab48d 224 '... role attrs conflicted and cannot be manually disambiguted';
225
226}
227
228ok(!My::Test7->meta->has_attribute('ghost'), '... we didnt get any attributes in the conflict');
229ok(My::Test8->meta->has_attribute('ghost'), '... we did get an attributes when manually composed');
230ok(My::Test9->meta->has_attribute('ghost'), '... we did get an attributes when manually composed');
231ok(My::Test10->meta->has_attribute('ghost'), '... we did still have an attribute ghost (conflict does not mess with class)');
232
d79e62fd 233ok(!My::Test7->does('Role::Boo'), '... our class does() the correct roles');
234ok(!My::Test7->does('Role::Boo::Hoo'), '... our class does() the correct roles');
235ok(My::Test8->does('Role::Boo'), '... our class does() the correct roles');
236ok(My::Test8->does('Role::Boo::Hoo'), '... our class does() the correct roles');
237ok(My::Test9->does('Role::Boo'), '... our class does() the correct roles');
238ok(My::Test9->does('Role::Boo::Hoo'), '... our class does() the correct roles');
239ok(!My::Test10->does('Role::Boo'), '... our class does() the correct roles');
240ok(!My::Test10->does('Role::Boo::Hoo'), '... our class does() the correct roles');
241
db1ab48d 242can_ok('My::Test8', 'ghost');
243can_ok('My::Test9', 'ghost');
244can_ok('My::Test10', 'ghost');
245
246is(My::Test8->new->ghost, 'Role::Boo::ghost', '... got the expected default attr value');
247is(My::Test9->new->ghost, 'Role::Boo::Hoo::ghost', '... got the expected default attr value');
248is(My::Test10->new->ghost, 'My::Test10::ghost', '... got the expected default attr value');
249
d05cd563 250=pod
251
252Role override method conflicts
253
254=cut
255
0558683c 256{
257 package Role::Plot;
258 use Moose::Role;
259
260 override 'twist' => sub {
261 super() . ' -> Role::Plot::twist';
262 };
263
264 package Role::Truth;
265 use Moose::Role;
266
267 override 'twist' => sub {
268 super() . ' -> Role::Truth::twist';
269 };
270}
271
272{
273 package My::Test::Base;
274 use Moose;
275
276 sub twist { 'My::Test::Base::twist' }
277
278 package My::Test11;
279 use Moose;
280
281 extends 'My::Test::Base';
282
283 ::lives_ok {
284 with 'Role::Truth';
285 } '... composed the role with override okay';
286
287 package My::Test12;
288 use Moose;
289
290 extends 'My::Test::Base';
291
292 ::lives_ok {
293 with 'Role::Plot';
294 } '... composed the role with override okay';
295
296 package My::Test13;
297 use Moose;
298
299 ::dies_ok {
300 with 'Role::Plot';
301 } '... cannot compose it because we have no superclass';
302
303 package My::Test14;
304 use Moose;
305
306 extends 'My::Test::Base';
307
308 ::throws_ok {
309 with 'Role::Plot', 'Role::Truth';
310 } qr/Two \'override\' methods of the same name encountered/,
311 '... cannot compose it because we have no superclass';
312}
313
314ok(My::Test11->meta->has_method('twist'), '... the twist method has been added');
315ok(My::Test12->meta->has_method('twist'), '... the twist method has been added');
316ok(!My::Test13->meta->has_method('twist'), '... the twist method has not been added');
317ok(!My::Test14->meta->has_method('twist'), '... the twist method has not been added');
318
319ok(!My::Test11->does('Role::Plot'), '... our class does() the correct roles');
320ok(My::Test11->does('Role::Truth'), '... our class does() the correct roles');
321ok(!My::Test12->does('Role::Truth'), '... our class does() the correct roles');
322ok(My::Test12->does('Role::Plot'), '... our class does() the correct roles');
323ok(!My::Test13->does('Role::Plot'), '... our class does() the correct roles');
324ok(!My::Test14->does('Role::Truth'), '... our class does() the correct roles');
325ok(!My::Test14->does('Role::Plot'), '... our class does() the correct roles');
326
327is(My::Test11->twist(), 'My::Test::Base::twist -> Role::Truth::twist', '... got the right method return');
328is(My::Test12->twist(), 'My::Test::Base::twist -> Role::Plot::twist', '... got the right method return');
329ok(!My::Test13->can('twist'), '... no twist method here at all');
330is(My::Test14->twist(), 'My::Test::Base::twist', '... got the right method return (from superclass)');
331
332{
333 package Role::Reality;
334 use Moose::Role;
335
336 ::throws_ok {
337 with 'Role::Plot';
338 } qr/A local method of the same name as been found/,
339 '... could not compose roles here, it dies';
340
341 sub twist {
342 'Role::Reality::twist';
343 }
344}
345
346ok(Role::Reality->meta->has_method('twist'), '... the twist method has not been added');
fb1e11d5 347#ok(!Role::Reality->meta->does_role('Role::Plot'), '... our role does() the correct roles');
0558683c 348is(Role::Reality->meta->get_method('twist')->(),
349 'Role::Reality::twist',
350 '... the twist method returns the right value');
3c5fd53a 351
352=pod
353
354Role conflicts between attributes and methods
355
21716c07 356[15:23] <kolibrie> when class defines method and role defines method, class wins
357[15:24] <kolibrie> when class 'has' method and role defines method, class wins
358[15:24] <kolibrie> when class defines method and role 'has' method, role wins
359[15:24] <kolibrie> when class 'has' method and role 'has' method, role wins
360[15:24] <kolibrie> which means when class 'has' method and two roles 'has' method, no tiebreak is d
361[15:24] <kolibrie> etected
362[15:24] <perigrin> this is with role and has declaration in the exact same order in every case?
363[15:25] <kolibrie> yes
364[15:25] <perigrin> interesting
365[15:25] <kolibrie> that's what I thought
366[15:26] <kolibrie> does that sound like something I should write a test for?
367[15:27] <perigrin> stevan, ping?
368[15:27] <perigrin> I'm not sure what the right answer for composition is.
369[15:27] <perigrin> who should win
370[15:27] <perigrin> if I were to guess I'd say the class should always win.
371[15:27] <kolibrie> that would be my guess, but I thought I would ask to make sure
372[15:29] <stevan> kolibrie: please write a test
373[15:29] <stevan> I am not exactly sure who should win either,.. but I suspect it is not working correctly right now
374[15:29] <stevan> I know exactly why it is doing what it is doing though
375
376Now I have to decide actually what happens, and how to fix it.
377- SL
3c5fd53a 378
379{
380 package Role::Method;
381 use Moose::Role;
382
383 sub ghost { 'Role::Method::ghost' }
384
385 package Role::Method2;
386 use Moose::Role;
387
388 sub ghost { 'Role::Method2::ghost' }
389
390 package Role::Attribute;
391 use Moose::Role;
392
393 has 'ghost' => (is => 'ro', default => 'Role::Attribute::ghost');
394
395 package Role::Attribute2;
396 use Moose::Role;
397
398 has 'ghost' => (is => 'ro', default => 'Role::Attribute2::ghost');
399}
400
401{
402 package My::Test15;
403 use Moose;
404
405 ::lives_ok {
406 with 'Role::Method';
407 } '... composed the method role into the method class';
408
409 sub ghost { 'My::Test15::ghost' }
410
411 package My::Test16;
412 use Moose;
413
414 ::lives_ok {
415 with 'Role::Method';
416 } '... composed the method role into the attribute class';
417
418 has 'ghost' => (is => 'ro', default => 'My::Test16::ghost');
419
420 package My::Test17;
421 use Moose;
422
423 ::lives_ok {
424 with 'Role::Attribute';
425 } '... composed the attribute role into the method class';
426
427 sub ghost { 'My::Test17::ghost' }
428
429 package My::Test18;
430 use Moose;
431
432 ::lives_ok {
433 with 'Role::Attribute';
434 } '... composed the attribute role into the attribute class';
435
436 has 'ghost' => (is => 'ro', default => 'My::Test18::ghost');
437
438 package My::Test19;
439 use Moose;
440
441 ::lives_ok {
442 with 'Role::Method', 'Role::Method2';
443 } '... composed method roles into class with method tiebreaker';
444
445 sub ghost { 'My::Test19::ghost' }
446
447 package My::Test20;
448 use Moose;
449
450 ::lives_ok {
451 with 'Role::Method', 'Role::Method2';
452 } '... composed method roles into class with attribute tiebreaker';
453
454 has 'ghost' => (is => 'ro', default => 'My::Test20::ghost');
455
456 package My::Test21;
457 use Moose;
458
459 ::lives_ok {
460 with 'Role::Attribute', 'Role::Attribute2';
461 } '... composed attribute roles into class with method tiebreaker';
462
463 sub ghost { 'My::Test21::ghost' }
464
465 package My::Test22;
466 use Moose;
467
468 ::lives_ok {
469 with 'Role::Attribute', 'Role::Attribute2';
470 } '... composed attribute roles into class with attribute tiebreaker';
471
472 has 'ghost' => (is => 'ro', default => 'My::Test22::ghost');
473
474 package My::Test23;
475 use Moose;
476
477 ::lives_ok {
478 with 'Role::Method', 'Role::Attribute';
479 } '... composed method and attribute role into class with method tiebreaker';
480
481 sub ghost { 'My::Test23::ghost' }
482
483 package My::Test24;
484 use Moose;
485
486 ::lives_ok {
487 with 'Role::Method', 'Role::Attribute';
488 } '... composed method and attribute role into class with attribute tiebreaker';
489
490 has 'ghost' => (is => 'ro', default => 'My::Test24::ghost');
491
492 package My::Test25;
493 use Moose;
494
495 ::lives_ok {
496 with 'Role::Attribute', 'Role::Method';
497 } '... composed attribute and method role into class with method tiebreaker';
498
499 sub ghost { 'My::Test25::ghost' }
500
501 package My::Test26;
502 use Moose;
503
504 ::lives_ok {
505 with 'Role::Attribute', 'Role::Method';
506 } '... composed attribute and method role into class with attribute tiebreaker';
507
508 has 'ghost' => (is => 'ro', default => 'My::Test26::ghost');
509}
510
511my $test15 = My::Test15->new;
512isa_ok($test15, 'My::Test15');
513is($test15->ghost, 'My::Test15::ghost', '... we access the method from the class and ignore the role method');
514
515my $test16 = My::Test16->new;
516isa_ok($test16, 'My::Test16');
517is($test16->ghost, 'My::Test16::ghost', '... we access the attribute from the class and ignore the role method');
518
519my $test17 = My::Test17->new;
520isa_ok($test17, 'My::Test17');
521is($test17->ghost, 'My::Test17::ghost', '... we access the method from the class and ignore the role attribute');
522
523my $test18 = My::Test18->new;
524isa_ok($test18, 'My::Test18');
525is($test18->ghost, 'My::Test18::ghost', '... we access the attribute from the class and ignore the role attribute');
526
527my $test19 = My::Test19->new;
528isa_ok($test19, 'My::Test19');
529is($test19->ghost, 'My::Test19::ghost', '... we access the method from the class and ignore the role methods');
530
531my $test20 = My::Test20->new;
532isa_ok($test20, 'My::Test20');
533is($test20->ghost, 'My::Test20::ghost', '... we access the attribute from the class and ignore the role methods');
534
535my $test21 = My::Test21->new;
536isa_ok($test21, 'My::Test21');
537is($test21->ghost, 'My::Test21::ghost', '... we access the method from the class and ignore the role attributes');
538
539my $test22 = My::Test22->new;
540isa_ok($test22, 'My::Test22');
541is($test22->ghost, 'My::Test22::ghost', '... we access the attribute from the class and ignore the role attributes');
542
543my $test23 = My::Test23->new;
544isa_ok($test23, 'My::Test23');
545is($test23->ghost, 'My::Test23::ghost', '... we access the method from the class and ignore the role method and attribute');
546
547my $test24 = My::Test24->new;
548isa_ok($test24, 'My::Test24');
549is($test24->ghost, 'My::Test24::ghost', '... we access the attribute from the class and ignore the role method and attribute');
550
551my $test25 = My::Test25->new;
552isa_ok($test25, 'My::Test25');
553is($test25->ghost, 'My::Test25::ghost', '... we access the method from the class and ignore the role attribute and method');
554
555my $test26 = My::Test26->new;
556isa_ok($test26, 'My::Test26');
557is($test26->ghost, 'My::Test26::ghost', '... we access the attribute from the class and ignore the role attribute and method');
558
21716c07 559=cut