f80138587c0b0ee81934db71aebc919161958bb9
[gitmo/Class-MOP.git] / t / 041_metaclass_incompatibility.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use Test::Exception;
6
7 use metaclass;
8
9 my %metaclass_attrs = (
10     'Instance'            => 'instance_metaclass',
11     'Attribute'           => 'attribute_metaclass',
12     'Method'              => 'method_metaclass',
13     'Method::Wrapped'     => 'wrapped_method_metaclass',
14     'Method::Constructor' => 'constructor_class',
15 );
16
17 # meta classes
18 for my $suffix ('Class', keys %metaclass_attrs) {
19     Class::MOP::Class->create(
20         "Foo::Meta::$suffix",
21         superclasses => ["Class::MOP::$suffix"]
22     );
23     Class::MOP::Class->create(
24         "Bar::Meta::$suffix",
25         superclasses => ["Class::MOP::$suffix"]
26     );
27     Class::MOP::Class->create(
28         "FooBar::Meta::$suffix",
29         superclasses => ["Foo::Meta::$suffix", "Bar::Meta::$suffix"]
30     );
31 }
32
33 # checking...
34
35 lives_ok {
36     Foo::Meta::Class->create('Foo')
37 } '... Foo.meta => Foo::Meta::Class is compatible';
38 lives_ok {
39     Bar::Meta::Class->create('Bar')
40 } '... Bar.meta => Bar::Meta::Class is compatible';
41
42 throws_ok {
43     Bar::Meta::Class->create('Foo::Foo', superclasses => ['Foo'])
44 } qr/compatible/, '... Foo::Foo.meta => Bar::Meta::Class is not compatible';
45 throws_ok {
46     Foo::Meta::Class->create('Bar::Bar', superclasses => ['Bar'])
47 } qr/compatible/, '... Bar::Bar.meta => Foo::Meta::Class is not compatible';
48
49 lives_ok {
50     FooBar::Meta::Class->create('FooBar', superclasses => ['Foo'])
51 } '... FooBar.meta => FooBar::Meta::Class is compatible';
52 lives_ok {
53     FooBar::Meta::Class->create('FooBar2', superclasses => ['Bar'])
54 } '... FooBar2.meta => FooBar::Meta::Class is compatible';
55
56 Foo::Meta::Class->create(
57     'Foo::All',
58     map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
59 );
60
61 throws_ok {
62     Bar::Meta::Class->create(
63         'Foo::All::Sub::Class',
64         superclasses => ['Foo::All'],
65         map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
66     )
67 } qr/compatible/, 'incompatible Class metaclass';
68 for my $suffix (keys %metaclass_attrs) {
69     throws_ok {
70         Foo::Meta::Class->create(
71             "Foo::All::Sub::$suffix",
72             superclasses => ['Foo::All'],
73             (map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs),
74             $metaclass_attrs{$suffix} => "Bar::Meta::$suffix",
75         )
76     } qr/compatible/, "incompatible $suffix metaclass";
77 }
78
79 # fixing...
80
81 lives_ok {
82     Class::MOP::Class->create('Foo::Foo::CMOP', superclasses => ['Foo'])
83 } 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass';
84 isa_ok(Foo::Foo::CMOP->meta, 'Foo::Meta::Class');
85 lives_ok {
86     Class::MOP::Class->create('Bar::Bar::CMOP', superclasses => ['Bar'])
87 } 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass';
88 isa_ok(Bar::Bar::CMOP->meta, 'Bar::Meta::Class');
89
90 lives_ok {
91     Class::MOP::Class->create(
92         'Foo::All::Sub::CMOP::Class',
93         superclasses => ['Foo::All'],
94         map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
95     )
96 } 'metaclass fixing works with other non-default metaclasses';
97 isa_ok(Foo::All::Sub::CMOP::Class->meta, 'Foo::Meta::Class');
98
99 for my $suffix (keys %metaclass_attrs) {
100     lives_ok {
101         Foo::Meta::Class->create(
102             "Foo::All::Sub::CMOP::$suffix",
103             superclasses => ['Foo::All'],
104             (map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs),
105             $metaclass_attrs{$suffix} => "Class::MOP::$suffix",
106         )
107     } "$metaclass_attrs{$suffix} fixing works with other non-default metaclasses";
108     for my $suffix2 (keys %metaclass_attrs) {
109         my $method = $metaclass_attrs{$suffix2};
110         isa_ok("Foo::All::Sub::CMOP::$suffix"->meta->$method, "Foo::Meta::$suffix2");
111     }
112 }
113
114 # initializing...
115
116 {
117     package Foo::NoMeta;
118 }
119
120 Class::MOP::Class->create('Foo::NoMeta::Sub', superclasses => ['Foo::NoMeta']);
121 ok(!Foo::NoMeta->can('meta'), "non-cmop superclass doesn't get methods installed");
122 isa_ok(Class::MOP::class_of('Foo::NoMeta'), 'Class::MOP::Class');
123 isa_ok(Foo::NoMeta::Sub->meta, 'Class::MOP::Class');
124
125 {
126     package Foo::NoMeta2;
127 }
128 Foo::Meta::Class->create('Foo::NoMeta2::Sub', superclasses => ['Foo::NoMeta2']);
129 ok(!Foo::NoMeta->can('meta'), "non-cmop superclass doesn't get methods installed");
130 isa_ok(Class::MOP::class_of('Foo::NoMeta2'), 'Class::MOP::Class');
131 isa_ok(Foo::NoMeta2::Sub->meta, 'Foo::Meta::Class');
132
133 Foo::Meta::Class->create('Foo::WithMeta');
134 {
135     package Foo::WithMeta::Sub;
136     use base 'Foo::WithMeta';
137 }
138 Class::MOP::Class->create(
139     'Foo::WithMeta::Sub::Sub',
140     superclasses => ['Foo::WithMeta::Sub']
141 );
142
143 isa_ok(Class::MOP::class_of('Foo::WithMeta'), 'Foo::Meta::Class');
144 isa_ok(Class::MOP::class_of('Foo::WithMeta::Sub'), 'Foo::Meta::Class');
145 isa_ok(Class::MOP::class_of('Foo::WithMeta::Sub::Sub'), 'Foo::Meta::Class');
146
147 # unsafe fixing...
148
149 {
150     Class::MOP::Class->create(
151         'Foo::Unsafe',
152         attribute_metaclass => 'Foo::Meta::Attribute',
153     );
154     my $meta = Class::MOP::Class->create(
155         'Foo::Unsafe::Sub',
156     );
157     $meta->add_attribute(foo => reader => 'foo');
158     throws_ok { $meta->superclasses('Foo::Unsafe') }
159               qr/compatibility.*pristine/,
160               "can't switch out the attribute metaclass of a class that already has attributes";
161 }
162
163 done_testing;