diff --git a/core/tests/Drupal/Tests/Component/Scaffold/AssertUtilsTrait.php b/core/tests/Drupal/Tests/Component/Scaffold/AssertUtilsTrait.php index 3fdb950e36..28041c69ff 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/AssertUtilsTrait.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/AssertUtilsTrait.php @@ -20,7 +20,7 @@ trait AssertUtilsTrait { protected function assertScaffoldedFile($path, $is_link, $contents_contains) { $this->assertFileExists($path); $contents = file_get_contents($path); - $this->assertRegExp($contents_contains, basename($path) . ': ' . $contents); + $this->assertContains($contents_contains, basename($path) . ': ' . $contents); $this->assertSame($is_link, is_link($path)); } diff --git a/core/tests/Drupal/Tests/Component/Scaffold/ExecTrait.php b/core/tests/Drupal/Tests/Component/Scaffold/ExecTrait.php index 0986961788..65681d7b6f 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/ExecTrait.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/ExecTrait.php @@ -19,8 +19,8 @@ trait ExecTrait { * @param array $env * Environment variables to define for the subprocess. * - * @return array - * Standard output and standard error from the command + * @return string + * Standard output from the command */ protected function mustExec($cmd, $cwd, array $env = []) { $process = new Process($cmd, $cwd, $env + ['PATH' => getenv('PATH'), 'HOME' => getenv('HOME')]); @@ -30,7 +30,7 @@ protected function mustExec($cmd, $cwd, array $env = []) { if (0 != $exitCode) { throw new \RuntimeException("Exit code: {$exitCode}\n\n" . $process->getErrorOutput() . "\n\n" . $process->getOutput()); } - return [$process->getOutput(), $process->getErrorOutput()]; + return $process->getOutput(); } } diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php b/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php index de91e9381b..d66d7802f9 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php @@ -296,6 +296,8 @@ public function tearDown() { */ public function cloneFixtureProjects($fixturesDir, array $replacements = []) { $filesystem = new Filesystem(); + // We will replace 'SYMLINK' with the string 'true' in our composer.json + // fixture. $replacements += ['SYMLINK' => 'true']; $interpolator = new Interpolator('__', '__'); $interpolator->setData($replacements); diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ComposerHookTest.php b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ComposerHookTest.php index dde027d09b..d703d5bd11 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ComposerHookTest.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ComposerHookTest.php @@ -79,74 +79,60 @@ protected function tearDown() { */ public function testComposerHooks() { $this->fixturesDir = $this->fixtures->tmpDir($this->getName()); - $is_link = FALSE; - $replacements = ['SYMLINK' => $is_link ? 'true' : 'false', 'PROJECT_ROOT' => $this->projectRoot]; + $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 // since there is no lock file. Ensure that scaffold operation ran. - $this->execComposer("install --no-ansi", $sut); - $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', $is_link, '#Test version of default.settings.php from drupal/core#'); + $this->mustExec("composer install --no-ansi", $sut); + $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', FALSE, 'Test version of default.settings.php from drupal/core'); // Run composer required to add in the scaffold-override-fixture. This // project is "allowed" in our main fixture project, but not required. // We expect that requiring this library should re-scaffold, resulting // in a changed default.settings.php file. - list($stdout,) = $this->execComposer("require --no-ansi --no-interaction fixtures/scaffold-override-fixture:dev-master", $sut); - $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', $is_link, '#scaffolded from the scaffold-override-fixture#'); + $stdout = $this->mustExec("composer require --no-ansi --no-interaction fixtures/scaffold-override-fixture:dev-master", $sut); + $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', FALSE, 'scaffolded from the scaffold-override-fixture'); // Make sure that the appropriate notice informing us that scaffolding // is allowed was printed. $this->assertContains('Package fixtures/scaffold-override-fixture has scaffold operations, and is already allowed in the root-level composer.json file.', $stdout); // Delete one scaffold file, just for test purposes, then run // 'composer update' and see if the scaffold file is replaced. @unlink($sut . '/sites/default/default.settings.php'); - $this->execComposer("update --no-ansi", $sut); - $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', $is_link, '#scaffolded from the scaffold-override-fixture#'); + $this->assertFileNotExists($sut . '/sites/default/default.settings.php'); + $this->mustExec("composer update --no-ansi", $sut); + $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', FALSE, 'scaffolded from the scaffold-override-fixture'); // Delete the same test scaffold file again, then run - // 'composer composer:scaffold' and see if the scaffold file is re-scaffolded. + // 'composer composer:scaffold' and see if the scaffold file is + // re-scaffolded. @unlink($sut . '/sites/default/default.settings.php'); - $this->execComposer("install --no-ansi", $sut); - $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', $is_link, '#scaffolded from the scaffold-override-fixture#'); + $this->assertFileNotExists($sut . '/sites/default/default.settings.php'); + $this->mustExec("composer install --no-ansi", $sut); + $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', FALSE, 'scaffolded from the scaffold-override-fixture'); // Delete the same test scaffold file yet again, then run // 'composer install' and see if the scaffold file is re-scaffolded. @unlink($sut . '/sites/default/default.settings.php'); - $this->execComposer("composer:scaffold --no-ansi", $sut); - $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', $is_link, '#scaffolded from the scaffold-override-fixture#'); + $this->assertFileNotExists($sut . '/sites/default/default.settings.php'); + $this->mustExec("composer composer:scaffold --no-ansi", $sut); + $this->assertScaffoldedFile($sut . '/sites/default/default.settings.php', FALSE, 'scaffolded from the scaffold-override-fixture'); // Run 'composer create-project' to create a new test project called // 'create-project-test', which is a copy of 'fixtures/drupal-drupal'. $sut = $this->fixturesDir . '/create-project-test'; $filesystem = new Filesystem(); $filesystem->remove($sut); - list($stdout,) = $this->execComposer("create-project --repository=packages.json fixtures/drupal-drupal {$sut}", $this->fixturesDir, ['COMPOSER_MIRROR_PATH_REPOS' => 1]); + $stdout = $this->mustExec("composer create-project --repository=packages.json fixtures/drupal-drupal {$sut}", $this->fixturesDir, ['COMPOSER_MIRROR_PATH_REPOS' => 1]); $this->assertDirectoryExists($sut); $this->assertContains('Scaffolding files for fixtures/drupal-drupal', $stdout); - $this->assertScaffoldedFile($sut . '/index.php', FALSE, '#Test version of index.php from drupal/core#'); + $this->assertScaffoldedFile($sut . '/index.php', FALSE, 'Test version of index.php from drupal/core'); $topLevelProjectDir = 'composer-hooks-nothing-allowed-fixture'; $sut = $this->fixturesDir . '/' . $topLevelProjectDir; // Run composer install on an empty project. - $this->execComposer("install --no-ansi", $sut); + $this->mustExec("composer install --no-ansi", $sut); // Require a project that is not allowed to scaffold and confirm that we // get a warning, and it does not scaffold. - list($stdout,) = $this->execComposer("require --no-ansi --no-interaction fixtures/scaffold-override-fixture:dev-master", $sut); + $stdout = $this->mustExec("composer require --no-ansi --no-interaction fixtures/scaffold-override-fixture:dev-master", $sut); $this->assertFileNotExists($sut . '/sites/default/default.settings.php'); $this->assertContains('Package fixtures/scaffold-override-fixture has scaffold operations, but it is not allowed in the root-level composer.json file.', $stdout); } - /** - * Runs a `composer` command. - * - * @param string $cmd - * The Composer command to execute (escaped as required) - * @param string $cwd - * The current working directory to run the command from. - * @param array $env - * Environment variables to define for the subprocess. - * - * @return array - * Standard output and standard error from the command - */ - protected function execComposer($cmd, $cwd, array $env = []) { - return $this->mustExec("composer {$cmd}", $cwd, $env); - } - } diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php index fc6f63f8c7..c27e3e2fdf 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php @@ -80,10 +80,9 @@ protected function tearDown() { * The path to the fixture directory. */ protected function createSutWithGit($fixture_name) { - $is_link = FALSE; $this->fixturesDir = $this->fixtures->tmpDir($this->getName()); $sut = $this->fixturesDir . '/' . $fixture_name; - $replacements = ['SYMLINK' => $is_link ? 'true' : 'false', 'PROJECT_ROOT' => $this->projectRoot]; + $replacements = ['SYMLINK' => 'false', 'PROJECT_ROOT' => $this->projectRoot]; $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements); // .gitignore files will not be managed unless there is a git repository. $this->mustExec('git init', $sut); @@ -126,9 +125,9 @@ public function testManageGitIgnore() { // At this point we should have a .gitignore file, because although we did // not explicitly ask for .gitignore tracking, the vendor directory is not // tracked, so the default in that instance is to manage .gitignore files. - $this->assertScaffoldedFile($sut . '/docroot/.gitignore', FALSE, '#' . $expected . '#msi'); - $this->assertScaffoldedFile($sut . '/docroot/sites/.gitignore', FALSE, '#example.settings.local.php#'); - $this->assertScaffoldedFile($sut . '/docroot/sites/default/.gitignore', FALSE, '#default.services.yml#'); + $this->assertScaffoldedFile($sut . '/docroot/.gitignore', FALSE, $expected); + $this->assertScaffoldedFile($sut . '/docroot/sites/.gitignore', FALSE, 'example.settings.local.php'); + $this->assertScaffoldedFile($sut . '/docroot/sites/default/.gitignore', FALSE, 'default.services.yml'); $expected = <<mustExec('git status --porcelain', $sut); + $stdout = $this->mustExec('git status --porcelain', $sut); $this->assertEquals(trim($expected), trim($stdout)); } diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ScaffoldTest.php b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ScaffoldTest.php index 8046662f16..9fa38ab07e 100644 --- a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ScaffoldTest.php +++ b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ScaffoldTest.php @@ -3,8 +3,9 @@ namespace Drupal\Tests\Component\Scaffold\Functional; use Composer\Util\Filesystem; -use Drupal\Tests\Component\Scaffold\Fixtures; use Drupal\Tests\Component\Scaffold\AssertUtilsTrait; +use Drupal\Tests\Component\Scaffold\Fixtures; +use Drupal\Tests\Component\Scaffold\ScaffoldTestResult; use PHPUnit\Framework\TestCase; /** @@ -125,7 +126,7 @@ public function scaffoldSut($fixture_name, $is_link = FALSE, $relocated_docroot $this->assertFileNotExists($sut . '/docroot'); } - return [$docroot, $scaffoldOutput]; + return new ScaffoldTestResult($docroot, $scaffoldOutput); } /** @@ -178,11 +179,9 @@ public function testScaffoldWithExpectedException($fixture_name, $expected_excep */ public function testEmptyProject() { $fixture_name = 'empty-fixture'; - $is_link = FALSE; - $relocated_docroot = FALSE; - list($docroot, $scaffoldOutput) = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot); - $this->assertContains('Nothing scaffolded because no packages are allowed in the top-level composer.json file', $scaffoldOutput); + $result = $this->scaffoldSut($fixture_name, FALSE, FALSE); + $this->assertContains('Nothing scaffolded because no packages are allowed in the top-level composer.json file', $result->scaffoldOutput()); } /** @@ -191,10 +190,9 @@ public function testEmptyProject() { public function testProjectThatScaffoldsEmptyProject() { $fixture_name = 'project-allowing-empty-fixture'; $is_link = FALSE; - $relocated_docroot = FALSE; - list($docroot, $scaffoldOutput) = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot); - $this->assertContains('The allowed package fixtures/empty-fixture does not provide a file mapping for Composer Scaffold', $scaffoldOutput); - $this->assertCommonDrupalAssetsWereScaffolded($docroot, $is_link); + $result = $this->scaffoldSut($fixture_name, FALSE, FALSE); + $this->assertContains('The allowed package fixtures/empty-fixture does not provide a file mapping for Composer Scaffold', $result->scaffoldOutput()); + $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), FALSE); } public function scaffoldOverridingSettingsExcludingHtaccessValues() { @@ -214,7 +212,7 @@ public function scaffoldOverridingSettingsExcludingHtaccessValues() { } /** - * Asserts that the drupal/assets scaffold files correct for drupal/project and drupal/drupal. + * Asserts that the drupal/assets scaffold files correct for sut. * * @param string $fixture_name * The name of the fixture to use from @@ -227,11 +225,11 @@ public function scaffoldOverridingSettingsExcludingHtaccessValues() { * @dataProvider scaffoldOverridingSettingsExcludingHtaccessValues */ public function testScaffoldOverridingSettingsExcludingHtaccess($fixture_name, $is_link, $relocated_docroot) { - list($docroot, $scaffoldOutput) = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot); + $result = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot); - $this->assertCommonDrupalAssetsWereScaffolded($docroot, $is_link); - $this->assertDefaultSettingsFromScaffoldOverride($docroot, $is_link); - $this->assertHtaccessExcluded($docroot); + $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), $is_link); + $this->assertDefaultSettingsFromScaffoldOverride($result->docroot(), $is_link); + $this->assertHtaccessExcluded($result->docroot()); } /** @@ -243,15 +241,13 @@ public function testScaffoldOverridingSettingsExcludingHtaccess($fixture_name, $ */ public function testDrupalDrupalFileWasReplaced() { $fixture_name = 'drupal-drupal-test-overwrite'; - $is_link = FALSE; - $relocated_docroot = FALSE; - list($docroot, $scaffoldOutput) = $this->scaffoldSut($fixture_name, $is_link, $relocated_docroot); - - $this->assertScaffoldedFile($docroot . '/replace-me.txt', $is_link, '#from assets that replaces file#'); - $this->assertScaffoldedFile($docroot . '/keep-me.txt', $is_link, '#File in drupal-drupal-test-overwrite that is not replaced#'); - $this->assertScaffoldedFile($docroot . '/make-me.txt', $is_link, '#from assets that replaces file#'); - $this->assertCommonDrupalAssetsWereScaffolded($docroot, $is_link); - $this->assertScaffoldedFile($docroot . '/robots.txt', $is_link, "#{$fixture_name}#"); + $result = $this->scaffoldSut($fixture_name, FALSE, FALSE); + + $this->assertScaffoldedFile($result->docroot() . '/replace-me.txt', FALSE, 'from assets that replaces file'); + $this->assertScaffoldedFile($result->docroot() . '/keep-me.txt', FALSE, 'File in drupal-drupal-test-overwrite that is not replaced'); + $this->assertScaffoldedFile($result->docroot() . '/make-me.txt', FALSE, 'from assets that replaces file'); + $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), FALSE); + $this->assertScaffoldedFile($result->docroot() . '/robots.txt', FALSE, $fixture_name); } /** @@ -275,13 +271,34 @@ protected function scaffoldAppendTestValuesToPermute($is_link) { [ 'drupal-drupal-test-append', $is_link, - '#in drupal-drupal-test-append composer.json fixture.*This content is prepended to the top of the existing robots.txt fixture.*Test version of robots.txt from drupal/core.*This content is appended to the bottom of the existing robots.txt fixture.*in drupal-drupal-test-append composer.json fixture#ms', + '# 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. +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +# Test version of robots.txt from drupal/core. + +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# 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. +', ], [ 'drupal-drupal-append-to-append', $is_link, - '#in drupal-drupal-append-to-append composer.json fixture.*This content is prepended to the top of the existing robots.txt fixture.*Test version of robots.txt from drupal/core.*This content is appended to the bottom of the existing robots.txt fixture.*in profile-with-append composer.json fixture.*This content is appended to the bottom of the existing robots.txt fixture.*in drupal-drupal-append-to-append composer.json fixture#ms', + '# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-append-to-append composer.json fixture. +# This content is prepended to the top of the existing robots.txt fixture. +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +# Test version of robots.txt from drupal/core. + +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# This content is appended to the bottom of the existing robots.txt fixture. +# robots.txt fixture scaffolded from "file-mappings" in profile-with-append composer.json fixture. + +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# This content is appended to the bottom of the existing robots.txt fixture. +# robots.txt fixture scaffolded from "file-mappings" in drupal-drupal-append-to-append composer.json fixture.', ], ]; } @@ -300,10 +317,10 @@ protected function scaffoldAppendTestValuesToPermute($is_link) { * @dataProvider scaffoldAppendTestValues */ public function testDrupalDrupalFileWasAppended($fixture_name, $is_link, $robots_txt_contents) { - list($docroot, $scaffoldOutput) = $this->scaffoldSut($fixture_name, $is_link, FALSE); + $result = $this->scaffoldSut($fixture_name, $is_link, FALSE); - $this->assertScaffoldedFile($docroot . '/robots.txt', FALSE, $robots_txt_contents); - $this->assertCommonDrupalAssetsWereScaffolded($docroot, $is_link); + $this->assertScaffoldedFile($result->docroot() . '/robots.txt', FALSE, $robots_txt_contents); + $this->assertCommonDrupalAssetsWereScaffolded($result->docroot(), $is_link); } /** @@ -315,7 +332,7 @@ public function testDrupalDrupalFileWasAppended($fixture_name, $is_link, $robots * Whether or not symlinking is used. */ protected function assertDefaultSettingsFromScaffoldOverride($docroot, $is_link) { - $this->assertScaffoldedFile($docroot . '/sites/default/default.settings.php', $is_link, '#scaffolded from the scaffold-override-fixture#'); + $this->assertScaffoldedFile($docroot . '/sites/default/default.settings.php', $is_link, 'scaffolded from the scaffold-override-fixture'); } /** @@ -343,22 +360,21 @@ protected function assertHtaccessExcluded($docroot) { * Whether or not symlinking is used. */ protected function assertCommonDrupalAssetsWereScaffolded($docroot, $is_link) { - $from_core = '#from drupal/core#'; // Ensure that the autoload.php file was written. $this->assertFileExists($docroot . '/autoload.php'); // Assert other scaffold files are written in the correct locations. - $this->assertScaffoldedFile($docroot . '/.csslintrc', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/.editorconfig', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/.eslintignore', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/.eslintrc.json', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/.gitattributes', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/.ht.router.php', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/sites/default/default.services.yml', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/sites/example.settings.local.php', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/sites/example.sites.php', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/index.php', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/update.php', $is_link, $from_core); - $this->assertScaffoldedFile($docroot . '/web.config', $is_link, $from_core); + $this->assertScaffoldedFile($docroot . '/.csslintrc', $is_link, 'Test version of .csslintrc from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/.editorconfig', $is_link, 'Test version of .editorconfig from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/.eslintignore', $is_link, 'Test version of .eslintignore from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/.eslintrc.json', $is_link, 'Test version of .eslintrc.json from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/.gitattributes', $is_link, 'Test version of .gitattributes from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/.ht.router.php', $is_link, 'Test version of .ht.router.php from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/sites/default/default.services.yml', $is_link, 'Test version of default.services.yml from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/sites/example.settings.local.php', $is_link, 'Test version of example.settings.local.php from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/sites/example.sites.php', $is_link, 'Test version of example.sites.php from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/index.php', $is_link, 'Test version of index.php from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/update.php', $is_link, 'Test version of update.php from drupal/core.'); + $this->assertScaffoldedFile($docroot . '/web.config', $is_link, 'Test version of web.config from drupal/core.'); } } diff --git a/core/tests/Drupal/Tests/Component/Scaffold/ScaffoldTestResult.php b/core/tests/Drupal/Tests/Component/Scaffold/ScaffoldTestResult.php new file mode 100644 index 0000000000..3841cccb69 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Scaffold/ScaffoldTestResult.php @@ -0,0 +1,42 @@ +docroot = $docroot; + $this->scaffoldOutput = $scaffoldOutput; + } + + /** + * Returns the location of the docroot from the scaffold test. + * + * @return string + */ + public function docroot() { + return $this->docroot; + } + + /** + * Returns the standard output from the scaffold test. + * + * @return string + */ + public function scaffoldOutput() { + return $this->scaffoldOutput; + } + +}