Skip to content

Commit

Permalink
Add some full-stack tests to ensure components work well together.
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-1-anderson committed Apr 25, 2016
1 parent 5f82184 commit 409b662
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/AnnotatedCommandFactory.php
Expand Up @@ -61,6 +61,7 @@ public function createCommandsFromClass($commandFileInstance)
{
$this->notify($commandFileInstance);
$commandInfoList = $this->getCommandInfoListFromClass($commandFileInstance);
$this->registerCommandHooksFromClassInfo($commandInfoList, $commandFileInstance);
return $this->createCommandsFromClassInfo($commandInfoList, $commandFileInstance);
}

Expand Down
19 changes: 18 additions & 1 deletion src/CommandFileDiscovery.php
Expand Up @@ -33,6 +33,7 @@ class CommandFileDiscovery
protected $excludeList;
protected $searchLocations;
protected $searchPattern = '*Commands.php';
protected $includeFilesAtBase = true;

public function __construct()
{
Expand All @@ -43,6 +44,19 @@ public function __construct()
];
}

/**
* Specify whether to search for files at the base directory
* ($directoryList parameter to discover and discoverNamespaced
* methods), or only in the directories listed in the search paths.
*
* @param type $includeFilesAtBase
*/
public function setIncludeFilesAtBase($includeFilesAtBase)
{
$this->includeFilesAtBase = $includeFilesAtBase;
return $this;
}

/**
* Set the list of excludes to add to the finder, replacing
* whatever was there before.
Expand Down Expand Up @@ -181,9 +195,12 @@ public function discover($directoryList, $baseNamespace = '')
*/
protected function discoverCommandFiles($directory, $baseNamespace)
{
$commandFiles = [];
// In the search location itself, we will search for command files
// immediately inside the directory only.
$commandFiles = $this->discoverCommandFilesInLocation($directory, '== 0', $baseNamespace);
if ($this->includeFilesAtBase) {
$commandFiles = $this->discoverCommandFilesInLocation($directory, '== 0', $baseNamespace);
}

// In the other search locations,
foreach ($this->searchLocations as $location) {
Expand Down
33 changes: 33 additions & 0 deletions tests/src/alpha/AlphaCommandFile.php
@@ -1,6 +1,8 @@
<?php
namespace Consolidation\TestUtils\alpha;

use Consolidation\AnnotatedCommand\CommandError;

/**
* Test file used in the testCommandDiscovery() test.
*
Expand All @@ -10,5 +12,36 @@
*/
class AlphaCommandFile
{
public function alwaysFail()
{
return new CommandError('This command always fails.', 13);
}

public function simulatedStatus()
{
return ['status-code' => 42];
}

public function exampleOutput()
{
return 'Hello, World.';
}

public function exampleCat($one, $two = '', $options = ['flip' => false])
{
if ($options['flip']) {
return "{$two}{$one}";
}
return "{$one}{$two}";
}

public function exampleEcho(array $args)
{
return ['item-list' => $args];
}

public function exampleMessage()
{
return ['message' => 'Shipwrecked; send bananas.'];
}
}
@@ -1,5 +1,5 @@
<?php
namespace Consolidation\TestUtils\alpha\Include;
namespace Consolidation\TestUtils\alpha\Inclusive;

/**
* Test file used in the testCommandDiscovery() test.
Expand Down
7 changes: 5 additions & 2 deletions tests/src/beta/BetaCommandFile.php
Expand Up @@ -4,11 +4,14 @@
/**
* Test file used in the testCommandDiscovery() test.
*
* This commandfile is found by the test. The test search base is the
* This commandfile is not found by the test. The test search base is the
* 'src' directory, but 'beta' is NOT one of the search directories available
* for searching, so nothing in this folder will be examined.
*/
class BetaCommandFile
{

public function unavailableCommand()
{
return 'This command is not available, because this commandfile is not in a location that is searched by the tests.';
}
}
4 changes: 2 additions & 2 deletions tests/testCommandFileDiscovery.php
Expand Up @@ -20,7 +20,7 @@ function testCommandDiscovery()
// find were all found.
$this->assertContains('./src/ExampleCommandFile.php', $commandFilePaths);
$this->assertContains('./src/alpha/AlphaCommandFile.php', $commandFilePaths);
$this->assertContains('./src/alpha/Include/IncludedCommandFile.php', $commandFilePaths);
$this->assertContains('./src/alpha/Inclusive/IncludedCommandFile.php', $commandFilePaths);

// Make sure that there are no additional items found.
$this->assertEquals(3, count($commandFilePaths));
Expand All @@ -29,7 +29,7 @@ function testCommandDiscovery()
// to be generated all match.
$this->assertContains('\Consolidation\TestUtils\ExampleCommandFile', $commandFileNamespaces);
$this->assertContains('\Consolidation\TestUtils\alpha\AlphaCommandFile', $commandFileNamespaces);
$this->assertContains('\Consolidation\TestUtils\alpha\Include\IncludedCommandFile', $commandFileNamespaces);
$this->assertContains('\Consolidation\TestUtils\alpha\Inclusive\IncludedCommandFile', $commandFileNamespaces);

// We do not need to test for additional namespace items, because we
// know that the length of the array_keys must be the same as the
Expand Down
146 changes: 146 additions & 0 deletions tests/testFullStack.php
@@ -0,0 +1,146 @@
<?php
namespace Consolidation\AnnotatedCommand;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Application;

use Consolidation\AnnotatedCommand\Parser\CommandInfo;

use Consolidation\AnnotatedCommand\Hooks\ValidatorInterface;
use Consolidation\AnnotatedCommand\Hooks\ProcessResultInterface;
use Consolidation\AnnotatedCommand\Hooks\AlterResultInterface;
use Consolidation\AnnotatedCommand\Hooks\ExtractOutputInterface;
use Consolidation\AnnotatedCommand\Hooks\StatusDeterminerInterface;

/**
* Do a test of all of the classes in this project, top-to-bottom.
*/
class FullStackTests extends \PHPUnit_Framework_TestCase
{
function setup() {
$this->application = new Application('TestApplication', '0.0.0');
$this->application->setAutoExit(false);
}

function testCommandsAndHooks()
{
// First, search for commandfiles in the 'alpha'
// directory. Note that this same functionality
// is tested more thoroughly in isolation in
// testCommandFileDiscovery.php
$discovery = new CommandFileDiscovery();
$discovery
->setSearchPattern('*CommandFile.php')
->setIncludeFilesAtBase(false)
->setSearchLocations(['alpha']);

chdir(__DIR__);
$commandFiles = $discovery->discover('.', '\Consolidation\TestUtils');

// Create a new factory, and load all of the files
// discovered above. The command factory class is
// tested in isolation in testAnnotatedCommandFactory.php,
// but this is the only place where
$factory = new AnnotatedCommandFactory();
// $factory->addListener(...);
foreach ($commandFiles as $path => $commandClass) {
$this->assertFileExists($path);
include $path;
$commandInstance = new $commandClass();
$commandList = $factory->createCommandsFromClass($commandInstance);
foreach ($commandList as $command) {
$this->application->add($command);
}
}

// Control: run commands without hooks.
$this->assertRunCommandViaApplicationEquals('always:fail', 'This command always fails.', 13);
$this->assertRunCommandViaApplicationEquals('simulated:status', '');
$this->assertRunCommandViaApplicationEquals('example:output', 'Hello, World.');
$this->assertRunCommandViaApplicationEquals('example:cat bet alpha --flip', 'alphabet');
$this->assertRunCommandViaApplicationEquals('example:echo a b c', '');
$this->assertRunCommandViaApplicationEquals('example:message', '');

// Add some hooks.
$factory->hookManager()->addValidator(new ExampleValidator());
$factory->hookManager()->addResultProcessor(new ExampleResultProcessor());
$factory->hookManager()->addAlterResult(new ExampleResultAlterer());
$factory->hookManager()->addStatusDeterminer(new ExampleStatusDeterminer());
$factory->hookManager()->addOutputExtractor(new ExampleOutputExtractor());

// Run the same commands as before, and confirm that results
// are different now that the hooks are in place.
$this->assertRunCommandViaApplicationEquals('simulated:status', '', 42);
$this->assertRunCommandViaApplicationEquals('example:output', 'Hello, World!');
$this->assertRunCommandViaApplicationEquals('example:cat bet alpha --flip', 'alphabeta');
$this->assertRunCommandViaApplicationEquals('example:echo a b c', 'a,b,c');
$this->assertRunCommandViaApplicationEquals('example:message', 'Shipwrecked; send bananas.');
}

function assertRunCommandViaApplicationEquals($cmd, $expectedOutput, $expectedStatusCode = 0)
{
$input = new StringInput($cmd);
$output = new BufferedOutput();

$statusCode = $this->application->run($input, $output);
$commandOutput = trim($output->fetch());

$this->assertEquals($expectedOutput, $commandOutput);
$this->assertEquals($expectedStatusCode, $statusCode);
}
}

class ExampleValidator implements ValidatorInterface
{
public function validate($args)
{
if (isset($args['one']) && ($args['one'] == 'bet')) {
$args['one'] = 'beta';
return $args;
}
}
}

class ExampleResultProcessor implements ProcessResultInterface
{
public function process($result, array $args)
{
if (is_array($result) && array_key_exists('item-list', $result)) {
return implode(',', $result['item-list']);
}
}
}

class ExampleResultAlterer implements AlterResultInterface
{
public function process($result, array $args)
{
if (is_string($result) && ($result == 'Hello, World.')) {
return 'Hello, World!';
}
}
}

class ExampleStatusDeterminer implements StatusDeterminerInterface
{
public function determineStatusCode($result)
{
if (is_array($result) && array_key_exists('status-code', $result)) {
return $result['status-code'];
}
}
}

class ExampleOutputExtractor implements ExtractOutputInterface
{
public function extractOutput($result)
{
if (is_array($result) && array_key_exists('message', $result)) {
return $result['message'];
}
}
}

0 comments on commit 409b662

Please sign in to comment.