Redo conversion to Test::Fatal
[gitmo/Class-MOP.git] / t / 041_metaclass_incompatibility.t
CommitLineData
550d56db 1use strict;
2use warnings;
3
86a4d873 4use Test::More;
871e9eb5 5use Test::Fatal;
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
871e9eb5 35is( exception {
4920ae3d 36 Foo::Meta::Class->create('Foo')
871e9eb5 37}, undef, '... Foo.meta => Foo::Meta::Class is compatible' );
38is( exception {
4920ae3d 39 Bar::Meta::Class->create('Bar')
871e9eb5 40}, undef, '... Bar.meta => Bar::Meta::Class is compatible' );
4920ae3d 41
871e9eb5 42like( exception {
4920ae3d 43 Bar::Meta::Class->create('Foo::Foo', superclasses => ['Foo'])
871e9eb5 44}, qr/compatible/, '... Foo::Foo.meta => Bar::Meta::Class is not compatible' );
45like( exception {
4920ae3d 46 Foo::Meta::Class->create('Bar::Bar', superclasses => ['Bar'])
871e9eb5 47}, qr/compatible/, '... Bar::Bar.meta => Foo::Meta::Class is not compatible' );
4920ae3d 48
871e9eb5 49is( exception {
4920ae3d 50 FooBar::Meta::Class->create('FooBar', superclasses => ['Foo'])
871e9eb5 51}, undef, '... FooBar.meta => FooBar::Meta::Class is compatible' );
52is( exception {
4920ae3d 53 FooBar::Meta::Class->create('FooBar2', superclasses => ['Bar'])
871e9eb5 54}, undef, '... 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
871e9eb5 61like( exception {
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 )
871e9eb5 67}, qr/compatible/, 'incompatible Class metaclass' );
4920ae3d 68for my $suffix (keys %metaclass_attrs) {
871e9eb5 69 like( exception {
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 )
871e9eb5 76 }, qr/compatible/, "incompatible $suffix metaclass" );
4920ae3d 77}
78
79# fixing...
80
871e9eb5 81is( exception {
4920ae3d 82 Class::MOP::Class->create('Foo::Foo::CMOP', superclasses => ['Foo'])
871e9eb5 83}, undef, 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass' );
4920ae3d 84isa_ok(Foo::Foo::CMOP->meta, 'Foo::Meta::Class');
871e9eb5 85is( exception {
4920ae3d 86 Class::MOP::Class->create('Bar::Bar::CMOP', superclasses => ['Bar'])
871e9eb5 87}, undef, 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass' );
4920ae3d 88isa_ok(Bar::Bar::CMOP->meta, 'Bar::Meta::Class');
89
871e9eb5 90is( exception {
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 )
871e9eb5 96}, undef, '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) {
871e9eb5 100 is( exception {
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 )
871e9eb5 107 }, undef, "$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');
871e9eb5 191 like( exception { $meta->superclasses('Foo::Unsafe') }, qr/compatibility.*pristine/, "can't switch out the attribute metaclass of a class that already has attributes" );
4920ae3d 192}
550d56db 193
3a9318e6 194# immutability...
195
196{
197 my $foometa = Foo::Meta::Class->create(
198 'Foo::Immutable',
199 );
200 $foometa->make_immutable;
201 my $barmeta = Class::MOP::Class->create(
202 'Bar::Mutable',
203 );
204 my $bazmeta = Class::MOP::Class->create(
205 'Baz::Mutable',
206 );
207 $bazmeta->superclasses($foometa->name);
871e9eb5 208 is( exception { $bazmeta->superclasses($barmeta->name) }, undef, "can still set superclasses" );
3a9318e6 209 ok(!$bazmeta->is_immutable,
210 "immutable superclass doesn't make this class immutable");
871e9eb5 211 is( exception { $bazmeta->make_immutable }, undef, "can still make immutable" );
3a9318e6 212}
213
06ea51c7 214# nonexistent metaclasses
215
8b1cc359 216Class::MOP::Class->create(
217 'Weird::Meta::Method::Destructor',
218 superclasses => ['Class::MOP::Method'],
219);
06ea51c7 220
871e9eb5 221is( exception {
06ea51c7 222 Class::MOP::Class->create(
223 'Weird::Class',
224 destructor_class => 'Weird::Meta::Method::Destructor',
225 );
871e9eb5 226}, undef, "defined metaclass in child with defined metaclass in parent is fine" );
06ea51c7 227
228is(Weird::Class->meta->destructor_class, 'Weird::Meta::Method::Destructor',
229 "got the right destructor class");
230
871e9eb5 231is( exception {
06ea51c7 232 Class::MOP::Class->create(
233 'Weird::Class::Sub',
234 superclasses => ['Weird::Class'],
235 destructor_class => undef,
236 );
871e9eb5 237}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
06ea51c7 238
239is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
240 "got the right destructor class");
241
871e9eb5 242is( exception {
06ea51c7 243 Class::MOP::Class->create(
244 'Weird::Class::Sub2',
245 destructor_class => undef,
246 );
871e9eb5 247}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
06ea51c7 248
871e9eb5 249is( exception {
06ea51c7 250 Weird::Class::Sub2->meta->superclasses('Weird::Class');
871e9eb5 251}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
06ea51c7 252
253is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
254 "got the right destructor class");
255
86a4d873 256done_testing;