Completed
Pull Request — master (#615)
by Asmir
06:59 queued 03:47
created

JMSSerializerExtensionTest::testLoad()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 19
nc 1
nop 0
1
<?php
2
3
/*
4
 * Copyright 2011 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace JMS\SerializerBundle\Tests\DependencyInjection;
20
21
use Doctrine\Common\Annotations\AnnotationReader;
22
use JMS\Serializer\SerializationContext;
23
use JMS\SerializerBundle\JMSSerializerBundle;
24
use JMS\SerializerBundle\Tests\DependencyInjection\Fixture\ObjectUsingExpressionLanguage;
25
use JMS\SerializerBundle\Tests\DependencyInjection\Fixture\ObjectUsingExpressionProperties;
26
use JMS\SerializerBundle\Tests\DependencyInjection\Fixture\SimpleObject;
27
use JMS\SerializerBundle\Tests\DependencyInjection\Fixture\VersionedObject;
28
use PHPUnit\Framework\TestCase;
29
use Symfony\Component\DependencyInjection\ContainerBuilder;
30
use Symfony\Component\DependencyInjection\Definition;
31
32
class JMSSerializerExtensionTest extends TestCase
33
{
34
    protected function setUp()
35
    {
36
        $this->clearTempDir();
37
    }
38
39
    protected function tearDown()
40
    {
41
        $this->clearTempDir();
42
    }
43
44
    private function clearTempDir()
45
    {
46
        // clear temporary directory
47
        $dir = sys_get_temp_dir() . '/serializer';
48
        if (is_dir($dir)) {
49
            foreach (new \RecursiveDirectoryIterator($dir) as $file) {
50
                $filename = $file->getFileName();
51
                if ('.' === $filename || '..' === $filename) {
52
                    continue;
53
                }
54
55
                @unlink($file->getPathName());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
56
            }
57
58
            @rmdir($dir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
59
        }
60
    }
61
62
    public function testHasContextFactories()
63
    {
64
        $container = $this->getContainerForConfig(array(array()));
65
66
        $factory = $container->get('jms_serializer.serialization_context_factory');
67
        $this->assertInstanceOf('JMS\Serializer\ContextFactory\SerializationContextFactoryInterface', $factory);
68
69
        $factory = $container->get('jms_serializer.deserialization_context_factory');
70
        $this->assertInstanceOf('JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface', $factory);
71
    }
72
73
    public function testSerializerContextFactoriesAreSet()
74
    {
75
        $container = $this->getContainerForConfig(array(array()));
76
77
        $def = $container->getDefinition('jms_serializer');
78
        $calls = $def->getMethodCalls();
79
80
        $this->assertCount(2, $calls);
81
82
        $serializationCall = $calls[0];
83
        $this->assertEquals('setSerializationContextFactory', $serializationCall[0]);
84
        $this->assertEquals('jms_serializer.serialization_context_factory', (string)$serializationCall[1][0]);
85
86
        $serializationCall = $calls[1];
87
        $this->assertEquals('setDeserializationContextFactory', $serializationCall[0]);
88
        $this->assertEquals('jms_serializer.deserialization_context_factory', (string)$serializationCall[1][0]);
89
    }
90
91
    public function testSerializerContextFactoriesWithId()
92
    {
93
        $config = array(
94
            'default_context' => array(
95
                'serialization' => array(
96
                    'id' => 'foo'
97
                ),
98
                'deserialization' => array(
99
                    'id' => 'bar'
100
                )
101
            )
102
        );
103
104
        $container = $this->getContainerForConfig(array($config), function(ContainerBuilder $containerBuilder){
105
            $containerBuilder->setDefinition('foo', new Definition('stdClass'));
106
            $containerBuilder->setDefinition('bar', new Definition('stdClass'));
107
        });
108
109
        $def = $container->getDefinition('jms_serializer');
110
        $calls = $def->getMethodCalls();
111
112
        $this->assertCount(2, $calls);
113
114
        $serializationCall = $calls[0];
115
        $this->assertEquals('setSerializationContextFactory', $serializationCall[0]);
116
        $this->assertEquals('foo', (string)$serializationCall[1][0]);
117
118
        $serializationCall = $calls[1];
119
        $this->assertEquals('setDeserializationContextFactory', $serializationCall[0]);
120
        $this->assertEquals('bar', (string)$serializationCall[1][0]);
121
122
        $this->assertEquals('bar', (string)$container->getAlias('jms_serializer.deserialization_context_factory'));
123
        $this->assertEquals('foo', (string)$container->getAlias('jms_serializer.serialization_context_factory'));
124
    }
125
126
    public function testLoadWithoutTranslator()
127
    {
128
        $container = $this->getContainerForConfig(array(array()), function(ContainerBuilder $containerBuilder){
129
            $containerBuilder->set('translator', null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
130
        });
131
132
        $def = $container->getDefinition('jms_serializer.form_error_handler');
133
        $this->assertSame(null, $def->getArgument(0));
134
    }
135
136
    public function testConfiguringContextFactories()
137
    {
138
        $container = $this->getContainerForConfig(array(array()));
139
140
        $def = $container->getDefinition('jms_serializer.serialization_context_factory');
141
        $this->assertCount(0, $def->getMethodCalls());
142
143
        $def = $container->getDefinition('jms_serializer.deserialization_context_factory');
144
        $this->assertCount(0, $def->getMethodCalls());
145
    }
146
147
    public function testConfiguringContextFactoriesWithParams()
148
    {
149
        $config = array(
150
            'default_context' => array(
151
                'serialization' => array(
152
                    'version' => 1600,
153
                    'serialize_null' => true,
154
                    'attributes' => array('x' => 1720),
155
                    'groups' => array('Default', 'Registration'),
156
                    'enable_max_depth_checks' => true,
157
                ),
158
                'deserialization' => array(
159
                    'version' => 1640,
160
                    'serialize_null' => false,
161
                    'attributes' => array('x' => 1740),
162
                    'groups' => array('Default', 'Profile'),
163
                    'enable_max_depth_checks' => true,
164
                )
165
            )
166
        );
167
168
        $container = $this->getContainerForConfig(array($config));
169
        $services = [
170
            'serialization' => 'jms_serializer.serialization_context_factory',
171
            'deserialization' => 'jms_serializer.deserialization_context_factory',
172
        ];
173
        foreach ($services as $configKey => $serviceId) {
174
            $def = $container->getDefinition($serviceId);
175
            $values = $config['default_context'][$configKey];
176
177
            $this->assertSame($values['version'], $this->getDefinitionMethodCall($def, 'setVersion')[0]);
178
            $this->assertSame($values['serialize_null'], $this->getDefinitionMethodCall($def, 'setSerializeNulls')[0]);
179
            $this->assertSame($values['attributes'], $this->getDefinitionMethodCall($def, 'setAttributes')[0]);
180
            $this->assertSame($values['groups'], $this->getDefinitionMethodCall($def, 'setGroups')[0]);
181
            $this->assertSame($values['groups'], $this->getDefinitionMethodCall($def, 'setGroups')[0]);
182
            $this->assertSame(array(), $this->getDefinitionMethodCall($def, 'enableMaxDepthChecks'));
183
        }
184
    }
185
186
    public function testConfiguringContextFactoriesWithNullDefaults()
187
    {
188
        $config = array(
189
            'default_context' => array(
190
                'serialization' => array(
191
                    'version' => null,
192
                    'serialize_null' => null,
193
                    'attributes' => [],
194
                    'groups' => null,
195
                ),
196
                'deserialization' => array(
197
                    'version' => null,
198
                    'serialize_null' => null,
199
                    'attributes' => null,
200
                    'groups' => null,
201
                )
202
            )
203
        );
204
205
        $container = $this->getContainerForConfig(array($config));
206
        $services = [
207
            'serialization' => 'jms_serializer.serialization_context_factory',
208
            'deserialization' => 'jms_serializer.deserialization_context_factory',
209
        ];
210
        foreach ($services as $configKey => $serviceId) {
211
            $def = $container->getDefinition($serviceId);
212
            $this->assertCount(0, $def->getMethodCalls());
213
        }
214
    }
215
216
    private function getDefinitionMethodCall(Definition $def, $method)
217
    {
218
        foreach ($def->getMethodCalls() as $call) {
219
            if ($call[0] === $method) {
220
                return $call[1];
221
            }
222
        }
223
        return false;
224
    }
225
226
    public function testLoad()
227
    {
228
        $container = $this->getContainerForConfig(array(array()), function(ContainerBuilder $container) {
229
            $container->getDefinition('jms_serializer.doctrine_object_constructor')->setPublic(true);
230
            $container->getAlias('JMS\Serializer\SerializerInterface')->setPublic(true);
231
            $container->getAlias('JMS\Serializer\ArrayTransformerInterface')->setPublic(true);
232
        });
233
234
        $simpleObject = new SimpleObject('foo', 'bar');
235
        $versionedObject = new VersionedObject('foo', 'bar');
236
        $serializer = $container->get('jms_serializer');
237
238
        $this->assertTrue($container->has('JMS\Serializer\SerializerInterface'), 'Alias should be defined to allow autowiring');
239
        $this->assertTrue($container->has('JMS\Serializer\ArrayTransformerInterface'), 'Alias should be defined to allow autowiring');
240
241
        $this->assertFalse($container->getDefinition('jms_serializer.array_collection_handler')->getArgument(0));
242
243
        // the logic is inverted because arg 0 on doctrine_proxy_subscriber is $skipVirtualTypeInit = false
244
        $this->assertTrue($container->getDefinition('jms_serializer.doctrine_proxy_subscriber')->getArgument(0));
245
        $this->assertFalse($container->getDefinition('jms_serializer.doctrine_proxy_subscriber')->getArgument(1));
246
247
        $this->assertEquals("null", $container->getDefinition('jms_serializer.doctrine_object_constructor')->getArgument(2));
248
249
        // test that all components have been wired correctly
250
        $this->assertEquals(json_encode(array('name' => 'bar')), $serializer->serialize($versionedObject, 'json'));
251
        $this->assertEquals($simpleObject, $serializer->deserialize($serializer->serialize($simpleObject, 'json'), get_class($simpleObject), 'json'));
252
        $this->assertEquals($simpleObject, $serializer->deserialize($serializer->serialize($simpleObject, 'xml'), get_class($simpleObject), 'xml'));
253
254
        $this->assertEquals(json_encode(array('name' => 'foo')), $serializer->serialize($versionedObject, 'json', SerializationContext::create()->setVersion('0.0.1')));
255
256
        $this->assertEquals(json_encode(array('name' => 'bar')), $serializer->serialize($versionedObject, 'json', SerializationContext::create()->setVersion('1.1.1')));
257
    }
258
259
    public function testLoadWithOptions()
260
    {
261
        $container = $this->getContainerForConfig(array(array(
262
            'subscribers' => [
263
                'doctrine_proxy' => [
264
                    'initialize_virtual_types' => true,
265
                    'initialize_excluded' => true,
266
                ],
267
            ],
268
            'object_constructors' => [
269
                'doctrine' => [
270
                    'fallback_strategy' => "exception",
271
                ],
272
            ],
273
            'handlers' => [
274
                'array_collection' => [
275
                    'initialize_excluded' => true,
276
                ],
277
            ],
278
        )), function($container){
279
            $container->getDefinition('jms_serializer.doctrine_object_constructor')->setPublic(true);
280
        });
281
282
        $this->assertTrue($container->getDefinition('jms_serializer.array_collection_handler')->getArgument(0));
283
284
        // the logic is inverted because arg 0 on doctrine_proxy_subscriber is $skipVirtualTypeInit = false
285
        $this->assertFalse($container->getDefinition('jms_serializer.doctrine_proxy_subscriber')->getArgument(0));
286
        $this->assertTrue($container->getDefinition('jms_serializer.doctrine_proxy_subscriber')->getArgument(1));
287
288
        $this->assertEquals("exception", $container->getDefinition('jms_serializer.doctrine_object_constructor')->getArgument(2));
289
    }
290
291
    public function testLoadExistentMetadataDir()
292
    {
293
        $container = $this->getContainerForConfig(array(array(
294
            'metadata' => [
295
                'directories' => [
296
                    'foo' => [
297
                        'namespace_prefix' => 'foo_ns',
298
                        'path' => __DIR__,
299
                    ]
300
                ]
301
            ]
302
        )), function ($container){
303
            $container->getDefinition('jms_serializer.metadata.file_locator')->setPublic(true);
304
        });
305
306
        $fileLocatorDef = $container->getDefinition('jms_serializer.metadata.file_locator');
307
        $directories = $fileLocatorDef->getArgument(0);
308
        $this->assertEquals(['foo_ns' => __DIR__], $directories);
309
    }
310
311
    public function testWarmUpWithDirs()
312
    {
313
        $container = $this->getContainerForConfig([[
314
            'metadata' => [
315
                'warmup' => [
316
                    'paths' => [
317
                        'included' => ['a'],
318
                        'excluded' => ['b']
319
                    ]
320
                ]
321
            ]
322
        ]], function ($container){
323
            $container->getDefinition('jms_serializer.cache.cache_warmer')->setPublic(true);
324
        });
325
326
        $this->assertTrue($container->hasDefinition('jms_serializer.cache.cache_warmer'));
327
328
        $def = $container->getDefinition('jms_serializer.cache.cache_warmer');
329
330
        $this->assertEquals(['a'], $def->getArgument(0));
331
        $this->assertEquals(['b'], $def->getArgument(2));
332
    }
333
334
    public function testWarmUpWithDirsWithNoPaths()
335
    {
336
        $this->getContainerForConfig([[]], function ($container) {
337
            $this->assertFalse($container->hasDefinition('jms_serializer.cache.cache_warmer'));
338
        });
339
    }
340
341
342
    /**
343
     * @expectedException \JMS\Serializer\Exception\RuntimeException
344
     * @expectedExceptionMessage  The metadata directory "foo_dir" does not exist for the namespace "foo_ns"
345
     */
346
    public function testLoadNotExistentMetadataDir()
347
    {
348
        $this->getContainerForConfig(array(array(
349
            'metadata' => [
350
                'directories' => [
351
                    'foo' => [
352
                        'namespace_prefix' => 'foo_ns',
353
                        'path' => 'foo_dir',
354
                    ]
355
                ]
356
            ]
357
        )));
358
    }
359
360
    /**
361
     * @dataProvider getJsonVisitorConfigs
362
     */
363
    public function testJsonVisitorOptions($expectedOptions, $config)
364
    {
365
        $container = $this->getContainerForConfig(array($config));
366
        $this->assertSame($expectedOptions, $container->get('jms_serializer.json_serialization_visitor')->getOptions());
367
    }
368
369
    public function getJsonVisitorConfigs()
370
    {
371
        $configs = array();
372
373
        if (version_compare(PHP_VERSION, '5.4', '>=')) {
374
            $configs[] = array(JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT, array(
375
                'visitors' => array(
376
                    'json' => array(
377
                        'options' => array('JSON_UNESCAPED_UNICODE', 'JSON_PRETTY_PRINT')
378
                    )
379
                )
380
            ));
381
382
            $configs[] = array(JSON_UNESCAPED_UNICODE, array(
383
                'visitors' => array(
384
                    'json' => array(
385
                        'options' => 'JSON_UNESCAPED_UNICODE'
386
                    )
387
                )
388
            ));
389
        }
390
391
        $configs[] = array(128, array(
392
            'visitors' => array(
393
                'json' => array(
394
                    'options' => 128
395
                )
396
            )
397
        ));
398
399
        $configs[] = array(0, array());
400
401
        return $configs;
402
    }
403
404
    public function testExpressionLanguage()
405
    {
406
        if (!interface_exists('Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface')) {
407
            $this->markTestSkipped("The Symfony Expression Language is not available");
408
        }
409
        $container = $this->getContainerForConfig(array(array()));
410
        $serializer = $container->get('jms_serializer');
411
        // test that all components have been wired correctly
412
        $object = new ObjectUsingExpressionLanguage('foo', true);
413
        $this->assertEquals('{"name":"foo"}', $serializer->serialize($object, 'json'));
414
        $object = new ObjectUsingExpressionLanguage('foo', false);
415
        $this->assertEquals('{}', $serializer->serialize($object, 'json'));
416
    }
417
418
    public function testExpressionLanguageVirtualProperties()
419
    {
420
        if (!interface_exists('Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface')) {
421
            $this->markTestSkipped("The Symfony Expression Language is not available");
422
        }
423
        $container = $this->getContainerForConfig(array(array()));
424
        $serializer = $container->get('jms_serializer');
425
        // test that all components have been wired correctly
426
        $object = new ObjectUsingExpressionProperties('foo');
427
        $this->assertEquals('{"v_prop_name":"foo"}', $serializer->serialize($object, 'json'));
428
    }
429
430
    /**
431
     * @expectedException \JMS\Serializer\Exception\ExpressionLanguageRequiredException
432
     */
433
    public function testExpressionLanguageDisabledVirtualProperties()
434
    {
435
        if (!interface_exists('Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface')) {
436
            $this->markTestSkipped("The Symfony Expression Language is not available");
437
        }
438
        $container = $this->getContainerForConfig(array(array('expression_evaluator' => array('id' => null))));
439
        $serializer = $container->get('jms_serializer');
440
        // test that all components have been wired correctly
441
        $object = new ObjectUsingExpressionProperties('foo');
442
        $serializer->serialize($object, 'json');
443
    }
444
445
    /**
446
     * @expectedException \JMS\Serializer\Exception\ExpressionLanguageRequiredException
447
     * @expectedExceptionMessage  To use conditional exclude/expose in JMS\SerializerBundle\Tests\DependencyInjection\Fixture\ObjectUsingExpressionLanguage you must configure the expression language.
448
     */
449
    public function testExpressionLanguageNotLoaded()
450
    {
451
        $container = $this->getContainerForConfig(array(array('expression_evaluator' => array('id' => null))));
452
        $serializer = $container->get('jms_serializer');
453
        // test that all components have been wired correctly
454
        $object = new ObjectUsingExpressionLanguage('foo', true);
455
        $serializer->serialize($object, 'json');
456
    }
457
458
    /**
459
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
460
     * @expectedExceptionMessage Invalid configuration for path "jms_serializer.expression_evaluator.id": You need at least symfony/expression language v2.6 or v3.0 to use the expression evaluator features
461
     */
462
    public function testExpressionInvalidEvaluator()
463
    {
464
        if (interface_exists('Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface')) {
465
            $this->markTestSkipped('To pass this test the "symfony/expression-language" component should be available');
466
        }
467
        $this->getContainerForConfig(array(array('expression_evaluator' => array('id' => 'foo'))));
468
    }
469
470
    /**
471
     * @dataProvider getXmlVisitorWhitelists
472
     */
473
    public function testXmlVisitorOptions($expectedOptions, $config)
474
    {
475
        $container = $this->getContainerForConfig(array($config));
476
        $this->assertSame($expectedOptions, $container->get('jms_serializer.xml_deserialization_visitor')->getDoctypeWhitelist());
477
    }
478
479
    public function getXmlVisitorWhitelists()
480
    {
481
        $configs = array();
482
483
        $configs[] = array(array('good document', 'other good document'), array(
484
            'visitors' => array(
485
                'xml' => array(
486
                    'doctype_whitelist' => array('good document', 'other good document'),
487
                )
488
            )
489
        ));
490
491
        $configs[] = array(array(), array());
492
493
        return $configs;
494
    }
495
496
    public function testXmlVisitorFormatOutput()
497
    {
498
        $config = array(
499
            'visitors' => array(
500
                'xml' => array(
501
                    'format_output' => false,
502
                )
503
            )
504
        );
505
        $container = $this->getContainerForConfig(array($config));
506
507
        $this->assertFalse($container->get('jms_serializer.xml_serialization_visitor')->isFormatOutput());
508
    }
509
510
    public function testXmlVisitorDefaultValueToFormatOutput()
511
    {
512
        $container = $this->getContainerForConfig(array());
513
        $this->assertTrue($container->get('jms_serializer.xml_serialization_visitor')->isFormatOutput());
514
    }
515
516
    private function getContainerForConfig(array $configs, callable $configurator = null)
517
    {
518
        $bundle = new JMSSerializerBundle();
519
        $extension = $bundle->getContainerExtension();
520
521
        $container = new ContainerBuilder();
522
        $container->setParameter('kernel.debug', true);
523
        $container->setParameter('kernel.cache_dir', sys_get_temp_dir() . '/serializer');
524
        $container->setParameter('kernel.bundles', array());
525
        $container->set('annotation_reader', new AnnotationReader());
526
        $container->setDefinition('doctrine', new Definition(Registry::class));
527
        $container->set('translator', $this->getMockBuilder('Symfony\\Component\\Translation\\TranslatorInterface')->getMock());
528
        $container->set('debug.stopwatch', $this->getMockBuilder('Symfony\\Component\\Stopwatch\\Stopwatch')->getMock());
529
        $container->registerExtension($extension);
0 ignored issues
show
Bug introduced by
It seems like $extension defined by $bundle->getContainerExtension() on line 519 can be null; however, Symfony\Component\Depend...er::registerExtension() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
530
        $extension->load($configs, $container);
531
532
        $bundle->build($container);
533
534
        if ($configurator) {
535
            call_user_func($configurator, $container);
536
        }
537
538
        $container->compile();
539
540
        return $container;
541
    }
542
}
543