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