parent
32a7decc3e
commit
0805d73e34
|
@ -10,12 +10,20 @@ class Params
|
||||||
|
|
||||||
public function __construct($argv)
|
public function __construct($argv)
|
||||||
{
|
{
|
||||||
|
$noOptionFlag = false;
|
||||||
$this->program = array_shift($argv);
|
$this->program = array_shift($argv);
|
||||||
for ($i = 0; $i < count($argv); $i++) {
|
for ($i = 0; $i < count($argv); $i++) {
|
||||||
if (substr($argv[$i], 0, 2) === '--') {
|
if ($argv[$i] === '--') {
|
||||||
|
$noOptionFlag = true;
|
||||||
|
} elseif (!$noOptionFlag && substr($argv[$i], 0, 2) === '--') {
|
||||||
$key = substr($argv[$i], 2);
|
$key = substr($argv[$i], 2);
|
||||||
if (! isset($argv[$i + 1]) || substr($argv[$i + 1], 0, 2) === '--') {
|
if (! isset($argv[$i + 1]) || substr($argv[$i + 1], 0, 2) === '--') {
|
||||||
$this->params[$key] = true;
|
$this->params[$key] = true;
|
||||||
|
} elseif (array_key_exists($key, $this->params)) {
|
||||||
|
if (!is_array($this->params[$key])) {
|
||||||
|
$this->params[$key] = array($this->params[$key]);
|
||||||
|
}
|
||||||
|
$this->params[$key][] = $argv[++$i];
|
||||||
} else {
|
} else {
|
||||||
$this->params[$key] = $argv[++$i];
|
$this->params[$key] = $argv[++$i];
|
||||||
}
|
}
|
||||||
|
@ -43,6 +51,11 @@ class Params
|
||||||
return $this->params;
|
return $this->params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAllStandalone()
|
||||||
|
{
|
||||||
|
return $this->standalone;
|
||||||
|
}
|
||||||
|
|
||||||
public function __get($key)
|
public function __get($key)
|
||||||
{
|
{
|
||||||
return $this->get($key);
|
return $this->get($key);
|
||||||
|
@ -95,7 +108,14 @@ class Params
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
$result = $this->get($key, $default);
|
$result = $this->get($key, $default);
|
||||||
$this->remove($key);
|
if (is_array($result) && !is_array($default)) {
|
||||||
|
$result = array_shift($result) || $default;
|
||||||
|
if ($result === $default) {
|
||||||
|
$this->remove($key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->remove($key);
|
||||||
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
<?php
|
||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
namespace Icinga\Util;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process
|
||||||
|
*
|
||||||
|
* A class to easily create child processes and handle in-/output capabilities.
|
||||||
|
*/
|
||||||
|
class Process
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The resource representing the process
|
||||||
|
*
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
protected $resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pipes in use by the process
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $pipes = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The returncode of the process
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $returnCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new process
|
||||||
|
*
|
||||||
|
* @see Process::start()
|
||||||
|
*/
|
||||||
|
protected function __construct($cmd, $cwd = null, $stdout = null, $stderr = null, $stdin = null, $env = array())
|
||||||
|
{
|
||||||
|
$descriptorSpec = array();
|
||||||
|
if ($stdin !== null) {
|
||||||
|
$descriptorSpec[0] = $stdin === 'pipe' ? array('pipe', 'r') : (
|
||||||
|
is_string($stdin) ? array('file', $stdin, 'r') : $stdin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($stdout !== null) {
|
||||||
|
$descriptorSpec[1] = $stdout === 'pipe' ? array('pipe', 'w') : (
|
||||||
|
is_string($stdout) ? array('file', $stdout, 'a') : $stdout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($stderr !== null) {
|
||||||
|
$descriptorSpec[2] = $stderr === 'pipe' ? array('pipe', 'w') : (
|
||||||
|
is_string($stderr) ? array('file', $stderr, 'a') : $stderr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->resource = proc_open(
|
||||||
|
$cmd,
|
||||||
|
$descriptorSpec,
|
||||||
|
$this->pipes,
|
||||||
|
$cwd,
|
||||||
|
$env
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!is_resource($this->resource)) {
|
||||||
|
throw new RuntimeException("Cannot start process: $cmd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start and return a new process
|
||||||
|
*
|
||||||
|
* @param string $cmd The command to start the process
|
||||||
|
* @param string $cwd The working directory of the new process (Must be an absolute path or null)
|
||||||
|
* @param string $stdout A filedescriptor, "pipe" or a filepath
|
||||||
|
* @param string $stderr A filedescriptor, "pipe" or a filepath
|
||||||
|
* @param string $stdin A filedescriptor, "pipe" or a filepath
|
||||||
|
* @param array $env The environment variables (Must be an array or null)
|
||||||
|
*
|
||||||
|
* @return Process
|
||||||
|
*
|
||||||
|
* @throws RuntimeException When the process could not be started
|
||||||
|
*/
|
||||||
|
public static function start($cmd, $cwd = null, $stdout = null, $stderr = null, $stdin = null, $env = array())
|
||||||
|
{
|
||||||
|
return new static($cmd, $cwd, $stdout, $stderr, $stdin, $env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interact with process
|
||||||
|
*
|
||||||
|
* Send data to stdin. Read data from stdout and stderr, until end-of-file is reached.
|
||||||
|
* Wait for process to terminate. The optional input argument should be a string to be
|
||||||
|
* sent to the child process, or null, if no data should be sent to the child.
|
||||||
|
*
|
||||||
|
* Note that you need to pass the equivalent pipes to the constructor for this to work.
|
||||||
|
*
|
||||||
|
* @param string $input Data to send to the child.
|
||||||
|
*
|
||||||
|
* @return array The data from stdout and stderr.
|
||||||
|
*/
|
||||||
|
public function communicate($input = null)
|
||||||
|
{
|
||||||
|
if (!isset($this->pipes[1]) && !isset($this->pipes[2])) {
|
||||||
|
$this->wait();
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$read = $write = array();
|
||||||
|
if (isset($this->pipes[0])) {
|
||||||
|
$write[] = $this->pipes[0];
|
||||||
|
}
|
||||||
|
if (isset($this->pipes[1])) {
|
||||||
|
$read[] = $this->pipes[1];
|
||||||
|
stream_set_blocking($this->pipes[1], 0);
|
||||||
|
}
|
||||||
|
if (isset($this->pipes[2])) {
|
||||||
|
$read[] = $this->pipes[2];
|
||||||
|
stream_set_blocking($this->pipes[2], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stdout = $stderr = '';
|
||||||
|
$readToWatch = $read;
|
||||||
|
$writeToWatch = $write;
|
||||||
|
$exceptToWatch = array();
|
||||||
|
while (stream_select($readToWatch, $writeToWatch, $exceptToWatch, 0, 20000) !== false) {
|
||||||
|
if (!empty($writeToWatch) && $input) {
|
||||||
|
$input = substr($input, fwrite($writeToWatch[0], $input));
|
||||||
|
}
|
||||||
|
foreach ($readToWatch as $pipe) {
|
||||||
|
if (isset($this->pipes[1]) && $pipe === $this->pipes[1]) {
|
||||||
|
$stdout .= stream_get_contents($pipe);
|
||||||
|
} elseif (isset($this->pipes[2]) && $pipe === $this->pipes[2]) {
|
||||||
|
$stderr .= stream_get_contents($pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$readToWatch = array_filter($read, function ($h) { return !feof($h); });
|
||||||
|
$writeToWatch = $input ? $write : array();
|
||||||
|
if (empty($readToWatch) && empty($writeToWatch)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->wait(); // To ensure the process is actually stopped when calling cleanUp() we utilize wait()
|
||||||
|
return array($stdout, $stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the process is still alive and set the returncode
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function poll()
|
||||||
|
{
|
||||||
|
if ($this->resource !== null) {
|
||||||
|
$info = @proc_get_status($this->resource);
|
||||||
|
if ($info !== false) {
|
||||||
|
if ($info['running']) {
|
||||||
|
return true;
|
||||||
|
} elseif ($info['exitcode'] !== -1) {
|
||||||
|
$this->returnCode = $info['exitcode'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for process to terminate and return its returncode
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function wait()
|
||||||
|
{
|
||||||
|
if ($this->returnCode === null && $this->resource !== null) {
|
||||||
|
while ($this->poll()) {
|
||||||
|
usleep(500000);
|
||||||
|
}
|
||||||
|
$this->cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->returnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup the process resource and its associated pipes
|
||||||
|
*/
|
||||||
|
protected function cleanUp()
|
||||||
|
{
|
||||||
|
foreach ($this->pipes as $pipe) {
|
||||||
|
if (is_resource($pipe)) {
|
||||||
|
fclose($pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc_close($this->resource);
|
||||||
|
$this->resource = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
<?php
|
||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
namespace Icinga\Module\Test\Clicommands;
|
||||||
|
|
||||||
|
use Icinga\Cli\Command;
|
||||||
|
use Icinga\Util\Process;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP unit- & style-tests
|
||||||
|
*/
|
||||||
|
class PhpCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default arguments and options for PHP_CodeSniffer
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $phpcsDefaultParams = array(
|
||||||
|
'-p',
|
||||||
|
'--standard=PSR2',
|
||||||
|
'--extensions=php',
|
||||||
|
'--encoding=utf-8'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all unit-test suites
|
||||||
|
*
|
||||||
|
* This command runs the unit- and regression-tests of icingaweb and installed modules.
|
||||||
|
*
|
||||||
|
* USAGE
|
||||||
|
*
|
||||||
|
* icingacli test php unit [options]
|
||||||
|
*
|
||||||
|
* OPTIONS
|
||||||
|
*
|
||||||
|
* --verbose Be more verbose.
|
||||||
|
* --build Enable reporting.
|
||||||
|
* --include Pattern to use for including files/test cases.
|
||||||
|
*
|
||||||
|
* EXAMPLES
|
||||||
|
*
|
||||||
|
* icingacli test php unit --verbose
|
||||||
|
* icingacli test php unit --build
|
||||||
|
* icingacli test php unit --include *SpecialTest
|
||||||
|
*/
|
||||||
|
public function unitAction()
|
||||||
|
{
|
||||||
|
$build = $this->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';
|
||||||
|
}
|
||||||
|
if ($build) {
|
||||||
|
$reportPath = $this->setupAndReturnReportDirectory();
|
||||||
|
echo $reportPath;
|
||||||
|
$options[] = '--log-junit';
|
||||||
|
$options[] = $reportPath . '/phpunit_results.xml';
|
||||||
|
$options[] = '--coverage-html';
|
||||||
|
$options[] = $reportPath . '/php_html_coverage';
|
||||||
|
}
|
||||||
|
if ($include !== null) {
|
||||||
|
$options[] = '--filter';
|
||||||
|
$options[] = $include;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::start(
|
||||||
|
$phpUnit . ' ' . join(' ', array_merge($options, $this->params->getAllStandalone())),
|
||||||
|
realpath(__DIR__ . '/../..')
|
||||||
|
)->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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*
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
if (empty($arguments)) {
|
||||||
|
$arguments = array(
|
||||||
|
realpath(__DIR__ . '/../../../../application'),
|
||||||
|
realpath(__DIR__ . '/../../../../library/Icinga')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::start(
|
||||||
|
$phpcs . ' ' . join(
|
||||||
|
' ',
|
||||||
|
array_merge(
|
||||||
|
$options,
|
||||||
|
$this->phpcsDefaultParams,
|
||||||
|
$arguments,
|
||||||
|
$this->params->getAllStandalone()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
realpath(__DIR__ . '/../..')
|
||||||
|
)->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue