diff --git a/composer/Plugin/Scaffold/Handler.php b/composer/Plugin/Scaffold/Handler.php index 0e1b75665d49877397921bedd2d3a1fa42469507..df7748bba1e192bcd5247faacd852ab1e794bf63 100644 --- a/composer/Plugin/Scaffold/Handler.php +++ b/composer/Plugin/Scaffold/Handler.php @@ -154,11 +154,17 @@ public function scaffold() { $scaffold_options = $this->manageOptions->getOptions(); // Create a collection of scaffolded files to process. This determines which - // take priority and which are conjoined. + // take priority and which are combined. $scaffold_files = new ScaffoldFileCollection($file_mappings, $location_replacements); + // Get the scaffold files whose contents on disk match what we are about to + // write. We can remove these from consideration, as rewriting would be a + // no-op. + $unchanged = $scaffold_files->checkUnchanged(); + $scaffold_files->filterFiles($unchanged); + // Process the list of scaffolded files. - $scaffold_results = ScaffoldFileCollection::process($scaffold_files, $this->io, $scaffold_options); + $scaffold_results = $scaffold_files->process($this->io, $scaffold_options); // Generate an autoload file in the document root that includes the // autoload.php file in the vendor directory, wherever that is. Drupal @@ -195,7 +201,7 @@ protected function getVendorPath() { * A multidimensional array of file mappings, as returned by * self::getAllowedPackages(). * - * @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[] + * @return \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[][] * An array of destination paths => scaffold operation objects. */ protected function getFileMappingsFromPackages(array $allowed_packages) { diff --git a/composer/Plugin/Scaffold/Operations/AbstractOperation.php b/composer/Plugin/Scaffold/Operations/AbstractOperation.php index 96bd5904d0094537d1cef0c1765259d630f7435a..a2a994532816e9cd38393da28fba4af2b0c8c08b 100644 --- a/composer/Plugin/Scaffold/Operations/AbstractOperation.php +++ b/composer/Plugin/Scaffold/Operations/AbstractOperation.php @@ -11,17 +11,42 @@ */ abstract class AbstractOperation implements OperationInterface { + /** + * Cached contents of scaffold file to be written to disk. + * + * @var string + */ + protected $contents; + + /** + * {@inheritdoc} + */ + final public function contents() { + if (!isset($this->contents)) { + $this->contents = $this->generateContents(); + } + return $this->contents; + } + + /** + * Load the scaffold contents or otherwise generate what is needed. + * + * @return string + * The contents of the scaffold file. + */ + abstract protected function generateContents(); + /** * {@inheritdoc} */ - public function combineWithConjunctionTarget(OperationInterface $conjunction_target) { + public function scaffoldOverExistingTarget(OperationInterface $existing_target) { return $this; } /** * {@inheritdoc} */ - public function missingConjunctionTarget(ScaffoldFilePath $destination) { + public function scaffoldAtNewLocation(ScaffoldFilePath $destination) { return $this; } diff --git a/composer/Plugin/Scaffold/Operations/AppendOp.php b/composer/Plugin/Scaffold/Operations/AppendOp.php index 802b474bb6deab3e417777037a42bba0cb881875..0e5b51023a69745c3b71b7f9ff52ce087bf23154 100644 --- a/composer/Plugin/Scaffold/Operations/AppendOp.php +++ b/composer/Plugin/Scaffold/Operations/AppendOp.php @@ -49,6 +49,13 @@ class AppendOp extends AbstractOperation { */ protected $forceAppend; + /** + * The contents from the file that we are prepending / appending to. + * + * @var string + */ + protected $originalContents; + /** * Constructs an AppendOp. * @@ -69,16 +76,36 @@ public function __construct(ScaffoldFilePath $prepend_path = NULL, ScaffoldFileP $this->managed = TRUE; } + /** + * {@inheritdoc} + */ + protected function generateContents() { + // Fetch the prepend contents, if provided. + $prepend_contents = ''; + if (!empty($this->prepend)) { + $prepend_contents = file_get_contents($this->prepend->fullPath()) . "\n"; + } + // Fetch the append contents, if provided. + $append_contents = ''; + if (!empty($this->append)) { + $append_contents = "\n" . file_get_contents($this->append->fullPath()); + } + + // Get the original contents, or the default data if the original is empty. + $original_contents = $this->originalContents; + if (empty($original_contents) && !empty($this->default)) { + $original_contents = file_get_contents($this->default->fullPath()); + } + + // Attach it all together. + return $prepend_contents . $original_contents . $append_contents; + } + /** * {@inheritdoc} */ public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) { $destination_path = $destination->fullPath(); - // This is just a sanity check; the OperationFactory has in theory already - // accounted for this, and will return a SkipOp with a warning message. - if (!file_exists($destination_path) && empty($this->default)) { - throw new \RuntimeException($destination->getInterpolator()->interpolate("Cannot append/prepend because no prior package provided a scaffold file at [dest-rel-path].")); - } $interpolator = $destination->getInterpolator(); // Be extra-noisy of creating a new file or appending to a non-scaffold @@ -93,33 +120,20 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold $io->write($interpolator->interpolate($message)); } - // Fetch the prepend contents, if provided. - $prepend_contents = ''; + // Notify that we are prepending, if there is prepend data. if (!empty($this->prepend)) { $this->prepend->addInterpolationData($interpolator, 'prepend'); - $prepend_contents = file_get_contents($this->prepend->fullPath()) . "\n"; $io->write($interpolator->interpolate(" - Prepend to <info>[dest-rel-path]</info> from <info>[prepend-rel-path]</info>")); } - // Fetch the append contents, if provided. + // Notify that we are appending, if there is append data. $append_contents = ''; if (!empty($this->append)) { $this->append->addInterpolationData($interpolator, 'append'); - $append_contents = "\n" . file_get_contents($this->append->fullPath()); $io->write($interpolator->interpolate(" - Append to <info>[dest-rel-path]</info> from <info>[append-rel-path]</info>")); } - // We typically should always have content if we get here; the - // OperationFactory should create a SkipOp instead of an AppendOp if there - // is no append / prepend content. The edge case is if there is content - // that is all 'trim'ed away. Then we get a message that we are appending, - // although nothing will in fact actually happen. - if (!empty(trim($prepend_contents)) || !empty(trim($append_contents))) { - // None of our asset files are very large, so we will load each one into - // memory for processing. - $original_contents = file_get_contents(file_exists($destination_path) ? $destination_path : $this->default->fullPath()); - // Write the appended and prepended contents back to the file. - $altered_contents = $prepend_contents . $original_contents . $append_contents; - file_put_contents($destination_path, $altered_contents); - } + + // Write the resulting data + file_put_contents($destination_path, $this->contents()); // Return a ScaffoldResult with knowledge of whether this file is managed. return new ScaffoldResult($destination, $this->managed); @@ -128,16 +142,17 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold /** * {@inheritdoc} */ - public function combineWithConjunctionTarget(OperationInterface $conjunction_target) { - return new ConjunctionOp($conjunction_target, $this); + public function scaffoldOverExistingTarget(OperationInterface $existing_target) { + $this->originalContents = $existing_target->contents(); + return $this; } /** * {@inheritdoc} */ - public function missingConjunctionTarget(ScaffoldFilePath $destination) { - // If there is no conjunction target (the destination is not scaffolded), - // then any append we do will be to an unmanaged file. + public function scaffoldAtNewLocation(ScaffoldFilePath $destination) { + // If there is no existing scaffold file at the target location, then any + // append we do will be to an unmanaged file. $this->managed = FALSE; // Default: do not allow an append over a file that was not scaffolded. @@ -164,6 +179,9 @@ public function missingConjunctionTarget(ScaffoldFilePath $destination) { return new SkipOp($message); } + // Cache the original data to use during append. + $this->originalContents = $existingData; + return $this; } diff --git a/composer/Plugin/Scaffold/Operations/ConjunctionOp.php b/composer/Plugin/Scaffold/Operations/ConjunctionOp.php deleted file mode 100644 index fbbb176b8aeeea4579894a1828559284206d128b..0000000000000000000000000000000000000000 --- a/composer/Plugin/Scaffold/Operations/ConjunctionOp.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -namespace Drupal\Composer\Plugin\Scaffold\Operations; - -use Composer\IO\IOInterface; -use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath; -use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions; - -/** - * Joins two operations on the same file into a single operation. - * - * @internal - */ -class ConjunctionOp extends AbstractOperation { - - /** - * The first operation. - * - * @var \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface - */ - protected $firstOperation; - - /** - * The second operation. - * - * @var \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface - */ - protected $secondOperation; - - /** - * ConjunctionOp constructor. - * - * @param \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface $first_operation - * @param \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface $second_operation - */ - public function __construct(OperationInterface $first_operation, OperationInterface $second_operation) { - $this->firstOperation = $first_operation; - $this->secondOperation = $second_operation; - } - - /** - * {@inheritdoc} - */ - public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options) { - $destination_path = $destination->fullPath(); - // First, scaffold the original file. Disable symlinking, because we - // need a copy of the file if we're going to append / prepend to it. - @unlink($destination_path); - $this->firstOperation->process($destination, $io, $options->overrideSymlink(FALSE)); - return $this->secondOperation->process($destination, $io, $options); - } - -} diff --git a/composer/Plugin/Scaffold/Operations/OperationInterface.php b/composer/Plugin/Scaffold/Operations/OperationInterface.php index 4b57c74353670f859429c63e49efc4c109947f75..9dc20e0fc922332975af8a6665784b2ab31d3972 100644 --- a/composer/Plugin/Scaffold/Operations/OperationInterface.php +++ b/composer/Plugin/Scaffold/Operations/OperationInterface.php @@ -13,6 +13,14 @@ */ interface OperationInterface { + /** + * Returns the exact data that will be written to the scaffold files. + * + * @return string + * Data to be written to the scaffold location. + */ + public function contents(); + /** * Process this scaffold operation. * @@ -29,18 +37,18 @@ interface OperationInterface { public function process(ScaffoldFilePath $destination, IOInterface $io, ScaffoldOptions $options); /** - * Determines what to do if operation is used with a previous operation. + * Determines what to do if operation is used at same path as a previous op. * * Default behavior is to scaffold this operation at the specified * destination, ignoring whatever was there before. * - * @param OperationInterface $conjunction_target + * @param OperationInterface $existing_target * Existing file at the destination path that we should combine with. * * @return OperationInterface * The op to use at this destination. */ - public function combineWithConjunctionTarget(OperationInterface $conjunction_target); + public function scaffoldOverExistingTarget(OperationInterface $existing_target); /** * Determines what to do if operation is used without a previous operation. @@ -50,9 +58,12 @@ public function combineWithConjunctionTarget(OperationInterface $conjunction_tar * and therefore do not need to do anything special when there is no existing * file. * + * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath $destination + * Scaffold file's destination path. + * * @return OperationInterface * The op to use at this destination. */ - public function missingConjunctionTarget(ScaffoldFilePath $destination); + public function scaffoldAtNewLocation(ScaffoldFilePath $destination); } diff --git a/composer/Plugin/Scaffold/Operations/ReplaceOp.php b/composer/Plugin/Scaffold/Operations/ReplaceOp.php index 9f3eba09634dcf1048bf639c774a847a6a365734..66f8ea35b34c091975170602175920fa1185bdef 100644 --- a/composer/Plugin/Scaffold/Operations/ReplaceOp.php +++ b/composer/Plugin/Scaffold/Operations/ReplaceOp.php @@ -47,6 +47,13 @@ public function __construct(ScaffoldFilePath $sourcePath, $overwrite = TRUE) { $this->overwrite = $overwrite; } + /** + * {@inheritdoc} + */ + protected function generateContents() { + return file_get_contents($this->source->fullPath()); + } + /** * {@inheritdoc} */ @@ -85,8 +92,7 @@ public function process(ScaffoldFilePath $destination, IOInterface $io, Scaffold protected function copyScaffold(ScaffoldFilePath $destination, IOInterface $io) { $interpolator = $destination->getInterpolator(); $this->source->addInterpolationData($interpolator); - $fs = new Filesystem(); - $success = $fs->copy($this->source->fullPath(), $destination->fullPath()); + $success = file_put_contents($destination->fullPath(), $this->contents()); if (!$success) { throw new \RuntimeException($interpolator->interpolate("Could not copy source file <info>[src-rel-path]</info> to <info>[dest-rel-path]</info>!")); } diff --git a/composer/Plugin/Scaffold/Operations/ScaffoldFileCollection.php b/composer/Plugin/Scaffold/Operations/ScaffoldFileCollection.php index 59cc93c3612f7b0921a6bd07ea37f4683149b51a..fce72fe93f1e604ab7657518d747805baef7c4d8 100644 --- a/composer/Plugin/Scaffold/Operations/ScaffoldFileCollection.php +++ b/composer/Plugin/Scaffold/Operations/ScaffoldFileCollection.php @@ -29,15 +29,16 @@ class ScaffoldFileCollection implements \IteratorAggregate { /** * ScaffoldFileCollection constructor. * - * @param array $file_mappings + * @param \Drupal\Composer\Plugin\Scaffold\Operations\OperationInterface[][] $file_mappings * A multidimensional array of file mappings. * @param \Drupal\Composer\Plugin\Scaffold\Interpolator $location_replacements * An object with the location mappings (e.g. [web-root]). */ public function __construct(array $file_mappings, Interpolator $location_replacements) { // Collection of all destination paths to be scaffolded. Used to determine - // when two project scaffold the same file and we have to skip or use a - // ConjunctionOp. + // when two projects scaffold the same file and we have to either replace or + // combine them together. + // @see OperationInterface::scaffoldOverExistingTarget(). $scaffoldFiles = []; // Build the list of ScaffoldFileInfo objects by project. @@ -46,24 +47,20 @@ public function __construct(array $file_mappings, Interpolator $location_replace $destination = ScaffoldFilePath::destinationPath($package_name, $destination_rel_path, $location_replacements); // If there was already a scaffolding operation happening at this path, - // and the new operation is Conjoinable, then use a ConjunctionOp to - // join together both operations. This will cause both operations to - // run, one after the other. At the moment, only AppendOp is - // conjoinable; all other operations simply replace anything at the same - // path. + // allow the new operation to decide how to handle the override. + // Usually, the new operation will replace whatever was there before. if (isset($scaffoldFiles[$destination_rel_path])) { $previous_scaffold_file = $scaffoldFiles[$destination_rel_path]; - $op = $op->combineWithConjunctionTarget($previous_scaffold_file->op()); + $op = $op->scaffoldOverExistingTarget($previous_scaffold_file->op()); // Remove the previous op so we only touch the destination once. $message = " - Skip <info>[dest-rel-path]</info>: overridden in <comment>{$package_name}</comment>"; $this->scaffoldFilesByProject[$previous_scaffold_file->packageName()][$destination_rel_path] = new ScaffoldFileInfo($destination, new SkipOp($message)); } // If there is NOT already a scaffolding operation happening at this - // path, but the operation is a ConjunctionOp, then we need to check - // to see if there is a strategy for non-conjunction use. + // path, notify the scaffold operation of this fact. else { - $op = $op->missingConjunctionTarget($destination); + $op = $op->scaffoldAtNewLocation($destination); } // Combine the scaffold operation with the destination and record it. @@ -74,18 +71,57 @@ public function __construct(array $file_mappings, Interpolator $location_replace } } + /** + * Removes any item that has a path matching any path in the provided list. + * + * Matching is done via destination path. + * + * @param string[] $files_to_filter + * List of destination paths + */ + public function filterFiles(array $files_to_filter) { + foreach ($this->scaffoldFilesByProject as $project_name => $scaffold_files) { + foreach ($scaffold_files as $destination_rel_path => $scaffold_file) { + if (in_array($destination_rel_path, $files_to_filter, TRUE)) { + unset($scaffold_files[$destination_rel_path]); + } + } + $this->scaffoldFilesByProject[$project_name] = $scaffold_files; + if (!$this->checkListHasItemWithContent($scaffold_files)) { + unset($this->scaffoldFilesByProject[$project_name]); + } + } + } + + /** + * Scans through a list of scaffold files and determines if any has contents. + * + * @param Drupal\Composer\Plugin\Scaffold\ScaffoldFileInfo[] $scaffold_files + * List of scaffold files, path: ScaffoldFileInfo + * + * @return bool + * TRUE if at least one item in the list has content + */ + protected function checkListHasItemWithContent(array $scaffold_files) { + foreach ($scaffold_files as $destination_rel_path => $scaffold_file) { + $contents = $scaffold_file->op()->contents(); + if (!empty($contents)) { + return TRUE; + } + } + return FALSE; + } + /** * {@inheritdoc} */ public function getIterator() { - return new \RecursiveArrayIterator($this->scaffoldFilesByProject, \RecursiveArrayIterator::CHILD_ARRAYS_ONLY); + return new \ArrayIterator($this->scaffoldFilesByProject); } /** - * Processes the iterator created by ScaffoldFileCollection::create(). + * Processes the files in our collection. * - * @param \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldFileCollection $collection - * The iterator to process. * @param \Composer\IO\IOInterface $io * The Composer IO object. * @param \Drupal\Composer\Plugin\Scaffold\ScaffoldOptions $scaffold_options @@ -94,9 +130,9 @@ public function getIterator() { * @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult[] * The results array. */ - public static function process(ScaffoldFileCollection $collection, IOInterface $io, ScaffoldOptions $scaffold_options) { + public function process(IOInterface $io, ScaffoldOptions $scaffold_options) { $results = []; - foreach ($collection as $project_name => $scaffold_files) { + foreach ($this as $project_name => $scaffold_files) { $io->write("Scaffolding files for <comment>{$project_name}</comment>:"); foreach ($scaffold_files as $scaffold_file) { $results[$scaffold_file->destination()->relativePath()] = $scaffold_file->process($io, $scaffold_options); @@ -105,4 +141,30 @@ public static function process(ScaffoldFileCollection $collection, IOInterface $ return $results; } + /** + * Returns the list of files that have not changed since they were scaffolded. + * + * Note that there are two reasons a file may have changed: + * - The user modified it after it was scaffolded. + * - The package the file came to was updated, and the file is different in + * the new version. + * + * With the current scaffold code, we cannot tell the difference between the + * two. @see https://www.drupal.org/project/drupal/issues/3092563 + * + * @return string[] + * List of relative paths to unchanged files on disk. + */ + public function checkUnchanged() { + $results = []; + foreach ($this as $project_name => $scaffold_files) { + foreach ($scaffold_files as $scaffold_file) { + if (!$scaffold_file->hasChanged()) { + $results[] = $scaffold_file->destination()->relativePath(); + } + } + } + return $results; + } + } diff --git a/composer/Plugin/Scaffold/Operations/SkipOp.php b/composer/Plugin/Scaffold/Operations/SkipOp.php index b9f81085c7d4c6f9c357983b7fa4b1f1cfa253d3..4180bd8683486f7440206ccf77b708fda917ef6b 100644 --- a/composer/Plugin/Scaffold/Operations/SkipOp.php +++ b/composer/Plugin/Scaffold/Operations/SkipOp.php @@ -35,6 +35,13 @@ public function __construct($message = " - Skip <info>[dest-rel-path]</info>: d $this->message = $message; } + /** + * {@inheritdoc} + */ + protected function generateContents() { + return ''; + } + /** * {@inheritdoc} */ diff --git a/composer/Plugin/Scaffold/ScaffoldFileInfo.php b/composer/Plugin/Scaffold/ScaffoldFileInfo.php index 94e35e5bc2c5ec1596a96e0100de025b38192aaa..08ce59cfba58e9c185107fe397f7ece4c2a6fa26 100644 --- a/composer/Plugin/Scaffold/ScaffoldFileInfo.php +++ b/composer/Plugin/Scaffold/ScaffoldFileInfo.php @@ -123,4 +123,17 @@ public function process(IOInterface $io, ScaffoldOptions $options) { return $this->op()->process($this->destination, $io, $options); } + /** + * Returns TRUE if the target does not exist or has changed. + * + * @return bool + */ + final public function hasChanged() { + $path = $this->destination()->fullPath(); + if (!file_exists($path)) { + return TRUE; + } + return $this->op()->contents() !== file_get_contents($path); + } + } diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php index b2c9331bf1ef443ea48a2a0f2c2ca5f5049742c5..6f351eeb4eb087f81e784a4eac450772dae06b35 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php @@ -26,16 +26,6 @@ class ComposerHookTest extends TestCase { use ExecTrait; use AssertUtilsTrait; - /** - * The root of this project. - * - * Used to substitute this project's base directory into composer.json files - * so Composer can find it. - * - * @var string - */ - protected $projectRoot; - /** * Directory to perform the tests in. * @@ -64,7 +54,9 @@ protected function setUp(): void { $this->fileSystem = new Filesystem(); $this->fixtures = new Fixtures(); $this->fixtures->createIsolatedComposerCacheDir(); - $this->projectRoot = $this->fixtures->projectRoot(); + $this->fixturesDir = $this->fixtures->tmpDir($this->getName()); + $replacements = ['SYMLINK' => 'false', 'PROJECT_ROOT' => $this->fixtures->projectRoot()]; + $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements); } /** @@ -79,9 +71,6 @@ protected function tearDown(): void { * Test to see if scaffold operation runs at the correct times. */ public function testComposerHooks() { - $this->fixturesDir = $this->fixtures->tmpDir($this->getName()); - $replacements = ['SYMLINK' => 'false', 'PROJECT_ROOT' => $this->projectRoot]; - $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements); $topLevelProjectDir = 'composer-hooks-fixture'; $sut = $this->fixturesDir . '/' . $topLevelProjectDir; // First test: run composer install. This is the same as composer update @@ -136,4 +125,29 @@ public function testComposerHooks() { $this->assertStringContainsString("Not scaffolding files for fixtures/scaffold-override-fixture, because it is not listed in the element 'extra.drupal-scaffold.allowed-packages' in the root-level composer.json file.", $stdout); } + /** + * Test to see if scaffold messages are omitted when running scaffold twice. + */ + public function testScaffoldMessagesDoNotPrintTwice() { + $topLevelProjectDir = 'drupal-drupal'; + $sut = $this->fixturesDir . '/' . $topLevelProjectDir; + // First test: run composer install. This is the same as composer update + // since there is no lock file. Ensure that scaffold operation ran. + $stdout = $this->mustExec("composer install --no-ansi", $sut); + + $this->assertStringContainsString('- Copy [web-root]/index.php from assets/index.php', $stdout); + $this->assertStringContainsString('- Copy [web-root]/update.php from assets/update.php', $stdout); + + // Run scaffold operation again. It should not print anything. + $stdout = $this->mustExec("composer scaffold --no-ansi", $sut); + + $this->assertEquals('', $stdout); + + // Delete a file and run it again. It should re-scaffold the removed file. + unlink("$sut/index.php"); + $stdout = $this->mustExec("composer scaffold --no-ansi", $sut); + $this->assertStringContainsString('- Copy [web-root]/index.php from assets/index.php', $stdout); + $this->assertStringNotContainsString('- Copy [web-root]/update.php from assets/update.php', $stdout); + } + } diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php index e6f7bd67ece57711098a15e76319758a77fbcff8..e37d06f1f544eb260c3b0e1ec20a72b2fe217cf1 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php @@ -31,14 +31,9 @@ public function testProcess() { $prepend = $fixtures->sourcePath('drupal-drupal-test-append', 'prepend-to-robots.txt'); $append = $fixtures->sourcePath('drupal-drupal-test-append', 'append-to-robots.txt'); - $sut = new AppendOp($prepend, $append); + $sut = new AppendOp($prepend, $append, TRUE); + $sut->scaffoldAtNewLocation($destination); - // Test the system under test. - $sut->process($destination, $fixtures->io(), $options); - // Assert that the target file was created. - $this->assertFileExists($destination->fullPath()); - // Assert the target contained the contents from the correct scaffold files. - $contents = trim(file_get_contents($destination->fullPath())); $expected = <<<EOT # robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-test-append composer.json fixture. # This content is prepended to the top of the existing robots.txt fixture. @@ -50,6 +45,16 @@ public function testProcess() { # This content is appended to the bottom of the existing robots.txt fixture. # robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-test-append composer.json fixture. EOT; + + $pre_calculated_contents = $sut->contents(); + $this->assertEquals(trim($expected), trim($pre_calculated_contents)); + + // Test the system under test. + $sut->process($destination, $fixtures->io(), $options); + // Assert that the target file was created. + $this->assertFileExists($destination->fullPath()); + // Assert the target contained the contents from the correct scaffold files. + $contents = trim(file_get_contents($destination->fullPath())); $this->assertEquals(trim($expected), $contents); // Confirm that expected output was written to our io fixture. $output = $fixtures->getOutput(); diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ScaffoldFileCollectionTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ScaffoldFileCollectionTest.php index b97079886207ac2414141842c47e6157ef249d06..69b8acfeb57a49d641fbf6bd2d16988c2c93ecd9 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ScaffoldFileCollectionTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ScaffoldFileCollectionTest.php @@ -4,7 +4,7 @@ use PHPUnit\Framework\TestCase; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; -use Drupal\Composer\Plugin\Scaffold\Operations\ConjunctionOp; +use Drupal\Composer\Plugin\Scaffold\Operations\AppendOp; use Drupal\Composer\Plugin\Scaffold\Operations\SkipOp; use Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldFileCollection; @@ -41,8 +41,8 @@ public function testCreate() { // Confirm that the keys of the output are the same as the keys of the // input. $this->assertEquals(array_keys($scaffold_file_fixtures), array_keys($resolved_file_mappings)); - // '[web-root]/robots.txt' is now a SkipOp, as it is now part of a - // conjunction operation. + // '[web-root]/robots.txt' is now a SkipOp, as it is now part of an + // append operation. $this->assertEquals([ '[web-root]/index.php', '[web-root]/.htaccess', @@ -62,8 +62,8 @@ public function testCreate() { // Test that .htaccess is skipped. $this->assertInstanceOf(SkipOp::class, $resolved_file_mappings['fixtures/drupal-assets-fixture']['[web-root]/.htaccess']->op()); - // Test that the expected conjunction operation exists. - $this->assertInstanceOf(ConjunctionOp::class, $resolved_file_mappings['fixtures/drupal-drupal']['[web-root]/robots.txt']->op()); + // Test that the expected append operation exists. + $this->assertInstanceOf(AppendOp::class, $resolved_file_mappings['fixtures/drupal-drupal']['[web-root]/robots.txt']->op()); } }