Skip to content

Commit 6adc426

Browse files
authored
chore: improve tests (#6)
1 parent f89ccf0 commit 6adc426

File tree

11 files changed

+192
-33
lines changed

11 files changed

+192
-33
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
* (c) Fabien Potencier <[email protected]>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Symfony\Component\Marshaller\Internal\Exception;
11+
12+
/**
13+
* @author Mathias Arlaud <[email protected]>
14+
*
15+
* @internal
16+
*/
17+
final class UnknownFormatException extends \InvalidArgumentException
18+
{
19+
public function __construct(string $format)
20+
{
21+
parent::__construct(sprintf('Unknown "%s" format.', $format));
22+
}
23+
}

Internal/Lexer/LexerFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace Symfony\Component\Marshaller\Internal\Lexer;
1111

12+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
13+
1214
/**
1315
* @author Mathias Arlaud <[email protected]>
1416
*
@@ -24,7 +26,7 @@ public static function create(string $format): LexerInterface
2426
{
2527
return match ($format) {
2628
'json' => new JsonLexer(),
27-
default => throw new \InvalidArgumentException(sprintf('Unknown "%s" format.', $format)),
29+
default => throw new UnknownFormatException($format),
2830
};
2931
}
3032
}

Internal/Parser/Parser.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?php
22

3-
declare(strict_types=1);
4-
53
/*
64
* This file is part of the Symfony package.
75
* (c) Fabien Potencier <[email protected]>
@@ -56,7 +54,15 @@ public function parse(\Iterator $tokens, Type|UnionType $type, array $context):
5654
}
5755

5856
if ($type->isScalar()) {
59-
return $this->scalarParser->parse($tokens, $type, $context);
57+
$result = $this->scalarParser->parse($tokens, $type, $context);
58+
59+
return match ($type->name()) {
60+
'int' => (int) $result,
61+
'float' => (float) $result,
62+
'string' => (string) $result,
63+
'bool' => (bool) $result,
64+
default => throw new \LogicException(sprintf('Cannot cast value to "%s".', $type->name())),
65+
};
6066
}
6167

6268
if ($type->isList()) {

Internal/Parser/ParserFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Symfony\Component\Marshaller\Internal\Parser;
1111

12+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
1213
use Symfony\Component\Marshaller\Internal\Parser\Json\JsonDictParser;
1314
use Symfony\Component\Marshaller\Internal\Parser\Json\JsonListParser;
1415
use Symfony\Component\Marshaller\Internal\Parser\Json\JsonNullableParser;
@@ -29,7 +30,7 @@ public static function create(string $format): Parser
2930
{
3031
return match ($format) {
3132
'json' => self::createJson(),
32-
default => throw new \InvalidArgumentException(sprintf('Unknown "%s" format.', $format)),
33+
default => throw new UnknownFormatException($format),
3334
};
3435
}
3536

Internal/Template/TemplateGeneratorFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Marshaller\Internal\Ast\Node\NodeInterface;
1616
use Symfony\Component\Marshaller\Internal\Ast\Node\ScalarNode;
1717
use Symfony\Component\Marshaller\Internal\Ast\Node\VariableNode;
18+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
1819

1920
/**
2021
* @author Mathias Arlaud <[email protected]>
@@ -31,7 +32,7 @@ public static function create(string $format): TemplateGenerator
3132
{
3233
return match ($format) {
3334
'json' => self::createJson(),
34-
default => throw new \InvalidArgumentException(sprintf('Unknown "%s" format.', $format)),
35+
default => throw new UnknownFormatException($format),
3536
};
3637
}
3738

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
## Unmarshal
77
- Unmarshal name and formatter hook (property hook)
88
- UTF-8 BOM
9-
- tests (hooks, context generation, internal unmarshal, unmarshal)
9+
- tests (hooks, context generation, unmarshal)
1010
- create dedicated exceptions and wrap native ones
1111

1212
## Questions

Resources/functions.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ function marshal_generate(string $type, string $format, array $context = []): st
5757

5858
$context = $context + [
5959
'generated_classes' => [],
60-
'hooks' => [],
6160
'variable_counters' => [],
6261
];
6362

Tests/Internal/MarshalGenerateTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Symfony\Component\Marshaller\Tests\Internal;
1111

1212
use PHPUnit\Framework\TestCase;
13+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
1314

1415
use function Symfony\Component\Marshaller\marshal_generate;
1516

@@ -332,14 +333,6 @@ public function generateJsonTemplateDataProvider(): iterable
332333
], ];
333334
}
334335

335-
public function testThrowOnUnknownFormat(): void
336-
{
337-
$this->expectException(\InvalidArgumentException::class);
338-
$this->expectExceptionMessage('Unknown "foo" format.');
339-
340-
marshal_generate('int', 'foo');
341-
}
342-
343336
/**
344337
* @dataProvider checkForCircularReferencesDataProvider
345338
*/
@@ -380,4 +373,11 @@ public function checkForCircularReferencesDataProvider(): iterable
380373
yield [CircularReferencingDummyRight::class, sprintf('array<string, %s>', CircularReferencingDummyRight::class)];
381374
yield [CircularReferencingDummyRight::class, sprintf('%s|%1$s', CircularReferencingDummyRight::class)];
382375
}
376+
377+
public function testThrowOnUnknownFormat(): void
378+
{
379+
$this->expectException(UnknownFormatException::class);
380+
381+
marshal_generate('int', 'unknown', []);
382+
}
383383
}

Tests/Internal/MarshalTest.php

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Symfony\Component\Marshaller\Tests\Internal;
1111

1212
use PHPUnit\Framework\TestCase;
13+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
1314

1415
use function Symfony\Component\Marshaller\marshal;
1516

@@ -38,17 +39,17 @@ protected function tearDown(): void
3839
}
3940

4041
/**
41-
* @dataProvider marshalContentDataProvider
42+
* @dataProvider marshalDataProvider
4243
*/
43-
public function testMarshalContent(mixed $data, string $type = null): void
44+
public function testMarshal(mixed $data, string $type = null): void
4445
{
45-
$this->assertSame(json_encode($data), $this->marshalAsString($data, 'json', (null !== $type) ? ['type' => $type] : []));
46+
$this->assertSame(json_encode($data), $this->marshalAsString($data, context: (null !== $type) ? ['type' => $type] : []));
4647
}
4748

4849
/**
4950
* @return iterable<array{0: mixed, 1: string?}>
5051
*/
51-
public function marshalContentDataProvider(): iterable
52+
public function marshalDataProvider(): iterable
5253
{
5354
yield [1];
5455
yield ['1'];
@@ -69,16 +70,16 @@ public function marshalContentDataProvider(): iterable
6970
yield [[1, 2.12, new ClassicDummy()], sprintf('array<int, int|float|%s>', ClassicDummy::class)];
7071
}
7172

72-
public function testMarshalContentWithJsonEncodeFlags(): void
73+
public function testMarshalWithJsonEncodeFlags(): void
7374
{
74-
$this->assertSame('"123"', $this->marshalAsString('123', 'json'));
75-
$this->assertSame('123', $this->marshalAsString('123', 'json', ['json_encode_flags' => \JSON_NUMERIC_CHECK]));
75+
$this->assertSame('"123"', $this->marshalAsString('123'));
76+
$this->assertSame('123', $this->marshalAsString('123', context: ['json_encode_flags' => \JSON_NUMERIC_CHECK]));
7677
}
7778

7879
public function testCreateCacheFile(): void
7980
{
8081
$cacheFileCount = \count(glob($this->cacheDir.\DIRECTORY_SEPARATOR.'*'));
81-
$this->marshalAsString(1, 'json');
82+
$this->marshalAsString(1);
8283

8384
$this->assertCount($cacheFileCount + 1, glob($this->cacheDir.\DIRECTORY_SEPARATOR.'*'));
8485
}
@@ -92,7 +93,7 @@ public function testCreateCacheFileInCustomDirectory(): void
9293
rmdir($cacheDir);
9394
}
9495

95-
$this->marshalAsString(1, 'json', ['cache_dir' => $cacheDir]);
96+
$this->marshalAsString(1, context: ['cache_dir' => $cacheDir]);
9697

9798
$this->assertFileExists($cacheDir);
9899
$this->assertCount(1, glob($cacheDir.\DIRECTORY_SEPARATOR.'*'));
@@ -110,22 +111,27 @@ public function testCreateCacheFileOnlyIfNotExists(): void
110111

111112
file_put_contents($cacheFilename, '<?php return static function ($data, $resource) { \fwrite($resource, "CACHED_FILE"); };');
112113

113-
$this->assertSame('CACHED_FILE', $this->marshalAsString(1, 'json'));
114+
$this->assertSame('CACHED_FILE', $this->marshalAsString(1));
115+
}
116+
117+
public function testThrowOnUnknownFormat(): void
118+
{
119+
$this->expectException(UnknownFormatException::class);
120+
121+
marshal(null, fopen('php://temp', 'w'), 'unknown', []);
114122
}
115123

116124
/**
117125
* @param array<string, mixed> $context
118126
*/
119-
private function marshalAsString(mixed $data, string $format, array $context = []): string
127+
private function marshalAsString(mixed $data, string $format = 'json', array $context = []): string
120128
{
121129
/** @var resource $resource */
122130
$resource = fopen('php://temp', 'w');
123-
marshal($data, $resource, 'json', $context);
131+
marshal($data, $resource, $format, $context);
124132

125133
rewind($resource);
126-
$string = stream_get_contents($resource);
127-
fclose($resource);
128134

129-
return $string;
135+
return stream_get_contents($resource);
130136
}
131137
}

Tests/Internal/Parser/ParserTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public function testParseUnion(): void
4242
->method('parse');
4343

4444
$parser = $this->createParser(scalarParser: $scalarParser, listParser: $listParser);
45-
$value = $parser->parse(new \ArrayIterator(), new UnionType([new Type('int'), Type::createFromString('array<string, string>')]), [
45+
$value = $parser->parse(new \ArrayIterator(), new UnionType([new Type('string'), Type::createFromString('array<string, string>')]), [
4646
'union_selector' => [
47-
'int|array<string, string>' => 'int',
47+
'string|array<string, string>' => 'string',
4848
],
4949
]);
5050

@@ -218,6 +218,7 @@ public function testParseNestedIterableList(): void
218218

219219
$result = [];
220220
foreach ($value as $v) {
221+
$this->assertInstanceOf(\Generator::class, $v);
221222
$result[] = iterator_to_array($v);
222223
}
223224

Tests/Internal/UnmarshalTest.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
* (c) Fabien Potencier <[email protected]>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Symfony\Component\Marshaller\Tests\Internal;
11+
12+
use PHPUnit\Framework\TestCase;
13+
use Symfony\Component\Marshaller\Internal\Exception\UnknownFormatException;
14+
use Symfony\Component\Marshaller\Tests\Fixtures\Dto\ClassicDummy;
15+
16+
use function Symfony\Component\Marshaller\unmarshal;
17+
18+
final class UnmarshalTest extends TestCase
19+
{
20+
/**
21+
* @dataProvider unmarshalDataProvider
22+
*/
23+
public function testUnmarshal(string $json, string $type): void
24+
{
25+
$this->assertSame(json_decode($json, associative: true), $this->unmarshalString($json, $type));
26+
}
27+
28+
/**
29+
* @return iterable<array{0: mixed, 1: string?}>
30+
*/
31+
public function unmarshalDataProvider(): iterable
32+
{
33+
yield ['1', 'int'];
34+
yield ['"foo"', 'string'];
35+
yield ['-1e100', 'float'];
36+
yield ['true', 'bool'];
37+
yield ['null', '?bool'];
38+
yield ['[[1, 2], [3, null], null]', 'array<int, ?array<int, ?int>>'];
39+
yield ['{"foo": {"bar": 1, "baz": null}, "foo2": null}', 'array<string, ?array<string, ?int>>'];
40+
}
41+
42+
public function testUnmarshalUnionType(): void
43+
{
44+
$this->assertSame([1, 2, 3], $this->unmarshalString('[1, "2", "3"]', 'array<int, int|string>', context: ['union_selector' => ['int|string' => 'int']]));
45+
46+
$this->expectException(\RuntimeException::class);
47+
$this->expectExceptionMessage('Cannot guess type to use for "int|string", you may specify a type in "$context[\'union_selector\'][\'int|string\']".');
48+
49+
$this->assertSame([1, 2, 3], $this->unmarshalString('[1, "2", "3"]', 'array<int, int|string>'));
50+
}
51+
52+
public function testUnmarshalIterable(): void
53+
{
54+
$value = $this->unmarshalString('[{"foo": 1, "bar": 2}, {"baz": 3}]', 'iterable<int, iterable<string, int>>');
55+
56+
$this->assertInstanceOf(\Generator::class, $value);
57+
58+
$result = [];
59+
foreach ($value as $v) {
60+
$this->assertInstanceOf(\Generator::class, $v);
61+
$result[] = iterator_to_array($v);
62+
}
63+
64+
$this->assertSame([['foo' => 1, 'bar' => 2], ['baz' => 3]], $result);
65+
}
66+
67+
public function testUnmarshalObject(): void
68+
{
69+
$value = $this->unmarshalString('{"id": 123, "name": "thename"}', ClassicDummy::class);
70+
71+
$expectedObject = new ClassicDummy();
72+
$expectedObject->id = 123;
73+
$expectedObject->name = 'thename';
74+
75+
$this->assertEquals($expectedObject, $value);
76+
77+
$value = $this->unmarshalString('{"@id": 123, "name": "thename"}', ClassicDummy::class, context: [
78+
'hooks' => [
79+
ClassicDummy::class => [
80+
'@id' => static function (\ReflectionClass $reflection, object $object, callable $value, array $context): void {
81+
$object->id = $value('int', $context);
82+
},
83+
'name' => static function (\ReflectionClass $reflection, object $object, callable $value, array $context): void {
84+
$object->name = 'HOOK_VALUE';
85+
},
86+
],
87+
],
88+
]);
89+
$expectedObject->name = 'HOOK_VALUE';
90+
91+
$this->assertEquals($expectedObject, $value);
92+
}
93+
94+
public function testUnmarshalWithJsonDecodeFlags(): void
95+
{
96+
$this->assertSame('1.2345678901235E+29', $this->unmarshalString('123456789012345678901234567890', 'string'));
97+
$this->assertSame('123456789012345678901234567890', $this->unmarshalString('123456789012345678901234567890', 'string', context: ['json_decode_flags' => \JSON_BIGINT_AS_STRING]));
98+
}
99+
100+
public function testThrowOnUnknownFormat(): void
101+
{
102+
$this->expectException(UnknownFormatException::class);
103+
104+
unmarshal(fopen('php://memory', 'w+'), 'int', 'unknown', []);
105+
}
106+
107+
/**
108+
* @param array<string, mixed> $context
109+
*/
110+
private function unmarshalString(string $string, string $type, string $format = 'json', array $context = []): mixed
111+
{
112+
/** @var resource $resource */
113+
$resource = fopen('php://memory', 'w+');
114+
115+
fwrite($resource, $string);
116+
rewind($resource);
117+
118+
return unmarshal($resource, $type, $format, $context);
119+
}
120+
}

0 commit comments

Comments
 (0)