MDL-41817 Backup/restore without blocks gives error
[moodle.git] / backup / util / progress / tests / progress_test.php
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/>.
17 /**
18  * Unit tests for the progress classes.
19  *
20  * @package core_backup
21  * @category phpunit
22  * @copyright 2013 The Open University
23  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 // Include all the needed stuff.
29 global $CFG;
30 require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
32 /**
33  * Progress tests.
34  */
35 class backup_progress_testcase extends basic_testcase {
37     /**
38      * Tests for basic use with simple numeric progress.
39      */
40     public function test_basic() {
41         $progress = new core_backup_mock_progress();
43         // Check values of empty progress things.
44         $this->assertFalse($progress->is_in_progress_section());
46         // Start progress counting, check basic values and check that update
47         // gets called.
48         $progress->start_progress('hello', 10);
49         $this->assertTrue($progress->was_update_called());
50         $this->assertTrue($progress->is_in_progress_section());
51         $this->assertEquals('hello', $progress->get_current_description());
53         // Check numeric position and indeterminate count.
54         $this->assert_min_max(0.0, 0.0, $progress);
55         $this->assertEquals(0, $progress->get_progress_count());
57         // Make some progress and check that the time limit gets added.
58         $progress->step_time();
59         $progress->progress(2);
60         $this->assertTrue($progress->was_update_called());
61         $this->assertEquals(120, ini_get('max_execution_time'));
63         // Check the new value.
64         $this->assert_min_max(0.2, 0.2, $progress);
66         // Do another progress run at same time, it should be ignored.
67         $progress->progress(3);
68         $this->assertFalse($progress->was_update_called());
69         $this->assert_min_max(0.2, 0.2, $progress);
71         // End the section. This should cause an update.
72         $progress->end_progress();
73         $this->assertTrue($progress->was_update_called());
75         // Because there are no sections left open, it thinks we finished.
76         $this->assert_min_max(1.0, 1.0, $progress);
78         // There was 1 progress call.
79         $this->assertEquals(1, $progress->get_progress_count());
81         // Clear the time limit, otherwise phpunit complains.
82         set_time_limit(0);
83     }
85     /**
86      * Tests progress that is nested and/or indeterminate.
87      */
88     public function test_nested() {
89         // Outer progress goes from 0 to 10.
90         $progress = new core_backup_mock_progress();
91         $progress->start_progress('hello', 10);
93         // Get up to 4, check position.
94         $progress->step_time();
95         $progress->progress(4);
96         $this->assert_min_max(0.4, 0.4, $progress);
97         $this->assertEquals('hello', $progress->get_current_description());
99         // Now start indeterminate progress.
100         $progress->start_progress('world');
101         $this->assert_min_max(0.4, 0.5, $progress);
102         $this->assertEquals('world', $progress->get_current_description());
104         // Do some indeterminate progress and count it (once per second).
105         $progress->step_time();
106         $progress->progress();
107         $this->assertEquals(2, $progress->get_progress_count());
108         $progress->progress();
109         $this->assertEquals(2, $progress->get_progress_count());
110         $progress->step_time();
111         $progress->progress();
112         $this->assertEquals(3, $progress->get_progress_count());
113         $this->assert_min_max(0.4, 0.5, $progress);
115         // Exit the indeterminate section.
116         $progress->end_progress();
117         $this->assert_min_max(0.5, 0.5, $progress);
119         $progress->step_time();
120         $progress->progress(7);
121         $this->assert_min_max(0.7, 0.7, $progress);
123         // Enter a numbered section (this time with a range of 5).
124         $progress->start_progress('frogs', 5);
125         $this->assert_min_max(0.7, 0.7, $progress);
126         $progress->step_time();
127         $progress->progress(1);
128         $this->assert_min_max(0.72, 0.72, $progress);
129         $progress->step_time();
130         $progress->progress(3);
131         $this->assert_min_max(0.76, 0.76, $progress);
133         // Now enter another indeterminate section.
134         $progress->start_progress('and');
135         $this->assert_min_max(0.76, 0.78, $progress);
137         // Make some progress, should increment indeterminate count.
138         $progress->step_time();
139         $progress->progress();
140         $this->assertEquals(7, $progress->get_progress_count());
142         // Enter numbered section, won't make any difference to values.
143         $progress->start_progress('zombies', 2);
144         $progress->step_time();
145         $progress->progress(1);
146         $this->assert_min_max(0.76, 0.78, $progress);
147         $this->assertEquals(8, $progress->get_progress_count());
149         // Leaving it will make no difference too.
150         $progress->end_progress();
152         // Leaving the indeterminate section will though.
153         $progress->end_progress();
154         $this->assert_min_max(0.78, 0.78, $progress);
156         // Leave the two numbered sections.
157         $progress->end_progress();
158         $this->assert_min_max(0.8, 0.8, $progress);
159         $progress->end_progress();
160         $this->assertFalse($progress->is_in_progress_section());
162         set_time_limit(0);
163     }
165     /**
166      * Tests the feature for 'weighting' nested progress.
167      */
168     public function test_nested_weighted() {
169         $progress = new core_backup_mock_progress();
170         $progress->start_progress('', 10);
172         // First nested child has 2 units of its own and is worth 1 unit.
173         $progress->start_progress('', 2);
174         $progress->step_time();
175         $progress->progress(1);
176         $this->assert_min_max(0.05, 0.05, $progress);
177         $progress->end_progress();
178         $this->assert_min_max(0.1, 0.1, $progress);
180         // Next child has 2 units of its own but is worth 3 units.
181         $progress->start_progress('weighted', 2, 3);
182         $progress->step_time();
183         $progress->progress(1);
184         $this->assert_min_max(0.25, 0.25, $progress);
185         $progress->end_progress();
186         $this->assert_min_max(0.4, 0.4, $progress);
188         // Next indeterminate child is worth 6 units.
189         $progress->start_progress('', core_backup_progress::INDETERMINATE, 6);
190         $progress->step_time();
191         $progress->progress();
192         $this->assert_min_max(0.4, 1.0, $progress);
193         $progress->end_progress();
194         $this->assert_min_max(1.0, 1.0, $progress);
196         set_time_limit(0);
197     }
199     /**
200      * I had some issues with real use in backup/restore, this test is intended
201      * to be similar.
202      */
203     public function test_realistic() {
204         $progress = new core_backup_mock_progress();
205         $progress->start_progress('parent', 100);
206         $progress->start_progress('child', 1);
207         $progress->progress(1);
208         $this->assert_min_max(0.01, 0.01, $progress);
209         $progress->end_progress();
210         $this->assert_min_max(0.01, 0.01, $progress);
212         // Clear the time limit, otherwise phpunit complains.
213         set_time_limit(0);
214     }
216     /**
217      * To avoid causing problems, progress needs to work for sections that have
218      * zero entries.
219      */
220     public function test_zero() {
221         $progress = new core_backup_mock_progress();
222         $progress->start_progress('parent', 100);
223         $progress->progress(1);
224         $this->assert_min_max(0.01, 0.01, $progress);
225         $progress->start_progress('child', 0);
227         // For 'zero' progress, the progress section as immediately complete
228         // within the parent count, so it moves up to 2%.
229         $this->assert_min_max(0.02, 0.02, $progress);
230         $progress->progress(0);
231         $this->assert_min_max(0.02, 0.02, $progress);
232         $progress->end_progress();
233         $this->assert_min_max(0.02, 0.02, $progress);
235         set_time_limit(0);
236     }
238     /**
239      * Tests for any exceptions due to invalid calls.
240      */
241     public function test_exceptions() {
242         $progress = new core_backup_mock_progress();
244         // Check errors when empty.
245         try {
246             $progress->progress();
247             $this->fail();
248         } catch (coding_exception $e) {
249             $this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
250         }
251         try {
252             $progress->end_progress();
253             $this->fail();
254         } catch (coding_exception $e) {
255             $this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
256         }
257         try {
258             $progress->get_current_description();
259             $this->fail();
260         } catch (coding_exception $e) {
261             $this->assertEquals(1, preg_match('~Not inside progress~', $e->getMessage()));
262         }
263         try {
264             $progress->start_progress('', 1, 7);
265             $this->fail();
266         } catch (coding_exception $e) {
267             $this->assertEquals(1, preg_match('~must be 1~', $e->getMessage()));
268         }
270         // Check invalid start (-2).
271         try {
272             $progress->start_progress('hello', -2);
273             $this->fail();
274         } catch (coding_exception $e) {
275             $this->assertEquals(1, preg_match('~cannot be negative~', $e->getMessage()));
276         }
278         // Indeterminate when value expected.
279         $progress->start_progress('hello', 10);
280         try {
281             $progress->progress(core_backup_progress::INDETERMINATE);
282             $this->fail();
283         } catch (coding_exception $e) {
284             $this->assertEquals(1, preg_match('~expecting value~', $e->getMessage()));
285         }
287         // Value when indeterminate expected.
288         $progress->start_progress('hello');
289         try {
290             $progress->progress(4);
291             $this->fail();
292         } catch (coding_exception $e) {
293             $this->assertEquals(1, preg_match('~expecting INDETERMINATE~', $e->getMessage()));
294         }
296         // Illegal values.
297         $progress->start_progress('hello', 10);
298         try {
299             $progress->progress(-2);
300             $this->fail();
301         } catch (coding_exception $e) {
302             $this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
303         }
304         try {
305             $progress->progress(11);
306             $this->fail();
307         } catch (coding_exception $e) {
308             $this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
309         }
311         // You are allowed two with the same value...
312         $progress->progress(4);
313         $progress->step_time();
314         $progress->progress(4);
315         $progress->step_time();
317         // ...but not to go backwards.
318         try {
319             $progress->progress(3);
320             $this->fail();
321         } catch (coding_exception $e) {
322             $this->assertEquals(1, preg_match('~backwards~', $e->getMessage()));
323         }
325         // When you go forward, you can't go further than there is room.
326         try {
327             $progress->start_progress('', 1, 7);
328             $this->fail();
329         } catch (coding_exception $e) {
330             $this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
331         }
333         // Clear the time limit, otherwise phpunit complains.
334         set_time_limit(0);
335     }
337     /**
338      * Checks the current progress values are as expected.
339      *
340      * @param number $min Expected min progress
341      * @param number $max Expected max progress
342      * @param core_backup_mock_progress $progress
343      */
344     private function assert_min_max($min, $max, core_backup_mock_progress $progress) {
345         $this->assertEquals(array($min, $max),
346                 $progress->get_progress_proportion_range());
347     }
350 /**
351  * Helper class that records when update_progress is called and allows time
352  * stepping.
353  */
354 class core_backup_mock_progress extends core_backup_progress {
355     private $updatecalled = false;
356     private $time = 1;
358     /**
359      * Checks if update was called since the last call to this function.
360      *
361      * @return boolean True if update was called
362      */
363     public function was_update_called() {
364         if ($this->updatecalled) {
365             $this->updatecalled = false;
366             return true;
367         }
368         return false;
369     }
371     /**
372      * Steps the current time by 1 second.
373      */
374     public function step_time() {
375         $this->time++;
376     }
378     protected function update_progress() {
379         $this->updatecalled = true;
380     }
382     protected function get_time() {
383         return $this->time;
384     }