diff --git a/lib/statslib.php b/lib/statslib.php index 67a446d7399..eee36ec741f 100644 --- a/lib/statslib.php +++ b/lib/statslib.php @@ -217,12 +217,16 @@ function stats_cron_daily($maxdays=1) { break; } - stats_progress('0'); - // Find out if any logs available for this day $sql = "SELECT 'x' FROM {temp_log1} l"; $logspresent = $DB->get_records_sql($sql, null, 0, 1); + if ($logspresent) { + // Insert blank record to force Query 10 to generate additional row when no logs for + // the site with userid 0 exist. Added for backwards compatibility. + $DB->insert_record('temp_log1', array('userid' => 0, 'course' => SITEID, 'action' => '')); + } + // Calculate the number of active users today $sql = 'SELECT COUNT(DISTINCT u.id) FROM {user} u @@ -230,6 +234,8 @@ function stats_cron_daily($maxdays=1) { WHERE u.deleted = 0'; $dailyactiveusers = $DB->count_records_sql($sql); + stats_progress('0'); + // Process login info first // Note: PostgreSQL doesn't like aliases in HAVING clauses $sql = "INSERT INTO {temp_stats_user_daily} @@ -427,7 +433,6 @@ function stats_cron_daily($maxdays=1) { SUM(CASE WHEN action $viewactionssql THEN 1 ELSE 0 END) AS statsreads, SUM(CASE WHEN action $postactionssql THEN 1 ELSE 0 END) AS statswrites FROM {temp_log1} l - WHERE !(course = 0 AND userid = 0) GROUP BY userid, courseid"; if ($logspresent && !stats_run_query($sql, array_merge($params1, $params2))) { @@ -1640,10 +1645,10 @@ function stats_temp_table_drop() { function stats_temp_table_fill($timestart, $timeend) { global $DB; - $sql = "INSERT INTO {temp_log1} (userid, course, action) + $sql = 'INSERT INTO {temp_log1} (userid, course, action) SELECT userid, course, action FROM {log} - WHERE time >= ? AND time < ?"; + WHERE time >= ? AND time < ?'; $DB->execute($sql, array($timestart, $timeend)); diff --git a/lib/tests/fixtures/statslib-test00.xml b/lib/tests/fixtures/statslib-test00.xml new file mode 100644 index 00000000000..f41e8f34d62 --- /dev/null +++ b/lib/tests/fixtures/statslib-test00.xml @@ -0,0 +1,35 @@ + + + + + time + userid + course + action +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + [course1_id] + [end_no_logs] + 5 + enrolments + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype +
+
diff --git a/lib/tests/fixtures/statslib-test01.xml b/lib/tests/fixtures/statslib-test01.xml new file mode 100644 index 00000000000..bcfc903a56a --- /dev/null +++ b/lib/tests/fixtures/statslib-test01.xml @@ -0,0 +1,116 @@ + + + + + time + userid + course + action + + [start_1] + [guest_id] + [site_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 0 + 0 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 1 + 0 + + + + [site_id] + [end] + [guest_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [guest_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test02.xml b/lib/tests/fixtures/statslib-test02.xml new file mode 100644 index 00000000000..1b1cd351751 --- /dev/null +++ b/lib/tests/fixtures/statslib-test02.xml @@ -0,0 +1,117 @@ + + + + + time + userid + course + action + + [start_1] + [user1_id] + [site_id] + login + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 0 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + logins + + + + [site_id] + [user1_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test03.xml b/lib/tests/fixtures/statslib-test03.xml new file mode 100644 index 00000000000..db0c2a0344e --- /dev/null +++ b/lib/tests/fixtures/statslib-test03.xml @@ -0,0 +1,151 @@ + + + + + time + userid + course + action + + [start_1] + [guest_id] + [site_id] + login + + + [start_2] + [guest_id] + [course1_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 0 + 0 + + + + [course1_id] + [end] + 0 + activity + 1 + 0 + + + + [course1_id] + [end] + [guest_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [guest_id] + 0 + [end] + 1 + 0 + logins + + + + [course1_id] + [guest_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + [guest_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test04.xml b/lib/tests/fixtures/statslib-test04.xml new file mode 100644 index 00000000000..efe9e2ace0d --- /dev/null +++ b/lib/tests/fixtures/statslib-test04.xml @@ -0,0 +1,157 @@ + + + + + time + userid + course + action + + [start_1] + [guest_id] + [site_id] + login + + + [start_2] + [guest_id] + [course1_id] + view + + + [start_3] + [guest_id] + [course1_id] + add post + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 0 + 0 + + + + [course1_id] + [end] + 0 + activity + 1 + 1 + + + + [course1_id] + [end] + [guest_roleid] + activity + 1 + 1 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [guest_id] + 0 + [end] + 1 + 0 + logins + + + + [course1_id] + [guest_id] + 0 + [end] + 1 + 1 + activity + + + + [site_id] + [guest_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test05.xml b/lib/tests/fixtures/statslib-test05.xml new file mode 100644 index 00000000000..b839f74f78b --- /dev/null +++ b/lib/tests/fixtures/statslib-test05.xml @@ -0,0 +1,151 @@ + + + + + time + userid + course + action + + [start_1] + [user1_id] + [site_id] + login + + + [start_2] + [user1_id] + [course1_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 1 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 1 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 0 + 0 + + + + [course1_id] + [end] + 0 + activity + 1 + 0 + + + + [course1_id] + [end] + [student_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + logins + + + + [course1_id] + [user1_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + [user1_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test06.xml b/lib/tests/fixtures/statslib-test06.xml new file mode 100644 index 00000000000..04017102ee5 --- /dev/null +++ b/lib/tests/fixtures/statslib-test06.xml @@ -0,0 +1,157 @@ + + + + + time + userid + course + action + + [start_1] + [user1_id] + [site_id] + login + + + [start_2] + [user1_id] + [course1_id] + view + + + [start_3] + [user1_id] + [course1_id] + add post + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 1 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 1 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 0 + 0 + + + + [course1_id] + [end] + 0 + activity + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + activity + 1 + 1 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + logins + + + + [course1_id] + [user1_id] + 0 + [end] + 1 + 1 + activity + + + + [site_id] + [user1_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test07.xml b/lib/tests/fixtures/statslib-test07.xml new file mode 100644 index 00000000000..86379b8b91d --- /dev/null +++ b/lib/tests/fixtures/statslib-test07.xml @@ -0,0 +1,166 @@ + + + + + time + userid + course + action + + [start_1] + [user2_id] + [site_id] + login + + + [start_2] + [user2_id] + [site_id] + view + + + [start_3] + [user2_id] + [course1_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 1 + 0 + + + + [course1_id] + [end] + 0 + activity + 1 + 0 + + + + [course1_id] + [end] + [guest_roleid] + activity + 1 + 0 + + + + [site_id] + [end] + [frontpage_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user2_id] + 0 + [end] + 1 + 0 + logins + + + + [course1_id] + [user2_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + [user2_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test08.xml b/lib/tests/fixtures/statslib-test08.xml new file mode 100644 index 00000000000..62a90c91bf1 --- /dev/null +++ b/lib/tests/fixtures/statslib-test08.xml @@ -0,0 +1,132 @@ + + + + + time + userid + course + action + + [start_1] + [user1_id] + [site_id] + login + + + [start_2] + [user1_id] + [site_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 1 + 0 + + + + [site_id] + [end] + [frontpage_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + logins + + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test09.xml b/lib/tests/fixtures/statslib-test09.xml new file mode 100644 index 00000000000..efe77b691d5 --- /dev/null +++ b/lib/tests/fixtures/statslib-test09.xml @@ -0,0 +1,129 @@ + + + + + time + userid + course + action + + [start_0] + [user1_id] + [site_id] + login + + + [start_1] + [user1_id] + [site_id] + login + + + [start_4] + [user1_id] + [site_id] + login + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 1 + 1 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + [frontpage_roleid] + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [user1_id] + 0 + [end] + 1 + 0 + logins + + + + [site_id] + [user1_id] + 0 + [end] + 0 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/fixtures/statslib-test10.xml b/lib/tests/fixtures/statslib-test10.xml new file mode 100644 index 00000000000..83d7fcc2c1f --- /dev/null +++ b/lib/tests/fixtures/statslib-test10.xml @@ -0,0 +1,107 @@ + + + + + time + userid + course + action + + [start_1] + [guest_id] + [site_id] + view + +
+ + courseid + timeend + roleid + stattype + stat1 + stat2 + + + [site_id] + [end] + 0 + logins + 0 + 0 + + + + [course1_id] + [end] + [student_roleid] + enrolments + 1 + 0 + + + + [course1_id] + [end] + 0 + enrolments + 1 + 0 + + + + [site_id] + [end] + 0 + enrolments + 4 + 1 + + + + [site_id] + [end] + 0 + activity + 1 + 0 + + + + [site_id] + [end] + [guest_roleid] + activity + 1 + 0 + +
+ + courseid + userid + roleid + timeend + statsreads + statswrites + stattype + + + [site_id] + [guest_id] + 0 + [end] + 1 + 0 + activity + + + + [site_id] + 0 + 0 + [end] + 0 + 0 + activity + +
+
diff --git a/lib/tests/statslib_test.php b/lib/tests/statslib_test.php new file mode 100644 index 00000000000..6c02d25999d --- /dev/null +++ b/lib/tests/statslib_test.php @@ -0,0 +1,581 @@ +. + +/** + * Tests for ../statslib.php + * + * @package core + * @subpackage stats + * @copyright 2012 Tyler Bannister + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/statslib.php'); + +/** + * Test functions that affect daily stats + */ +class statslib_daily_testcase extends advanced_testcase { + /** The student role ID **/ + const STID = 5; + + /** The day to use for testing **/ + const DAY = 1272700810; // 1272758400 + + /** @var array The list of temporary tables created for the statistic calculations **/ + protected $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily'); + + /** @var array The replacements to be used when loading XML files **/ + protected $replacements = null; + + /** + * Set up the database for tests + * + * This function is needed so that daily_log_provider has the before-test set up from setUp() + */ + public function setUpDB() { + global $DB; + + if ($DB->record_exists('user', array('username' => 'user1'))) { + return; + } + + $datagen = self::getDataGenerator(); + + $user1 = $datagen->create_user(array('username'=>'user1')); + $user2 = $datagen->create_user(array('username'=>'user2')); + + $course1 = $datagen->create_course(array('shortname'=>'course1')); + + $success = enrol_try_internal_enrol($course1->id, $user1->id, 5); + + if (! $success) { + trigger_error('User enrollment failed', E_USER_ERROR); + } + + $context = context_system::instance(); + role_assign(self::STID, $user2->id, $context->id); + + $this->generate_replacement_list(); + } + + /** + * Setup function + * - Allow changes to CFG->debug for testing purposes. + */ + protected function setUp() { + global $CFG; + parent::setUp(); + + // Settings to force statistic to run during testing + $CFG->timezone = 99; + $CFG->statsfirstrun = 'all'; + $CFG->statslastdaily = 0; + $CFG->statslastexecution = 0; + $CFG->statsruntimestarthour = date('H'); + $CFG->statsruntimestartminute = 0; + + $this->setUpDB(); + + $this->resetAfterTest(true); + } + + /** + * Function to setup database. + * + * @param array $dataset An array of tables including the log table. + */ + protected function prepare_db($dataset, $tables) { + global $DB; + + foreach ($tables as $tablename) { + $DB->delete_records($tablename); + + foreach ($dataset as $name => $table) { + + if ($tablename == $name) { + + $rows = $table->getRowCount(); + + for ($i = 0; $i < $rows; $i++) { + $row = $table->getRow($i); + + $DB->insert_record($tablename, $row, false, true); + } + } + } + } + } + + /** + * Load dataset from XML file + * + * @param string $file The name of the file to load + */ + protected function generate_replacement_list() { + global $CFG, $DB; + + if ($this->replacements !== null) { + return; + } + + $guest = $DB->get_record('user', array('id' => $CFG->siteguest)); + $user1 = $DB->get_record('user', array('username' => 'user1')); + $user2 = $DB->get_record('user', array('username' => 'user2')); + + if (($guest === false) || ($user1 === false) || ($user2 === false)) { + trigger_error('User setup incomplete', E_USER_ERROR); + } + + $site = $DB->get_record('course', array('id' => SITEID)); + $course1 = $DB->get_record('course', array('shortname' => 'course1')); + + if (($site === false) || ($course1 === false)) { + trigger_error('Course setup incomplete', E_USER_ERROR); + } + + $start = stats_get_base_daily(self::DAY + 3600); + $startnolog = stats_get_base_daily(stats_get_start_from('daily')); + $gr = get_guest_role(); + + $this->replacements = array( + // Start and end times + '[start_0]' => $start - 14410, // 4 hours before + '[start_1]' => $start + 14410, // 4 hours after + '[start_2]' => $start + 14420, + '[start_3]' => $start + 14430, + '[start_4]' => $start + 100800, // 28 hours after + '[end]' => stats_get_next_day_start($start), + '[end_no_logs]' => stats_get_next_day_start($startnolog), + + // User ids + '[guest_id]' => $guest->id, + '[user1_id]' => $user1->id, + '[user2_id]' => $user2->id, + + // Course ids + '[course1_id]' => $course1->id, + '[site_id]' => SITEID, + + // Role ids + '[frontpage_roleid]' => (int) $CFG->defaultfrontpageroleid, + '[guest_roleid]' => $gr->id, + '[student_roleid]' => self::STID, + ); + } + + /** + * Load dataset from XML file + * + * @param string $file The name of the file to load + */ + protected function load_xml_data_file($file) { + static $replacements = null; + + $raw = $this->createXMLDataSet($file); + $clean = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($raw); + + foreach ($this->replacements as $placeholder => $value) { + $clean->addFullReplacement($placeholder, $value); + } + + $logs = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($clean); + $logs->addIncludeTables(array('log')); + + $stats = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($clean); + $stats->addIncludeTables(array('stats_daily', 'stats_user_daily')); + + return array($logs, $stats); + } + + /** + * Provides the log data for test_statslib_cron_daily + */ + public function daily_log_provider() { + global $CFG, $DB; + + $this->setUpDB(); + + $tests = array('00', '01', '02', '03', '04', '05', '06', '07', '08'); + + $dataset = array(); + + foreach ($tests as $test) { + $dataset[] = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test{$test}.xml"); + } + + return $dataset; + } + + /** + * Compare the expected stats to those in the database. + * + * @param array $stats An array of arrays of arrays of both types of stats + */ + protected function verify_stats($expected) { + global $DB; + + // Note: We can not use $this->assertDataSetEqual($expected, $actual) because there's no + // $this->getConnection() in advanced_testcase. + + foreach ($expected as $type => $table) { + $records = $DB->get_records($type); + + $rows = $table->getRowCount(); + + $this->assertEquals($rows, sizeof($records), + 'Incorrect number of results returned for '. $type); + + for ($i = 0; $i < $rows; $i++) { + $row = $table->getRow($i); + $found = 0; + + foreach ($records as $key => $record) { + $record = (array) $record; + unset($record['id']); + $diff = array_merge(array_diff_assoc($row, $record), + array_diff_assoc($record, $row)); + + if (empty($diff)) { + $found = $key; + break; + } + } + $this->assertGreaterThan(0, $found, 'Expected log '. var_export($row, true) + ." was not found in $type ". var_export($records, true)); + unset($records[$found]); + } + } + } + + /** + * Test progress output when debug is on + */ + public function test_statslib_progress_debug() { + global $CFG; + + $CFG->debug = DEBUG_ALL; + $this->expectOutputString('1:0 '); + stats_progress('init'); + stats_progress('1'); + } + + /** + * Test progress output when debug is off + */ + public function test_statslib_progress_no_debug() { + global $CFG; + + $CFG->debug = DEBUG_NONE; + $this->expectOutputString('.'); + stats_progress('init'); + stats_progress('1'); + } + + /** + * Test the function that gets the start date from the config + */ + public function test_statslib_get_start_from() { + global $CFG, $DB; + + $dataset = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test01.xml"); + + $time = time(); + $DB->delete_records('log'); + + $CFG->statsfirstrun = 'all'; + // Allow 1 second difference in case we cross a second boundary. + $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (3 * 24 * 3600)), 'All start time'); + + $this->prepare_db($dataset[0], array('log')); + $records = $DB->get_records('log'); + + $this->assertEquals(self::DAY, stats_get_start_from('daily'), 'Log entry start'); + + $CFG->statsfirstrun = 'none'; + $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (3 * 24 * 3600)), 'None start time'); + + $CFG->statsfirstrun = 14515200; + $this->assertLessThanOrEqual(1, stats_get_start_from('daily') - ($time - (14515200)), 'Specified start time'); + + $this->prepare_db($dataset[1], array('stats_daily')); + $this->assertEquals(self::DAY - 14410 + (24 * 3600), stats_get_start_from('daily'), 'Daily stats start time'); + } + + /** + * Test the function that calculates the start of the day + */ + public function test_statslib_get_base_daily() { + global $CFG; + + $CFG->timezone = 0; + $this->assertEquals(1272672000, stats_get_base_daily(1272686410)); + $CFG->timezone = 5; + $this->assertEquals(1272654000, stats_get_base_daily(1272686410)); + } + + /** + * Test the function that gets the start of the next day + */ + public function test_statslib_get_next_day_start() { + global $CFG; + + $CFG->timezone = 0; + $this->assertEquals(1272758400, stats_get_next_day_start(1272686410)); + } + + /** + * Test the function that gets the action names + * + * Note: The function results depend on installed modules. The hard coded lists are the + * defaults for a new Moodle 2.3 install. + */ + public function test_statslib_get_action_names() { + $basepostactions = array ( + 0 => 'add', + 1 => 'delete', + 2 => 'edit', + 3 => 'add mod', + 4 => 'delete mod', + 5 => 'edit sectionenrol', + 6 => 'loginas', + 7 => 'new', + 8 => 'unenrol', + 9 => 'update', + 10 => 'update mod', + 11 => 'upload', + 12 => 'submit', + 13 => 'submit for grading', + 14 => 'talk', + 15 => 'choose', + 16 => 'choose again', + 17 => 'record delete', + 18 => 'add discussion', + 19 => 'add post', + 20 => 'delete discussion', + 21 => 'delete post', + 22 => 'move discussion', + 23 => 'prune post', + 24 => 'update post', + 25 => 'add category', + 26 => 'add entry', + 27 => 'approve entry', + 28 => 'delete category', + 29 => 'delete entry', + 30 => 'edit category', + 31 => 'update entry', + 32 => 'end', + 33 => 'start', + 34 => 'attempt', + 35 => 'close attempt', + 36 => 'preview', + 37 => 'editquestions', + 38 => 'delete attempt', + 39 => 'manualgrade', + ); + + $baseviewactions = array ( + 0 => 'view', + 1 => 'view all', + 2 => 'history', + 3 => 'view submission', + 4 => 'view feedback', + 5 => 'print', + 6 => 'report', + 7 => 'view discussion', + 8 => 'search', + 9 => 'forum', + 10 => 'forums', + 11 => 'subscribers', + 12 => 'view forum', + 13 => 'view entry', + 14 => 'review', + 15 => 'pre-view', + 16 => 'download', + 17 => 'view form', + 18 => 'view graph', + 19 => 'view report', + ); + + $postactions = stats_get_action_names('post'); + + foreach ($basepostactions as $action) { + $this->assertContains($action, $postactions); + } + + $viewactions = stats_get_action_names('view'); + + foreach ($baseviewactions as $action) { + $this->assertContains($action, $viewactions); + } + } + + /** + * Test the temporary table creation and deletion. + */ + public function test_statslib_temp_table_create_and_drop() { + global $DB; + + foreach ($this->tables as $table) { + $this->assertFalse($DB->get_manager()->table_exists($table)); + } + + stats_temp_table_create(); + + foreach ($this->tables as $table) { + $this->assertTrue($DB->get_manager()->table_exists($table)); + } + + stats_temp_table_drop(); + + foreach ($this->tables as $table) { + $this->assertFalse($DB->get_manager()->table_exists($table)); + } + } + + /** + * Test the temporary table creation and deletion. + * + * @depends test_statslib_temp_table_create_and_drop + */ + public function test_statslib_temp_table_fill() { + global $DB; + + $dataset = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test09.xml"); + + $this->prepare_db($dataset[0], array('log')); + + stats_temp_table_create(); + stats_temp_table_fill(1272686410, 1272758400); + + $this->assertEquals(1, $DB->count_records('temp_log1')); + $this->assertEquals(1, $DB->count_records('temp_log2')); + + stats_temp_table_drop(); + } + + /** + * Test the temporary table creation and deletion. + * + * @depends test_statslib_temp_table_create_and_drop + */ + public function test_statslib_temp_table_setup() { + global $DB; + + $logs = array(); + $this->prepare_db($logs, array('log')); + + stats_temp_table_create(); + stats_temp_table_setup(); + + $this->assertEquals(1, $DB->count_records('temp_enroled')); + + stats_temp_table_drop(); + } + + /** + * Test the function that clean out the temporary tables. + * + * @depends test_statslib_temp_table_create_and_drop + */ + public function test_statslib_temp_table_clean() { + global $DB; + + $rows = array( + 'temp_log1' => array('id' => 1, 'course' => 1), + 'temp_log2' => array('id' => 1, 'course' => 1), + 'temp_stats_daily' => array('id' => 1, 'courseid' => 1), + 'temp_stats_user_daily' => array('id' => 1, 'courseid' => 1), + ); + + stats_temp_table_create(); + + foreach ($rows as $table => $row) { + $DB->insert_record_raw($table, $row); + $this->assertEquals(1, $DB->count_records($table)); + } + + stats_temp_table_clean(); + + foreach ($rows as $table => $row) { + $this->assertEquals(0, $DB->count_records($table)); + } + + $this->assertEquals(1, $DB->count_records('stats_daily')); + $this->assertEquals(1, $DB->count_records('stats_user_daily')); + + stats_temp_table_drop(); + } + + /** + * Test the daily stats function + * + * @depends test_statslib_get_base_daily + * @depends test_statslib_get_next_day_start + * @depends test_statslib_temp_table_create_and_drop + * @depends test_statslib_temp_table_setup + * @depends test_statslib_temp_table_fill + * @dataProvider daily_log_provider + */ + public function test_statslib_cron_daily($logs, $stats) { + global $CFG; + + $CFG->debug = DEBUG_NONE; + + $this->prepare_db($logs, array('log')); + + // Stats cron daily uses mtrace, turn on buffering to silence output. + ob_start(); + stats_cron_daily(1); + ob_end_clean(); + + $this->verify_stats($stats); + } + + /** + * Test the daily stats function + * @depends test_statslib_get_base_daily + * @depends test_statslib_get_next_day_start + */ + public function test_statslib_cron_daily_no_default_profile_id() { + global $CFG, $DB; + $CFG->defaultfrontpageroleid = 0; + + $course1 = $DB->get_record('course', array('shortname' => 'course1')); + $guestid = $CFG->siteguest; + $start = stats_get_base_daily(1272758400); + $end = stats_get_next_day_start($start); + $fpid = (int) $CFG->defaultfrontpageroleid; + $gr = get_guest_role(); + + $dataset = $this->load_xml_data_file(__DIR__."/fixtures/statslib-test10.xml"); + + $CFG->debug = DEBUG_NONE; + + $this->prepare_db($dataset[0], array('log')); + + // Stats cron daily uses mtrace, turn on buffering to silence output. + ob_start(); + stats_cron_daily($maxdays=1); + ob_end_clean(); + + $this->verify_stats($dataset[1]); + } +}