See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
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 Random allocation 19 * 20 * @package workshopallocation_random 21 * @category phpunit 22 * @copyright 2009 David Mudrak <david.mudrak@gmail.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 // Include the code to test 29 global $CFG; 30 require_once($CFG->dirroot . '/mod/workshop/locallib.php'); 31 require_once($CFG->dirroot . '/mod/workshop/allocation/random/lib.php'); 32 33 34 class workshopallocation_random_testcase extends advanced_testcase { 35 36 /** workshop instance emulation */ 37 protected $workshop; 38 39 /** allocator instance */ 40 protected $allocator; 41 42 protected function setUp() { 43 parent::setUp(); 44 $this->resetAfterTest(); 45 $this->setAdminUser(); 46 $course = $this->getDataGenerator()->create_course(); 47 $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course)); 48 $cm = get_fast_modinfo($course)->instances['workshop'][$workshop->id]; 49 $this->workshop = new workshop($workshop, $cm, $course); 50 $this->allocator = new testable_workshop_random_allocator($this->workshop); 51 } 52 53 protected function tearDown() { 54 $this->allocator = null; 55 $this->workshop = null; 56 parent::tearDown(); 57 } 58 59 public function test_self_allocation_empty_values() { 60 // fixture setup & exercise SUT & verify 61 $this->assertEquals(array(), $this->allocator->self_allocation()); 62 } 63 64 public function test_self_allocation_equal_user_groups() { 65 // fixture setup 66 $authors = array(0 => array_fill_keys(array(4, 6, 10), new stdclass())); 67 $reviewers = array(0 => array_fill_keys(array(4, 6, 10), new stdclass())); 68 // exercise SUT 69 $newallocations = $this->allocator->self_allocation($authors, $reviewers); 70 // verify 71 $this->assertEquals(array(array(4 => 4), array(6 => 6), array(10 => 10)), $newallocations); 72 } 73 74 public function test_self_allocation_different_user_groups() { 75 // fixture setup 76 $authors = array(0 => array_fill_keys(array(1, 4, 5, 10, 13), new stdclass())); 77 $reviewers = array(0 => array_fill_keys(array(4, 7, 10), new stdclass())); 78 // exercise SUT 79 $newallocations = $this->allocator->self_allocation($authors, $reviewers); 80 // verify 81 $this->assertEquals(array(array(4 => 4), array(10 => 10)), $newallocations); 82 } 83 84 public function test_self_allocation_skip_existing() { 85 // fixture setup 86 $authors = array(0 => array_fill_keys(array(3, 4, 10), new stdclass())); 87 $reviewers = array(0 => array_fill_keys(array(3, 4, 10), new stdclass())); 88 $assessments = array(23 => (object)array('authorid' => 3, 'reviewerid' => 3)); 89 // exercise SUT 90 $newallocations = $this->allocator->self_allocation($authors, $reviewers, $assessments); 91 // verify 92 $this->assertEquals(array(array(4 => 4), array(10 => 10)), $newallocations); 93 } 94 95 public function test_get_author_ids() { 96 // fixture setup 97 $newallocations = array(array(1 => 3), array(2 => 1), array(3 => 1)); 98 // exercise SUT & verify 99 $this->assertEquals(array(3, 1), $this->allocator->get_author_ids($newallocations)); 100 } 101 102 public function test_index_submissions_by_authors() { 103 // fixture setup 104 $submissions = array( 105 676 => (object)array('id' => 676, 'authorid' => 23), 106 121 => (object)array('id' => 121, 'authorid' => 56), 107 ); 108 // exercise SUT 109 $submissions = $this->allocator->index_submissions_by_authors($submissions); 110 // verify 111 $this->assertEquals(array( 112 23 => (object)array('id' => 676, 'authorid' => 23), 113 56 => (object)array('id' => 121, 'authorid' => 56), 114 ), $submissions); 115 } 116 117 /** 118 * @expectedException moodle_exception 119 */ 120 public function test_index_submissions_by_authors_duplicate_author() { 121 // fixture setup 122 $submissions = array( 123 14 => (object)array('id' => 676, 'authorid' => 3), 124 87 => (object)array('id' => 121, 'authorid' => 3), 125 ); 126 // exercise SUT 127 $submissions = $this->allocator->index_submissions_by_authors($submissions); 128 } 129 130 public function test_get_unique_allocations() { 131 // fixture setup 132 $newallocations = array(array(4 => 5), array(6 => 6), array(1 => 16), array(4 => 5), array(16 => 1)); 133 // exercise SUT 134 $newallocations = $this->allocator->get_unique_allocations($newallocations); 135 // verify 136 $this->assertEquals(array(array(4 => 5), array(6 => 6), array(1 => 16), array(16 => 1)), $newallocations); 137 } 138 139 public function test_get_unkept_assessments_no_keep_selfassessments() { 140 // fixture setup 141 $assessments = array( 142 23 => (object)array('authorid' => 3, 'reviewerid' => 3), 143 45 => (object)array('authorid' => 5, 'reviewerid' => 11), 144 12 => (object)array('authorid' => 6, 'reviewerid' => 3), 145 ); 146 $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); 147 // exercise SUT 148 $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, false); 149 // verify 150 // we want to keep $assessments[45] because it has been re-allocated 151 $this->assertEquals(array(23, 12), $delassessments); 152 } 153 154 public function test_get_unkept_assessments_keep_selfassessments() { 155 // fixture setup 156 $assessments = array( 157 23 => (object)array('authorid' => 3, 'reviewerid' => 3), 158 45 => (object)array('authorid' => 5, 'reviewerid' => 11), 159 12 => (object)array('authorid' => 6, 'reviewerid' => 3), 160 ); 161 $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); 162 // exercise SUT 163 $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, true); 164 // verify 165 // we want to keep $assessments[45] because it has been re-allocated 166 // we want to keep $assessments[23] because if is self assessment 167 $this->assertEquals(array(12), $delassessments); 168 } 169 170 /** 171 * Aggregates assessment info per author and per reviewer 172 */ 173 public function test_convert_assessments_to_links() { 174 // fixture setup 175 $assessments = array( 176 23 => (object)array('authorid' => 3, 'reviewerid' => 3), 177 45 => (object)array('authorid' => 5, 'reviewerid' => 11), 178 12 => (object)array('authorid' => 5, 'reviewerid' => 3), 179 ); 180 // exercise SUT 181 list($authorlinks, $reviewerlinks) = $this->allocator->convert_assessments_to_links($assessments); 182 // verify 183 $this->assertEquals(array(3 => array(3), 5 => array(11, 3)), $authorlinks); 184 $this->assertEquals(array(3 => array(3, 5), 11 => array(5)), $reviewerlinks); 185 } 186 187 /** 188 * Trivial case 189 */ 190 public function test_convert_assessments_to_links_empty() { 191 // fixture setup 192 $assessments = array(); 193 // exercise SUT 194 list($authorlinks, $reviewerlinks) = $this->allocator->convert_assessments_to_links($assessments); 195 // verify 196 $this->assertEquals(array(), $authorlinks); 197 $this->assertEquals(array(), $reviewerlinks); 198 } 199 200 /** 201 * If there is a single element with the lowest workload, it should be chosen 202 */ 203 public function test_get_element_with_lowest_workload_deterministic() { 204 // fixture setup 205 $workload = array(4 => 6, 9 => 1, 10 => 2); 206 // exercise SUT 207 $chosen = $this->allocator->get_element_with_lowest_workload($workload); 208 // verify 209 $this->assertEquals(9, $chosen); 210 } 211 212 /** 213 * If there are no elements available, must return false 214 */ 215 public function test_get_element_with_lowest_workload_impossible() { 216 // fixture setup 217 $workload = array(); 218 // exercise SUT 219 $chosen = $this->allocator->get_element_with_lowest_workload($workload); 220 // verify 221 $this->assertTrue($chosen === false); 222 } 223 224 /** 225 * If there are several elements with the lowest workload, one of them should be chosen randomly 226 */ 227 public function test_get_element_with_lowest_workload_random() { 228 // fixture setup 229 $workload = array(4 => 6, 9 => 2, 10 => 2); 230 // exercise SUT 231 $elements = $this->allocator->get_element_with_lowest_workload($workload); 232 // verify 233 // in theory, this test can fail even if the function works well. However, the probability of getting 234 // a row of a hundred same ids in this use case is 1/pow(2, 100) 235 // also, this just tests that each of the two elements has been chosen at least once. this is not to 236 // measure the quality or randomness of the algorithm 237 $counts = array(4 => 0, 9 => 0, 10 => 0); 238 for ($i = 0; $i < 100; $i++) { 239 $chosen = $this->allocator->get_element_with_lowest_workload($workload); 240 if (!in_array($chosen, array(4, 9, 10))) { 241 $this->fail('Invalid element ' . var_export($chosen, true) . ' chosen'); 242 break; 243 } else { 244 $counts[$this->allocator->get_element_with_lowest_workload($workload)]++; 245 } 246 } 247 $this->assertTrue(($counts[9] > 0) && ($counts[10] > 0)); 248 } 249 250 /** 251 * Floats should be rounded before they are compared 252 * 253 * This should test 254 */ 255 public function test_get_element_with_lowest_workload_random_floats() { 256 // fixture setup 257 $workload = array(1 => 1/13, 2 => 0.0769230769231); // should be considered as the same value 258 // exercise SUT 259 $elements = $this->allocator->get_element_with_lowest_workload($workload); 260 // verify 261 $counts = array(1 => 0, 2 => 0); 262 for ($i = 0; $i < 100; $i++) { 263 $chosen = $this->allocator->get_element_with_lowest_workload($workload); 264 if (!in_array($chosen, array(1, 2))) { 265 $this->fail('Invalid element ' . var_export($chosen, true) . ' chosen'); 266 break; 267 } else { 268 $counts[$this->allocator->get_element_with_lowest_workload($workload)]++; 269 } 270 } 271 $this->assertTrue(($counts[1] > 0) && ($counts[2] > 0)); 272 273 } 274 275 /** 276 * Filter new assessments so they do not contain existing 277 */ 278 public function test_filter_current_assessments() { 279 // fixture setup 280 $newallocations = array(array(3 => 5), array(11 => 5), array(2 => 9), array(3 => 5)); 281 $assessments = array( 282 23 => (object)array('authorid' => 3, 'reviewerid' => 3), 283 45 => (object)array('authorid' => 5, 'reviewerid' => 11), 284 12 => (object)array('authorid' => 5, 'reviewerid' => 3), 285 ); 286 // exercise SUT 287 $this->allocator->filter_current_assessments($newallocations, $assessments); 288 // verify 289 $this->assertEquals(array_values($newallocations), array(array(2 => 9))); 290 } 291 292 293 } 294 295 296 /** 297 * Make protected methods we want to test public 298 */ 299 class testable_workshop_random_allocator extends workshop_random_allocator { 300 public function self_allocation($authors=array(), $reviewers=array(), $assessments=array()) { 301 return parent::self_allocation($authors, $reviewers, $assessments); 302 } 303 public function get_author_ids($newallocations) { 304 return parent::get_author_ids($newallocations); 305 } 306 public function index_submissions_by_authors($submissions) { 307 return parent::index_submissions_by_authors($submissions); 308 } 309 public function get_unique_allocations($newallocations) { 310 return parent::get_unique_allocations($newallocations); 311 } 312 public function get_unkept_assessments($assessments, $newallocations, $keepselfassessments) { 313 return parent::get_unkept_assessments($assessments, $newallocations, $keepselfassessments); 314 } 315 public function convert_assessments_to_links($assessments) { 316 return parent::convert_assessments_to_links($assessments); 317 } 318 public function get_element_with_lowest_workload($workload) { 319 return parent::get_element_with_lowest_workload($workload); 320 } 321 public function filter_current_assessments(&$newallocations, $assessments) { 322 return parent::filter_current_assessments($newallocations, $assessments); 323 } 324 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body