diff --git a/src/error/__tests__/PhutilOpaqueEnvelopeTestCase.php b/src/error/__tests__/PhutilOpaqueEnvelopeTestCase.php index 2f6c06b2..ba416ce6 100644 --- a/src/error/__tests__/PhutilOpaqueEnvelopeTestCase.php +++ b/src/error/__tests__/PhutilOpaqueEnvelopeTestCase.php @@ -1,47 +1,62 @@ assertFalse(strpos(var_export($envelope, true), $secret)); $this->assertFalse(strpos(print_r($envelope, true), $secret)); ob_start(); var_dump($envelope); $dump = ob_get_clean(); $this->assertFalse(strpos($dump, $secret)); try { - $this->throwTrace($envelope); + $this->throwTrace($envelope, $signpost); } catch (Exception $ex) { $trace = $ex->getTrace(); - $this->assertFalse(strpos(print_r($trace, true), $secret)); + + // NOTE: The entire trace may be very large and contain complex + // recursive datastructures. Look at only the last few frames: we expect + // to see the signpost value but not the secret. + $trace = array_slice($trace, 0, 2); + $trace = print_r($trace, true); + + $this->assertTrue(strpos($trace, $signpost) !== false); + $this->assertFalse(strpos($trace, $secret)); } - $backtrace = $this->getBacktrace($envelope); + $backtrace = $this->getBacktrace($envelope, $signpost); + $backtrace = array_slice($backtrace, 0, 2); + + $this->assertTrue(strpos($trace, $signpost) !== false); $this->assertFalse(strpos(print_r($backtrace, true), $secret)); $this->assertEqual($secret, $envelope->openEnvelope()); } - private function throwTrace($v) { + private function throwTrace($v, $w) { throw new Exception('!'); } - private function getBacktrace($v) { + private function getBacktrace($v, $w) { return debug_backtrace(); } } diff --git a/src/parser/__tests__/PhutilTypeSpecTestCase.php b/src/parser/__tests__/PhutilTypeSpecTestCase.php index c294c5b6..94497fe9 100644 --- a/src/parser/__tests__/PhutilTypeSpecTestCase.php +++ b/src/parser/__tests__/PhutilTypeSpecTestCase.php @@ -1,320 +1,319 @@ ', 'int | null', 'list < string >', 'int (must be even)', 'optional int', 'int?', 'int|null?', 'optional int? (minimum 300)', 'list', 'list>>> (easy)', '\\SomeClass', '\\Namespace\\SomeClass', '\\NamespaceA\\NamespaceB\\NamespaceC', 'NamespaceA\\NamespaceB\\NamespaceC', ); $bad = array( '', 'list<>', 'list', 'map|map', 'int optional', '(derp)', 'list', 'int?|string', '\\', '\\\\', '\\SomeClass\\', 'SomeClass\\', ); $good = array_fill_keys($good, true); $bad = array_fill_keys($bad, false); foreach ($good + $bad as $input => $expect) { $caught = null; try { PhutilTypeSpec::newFromString($input); } catch (Exception $ex) { $caught = $ex; } $this->assertEqual( $expect, ($caught === null), $input); } } public function testTypeSpecStringify() { $types = array( 'int', 'list', 'map', 'list>', 'map>', 'int|null', 'int|string|null', 'list', 'list', 'optional int', 'int (even)', ); foreach ($types as $type) { $this->assertEqual( $type, PhutilTypeSpec::newFromString($type)->toString()); } } public function testCanonicalize() { $tests = array( 'int?' => 'optional int', 'int | null' => 'int|null', 'list < map < int , string > > ?' => 'optional list>', 'int ( x )' => 'int ( x )', ); foreach ($tests as $input => $expect) { $this->assertEqual( $expect, PhutilTypeSpec::newFromString($input)->toString(), $input); } } public function testGetCommonParentClass() { $map = array( 'stdClass' => array( array('stdClass', 'stdClass'), ), false => array( array('Exception', 'stdClass'), ), 'Exception' => array( array('Exception', 'RuntimeException'), array('LogicException', 'RuntimeException'), array('BadMethodCallException', 'OutOfBoundsException'), ), ); foreach ($map as $expect => $tests) { if (is_int($expect)) { $expect = (bool)$expect; } foreach ($tests as $input) { list($class_a, $class_b) = $input; $this->assertEqual( $expect, PhutilTypeSpec::getCommonParentClass($class_a, $class_b), print_r($input, true)); } } } public function testGetTypeOf() { $map = array( 'int' => 1, 'string' => 'asdf', 'float' => 1.5, 'bool' => true, 'null' => null, 'map' => array(), 'list' => array('a', 'b'), 'list' => array(1, 2, 3), 'map' => array('x' => 3), 'map>' => array(1 => array('x', 'y')), 'stdClass' => new stdClass(), 'list' => array( new Exception(), new LogicException(), new RuntimeException(), ), 'map' => array('x' => new stdClass()), ); foreach ($map as $expect => $input) { $this->assertEqual( $expect, - PhutilTypeSpec::getTypeOf($input), - print_r($input, true)); + PhutilTypeSpec::getTypeOf($input)); PhutilTypeSpec::newFromString($expect)->check($input); } } public function testTypeCheckFailures() { $map = array( 'int' => 'string', 'string' => 32, 'null' => true, 'bool' => null, 'map' => 16, 'list' => array('y' => 'z'), 'int|null' => 'ducks', 'stdClass' => new Exception(), 'list' => array(new Exception()), ); foreach ($map as $type => $value) { $caught = null; try { PhutilTypeSpec::newFromString($type)->check($value); } catch (PhutilTypeCheckException $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhutilTypeCheckException); } } public function testCheckMap() { $spec = array( 'count' => 'int', 'color' => 'optional string', ); // Valid PhutilTypeSpec::checkMap( array( 'count' => 1, ), $spec); // Valid, with optional parameter. PhutilTypeSpec::checkMap( array( 'count' => 3, 'color' => 'red', ), $spec); // Parameter "count" is required but missing. $caught = null; try { PhutilTypeSpec::checkMap( array(), $spec); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhutilTypeMissingParametersException); // Parameter "size" is specified but does not exist. $caught = null; try { PhutilTypeSpec::checkMap( array( 'count' => 4, 'size' => 'large', ), $spec); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhutilTypeExtraParametersException); } public function testRegexValidation() { PhutilTypeSpec::checkMap( array( 'regex' => '/.*/', ), array( 'regex' => 'regex', )); $caught = null; try { PhutilTypeSpec::checkMap( array( 'regex' => '.*', ), array( 'regex' => 'regex', )); } catch (PhutilTypeCheckException $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhutilTypeCheckException); } public function testScalarOrListRegexp() { PhutilTypeSpec::checkMap( array( 'regex' => '/.*/', ), array( 'regex' => 'regex | list', )); PhutilTypeSpec::checkMap( array( 'regex' => array('/.*/'), ), array( 'regex' => 'regex | list', )); PhutilTypeSpec::checkMap( array( 'regex' => '/.*/', ), array( 'regex' => 'list | regex', )); PhutilTypeSpec::checkMap( array( 'regex' => array('/.*/'), ), array( 'regex' => 'list | regex', )); $this->assertTrue(true); } public function testMixedVector() { // This is a test case for an issue where we would not infer the type // of a vector containing a mixture of scalar and nonscalar elements // correctly. $caught = null; try { PhutilTypeSpec::checkMap( array( 'key' => array('!', (object)array()), ), array( 'key' => 'list', )); } catch (PhutilTypeCheckException $ex) { $caught = $ex; } $this->assertTrue($caught instanceof PhutilTypeCheckException); } }