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