Skip to content

Commit

Permalink
Add rector test suite to phpunit.xml or phpunit.xml.dist (#5717)
Browse files Browse the repository at this point in the history
* Add `rector` test suite to `phpunit.xml` or `phpunit.xml.dist`

Resolves rectorphp/rector#8486

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* Fix a few CI issues

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* Fix a few more CI issues

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* Appease the CI gods

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* check if dom extension is loaded

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* suggest dom extension

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* pass null to 3rd arg to ensure keep permission

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* clean up

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

* warn user if rector is not able generate test suite

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>

---------

Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>
Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
  • Loading branch information
ghostwriter and samsonasik committed Mar 26, 2024
1 parent ff0705b commit 83046fc
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 3 deletions.
5 changes: 4 additions & 1 deletion build/target-repository/composer.json
Expand Up @@ -22,5 +22,8 @@
"rector/rector-downgrade-php": "*"
},
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"suggest": {
"ext-dom": "To manipulate phpunit.xml via the custom-rule command"
}
}
152 changes: 150 additions & 2 deletions src/Console/Command/CustomRuleCommand.php
Expand Up @@ -4,6 +4,10 @@

namespace Rector\Console\Command;

use DOMDocument;
use DOMElement;
use DOMXPath;
use Generator;
use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use Rector\Exception\ShouldNotHappenException;
Expand Down Expand Up @@ -86,6 +90,8 @@ static function (string $answer): string {
]);
}

$currentDirectory = getcwd();

$generatedFilePaths = [];

$fileInfos = iterator_to_array($finder->getIterator());
Expand All @@ -95,7 +101,7 @@ static function (string $answer): string {
$newContent = $this->replaceNameVariable($rectorName, $fileInfo->getContents());
$newFilePath = $this->replaceNameVariable($rectorName, $fileInfo->getRelativePathname());

FileSystem::write(getcwd() . '/' . $newFilePath, $newContent, null);
FileSystem::write($currentDirectory . '/' . $newFilePath, $newContent, null);

$generatedFilePaths[] = $newFilePath;
}
Expand All @@ -107,7 +113,7 @@ static function (string $answer): string {
);

// 2. update autoload-dev in composer.json
$composerJsonFilePath = getcwd() . '/composer.json';
$composerJsonFilePath = $currentDirectory . '/composer.json';
if (file_exists($composerJsonFilePath)) {
$hasChanged = false;
$composerJson = JsonFileSystem::readFilePath($composerJsonFilePath);
Expand All @@ -126,9 +132,151 @@ static function (string $answer): string {
}
}

// 3. update phpunit.xml(.dist) to include rector test suite
$this->setupRectorTestSuite($currentDirectory);

return Command::SUCCESS;
}

private function setupRectorTestSuite(string $currentDirectory): void
{
if (! extension_loaded('dom')) {
$this->symfonyStyle->warning(
'The "dom" extension is not loaded. Rector could not add the rector test suite to phpunit.xml'
);

return;
}

$phpunitXmlExists = file_exists($currentDirectory . '/phpunit.xml');
$phpunitXmlDistExists = file_exists($currentDirectory . '/phpunit.xml.dist');

if (! $phpunitXmlExists && ! $phpunitXmlDistExists) {
$this->symfonyStyle->warning(
'No phpunit.xml or phpunit.xml.dist found. Rector could not add the rector test suite to it'
);

return;
}

$phpunitFile = $phpunitXmlExists ? 'phpunit.xml' : 'phpunit.xml.dist';

$phpunitFilePath = $currentDirectory . '/' . $phpunitFile;

$domDocument = new DOMDocument('1.0');
$domDocument->preserveWhiteSpace = false;
$domDocument->formatOutput = true;
$domDocument->loadXML(FileSystem::read($phpunitFilePath));

if ($this->hasRectorTestSuite($domDocument)) {
$this->symfonyStyle->success(
'The rector test suite already exists in ' . $phpunitFilePath . ". No changes were made.\n You can run the rector tests by running: phpunit --testsuite rector"
);

return;
}

$testsuitesElement = $domDocument->getElementsByTagName('testsuites')
->item(0);

if (! $testsuitesElement instanceof DOMElement) {
$this->symfonyStyle->warning(
'No <testsuites> element found in ' . $phpunitFilePath . '. Rector could not add the rector test suite to it'
);

return;
}

$phpunitXML = $this->updatePHPUnitXMLFile($domDocument, $testsuitesElement, $phpunitFilePath);

FileSystem::write($phpunitFilePath, $phpunitXML, null);

$this->symfonyStyle->success(
'We also update ' . $phpunitFilePath . ", to add a rector test suite.\n You can run the rector tests by running: phpunit --testsuite rector"
);
}

private function hasRectorTestSuite(DOMDocument $domDocument): bool
{
foreach ($this->getTestSuiteElements($domDocument) as $testSuiteElement) {
foreach ($testSuiteElement->getElementsByTagName('directory') as $directoryNode) {
if (! $directoryNode instanceof DOMElement) {
continue;
}

$name = $testSuiteElement->getAttribute('name');
if ($name !== 'rector') {
continue;
}

$directory = $directoryNode->textContent;
if ($directory === 'utils/rector/tests') {
return true;
}
}
}

return false;
}

private function updatePHPUnitXMLFile(
DOMDocument $domDocument,
DOMElement $testsuitesElement,
string $phpunitFilePath
): string {
$domElement = $domDocument->createElement('testsuite');
$domElement->setAttribute('name', 'rector');

$rectorTestSuiteDirectory = $domDocument->createElement('directory', 'utils/rector/tests');
$domElement->appendChild($rectorTestSuiteDirectory);

$testsuitesElement->appendChild($domElement);

$phpunitXML = $domDocument->saveXML();
if ($phpunitXML === false) {
throw new ShouldNotHappenException('Could not save XML');
}

return $phpunitXML;
}

/**
* @return Generator<DOMElement>
*/
private function getTestSuiteElements(DOMDocument $domDocument): Generator
{
$domxPath = new DOMXPath($domDocument);
$testSuiteNodes = $domxPath->query('testsuites/testsuite');
if ($testSuiteNodes === false) {
return;
}

if ($testSuiteNodes->length === 0) {
$testSuiteNodes = $domxPath->query('testsuite');
if ($testSuiteNodes === false) {
return;
}
}

if ($testSuiteNodes->length === 1) {
$element = $testSuiteNodes->item(0);

if ($element instanceof DOMElement) {
yield $element;
}

return;
}

foreach ($testSuiteNodes as $testSuiteNode) {
if (! $testSuiteNode instanceof DOMElement) {
continue;
}

yield $testSuiteNode;
}
}

private function replaceNameVariable(string $rectorName, string $contents): string
{
return str_replace('__Name__', $rectorName, $contents);
Expand Down

0 comments on commit 83046fc

Please sign in to comment.