Skip to content

Commit

Permalink
Allow running multiple suites individually and by wildcards in multi-…
Browse files Browse the repository at this point in the history
…application setup.

Fix: #6434
  • Loading branch information
calvinalkan committed May 3, 2022
1 parent eb9c76e commit e7192e3
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 19 deletions.
106 changes: 91 additions & 15 deletions src/Codeception/Command/Run.php
Expand Up @@ -3,8 +3,6 @@

use Codeception\Codecept;
use Codeception\Configuration;
use Codeception\Lib\GroupManager;
use Codeception\Util\PathResolver;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -330,7 +328,7 @@ public function execute(InputInterface $input, OutputInterface $output)

$suite = $input->getArgument('suite');
$test = $input->getArgument('test');

if ($this->options['group']) {
$this->output->writeln(sprintf("[Groups] <info>%s</info> ", implode(', ', $this->options['group'])));
}
Expand All @@ -348,6 +346,7 @@ public function execute(InputInterface $input, OutputInterface $output)
foreach ($config['include'] as $include) {
// Find if the suite begins with an include path
if (strpos($suite, $include) === 0) {

// Use include config
$config = Configuration::config($projectDir.$include);

Expand Down Expand Up @@ -385,10 +384,14 @@ public function execute(InputInterface $input, OutputInterface $output)
}
}
}

if ($test) {
$userOptions['filter'] = $this->matchFilteredTestName($test);
} elseif ($suite) {
} elseif (
$suite
&& ! $this->isWildcardSuiteName($suite)
&& ! $this->isSuiteInMultiApplication($suite)
) {
$userOptions['filter'] = $this->matchFilteredTestName($suite);
}
if (!$this->options['silent'] && $config['settings']['shuffle']) {
Expand All @@ -405,18 +408,60 @@ public function execute(InputInterface $input, OutputInterface $output)

// Run all tests of given suite or all suites
if (!$test) {
$suites = $suite ? explode(',', $suite) : Configuration::suites();
$this->executed = $this->runSuites($suites, $this->options['skip']);

if (!empty($config['include']) and !$suite) {
$raw_suites = $suite ? explode(',', $suite) : Configuration::suites();

/** @var string[] $main_app_suites */
$main_app_suites = [];

/** @var array<string,string> $app_specific_suites */
$app_specific_suites = [];

/** @var string[] $wildcard_suites */
$wildcard_suites = [];

foreach ($raw_suites as $raw_suite) {
if($this->isWildcardSuiteName($raw_suite)){
$wildcard_suites[] = explode('*::', $raw_suite)[1];
continue;
}
if($this->isSuiteInMultiApplication($raw_suite)){
$app_and_suite = explode('::', $raw_suite);
$app_specific_suites[$app_and_suite[0]][] = $app_and_suite[1];
continue;
}
$main_app_suites[] = $raw_suite;
}

if([] !== $main_app_suites) {
$this->executed = $this->runSuites($main_app_suites, $this->options['skip']);
}

if(!empty($wildcard_suites) && ! empty($app_specific_suites)) {
$this->output->writeLn('<error>Wildcard options can not be combined with specific suites of included apps.</error>');
return self::INVALID;
}

if(!empty($config['include'])) {

$current_dir = Configuration::projectDir();
$suites += $config['include'];
$this->runIncludedSuites($config['include'], $current_dir);
$included_apps = $config['include'];

if(!empty($app_specific_suites)){
$included_apps = array_intersect($included_apps, array_keys($app_specific_suites));
}

$this->runIncludedSuites(
$included_apps,
$current_dir,
$app_specific_suites,
$wildcard_suites
);

}

if ($this->executed === 0) {
throw new \RuntimeException(
sprintf("Suite '%s' could not be found", implode(', ', $suites))
sprintf("Suite '%s' could not be found", implode(', ', $raw_suites))
);
}
}
Expand Down Expand Up @@ -488,8 +533,10 @@ protected function matchSingleTest($suite, $config)
*
* @param array $suites
* @param string $parent_dir
* @param array<string,string[]> $filter_app_suites An array keyed by included app name where values are suite names to run.
* @param string[] $filter_suites_by_wildcard A list of suite names (applies to all included apps)
*/
protected function runIncludedSuites($suites, $parent_dir)
protected function runIncludedSuites($suites, $parent_dir, $filter_app_suites = [], $filter_suites_by_wildcard = [])
{
$defaultConfig = Configuration::config();
$absolutePath = \Codeception\Configuration::projectDir();
Expand All @@ -506,7 +553,15 @@ protected function runIncludedSuites($suites, $parent_dir)
}

$suites = Configuration::suites();


if(!empty($filter_suites_by_wildcard)){
$suites = array_intersect($suites, $filter_suites_by_wildcard);
}

if(isset($filter_app_suites[$relativePath])) {
$suites = array_intersect($suites, $filter_app_suites[$relativePath]);
}

$namespace = $this->currentNamespace();
$this->output->writeln(
"\n<fg=white;bg=magenta>\n[$namespace]: tests from $current_dir\n</fg=white;bg=magenta>"
Expand Down Expand Up @@ -661,4 +716,25 @@ private function ensurePhpExtIsAvailable($ext)
);
}
}

/**
* @param string $suite_name
*
* @return bool
*/
private function isWildcardSuiteName($suite_name)
{
return '*::' === substr($suite_name, 0, 3);
}

/**
* @param string $suite_name
*
* @return bool
*/
private function isSuiteInMultiApplication($suite_name)
{
return false !== strpos($suite_name, '::');
}

}
74 changes: 74 additions & 0 deletions tests/cli/IncludedCest.php
Expand Up @@ -205,4 +205,78 @@ public function includedSuitesAreNotRunTwice (CliGuy $I) {
$I->seeInShellOutput('2 tests');
$I->dontSeeInShellOutput('4 tests');
}

/**
* @before moveToIncluded
* @param CliGuy $I
*/
public function someSuitesForSomeIncludedApplicationCanBeRun(CliGuy $I)
{
$I->executeCommand('run jazz::functional');

$I->seeInShellOutput('Jazz.functional Tests');
$I->dontSeeInShellOutput('Jazz.unit Tests');
$I->dontSeeInShellOutput('Shire.functional');

$I->executeCommand('run jazz::functional,jazz::unit');

$I->seeInShellOutput('Jazz.functional Tests');
$I->seeInShellOutput('Jazz.unit Tests');

$I->dontSeeInShellOutput('Shire.functional');

$I->executeCommand('run jazz::unit,shire::functional');

$I->seeInShellOutput('Shire.functional Tests');
$I->seeInShellOutput('Jazz.unit Tests');
$I->dontSeeInShellOutput('Jazz.functional Tests');

$I->executeCommand('run jazz/pianist::functional');

$I->dontSeeInShellOutput('Jazz.functional Tests');
$I->seeInShellOutput('Jazz\Pianist.functional');
}

/**
* @before moveToIncluded
* @param CliGuy $I
*/
public function someSuitesCanBeRunForAllIncludedApplications(\CliGuy $I)
{
$I->executeCommand('run *::functional');

// only functional tests are run
$I->seeInShellOutput('Jazz.functional Tests');
$I->seeInShellOutput('Jazz\Pianist.functional');
$I->seeInShellOutput('Shire.functional Tests');
// unit suites are not run
$I->dontSeeInShellOutput('Jazz.unit Tests');


$I->executeCommand('run *::unit');
// only unit tests are run
$I->seeInShellOutput('Jazz.unit Tests');
$I->dontSeeInShellOutput('Jazz.functional Tests');
$I->dontSeeInShellOutput('Jazz\Pianist.functional');
$I->dontSeeInShellOutput('Shire.functional Tests');

$I->executeCommand('run *::functional,*::unit');
// Both suites are run now
$I->seeInShellOutput('Jazz.functional Tests');
$I->seeInShellOutput('Jazz\Pianist.functional');
$I->seeInShellOutput('Shire.functional Tests');
$I->seeInShellOutput('Jazz.unit Tests');
}

/**
* @before moveToIncluded
* @param CliGuy $I
*/
public function wildCardSuitesAndAppSpecificSuitesCantBeCombined(CliGuy $I)
{
$I->executeCommand('run jazz::unit,*::functional', false);
$I->seeResultCodeIs(2);
$I->seeInShellOutput('Wildcard options can not be combined with specific suites of included apps.');
}

}
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"></xs:schema>
<note>
<to>Son</to>
<from>Vader</from>
<heading>Disclaimer</heading>
<body>I'm your father!</body>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

0 comments on commit e7192e3

Please sign in to comment.