Passed
Pull Request — 4 (#9744)
by Garion
07:22
created

ClassInfoTest::provideHasMethodCases()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 37
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 28
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 37
rs 9.472
1
<?php
2
3
namespace SilverStripe\Core\Tests;
4
5
use DateTime;
6
use ReflectionException;
7
use SilverStripe\Core\ClassInfo;
8
use SilverStripe\Core\Tests\ClassInfoTest\BaseClass;
9
use SilverStripe\Core\Tests\ClassInfoTest\BaseDataClass;
10
use SilverStripe\Core\Tests\ClassInfoTest\BaseObject;
11
use SilverStripe\Core\Tests\ClassInfoTest\ChildClass;
12
use SilverStripe\Core\Tests\ClassInfoTest\ExtendTest;
13
use SilverStripe\Core\Tests\ClassInfoTest\ExtendTest2;
14
use SilverStripe\Core\Tests\ClassInfoTest\ExtendTest3;
15
use SilverStripe\Core\Tests\ClassInfoTest\ExtensionTest1;
16
use SilverStripe\Core\Tests\ClassInfoTest\ExtensionTest2;
17
use SilverStripe\Core\Tests\ClassInfoTest\GrandChildClass;
18
use SilverStripe\Core\Tests\ClassInfoTest\HasFields;
19
use SilverStripe\Core\Tests\ClassInfoTest\HasMethod;
20
use SilverStripe\Core\Tests\ClassInfoTest\NoFields;
21
use SilverStripe\Core\Tests\ClassInfoTest\WithCustomTable;
22
use SilverStripe\Core\Tests\ClassInfoTest\WithRelation;
23
use SilverStripe\Dev\SapphireTest;
24
use SilverStripe\ORM\DataObject;
25
use SilverStripe\View\ViewableData;
26
27
class ClassInfoTest extends SapphireTest
28
{
29
30
    protected static $extra_dataobjects = [
31
        BaseClass::class,
32
        BaseDataClass::class,
33
        ChildClass::class,
34
        GrandChildClass::class,
35
        HasFields::class,
36
        NoFields::class,
37
        WithCustomTable::class,
38
        WithRelation::class,
39
        BaseObject::class,
40
        ExtendTest::class,
41
        ExtendTest2::class,
42
        ExtendTest3::class,
43
    ];
44
45
    protected function setUp()
46
    {
47
        parent::setUp();
48
        ClassInfo::reset_db_cache();
49
    }
50
51
    public function testExists()
52
    {
53
        $this->assertTrue(ClassInfo::exists(ClassInfo::class));
54
        $this->assertTrue(ClassInfo::exists('SilverStripe\\Core\\classinfo'));
55
        $this->assertTrue(ClassInfo::exists('SilverStripe\\Core\\Tests\\ClassInfoTest'));
56
        $this->assertTrue(ClassInfo::exists('SilverStripe\\Core\\Tests\\CLASSINFOTEST'));
57
        $this->assertTrue(ClassInfo::exists('stdClass'));
58
        $this->assertTrue(ClassInfo::exists('stdCLASS'));
59
        $this->assertFalse(ClassInfo::exists('SomeNonExistantClass'));
60
    }
61
62
    public function testSubclassesFor()
63
    {
64
        $subclasses = [
65
            'silverstripe\\core\\tests\\classinfotest\\baseclass' => BaseClass::class,
66
            'silverstripe\\core\\tests\\classinfotest\\childclass' => ChildClass::class,
67
            'silverstripe\\core\\tests\\classinfotest\\grandchildclass' => GrandChildClass::class,
68
        ];
69
        $subclassesWithoutBase = [
70
            'silverstripe\\core\\tests\\classinfotest\\childclass' => ChildClass::class,
71
            'silverstripe\\core\\tests\\classinfotest\\grandchildclass' => GrandChildClass::class,
72
        ];
73
        $this->assertEquals(
74
            $subclasses,
75
            ClassInfo::subclassesFor(BaseClass::class),
76
            'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
77
        );
78
        ClassInfo::reset_db_cache();
79
        $this->assertEquals(
80
            $subclasses,
81
            ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass'),
82
            'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
83
        );
84
        ClassInfo::reset_db_cache();
85
        $this->assertEquals(
86
            $subclassesWithoutBase,
87
            ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass', false)
88
        );
89
    }
90
91
    public function testClassName()
92
    {
93
        $this->assertEquals(
94
            ClassInfoTest::class,
95
            ClassInfo::class_name($this)
96
        );
97
        $this->assertEquals(
98
            ClassInfoTest::class,
99
            ClassInfo::class_name('SilverStripe\\Core\\Tests\\ClassInfoTest')
100
        );
101
        $this->assertEquals(
102
            ClassInfoTest::class,
103
            ClassInfo::class_name('SilverStripe\\Core\\TESTS\\CLaSsInfOTEsT')
104
        );
105
    }
106
107
    public function testNonClassName()
108
    {
109
        $this->expectException(ReflectionException::class);
110
        $this->expectExceptionMessageRegExp('/Class "?IAmAClassThatDoesNotExist"? does not exist/');
111
        $this->assertEquals('IAmAClassThatDoesNotExist', ClassInfo::class_name('IAmAClassThatDoesNotExist'));
112
    }
113
114
    public function testClassesForFolder()
115
    {
116
        $classes = ClassInfo::classes_for_folder(ltrim(FRAMEWORK_DIR . '/tests', '/'));
117
        $this->assertArrayHasKey(
118
            'silverstripe\\core\\tests\\classinfotest',
119
            $classes,
120
            'ClassInfo::classes_for_folder() returns classes matching the filename'
121
        );
122
        $this->assertContains(
123
            ClassInfoTest::class,
124
            $classes,
125
            'ClassInfo::classes_for_folder() returns classes matching the filename'
126
        );
127
        $this->assertArrayHasKey(
128
            'silverstripe\\core\\tests\\classinfotest\\baseclass',
129
            $classes,
130
            'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
131
        );
132
        $this->assertContains(
133
            BaseClass::class,
134
            $classes,
135
            'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
136
        );
137
    }
138
139
    /**
140
     * @covers \SilverStripe\Core\ClassInfo::ancestry()
141
     */
142
    public function testAncestry()
143
    {
144
        $ancestry = ClassInfo::ancestry(ChildClass::class);
145
        $expect = [
146
            'silverstripe\\view\\viewabledata' => ViewableData::class,
147
            'silverstripe\\orm\\dataobject' => DataObject::class,
148
            'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class,
149
            'silverstripe\\core\tests\classinfotest\\childclass' => ChildClass::class,
150
        ];
151
        $this->assertEquals($expect, $ancestry);
152
153
        ClassInfo::reset_db_cache();
154
        $this->assertEquals(
155
            $expect,
156
            ClassInfo::ancestry('silverstripe\\core\\tests\\classINFOtest\\Childclass')
157
        );
158
159
        ClassInfo::reset_db_cache();
160
        $ancestry = ClassInfo::ancestry(ChildClass::class, true);
161
        $this->assertEquals(
162
            [
163
                'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class
164
            ],
165
            $ancestry,
166
            '$tablesOnly option excludes memory-only inheritance classes'
167
        );
168
    }
169
170
    /**
171
     * @covers \SilverStripe\Core\ClassInfo::dataClassesFor()
172
     */
173
    public function testDataClassesFor()
174
    {
175
        $expect = [
176
            'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
177
            'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
178
            'silverstripe\\core\\tests\\classinfotest\\withrelation' => WithRelation::class,
179
            'silverstripe\\core\\tests\\classinfotest\\withcustomtable' => WithCustomTable::class,
180
        ];
181
        $classes = [
182
            BaseDataClass::class,
183
            NoFields::class,
184
            HasFields::class,
185
        ];
186
187
        ClassInfo::reset_db_cache();
188
        $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[0]));
189
        ClassInfo::reset_db_cache();
190
        $this->assertEquals($expect, ClassInfo::dataClassesFor(strtoupper($classes[0])));
191
        ClassInfo::reset_db_cache();
192
        $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
193
194
        $expect = [
195
            'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
196
            'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
197
        ];
198
199
        ClassInfo::reset_db_cache();
200
        $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
201
        ClassInfo::reset_db_cache();
202
        $this->assertEquals($expect, ClassInfo::dataClassesFor(strtolower($classes[2])));
203
    }
204
205
    /**
206
     * @covers \SilverStripe\Core\ClassInfo::classesWithExtension()
207
     */
208
    public function testClassesWithExtensionUsingConfiguredExtensions()
209
    {
210
        $expect = [
211
            'silverstripe\\core\\tests\\classinfotest\\extendtest' => ExtendTest::class,
212
            'silverstripe\\core\\tests\\classinfotest\\extendtest2' => ExtendTest2::class,
213
            'silverstripe\\core\\tests\\classinfotest\\extendtest3' => ExtendTest3::class,
214
        ];
215
        $this->assertEquals(
216
            $expect,
217
            ClassInfo::classesWithExtension(ExtensionTest1::class, BaseObject::class),
218
            'ClassInfo::testClassesWithExtension() returns class with extensions applied via class config'
219
        );
220
221
        $expect = [
222
            'silverstripe\\core\\tests\\classinfotest\\extendtest' => ExtendTest::class,
223
            'silverstripe\\core\\tests\\classinfotest\\extendtest2' => ExtendTest2::class,
224
            'silverstripe\\core\\tests\\classinfotest\\extendtest3' => ExtendTest3::class,
225
        ];
226
        $this->assertEquals(
227
            $expect,
228
            ClassInfo::classesWithExtension(ExtensionTest1::class, ExtendTest::class, true),
229
            'ClassInfo::testClassesWithExtension() returns class with extensions applied via class config, including the base class'
230
        );
231
    }
232
233
    /**
234
     * @covers \SilverStripe\Core\ClassInfo::classesWithExtension()
235
     */
236
    public function testClassesWithExtensionUsingDynamicallyAddedExtensions()
237
    {
238
        $this->assertEquals(
239
            [],
240
            ClassInfo::classesWithExtension(ExtensionTest2::class, BaseObject::class),
241
            'ClassInfo::testClassesWithExtension() returns no classes for extension that hasn\'t been applied yet.'
242
        );
243
244
        ExtendTest::add_extension(ExtensionTest2::class);
245
246
        $expect = [
247
            'silverstripe\\core\\tests\\classinfotest\\extendtest2' => ExtendTest2::class,
248
            'silverstripe\\core\\tests\\classinfotest\\extendtest3' => ExtendTest3::class,
249
        ];
250
        $this->assertEquals(
251
            $expect,
252
            ClassInfo::classesWithExtension(ExtensionTest2::class, ExtendTest::class),
253
            'ClassInfo::testClassesWithExtension() returns class with extra extension dynamically added'
254
        );
255
    }
256
257
    /**
258
     * @covers \SilverStripe\Core\ClassInfo::classesWithExtension()
259
     */
260
    public function testClassesWithExtensionWithDynamicallyRemovedExtensions()
261
    {
262
        ExtendTest::remove_extension(ExtensionTest1::class);
263
264
        $this->assertEquals(
265
            [],
266
            ClassInfo::classesWithExtension(ExtensionTest1::class, BaseObject::class),
267
            'ClassInfo::testClassesWithExtension() returns no classes after an extension being removed'
268
        );
269
    }
270
271
    /** @dataProvider provideHasMethodCases */
272
    public function testHasMethod($object, $method, $output)
273
    {
274
        $this->assertEquals(
275
            $output,
276
            ClassInfo::hasMethod($object, $method)
277
        );
278
    }
279
280
    public function provideHasMethodCases()
281
    {
282
        return [
283
            'Basic object' => [
284
                new DateTime(),
285
                'format',
286
                true,
287
            ],
288
            'CustomMethod object' => [
289
                new HasMethod(),
290
                'example',
291
                true,
292
            ],
293
            'Class Name' => [
294
                'DateTime',
295
                'format',
296
                true,
297
            ],
298
            'FQCN' => [
299
                '\DateTime',
300
                'format',
301
                true,
302
            ],
303
            'Invalid FQCN' => [
304
                '--GreatTime',
305
                'format',
306
                false,
307
            ],
308
            'Integer' => [
309
                1,
310
                'format',
311
                false,
312
            ],
313
            'Array' => [
314
                ['\DateTime'],
315
                'format',
316
                false,
317
            ],
318
        ];
319
    }
320
321
    /** @dataProvider provideClassSpecCases */
322
    public function testParseClassSpec($input, $output)
323
    {
324
        $this->assertEquals(
325
            $output,
326
            ClassInfo::parse_class_spec($input)
327
        );
328
    }
329
330
    public function provideClassSpecCases()
331
    {
332
        return [
333
            'Standard class' => [
334
                'SimpleClass',
335
                ['SimpleClass', []],
336
            ],
337
            'Namespaced class' => [
338
                'Foo\\Bar\\NamespacedClass',
339
                ['Foo\\Bar\\NamespacedClass', []],
340
            ],
341
            'Namespaced class with service name' => [
342
                'Foo\\Bar\\NamespacedClass.withservicename',
343
                ['Foo\\Bar\\NamespacedClass.withservicename', []],
344
            ],
345
            'Namespaced class with argument' => [
346
                'Foo\\Bar\\NamespacedClass(["with-arg" => true])',
347
                ['Foo\\Bar\\NamespacedClass', [["with-arg" => true]]],
348
            ],
349
            'Namespaced class with service name and argument' => [
350
                'Foo\\Bar\\NamespacedClass.withmodifier(["and-arg" => true])',
351
                ['Foo\\Bar\\NamespacedClass.withmodifier', [["and-arg" => true]]],
352
            ],
353
        ];
354
    }
355
}
356