Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

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