1
0
Fork 0
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:
epriestley 2022-04-20 10:43:43 -07:00
parent 1fc4439ca5
commit f098e8d863
4 changed files with 339 additions and 0 deletions

View file

@ -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',

View file

@ -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 )------------------------------------------------- */

View file

@ -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);
}
}
} }

View file

@ -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)));
}