clean up some test stuff
[gitmo/Moose.git] / t / cmop / metaclass_incompatibility.t
CommitLineData
38bf2a25 1use strict;
2use warnings;
3
4use Test::More;
5use Test::Fatal;
6
7use metaclass;
8
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
17# meta classes
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
35is( exception {
36 Foo::Meta::Class->create('Foo')
37}, undef, '... Foo.meta => Foo::Meta::Class is compatible' );
38is( exception {
39 Bar::Meta::Class->create('Bar')
40}, undef, '... Bar.meta => Bar::Meta::Class is compatible' );
41
42like( exception {
43 Bar::Meta::Class->create('Foo::Foo', superclasses => ['Foo'])
44}, qr/compatible/, '... Foo::Foo.meta => Bar::Meta::Class is not compatible' );
45like( exception {
46 Foo::Meta::Class->create('Bar::Bar', superclasses => ['Bar'])
47}, qr/compatible/, '... Bar::Bar.meta => Foo::Meta::Class is not compatible' );
48
49is( exception {
50 FooBar::Meta::Class->create('FooBar', superclasses => ['Foo'])
51}, undef, '... FooBar.meta => FooBar::Meta::Class is compatible' );
52is( exception {
53 FooBar::Meta::Class->create('FooBar2', superclasses => ['Bar'])
54}, undef, '... FooBar2.meta => FooBar::Meta::Class is compatible' );
55
56Foo::Meta::Class->create(
57 'Foo::All',
58 map { $metaclass_attrs{$_} => "Foo::Meta::$_" } keys %metaclass_attrs,
59);
60
61like( exception {
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' );
68for my $suffix (keys %metaclass_attrs) {
69 like( exception {
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
81is( exception {
82 Class::MOP::Class->create('Foo::Foo::CMOP', superclasses => ['Foo'])
83}, undef, 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass' );
84isa_ok(Foo::Foo::CMOP->meta, 'Foo::Meta::Class');
85is( exception {
86 Class::MOP::Class->create('Bar::Bar::CMOP', superclasses => ['Bar'])
87}, undef, 'metaclass fixing fixes a cmop metaclass, when the parent has a subclass' );
88isa_ok(Bar::Bar::CMOP->meta, 'Bar::Meta::Class');
89
90is( exception {
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}, undef, 'metaclass fixing works with other non-default metaclasses' );
97isa_ok(Foo::All::Sub::CMOP::Class->meta, 'Foo::Meta::Class');
98
99for my $suffix (keys %metaclass_attrs) {
100 is( exception {
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 }, undef, "$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
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');
124
125{
126 package Foo::NoMeta2;
127}
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
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
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');
8813e6af 175{ local $TODO = 'No idea how to handle case where child class is created before parent';
38bf2a25 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
180# unsafe fixing...
181
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');
191 like( exception { $meta->superclasses('Foo::Unsafe') }, qr/compatibility.*pristine/, "can't switch out the attribute metaclass of a class that already has attributes" );
192}
193
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);
208 is( exception { $bazmeta->superclasses($barmeta->name) }, undef, "can still set superclasses" );
209 ok(!$bazmeta->is_immutable,
210 "immutable superclass doesn't make this class immutable");
211 is( exception { $bazmeta->make_immutable }, undef, "can still make immutable" );
212}
213
214# nonexistent metaclasses
215
216Class::MOP::Class->create(
217 'Weird::Meta::Method::Destructor',
218 superclasses => ['Class::MOP::Method'],
219);
220
221is( exception {
222 Class::MOP::Class->create(
223 'Weird::Class',
224 destructor_class => 'Weird::Meta::Method::Destructor',
225 );
226}, undef, "defined metaclass in child with defined metaclass in parent is fine" );
227
228is(Weird::Class->meta->destructor_class, 'Weird::Meta::Method::Destructor',
229 "got the right destructor class");
230
231is( exception {
232 Class::MOP::Class->create(
233 'Weird::Class::Sub',
234 superclasses => ['Weird::Class'],
235 destructor_class => undef,
236 );
237}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
238
239is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
240 "got the right destructor class");
241
242is( exception {
243 Class::MOP::Class->create(
244 'Weird::Class::Sub2',
245 destructor_class => undef,
246 );
247}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
248
249is( exception {
250 Weird::Class::Sub2->meta->superclasses('Weird::Class');
251}, undef, "undef metaclass in child with defined metaclass in parent can be fixed" );
252
253is(Weird::Class::Sub->meta->destructor_class, 'Weird::Meta::Method::Destructor',
254 "got the right destructor class");
255
256done_testing;