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]);
+ }
+}