Created
July 2, 2014 12:59
-
-
Save sun/121c93db423d2c7b0911 to your computer and use it in GitHub Desktop.
Rewrite D8 tests to convert getInfo() into PHPDoc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Drupal\Core\DrupalKernel; | |
use Drupal\simpletest\TestDiscovery; | |
use Sun\StaticReflection\ReflectionClass; | |
use Sun\StaticReflection\ReflectionDocComment; | |
use Symfony\Component\HttpFoundation\Request; | |
$autoloader = require __DIR__ . '/../vendor/autoload.php'; | |
if (!isset($autoloader->getPrefixesPsr4()['Sun\\StaticReflection\\'])) { | |
if (file_exists($dir = dirname(__DIR__) . '/vendor/sun/staticreflection/src')) { | |
$autoloader->addPsr4('Sun\\StaticReflection\\', $dir); | |
} | |
else { | |
$autoloader->addPsr4('Sun\\StaticReflection\\', 'h:/git/staticreflection/src'); | |
} | |
} | |
$request = Request::createFromGlobals(); | |
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); | |
$kernel->boot(); | |
restore_error_handler(); | |
$container = \Drupal::getContainer(); | |
$tests = $container->get('test_discovery')->findAllClassFiles(); | |
#$tests = array_slice($tests, 0, 50, TRUE); | |
#$tests = array_slice($tests, 4, 1, TRUE); | |
#$tests = preg_grep('@/BlockConfigSchemaTest.php$|/ConfigExportImportUITest.php$|/ActionLocalTasksTest.php$|/XssTest.php$@', $tests); | |
$count = 0; | |
foreach ($tests as $classname => $pathname) { | |
if ($classname === 'Drupal\Tests\UnitTestCase' || $classname === 'Drupal\simpletest\TestBase') { | |
continue; | |
} | |
$rc = new \ReflectionClass($classname); | |
$is_phpunit = $rc->isSubclassOf('PHPUnit_Framework_TestCase'); | |
$is_simple = $rc->isSubclassOf('Drupal\simpletest\TestBase'); | |
if (!$is_phpunit && !$is_simple) { | |
continue; | |
} | |
if (!$rc->hasMethod('getInfo')) { | |
continue; | |
} | |
$rm = $rc->getMethod('getInfo'); | |
if ($rm->class !== $classname) { | |
if ($rm->class !== 'Drupal\Tests\UnitTestCase' && $rm->class !== 'Drupal\simpletest\TestBase') { | |
throw new \LogicException(sprintf('Base class %s in %s implements getInfo().', $classname, $pathname)); | |
} | |
continue; | |
} | |
if ($rc->isAbstract()) { | |
throw new \LogicException(sprintf('Abstract class %s in %s implements getInfo().', $classname, $pathname)); | |
} | |
if (++$count % 10 != 0) { | |
// continue; | |
} | |
echo $classname, "\n"; | |
$file = file_get_contents($pathname); | |
// Retrieve legacy getInfo(). | |
$info = $classname::getInfo(); | |
// Remove getInfo() method from file content (including possible doc comment | |
// and newlines to next method). | |
#$file = preg_replace('@(?: +/\*\*.+?)?^[^\n]+function getInfo[^}]+\}\s+^@ms', '', $file); | |
$docblock_length = $rm->getDocComment() ? count(explode("\n", $rm->getDocComment())) : 0; | |
$lines = file($pathname); | |
$method = array_slice($lines, | |
$rm->getStartLine() - 1 - $docblock_length, | |
$rm->getEndLine() + 1 - $rm->getStartLine() + $docblock_length | |
); | |
$method = implode('', $method); | |
$file = preg_replace('`' . preg_quote($method, '`') . '\n*`', '', $file); | |
// Retrieve current class doc comment block. | |
$docblock = $rc->getDocComment(); | |
if (!$docblock) { | |
throw new \LogicException(sprintf('Missing class PHPDoc comment on %s in %s.', $classname, $pathname)); | |
} | |
// Retrieve PHPDoc summary and (optional) description + annotations. | |
$clean = ReflectionDocComment::getPlainDocComment($docblock); | |
preg_match('/(?P<summary>.+?)(?=\z|\n\n|^@)\n*(?P<description>.*?)(?=\z|^@)(?P<annotations>.*)?/ms', $clean, $matches); | |
// echo var_dump($matches), "\n"; | |
// continue; | |
// Normalize summaries. | |
if (!empty($info['description'])) { | |
$info['description'] = format_summary($info['description']); | |
} | |
if (!empty($matches['summary'])) { | |
$matches['summary'] = format_summary(trim($matches['summary'], "\n")); | |
} | |
// PHPDoc summary is often much more accurate. | |
if (!empty($info['description']) && !empty($matches['summary'])) { | |
similar_text($matches['summary'], $info['description'], $similarity); | |
if ($similarity > 80) { | |
$info['description'] = $matches['summary']; | |
} | |
} | |
$classparts = explode('\\', $classname); | |
$annotations = ReflectionDocComment::parseAnnotations($clean); | |
// Rebuild new doc comment block from scratch for PHPUnit tests. | |
if ($is_phpunit) { | |
if (isset($annotations['coversDefaultClass'])) { | |
$new = '@coversDefaultClass ' . $annotations['coversDefaultClass'][0] . "\n"; | |
} | |
// Fix bogus @covers tags. | |
elseif (isset($annotations['covers'])) { | |
$new = '@coversDefaultClass ' . $annotations['covers'][0] . "\n"; | |
} | |
// Fix bogus @coversClass tags. | |
elseif (isset($annotations['coversClass'])) { | |
$new = '@coversDefaultClass ' . $annotations['coversClass'][0] . "\n"; | |
} | |
// Fix bogus @see tags: | |
// 1. wrongly used instead of @coversDefaultClass (must contain backslash) | |
// 2. totally bogus instead of @group | |
elseif (isset($annotations['see']) && $sees = preg_grep('@(?:\w+\\\\)+@', $annotations['see'])) { | |
$new = '@coversDefaultClass ' . array_shift($sees) . "\n"; | |
} | |
elseif (!empty($info['description'])) { | |
$new = $info['description'] . "\n\n"; | |
} | |
else { | |
$new = $matches['summary'] . "\n\n"; | |
} | |
if (strpos($classname, 'Drupal\\Tests\\') === 0) { | |
// e.g. Drupal\Tests\Component\{group}\... | |
$new .= '@group ' . $classparts[3] . "\n"; | |
} | |
else { | |
// e.g. Drupal\{group}\Tests\... | |
$new .= '@group ' . $classparts[1] . "\n"; | |
} | |
if (!empty($matches['description'])) { | |
$new .= "\n" . trim($matches['description'], "\n") . "\n"; | |
} | |
} | |
// Build new doc comment block, using legacy description, group, and dependencies. | |
else { | |
if (!empty($info['description'])) { | |
$new = $info['description'] . "\n\n"; | |
} | |
else { | |
$new = $matches['summary'] . "\n\n"; | |
} | |
if (!empty($matches['description'])) { | |
$new .= trim($matches['description'], "\n") . "\n\n"; | |
} | |
// Use originating module as @group instead of (partially bogus) getInfo() | |
// 'group' definitions. | |
// $new .= '@group ' . $info['group'] . "\n"; | |
if ($classparts[1] == 'system' && $classparts[3] != 'System') { | |
// e.g. Drupal\system\Tests\{group}\... | |
$new .= '@group ' . $classparts[3] . "\n"; | |
} | |
else { | |
// e.g. Drupal\{group}\Tests\... | |
$new .= '@group ' . $classparts[1] . "\n"; | |
} | |
if (isset($info['dependencies'])) { | |
foreach ($info['dependencies'] as $module) { | |
$new .= "@requires module $module\n"; | |
} | |
} | |
if (isset($matches['annotations'])) { | |
$new .= $matches['annotations']; | |
} | |
} | |
// echo var_dump($new), "\n"; | |
// continue; | |
// Reformat as doc comment block. | |
$new = explode("\n", $new); | |
foreach ($new as &$line) { | |
$line = ' *' . ($line !== '' ? ' ' : '') . $line; | |
} | |
$new = "/**\n" . implode("\n", $new) . "/"; | |
// Replace the doc comment block. | |
$file = strtr($file, array($docblock => $new)); | |
file_put_contents($pathname, $file); | |
} | |
function format_summary($string) { | |
$string = preg_replace(['@^Test @', '@^Checks? @', '@([^.])$@'], ['Tests ', 'Tests ', '$1.'], $string); | |
return wordwrap($string, 80 - 3); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment