diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fd1eeb9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: php + +notifications: + email: false + +php: + - "5.6" + - "7.0" + - "7.1" + +install: + - composer install + +script: + - vendor/bin/phpunit tests diff --git a/README.rst b/README.rst index 4d4c643..2f7df49 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,12 @@ OXID eShop composer plugin ========================== +.. image:: https://travis-ci.org/OXID-eSales/oxideshop_composer_plugin.svg?branch=master + :target: https://travis-ci.org/OXID-eSales/oxideshop_composer_plugin + +.. image:: https://img.shields.io/packagist/v/oxid-esales/oxideshop-composer-plugin.svg?maxAge=3600 + :target: https://packagist.org/packages/oxid-esales/oxideshop-composer-plugin + This plugin is used to install OXID eShop and OXID eShop third party integrations (modules, themes). More information how to install OXID eShop using this plugin can be found `here `__. diff --git a/composer.json b/composer.json index 20e60c0..b0c72c1 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,9 @@ "prefer-stable": true, "require": { "composer-plugin-api": "^1.0", - "symfony/filesystem": "^3.0" + "symfony/filesystem": "^3.0", + "webmozart/glob": "^4.1", + "webmozart/path-util": "^2.3" }, "require-dev": { "phpunit/phpunit": "^4.8", diff --git a/src/Installer/DirectoriesSkipIteratorBuilder.php b/src/Installer/DirectoriesSkipIteratorBuilder.php deleted file mode 100644 index b9da797..0000000 --- a/src/Installer/DirectoriesSkipIteratorBuilder.php +++ /dev/null @@ -1,29 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Installer; - -use RecursiveFilterIterator; -use RecursiveIterator; - -class DirectoryRecursiveFilterIterator extends RecursiveFilterIterator -{ - /** @var array */ - private $directoriesToSkip = []; - - /** - * DirectoryRecursiveFilterIterator constructor. - * - * @param RecursiveIterator $iterator - * @param $directoriesToSkip - */ - public function __construct(RecursiveIterator $iterator, $directoriesToSkip) - { - $this->directoriesToSkip = $directoriesToSkip; - parent::__construct($iterator); - } - - /** - * If directory start matches the one from provided array, it will be skipped - * - * @return bool - */ - public function accept() - { - foreach ($this->directoriesToSkip as $skip) { - $skip = preg_quote(rtrim($skip, '/'), '/'); - if (preg_match("/^${skip}(\\/|$)/", $this->current()->getPathName())) { - return false; - } - } - return true; - } - - /** - * @return DirectoryRecursiveFilterIterator - */ - public function getChildren() - { - return new self($this->getInnerIterator()->getChildren(), $this->directoriesToSkip); - } -} diff --git a/src/Installer/AbstractInstaller.php b/src/Installer/Package/AbstractPackageInstaller.php similarity index 53% rename from src/Installer/AbstractInstaller.php rename to src/Installer/Package/AbstractPackageInstaller.php index 30774f6..8a082ce 100644 --- a/src/Installer/AbstractInstaller.php +++ b/src/Installer/Package/AbstractPackageInstaller.php @@ -20,17 +20,16 @@ * @version OXID eShop Composer plugin */ -namespace OxidEsales\ComposerPlugin\Installer; +namespace OxidEsales\ComposerPlugin\Installer\Package; use Composer\IO\IOInterface; use Composer\Package\PackageInterface; -use Symfony\Component\Filesystem\Filesystem; /** * Class is responsible for preparing project structure. * It copies necessary files to specific directories. */ -abstract class AbstractInstaller +abstract class AbstractPackageInstaller { const EXTRA_PARAMETER_KEY_ROOT = 'oxideshop'; @@ -46,8 +45,20 @@ abstract class AbstractInstaller /** Used to decide what the shop source directory is. */ const EXTRA_PARAMETER_SOURCE_PATH = 'source-path'; - /** @var Filesystem */ - private $fileSystem; + /** List of glob expressions used to blacklist files being copied. */ + const EXTRA_PARAMETER_FILTER_BLACKLIST = 'blacklist-filter'; + + /** Glob expression to filter all files, might be used to filter whole directory. */ + const BLACKLIST_ALL_FILES = '**/*'; + + /** Name of directory to be excluded for VCS */ + const BLACKLIST_VCS_DIRECTORY = '.git'; + + /** Name of ignore files to be excluded for VCS */ + const BLACKLIST_VCS_IGNORE_FILE = '.gitignore'; + + /** Glob filter expression to exclude VCS files */ + const BLACKLIST_VCS_DIRECTORY_FILTER = self::BLACKLIST_VCS_DIRECTORY . DIRECTORY_SEPARATOR . self::BLACKLIST_ALL_FILES; /** @var IOInterface */ private $io; @@ -61,14 +72,12 @@ abstract class AbstractInstaller /** * AbstractInstaller constructor. * - * @param Filesystem $fileSystem - * @param IOInterface $io - * @param string $rootDirectory + * @param IOInterface $io + * @param string $rootDirectory * @param PackageInterface $package */ - public function __construct(Filesystem $fileSystem, IOInterface $io, $rootDirectory, PackageInterface $package) + public function __construct(IOInterface $io, $rootDirectory, PackageInterface $package) { - $this->fileSystem = $fileSystem; $this->io = $io; $this->rootDirectory = $rootDirectory; $this->package = $package; @@ -98,14 +107,6 @@ public function isInstalled() return false; } - /** - * @return Filesystem - */ - protected function getFileSystem() - { - return $this->fileSystem; - } - /** * @return IOInterface */ @@ -130,23 +131,68 @@ public function getPackage() return $this->package; } + /** + * @return string + */ + protected function getPackageName() + { + return $this->package->getName(); + } + /** * Search for parameter with specific key in "extra" composer configuration block * * @param string $extraParameterKey - * @return null|string + * @param string $defaultValue + * + * @return array|string|null + */ + protected function getExtraParameterValueByKey($extraParameterKey, $defaultValue = null) + { + $extraParameters = $this->getPackage()->getExtra(); + + $extraParameterValue = isset($extraParameters[static::EXTRA_PARAMETER_KEY_ROOT][$extraParameterKey])? + $extraParameters[static::EXTRA_PARAMETER_KEY_ROOT][$extraParameterKey]: + null; + + return (!empty($extraParameterValue)) ? $extraParameterValue : $defaultValue; + } + + /** + * Return the value defined in composer extra parameters for blacklist filtering. + * + * @return array + */ + protected function getBlacklistFilterValue() + { + return $this->getExtraParameterValueByKey(static::EXTRA_PARAMETER_FILTER_BLACKLIST, []); + } + + /** + * Get VCS glob filter expression + * + * @return array */ - protected function getExtraParameterValueByKey($extraParameterKey) + protected function getVCSFilter() { - $extraParameterValue = null; - $package = $this->getPackage(); - $extraParameters = $package->getExtra(); - if (isset($extraParameters[static::EXTRA_PARAMETER_KEY_ROOT]) - && isset($extraParameters[static::EXTRA_PARAMETER_KEY_ROOT][$extraParameterKey]) - ) { - $extraParameterValue = $extraParameters[static::EXTRA_PARAMETER_KEY_ROOT][$extraParameterKey]; + return [self::BLACKLIST_VCS_DIRECTORY_FILTER, self::BLACKLIST_VCS_IGNORE_FILE]; + } + + /** + * Combine multiple glob expression lists into one list + * + * @param array $listOfGlobExpressionLists E.g. [["*.txt", "*.pdf"], ["*.md"]] + * + * @return array + */ + protected function getCombinedFilters($listOfGlobExpressionLists) + { + $filters = []; + foreach ($listOfGlobExpressionLists as $filter) { + $filters = array_merge($filters, $filter); } - return $extraParameterValue; + + return $filters; } /** @@ -155,22 +201,33 @@ protected function getExtraParameterValueByKey($extraParameterKey) */ protected function askQuestionIfNotInstalled($messageToAsk) { - if ($this->isInstalled()) { - return $this->askQuestion($messageToAsk); - } - return true; + return $this->isInstalled() ? $this->askQuestion($messageToAsk) : true; } /** + * Returns true if the human answer to the given question was answered with a positive value (Yes/yes/Y/y). + * * @param string $messageToAsk * @return bool */ protected function askQuestion($messageToAsk) { - $response = $this->getIO()->ask($messageToAsk, 'No'); - if ((strtolower($response) === 'yes' || strtolower($response) === 'y')) { - return true; - } - return false; + $userInput = $this->getIO()->ask($messageToAsk, 'No'); + + return $this->isPositiveUserInput($userInput); + } + + /** + * Return true if the input from user is a positive answer (Yes/yes/Y/y) + * + * @param string $userInput Raw user input + * + * @return bool + */ + private function isPositiveUserInput($userInput) + { + $positiveAnswers = ['yes', 'y']; + + return in_array(strtolower(trim($userInput)), $positiveAnswers, true); } } diff --git a/src/Installer/ModuleInstaller.php b/src/Installer/Package/ModulePackageInstaller.php similarity index 58% rename from src/Installer/ModuleInstaller.php rename to src/Installer/Package/ModulePackageInstaller.php index 9036eeb..532e211 100644 --- a/src/Installer/ModuleInstaller.php +++ b/src/Installer/Package/ModulePackageInstaller.php @@ -20,21 +20,25 @@ * @version OXID eShop Composer plugin */ -namespace OxidEsales\ComposerPlugin\Installer; +namespace OxidEsales\ComposerPlugin\Installer\Package; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\CopyGlobFilteredFileManager; +use Webmozart\PathUtil\Path; /** * @inheritdoc */ -class ModuleInstaller extends AbstractInstaller +class ModulePackageInstaller extends AbstractPackageInstaller { const METADATA_FILE_NAME = 'metadata.php'; + const MODULES_DIRECTORY = 'modules'; /** * @return bool */ public function isInstalled() { - return file_exists($this->formTargetPath() . '/' . static::METADATA_FILE_NAME . ''); + return file_exists(Path::join($this->formTargetPath(), static::METADATA_FILE_NAME)); } /** @@ -44,8 +48,8 @@ public function isInstalled() */ public function install($packagePath) { - $this->getIO()->write("Installing module {$this->getPackage()->getName()} package."); - $this->getFileSystem()->mirror($this->formSourcePath($packagePath), $this->formTargetPath()); + $this->getIO()->write("Installing module {$this->getPackageName()} package."); + $this->copyPackage($packagePath); } /** @@ -55,18 +59,32 @@ public function install($packagePath) */ public function update($packagePath) { - if ($this->askQuestionIfNotInstalled("Update operation will overwrite {$this->getPackage()->getName()} files." + if ($this->askQuestionIfNotInstalled("Update operation will overwrite {$this->getPackageName()} files." ." Do you want to continue? (Yes/No) ")) { - $this->getIO()->write("Copying module {$this->getPackage()->getName()} files..."); - $this->getFileSystem()->mirror( - $this->formSourcePath($packagePath), - $this->formTargetPath(), - null, - ['override' => true] - ); + $this->getIO()->write("Copying module {$this->getPackageName()} files..."); + $this->copyPackage($packagePath); } } + /** + * Copy files from package source to defined target path. + * + * @param string $packagePath Absolute path to the package. + */ + protected function copyPackage($packagePath) + { + $filtersToApply = [ + $this->getBlacklistFilterValue(), + $this->getVCSFilter(), + ]; + + CopyGlobFilteredFileManager::copy( + $this->formSourcePath($packagePath), + $this->formTargetPath(), + $this->getCombinedFilters($filtersToApply) + ); + } + /** * If module source directory option provided add it's relative path. * Otherwise return plain package path. @@ -79,11 +97,9 @@ protected function formSourcePath($packagePath) { $sourceDirectory = $this->getExtraParameterValueByKey(static::EXTRA_PARAMETER_KEY_SOURCE); - if (empty($sourceDirectory)) { - return $packagePath; - } - - return $packagePath . "/$sourceDirectory"; + return !empty($sourceDirectory)? + Path::join($packagePath, $sourceDirectory): + $packagePath; } /** @@ -91,12 +107,11 @@ protected function formSourcePath($packagePath) */ protected function formTargetPath() { - $package = $this->getPackage(); - $targetDirectory = $this->getExtraParameterValueByKey(static::EXTRA_PARAMETER_KEY_TARGET); - if (is_null($targetDirectory)) { - $targetDirectory = $package->getName(); - } - $targetDirectory = $this->getRootDirectory() . "/modules/$targetDirectory"; - return $targetDirectory; + $targetDirectory = $this->getExtraParameterValueByKey( + static::EXTRA_PARAMETER_KEY_TARGET, + $this->getPackage()->getName() + ); + + return Path::join($this->getRootDirectory(), static::MODULES_DIRECTORY, $targetDirectory); } } diff --git a/src/Installer/Package/ShopPackageInstaller.php b/src/Installer/Package/ShopPackageInstaller.php new file mode 100644 index 0000000..a0f38ee --- /dev/null +++ b/src/Installer/Package/ShopPackageInstaller.php @@ -0,0 +1,227 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Installer\Package; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\CopyGlobFilteredFileManager; +use Webmozart\Glob\Iterator\GlobIterator; +use Webmozart\PathUtil\Path; + +/** + * @inheritdoc + */ +class ShopPackageInstaller extends AbstractPackageInstaller +{ + const FILE_TO_CHECK_IF_PACKAGE_INSTALLED = 'index.php'; + const SHOP_SOURCE_CONFIGURATION_FILE = 'config.inc.php'; + const DISTRIBUTION_FILE_EXTENSION_MARK = '.dist'; + const SHOP_SOURCE_DIRECTORY = 'source'; + const SHOP_SOURCE_SETUP_DIRECTORY = 'Setup'; + const HTACCESS_FILTER = '**/.htaccess'; + const SETUP_FILES_FILTER = self::SHOP_SOURCE_SETUP_DIRECTORY . AbstractPackageInstaller::BLACKLIST_ALL_FILES; + + /** + * @return bool + */ + public function isInstalled() + { + return file_exists( + Path::join($this->getTargetDirectoryOfShopSource(), self::FILE_TO_CHECK_IF_PACKAGE_INSTALLED) + ); + } + + /** + * Copies all shop files from vendors to source directory. + * + * @param string $packagePath + */ + public function install($packagePath) + { + $this->getIO()->write("Installing shop package."); + $this->copyPackage($packagePath); + } + + /** + * Overwrites files in core directories. + * + * @param string $packagePath + */ + public function update($packagePath) + { + $this->getIO()->write("Installing shop package."); + if ($this->askQuestionIfNotInstalled('Do you want to overwrite existing OXID eShop files? (Yes/No) ')) { + $this->getIO()->write("Copying shop files to source directory..."); + $this->copyPackage($packagePath); + } + } + + /** + * @param string $packagePath + */ + private function copyPackage($packagePath) + { + $this->copyShopSourceFromPackageToTarget($packagePath); + $this->copySetupFilesIfNecessary($packagePath); + $this->copyConfigurationDistFileWithinTarget(); + $this->copyHtaccessFilesIfNecessary($packagePath); + } + + /** + * Copy shop source files from package source to defined target path. + * + * @param string $packagePath + */ + private function copyShopSourceFromPackageToTarget($packagePath) + { + $filtersToApply = [ + $this->getBlacklistFilterValue(), + [self::HTACCESS_FILTER], + [self::SETUP_FILES_FILTER], + $this->getVCSFilter(), + ]; + + CopyGlobFilteredFileManager::copy( + $this->getPackageDirectoryOfShopSource($packagePath), + $this->getTargetDirectoryOfShopSource(), + $this->getCombinedFilters($filtersToApply) + ); + } + + /** + * Copy shop's configuration file from distribution file if necessary. + */ + private function copyConfigurationDistFileWithinTarget() + { + $pathToConfig = Path::join($this->getTargetDirectoryOfShopSource(), self::SHOP_SOURCE_CONFIGURATION_FILE); + $pathToConfigDist = $pathToConfig . self::DISTRIBUTION_FILE_EXTENSION_MARK; + + if (!file_exists($pathToConfig)) { + CopyGlobFilteredFileManager::copy($pathToConfigDist, $pathToConfig); + } + } + + /** + * Copy shop's htaccess files from package if necessary. + * + * @param string $packagePath Absolute path which points to shop's package directory. + */ + private function copyHtaccessFilesIfNecessary($packagePath) + { + $packageDirectoryOfShopSource = $this->getPackageDirectoryOfShopSource($packagePath); + $installationDirectoryOfShopSource = $this->getTargetDirectoryOfShopSource(); + + $htAccessFilesIterator = new GlobIterator(Path::join($packageDirectoryOfShopSource, self::HTACCESS_FILTER)); + + foreach ($htAccessFilesIterator as $absolutePathToHtAccessFromPackage) { + $relativePathOfSourceFromPackage = Path::makeRelative( + $absolutePathToHtAccessFromPackage, + $packageDirectoryOfShopSource + ); + $absolutePathToHtAccessFromInstallation = Path::join( + $installationDirectoryOfShopSource, + $relativePathOfSourceFromPackage + ); + + if (!file_exists($absolutePathToHtAccessFromInstallation)) { + CopyGlobFilteredFileManager::copy( + $absolutePathToHtAccessFromPackage, + $absolutePathToHtAccessFromInstallation + ); + } + } + } + + /** + * Copy shop's setup files from package if necessary. + * + * @param string $packagePath Absolute path which points to shop's package directory. + */ + private function copySetupFilesIfNecessary($packagePath) + { + $packageDirectoryOfShopSource = $this->getPackageDirectoryOfShopSource($packagePath); + $installationDirectoryOfShopSource = $this->getTargetDirectoryOfShopSource(); + + $shopConfigFileName = Path::join($installationDirectoryOfShopSource, self::SHOP_SOURCE_CONFIGURATION_FILE); + + if ($this->isConfigFileNotConfiguredOrMissing($shopConfigFileName)) { + CopyGlobFilteredFileManager::copy( + Path::join($packageDirectoryOfShopSource, self::SHOP_SOURCE_SETUP_DIRECTORY), + Path::join($installationDirectoryOfShopSource, self::SHOP_SOURCE_SETUP_DIRECTORY) + ); + } + } + + /** + * Return true if config file is not configured or missing. + * + * @param string $shopConfigFileName Absolute path to shop configuration file to check. + * + * @return bool + */ + private function isConfigFileNotConfiguredOrMissing($shopConfigFileName) + { + if (!file_exists($shopConfigFileName)) { + return true; + } + + $shopConfigFileContents = file_get_contents($shopConfigFileName); + $wordsIndicatingNotConfigured = [ + '', + '', + '', + '', + '', + '', + '', + ]; + + foreach ($wordsIndicatingNotConfigured as $word) { + if (strpos($shopConfigFileContents, $word) !== false) { + return true; + } + } + + return false; + } + + /** + * Return package directory which points to shop's source directory. + * + * @param string $packagePath Absolute path which points to shop's package directory. + * + * @return string + */ + private function getPackageDirectoryOfShopSource($packagePath) + { + return Path::join($packagePath, self::SHOP_SOURCE_DIRECTORY); + } + + /** + * Return target directory where shop's source files needs to be copied. + * + * @return string + */ + private function getTargetDirectoryOfShopSource() + { + return $this->getRootDirectory(); + } +} diff --git a/src/Installer/ThemeInstaller.php b/src/Installer/Package/ThemePackageInstaller.php similarity index 74% rename from src/Installer/ThemeInstaller.php rename to src/Installer/Package/ThemePackageInstaller.php index 9038485..20ea76f 100644 --- a/src/Installer/ThemeInstaller.php +++ b/src/Installer/Package/ThemePackageInstaller.php @@ -20,12 +20,16 @@ * @version OXID eShop Composer plugin */ -namespace OxidEsales\ComposerPlugin\Installer; +namespace OxidEsales\ComposerPlugin\Installer\Package; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\CopyGlobFilteredFileManager; +use Webmozart\PathUtil\Path; +use Composer\Package\PackageInterface; /** * @inheritdoc */ -class ThemeInstaller extends AbstractInstaller +class ThemePackageInstaller extends AbstractPackageInstaller { const METADATA_FILE_NAME = 'theme.php'; const PATH_TO_THEMES = "Application/views"; @@ -46,7 +50,7 @@ public function isInstalled() public function install($packagePath) { $this->getIO()->write("Installing {$this->getPackage()->getName()} package"); - $this->copyFiles($packagePath); + $this->copyPackage($packagePath); } /** @@ -59,21 +63,28 @@ public function update($packagePath) if ($this->askQuestionIfNotInstalled("Update operation will overwrite {$this->getPackage()->getName()} files." ." Do you want to continue? (Yes/No) ")) { $this->getIO()->write("Copying theme {$this->getPackage()->getName()} files..."); - $this->copyFiles($packagePath, ['override' => true]); + $this->copyPackage($packagePath); } } /** * @param string $packagePath - * @param array $options */ - protected function copyFiles($packagePath, $options = []) + protected function copyPackage($packagePath) { - $iterator = $this->getDirectoriesToSkipIteratorBuilder() - ->build($packagePath, [$this->formAssetsDirectoryName()]); - $fileSystem = $this->getFileSystem(); - $fileSystem->mirror($packagePath, $this->formThemeTargetPath(), $iterator, $options); - $this->installAssets($packagePath, $options); + $filtersToApply = [ + [Path::join($this->formAssetsDirectoryName(), AbstractPackageInstaller::BLACKLIST_ALL_FILES)], + $this->getBlacklistFilterValue(), + $this->getVCSFilter(), + ]; + + CopyGlobFilteredFileManager::copy( + $packagePath, + $this->formThemeTargetPath(), + $this->getCombinedFilters($filtersToApply) + ); + + $this->installAssets($packagePath); } /** @@ -88,9 +99,8 @@ protected function formThemeTargetPath() /** * @param string $packagePath - * @param array $options */ - protected function installAssets($packagePath, $options) + protected function installAssets($packagePath) { $package = $this->getPackage(); $target = $this->getRootDirectory() . '/out/' . $this->formThemeDirectoryName($package); @@ -98,15 +108,18 @@ protected function installAssets($packagePath, $options) $assetsDirectory = $this->formAssetsDirectoryName(); $source = $packagePath . '/' . $assetsDirectory; - $fileSystem = $this->getFileSystem(); if (file_exists($source)) { - $fileSystem->mirror($source, $target, null, $options); + CopyGlobFilteredFileManager::copy( + $source, + $target, + $this->getBlacklistFilterValue() + ); } } /** - * @param $package - * @return mixed + * @param PackageInterface $package + * @return string */ protected function formThemeDirectoryName($package) { @@ -128,12 +141,4 @@ protected function formAssetsDirectoryName() } return $assetsDirectory; } - - /** - * @return DirectoriesSkipIteratorBuilder - */ - protected function getDirectoriesToSkipIteratorBuilder() - { - return new DirectoriesSkipIteratorBuilder(); - } } diff --git a/src/Installer/PackagesInstaller.php b/src/Installer/PackageInstallerTrigger.php similarity index 70% rename from src/Installer/PackagesInstaller.php rename to src/Installer/PackageInstallerTrigger.php index 7c58f78..ca20ef7 100644 --- a/src/Installer/PackagesInstaller.php +++ b/src/Installer/PackageInstallerTrigger.php @@ -24,12 +24,16 @@ use Composer\Installer\LibraryInstaller; use Composer\Package\PackageInterface; -use Symfony\Component\Filesystem\Filesystem; +use OxidEsales\ComposerPlugin\Installer\Package\AbstractPackageInstaller; +use OxidEsales\ComposerPlugin\Installer\Package\ShopPackageInstaller; +use OxidEsales\ComposerPlugin\Installer\Package\ModulePackageInstaller; +use OxidEsales\ComposerPlugin\Installer\Package\ThemePackageInstaller; +use Webmozart\PathUtil\Path; /** * Class responsible for triggering installation process. */ -class PackagesInstaller extends LibraryInstaller +class PackageInstallerTrigger extends LibraryInstaller { const TYPE_ESHOP = 'oxideshop'; const TYPE_MODULE = 'oxideshop-module'; @@ -38,9 +42,9 @@ class PackagesInstaller extends LibraryInstaller /** @var array Available installers for packages. */ private $installers = [ - self::TYPE_ESHOP => ShopInstaller::class, - self::TYPE_MODULE => ModuleInstaller::class, - self::TYPE_THEME => ThemeInstaller::class, + self::TYPE_ESHOP => ShopPackageInstaller::class, + self::TYPE_MODULE => ModulePackageInstaller::class, + self::TYPE_THEME => ThemePackageInstaller::class, ]; /** @@ -60,7 +64,7 @@ public function supports($packageType) } /** - * @param array set additional settings + * @param array $settings Set additional settings. */ public function setSettings($settings) { @@ -78,6 +82,9 @@ public function installPackage(PackageInterface $package) } } + /** + * @param PackageInterface $package + */ public function updatePackage(PackageInterface $package) { $installer = $this->createInstaller($package); @@ -91,10 +98,10 @@ public function updatePackage(PackageInterface $package) */ public function getShopSourcePath() { - $shopSource = getcwd() . '/source'; + $shopSource = Path::join(getcwd(), ShopPackageInstaller::SHOP_SOURCE_DIRECTORY); - if (isset($this->settings[AbstractInstaller::EXTRA_PARAMETER_SOURCE_PATH])) { - $shopSource = $this->settings[AbstractInstaller::EXTRA_PARAMETER_SOURCE_PATH]; + if (isset($this->settings[AbstractPackageInstaller::EXTRA_PARAMETER_SOURCE_PATH])) { + $shopSource = $this->settings[AbstractPackageInstaller::EXTRA_PARAMETER_SOURCE_PATH]; } return $shopSource; @@ -104,10 +111,10 @@ public function getShopSourcePath() * Creates package installer. * * @param PackageInterface $package - * @return AbstractInstaller + * @return AbstractPackageInstaller */ protected function createInstaller(PackageInterface $package) { - return new $this->installers[$package->getType()](new Filesystem(), $this->io, $this->getShopSourcePath(), $package); + return new $this->installers[$package->getType()]($this->io, $this->getShopSourcePath(), $package); } } diff --git a/src/Installer/ShopInstaller.php b/src/Installer/ShopInstaller.php deleted file mode 100644 index 4e7e39e..0000000 --- a/src/Installer/ShopInstaller.php +++ /dev/null @@ -1,96 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Installer; - -/** - * @inheritdoc - */ -class ShopInstaller extends AbstractInstaller -{ - /** @var array Directories which shouldn't be copied. */ - private $directoriesToSkip = [ - 'Application/Component', - 'Application/Controller', - 'Application/Model', - 'Core' - ]; - - /** - * @return bool - */ - public function isInstalled() - { - return file_exists($this->getRootDirectory() .'/index.php'); - } - - /** - * Copies all shop files from vendors to source directory. - * - * @param string $packagePath - */ - public function install($packagePath) - { - $this->getIO()->write("Installing shop package."); - $this->copyFiles($packagePath); - } - - /** - * Overwrites files in core directories. - * - * @param string $packagePath - */ - public function update($packagePath) - { - $this->getIO()->write("Installing shop package."); - if ($this->askQuestionIfNotInstalled('Do you want to overwrite existing OXID eShop files? (Yes/No) ')) { - $this->getIO()->write("Copying shop files to source directory..."); - $this->copyFiles($packagePath, ['override' => true]); - } - } - - /** - * @return DirectoriesSkipIteratorBuilder - */ - protected function getDirectoriesToSkipIteratorBuilder() - { - return new DirectoriesSkipIteratorBuilder(); - } - - /** - * @param $packagePath - * @param array $options - */ - protected function copyFiles($packagePath, $options = []) - { - $packagePath = rtrim($packagePath, '/') . '/source'; - $root = $this->getRootDirectory(); - - $iterator = $this->getDirectoriesToSkipIteratorBuilder(); - $fileSystem = $this->getFileSystem(); - $fileSystem->mirror($packagePath, $root, $iterator->build($packagePath, $this->directoriesToSkip), $options); - - if (file_exists($root.'/config.inc.php.dist') && !file_exists($root.'/config.inc.php')) { - $fileSystem->copy($root.'/config.inc.php.dist', $root.'/config.inc.php'); - } - } -} diff --git a/src/Plugin.php b/src/Plugin.php index 579a5cc..0f06bd4 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -26,9 +26,12 @@ use Composer\EventDispatcher\EventSubscriberInterface; use Composer\IO\IOInterface; use Composer\Plugin\PluginInterface; -use OxidEsales\ComposerPlugin\Installer\AbstractInstaller; -use OxidEsales\ComposerPlugin\Installer\PackagesInstaller; +use OxidEsales\ComposerPlugin\Installer\Package\AbstractPackageInstaller; +use OxidEsales\ComposerPlugin\Installer\PackageInstallerTrigger; +/** + * Class Plugin. + */ class Plugin implements PluginInterface, EventSubscriberInterface { const ACTION_INSTALL = 'install'; @@ -41,8 +44,8 @@ class Plugin implements PluginInterface, EventSubscriberInterface /** @var IOInterface */ private $io; - /** @var PackagesInstaller */ - private $packageInstaller; + /** @var PackageInstallerTrigger */ + private $packageInstallerTrigger; /** * Register events. @@ -65,16 +68,16 @@ public static function getSubscribedEvents() */ public function activate(Composer $composer, IOInterface $io) { - $installer = new PackagesInstaller($io, $composer); - $composer->getInstallationManager()->addInstaller($installer); + $packageInstallerTrigger = new PackageInstallerTrigger($io, $composer); + $composer->getInstallationManager()->addInstaller($packageInstallerTrigger); $this->composer = $composer; $this->io = $io; - $this->packageInstaller = $installer; + $this->packageInstallerTrigger = $packageInstallerTrigger; $extraSettings = $this->composer->getPackage()->getExtra(); - if (isset($extraSettings[AbstractInstaller::EXTRA_PARAMETER_KEY_ROOT])) { - $this->packageInstaller->setSettings($extraSettings[AbstractInstaller::EXTRA_PARAMETER_KEY_ROOT]); + if (isset($extraSettings[AbstractPackageInstaller::EXTRA_PARAMETER_KEY_ROOT])) { + $this->packageInstallerTrigger->setSettings($extraSettings[AbstractPackageInstaller::EXTRA_PARAMETER_KEY_ROOT]); } } @@ -94,17 +97,20 @@ public function updatePackages() $this->executeAction(static::ACTION_UPDATE); } + /** + * @param string $actionName + */ protected function executeAction($actionName) { $repo = $this->composer->getRepositoryManager()->getLocalRepository(); foreach ($repo->getPackages() as $package) { - if ($this->packageInstaller->supports($package->getType())) { + if ($this->packageInstallerTrigger->supports($package->getType())) { if ($actionName === static::ACTION_INSTALL) { - $this->packageInstaller->installPackage($package); + $this->packageInstallerTrigger->installPackage($package); } if ($actionName === static::ACTION_UPDATE) { - $this->packageInstaller->updatePackage($package); + $this->packageInstallerTrigger->updatePackage($package); } } } diff --git a/src/Utilities/CopyFileManager/CopyGlobFilteredFileManager.php b/src/Utilities/CopyFileManager/CopyGlobFilteredFileManager.php new file mode 100644 index 0000000..cdd92c9 --- /dev/null +++ b/src/Utilities/CopyFileManager/CopyGlobFilteredFileManager.php @@ -0,0 +1,163 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2017 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobMatcher; +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Iteration\BlacklistFilterIterator; +use Symfony\Component\Filesystem\Filesystem; +use Webmozart\PathUtil\Path; + +/** + * Class CopyGlobFilteredFileManager. + * + * Copies files/directories from source to destination which matches the criteria described in a glob filter. + */ +class CopyGlobFilteredFileManager +{ + /** + * Copy files/directories from source to destination. + * + * @param string $sourcePath Absolute path to file or directory. + * @param string $destinationPath Absolute path to file or directory. + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + * + * @throws \InvalidArgumentException If given $sourcePath is not a string. + * @throws \InvalidArgumentException If given $destinationPath is not a string. + * + * @return null + */ + public static function copy($sourcePath, $destinationPath, $globExpressionList = []) + { + if (!is_string($sourcePath)) { + $message = "Given value \"$sourcePath\" is not a valid source path entry. ". + "Valid entry must be an absolute path to an existing file or directory."; + + throw new \InvalidArgumentException($message); + } + + if (!is_string($destinationPath)) { + $message = "Given value \"$destinationPath\" is not a valid destination path entry. ". + "Valid entry must be an absolute path to an existing directory."; + + throw new \InvalidArgumentException($message); + } + + if (!file_exists($sourcePath)) { + return; + } + + if (is_dir($sourcePath)) { + self::copyDirectory($sourcePath, $destinationPath, $globExpressionList); + } else { + self::copyFile($sourcePath, $destinationPath, $globExpressionList); + } + } + + /** + * Returns relative path from an absolute path to a file. + * + * @param string $sourcePath Absolute path to a file. + * + * @return string + */ + private static function getRelativePathForSingleFile($sourcePath) + { + return Path::makeRelative($sourcePath, Path::getDirectory($sourcePath)); + } + + /** + * Return an iterator which iterates through a given directory tree in a one-dimensional fashion. + * + * Consider the following file/directory structure as an example: + * + * * directory_a + * * file_a_a + * * directory_b + * * file_b_a + * * file_b_b + * * file_c + * + * RecursiveDirectoryIterator would iterate through: + * * directory_a [iterator] + * * directory_b [iterator] + * * file_c [SplFileInfo] + * + * In contrast current method would iterate through: + * * directory_a [SplFileInfo] + * * directory_a/file_a_a [SplFileInfo] + * * directory_b [SplFileInfo] + * * directory_b/file_b_a [SplFileInfo] + * * directory_b/file_b_b [SplFileInfo] + * * file_c [SplFileInfo] + * + * @param string $sourcePath Absolute path to directory. + * + * @return \Iterator + */ + private static function getFlatFileListIterator($sourcePath) + { + $recursiveFileIterator = new \RecursiveDirectoryIterator($sourcePath, \FilesystemIterator::SKIP_DOTS); + $flatFileListIterator = new \RecursiveIteratorIterator($recursiveFileIterator); + + return $flatFileListIterator; + } + + /** + * Copy whole directory using given glob filters. + * + * @param string $sourcePath Absolute path to directory. + * @param string $destinationPath Absolute path to directory. + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + */ + private static function copyDirectory($sourcePath, $destinationPath, $globExpressionList) + { + $filesystem = new Filesystem(); + + $flatFileListIterator = self::getFlatFileListIterator($sourcePath); + $filteredFileListIterator = new BlacklistFilterIterator( + $flatFileListIterator, + $sourcePath, + $globExpressionList + ); + + $filesystem->mirror($sourcePath, $destinationPath, $filteredFileListIterator, ["override" => true]); + } + + /** + * Copy file using given glob filters. + * + * @param string $sourcePathOfFile Absolute path to file. + * @param string $destinationPath Absolute path to directory. + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + */ + private static function copyFile($sourcePathOfFile, $destinationPath, $globExpressionList) + { + $filesystem = new Filesystem(); + + $relativeSourcePath = self::getRelativePathForSingleFile($sourcePathOfFile); + + if (!GlobMatcher::matchAny($relativeSourcePath, $globExpressionList)) { + $filesystem->copy($sourcePathOfFile, $destinationPath, ["override" => true]); + } + } +} diff --git a/src/Utilities/CopyFileManager/GlobMatcher/GlobListMatcher/GlobListMatcher.php b/src/Utilities/CopyFileManager/GlobMatcher/GlobListMatcher/GlobListMatcher.php new file mode 100644 index 0000000..ddb83b9 --- /dev/null +++ b/src/Utilities/CopyFileManager/GlobMatcher/GlobListMatcher/GlobListMatcher.php @@ -0,0 +1,92 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2017 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobListMatcher; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration\AbstractGlobMatcher; + +/** + * Class GlobListMatcher. + * + * Enables glob matching for a relative path against a list of glob expressions. + */ +class GlobListMatcher +{ + /** @var AbstractGlobMatcher */ + protected $globMatcher; + + /** + * GlobListMatcher constructor. + * + * @param AbstractGlobMatcher $globMatcher Instance of a variant from AbstractGlobMatcher. + */ + public function __construct($globMatcher) + { + $this->globMatcher = $globMatcher; + } + + /** + * Returns true if given relative path matches against at least one glob expression from provided list. + * + * @param string $relativePath + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + * + * @throws \InvalidArgumentException If $globExpressionList is not a \Traversable instance. + * + * @return bool + */ + public function matchAny($relativePath, $globExpressionList) + { + if (!is_array($globExpressionList) && (!$globExpressionList instanceof \Traversable) + && (!is_null($globExpressionList))) { + $message = "Given value \"$globExpressionList\" is not a valid glob expression list. ". + "Valid entry must be a list of glob expressions e.g. [\"*.txt\", \"*.pdf\"]."; + + throw new \InvalidArgumentException($message); + } + + if (count($globExpressionList) > 0) { + return $this->isMatchInList($relativePath, $globExpressionList); + } + + return false; + } + + /** + * Returns true if the supplied globMatcher indicates a match for at least one item in given glob expression list. + * + * @param string $relativePath + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + * + * @return bool + */ + private function isMatchInList($relativePath, $globExpressionList) + { + foreach ($globExpressionList as $globExpression) { + if ($this->globMatcher->match($relativePath, $globExpression)) { + return true; + } + } + + return false; + } +} diff --git a/src/Utilities/CopyFileManager/GlobMatcher/GlobMatcher.php b/src/Utilities/CopyFileManager/GlobMatcher/GlobMatcher.php new file mode 100644 index 0000000..6181e6b --- /dev/null +++ b/src/Utilities/CopyFileManager/GlobMatcher/GlobMatcher.php @@ -0,0 +1,48 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2017 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration\WebmozartGlobMatcher; +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobListMatcher\GlobListMatcher; + +/** + * Class GlobMatcher. + * + * Expose multiple glob matching interface for given relative path. + */ +class GlobMatcher +{ + /** + * @param string $relativePath Relative path to match against. + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + * + * @return bool True if given path matches any of given glob expression. + */ + public static function matchAny($relativePath, $globExpressionList) + { + $globMatcher = new WebmozartGlobMatcher(); + $globListMatcher = new GlobListMatcher($globMatcher); + + return $globListMatcher->matchAny($relativePath, $globExpressionList); + } +} diff --git a/src/Utilities/CopyFileManager/GlobMatcher/Integration/AbstractGlobMatcher.php b/src/Utilities/CopyFileManager/GlobMatcher/Integration/AbstractGlobMatcher.php new file mode 100644 index 0000000..8ec8c58 --- /dev/null +++ b/src/Utilities/CopyFileManager/GlobMatcher/Integration/AbstractGlobMatcher.php @@ -0,0 +1,76 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2017 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration; + +use InvalidArgumentException; +use Webmozart\PathUtil\Path; + +/** + * Class AbstractGlobMatcher. + * + * Abstract which defines API for matching a path against a glob expression. + */ +abstract class AbstractGlobMatcher +{ + /** + * Returns true if given path matches a glob expression. + * + * @param string $relativePath + * @param string $globExpression Glob filter expressions, e.g. "*.txt" or "*.pdf". + * + * @throws \InvalidArgumentException If given $globExpression is not a valid string. + * @throws \InvalidArgumentException If given $globExpression is an absolute path. + * + * @return bool + */ + public function match($relativePath, $globExpression) + { + if (!is_string($globExpression) && !is_null($globExpression)) { + $message = "Given value \"$globExpression\" is not a valid glob expression. ". + "Valid expression must be a string e.g. \"*.txt\"."; + + throw new InvalidArgumentException($message); + } + + if (Path::isAbsolute((string)$globExpression)) { + $message = "Given value \"$globExpression\" is an absolute path. ". + "Glob expression can only be accepted if it's a relative path."; + + throw new InvalidArgumentException($message); + } + + if (is_null($globExpression)) { + return true; + } + + return static::isGlobMatch($relativePath, $globExpression); + } + + /** + * Implementation details for matching a given path against glob expression. + * + * @param string $relativePath + * @param string $globExpression Glob filter expressions, e.g. "*.txt" or "*.pdf". + */ + abstract protected function isGlobMatch($relativePath, $globExpression); +} diff --git a/tests/Integration/Installer/StructurePreparator.php b/src/Utilities/CopyFileManager/GlobMatcher/Integration/WebmozartGlobMatcher.php similarity index 54% rename from tests/Integration/Installer/StructurePreparator.php rename to src/Utilities/CopyFileManager/GlobMatcher/Integration/WebmozartGlobMatcher.php index 93585fd..99e422f 100644 --- a/tests/Integration/Installer/StructurePreparator.php +++ b/src/Utilities/CopyFileManager/GlobMatcher/Integration/WebmozartGlobMatcher.php @@ -16,34 +16,31 @@ * along with OXID eShop Composer plugin. If not, see . * * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 + * @copyright (C) OXID eSales AG 2003-2017 * @version OXID eShop Composer plugin */ -namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer; +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration; + +use Webmozart\Glob\Glob; /** - * Makes a structure for vfsStream. + * Class WebmozartGlobMatcher. + * + * An integration of "webmozart/glob" package to match AbstractGlobMatcher. */ -class StructurePreparator +class WebmozartGlobMatcher extends AbstractGlobMatcher { /** - * @param array $structure + * Check if given path matches provided glob expression using "webmozart/glob" package. + * + * @param string $relativePath + * @param string $globExpression Glob filter expressions, e.g. "*.txt" or "*.pdf". * - * @return array + * @return bool True in case the path matches the given glob expression. */ - public function prepareStructure($structure) + protected function isGlobMatch($relativePath, $globExpression) { - $newStructure = []; - foreach ($structure as $path => $element) { - $position = &$newStructure; - foreach (explode('/', $path) as $part) { - $position[$part] = []; - $position = &$position[$part]; - } - $position = strpos($path, '/') === false ? [] : $position; - $position = is_array($element) ? $this->prepareStructure($element) : $element; - } - return $newStructure; + return Glob::match("/$relativePath", "/$globExpression"); } } diff --git a/src/Utilities/CopyFileManager/GlobMatcher/Iteration/BlacklistFilterIterator.php b/src/Utilities/CopyFileManager/GlobMatcher/Iteration/BlacklistFilterIterator.php new file mode 100644 index 0000000..f38e030 --- /dev/null +++ b/src/Utilities/CopyFileManager/GlobMatcher/Iteration/BlacklistFilterIterator.php @@ -0,0 +1,92 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2017 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Iteration; + +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobMatcher; +use Webmozart\PathUtil\Path; + +/** + * Class BlacklistFilterIterator. + * + * An iterator which iterates through given iterator of files/directories and filters out the items described in list of + * glob filter definitions (black list filtering). + */ +class BlacklistFilterIterator extends \FilterIterator +{ + /** @var array List of glob expressions, e.g. ["*.txt", "*.pdf"]. */ + private $globExpressionList; + + /** @var string Absolute root path from the start of iteration. */ + private $rootPath; + + /** + * BlacklistFilterIterator constructor. + * + * @param \Iterator $iterator An iterator which iterates through files/directories. + * @param string $rootPath Absolute root path from the start of iteration. + * @param array $globExpressionList List of glob expressions, e.g. ["*.txt", "*.pdf"]. + */ + public function __construct(\Iterator $iterator, $rootPath, $globExpressionList) + { + parent::__construct($iterator); + + $this->globExpressionList = $globExpressionList; + $this->rootPath = $rootPath; + } + + /** + * {@inheritdoc} + * + * @return bool + */ + public function accept() + { + $path = $this->convertFromSplFileInfoToString(parent::current()); + + return !GlobMatcher::matchAny($this->getRelativePath($path), $this->globExpressionList); + } + + /** + * Get relative path from given item of iteration compared to provided root path. + * + * @param string $absolutePath Absolute path from iteration. + * + * @return string + */ + private function getRelativePath($absolutePath) + { + return Path::makeRelative($absolutePath, $this->rootPath); + } + + /** + * Returns string to absolute path from an entry of SplFileInfo. + * + * @param \SplFileInfo $item Item from iteration. + * + * @return string + */ + private function convertFromSplFileInfoToString(\SplFileInfo $item) + { + return (string)$item; + } +} diff --git a/src/Utilities/VfsFileStructureOperator.php b/src/Utilities/VfsFileStructureOperator.php new file mode 100644 index 0000000..3b72d06 --- /dev/null +++ b/src/Utilities/VfsFileStructureOperator.php @@ -0,0 +1,76 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Utilities; + +/** + * Class VfsFileStructureOperator. + */ +class VfsFileStructureOperator +{ + /** + * Convert given flat file system structure into nested one. + * + * @param array|null $flatFileSystemStructure + * + * @return array + */ + public static function nest($flatFileSystemStructure = null) + { + if (!is_null($flatFileSystemStructure) && !is_array($flatFileSystemStructure)) { + throw new \InvalidArgumentException("Given input argument must be an array."); + } + + if (is_null($flatFileSystemStructure)) { + return []; + } + + $nestedFileSystemStructure = []; + + foreach ($flatFileSystemStructure as $pathEntry => $contents) { + $pathEntries = explode(DIRECTORY_SEPARATOR, $pathEntry); + + $pointerToBranch = &$nestedFileSystemStructure; + foreach ($pathEntries as $singlePathEntry) { + $singlePathEntry = trim($singlePathEntry); + + if ($singlePathEntry !== '') { + if (!is_array($pointerToBranch)) { + $pointerToBranch = []; + } + + if (!key_exists($singlePathEntry, $pointerToBranch)) { + $pointerToBranch[$singlePathEntry] = []; + } + + $pointerToBranch = &$pointerToBranch[$singlePathEntry]; + } + } + + if (substr($pathEntry, -1) !== DIRECTORY_SEPARATOR) { + $pointerToBranch = $contents; + } + } + + return $nestedFileSystemStructure; + } +} diff --git a/tests/Integration/Installer/DirectoryRecursiveFilterIteratorTest.php b/tests/Integration/Installer/DirectoryRecursiveFilterIteratorTest.php deleted file mode 100644 index d098b2e..0000000 --- a/tests/Integration/Installer/DirectoryRecursiveFilterIteratorTest.php +++ /dev/null @@ -1,61 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer; - -use org\bovigo\vfs\vfsStream; -use OxidEsales\ComposerPlugin\Installer\DirectoryRecursiveFilterIterator; - -class DirectoryRecursiveFilterIteratorTest extends \PHPUnit_Framework_TestCase -{ - public function testFilteringDirectories() - { - $structure = [ - 'Directory' => [ - 'NotSkipped' => [], - 'Skipped' => [ - 'SkippedInside' => [], - 'Class.php' => 'content' - ], - 'SkippedNot' => [], - ] - ]; - vfsStream::setup('root', 777, ['projectRoot' => $structure]); - $rootPath = vfsStream::url('root/projectRoot'); - - $directoryIterator = new \RecursiveDirectoryIterator($rootPath, \FilesystemIterator::SKIP_DOTS); - $directoryFilter = new DirectoryRecursiveFilterIterator($directoryIterator, [$rootPath.'/Directory/Skipped']); - $iterator = new \RecursiveIteratorIterator($directoryFilter, \RecursiveIteratorIterator::SELF_FIRST); - - $result = []; - foreach ($iterator as $path) { - $result[] = $path->getPathName(); - } - - $expected = [ - $rootPath.'/Directory', - $rootPath.'/Directory/NotSkipped', - $rootPath.'/Directory/SkippedNot' - ]; - $this->assertEquals($expected, $result); - } -} diff --git a/tests/Integration/Installer/ModuleInstallerTest.php b/tests/Integration/Installer/ModuleInstallerTest.php deleted file mode 100644 index 2f911c8..0000000 --- a/tests/Integration/Installer/ModuleInstallerTest.php +++ /dev/null @@ -1,131 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer; - -use Composer\IO\NullIO; -use Composer\Package\Package; -use OxidEsales\ComposerPlugin\Installer\ModuleInstaller; -use org\bovigo\vfs\vfsStream; -use Symfony\Component\Filesystem\Filesystem; - -class ModuleInstallerTest extends \PHPUnit_Framework_TestCase -{ - const PRODUCT_NAME_IN_COMPOSER_FILE = "oxid-esales/paypal-module"; - - public function testChecksIfModuleIsNotInstalled() - { - $structure = [ - 'vendor/'.static::PRODUCT_NAME_IN_COMPOSER_FILE.'/metadata.php' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $shopPreparator = new ModuleInstaller(new Filesystem(), new NullIO, $rootPath, new Package(static::PRODUCT_NAME_IN_COMPOSER_FILE, 'dev', 'dev')); - $this->assertFalse($shopPreparator->isInstalled()); - } - - public function testChecksIfModuleIsInstalled() - { - $structure = [ - 'source/modules/oxid-esales/paypal-module/metadata.php' => ' ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $shopPreparator = new ModuleInstaller(new Filesystem(), new NullIO, $rootPath, new Package(static::PRODUCT_NAME_IN_COMPOSER_FILE, 'dev', 'dev')); - $this->assertTrue($shopPreparator->isInstalled()); - } - - public function providerChecksIfModuleFilesExistsAfterInstallation() - { - return [ - [[ModuleInstaller::EXTRA_PARAMETER_KEY_ROOT => [ModuleInstaller::EXTRA_PARAMETER_KEY_TARGET => 'oe/paypal']], 'modules/oe/paypal/metadata.php'], - [[ModuleInstaller::EXTRA_PARAMETER_KEY_ROOT => [ModuleInstaller::EXTRA_PARAMETER_KEY_TARGET => 'paypal']], 'modules/paypal/metadata.php'], - [[], 'modules/oxid-esales/paypal-module/metadata.php'] - ]; - } - - /** - * @param $composerExtras - * @param $installedModuleMetadata - * - * @dataProvider providerChecksIfModuleFilesExistsAfterInstallation - */ - public function testChecksIfModuleFilesExistsAfterInstallation($composerExtras, $installedModuleMetadata) - { - $structure = [ - 'vendor/oxid-esales/paypal-module' => [ - 'metadata.php' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - - $rootPath = vfsStream::url('root/projectRoot'); - $eshopRootPath = "$rootPath/source"; - $installedModuleMetadata = "$eshopRootPath/$installedModuleMetadata"; - - $package = new Package(static::PRODUCT_NAME_IN_COMPOSER_FILE, 'dev', 'dev'); - $shopPreparator = new ModuleInstaller(new Filesystem(), new NullIO(), $eshopRootPath, $package); - $package->setExtra($composerExtras); - $moduleInVendor = "$rootPath/vendor/" . static::PRODUCT_NAME_IN_COMPOSER_FILE . ""; - $shopPreparator->install($moduleInVendor); - - $this->assertFileExists($installedModuleMetadata); - } - - public function testCheckIfModuleIsInstalledFromProvidedSourceDirectory() - { - $structure = [ - 'vendor/oxid-esales/erp/copy_this/modules/erp' => [ - 'metadata.php' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - - $rootPath = vfsStream::url('root/projectRoot'); - $eshopRootPath = "$rootPath/source"; - $installedModuleMetadata = "$eshopRootPath/modules/erp/metadata.php"; - - $package = new Package('oxid-esales/erp', 'dev', 'dev'); - $shopPreparator = new ModuleInstaller(new Filesystem(), new NullIO(), $eshopRootPath, $package); - $package->setExtra( - [ModuleInstaller::EXTRA_PARAMETER_KEY_ROOT => [ - ModuleInstaller::EXTRA_PARAMETER_KEY_SOURCE => 'copy_this/modules/erp', - ModuleInstaller::EXTRA_PARAMETER_KEY_TARGET => 'erp', - ]] - ); - $moduleInVendor = "$rootPath/vendor/oxid-esales/erp"; - $shopPreparator->install($moduleInVendor); - - $this->assertFileExists($installedModuleMetadata); - } - - /** - * @return StructurePreparator - */ - public function getStructurePreparator() - { - return new StructurePreparator(); - } -} diff --git a/tests/Integration/Installer/Package/AbstractPackageInstallerTest.php b/tests/Integration/Installer/Package/AbstractPackageInstallerTest.php new file mode 100644 index 0000000..4e12542 --- /dev/null +++ b/tests/Integration/Installer/Package/AbstractPackageInstallerTest.php @@ -0,0 +1,97 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use org\bovigo\vfs\vfsStream; +use Webmozart\PathUtil\Path; + +abstract class AbstractPackageInstallerTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + $this->setupVirtualFileSystem(); + } + + protected function setupVirtualFileSystem() + { + vfsStream::setup('root', 777, + [ + 'vendor' => [], + 'source' => [], + ] + ); + } + + protected function setupVirtualProjectRoot($prefix, $input) + { + $updated = []; + + foreach ($input as $path => $contents) { + $updated[Path::join($prefix, $path)] = $contents; + } + + return vfsStream::create(VfsFileStructureOperator::nest($updated)); + } + + protected function getVirtualShopSourcePath() + { + return $this->getVirtualFileSystemRootPath('source'); + } + + protected function getVirtualVendorPath() + { + return $this->getVirtualFileSystemRootPath('vendor'); + } + + protected function getVirtualFileSystemRootPath($suffix = '') + { + return Path::join(vfsStream::url('root'), $suffix); + } + + protected function assertVirtualFileExists($path) + { + $this->assertFileExists($this->getVirtualFileSystemRootPath($path)); + } + + protected function assertVirtualFileNotExists($path) + { + $this->assertFileNotExists($this->getVirtualFileSystemRootPath($path)); + } + + protected function assertVirtualFileEquals($expected, $actual) + { + $this->assertFileEquals( + $this->getVirtualFileSystemRootPath($expected), + $this->getVirtualFileSystemRootPath($actual) + ); + } + + protected function assertVirtualFileNotEquals($expected, $actual) + { + $this->assertFileNotEquals( + $this->getVirtualFileSystemRootPath($expected), + $this->getVirtualFileSystemRootPath($actual) + ); + } +} diff --git a/tests/Integration/Installer/Package/ModulePackageInstallerTest.php b/tests/Integration/Installer/Package/ModulePackageInstallerTest.php new file mode 100644 index 0000000..140a632 --- /dev/null +++ b/tests/Integration/Installer/Package/ModulePackageInstallerTest.php @@ -0,0 +1,384 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use OxidEsales\ComposerPlugin\Installer\Package\AbstractPackageInstaller; +use OxidEsales\ComposerPlugin\Installer\Package\ModulePackageInstaller; +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use org\bovigo\vfs\vfsStream; +use Webmozart\PathUtil\Path; + +class ModulePackageInstallerTest extends AbstractPackageInstallerTest +{ + protected function getPackageInstaller($packageName, $version = '1.0.0', $extra = []) + { + $package = new Package($packageName, $version, $version); + $package->setExtra($extra); + + return new ModulePackageInstaller( + new NullIO(), + $this->getVirtualShopSourcePath(), + $package + ); + } + + public function testModuleNotInstalledByDefault() + { + $installer = $this->getPackageInstaller('test-vendor/test-package'); + + $this->assertFalse($installer->isInstalled()); + } + + public function testModuleIsInstalledIfAlreadyExistsInShop() + { + $this->setupVirtualProjectRoot('source/modules/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package'); + + $this->assertTrue($installer->isInstalled()); + } + + public function testModuleIsInstalledAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertTrue($installer->isInstalled()); + } + + public function testModuleFilesAreCopiedAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/metadata.php', + 'source/modules/test-vendor/test-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithSameSourceDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'source-directory' => '' + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/metadata.php', + 'source/modules/test-vendor/test-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithSameTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'test-vendor/test-package' + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/metadata.php', + 'source/modules/test-vendor/test-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithSameSourceDirectoryAndSameTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'source-directory' => '', + 'target-directory' => 'test-vendor/test-package' + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/metadata.php', + 'source/modules/test-vendor/test-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithCustomSourceDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/custom-root', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'source-directory' => 'custom-root', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom-root/metadata.php', + 'source/modules/test-vendor/test-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithCustomTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'custom-vendor/custom-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/metadata.php', + 'source/modules/custom-vendor/custom-package/metadata.php' + ); + } + + public function testModuleFilesAreCopiedAfterInstallProcessWithCustomSourceDirectoryAndCustomTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/custom-root', [ + 'metadata.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'source-directory' => 'custom-root', + 'target-directory' => 'custom-vendor/custom-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom-root/metadata.php', + 'source/modules/custom-vendor/custom-package/metadata.php' + ); + } + + public function testBlacklistedFilesArePresentWhenNoBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' ' 'readme', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/module.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/readme.txt'); + } + + public function testBlacklistedFilesArePresentWhenEmptyBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' ' 'readme', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/module.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/readme.txt'); + } + + public function testBlacklistedFilesArePresentWhenDifferentBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' ' 'readme', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.pdf' + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/module.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/readme.txt'); + } + + public function testBlacklistedFilesAreSkippedWhenABlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' ' 'readme', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.txt' + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/module.php'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/readme.txt'); + } + + public function testVCSFilesAreSkippedWhenNoBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' 'HEAD', + '.git/index' => 'index', + '.git/objects/ff/fftest' => 'blob', + '.gitignore' => 'git ignore', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/HEAD'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/index'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/objects/ff/fftest'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.gitignore'); + } + + public function testVCSFilesAreSkippedWhenABlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'metadata.php' => ' ' 'readme', + '.git/HEAD' => 'HEAD', + '.git/index' => 'index', + '.git/objects/ff/fftest' => 'blob', + '.gitignore' => 'git ignore', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.txt' + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/metadata.php'); + $this->assertVirtualFileExists('source/modules/test-vendor/test-package/module.php'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/readme.txt'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/HEAD'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/index'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.git/objects/ff/fftest'); + $this->assertVirtualFileNotExists('source/modules/test-vendor/test-package/.gitignore'); + } + + public function testComplexCase() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/custom-root', [ + 'metadata.php' => ' ' 'readme', + 'readme.pdf' => 'PDF', + 'documentation/readme.txt' => 'readme', + 'documentation/example.php' => ' 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'source-directory' => 'custom-root', + 'target-directory' => 'custom-out', + 'blacklist-filter' => [ + '**/*.txt', + '**/*.pdf', + 'documentation/**/*.*', + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertTrue($installer->isInstalled()); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom-root/metadata.php', + 'source/modules/custom-out/metadata.php' + ); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom-root/module.php', + 'source/modules/custom-out/module.php' + ); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom-root/model/model.php', + 'source/modules/custom-out/model/model.php' + ); + $this->assertVirtualFileNotExists('source/modules/custom-out/readme.txt'); + $this->assertVirtualFileNotExists('source/modules/custom-out/readme.pdf'); + $this->assertVirtualFileNotExists('source/modules/custom-out/documentation'); + $this->assertVirtualFileNotExists('source/modules/custom-out/documentation/readme.txt'); + $this->assertVirtualFileNotExists('source/modules/custom-out/documentation/example.php'); + } +} diff --git a/tests/Integration/Installer/Package/ShopPackageInstallerHtaccessFilesTest.php b/tests/Integration/Installer/Package/ShopPackageInstallerHtaccessFilesTest.php new file mode 100644 index 0000000..c913b26 --- /dev/null +++ b/tests/Integration/Installer/Package/ShopPackageInstallerHtaccessFilesTest.php @@ -0,0 +1,103 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use OxidEsales\ComposerPlugin\Installer\Package\ShopPackageInstaller; +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use org\bovigo\vfs\vfsStream; +use Webmozart\PathUtil\Path; + +class ShopPackageInstallerHtaccessFilesTest extends AbstractPackageInstallerTest +{ + protected function getPackageInstaller($packageName, $version = '1.0.0', $extra = []) + { + $package = new Package($packageName, $version, $version); + $extra['oxideshop']['blacklist-filter'] = [ + "Application/Component/**/*.*", + "Application/Controller/**/*.*", + "Application/Model/**/*.*", + "Core/**/*.*" + ]; + $package->setExtra($extra); + + return new ShopPackageInstaller( + new NullIO(), + $this->getVirtualShopSourcePath(), + $package + ); + } + + public function HtaccessFilesProvider() + { + return [ + ['.htaccess'], + ['bin/.htaccess'], + ['cache/.htaccess'], + ['out/downloads/.htaccess'], + ['Application/views/admin/tpl/.htaccess'], + ['test/.htaccess'], + ]; + } + + /** + * @dataProvider HtaccessFilesProvider + */ + public function testShopInstallProcessCopiesHtaccessFilesIfTheyAreMissing($htaccessFile) + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'Original htaccess', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals("vendor/test-vendor/test-package/source/$htaccessFile", "source/$htaccessFile"); + } + + /** + * @dataProvider HtaccessFilesProvider + */ + public function testShopInstallProcessDoesNotCopyHtaccessFilesIfTheyAreAlreadyPresent($htaccessFile) + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'Original htaccess', + ]); + $this->setupVirtualProjectRoot('source', [ + $htaccessFile => 'Old', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileNotEquals( + "vendor/test-vendor/test-package/source/$htaccessFile", + "source/$htaccessFile" + ); + } +} diff --git a/tests/Integration/Installer/Package/ShopPackageInstallerSetupFilesTest.php b/tests/Integration/Installer/Package/ShopPackageInstallerSetupFilesTest.php new file mode 100644 index 0000000..6a2389f --- /dev/null +++ b/tests/Integration/Installer/Package/ShopPackageInstallerSetupFilesTest.php @@ -0,0 +1,195 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use OxidEsales\ComposerPlugin\Installer\Package\ShopPackageInstaller; +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use org\bovigo\vfs\vfsStream; +use Webmozart\PathUtil\Path; + +class ShopPackageInstallerSetupFilesTest extends AbstractPackageInstallerTest +{ + protected function getPackageInstaller($packageName, $version = '1.0.0', $extra = []) + { + $package = new Package($packageName, $version, $version); + $extra['oxideshop']['blacklist-filter'] = [ + "Application/Component/**/*.*", + "Application/Controller/**/*.*", + "Application/Model/**/*.*", + "Core/**/*.*" + ]; + $package->setExtra($extra); + + return new ShopPackageInstaller( + new NullIO(), + $this->getVirtualShopSourcePath(), + $package + ); + } + + public function testShopInstallProcessCopiesSetupFilesIfShopConfigIsMissing() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'dist', + 'Setup/index.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + "vendor/test-vendor/test-package/source/Setup/index.php", + "source/Setup/index.php" + ); + } + + public function testShopInstallProcessOverwritesSetupFilesIfShopConfigIsMissing() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'dist', + 'Setup/index.php' => 'setupVirtualProjectRoot('source', [ + 'Setup/index.php' => 'Old index file' + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + "vendor/test-vendor/test-package/source/Setup/index.php", + "source/Setup/index.php" + ); + } + + public function testShopInstallProcessCopiesSetupFilesIfShopConfigIsNotConfigured() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'setupVirtualProjectRoot('source', [ + 'config.inc.php' => $this->getNonConfiguredConfigFileContents(), + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + "vendor/test-vendor/test-package/source/Setup/index.php", + "source/Setup/index.php" + ); + } + + public function testShopInstallProcessOverwritesSetupFilesIfShopConfigIsNotConfigured() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'setupVirtualProjectRoot('source', [ + 'config.inc.php' => $this->getNonConfiguredConfigFileContents(), + 'Setup/index.php' => 'Old index file' + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + "vendor/test-vendor/test-package/source/Setup/index.php", + "source/Setup/index.php" + ); + } + + public function testShopInstallProcessDoesNotCopySetupFilesIfShopConfigIsConfigured() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'setupVirtualProjectRoot('source', [ + 'config.inc.php' => $this->getConfiguredConfigFileContents(), + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileNotExists('source/Setup/index.php'); + } + + public function testShopInstallProcessDoesNotOverwriteSetupFilesIfShopConfigIsConfigured() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'setupVirtualProjectRoot('source', [ + 'config.inc.php' => $this->getConfiguredConfigFileContents(), + 'Setup/index.php' => 'Old index file' + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileNotEquals( + "vendor/test-vendor/test-package/source/Setup/index.php", + "source/Setup/index.php" + ); + } + + protected function getNonConfiguredConfigFileContents() + { + return <<<'EOT' + $this->dbType = 'pdo_mysql'; + $this->dbHost = ''; + $this->dbPort = 3306; + $this->dbName = ''; + $this->dbUser = ''; + $this->dbPwd = ''; + $this->sShopURL = ''; + $this->sShopDir = ''; + $this->sCompileDir = ''; +EOT; + } + + protected function getConfiguredConfigFileContents() + { + return <<<'EOT' + $this->dbType = 'pdo_mysql'; + $this->dbHost = 'test_host'; + $this->dbPort = 3306; + $this->dbName = 'test_db'; + $this->dbUser = 'test_user'; + $this->dbPwd = 'test_password'; + $this->sShopURL = 'http://test.url/'; + $this->sShopDir = '/var/www/test/dir'; + $this->sCompileDir = '/var/www/test/dir/tmp'; +EOT; + } +} diff --git a/tests/Integration/Installer/Package/ShopPackageInstallerTest.php b/tests/Integration/Installer/Package/ShopPackageInstallerTest.php new file mode 100644 index 0000000..846b315 --- /dev/null +++ b/tests/Integration/Installer/Package/ShopPackageInstallerTest.php @@ -0,0 +1,192 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use OxidEsales\ComposerPlugin\Installer\Package\ShopPackageInstaller; +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use org\bovigo\vfs\vfsStream; +use Webmozart\PathUtil\Path; + +class ShopPackageInstallerTest extends AbstractPackageInstallerTest +{ + protected function getPackageInstaller($packageName, $version = '1.0.0', $extra = []) + { + $package = new Package($packageName, $version, $version); + $extra['oxideshop']['blacklist-filter'] = [ + "Application/Component/**/*", + "Application/Controller/**/*", + "Application/Model/**/*", + "Core/**/*" + ]; + $package->setExtra($extra); + + return new ShopPackageInstaller( + new NullIO(), + $this->getVirtualShopSourcePath(), + $package + ); + } + + public function testShopNotInstalledByDefault() + { + $installer = $this->getPackageInstaller('test-vendor/test-package'); + + $this->assertFalse($installer->isInstalled()); + } + + public function testShopIsInstalledIfSourceFilesAlreadyExist() + { + $this->setupVirtualProjectRoot('source/', [ + 'index.php' => 'getPackageInstaller('test-vendor/test-package'); + + $this->assertTrue($installer->isInstalled()); + $this->assertVirtualFileExists('source/index.php'); + } + + public function testShopIsInstalledAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertTrue($installer->isInstalled()); + } + + public function testShopFilesAreCopiedAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'tpl', + 'config.inc.php.dist' => 'dist', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/index.php', + 'source/index.php' + ); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/Application/views/template.tpl', + 'source/Application/views/template.tpl' + ); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/config.inc.php.dist', + 'source/config.inc.php.dist' + ); + } + + public function testShopInstallProcessCopiesConfigFileIfItDoesNotExist() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'dist', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/config.inc.php.dist', + 'source/config.inc.php' + ); + } + + public function testShopInstallProcessDoesNotCopyConfigFileIfItAlreadyExists() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'dist', + ]); + $this->setupVirtualProjectRoot('source', [ + 'config.inc.php' => 'old', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileNotEquals( + 'vendor/test-vendor/test-package/source/config.inc.php.dist', + 'source/config.inc.php' + ); + } + + public function testShopInstallProcessDoesNotCopyFilteredClasses() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' ' ' ' ' ' 'dist', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/Class.php', + 'source/Class.php' + ); + $this->assertVirtualFileNotExists('source/Core/Class.php'); + $this->assertVirtualFileNotExists('source/Application/Model/Class.php'); + $this->assertVirtualFileNotExists('source/Application/Controller/Class.php'); + $this->assertVirtualFileNotExists('source/Application/Component/Class.php'); + } + + public function testShopInstallProcessDoesNotCopyVCSFiles() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package/source', [ + 'index.php' => ' 'HEAD', + '.git/index' => 'index', + '.git/objects/ff/fftest' => 'blob', + '.gitignore' => 'git ignore', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/source/index.php', + 'source/index.php' + ); + $this->assertVirtualFileNotExists('source/.git/HEAD'); + $this->assertVirtualFileNotExists('source/.git/index'); + $this->assertVirtualFileNotExists('source/.git/objects/ff/fftest'); + $this->assertVirtualFileNotExists('source/.gitignore'); + } +} diff --git a/tests/Integration/Installer/Package/ThemePackageInstallerTest.php b/tests/Integration/Installer/Package/ThemePackageInstallerTest.php new file mode 100644 index 0000000..78624f4 --- /dev/null +++ b/tests/Integration/Installer/Package/ThemePackageInstallerTest.php @@ -0,0 +1,464 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer\Package; + +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use org\bovigo\vfs\vfsStream; +use OxidEsales\ComposerPlugin\Installer\Package\ThemePackageInstaller; +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; +use Webmozart\PathUtil\Path; + +class ThemePackageInstallerTest extends AbstractPackageInstallerTest +{ + protected function getPackageInstaller($packageName, $version = '1.0.0', $extra = []) + { + $package = new Package($packageName, $version, $version); + $package->setExtra($extra); + + return new ThemePackageInstaller( + new NullIO(), + $this->getVirtualShopSourcePath(), + $package + ); + } + + public function testThemeNotInstalledByDefault() + { + $installer = $this->getPackageInstaller('test-vendor/test-package'); + + $this->assertFalse($installer->isInstalled()); + } + + public function testThemeIsInstalledIfAlreadyExistsInShop() + { + $this->setupVirtualProjectRoot('source/Application/views/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package'); + + $this->assertTrue($installer->isInstalled()); + } + + public function testThemeIsInstalledAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertTrue($installer->isInstalled()); + } + + public function testThemeFilesAreCopiedAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/theme.php', + 'source/Application/views/test-package/theme.php' + ); + } + + public function testThemeFilesAreCopiedAfterInstallProcessWithSameTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'test-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/theme.php', + 'source/Application/views/test-package/theme.php' + ); + } + + public function testThemeFilesAreCopiedAfterInstallProcessWithCustomTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'custom-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/theme.php', + 'source/Application/views/custom-package/theme.php' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcess() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/out/style.css', + 'source/out/test-package/style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithSameAssetsDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'out', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/out/style.css', + 'source/out/test-package/style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithSameTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'test-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/out/style.css', + 'source/out/test-package/style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithSameAssetsDirectoryAndSameTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'out', + 'target-directory' => 'test-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/out/style.css', + 'source/out/test-package/style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithCustomAssetsDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'custom_assets', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom_assets/custom_style.css', + 'source/out/test-package/custom_style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithCustomTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'target-directory' => 'custom-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/out/style.css', + 'source/out/custom-package/style.css' + ); + } + + public function testThemeAssetsAreCopiedAfterInstallProcessWithCustomAssetsDirectoryAndCustomTargetDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'css', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'custom_assets', + 'target-directory' => 'custom-package', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom_assets/custom_style.css', + 'source/out/custom-package/custom_style.css' + ); + } + + public function testThemeAssetsAreNotCopiedAfterInstallProcessWithNonExistingCustomAssetsDirectory() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => 'getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'custom_assets', + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileNotExists('source/out/test-package/custom_style.css'); + } + + public function testBlacklistedFilesArePresentWhenNoBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileExists('source/Application/views/test-package/theme.txt'); + $this->assertVirtualFileExists('source/out/test-package/style.css'); + $this->assertVirtualFileExists('source/out/test-package/style.pdf'); + } + + public function testBlacklistedFilesArePresentWhenEmptyBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileExists('source/Application/views/test-package/theme.txt'); + $this->assertVirtualFileExists('source/out/test-package/style.css'); + $this->assertVirtualFileExists('source/out/test-package/style.pdf'); + } + + public function testBlacklistedFilesArePresentWhenDifferentBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.doc' + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileExists('source/Application/views/test-package/theme.txt'); + $this->assertVirtualFileExists('source/out/test-package/style.css'); + $this->assertVirtualFileExists('source/out/test-package/style.pdf'); + } + + public function testBlacklistedFilesAreSkippedWhenABlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.txt', + '**/*.pdf', + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/theme.txt'); + $this->assertVirtualFileExists('source/out/test-package/style.css'); + $this->assertVirtualFileNotExists('source/out/test-package/style.pdf'); + } + + public function testVCSFilesAreSkippedWhenNoBlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'HEAD', + '.git/index' => 'index', + '.git/objects/ff/fftest' => 'blob', + '.gitignore' => 'git ignore', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0'); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/HEAD'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/index'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/objects/ff/fftest'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.gitignore'); + } + + public function testVCSFilesAreSkippedWhenABlacklistFilterIsDefined() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + '.git/HEAD' => 'HEAD', + '.git/index' => 'index', + '.git/objects/ff/fftest' => 'blob', + '.gitignore' => 'git ignore', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'blacklist-filter' => [ + '**/*.txt', + '**/*.pdf', + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertVirtualFileExists('source/Application/views/test-package/theme.php'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/theme.txt'); + $this->assertVirtualFileExists('source/out/test-package/style.css'); + $this->assertVirtualFileNotExists('source/out/test-package/style.pdf'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/HEAD'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/index'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.git/objects/ff/fftest'); + $this->assertVirtualFileNotExists('source/Application/views/test-package/.gitignore'); + } + + public function testComplexCase() + { + $this->setupVirtualProjectRoot('vendor/test-vendor/test-package', [ + 'theme.php' => ' 'txt', + 'out/style.css' => 'css', + 'out/style.pdf' => 'PDF', + 'custom_assets/custom_style.css' => 'css', + 'custom_assets/custom_style.pdf' => 'PDF', + ]); + + $installer = $this->getPackageInstaller('test-vendor/test-package', '1.0.0', [ + 'oxideshop' => [ + 'assets-directory' => 'custom_assets', + 'target-directory' => 'custom-package', + 'blacklist-filter' => [ + '**/*.txt', + '**/*.pdf', + ] + ] + ]); + $installer->install($this->getVirtualFileSystemRootPath('vendor/test-vendor/test-package')); + + $this->assertTrue($installer->isInstalled()); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/theme.php', + 'source/Application/views/custom-package/theme.php' + ); + $this->assertVirtualFileEquals( + 'vendor/test-vendor/test-package/custom_assets/custom_style.css', + 'source/out/custom-package/custom_style.css' + ); + $this->assertVirtualFileNotExists('source/Application/views/custom-package/theme.txt'); + $this->assertVirtualFileNotExists('source/out/custom-package/style.css'); + $this->assertVirtualFileNotExists('source/out/custom-package/style.pdf'); + $this->assertVirtualFileNotExists('source/out/custom-package/custom_style.pdf'); + } +} diff --git a/tests/Integration/Installer/PackagesInstallerTest.php b/tests/Integration/Installer/PackageInstallerTriggerTest.php similarity index 82% rename from tests/Integration/Installer/PackagesInstallerTest.php rename to tests/Integration/Installer/PackageInstallerTriggerTest.php index d70c14f..8422155 100644 --- a/tests/Integration/Installer/PackagesInstallerTest.php +++ b/tests/Integration/Installer/PackageInstallerTriggerTest.php @@ -25,13 +25,9 @@ use Composer\Composer; use Composer\Config; use Composer\IO\NullIO; -use org\bovigo\vfs\vfsStream; -use org\bovigo\vfs\vfsStreamWrapper; -use org\bovigo\vfs\visitor\vfsStreamStructureVisitor; -use OxidEsales\ComposerPlugin\Installer\PackagesInstaller; -use Symfony\Component\Filesystem\Filesystem; +use OxidEsales\ComposerPlugin\Installer\PackageInstallerTrigger; -class PackagesInstallerTest extends \PHPUnit_Framework_TestCase +class PackageInstallerTriggerTest extends \PHPUnit_Framework_TestCase { /** * The composer.json file already in source for 5.3. @@ -42,7 +38,7 @@ public function testGetShopSourcePathByConfiguration() $composerMock = $this->getMock(Composer::class); $composerMock->method('getConfig')->withAnyParameters()->willReturn($composerConfigMock); - $packageInstallerStub = new PackagesInstaller(new NullIO(), $composerMock); + $packageInstallerStub = new PackageInstallerTrigger(new NullIO(), $composerMock); $packageInstallerStub->setSettings([ 'source-path' => 'some/path/to/source' ]); @@ -58,7 +54,7 @@ public function testGetShopSourcePathFor60() $composerMock = $this->getMock(Composer::class); $composerMock->method('getConfig')->withAnyParameters()->willReturn($composerConfigMock); - $packageInstallerStub = new PackagesInstaller(new NullIO(), $composerMock); + $packageInstallerStub = new PackageInstallerTrigger(new NullIO(), $composerMock); $result = $packageInstallerStub->getShopSourcePath(); $this->assertEquals($result, getcwd() . '/source'); diff --git a/tests/Integration/Installer/ShopInstallerTest.php b/tests/Integration/Installer/ShopInstallerTest.php deleted file mode 100644 index 23960e8..0000000 --- a/tests/Integration/Installer/ShopInstallerTest.php +++ /dev/null @@ -1,130 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer; - -use Composer\IO\NullIO; -use Composer\Package\Package; -use OxidEsales\ComposerPlugin\Installer\ShopInstaller; -use org\bovigo\vfs\vfsStream; -use Symfony\Component\Filesystem\Filesystem; - -class ShopInstallerTest extends \PHPUnit_Framework_TestCase -{ - public function testChecksIfPackageIsNotInstalled() - { - $structure = [ - 'source/vendor/oxideshop_ce/source/index.php' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $shopPreparator = new ShopInstaller(new Filesystem(), new NullIO, $rootPath, new Package('oxid-esales/oxideshop-ce', 'dev', 'dev')); - $this->assertFalse($shopPreparator->isInstalled()); - } - - public function testChecksIfPackageInstalled() - { - $structure = [ - 'source' => [ - 'index.php' => ' ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $shopPreparator = new ShopInstaller(new Filesystem(), new NullIO, $rootPath, new Package('oxid-esales/oxideshop-ce', 'dev', 'dev')); - $this->assertTrue($shopPreparator->isInstalled()); - } - - public function testInstallationOfPackage() - { - $structure = [ - 'source/vendor/oxideshop_ce/source' => [ - 'index.php' => ' ' ' $this->getStructurePreparator()->prepareStructure($structure)]); - - $rootPath = vfsStream::url('root/projectRoot/source'); - $shopDirectory = "$rootPath/vendor/oxideshop_ce"; - - $shopPreparator = new ShopInstaller(new Filesystem(), new NullIO, $rootPath, new Package('oxid-esales/oxideshop-ce', 'dev', 'dev')); - $shopPreparator->install($shopDirectory); - - $this->assertFileExists($rootPath . '/index.php'); - $this->assertFileExists($rootPath . '/Application/views/template.tpl'); - $this->assertFileExists($rootPath . '/config.inc.php.dist'); - } - - public function testInstallCreatesConfigInc() - { - $structure = [ - 'source/vendor/oxideshop_ce/source' => [ - 'config.inc.php.dist' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - - $rootPath = vfsStream::url('root/projectRoot/source'); - $shopDirectory = "$rootPath/vendor/oxideshop_ce"; - - $shopPreparator = new ShopInstaller(new Filesystem(), new NullIO, $rootPath, new Package('oxid-esales/oxideshop-ce', 'dev', 'dev')); - $shopPreparator->install($shopDirectory); - - $this->assertFileExists($rootPath . '/config.inc.php'); - } - - public function testInstallDoesNotCopyClasses() - { - $structure = [ - 'source/vendor/oxideshop_ce/source' => [ - 'Core/Class.php' => ' ' ' $this->getStructurePreparator()->prepareStructure($structure)]); - - $rootPath = vfsStream::url('root/projectRoot/source'); - $shopDirectory = "$rootPath/vendor/oxideshop_ce"; - - $shopPreparator = new ShopInstaller(new Filesystem(), new NullIO, $rootPath, new Package('oxid-esales/oxideshop-ce', 'dev', 'dev')); - $shopPreparator->install($shopDirectory); - - $this->assertFileNotExists($rootPath . '/Core/Class.php'); - $this->assertFileNotExists($rootPath . '/Application/Model/Class.php'); - $this->assertFileNotExists($rootPath . '/Application/Controller/Class.php'); - $this->assertFileNotExists($rootPath . '/Application/Controller'); - } - - /** - * @return StructurePreparator - */ - public function getStructurePreparator() - { - return new StructurePreparator(); - } -} diff --git a/tests/Integration/Installer/ThemeInstallerTest.php b/tests/Integration/Installer/ThemeInstallerTest.php deleted file mode 100644 index 54773d1..0000000 --- a/tests/Integration/Installer/ThemeInstallerTest.php +++ /dev/null @@ -1,166 +0,0 @@ -. - * - * @link http://www.oxid-esales.com - * @copyright (C) OXID eSales AG 2003-2016 - * @version OXID eShop Composer plugin - */ - -namespace OxidEsales\ComposerPlugin\Tests\Integration\Installer; - -use Composer\IO\NullIO; -use Composer\Package\Package; -use org\bovigo\vfs\vfsStream; -use OxidEsales\ComposerPlugin\Installer\ThemeInstaller; -use Symfony\Component\Filesystem\Filesystem; - -class ThemeInstallerTest extends \PHPUnit_Framework_TestCase -{ - const THEME_NAME_IN_COMPOSER = "oxid-esales/flow-theme"; - - public function testChecksIfThemeIsNotInstalled() - { - $structure = [ - 'vendor/'.static::THEME_NAME_IN_COMPOSER.'/theme.php' => ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $package = new Package(static::THEME_NAME_IN_COMPOSER, 'dev', 'dev'); - $themeInstaller = new ThemeInstaller(new Filesystem(), new NullIO, $rootPath, $package); - $this->assertFalse($themeInstaller->isInstalled()); - } - - public function testChecksIfThemeIsInstalled() - { - $structure = [ - 'source/Application/views/flow-theme/theme.php' => ' ' $this->getStructurePreparator()->prepareStructure($structure)]); - $rootPath = vfsStream::url('root/projectRoot/source'); - - $package = new Package(static::THEME_NAME_IN_COMPOSER, 'dev', 'dev'); - $shopPreparator = new ThemeInstaller(new Filesystem(), new NullIO(), $rootPath, $package); - $this->assertTrue($shopPreparator->isInstalled()); - } - - /** - * @return array - */ - public function providerChecksIfThemeFilesExistsAfterInstallation() - { - return [ - [ - [ThemeInstaller::EXTRA_PARAMETER_KEY_ROOT => [ThemeInstaller::EXTRA_PARAMETER_KEY_TARGET => 'flow']], - 'Application/views/flow/theme.php', - 'out/flow/style.css' - ], - [ - [], - 'Application/views/flow-theme/theme.php', - 'out/flow-theme/style.css' - ], - [ - [ThemeInstaller::EXTRA_PARAMETER_KEY_ROOT => [ThemeInstaller::EXTRA_PARAMETER_KEY_ASSETS => 'custom_directory_name']], - 'Application/views/flow-theme/theme.php', - 'out/flow-theme/custom_style.css' - ], - [ - [ThemeInstaller::EXTRA_PARAMETER_KEY_ROOT => [ - ThemeInstaller::EXTRA_PARAMETER_KEY_TARGET => 'flow', - ThemeInstaller::EXTRA_PARAMETER_KEY_ASSETS => 'custom_directory_name', - ]], - 'Application/views/flow/theme.php', - 'out/flow/custom_style.css' - ], - ]; - } - - /** - * @param $composerExtras - * @param $installedThemeMetadata - * @param $assetsFile - * @dataProvider providerChecksIfThemeFilesExistsAfterInstallation - */ - public function testChecksIfThemeFilesExistsAfterInstallation($composerExtras, $installedThemeMetadata, $assetsFile) - { - $rootPath = vfsStream::url('root/projectRoot'); - $eshopRootPath = "$rootPath/source"; - $this->simulateInstallation($composerExtras, $rootPath, $eshopRootPath); - - $installedThemeMetadata = "$eshopRootPath/$installedThemeMetadata"; - $assetsFile = "$eshopRootPath/$assetsFile"; - $this->assertFileExists($installedThemeMetadata); - $this->assertFileExists($assetsFile); - } - - public function testChecksIfAssetFileDoesNotExist() - { - $composerExtras = [ThemeInstaller::EXTRA_PARAMETER_KEY_ROOT => [ - ThemeInstaller::EXTRA_PARAMETER_KEY_TARGET => 'flow', - ThemeInstaller::EXTRA_PARAMETER_KEY_ASSETS => 'non_existing_directory', - ]]; - - $rootPath = vfsStream::url('root/projectRoot'); - $eshopRootPath = "$rootPath/source"; - $this->simulateInstallation($composerExtras, $rootPath, $eshopRootPath); - $this->assertFileNotExists($eshopRootPath.'/out/non_existing_directory'); - } - - public function testChecksIfAssetsDirectoryWasNotCopied() - { - $composerExtras = [ThemeInstaller::EXTRA_PARAMETER_KEY_ROOT => [ - ThemeInstaller::EXTRA_PARAMETER_KEY_TARGET => 'flow' - ]]; - - $rootPath = vfsStream::url('root/projectRoot'); - $eshopRootPath = "$rootPath/source"; - $this->simulateInstallation($composerExtras, $rootPath, $eshopRootPath); - $this->assertFileNotExists($eshopRootPath . '/Application/views/flow/out/style.css'); - } - - /** - * @return StructurePreparator - */ - protected function getStructurePreparator() - { - return new StructurePreparator(); - } - - /** - * @param $composerExtras - * @return string - */ - protected function simulateInstallation($composerExtras, $rootPath, $eshopRootPath) - { - $structure = [ - 'vendor/' . static::THEME_NAME_IN_COMPOSER => [ - 'theme.php' => ' '.class {}', - 'custom_directory_name/custom_style.css' => '.class {}', - ] - ]; - vfsStream::setup('root', 777, ['projectRoot' => $this->getStructurePreparator()->prepareStructure($structure)]); - - $package = new Package(static::THEME_NAME_IN_COMPOSER, 'dev', 'dev'); - $shopPreparator = new ThemeInstaller(new Filesystem(), new NullIO(), $eshopRootPath, $package); - $package->setExtra($composerExtras); - $themeInVendor = "$rootPath/vendor/" . static::THEME_NAME_IN_COMPOSER; - $shopPreparator->install($themeInVendor); - } -} diff --git a/tests/Unit/Utilities/CopyFileManager/CopyGlobFilteredFileManagerTest.php b/tests/Unit/Utilities/CopyFileManager/CopyGlobFilteredFileManagerTest.php new file mode 100644 index 0000000..b273683 --- /dev/null +++ b/tests/Unit/Utilities/CopyFileManager/CopyGlobFilteredFileManagerTest.php @@ -0,0 +1,374 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Unit\Utilities\CopyFileManager; + +use org\bovigo\vfs\vfsStream; +use OxidEsales\ComposerPlugin\Utilities\CopyFileManager\CopyGlobFilteredFileManager; +use Webmozart\PathUtil\Path; + +/** + * Class CopyGlobFilteredFileManagerTest. + * + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\CopyGlobFilteredFileManager + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobMatcher + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Iteration\BlacklistFilterIterator + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration\AbstractGlobMatcher + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\Integration\WebmozartGlobMatcher + * @covers \OxidEsales\ComposerPlugin\Utilities\CopyFileManager\GlobMatcher\GlobListMatcher\GlobListMatcher + */ +class CopyGlobFilteredFileManagerTest extends \PHPUnit_Framework_TestCase +{ + /** @var array */ + private $filter = []; + + public function testBasicFileCopyOperation() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->simulateCopyWithFilter('module.php', 'module.php'); + + $this->assertFileCopyIsIdentical(['module.php']); + } + + public function testNoExceptionThrownWhenSourceFileDoesNotExist() + { + $this->prepareVirtualFileSystem([], []); + $this->simulateCopyWithFilter('module.php', 'module.php'); + + $this->assertFilesNotExistInDestination(['module.php']); + } + + public function testThrowsExceptionWhenSourceValueIsInvalid() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setExpectedException( + \InvalidArgumentException::class, + "Given value \"1\" is not a valid source path entry. ". + "Valid entry must be an absolute path to an existing file or directory." + ); + + $destinationPath = $this->getSourcePath('module.php'); + CopyGlobFilteredFileManager::copy(1, $destinationPath); + } + + public function testThrowsExceptionWhenDestinationValueIsInvalid() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setExpectedException( + \InvalidArgumentException::class, + "Given value \"1\" is not a valid destination path entry. ". + "Valid entry must be an absolute path to an existing directory." + ); + + $sourcePath = $this->getSourcePath('module.php'); + CopyGlobFilteredFileManager::copy($sourcePath, 1); + } + + public function testThrowsExceptionWhenFilterValueIsInvalid() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setExpectedException( + \InvalidArgumentException::class, + "Given value \"1\" is not a valid glob expression list. ". + "Valid entry must be a list of glob expressions e.g. [\"*.txt\", \"*.pdf\"]." + ); + + $this->setFilter(1); + $this->simulateCopyWithFilter('module.php', 'module.php'); + } + + public function testThrowsExceptionWhenFilterItemValueIsInvalid() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setExpectedException( + \InvalidArgumentException::class, + "Given value \"1\" is not a valid glob expression. ". + "Valid expression must be a string e.g. \"*.txt\"." + ); + + $this->setFilter([1]); + $this->simulateCopyWithFilter('module.php', 'module.php'); + } + + public function testThrowsExceptionWhenFilterItemValueIsAbsolutePath() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setExpectedException( + \InvalidArgumentException::class, + "Given value \"/some/absolute/path/*.*\" is an absolute path. ". + "Glob expression can only be accepted if it's a relative path." + ); + + $this->setFilter(["/some/absolute/path/*.*"]); + $this->simulateCopyWithFilter('module.php', 'module.php'); + } + + public function testSingleFileCopyFilteringOperation() + { + $inputFiles = [ + "module.txt" => "TXT_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setFilter(["*.txt"]); + $this->simulateCopyWithFilter('module.txt', 'module.txt'); + + $this->assertFilesExistInSource(['module.txt']); + $this->assertFilesNotExistInDestination(['modules.txt']); + } + + public function testSingleFileCopyFilteringOperationWhenFilterIsEmpty() + { + $inputFiles = [ + "module.txt" => "TXT_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setFilter([]); + $this->simulateCopyWithFilter('module.txt', 'module.txt'); + + $this->assertFilesExistInSource(['module.txt']); + $this->assertFilesNotExistInDestination(['modules.txt']); + } + + public function testSingleFileCopyFilteringOperationWhenFilterContainsEmptyValues() + { + $inputFiles = [ + "module.txt" => "TXT_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setFilter([null]); + $this->simulateCopyWithFilter('module.txt', 'module.txt'); + + $this->assertFilesExistInSource(['module.txt']); + $this->assertFilesNotExistInDestination(['modules.txt']); + } + + public function testBasicDirectoryTreeCopyOperation() + { + $inputFiles = [ + "module.php" => "PHP_1", + "readme.md" => "MD_1", + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->simulateCopyWithFilter(); + + $this->assertFileCopyIsIdentical([ + "module.php", + "readme.md", + ]); + } + + public function testCopyOverwritesFilesByDefault() + { + $inputFiles = [ + "module.php" => "PHP_1", + ]; + + $outputFiles = [ + "module.php" => "PHP_2", + ]; + + $this->prepareVirtualFileSystem($inputFiles, $outputFiles); + + $this->simulateCopyWithFilter(); + + $this->assertFileCopyIsIdentical(["module.php"]); + } + + public function testCopyDoesNotThrowAnErrorWhenSourceIsMissing() + { + $this->prepareVirtualFileSystem([], []); + + $this->simulateCopyWithFilter('module.php'); + + $this->assertFilesNotExistInDestination(["module.php"]); + } + + public function testFilteringFileCopyOperation() + { + $inputFiles = [ + "module.php" => "PHP_1", + "readme.md" => "MD_1", + "documentation.txt" => "TXT_1", + "src" => [ + "a.php" => "PHP_2", + "b.php" => "PHP_3", + "c.php" => "PHP_3", + ], + "tests" => [ + "test.php" => "PHP_4", + "unit" => [ + "test.php" => "PHP_5", + ], + "integration" => [ + "test.php" => "PHP_6", + ] + ], + "documentation" => [ + "document_a.pdf" => "PDF_1", + "document_b.pdf" => "PDF_2", + "index.txt" => "TXT_2", + "example.php" => "PHP_7", + ] + ]; + + $this->prepareVirtualFileSystem($inputFiles, []); + + $this->setFilter([ + "**/*.md", + "**/*.txt", + "tests/**/*.*", + "documentation/**/*.pdf", + ]); + $this->simulateCopyWithFilter(); + + $this->assertFileCopyIsIdentical([ + "module.php", + "src/a.php", + "src/b.php", + "src/c.php", + "documentation/example.php", + ]); + + $this->assertFilesNotExistInDestination([ + "readme.md", + "documentation.txt", + "tests/test.php", + "tests/unit/test.php", + "tests/integration/test.php", + "documentation/document_a.pdf", + "documentation/document_b.pdf", + "documentation/index.txt", + ]); + } + + protected function setFilter($filter) + { + $this->filter = $filter; + } + + protected function prepareVirtualFileSystem($inputStructure, $outputStructure) + { + vfsStream::setup('root', 777); + vfsStream::create([ + 'src' => $inputStructure, + 'dest' => $outputStructure, + ]); + } + + protected function simulateCopyWithFilter($source = null, $destination = null) + { + $sourcePath = $this->getSourcePath($source); + $destinationPath = $this->getDestinationPath($destination); + + CopyGlobFilteredFileManager::copy($sourcePath, $destinationPath, $this->filter); + } + + protected function getSourcePath($suffixForSource = null) + { + return Path::join(vfsStream::url('root/src'), !is_null($suffixForSource) ? $suffixForSource : ""); + } + + protected function getDestinationPath($suffixForDestination = null) + { + return Path::join(vfsStream::url('root/dest'), !is_null($suffixForDestination) ? $suffixForDestination : ""); + } + + protected function assertFilesExistInSource($paths) + { + foreach ($paths as $path) { + $this->assertFileExists($this->getSourcePath($path)); + } + } + + protected function assertFilesExistInDestination($paths) + { + foreach ($paths as $path) { + $this->assertFileExists($this->getDestinationPath($path)); + } + } + + protected function assertFilesNotExistInSource($paths) + { + foreach ($paths as $path) { + $this->assertFileNotExists($this->getSourcePath($path)); + } + } + + protected function assertFilesNotExistInDestination($paths) + { + foreach ($paths as $path) { + $this->assertFileNotExists($this->getDestinationPath($path)); + } + } + + protected function assertFileCopyIsIdentical($paths) + { + foreach ($paths as $path) { + $this->assertFileEquals($this->getSourcePath($path), $this->getDestinationPath($path)); + } + } + + protected function assertFileCopyIsDifferent($paths) + { + foreach ($paths as $path) { + $this->assertFileNotEquals($this->getSourcePath($path), $this->getDestinationPath($path)); + } + } +} diff --git a/tests/Unit/Utilities/VfsFileStructureOperatorTest.php b/tests/Unit/Utilities/VfsFileStructureOperatorTest.php new file mode 100644 index 0000000..133acaf --- /dev/null +++ b/tests/Unit/Utilities/VfsFileStructureOperatorTest.php @@ -0,0 +1,265 @@ +. + * + * @link http://www.oxid-esales.com + * @copyright (C) OXID eSales AG 2003-2016 + * @version OXID eShop Composer plugin + */ + +namespace OxidEsales\ComposerPlugin\Tests\Unit\Utilities; + +use OxidEsales\ComposerPlugin\Utilities\VfsFileStructureOperator; + +/** + * Class FileStructureOperatorTest. + */ +class VfsFileStructureOperatorTest extends \PHPUnit_Framework_TestCase +{ + public function testReturnEmptyListWhenNoInputIsProvided() + { + $this->assertSame([], VfsFileStructureOperator::nest()); + } + + public function testReturnEmptyListWhenNullInputIsProvided() + { + $this->assertSame([], VfsFileStructureOperator::nest(null)); + } + + public function testReturnEmptyListWhenEmptyInputArrayIsProvided() + { + $this->assertSame([], VfsFileStructureOperator::nest([])); + } + + public function testThrowAnExceptionIfInputIsNotAnArray() + { + $this->setExpectedException(\InvalidArgumentException::class, "Given input argument must be an array."); + VfsFileStructureOperator::nest(1); + } + + public function testReturnArrayAsIsWhenOnlyItemIsPresent() + { + $this->assertSame(['abc'], VfsFileStructureOperator::nest(['abc'])); + } + + public function testReturnArrayAsIsWhenMultipleItemsArePresent() + { + $this->assertSame(['abc', 'def'], VfsFileStructureOperator::nest(['abc', 'def'])); + } + + public function testReturnArrayAsIsWhenOnlyOneFileIsPresent() + { + $input = [ + 'file' => 'Contents' + ]; + + $this->assertSame($input, VfsFileStructureOperator::nest($input)); + } + + public function testReturnArrayAsIsWhenOnlyOneFileIsPresentIgnoringSpacesAtBeginningAndEnd() + { + $input = [ + ' file ' => 'Contents' + ]; + + $expectedOutput = [ + 'file' => 'Contents' + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnArrayAsIsWhenMultipleFilesArePresent() + { + $input = [ + 'file' => 'Contents', + 'second_file' => 'Second Contents' + ]; + + $this->assertSame($input, VfsFileStructureOperator::nest($input)); + } + + public function testReturnArrayWithSingleItemWhenSameMultipleFilesArePresentLastOneBeingAsOverrider() + { + $input = [ + 'file' => 'Contents', + ' file ' => 'Second Contents' + ]; + + $expectedOutput = [ + 'file' => 'Second Contents', + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenSingleItemContainsMultiLevelPath() + { + $input = [ + 'directory/file' => 'contents' + ]; + + $expectedOutput = [ + 'directory' => [ + 'file' => 'contents' + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenLastItemContainsMultiLevelPath() + { + $input = [ + 'directory/fake_file' => 'contents', + 'directory/fake_file/real_file' => 'real contents', + ]; + + $expectedOutput = [ + 'directory' => [ + 'fake_file' => [ + 'real_file' => 'real contents' + ] + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenSingleItemContainsMultiLevelPathWithTrailingSlash() + { + $input = [ + 'directory/sub/' => 'contents' + ]; + + $expectedOutput = [ + 'directory' => [ + 'sub' => [] + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenMultipleItemsContainsMultiLevelPathWithSameBase() + { + $input = [ + 'directory/file' => 'contents', + 'directory/second_file' => 'second contents', + ]; + + $expectedOutput = [ + 'directory' => [ + 'file' => 'contents', + 'second_file' => 'second contents', + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenMultipleItemsContainsMultiLevelPathWithSameBaseButWithBreakPointInTheMiddle() + { + $input = [ + 'directory/file' => 'contents', + 'directory_a/file' => 'a contents', + 'directory/second_file' => 'second contents', + ]; + + $expectedOutput = [ + 'directory' => [ + 'file' => 'contents', + 'second_file' => 'second contents', + ], + 'directory_a' => [ + 'file' => 'a contents', + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenSingleItemContainsMultiLevelPathMoreThenOneLevelDeep() + { + $input = [ + 'directory/another_directory/file' => 'contents' + ]; + + $expectedOutput = [ + 'directory' => [ + 'another_directory' => [ + 'file' => 'contents' + ] + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenMultipleItemsContainsMultiLevelPathWithDifferentBase() + { + $input = [ + 'directory/file' => 'contents', + 'second_directory/second_file' => 'second contents', + ]; + + $expectedOutput = [ + 'directory' => [ + 'file' => 'contents', + ], + 'second_directory' => [ + 'second_file' => 'second contents', + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } + + public function testReturnNestedArrayWhenComplexCasePresented() + { + $input = [ + 'file' => 'contents', + 'directory/file' => 'second contents', + 'directory_a/directory_b/directory_c/file' => 'third contents', + 'directory/file_b' => 'b contents', + ' file_c ' => 'c contents', + ' file_c ' => 'c override contents', + 'fake_file' => 'fake contents', + 'fake_file/real_file' => 'real contents', + ]; + + $expectedOutput = [ + 'file' => 'contents', + 'directory' => [ + 'file' => 'second contents', + 'file_b' => 'b contents', + ], + 'directory_a' => [ + 'directory_b' => [ + 'directory_c' => [ + 'file' => 'third contents', + ] + ] + ], + 'file_c' => 'c override contents', + 'fake_file' => [ + 'real_file' => 'real contents', + ] + ]; + + $this->assertSame($expectedOutput, VfsFileStructureOperator::nest($input)); + } +}