Skip to content
This repository has been archived by the owner on Mar 30, 2018. It is now read-only.

Commit

Permalink
Run Humbug in isolation during Behat tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pamil committed Apr 15, 2017
1 parent 9558aad commit 8167f14
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 186 deletions.
2 changes: 1 addition & 1 deletion behat.yml.dist
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
default:
suites:
application:
contexts: [ FeatureContext, FilesystemContext ]
contexts: [FeatureContext]
formatters:
progress: ~
211 changes: 153 additions & 58 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
@@ -1,115 +1,210 @@
<?php

use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Tester\Exception\PendingException;
use SebastianBergmann\Diff\Differ;
use Symfony\Component\Console\Tester\ApplicationTester;
use Humbug\Console\Application;
use Humbug\Command\Humbug as HumbugCommand;

/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context, SnippetAcceptingContext
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;

final class FeatureContext implements Context
{
private $application;
/**
* @var string
*/
private static $workingDir;

private $appTester;
/**
* @var Filesystem
*/
private static $filesystem;

private $startingDirectory;
/**
* @var string
*/
private static $phpBin;

/**
* @var Differ
*/
private $differ;

/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
* @var Process
*/
private $process;

public function __construct()
{
require_once __DIR__ . '/../../bootstrap.php';

$this->application = new Application;
$this->application->setAutoExit(false);
$this->application->add(new HumbugCommand);
$this->appTester = new ApplicationTester($this->application);
$this->differ = new Differ();
}

/**
* @beforeScenario
* @BeforeFeature
*/
public function retainStartingDirectory()
public static function beforeFeature()
{
$this->startingDirectory = getcwd();
self::$workingDir = sprintf('%s/%s/', sys_get_temp_dir(), uniqid('', true));
self::$filesystem = new Filesystem();
self::$phpBin = self::findPhpBinary();
}

/**
* @afterScenario
* @BeforeScenario
*/
public function restoreWorkingDirectory()
public function beforeScenario()
{
if ($this->startingDirectory !== getcwd()) {
chdir($this->startingDirectory);
}
self::$filesystem->remove(self::$workingDir);
self::$filesystem->mkdir(self::$workingDir, 0777);
self::$filesystem->symlink(__DIR__ . '/../../composer.json', self::$workingDir . '/composer.json');
self::$filesystem->symlink(__DIR__ . '/../../vendor', self::$workingDir . '/vendor');
}

/**
* @Given I am in any directory
* @AfterScenario
*/
public function iAmInAnyDirectory()
public function afterScenario()
{
chdir(sys_get_temp_dir());
self::$filesystem->remove(self::$workingDir);
}

/**
* @When I run humbug
* @When I run humbug with :arguments
*/
public function iRunHumbug()
public function iRunHumbug($arguments = '--no-progress-bar')
{
$this->appTester->run(['run', '--no-progress-bar'=>true]);
$this->process = new Process(sprintf('%s %s run %s', self::$phpBin, escapeshellarg(__DIR__ . '/../../bin/humbug'), $arguments));
$this->process->setWorkingDirectory(self::$workingDir);
$this->process->start();
$this->process->wait();
}

/**
* @When I run humbug with :arg1
* @Then I should see:
* @Then I should see containing:
* @Then I should see output containing:
*/
public function iRunHumbugWith($arg1)
public function iShouldSeeContaining(PyStringNode $string)
{
$arguments = explode(' ', $arg1);
$this->appTester->run($arguments);
if (!preg_match('/' . preg_quote((string) $string, '/') . '/', $this->getProcessOutput())) {
throw new \RuntimeException(sprintf(
'Output did not match expected pattern:%s%s', PHP_EOL, $this->getProcessOutput()
));
}
}

/**
* @Then I should see:
* @Given the class file :file contains:
* @Given the test file :file contains:
* @Given the phpunit config file :file contains:
* @Given the phpunit bootstrap file :file contains:
*/
public function iShouldSee(PyStringNode $string)
public function theClassOrTestFileContains($file, PyStringNode $contents)
{
$string = (string) $string;
$output = $this->appTester->getDisplay();
if (trim($output) !== trim($string)) {
throw new \RuntimeException(sprintf(
'Output difference:%s%s',
$this->theFileContains($file, $contents);
}

/**
* @Given the humbug config file contains:
*/
public function theConfigFileContains(PyStringNode $contents)
{
$this->theFileContains('humbug.json', $contents);
}

/**
* @Given there is no file :file
*/
public function thereIsNoFile($file)
{
$this->throwExceptionIfFalse(!self::$filesystem->exists(self::getAbsolutePath($file)), sprintf('Expected file to not exist: %s', $file));
}

/**
* @Then the file :file should exist
*/
public function theFileShouldExist($file)
{
$this->throwExceptionIfFalse(self::$filesystem->exists(self::getAbsolutePath($file)), sprintf('Expected file to exist: %s', $file));
}

/**
* @Then the file :file should contain:
*/
public function theFileShouldContain($file, PyStringNode $expectedContent)
{
$this->theFileShouldExist($file);

$actualContent = file_get_contents(self::getAbsolutePath($file));

$this->throwExceptionIfFalse(
trim($actualContent) === trim((string) $expectedContent),
sprintf(
'Actual file content differs:%s%s',
PHP_EOL,
$this->differ->diff(trim($string), trim($output))
));
$this->differ->diff((string) $expectedContent, $actualContent)
)
);
}

private function throwExceptionIfFalse($result, $message)
{
if ($result === false) {
throw new \RuntimeException($message);
}
}

/**
* @Then I should see containing:
* @Then I should see output containing:
* @param string $file
* @param PyStringNode $contents
*/
public function iShouldSeeContaining(PyStringNode $string)
private function theFileContains($file, PyStringNode $contents)
{
if (!preg_match('/' . preg_quote((string) $string) . '/', $this->appTester->getDisplay())) {
throw new \RuntimeException(sprintf(
'Output did not match expected pattern:%s%s', PHP_EOL, $this->appTester->getDisplay()
));
self::$filesystem->dumpFile(self::getAbsolutePath($file), (string) $contents);
}

/**
* @return string
*/
private function getProcessOutput()
{
$this->assertProcessIsAvailable();

return $this->process->getErrorOutput() . $this->process->getOutput();
}

/**
* @throws \BadMethodCallException
*/
private function assertProcessIsAvailable()
{
if (null === $this->process) {
throw new \BadMethodCallException('Behat proccess cannot be found. Did you run it before making assertions?');
}
}

/**
* @return string
*
* @throws \RuntimeException
*/
private static function findPhpBinary()
{
$phpBinary = (new PhpExecutableFinder())->find();
if (false === $phpBinary) {
throw new \RuntimeException('Unable to find the PHP executable.');
}
return $phpBinary;
}

/**
* @param string $relativePath
*
* @return string
*/
private static function getAbsolutePath($relativePath)
{
return self::$workingDir . '/' . $relativePath;
}
}

0 comments on commit 8167f14

Please sign in to comment.