Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Provides the unit tests class and some helper classes
  19   *
  20   * @package     core_plugin
  21   * @category    test
  22   * @copyright   2013, 2015 David Mudrak <david@moodle.com>
  23   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once (__DIR__.'/fixtures/testable_update_validator.php');
  29  
  30  /**
  31   * Unit tests for the {@link \core\update\validator} class
  32   *
  33   * @copyright 2013, 2015 David Mudrak <david@moodle.com>
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class core_update_validator_testcase extends advanced_testcase {
  37  
  38      public function test_validate_files_layout() {
  39          $fixtures = __DIR__.'/fixtures/update_validator';
  40  
  41          // Non-existing directory.
  42          $validator = testable_core_update_validator::instance($fixtures.'/nulldir', array(
  43              'null/' => true,
  44              'null/lang/' => true,
  45              'null/lang/en/' => true,
  46              'null/lang/en/null.php' => true));
  47          $this->assertEquals('testable_core_update_validator', get_class($validator));
  48          $this->assertFalse($validator->execute());
  49          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR,
  50              'filenotexists', array('file' => 'null/')));
  51  
  52          // Missing expected file.
  53          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
  54              'foobar/' => true,
  55              'foobar/version.php' => true,
  56              'foobar/index.php' => true,
  57              'foobar/lang/' => true,
  58              'foobar/lang/en/' => true,
  59              'foobar/lang/en/local_foobar.php' => true,
  60              'foobar/NOTEXISTS.txt' => true));
  61          $this->assertFalse($validator->execute());
  62          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR,
  63              'filenotexists', array('file' => 'foobar/NOTEXISTS.txt')));
  64  
  65          // Errors during ZIP extraction.
  66          $validator = testable_core_update_validator::instance($fixtures.'/multidir', array(
  67              'one/' => true,
  68              'one/version.php' => 'Can not write target file',
  69              'two/' => true,
  70              'two/README.txt' => true));
  71          $this->assertFalse($validator->execute());
  72          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'filestatus',
  73              array('file' => 'one/version.php', 'status' => 'Can not write target file')));
  74  
  75          // Insufficient number of extracted files.
  76          $validator = testable_core_update_validator::instance($fixtures.'/emptydir', array(
  77              'emptydir/' => true,
  78              'emptydir/README.txt' => true));
  79          $this->assertFalse($validator->execute());
  80          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'filesnumber'));
  81  
  82          // No wrapping directory.
  83          $validator = testable_core_update_validator::instance($fixtures.'/nowrapdir', array(
  84              'version.php' => true,
  85              'index.php' => true,
  86              'lang/' => true,
  87              'lang/en/' => true,
  88              'lang/en/foo.php' => true));
  89          $this->assertFalse($validator->execute());
  90          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
  91  
  92          // Multiple directories.
  93          $validator = testable_core_update_validator::instance($fixtures.'/multidir', array(
  94              'one/' => true,
  95              'one/version.php' => true,
  96              'two/' => true,
  97              'two/README.txt' => true));
  98          $this->assertFalse($validator->execute());
  99          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'onedir'));
 100  
 101          // Invalid root directory name.
 102          $validator = testable_core_update_validator::instance($fixtures.'/github', array(
 103              'moodle-repository_mahara-master/' => true,
 104              'moodle-repository_mahara-master/lang/' => true,
 105              'moodle-repository_mahara-master/lang/en/' => true,
 106              'moodle-repository_mahara-master/lang/en/repository_mahara.php' => true,
 107              'moodle-repository_mahara-master/version.php' => true));
 108          $this->assertFalse($validator->execute());
 109          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'rootdirinvalid',
 110              'moodle-repository_mahara-master'));
 111      }
 112  
 113      public function test_validate_version_php() {
 114          $fixtures = __DIR__.'/fixtures/update_validator';
 115  
 116          $validator = testable_core_update_validator::instance($fixtures.'/noversiontheme', array(
 117              'noversion/' => true,
 118              'noversion/lang/' => true,
 119              'noversion/lang/en/' => true,
 120              'noversion/lang/en/theme_noversion.php' => true));
 121          $validator->assert_plugin_type('theme');
 122          $validator->assert_moodle_version(0);
 123          $this->assertTrue($validator->execute());
 124          $this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'missingversionphp'));
 125          $this->assertTrue(is_null($validator->get_versionphp_info()));
 126  
 127          $validator = testable_core_update_validator::instance($fixtures.'/noversionmod', array(
 128              'noversion/' => true,
 129              'noversion/lang/' => true,
 130              'noversion/lang/en/' => true,
 131              'noversion/lang/en/noversion.php' => true));
 132          $validator->assert_plugin_type('mod');
 133          $validator->assert_moodle_version(0);
 134          $this->assertFalse($validator->execute());
 135          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingversionphp'));
 136  
 137          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
 138              'legacymod/' => true,
 139              'legacymod/version.php' => true,
 140              'legacymod/lang/' => true,
 141              'legacymod/lang/en/' => true,
 142              'legacymod/lang/en/legacymod.php' => true));
 143          $validator->assert_plugin_type('mod');
 144          $validator->assert_moodle_version(0);
 145          $this->assertFalse($validator->execute());
 146          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'versionphpsyntax', '$module'));
 147  
 148          $validator = testable_core_update_validator::instance($fixtures.'/nocomponent', array(
 149              'baz/' => true,
 150              'baz/version.php' => true,
 151              'baz/lang/' => true,
 152              'baz/lang/en/' => true,
 153              'baz/lang/en/auth_baz.php' => true));
 154          $validator->assert_plugin_type('auth');
 155          $validator->assert_moodle_version(0);
 156          $this->assertFalse($validator->execute());
 157          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingcomponent'));
 158  
 159          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
 160              'foobar/' => true,
 161              'foobar/version.php' => true,
 162              'foobar/index.php' => true,
 163              'foobar/lang/' => true));
 164          $validator->assert_plugin_type('block');
 165          $validator->assert_moodle_version('2013031400.00');
 166          $this->assertFalse($validator->execute());
 167          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'componentmismatchtype',
 168              array('expected' => 'block', 'found' => 'local')));
 169  
 170          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
 171              'foobar/' => true,
 172              'foobar/version.php' => true,
 173              'foobar/index.php' => true,
 174              'foobar/lang/' => true,
 175              'foobar/lang/en/' => true,
 176              'foobar/lang/en/local_foobar.php' => true));
 177          $validator->assert_plugin_type('local');
 178          $validator->assert_moodle_version('2013031400.00');
 179          $this->assertTrue($validator->execute());
 180          $this->assertTrue($validator->get_result());
 181          $this->assertEquals('foobar', $validator->get_rootdir());
 182          $this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'rootdir', 'foobar'));
 183          $versionphpinfo = $validator->get_versionphp_info();
 184          $this->assertIsArray($versionphpinfo);
 185          $this->assertCount(4, $versionphpinfo);
 186          $this->assertEquals(2013031900, $versionphpinfo['version']);
 187          $this->assertEquals(2013031200, $versionphpinfo['requires']);
 188          $this->assertEquals('local_foobar', $versionphpinfo['component']);
 189          $this->assertEquals('MATURITY_ALPHA', $versionphpinfo['maturity']); // Note we get the constant name here.
 190          $this->assertEquals(MATURITY_ALPHA, constant($versionphpinfo['maturity'])); // This is how to get the real value.
 191          $this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'maturity', 'MATURITY_ALPHA'));
 192      }
 193  
 194      public function test_validate_language_pack() {
 195          $fixtures = __DIR__.'/fixtures/update_validator';
 196  
 197          $validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
 198              'bah/' => true,
 199              'bah/index.php' => true,
 200              'bah/view.php' => true,
 201              'bah/version.php' => true));
 202          $validator->assert_plugin_type('mod');
 203          $validator->assert_moodle_version(0);
 204          $this->assertFalse($validator->execute());
 205          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfolder'));
 206  
 207          $validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
 208              'bah/' => true,
 209              'bah/version.php' => true,
 210              'bah/lang/' => true,
 211              'bah/lang/en/' => true));
 212          $validator->assert_plugin_type('mod');
 213          $validator->assert_moodle_version(0);
 214          $this->assertFalse($validator->execute());
 215          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missinglangenfile'));
 216  
 217          $validator = testable_core_update_validator::instance($fixtures.'/nolang', array(
 218              'bah/' => true,
 219              'bah/version.php' => true,
 220              'bah/lang/' => true,
 221              'bah/lang/en/' => true,
 222              'bah/lang/en/bleh.php' => true,
 223              'bah/lang/en/bah.php' => true));
 224          $validator->assert_plugin_type('mod');
 225          $validator->assert_moodle_version(0);
 226          $this->assertTrue($validator->execute());
 227          $this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'multiplelangenfiles'));
 228          $this->assertTrue(is_null($validator->get_language_file_name()));
 229  
 230          $validator = testable_core_update_validator::instance($fixtures.'/wronglang', array(
 231              'bah/' => true,
 232              'bah/version.php' => true,
 233              'bah/lang/' => true,
 234              'bah/lang/en/' => true,
 235              'bah/lang/en/bah.php' => true));
 236          $validator->assert_plugin_type('block');
 237          $validator->assert_moodle_version(0);
 238          $this->assertFalse($validator->execute());
 239          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'missingexpectedlangenfile',
 240              'block_bah.php'));
 241          $this->assertEquals('bah', $validator->get_language_file_name());
 242  
 243          $validator = testable_core_update_validator::instance($fixtures.'/noversiontheme', array(
 244              'noversion/' => true,
 245              'noversion/lang/' => true,
 246              'noversion/lang/en/' => true,
 247              'noversion/lang/en/theme_noversion.php' => true));
 248          $validator->assert_plugin_type('theme');
 249          $validator->assert_moodle_version(0);
 250          $this->assertTrue($validator->execute());
 251          $this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'foundlangfile', 'theme_noversion'));
 252          $this->assertEquals('theme_noversion', $validator->get_language_file_name());
 253  
 254          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
 255              'foobar/' => true,
 256              'foobar/version.php' => true,
 257              'foobar/index.php' => true,
 258              'foobar/lang/' => true,
 259              'foobar/lang/en/' => true,
 260              'foobar/lang/en/local_foobar.php' => true));
 261          $validator->assert_plugin_type('local');
 262          $validator->assert_moodle_version('2013031400.00');
 263          $this->assertTrue($validator->execute());
 264          $this->assertTrue($this->has_message($validator->get_messages(), $validator::DEBUG, 'foundlangfile', 'local_foobar'));
 265          $this->assertEquals('local_foobar', $validator->get_language_file_name());
 266      }
 267  
 268      public function test_validate_target_location() {
 269          $fixtures = __DIR__.'/fixtures/update_validator';
 270  
 271          $validator = testable_core_update_validator::instance($fixtures.'/installed', array(
 272              'greenbar/' => true,
 273              'greenbar/version.php' => true,
 274              'greenbar/index.php' => true,
 275              'greenbar/lang/' => true,
 276              'greenbar/lang/en/' => true,
 277              'greenbar/lang/en/local_greenbar.php' => true));
 278          $validator->assert_plugin_type('local');
 279          $validator->assert_moodle_version('2013031400.00');
 280  
 281          $this->assertTrue($validator->execute());
 282          $this->assertFalse($this->has_message($validator->get_messages(), $validator::WARNING, 'targetexists',
 283              $validator->get_plugintype_location('local').'/greenbar'));
 284  
 285          $typeroot = $validator->get_plugintype_location('local');
 286          make_writable_directory($typeroot.'/greenbar');
 287          $this->assertTrue($validator->execute());
 288          $this->assertTrue($this->has_message($validator->get_messages(), $validator::WARNING, 'targetexists',
 289              $validator->get_plugintype_location('local').'/greenbar'));
 290  
 291          remove_dir($typeroot.'/greenbar');
 292          file_put_contents($typeroot.'/greenbar', 'This file occupies a place where a plugin should land.');
 293          $this->assertFalse($validator->execute());
 294          $this->assertTrue($this->has_message($validator->get_messages(), $validator::ERROR, 'targetnotdir',
 295              $validator->get_plugintype_location('local').'/greenbar'));
 296  
 297          unlink($typeroot.'/greenbar');
 298          $this->assertTrue($validator->execute());
 299  
 300          $validator = testable_core_update_validator::instance($fixtures.'/plugindir', array(
 301              'foobar/' => true,
 302              'foobar/version.php' => true,
 303              'foobar/index.php' => true,
 304              'foobar/lang/' => true,
 305              'foobar/lang/en/' => true,
 306              'foobar/lang/en/local_foobar.php' => true));
 307          $validator->assert_plugin_type('local');
 308          $validator->assert_moodle_version('2013031400.00');
 309          $this->assertTrue($validator->execute());
 310          $this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'pathwritable',
 311              $validator->get_plugintype_location('local')));
 312      }
 313  
 314      public function test_parse_version_php() {
 315          $fixtures = __DIR__.'/fixtures/update_validator/versionphp';
 316  
 317          $validator = testable_core_update_validator::instance($fixtures, array());
 318          $this->assertEquals('testable_core_update_validator', get_class($validator));
 319  
 320          $info = $validator->testable_parse_version_php($fixtures.'/version1.php');
 321          $this->assertIsArray($info);
 322          $this->assertCount(7, $info);
 323          $this->assertEquals('block_foobar', $info['plugin->component']);  // Later in the file.
 324          $this->assertEquals('2013010100', $info['plugin->version']);      // Numeric wins over strings.
 325          $this->assertEquals('2012122401', $info['plugin->requires']);     // Commented.
 326          $this->assertEquals('MATURITY_STABLE', $info['module->maturity']);// Constant wins regardless the order (non-PHP behaviour).
 327          $this->assertEquals('MATURITY_ALPHA', $info['plugin->maturity']); // Constant wins regardless the order (non-PHP behaviour).
 328          $this->assertEquals('v2.3', $info['module->release']);            // String wins over numeric (non-PHP behaviour).
 329          $this->assertEquals('v2.4', $info['plugin->release']);            // String wins over numeric (non-PHP behaviour).
 330      }
 331  
 332      public function test_messages_output() {
 333          $fixtures = __DIR__.'/fixtures/update_validator';
 334          $validator = testable_core_update_validator::instance($fixtures, array());
 335  
 336          $this->assertDebuggingNotCalled();
 337          $this->assertNotEmpty($validator->message_level_name($validator::ERROR));
 338          $this->assertNotEmpty($validator->message_level_name($validator::WARNING));
 339          $this->assertNotEmpty($validator->message_level_name($validator::INFO));
 340          $this->assertNotEmpty($validator->message_level_name($validator::DEBUG));
 341  
 342          $this->assertNotEmpty($validator->message_code_name('missingversion'));
 343          $this->assertSame(
 344              'some_really_crazy_message_code_that_is_not_localised',
 345              $validator->message_code_name('some_really_crazy_message_code_that_is_not_localised')
 346          );
 347  
 348          $this->assertInstanceOf('help_icon', $validator->message_help_icon('onedir'));
 349          $this->assertFalse($validator->message_help_icon('some_really_crazy_message_code_that_is_not_localised'));
 350  
 351          $this->assertNotEmpty($validator->message_code_info('missingexpectedlangenfile', 'something'));
 352          $this->assertSame('', $validator->message_code_info('missingexpectedlangenfile', null));
 353          $this->assertSame('', $validator->message_code_info('some_really_crazy_message_code_that_is_not_localised', 'something'));
 354      }
 355  
 356      /**
 357       * Helper method for checking if the given message has been raised by the validator.
 358       *
 359       * @param array $messages list of returned messages
 360       * @param string $level expected message severity level
 361       * @param string $msgcode expected message code
 362       * @param string|array $addinfo expected additional info
 363       * @return bool
 364       */
 365      protected function has_message(array $messages, $level, $msgcode, $addinfo = null) {
 366          foreach ($messages as $message) {
 367              if ($message->level === $level and $message->msgcode === $msgcode and $message->addinfo === $addinfo) {
 368                  return true;
 369              }
 370          }
 371          return false;
 372      }
 373  }