Skip to content

Commit

Permalink
Add extra test cases and some sanity checks
Browse files Browse the repository at this point in the history
  • Loading branch information
Stratadox committed Mar 23, 2019
1 parent fe3c860 commit 47ab578
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 2 deletions.
3 changes: 3 additions & 0 deletions phpunit.xml
Expand Up @@ -13,6 +13,9 @@
<testsuite name="Efficiency">
<directory suffix=".php">tests/Efficiency</directory>
</testsuite>
<testsuite name="Sanity">
<directory suffix=".php">tests/Sanity</directory>
</testsuite>
</testsuites>

<filter>
Expand Down
2 changes: 1 addition & 1 deletion src/Graph/Builder/Obstacle.php
Expand Up @@ -18,7 +18,7 @@ public function isBlocked(): bool

public function label(): string
{
return 'N/A';
return '';
}

public function price(): float
Expand Down
13 changes: 13 additions & 0 deletions tests/Functionality/ShortestPathBetweenTwoLocations.php
Expand Up @@ -7,6 +7,7 @@
use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Environment;
use Stratadox\Pathfinder\Estimate\Estimate;
use Stratadox\Pathfinder\FloydWarshallIndexer;
use Stratadox\Pathfinder\Heuristic;
use Stratadox\Pathfinder\DynamicPathfinder;
use Stratadox\Pathfinder\NoPathAvailable;
Expand Down Expand Up @@ -147,6 +148,18 @@ function finding_a_path_on_a_relatively_wide_grid()
);
}

/** @test */
function finding_a_path_based_on_an_exact_heuristic()
{
$indexer = FloydWarshallIndexer::operatingIn($this->grid->fromExample());
$shortestPath = DynamicPathfinder::withHeuristic($indexer->heuristic());

$this->assertSame(
['B1', 'A1', 'A2', 'A3', 'B3'],
$shortestPath->between('B1', 'B3')
);
}

/** @test */
function cannot_find_a_path_if_there_is_no_path_available()
{
Expand Down
38 changes: 37 additions & 1 deletion tests/Functionality/ShortestPathsBasedOnAnIndex.php
Expand Up @@ -9,7 +9,7 @@
use Stratadox\Pathfinder\StaticPathfinder;
use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Test\Functionality\Fixture\Environments;
use function var_dump;
use Stratadox\Pathfinder\Test\Functionality\Fixture\Networks;

/**
* @testdox Finding all shortest paths in a network, using an index
Expand All @@ -19,9 +19,13 @@ class ShortestPathsBasedOnAnIndex extends TestCase
/** @var Environments */
private $environment;

/** @var Networks */
private $network;

protected function setUp(): void
{
$this->environment = new Environments();
$this->network = new Networks();
}

/** @test */
Expand Down Expand Up @@ -86,6 +90,38 @@ function constructing_a_path_based_on_the_information_from_the_index()
);
}

/** @test */
function constructing_a_path_based_on_the_information_of_the_network_index()
{
$network = $this->network->fromExample();

$shortestPath = StaticPathfinder::using(
FloydWarshallIndexer::operatingIn($network)->allShortestPaths(),
$network
);

$this->assertSame(['A', 'C', 'D'], $shortestPath->between('A', 'D'));
$this->assertSame(['D', 'B', 'A'], $shortestPath->between('D', 'A'));
}

/** @test */
function indexing_a_network_with_negative_cycles()
{
$network = $this->network->withNegativeCycles();

$shortestPath = StaticPathfinder::using(
FloydWarshallIndexer::operatingIn($network)->allShortestPaths(),
$network
);

$this->assertSame(
['A', 'B', 'C', 'D'],
$shortestPath->between('A', 'D'),
'Indexing should work fine, but once the static pathfinder ' .
'encounters a cyclical reference, it will loop forever.'
);
}

/** @test */
function constructing_all_paths_from_start_node_A_based_on_an_index()
{
Expand Down
64 changes: 64 additions & 0 deletions tests/Sanity/AtPositionTest.php
@@ -0,0 +1,64 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use BadMethodCallException;
use PHPUnit\Framework\TestCase;
use function random_int;
use Stratadox\Pathfinder\Graph\At;

/**
* @testdox Sanity check to assert that positions are infinite and immutable
*/
class AtPositionTest extends TestCase
{
/**
* @test
* @dataProvider dimensionAndCoordinates
*/
function all_dimensions_are_accessible(int $dimension, float ...$coordinates)
{
$position = At::position(...$coordinates);

$this->assertTrue(isset($position[$dimension]));
}

/**
* @test
* @dataProvider dimensionAndCoordinates
*/
function cannot_write_to_positions(int $dimension, float ...$coordinates)
{
$position = At::position(...$coordinates);
$number = random_int(100, 1000) / random_int(10, 1000);

$this->expectException(BadMethodCallException::class);

$position[$dimension] = $number;
}

/**
* @test
* @dataProvider dimensionAndCoordinates
*/
function cannot_unset_positions(int $dimension, float ...$coordinates)
{
$position = At::position(...$coordinates);

$this->expectException(BadMethodCallException::class);

unset($position[$dimension]);
}

public function dimensionAndCoordinates(): iterable
{
$random = random_int(10, 90000);
return [
'First dimension of [1,1]' => [0, 1, 1],
'Third dimension of [1,1]' => [2, 1, 1],
'Fifth dimension of [5,3]' => [4, 5, 3],
'Fifth dimension of [5,3,4]' => [4, 5, 3, 4],
$random.'th dimension of [5,3,4]' => [$random, 5, 3, 4],
];
}
}
20 changes: 20 additions & 0 deletions tests/Sanity/EdgeBuildingTest.php
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Graph\Builder\WithoutEdges;

/**
* @testdox Sanity check to assert that edge creation behaves properly
*/
class EdgeBuildingTest extends TestCase
{
/** @test */
function zero_edges_plus_one_edge_is_one_edge()
{
$edges = WithoutEdges::poorThing()->andTo('A');

$this->assertCount(1, $edges->gather());
}
}
57 changes: 57 additions & 0 deletions tests/Sanity/GeometricViewTest.php
@@ -0,0 +1,57 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Graph\Builder\GraphNetwork;
use Stratadox\Pathfinder\Graph\Builder\WithEdge;
use Stratadox\Pathfinder\Graph\GeometricView;
use Stratadox\Pathfinder\Graph\Ids;

/**
* @testdox Sanity check to assert that the geometric view is a proper adapter
*/
class GeometricViewTest extends TestCase
{
/** @test */
function fetching_all_indices()
{
$graph = GraphNetwork::create()
->withVertex('A', WithEdge::to('B'))
->withVertex('B', WithEdge::to('A'))
->make();

$environment = GeometricView::of($graph);

$this->assertEquals(
Ids::consistingOf('A', 'B'),
$environment->all()
);
}

/** @test */
function detecting_negative_weights()
{
$graph = GraphNetwork::create()
->withVertex('A', WithEdge::to('B', -1.5))
->withVertex('B', WithEdge::to('A'))
->make();

$environment = GeometricView::of($graph);

$this->assertTrue($environment->hasNegativeEdgeCosts());
}

/** @test */
function detecting_no_negative_weights()
{
$graph = GraphNetwork::create()
->withVertex('A', WithEdge::to('B'))
->withVertex('B', WithEdge::to('A'))
->make();

$environment = GeometricView::of($graph);

$this->assertFalse($environment->hasNegativeEdgeCosts());
}
}
28 changes: 28 additions & 0 deletions tests/Sanity/GraphEnvironmentTest.php
@@ -0,0 +1,28 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Distance\Euclidean;
use Stratadox\Pathfinder\Graph\At;
use Stratadox\Pathfinder\Graph\Builder\GraphEnvironment;
use Stratadox\Pathfinder\Graph\Builder\WithEdge;

/**
* @testdox Sanity check to assert that graphs behave properly
*/
class GraphEnvironmentTest extends TestCase
{
/** @test */
function cannot_auto_determine_the_cost_for_an_edge_that_goes_nowhere()
{
$builder = GraphEnvironment::create()
->withLocation('A', At::position(0, 0), WithEdge::to('B'))
->determineEdgeCostsAs(Euclidean::distance());

$this->expectException(InvalidArgumentException::class);

$builder->make();
}
}
44 changes: 44 additions & 0 deletions tests/Sanity/GraphTest.php
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Graph\At;
use Stratadox\Pathfinder\Graph\Builder\GraphEnvironment;
use Stratadox\Pathfinder\Graph\Builder\GraphNetwork;
use Stratadox\Pathfinder\Graph\Builder\WithEdge;

/**
* @testdox Sanity check to assert that graphs behave properly
*/
class GraphTest extends TestCase
{
/** @test */
function cannot_get_cost_between_non_neighbouring_nodes_in_a_network()
{
$network = GraphNetwork::create()
->withVertex('A', WithEdge::to('B'))
->withVertex('B', WithEdge::to('C'))
->withVertex('C', WithEdge::to('A'))
->make();

$this->expectException(InvalidArgumentException::class);

$network->movementCostBetween('A', 'C');
}

/** @test */
function cannot_get_cost_between_non_neighbouring_nodes_in_an_environment()
{
$network = GraphEnvironment::create()
->withLocation('A', At::position(0, 0), WithEdge::to('B'))
->withLocation('B', At::position(0, 1), WithEdge::to('C'))
->withLocation('C', At::position(0, 2), WithEdge::to('A'))
->make();

$this->expectException(InvalidArgumentException::class);

$network->movementCostBetween('A', 'C');
}
}
36 changes: 36 additions & 0 deletions tests/Sanity/ObstacleTest.php
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);

namespace Stratadox\Pathfinder\Test\Sanity;

use PHPUnit\Framework\TestCase;
use Stratadox\Pathfinder\Graph\Builder\Obstacle;

/**
* @testdox Sanity check to assert that obstacles have infinite costs
*/
class ObstacleTest extends TestCase
{
/** @test */
function obstacles_have_infinite_costs()
{
$obstacle = Obstacle::here();

$this->assertInfinite($obstacle->price());
}

/** @test */
function repriced_obstacles_still_have_infinite_costs()
{
$obstacle = Obstacle::here()->costing(1.0);

$this->assertInfinite($obstacle->price());
}

/** @test */
function obstacles_do_not_have_labels()
{
$obstacle = Obstacle::here();

$this->assertEmpty($obstacle->label());
}
}

0 comments on commit 47ab578

Please sign in to comment.