Skip to content

Commit

Permalink
Add PhpCodeValidityTest
Browse files Browse the repository at this point in the history
In addition to php -l and phpcs this can discover
e.g. method signature incompatibilities
  • Loading branch information
Al2Klimov committed May 10, 2019
1 parent ef470b3 commit 76c4c06
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 1 deletion.
104 changes: 103 additions & 1 deletion modules/test/application/clicommands/PhpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Icinga\Application\Icinga;
use Icinga\Cli\Command;
use Icinga\File\Storage\TemporaryLocalFileStorage;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

/**
* PHP unit- & style-tests
Expand Down Expand Up @@ -79,7 +81,7 @@ public function unitAction()
. $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) {
Expand Down Expand Up @@ -166,6 +168,106 @@ public function styleAction()
);
}

/**
* Run code-validity checks
*
* This command checks whether icingaweb and installed modules match PHP syntax.
*
* USAGE
*
* icingacli test php validity
*/
public function validityAction()
{
$types = array(
T_CLASS => 'class',
T_INTERFACE => 'interface'
);

if (version_compare(PHP_VERSION, '5.4.0') > -1) {
$types[T_TRAIT] = 'trait';
}

$app = Icinga::app();
$mods = $app->getModuleManager();

$dirs = array($app->getBaseDir() => '~\A(?:test|modules|library/(?:vendor|Icinga/Test))(?:/|\z)~');

foreach ($mods->listEnabledModules() as $module) {
$dirs[$mods->getModuleDir($module)] = '~\A(?:test|library/[^/]+/ProvidedHook)(?:/|\z)~';
}

$files = array();

foreach ($dirs as $dir => $excludes) {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));

foreach ($iterator as $path => $info) {
/** @var \SplFileInfo $info */
if (preg_match($excludes, $iterator->getInnerIterator()->getSubPath()) || ! (
$info->isFile() && preg_match('/\.php\z/', $path)
)) {
continue;
}

$content = file_get_contents($path);
$lines = explode("\n", $content);
$tokens = token_get_all($content);
$lastDocComment = '';

foreach ($tokens as $token) {
if (! is_array($token)) {
continue;
}

list($tokenNr, $raw, $lineNr) = $token;

if ($tokenNr === T_DOC_COMMENT) {
$lastDocComment = $raw;
continue;
}

if (array_key_exists($tokenNr, $types)) {
$matches = array();
if (preg_match('/\A\s*(\w+)\s+\w+/', $lines[$lineNr - 1], $matches)) {
list($_, $type) = $matches;

if ($type === $types[$tokenNr]) {
// Valid definition header

if (! preg_match('/@deprecated\b/', $lastDocComment)) {
$files[] = $path;
}
}
}

// Bad definition header
break;
}
}

// No definition header
}
}

define('ICINGA_LIBDIR', $app->getLibraryDir());

require_once 'HTMLPurifier/Bootstrap.php';
require_once 'HTMLPurifier.php';

$oldErrorReportingLevel = error_reporting();
error_reporting($oldErrorReportingLevel & ~ E_DEPRECATED);

require_once 'HTMLPurifier.autoload.php';

error_reporting($oldErrorReportingLevel);

foreach ($files as $file) {
printf('+ require_once %s;%s', var_export($file, true), PHP_EOL);
require_once $file;
}
}

/**
* Setup the directory where to put report files and return its path
*
Expand Down
56 changes: 56 additions & 0 deletions test/php/library/Icinga/Application/PhpCodeValidityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */

namespace Tests\Icinga\Application;

use Icinga\Application\Config;
use Icinga\Data\ConfigObject;
use Icinga\File\Storage\TemporaryLocalFileStorage;
use Icinga\Test\BaseTestCase;

class PhpCodeValidityTest extends BaseTestCase
{
/**
* Collect all classes, interfaces and traits and let PHP validate them as if included
*/
public function testAllClassesInterfacesAndTraits()
{
$modDirs = array();
$modules = array();

foreach (preg_split('/:/', getenv('ICINGAWEB_MODULE_DIRS'), -1, PREG_SPLIT_NO_EMPTY) as $modDir) {
$modDirs[dirname($modDir)] = null;
$modules[] = basename($modDir);
}

$storage = new TemporaryLocalFileStorage();

$storage->create('etc/icingaweb2/config.ini', (string) new Config(new ConfigObject(array(
'global' => array(
'module_path' => implode(':', array_keys($modDirs)),
'config_backend' => 'ini'
)
))));

$icingacli = 'ICINGAWEB_CONFIGDIR=' . $storage->resolvePath('etc/icingaweb2')
. ' ' . realpath('/proc/self/exe') . ' ' . (
getenv('ICINGAWEB_BASEDIR') ?: realpath(__DIR__ . '/../../../../..')
) . '/bin/icingacli';

foreach ($modules as $module) {
$this->system("$icingacli module enable $module");
}

$this->system("$icingacli test php validity");
}

protected function system($command)
{
echo "+ $command" . PHP_EOL;

$return = 127;
system($command, $return);

$this->assertSame(0, $return);
}
}

0 comments on commit 76c4c06

Please sign in to comment.