Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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 the model.
19
 *
20
 * @package   core_analytics
21
 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_analytics;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
30
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
31
require_once(__DIR__ . '/fixtures/test_indicator_fullname.php');
32
require_once(__DIR__ . '/fixtures/test_target_shortname.php');
33
require_once(__DIR__ . '/fixtures/test_static_target_shortname.php');
34
require_once(__DIR__ . '/fixtures/test_target_course_level_shortname.php');
35
require_once(__DIR__ . '/fixtures/test_analysis.php');
36
 
37
/**
38
 * Unit tests for the model.
39
 *
40
 * @package   core_analytics
41
 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
42
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43
 */
44
class model_test extends \advanced_testcase {
45
 
46
    /** @var model Store Model. */
47
    protected $model;
48
 
49
    /** @var \stdClass Store model object. */
50
    protected $modelobj;
51
 
52
    public function setUp(): void {
53
 
54
        $this->setAdminUser();
55
 
56
        $target = \core_analytics\manager::get_target('test_target_shortname');
57
        $indicators = array('test_indicator_max', 'test_indicator_min', 'test_indicator_fullname');
58
        foreach ($indicators as $key => $indicator) {
59
            $indicators[$key] = \core_analytics\manager::get_indicator($indicator);
60
        }
61
 
62
        $this->model = testable_model::create($target, $indicators);
63
        $this->modelobj = $this->model->get_model_obj();
64
    }
65
 
11 efrain 66
    public function test_enable(): void {
1 efrain 67
        $this->resetAfterTest(true);
68
 
69
        $this->assertEquals(0, $this->model->get_model_obj()->enabled);
70
        $this->assertEquals(0, $this->model->get_model_obj()->trained);
71
        $this->assertEquals('', $this->model->get_model_obj()->timesplitting);
72
 
73
        $this->model->enable('\core\analytics\time_splitting\quarters');
74
        $this->assertEquals(1, $this->model->get_model_obj()->enabled);
75
        $this->assertEquals(0, $this->model->get_model_obj()->trained);
76
        $this->assertEquals('\core\analytics\time_splitting\quarters', $this->model->get_model_obj()->timesplitting);
77
    }
78
 
11 efrain 79
    public function test_create(): void {
1 efrain 80
        $this->resetAfterTest(true);
81
 
82
        $target = \core_analytics\manager::get_target('\core_course\analytics\target\course_dropout');
83
        $indicators = array(
84
            \core_analytics\manager::get_indicator('\core\analytics\indicator\any_write_action'),
85
            \core_analytics\manager::get_indicator('\core\analytics\indicator\read_actions')
86
        );
87
        $model = \core_analytics\model::create($target, $indicators);
88
        $this->assertInstanceOf('\core_analytics\model', $model);
89
    }
90
 
91
    /**
92
     * test_delete
93
     */
11 efrain 94
    public function test_delete(): void {
1 efrain 95
        global $DB;
96
 
97
        $this->resetAfterTest(true);
98
        set_config('enabled_stores', 'logstore_standard', 'tool_log');
99
 
100
        $coursepredict1 = $this->getDataGenerator()->create_course(array('visible' => 0));
101
        $coursepredict2 = $this->getDataGenerator()->create_course(array('visible' => 0));
102
        $coursetrain1 = $this->getDataGenerator()->create_course(array('visible' => 1));
103
        $coursetrain2 = $this->getDataGenerator()->create_course(array('visible' => 1));
104
 
105
        $this->model->enable('\core\analytics\time_splitting\single_range');
106
 
107
        $this->model->train();
108
        $this->model->predict();
109
 
110
        // Fake evaluation results record to check that it is actually deleted.
111
        $this->add_fake_log();
112
 
113
        $modeloutputdir = $this->model->get_output_dir(array(), true);
114
        $this->assertTrue(is_dir($modeloutputdir));
115
 
116
        // Generate a prediction action to confirm that it is deleted when there is an important update.
117
        $predictions = $DB->get_records('analytics_predictions');
118
        $prediction = reset($predictions);
119
        $prediction = new \core_analytics\prediction($prediction, array('whatever' => 'not used'));
120
        $prediction->action_executed(\core_analytics\prediction::ACTION_FIXED, $this->model->get_target());
121
 
122
        $this->model->delete();
123
        $this->assertEmpty($DB->count_records('analytics_models', array('id' => $this->modelobj->id)));
124
        $this->assertEmpty($DB->count_records('analytics_models_log', array('modelid' => $this->modelobj->id)));
125
        $this->assertEmpty($DB->count_records('analytics_predictions'));
126
        $this->assertEmpty($DB->count_records('analytics_prediction_actions'));
127
        $this->assertEmpty($DB->count_records('analytics_train_samples'));
128
        $this->assertEmpty($DB->count_records('analytics_predict_samples'));
129
        $this->assertEmpty($DB->count_records('analytics_used_files'));
130
        $this->assertFalse(is_dir($modeloutputdir));
131
 
132
        set_config('enabled_stores', '', 'tool_log');
133
        get_log_manager(true);
134
    }
135
 
136
    /**
137
     * test_clear
138
     */
11 efrain 139
    public function test_clear(): void {
1 efrain 140
        global $DB;
141
 
142
        $this->resetAfterTest(true);
143
        set_config('enabled_stores', 'logstore_standard', 'tool_log');
144
 
145
        $coursepredict1 = $this->getDataGenerator()->create_course(array('visible' => 0));
146
        $coursepredict2 = $this->getDataGenerator()->create_course(array('visible' => 0));
147
        $coursetrain1 = $this->getDataGenerator()->create_course(array('visible' => 1));
148
        $coursetrain2 = $this->getDataGenerator()->create_course(array('visible' => 1));
149
 
150
        $this->model->enable('\core\analytics\time_splitting\single_range');
151
 
152
        $this->model->train();
153
        $this->model->predict();
154
 
155
        // Fake evaluation results record to check that it is actually deleted.
156
        $this->add_fake_log();
157
 
158
        // Generate a prediction action to confirm that it is deleted when there is an important update.
159
        $predictions = $DB->get_records('analytics_predictions');
160
        $prediction = reset($predictions);
161
        $prediction = new \core_analytics\prediction($prediction, array('whatever' => 'not used'));
162
        $prediction->action_executed(\core_analytics\prediction::ACTION_FIXED, $this->model->get_target());
163
 
164
        $modelversionoutputdir = $this->model->get_output_dir();
165
        $this->assertTrue(is_dir($modelversionoutputdir));
166
 
167
        // Update to an empty time splitting method to force model::clear execution.
168
        $this->model->clear();
169
        $this->assertFalse(is_dir($modelversionoutputdir));
170
 
171
        // Check that most of the stuff got deleted.
172
        $this->assertEquals(1, $DB->count_records('analytics_models', array('id' => $this->modelobj->id)));
173
        $this->assertEquals(1, $DB->count_records('analytics_models_log', array('modelid' => $this->modelobj->id)));
174
        $this->assertEmpty($DB->count_records('analytics_predictions'));
175
        $this->assertEmpty($DB->count_records('analytics_prediction_actions'));
176
        $this->assertEmpty($DB->count_records('analytics_train_samples'));
177
        $this->assertEmpty($DB->count_records('analytics_predict_samples'));
178
        $this->assertEmpty($DB->count_records('analytics_used_files'));
179
 
180
        // Check that the model is marked as not trained after clearing (as it is not a static one).
181
        $this->assertEquals(0, $DB->get_field('analytics_models', 'trained', array('id' => $this->modelobj->id)));
182
 
183
        set_config('enabled_stores', '', 'tool_log');
184
        get_log_manager(true);
185
    }
186
 
187
    /**
188
     * Test behaviour of {\core_analytics\model::clear()} for static models.
189
     */
11 efrain 190
    public function test_clear_static(): void {
1 efrain 191
        global $DB;
192
        $this->resetAfterTest();
193
 
194
        $statictarget = new \test_static_target_shortname();
195
        $indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
196
        $model = \core_analytics\model::create($statictarget, $indicators, '\core\analytics\time_splitting\quarters');
197
        $modelobj = $model->get_model_obj();
198
 
199
        // Static models are always considered trained.
200
        $this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
201
 
202
        $model->clear();
203
 
204
        // Check that the model is still marked as trained even after clearing.
205
        $this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
206
    }
207
 
11 efrain 208
    public function test_model_manager(): void {
1 efrain 209
        $this->resetAfterTest(true);
210
 
211
        $this->assertCount(3, $this->model->get_indicators());
212
        $this->assertInstanceOf('\core_analytics\local\target\binary', $this->model->get_target());
213
 
214
        // Using evaluation as the model is not yet enabled.
215
        $this->model->init_analyser(array('evaluation' => true));
216
        $this->assertInstanceOf('\core_analytics\local\analyser\base', $this->model->get_analyser());
217
 
218
        $this->model->enable('\core\analytics\time_splitting\quarters');
219
        $this->assertInstanceOf('\core\analytics\analyser\site_courses', $this->model->get_analyser());
220
    }
221
 
11 efrain 222
    public function test_output_dir(): void {
1 efrain 223
        $this->resetAfterTest(true);
224
 
225
        $dir = make_request_directory();
226
        set_config('modeloutputdir', $dir, 'analytics');
227
 
228
        $modeldir = $dir . DIRECTORY_SEPARATOR . $this->modelobj->id . DIRECTORY_SEPARATOR . $this->modelobj->version;
229
        $this->assertEquals($modeldir, $this->model->get_output_dir());
230
        $this->assertEquals($modeldir . DIRECTORY_SEPARATOR . 'testing', $this->model->get_output_dir(array('testing')));
231
    }
232
 
11 efrain 233
    public function test_unique_id(): void {
1 efrain 234
        global $DB;
235
 
236
        $this->resetAfterTest(true);
237
 
238
        $originaluniqueid = $this->model->get_unique_id();
239
 
240
        // Same id across instances.
241
        $this->model = new testable_model($this->modelobj);
242
        $this->assertEquals($originaluniqueid, $this->model->get_unique_id());
243
 
244
        // We will restore it later.
245
        $originalversion = $this->modelobj->version;
246
 
247
        // Generates a different id if timemodified changes.
248
        $this->modelobj->version = $this->modelobj->version + 10;
249
        $DB->update_record('analytics_models', $this->modelobj);
250
        $this->model = new testable_model($this->modelobj);
251
        $this->assertNotEquals($originaluniqueid, $this->model->get_unique_id());
252
 
253
        // Restore original timemodified to continue testing.
254
        $this->modelobj->version = $originalversion;
255
        $DB->update_record('analytics_models', $this->modelobj);
256
        // Same when updating through an action that changes the model.
257
        $this->model = new testable_model($this->modelobj);
258
 
259
        $this->model->mark_as_trained();
260
        $this->assertEquals($originaluniqueid, $this->model->get_unique_id());
261
 
262
        // Wait for the current timestamp to change.
263
        $this->waitForSecond();
264
        $this->model->enable('\core\analytics\time_splitting\deciles');
265
        $this->assertNotEquals($originaluniqueid, $this->model->get_unique_id());
266
        $uniqueid = $this->model->get_unique_id();
267
 
268
        // Wait for the current timestamp to change.
269
        $this->waitForSecond();
270
        $this->model->enable('\core\analytics\time_splitting\quarters');
271
        $this->assertNotEquals($originaluniqueid, $this->model->get_unique_id());
272
        $this->assertNotEquals($uniqueid, $this->model->get_unique_id());
273
    }
274
 
275
    /**
276
     * test_exists
277
     *
278
     * @return void
279
     */
11 efrain 280
    public function test_exists(): void {
1 efrain 281
        $this->resetAfterTest(true);
282
 
283
        $target = \core_analytics\manager::get_target('\core_course\analytics\target\no_teaching');
284
        $this->assertTrue(\core_analytics\model::exists($target));
285
 
286
        foreach (\core_analytics\manager::get_all_models() as $model) {
287
            $model->delete();
288
        }
289
 
290
        $this->assertFalse(\core_analytics\model::exists($target));
291
    }
292
 
293
    /**
294
     * test_model_timelimit
295
     *
296
     * @return null
297
     */
11 efrain 298
    public function test_model_timelimit(): void {
1 efrain 299
        global $DB;
300
 
301
        $this->resetAfterTest(true);
302
 
303
        set_config('modeltimelimit', 2, 'analytics');
304
 
305
        $courses = array();
306
        for ($i = 0; $i < 5; $i++) {
307
            $course = $this->getDataGenerator()->create_course();
308
            $analysable = new \core_analytics\course($course);
309
            $courses[$analysable->get_id()] = $course;
310
        }
311
 
312
        $target = new \test_target_course_level_shortname();
313
        $analyser = new \core\analytics\analyser\courses(1, $target, [], [], []);
314
 
315
        $result = new \core_analytics\local\analysis\result_array(1, false, []);
316
        $analysis = new \test_analysis($analyser, false, $result);
317
 
318
        // Each analysable element takes 0.5 secs minimum (test_analysis), so the max (and likely) number of analysable
319
        // elements that will be processed is 2.
320
        $analysis->run();
321
        $params = array('modelid' => 1, 'action' => 'prediction');
322
        $this->assertLessThanOrEqual(2, $DB->count_records('analytics_used_analysables', $params));
323
 
324
        $analysis->run();
325
        $this->assertLessThanOrEqual(4, $DB->count_records('analytics_used_analysables', $params));
326
 
327
        // Check that analysable elements have been processed following the analyser order
328
        // (course->sortorder here). We can not check this nicely after next get_unlabelled_data round
329
        // because the first analysed element will be analysed again.
330
        $analysedelems = $DB->get_records('analytics_used_analysables', $params, 'timeanalysed ASC');
331
        // Just a default for the first checked element.
332
        $last = (object)['sortorder' => PHP_INT_MAX];
333
        foreach ($analysedelems as $analysed) {
334
            if ($courses[$analysed->analysableid]->sortorder > $last->sortorder) {
335
                $this->fail('Analysable elements have not been analysed sorted by course sortorder.');
336
            }
337
            $last = $courses[$analysed->analysableid];
338
        }
339
 
340
        // No time limit now to process the rest.
341
        set_config('modeltimelimit', 1000, 'analytics');
342
 
343
        $analysis->run();
344
        $this->assertEquals(5, $DB->count_records('analytics_used_analysables', $params));
345
 
346
        // New analysable elements are immediately pulled.
347
        $this->getDataGenerator()->create_course();
348
        $analysis->run();
349
        $this->assertEquals(6, $DB->count_records('analytics_used_analysables', $params));
350
 
351
        // Training and prediction data do not get mixed.
352
        $result = new \core_analytics\local\analysis\result_array(1, false, []);
353
        $analysis = new \test_analysis($analyser, false, $result);
354
        $analysis->run();
355
        $params = array('modelid' => 1, 'action' => 'training');
356
        $this->assertLessThanOrEqual(2, $DB->count_records('analytics_used_analysables', $params));
357
    }
358
 
359
    /**
360
     * Test model_config::get_class_component.
361
     */
11 efrain 362
    public function test_model_config_get_class_component(): void {
1 efrain 363
        $this->resetAfterTest(true);
364
 
365
        $this->assertEquals('core',
366
            \core_analytics\model_config::get_class_component('\\core\\analytics\\indicator\\read_actions'));
367
        $this->assertEquals('core',
368
            \core_analytics\model_config::get_class_component('core\\analytics\\indicator\\read_actions'));
369
        $this->assertEquals('core',
370
            \core_analytics\model_config::get_class_component('\\core_course\\analytics\\indicator\\completion_enabled'));
371
        $this->assertEquals('mod_forum',
372
            \core_analytics\model_config::get_class_component('\\mod_forum\\analytics\\indicator\\cognitive_depth'));
373
 
374
        $this->assertEquals('core', \core_analytics\model_config::get_class_component('\\core_class'));
375
    }
376
 
377
    /**
378
     * Test that import_model import models' configurations.
379
     */
11 efrain 380
    public function test_import_model_config(): void {
1 efrain 381
        $this->resetAfterTest(true);
382
 
383
        $this->model->enable('\\core\\analytics\\time_splitting\\quarters');
384
        $zipfilepath = $this->model->export_model('yeah-config.zip');
385
 
386
        $this->modelobj = $this->model->get_model_obj();
387
 
388
        $importedmodelobj = \core_analytics\model::import_model($zipfilepath)->get_model_obj();
389
 
390
        $this->assertSame($this->modelobj->target, $importedmodelobj->target);
391
        $this->assertSame($this->modelobj->indicators, $importedmodelobj->indicators);
392
        $this->assertSame($this->modelobj->timesplitting, $importedmodelobj->timesplitting);
393
 
394
        $predictionsprocessor = $this->model->get_predictions_processor();
395
        $this->assertSame('\\' . get_class($predictionsprocessor), $importedmodelobj->predictionsprocessor);
396
    }
397
 
398
    /**
399
     * Test can export configuration
400
     */
11 efrain 401
    public function test_can_export_configuration(): void {
1 efrain 402
        $this->resetAfterTest(true);
403
 
404
        // No time splitting method.
405
        $this->assertFalse($this->model->can_export_configuration());
406
 
407
        $this->model->enable('\\core\\analytics\\time_splitting\\quarters');
408
        $this->assertTrue($this->model->can_export_configuration());
409
 
410
        $this->model->update(true, [], false);
411
        $this->assertFalse($this->model->can_export_configuration());
412
 
413
        $statictarget = new \test_static_target_shortname();
414
        $indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
415
        $model = \core_analytics\model::create($statictarget, $indicators, '\\core\\analytics\\time_splitting\\quarters');
416
        $this->assertFalse($model->can_export_configuration());
417
    }
418
 
419
    /**
420
     * Test export_config
421
     */
11 efrain 422
    public function test_export_config(): void {
1 efrain 423
        $this->resetAfterTest(true);
424
 
425
        $this->model->enable('\\core\\analytics\\time_splitting\\quarters');
426
 
427
        $modelconfig = new \core_analytics\model_config($this->model);
428
 
429
        $method = new \ReflectionMethod('\\core_analytics\\model_config', 'export_model_data');
430
 
431
        $modeldata = $method->invoke($modelconfig);
432
 
433
        $this->assertArrayHasKey('core', $modeldata->dependencies);
434
        $this->assertIsFloat($modeldata->dependencies['core']);
435
        $this->assertNotEmpty($modeldata->target);
436
        $this->assertNotEmpty($modeldata->timesplitting);
437
        $this->assertCount(3, $modeldata->indicators);
438
 
439
        $indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
440
        $this->model->update(true, $indicators, false);
441
 
442
        $modeldata = $method->invoke($modelconfig);
443
 
444
        $this->assertCount(1, $modeldata->indicators);
445
    }
446
 
447
    /**
448
     * Test the implementation of {@link \core_analytics\model::inplace_editable_name()}.
449
     */
11 efrain 450
    public function test_inplace_editable_name(): void {
1 efrain 451
        global $PAGE;
452
 
453
        $this->resetAfterTest();
454
 
455
        $output = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
456
 
457
        // Check as a user with permission to edit the name.
458
        $this->setAdminUser();
459
        $ie = $this->model->inplace_editable_name();
460
        $this->assertInstanceOf(\core\output\inplace_editable::class, $ie);
461
        $data = $ie->export_for_template($output);
462
        $this->assertEquals('core_analytics', $data['component']);
463
        $this->assertEquals('modelname', $data['itemtype']);
464
 
465
        // Check as a user without permission to edit the name.
466
        $this->setGuestUser();
467
        $ie = $this->model->inplace_editable_name();
468
        $this->assertInstanceOf(\core\output\inplace_editable::class, $ie);
469
        $data = $ie->export_for_template($output);
470
        $this->assertArrayHasKey('displayvalue', $data);
471
    }
472
 
473
    /**
474
     * Test how the models present themselves in the UI and that they can be renamed.
475
     */
11 efrain 476
    public function test_get_name_and_rename(): void {
1 efrain 477
        global $PAGE;
478
 
479
        $this->resetAfterTest();
480
 
481
        $output = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
482
 
483
        // By default, the model exported for template uses its target's name in the name inplace editable element.
484
        $this->assertEquals($this->model->get_name(), $this->model->get_target()->get_name());
485
        $data = $this->model->export($output);
486
        $this->assertEquals($data->name['displayvalue'], $this->model->get_target()->get_name());
487
        $this->assertEquals($data->name['value'], '');
488
 
489
        // Rename the model.
490
        $this->model->rename('Nějaký pokusný model');
491
        $this->assertEquals($this->model->get_name(), 'Nějaký pokusný model');
492
        $data = $this->model->export($output);
493
        $this->assertEquals($data->name['displayvalue'], 'Nějaký pokusný model');
494
        $this->assertEquals($data->name['value'], 'Nějaký pokusný model');
495
 
496
        // Undo the renaming.
497
        $this->model->rename('');
498
        $this->assertEquals($this->model->get_name(), $this->model->get_target()->get_name());
499
        $data = $this->model->export($output);
500
        $this->assertEquals($data->name['displayvalue'], $this->model->get_target()->get_name());
501
        $this->assertEquals($data->name['value'], '');
502
    }
503
 
504
    /**
505
     * Tests model::get_potential_timesplittings()
506
     */
11 efrain 507
    public function test_potential_timesplittings(): void {
1 efrain 508
        $this->resetAfterTest();
509
 
510
        $this->assertArrayNotHasKey('\core\analytics\time_splitting\no_splitting', $this->model->get_potential_timesplittings());
511
        $this->assertArrayHasKey('\core\analytics\time_splitting\single_range', $this->model->get_potential_timesplittings());
512
        $this->assertArrayHasKey('\core\analytics\time_splitting\quarters', $this->model->get_potential_timesplittings());
513
    }
514
 
515
    /**
516
     * Tests model::get_samples()
517
     *
518
     * @return null
519
     */
11 efrain 520
    public function test_get_samples(): void {
1 efrain 521
        $this->resetAfterTest();
522
 
523
        if (!PHPUNIT_LONGTEST) {
524
            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
525
        }
526
 
527
        // 10000 should be enough to make oracle and mssql fail, if we want pgsql to fail we need around 70000
528
        // users, that is a few minutes just to create the users.
529
        $nusers = 10000;
530
 
531
        $userids = [];
532
        for ($i = 0; $i < $nusers; $i++) {
533
            $user = $this->getDataGenerator()->create_user();
534
            $userids[] = $user->id;
535
        }
536
 
537
        $upcomingactivities = null;
538
        foreach (\core_analytics\manager::get_all_models() as $model) {
539
            if (get_class($model->get_target()) === 'core_user\\analytics\\target\\upcoming_activities_due') {
540
                $upcomingactivities = $model;
541
            }
542
        }
543
 
544
        list($sampleids, $samplesdata) = $upcomingactivities->get_samples($userids);
545
        $this->assertCount($nusers, $sampleids);
546
        $this->assertCount($nusers, $samplesdata);
547
 
548
        $subset = array_slice($userids, 0, 100);
549
        list($sampleids, $samplesdata) = $upcomingactivities->get_samples($subset);
550
        $this->assertCount(100, $sampleids);
551
        $this->assertCount(100, $samplesdata);
552
 
553
        $subset = array_slice($userids, 0, 2);
554
        list($sampleids, $samplesdata) = $upcomingactivities->get_samples($subset);
555
        $this->assertCount(2, $sampleids);
556
        $this->assertCount(2, $samplesdata);
557
 
558
        $subset = array_slice($userids, 0, 1);
559
        list($sampleids, $samplesdata) = $upcomingactivities->get_samples($subset);
560
        $this->assertCount(1, $sampleids);
561
        $this->assertCount(1, $samplesdata);
562
 
563
        // Unexisting, so nothing returned, but still 2 arrays.
564
        list($sampleids, $samplesdata) = $upcomingactivities->get_samples([1231231231231231]);
565
        $this->assertEmpty($sampleids);
566
        $this->assertEmpty($samplesdata);
567
 
568
    }
569
 
570
    /**
571
     * Generates a model log record.
572
     */
573
    private function add_fake_log() {
574
        global $DB, $USER;
575
 
576
        $log = new \stdClass();
577
        $log->modelid = $this->modelobj->id;
578
        $log->version = $this->modelobj->version;
579
        $log->target = $this->modelobj->target;
580
        $log->indicators = $this->modelobj->indicators;
581
        $log->score = 1;
582
        $log->info = json_encode([]);
583
        $log->dir = 'not important';
584
        $log->timecreated = time();
585
        $log->usermodified = $USER->id;
586
        $DB->insert_record('analytics_models_log', $log);
587
    }
588
}
589
 
590
/**
591
 * Testable version to change methods' visibility.
592
 *
593
 * @package   core_analytics
594
 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
595
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
596
 */
597
class testable_model extends \core_analytics\model {
598
 
599
    /**
600
     * init_analyser
601
     *
602
     * @param array $options
603
     * @return void
604
     */
605
    public function init_analyser($options = array()) {
606
        parent::init_analyser($options);
607
    }
608
}