Last active
May 29, 2022 06:43
-
-
Save inxilpro/23ccfbf13813d574eef6d67f00bd1a8a to your computer and use it in GitHub Desktop.
This file contains 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 | |
namespace Tests\Project; | |
use RecursiveCallbackFilterIterator; | |
use RecursiveDirectoryIterator; | |
use RecursiveIteratorIterator; | |
use SplFileInfo; | |
use Tests\Constraints; | |
use Tests\TestCase; | |
class FixMeTest extends TestCase | |
{ | |
/** | |
* These are directories that won't be checked | |
* | |
* @var array | |
*/ | |
protected $exclude = [ | |
'.git', | |
'.idea', | |
'docs', | |
'node_modules', | |
'public', | |
'storage', | |
'vendor', | |
'tests/Project/FixMeTest.php', | |
'tests/Constraints/HasNoFixmes.php', | |
]; | |
/** | |
* Only check these extensions | |
* | |
* @var array | |
*/ | |
protected $extensions = [ | |
'.php', | |
'.blade.php', | |
'.js', | |
'.jsx', | |
'.less', | |
]; | |
/** | |
* @var string | |
*/ | |
protected $baseDirectory; | |
/** | |
* Set up base directory | |
*/ | |
protected function setUp(): void | |
{ | |
parent::setUp(); | |
foreach ($this->exclude as $key => $value) { | |
$this->exclude[$key] = $this->app->basePath($value); | |
} | |
} | |
/** | |
* Ensure that there are no "FIXME" statements in the project | |
*/ | |
public function test_project_has_no_fixme_comments(): void | |
{ | |
$directory_iterator = new RecursiveDirectoryIterator( | |
$this->app->basePath(), | |
RecursiveDirectoryIterator::SKIP_DOTS | |
); | |
$filtered_iterator = new RecursiveIteratorIterator( | |
new RecursiveCallbackFilterIterator($directory_iterator, function(SplFileInfo $file, $key, RecursiveDirectoryIterator $iterator) { | |
// Exclude root directories | |
if (starts_with($file->getPathname(), $this->exclude)) { | |
return false; | |
} | |
// Allow iteration | |
if ($iterator->hasChildren()) { | |
return true; | |
} | |
// Only include our file types | |
if (! ends_with($file->getFilename(), $this->extensions)) { | |
return false; | |
} | |
return $file->isFile(); | |
}) | |
); | |
$this->assertThat($filtered_iterator, new Constraints\HasNoFixmes($this->app->basePath())); | |
} | |
} |
This file contains 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 | |
namespace Tests\Constraints; | |
use PHPUnit\Framework\Constraint\Constraint; | |
class HasNoFixmes extends Constraint | |
{ | |
protected string $base_path; | |
protected $fixme_comments = []; | |
protected $ran = false; | |
public function __construct(string $base_path) | |
{ | |
$this->base_path = $base_path; | |
} | |
public function toString(): string | |
{ | |
return 'has no FIXME comments'; | |
} | |
protected function matches($iterator): bool | |
{ | |
if (! $this->ran) { | |
foreach ($iterator as $pathname => $file_info) { | |
$content = file_get_contents($pathname); | |
if (stripos($content, 'fixme') === false) { | |
continue; | |
} | |
$comments = stripos($pathname, '.php') === false | |
? $this->readGenericComments($content) | |
: $this->readPhpComments($content); | |
if (count($comments)) { | |
$this->fixme_comments[$pathname] = (object) [ | |
'filename' => str_replace($this->base_path, '', $pathname), | |
'comments' => $comments, | |
]; | |
} | |
} | |
$this->ran = true; | |
} | |
return empty($this->fixme_comments); | |
} | |
protected function readPhpComments(string $code): array | |
{ | |
$tokens = token_get_all($code); | |
return collect($tokens) | |
->filter(function($token) { | |
return is_array($token); | |
}) | |
->map(function($token) { | |
return (object) [ | |
'type' => $token[0], | |
'source_code' => $token[1], | |
'line_number' => $token[2], | |
]; | |
}) | |
->filter(function($token) { | |
return T_COMMENT === $token->type || T_DOC_COMMENT === $token->type; | |
}) | |
->filter(function($token) { | |
return stripos($token->source_code, 'fixme') !== false; | |
}) | |
->map(function($token) { | |
$trim_syntax = '~(^\s*(?://\s?|/\*\s?|\*\s)|\*/\s*$|FIXME:?\s?)~m'; | |
$comment = trim(preg_replace($trim_syntax, '', $token->source_code)); | |
return (object) [ | |
'line_number' => $token->line_number, | |
'comment' => $comment, | |
]; | |
}) | |
->toArray(); | |
} | |
protected function readGenericComments(string $code): array | |
{ | |
return collect(explode(PHP_EOL, $code)) | |
->map(function($code, $line_number) { | |
return (object) [ | |
'source_code' => (string) $code, | |
'line_number' => $line_number, | |
]; | |
}) | |
->filter(function($line) { | |
return stripos($line->source_code, 'fixme') !== false; | |
}) | |
->map(function($line) { | |
$trim_syntax = '~(^\s*(?://\s?|/\*\s?|\*\s)|\*/\s*$|FIXME:?\s?)~m'; | |
$comment = trim(preg_replace($trim_syntax, '', $line->source_code)); | |
return (object) [ | |
'line_number' => $line->line_number, | |
'comment' => (string) $comment, | |
]; | |
}) | |
->toArray(); | |
} | |
protected function failureDescription($other): string | |
{ | |
return 'the project has no FIXME comments'; | |
} | |
protected function additionalFailureDescription($other): string | |
{ | |
return collect($this->fixme_comments) | |
->map(function($file) { | |
$comments = collect($file->comments) | |
->map(function($comment) { | |
$text = str_replace("\n", ' ', trim($comment->comment)); | |
return empty($text) | |
? "Line {$comment->line_number}" | |
: "{$comment->line_number}: {$text}"; | |
}); | |
if (1 === count($comments)) { | |
return " - {$file->filename}: {$comments->first()}"; | |
} | |
return " - {$file->filename}\n + ".$comments->implode("\n + "); | |
}) | |
->implode("\n"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment