Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some full-stack tests to ensure components work well together. #21

Merged
merged 1 commit into from Apr 25, 2016
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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'];
}
}
}