Page MenuHomePhabricator

D21762.id.diff
No OneTemporary

D21762.id.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1014,6 +1014,9 @@
'phutil_load_library' => 'init/lib/moduleutils.php',
'phutil_loggable_string' => '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_partition' => 'utils/utils.php',
'phutil_passthru' => 'future/exec/execx.php',
diff --git a/src/unit/engine/phutil/PhutilTestCase.php b/src/unit/engine/phutil/PhutilTestCase.php
--- a/src/unit/engine/phutil/PhutilTestCase.php
+++ b/src/unit/engine/phutil/PhutilTestCase.php
@@ -154,6 +154,147 @@
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 )------------------------------------------------- */
diff --git a/src/utils/__tests__/PhutilUtilsTestCase.php b/src/utils/__tests__/PhutilUtilsTestCase.php
--- a/src/utils/__tests__/PhutilUtilsTestCase.php
+++ b/src/utils/__tests__/PhutilUtilsTestCase.php
@@ -1000,4 +1000,74 @@
}
}
+ 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);
+ }
+ }
+
}
diff --git a/src/utils/utils.php b/src/utils/utils.php
--- a/src/utils/utils.php
+++ b/src/utils/utils.php
@@ -2094,3 +2094,128 @@
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)));
+}

File Metadata

Mime Type
text/plain
Expires
Thu, May 9, 3:17 AM (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6269183
Default Alt Text
D21762.id.diff (10 KB)

Event Timeline