0ba77486a4715cac30328ab8ef02e92a23a0f979
[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         core_php_time_limit::get_and_clear_unit_test_data();
60         $progress->progress(2);
61         $this->assertTrue($progress->was_update_called());
62         $this->assertEquals(array(core_backup_progress::TIME_LIMIT_WITHOUT_PROGRESS),
63                 core_php_time_limit::get_and_clear_unit_test_data());
65         // Check the new value.
66         $this->assert_min_max(0.2, 0.2, $progress);
68         // Do another progress run at same time, it should be ignored.
69         $progress->progress(3);
70         $this->assertFalse($progress->was_update_called());
71         $this->assert_min_max(0.2, 0.2, $progress);
73         // End the section. This should cause an update.
74         $progress->end_progress();
75         $this->assertTrue($progress->was_update_called());
77         // Because there are no sections left open, it thinks we finished.
78         $this->assert_min_max(1.0, 1.0, $progress);
80         // There was 1 progress call.
81         $this->assertEquals(1, $progress->get_progress_count());
83         // Clear the time limit, otherwise phpunit complains.
84         set_time_limit(0);
85     }
87     /**
88      * Tests progress that is nested and/or indeterminate.
89      */
90     public function test_nested() {
91         // Outer progress goes from 0 to 10.
92         $progress = new core_backup_mock_progress();
93         $progress->start_progress('hello', 10);
95         // Get up to 4, check position.
96         $progress->step_time();
97         $progress->progress(4);
98         $this->assert_min_max(0.4, 0.4, $progress);
99         $this->assertEquals('hello', $progress->get_current_description());
101         // Now start indeterminate progress.
102         $progress->start_progress('world');
103         $this->assert_min_max(0.4, 0.5, $progress);
104         $this->assertEquals('world', $progress->get_current_description());
106         // Do some indeterminate progress and count it (once per second).
107         $progress->step_time();
108         $progress->progress();
109         $this->assertEquals(2, $progress->get_progress_count());
110         $progress->progress();
111         $this->assertEquals(2, $progress->get_progress_count());
112         $progress->step_time();
113         $progress->progress();
114         $this->assertEquals(3, $progress->get_progress_count());
115         $this->assert_min_max(0.4, 0.5, $progress);
117         // Exit the indeterminate section.
118         $progress->end_progress();
119         $this->assert_min_max(0.5, 0.5, $progress);
121         $progress->step_time();
122         $progress->progress(7);
123         $this->assert_min_max(0.7, 0.7, $progress);
125         // Enter a numbered section (this time with a range of 5).
126         $progress->start_progress('frogs', 5);
127         $this->assert_min_max(0.7, 0.7, $progress);
128         $progress->step_time();
129         $progress->progress(1);
130         $this->assert_min_max(0.72, 0.72, $progress);
131         $progress->step_time();
132         $progress->progress(3);
133         $this->assert_min_max(0.76, 0.76, $progress);
135         // Now enter another indeterminate section.
136         $progress->start_progress('and');
137         $this->assert_min_max(0.76, 0.78, $progress);
139         // Make some progress, should increment indeterminate count.
140         $progress->step_time();
141         $progress->progress();
142         $this->assertEquals(7, $progress->get_progress_count());
144         // Enter numbered section, won't make any difference to values.
145         $progress->start_progress('zombies', 2);
146         $progress->step_time();
147         $progress->progress(1);
148         $this->assert_min_max(0.76, 0.78, $progress);
149         $this->assertEquals(8, $progress->get_progress_count());
151         // Leaving it will make no difference too.
152         $progress->end_progress();
154         // Leaving the indeterminate section will though.
155         $progress->end_progress();
156         $this->assert_min_max(0.78, 0.78, $progress);
158         // Leave the two numbered sections.
159         $progress->end_progress();
160         $this->assert_min_max(0.8, 0.8, $progress);
161         $progress->end_progress();
162         $this->assertFalse($progress->is_in_progress_section());
164         set_time_limit(0);
165     }
167     /**
168      * Tests the feature for 'weighting' nested progress.
169      */
170     public function test_nested_weighted() {
171         $progress = new core_backup_mock_progress();
172         $progress->start_progress('', 10);
174         // First nested child has 2 units of its own and is worth 1 unit.
175         $progress->start_progress('', 2);
176         $progress->step_time();
177         $progress->progress(1);
178         $this->assert_min_max(0.05, 0.05, $progress);
179         $progress->end_progress();
180         $this->assert_min_max(0.1, 0.1, $progress);
182         // Next child has 2 units of its own but is worth 3 units.
183         $progress->start_progress('weighted', 2, 3);
184         $progress->step_time();
185         $progress->progress(1);
186         $this->assert_min_max(0.25, 0.25, $progress);
187         $progress->end_progress();
188         $this->assert_min_max(0.4, 0.4, $progress);
190         // Next indeterminate child is worth 6 units.
191         $progress->start_progress('', core_backup_progress::INDETERMINATE, 6);
192         $progress->step_time();
193         $progress->progress();
194         $this->assert_min_max(0.4, 1.0, $progress);
195         $progress->end_progress();
196         $this->assert_min_max(1.0, 1.0, $progress);
198         set_time_limit(0);
199     }
201     /**
202      * I had some issues with real use in backup/restore, this test is intended
203      * to be similar.
204      */
205     public function test_realistic() {
206         $progress = new core_backup_mock_progress();
207         $progress->start_progress('parent', 100);
208         $progress->start_progress('child', 1);
209         $progress->progress(1);
210         $this->assert_min_max(0.01, 0.01, $progress);
211         $progress->end_progress();
212         $this->assert_min_max(0.01, 0.01, $progress);
214         // Clear the time limit, otherwise phpunit complains.
215         set_time_limit(0);
216     }
218     /**
219      * To avoid causing problems, progress needs to work for sections that have
220      * zero entries.
221      */
222     public function test_zero() {
223         $progress = new core_backup_mock_progress();
224         $progress->start_progress('parent', 100);
225         $progress->progress(1);
226         $this->assert_min_max(0.01, 0.01, $progress);
227         $progress->start_progress('child', 0);
229         // For 'zero' progress, the progress section as immediately complete
230         // within the parent count, so it moves up to 2%.
231         $this->assert_min_max(0.02, 0.02, $progress);
232         $progress->progress(0);
233         $this->assert_min_max(0.02, 0.02, $progress);
234         $progress->end_progress();
235         $this->assert_min_max(0.02, 0.02, $progress);
237         set_time_limit(0);
238     }
240     /**
241      * Tests for any exceptions due to invalid calls.
242      */
243     public function test_exceptions() {
244         $progress = new core_backup_mock_progress();
246         // Check errors when empty.
247         try {
248             $progress->progress();
249             $this->fail();
250         } catch (coding_exception $e) {
251             $this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
252         }
253         try {
254             $progress->end_progress();
255             $this->fail();
256         } catch (coding_exception $e) {
257             $this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
258         }
259         try {
260             $progress->get_current_description();
261             $this->fail();
262         } catch (coding_exception $e) {
263             $this->assertEquals(1, preg_match('~Not inside progress~', $e->getMessage()));
264         }
265         try {
266             $progress->start_progress('', 1, 7);
267             $this->fail();
268         } catch (coding_exception $e) {
269             $this->assertEquals(1, preg_match('~must be 1~', $e->getMessage()));
270         }
272         // Check invalid start (-2).
273         try {
274             $progress->start_progress('hello', -2);
275             $this->fail();
276         } catch (coding_exception $e) {
277             $this->assertEquals(1, preg_match('~cannot be negative~', $e->getMessage()));
278         }
280         // Indeterminate when value expected.
281         $progress->start_progress('hello', 10);
282         try {
283             $progress->progress(core_backup_progress::INDETERMINATE);
284             $this->fail();
285         } catch (coding_exception $e) {
286             $this->assertEquals(1, preg_match('~expecting value~', $e->getMessage()));
287         }
289         // Value when indeterminate expected.
290         $progress->start_progress('hello');
291         try {
292             $progress->progress(4);
293             $this->fail();
294         } catch (coding_exception $e) {
295             $this->assertEquals(1, preg_match('~expecting INDETERMINATE~', $e->getMessage()));
296         }
298         // Illegal values.
299         $progress->start_progress('hello', 10);
300         try {
301             $progress->progress(-2);
302             $this->fail();
303         } catch (coding_exception $e) {
304             $this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
305         }
306         try {
307             $progress->progress(11);
308             $this->fail();
309         } catch (coding_exception $e) {
310             $this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
311         }
313         // You are allowed two with the same value...
314         $progress->progress(4);
315         $progress->step_time();
316         $progress->progress(4);
317         $progress->step_time();
319         // ...but not to go backwards.
320         try {
321             $progress->progress(3);
322             $this->fail();
323         } catch (coding_exception $e) {
324             $this->assertEquals(1, preg_match('~backwards~', $e->getMessage()));
325         }
327         // When you go forward, you can't go further than there is room.
328         try {
329             $progress->start_progress('', 1, 7);
330             $this->fail();
331         } catch (coding_exception $e) {
332             $this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
333         }
335         // Clear the time limit, otherwise phpunit complains.
336         set_time_limit(0);
337     }
339     /**
340      * Checks the current progress values are as expected.
341      *
342      * @param number $min Expected min progress
343      * @param number $max Expected max progress
344      * @param core_backup_mock_progress $progress
345      */
346     private function assert_min_max($min, $max, core_backup_mock_progress $progress) {
347         $this->assertEquals(array($min, $max),
348                 $progress->get_progress_proportion_range());
349     }
352 /**
353  * Helper class that records when update_progress is called and allows time
354  * stepping.
355  */
356 class core_backup_mock_progress extends core_backup_progress {
357     private $updatecalled = false;
358     private $time = 1;
360     /**
361      * Checks if update was called since the last call to this function.
362      *
363      * @return boolean True if update was called
364      */
365     public function was_update_called() {
366         if ($this->updatecalled) {
367             $this->updatecalled = false;
368             return true;
369         }
370         return false;
371     }
373     /**
374      * Steps the current time by 1 second.
375      */
376     public function step_time() {
377         $this->time++;
378     }
380     protected function update_progress() {
381         $this->updatecalled = true;
382     }
384     protected function get_time() {
385         return $this->time;
386     }