Revert "convert all uses of Test::Exception to Test::Fatal."
[gitmo/Class-MOP.git] / t / 041_metaclass_incompatibility.t
CommitLineData
550d56db 1use strict;
2use warnings;
3
86a4d873 4use Test::More;
8371f3de 5use Test::Exception;
550d56db 6
86a4d873 7use metaclass;
550d56db 8
4920ae3d 9my %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
550d56db 17# meta classes
4920ae3d 18for 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
8371f3de 35lives_ok {
4920ae3d 36 Foo::Meta::Class->create('Foo')
8371f3de 37} '... Foo.meta => Foo::Meta::Class is compatible';
38lives_ok {
4920ae3d 39 Bar::Meta::Class->create('Bar')
8371f3de 40} '... Bar.meta => Bar::Meta::Class is compatible';
4920ae3d 41
8371f3de 42throws_ok {
4920ae3d 43 Bar::Meta::Class->create('Foo::Foo', superclasses => ['Foo'])
8371f3de 44} qr/compatible/, '... Foo::Foo.meta => Bar::Meta::Class is not compatible';
45throws_ok {
4920ae3d 46 Foo::Meta::Class->create('Bar::Bar', superclasses => ['Bar'])
8371f3de 47} qr/compatible/, '... Bar::Bar.meta => Foo::Meta::Class is not compatible';
4920ae3d 48
8371f3de 49lives_ok {
4920ae3d 50 FooBar::Meta::Class->create('FooBar', superclasses => ['Foo'])
8371f3de 51} '... FooBar.meta => FooBar::Meta::Class is compatible';
52lives_ok {
4920ae3d 53 FooBar::Meta::Class->create('FooBar2', superclasses => ['Bar'])
8371f3de 54} '... FooBar2.meta => FooBar::Meta::Class is compatible';
4920ae3d 55
56Foo::Meta::Class->create(
57 'Foo::All',
58 map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
59);
60
8371f3de 61throws_ok {
4920ae3d 62 Bar::Meta::Class->create(
63 'Foo::All::Sub::Class',
64 superclasses => ['Foo::All'],
65 map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
66 )
8371f3de 67} qr/compatible/, 'incompatible Class metaclass';
4920ae3d 68for my $suffix (keys %metaclass_attrs) {
8371f3de 69 throws_ok {
4920ae3d 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 )
8371f3de 76 } qr/compatible/, "incompatible $suffix metaclass";
4920ae3d 77}
78
79# fixing...
80
8371f3de 81lives_ok {
4920ae3d 82 Class::MOP::Class->create('Foo::Foo::CMOP', superclasses => ['Foo'])
8371f3de 83} 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass';
4920ae3d 84isa_ok(Foo::Foo::CMOP->meta, 'Foo::Meta::Class');
8371f3de 85lives_ok {
4920ae3d 86 Class::MOP::Class->create('Bar::Bar::CMOP', superclasses => ['Bar'])
8371f3de 87} 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass';
4920ae3d 88isa_ok(Bar::Bar::CMOP->meta, 'Bar::Meta::Class');
89
8371f3de 90lives_ok {
4920ae3d 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 )
8371f3de 96} 'metaclass fixing works with other non-default metaclasses';
4920ae3d 97isa_ok(Foo::All::Sub::CMOP::Class->meta, 'Foo::Meta::Class');
98
99for my $suffix (keys %metaclass_attrs) {
8371f3de 100 lives_ok {
4920ae3d 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 )
8371f3de 107 } "$metaclass_attrs{$suffix} fixing works with other non-default metaclasses";
4920ae3d 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
550d56db 116{
4920ae3d 117 package Foo::NoMeta;
118}
86a4d873 119
4920ae3d 120Class::MOP::Class->create('Foo::NoMeta::Sub', superclasses => ['Foo::NoMeta']);
121ok(!Foo::NoMeta->can('meta'), "non-cmop superclass doesn't get methods installed");
122isa_ok(Class::MOP::class_of('Foo::NoMeta'), 'Class::MOP::Class');
123isa_ok(Foo::NoMeta::Sub->meta, 'Class::MOP::Class');
86a4d873 124
4920ae3d 125{
126 package Foo::NoMeta2;
550d56db 127}
4920ae3d 128Foo::Meta::Class->create('Foo::NoMeta2::Sub', superclasses => ['Foo::NoMeta2']);
129ok(!Foo::NoMeta->can('meta'), "non-cmop superclass doesn't get methods installed");
130isa_ok(Class::MOP::class_of('Foo::NoMeta2'), 'Class::MOP::Class');
131isa_ok(Foo::NoMeta2::Sub->meta, 'Foo::Meta::Class');
132
b9f7897b 133Foo::Meta::Class->create('Foo::WithMeta');
134{
135 package Foo::WithMeta::Sub;
136 use base 'Foo::WithMeta';
137}
138Class::MOP::Class->create(
139 'Foo::WithMeta::Sub::Sub',
140 superclasses => ['Foo::WithMeta::Sub']
141);
142
143isa_ok(Class::MOP::class_of('Foo::WithMeta'), 'Foo::Meta::Class');
144isa_ok(Class::MOP::class_of('Foo::WithMeta::Sub'), 'Foo::Meta::Class');
145isa_ok(Class::MOP::class_of('Foo::WithMeta::Sub::Sub'), 'Foo::Meta::Class');
146
6b0ff0b1 147Foo::Meta::Class->create('Foo::WithMeta2');
148{
149 package Foo::WithMeta2::Sub;
150 use base 'Foo::WithMeta2';
151}
152{
153 package Foo::WithMeta2::Sub::Sub;
154 use base 'Foo::WithMeta2::Sub';
155}
156Class::MOP::Class->create(
157 'Foo::WithMeta2::Sub::Sub::Sub',
158 superclasses => ['Foo::WithMeta2::Sub::Sub']
159);
160
161isa_ok(Class::MOP::class_of('Foo::WithMeta2'), 'Foo::Meta::Class');
162isa_ok(Class::MOP::class_of('Foo::WithMeta2::Sub'), 'Foo::Meta::Class');
163isa_ok(Class::MOP::class_of('Foo::WithMeta2::Sub::Sub'), 'Foo::Meta::Class');
164isa_ok(Class::MOP::class_of('Foo::WithMeta2::Sub::Sub::Sub'), 'Foo::Meta::Class');
165
166Class::MOP::Class->create(
167 'Foo::Reverse::Sub::Sub',
168 superclasses => ['Foo::Reverse::Sub'],
169);
170eval "package Foo::Reverse::Sub; use base 'Foo::Reverse';";
171Foo::Meta::Class->create(
172 'Foo::Reverse',
173);
174isa_ok(Class::MOP::class_of('Foo::Reverse'), 'Foo::Meta::Class');
57e6687d 175{ local $TODO = 'No idea how to handle case where parent class is created before children';
6b0ff0b1 176isa_ok(Class::MOP::class_of('Foo::Reverse::Sub'), 'Foo::Meta::Class');
177isa_ok(Class::MOP::class_of('Foo::Reverse::Sub::Sub'), 'Foo::Meta::Class');
178}
179
4920ae3d 180# unsafe fixing...
550d56db 181
4920ae3d 182{
183 Class::MOP::Class->create(
184 'Foo::Unsafe',
185 attribute_metaclass => 'Foo::Meta::Attribute',
186 );
187 my $meta = Class::MOP::Class->create(
188 'Foo::Unsafe::Sub',
189 );
190 $meta->add_attribute(foo => reader => 'foo');
8371f3de 191 throws_ok { $meta->superclasses('Foo::Unsafe') }
4920ae3d 192 qr/compatibility.*pristine/,
193 "can't switch out the attribute metaclass of a class that already has attributes";
194}
550d56db 195
3a9318e6 196# immutability...
197
198{
199 my $foometa = Foo::Meta::Class->create(
200 'Foo::Immutable',
201 );
202 $foometa->make_immutable;
203 my $barmeta = Class::MOP::Class->create(
204 'Bar::Mutable',
205 );
206 my $bazmeta = Class::MOP::Class->create(
207 'Baz::Mutable',
208 );
209 $bazmeta->superclasses($foometa->name);
8371f3de 210 lives_ok { $bazmeta->superclasses($barmeta->name) }
3a9318e6 211 "can still set superclasses";
212 ok(!$bazmeta->is_immutable,
213 "immutable superclass doesn't make this class immutable");
8371f3de 214 lives_ok { $bazmeta->make_immutable } "can still make immutable";
3a9318e6 215}
216
06ea51c7 217# nonexistent metaclasses
218
8b1cc359 219Class::MOP::Class->create(
220 'Weird::Meta::Method::Destructor',
221 superclasses => ['Class::MOP::Method'],
222);
06ea51c7 223
8371f3de 224lives_ok {
06ea51c7 225 Class::MOP::Class->create(
226 'Weird::Class',
227 destructor_class => 'Weird::Meta::Method::Destructor',
228 );
8371f3de 229} "defined metaclass in child with defined metaclass in parent is fine";
06ea51c7 230
231is(Weird::Class->meta->destructor_class, 'Weird::Meta::Method::Destructor',
232 "got the right destructor class");
233
8371f3de 234lives_ok {
06ea51c7 235 Class::MOP::Class->create(
236 'Weird::Class::Sub',
237 superclasses => ['Weird::Class'],
238 destructor_class => undef,
239 );
8371f3de 240} "undef metaclass in child with defined metaclass in parent can be fixed";
06ea51c7 241
242is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
243 "got the right destructor class");
244
8371f3de 245lives_ok {
06ea51c7 246 Class::MOP::Class->create(
247 'Weird::Class::Sub2',
248 destructor_class => undef,
249 );
8371f3de 250} "undef metaclass in child with defined metaclass in parent can be fixed";
06ea51c7 251
8371f3de 252lives_ok {
06ea51c7 253 Weird::Class::Sub2->meta->superclasses('Weird::Class');
8371f3de 254} "undef metaclass in child with defined metaclass in parent can be fixed";
06ea51c7 255
256is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
257 "got the right destructor class");
258
86a4d873 259done_testing;