-
-
Notifications
You must be signed in to change notification settings - Fork 236
Add winter:test command #202
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
Merged
Merged
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
6e50f2c
Add plugin:test command
RomainMazB cea9433
Remove author name
RomainMazB b17dc7c
Merge branch 'wintercms:develop' into plugintest
RomainMazB 5ed06e0
rename to winter:test and support core tests
RomainMazB dc7c7d1
Use Process to run live-tests
RomainMazB 5535109
Proxy all options to phpunit
RomainMazB b12b2c0
Change comment for ignoring validation
RomainMazB 7f83be2
Improve comments
RomainMazB 0638b3a
Improve command description
RomainMazB 186da19
Improve class comment
RomainMazB 42e1a0c
Improve comments
RomainMazB 4fa873f
Change output info with info shortcut
RomainMazB 17bc4d0
Apply suggestions from code review
RomainMazB 9dc7e52
Gives the ability to pass a custom configuration file, and now fallba…
RomainMazB 32bfc27
Fix indentation
RomainMazB f8ccb56
Remove useless part of a comment
RomainMazB c34af66
Reworked command argument handling and test execution
jaxwilko e2d5a97
Apply suggestions from code review
LukeTowers 09c0d4d
Apply suggestions from code review
LukeTowers ddcfc35
Update modules/system/console/WinterTest.php
LukeTowers 6d9a6c4
Update modules/system/console/WinterTest.php
LukeTowers ab56914
Update modules/system/console/WinterTest.php
LukeTowers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,263 @@ | ||
| <?php namespace System\Console; | ||
|
|
||
| use Illuminate\Console\Command; | ||
| use Symfony\Component\Console\Input\InputOption; | ||
| use Symfony\Component\Process\Exception\ProcessSignaledException; | ||
| use Symfony\Component\Process\ExecutableFinder; | ||
| use Symfony\Component\Process\Process; | ||
| use System\Classes\PluginManager; | ||
| use Winter\Storm\Exception\ApplicationException; | ||
|
|
||
| /** | ||
| * Console command to test a plugin or the Winter CMS core. | ||
| * | ||
| * If a plugin is provided, this command will search for a `phpunit.xml` file inside the plugin's directory and run its tests. | ||
| * | ||
| * @package winter\wn-system-module | ||
| */ | ||
| class WinterTest extends Command | ||
| { | ||
| /** | ||
| * The console command name. | ||
| * @var string | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| protected $name = 'winter:test'; | ||
|
|
||
| /** | ||
| * @var string The console command signature as ignoreValidationErrors causes options not to be registered. | ||
| */ | ||
| protected $signature = 'winter:test {?--p|plugin=} {?--c|configuration=} {?--o|core}'; | ||
|
|
||
| /** | ||
| * @var string The console command description. | ||
| */ | ||
| protected $description = 'Run tests for the Winter CMS core or an existing plugin.'; | ||
|
|
||
| /** | ||
| * @var ?string Path to phpunit binary | ||
| */ | ||
| protected $phpUnitExec = null; | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Create a new command instance. | ||
| * | ||
| * @return void | ||
| */ | ||
| public function __construct() | ||
| { | ||
| parent::__construct(); | ||
|
|
||
| /** | ||
| * Ignore validation errors as option proxying is used by this command | ||
| * @see https://github.com/nunomaduro/collision/blob/stable/src/Adapters/Laravel/Commands/TestCommand.php | ||
| */ | ||
| $this->ignoreValidationErrors(); | ||
| } | ||
|
|
||
| /** | ||
| * Execute the console command. | ||
| * | ||
| * @throws ApplicationException | ||
| * @return int|void | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| public function handle() | ||
| { | ||
| $arguments = $this->getAdditionalArguments(); | ||
|
|
||
| if (($config = $this->option('configuration')) && file_exists($config)) { | ||
| return $this->execPhpUnit($config, $arguments); | ||
| } | ||
|
|
||
| $configs = $this->getPhpUnitConfigs(); | ||
|
|
||
| if ($this->option('core')) { | ||
| if (!$configs['core']) { | ||
| throw new ApplicationException("Unable to find the core's phpunit.xml file. Try downloading it from GitHub."); | ||
| } | ||
| $this->info('Running tests for: Winter CMS core'); | ||
|
|
||
| return $this->execPhpUnit($configs['core'], $arguments); | ||
| } | ||
|
|
||
| if ($plugin = $this->option('plugin')) { | ||
| if (!isset($configs['plugins'][strtolower($plugin)])) { | ||
| throw new ApplicationException(sprintf("Unable to find %s\'s phpunit.xml file', $plugin)); | ||
LukeTowers marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| $this->info('Running tests for plugin: ' . PluginManager::instance()->normalizeIdentifier($plugin)); | ||
|
|
||
| return $this->execPhpUnit($configs['plugins'][strtolower($plugin)], $arguments); | ||
| } | ||
|
|
||
| $exitCode = 0; | ||
|
|
||
| foreach (['core', 'plugins'] as $type) { | ||
| if (is_array($configs[$type])) { | ||
| foreach ($configs[$type] as $plugin => $config) { | ||
| $this->info('Running tests for plugin: ' . PluginManager::instance()->normalizeIdentifier($plugin)); | ||
| $exit = $this->execPhpUnit($config, $arguments); | ||
| $exitCode = $exitCode === 0 ? $exit : $exitCode; | ||
| } | ||
| continue; | ||
| } | ||
|
|
||
| $this->info('Running tests for Winter CMS: ' . $type); | ||
| $exit = $this->execPhpUnit($configs[$type], $arguments); | ||
| $exitCode = $exitCode === 0 ? $exit : $exitCode; | ||
| } | ||
|
|
||
| return $exitCode; | ||
| } | ||
|
|
||
| /** | ||
| * Get the console command options. | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| protected function getOptions(): array | ||
| { | ||
| return [ | ||
| ['plugin', 'p', InputOption::VALUE_OPTIONAL, 'The name of the plugin. Ex: AuthorName.PluginName', null], | ||
| ['configuration', 'c', InputOption::VALUE_OPTIONAL, 'The path to a PHPUnit XML config file', null], | ||
| ['core', 'o', InputOption::VALUE_NONE, 'Run the Winter CMS core tests'], | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * Get the console command arguments. | ||
| */ | ||
| protected function getArguments(): array | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
LukeTowers marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /** | ||
| * Execute a phpunit test | ||
| * | ||
| * @param string $config Path to configuration file | ||
| * @param array $args Array of params for PHPUnit | ||
| * @return int Exit code from process | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| protected function execPhpUnit(string $config, array $args): int | ||
| { | ||
| // Find and bind the phpunit executable | ||
| if (!$this->phpUnitExec) { | ||
| $this->phpUnitExec = (new ExecutableFinder()) | ||
| ->find('phpunit', base_path('vendor/bin/phpunit'), [base_path('vendor')]); | ||
| } | ||
|
|
||
| $process = new Process( | ||
| array_merge([$this->phpUnitExec, '--configuration=' . $config], $args), | ||
| dirname($config), | ||
| null, | ||
| null | ||
| ); | ||
|
|
||
| // Attempt to set tty mode, catch and warn with the exception message if unsupported | ||
| try { | ||
| $process->setTty(true); | ||
| } catch (\Throwable $e) { | ||
| $this->warn($e->getMessage()); | ||
| } | ||
|
|
||
| try { | ||
| return $process->run(function ($type, $line) { | ||
| $this->output->write($line); | ||
| }); | ||
| } catch (ProcessSignaledException $e) { | ||
| if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) { | ||
| throw $e; | ||
| } | ||
|
|
||
| return 1; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Find all PHPUnit config files (core, lib, plugins) | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| protected function getPhpUnitConfigs(): array | ||
| { | ||
| $configs = [ | ||
| 'core' => $this->getPhpUnitXmlFile(base_path()), | ||
| 'plugins' => [] | ||
| ]; | ||
|
|
||
| foreach (PluginManager::instance()->getPlugins() as $plugin) { | ||
| if ($path = $this->getPhpUnitXmlFile($plugin->getPluginPath())) { | ||
| $configs['plugins'][strtolower($plugin->getPluginIdentifier())] = $path; | ||
| } | ||
| } | ||
|
|
||
| return $configs; | ||
| } | ||
|
|
||
| /** | ||
| * Search for the config file to use. | ||
| * Priority order is: phpunit.xml, phpunit.xml.dist | ||
| */ | ||
LukeTowers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| protected function getPhpUnitXmlFile(string $path): ?string | ||
| { | ||
| // If a phpunit.xml file exists, returns its path | ||
| $distFilePath = $path . DIRECTORY_SEPARATOR . 'phpunit.xml'; | ||
| if (file_exists($distFilePath)) { | ||
| return $distFilePath; | ||
| } | ||
|
|
||
| // Fallback to phpunit.xml.dist file path if it exists | ||
| $configFilePath = $path . DIRECTORY_SEPARATOR . 'phpunit.xml.dist'; | ||
| if (file_exists($configFilePath)) { | ||
| return $configFilePath; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Strips out commands arguments and options in order to return arguments/options for PHPUnit. | ||
| */ | ||
| protected function getAdditionalArguments(): array | ||
| { | ||
| $arguments = $_SERVER['argv']; | ||
|
|
||
| // First two are always "artisan" and "winter:test" | ||
| $arguments = array_slice($arguments, 2); | ||
|
|
||
| // If nothing to do then just return | ||
| if (!count($arguments)) { | ||
| return $arguments; | ||
| } | ||
|
|
||
| // Get the arguments provided by this command | ||
| foreach ($this->getOptions() as $argument) { | ||
| // For position 0 & 1, pass their names with appropriate dashes | ||
| for ($i = 0; $i < 2; $i++) { | ||
| $arguments = $this->removeArgument($arguments, str_repeat('-', 2 - $i) . $argument[$i]); | ||
| } | ||
| } | ||
|
|
||
| return $arguments; | ||
| } | ||
|
|
||
| /** | ||
| * Removes flags from argument list and their value if present | ||
| */ | ||
| protected function removeArgument(array $arguments, string $remove): array | ||
| { | ||
| // find args that have trailing chars | ||
| $key = array_values(preg_grep("/^({$remove}|{$remove}=).*/i", $arguments)); | ||
| $remove = (isset($key[0])) ? $key[0] : $remove; | ||
|
|
||
| // find the position of arguments to remove | ||
| if (($position = array_search($remove, $arguments)) === false) { | ||
| return $arguments; | ||
| } | ||
|
|
||
| // remove argument | ||
| unset($arguments[$position]); | ||
|
|
||
| // if the next item in the array is not a flag, consider it a value of the removed argument | ||
| if (isset($arguments[$position + 1]) && substr($arguments[$position + 1], 0, 1) !== '-') { | ||
| unset($arguments[$position + 1]); | ||
| } | ||
|
|
||
| return array_values($arguments); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.