mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 08:12:40 +01:00
Introduce PHP8.1 replacement functions for string tests which may take multiple types
Summary: Ref T13588. Adds "phutil_nonempty_string()" and similar methods to support PHP8.1 compatibility. Test Plan: Added unit tests, ran unit tests. Maniphest Tasks: T13588 Differential Revision: https://secure.phabricator.com/D21762
This commit is contained in:
parent
1fc4439ca5
commit
f098e8d863
4 changed files with 339 additions and 0 deletions
|
@ -1014,6 +1014,9 @@ phutil_register_library_map(array(
|
||||||
'phutil_load_library' => 'init/lib/moduleutils.php',
|
'phutil_load_library' => 'init/lib/moduleutils.php',
|
||||||
'phutil_loggable_string' => 'utils/utils.php',
|
'phutil_loggable_string' => 'utils/utils.php',
|
||||||
'phutil_microseconds_since' => 'utils/utils.php',
|
'phutil_microseconds_since' => 'utils/utils.php',
|
||||||
|
'phutil_nonempty_scalar' => 'utils/utils.php',
|
||||||
|
'phutil_nonempty_string' => 'utils/utils.php',
|
||||||
|
'phutil_nonempty_stringlike' => 'utils/utils.php',
|
||||||
'phutil_parse_bytes' => 'utils/viewutils.php',
|
'phutil_parse_bytes' => 'utils/viewutils.php',
|
||||||
'phutil_partition' => 'utils/utils.php',
|
'phutil_partition' => 'utils/utils.php',
|
||||||
'phutil_passthru' => 'future/exec/execx.php',
|
'phutil_passthru' => 'future/exec/execx.php',
|
||||||
|
|
|
@ -154,6 +154,147 @@ abstract class PhutilTestCase extends Phobject {
|
||||||
throw new PhutilTestSkippedException($message);
|
throw new PhutilTestSkippedException($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function assertCaught(
|
||||||
|
$expect,
|
||||||
|
$actual,
|
||||||
|
$message = null) {
|
||||||
|
|
||||||
|
if ($message !== null) {
|
||||||
|
$message = phutil_string_cast($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actual === null) {
|
||||||
|
// This is okay: no exception.
|
||||||
|
} else if ($actual instanceof Exception) {
|
||||||
|
// This is also okay.
|
||||||
|
} else if ($actual instanceof Throwable) {
|
||||||
|
// And this is okay too.
|
||||||
|
} else {
|
||||||
|
// Anything else is no good.
|
||||||
|
|
||||||
|
if ($message !== null) {
|
||||||
|
$output = pht(
|
||||||
|
'Call to "assertCaught(..., <junk>, ...)" for test case "%s" '.
|
||||||
|
'passed bad value for test result. Expected null, Exception, '.
|
||||||
|
'or Throwable; got: %s.',
|
||||||
|
$message,
|
||||||
|
phutil_describe_type($actual));
|
||||||
|
} else {
|
||||||
|
$output = pht(
|
||||||
|
'Call to "assertCaught(..., <junk>, ...)" passed bad value for '.
|
||||||
|
'test result. Expected null, Exception, or Throwable; got: %s.',
|
||||||
|
phutil_describe_type($actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->failTest($output);
|
||||||
|
|
||||||
|
throw new PhutilTestTerminatedException($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
$expect_list = null;
|
||||||
|
|
||||||
|
if ($expect === false) {
|
||||||
|
$expect_list = array();
|
||||||
|
} else if ($expect === true) {
|
||||||
|
$expect_list = array(
|
||||||
|
'Exception',
|
||||||
|
'Throwable',
|
||||||
|
);
|
||||||
|
} else if (is_string($expect) || is_array($expect)) {
|
||||||
|
$list = (array)$expect;
|
||||||
|
|
||||||
|
$items_ok = true;
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
if (!phutil_nonempty_stringlike($item)) {
|
||||||
|
$items_ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$list[$key] = phutil_string_cast($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($items_ok) {
|
||||||
|
$expect_list = $list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expect_list === null) {
|
||||||
|
if ($message !== null) {
|
||||||
|
$output = pht(
|
||||||
|
'Call to "assertCaught(<junk>, ...)" for test case "%s" '.
|
||||||
|
'passed bad expected value. Expected bool, class name as a string, '.
|
||||||
|
'or a list of class names. Got: %s.',
|
||||||
|
$message,
|
||||||
|
phutil_describe_type($expect));
|
||||||
|
} else {
|
||||||
|
$output = pht(
|
||||||
|
'Call to "assertCaught(<junk>, ...)" passed bad expected value. '.
|
||||||
|
'expected result. Expected null, Exception, or Throwable; got: %s.',
|
||||||
|
phutil_describe_type($expect));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->failTest($output);
|
||||||
|
|
||||||
|
throw new PhutilTestTerminatedException($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actual === null) {
|
||||||
|
$is_match = !$expect_list;
|
||||||
|
} else {
|
||||||
|
$is_match = false;
|
||||||
|
foreach ($expect_list as $exception_class) {
|
||||||
|
if ($actual instanceof $exception_class) {
|
||||||
|
$is_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_match) {
|
||||||
|
$this->assertions++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$caller = self::getCallerInfo();
|
||||||
|
$file = $caller['file'];
|
||||||
|
$line = $caller['line'];
|
||||||
|
|
||||||
|
$output = array();
|
||||||
|
|
||||||
|
if ($message !== null) {
|
||||||
|
$output[] = pht(
|
||||||
|
'Assertion of caught exception failed (at %s:%d in test case "%s").',
|
||||||
|
$file,
|
||||||
|
$line,
|
||||||
|
$message);
|
||||||
|
} else {
|
||||||
|
$output[] = pht(
|
||||||
|
'Assertion of caught exception failed (at %s:%d).',
|
||||||
|
$file,
|
||||||
|
$line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actual === null) {
|
||||||
|
$output[] = pht('Expected any exception, got no exception.');
|
||||||
|
} else if (!$expect_list) {
|
||||||
|
$output[] = pht(
|
||||||
|
'Expected no exception, got exception of class "%s".',
|
||||||
|
get_class($actual));
|
||||||
|
} else {
|
||||||
|
$expected_classes = implode(', ', $expect_list);
|
||||||
|
$output[] = pht(
|
||||||
|
'Expected exception (in class(es): %s), got exception of class "%s".',
|
||||||
|
$expected_classes,
|
||||||
|
get_class($actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = implode("\n\n", $output);
|
||||||
|
|
||||||
|
$this->failTest($output);
|
||||||
|
|
||||||
|
throw new PhutilTestTerminatedException($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Exception Handling )------------------------------------------------- */
|
/* -( Exception Handling )------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -1000,4 +1000,74 @@ final class PhutilUtilsTestCase extends PhutilTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEmptyStringMethods() {
|
||||||
|
|
||||||
|
$uri = new PhutilURI('http://example.org/');
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
array(null, false, false, false, 'literal null'),
|
||||||
|
array('', false, false, false, 'empty string'),
|
||||||
|
array('x', true, true, true, 'nonempty string'),
|
||||||
|
array(false, null, null, null, 'bool'),
|
||||||
|
array(1, null, null, true, 'integer'),
|
||||||
|
array($uri, null, true, true, 'uri object'),
|
||||||
|
array(2.5, null, null, true, 'float'),
|
||||||
|
array(array(), null, null, null, 'array'),
|
||||||
|
array((object)array(), null, null, null, 'object'),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($map as $test_case) {
|
||||||
|
$input = $test_case[0];
|
||||||
|
|
||||||
|
$expect_string = $test_case[1];
|
||||||
|
$expect_stringlike = $test_case[2];
|
||||||
|
$expect_scalar = $test_case[3];
|
||||||
|
|
||||||
|
$test_name = $test_case[4];
|
||||||
|
|
||||||
|
$this->executeEmptyStringTest(
|
||||||
|
$input,
|
||||||
|
$expect_string,
|
||||||
|
'phutil_nonempty_string',
|
||||||
|
$test_name);
|
||||||
|
|
||||||
|
$this->executeEmptyStringTest(
|
||||||
|
$input,
|
||||||
|
$expect_stringlike,
|
||||||
|
'phutil_nonempty_stringlike',
|
||||||
|
$test_name);
|
||||||
|
|
||||||
|
$this->executeEmptyStringTest(
|
||||||
|
$input,
|
||||||
|
$expect_scalar,
|
||||||
|
'phutil_nonempty_scalar',
|
||||||
|
$test_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeEmptyStringTest($input, $expect, $call, $name) {
|
||||||
|
$name = sprintf('%s(<%s>)', $call, $name);
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$actual = call_user_func($call, $input);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expect === null) {
|
||||||
|
$expect_exceptions = array('InvalidArgumentException');
|
||||||
|
} else {
|
||||||
|
$expect_exceptions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertCaught($expect_exceptions, $caught, $name);
|
||||||
|
if (!$caught) {
|
||||||
|
$this->assertEqual($expect, $actual, $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2094,3 +2094,128 @@ function phutil_raise_preg_exception($function, array $argv) {
|
||||||
|
|
||||||
throw new PhutilRegexException($message);
|
throw new PhutilRegexException($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if a value is a nonempty string.
|
||||||
|
*
|
||||||
|
* The value "null" and the empty string are considered empty; all other
|
||||||
|
* strings are considered nonempty.
|
||||||
|
*
|
||||||
|
* This method raises an exception if passed a value which is neither null
|
||||||
|
* nor a string.
|
||||||
|
*
|
||||||
|
* @param Value to test.
|
||||||
|
* @return bool True if the parameter is a nonempty string.
|
||||||
|
*/
|
||||||
|
function phutil_nonempty_string($value) {
|
||||||
|
if ($value === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
pht(
|
||||||
|
'Call to phutil_nonempty_string() expected null or a string, got: %s.',
|
||||||
|
phutil_describe_type($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if a value is a nonempty, stringlike value.
|
||||||
|
*
|
||||||
|
* The value "null", the empty string, and objects which have a "__toString()"
|
||||||
|
* method which returns the empty string are empty.
|
||||||
|
*
|
||||||
|
* Other strings, and objects with a "__toString()" method that returns a
|
||||||
|
* string other than the empty string are considered nonempty.
|
||||||
|
*
|
||||||
|
* This method raises an exception if passed any other value.
|
||||||
|
*
|
||||||
|
* @param Value to test.
|
||||||
|
* @return bool True if the parameter is a nonempty, stringlike value.
|
||||||
|
*/
|
||||||
|
function phutil_nonempty_stringlike($value) {
|
||||||
|
if ($value === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($value)) {
|
||||||
|
try {
|
||||||
|
$string = phutil_string_cast($value);
|
||||||
|
return phutil_nonempty_string($string);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// Continue below.
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
// Continue below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
pht(
|
||||||
|
'Call to phutil_nonempty_stringlike() expected a string or stringlike '.
|
||||||
|
'object, got: %s.',
|
||||||
|
phutil_describe_type($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if a value is a nonempty, scalar value.
|
||||||
|
*
|
||||||
|
* The value "null", the empty string, and objects which have a "__toString()"
|
||||||
|
* method which returns the empty string are empty.
|
||||||
|
*
|
||||||
|
* Other strings, objects with a "__toString()" method which returns a
|
||||||
|
* string other than the empty string, integers, and floats are considered
|
||||||
|
* scalar.
|
||||||
|
*
|
||||||
|
* This method raises an exception if passed any other value.
|
||||||
|
*
|
||||||
|
* @param Value to test.
|
||||||
|
* @return bool True if the parameter is a nonempty, scalar value.
|
||||||
|
*/
|
||||||
|
function phutil_nonempty_scalar($value) {
|
||||||
|
if ($value === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value) || is_int($value) || is_float($value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($value)) {
|
||||||
|
try {
|
||||||
|
$string = phutil_string_cast($value);
|
||||||
|
return phutil_nonempty_string($string);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// Continue below.
|
||||||
|
} catch (Throwable $ex) {
|
||||||
|
// Continue below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
pht(
|
||||||
|
'Call to phutil_nonempty_scalar() expected: a string; or stringlike '.
|
||||||
|
'object; or int; or float. Got: %s.',
|
||||||
|
phutil_describe_type($value)));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue