Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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

   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 tool_uploadcourse;
  18  
  19  use tool_uploadcourse_helper;
  20  
  21  /**
  22   * Helper test case.
  23   *
  24   * @package    tool_uploadcourse
  25   * @copyright  2013 Frédéric Massart
  26   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  class helper_test extends \advanced_testcase {
  29  
  30      public function test_generate_shortname() {
  31          $data = (object) array('fullname' => 'Ah bh Ch 01 02 03', 'idnumber' => 'ID123');
  32  
  33          $this->assertSame($data->fullname, tool_uploadcourse_helper::generate_shortname($data, '%f'));
  34          $this->assertSame($data->idnumber, tool_uploadcourse_helper::generate_shortname($data, '%i'));
  35          $this->assertSame('Ah Bh Ch', tool_uploadcourse_helper::generate_shortname($data, '%~8f'));
  36          $this->assertSame('AH BH CH', tool_uploadcourse_helper::generate_shortname($data, '%+8f'));
  37          $this->assertSame('id123', tool_uploadcourse_helper::generate_shortname($data, '%-i'));
  38          $this->assertSame('[Ah bh Ch] = ID123', tool_uploadcourse_helper::generate_shortname($data, '[%8f] = %i'));
  39          $this->assertSame('0', tool_uploadcourse_helper::generate_shortname($data, '0'));
  40          $this->assertSame('%unknown', tool_uploadcourse_helper::generate_shortname($data, '%unknown'));
  41  
  42          $this->assertNull(tool_uploadcourse_helper::generate_shortname($data, ''));
  43          $this->assertNull(tool_uploadcourse_helper::generate_shortname(array(), '%f'));
  44      }
  45  
  46      public function test_get_course_formats() {
  47          $result = tool_uploadcourse_helper::get_course_formats();
  48          $this->assertSame(array_keys(\core_component::get_plugin_list('format')), $result);
  49          // Should be similar as first result, as cached.
  50          $this->assertSame($result, tool_uploadcourse_helper::get_course_formats());
  51      }
  52  
  53      public function test_get_enrolment_data() {
  54          $this->resetAfterTest(true);
  55          $data = array(
  56              'enrolment_1' => 'unknown',
  57              'enrolment_1_foo' => '1',
  58              'enrolment_1_bar' => '2',
  59              'enrolment_2' => 'self',
  60              'enrolment_2_delete' => '1',
  61              'enrolment_2_foo' => 'a',
  62              'enrolment_2_bar' => '1',
  63              'enrolment_3' => 'manual',
  64              'enrolment_3_disable' => '2',
  65              'enrolment_3_foo' => 'b',
  66              'enrolment_3_bar' => '2',
  67              'enrolment_4' => 'database',
  68              'enrolment_4_foo' => 'x',
  69              'enrolment_4_bar' => '3',
  70              'enrolment_5_test3' => 'test3',
  71              'enrolment_5_test2' => 'test2',
  72              'enrolment_5_test1' => 'test1',
  73              'enrolment_5' => 'flatfile',
  74          );
  75          $expected = array(
  76              'self' => array(
  77                  'delete' => '1',
  78                  'foo' => 'a',
  79                  'bar' => '1',
  80              ),
  81              'manual' => array(
  82                  'disable' => '2',
  83                  'foo' => 'b',
  84                  'bar' => '2',
  85              ),
  86              'database' => array(
  87                  'foo' => 'x',
  88                  'bar' => '3',
  89              ),
  90              'flatfile' => array(
  91                  'test3' => 'test3',
  92                  'test2' => 'test2',
  93                  'test1' => 'test1',
  94              )
  95          );
  96          $this->assertSame(tool_uploadcourse_helper::get_enrolment_data($data), $expected);
  97      }
  98  
  99      public function test_get_enrolment_plugins() {
 100          $this->resetAfterTest(true);
 101          $actual = tool_uploadcourse_helper::get_enrolment_plugins();
 102          $this->assertSame(array_keys(enrol_get_plugins(false)), array_keys($actual));
 103          // This should be identical as cached.
 104          $secondactual = tool_uploadcourse_helper::get_enrolment_plugins();
 105          $this->assertEquals($actual, $secondactual);
 106      }
 107  
 108      public function test_get_restore_content_dir() {
 109          global $CFG;
 110          $this->resetAfterTest(true);
 111          $this->setAdminUser();
 112  
 113          $c1 = $this->getDataGenerator()->create_course();
 114          $c2 = $this->getDataGenerator()->create_course((object) array('shortname' => 'Yay'));
 115  
 116          // Creating backup file.
 117          $bc = new \backup_controller(\backup::TYPE_1COURSE, $c1->id, \backup::FORMAT_MOODLE,
 118              \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, 2);
 119          $bc->execute_plan();
 120          $result = $bc->get_results();
 121          $this->assertTrue(isset($result['backup_destination']));
 122          $c1backupfile = $result['backup_destination']->copy_content_to_temp();
 123          $bc->destroy();
 124  
 125          // Creating backup file.
 126          $bc = new \backup_controller(\backup::TYPE_1COURSE, $c2->id, \backup::FORMAT_MOODLE,
 127              \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, 2);
 128          $bc->execute_plan();
 129          $result = $bc->get_results();
 130          $this->assertTrue(isset($result['backup_destination']));
 131          $c2backupfile = $result['backup_destination']->copy_content_to_temp();
 132          $bc->destroy();
 133  
 134          $oldcfg = isset($CFG->keeptempdirectoriesonbackup) ? $CFG->keeptempdirectoriesonbackup : false;
 135          $CFG->keeptempdirectoriesonbackup = true;
 136  
 137          // Checking restore dir.
 138          $dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
 139          $bcinfo = \backup_general_helper::get_backup_information($dir);
 140          $this->assertEquals($bcinfo->original_course_id, $c1->id);
 141          $this->assertEquals($bcinfo->original_course_fullname, $c1->fullname);
 142  
 143          // Do it again, it should be the same directory.
 144          $dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
 145          $this->assertEquals($dir, $dir2);
 146  
 147          // Get the second course.
 148          $dir = tool_uploadcourse_helper::get_restore_content_dir($c2backupfile, null);
 149          $bcinfo = \backup_general_helper::get_backup_information($dir);
 150          $this->assertEquals($bcinfo->original_course_id, $c2->id);
 151          $this->assertEquals($bcinfo->original_course_fullname, $c2->fullname);
 152  
 153          // Checking with a shortname.
 154          $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
 155          $bcinfo = \backup_general_helper::get_backup_information($dir);
 156          $this->assertEquals($bcinfo->original_course_id, $c1->id);
 157          $this->assertEquals($bcinfo->original_course_fullname, $c1->fullname);
 158  
 159          // Do it again, it should be the same directory.
 160          $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
 161          $this->assertEquals($dir, $dir2);
 162  
 163          // Get the second course.
 164          $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c2->shortname);
 165          $bcinfo = \backup_general_helper::get_backup_information($dir);
 166          $this->assertEquals($bcinfo->original_course_id, $c2->id);
 167          $this->assertEquals($bcinfo->original_course_fullname, $c2->fullname);
 168  
 169          // Get a course that does not exist.
 170          $errors = array();
 171          $dir = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors);
 172          $this->assertFalse($dir);
 173          $this->assertArrayHasKey('coursetorestorefromdoesnotexist', $errors);
 174  
 175          // Trying again without caching. $CFG->keeptempdirectoriesonbackup is required for caching.
 176          $CFG->keeptempdirectoriesonbackup = false;
 177  
 178          // Checking restore dir.
 179          $dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
 180          $dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
 181          $this->assertNotEquals($dir, $dir2);
 182  
 183          // Checking with a shortname.
 184          $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
 185          $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
 186          $this->assertNotEquals($dir, $dir2);
 187  
 188          // Get a course that does not exist.
 189          $errors = array();
 190          $dir = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors);
 191          $this->assertFalse($dir);
 192          $this->assertArrayHasKey('coursetorestorefromdoesnotexist', $errors);
 193          $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors);
 194          $this->assertEquals($dir, $dir2);
 195  
 196          $CFG->keeptempdirectoriesonbackup = $oldcfg;
 197      }
 198  
 199      public function test_get_role_ids() {
 200          $this->getDataGenerator();
 201          // Mimic function result.
 202          $expected = array();
 203          $roles = get_all_roles();
 204          foreach ($roles as $role) {
 205              $expected[$role->shortname] = $role->id;
 206          }
 207  
 208          $actual = tool_uploadcourse_helper::get_role_ids();
 209          $this->assertSame($actual, $expected);
 210  
 211          // Check cache.
 212          $this->assertSame($actual, tool_uploadcourse_helper::get_role_ids());
 213      }
 214  
 215      public function test_get_role_names() {
 216          $this->resetAfterTest(true);
 217  
 218          create_role('Villain', 'villain', 'The bad guys');
 219          $data = array(
 220              'role_student' => 'Padawan',
 221              'role_teacher' => 'Guardian',
 222              'role_editingteacher' => 'Knight',
 223              'role_manager' => 'Master',
 224              'role_villain' => 'Jabba the Hutt',
 225              'role_android' => 'R2D2',
 226          );
 227  
 228          // Get the role IDs, but need to force the cache reset as a new role is defined.
 229          $roleids = tool_uploadcourse_helper::get_role_ids(true);
 230  
 231          $expected = array(
 232              'role_' . $roleids['student'] => 'Padawan',
 233              'role_' . $roleids['teacher'] => 'Guardian',
 234              'role_' . $roleids['editingteacher'] => 'Knight',
 235              'role_' . $roleids['manager'] => 'Master',
 236              'role_' . $roleids['villain'] => 'Jabba the Hutt',
 237          );
 238  
 239          $errors = array();
 240          $actual = tool_uploadcourse_helper::get_role_names($data, $errors);
 241          $this->assertSame($actual, $expected);
 242          $this->assertArrayHasKey('invalidroles', $errors);
 243      }
 244  
 245      /**
 246       * Test custom field data processing
 247       */
 248      public function test_get_custom_course_field_data() {
 249          global $DB;
 250  
 251          $this->resetAfterTest();
 252  
 253          // Create all the fields!
 254          $category = $this->get_customfield_generator()->create_category();
 255  
 256          $checkboxfield = $this->create_custom_field($category, 'checkbox', 'mycheckbox');
 257          $datefield = $this->create_custom_field($category, 'date', 'mydate');
 258          $selectfield = $this->create_custom_field($category, 'select', 'myselect', ['options' => "Red\nGreen\nBlue"]);
 259          $textfield = $this->create_custom_field($category, 'text', 'mytext', ['locked' => 1]);
 260          $textareafield = $this->create_custom_field($category, 'textarea', 'mytextarea');
 261  
 262          $fields = tool_uploadcourse_helper::get_custom_course_fields();
 263          $this->assertCount(5, $fields);
 264  
 265          $this->assertArrayHasKey($checkboxfield->get('shortname'), $fields);
 266          $this->assertInstanceOf(\customfield_checkbox\field_controller::class, $fields[$checkboxfield->get('shortname')]);
 267  
 268          $this->assertArrayHasKey($datefield->get('shortname'), $fields);
 269          $this->assertInstanceOf(\customfield_date\field_controller::class, $fields[$datefield->get('shortname')]);
 270  
 271          $this->assertArrayHasKey($selectfield->get('shortname'), $fields);
 272          $this->assertInstanceOf(\customfield_select\field_controller::class, $fields[$selectfield->get('shortname')]);
 273  
 274          $this->assertArrayHasKey($textfield->get('shortname'), $fields);
 275          $this->assertInstanceOf(\customfield_text\field_controller::class, $fields[$textfield->get('shortname')]);
 276  
 277          $this->assertArrayHasKey($textareafield->get('shortname'), $fields);
 278          $this->assertInstanceOf(\customfield_textarea\field_controller::class, $fields[$textareafield->get('shortname')]);
 279  
 280          $data = [
 281              'customfield_mycheckbox' => '1',
 282              'customfield_mydate' => '2019-10-01',
 283              'customfield_myselect' => 'Green',
 284              'customfield_mytext' => 'Hello',
 285              'customfield_myunknownfield' => 'Goodbye',
 286          ];
 287  
 288          $expected = [
 289              'customfield_mycheckbox' => '1',
 290              'customfield_mydate' => strtotime('2019-10-01'),
 291              'customfield_myselect' => 2,
 292              'customfield_mytext' => 'Hello',
 293          ];
 294  
 295          $course = $this->getDataGenerator()->create_course();
 296          $user = $this->getDataGenerator()->create_and_enrol($course, 'manager');
 297          $this->setUser($user);
 298  
 299          $context = \context_course::instance($course->id);
 300  
 301          $this->assertEquals($expected, tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context));
 302  
 303          // Now add our custom textarea field (separately because the value of it's 'itemid' element is unknown).
 304          $data['customfield_mytextarea'] = 'Something';
 305          $fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
 306          $this->assertArrayHasKey('customfield_mytextarea_editor', $fields);
 307          $this->assertArrayHasKey('text', $fields['customfield_mytextarea_editor']);
 308          $this->assertEquals('Something', $fields['customfield_mytextarea_editor']['text']);
 309  
 310          // Now prohibit the capability to change locked fields for the manager role.
 311          $managerrole = $DB->get_record('role', ['shortname' => 'manager']);
 312          role_change_permission($managerrole->id, $context, 'moodle/course:changelockedcustomfields', CAP_PROHIBIT);
 313  
 314          // The locked 'mytext' custom field should not be returned.
 315          $fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
 316          $this->assertCount(4, $fields);
 317          $this->assertArrayNotHasKey('customfield_mytext', $fields);
 318      }
 319  
 320      public function test_increment_idnumber() {
 321          $this->resetAfterTest(true);
 322  
 323          $c1 = $this->getDataGenerator()->create_course(array('idnumber' => 'C1'));
 324          $c2 = $this->getDataGenerator()->create_course(array('idnumber' => 'C2'));
 325          $c3 = $this->getDataGenerator()->create_course(array('idnumber' => 'Yo'));
 326  
 327          $this->assertEquals('C3', tool_uploadcourse_helper::increment_idnumber('C1'));
 328          $this->assertEquals('Yo_2', tool_uploadcourse_helper::increment_idnumber('Yo'));
 329          $this->assertEquals('DoesNotExist', tool_uploadcourse_helper::increment_idnumber('DoesNotExist'));
 330      }
 331  
 332      public function test_increment_shortname() {
 333          $this->resetAfterTest(true);
 334  
 335          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'C1'));
 336          $c2 = $this->getDataGenerator()->create_course(array('shortname' => 'C2'));
 337          $c3 = $this->getDataGenerator()->create_course(array('shortname' => 'Yo'));
 338  
 339          // FYI: increment_shortname assumes that the course exists, and so increment the shortname immediately.
 340          $this->assertEquals('C3', tool_uploadcourse_helper::increment_shortname('C1'));
 341          $this->assertEquals('Yo_2', tool_uploadcourse_helper::increment_shortname('Yo'));
 342          $this->assertEquals('DoesNotExist_2', tool_uploadcourse_helper::increment_shortname('DoesNotExist'));
 343      }
 344  
 345      public function test_resolve_category() {
 346          $this->resetAfterTest(true);
 347  
 348          $c1 = $this->getDataGenerator()->create_category(array('name' => 'First level'));
 349          $c2 = $this->getDataGenerator()->create_category(array('name' => 'Second level', 'parent' => $c1->id));
 350          $c3 = $this->getDataGenerator()->create_category(array('idnumber' => 'C3'));
 351  
 352          $data = array(
 353              'category' => $c1->id,
 354              'category_path' => $c1->name . ' / ' . $c2->name,
 355              'category_idnumber' => $c3->idnumber,
 356          );
 357  
 358          $this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category($data));
 359          unset($data['category']);
 360          $this->assertEquals($c3->id, tool_uploadcourse_helper::resolve_category($data));
 361          unset($data['category_idnumber']);
 362          $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data));
 363  
 364          // Adding unexisting data.
 365          $errors = array();
 366          $data['category_idnumber'] = 1234;
 367          $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data, $errors));
 368          $this->assertArrayHasKey('couldnotresolvecatgorybyidnumber', $errors);
 369          $errors = array();
 370          $data['category'] = 1234;
 371          $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data, $errors));
 372          $this->assertArrayHasKey('couldnotresolvecatgorybyid', $errors);
 373          $errors = array();
 374          $data['category_path'] = 'Not exist';
 375          $this->assertEmpty(tool_uploadcourse_helper::resolve_category($data, $errors));
 376          $this->assertArrayHasKey('couldnotresolvecatgorybypath', $errors);
 377      }
 378  
 379      public function test_resolve_category_by_idnumber() {
 380          $this->resetAfterTest(true);
 381  
 382          $c1 = $this->getDataGenerator()->create_category(array('idnumber' => 'C1'));
 383          $c2 = $this->getDataGenerator()->create_category(array('idnumber' => 'C2'));
 384  
 385          // Doubled for cache check.
 386          $this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C1'));
 387          $this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C1'));
 388          $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C2'));
 389          $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C2'));
 390          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_idnumber('DoesNotExist'));
 391          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_idnumber('DoesNotExist'));
 392      }
 393  
 394      public function test_resolve_category_by_path() {
 395          $this->resetAfterTest(true);
 396  
 397          $cat1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1'));
 398          $cat1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1', 'parent' => $cat1->id));
 399          $cat1_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1.1', 'parent' => $cat1_1->id));
 400          $cat1_1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1.2', 'parent' => $cat1_1->id));
 401          $cat1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.2', 'parent' => $cat1->id));
 402  
 403          $cat2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2'));
 404          $cat2_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1', 'parent' => $cat2->id, 'visible' => false));
 405          $cat2_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1.1', 'parent' => $cat2_1->id));
 406          $cat2_1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1.2', 'parent' => $cat2_1->id));
 407          $cat2_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.2', 'parent' => $cat2->id));
 408  
 409          $cat3 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3'));
 410          $cat3_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1 Doubled', 'parent' => $cat3->id));
 411          $cat3_1b = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1 Doubled', 'parent' => $cat3->id));
 412          $cat3_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1.1', 'parent' => $cat3_1->id));
 413          $cat3_fakedouble = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1.1', 'parent' => $cat3->id));
 414  
 415          // Existing categories. Doubled for cache testing.
 416          $path = array('Cat 1');
 417          $this->assertEquals($cat1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 418          $this->assertEquals($cat1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 419  
 420          $path = array('Cat 1', 'Cat 1.1', 'Cat 1.1.2');
 421          $this->assertEquals($cat1_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 422          $this->assertEquals($cat1_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 423  
 424          $path = array('Cat 1', 'Cat 1.2');
 425          $this->assertEquals($cat1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 426          $this->assertEquals($cat1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 427  
 428          $path = array('Cat 2');
 429          $this->assertEquals($cat2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 430          $this->assertEquals($cat2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 431  
 432          // Hidden category.
 433          $path = array('Cat 2', 'Cat 2.1');
 434          $this->assertEquals($cat2_1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 435          $this->assertEquals($cat2_1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 436  
 437          // Hidden parent.
 438          $path = array('Cat 2', 'Cat 2.1', 'Cat 2.1.2');
 439          $this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 440          $this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 441  
 442          // Does not exist.
 443          $path = array('No cat 3', 'Cat 1.2');
 444          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 445          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 446  
 447          $path = array('Cat 2', 'Cat 2.x');
 448          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 449          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 450  
 451          // Name conflict.
 452          $path = array('Cat 3', 'Cat 3.1 Doubled');
 453          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 454          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 455  
 456          $path = array('Cat 3', 'Cat 3.1 Doubled', 'Cat 3.1.1');
 457          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 458          $this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
 459  
 460          $path = array('Cat 3', 'Cat 3.1.1');
 461          $this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 462          $this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
 463      }
 464  
 465      /**
 466       * Get custom field plugin generator
 467       *
 468       * @return core_customfield_generator
 469       */
 470      protected function get_customfield_generator() : \core_customfield_generator {
 471          return $this->getDataGenerator()->get_plugin_generator('core_customfield');
 472      }
 473  
 474      /**
 475       * Helper method to create custom course field
 476       *
 477       * @param \core_customfield\category_controller $category
 478       * @param string $type
 479       * @param string $shortname
 480       * @param array $configdata
 481       * @return \core_customfield\field_controller
 482       */
 483      protected function create_custom_field(\core_customfield\category_controller $category, string $type, string $shortname,
 484              array $configdata = []) : \core_customfield\field_controller {
 485  
 486          return $this->get_customfield_generator()->create_field([
 487              'categoryid' => $category->get('id'),
 488              'type' => $type,
 489              'shortname' => $shortname,
 490              'configdata' => $configdata,
 491          ]);
 492      }
 493  }