Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   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  /**
  19   * PHPUnit tests for fileconverter API.
  20   *
  21   * @package    core_files
  22   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  global $CFG;
  28  
  29  use core_files\conversion;
  30  use core_files\converter;
  31  
  32  /**
  33   * PHPUnit tests for fileconverter API.
  34   *
  35   * @package    core_files
  36   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class converter_test extends advanced_testcase {
  40  
  41      /**
  42       * Get a testable mock of the abstract files_converter class.
  43       *
  44       * @param   array   $mockedmethods A list of methods you intend to override
  45       *                  If no methods are specified, only abstract functions are mocked.
  46       * @return  \core_files\converter
  47       */
  48      protected function get_testable_mock($mockedmethods = []) {
  49          $converter = $this->getMockBuilder(\core_files\converter::class)
  50              ->onlyMethods($mockedmethods)
  51              ->getMockForAbstractClass();
  52  
  53          return $converter;
  54      }
  55  
  56      /**
  57       * Get a testable mock of the conversion.
  58       *
  59       * @param   array   $mockedmethods A list of methods you intend to override
  60       * @return  \core_files\conversion
  61       */
  62      protected function get_testable_conversion($mockedmethods = []) {
  63          $conversion = $this->getMockBuilder(\core_files\conversion::class)
  64              ->onlyMethods($mockedmethods)
  65              ->setConstructorArgs([0, (object) []])
  66              ->getMock();
  67  
  68          return $conversion;
  69      }
  70  
  71      /**
  72       * Get a testable mock of the abstract files_converter class.
  73       *
  74       * @param   array   $mockedmethods A list of methods you intend to override
  75       *                  If no methods are specified, only abstract functions are mocked.
  76       * @return  \core_files\converter_interface
  77       */
  78      protected function get_mocked_converter($mockedmethods = []) {
  79          $converter = $this->getMockBuilder(\core_files\converter_interface::class)
  80              ->onlyMethods($mockedmethods)
  81              ->getMockForAbstractClass();
  82  
  83          return $converter;
  84      }
  85  
  86      /**
  87       * Helper to create a stored file objectw with the given supplied content.
  88       *
  89       * @param   string  $filecontent The content of the mocked file
  90       * @param   string  $filename The file name to use in the stored_file
  91       * @param   array   $mockedmethods A list of methods you intend to override
  92       *                  If no methods are specified, only abstract functions are mocked.
  93       * @return  stored_file
  94       */
  95      protected function get_stored_file($filecontent = 'content', $filename = null, $filerecord = [], $mockedmethods = []) {
  96          global $CFG;
  97  
  98          $contenthash = sha1($filecontent);
  99          if (empty($filename)) {
 100              $filename = $contenthash;
 101          }
 102  
 103          $filerecord['contenthash'] = $contenthash;
 104          $filerecord['filesize'] = strlen($filecontent);
 105          $filerecord['filename'] = $filename;
 106          $filerecord['id'] = 42;
 107  
 108          $file = $this->getMockBuilder(stored_file::class)
 109              ->onlyMethods($mockedmethods)
 110              ->setConstructorArgs([get_file_storage(), (object) $filerecord])
 111              ->getMock();
 112  
 113          return $file;
 114      }
 115  
 116      /**
 117       * Helper to create a stored file object with the given supplied content.
 118       *
 119       * @param   string $filecontent The content of the mocked file
 120       * @param   string $filename The file name to use in the stored_file
 121       * @param   string $filerecord Any overrides to the filerecord
 122       * @return  stored_file
 123       */
 124      protected function create_stored_file($filecontent = 'content', $filename = 'testfile.txt', $filerecord = []) {
 125          $filerecord = array_merge([
 126                  'contextid' => context_system::instance()->id,
 127                  'component' => 'core',
 128                  'filearea'  => 'unittest',
 129                  'itemid'    => 0,
 130                  'filepath'  => '/',
 131                  'filename'  => $filename,
 132              ], $filerecord);
 133  
 134          $fs = get_file_storage();
 135          $file = $fs->create_file_from_string($filerecord, $filecontent);
 136  
 137          return $file;
 138      }
 139  
 140      /**
 141       * Get a mock of the file_storage API.
 142       *
 143       * @param   array   $mockedmethods A list of methods you intend to override
 144       * @return  file_storage
 145       */
 146      protected function get_file_storage_mock($mockedmethods = []) {
 147          $fs = $this->getMockBuilder(\file_storage::class)
 148              ->onlyMethods($mockedmethods)
 149              ->disableOriginalConstructor()
 150              ->getMock();
 151  
 152          return $fs;
 153      }
 154  
 155      /**
 156       * Test the start_conversion function.
 157       */
 158      public function test_start_conversion_existing_single() {
 159          $this->resetAfterTest();
 160  
 161          $sourcefile = $this->create_stored_file();
 162  
 163          $first = new conversion(0, (object) [
 164                  'sourcefileid' => $sourcefile->get_id(),
 165                  'targetformat' => 'pdf',
 166              ]);
 167          $first->create();
 168  
 169          $converter = $this->get_testable_mock(['poll_conversion']);
 170          $conversion = $converter->start_conversion($sourcefile, 'pdf', false);
 171  
 172          // The old conversions should still be present and match the one returned.
 173          $this->assertEquals($first->get('id'), $conversion->get('id'));
 174      }
 175  
 176      /**
 177       * Test the start_conversion function.
 178       */
 179      public function test_start_conversion_existing_multiple() {
 180          $this->resetAfterTest();
 181  
 182          $sourcefile = $this->create_stored_file();
 183  
 184          $first = new conversion(0, (object) [
 185                  'sourcefileid' => $sourcefile->get_id(),
 186                  'targetformat' => 'pdf',
 187              ]);
 188          $first->create();
 189  
 190          $second = new conversion(0, (object) [
 191                  'sourcefileid' => $sourcefile->get_id(),
 192                  'targetformat' => 'pdf',
 193              ]);
 194          $second->create();
 195  
 196          $converter = $this->get_testable_mock(['poll_conversion']);
 197          $conversion = $converter->start_conversion($sourcefile, 'pdf', false);
 198  
 199          // The old conversions should have been removed.
 200          $this->assertFalse(conversion::get_record(['id' => $first->get('id')]));
 201          $this->assertFalse(conversion::get_record(['id' => $second->get('id')]));
 202      }
 203  
 204      /**
 205       * Test the start_conversion function.
 206       */
 207      public function test_start_conversion_no_existing() {
 208          $this->resetAfterTest();
 209  
 210          $sourcefile = $this->create_stored_file();
 211  
 212          $converter = $this->get_testable_mock(['poll_conversion']);
 213          $conversion = $converter->start_conversion($sourcefile, 'pdf', false);
 214  
 215          $this->assertInstanceOf(\core_files\conversion::class, $conversion);
 216      }
 217  
 218      /**
 219       * Test the get_document_converter_classes function with no enabled plugins.
 220       */
 221      public function test_get_document_converter_classes_no_plugins() {
 222          $converter = $this->get_testable_mock(['get_enabled_plugins']);
 223          $converter->method('get_enabled_plugins')->willReturn([]);
 224  
 225          $method = new ReflectionMethod(\core_files\converter::class, 'get_document_converter_classes');
 226          $method->setAccessible(true);
 227          $result = $method->invokeArgs($converter, ['docx', 'pdf']);
 228          $this->assertEmpty($result);
 229      }
 230  
 231      /**
 232       * Test the get_document_converter_classes function when no class was found.
 233       */
 234      public function test_get_document_converter_classes_plugin_class_not_found() {
 235          $converter = $this->get_testable_mock(['get_enabled_plugins']);
 236          $converter->method('get_enabled_plugins')->willReturn([
 237                  'noplugin' => '\not\a\real\plugin',
 238              ]);
 239  
 240          $method = new ReflectionMethod(\core_files\converter::class, 'get_document_converter_classes');
 241          $method->setAccessible(true);
 242          $result = $method->invokeArgs($converter, ['docx', 'pdf']);
 243          $this->assertEmpty($result);
 244      }
 245  
 246      /**
 247       * Test the get_document_converter_classes function when the returned classes do not meet requirements.
 248       */
 249      public function test_get_document_converter_classes_plugin_class_requirements_not_met() {
 250          $plugin = $this->getMockBuilder(\core_file_converter_requirements_not_met::class)
 251              ->onlyMethods([])
 252              ->getMock();
 253  
 254          $converter = $this->get_testable_mock(['get_enabled_plugins']);
 255          $converter->method('get_enabled_plugins')->willReturn([
 256                  'test_plugin' => get_class($plugin),
 257              ]);
 258  
 259          $method = new ReflectionMethod(\core_files\converter::class, 'get_document_converter_classes');
 260          $method->setAccessible(true);
 261          $result = $method->invokeArgs($converter, ['docx', 'pdf']);
 262          $this->assertEmpty($result);
 263      }
 264  
 265      /**
 266       * Test the get_document_converter_classes function when the returned classes do not meet requirements.
 267       */
 268      public function test_get_document_converter_classes_plugin_class_met_not_supported() {
 269          $plugin = $this->getMockBuilder(\core_file_converter_type_not_supported::class)
 270              ->onlyMethods([])
 271              ->getMock();
 272  
 273          $converter = $this->get_testable_mock(['get_enabled_plugins']);
 274          $converter->method('get_enabled_plugins')->willReturn([
 275                  'test_plugin' => get_class($plugin),
 276              ]);
 277  
 278          $method = new ReflectionMethod(\core_files\converter::class, 'get_document_converter_classes');
 279          $method->setAccessible(true);
 280          $result = $method->invokeArgs($converter, ['docx', 'pdf']);
 281          $this->assertEmpty($result);
 282      }
 283  
 284      /**
 285       * Test the get_document_converter_classes function when the returned classes do not meet requirements.
 286       */
 287      public function test_get_document_converter_classes_plugin_class_met_and_supported() {
 288          $plugin = $this->getMockBuilder(\core_file_converter_type_supported::class)
 289              ->onlyMethods([])
 290              ->getMock();
 291          $classname = get_class($plugin);
 292  
 293          $converter = $this->get_testable_mock(['get_enabled_plugins']);
 294          $converter->method('get_enabled_plugins')->willReturn([
 295                  'test_plugin' => $classname,
 296              ]);
 297  
 298          $method = new ReflectionMethod(\core_files\converter::class, 'get_document_converter_classes');
 299          $method->setAccessible(true);
 300          $result = $method->invokeArgs($converter, ['docx', 'pdf']);
 301          $this->assertCount(1, $result);
 302          $this->assertNotFalse(array_search($classname, $result));
 303      }
 304  
 305      /**
 306       * Test the can_convert_storedfile_to function with a directory.
 307       */
 308      public function test_can_convert_storedfile_to_directory() {
 309          $converter = $this->get_testable_mock();
 310  
 311          // A file with filename '.' is a directory.
 312          $file = $this->get_stored_file('', '.');
 313  
 314          $this->assertFalse($converter->can_convert_storedfile_to($file, 'target'));
 315      }
 316  
 317      /**
 318       * Test the can_convert_storedfile_to function with an empty file.
 319       */
 320      public function test_can_convert_storedfile_to_emptyfile() {
 321          $converter = $this->get_testable_mock();
 322  
 323          // A file with filename '.' is a directory.
 324          $file = $this->get_stored_file('');
 325  
 326          $this->assertFalse($converter->can_convert_storedfile_to($file, 'target'));
 327      }
 328  
 329      /**
 330       * Test the can_convert_storedfile_to function with a file with indistinguished mimetype.
 331       */
 332      public function test_can_convert_storedfile_to_no_mimetype() {
 333          $converter = $this->get_testable_mock();
 334  
 335          // A file with filename '.' is a directory.
 336          $file = $this->get_stored_file('example content', 'example', [
 337                  'mimetype' => null,
 338              ]);
 339  
 340          $this->assertFalse($converter->can_convert_storedfile_to($file, 'target'));
 341      }
 342  
 343      /**
 344       * Test the can_convert_storedfile_to function with a file with a known mimetype and extension.
 345       */
 346      public function test_can_convert_storedfile_to_docx() {
 347          $returnvalue = (object) [];
 348  
 349          $converter = $this->get_testable_mock([
 350                  'can_convert_format_to'
 351              ]);
 352  
 353          $types = \core_filetypes::get_types();
 354  
 355          $file = $this->get_stored_file('example content', 'example.docx', [
 356                  'mimetype' => $types['docx']['type'],
 357              ]);
 358  
 359          $converter->expects($this->once())
 360              ->method('can_convert_format_to')
 361              ->willReturn($returnvalue);
 362  
 363          $result = $converter->can_convert_storedfile_to($file, 'target');
 364          $this->assertEquals($returnvalue, $result);
 365      }
 366  
 367  
 368      /**
 369       * Test the can_convert_format_to function.
 370       */
 371      public function test_can_convert_format_to_found() {
 372          $converter = $this->get_testable_mock(['get_document_converter_classes']);
 373  
 374          $mock = $this->get_mocked_converter();
 375  
 376          $converter->method('get_document_converter_classes')
 377              ->willReturn([$mock]);
 378  
 379          $result = $converter->can_convert_format_to('from', 'to');
 380          $this->assertTrue($result);
 381      }
 382  
 383      /**
 384       * Test the can_convert_format_to function.
 385       */
 386      public function test_can_convert_format_to_not_found() {
 387          $converter = $this->get_testable_mock(['get_document_converter_classes']);
 388  
 389          $converter->method('get_document_converter_classes')
 390              ->willReturn([]);
 391  
 392          $result = $converter->can_convert_format_to('from', 'to');
 393          $this->assertFalse($result);
 394      }
 395  
 396      /**
 397       * Test the can_convert_storedfile_to function with an empty file.
 398       */
 399      public function test_poll_conversion_in_progress() {
 400          $this->resetAfterTest();
 401  
 402          $converter = $this->get_testable_mock([
 403                  'get_document_converter_classes',
 404                  'get_next_converter',
 405              ]);
 406  
 407          $converter->method('get_document_converter_classes')->willReturn([]);
 408          $converter->method('get_next_converter')->willReturn(false);
 409          $file = $this->create_stored_file('example content', 'example', [
 410                  'mimetype' => null,
 411              ]);
 412  
 413          $conversion = $this->get_testable_conversion([
 414                  'get_converter_instance',
 415              ]);
 416          $conversion->set_sourcefile($file);
 417          $conversion->set('targetformat', 'target');
 418          $conversion->set('status', conversion::STATUS_IN_PROGRESS);
 419          $conversion->create();
 420  
 421          $converterinstance = $this->get_mocked_converter([
 422                  'poll_conversion_status',
 423              ]);
 424          $converterinstance->expects($this->once())
 425              ->method('poll_conversion_status');
 426          $conversion->method('get_converter_instance')->willReturn($converterinstance);
 427  
 428          $converter->poll_conversion($conversion);
 429  
 430          $this->assertEquals(conversion::STATUS_IN_PROGRESS, $conversion->get('status'));
 431      }
 432  
 433      /**
 434       * Test poll_conversion with an in-progress conversion where we are
 435       * unable to instantiate the converter instance.
 436       */
 437      public function test_poll_conversion_in_progress_fail() {
 438          $this->resetAfterTest();
 439  
 440          $converter = $this->get_testable_mock([
 441                  'get_document_converter_classes',
 442                  'get_next_converter',
 443              ]);
 444  
 445          $converter->method('get_document_converter_classes')->willReturn([]);
 446          $converter->method('get_next_converter')->willReturn(false);
 447          $file = $this->create_stored_file('example content', 'example', [
 448                  'mimetype' => null,
 449              ]);
 450  
 451          $conversion = $this->get_testable_conversion([
 452                  'get_converter_instance',
 453              ]);
 454          $conversion->set_sourcefile($file);
 455          $conversion->set('targetformat', 'target');
 456          $conversion->set('status', conversion::STATUS_IN_PROGRESS);
 457          $conversion->create();
 458  
 459          $conversion->method('get_converter_instance')->willReturn(false);
 460  
 461          $converter->poll_conversion($conversion);
 462  
 463          $this->assertEquals(conversion::STATUS_FAILED, $conversion->get('status'));
 464      }
 465  
 466      /**
 467       * Test the can_convert_storedfile_to function with an empty file.
 468       */
 469      public function test_poll_conversion_none_supported() {
 470          $this->resetAfterTest();
 471  
 472          $converter = $this->get_testable_mock([
 473                  'get_document_converter_classes',
 474                  'get_next_converter',
 475              ]);
 476  
 477          $converter->method('get_document_converter_classes')->willReturn([]);
 478          $converter->method('get_next_converter')->willReturn(false);
 479          $file = $this->create_stored_file('example content', 'example', [
 480                  'mimetype' => null,
 481              ]);
 482  
 483          $conversion = new conversion(0, (object) [
 484              'sourcefileid' => $file->get_id(),
 485              'targetformat' => 'target',
 486          ]);
 487          $conversion->create();
 488  
 489          $converter->poll_conversion($conversion);
 490  
 491          $this->assertEquals(conversion::STATUS_FAILED, $conversion->get('status'));
 492      }
 493  
 494      /**
 495       * Test the can_convert_storedfile_to function with an empty file.
 496       */
 497      public function test_poll_conversion_pick_first() {
 498          $this->resetAfterTest();
 499  
 500          $converterinstance = $this->get_mocked_converter([
 501                  'start_document_conversion',
 502                  'poll_conversion_status',
 503              ]);
 504          $converter = $this->get_testable_mock([
 505                  'get_document_converter_classes',
 506                  'get_next_converter',
 507              ]);
 508  
 509          $converter->method('get_document_converter_classes')->willReturn([]);
 510          $converter->method('get_next_converter')->willReturn(get_class($converterinstance));
 511          $file = $this->create_stored_file('example content', 'example', [
 512                  'mimetype' => null,
 513              ]);
 514  
 515          $conversion = $this->get_testable_conversion([
 516                  'get_converter_instance',
 517              ]);
 518          $conversion->set_sourcefile($file);
 519          $conversion->set('targetformat', 'target');
 520          $conversion->set('status', conversion::STATUS_PENDING);
 521          $conversion->create();
 522  
 523          $conversion->method('get_converter_instance')->willReturn($converterinstance);
 524  
 525          $converterinstance->expects($this->once())
 526              ->method('start_document_conversion');
 527          $converterinstance->expects($this->never())
 528              ->method('poll_conversion_status');
 529  
 530          $converter->poll_conversion($conversion);
 531  
 532          $this->assertEquals(conversion::STATUS_IN_PROGRESS, $conversion->get('status'));
 533      }
 534  
 535      /**
 536       * Test the can_convert_storedfile_to function with an empty file.
 537       */
 538      public function test_poll_conversion_pick_subsequent() {
 539          $this->resetAfterTest();
 540  
 541          $converterinstance = $this->get_mocked_converter([
 542                  'start_document_conversion',
 543                  'poll_conversion_status',
 544              ]);
 545          $converterinstance2 = $this->get_mocked_converter([
 546                  'start_document_conversion',
 547                  'poll_conversion_status',
 548              ]);
 549          $converter = $this->get_testable_mock([
 550                  'get_document_converter_classes',
 551                  'get_next_converter',
 552              ]);
 553  
 554          $converter->method('get_document_converter_classes')->willReturn([]);
 555          $converter->method('get_next_converter')
 556              ->will($this->onConsecutiveCalls(
 557                  get_class($converterinstance),
 558                  get_class($converterinstance2)
 559              ));
 560  
 561          $file = $this->create_stored_file('example content', 'example', [
 562                  'mimetype' => null,
 563              ]);
 564  
 565          $conversion = $this->get_testable_conversion([
 566                  'get_converter_instance',
 567                  'get_status',
 568              ]);
 569          $conversion->set_sourcefile($file);
 570          $conversion->set('targetformat', 'target');
 571          $conversion->set('status', conversion::STATUS_PENDING);
 572          $conversion->create();
 573  
 574          $conversion->method('get_status')
 575              ->will($this->onConsecutiveCalls(
 576                  // Initial status check.
 577                  conversion::STATUS_PENDING,
 578                  // Second check to make sure it's still pending after polling.
 579                  conversion::STATUS_PENDING,
 580                  // First one fails.
 581                  conversion::STATUS_FAILED,
 582                  // Second one succeeds.
 583                  conversion::STATUS_COMPLETE,
 584                  // And the final result checked in this unit test.
 585                  conversion::STATUS_COMPLETE
 586              ));
 587  
 588          $conversion->method('get_converter_instance')
 589              ->will($this->onConsecutiveCalls(
 590                  $converterinstance,
 591                  $converterinstance2
 592              ));
 593  
 594          $converterinstance->expects($this->once())
 595              ->method('start_document_conversion');
 596          $converterinstance->expects($this->never())
 597              ->method('poll_conversion_status');
 598          $converterinstance2->expects($this->once())
 599              ->method('start_document_conversion');
 600          $converterinstance2->expects($this->never())
 601              ->method('poll_conversion_status');
 602  
 603          $converter->poll_conversion($conversion);
 604  
 605          $this->assertEquals(conversion::STATUS_COMPLETE, $conversion->get('status'));
 606      }
 607  
 608      /**
 609       * Test the start_conversion with a single converter which succeeds.
 610       */
 611      public function test_start_conversion_one_supported_success() {
 612          $this->resetAfterTest();
 613  
 614          $converter = $this->get_testable_mock([
 615                  'get_document_converter_classes',
 616              ]);
 617  
 618          $converter->method('get_document_converter_classes')
 619              ->willReturn([\core_file_converter_type_successful::class]);
 620  
 621          $file = $this->create_stored_file('example content', 'example', [
 622                  'mimetype' => null,
 623              ]);
 624  
 625          $conversion = $converter->start_conversion($file, 'target');
 626  
 627          $this->assertEquals(conversion::STATUS_COMPLETE, $conversion->get('status'));
 628      }
 629  
 630      /**
 631       * Test the start_conversion with a single converter which failes.
 632       */
 633      public function test_start_conversion_one_supported_failure() {
 634          $this->resetAfterTest();
 635  
 636          $converter = $this->get_testable_mock([
 637                  'get_document_converter_classes',
 638              ]);
 639  
 640          $mock = $this->get_mocked_converter(['start_document_conversion']);
 641          $converter->method('get_document_converter_classes')
 642              ->willReturn([\core_file_converter_type_failed::class]);
 643  
 644          $file = $this->create_stored_file('example content', 'example', [
 645                  'mimetype' => null,
 646              ]);
 647  
 648          $conversion = $converter->start_conversion($file, 'target');
 649  
 650          $this->assertEquals(conversion::STATUS_FAILED, $conversion->get('status'));
 651      }
 652  
 653      /**
 654       * Test the start_conversion with two converters - fail, then succeed.
 655       */
 656      public function test_start_conversion_two_supported() {
 657          $this->resetAfterTest();
 658  
 659          $converter = $this->get_testable_mock([
 660                  'get_document_converter_classes',
 661              ]);
 662  
 663          $mock = $this->get_mocked_converter(['start_document_conversion']);
 664          $converter->method('get_document_converter_classes')
 665              ->willReturn([
 666                  \core_file_converter_type_failed::class,
 667                  \core_file_converter_type_successful::class,
 668              ]);
 669  
 670          $file = $this->create_stored_file('example content', 'example', [
 671                  'mimetype' => null,
 672              ]);
 673  
 674          $conversion = $converter->start_conversion($file, 'target');
 675  
 676          $this->assertEquals(conversion::STATUS_COMPLETE, $conversion->get('status'));
 677      }
 678  
 679      /**
 680       * Ensure that get_next_converter returns false when no converters are available.
 681       */
 682      public function test_get_next_converter_no_converters() {
 683          $rcm = new \ReflectionMethod(converter::class, 'get_next_converter');
 684          $rcm->setAccessible(true);
 685  
 686          $converter = new \core_files\converter();
 687          $result = $rcm->invoke($converter, [], null);
 688          $this->assertFalse($result);
 689      }
 690  
 691      /**
 692       * Ensure that get_next_converter returns false when already on the
 693       * only converter.
 694       */
 695      public function test_get_next_converter_only_converters() {
 696          $rcm = new \ReflectionMethod(converter::class, 'get_next_converter');
 697          $rcm->setAccessible(true);
 698  
 699          $converter = new converter();
 700          $result = $rcm->invoke($converter, ['example'], 'example');
 701          $this->assertFalse($result);
 702      }
 703  
 704      /**
 705       * Ensure that get_next_converter returns false when already on the
 706       * last converter.
 707       */
 708      public function test_get_next_converter_last_converters() {
 709          $rcm = new \ReflectionMethod(converter::class, 'get_next_converter');
 710          $rcm->setAccessible(true);
 711  
 712          $converter = new converter();
 713          $result = $rcm->invoke($converter, ['foo', 'example'], 'example');
 714          $this->assertFalse($result);
 715      }
 716  
 717      /**
 718       * Ensure that get_next_converter returns the next vlaue when in a
 719       * current converter.
 720       */
 721      public function test_get_next_converter_middle_converters() {
 722          $rcm = new \ReflectionMethod(converter::class, 'get_next_converter');
 723          $rcm->setAccessible(true);
 724  
 725          $converter = new converter();
 726          $result = $rcm->invoke($converter, ['foo', 'bar', 'baz', 'example'], 'bar');
 727          $this->assertEquals('baz', $result);
 728      }
 729      /**
 730       *
 731       * Ensure that get_next_converter returns the next vlaue when in a
 732       * current converter.
 733       */
 734      public function test_get_next_converter_first() {
 735          $rcm = new \ReflectionMethod(converter::class, 'get_next_converter');
 736          $rcm->setAccessible(true);
 737  
 738          $converter = new converter();
 739          $result = $rcm->invoke($converter, ['foo', 'bar', 'baz', 'example']);
 740          $this->assertEquals('foo', $result);
 741      }
 742  }
 743  
 744  class core_file_converter_requirements_base implements \core_files\converter_interface {
 745  
 746      /**
 747       * Whether the plugin is configured and requirements are met.
 748       *
 749       * @return  bool
 750       */
 751      public static function are_requirements_met() {
 752          return false;
 753      }
 754  
 755      /**
 756       * Convert a document to a new format and return a conversion object relating to the conversion in progress.
 757       *
 758       * @param   conversion $conversion The file to be converted
 759       * @return  conversion
 760       */
 761      public function start_document_conversion(conversion $conversion) {
 762      }
 763  
 764      /**
 765       * Poll an existing conversion for status update.
 766       *
 767       * @param   conversion $conversion The file to be converted
 768       * @return  conversion
 769       */
 770      public function poll_conversion_status(conversion $conversion) {
 771      }
 772  
 773      /**
 774       * Whether a file conversion can be completed using this converter.
 775       *
 776       * @param   string $from The source type
 777       * @param   string $to The destination type
 778       * @return  bool
 779       */
 780      public static function supports($from, $to) {
 781          return false;
 782      }
 783  
 784      /**
 785       * A list of the supported conversions.
 786       *
 787       * @return  string
 788       */
 789      public function  get_supported_conversions() {
 790          return [];
 791      }
 792  
 793  }
 794  
 795  /**
 796   * Test class for converter support with requirements are not met.
 797   *
 798   * @package    core_files
 799   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
 800   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 801   */
 802  class core_file_converter_requirements_not_met extends core_file_converter_requirements_base {
 803  }
 804  
 805  /**
 806   * Test class for converter support with requirements met and conversion not supported.
 807   *
 808   * @package    core_files
 809   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
 810   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 811   */
 812  class core_file_converter_type_not_supported extends core_file_converter_requirements_base {
 813  
 814      /**
 815       * Whether the plugin is configured and requirements are met.
 816       *
 817       * @return  bool
 818       */
 819      public static function are_requirements_met() {
 820          return true;
 821      }
 822  }
 823  
 824  /**
 825   * Test class for converter support with requirements met and conversion supported.
 826   *
 827   * @package    core_files
 828   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
 829   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 830   */
 831  class core_file_converter_type_supported extends core_file_converter_requirements_base {
 832  
 833      /**
 834       * Whether the plugin is configured and requirements are met.
 835       *
 836       * @return  bool
 837       */
 838      public static function are_requirements_met() {
 839          return true;
 840      }
 841  
 842      /**
 843       * Whether a file conversion can be completed using this converter.
 844       *
 845       * @param   string $from The source type
 846       * @param   string $to The destination type
 847       * @return  bool
 848       */
 849      public static function supports($from, $to) {
 850          return true;
 851      }
 852  }
 853  
 854  /**
 855   * Test class for converter support with requirements met and successful conversion.
 856   *
 857   * @package    core_files
 858   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
 859   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 860   */
 861  class core_file_converter_type_successful extends core_file_converter_requirements_base {
 862  
 863      /**
 864       * Convert a document to a new format and return a conversion object relating to the conversion in progress.
 865       *
 866       * @param   conversion $conversion The file to be converted
 867       * @return  conversion
 868       */
 869      public function start_document_conversion(conversion $conversion) {
 870          $conversion->set('status', conversion::STATUS_COMPLETE);
 871  
 872          return $conversion;
 873      }
 874  
 875      /**
 876       * Whether a file conversion can be completed using this converter.
 877       *
 878       * @param   string $from The source type
 879       * @param   string $to The destination type
 880       * @return  bool
 881       */
 882      public static function supports($from, $to) {
 883          return true;
 884      }
 885  }
 886  
 887  /**
 888   * Test class for converter support with requirements met and failed conversion.
 889   *
 890   * @package    core_files
 891   * @copyright  2017 Andrew nicols <andrew@nicols.co.uk>
 892   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 893   */
 894  class core_file_converter_type_failed extends core_file_converter_requirements_base {
 895  
 896      /**
 897       * Whether the plugin is configured and requirements are met.
 898       *
 899       * @return  bool
 900       */
 901      public static function are_requirements_met() {
 902          return true;
 903      }
 904  
 905      /**
 906       * Convert a document to a new format and return a conversion object relating to the conversion in progress.
 907       *
 908       * @param   conversion $conversion The file to be converted
 909       * @return  conversion
 910       */
 911      public function start_document_conversion(conversion $conversion) {
 912          $conversion->set('status', conversion::STATUS_FAILED);
 913  
 914          return $conversion;
 915      }
 916  
 917      /**
 918       * Whether a file conversion can be completed using this converter.
 919       *
 920       * @param   string $from The source type
 921       * @param   string $to The destination type
 922       * @return  bool
 923       */
 924      public static function supports($from, $to) {
 925          return true;
 926      }
 927  }