Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quality/php-mess-detector/test-ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<!-- There are never too many tests. -->
<exclude name="TooManyPublicMethods"/>
<exclude name="TooManyMethods"/>
<exclude name="ExcessiveMethodLength"/>
</rule>

<rule ref="rulesets/controversial.xml"/>
Expand Down
73 changes: 73 additions & 0 deletions tests/Legacy/EwktParser/CircularStringTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* This file is part of the spatial-decoder project.
*
* PHP 8.4 | 8.5
*
* Copyright Alexandre Tranchant <alexandre.tranchant@gmail.com> 2026
* Copyright Longitude One 2026
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/

declare(strict_types=1);

namespace LongitudeOne\SpatialDecoder\Tests\Legacy\EwktParser;

use LongitudeOne\SpatialDecoder\Decoder;
use LongitudeOne\SpatialDecoder\Strategy\EwktDecoderStrategy;
use LongitudeOne\SpatialDecoder\Tests\Legacy\EwktParser\Utils\SpecificTestCase;
use PHPUnit\Framework\Attributes\DataProvider;

class CircularStringTest extends SpecificTestCase
{
private Decoder $parser;

/** Prepare the decoder before each test. */
protected function setUp(): void
{
parent::setUp();
$this->parser = new Decoder(new EwktDecoderStrategy());
}

/** Release the decoder after each test. */
protected function tearDown(): void
{
unset($this->parser);
parent::tearDown();
}

/**
* @return \Generator<string, array{0: string, 1: ?int, 2: (float|int|string)[][], 3: ?string}, null, void>
*/
public static function circularStringProvider(): \Generator
{
yield 'testCircularString' => ['CIRCULARSTRING(0 0, 1 1, 1 0)', null, [[0, 0], [1, 1], [1, 0]], null];
// This legacy test was updated because the decoder now maintains float values.
yield 'testCircularStringWithFloat' => ['CIRCULARSTRING(0.0 0.0, 1.1 1.1, 1.0 0.0)', null, [[0.0, 0.0], [1.1, 1.1], [1.0, 0.0]], null];
yield 'testCircularStringWithSrid' => ['SRID=4326;CIRCULARSTRING(0 0, 1 1, 1 0)', 4326, [[0, 0], [1, 1], [1, 0]], null];
yield 'testCircularStringWithZ' => ['CIRCULARSTRINGZ(0 0 0, 1 1 1, 1 0 -1)', null, [[0, 0, 0], [1, 1, 1], [1, 0, -1]], 'Z'];
yield 'testCircularStringWithZAndSrid' => ['SRID=4326;CIRCULARSTRINGZ(0 0 0, 1 1 1, 1 0 -1)', 4326, [[0, 0, 0], [1, 1, 1], [1, 0, -1]], 'Z'];
yield 'testCircularStringWithM' => ['CIRCULARSTRINGM(0 0 0, 1 1 1, 1 0 -1)', null, [[0, 0, 0], [1, 1, 1], [1, 0, -1]], 'M'];
yield 'testCircularStringWithMAndSrid' => ['SRID=4326;CIRCULARSTRINGM(0 0 0, 1 1 1, 1 0 -1)', 4326, [[0, 0, 0], [1, 1, 1], [1, 0, -1]], 'M'];
yield 'testCircularStringWithZM' => ['CIRCULARSTRINGZM(0 0 0 0, 1 1 1 1, 1 0 -1 0)', null, [[0, 0, 0, 0], [1, 1, 1, 1], [1, 0, -1, 0]], 'ZM'];
yield 'testCircularStringWithZMAndSrid' => ['SRID=4326;CIRCULARSTRINGZM(0 0 0 0, 1 1 1 1.2, 1 0 -1 0)', 4326, [[0, 0, 0, 0], [1, 1, 1, '1.2'], [1, 0, -1, 0]], 'ZM'];
}

/**
* Verify that CIRCULARSTRING values decode to the expected control points and metadata.
*
* @param string $value EWKT value to decode
* @param int|null $srid Expected spatial reference identifier
* @param (float|int|string)[][] $coordinates Expected circular string coordinates
* @param string|null $dimension Expected coordinate dimension
*/
#[DataProvider('circularStringProvider')]
public function testCircularString(string $value, ?int $srid, array $coordinates, ?string $dimension): void
{
$actual = $this->parser->decode($value);
self::assertCircularStringParsed($srid, $coordinates, $dimension, $actual);
}
}
114 changes: 114 additions & 0 deletions tests/Legacy/EwktParser/ExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php
/**
* This file is part of the spatial-decoder project.
*
* PHP 8.4 | 8.5
*
* Copyright Alexandre Tranchant <alexandre.tranchant@gmail.com> 2026
* Copyright Longitude One 2026
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/

declare(strict_types=1);

namespace LongitudeOne\SpatialDecoder\Tests\Legacy\EwktParser;

use LongitudeOne\Core\Enum\GeometryTypeEnum;
use LongitudeOne\SpatialDecoder\Decoder;
use LongitudeOne\SpatialDecoder\Exception\InvalidArgumentException;
use LongitudeOne\SpatialDecoder\Exception\NonInstantiableGeometryTypeException;
use LongitudeOne\SpatialDecoder\Exception\NotYetImplementedException;
use LongitudeOne\SpatialDecoder\Strategy\EwktDecoderStrategy;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class ExceptionTest extends TestCase
{
/**
* @return \Generator<string, array{0: string, 1: string}, null, void>
*/
public static function notInstantiableTypes(): \Generator
{
yield 'CURVE' => ['CURVE', 'Geometry type "Curve" is recognized but is not instantiable.'];
yield 'GEOMETRY' => ['GEOMETRY', 'Geometry type "Geometry" is recognized but is not instantiable.'];
yield 'SOLID' => ['SOLID', 'Geometry type "Solid" is recognized but is not instantiable.'];
yield 'SURFACE' => ['SURFACE', 'Geometry type "Surface" is recognized but is not instantiable.'];
}

/**
* @return \Generator<string, array{0: string, 1: string}, null, void>
*/
public static function notYetImplementedTypes(): \Generator
{
yield 'BREPSOLID' => ['BREPSOLID', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::BREPSOLID->value)];
yield 'CIRCLE' => ['CIRCLE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::CIRCLE->value)];
yield 'CLOTHOID' => ['CLOTHOID', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::CLOTHOID->value)];
// This legacy test is commented because it is now implemented in the decoder.
// yield 'COMPOUNDCURVE' => ['COMPOUNDCURVE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::COMPOUNDCURVE->value)];
yield 'COMPOUNDSURFACE' => ['COMPOUNDSURFACE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::COMPOUNDSURFACE->value)];
yield 'CURVEPOLYGON' => ['CURVEPOLYGON', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::CURVEPOLYGON->value)];
yield 'ELLIPTICALCURVE' => ['ELLIPTICALCURVE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::ELLIPTICALCURVE->value)];
yield 'GEODESICSTRING' => ['GEODESICSTRING', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::GEODESICSTRING->value)];
yield 'MULTICURVE' => ['MULTICURVE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::MULTICURVE->value)];
yield 'MULTISURFACE' => ['MULTISURFACE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::MULTISURFACE->value)];
yield 'NURBSCURVE' => ['NURBSCURVE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::NURBSCURVE->value)];
yield 'SPIRALCURVE' => ['SPIRALCURVE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::SPIRALCURVE->value)];
// This legacy test is commented because it is now implemented in the decoder.
// yield 'POLYHEDRALSURFACE' => ['POLYHEDRALSURFACE', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::POLYHEDRALSURFACE->value)];
yield 'TIN' => ['TIN', \sprintf('Decoding of spatial type "%s" is not yet implemented.', GeometryTypeEnum::TIN->value)];
}

/** Ensure an unknown geometry name raises the decoder's invalid-argument exception. */
public function testNotExistentException(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessageIsOrContains('The supplied WKT geometry type is not supported. Invalid WKT input: "FOO(42 42)".');

(new Decoder(new EwktDecoderStrategy()))->decode('FOO(42 42)');
}

/**
* Ensure recognized but non-instantiable types raise the matching geometry-type exception.
*
* @param string $notInstantiableType Type to reject
* @param string $expectedMessage Expected exception message
*/
#[DataProvider('notInstantiableTypes')]
public function testNotInstantiable(string $notInstantiableType, string $expectedMessage): void
{
$this->expectException(NonInstantiableGeometryTypeException::class);
$this->expectExceptionMessageIsOrContains($expectedMessage);

$toParse = \sprintf('%s(42 42)', $notInstantiableType);

(new Decoder(new EwktDecoderStrategy()))->decode($toParse);
}

/** Ensure the non-instantiable exception exposes the correct message and geometry type. */
public function testNotInstantiableException(): void
{
$exception = new NonInstantiableGeometryTypeException(GeometryTypeEnum::GEOMETRY);
self::assertSame('Geometry type "Geometry" is recognized but is not instantiable.', $exception->getMessage());
self::assertSame(GeometryTypeEnum::GEOMETRY, $exception->getSpatialType());
}

/**
* Ensure recognized but unimplemented geometry types raise the matching exception and message.
*
* @param string $notYetImplemented Type not yet supported
* @param string $expectedMessage Expected exception message
*/
#[DataProvider('notYetImplementedTypes')]
public function testNotYetImplemented(string $notYetImplemented, string $expectedMessage): void
{
$this->expectException(NotYetImplementedException::class);
$this->expectExceptionMessageIsOrContains($expectedMessage);

$toParse = \sprintf('%s(42 42)', $notYetImplemented);

(new Decoder(new EwktDecoderStrategy()))->decode($toParse);
}
}
72 changes: 72 additions & 0 deletions tests/Legacy/EwktParser/GeometryCollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
/**
* This file is part of the spatial-decoder project.
*
* PHP 8.4 | 8.5
*
* Copyright Alexandre Tranchant <alexandre.tranchant@gmail.com> 2026
* Copyright Longitude One 2026
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/

declare(strict_types=1);

namespace LongitudeOne\SpatialDecoder\Tests\Legacy\EwktParser;

use LongitudeOne\SpatialDecoder\Decoder;
use LongitudeOne\SpatialDecoder\Strategy\EwktDecoderStrategy;
use LongitudeOne\SpatialDecoder\Tests\Legacy\EwktParser\Utils\SpecificTestCase;
use PHPUnit\Framework\Attributes\DataProvider;

class GeometryCollectionTest extends SpecificTestCase
{
private Decoder $parser;

/** Prepare the decoder before each test. */
protected function setUp(): void
{
parent::setUp();
$this->parser = new Decoder(new EwktDecoderStrategy());
}

/** Release the decoder after each test. */
protected function tearDown(): void
{
unset($this->parser);
parent::tearDown();
}

/**
* @return \Generator<string, array{0:string, 1:int|null, 2: array<array{'type': string, value:(int|string)[]|(int|string)[][]}>, 3: ?string}, null, void>
*/
public static function geometryCollectionProvider(): \Generator
{
yield 'testGeometryCollection' => ['GEOMETRYCOLLECTION(POINT(34.23 -87), LINESTRING(34.23 -87, 45.3 -92))', null, [['type' => 'POINT', 'value' => ['34.23', -87]], ['type' => 'LINESTRING', 'value' => [['34.23', -87], ['45.3', -92]]]], null];
yield 'testGeometryCollectionWithSrid' => ['SRID=4326;GEOMETRYCOLLECTION(POINT(34.23 -87), LINESTRING(34.23 -87, 45.3 -92))', 4326, [['type' => 'POINT', 'value' => ['34.23', -87]], ['type' => 'LINESTRING', 'value' => [['34.23', -87], ['45.3', -92]]]], null];
yield 'testGeometryCollectionWithZ' => ['GEOMETRYCOLLECTION(POINT Z(34.23 -87 10), LINESTRING Z(34.23 -87 10, 45.3 -92 10))', null, [['type' => 'POINT', 'value' => ['34.23', -87, 10]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10], ['45.3', -92, 10]]]], 'Z'];
yield 'testGeometryCollectionWithZAndSrid' => ['SRID=4326;GEOMETRYCOLLECTION(POINT Z(34.23 -87 10), LINESTRING Z(34.23 -87 10, 45.3 -92 10))', 4326, [['type' => 'POINT', 'value' => ['34.23', -87, 10]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10], ['45.3', -92, 10]]]], 'Z'];
yield 'testGeometryCollectionWithM' => ['GEOMETRYCOLLECTION(POINT M(34.23 -87 10), LINESTRING M(34.23 -87 10, 45.3 -92 10))', null, [['type' => 'POINT', 'value' => ['34.23', -87, 10]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10], ['45.3', -92, 10]]]], 'M'];
yield 'testGeometryCollectionWithMAndSrid' => ['SRID=4326;GEOMETRYCOLLECTION(POINT M(34.23 -87 10), LINESTRING M(34.23 -87 10, 45.3 -92 10))', 4326, [['type' => 'POINT', 'value' => ['34.23', -87, 10]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10], ['45.3', -92, 10]]]], 'M'];
yield 'testGeometryCollectionWithZM' => ['GEOMETRYCOLLECTION(POINT ZM(34.23 -87 10 20), LINESTRING ZM(34.23 -87 10 20, 45.3 -92 10 20))', null, [['type' => 'POINT', 'value' => ['34.23', -87, 10, 20]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10, 20], ['45.3', -92, 10, 20]]]], 'ZM'];
yield 'testGeometryCollectionWithZMAndSrid' => ['SRID=4326;GEOMETRYCOLLECTION(POINT ZM(34.23 -87 10 20), LINESTRING ZM(34.23 -87 10 20, 45.3 -92 10 20))', 4326, [['type' => 'POINT', 'value' => ['34.23', -87, 10, 20]], ['type' => 'LINESTRING', 'value' => [['34.23', -87, 10, 20], ['45.3', -92, 10, 20]]]], 'ZM'];
}

/**
* Verify that each geometry in a collection decodes with its expected type and coordinates.
*
* @param string $value EWKT value to decode
* @param int|null $srid Expected spatial reference identifier
* @param array<array{type: string, value: (int|string)[]|(int|string)[][]}> $coordinates Expected child geometries
* @param string|null $dimension Expected coordinate dimension
*/
#[DataProvider('geometryCollectionProvider')]
public function testGeometryCollection(string $value, ?int $srid, array $coordinates, ?string $dimension): void
{
$actual = $this->parser->decode($value);

self::assertGeometryCollectionParsed($srid, $coordinates, $dimension, $actual);
}
}
Loading
Loading