Complete VersionChecker test coverage
[lhc/web/wiklou.git] / includes / registration / VersionChecker.php
index b61a10e..02e3a7c 100644 (file)
@@ -35,22 +35,42 @@ class VersionChecker {
         */
        private $coreVersion = false;
 
+       /**
+        * @var array Loaded extensions
+        */
+       private $loaded = [];
+
        /**
         * @var VersionParser
         */
        private $versionParser;
 
-       public function __construct() {
+       /**
+        * @param string $coreVersion Current version of core
+        */
+       public function __construct( $coreVersion ) {
                $this->versionParser = new VersionParser();
+               $this->setCoreVersion( $coreVersion );
+       }
+
+       /**
+        * Set an array with credits of all loaded extensions and skins.
+        *
+        * @param array $credits An array of installed extensions with credits of them
+        * @return VersionChecker $this
+        */
+       public function setLoadedExtensionsAndSkins( array $credits ) {
+               $this->loaded = $credits;
+
+               return $this;
        }
 
        /**
         * Set MediaWiki core version.
         *
         * @param string $coreVersion Current version of core
-        * @return VersionChecker $this
         */
-       public function setCoreVersion( $coreVersion ) {
+       private function setCoreVersion( $coreVersion ) {
                try {
                        $this->coreVersion = new Constraint(
                                '==',
@@ -60,8 +80,6 @@ class VersionChecker {
                } catch ( UnexpectedValueException $e ) {
                        // Non-parsable version, don't fatal.
                }
-
-               return $this;
        }
 
        /**
@@ -69,11 +87,17 @@ class VersionChecker {
         * installed extensions in the $credits array.
         *
         * Example $extDependencies:
-        *      {
-        *              'GoogleAPIClient' => {
-        *                      'MediaWiki' => '>= 1.25.0'
-        *              }
-        *      }
+        *     {
+        *       'FooBar' => {
+        *         'MediaWiki' => '>= 1.25.0',
+        *         'extensions' => {
+        *           'FooBaz' => '>= 1.25.0'
+        *         },
+        *         'skins' => {
+        *           'BazBar' => '>= 1.0.0'
+        *         }
+        *       }
+        *     }
         *
         * @param array $extDependencies All extensions that depend on other ones
         * @return array
@@ -84,10 +108,19 @@ class VersionChecker {
                        foreach ( $dependencies as $dependencyType => $values ) {
                                switch ( $dependencyType ) {
                                        case ExtensionRegistry::MEDIAWIKI_CORE:
-                                               $errors = array_merge(
-                                                       $errors,
-                                                       $this->handleMediaWikiDependency( $values, $extension )
-                                               );
+                                               $mwError = $this->handleMediaWikiDependency( $values, $extension );
+                                               if ( $mwError !== false ) {
+                                                       $errors[] = $mwError;
+                                               }
+                                               break;
+                                       case 'extensions':
+                                       case 'skin':
+                                               foreach ( $values as $dependency => $constraint ) {
+                                                       $extError = $this->handleExtensionDependency( $dependency, $constraint, $extension );
+                                                       if ( $extError !== false ) {
+                                                               $errors[] = $extError;
+                                                       }
+                                               }
                                                break;
                                        default:
                                                throw new UnexpectedValueException( 'Dependency type ' . $dependencyType .
@@ -106,23 +139,73 @@ class VersionChecker {
         *
         * @param string $constraint The required version constraint for this dependency
         * @param string $checkedExt The Extension, which depends on this dependency
-        * @return array An empty array, if MediaWiki version is compatible with $constraint, an array
-        *  with an error message, otherwise.
+        * @return bool|string false if no error, or a string with the message
         */
        private function handleMediaWikiDependency( $constraint, $checkedExt ) {
                if ( $this->coreVersion === false ) {
                        // Couldn't parse the core version, so we can't check anything
-                       return [];
+                       return false;
                }
 
                // if the installed and required version are compatible, return an empty array
                if ( $this->versionParser->parseConstraints( $constraint )
                        ->matches( $this->coreVersion ) ) {
-                       return [];
+                       return false;
                }
                // otherwise mark this as incompatible.
-               return [ "{$checkedExt} is not compatible with the current "
-                        . "MediaWiki core (version {$this->coreVersion->getPrettyString()}), it requires: "
-                        . $constraint . '.' ];
+               return "{$checkedExt} is not compatible with the current "
+                       . "MediaWiki core (version {$this->coreVersion->getPrettyString()}), it requires: "
+                       . "$constraint.";
+       }
+
+       /**
+        * Handle a dependency to another extension.
+        *
+        * @param string $dependencyName The name of the dependency
+        * @param string $constraint The required version constraint for this dependency
+        * @param string $checkedExt The Extension, which depends on this dependency
+        * @return bool|string false for no errors, or a string message
+        */
+       private function handleExtensionDependency( $dependencyName, $constraint, $checkedExt ) {
+               // Check if the dependency is even installed
+               if ( !isset( $this->loaded[$dependencyName] ) ) {
+                       return "{$checkedExt} requires {$dependencyName} to be installed.";
+               }
+               // Check if the dependency has specified a version
+               if ( !isset( $this->loaded[$dependencyName]['version'] ) ) {
+                       // If we depend upon any version, and none is set, that's fine.
+                       if ( $constraint === '*' ) {
+                               wfDebug( "{$dependencyName} does not expose its version, but {$checkedExt}"
+                                       . " mentions it with constraint '*'. Assume it's ok so." );
+                               return false;
+                       } else {
+                               // Otherwise, mark it as incompatible.
+                               return "{$dependencyName} does not expose its version, but {$checkedExt}"
+                                       . " requires: {$constraint}.";
+                       }
+               } else {
+                       // Try to get a constraint for the dependency version
+                       try {
+                               $installedVersion = new Constraint(
+                                       '==',
+                                       $this->versionParser->normalize( $this->loaded[$dependencyName]['version'] )
+                               );
+                       } catch ( UnexpectedValueException $e ) {
+                               // Non-parsable version, output an error message that the version
+                               // string is invalid
+                               return "$dependencyName does not have a valid version string.";
+                       }
+                       // Check if the constraint actually matches...
+                       if (
+                               !$this->versionParser->parseConstraints( $constraint )->matches( $installedVersion )
+                       ) {
+                               return "{$checkedExt} is not compatible with the current "
+                                       . "installed version of {$dependencyName} "
+                                       . "({$this->loaded[$dependencyName]['version']}), "
+                                       . "it requires: " . $constraint . '.';
+                       }
+               }
+
+               return false;
        }
 }