first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+803
View File
@@ -0,0 +1,803 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
/**
* Test advanced_testcase extra features.
*
* @package core
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \advanced_testcase
*/
class advanced_test extends \advanced_testcase {
public static function setUpBeforeClass(): void {
global $CFG;
require_once(__DIR__ . '/fixtures/adhoc_test_task.php');
}
public function test_debugging(): void {
global $CFG;
$this->resetAfterTest();
debugging('hokus');
$this->assertDebuggingCalled();
debugging('pokus');
$this->assertDebuggingCalled('pokus');
debugging('pokus', DEBUG_MINIMAL);
$this->assertDebuggingCalled('pokus', DEBUG_MINIMAL);
$this->assertDebuggingNotCalled();
debugging('a');
debugging('b', DEBUG_MINIMAL);
debugging('c', DEBUG_DEVELOPER);
$debuggings = $this->getDebuggingMessages();
$this->assertCount(3, $debuggings);
$this->assertSame('a', $debuggings[0]->message);
$this->assertSame(DEBUG_NORMAL, $debuggings[0]->level);
$this->assertSame('b', $debuggings[1]->message);
$this->assertSame(DEBUG_MINIMAL, $debuggings[1]->level);
$this->assertSame('c', $debuggings[2]->message);
$this->assertSame(DEBUG_DEVELOPER, $debuggings[2]->level);
$this->resetDebugging();
$this->assertDebuggingNotCalled();
$debuggings = $this->getDebuggingMessages();
$this->assertCount(0, $debuggings);
set_debugging(DEBUG_NONE);
debugging('hokus');
$this->assertDebuggingNotCalled();
set_debugging(DEBUG_DEVELOPER);
}
/**
* @test
*
* Annotations are a valid PHPUnit method for running tests. Debugging needs to support them.
*/
public function debugging_called_with_annotation() {
debugging('pokus', DEBUG_MINIMAL);
$this->assertDebuggingCalled('pokus', DEBUG_MINIMAL);
}
public function test_set_user(): void {
global $USER, $DB, $SESSION;
$this->resetAfterTest();
$this->assertEquals(0, $USER->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
$user = $DB->get_record('user', array('id'=>2));
$this->assertNotEmpty($user);
$this->setUser($user);
$this->assertEquals(2, $USER->id);
$this->assertEquals(2, $_SESSION['USER']->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
$USER->id = 3;
$this->assertEquals(3, $USER->id);
$this->assertEquals(3, $_SESSION['USER']->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
\core\session\manager::set_user($user);
$this->assertEquals(2, $USER->id);
$this->assertEquals(2, $_SESSION['USER']->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
$USER = $DB->get_record('user', array('id'=>1));
$this->assertNotEmpty($USER);
$this->assertEquals(1, $USER->id);
$this->assertEquals(1, $_SESSION['USER']->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
$this->setUser(null);
$this->assertEquals(0, $USER->id);
$this->assertSame($_SESSION['USER'], $USER);
$this->assertSame($GLOBALS['USER'], $USER);
// Ensure session is reset after setUser, as it may contain extra info.
$SESSION->sometestvalue = true;
$this->setUser($user);
$this->assertObjectNotHasProperty('sometestvalue', $SESSION);
}
public function test_set_admin_user(): void {
global $USER;
$this->resetAfterTest();
$this->setAdminUser();
$this->assertEquals($USER->id, 2);
$this->assertTrue(is_siteadmin());
}
public function test_set_guest_user(): void {
global $USER;
$this->resetAfterTest();
$this->setGuestUser();
$this->assertEquals($USER->id, 1);
$this->assertTrue(isguestuser());
}
public function test_database_reset(): void {
global $DB;
$this->resetAfterTest();
$this->preventResetByRollback();
$this->assertEquals(1, $DB->count_records('course')); // Only frontpage in new site.
// This is weird table - id is NOT a sequence here.
$this->assertEquals(0, $DB->count_records('context_temp'));
$DB->import_record('context_temp', array('id'=>5, 'path'=>'/1/2', 'depth'=>2));
$record = $DB->get_record('context_temp', array());
$this->assertEquals(5, $record->id);
$this->assertEquals(0, $DB->count_records('user_preferences'));
$originaldisplayid = $DB->insert_record('user_preferences', array('userid'=>2, 'name'=> 'phpunittest', 'value'=>'x'));
$this->assertEquals(1, $DB->count_records('user_preferences'));
$numcourses = $DB->count_records('course');
$course = $this->getDataGenerator()->create_course();
$this->assertEquals($numcourses + 1, $DB->count_records('course'));
$this->assertEquals(2, $DB->count_records('user'));
$DB->delete_records('user', array('id'=>1));
$this->assertEquals(1, $DB->count_records('user'));
$this->resetAllData();
$this->assertEquals(1, $DB->count_records('course')); // Only frontpage in new site.
$this->assertEquals(0, $DB->count_records('context_temp')); // Only frontpage in new site.
$numcourses = $DB->count_records('course');
$course = $this->getDataGenerator()->create_course();
$this->assertEquals($numcourses + 1, $DB->count_records('course'));
$displayid = $DB->insert_record('user_preferences', array('userid'=>2, 'name'=> 'phpunittest', 'value'=>'x'));
$this->assertEquals($originaldisplayid, $displayid);
$this->assertEquals(2, $DB->count_records('user'));
$DB->delete_records('user', array('id'=>2));
$user = $this->getDataGenerator()->create_user();
$this->assertEquals(2, $DB->count_records('user'));
$this->assertGreaterThan(2, $user->id);
$this->resetAllData();
$numcourses = $DB->count_records('course');
$course = $this->getDataGenerator()->create_course();
$this->assertEquals($numcourses + 1, $DB->count_records('course'));
$this->assertEquals(2, $DB->count_records('user'));
$DB->delete_records('user', array('id'=>2));
$this->resetAllData();
$numcourses = $DB->count_records('course');
$course = $this->getDataGenerator()->create_course();
$this->assertEquals($numcourses + 1, $DB->count_records('course'));
$this->assertEquals(2, $DB->count_records('user'));
}
public function test_change_detection(): void {
global $DB, $CFG, $COURSE, $SITE, $USER;
$this->preventResetByRollback();
self::resetAllData(true);
// Database change.
$this->assertEquals(1, $DB->get_field('user', 'confirmed', array('id'=>2)));
$DB->set_field('user', 'confirmed', 0, array('id'=>2));
try {
self::resetAllData(true);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
}
$this->assertEquals(1, $DB->get_field('user', 'confirmed', array('id'=>2)));
// Config change.
$CFG->xx = 'yy';
unset($CFG->admin);
$CFG->rolesactive = 0;
try {
self::resetAllData(true);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
$this->assertStringContainsString('xx', $e->getMessage());
$this->assertStringContainsString('admin', $e->getMessage());
$this->assertStringContainsString('rolesactive', $e->getMessage());
}
$this->assertFalse(isset($CFG->xx));
$this->assertTrue(isset($CFG->admin));
$this->assertEquals(1, $CFG->rolesactive);
// _GET change.
$_GET['__somethingthatwillnotnormallybepresent__'] = 'yy';
self::resetAllData(true);
$this->assertEquals(array(), $_GET);
// _POST change.
$_POST['__somethingthatwillnotnormallybepresent2__'] = 'yy';
self::resetAllData(true);
$this->assertEquals(array(), $_POST);
// _FILES change.
$_FILES['__somethingthatwillnotnormallybepresent3__'] = 'yy';
self::resetAllData(true);
$this->assertEquals(array(), $_FILES);
// _REQUEST change.
$_REQUEST['__somethingthatwillnotnormallybepresent4__'] = 'yy';
self::resetAllData(true);
$this->assertEquals(array(), $_REQUEST);
// Silent changes.
$_SERVER['xx'] = 'yy';
self::resetAllData(true);
$this->assertFalse(isset($_SERVER['xx']));
// COURSE change.
$SITE->id = 10;
$COURSE = new \stdClass();
$COURSE->id = 7;
try {
self::resetAllData(true);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
$this->assertEquals(1, $SITE->id);
$this->assertSame($SITE, $COURSE);
$this->assertSame($SITE, $COURSE);
}
// USER change.
$this->setUser(2);
try {
self::resetAllData(true);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
$this->assertEquals(0, $USER->id);
}
}
public function test_getDataGenerator(): void {
$generator = $this->getDataGenerator();
$this->assertInstanceOf('testing_data_generator', $generator);
}
public function test_database_mock1(): void {
global $DB;
try {
$DB->get_record('pokus', array());
$this->fail('Exception expected when accessing non existent table');
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
$DB = $this->createMock(get_class($DB));
$this->assertNull($DB->get_record('pokus', array()));
// Rest continues after reset.
}
public function test_database_mock2(): void {
global $DB;
// Now the database should be back to normal.
$this->assertFalse($DB->get_record('user', array('id'=>9999)));
}
public function test_assert_time_current(): void {
$this->assertTimeCurrent(time());
$this->setCurrentTimeStart();
$this->assertTimeCurrent(time());
$this->waitForSecond();
$this->assertTimeCurrent(time());
$this->assertTimeCurrent(time()-1);
try {
$this->setCurrentTimeStart();
$this->assertTimeCurrent(time()+10);
$this->fail('Failed assert expected');
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\ExpectationFailedException', $e);
}
try {
$this->setCurrentTimeStart();
$this->assertTimeCurrent(time()-10);
$this->fail('Failed assert expected');
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\ExpectationFailedException', $e);
}
}
/**
* Test the assertEventContextNotUsed() assertion.
*
* Verify that events using the event context in some of their
* methods are detected properly (will throw a warning if they are).
*
* To do so, we'll be using some fixture events (context_used_in_event_xxxx),
* that, on purpose, use the event context (incorrectly) in their methods.
*
* Note that because we are using imported fixture classes, and because we
* are testing for warnings, better we run the tests in a separate process.
*
* @param string $fixture The fixture class to use.
* @param bool $phpwarn Whether a PHP warning is expected.
*
* @runInSeparateProcess
* @dataProvider assert_event_context_not_used_provider
* @covers ::assertEventContextNotUsed
*/
public function test_assert_event_context_not_used($fixture, $phpwarn): void {
require(__DIR__ . '/fixtures/event_fixtures.php');
// Create an event that uses the event context in its get_url() and get_description() methods.
$event = $fixture::create([
'other' => [
'sample' => 1,
'xx' => 10,
],
]);
if ($phpwarn) {
// Let's convert the warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
}
$this->assertEventContextNotUsed($event);
}
/**
* Data provider for test_assert_event_context_not_used().
*
* @return array
*/
public static function assert_event_context_not_used_provider(): array {
return [
'correct' => ['\core\event\context_used_in_event_correct', false],
'wrong_get_url' => ['\core\event\context_used_in_event_get_url', true],
'wrong_get_description' => ['\core\event\context_used_in_event_get_description', true],
];
}
public function test_message_processors_reset(): void {
global $DB;
$this->resetAfterTest(true);
// Get all processors first.
$processors1 = get_message_processors();
// Add a new message processor and get all processors again.
$processor = new \stdClass();
$processor->name = 'test_processor';
$processor->enabled = 1;
$DB->insert_record('message_processors', $processor);
$processors2 = get_message_processors();
// Assert that new processor still haven't been added to the list.
$this->assertSame($processors1, $processors2);
// Reset message processors data.
$processors3 = get_message_processors(false, true);
// Now, list of processors should not be the same any more,
// And we should have one more message processor in the list.
$this->assertNotSame($processors1, $processors3);
$this->assertEquals(count($processors1) + 1, count($processors3));
}
public function test_message_redirection(): \phpunit_message_sink {
$this->preventResetByRollback(); // Messaging is not compatible with transactions...
$this->resetAfterTest(false);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// Any core message will do here.
$message1 = new \core\message\message();
$message1->courseid = 1;
$message1->component = 'moodle';
$message1->name = 'instantmessage';
$message1->userfrom = $user1;
$message1->userto = $user2;
$message1->subject = 'message subject 1';
$message1->fullmessage = 'message body';
$message1->fullmessageformat = FORMAT_MARKDOWN;
$message1->fullmessagehtml = '<p>message body</p>';
$message1->smallmessage = 'small message';
$message1->notification = 0;
$message2 = new \core\message\message();
$message2->courseid = 1;
$message2->component = 'moodle';
$message2->name = 'instantmessage';
$message2->userfrom = $user2;
$message2->userto = $user1;
$message2->subject = 'message subject 2';
$message2->fullmessage = 'message body';
$message2->fullmessageformat = FORMAT_MARKDOWN;
$message2->fullmessagehtml = '<p>message body</p>';
$message2->smallmessage = 'small message';
$message2->notification = 0;
// There should be debugging message without redirection.
$mailsink = $this->redirectEmails();
$mailsink->close();
message_send($message1);
$this->assertDebuggingCalled(null, null, 'message_send() must print debug message that messaging is disabled in phpunit tests.');
// Sink should catch messages.
$sink = $this->redirectMessages();
$mid1 = message_send($message1);
$mid2 = message_send($message2);
$this->assertDebuggingNotCalled('message redirection must prevent debug messages from the message_send()');
$this->assertEquals(2, $sink->count());
$this->assertGreaterThanOrEqual(1, $mid1);
$this->assertGreaterThanOrEqual($mid1, $mid2);
$messages = $sink->get_messages();
$this->assertIsArray($messages);
$this->assertCount(2, $messages);
$this->assertEquals($mid1, $messages[0]->id);
$this->assertEquals($message1->userto->id, $messages[0]->useridto);
$this->assertEquals($message1->userfrom->id, $messages[0]->useridfrom);
$this->assertEquals($message1->smallmessage, $messages[0]->smallmessage);
$this->assertEquals($mid2, $messages[1]->id);
$this->assertEquals($message2->userto->id, $messages[1]->useridto);
$this->assertEquals($message2->userfrom->id, $messages[1]->useridfrom);
$this->assertEquals($message2->smallmessage, $messages[1]->smallmessage);
// Test resetting.
$sink->clear();
$messages = $sink->get_messages();
$this->assertIsArray($messages);
$this->assertCount(0, $messages);
message_send($message1);
$messages = $sink->get_messages();
$this->assertIsArray($messages);
$this->assertCount(1, $messages);
// Test closing.
$sink->close();
$messages = $sink->get_messages();
$this->assertIsArray($messages);
$this->assertCount(1, $messages, 'Messages in sink are supposed to stay there after close');
// Test debugging is enabled again.
message_send($message1);
$this->assertDebuggingCalled(null, null, 'message_send() must print debug message that messaging is disabled in phpunit tests.');
// Test invalid names and components.
$sink = $this->redirectMessages();
$message3 = new \core\message\message();
$message3->courseid = 1;
$message3->component = 'xxxx_yyyyy';
$message3->name = 'instantmessage';
$message3->userfrom = $user2;
$message3->userto = $user1;
$message3->subject = 'message subject 2';
$message3->fullmessage = 'message body';
$message3->fullmessageformat = FORMAT_MARKDOWN;
$message3->fullmessagehtml = '<p>message body</p>';
$message3->smallmessage = 'small message';
$message3->notification = 0;
$this->assertFalse(message_send($message3));
$this->assertDebuggingCalled('Attempt to send msg from a provider xxxx_yyyyy/instantmessage '.
'that is inactive or not allowed for the user id='.$user1->id);
$message3->component = 'moodle';
$message3->name = 'yyyyyy';
$this->assertFalse(message_send($message3));
$this->assertDebuggingCalled('Attempt to send msg from a provider moodle/yyyyyy '.
'that is inactive or not allowed for the user id='.$user1->id);
message_send($message1);
$this->assertEquals(1, $sink->count());
// Test if sink can be carried over to next test.
$this->assertTrue(\phpunit_util::is_redirecting_messages());
return $sink;
}
/**
* @depends test_message_redirection
*/
public function test_message_redirection_noreset(\phpunit_message_sink $sink): void {
if ($this->isInIsolation()) {
$this->markTestSkipped('State cannot be carried over between tests in isolated tests');
}
$this->preventResetByRollback(); // Messaging is not compatible with transactions...
$this->resetAfterTest();
$this->assertTrue(\phpunit_util::is_redirecting_messages());
$this->assertEquals(1, $sink->count());
$message = new \core\message\message();
$message->courseid = 1;
$message->component = 'moodle';
$message->name = 'instantmessage';
$message->userfrom = get_admin();
$message->userto = get_admin();
$message->subject = 'message subject 1';
$message->fullmessage = 'message body';
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = '<p>message body</p>';
$message->smallmessage = 'small message';
$message->notification = 0;
message_send($message);
$this->assertEquals(2, $sink->count());
}
/**
* @depends test_message_redirection_noreset
*/
public function test_message_redirection_reset(): void {
$this->assertFalse(\phpunit_util::is_redirecting_messages(), 'Test reset must stop message redirection.');
}
public function test_set_timezone(): void {
global $CFG;
$this->resetAfterTest();
$this->assertSame('Australia/Perth', $CFG->timezone);
$this->assertSame('Australia/Perth', date_default_timezone_get());
$this->setTimezone('Pacific/Auckland', 'Europe/Prague');
$this->assertSame('Pacific/Auckland', $CFG->timezone);
$this->assertSame('Pacific/Auckland', date_default_timezone_get());
$this->setTimezone('99', 'Europe/Prague');
$this->assertSame('99', $CFG->timezone);
$this->assertSame('Europe/Prague', date_default_timezone_get());
$this->setTimezone('xxx', 'Europe/Prague');
$this->assertSame('xxx', $CFG->timezone);
$this->assertSame('Europe/Prague', date_default_timezone_get());
$this->setTimezone();
$this->assertSame('Australia/Perth', $CFG->timezone);
$this->assertSame('Australia/Perth', date_default_timezone_get());
try {
$this->setTimezone('Pacific/Auckland', '');
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
}
try {
$this->setTimezone('Pacific/Auckland', 'xxxx');
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
}
try {
$this->setTimezone('Pacific/Auckland', null);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
}
}
public function test_locale_reset(): void {
global $CFG;
$this->resetAfterTest();
// If this fails self::resetAllData(); must be updated.
$this->assertSame('en_AU.UTF-8', get_string('locale', 'langconfig'));
$this->assertSame('English_Australia.1252', get_string('localewin', 'langconfig'));
if ($CFG->ostype === 'WINDOWS') {
$this->assertSame('English_Australia.1252', setlocale(LC_TIME, 0));
setlocale(LC_TIME, 'English_USA.1252');
} else {
$this->assertSame('en_AU.UTF-8', setlocale(LC_TIME, 0));
setlocale(LC_TIME, 'en_US.UTF-8');
}
try {
self::resetAllData(true);
} catch (\Exception $e) {
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
}
if ($CFG->ostype === 'WINDOWS') {
$this->assertSame('English_Australia.1252', setlocale(LC_TIME, 0));
} else {
$this->assertSame('en_AU.UTF-8', setlocale(LC_TIME, 0));
}
if ($CFG->ostype === 'WINDOWS') {
$this->assertSame('English_Australia.1252', setlocale(LC_TIME, 0));
setlocale(LC_TIME, 'English_USA.1252');
} else {
$this->assertSame('en_AU.UTF-8', setlocale(LC_TIME, 0));
setlocale(LC_TIME, 'en_US.UTF-8');
}
self::resetAllData(false);
if ($CFG->ostype === 'WINDOWS') {
$this->assertSame('English_Australia.1252', setlocale(LC_TIME, 0));
} else {
$this->assertSame('en_AU.UTF-8', setlocale(LC_TIME, 0));
}
}
/**
* This test sets a user agent and makes sure that it is cleared when the test is reset.
*/
public function test_it_resets_useragent_after_test(): void {
$this->resetAfterTest();
$fakeagent = 'New user agent set.';
// Sanity check: it should not be set when test begins.
self::assertFalse(\core_useragent::get_user_agent_string(), 'It should not be set at first.');
// Set a fake useragent and check it was set properly.
\core_useragent::instance(true, $fakeagent);
self::assertSame($fakeagent, \core_useragent::get_user_agent_string(), 'It should be the forced agent.');
// Reset test data and ansure the useragent was cleaned.
self::resetAllData(false);
self::assertFalse(\core_useragent::get_user_agent_string(), 'It should not be set again, data was reset.');
}
/**
* @covers ::runAdhocTasks
*/
public function test_runadhoctasks_no_tasks_queued(): void {
$this->runAdhocTasks();
$this->expectOutputRegex('/^$/');
}
/**
* @covers ::runAdhocTasks
*/
public function test_runadhoctasks_tasks_queued(): void {
$this->resetAfterTest(true);
$admin = get_admin();
\core\task\manager::queue_adhoc_task(new \core_phpunit\adhoc_test_task());
$this->runAdhocTasks();
$this->expectOutputRegex("/Task was run as {$admin->id}/");
}
/**
* @covers ::runAdhocTasks
*/
public function test_runadhoctasks_with_existing_user_change(): void {
$this->resetAfterTest(true);
$admin = get_admin();
$this->setGuestUser();
\core\task\manager::queue_adhoc_task(new \core_phpunit\adhoc_test_task());
$this->runAdhocTasks();
$this->expectOutputRegex("/Task was run as {$admin->id}/");
}
/**
* @covers ::runAdhocTasks
*/
public function test_runadhoctasks_with_existing_user_change_and_specified(): void {
global $USER;
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$this->setGuestUser();
$task = new \core_phpunit\adhoc_test_task();
$task->set_userid($user->id);
\core\task\manager::queue_adhoc_task($task);
$this->runAdhocTasks();
$this->expectOutputRegex("/Task was run as {$user->id}/");
}
/**
* Test the incrementing mock clock.
*
* @covers ::mock_clock_with_incrementing
* @covers \incrementing_clock
*/
public function test_mock_clock_with_incrementing(): void {
$standard = \core\di::get(\core\clock::class);
$this->assertInstanceOf(\Psr\Clock\ClockInterface::class, $standard);
$this->assertInstanceOf(\core\clock::class, $standard);
$newclock = $this->mock_clock_with_incrementing(0);
$mockedclock = \core\di::get(\core\clock::class);
$this->assertInstanceOf(\incrementing_clock::class, $newclock);
$this->assertSame($newclock, $mockedclock);
// Test the functionality.
$this->assertEquals(0, $mockedclock->now()->getTimestamp());
$this->assertEquals(1, $newclock->now()->getTimestamp());
$this->assertEquals(2, $mockedclock->now()->getTimestamp());
// Specify a specific start time.
$newclock = $this->mock_clock_with_incrementing(12345);
$mockedclock = \core\di::get(\core\clock::class);
$this->assertSame($newclock, $mockedclock);
$this->assertEquals(12345, $mockedclock->now()->getTimestamp());
$this->assertEquals(12346, $newclock->now()->getTimestamp());
$this->assertEquals(12347, $mockedclock->now()->getTimestamp());
$this->assertEquals($newclock->time, $mockedclock->now()->getTimestamp());
}
/**
* Test the incrementing mock clock.
*
* @covers ::mock_clock_with_frozen
* @covers \frozen_clock
*/
public function test_mock_clock_with_frozen(): void {
$standard = \core\di::get(\core\clock::class);
$this->assertInstanceOf(\Psr\Clock\ClockInterface::class, $standard);
$this->assertInstanceOf(\core\clock::class, $standard);
$newclock = $this->mock_clock_with_frozen(0);
$mockedclock = \core\di::get(\core\clock::class);
$this->assertInstanceOf(\frozen_clock::class, $newclock);
$this->assertSame($newclock, $mockedclock);
// Test the functionality.
$initialtime = $mockedclock->now()->getTimestamp();
$this->assertEquals($initialtime, $newclock->now()->getTimestamp());
$this->assertEquals($initialtime, $mockedclock->now()->getTimestamp());
// Specify a specific start time.
$newclock = $this->mock_clock_with_frozen(12345);
$mockedclock = \core\di::get(\core\clock::class);
$this->assertSame($newclock, $mockedclock);
$initialtime = $mockedclock->now();
$this->assertEquals($initialtime, $mockedclock->now());
$this->assertEquals($initialtime, $newclock->now());
$this->assertEquals($initialtime, $mockedclock->now());
}
}
+380
View File
@@ -0,0 +1,380 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use phpunit_util;
/**
* Test basic_testcase extra features and PHPUnit Moodle integration.
*
* @package core
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class basic_test extends \basic_testcase {
protected $testassertexecuted = false;
protected function setUp(): void {
parent::setUp();
if ($this->getName() === 'test_setup_assert') {
$this->assertTrue(true);
$this->testassertexecuted = true;
return;
}
}
/**
* Tests that bootstrapping has occurred correctly
* @return void
*/
public function test_bootstrap(): void {
global $CFG;
// The httpswwwroot has been deprecated, we keep it as an alias for backwards compatibility with plugins only.
$this->assertTrue(isset($CFG->httpswwwroot));
$this->assertEquals($CFG->httpswwwroot, $CFG->wwwroot);
$this->assertEquals($CFG->prefix, $CFG->phpunit_prefix);
}
/**
* This is just a verification if I understand the PHPUnit assert docs right --skodak
* @return void
*/
public function test_assert_behaviour(): void {
// Arrays.
$a = array('a', 'b', 'c');
$b = array('a', 'c', 'b');
$c = array('a', 'b', 'c');
$d = array('a', 'b', 'C');
$this->assertNotEquals($a, $b);
$this->assertNotEquals($a, $d);
$this->assertEquals($a, $c);
$this->assertEqualsCanonicalizing($a, $b);
// Objects.
$a = new \stdClass();
$a->x = 'x';
$a->y = 'y';
$b = new \stdClass(); // Switched order.
$b->y = 'y';
$b->x = 'x';
$c = $a;
$d = new \stdClass();
$d->x = 'x';
$d->y = 'y';
$d->z = 'z';
$this->assertEquals($a, $b);
$this->assertNotSame($a, $b);
$this->assertEquals($a, $c);
$this->assertSame($a, $c);
$this->assertNotEquals($a, $d);
// String comparison.
$this->assertEquals(1, '1');
$this->assertEquals(null, '');
$this->assertNotEquals(0, '');
$this->assertNotEquals(null, '0');
$this->assertNotEquals(array(), '');
// Other comparison.
$this->assertEquals(null, null);
$this->assertEquals(false, null);
$this->assertEquals(0, null);
// Emptiness.
$this->assertEmpty(0);
$this->assertEmpty(0.0);
$this->assertEmpty('');
$this->assertEmpty('0');
$this->assertEmpty(false);
$this->assertEmpty(null);
$this->assertEmpty(array());
$this->assertNotEmpty(1);
$this->assertNotEmpty(0.1);
$this->assertNotEmpty(-1);
$this->assertNotEmpty(' ');
$this->assertNotEmpty('0 ');
$this->assertNotEmpty(true);
$this->assertNotEmpty(array(null));
$this->assertNotEmpty(new \stdClass());
}
/**
* Make sure there are no sloppy Windows line endings
* that would break our tests.
*/
public function test_lineendings(): void {
$string = <<<STRING
a
b
STRING;
$this->assertSame("a\nb", $string, 'Make sure all project files are checked out with unix line endings.');
}
/**
* Make sure asserts in setUp() do not create problems.
*/
public function test_setup_assert(): void {
$this->assertTrue($this->testassertexecuted);
$this->testassertexecuted = false;
}
/**
* Test assert Tag
*/
public function test_assert_tag(): void {
// This should succeed.
self::assertTag(['id' => 'testid'], "<div><span id='testid'></span></div>");
$this->expectException(\PHPUnit\Framework\ExpectationFailedException::class);
self::assertTag(['id' => 'testid'], "<div><div>");
}
/**
* Tests for assertEqualsIgnoringWhitespace.
*
* @param string $expected
* @param string $actual
* @param bool $expectationvalid
* @dataProvider equals_ignoring_whitespace_provider
*/
public function test_assertEqualsIgnoringWhitespace( // phpcs:ignore
string $expected,
string $actual,
bool $expectationvalid,
): void {
if (!$expectationvalid) {
$this->expectException(\PHPUnit\Framework\ExpectationFailedException::class);
}
self::assertEqualsIgnoringWhitespace($expected, $actual);
}
/**
* Data provider for assertEqualsIgnoringWhitespace tests
*
* @return array
*/
public static function equals_ignoring_whitespace_provider(): array {
return [
'equal' => ['a b c', 'a b c', true],
'equal with whitespace' => ["a b c", "a\nb c", true],
'equal with extra whitespace' => ["a b c", "a\nb c", true],
'whitespace missing' => ["ab c", "a\nb c", false],
'not equal' => ['a b c', 'a b d', false],
'various space types' => [
implode(' ', [
'20', // Regular space.
"a0", // No-Break Space (NBSP).
"80", // Ogham Space Mark.
"0", // En Quad.
"1", // Em Quad.
"2", // En Space.
"3", // Em Space.
"4", // Three-Per-Em Space.
"5", // Four-Per-Em Space.
"6", // Six-Per-Em Space.
"7", // Figure Space.
"8", // Punctuation Space.
"9", // Thin Space.
"0a", // Hair Space.
"2f", // Narrow No-Break Space (NNBSP).
"5f", // Medium Mathematical Space.
"3000", // Ideographic Space.
".",
]),
implode('', [
// All space chars taken from https://www.compart.com/en/unicode/category/Zs.
"20\u{0020}", // Regular space.
"a0\u{00a0}", // No-Break Space (NBSP).
"80\u{1680}", // Ogham Space Mark.
"0\u{2000}", // En Quad.
"1\u{2001}", // Em Quad.
"2\u{2002}", // En Space.
"3\u{2003}", // Em Space.
"4\u{2004}", // Three-Per-Em Space.
"5\u{2005}", // Four-Per-Em Space.
"6\u{2006}", // Six-Per-Em Space.
"7\u{2007}", // Figure Space.
"8\u{2008}", // Punctuation Space.
"9\u{2009}", // Thin Space.
"0a\u{200a}", // Hair Space.
"2f\u{202f}", // Narrow No-Break Space (NNBSP).
"5f\u{205f}", // Medium Mathematical Space.
"3000\u{3000}", // Ideographic Space.
".",
]),
true,
],
];
}
/**
* Test that a database modification is detected.
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_db_modification(): void {
global $DB;
$DB->set_field('user', 'confirmed', 1, ['id' => -1]);
// Let's convert the user warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_USER_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Warning: unexpected database modification');
phpunit_util::reset_all_data(true);
}
/**
* Test that a $CFG modification is detected.
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_cfg_modification(): void {
global $CFG;
$CFG->xx = 'yy';
unset($CFG->admin);
$CFG->rolesactive = 0;
// Let's convert the user warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_USER_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
$this->expectExceptionMessageMatches('/rolesactive.*xx value.*removal.*admin/ms'); // 3 messages matched.
phpunit_util::reset_all_data(true);
}
/**
* Test that a $USER modification is detected.
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_user_modification(): void {
global $USER;
$USER->id = 10;
// Let's convert the user warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_USER_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Warning: unexpected change of $USER');
phpunit_util::reset_all_data(true);
}
/**
* Test that a $COURSE modification is detected.
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_course_modification(): void {
global $COURSE;
$COURSE->id = 10;
// Let's convert the user warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_USER_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Warning: unexpected change of $COURSE');
phpunit_util::reset_all_data(true);
}
/**
* Test that all modifications are detected together.
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_all_modifications(): void {
global $DB, $CFG, $USER, $COURSE;
$DB->set_field('user', 'confirmed', 1, ['id' => -1]);
$CFG->xx = 'yy';
unset($CFG->admin);
$CFG->rolesactive = 0;
$USER->id = 10;
$COURSE->id = 10;
// Let's convert the user warnings into an assert-able exception.
set_error_handler(
static function ($errno, $errstr) {
restore_error_handler();
throw new \Exception($errstr, $errno);
},
E_USER_WARNING // Or any other specific E_ that we want to assert.
);
$this->expectException(\Exception::class);
$this->expectExceptionMessageMatches('/resetting.*rolesactive.*new.*removal.*USER.*COURSE/ms'); // 6 messages matched.
phpunit_util::reset_all_data(true);
}
/**
* Test that an open transaction are managed ok by the reset code (silently rolled back).
*
* @runInSeparateProcess
* @covers \phpunit_util
*/
public function test_transaction_problem(): void {
global $DB, $COURSE;
$originalname = $DB->get_field('course', 'fullname', ['id' => $COURSE->id]); // Normally "PHPUnit test site".
$changedname = 'Ongoing transaction test site';
// Start a transaction and make some database changes.
$DB->start_delegated_transaction();
$DB->set_field('course', 'fullname', $changedname, ['id' => $COURSE->id]);
// Assert that the transaction is open and the changes were made.
$this->assertTrue($DB->is_transaction_started());
$this->assertEquals($changedname, $DB->get_field('course', 'fullname', ['id' => $COURSE->id]));
phpunit_util::reset_all_data(false); // We don't want to detect/warn on database changes for this test.
// Assert that the transaction is now closed and the changes were rolled back.
$this->assertFalse($DB->is_transaction_started());
$this->assertEquals($originalname, $DB->get_field('course', 'fullname', ['id' => $COURSE->id]));
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_phpunit;
/**
* Fixtures for task tests.
*
* @package core
* @category phpunit
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class adhoc_test_task extends \core\task\adhoc_task {
/**
* Execute.
*/
public function execute() {
global $USER;
mtrace("Task was run as {$USER->id}");
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
/**
* Fixtures for advanced_testcase tests.
*
* @package core
* @category event
* @copyright 2024 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* Event to test that \advanced_testcase::assertEventContextNotUsed() passes ok when no context is used.
*/
class context_used_in_event_correct extends \core\event\base {
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->context = \context_system::instance();
}
public function get_url() {
return new \moodle_url('/somepath/somefile.php'); // No context used.
}
public function get_description() {
return 'Description'; // No context used.
}
}
/**
* Event to test that \advanced_testcase::assertEventContextNotUsed() detects context usage on get_url().
*/
class context_used_in_event_get_url extends \core\event\base {
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->context = \context_system::instance();
}
public function get_url() {
return new \moodle_url('/somepath/somefile.php', ['id' => $this->context->instanceid]); // Causes a PHP Warning.
}
}
/**
* Event to test that \advanced_testcase::assertEventContextNotUsed() detects context usage on get_description().
*/
class context_used_in_event_get_description extends \core\event\base {
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->context = \context_system::instance();
}
public function get_description() {
return $this->context->instanceid . " Description"; // Causes a PHP Warning.
}
}
+3
View File
@@ -0,0 +1,3 @@
id,username,email
5,bozka.novakova,bozka@example.com
7,pepa.novak,pepa@example.com
1 id username email
2 5 bozka.novakova bozka@example.com
3 7 pepa.novak pepa@example.com
+3
View File
@@ -0,0 +1,3 @@
username,email
pepa.novak,pepa@example.com
bozka.novakova,bozka@example.com
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
<row>
<value>7</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
</table>
</dataset>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="course">
<column>id</column>
<column>shortname</column>
<column>fullname</column>
<row>
<value>6</value>
<value>101</value>
<value>1-0-1</value>
</row>
<row>
<value>8</value>
<value>202</value>
<value>2-0-2</value>
</row>
</table>
</dataset>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<column>toolate</column>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>username</column>
<column>email</column>
<row>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
<row>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
</table>
</dataset>
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
<row>
<value>7</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
</table>
<table name="course">
<column>id</column>
<column>shortname</column>
<column>fullname</column>
<row>
<value>6</value>
<value>101</value>
<value>1-0-1</value>
</row>
<row>
<value>8</value>
<value>202</value>
<value>2-0-2</value>
</row>
</table>
</dataset>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
<row>
<value>7</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
</table>
<table name="empty1" />
<table name="empty2">
<column>id</column>
<column>shortname</column>
<column>fullname</column>
</table>
<table name="course">
<column>id</column>
<column>shortname</column>
<column>fullname</column>
<row>
<value>6</value>
<value>101</value>
<value>1-0-1</value>
</row>
<row>
<value>8</value>
<value>202</value>
<value>2-0-2</value>
</row>
</table>
</dataset>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<column>toomany</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<nocolumn>id</nocolumn>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<row>
<value>tooearly</value>
</row>
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table noname="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<nodataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</nodataset>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<notable name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</notable>
</dataset>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<novalue>5</novalue>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
<table name="user">
<column>id</column>
<column>username</column>
<column>email</column>
<row>
<value>5</value>
<value>pepa.novak</value>
<value>pepa@example.com</value>
</row>
<row>
<value>7</value>
<value>bozka.novakova</value>
<value>bozka@example.com</value>
</row>
</table>
</dataset>
+924
View File
@@ -0,0 +1,924 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Test phpunit_dataset features.
*
* @package core
* @category tests
* @copyright 2020 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace core;
use advanced_testcase;
use phpunit_dataset;
use org\bovigo\vfs\vfsStream;
/**
* Test phpunit_dataset features.
*
* @coversDefaultClass \phpunit_dataset
*/
class phpunit_dataset_test extends advanced_testcase {
/**
* @covers ::from_files
*/
public function test_from_files(): void {
$ds = new phpunit_dataset();
$files = [
__DIR__ . '/fixtures/sample_dataset.xml',
'user2' => __DIR__ . '/fixtures/sample_dataset.csv',
];
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// Expectations.
$exptables = ['user', 'user2'];
$expcolumns = ['id', 'username', 'email'];
$exprows = [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
];
$ds->from_files($files);
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($exptables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($expcolumns, $dscolumns->getValue($ds)['user']);
$this->assertSame($expcolumns, $dscolumns->getValue($ds)['user2']);
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($exprows, $dsrows->getValue($ds)['user']); // Equals because of stringified integers on load.
$this->assertEquals($exprows, $dsrows->getValue($ds)['user2']); // Equals because of stringified integers on load.
}
/**
* test_from_file() data provider.
*/
public function from_file_provider() {
// Create an unreadable file with vfsStream.
$vfsfile = vfsStream::newFile('unreadable', 0222);
vfsStream::setup('root')->addChild($vfsfile);
return [
'file not found' => [
'fullpath' => '/this/does/not/exist',
'tablename' => 'user',
'exception' => 'from_file, file not found: /this/does/not/exist',
'tables' => [],
'columns' => [],
'rows' => [],
],
'file not readable' => [
'fullpath' => $vfsfile->url(),
'tablename' => 'user',
'exception' => 'from_file, file not readable: ' . $vfsfile->url(),
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong extension' => [
'fullpath' => __DIR__ . '/fixtures/sample_dataset.txt',
'tablename' => 'user',
'exception' => 'from_file, cannot handle files with extension: txt',
'tables' => [],
'columns' => [],
'rows' => [],
],
'csv loads ok' => [
'fullpath' => __DIR__ . '/fixtures/sample_dataset.csv',
'tablename' => 'user',
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'xml loads ok' => [
'fullpath' => __DIR__ . '/fixtures/sample_dataset.xml',
'tablename' => 'user',
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
];
}
/**
* @dataProvider from_file_provider
* @covers ::from_file
*/
public function test_from_file(string $fullpath, string $tablename, ?string $exception,
array $tables, array $columns, array $rows): void {
$ds = new phpunit_dataset();
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_file($fullpath, $tablename);
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($tables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($columns, $dscolumns->getValue($ds));
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
}
/**
* test_from_string() data provider.
*/
public function from_string_provider() {
return [
'wrong type' => [
'content' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.xml'),
'type' => 'txt',
'tablename' => 'user',
'exception' => 'from_string, cannot handle contents of type: txt',
'tables' => [],
'columns' => [],
'rows' => [],
],
'missing cvs table' => [
'content' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.csv'),
'type' => 'csv',
'tablename' => '',
'exception' => 'from_string, contents of type "cvs" require a $table to be passed, none found',
'tables' => [],
'columns' => [],
'rows' => [],
],
'csv loads ok' => [
'fullpath' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.csv'),
'type' => 'csv',
'tablename' => 'user',
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'xml loads ok' => [
'fullpath' => file_get_contents(__DIR__ . '/fixtures/sample_dataset.xml'),
'type' => 'xml',
'tablename' => 'user',
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
];
}
/**
* @dataProvider from_string_provider
* @covers ::from_string
*/
public function test_from_string(string $content, string $type, string $tablename, ?string $exception,
array $tables, array $columns, array $rows): void {
$ds = new phpunit_dataset();
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_string($content, $type, $tablename);
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($tables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($columns, $dscolumns->getValue($ds));
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
}
/**
* test_from_array() data provider.
*/
public function from_array_provider() {
return [
'repeated array table many structures' => [
'structure' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
],
'exception' => 'from_array, table already added to dataset: user',
'tables' => [],
'columns' => [],
'rows' => [],
'repeated' => true, // To force the table already exists exception.
],
'wrong number of columns' => [
'structure' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6],
],
],
'exception' => 'from_array, number of columns must match number of values, found: 2 vs 1',
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong not matching names of columns' => [
'structure' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6, 'noname' => 'Jane'],
],
],
'exception' => 'from_array, columns in all elements must match first one, found: id, noname',
'tables' => [],
'columns' => [],
'rows' => [],
],
'ok non-associative format' => [
'structure' => [
'user' => [
['id', 'name'],
[5, 'John'],
[6, 'Jane'],
],
],
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'name'],
],
'rows' => ['user' =>
[
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
],
],
'ok associative format' => [
'structure' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
],
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'name'],
],
'rows' => ['user' =>
[
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
],
],
'ok multiple' => [
'structure' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
'course' => [
['id' => 7, 'name' => '101'],
['id' => 8, 'name' => '102'],
],
],
'exception' => null,
'tables' => ['user', 'course'],
'columns' => [
'user' => ['id', 'name'],
'course' => ['id', 'name'],
],
'rows' => [
'user' => [
['id' => 5, 'name' => 'John'],
['id' => 6, 'name' => 'Jane'],
],
'course' => [
['id' => 7, 'name' => '101'],
['id' => 8, 'name' => '102'],
],
],
],
];
}
/**
* @dataProvider from_array_provider
* @covers ::from_array
*/
public function test_from_array(array $structure, ?string $exception,
array $tables, array $columns, array $rows, ?bool $repeated = false): void {
$ds = new phpunit_dataset();
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_array($structure);
if ($repeated) {
$ds->from_array($structure);
}
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($tables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($columns, $dscolumns->getValue($ds));
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
}
/**
* test_load_csv() data provider.
*/
public function load_csv_provider() {
return [
'repeated csv table many files' => [
'files' => [
__DIR__ . '/fixtures/sample_dataset.xml',
'user' => __DIR__ . '/fixtures/sample_dataset.csv',
],
'exception' => 'csv_dataset_format, table already added to dataset: user',
'tables' => [],
'columns' => [],
'rows' => [],
],
'ok one csv file' => [
'files' => [
'user' => __DIR__ . '/fixtures/sample_dataset.csv',
],
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'ok multiple csv files' => [
'files' => [
'user1' => __DIR__ . '/fixtures/sample_dataset.csv',
'user2' => __DIR__ . '/fixtures/sample_dataset.csv',
],
'exception' => null,
'tables' => ['user1', 'user2'],
'columns' => [
'user1' => ['id', 'username', 'email'],
'user2' => ['id', 'username', 'email'],
],
'rows' => [
'user1' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'user2' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
],
],
];
}
/**
* @dataProvider load_csv_provider
* @covers ::load_csv
*/
public function test_load_csv(array $files, ?string $exception,
array $tables, array $columns, array $rows): void {
$ds = new phpunit_dataset();
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_files($files);
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($tables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($columns, $dscolumns->getValue($ds));
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
}
/**
* test_load_xml() data provider.
*/
public function load_xml_provider() {
return [
'repeated xml table multiple files' => [
'files' => [
'user' => __DIR__ . '/fixtures/sample_dataset.csv',
__DIR__ . '/fixtures/sample_dataset.xml',
],
'exception' => 'xml_dataset_format, table already added to dataset: user',
'tables' => [],
'columns' => [],
'rows' => [],
],
'repeated xml table one file' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_repeated.xml'],
'exception' => 'xml_dataset_format, table already added to dataset: user',
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong dataset element' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_dataset.xml'],
'exception' => 'xml_dataset_format, main xml element must be "dataset", found: nodataset',
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong table element' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_table.xml'],
'exception' => 'xml_dataset_format, only "table" elements allowed, found: notable',
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong table name attribute' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_attribute.xml'],
'exception' => 'xml_dataset_format, "table" element only allows "name" attribute',
'tables' => [],
'columns' => [],
'rows' => [],
],
'only col and row allowed' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_only_colrow.xml'],
'exception' => 'xml_dataset_format, only "column or "row" elements allowed, found: nocolumn',
'tables' => [],
'columns' => [],
'rows' => [],
],
'wrong value element' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_wrong_value.xml'],
'exception' => 'xml_dataset_format, only "value" elements allowed, found: novalue',
'tables' => [],
'columns' => [],
'rows' => [],
],
'column before row' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_col_before_row.xml'],
'exception' => 'xml_dataset_format, "column" elements always must be before "row" ones',
'tables' => [],
'columns' => [],
'rows' => [],
],
'row after column' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_row_after_col.xml'],
'exception' => 'xml_dataset_format, "row" elements always must be after "column" ones',
'tables' => [],
'columns' => [],
'rows' => [],
],
'number of columns' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_number_of_columns.xml'],
'exception' => 'xml_dataset_format, number of columns must match number of values, found: 4 vs 3',
'tables' => [],
'columns' => [],
'rows' => [],
],
'ok one xml file' => [
'files' => [__DIR__ . '/fixtures/sample_dataset.xml'],
'exception' => null,
'tables' => ['user'],
'columns' => ['user' =>
['id', 'username', 'email']
],
'rows' => ['user' =>
[
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'ok multiple xml files' => [
'files' => [
'user1' => __DIR__ . '/fixtures/sample_dataset.csv',
__DIR__ . '/fixtures/sample_dataset.xml',
],
'exception' => null,
'tables' => ['user1', 'user'],
'columns' => [
'user1' => ['id', 'username', 'email'],
'user' => ['id', 'username', 'email'],
],
'rows' => [
'user1' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
],
],
'ok many tables in one xml' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
'exception' => null,
'tables' => ['user', 'course'],
'columns' => [
'user' => ['id', 'username', 'email'],
'course' => ['id', 'shortname', 'fullname'],
],
'rows' => [
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'course' => [
['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
];
}
/**
* @dataProvider load_xml_provider
* @covers ::load_xml
*/
public function test_load_xml(array $files, ?string $exception,
array $tables, array $columns, array $rows): void {
$ds = new phpunit_dataset();
// We need public properties to check the basis.
$dsref = new \ReflectionClass($ds);
$dstables = $dsref->getProperty('tables');
$dscolumns = $dsref->getProperty('columns');
$dsrows = $dsref->getProperty('rows');
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_files($files);
$this->assertIsArray($dstables->getValue($ds));
$this->assertSame($tables, $dstables->getValue($ds));
$this->assertIsArray($dscolumns->getValue($ds));
$this->assertSame($columns, $dscolumns->getValue($ds));
$this->assertIsArray($dsrows->getValue($ds));
$this->assertEquals($rows, $dsrows->getValue($ds)); // Equals because of stringified integers on load.
}
/**
* test_to_database() data provider.
*/
public function to_database_provider() {
return [
'wrong table requested' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_insert.xml'],
'filter' => ['wrongtable'],
'exception' => 'dataset_to_database, table is not in the dataset: wrongtable',
'columns' => [],
'rows' => [],
],
'one table insert' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_insert.xml'],
'filter' => [],
'exception' => null,
'columns' => [
'user' => ['username', 'email'],
],
'rows' => ['user' =>
[
(object)['username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
(object)['username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'one table import' => [
'files' => [__DIR__ . '/fixtures/sample_dataset.xml'],
'filter' => [],
'exception' => null,
'columns' => [
'user' => ['id', 'username', 'email'],
],
'rows' => ['user' =>
[
(object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
(object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
]
],
],
'multiple table many files import' => [
'files' => [
__DIR__ . '/fixtures/sample_dataset.xml',
__DIR__ . '/fixtures/sample_dataset2.xml',
],
'filter' => [],
'exception' => null,
'columns' => [
'user' => ['id', 'username', 'email'],
'course' => ['id', 'shortname', 'fullname'],
],
'rows' => [
'user' => [
(object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
(object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'course' => [
(object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
(object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
'multiple table one file import' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
'filter' => [],
'exception' => null,
'columns' => [
'user' => ['id', 'username', 'email'],
'course' => ['id', 'shortname', 'fullname'],
],
'rows' => [
'user' => [
(object)['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
(object)['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'course' => [
(object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
(object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
'filtering tables' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
'filter' => ['course'],
'exception' => null,
'columns' => [
'user' => ['id', 'username', 'email'],
'course' => ['id', 'shortname', 'fullname'],
],
'rows' => [
'user' => [], // Table user is being excluded via filter, expect no rows sent to database.
'course' => [
(object)['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
(object)['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
];
}
/**
* @dataProvider to_database_provider
* @covers ::to_database
*/
public function test_to_database(array $files, ?array $filter, ?string $exception, array $columns, array $rows): void {
global $DB;
$this->resetAfterTest();
// Grab the status before loading to database.
$before = [];
foreach ($columns as $tablename => $tablecolumns) {
if (!isset($before[$tablename])) {
$before[$tablename] = [];
}
$before[$tablename] = $DB->get_records($tablename, null, '', implode(', ', $tablecolumns));
}
$ds = new phpunit_dataset();
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_files($files);
$ds->to_database($filter);
// Grab the status after loading to database.
$after = [];
foreach ($columns as $tablename => $tablecolumns) {
if (!isset($after[$tablename])) {
$after[$tablename] = [];
}
$sortandcol = implode(', ', $tablecolumns);
$after[$tablename] = $DB->get_records($tablename, null, $sortandcol, $sortandcol);
}
// Differences must match the expectations.
foreach ($rows as $tablename => $expectedrows) {
$changes = array_udiff($after[$tablename], $before[$tablename], function ($b, $a) {
if ((array)$b > (array)$a) {
return 1;
} else if ((array)$b < (array)$a) {
return -1;
} else {
return 0;
}
});
$this->assertEquals(array_values($expectedrows), array_values($changes));
}
}
/**
* test_get_rows() data provider.
*/
public function get_rows_provider() {
return [
'wrong table requested' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many.xml'],
'filter' => ['wrongtable'],
'exception' => 'dataset_get_rows, table is not in the dataset: wrongtable',
'rows' => [],
],
'ok get rows from empty tables' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
'filter' => ['empty1', 'empty2'],
'exception' => null,
'rows' => [
'empty1' => [],
'empty2' => [],
],
],
'ok get rows from one table' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
'filter' => ['user'],
'exception' => null,
'rows' => [
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
],
],
'ok get rows from two tables' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
'filter' => ['user', 'course'],
'exception' => null,
'rows' => [
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'course' => [
['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
'ok get rows from three tables' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
'filter' => ['user', 'empty1', 'course'],
'exception' => null,
'rows' => [
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'empty1' => [],
'course' => [
['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
'ok no filter returns all' => [
'files' => [__DIR__ . '/fixtures/sample_dataset_many_with_empty.xml'],
'filter' => [],
'exception' => null,
'rows' => [
'user' => [
['id' => 5, 'username' => 'bozka.novakova', 'email' => 'bozka@example.com'],
['id' => 7, 'username' => 'pepa.novak', 'email' => 'pepa@example.com'],
],
'empty1' => [],
'empty2' => [],
'course' => [
['id' => 6, 'shortname' => '101', 'fullname' => '1-0-1'],
['id' => 8, 'shortname' => '202', 'fullname' => '2-0-2'],
],
],
],
];
}
/**
* @dataProvider get_rows_provider
* @covers ::get_rows
*/
public function test_get_rows(array $files, array $filter, ?string $exception, array $rows): void {
$ds = new phpunit_dataset();
// We are expecting an exception.
if (!empty($exception)) {
$this->expectException('coding_exception');
$this->expectExceptionMessage($exception);
}
$ds->from_files($files);
$this->assertEquals($rows, $ds->get_rows($filter));
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
/**
* Test util extra features.
*
* @package core
* @category test
* @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class util_test extends \advanced_testcase {
/**
* @dataProvider set_table_modified_by_sql_provider
*/
public function test_set_table_modified_by_sql($sql, $expectations): void {
\phpunit_util::reset_updated_table_list();
\phpunit_util::set_table_modified_by_sql($sql);
foreach ($expectations as $table => $present) {
$this->assertEquals($present, !empty(\phpunit_util::$tableupdated[$table]));
}
}
public function set_table_modified_by_sql_provider() {
global $DB;
$prefix = $DB->get_prefix();
return array(
'Basic update' => array(
'sql' => "UPDATE {$prefix}user SET username = username || '_test'",
'expectations' => array(
'user' => true,
'course' => false,
),
),
'Basic update with a fieldname sharing the same prefix' => array(
'sql' => "UPDATE {$prefix}user SET {$prefix}username = username || '_test'",
'expectations' => array(
'user' => true,
'course' => false,
),
),
'Basic update with a table which contains the prefix' => array(
'sql' => "UPDATE {$prefix}user{$prefix} SET username = username || '_test'",
'expectations' => array(
"user{$prefix}" => true,
'course' => false,
),
),
'Update table with a numeric name' => array(
'sql' => "UPDATE {$prefix}example42 SET username = username || '_test'",
'expectations' => array(
'example42' => true,
'user' => false,
'course' => false,
),
),
'Drop basic table' => array(
'sql' => "DROP TABLE {$prefix}user",
'expectations' => array(
'user' => true,
'course' => false,
),
),
'Drop table with a numeric name' => array(
'sql' => "DROP TABLE {$prefix}example42",
'expectations' => array(
'example42' => true,
'user' => false,
'course' => false,
),
),
'Insert in table' => array(
'sql' => "INSERT INTO {$prefix}user (username,password) VALUES ('moodle', 'test')",
'expectations' => array(
'user' => true,
'course' => false,
),
),
);
}
}