first commit

This commit is contained in:
2024-11-10 21:08:49 +01:00
commit 0d932ce5ee
14455 changed files with 2567501 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
class ArgumentsTest extends TestCase
{
public function numArgs(&$arg = null)
{
//echo 'number of arguments:' . func_num_args();
return func_num_args();
}
public function getArg0Default8(&$arg = 8)
{
return func_get_arg(0);
}
public function getArg0DefaultNull(&$arg = null)
{
//return $arg;
return func_get_arg(0);
}
public function isArg0Set(&$arg0 = null, &$arg1 = null)
{
//return (func_num_args() > 0);
$a = func_get_args();
try {
$b = $a[0];
} catch (\Exception $e) {
return false;
} catch (\Throwable $e) {
return false;
}
if (count($a) == 2) {
return isset($a[0]);
}
//print_r($a);
//echo count($a);
return true;
//return !is_null($arg0);
//return isset($a[0]);
//return count($a) > 0;
//return isset($a[0])
//return isset($a[0]);
}
public function testFuncNumArgs()
{
$this->assertSame(0, $this->numArgs());
$this->assertSame(1, $this->numArgs($arg));
}
public function supplied($command, &$output = null, &$result_code = null)
{
//echo "\n" . $command . ':' . func_num_args() . "\n";
$outputSupplied = (func_num_args() >= 2);
$resultCodeSupplied = (func_num_args() >= 3);
/*
$a = func_get_args();
//return (func_num_args() > 0);
$outputSupplied = true;
try {
$o = $a[1];
} catch (\Exception $e) {
$outputSupplied = false;
} catch (\Throwable $e) {
$outputSupplied = false;
}
if (count($a) == 3) {
//$outputSupplied = isset($a[1]);
}
$resultCodeSupplied = true;
try {
$r = $a[2];
} catch (\Exception $e) {
$resultCodeSupplied = false;
} catch (\Throwable $e) {
$resultCodeSupplied = false;
}*/
//echo $command;
//print_r($a);
$result = 0;
if ($outputSupplied) $result += 1;
if ($resultCodeSupplied) $result += 2;
return $result;
//return !is_null($arg0);
//return isset($a[0]);
//return count($a) > 0;
//return isset($a[0])
//return isset($a[0]);*/
}
public function testFuncGetArg()
{
$seven = 7;
//$this->assertSame(7, $this->getArg0Default8($seven));
/*$a=[];
$this->assertFalse(isset($a[1]));
$a[0] = null;
$this->assertTrue(isset($a[1]));*/
$this->assertSame(0, $this->supplied('only-one'));
$this->assertSame(1, $this->supplied('two', $seven));
$this->assertSame(3, $this->supplied('three', $seven, $seven));
$this->assertSame(1, $this->supplied('', $yetUndefined));
$this->assertSame(3, $this->supplied('', $yetUndefined, $yetUndefined));
/*
TODO: enable in PHP 8.0
$this->assertSame(3, $this->supplied(
command:'hi',
output: $yetUndefined,
result_code: $yetUndefined
));
// Sorry, the two below would be better if they return 2,
// but it seems impossible
// So, these are handled as if $output is supplied.
// - which means our selector will not choose system(), even though
// it could
$this->assertSame(3, $this->supplied(
command:'hi2',
result_code: $yetUndefined
));
$this->assertSame(3, $this->supplied(
'',
result_code: $yetUndefined
));
*/
/*
$this->assertFalse($this->isArg0Set());
$this->assertTrue($this->isArg0Set($seven));
$this->assertTrue($this->isArg0Set($yetUndefined));
// PHP 8:
$this->assertTrue($this->isArg0Set(arg0: $seven));
$this->assertTrue($this->isArg0Set(arg0: $yetUndefined));
$this->assertFalse($this->isArg0Set(arg1: $seven));
*/
//$this->assertSame(null, $this->getArg0Default8($yetUndefined));
//$this->assertSame(8,9);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
use ExecWithFallback\Availability;
class AvailabilityTest extends TestCase
{
public function testMethodAvailable()
{
$methods = ['exec', 'passthru', 'popen', 'proc_open', 'shell_exec'];
$anyAvailable = false;
$anyOtherThanShellExecAvailable = false;
foreach ($methods as $method) {
if (function_exists($method)) {
$anyAvailable = true;
if ($method == 'shell_exec') {
$this->assertTrue(Availability::methodAvailable($method, false));
$this->assertFalse(Availability::methodAvailable($method, true));
$this->assertFalse(Availability::methodAvailable($method));
} else {
$anyOtherThanShellExecAvailable = true;
$this->assertTrue(Availability::methodAvailable($method));
}
} else {
$this->assertFalse(Availability::methodAvailable($method));
}
}
$this->assertSame($anyAvailable, Availability::anyAvailable(false));
$this->assertSame($anyOtherThanShellExecAvailable, Availability::anyAvailable(true));
$this->assertSame($anyOtherThanShellExecAvailable, Availability::anyAvailable());
}
}

View File

@@ -0,0 +1,276 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
class BaseTest extends TestCase
{
public $className = '';
public $supportsResultCode = true;
public function checkAvailability()
{
$this->assertEquals(0, 0); // to awoid warnings about risky tests
return $this->isAvailable();
}
public function isAvailable()
{
$this->assertEquals(0, 0);
return function_exists('exec');
}
public function runExec($command, &$output = null, &$result_code = null)
{
return exec($command, $output, $result_code);
}
/*
Doesn't work with ProcOpen
public function testDevNull()
{
$output = [];
$execResult = $this->runExec('ls 1>/dev/null', $output, $result_code);
$this->assertEquals(0, $result_code);
$this->assertEquals(0, count($output));
$this->assertEquals('', $execResult);
}
*/
public function testNoReturnCodeSupplied()
{
if ($this->checkAvailability()) {
$output = [];
$execResult = $this->runExec('echo hi', $output);
$this->assertEquals('hi', $execResult);
}
}
public function testNoOutputSupplied()
{
if ($this->checkAvailability()) {
$result = $this->runExec('echo hi');
$this->assertEquals('hi', $result);
}
}
/*
TODO: enable this test in PHP 8
public function testNoOutputSuppliedButReturnCodeIs()
{
if ($this->checkAvailability()) {
//$result = $this->runExec('echo hi', null, $result_code);
//$result = exec('echo hi', null, $result_code);
// https://stackoverflow.com/questions/1066625/how-would-i-skip-optional-arguments-in-a-function-call
//$result = exec(command:'echo hi', result_code:$result_code);
$result = $this->runExec(command:'echo hi', result_code:$result_code);
$this->assertEquals('hi', $result);
$this->assertSame(0, $result_code);
}
}
*/
public function testTwoLines()
{
if ($this->checkAvailability()) {
if ($this->supportsResultCode) {
$result = $this->runExec('echo hi && echo world', $output, $result_code);
$this->assertEquals(0, $result_code);
} else {
$result = $this->runExec('echo hi && echo world', $output);
}
$this->assertEquals('world', $result);
$this->assertEquals(count($output), 2, print_r($output, true));
$this->assertEquals('hi', trim($output[0])); // "hi " on windows, using exec()
$this->assertEquals('world', $output[1]);
}
}
public function testWhiteSpace()
{
if ($this->checkAvailability()) {
if ($this->supportsResultCode) {
$result = $this->runExec('echo " hi " && echo " world "', $output, $result_code);
} else {
$result = $this->runExec('echo " hi " && echo " world "', $output);
}
$this->assertThat(
$result,
$this->logicalOr(
$this->identicalTo(' world'), // Linux
$this->identicalTo('" world "') // Windows
)
);
$this->assertThat(
$output,
$this->logicalOr(
$this->equalTo([' hi', ' world']), // Linux
$this->identicalTo(['" hi "', '" world "']) // Windows
)
);
}
}
public function testNoOutput()
{
if ($this->checkAvailability()) {
$result = $this->runExec('echo hi 1>/dev/null', $output);
$this->assertSame('', $result);
}
}
public function testStdErrPipedToStdOut()
{
if ($this->checkAvailability()) {
$result = $this->runExec('echo hi && aoeuaoeuaoeu 2>&1', $output);
$this->assertTrue(count($output) > 1);
}
}
public function testOutputIsInt()
{
if ($this->checkAvailability()) {
$output = 10;
if ($this->supportsResultCode) {
$result = $this->runExec('echo hi', $output, $result_code);
$this->assertEquals(0, $result_code);
} else {
$result = $this->runExec('echo hi', $output);
}
$this->assertSame('hi', $result);
$this->assertEquals('array', gettype($output));
$this->assertSame($output[0], 'hi');
}
}
public function testOutputIsString()
{
if ($this->checkAvailability()) {
$output = 'abc';
if ($this->supportsResultCode) {
$result = $this->runExec('echo hi', $output, $result_code);
$this->assertEquals(0, $result_code);
} else {
$result = $this->runExec('echo hi', $output);
}
$this->assertEquals('hi', $result);
$this->assertEquals('array', gettype($output));
$this->assertEquals('hi', $output[0]);
}
}
public function testOutputIsExistingArray()
{
if ($this->checkAvailability()) {
$output = ['abc'];
if ($this->supportsResultCode) {
$result = $this->runExec('echo hi', $output, $result_code);
$this->assertEquals(0, $result_code);
} else {
$result = $this->runExec('echo hi', $output);
}
$this->assertEquals('hi', $result);
$this->assertEquals('array', gettype($output));
$this->assertEquals('abc', $output[0]);
$this->assertEquals('hi', $output[1]);
}
}
public function testUnknownCommand()
{
if ($this->checkAvailability()) {
if ($this->supportsResultCode) {
$result = $this->runExec('aoebuaoeu', $output, $result_code);
$this->assertNotEquals(0, $result_code); // 127 on linux, 1 on windows
} else {
$result = $this->runExec('aoebuaoeu', $output);
}
$this->assertEquals('', $result);
}
}
public function testUnknownCommand2()
{
if ($this->checkAvailability()) {
if ($this->supportsResultCode) {
$result = $this->runExec('aoebuaoeu 2>&1', $output, $result_code);
$this->assertNotEquals(0, $result_code); // 127 on linux, 1 on windows
} else {
$result = $this->runExec('aoebuaoeu 2>&1', $output);
}
$this->assertSame('string', gettype($result));
$this->assertTrue(strlen($result) > 0);
}
}
public function testIsAvailable()
{
// The purpose of this test is merely to inform
//echo $this->className . function_exists('proc_open') ? 'yes' : 'no';
if ($this->isAvailable()) {
} else {
echo "\nNote: " . ($this->className ? $this->className : 'exec()') . ' is not tested, as it is unavailable';
}
$this->assertEquals(0, 0);
}
public function testWhenUnavailable()
{
if ($this->isAvailable()) {
$this->assertEquals(0, 0);
} else {
$hasThrown = false;
//$result = $this->runExec('echo "hi"', $output, $result_code);
try {
$result = $this->runExec('echo hi', $output, $result_code);
} catch (\Exception $e) {
$hasThrown = true;
} catch (\Throwable $e) {
$hasThrown = true;
}
$this->assertEquals(true, $hasThrown, "Expected it to throw when unavailable");
}
}
/*
// test wait script (only locally available...)
public function testWaitScript()
{
$result = $this->runExec('wait.sh 0.1 0', $output, $result_code);
$this->assertEquals(0, $result_code, 'wait.sh command did not return with exit code 0');
$this->assertEquals('Sleep for 0.1 seconds', $output[0]);
$this->assertEquals('OK', $output[1]);
}
// test wait script (only locally available...)
public function testWaitScriptReturnCode2()
{
$result = $this->runExec('wait.sh 0.1 2', $output, $result_code);
$this->assertEquals(2, $result_code, 'wait.sh command did not return with exit code 2');
$this->assertEquals('NOT OK', $output[1]);
}
public function testWaitScriptLongWait()
{
$result = $this->runExec('wait.sh 5 0', $output, $result_code);
$this->assertEquals(0, $result_code, 'wait.sh command did not return with exit code 0');
$this->assertEquals('OK', $output[1]);
}
*/
/*
public function all()
{
$this->devNull($className);
$this->noReturnCodeSupplied($className);
$this->noOutputSupplied($className);
}
*/
}

View File

@@ -0,0 +1,181 @@
<?php
namespace ExecWithFallback;
class ExecWithFallbackWithSetMethods extends ExecWithFallback
{
public static function setMethods($methods)
{
self::$methods = $methods;
}
public static function resetMethods()
{
self::$methods = ['exec', 'passthru', 'popen', 'proc_open', 'shell_exec'];
}
}
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
use ExecWithFallback\ExecWithFallback;
use ExecWithFallback\ExecWithFallbackWithSetMethods;
class ExecWithFallbackTest extends BaseTest
{
public $className = 'ExecWithFallback';
public function isAvailable()
{
return
ExecWithFallback::functionEnabled('exec') ||
ExecWithFallback::functionEnabled('proc_open') ||
ExecWithFallback::functionEnabled('passthru') ||
ExecWithFallback::functionEnabled('popen') ||
ExecWithFallback::functionEnabled('shell_exec');
}
public function runExec($command, &$output = null, &$result_code = null)
{
return ExecWithFallback::exec($command, $output, $result_code);
}
public function testFunctionEnabled()
{
$this->assertTrue(ExecWithFallback::functionEnabled('function_exists'));
$this->assertFalse(ExecWithFallback::functionEnabled('atoehutaoeut'));
$d = ini_get('disable_functions');
if (function_exists('ini_set') && ini_set('disable_functions', 'passthru')) {
$this->assertFalse(ExecWithFallback::functionEnabled('passthru'));
ini_set('disable_functions', $d);
}
$d = ini_get('suhosin.executor.func.blacklist');
if (function_exists('ini_set') && ini_set('suhosin.executor.func.blacklist', 'passthru')) {
$this->assertFalse(ExecWithFallback::functionEnabled('passthru'));
ini_set('suhosin.executor.func.blacklist', $d);
}
}
public function testRunExec()
{
if (ExecWithFallback::functionEnabled('exec')) {
$result = ExecWithFallback::runExec('exec', 'echo hi');
$this->assertEquals('hi', $result);
} elseif (ExecWithFallback::functionEnabled('passthru')) {
$result = ExecWithFallback::runExec('passthru', 'echo hi');
$this->assertEquals('hi', $result);
} else {
$this->assertTrue(true);
}
/*
if (ExecWithFallback::functionEnabled('shell_exec')) {
$result = ExecWithFallback::runExec('shell_exec', 'echo hi', $output, $result_code);
$this->assertFalse($result);
}*/
}
public function testAnyMethodAvailable()
{
$methods = ['exec', 'passthru', 'popen', 'proc_open', 'shell_exec'];
$anyAvailable = false;
$anyOtherThanShellExecAvailable = false;
foreach ($methods as $method) {
if (ExecWithFallback::functionEnabled($method)) {
$anyAvailable = true;
if ($method != 'shell_exec') {
$anyOtherThanShellExecAvailable = true;
}
}
}
$this->assertSame($anyAvailable, ExecWithFallback::anyAvailable(false));
$this->assertSame($anyOtherThanShellExecAvailable, ExecWithFallback::anyAvailable(true));
$this->assertSame($anyOtherThanShellExecAvailable, ExecWithFallback::anyAvailable());
}
public function testExec()
{
if (ExecWithFallback::anyAvailable()) {
$result = ExecWithFallback::exec('echo hi', $output);
$this->assertSame('hi', $result);
} else {
$exceptionThrown = true;
try {
$result = ExecWithFallback::exec('echo hi', $output);
} catch (\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown);
}
}
public function testExecNoMethods()
{
ExecWithFallbackWithSetMethods::setMethods([]);
$exceptionThrown = true;
try {
$result = ExecWithFallbackWithSetMethods::exec('echo hi', $output);
} catch (\Exception $e) {
$exceptionThrown = true;
}
ExecWithFallbackWithSetMethods::resetMethods();
$this->assertTrue($exceptionThrown);
}
public function testExecOnlyShellExec()
{
ExecWithFallbackWithSetMethods::setMethods(['shell_exec']);
$exceptionThrown = false;
try {
// shell_exec cannot be used with 3 arguments, so an exception is expected
$result = ExecWithFallbackWithSetMethods::exec('echo hi', $output, $result_code);
} catch (\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown);
if (ExecWithFallback::functionEnabled('shell_exec')) {
// shell_exec works with 2 arguments, so no exception is expected
$result = ExecWithFallbackWithSetMethods::exec('echo hm', $output);
$this->assertSame('hm', $result);
}
ExecWithFallbackWithSetMethods::resetMethods();
}
public function testExecOnlyPassthru()
{
ExecWithFallbackWithSetMethods::setMethods(['passthru']);
if (ExecWithFallback::functionEnabled('passthru')) {
// shell_exec works with 2 arguments, so no exception is expected
$result = ExecWithFallbackWithSetMethods::exec('echo hi', $output);
$this->assertSame('hi', $result);
}
ExecWithFallbackWithSetMethods::resetMethods();
}
/**
* This may throw FATAL! (but does not do in any of the systems in our CI)
*/
public function testExecNoMercyNoMethods()
{
ExecWithFallbackWithSetMethods::setMethods([]);
$exceptionThrown = false;
try {
$result = ExecWithFallbackWithSetMethods::exec('echo hi', $output);
} catch (\Exception $e) {
$exceptionThrown = true;
} catch (\Error $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown);
ExecWithFallbackWithSetMethods::resetMethods();
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
class LineSplittingTest extends TestCase
{
public function splitLines($text)
{
//return preg_split('/\r\n|\r|\n/', $text);
//return preg_split("/\\n\\r|\\r\\n|\\n|\\r/", $text);
//return preg_split('/\n\r|\r\n|\n|\r/', $text);
return preg_split('/\r\n|\n\r|\n|\r/', $text);
}
public function splitLinesNoEmpty($text)
{
//return preg_split('/\r\n|\r|\n/', $text);
//return preg_split("/\\n\\r|\\r\\n|\\n|\\r/", $text);
//return preg_split('/\n\r|\r\n|\n|\r/', $text);
return preg_split('/[\r\n]+/', $text);
}
public function testLineSplitting()
{
$this->assertEquals(['one', 'two', 'three'], $this->splitLines("one\r\ntwo\r\nthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLines("one\ntwo\nthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLines("one\rtwo\rthree"));
$this->assertEquals(['one', '', 'two', 'three'], $this->splitLines("one\n\ntwo\nthree"));
$this->assertEquals(['one', '', 'two', '', 'three'], $this->splitLines("one\r\n\ntwo\n\r\nthree"));
// the unusual \n\r sequence
$this->assertEquals(['one', 'two', 'three'], $this->splitLines("one\n\rtwo\n\rthree"));
$this->assertEquals(['one', '', 'two', '', 'three'], $this->splitLines("one\n\r\ntwo\n\n\rthree"));
}
public function testLineSplittingNoEmpty()
{
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\r\ntwo\r\nthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\ntwo\nthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\rtwo\rthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\n\ntwo\nthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\r\n\ntwo\n\r\nthree"));
// the unusual \n\r sequence
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\n\rtwo\n\rthree"));
$this->assertEquals(['one', 'two', 'three'], $this->splitLinesNoEmpty("one\n\r\ntwo\n\n\rthree"));
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
use ExecWithFallback\POpen;
class POpenTest extends BaseTest
{
public $className = 'POpen';
public function isAvailable()
{
return function_exists('popen');
}
public function runExec($command, &$output = null, &$result_code = null)
{
return POpen::exec($command, $output, $result_code);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace ExecWithFallback\Tests;
use ExecWithFallback\Passthru;
use PHPUnit\Framework\TestCase;
class PassthruTest extends BaseTest
{
public $className = 'Passthru';
public function isAvailable()
{
return function_exists('passthru');
}
public function runExec($command, &$output = null, &$result_code = null)
{
return Passthru::exec($command, $output, $result_code);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
use ExecWithFallback\ProcOpen;
class ProcOpenTest extends BaseTest
{
public $className = 'ProcOpen';
public function isAvailable()
{
return function_exists('proc_open');
}
public function runExec($command, &$output = null, &$result_code = null)
{
return ProcOpen::exec($command, $output, $result_code);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace ExecWithFallback\Tests;
use PHPUnit\Framework\TestCase;
use ExecWithFallback\ShellExec;
class ShellExecTest extends BaseTest
{
public $className = 'ShellExec';
public $supportsResultCode = false;
public function isAvailable()
{
return function_exists('shell_exec');
}
public function runExec($command, &$output = null, &$result_code = null)
{
if (func_num_args() == 3) {
return ShellExec::exec($command, $output, $result_code);
} else {
return ShellExec::exec($command, $output);
}
}
/*
public function testCodeSupplied()
{
if ($this->checkAvailability()) {
$output = [];
$execResult = $this->runExec('echo hi', $output, $return_code);
$this->assertFalse($execResult);
}
}*/
}