diff --git a/src/unit/engine/phutil/ArcanistPhutilTestCase.php b/src/unit/engine/phutil/ArcanistPhutilTestCase.php index 73234d77..419c9b4c 100644 --- a/src/unit/engine/phutil/ArcanistPhutilTestCase.php +++ b/src/unit/engine/phutil/ArcanistPhutilTestCase.php @@ -25,6 +25,65 @@ abstract class ArcanistPhutilTestCase { /* -( Making Test Assertions )--------------------------------------------- */ + /** + * Assert that a value is false. The test fails if it is not. + * + * @param wild The empirically derived value, generated by executing the + * test. + * @param string A human-readable description of what these values represent, + * and particularly of what a discrepancy means. + * + * @return void + * @task assert + */ + final protected function assertFalse($result, $message = null) { + if ($result === false) { + $this->assertions++; + return; + } + + $result = PhutilReadableSerializer::printableValue($result); + $caller = self::getCallerInfo(); + + $output = "Assertion failed at line {$caller['line']} in {$caller['file']}"; + + if ($message) { + $output .= ": {$message}"; + } + + $this->failTest($output); + throw new ArcanistPhutilTestTerminatedException($output); + } + + /** + * Assert that a value is true. The test fails if it is not. + * + * @param wild The empirically derived value, generated by executing the + * test. + * @param string A human-readable description of what these values represent, + * and particularly of what a discrepancy means. + * + * @return void + * @task assert + */ + final protected function assertTrue($result, $message = null) { + if ($result === true) { + $this->assertions++; + return; + } + + $result = PhutilReadableSerializer::printableValue($result); + $caller = self::getCallerInfo(); + + $output = "Assertion failed at line {$caller['line']} in {$caller['file']}"; + + if ($message) { + $output .= ": {$message}"; + } + + $this->failTest($output); + throw new ArcanistPhutilTestTerminatedException($output); + } /** * Assert that two values are equal. The test fails if they are not. @@ -51,18 +110,9 @@ abstract class ArcanistPhutilTestCase { $expect = PhutilReadableSerializer::printableValue($expect); $result = PhutilReadableSerializer::printableValue($result); + $caller = self::getCallerInfo(); - foreach (debug_backtrace() as $location) { - if (!preg_match('/^assert[A-Z]/', idx($location, 'function'))) { - break; - } - $where = $location; - } - - $line = idx($where, 'line'); - $file = basename(idx($where, 'file')); - - $output = "Assertion failed at line {$line} in {$file}"; + $output = "Assertion failed at line {$caller['line']} in {$caller['file']}"; if ($message) { $output .= ": {$message}"; @@ -582,4 +632,37 @@ abstract class ArcanistPhutilTestCase { return $this; } + /** + * Returns info about the caller function. + * + * @return map + */ + private static final function getCallerInfo() { + $callee = array(); + $caller = array(); + $seen = false; + + foreach (array_slice(debug_backtrace(), 1) as $location) { + $function = idx($location, 'function'); + + if (!$seen && preg_match('/^assert[A-Z]/', $function)) { + $seen = true; + $caller = $location; + } else if ($seen && !preg_match('/^assert[A-Z]/', $function)) { + $callee = $location; + break; + } + } + + return array( + 'file' => basename(idx($caller, 'file')), + 'line' => idx($caller, 'line'), + 'function' => idx($callee, 'function'), + 'class' => idx($callee, 'class'), + 'object' => idx($caller, 'object'), + 'type' => idx($callee, 'type'), + 'args' => idx($caller, 'args'), + ); + } + }