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 311 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 tool_usertours;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->libdir . '/formslib.php');
  23  
  24  /**
  25   * Tests for step.
  26   *
  27   * @package    tool_usertours
  28   * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class step_test extends \advanced_testcase {
  32  
  33      /**
  34       * @var moodle_database
  35       */
  36      protected $db;
  37  
  38      /**
  39       * Setup to store the DB reference.
  40       */
  41      public function setUp(): void {
  42          global $DB;
  43  
  44          $this->db = $DB;
  45      }
  46  
  47      /**
  48       * Tear down to restore the original DB reference.
  49       */
  50      public function tearDown(): void {
  51          global $DB;
  52  
  53          $DB = $this->db;
  54      }
  55  
  56      /**
  57       * Helper to mock the database.
  58       *
  59       * @return moodle_database
  60       */
  61      public function mock_database() {
  62          global $DB;
  63  
  64          $DB = $this->getMockBuilder('moodle_database')
  65              ->getMock()
  66              ;
  67  
  68          return $DB;
  69      }
  70  
  71      /**
  72       * Data provider for the dirty value tester.
  73       *
  74       * @return array
  75       */
  76      public function dirty_value_provider() {
  77          return [
  78                  'tourid' => [
  79                          'tourid',
  80                          [1],
  81                      ],
  82                  'title' => [
  83                          'title',
  84                          ['Lorem'],
  85                      ],
  86                  'content' => [
  87                          'content',
  88                          ['Lorem'],
  89                      ],
  90                  'targettype' => [
  91                          'targettype',
  92                          ['Lorem'],
  93                      ],
  94                  'targetvalue' => [
  95                          'targetvalue',
  96                          ['Lorem'],
  97                      ],
  98                  'sortorder' => [
  99                          'sortorder',
 100                          [1],
 101                      ],
 102                  'config' => [
 103                          'config',
 104                          ['key', 'value'],
 105                      ],
 106              ];
 107      }
 108  
 109      /**
 110       * Test the fetch function.
 111       */
 112      public function test_fetch() {
 113          $step = $this->getMockBuilder(\tool_usertours\step::class)
 114              ->onlyMethods(['reload_from_record'])
 115              ->getMock()
 116              ;
 117  
 118          $idretval = rand(1, 100);
 119          $DB = $this->mock_database();
 120          $DB->method('get_record')
 121              ->willReturn($idretval)
 122              ;
 123  
 124          $retval = rand(1, 100);
 125          $step->expects($this->once())
 126              ->method('reload_from_record')
 127              ->with($this->equalTo($idretval))
 128              ->wilLReturn($retval)
 129              ;
 130  
 131          $rc = new \ReflectionClass(\tool_usertours\step::class);
 132          $rcm = $rc->getMethod('fetch');
 133          $rcm->setAccessible(true);
 134  
 135          $id = rand(1, 100);
 136          $this->assertEquals($retval, $rcm->invoke($step, 'fetch', $id));
 137      }
 138  
 139      /**
 140       * Test that setters mark things as dirty.
 141       *
 142       * @dataProvider dirty_value_provider
 143       * @param   string  $name       The key to update
 144       * @param   string  $value      The value to set
 145       */
 146      public function test_dirty_values($name, $value) {
 147          $step = new \tool_usertours\step();
 148          $method = 'set_' . $name;
 149          call_user_func_array([$step, $method], $value);
 150  
 151          $rc = new \ReflectionClass(\tool_usertours\step::class);
 152          $rcp = $rc->getProperty('dirty');
 153          $rcp->setAccessible(true);
 154  
 155          $this->assertTrue($rcp->getValue($step));
 156      }
 157  
 158      /**
 159       * Provider for is_first_step.
 160       *
 161       * @return array
 162       */
 163      public function step_sortorder_provider() {
 164          return [
 165                  [0, 5, true, false],
 166                  [1, 5, false, false],
 167                  [4, 5, false, true],
 168              ];
 169      }
 170  
 171      /**
 172       * Test is_first_step.
 173       *
 174       * @dataProvider step_sortorder_provider
 175       * @param   int     $sortorder      The sortorder to check
 176       * @param   int     $count          Unused in this function
 177       * @param   bool    $isfirst        Whether this is the first step
 178       * @param   bool    $islast         Whether this is the last step
 179       */
 180      public function test_is_first_step($sortorder, $count, $isfirst, $islast) {
 181          $step = $this->getMockBuilder(\tool_usertours\step::class)
 182              ->onlyMethods(['get_sortorder'])
 183              ->getMock();
 184  
 185          $step->expects($this->once())
 186              ->method('get_sortorder')
 187              ->willReturn($sortorder)
 188              ;
 189  
 190          $this->assertEquals($isfirst, $step->is_first_step());
 191      }
 192  
 193      /**
 194       * Test is_last_step.
 195       *
 196       * @dataProvider step_sortorder_provider
 197       * @param   int     $sortorder      The sortorder to check
 198       * @param   int     $count          Total number of steps for this test
 199       * @param   bool    $isfirst        Whether this is the first step
 200       * @param   bool    $islast         Whether this is the last step
 201       */
 202      public function test_is_last_step($sortorder, $count, $isfirst, $islast) {
 203          $step = $this->getMockBuilder(\tool_usertours\step::class)
 204              ->onlyMethods(['get_sortorder', 'get_tour'])
 205              ->getMock();
 206  
 207          $tour = $this->getMockBuilder(\tool_usertours\tour::class)
 208              ->onlyMethods(['count_steps'])
 209              ->getMock();
 210  
 211          $step->expects($this->once())
 212              ->method('get_tour')
 213              ->willReturn($tour)
 214              ;
 215  
 216          $tour->expects($this->once())
 217              ->method('count_steps')
 218              ->willReturn($count)
 219              ;
 220  
 221          $step->expects($this->once())
 222              ->method('get_sortorder')
 223              ->willReturn($sortorder)
 224              ;
 225  
 226          $this->assertEquals($islast, $step->is_last_step());
 227      }
 228  
 229      /**
 230       * Test get_config with no keys provided.
 231       */
 232      public function test_get_config_no_keys() {
 233          $step = new \tool_usertours\step();
 234  
 235          $rc = new \ReflectionClass(\tool_usertours\step::class);
 236          $rcp = $rc->getProperty('config');
 237          $rcp->setAccessible(true);
 238  
 239          $allvalues = (object) [
 240                  'some' => 'value',
 241                  'another' => 42,
 242                  'key' => [
 243                      'somethingelse',
 244                  ],
 245              ];
 246  
 247          $rcp->setValue($step, $allvalues);
 248  
 249          $this->assertEquals($allvalues, $step->get_config());
 250      }
 251  
 252      /**
 253       * Data provider for get_config.
 254       *
 255       * @return array
 256       */
 257      public function get_config_provider() {
 258          $allvalues = (object) [
 259                  'some' => 'value',
 260                  'another' => 42,
 261                  'key' => [
 262                      'somethingelse',
 263                  ],
 264              ];
 265  
 266          $tourconfig = rand(1, 100);
 267          $forcedconfig = rand(1, 100);
 268  
 269          return [
 270                  'No initial config' => [
 271                          null,
 272                          null,
 273                          null,
 274                          $tourconfig,
 275                          false,
 276                          $forcedconfig,
 277                          (object) [],
 278                      ],
 279                  'All values' => [
 280                          $allvalues,
 281                          null,
 282                          null,
 283                          $tourconfig,
 284                          false,
 285                          $forcedconfig,
 286                          $allvalues,
 287                      ],
 288                  'Valid string value' => [
 289                          $allvalues,
 290                          'some',
 291                          null,
 292                          $tourconfig,
 293                          false,
 294                          $forcedconfig,
 295                          'value',
 296                      ],
 297                  'Valid array value' => [
 298                          $allvalues,
 299                          'key',
 300                          null,
 301                          $tourconfig,
 302                          false,
 303                          $forcedconfig,
 304                          ['somethingelse'],
 305                      ],
 306                  'Invalid value' => [
 307                          $allvalues,
 308                          'notavalue',
 309                          null,
 310                          $tourconfig,
 311                          false,
 312                          $forcedconfig,
 313                          $tourconfig,
 314                      ],
 315                  'Configuration value' => [
 316                          $allvalues,
 317                          'placement',
 318                          null,
 319                          $tourconfig,
 320                          false,
 321                          $forcedconfig,
 322                          $tourconfig,
 323                      ],
 324                  'Invalid value with default' => [
 325                          $allvalues,
 326                          'notavalue',
 327                          'somedefault',
 328                          $tourconfig,
 329                          false,
 330                          $forcedconfig,
 331                          'somedefault',
 332                      ],
 333                  'Value forced at target' => [
 334                          $allvalues,
 335                          'somevalue',
 336                          'somedefault',
 337                          $tourconfig,
 338                          true,
 339                          $forcedconfig,
 340                          $forcedconfig,
 341                      ],
 342              ];
 343      }
 344  
 345      /**
 346       * Test get_config with valid keys provided.
 347       *
 348       * @dataProvider get_config_provider
 349       * @param   object  $values     The config values
 350       * @param   string  $key        The key
 351       * @param   mixed   $default    The default value
 352       * @param   mixed   $tourconfig The tour config
 353       * @param   bool    $isforced   Whether the setting is forced
 354       * @param   mixed   $forcedvalue    The example value
 355       * @param   mixed   $expected   The expected value
 356       */
 357      public function test_get_config_valid_keys($values, $key, $default, $tourconfig, $isforced, $forcedvalue, $expected) {
 358          $step = $this->getMockBuilder(\tool_usertours\step::class)
 359              ->onlyMethods(['get_target', 'get_targettype', 'get_tour'])
 360              ->getMock();
 361  
 362          $rc = new \ReflectionClass(\tool_usertours\step::class);
 363          $rcp = $rc->getProperty('config');
 364          $rcp->setAccessible(true);
 365          $rcp->setValue($step, $values);
 366  
 367          $target = $this->getMockBuilder(\tool_usertours\local\target\base::class)
 368              ->disableOriginalConstructor()
 369              ->getMock()
 370              ;
 371  
 372          $target->expects($this->any())
 373              ->method('is_setting_forced')
 374              ->willReturn($isforced)
 375              ;
 376  
 377          $target->expects($this->any())
 378              ->method('get_forced_setting_value')
 379              ->with($this->equalTo($key))
 380              ->willReturn($forcedvalue)
 381              ;
 382  
 383          $step->expects($this->any())
 384              ->method('get_targettype')
 385              ->willReturn('type')
 386              ;
 387  
 388          $step->expects($this->any())
 389              ->method('get_target')
 390              ->willReturn($target)
 391              ;
 392  
 393          $tour = $this->getMockBuilder(\tool_usertours\tour::class)
 394              ->getMock()
 395              ;
 396  
 397          $tour->expects($this->any())
 398              ->method('get_config')
 399              ->willReturn($tourconfig)
 400              ;
 401  
 402          $step->expects($this->any())
 403              ->method('get_tour')
 404              ->willReturn($tour)
 405              ;
 406  
 407          $this->assertEquals($expected, $step->get_config($key, $default));
 408      }
 409  
 410      /**
 411       * Data provider for set_config.
 412       */
 413      public function set_config_provider() {
 414          $allvalues = (object) [
 415                  'some' => 'value',
 416                  'another' => 42,
 417                  'key' => [
 418                      'somethingelse',
 419                  ],
 420              ];
 421  
 422          $randvalue = rand(1, 100);
 423  
 424          $provider = [];
 425  
 426          $newvalues = $allvalues;
 427          $newvalues->some = 'unset';
 428          $provider['Unset an existing value'] = [
 429                  $allvalues,
 430                  'some',
 431                  null,
 432                  $newvalues,
 433              ];
 434  
 435          $newvalues = $allvalues;
 436          $newvalues->some = $randvalue;
 437          $provider['Set an existing value'] = [
 438                  $allvalues,
 439                  'some',
 440                  $randvalue,
 441                  $newvalues,
 442              ];
 443  
 444          $provider['Set a new value'] = [
 445                  $allvalues,
 446                  'newkey',
 447                  $randvalue,
 448                  (object) array_merge((array) $allvalues, ['newkey' => $randvalue]),
 449              ];
 450  
 451          return $provider;
 452      }
 453  
 454      /**
 455       * Test that set_config works in the anticipated fashion.
 456       *
 457       * @dataProvider set_config_provider
 458       * @param   mixed   $initialvalues  The inital value to set
 459       * @param   string  $key        The key to test
 460       * @param   mixed   $newvalue   The new value to set
 461       * @param   mixed   $expected   The expected value
 462       */
 463      public function test_set_config($initialvalues, $key, $newvalue, $expected) {
 464          $step = new \tool_usertours\step();
 465  
 466          $rc = new \ReflectionClass(\tool_usertours\step::class);
 467          $rcp = $rc->getProperty('config');
 468          $rcp->setAccessible(true);
 469          $rcp->setValue($step, $initialvalues);
 470  
 471          $target = $this->getMockBuilder(\tool_usertours\local\target\base::class)
 472              ->disableOriginalConstructor()
 473              ->getMock()
 474              ;
 475  
 476          $target->expects($this->any())
 477              ->method('is_setting_forced')
 478              ->willReturn(false)
 479              ;
 480  
 481          $step->set_config($key, $newvalue);
 482  
 483          $this->assertEquals($expected, $rcp->getValue($step));
 484      }
 485  
 486      /**
 487       * Ensure that non-dirty tours are not persisted.
 488       */
 489      public function test_persist_non_dirty() {
 490          $step = $this->getMockBuilder(\tool_usertours\step::class)
 491              ->onlyMethods([
 492                      'to_record',
 493                      'reload',
 494                  ])
 495              ->getMock()
 496              ;
 497  
 498          $step->expects($this->never())
 499              ->method('to_record')
 500              ;
 501  
 502          $step->expects($this->never())
 503              ->method('reload')
 504              ;
 505  
 506          $this->assertSame($step, $step->persist());
 507      }
 508  
 509      /**
 510       * Ensure that new dirty steps are persisted.
 511       */
 512      public function test_persist_dirty_new() {
 513          // Mock the database.
 514          $DB = $this->mock_database();
 515          $DB->expects($this->once())
 516              ->method('insert_record')
 517              ->willReturn(42)
 518              ;
 519  
 520          // Mock the tour.
 521          $step = $this->getMockBuilder(\tool_usertours\step::class)
 522              ->onlyMethods([
 523                      'to_record',
 524                      'calculate_sortorder',
 525                      'reload',
 526                  ])
 527              ->getMock()
 528              ;
 529  
 530          $step->expects($this->once())
 531              ->method('to_record')
 532              ->willReturn((object)['id' => 42]);
 533              ;
 534  
 535          $step->expects($this->once())
 536              ->method('calculate_sortorder')
 537              ;
 538  
 539          $step->expects($this->once())
 540              ->method('reload')
 541              ;
 542  
 543          $rc = new \ReflectionClass(\tool_usertours\step::class);
 544          $rcp = $rc->getProperty('dirty');
 545          $rcp->setAccessible(true);
 546          $rcp->setValue($step, true);
 547  
 548          $tour = $this->createMock(\tool_usertours\tour::class);
 549          $rcp = $rc->getProperty('tour');
 550          $rcp->setAccessible(true);
 551          $rcp->setValue($step, $tour);
 552  
 553          $this->assertSame($step, $step->persist());
 554      }
 555  
 556      /**
 557       * Ensure that new non-dirty, forced steps are persisted.
 558       */
 559      public function test_persist_force_new() {
 560          global $DB;
 561  
 562          // Mock the database.
 563          $DB = $this->mock_database();
 564          $DB->expects($this->once())
 565              ->method('insert_record')
 566              ->willReturn(42)
 567              ;
 568  
 569          // Mock the tour.
 570          $step = $this->getMockBuilder(\tool_usertours\step::class)
 571              ->onlyMethods([
 572                      'to_record',
 573                      'calculate_sortorder',
 574                      'reload',
 575                  ])
 576              ->getMock()
 577              ;
 578  
 579          $step->expects($this->once())
 580              ->method('to_record')
 581              ->willReturn((object)['id' => 42]);
 582              ;
 583  
 584          $step->expects($this->once())
 585              ->method('calculate_sortorder')
 586              ;
 587  
 588          $step->expects($this->once())
 589              ->method('reload')
 590              ;
 591  
 592          $tour = $this->createMock(\tool_usertours\tour::class);
 593          $rc = new \ReflectionClass(\tool_usertours\step::class);
 594          $rcp = $rc->getProperty('tour');
 595          $rcp->setAccessible(true);
 596          $rcp->setValue($step, $tour);
 597  
 598          $this->assertSame($step, $step->persist(true));
 599      }
 600  
 601      /**
 602       * Ensure that existing dirty steps are persisted.
 603       */
 604      public function test_persist_dirty_existing() {
 605          // Mock the database.
 606          $DB = $this->mock_database();
 607          $DB->expects($this->once())
 608              ->method('update_record')
 609              ;
 610  
 611          // Mock the tour.
 612          $step = $this->getMockBuilder(\tool_usertours\step::class)
 613              ->onlyMethods([
 614                      'to_record',
 615                      'calculate_sortorder',
 616                      'reload',
 617                  ])
 618              ->getMock()
 619              ;
 620  
 621          $step->expects($this->once())
 622              ->method('to_record')
 623              ->willReturn((object)['id' => 42]);
 624              ;
 625  
 626          $step->expects($this->never())
 627              ->method('calculate_sortorder')
 628              ;
 629  
 630          $step->expects($this->once())
 631              ->method('reload')
 632              ;
 633  
 634          $rc = new \ReflectionClass(\tool_usertours\step::class);
 635          $rcp = $rc->getProperty('id');
 636          $rcp->setAccessible(true);
 637          $rcp->setValue($step, 42);
 638  
 639          $rcp = $rc->getProperty('dirty');
 640          $rcp->setAccessible(true);
 641          $rcp->setValue($step, true);
 642  
 643          $tour = $this->createMock(\tool_usertours\tour::class);
 644          $rcp = $rc->getProperty('tour');
 645          $rcp->setAccessible(true);
 646          $rcp->setValue($step, $tour);
 647  
 648          $this->assertSame($step, $step->persist());
 649      }
 650  
 651      /**
 652       * Ensure that existing non-dirty, forced steps are persisted.
 653       */
 654      public function test_persist_force_existing() {
 655          global $DB;
 656  
 657          // Mock the database.
 658          $DB = $this->mock_database();
 659          $DB->expects($this->once())
 660              ->method('update_record')
 661              ;
 662  
 663          // Mock the tour.
 664          $step = $this->getMockBuilder(\tool_usertours\step::class)
 665              ->onlyMethods([
 666                      'to_record',
 667                      'calculate_sortorder',
 668                      'reload',
 669                  ])
 670              ->getMock()
 671              ;
 672  
 673          $step->expects($this->once())
 674              ->method('to_record')
 675              ->willReturn((object)['id' => 42]);
 676              ;
 677  
 678          $step->expects($this->never())
 679              ->method('calculate_sortorder')
 680              ;
 681  
 682          $step->expects($this->once())
 683              ->method('reload')
 684              ;
 685  
 686          $rc = new \ReflectionClass(\tool_usertours\step::class);
 687          $rcp = $rc->getProperty('id');
 688          $rcp->setAccessible(true);
 689          $rcp->setValue($step, 42);
 690  
 691          $tour = $this->createMock(\tool_usertours\tour::class);
 692          $rcp = $rc->getProperty('tour');
 693          $rcp->setAccessible(true);
 694          $rcp->setValue($step, $tour);
 695  
 696          $this->assertSame($step, $step->persist(true));
 697      }
 698  
 699      /**
 700       * Check that a tour which has never been persisted is removed correctly.
 701       */
 702      public function test_remove_non_persisted() {
 703          $step = $this->getMockBuilder(\tool_usertours\step::class)
 704              ->onlyMethods([])
 705              ->getMock()
 706              ;
 707  
 708          // Mock the database.
 709          $DB = $this->mock_database();
 710          $DB->expects($this->never())
 711              ->method('delete_records')
 712              ;
 713  
 714          $this->assertNull($step->remove());
 715      }
 716  
 717      /**
 718       * Check that a tour which has been persisted is removed correctly.
 719       */
 720      public function test_remove_persisted() {
 721          $id = rand(1, 100);
 722  
 723          $tour = $this->getMockBuilder(\tool_usertours\tour::class)
 724              ->onlyMethods([
 725                      'reset_step_sortorder',
 726                  ])
 727              ->getMock()
 728              ;
 729  
 730          $tour->expects($this->once())
 731              ->method('reset_step_sortorder')
 732              ;
 733  
 734          $step = $this->getMockBuilder(\tool_usertours\step::class)
 735              ->onlyMethods([
 736                      'get_tour',
 737                  ])
 738              ->getMock()
 739              ;
 740  
 741          $step->expects($this->once())
 742              ->method('get_tour')
 743              ->willReturn($tour)
 744              ;
 745  
 746          // Mock the database.
 747          $DB = $this->mock_database();
 748          $DB->expects($this->once())
 749              ->method('delete_records')
 750              ->with($this->equalTo('tool_usertours_steps'), $this->equalTo(['id' => $id]))
 751              ;
 752  
 753          $rc = new \ReflectionClass(\tool_usertours\step::class);
 754          $rcp = $rc->getProperty('id');
 755          $rcp->setAccessible(true);
 756          $rcp->setValue($step, $id);
 757  
 758          $this->assertEquals($id, $step->get_id());
 759          $this->assertNull($step->remove());
 760      }
 761  
 762      /**
 763       * Data provider for the get_ tests.
 764       *
 765       * @return array
 766       */
 767      public function getter_provider() {
 768          return [
 769                  'id' => [
 770                          'id',
 771                          rand(1, 100),
 772                      ],
 773                  'tourid' => [
 774                          'tourid',
 775                          rand(1, 100),
 776                      ],
 777                  'title' => [
 778                          'title',
 779                          'Lorem',
 780                      ],
 781                  'content' => [
 782                          'content',
 783                          'Lorem',
 784                      ],
 785                  'targettype' => [
 786                          'targettype',
 787                          'Lorem',
 788                      ],
 789                  'targetvalue' => [
 790                          'targetvalue',
 791                          'Lorem',
 792                      ],
 793                  'sortorder' => [
 794                          'sortorder',
 795                          rand(1, 100),
 796                      ],
 797              ];
 798      }
 799  
 800      /**
 801       * Test that getters return the configured value.
 802       *
 803       * @dataProvider getter_provider
 804       * @param   string  $key        The key to test
 805       * @param   mixed   $value      The expected value
 806       */
 807      public function test_getters($key, $value) {
 808          $step = new \tool_usertours\step();
 809  
 810          $rc = new \ReflectionClass(\tool_usertours\step::class);
 811  
 812          $rcp = $rc->getProperty($key);
 813          $rcp->setAccessible(true);
 814          $rcp->setValue($step, $value);
 815  
 816          $getter = 'get_' . $key;
 817  
 818          $this->assertEquals($value, $step->$getter());
 819      }
 820  
 821      /**
 822       * Ensure that the get_step_image_from_input function replace PIXICON placeholder with the correct images correctly.
 823       */
 824      public function test_get_step_image_from_input() {
 825          // Test step content with single image.
 826          $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test';
 827          $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent);
 828  
 829          // If the format is correct, PIXICON placeholder will be replaced with the img tag.
 830          $this->assertStringStartsWith('<img', $stepcontent);
 831          $this->assertStringEndsWith('Test', $stepcontent);
 832          $this->assertStringNotContainsString('PIXICON', $stepcontent);
 833  
 834          // Test step content with multiple images.
 835          $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test<br>@@PIXICON::tour/tour_myhomepage::tool_usertours@@';
 836          $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent);
 837          // If the format is correct, PIXICON placeholder will be replaced with the img tag.
 838          $this->assertStringStartsWith('<img', $stepcontent);
 839          // We should have 2 img tags here.
 840          $this->assertEquals(2, substr_count($stepcontent, '<img'));
 841          $this->assertStringNotContainsString('PIXICON', $stepcontent);
 842  
 843          // Test step content with incorrect format.
 844          $stepcontent = '@@PIXICON::tour/tour_mycourses<br>Test';
 845          $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent);
 846  
 847          // If the format is not correct, PIXICON placeholder will not be replaced with the img tag.
 848          $this->assertStringStartsNotWith('<img', $stepcontent);
 849          $this->assertStringStartsWith('@@PIXICON', $stepcontent);
 850          $this->assertStringEndsWith('Test', $stepcontent);
 851          $this->assertStringContainsString('PIXICON', $stepcontent);
 852      }
 853  }