Chris@0: 'No errors found', Chris@0: JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded', Chris@0: JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', Chris@0: JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded', Chris@0: JSON_ERROR_SYNTAX => 'Syntax error', Chris@0: JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', Chris@0: ]; Chris@0: Chris@0: if (version_compare(phpversion(), '5.5.0', '>=')) { Chris@0: $messages[JSON_ERROR_RECURSION] = 'One or more recursive references in the value to be encoded'; Chris@0: $messages[JSON_ERROR_INF_OR_NAN] = 'One or more NAN or INF values in the value to be encoded'; Chris@0: $messages[JSON_ERROR_UNSUPPORTED_TYPE] = 'A value of a type that cannot be encoded was given'; Chris@0: } Chris@0: Chris@0: return $messages; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the paths to the folders that contain the Composer integration. Chris@0: * Chris@0: * @return string[] Chris@0: * The paths. Chris@0: */ Chris@0: protected function getPaths() { Chris@0: return [ Chris@0: $this->root, Chris@0: $this->root . '/core', Chris@0: $this->root . '/core/lib/Drupal/Component/Annotation', Chris@0: $this->root . '/core/lib/Drupal/Component/Assertion', Chris@0: $this->root . '/core/lib/Drupal/Component/Bridge', Chris@0: $this->root . '/core/lib/Drupal/Component/ClassFinder', Chris@0: $this->root . '/core/lib/Drupal/Component/Datetime', Chris@0: $this->root . '/core/lib/Drupal/Component/DependencyInjection', Chris@0: $this->root . '/core/lib/Drupal/Component/Diff', Chris@0: $this->root . '/core/lib/Drupal/Component/Discovery', Chris@0: $this->root . '/core/lib/Drupal/Component/EventDispatcher', Chris@0: $this->root . '/core/lib/Drupal/Component/FileCache', Chris@0: $this->root . '/core/lib/Drupal/Component/FileSystem', Chris@0: $this->root . '/core/lib/Drupal/Component/Gettext', Chris@0: $this->root . '/core/lib/Drupal/Component/Graph', Chris@0: $this->root . '/core/lib/Drupal/Component/HttpFoundation', Chris@0: $this->root . '/core/lib/Drupal/Component/PhpStorage', Chris@0: $this->root . '/core/lib/Drupal/Component/Plugin', Chris@0: $this->root . '/core/lib/Drupal/Component/ProxyBuilder', Chris@0: $this->root . '/core/lib/Drupal/Component/Render', Chris@0: $this->root . '/core/lib/Drupal/Component/Serialization', Chris@0: $this->root . '/core/lib/Drupal/Component/Transliteration', Chris@0: $this->root . '/core/lib/Drupal/Component/Utility', Chris@0: $this->root . '/core/lib/Drupal/Component/Uuid', Chris@18: $this->root . '/core/lib/Drupal/Component/Version', Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests composer.json. Chris@0: */ Chris@0: public function testComposerJson() { Chris@0: foreach ($this->getPaths() as $path) { Chris@0: $json = file_get_contents($path . '/composer.json'); Chris@0: $result = json_decode($json); Chris@0: $this->assertNotNull($result, $this->getErrorMessages()[json_last_error()]); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests composer.lock content-hash. Chris@0: */ Chris@0: public function testComposerLockHash() { Chris@0: $content_hash = self::getContentHash(file_get_contents($this->root . '/composer.json')); Chris@0: $lock = json_decode(file_get_contents($this->root . '/composer.lock'), TRUE); Chris@0: $this->assertSame($content_hash, $lock['content-hash']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests composer.json versions. Chris@0: * Chris@0: * @param string $path Chris@0: * Path to a composer.json to test. Chris@0: * Chris@0: * @dataProvider providerTestComposerJson Chris@0: */ Chris@0: public function testComposerTilde($path) { Chris@0: $content = json_decode(file_get_contents($path), TRUE); Chris@0: $composer_keys = array_intersect(['require', 'require-dev'], array_keys($content)); Chris@0: if (empty($composer_keys)) { Chris@0: $this->markTestSkipped("$path has no keys to test"); Chris@0: } Chris@0: foreach ($composer_keys as $composer_key) { Chris@0: foreach ($content[$composer_key] as $dependency => $version) { Chris@0: // We allow tildes if the dependency is a Symfony component. Chris@0: // @see https://www.drupal.org/node/2887000 Chris@0: if (strpos($dependency, 'symfony/') === 0) { Chris@0: continue; Chris@0: } Chris@0: $this->assertFalse(strpos($version, '~'), "Dependency $dependency in $path contains a tilde, use a caret."); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Data provider for all the composer.json provided by Drupal core. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function providerTestComposerJson() { Chris@0: $root = realpath(__DIR__ . '/../../../../'); Chris@0: $tests = [[$root . '/composer.json']]; Chris@0: $directory = new \RecursiveDirectoryIterator($root . '/core'); Chris@0: $iterator = new \RecursiveIteratorIterator($directory); Chris@0: /** @var \SplFileInfo $file */ Chris@0: foreach ($iterator as $file) { Chris@0: if ($file->getFilename() === 'composer.json' && strpos($file->getPath(), 'core/modules/system/tests/fixtures/HtaccessTest') === FALSE) { Chris@0: $tests[] = [$file->getRealPath()]; Chris@0: } Chris@0: } Chris@0: return $tests; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests core's composer.json replace section. Chris@0: * Chris@0: * Verify that all core modules are also listed in the 'replace' section of Chris@0: * core's composer.json. Chris@0: */ Chris@0: public function testAllModulesReplaced() { Chris@0: // Assemble a path to core modules. Chris@0: $module_path = $this->root . '/core/modules'; Chris@0: Chris@0: // Grab the 'replace' section of the core composer.json file. Chris@0: $json = json_decode(file_get_contents($this->root . '/core/composer.json')); Chris@0: $composer_replace_packages = (array) $json->replace; Chris@0: Chris@0: // Get a list of all the files in the module path. Chris@0: $folders = scandir($module_path); Chris@0: Chris@0: // Make sure we only deal with directories that aren't . or .. Chris@0: $module_names = []; Chris@0: $discard = ['.', '..']; Chris@0: foreach ($folders as $file_name) { Chris@0: if ((!in_array($file_name, $discard)) && is_dir($module_path . '/' . $file_name)) { Chris@0: $module_names[] = $file_name; Chris@0: } Chris@0: } Chris@0: Chris@0: // Assert that each core module has a corresponding 'replace' in Chris@0: // composer.json. Chris@0: foreach ($module_names as $module_name) { Chris@0: $this->assertArrayHasKey( Chris@0: 'drupal/' . $module_name, Chris@0: $composer_replace_packages, Chris@0: 'Unable to find ' . $module_name . ' in replace list of composer.json' Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: // @codingStandardsIgnoreStart Chris@0: /** Chris@0: * The following method is copied from \Composer\Package\Locker. Chris@0: * Chris@0: * @see https://github.com/composer/composer Chris@0: */ Chris@0: /** Chris@0: * Returns the md5 hash of the sorted content of the composer file. Chris@0: * Chris@0: * @param string $composerFileContents The contents of the composer file. Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: protected static function getContentHash($composerFileContents) Chris@0: { Chris@0: $content = json_decode($composerFileContents, true); Chris@0: Chris@0: $relevantKeys = array( Chris@0: 'name', Chris@0: 'version', Chris@0: 'require', Chris@0: 'require-dev', Chris@0: 'conflict', Chris@0: 'replace', Chris@0: 'provide', Chris@0: 'minimum-stability', Chris@0: 'prefer-stable', Chris@0: 'repositories', Chris@0: 'extra', Chris@0: ); Chris@0: Chris@0: $relevantContent = array(); Chris@0: Chris@0: foreach (array_intersect($relevantKeys, array_keys($content)) as $key) { Chris@0: $relevantContent[$key] = $content[$key]; Chris@0: } Chris@0: if (isset($content['config']['platform'])) { Chris@0: $relevantContent['config']['platform'] = $content['config']['platform']; Chris@0: } Chris@0: Chris@0: ksort($relevantContent); Chris@0: Chris@0: return md5(json_encode($relevantContent)); Chris@0: } Chris@0: // @codingStandardsIgnoreEnd Chris@0: Chris@0: }