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