123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- <?php
- /**
- * Sanity checks for making sure registered resources are sane.
- *
- * @file
- * @author Antoine Musso
- * @author Niklas Laxström
- * @author Santhosh Thottingal
- * @author Timo Tijhof
- * @copyright © 2012, Antoine Musso
- * @copyright © 2012, Niklas Laxström
- * @copyright © 2012, Santhosh Thottingal
- * @copyright © 2012, Timo Tijhof
- * @coversNothing
- */
- class ResourcesTest extends MediaWikiTestCase {
- /**
- * @dataProvider provideResourceFiles
- */
- public function testFileExistence( $filename, $module, $resource ) {
- $this->assertFileExists( $filename,
- "File '$resource' referenced by '$module' must exist."
- );
- }
- /**
- * @dataProvider provideMediaStylesheets
- */
- public function testStyleMedia( $moduleName, $media, $filename, $css ) {
- $cssText = CSSMin::minify( $css->cssText );
- $this->assertTrue(
- strpos( $cssText, '@media' ) === false,
- 'Stylesheets should not both specify "media" and contain @media'
- );
- }
- public function testVersionHash() {
- $data = self::getAllModules();
- foreach ( $data['modules'] as $moduleName => $module ) {
- $version = $module->getVersionHash( $data['context'] );
- $this->assertEquals( 7, strlen( $version ), "$moduleName must use ResourceLoader::makeHash" );
- }
- }
- /**
- * Verify that nothing explicitly depends on raw modules (such as "query").
- *
- * Depending on them is unsupported as they are not registered client-side by the startup module.
- *
- * @todo Modules can dynamically choose dependencies based on context. This method does not
- * test such dependencies. The same goes for testMissingDependencies() and
- * testUnsatisfiableDependencies().
- */
- public function testIllegalDependencies() {
- $data = self::getAllModules();
- $illegalDeps = [];
- foreach ( $data['modules'] as $moduleName => $module ) {
- if ( $module->isRaw() ) {
- $illegalDeps[] = $moduleName;
- }
- }
- /** @var ResourceLoaderModule $module */
- foreach ( $data['modules'] as $moduleName => $module ) {
- foreach ( $illegalDeps as $illegalDep ) {
- $this->assertNotContains(
- $illegalDep,
- $module->getDependencies( $data['context'] ),
- "Module '$moduleName' must not depend on '$illegalDep'"
- );
- }
- }
- }
- /**
- * Verify that all modules specified as dependencies of other modules actually exist.
- */
- public function testMissingDependencies() {
- $data = self::getAllModules();
- $validDeps = array_keys( $data['modules'] );
- /** @var ResourceLoaderModule $module */
- foreach ( $data['modules'] as $moduleName => $module ) {
- foreach ( $module->getDependencies( $data['context'] ) as $dep ) {
- $this->assertContains(
- $dep,
- $validDeps,
- "The module '$dep' required by '$moduleName' must exist"
- );
- }
- }
- }
- /**
- * Verify that all specified messages actually exist.
- */
- public function testMissingMessages() {
- $data = self::getAllModules();
- $lang = Language::factory( 'en' );
- /** @var ResourceLoaderModule $module */
- foreach ( $data['modules'] as $moduleName => $module ) {
- foreach ( $module->getMessages() as $msgKey ) {
- $this->assertTrue(
- wfMessage( $msgKey )->useDatabase( false )->inLanguage( $lang )->exists(),
- "Message '$msgKey' required by '$moduleName' must exist"
- );
- }
- }
- }
- /**
- * Verify that all dependencies of all modules are always satisfiable with the 'targets' defined
- * for the involved modules.
- *
- * Example: A depends on B. A has targets: mobile, desktop. B has targets: desktop. Therefore the
- * dependency is sometimes unsatisfiable: it's impossible to load module A on mobile.
- */
- public function testUnsatisfiableDependencies() {
- $data = self::getAllModules();
- /** @var ResourceLoaderModule $module */
- foreach ( $data['modules'] as $moduleName => $module ) {
- $moduleTargets = $module->getTargets();
- foreach ( $module->getDependencies( $data['context'] ) as $dep ) {
- if ( !isset( $data['modules'][$dep] ) ) {
- // Missing dependencies reported by testMissingDependencies
- continue;
- }
- $targets = $data['modules'][$dep]->getTargets();
- foreach ( $moduleTargets as $moduleTarget ) {
- $this->assertContains(
- $moduleTarget,
- $targets,
- "The module '$moduleName' must not have target '$moduleTarget' "
- . "because its dependency '$dep' does not have it"
- );
- }
- }
- }
- }
- /**
- * CSSMin::getLocalFileReferences should ignore url(...) expressions
- * that have been commented out.
- */
- public function testCommentedLocalFileReferences() {
- $basepath = __DIR__ . '/../data/css/';
- $css = file_get_contents( $basepath . 'comments.css' );
- $files = CSSMin::getLocalFileReferences( $css, $basepath );
- $expected = [ $basepath . 'not-commented.gif' ];
- $this->assertArrayEquals(
- $expected,
- $files,
- 'Url(...) expression in comment should be omitted.'
- );
- }
- /**
- * Get all registered modules from ResouceLoader.
- * @return array
- */
- protected static function getAllModules() {
- global $wgEnableJavaScriptTest;
- // Test existance of test suite files as well
- // (can't use setUp or setMwGlobals because providers are static)
- $org_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
- $wgEnableJavaScriptTest = true;
- // Initialize ResourceLoader
- $rl = new ResourceLoader();
- $modules = [];
- foreach ( $rl->getModuleNames() as $moduleName ) {
- $modules[$moduleName] = $rl->getModule( $moduleName );
- }
- // Restore settings
- $wgEnableJavaScriptTest = $org_wgEnableJavaScriptTest;
- return [
- 'modules' => $modules,
- 'resourceloader' => $rl,
- 'context' => new ResourceLoaderContext( $rl, new FauxRequest() )
- ];
- }
- /**
- * Get all stylesheet files from modules that are an instance of
- * ResourceLoaderFileModule (or one of its subclasses).
- */
- public static function provideMediaStylesheets() {
- $data = self::getAllModules();
- $cases = [];
- foreach ( $data['modules'] as $moduleName => $module ) {
- if ( !$module instanceof ResourceLoaderFileModule ) {
- continue;
- }
- $reflectedModule = new ReflectionObject( $module );
- $getStyleFiles = $reflectedModule->getMethod( 'getStyleFiles' );
- $getStyleFiles->setAccessible( true );
- $readStyleFile = $reflectedModule->getMethod( 'readStyleFile' );
- $readStyleFile->setAccessible( true );
- $styleFiles = $getStyleFiles->invoke( $module, $data['context'] );
- $flip = $module->getFlip( $data['context'] );
- foreach ( $styleFiles as $media => $files ) {
- if ( $media && $media !== 'all' ) {
- foreach ( $files as $file ) {
- $cases[] = [
- $moduleName,
- $media,
- $file,
- // XXX: Wrapped in an object to keep it out of PHPUnit output
- (object)[
- 'cssText' => $readStyleFile->invoke(
- $module,
- $file,
- $flip,
- $data['context']
- )
- ],
- ];
- }
- }
- }
- }
- return $cases;
- }
- /**
- * Get all resource files from modules that are an instance of
- * ResourceLoaderFileModule (or one of its subclasses).
- *
- * Since the raw data is stored in protected properties, we have to
- * overrride this through ReflectionObject methods.
- */
- public static function provideResourceFiles() {
- $data = self::getAllModules();
- $cases = [];
- // See also ResourceLoaderFileModule::__construct
- $filePathProps = [
- // Lists of file paths
- 'lists' => [
- 'scripts',
- 'debugScripts',
- 'styles',
- ],
- // Collated lists of file paths
- 'nested-lists' => [
- 'languageScripts',
- 'skinScripts',
- 'skinStyles',
- ],
- ];
- foreach ( $data['modules'] as $moduleName => $module ) {
- if ( !$module instanceof ResourceLoaderFileModule ) {
- continue;
- }
- $reflectedModule = new ReflectionObject( $module );
- $files = [];
- foreach ( $filePathProps['lists'] as $propName ) {
- $property = $reflectedModule->getProperty( $propName );
- $property->setAccessible( true );
- $list = $property->getValue( $module );
- foreach ( $list as $key => $value ) {
- // 'scripts' are numeral arrays.
- // 'styles' can be numeral or associative.
- // In case of associative the key is the file path
- // and the value is the 'media' attribute.
- if ( is_int( $key ) ) {
- $files[] = $value;
- } else {
- $files[] = $key;
- }
- }
- }
- foreach ( $filePathProps['nested-lists'] as $propName ) {
- $property = $reflectedModule->getProperty( $propName );
- $property->setAccessible( true );
- $lists = $property->getValue( $module );
- foreach ( $lists as $list ) {
- foreach ( $list as $key => $value ) {
- // We need the same filter as for 'lists',
- // due to 'skinStyles'.
- if ( is_int( $key ) ) {
- $files[] = $value;
- } else {
- $files[] = $key;
- }
- }
- }
- }
- // Get method for resolving the paths to full paths
- $method = $reflectedModule->getMethod( 'getLocalPath' );
- $method->setAccessible( true );
- // Populate cases
- foreach ( $files as $file ) {
- $cases[] = [
- $method->invoke( $module, $file ),
- $moduleName,
- ( $file instanceof ResourceLoaderFilePath ? $file->getPath() : $file ),
- ];
- }
- // To populate missingLocalFileRefs. Not sure how sane this is inside this test...
- $module->readStyleFiles(
- $module->getStyleFiles( $data['context'] ),
- $module->getFlip( $data['context'] ),
- $data['context']
- );
- $property = $reflectedModule->getProperty( 'missingLocalFileRefs' );
- $property->setAccessible( true );
- $missingLocalFileRefs = $property->getValue( $module );
- foreach ( $missingLocalFileRefs as $file ) {
- $cases[] = [
- $file,
- $moduleName,
- $file,
- ];
- }
- }
- return $cases;
- }
- }
|