قرینه از
https://github.com/matomo-org/matomo.git
synced 2025-08-21 22:47:43 +00:00

* [Coding Style] Enable rule PSR12.Files.FileHeader * Apply CS * Replace Piwik with Matomo in file headers * Unify file headers (position, no. of lines, https links) * Rebuild dist files * Apply CS * Fix system test that relies on line numbers in a file that had the file header updated --------- Co-authored-by: Stefan Giehl <stefan@matomo.org>
303 خطوط
10 KiB
PHP
303 خطوط
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*/
|
|
|
|
namespace Piwik\Plugins\TestRunner\Commands;
|
|
|
|
use Piwik\Db;
|
|
use Piwik\Plugin;
|
|
use Piwik\Profiler;
|
|
use Piwik\Plugin\ConsoleCommand;
|
|
|
|
/**
|
|
* Executes PHP tests.
|
|
*/
|
|
class TestsRun extends ConsoleCommand
|
|
{
|
|
private $returnVar = 0;
|
|
|
|
protected function configure()
|
|
{
|
|
$this->setName('tests:run');
|
|
$this->setDescription('Run Piwik PHPUnit tests one testsuite after the other');
|
|
$this->addOptionalArgument('variables', 'Eg a path to a file or directory, the name of a testsuite, the name of a plugin, ... We will try to detect what you meant. You can define multiple values', [], true);
|
|
$this->addOptionalValueOption('options', 'o', 'All options will be forwarded to phpunit', '');
|
|
$this->addOptionalValueOption('filter', null, 'Adds the phpunit filter option to run only specific tests that start with the given name', '');
|
|
$this->addNoValueOption('xhprof', null, 'Profile using xhprof.');
|
|
$this->addRequiredValueOption('group', null, 'Run only a specific test group. Separate multiple groups by comma, for instance core,plugins', '');
|
|
$this->addRequiredValueOption('file', null, 'Execute tests within this file. Should be a path relative to the tests/PHPUnit directory.');
|
|
$this->addRequiredValueOption('testsuite', null, 'Execute tests of a specific test suite, for instance unit, integration or system.');
|
|
$this->addNoValueOption('enable-logging', null, 'Enable logging to the configured log file during tests.');
|
|
}
|
|
|
|
protected function doExecute(): int
|
|
{
|
|
$input = $this->getInput();
|
|
$output = $this->getOutput();
|
|
$options = $input->getOption('options');
|
|
$groups = $input->getOption('group');
|
|
$magics = $input->getArgument('variables');
|
|
$matomoDomain = $input->getOption('matomo-domain');
|
|
$enableLogging = $input->getOption('enable-logging');
|
|
$filter = $input->getOption('filter');
|
|
|
|
if (!empty($filter)) {
|
|
$options .= ' --filter=' . escapeshellarg($filter);
|
|
}
|
|
|
|
$groups = $this->getGroupsFromString($groups);
|
|
|
|
// bin is the composer executeable directory, where all vendors (should) place their executables
|
|
$command = PIWIK_VENDOR_PATH . '/bin/phpunit';
|
|
|
|
if (!$this->isCoverageEnabled($options) && $this->isXdebugLoaded()) {
|
|
$message = 'Did you know? You can run tests faster by disabling xdebug';
|
|
if ($this->isXdebugCodeCoverageEnabled()) {
|
|
$message .= ' (if you need xdebug, speed up tests by setting xdebug.coverage_enable=0)</comment>';
|
|
}
|
|
$output->writeln('<comment>' . $message . '</comment>');
|
|
}
|
|
|
|
// force xdebug usage for coverage options
|
|
if ($this->isCoverageEnabled($options) && !$this->isXdebugLoaded()) {
|
|
$output->writeln('<info>xdebug extension required for code coverage.</info>');
|
|
|
|
$output->writeln('<info>searching for xdebug extension...</info>');
|
|
|
|
$extensionDir = shell_exec('php-config --extension-dir');
|
|
$xdebugFile = trim($extensionDir) . DIRECTORY_SEPARATOR . 'xdebug.so';
|
|
|
|
if (!file_exists($xdebugFile)) {
|
|
$xdebugFile = $this->askAndValidate(
|
|
'xdebug not found. Please provide path to xdebug.so',
|
|
function ($xdebugFile) {
|
|
return file_exists($xdebugFile);
|
|
}
|
|
);
|
|
} else {
|
|
$output->writeln('<info>xdebug extension found in extension path.</info>');
|
|
}
|
|
|
|
$output->writeln("<info>using $xdebugFile as xdebug extension.</info>");
|
|
|
|
$phpunitPath = trim(shell_exec('which phpunit') ?? '');
|
|
|
|
$command = sprintf('php -d zend_extension=%s %s', $xdebugFile, $phpunitPath);
|
|
}
|
|
|
|
if ($input->getOption('xhprof')) {
|
|
Profiler::setupProfilerXHProf($isMainRun = true);
|
|
|
|
putenv('PIWIK_USE_XHPROF=1');
|
|
}
|
|
|
|
$suite = $this->getTestsuite();
|
|
$testFile = $this->getTestFile();
|
|
|
|
if (!empty($magics)) {
|
|
foreach ($magics as $magic) {
|
|
if (empty($suite) && (in_array($magic, $this->getTestsSuites()))) {
|
|
$suite = $this->buildTestSuiteName($magic);
|
|
} elseif (empty($testFile) && 'core' === $magic) {
|
|
$testFile = $this->fixPathToTestFileOrDirectory('tests/PHPUnit');
|
|
} elseif (empty($testFile) && 'plugins' === $magic) {
|
|
$testFile = $this->fixPathToTestFileOrDirectory('plugins');
|
|
} elseif (empty($testFile) && file_exists($magic)) {
|
|
$testFile = $this->fixPathToTestFileOrDirectory($magic);
|
|
} elseif (empty($testFile) && $this->getPluginTestFolderName($magic)) {
|
|
$testFile = $this->getPluginTestFolderName($magic);
|
|
} elseif (empty($groups)) {
|
|
$groups = $this->getGroupsFromString($magic);
|
|
} else {
|
|
$groups[] = $magic;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tear down any DB that already exists
|
|
Db::destroyDatabaseObject();
|
|
|
|
$this->executeTests($matomoDomain, $suite, $testFile, $groups, $options, $command, $enableLogging);
|
|
|
|
return $this->returnVar;
|
|
}
|
|
|
|
private function getPluginTestFolderName($name)
|
|
{
|
|
$pluginName = $this->getPluginName($name);
|
|
|
|
$folder = '';
|
|
if (!empty($pluginName)) {
|
|
$path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginName;
|
|
|
|
if (is_dir($path . '/tests')) {
|
|
$folder = $this->fixPathToTestFileOrDirectory($path . '/tests');
|
|
} elseif (is_dir($path . '/Tests')) {
|
|
$folder = $this->fixPathToTestFileOrDirectory($path . '/Tests');
|
|
}
|
|
}
|
|
|
|
return $folder;
|
|
}
|
|
|
|
private function getPluginName($name)
|
|
{
|
|
$pluginNames = Plugin\Manager::getInstance()->getAllPluginsNames();
|
|
|
|
foreach ($pluginNames as $pluginName) {
|
|
if (strtolower($pluginName) === strtolower($name)) {
|
|
return $pluginName;
|
|
}
|
|
}
|
|
}
|
|
|
|
private function getTestFile()
|
|
{
|
|
$testFile = $this->getInput()->getOption('file');
|
|
|
|
if (empty($testFile)) {
|
|
return '';
|
|
}
|
|
|
|
return $this->fixPathToTestFileOrDirectory($testFile);
|
|
}
|
|
|
|
private function executeTests($piwikDomain, $suite, $testFile, $groups, $options, $command, $enableLogging)
|
|
{
|
|
if (empty($suite) && empty($groups) && empty($testFile)) {
|
|
foreach ($this->getTestsSuites() as $suite) {
|
|
$suite = $this->buildTestSuiteName($suite);
|
|
$this->executeTests($piwikDomain, $suite, $testFile, $groups, $options, $command, $enableLogging);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
$params = $this->buildPhpUnitCliParams($suite, $groups, $options);
|
|
|
|
if (!empty($testFile)) {
|
|
$params = $params . " " . $testFile;
|
|
}
|
|
|
|
$this->executeTestRun($piwikDomain, $command, $params, $enableLogging);
|
|
}
|
|
|
|
private function executeTestRun($piwikDomain, $command, $params, $enableLogging)
|
|
{
|
|
$output = $this->getOutput();
|
|
$envVars = '';
|
|
if (!empty($piwikDomain)) {
|
|
$envVars .= "PIWIK_DOMAIN=$piwikDomain";
|
|
}
|
|
if (!empty($enableLogging)) {
|
|
$envVars .= " MATOMO_TESTS_ENABLE_LOGGING=1";
|
|
}
|
|
|
|
$cmd = $this->getCommand($envVars, $command, $params);
|
|
$output->writeln('Executing command: <info>' . $cmd . '</info>');
|
|
passthru($cmd, $returnVar);
|
|
$output->writeln("");
|
|
|
|
$this->returnVar += $returnVar;
|
|
}
|
|
|
|
private function getTestsSuites()
|
|
{
|
|
return array('unit', 'integration', 'system', 'plugin');
|
|
}
|
|
|
|
/**
|
|
* @param $command
|
|
* @param $params
|
|
* @return string
|
|
*/
|
|
private function getCommand($envVars, $command, $params)
|
|
{
|
|
return sprintf('cd %s/tests/PHPUnit && %s %s %s', PIWIK_DOCUMENT_ROOT, $envVars, $command, $params);
|
|
}
|
|
|
|
private function buildPhpUnitCliParams($suite, $groups, $options)
|
|
{
|
|
$params = $options . " ";
|
|
|
|
if (!empty($groups)) {
|
|
$groups = implode(',', $groups);
|
|
$params .= '--group ' . $groups . ' ';
|
|
} else {
|
|
$groups = '';
|
|
}
|
|
|
|
if (!empty($suite)) {
|
|
$params .= ' --testsuite ' . $suite;
|
|
} else {
|
|
$suite = '';
|
|
}
|
|
|
|
$params = str_replace('%suite%', $suite, $params);
|
|
$params = str_replace('%group%', $groups, $params);
|
|
|
|
return $params;
|
|
}
|
|
|
|
private function getTestsuite()
|
|
{
|
|
$suite = $this->getInput()->getOption('testsuite');
|
|
|
|
if (empty($suite)) {
|
|
return;
|
|
}
|
|
|
|
$availableSuites = $this->getTestsSuites();
|
|
|
|
if (!in_array($suite, $availableSuites)) {
|
|
throw new \InvalidArgumentException('Invalid testsuite specified. Use one of: ' . implode(', ', $availableSuites));
|
|
}
|
|
|
|
$suite = $this->buildTestSuiteName($suite);
|
|
|
|
return $suite;
|
|
}
|
|
|
|
private function buildTestSuiteName($suite)
|
|
{
|
|
return ucfirst($suite) . 'Tests';
|
|
}
|
|
|
|
private function isCoverageEnabled($options)
|
|
{
|
|
return false !== strpos($options, '--coverage');
|
|
}
|
|
|
|
private function isXdebugLoaded()
|
|
{
|
|
return extension_loaded('xdebug');
|
|
}
|
|
|
|
private function isXdebugCodeCoverageEnabled()
|
|
{
|
|
return (bool)ini_get('xdebug.coverage_enable');
|
|
}
|
|
|
|
private function fixPathToTestFileOrDirectory($testFile)
|
|
{
|
|
if ('/' !== substr($testFile, 0, 1)) {
|
|
$testFile = '../../' . $testFile;
|
|
}
|
|
|
|
return $testFile;
|
|
}
|
|
|
|
private function getGroupsFromString($groups)
|
|
{
|
|
$groups = explode(",", $groups);
|
|
$groups = array_filter($groups, 'strlen');
|
|
|
|
return $groups;
|
|
}
|
|
}
|