diff --git a/src/Util/Test.php b/src/Util/Test.php index f858ac9a09b..76f534c735f 100644 --- a/src/Util/Test.php +++ b/src/Util/Test.php @@ -64,6 +64,7 @@ function trait_exists($traitname, $autoload = true) class PHPUnit_Util_Test { const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; + const REGEX_TEST_WITH = '/@testWith\s+/'; const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m'; const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P.+?)[ \t]*\r?$/m'; @@ -435,6 +436,67 @@ public static function getProvidedData($className, $methodName) return $data; } + /** + * @param string $docComment full docComment string + * @return array when @testWith annotation is defined + * null when @testWith annotation is omitted + * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed + */ + public static function getDataFromTestWithAnnotation($docComment) + { + //removing initial ' * ' for docComment + $docComment = preg_replace('/\n\s*\*\s?/', "\n", $docComment); + if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) { + $offset = strlen($matches[0][0]) + $matches[0][1]; + $nestLvl = 0; + $maxOffset = strlen($docComment); + $stringStop = false; + $outputStr = ''; + $jsonEscape = array("\t" => '\t', "\n" => '\n', "\r" => '\r'); + while ($offset < $maxOffset) { + $currentCharacter = substr($docComment, $offset, 1); + if ($stringStop) { + if ($currentCharacter === $stringStop) { + $stringStop = false; + $outputStr .= '"'; + } elseif ($currentCharacter === '\\') { + $outputStr .= $currentCharacter . $docComment[$offset + 1]; + $offset++; + } elseif(isset($jsonEscape[$currentCharacter])) { + $outputStr .= $jsonEscape[$currentCharacter]; + } else { + $outputStr .= $currentCharacter; + } + } else { + if ($currentCharacter === '"' || $currentCharacter === "'") { + $stringStop = $currentCharacter; + $outputStr .= '"'; + } elseif ($currentCharacter === '(') { + $outputStr .= ($outputStr ? ',' : '') . '['; + $nestLvl++; + } elseif ($currentCharacter === ')') { + $outputStr .= ']'; + $nestLvl--; + } elseif (!preg_match('/\s/', $currentCharacter) && $nestLvl === 0) { + break; + } else { + $outputStr .= $currentCharacter; + } + } + $offset++; + } + + $data = json_decode('[' . $outputStr . ']', true); + if ($data === null) { + throw new PHPUnit_Framework_Exception('Data set in invalid'); + } + + return $data; + } + + return null; + } + /** * @param string $className * @param string $methodName diff --git a/tests/Util/TestTest.php b/tests/Util/TestTest.php index cf633536be4..7fd4799bf32 100644 --- a/tests/Util/TestTest.php +++ b/tests/Util/TestTest.php @@ -272,6 +272,133 @@ public function testGetProvidedDataRegEx() $this->assertEquals('メソッド', $matches[1]); } + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithEmptyAnnotation() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/**\n * @anotherAnnotation\n */"); + $this->assertNull($result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithSimpleCase() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (1) + */"); + $this->assertEquals(array(array(1)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithMultiLineMultiParameterCase() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (1, 2) + * (3, 4) + */"); + $this->assertEquals(array(array(1, 2), array(3, 4)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithMultiCaseInSingleLine() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (1) (2) (3) + */"); + $this->assertEquals(array(array(1), array(2), array(3)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithVariousTypes() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation('/** + * @testWith ("ab") (true) (null) + */'); + $this->assertEquals(array(array("ab"), array(true), array(null)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithAnnotationAfter() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (1) + * (2) + * @annotation + */"); + $this->assertEquals(array(array(1), array(2)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithSimpleTextAfter() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (1) + * (2) + * blah blah + */"); + $this->assertEquals(array(array(1), array(2)), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithBraces() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation('/** + * @testWith (")", "(") + * ("(", ")") + */'); + $this->assertEquals(array(array(")", "("), array("(", ")")), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithCharacterEscape() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation('/** + * @testWith ("\"", "\"") + */'); + $this->assertEquals(array(array('"', '"')), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + */ + public function testTestWithMultiLineText() + { + $result = PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith ('a + * bc') + */"); + + $this->assertEquals(array(array("a\n bc")), $result); + } + + /** + * @covers PHPUnit_Util_Test::getDataFromTestWithAnnotation + * @expectedException PHPUnit_Framework_Exception + */ + public function testTestWithThrowsProperExceptionIfDatasetCannotBeParsed() + { + PHPUnit_Util_Test::getDataFromTestWithAnnotation("/** + * @testWith (s) + */"); + } + /** * @covers PHPUnit_Util_Test::getDependencies * @todo Not sure what this test tests (name is misleading at least)