params->shift('build'); $include = $this->params->shift('include'); $phpUnit = exec('which phpunit'); if (!file_exists($phpUnit)) { $this->fail('PHPUnit not found. Please install PHPUnit to be able to run the unit-test suites.'); } $options = array(); if ($this->isVerbose) { $options[] = '--verbose --testdox'; } if ($build) { $reportPath = $this->setupAndReturnReportDirectory(); $options[] = '--log-junit'; $options[] = $reportPath . '/phpunit_results.xml'; $options[] = '--coverage-html'; $options[] = $reportPath . '/php_html_coverage'; } if ($include !== null) { $options[] = '--filter'; $options[] = $include; } $baseDir = Icinga::app()->getBaseDir(); $phpunitXml = new DOMDocument(); $temp = new TemporaryLocalFileStorage(); $phpunitXml->loadXML(file_get_contents("$baseDir/modules/test/phpunit.xml")); $this->adjustPhpunitDom($phpunitXml); $temp->create('phpunit.xml', $phpunitXml->saveXML()); chdir($baseDir); $command = $this->getEnvironmentVariables() . $phpUnit . " -c {$temp->resolvePath('phpunit.xml')}" . ' ' . join(' ', array_merge($options, $this->params->getAllStandalone())); if ($this->isVerbose) { $res = `$command`; foreach (preg_split('/\n/', $res) as $line) { if (preg_match('~\s+\[([x\s])\]\s~', $line, $m)) { if ($m[1] === 'x') { echo $this->screen->colorize($line, 'green') . "\n"; } else { echo $this->screen->colorize($line, 'red') . "\n"; } } else { echo $line . "\n"; } } } else { passthru($command); } } /** * Run code-style checks * * This command checks whether icingaweb and installed modules match the PSR-2 coding standard. * * USAGE * * icingacli test php style [options] * * OPTIONS * * --verbose Be more verbose. * --build Enable reporting. * --include Include only specific files. (Can be supplied multiple times.) * --exclude Pattern to use for excluding files. (Can be supplied multiple times.) * * EXAMPLES * * icingacli test php style --verbose * icingacli test php style --build * icingacli test php style --include=path/to/your/file * icingacli test php style --exclude=*someFile* --exclude=someOtherFile* * * NOTES * * Default rules are defined via `phpcs.xml` and `icingaweb2.ruleset.xml` in icingaweb' * installation directory. */ public function styleAction() { $build = $this->params->shift('build'); $include = (array) $this->params->shift('include', array()); $exclude = (array) $this->params->shift('exclude', array()); $phpcs = exec('which phpcs'); if (!file_exists($phpcs)) { $this->fail( 'PHP_CodeSniffer not found. Please install PHP_CodeSniffer to be able to run code style tests.' ); } $options = array(); if ($this->isVerbose) { $options[] = '-v'; } if ($build) { $options[] = '--report-checkstyle=' . $this->setupAndReturnReportDirectory(); } if (!empty($exclude)) { $options[] = '--ignore=' . join(',', $exclude); } $arguments = array_filter(array_map(function ($p) { return realpath($p); }, $include)); chdir(Icinga::app()->getBaseDir()); passthru( $phpcs . ' ' . join( ' ', array_merge( $options, $arguments, $this->params->getAllStandalone() ) ) ); } /** * Setup the directory where to put report files and return its path * * @return string */ protected function setupAndReturnReportDirectory() { $path = realpath(__DIR__ . '/../../../..') . '/build/log'; if (!file_exists($path) && !@mkdir($path, 0755, true)) { $this->fail("Could not create directory: $path"); } return $path; } /** * Setup some required environment variables */ protected function getEnvironmentVariables() { $modulePaths = []; foreach (Icinga::app()->getModuleManager()->getModuleInfo() as $module) { if (! file_exists($module->path . '/phpunit.xml')) { $modulePaths[] = $module->path; } } $vars = array(); $vars[] = sprintf('ICINGAWEB_BASEDIR=%s', $this->app->getBaseDir()); $vars[] = sprintf('ICINGAWEB_ICINGA_LIB=%s', $this->app->getLibraryDir('Icinga')); $vars[] = sprintf('ICINGAWEB_MODULE_DIRS=%s', implode(':', $modulePaths)); // Disabled as the bootstrap.php for PHPUnit and class BaseTestCase can't handle multiple paths yet /*$vars[] = sprintf( 'ICINGAWEB_MODULES_DIR=%s', implode(PATH_SEPARATOR, $this->app->getModuleManager()->getModuleDirs()) );*/ return join(' ', $vars) . ' '; } /** * Make all relative paths absolute and include all installed modules * * @param DOMDocument $phpunitXml */ protected function adjustPhpunitDom(DOMDocument $phpunitXml) { $app = Icinga::app(); $modulesTest = "{$app->getBaseDir()}/modules/test/"; $domPath = new DOMXPath($phpunitXml); $phpunit = $domPath->query("//phpunit")->item(0); $phpunit->setAttribute('bootstrap', $modulesTest . $phpunit->getAttribute('bootstrap')); foreach ([ '//phpunit/testsuites/testsuite/directory', '//phpunit/testsuites/testsuite/exclude', '//phpunit/filter/whitelist/directory', '//phpunit/filter/whitelist/exclude/directory', '//phpunit/filter/whitelist/exclude/file' ] as $xPath) { $nodes = $domPath->query($xPath); for ($i = 0; $i < $nodes->length; ++$i) { $element = $nodes->item($i); $element->nodeValue = $modulesTest . $element->nodeValue; } } $unitModules = $domPath->query("//phpunit/testsuites/testsuite[@name='unit-modules']")->item(0); $regressionModules = $domPath->query("//phpunit/testsuites/testsuite[@name='regression-modules']")->item(0); while ($unitModules->hasChildNodes()) { $unitModules->removeChild($unitModules->childNodes->item(0)); } while ($regressionModules->hasChildNodes()) { $regressionModules->removeChild($regressionModules->childNodes->item(0)); } foreach ($app->getModuleManager()->getModuleInfo() as $module) { $testPhp = "$module->path/test/php"; if (file_exists($testPhp) && ! file_exists($module->path . '/phpunit.xml')) { $unitModules->appendChild($phpunitXml->createElement('directory', $testPhp)); $testPhpRegression = "$testPhp/regression"; if (file_exists($testPhpRegression)) { $regressionModules->appendChild($phpunitXml->createElement('directory', $testPhpRegression)); $unitModules->appendChild($phpunitXml->createElement('exclude', $testPhpRegression)); } } } if (! $unitModules->hasChildNodes()) { $unitModules->parentNode->removeChild($unitModules); } if (! $regressionModules->hasChildNodes()) { $regressionModules->parentNode->removeChild($regressionModules); } } }