Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Codeception/Actor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract class Actor
use Comment;
use Pause;

public function __construct(protected Scenario $scenario)
public function __construct(protected readonly Scenario $scenario)
{
}

Expand All @@ -40,10 +40,9 @@ public function wantToTest(string $text): void
{
}

public function __call(string $method, array $arguments)
public function __call(string $method, array $arguments): never
{
$class = static::class;
throw new RuntimeException("Call to undefined method {$class}::{$method}");
throw new RuntimeException(sprintf('Call to undefined method %s::%s', static::class, $method));
}

/**
Expand Down
50 changes: 25 additions & 25 deletions src/Codeception/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ class Application extends BaseApplication
*/
public function registerCustomCommands(): void
{
$output = new ConsoleOutput();
try {
$this->readCustomCommandsFromConfig();
} catch (ConfigurationException $e) {
if ($e->getCode() === 404) {
return;
}
$this->renderExceptionWrapper($e, new ConsoleOutput());
$this->renderExceptionWrapper($e, $output);
exit(1);
} catch (Exception $e) {
$this->renderExceptionWrapper($e, new ConsoleOutput());
$this->renderExceptionWrapper($e, $output);
exit(1);
}
}
Expand Down Expand Up @@ -67,8 +68,7 @@ protected function readCustomCommandsFromConfig(): void
}

foreach ($config['extensions']['commands'] as $commandClass) {
$commandName = $this->getCustomCommandName($commandClass);
$this->add(new $commandClass($commandName));
$this->add(new $commandClass($this->getCustomCommandName($commandClass)));
}
}

Expand All @@ -84,11 +84,10 @@ protected function getCustomCommandName(string $commandClass): string
throw new ConfigurationException("Extension: Command class {$commandClass} not found");
}

$interfaces = class_implements($commandClass);

if (!in_array(CustomCommandInterface::class, $interfaces)) {
throw new ConfigurationException("Extension: Command {$commandClass} must implement " .
"the interface `Codeception\\CustomCommandInterface`");
if (!is_subclass_of($commandClass, CustomCommandInterface::class)) {
throw new ConfigurationException(
"Extension: Command {$commandClass} must implement the interface `Codeception\\CustomCommandInterface`"
);
}

return $commandClass::getCommandName();
Expand Down Expand Up @@ -140,30 +139,31 @@ protected function getCoreArguments(): SymfonyArgvInput
}

$argvWithoutConfig = [];
if (isset($_SERVER['argv'])) {
$argv = $_SERVER['argv'];

for ($i = 0; $i < count($argv); ++$i) {
if (preg_match('#^(?:-([^c-]*)?c|--config(?:=|$))(.*)$#', $argv[$i], $match)) {
if (!empty($match[2])) { //same index
$this->preloadConfiguration($match[2]);
} elseif (isset($argv[$i + 1])) { //next index
$this->preloadConfiguration($argv[++$i]);
}
if (!empty($match[1])) {
$argvWithoutConfig[] = "-" . $match[1]; //rest commands
$argv = $_SERVER['argv'] ?? [];

for ($i = 0, $count = count($argv); $i < $count; ++$i) {
if (preg_match('#^(?:-([^c-]*)?c|--config(?:=|$))(.*)$#', $argv[$i], $match)) {
$value = $match[2] !== '' ? $match[2] : ($argv[$i + 1] ?? '');
if ($value !== '') {
$this->preloadConfiguration($value);
if ($match[2] === '') {
++$i;
}
continue;
}
$argvWithoutConfig[] = $argv[$i];
if (!empty($match[1])) {
$argvWithoutConfig[] = '-' . $match[1];
}
continue;
}

$argvWithoutConfig[] = $argv[$i];
}

return $this->coreArguments = new SymfonyArgvInput($argvWithoutConfig);
}

/**
* Pre load Configuration, the config option is use.
* Preload Configuration, the config option is use.
*
* @param string $configFile Path to Configuration
* @throws ConfigurationException
Expand All @@ -173,7 +173,7 @@ protected function preloadConfiguration(string $configFile): void
try {
Configuration::config($configFile);
} catch (ConfigurationException $e) {
if ($e->getCode() == 404) {
if ($e->getCode() === 404) {
throw new ConfigurationException("Your configuration file `{$configFile}` could not be found.", 405);
}
throw $e;
Expand Down
105 changes: 48 additions & 57 deletions src/Codeception/Codecept.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,13 @@ class Codecept
public function __construct(array $options = [])
{
$this->resultAggregator = new ResultAggregator();
$this->dispatcher = new EventDispatcher();
$this->extensionLoader = new ExtensionLoader($this->dispatcher);
$this->dispatcher = new EventDispatcher();
$this->extensionLoader = new ExtensionLoader($this->dispatcher);

$baseOptions = $this->mergeOptions($options);
$this->extensionLoader->bootGlobalExtensions($baseOptions); // extensions may override config
$this->extensionLoader->bootGlobalExtensions($this->mergeOptions($options));

$this->config = Configuration::config();
$this->options = $this->mergeOptions($options); // options updated from config
$this->options = $this->mergeOptions($options);

$this->output = new Output($this->options);

Expand All @@ -105,23 +104,31 @@ public function __construct(array $options = [])
*/
protected function mergeOptions(array $options): array
{
$config = Configuration::config();
$baseOptions = array_merge($this->options, $config['settings']);
return array_merge($baseOptions, $options);
return array_merge($this->options, Configuration::config()['settings'], $options);
}

/**
* \Symfony\Component\EventDispatcher\EventSubscriberInterface[] $subscribers
*/
private function addSubscribers(array $subscribers): void
{
foreach ($subscribers as $subscriber) {
$this->dispatcher->addSubscriber($subscriber);
}
}

public function registerSubscribers(): void
{
// required
$this->dispatcher->addSubscriber(new GracefulTermination($this->resultAggregator));
$this->dispatcher->addSubscriber(new ErrorHandler());
$this->dispatcher->addSubscriber(new Dependencies());
$this->dispatcher->addSubscriber(new Bootstrap());
$this->dispatcher->addSubscriber(new PrepareTest());
$this->dispatcher->addSubscriber(new Module());
$this->dispatcher->addSubscriber(new BeforeAfterTest());

// optional
$this->addSubscribers([
new GracefulTermination($this->resultAggregator),
new ErrorHandler(),
new Dependencies(),
new Bootstrap(),
new PrepareTest(),
new Module(),
new BeforeAfterTest(),
]);

if (!$this->options['no-rebuild']) {
$this->dispatcher->addSubscriber(new AutoRebuild());
}
Expand All @@ -131,10 +138,12 @@ public function registerSubscribers(): void
}

if ($this->options['coverage']) {
$this->dispatcher->addSubscriber(new Local($this->options));
$this->dispatcher->addSubscriber(new LocalServer($this->options));
$this->dispatcher->addSubscriber(new RemoteServer($this->options));
$this->dispatcher->addSubscriber(new CoveragePrinter($this->options, $this->output));
$this->addSubscribers([
new Local($this->options),
new LocalServer($this->options),
new RemoteServer($this->options),
new CoveragePrinter($this->options, $this->output),
]);
}

if ($this->options['report']) {
Expand All @@ -157,10 +166,7 @@ private function isConsolePrinterSubscribed(): bool
{
foreach ($this->dispatcher->getListeners() as $listeners) {
foreach ($listeners as $listener) {
if ($listener instanceof ConsolePrinter) {
return true;
}
if (is_array($listener) && $listener[0] instanceof ConsolePrinter) {
if ($listener instanceof ConsolePrinter || (is_array($listener) && $listener[0] instanceof ConsolePrinter)) {
return true;
}
}
Expand All @@ -176,33 +182,24 @@ private function registerReporters(): void
''
);
}
if ($this->options['html']) {
$this->dispatcher->addSubscriber(
new HtmlReporter($this->options, $this->output)
);
}
if ($this->options['xml']) {
$this->dispatcher->addSubscriber(
new JUnitReporter($this->options, $this->output)
);
}
if ($this->options['phpunit-xml']) {
$this->dispatcher->addSubscriber(
new PhpUnitReporter($this->options, $this->output)
);

$map = [
'html' => fn () => new HtmlReporter($this->options, $this->output),
'xml' => fn () => new JUnitReporter($this->options, $this->output),
'phpunit-xml' => fn () => new PhpUnitReporter($this->options, $this->output),
];
foreach ($map as $flag => $create) {
if ($this->options[$flag]) {
$this->dispatcher->addSubscriber($create());
}
}
}

public function run(string $suite, ?string $test = null, ?array $config = null): void
{
ini_set(
'memory_limit',
$this->config['settings']['memory_limit'] ?? '1024M'
);

$config = $config ?: Configuration::config();
$config = Configuration::suiteSettings($suite, $config);
ini_set('memory_limit', $this->config['settings']['memory_limit'] ?? '1024M');

$config = Configuration::suiteSettings($suite, $config ?: Configuration::config());
$selectedEnvironments = $this->options['env'];

if (!$selectedEnvironments || empty($config['env'])) {
Expand All @@ -214,9 +211,7 @@ public function run(string $suite, ?string $test = null, ?array $config = null):
foreach (array_unique($selectedEnvironments) as $envList) {
$envSet = explode(',', (string) $envList);
$suiteEnvConfig = $config;

// contains a list of the environments used in this suite configuration env set.
$envConfigs = [];
$envConfigs = [];
foreach ($envSet as $currentEnv) {
// The $settings['env'] actually contains all parsed configuration files as a
// filename => filecontents key-value array. If there is no configuration file for the
Expand All @@ -225,27 +220,23 @@ public function run(string $suite, ?string $test = null, ?array $config = null):
return;
}

// Merge configuration consecutively with already build configuration
if (is_array($config['env'][$currentEnv])) {
$suiteEnvConfig = Configuration::mergeConfigs($suiteEnvConfig, $config['env'][$currentEnv]);
}
$envConfigs[] = $currentEnv;
$envConfigs[] = $currentEnv;
}

$suiteEnvConfig['current_environment'] = implode(',', $envConfigs);

$suiteToRun = $suite;
if (!empty($envList)) {
$suiteToRun .= ' (' . implode(', ', $envSet) . ')';
}
$suiteToRun = $suite . (empty($envList) ? '' : ' (' . implode(', ', $envSet) . ')');
$this->runSuite($suiteEnvConfig, $suiteToRun, $test);
}
}

public function runSuite(array $settings, string $suite, ?string $test = null): void
{
$settings['shard'] = $this->options['shard'];
$suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options);
$suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options);
$suiteManager->initialize();
mt_srand($this->options['seed']);
$suiteManager->loadTests($test);
Expand Down
Loading