See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Unit tests for core time splitting methods. 19 * 20 * @package core 21 * @category analytics 22 * @copyright 2017 David MonllaĆ³ {@link http://www.davidmonllao.com} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once (__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_seconds.php'); 29 require_once (__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_upcoming_seconds.php'); 30 require_once (__DIR__ . '/../../lib/enrollib.php'); 31 32 /** 33 * Unit tests for core time splitting methods. 34 * 35 * @package core 36 * @category analytics 37 * @copyright 2017 David MonllaĆ³ {@link http://www.davidmonllao.com} 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class core_analytics_time_splittings_testcase extends advanced_testcase { 41 42 /** 43 * setUp 44 * 45 * @return void 46 */ 47 public function setUp() { 48 49 $this->resetAfterTest(true); 50 51 // Generate training data. 52 $params = array( 53 'startdate' => mktime(8, 15, 32, 10, 24, 2015), 54 'enddate' => mktime(12, 12, 31, 10, 24, 2016), 55 ); 56 $this->course = $this->getDataGenerator()->create_course($params); 57 $this->analysable = new \core_analytics\course($this->course); 58 } 59 60 /** 61 * test_ranges 62 * 63 * @return void 64 */ 65 public function test_valid_ranges() { 66 67 // All core time splitting methods. 68 $timesplittings = array( 69 '\core\analytics\time_splitting\deciles', 70 '\core\analytics\time_splitting\deciles_accum', 71 '\core\analytics\time_splitting\no_splitting', 72 '\core\analytics\time_splitting\quarters', 73 '\core\analytics\time_splitting\quarters_accum', 74 '\core\analytics\time_splitting\single_range', 75 '\core\analytics\time_splitting\upcoming_week', 76 ); 77 78 // Check that defined ranges are valid (tested through validate_ranges). 79 foreach ($timesplittings as $timesplitting) { 80 $instance = new $timesplitting(); 81 $instance->set_analysable($this->analysable); 82 } 83 } 84 85 /** 86 * test_range_dates 87 * 88 * @return void 89 */ 90 public function test_range_dates() { 91 92 $nov2015 = mktime(0, 0, 0, 11, 24, 2015); 93 $aug2016 = mktime(0, 0, 0, 8, 29, 2016); 94 95 // Equal parts. 96 $quarters = new \core\analytics\time_splitting\quarters(); 97 $quarters->set_analysable($this->analysable); 98 $ranges = $quarters->get_all_ranges(); 99 $this->assertCount(4, $ranges); 100 $this->assertCount(4, $quarters->get_training_ranges()); 101 $this->assertCount(4, $quarters->get_distinct_ranges()); 102 103 $this->assertGreaterThan($ranges[0]['start'], $ranges[1]['start']); 104 $this->assertGreaterThan($ranges[0]['end'], $ranges[1]['start']); 105 $this->assertGreaterThan($ranges[0]['end'], $ranges[1]['end']); 106 107 $this->assertGreaterThan($ranges[1]['start'], $ranges[2]['start']); 108 $this->assertGreaterThan($ranges[1]['end'], $ranges[2]['start']); 109 $this->assertGreaterThan($ranges[1]['end'], $ranges[2]['end']); 110 111 $this->assertGreaterThan($ranges[2]['start'], $ranges[3]['start']); 112 $this->assertGreaterThan($ranges[2]['end'], $ranges[3]['end']); 113 $this->assertGreaterThan($ranges[2]['end'], $ranges[3]['start']); 114 115 // First range. 116 $this->assertLessThan($nov2015, $ranges[0]['start']); 117 $this->assertGreaterThan($nov2015, $ranges[0]['end']); 118 119 // Last range. 120 $this->assertLessThan($aug2016, $ranges[3]['start']); 121 $this->assertGreaterThan($aug2016, $ranges[3]['end']); 122 123 // Accumulative. 124 $accum = new \core\analytics\time_splitting\quarters_accum(); 125 $accum->set_analysable($this->analysable); 126 $ranges = $accum->get_all_ranges(); 127 $this->assertCount(4, $ranges); 128 $this->assertCount(4, $accum->get_training_ranges()); 129 $this->assertCount(4, $accum->get_distinct_ranges()); 130 131 $this->assertEquals($ranges[0]['start'], $ranges[1]['start']); 132 $this->assertEquals($ranges[1]['start'], $ranges[2]['start']); 133 $this->assertEquals($ranges[2]['start'], $ranges[3]['start']); 134 135 $this->assertGreaterThan($ranges[0]['end'], $ranges[1]['end']); 136 $this->assertGreaterThan($ranges[1]['end'], $ranges[2]['end']); 137 $this->assertGreaterThan($ranges[2]['end'], $ranges[3]['end']); 138 139 // Present in all ranges. 140 $this->assertLessThan($nov2015, $ranges[0]['start']); 141 $this->assertGreaterThan($nov2015, $ranges[0]['end']); 142 $this->assertGreaterThan($nov2015, $ranges[1]['end']); 143 $this->assertGreaterThan($nov2015, $ranges[2]['end']); 144 $this->assertGreaterThan($nov2015, $ranges[3]['end']); 145 146 // Only in the last range. 147 $this->assertLessThan($aug2016, $ranges[0]['end']); 148 $this->assertLessThan($aug2016, $ranges[1]['end']); 149 $this->assertLessThan($aug2016, $ranges[2]['end']); 150 $this->assertLessThan($aug2016, $ranges[3]['start']); 151 $this->assertGreaterThan($aug2016, $ranges[3]['end']); 152 } 153 154 /** 155 * test_ready_predict 156 * 157 * @return void 158 */ 159 public function test_ready_predict() { 160 161 $quarters = new \core\analytics\time_splitting\quarters(); 162 $nosplitting = new \core\analytics\time_splitting\no_splitting(); 163 $singlerange = new \core\analytics\time_splitting\single_range(); 164 165 $range = array( 166 'start' => time() - 100, 167 'end' => time() - 20, 168 ); 169 $range['time'] = $range['end']; 170 $this->assertTrue($quarters->ready_to_predict($range)); 171 $this->assertTrue($nosplitting->ready_to_predict($range)); 172 173 // Single range time is 0. 174 $range['time'] = 0; 175 $this->assertTrue($singlerange->ready_to_predict($range)); 176 177 $range = array( 178 'start' => time() + 20, 179 'end' => time() + 100, 180 ); 181 $range['time'] = $range['end']; 182 $this->assertFalse($quarters->ready_to_predict($range)); 183 $this->assertTrue($nosplitting->ready_to_predict($range)); 184 185 // Single range time is 0. 186 $range['time'] = 0; 187 $this->assertTrue($singlerange->ready_to_predict($range)); 188 } 189 190 /** 191 * test_periodic 192 * 193 * @return void 194 */ 195 public function test_periodic() { 196 197 // Using a finished course. 198 199 $pastweek = new \core\analytics\time_splitting\past_week(); 200 $pastweek->set_analysable($this->analysable); 201 $this->assertCount(1, $pastweek->get_distinct_ranges()); 202 203 $ranges = $pastweek->get_all_ranges(); 204 $this->assertEquals(52, count($ranges)); 205 $this->assertEquals($this->course->startdate, $ranges[0]['start']); 206 $this->assertNotEquals($this->course->startdate, $ranges[0]['time']); 207 208 // The analysable is finished so all ranges are available for training. 209 $this->assertCount(count($ranges), $pastweek->get_training_ranges()); 210 211 $ranges = $pastweek->get_most_recent_prediction_range(); 212 $range = reset($ranges); 213 $this->assertEquals(51, key($ranges)); 214 215 // We now use an ongoing course not yet ready to generate predictions. 216 217 $threedaysago = new DateTime('-3 days'); 218 $params = array( 219 'startdate' => $threedaysago->getTimestamp(), 220 ); 221 $ongoingcourse = $this->getDataGenerator()->create_course($params); 222 $ongoinganalysable = new \core_analytics\course($ongoingcourse); 223 224 $pastweek = new \core\analytics\time_splitting\past_week(); 225 $pastweek->set_analysable($ongoinganalysable); 226 $ranges = $pastweek->get_all_ranges(); 227 $this->assertEquals(0, count($ranges)); 228 $this->assertCount(0, $pastweek->get_training_ranges()); 229 230 // We now use a ready-to-predict ongoing course. 231 232 $onemonthago = new DateTime('-30 days'); 233 $params = array( 234 'startdate' => $onemonthago->getTimestamp(), 235 ); 236 $ongoingcourse = $this->getDataGenerator()->create_course($params); 237 $ongoinganalysable = new \core_analytics\course($ongoingcourse); 238 239 $pastweek = new \core\analytics\time_splitting\past_week(); 240 $pastweek->set_analysable($ongoinganalysable); 241 $this->assertCount(1, $pastweek->get_distinct_ranges()); 242 243 $ranges = $pastweek->get_all_ranges(); 244 $this->assertEquals(4, count($ranges)); 245 $this->assertCount(4, $pastweek->get_training_ranges()); 246 247 $ranges = $pastweek->get_most_recent_prediction_range(); 248 $range = reset($ranges); 249 $this->assertEquals(3, key($ranges)); 250 $this->assertEquals(time(), $range['time'], '', 1); 251 // 1 second delta for the start just in case a second passes between the set_analysable call 252 // and this checking below. 253 $time = new \DateTime(); 254 $time->sub($pastweek->periodicity()); 255 $this->assertEquals($time->getTimestamp(), $range['start'], '', 1.0); 256 $this->assertEquals(time(), $range['end'], '', 1); 257 258 $starttime = time(); 259 260 $upcomingweek = new \core\analytics\time_splitting\upcoming_week(); 261 $upcomingweek->set_analysable($ongoinganalysable); 262 $this->assertCount(1, $upcomingweek->get_distinct_ranges()); 263 264 $ranges = $upcomingweek->get_all_ranges(); 265 $this->assertEquals(1, count($ranges)); 266 $range = reset($ranges); 267 $this->assertEquals(time(), $range['time'], '', 1); 268 $this->assertEquals(time(), $range['start'], '', 1); 269 $this->assertGreaterThan(time(), $range['end']); 270 271 $this->assertCount(0, $upcomingweek->get_training_ranges()); 272 273 $ranges = $upcomingweek->get_most_recent_prediction_range(); 274 $range = reset($ranges); 275 $this->assertEquals(0, key($ranges)); 276 $this->assertEquals(time(), $range['time'], '', 1); 277 $this->assertEquals(time(), $range['start'], '', 1); 278 $this->assertGreaterThanOrEqual($starttime, $range['time']); 279 $this->assertGreaterThanOrEqual($starttime, $range['start']); 280 $this->assertGreaterThan(time(), $range['end']); 281 282 $this->assertNotEmpty($upcomingweek->get_range_by_index(0)); 283 $this->assertFalse($upcomingweek->get_range_by_index(1)); 284 285 // We now check how new ranges get added as time passes. 286 287 $fewsecsago = new DateTime('-5 seconds'); 288 $params = array( 289 'startdate' => $fewsecsago->getTimestamp(), 290 'enddate' => (new DateTimeImmutable('+1 year'))->getTimestamp(), 291 ); 292 $course = $this->getDataGenerator()->create_course($params); 293 $analysable = new \core_analytics\course($course); 294 295 $seconds = new test_timesplitting_seconds(); 296 $seconds->set_analysable($analysable); 297 298 // Store the ranges we just obtained. 299 $ranges = $seconds->get_all_ranges(); 300 $nranges = count($ranges); 301 $ntrainingranges = count($seconds->get_training_ranges()); 302 $mostrecentrange = $seconds->get_most_recent_prediction_range(); 303 $mostrecentrange = reset($mostrecentrange); 304 305 // We wait for the next range to be added. 306 sleep(1); 307 308 // We set the analysable again so the time ranges are recalculated. 309 $seconds->set_analysable($analysable); 310 311 $newranges = $seconds->get_all_ranges(); 312 $nnewranges = count($newranges); 313 $nnewtrainingranges = $seconds->get_training_ranges(); 314 $newmostrecentrange = $seconds->get_most_recent_prediction_range(); 315 $newmostrecentrange = reset($newmostrecentrange); 316 $this->assertGreaterThan($nranges, $nnewranges); 317 $this->assertGreaterThan($ntrainingranges, $nnewtrainingranges); 318 $this->assertGreaterThan($mostrecentrange['time'], $newmostrecentrange['time']); 319 320 // All the ranges but the last one should return the same values. 321 array_pop($ranges); 322 array_pop($newranges); 323 foreach ($ranges as $key => $range) { 324 $this->assertEquals($newranges[$key]['start'], $range['start']); 325 $this->assertEquals($newranges[$key]['end'], $range['end']); 326 $this->assertEquals($newranges[$key]['time'], $range['time']); 327 } 328 329 // Fake model id, we can use any int, we will need to reference it later. 330 $modelid = 1505347200; 331 332 $upcomingseconds = new test_timesplitting_upcoming_seconds(); 333 $upcomingseconds->set_modelid($modelid); 334 $upcomingseconds->set_analysable($analysable); 335 336 // Store the ranges we just obtained. 337 $ranges = $upcomingseconds->get_all_ranges(); 338 $nranges = count($ranges); 339 $ntrainingranges = count($upcomingseconds->get_training_ranges()); 340 $mostrecentrange = $upcomingseconds->get_most_recent_prediction_range(); 341 $mostrecentrange = reset($mostrecentrange); 342 343 // Mimic the modelfirstanalyses caching in \core_analytics\analysis. 344 $this->mock_cache_first_analysis_caching($modelid, $analysable->get_id(), end($ranges)); 345 346 // We wait for the next range to be added. 347 sleep(1); 348 349 // We set the analysable again so the time ranges are recalculated. 350 $upcomingseconds->set_analysable($analysable); 351 352 $newranges = $upcomingseconds->get_all_ranges(); 353 $nnewranges = count($newranges); 354 $nnewtrainingranges = $upcomingseconds->get_training_ranges(); 355 $newmostrecentrange = $upcomingseconds->get_most_recent_prediction_range(); 356 $newmostrecentrange = reset($newmostrecentrange); 357 $this->assertGreaterThan($nranges, $nnewranges); 358 $this->assertGreaterThan($ntrainingranges, $nnewtrainingranges); 359 $this->assertGreaterThan($mostrecentrange['time'], $newmostrecentrange['time']); 360 361 // All the ranges but the last one should return the same values. 362 array_pop($ranges); 363 array_pop($newranges); 364 foreach ($ranges as $key => $range) { 365 $this->assertEquals($newranges[$key]['start'], $range['start']); 366 $this->assertEquals($newranges[$key]['end'], $range['end']); 367 $this->assertEquals($newranges[$key]['time'], $range['time']); 368 } 369 } 370 371 /** 372 * Mocks core_analytics\analysis caching of the first time analysables were analysed. 373 * 374 * @param int $modelid 375 * @param int $analysableid 376 * @param array $range 377 * @return null 378 */ 379 private function mock_cache_first_analysis_caching($modelid, $analysableid, $range) { 380 $cache = \cache::make('core', 'modelfirstanalyses'); 381 $cache->set($modelid . '_' . $analysableid, $range['time']); 382 } 383 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body