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
 * mod_h5pactivity attempt tests
19
 *
20
 * @package    mod_h5pactivity
21
 * @category   test
22
 * @copyright  2020 Ferran Recio <ferran@moodle.com>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace mod_h5pactivity\local;
27
 
28
use \core_xapi\local\statement;
29
use \core_xapi\local\statement\item;
30
use \core_xapi\local\statement\item_agent;
31
use \core_xapi\local\statement\item_activity;
32
use \core_xapi\local\statement\item_definition;
33
use \core_xapi\local\statement\item_verb;
34
use \core_xapi\local\statement\item_result;
35
use core_xapi\test_helper;
36
use stdClass;
37
 
38
/**
39
 * Attempt tests class for mod_h5pactivity.
40
 *
41
 * @package    mod_h5pactivity
42
 * @category   test
43
 * @copyright  2020 Ferran Recio <ferran@moodle.com>
44
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45
 */
46
class attempt_test extends \advanced_testcase {
47
 
48
    /**
49
     * Generate a scenario to run all tests.
50
     * @return array course_modules, user record, course record
51
     */
52
    private function generate_testing_scenario(): array {
53
        $this->resetAfterTest();
54
        $this->setAdminUser();
55
 
56
        $course = $this->getDataGenerator()->create_course();
57
        $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
58
        $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST);
59
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
60
 
61
        return [$cm, $student, $course];
62
    }
63
 
64
    /**
65
     * Test for create_attempt method.
66
     */
11 efrain 67
    public function test_create_attempt(): void {
1 efrain 68
        global $CFG, $DB;
69
        require_once($CFG->dirroot.'/lib/xapi/tests/helper.php');
70
 
71
        [$cm, $student, $course] = $this->generate_testing_scenario();
72
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
73
 
74
        // Save the current state for this activity for student1 and student2 (before creating the first attempt).
75
        $manager = manager::create_from_coursemodule($cm);
76
        $this->setUser($student2);
77
        test_helper::create_state([
78
            'activity' => item_activity::create_from_id($manager->get_context()->id),
79
            'component' => 'mod_h5pactivity',
80
        ], true);
81
        $this->setUser($student);
82
        test_helper::create_state([
83
            'activity' => item_activity::create_from_id($manager->get_context()->id),
84
            'component' => 'mod_h5pactivity',
85
        ], true);
86
 
87
        $this->assertEquals(2, $DB->count_records('xapi_states'));
88
 
89
        // Create first attempt.
90
        $attempt = attempt::new_attempt($student, $cm);
91
        $this->assertEquals($student->id, $attempt->get_userid());
92
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
93
        $this->assertEquals(1, $attempt->get_attempt());
94
        $this->assertEquals(1, $DB->count_records('xapi_states'));
95
        $this->assertEquals(0, $DB->count_records('xapi_states', ['userid' => $student->id]));
96
 
97
        // Create a second attempt.
98
        $attempt = attempt::new_attempt($student, $cm);
99
        $this->assertEquals($student->id, $attempt->get_userid());
100
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
101
        $this->assertEquals(2, $attempt->get_attempt());
102
    }
103
 
104
    /**
105
     * Test for last_attempt method
106
     */
11 efrain 107
    public function test_last_attempt(): void {
1 efrain 108
 
109
        list($cm, $student) = $this->generate_testing_scenario();
110
 
111
        // Create first attempt.
112
        $attempt = attempt::last_attempt($student, $cm);
113
        $this->assertEquals($student->id, $attempt->get_userid());
114
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
115
        $this->assertEquals(1, $attempt->get_attempt());
116
        $lastid = $attempt->get_id();
117
 
118
        // Get last attempt.
119
        $attempt = attempt::last_attempt($student, $cm);
120
        $this->assertEquals($student->id, $attempt->get_userid());
121
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
122
        $this->assertEquals(1, $attempt->get_attempt());
123
        $this->assertEquals($lastid, $attempt->get_id());
124
 
125
        // Now force a new attempt.
126
        $attempt = attempt::new_attempt($student, $cm);
127
        $this->assertEquals($student->id, $attempt->get_userid());
128
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
129
        $this->assertEquals(2, $attempt->get_attempt());
130
        $lastid = $attempt->get_id();
131
 
132
        // Get last attempt.
133
        $attempt = attempt::last_attempt($student, $cm);
134
        $this->assertEquals($student->id, $attempt->get_userid());
135
        $this->assertEquals($cm->instance, $attempt->get_h5pactivityid());
136
        $this->assertEquals(2, $attempt->get_attempt());
137
        $this->assertEquals($lastid, $attempt->get_id());
138
    }
139
 
140
    /**
141
     * Test saving statements.
142
     *
143
     * @dataProvider save_statement_data
144
     * @param string $subcontent subcontent identifier
145
     * @param bool $hasdefinition generate definition
146
     * @param bool $hasresult generate result
147
     * @param array $results 0 => insert ok, 1 => maxscore, 2 => rawscore, 3 => count
148
     */
11 efrain 149
    public function test_save_statement(string $subcontent, bool $hasdefinition, bool $hasresult, array $results): void {
1 efrain 150
 
151
        list($cm, $student) = $this->generate_testing_scenario();
152
 
153
        $attempt = attempt::new_attempt($student, $cm);
154
        $this->assertEquals(0, $attempt->get_maxscore());
155
        $this->assertEquals(0, $attempt->get_rawscore());
156
        $this->assertEquals(0, $attempt->count_results());
157
        $this->assertEquals(0, $attempt->get_duration());
158
        $this->assertNull($attempt->get_completion());
159
        $this->assertNull($attempt->get_success());
160
        $this->assertFalse($attempt->get_scoreupdated());
161
 
162
        $statement = $this->generate_statement($hasdefinition, $hasresult);
163
        $result = $attempt->save_statement($statement, $subcontent);
164
        $this->assertEquals($results[0], $result);
165
        $this->assertEquals($results[1], $attempt->get_maxscore());
166
        $this->assertEquals($results[2], $attempt->get_rawscore());
167
        $this->assertEquals($results[3], $attempt->count_results());
168
        $this->assertEquals($results[4], $attempt->get_duration());
169
        $this->assertEquals($results[5], $attempt->get_completion());
170
        $this->assertEquals($results[6], $attempt->get_success());
171
        if ($results[5]) {
172
            $this->assertTrue($attempt->get_scoreupdated());
173
        } else {
174
            $this->assertFalse($attempt->get_scoreupdated());
175
        }
176
    }
177
 
178
    /**
179
     * Data provider for data request creation tests.
180
     *
181
     * @return array
182
     */
183
    public function save_statement_data(): array {
184
        return [
185
            'Statement without definition and result' => [
186
                '', false, false, [false, 0, 0, 0, 0, null, null]
187
            ],
188
            'Statement with definition but no result' => [
189
                '', true, false, [false, 0, 0, 0, 0, null, null]
190
            ],
191
            'Statement with result but no definition' => [
192
                '', true, false, [false, 0, 0, 0, 0, null, null]
193
            ],
194
            'Statement subcontent without definition and result' => [
195
                '111-222-333', false, false, [false, 0, 0, 0, 0, null, null]
196
            ],
197
            'Statement subcontent with definition but no result' => [
198
                '111-222-333', true, false, [false, 0, 0, 0, 0, null, null]
199
            ],
200
            'Statement subcontent with result but no definition' => [
201
                '111-222-333', true, false, [false, 0, 0, 0, 0, null, null]
202
            ],
203
            'Statement with definition, result but no subcontent' => [
204
                '', true, true, [true, 2, 2, 1, 25, 1, 1]
205
            ],
206
            'Statement with definition, result and subcontent' => [
207
                '111-222-333', true, true, [true, 0, 0, 1, 0, null, null]
208
            ],
209
        ];
210
    }
211
 
212
    /**
213
     * Test delete results from attempt.
214
     */
11 efrain 215
    public function test_delete_results(): void {
1 efrain 216
 
217
        list($cm, $student) = $this->generate_testing_scenario();
218
 
219
        $attempt = $this->generate_full_attempt($student, $cm);
220
        $attempt->delete_results();
221
        $this->assertEquals(0, $attempt->count_results());
222
    }
223
 
224
    /**
225
     * Test delete attempt.
226
     */
11 efrain 227
    public function test_delete_attempt(): void {
1 efrain 228
        global $DB;
229
 
230
        list($cm, $student) = $this->generate_testing_scenario();
231
 
232
        // Check no previous attempts are created.
233
        $count = $DB->count_records('h5pactivity_attempts');
234
        $this->assertEquals(0, $count);
235
        $count = $DB->count_records('h5pactivity_attempts_results');
236
        $this->assertEquals(0, $count);
237
 
238
        // Generate one attempt.
239
        $attempt1 = $this->generate_full_attempt($student, $cm);
240
        $count = $DB->count_records('h5pactivity_attempts');
241
        $this->assertEquals(1, $count);
242
        $count = $DB->count_records('h5pactivity_attempts_results');
243
        $this->assertEquals(2, $count);
244
 
245
        // Generate a second attempt.
246
        $attempt2 = $this->generate_full_attempt($student, $cm);
247
        $count = $DB->count_records('h5pactivity_attempts');
248
        $this->assertEquals(2, $count);
249
        $count = $DB->count_records('h5pactivity_attempts_results');
250
        $this->assertEquals(4, $count);
251
 
252
        // Delete the first attempt.
253
        attempt::delete_attempt($attempt1);
254
        $count = $DB->count_records('h5pactivity_attempts');
255
        $this->assertEquals(1, $count);
256
        $count = $DB->count_records('h5pactivity_attempts_results');
257
        $this->assertEquals(2, $count);
258
        $this->assertEquals(2, $attempt2->count_results());
259
    }
260
 
261
    /**
262
     * Test delete all attempts.
263
     *
264
     * @dataProvider delete_all_attempts_data
265
     * @param bool $hasstudent if user is specificed
266
     * @param int[] 0-3 => statements count results, 4-5 => totals
267
     */
11 efrain 268
    public function test_delete_all_attempts(bool $hasstudent, array $results): void {
1 efrain 269
        global $DB;
270
 
271
        list($cm, $student, $course) = $this->generate_testing_scenario();
272
 
273
        // For this test we need extra activity and student.
274
        $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
275
        $cm2 = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST);
276
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
277
 
278
        // Check no previous attempts are created.
279
        $count = $DB->count_records('h5pactivity_attempts');
280
        $this->assertEquals(0, $count);
281
        $count = $DB->count_records('h5pactivity_attempts_results');
282
        $this->assertEquals(0, $count);
283
 
284
        // Generate some attempts attempt on both activities and students.
285
        $attempts = [];
286
        $attempts[] = $this->generate_full_attempt($student, $cm);
287
        $attempts[] = $this->generate_full_attempt($student2, $cm);
288
        $attempts[] = $this->generate_full_attempt($student, $cm2);
289
        $attempts[] = $this->generate_full_attempt($student2, $cm2);
290
        $count = $DB->count_records('h5pactivity_attempts');
291
        $this->assertEquals(4, $count);
292
        $count = $DB->count_records('h5pactivity_attempts_results');
293
        $this->assertEquals(8, $count);
294
 
295
        // Delete all specified attempts.
296
        $user = ($hasstudent) ? $student : null;
297
        attempt::delete_all_attempts($cm, $user);
298
 
299
        // Check data.
300
        for ($assert = 0; $assert < 4; $assert++) {
301
            $count = $attempts[$assert]->count_results();
302
            $this->assertEquals($results[$assert], $count);
303
        }
304
        $count = $DB->count_records('h5pactivity_attempts');
305
        $this->assertEquals($results[4], $count);
306
        $count = $DB->count_records('h5pactivity_attempts_results');
307
        $this->assertEquals($results[5], $count);
308
    }
309
 
310
    /**
311
     * Data provider for data request creation tests.
312
     *
313
     * @return array
314
     */
315
    public function delete_all_attempts_data(): array {
316
        return [
317
            'Delete all attempts from activity' => [
318
                false, [0, 0, 2, 2, 2, 4]
319
            ],
320
            'Delete all attempts from user' => [
321
                true, [0, 2, 2, 2, 3, 6]
322
            ],
323
        ];
324
    }
325
 
326
    /**
327
     * Test set_score method.
328
     *
329
     */
330
    public function test_set_score(): void {
331
        global $DB;
332
 
333
        list($cm, $student, $course) = $this->generate_testing_scenario();
334
 
335
        // Generate one attempt.
336
        $attempt = $this->generate_full_attempt($student, $cm);
337
 
338
        $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]);
339
        $this->assertEquals($dbattempt->rawscore, $attempt->get_rawscore());
340
        $this->assertEquals(2, $dbattempt->rawscore);
341
        $this->assertEquals($dbattempt->maxscore, $attempt->get_maxscore());
342
        $this->assertEquals(2, $dbattempt->maxscore);
343
        $this->assertEquals(1, $dbattempt->scaled);
344
 
345
        // Set attempt score.
346
        $attempt->set_score(5, 10);
347
 
348
        $this->assertEquals(5, $attempt->get_rawscore());
349
        $this->assertEquals(10, $attempt->get_maxscore());
350
        $this->assertTrue($attempt->get_scoreupdated());
351
 
352
        // Save new score into DB.
353
        $attempt->save();
354
 
355
        $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]);
356
        $this->assertEquals($dbattempt->rawscore, $attempt->get_rawscore());
357
        $this->assertEquals(5, $dbattempt->rawscore);
358
        $this->assertEquals($dbattempt->maxscore, $attempt->get_maxscore());
359
        $this->assertEquals(10, $dbattempt->maxscore);
360
        $this->assertEquals(0.5, $dbattempt->scaled);
361
    }
362
 
363
    /**
364
     * Test set_duration method.
365
     *
366
     * @dataProvider basic_setters_data
367
     * @param string $attribute the stribute to test
368
     * @param int $oldvalue attribute old value
369
     * @param int $newvalue attribute new expected value
370
     */
371
    public function test_basic_setters(string $attribute, int $oldvalue, int $newvalue): void {
372
        global $DB;
373
 
374
        list($cm, $student, $course) = $this->generate_testing_scenario();
375
 
376
        // Generate one attempt.
377
        $attempt = $this->generate_full_attempt($student, $cm);
378
 
379
        $setmethod = 'set_'.$attribute;
380
        $getmethod = 'get_'.$attribute;
381
 
382
        $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]);
383
        $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod());
384
        $this->assertEquals($oldvalue, $dbattempt->$attribute);
385
 
386
        // Set attempt attribute.
387
        $attempt->$setmethod($newvalue);
388
 
389
        $this->assertEquals($newvalue, $attempt->$getmethod());
390
 
391
        // Save new score into DB.
392
        $attempt->save();
393
 
394
        $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]);
395
        $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod());
396
        $this->assertEquals($newvalue, $dbattempt->$attribute);
397
 
398
        // Set null $attribute.
399
        $attempt->$setmethod(null);
400
 
401
        $this->assertNull($attempt->$getmethod());
402
 
403
        // Save new score into DB.
404
        $attempt->save();
405
 
406
        $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]);
407
        $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod());
408
        $this->assertNull($dbattempt->$attribute);
409
    }
410
 
411
    /**
412
     * Data provider for testing basic setters.
413
     *
414
     * @return array
415
     */
416
    public function basic_setters_data(): array {
417
        return [
418
            'Set attempt duration' => [
419
                'duration', 25, 35
420
            ],
421
            'Set attempt completion' => [
422
                'completion', 1, 0
423
            ],
424
            'Set attempt success' => [
425
                'success', 1, 0
426
            ],
427
        ];
428
    }
429
 
430
    /**
431
     * Generate a fake attempt with two results.
432
     *
433
     * @param stdClass $student a user record
434
     * @param stdClass $cm a course_module record
435
     * @return attempt
436
     */
437
    private function generate_full_attempt($student, $cm): attempt {
438
        $attempt = attempt::new_attempt($student, $cm);
439
        $this->assertEquals(0, $attempt->get_maxscore());
440
        $this->assertEquals(0, $attempt->get_rawscore());
441
        $this->assertEquals(0, $attempt->count_results());
442
 
443
        $statement = $this->generate_statement(true, true);
444
        $saveok = $attempt->save_statement($statement, '');
445
        $this->assertTrue($saveok);
446
        $saveok = $attempt->save_statement($statement, '111-222-333');
447
        $this->assertTrue($saveok);
448
        $this->assertEquals(2, $attempt->count_results());
449
 
450
        return $attempt;
451
    }
452
 
453
    /**
454
     * Return a xAPI partial statement with object defined.
455
     * @param bool $hasdefinition if has to include definition
456
     * @param bool $hasresult if has to include results
457
     * @return statement
458
     */
459
    private function generate_statement(bool $hasdefinition, bool $hasresult): statement {
460
        global $USER;
461
 
462
        $statement = new statement();
463
        $statement->set_actor(item_agent::create_from_user($USER));
464
        $statement->set_verb(item_verb::create_from_id('http://adlnet.gov/expapi/verbs/completed'));
465
        $definition = null;
466
        if ($hasdefinition) {
467
            $definition = item_definition::create_from_data((object)[
468
                'interactionType' => 'compound',
469
                'correctResponsesPattern' => '1',
470
            ]);
471
        }
472
        $statement->set_object(item_activity::create_from_id('something', $definition));
473
        if ($hasresult) {
474
            $statement->set_result(item_result::create_from_data((object)[
475
                'completion' => true,
476
                'success' => true,
477
                'score' => (object) ['min' => 0, 'max' => 2, 'raw' => 2, 'scaled' => 1],
478
                'duration' => 'PT25S',
479
            ]));
480
        }
481
        return $statement;
482
    }
483
}