Completed
Push — authenticator-refactor ( 0a18bb...b9e528 )
by Simon
08:12
created

ObjectTest::testCacheToFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Tests;
4
5
use SilverStripe\Core\ClassInfo;
6
use SilverStripe\Core\Injector\Injector;
7
use SilverStripe\Core\Tests\ObjectTest\ExtendTest1;
8
use SilverStripe\Core\Tests\ObjectTest\ExtendTest2;
9
use SilverStripe\Core\Tests\ObjectTest\ExtendTest3;
10
use SilverStripe\Core\Tests\ObjectTest\ExtendTest4;
11
use SilverStripe\Core\Tests\ObjectTest\ExtensionRemoveTest;
12
use SilverStripe\Core\Tests\ObjectTest\ExtensionTest;
13
use SilverStripe\Core\Tests\ObjectTest\ExtensionTest2;
14
use SilverStripe\Core\Tests\ObjectTest\ExtensionTest3;
15
use SilverStripe\Core\Tests\ObjectTest\MyObject;
16
use SilverStripe\Core\Tests\ObjectTest\MySubObject;
17
use SilverStripe\Core\Tests\ObjectTest\TestExtension;
18
use SilverStripe\Dev\SapphireTest;
19
use SilverStripe\Control\Controller;
20
use SilverStripe\Versioned\Versioned;
21
22
/**
23
 * @todo tests for addStaticVars()
24
 * @todo tests for setting statics which are not defined on the object as built-in PHP statics
25
 * @todo tests for setting statics through extensions (#2387)
26
 */
27
class ObjectTest extends SapphireTest
28
{
29
30
    protected function setUp()
31
    {
32
        parent::setUp();
33
        Injector::inst()->unregisterAllObjects();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method unregisterAllObjects() does only exist in the following implementations of said interface: SilverStripe\Core\Injector\Injector.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
34
    }
35
36
    public function testHasmethodBehaviour()
37
    {
38
        $obj = new ObjectTest\ExtendTest();
39
40
        $this->assertTrue($obj->hasMethod('extendableMethod'), "Extension method found in original spelling");
41
        $this->assertTrue($obj->hasMethod('ExTendableMethod'), "Extension method found case-insensitive");
42
43
        $objs = array();
44
        $objs[] = new ObjectTest\T2();
45
        $objs[] = new ObjectTest\T2();
46
        $objs[] = new ObjectTest\T2();
47
48
        // All these methods should exist and return true
49
        $trueMethods = array('testMethod','otherMethod','someMethod','t1cMethod','normalMethod');
50
51
        foreach ($objs as $i => $obj) {
52
            foreach ($trueMethods as $method) {
53
                $methodU = strtoupper($method);
54
                $methodL = strtoupper($method);
55
                $this->assertTrue($obj->hasMethod($method), "Test that obj#$i has method $method");
56
                $this->assertTrue($obj->hasMethod($methodU), "Test that obj#$i has method $methodU");
57
                $this->assertTrue($obj->hasMethod($methodL), "Test that obj#$i has method $methodL");
58
59
                $this->assertTrue($obj->$method(), "Test that obj#$i can call method $method");
60
                $this->assertTrue($obj->$methodU(), "Test that obj#$i can call method $methodU");
61
                $this->assertTrue($obj->$methodL(), "Test that obj#$i can call method $methodL");
62
            }
63
64
            $this->assertTrue($obj->hasMethod('Wrapping'), "Test that obj#$i has method Wrapping");
65
            $this->assertTrue($obj->hasMethod('WRAPPING'), "Test that obj#$i has method WRAPPING");
66
            $this->assertTrue($obj->hasMethod('wrapping'), "Test that obj#$i has method wrapping");
67
68
            $this->assertEquals("Wrapping", $obj->Wrapping(), "Test that obj#$i can call method Wrapping");
69
            $this->assertEquals("Wrapping", $obj->WRAPPING(), "Test that obj#$i can call method WRAPPIGN");
70
            $this->assertEquals("Wrapping", $obj->wrapping(), "Test that obj#$i can call method wrapping");
71
        }
72
    }
73
74
    public function testSingletonCreation()
75
    {
76
        $myObject = MyObject::singleton();
77
        $this->assertInstanceOf(
78
            MyObject::class,
79
            $myObject,
80
            'singletons are creating a correct class instance'
81
        );
82
        $mySubObject = MySubObject::singleton();
83
        $this->assertInstanceOf(
84
            MySubObject::class,
85
            $mySubObject,
86
            'singletons are creating a correct subclass instance'
87
        );
88
89
        $myFirstObject = MyObject::singleton();
90
        $mySecondObject = MyObject::singleton();
91
        $this->assertTrue(
92
            $myFirstObject === $mySecondObject,
93
            'singletons are using the same object on subsequent calls'
94
        );
95
    }
96
97
    public function testStaticGetterMethod()
98
    {
99
        $obj = singleton(MyObject::class);
100
        $this->assertEquals(
101
            'MyObject',
102
            $obj->stat('mystaticProperty'),
103
            'Uninherited statics through stat() on a singleton behave the same as built-in PHP statics'
104
        );
105
    }
106
107
    public function testStaticInheritanceGetters()
108
    {
109
        $subObj = singleton(MyObject::class);
110
        $this->assertEquals(
111
            $subObj->stat('mystaticProperty'),
112
            'MyObject',
113
            'Statics defined on a parent class are available through stat() on a subclass'
114
        );
115
    }
116
117
    public function testStaticSettingOnSingletons()
118
    {
119
        $singleton1 = singleton(MyObject::class);
120
        $singleton2 = singleton(MyObject::class);
121
        $singleton1->set_stat('mystaticProperty', 'changed');
122
        $this->assertEquals(
123
            $singleton2->stat('mystaticProperty'),
124
            'changed',
125
            'Statics setting is populated throughout singletons without explicitly clearing cache'
126
        );
127
    }
128
129
    public function testStaticSettingOnInstances()
130
    {
131
        $instance1 = new ObjectTest\MyObject();
132
        $instance2 = new ObjectTest\MyObject();
133
        $instance1->set_stat('mystaticProperty', 'changed');
134
        $this->assertEquals(
135
            $instance2->stat('mystaticProperty'),
136
            'changed',
137
            'Statics setting through set_stat() is populated throughout instances without explicitly clearing cache'
138
        );
139
    }
140
141
    /**
142
     * Tests that {@link Object::create()} correctly passes all arguments to the new object
143
     */
144
    public function testCreateWithArgs()
145
    {
146
        $createdObj = ObjectTest\CreateTest::create('arg1', 'arg2', array(), null, 'arg5');
147
        $this->assertEquals($createdObj->constructArguments, array('arg1', 'arg2', array(), null, 'arg5'));
148
    }
149
150
    public function testCreateLateStaticBinding()
151
    {
152
        $createdObj = ObjectTest\CreateTest::create('arg1', 'arg2', array(), null, 'arg5');
153
        $this->assertEquals($createdObj->constructArguments, array('arg1', 'arg2', array(), null, 'arg5'));
154
    }
155
156
    /**
157
     * Tests {@link Object::singleton()}
158
     */
159
    public function testSingleton()
160
    {
161
        $inst = Controller::singleton();
162
        $this->assertInstanceOf(Controller::class, $inst);
163
        $inst2 = Controller::singleton();
164
        $this->assertSame($inst2, $inst);
165
    }
166
167
    public function testGetExtensions()
168
    {
169
        $this->assertEquals(
170
            array(
171
                'SilverStripe\\Core\\Tests\\oBjEcTTEST\\EXTENDTest1',
172
                "SilverStripe\\Core\\Tests\\ObjectTest\\ExtendTest2",
173
            ),
174
            ExtensionTest::get_extensions()
175
        );
176
        $this->assertEquals(
177
            array(
178
                'SilverStripe\\Core\\Tests\\oBjEcTTEST\\EXTENDTest1',
179
                "SilverStripe\\Core\\Tests\\ObjectTest\\ExtendTest2('FOO', 'BAR')",
180
            ),
181
            ExtensionTest::get_extensions(null, true)
182
        );
183
        $inst = new ExtensionTest();
184
        $extensions = $inst->getExtensionInstances();
185
        $this->assertEquals(count($extensions), 2);
186
        $this->assertInstanceOf(
187
            ExtendTest1::class,
188
            $extensions[ExtendTest1::class]
189
        );
190
        $this->assertInstanceOf(
191
            ExtendTest2::class,
192
            $extensions[ExtendTest2::class]
193
        );
194
        $this->assertInstanceOf(
195
            ExtendTest1::class,
196
            $inst->getExtensionInstance(ExtendTest1::class)
197
        );
198
        $this->assertInstanceOf(
199
            ExtendTest2::class,
200
            $inst->getExtensionInstance(ExtendTest2::class)
201
        );
202
    }
203
204
    /**
205
     * Tests {@link Object::has_extension()}, {@link Object::add_extension()}
206
     */
207
    public function testHasAndAddExtension()
208
    {
209
        // ObjectTest_ExtendTest1 is built in via $extensions
210
        $this->assertTrue(
211
            ExtensionTest::has_extension('SilverStripe\\Core\\Tests\\oBjEcTTEST\\EXTENDTest1'),
212
            "Extensions are detected when set on Object::\$extensions on has_extension() without case-sensitivity"
213
        );
214
        $this->assertTrue(
215
            ExtensionTest::has_extension(ExtendTest1::class),
216
            "Extensions are detected when set on Object::\$extensions on has_extension() without case-sensitivity"
217
        );
218
        $this->assertTrue(
219
            singleton(ExtensionTest::class)->hasExtension(ExtendTest1::class),
220
            "Extensions are detected when set on Object::\$extensions on instance hasExtension() without"
221
                . " case-sensitivity"
222
        );
223
224
        // ObjectTest_ExtendTest2 is built in via $extensions (with parameters)
225
        $this->assertTrue(
226
            ExtensionTest::has_extension(ExtendTest2::class),
227
            "Extensions are detected with static has_extension() when set on Object::\$extensions with"
228
                . " additional parameters"
229
        );
230
        $this->assertTrue(
231
            singleton(ExtensionTest::class)->hasExtension(ExtendTest2::class),
232
            "Extensions are detected with instance hasExtension() when set on Object::\$extensions with"
233
                . " additional parameters"
234
        );
235
        $this->assertFalse(
236
            ExtensionTest::has_extension(ExtendTest3::class),
237
            "Other extensions available in the system are not present unless explicitly added to this object"
238
                . " when checking through has_extension()"
239
        );
240
        $this->assertFalse(
241
            singleton(ExtensionTest::class)->hasExtension(ExtendTest3::class),
242
            "Other extensions available in the system are not present unless explicitly added to this object"
243
                . " when checking through instance hasExtension()"
244
        );
245
246
        // ObjectTest_ExtendTest3 is added manually
247
        ExtensionTest::add_extension(ExtendTest3::class .'("Param")');
248
        $this->assertTrue(
249
            ExtensionTest::has_extension(ExtendTest3::class),
250
            "Extensions are detected with static has_extension() when added through add_extension()"
251
        );
252
        // ExtendTest4 is added manually
253
        ExtensionTest3::add_extension(ExtendTest4::class . '("Param")');
254
        // test against ObjectTest_ExtendTest3, not ObjectTest_ExtendTest3
255
        $this->assertTrue(
256
            ExtensionTest3::has_extension(ExtendTest4::class),
257
            "Extensions are detected with static has_extension() when added through add_extension()"
258
        );
259
        // test against ObjectTest_ExtendTest3, not ExtendTest4 to test if it picks up
260
        // the sub classes of ObjectTest_ExtendTest3
261
        $this->assertTrue(
262
            ExtensionTest3::has_extension(ExtendTest3::class),
263
            "Sub-Extensions are detected with static has_extension() when added through add_extension()"
264
        );
265
        // strictly test against ObjectTest_ExtendTest3, not ExtendTest4 to test if it picks up
266
        // the sub classes of ObjectTest_ExtendTest3
267
        $this->assertFalse(
268
            ExtensionTest3::has_extension(ExtendTest3::class, null, true),
269
            "Sub-Extensions are detected with static has_extension() when added through add_extension()"
270
        );
271
        // a singleton() wouldn't work as its already initialized
272
        $objectTest_ExtensionTest = new ExtensionTest();
273
        $this->assertTrue(
274
            $objectTest_ExtensionTest->hasExtension(ExtendTest3::class),
275
            "Extensions are detected with instance hasExtension() when added through add_extension()"
276
        );
277
278
        // @todo At the moment, this does NOT remove the extension due to parameterized naming,
279
        //  meaning the extension will remain added in further test cases
280
        ExtensionTest::remove_extension(ExtendTest3::class);
281
    }
282
283
    public function testRemoveExtension()
284
    {
285
        // manually add ObjectTest_ExtendTest2
286
        ObjectTest\ExtensionRemoveTest::add_extension(ExtendTest2::class);
287
        $this->assertTrue(
288
            ObjectTest\ExtensionRemoveTest::has_extension(ExtendTest2::class),
289
            "Extension added through \$add_extension() are added correctly"
290
        );
291
292
        ObjectTest\ExtensionRemoveTest::remove_extension(ExtendTest2::class);
293
        $this->assertFalse(
294
            ObjectTest\ExtensionRemoveTest::has_extension(ExtendTest2::class),
295
            "Extension added through \$add_extension() are detected as removed in has_extension()"
296
        );
297
        $this->assertFalse(
298
            singleton(ExtensionRemoveTest::class)->hasExtension(ExtendTest2::class),
299
            "Extensions added through \$add_extension() are detected as removed in instances through hasExtension()"
300
        );
301
302
        // ObjectTest_ExtendTest1 is already present in $extensions
303
        ObjectTest\ExtensionRemoveTest::remove_extension(ExtendTest1::class);
304
305
        $this->assertFalse(
306
            ObjectTest\ExtensionRemoveTest::has_extension(ExtendTest1::class),
307
            "Extension added through \$extensions are detected as removed in has_extension()"
308
        );
309
310
        $objectTest_ExtensionRemoveTest = new ObjectTest\ExtensionRemoveTest();
311
        $this->assertFalse(
312
            $objectTest_ExtensionRemoveTest->hasExtension(ExtendTest1::class),
313
            "Extensions added through \$extensions are detected as removed in instances through hasExtension()"
314
        );
315
    }
316
317
    public function testRemoveExtensionWithParameters()
318
    {
319
        ObjectTest\ExtensionRemoveTest::add_extension(ExtendTest2::class.'("MyParam")');
320
321
        $this->assertTrue(
322
            ObjectTest\ExtensionRemoveTest::has_extension(ExtendTest2::class),
323
            "Extension added through \$add_extension() are added correctly"
324
        );
325
326
        ObjectTest\ExtensionRemoveTest::remove_extension(ExtendTest2::class);
327
        $this->assertFalse(
328
            ExtensionRemoveTest::has_extension(ExtendTest2::class),
329
            "Extension added through \$add_extension() are detected as removed in has_extension()"
330
        );
331
332
        $objectTest_ExtensionRemoveTest = new ObjectTest\ExtensionRemoveTest();
333
        $this->assertFalse(
334
            $objectTest_ExtensionRemoveTest->hasExtension(ExtendTest2::class),
335
            "Extensions added through \$extensions are detected as removed in instances through hasExtension()"
336
        );
337
    }
338
339
    public function testIsA()
340
    {
341
        $this->assertTrue(ObjectTest\MyObject::create() instanceof ObjectTest\BaseObject);
342
        $this->assertTrue(ObjectTest\MyObject::create() instanceof ObjectTest\MyObject);
343
    }
344
345
    /**
346
     * Tests {@link Object::hasExtension() and Object::getExtensionInstance()}
347
     */
348
    public function testExtInstance()
349
    {
350
        $obj = new ExtensionTest2();
351
352
        $this->assertTrue($obj->hasExtension(TestExtension::class));
353
        $this->assertTrue($obj->getExtensionInstance(TestExtension::class) instanceof ObjectTest\TestExtension);
354
    }
355
356
    public function testExtend()
357
    {
358
        $object   = new ObjectTest\ExtendTest();
359
        $argument = 'test';
360
361
        $this->assertEquals($object->extend('extendableMethod'), array('ExtendTest2()'));
362
        $this->assertEquals($object->extend('extendableMethod', $argument), array('ExtendTest2(modified)'));
363
        $this->assertEquals($argument, 'modified');
364
365
        $this->assertEquals(
366
            array('ExtendTest()', 'ExtendTest2()'),
367
            $object->invokeWithExtensions('extendableMethod')
368
        );
369
        $arg1 = 'test';
370
        $arg2 = 'bob';
371
        $this->assertEquals(
372
            array('ExtendTest(test,bob)', 'ExtendTest2(modified,objectmodified)'),
373
            $object->invokeWithExtensions('extendableMethod', $arg1, $arg2)
374
        );
375
        $this->assertEquals('modified', $arg1);
376
        $this->assertEquals('objectmodified', $arg2);
377
378
        $object2 = new ObjectTest\Extending();
379
        $first = 1;
380
        $second = 2;
381
        $third = 3;
382
        $result = $object2->getResults($first, $second, $third);
383
        $this->assertEquals(
384
            array(array('before', 'extension', 'after')),
385
            $result
386
        );
387
        $this->assertEquals(31, $first);
388
        $this->assertEquals(32, $second);
389
        $this->assertEquals(33, $third);
390
    }
391
392
    public function testParseClassSpec()
393
    {
394
        // Simple case
395
        $this->assertEquals(
396
            array(Versioned::class,array('Stage', 'Live')),
397
            ClassInfo::parse_class_spec("SilverStripe\\Versioned\\Versioned('Stage','Live')")
398
        );
399
        // String with commas
400
        $this->assertEquals(
401
            array(Versioned::class,array('Stage,Live', 'Stage')),
402
            ClassInfo::parse_class_spec("SilverStripe\\Versioned\\Versioned('Stage,Live','Stage')")
403
        );
404
        // String with quotes
405
        $this->assertEquals(
406
            array(Versioned::class,array('Stage\'Stage,Live\'Live', 'Live')),
407
            ClassInfo::parse_class_spec("SilverStripe\\Versioned\\Versioned('Stage\\'Stage,Live\\'Live','Live')")
408
        );
409
410
        // True, false and null values
411
        $this->assertEquals(
412
            array('ClassName', array('string', true, array('string', false))),
413
            ClassInfo::parse_class_spec('ClassName("string", true, array("string", false))')
414
        );
415
        $this->assertEquals(
416
            array('ClassName', array(true, false, null)),
417
            ClassInfo::parse_class_spec('ClassName(true, false, null)')
418
        );
419
420
        // Array
421
        $this->assertEquals(
422
            array('Enum',array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')),
423
            ClassInfo::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')")
424
        );
425
        // Nested array
426
        $this->assertEquals(
427
            array('Enum',array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')),
428
                'Unsubmitted')),
429
            ClassInfo::parse_class_spec(
430
                "Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')"
431
            )
432
        );
433
        // 5.4 Shorthand Array
434
        $this->assertEquals(
435
            array('Enum',array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')),
436
            ClassInfo::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', 'Unsubmitted'], 'Unsubmitted')")
437
        );
438
        // 5.4 Nested shorthand array
439
        $this->assertEquals(
440
            array('Enum',array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')),
441
                'Unsubmitted')),
442
            ClassInfo::parse_class_spec(
443
                "Enum(['Accepted', 'Pending', 'Declined', ['UnsubmittedA','UnsubmittedB']], 'Unsubmitted')"
444
            )
445
        );
446
447
        // Associative array
448
        $this->assertEquals(
449
            array('Varchar', array(255, array('nullifyEmpty' => false))),
450
            ClassInfo::parse_class_spec("Varchar(255, array('nullifyEmpty' => false))")
451
        );
452
        // Nested associative array
453
        $this->assertEquals(
454
            array('Test', array('string', array('nested' => array('foo' => 'bar')))),
455
            ClassInfo::parse_class_spec("Test('string', array('nested' => array('foo' => 'bar')))")
456
        );
457
        // 5.4 shorthand associative array
458
        $this->assertEquals(
459
            array('Varchar', array(255, array('nullifyEmpty' => false))),
460
            ClassInfo::parse_class_spec("Varchar(255, ['nullifyEmpty' => false])")
461
        );
462
        // 5.4 shorthand nested associative array
463
        $this->assertEquals(
464
            array('Test', array('string', array('nested' => array('foo' => 'bar')))),
465
            ClassInfo::parse_class_spec("Test('string', ['nested' => ['foo' => 'bar']])")
466
        );
467
468
        // Namespaced class
469
        $this->assertEquals(
470
            array('Test\MyClass', array()),
471
            ClassInfo::parse_class_spec('Test\MyClass')
472
        );
473
        // Fully qualified namespaced class
474
        $this->assertEquals(
475
            array('\Test\MyClass', array()),
476
            ClassInfo::parse_class_spec('\Test\MyClass')
477
        );
478
    }
479
}
480