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 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [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  namespace core;
  18  
  19  use flexible_table;
  20  use testable_flexible_table;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->libdir . '/tablelib.php');
  26  require_once($CFG->libdir . '/tests/fixtures/testable_flexible_table.php');
  27  
  28  /**
  29   * Test some of tablelib.
  30   *
  31   * @package    core
  32   * @category   test
  33   * @copyright  2013 Damyon Wiese <damyon@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class tablelib_test extends \advanced_testcase {
  37      protected function generate_columns($cols) {
  38          $columns = array();
  39          foreach (range(0, $cols - 1) as $j) {
  40              array_push($columns, 'column' . $j);
  41          }
  42          return $columns;
  43      }
  44  
  45      protected function generate_headers($cols) {
  46          $columns = array();
  47          foreach (range(0, $cols - 1) as $j) {
  48              array_push($columns, 'Column ' . $j);
  49          }
  50          return $columns;
  51      }
  52  
  53      protected function generate_data($rows, $cols) {
  54          $data = array();
  55  
  56          foreach (range(0, $rows - 1) as $i) {
  57              $row = array();
  58              foreach (range(0, $cols - 1) as $j) {
  59                  $val =  'row ' . $i . ' col ' . $j;
  60                  $row['column' . $j] = $val;
  61              }
  62              array_push($data, $row);
  63          }
  64          return $data;
  65      }
  66  
  67      /**
  68       * Create a table with properties as passed in params, add data and output html.
  69       *
  70       * @param string[] $columns
  71       * @param string[] $headers
  72       * @param bool     $sortable
  73       * @param bool     $collapsible
  74       * @param string[] $suppress
  75       * @param string[] $nosorting
  76       * @param (array|object)[] $data
  77       * @param int      $pagesize
  78       */
  79      protected function run_table_test($columns, $headers, $sortable, $collapsible, $suppress, $nosorting, $data, $pagesize) {
  80          $table = $this->create_and_setup_table($columns, $headers, $sortable, $collapsible, $suppress, $nosorting);
  81          $table->pagesize($pagesize, count($data));
  82          foreach ($data as $row) {
  83              $table->add_data_keyed($row);
  84          }
  85          $table->finish_output();
  86      }
  87  
  88      /**
  89       * Create a table with properties as passed in params.
  90       *
  91       * @param string[] $columns
  92       * @param string[] $headers
  93       * @param bool $sortable
  94       * @param bool $collapsible
  95       * @param string[] $suppress
  96       * @param string[] $nosorting
  97       * @return flexible_table
  98       */
  99      protected function create_and_setup_table($columns, $headers, $sortable, $collapsible, $suppress, $nosorting) {
 100          $table = new flexible_table('tablelib_test');
 101  
 102          $table->define_columns($columns);
 103          $table->define_headers($headers);
 104          $table->define_baseurl('/invalid.php');
 105  
 106          $table->sortable($sortable);
 107          $table->collapsible($collapsible);
 108          foreach ($suppress as $column) {
 109              $table->column_suppress($column);
 110          }
 111  
 112          foreach ($nosorting as $column) {
 113              $table->no_sorting($column);
 114          }
 115  
 116          $table->setup();
 117          return $table;
 118      }
 119  
 120      public function test_empty_table() {
 121          $this->expectOutputRegex('/' . get_string('nothingtodisplay') . '/');
 122          $this->run_table_test(
 123              array('column1', 'column2'),       // Columns.
 124              array('Column 1', 'Column 2'),     // Headers.
 125              true,                              // Sortable.
 126              false,                             // Collapsible.
 127              array(),                           // Suppress columns.
 128              array(),                           // No sorting.
 129              array(),                           // Data.
 130              10                                 // Page size.
 131          );
 132      }
 133  
 134      public function test_has_next_pagination() {
 135  
 136          $data = $this->generate_data(11, 2);
 137          $columns = $this->generate_columns(2);
 138          $headers = $this->generate_headers(2);
 139  
 140          // Search for pagination controls containing 'page-link"\saria-label="Next"'.
 141          $this->expectOutputRegex('/Next page/');
 142  
 143          $this->run_table_test(
 144              $columns,
 145              $headers,
 146              true,
 147              false,
 148              array(),
 149              array(),
 150              $data,
 151              10
 152          );
 153      }
 154  
 155      public function test_has_hide() {
 156  
 157          $data = $this->generate_data(11, 2);
 158          $columns = $this->generate_columns(2);
 159          $headers = $this->generate_headers(2);
 160  
 161          // Search for 'hide' links in the column headers.
 162          $this->expectOutputRegex('/' . get_string('hide') . '/');
 163  
 164          $this->run_table_test(
 165              $columns,
 166              $headers,
 167              true,
 168              true,
 169              array(),
 170              array(),
 171              $data,
 172              10
 173          );
 174      }
 175  
 176      public function test_has_not_hide() {
 177  
 178          $data = $this->generate_data(11, 2);
 179          $columns = $this->generate_columns(2);
 180          $headers = $this->generate_headers(2);
 181  
 182          // Make sure there are no 'hide' links in the headers.
 183  
 184          ob_start();
 185          $this->run_table_test(
 186              $columns,
 187              $headers,
 188              true,
 189              false,
 190              array(),
 191              array(),
 192              $data,
 193              10
 194          );
 195          $output = ob_get_contents();
 196          ob_end_clean();
 197          $this->assertStringNotContainsString(get_string('hide'), $output);
 198      }
 199  
 200      public function test_has_sort() {
 201  
 202          $data = $this->generate_data(11, 2);
 203          $columns = $this->generate_columns(2);
 204          $headers = $this->generate_headers(2);
 205  
 206          // Search for pagination controls containing '1.*2</a>.*Next</a>'.
 207          $this->expectOutputRegex('/' . get_string('sortby') . '/');
 208  
 209          $this->run_table_test(
 210              $columns,
 211              $headers,
 212              true,
 213              false,
 214              array(),
 215              array(),
 216              $data,
 217              10
 218          );
 219      }
 220  
 221      public function test_has_not_sort() {
 222  
 223          $data = $this->generate_data(11, 2);
 224          $columns = $this->generate_columns(2);
 225          $headers = $this->generate_headers(2);
 226  
 227          // Make sure there are no 'Sort by' links in the headers.
 228  
 229          ob_start();
 230          $this->run_table_test(
 231              $columns,
 232              $headers,
 233              false,
 234              false,
 235              array(),
 236              array(),
 237              $data,
 238              10
 239          );
 240          $output = ob_get_contents();
 241          ob_end_clean();
 242          $this->assertStringNotContainsString(get_string('sortby'), $output);
 243      }
 244  
 245      public function test_has_not_next_pagination() {
 246  
 247          $data = $this->generate_data(10, 2);
 248          $columns = $this->generate_columns(2);
 249          $headers = $this->generate_headers(2);
 250  
 251          // Make sure there are no 'Next' links in the pagination.
 252  
 253          ob_start();
 254          $this->run_table_test(
 255              $columns,
 256              $headers,
 257              true,
 258              false,
 259              array(),
 260              array(),
 261              $data,
 262              10
 263          );
 264  
 265          $output = ob_get_contents();
 266          ob_end_clean();
 267          $this->assertStringNotContainsString(get_string('next'), $output);
 268      }
 269  
 270      public function test_1_col() {
 271  
 272          $data = $this->generate_data(100, 1);
 273          $columns = $this->generate_columns(1);
 274          $headers = $this->generate_headers(1);
 275  
 276          $this->expectOutputRegex('/row 0 col 0/');
 277  
 278          $this->run_table_test(
 279              $columns,
 280              $headers,
 281              true,
 282              false,
 283              array(),
 284              array(),
 285              $data,
 286              10
 287          );
 288      }
 289  
 290      public function test_empty_rows() {
 291  
 292          $data = $this->generate_data(1, 5);
 293          $columns = $this->generate_columns(5);
 294          $headers = $this->generate_headers(5);
 295  
 296          // Test that we have at least 5 columns generated for each empty row.
 297          $this->expectOutputRegex('/emptyrow.*r9_c4/');
 298  
 299          $this->run_table_test(
 300              $columns,
 301              $headers,
 302              true,
 303              false,
 304              array(),
 305              array(),
 306              $data,
 307              10
 308          );
 309      }
 310  
 311      public function test_5_cols() {
 312  
 313          $data = $this->generate_data(100, 5);
 314          $columns = $this->generate_columns(5);
 315          $headers = $this->generate_headers(5);
 316  
 317          $this->expectOutputRegex('/row 0 col 0/');
 318  
 319          $this->run_table_test(
 320              $columns,
 321              $headers,
 322              true,
 323              false,
 324              array(),
 325              array(),
 326              $data,
 327              10
 328          );
 329      }
 330  
 331      public function test_50_cols() {
 332  
 333          $data = $this->generate_data(100, 50);
 334          $columns = $this->generate_columns(50);
 335          $headers = $this->generate_headers(50);
 336  
 337          $this->expectOutputRegex('/row 0 col 0/');
 338  
 339          $this->run_table_test(
 340              $columns,
 341              $headers,
 342              true,
 343              false,
 344              array(),
 345              array(),
 346              $data,
 347              10
 348          );
 349      }
 350  
 351      /**
 352       * Data provider for test_fullname_column
 353       *
 354       * @return array
 355       */
 356      public function fullname_column_provider() {
 357          return [
 358              ['language'],
 359              ['alternatename lastname'],
 360              ['firstname lastnamephonetic'],
 361          ];
 362      }
 363  
 364      /**
 365       * Test fullname column observes configured alternate fullname format configuration
 366       *
 367       * @param string $format
 368       * @return void
 369       *
 370       * @dataProvider fullname_column_provider
 371       */
 372      public function test_fullname_column(string $format) {
 373          $this->resetAfterTest();
 374          $this->setAdminUser();
 375  
 376          set_config('alternativefullnameformat', $format);
 377  
 378          $user = $this->getDataGenerator()->create_user();
 379  
 380          $table = $this->create_and_setup_table(['fullname'], [], true, false, [], []);
 381          $this->assertStringContainsString(fullname($user, true), $table->format_row($user)['fullname']);
 382      }
 383  
 384      /**
 385       * Test fullname column ignores fullname format configuration for a user with viewfullnames capability prohibited
 386       *
 387       * @param string $format
 388       * @return void
 389       *
 390       * @dataProvider fullname_column_provider
 391       */
 392      public function test_fullname_column_prohibit_viewfullnames(string $format) {
 393          global $DB, $CFG;
 394  
 395          $this->resetAfterTest();
 396  
 397          set_config('alternativefullnameformat', $format);
 398  
 399          $currentuser = $this->getDataGenerator()->create_user();
 400          $this->setUser($currentuser);
 401  
 402          // Prohibit the viewfullnames from the default user role.
 403          $userrole = $DB->get_record('role', ['id' => $CFG->defaultuserroleid]);
 404          role_change_permission($userrole->id, \context_system::instance(), 'moodle/site:viewfullnames', CAP_PROHIBIT);
 405  
 406          $user = $this->getDataGenerator()->create_user();
 407  
 408          $table = $this->create_and_setup_table(['fullname'], [], true, false, [], []);
 409          $this->assertStringContainsString(fullname($user, false), $table->format_row($user)['fullname']);
 410      }
 411  
 412      public function test_get_row_html() {
 413          $data = $this->generate_data(1, 5);
 414          $columns = $this->generate_columns(5);
 415          $headers = $this->generate_headers(5);
 416          $data = array_keys(array_flip($data[0]));
 417  
 418          $table = new flexible_table('tablelib_test');
 419          $table->define_columns($columns);
 420          $table->define_headers($headers);
 421          $table->define_baseurl('/invalid.php');
 422  
 423          $row = $table->get_row_html($data);
 424          $this->assertMatchesRegularExpression('/row 0 col 0/', $row);
 425          $this->assertMatchesRegularExpression('/<tr class=""/', $row);
 426          $this->assertMatchesRegularExpression('/<td class="cell c0"/', $row);
 427      }
 428  
 429      public function test_persistent_table() {
 430          global $SESSION;
 431  
 432          $data = $this->generate_data(5, 5);
 433          $columns = $this->generate_columns(5);
 434          $headers = $this->generate_headers(5);
 435  
 436          // Testing without persistence first to verify that the results are different.
 437          $table1 = new flexible_table('tablelib_test');
 438          $table1->define_columns($columns);
 439          $table1->define_headers($headers);
 440          $table1->define_baseurl('/invalid.php');
 441  
 442          $table1->sortable(true);
 443          $table1->collapsible(true);
 444  
 445          $table1->is_persistent(false);
 446          $_GET['thide'] = 'column0';
 447          $_GET['tsort'] = 'column1';
 448          $_GET['tifirst'] = 'A';
 449          $_GET['tilast'] = 'Z';
 450  
 451          foreach ($data as $row) {
 452              $table1->add_data_keyed($row);
 453          }
 454          $table1->setup();
 455  
 456          // Clear session data between each new table.
 457          unset($SESSION->flextable);
 458  
 459          $table2 = new flexible_table('tablelib_test');
 460          $table2->define_columns($columns);
 461          $table2->define_headers($headers);
 462          $table2->define_baseurl('/invalid.php');
 463  
 464          $table2->sortable(true);
 465          $table2->collapsible(true);
 466  
 467          $table2->is_persistent(false);
 468          unset($_GET);
 469  
 470          foreach ($data as $row) {
 471              $table2->add_data_keyed($row);
 472          }
 473          $table2->setup();
 474  
 475          $this->assertNotEquals($table1, $table2);
 476  
 477          unset($SESSION->flextable);
 478  
 479          // Now testing with persistence to check that the tables are the same.
 480          $table3 = new flexible_table('tablelib_test');
 481          $table3->define_columns($columns);
 482          $table3->define_headers($headers);
 483          $table3->define_baseurl('/invalid.php');
 484  
 485          $table3->sortable(true);
 486          $table3->collapsible(true);
 487  
 488          $table3->is_persistent(true);
 489          $_GET['thide'] = 'column0';
 490          $_GET['tsort'] = 'column1';
 491          $_GET['tifirst'] = 'A';
 492          $_GET['tilast'] = 'Z';
 493  
 494          foreach ($data as $row) {
 495              $table3->add_data_keyed($row);
 496          }
 497          $table3->setup();
 498  
 499          unset($SESSION->flextable);
 500  
 501          $table4 = new flexible_table('tablelib_test');
 502          $table4->define_columns($columns);
 503          $table4->define_headers($headers);
 504          $table4->define_baseurl('/invalid.php');
 505  
 506          $table4->sortable(true);
 507          $table4->collapsible(true);
 508  
 509          $table4->is_persistent(true);
 510          unset($_GET);
 511  
 512          foreach ($data as $row) {
 513              $table4->add_data_keyed($row);
 514          }
 515          $table4->setup();
 516  
 517          $this->assertEquals($table3, $table4);
 518  
 519          unset($SESSION->flextable);
 520  
 521          // Finally, another test with no persistence, but without clearing the session data.
 522          $table5 = new flexible_table('tablelib_test');
 523          $table5->define_columns($columns);
 524          $table5->define_headers($headers);
 525          $table5->define_baseurl('/invalid.php');
 526  
 527          $table5->sortable(true);
 528          $table5->collapsible(true);
 529  
 530          $table5->is_persistent(true);
 531          $_GET['thide'] = 'column0';
 532          $_GET['tsort'] = 'column1';
 533          $_GET['tifirst'] = 'A';
 534          $_GET['tilast'] = 'Z';
 535  
 536          foreach ($data as $row) {
 537              $table5->add_data_keyed($row);
 538          }
 539          $table5->setup();
 540  
 541          $table6 = new flexible_table('tablelib_test');
 542          $table6->define_columns($columns);
 543          $table6->define_headers($headers);
 544          $table6->define_baseurl('/invalid.php');
 545  
 546          $table6->sortable(true);
 547          $table6->collapsible(true);
 548  
 549          $table6->is_persistent(true);
 550          unset($_GET);
 551  
 552          foreach ($data as $row) {
 553              $table6->add_data_keyed($row);
 554          }
 555          $table6->setup();
 556  
 557          $this->assertEquals($table5, $table6);
 558      }
 559  
 560      /**
 561       * Helper method for preparing tables instances in {@link self::test_can_be_reset()}.
 562       *
 563       * @param string $tableid
 564       * @return testable_flexible_table
 565       */
 566      protected function prepare_table_for_reset_test($tableid) {
 567          global $SESSION;
 568  
 569          unset($SESSION->flextable[$tableid]);
 570  
 571          $data = $this->generate_data(25, 3);
 572          $columns = array('column0', 'column1', 'column2');
 573          $headers = $this->generate_headers(3);
 574  
 575          $table = new testable_flexible_table($tableid);
 576          $table->define_baseurl('/invalid.php');
 577          $table->define_columns($columns);
 578          $table->define_headers($headers);
 579          $table->collapsible(true);
 580          $table->is_persistent(false);
 581  
 582          return $table;
 583      }
 584  
 585      public function test_can_be_reset() {
 586          // Table in its default state (as if seen for the first time), nothing to reset.
 587          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 588          $table->setup();
 589          $this->assertFalse($table->can_be_reset());
 590  
 591          // Table in its default state with default sorting defined, nothing to reset.
 592          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 593          $table->sortable(true, 'column1', SORT_DESC);
 594          $table->setup();
 595          $this->assertFalse($table->can_be_reset());
 596  
 597          // Table explicitly sorted by the default column & direction, nothing to reset.
 598          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 599          $table->sortable(true, 'column1', SORT_DESC);
 600          $_GET['tsort'] = 'column1';
 601          $_GET['tdir'] = SORT_DESC;
 602          $table->setup();
 603          unset($_GET['tsort']);
 604          unset($_GET['tdir']);
 605          $this->assertFalse($table->can_be_reset());
 606  
 607          // Table explicitly sorted twice by the default column & direction, nothing to reset.
 608          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 609          $table->sortable(true, 'column1', SORT_DESC);
 610          $_GET['tsort'] = 'column1';
 611          $_GET['tdir'] = SORT_DESC;
 612          $table->setup();
 613          $table->setup(); // Set up again to simulate the second page request.
 614          unset($_GET['tsort']);
 615          unset($_GET['tdir']);
 616          $this->assertFalse($table->can_be_reset());
 617  
 618          // Table sorted by other than default column, can be reset.
 619          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 620          $table->sortable(true, 'column1', SORT_DESC);
 621          $_GET['tsort'] = 'column2';
 622          $table->setup();
 623          unset($_GET['tsort']);
 624          $this->assertTrue($table->can_be_reset());
 625  
 626          // Table sorted by other than default direction, can be reset.
 627          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 628          $table->sortable(true, 'column1', SORT_DESC);
 629          $_GET['tsort'] = 'column1';
 630          $_GET['tdir'] = SORT_ASC;
 631          $table->setup();
 632          unset($_GET['tsort']);
 633          unset($_GET['tdir']);
 634          $this->assertTrue($table->can_be_reset());
 635  
 636          // Table sorted by the default column after another sorting previously selected.
 637          // This leads to different ORDER BY than just having a single sort defined, can be reset.
 638          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 639          $table->sortable(true, 'column1', SORT_DESC);
 640          $_GET['tsort'] = 'column0';
 641          $table->setup();
 642          $_GET['tsort'] = 'column1';
 643          $table->setup();
 644          unset($_GET['tsort']);
 645          $this->assertTrue($table->can_be_reset());
 646  
 647          // Table having some column collapsed, can be reset.
 648          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 649          $_GET['thide'] = 'column2';
 650          $table->setup();
 651          unset($_GET['thide']);
 652          $this->assertTrue($table->can_be_reset());
 653  
 654          // Table having some column explicitly expanded, nothing to reset.
 655          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 656          $_GET['tshow'] = 'column2';
 657          $table->setup();
 658          unset($_GET['tshow']);
 659          $this->assertFalse($table->can_be_reset());
 660  
 661          // Table after expanding a collapsed column, nothing to reset.
 662          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 663          $_GET['thide'] = 'column0';
 664          $table->setup();
 665          $_GET['tshow'] = 'column0';
 666          $table->setup();
 667          unset($_GET['thide']);
 668          unset($_GET['tshow']);
 669          $this->assertFalse($table->can_be_reset());
 670  
 671          // Table with some name filtering enabled, can be reset.
 672          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 673          $_GET['tifirst'] = 'A';
 674          $table->setup();
 675          unset($_GET['tifirst']);
 676          $this->assertTrue($table->can_be_reset());
 677      }
 678  
 679      /**
 680       * Test export in CSV format
 681       */
 682      public function test_table_export() {
 683          $table = new flexible_table('tablelib_test_export');
 684          $table->define_baseurl('/invalid.php');
 685          $table->define_columns(['c1', 'c2', 'c3']);
 686          $table->define_headers(['Col1', 'Col2', 'Col3']);
 687  
 688          ob_start();
 689          $table->is_downloadable(true);
 690          $table->is_downloading('csv');
 691  
 692          $table->setup();
 693          $table->add_data(['column0' => 'a', 'column1' => 'b', 'column2' => 'c']);
 694          $output = ob_get_contents();
 695          ob_end_clean();
 696  
 697          $this->assertEquals("Col1,Col2,Col3\na,b,c\n", substr($output, 3));
 698      }
 699  
 700      /**
 701       * Test the initials functionality.
 702       *
 703       * @dataProvider initials_provider
 704       * @param string|null $getvalue
 705       * @param string|null $setvalue
 706       * @param string|null $finalvalue
 707       */
 708      public function test_initials_first_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
 709          global $_GET;
 710  
 711          $this->resetAfterTest(true);
 712  
 713          $table = new flexible_table('tablelib_test');
 714  
 715          $user = $this->getDataGenerator()->create_user();
 716  
 717          $table->define_columns(['fullname']);
 718          $table->define_headers(['Fullname']);
 719          $table->define_baseurl('/invalid.php');
 720          $table->initialbars(true);
 721  
 722          if ($getvalue !== null) {
 723              $_GET['tifirst'] = $getvalue;
 724          }
 725  
 726          if ($setvalue !== null) {
 727              $table->set_first_initial($setvalue);
 728          }
 729  
 730          $table->setup();
 731  
 732          $this->assertEquals($finalvalue, $table->get_initial_first());
 733      }
 734  
 735      /**
 736       * Test the initials functionality.
 737       *
 738       * @dataProvider initials_provider
 739       * @param string|null $getvalue
 740       * @param string|null $setvalue
 741       * @param string|null $finalvalue
 742       */
 743      public function test_initials_last_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
 744          global $_GET;
 745  
 746          $this->resetAfterTest(true);
 747  
 748          $table = new flexible_table('tablelib_test');
 749  
 750          $user = $this->getDataGenerator()->create_user();
 751  
 752          $table->define_columns(['fullname']);
 753          $table->define_headers(['Fullname']);
 754          $table->define_baseurl('/invalid.php');
 755          $table->initialbars(true);
 756  
 757          if ($getvalue !== null) {
 758              $_GET['tilast'] = $getvalue;
 759          }
 760  
 761          if ($setvalue !== null) {
 762              $table->set_last_initial($setvalue);
 763          }
 764  
 765          $table->setup();
 766  
 767          $this->assertEquals($finalvalue, $table->get_initial_last());
 768      }
 769  
 770      /**
 771       * Data for testing initials providers.
 772       *
 773       * @return array
 774       */
 775      public function initials_provider(): array {
 776          return [
 777              [null, null, null],
 778              ['A', null, 'A'],
 779              ['Z', null, 'Z'],
 780              [null, 'A', 'A'],
 781              [null, 'Z', 'Z'],
 782              ['A', 'Z', 'Z'],
 783              ['Z', 'A', 'A'],
 784          ];
 785      }
 786  
 787  }