Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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 grading evaluation method "best"
  19   *
  20   * @package    workshopeval_best
  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/eval/best/lib.php');
  32  require_once($CFG->libdir . '/gradelib.php');
  33  
  34  
  35  class workshopeval_best_evaluation_testcase extends advanced_testcase {
  36  
  37      /** workshop instance emulation */
  38      protected $workshop;
  39  
  40      /** instance of the grading evaluator being tested */
  41      protected $evaluator;
  42  
  43      /**
  44       * Setup testing environment
  45       */
  46      protected function setUp(): void {
  47          parent::setUp();
  48          $this->resetAfterTest();
  49          $this->setAdminUser();
  50          $course = $this->getDataGenerator()->create_course();
  51          $workshop = $this->getDataGenerator()->create_module('workshop', array('evaluation' => 'best', 'course' => $course));
  52          $cm = get_fast_modinfo($course)->instances['workshop'][$workshop->id];
  53          $this->workshop = new workshop($workshop, $cm, $course);
  54          $this->evaluator = new testable_workshop_best_evaluation($this->workshop);
  55      }
  56  
  57      protected function tearDown(): void {
  58          $this->workshop = null;
  59          $this->evaluator = null;
  60          parent::tearDown();
  61      }
  62  
  63      public function test_normalize_grades() {
  64          // fixture set-up
  65          $assessments = array();
  66          $assessments[1] = (object)array(
  67              'dimgrades' => array(3 => 1.0000, 4 => 13.42300),
  68          );
  69          $assessments[3] = (object)array(
  70              'dimgrades' => array(3 => 2.0000, 4 => 19.1000),
  71          );
  72          $assessments[7] = (object)array(
  73              'dimgrades' => array(3 => 3.0000, 4 => 0.00000),
  74          );
  75          $diminfo = array(
  76              3 => (object)array('min' => 1, 'max' => 3),
  77              4 => (object)array('min' => 0, 'max' => 20),
  78          );
  79          // exercise SUT
  80          $norm = $this->evaluator->normalize_grades($assessments, $diminfo);
  81          // validate
  82          $this->assertEquals(gettype($norm), 'array');
  83          // the following grades from a scale
  84          $this->assertEquals($norm[1]->dimgrades[3], 0);
  85          $this->assertEquals($norm[3]->dimgrades[3], 50);
  86          $this->assertEquals($norm[7]->dimgrades[3], 100);
  87          // the following grades from an interval 0 - 20
  88          $this->assertEquals($norm[1]->dimgrades[4], grade_floatval(13.423 / 20 * 100));
  89          $this->assertEquals($norm[3]->dimgrades[4], grade_floatval(19.1 / 20 * 100));
  90          $this->assertEquals($norm[7]->dimgrades[4], 0);
  91      }
  92  
  93      public function test_normalize_grades_max_equals_min() {
  94          // fixture set-up
  95          $assessments = array();
  96          $assessments[1] = (object)array(
  97              'dimgrades' => array(3 => 100.0000),
  98          );
  99          $diminfo = array(
 100              3 => (object)array('min' => 100, 'max' => 100),
 101          );
 102          // exercise SUT
 103          $norm = $this->evaluator->normalize_grades($assessments, $diminfo);
 104          // validate
 105          $this->assertEquals(gettype($norm), 'array');
 106          $this->assertEquals($norm[1]->dimgrades[3], 100);
 107      }
 108  
 109      public function test_average_assessment_same_weights() {
 110          // fixture set-up
 111          $assessments = array();
 112          $assessments[18] = (object)array(
 113              'weight'        => 1,
 114              'dimgrades'     => array(1 => 50, 2 => 33.33333),
 115          );
 116          $assessments[16] = (object)array(
 117              'weight'        => 1,
 118              'dimgrades'     => array(1 => 0, 2 => 66.66667),
 119          );
 120          // exercise SUT
 121          $average = $this->evaluator->average_assessment($assessments);
 122          // validate
 123          $this->assertEquals(gettype($average->dimgrades), 'array');
 124          $this->assertEquals(grade_floatval($average->dimgrades[1]), grade_floatval(25));
 125          $this->assertEquals(grade_floatval($average->dimgrades[2]), grade_floatval(50));
 126      }
 127  
 128      public function test_average_assessment_different_weights() {
 129          // fixture set-up
 130          $assessments = array();
 131          $assessments[11] = (object)array(
 132              'weight'        => 1,
 133              'dimgrades'     => array(3 => 10.0, 4 => 13.4, 5 => 95.0),
 134          );
 135          $assessments[13] = (object)array(
 136              'weight'        => 3,
 137              'dimgrades'     => array(3 => 11.0, 4 => 10.1, 5 => 92.0),
 138          );
 139          $assessments[17] = (object)array(
 140              'weight'        => 1,
 141              'dimgrades'     => array(3 => 11.0, 4 => 8.1, 5 => 88.0),
 142          );
 143          // exercise SUT
 144          $average = $this->evaluator->average_assessment($assessments);
 145          // validate
 146          $this->assertEquals(gettype($average->dimgrades), 'array');
 147          $this->assertEquals(grade_floatval($average->dimgrades[3]), grade_floatval((10.0 + 11.0*3 + 11.0)/5));
 148          $this->assertEquals(grade_floatval($average->dimgrades[4]), grade_floatval((13.4 + 10.1*3 + 8.1)/5));
 149          $this->assertEquals(grade_floatval($average->dimgrades[5]), grade_floatval((95.0 + 92.0*3 + 88.0)/5));
 150      }
 151  
 152      public function test_average_assessment_noweight() {
 153          // fixture set-up
 154          $assessments = array();
 155          $assessments[11] = (object)array(
 156              'weight'        => 0,
 157              'dimgrades'     => array(3 => 10.0, 4 => 13.4, 5 => 95.0),
 158          );
 159          $assessments[17] = (object)array(
 160              'weight'        => 0,
 161              'dimgrades'     => array(3 => 11.0, 4 => 8.1, 5 => 88.0),
 162          );
 163          // exercise SUT
 164          $average = $this->evaluator->average_assessment($assessments);
 165          // validate
 166          $this->assertNull($average);
 167      }
 168  
 169      public function test_weighted_variance() {
 170          // fixture set-up
 171          $assessments[11] = (object)array(
 172              'weight'        => 1,
 173              'dimgrades'     => array(3 => 11, 4 => 2),
 174          );
 175          $assessments[13] = (object)array(
 176              'weight'        => 3,
 177              'dimgrades'     => array(3 => 11, 4 => 4),
 178          );
 179          $assessments[17] = (object)array(
 180              'weight'        => 2,
 181              'dimgrades'     => array(3 => 11, 4 => 5),
 182          );
 183          $assessments[20] = (object)array(
 184              'weight'        => 1,
 185              'dimgrades'     => array(3 => 11, 4 => 7),
 186          );
 187          $assessments[25] = (object)array(
 188              'weight'        => 1,
 189              'dimgrades'     => array(3 => 11, 4 => 9),
 190          );
 191          // exercise SUT
 192          $variance = $this->evaluator->weighted_variance($assessments);
 193          // validate
 194          // dimension [3] have all the grades equal to 11
 195          $this->assertEquals($variance[3], 0);
 196          // dimension [4] represents data 2, 4, 4, 4, 5, 5, 7, 9 having stdev=2 (stdev is sqrt of variance)
 197          $this->assertEquals($variance[4], 4);
 198      }
 199  
 200      public function test_assessments_distance_zero() {
 201          // fixture set-up
 202          $diminfo = array(
 203              3 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567),
 204              4 => (object)array('weight' => 1, 'min' => 1, 'max' => 5,   'variance' => 98.76543),
 205          );
 206          $assessment1 = (object)array('dimgrades' => array(3 => 15, 4 => 2));
 207          $assessment2 = (object)array('dimgrades' => array(3 => 15, 4 => 2));
 208          $settings = (object)array('comparison' => 5);
 209          // exercise SUT and validate
 210          $this->assertEquals($this->evaluator->assessments_distance($assessment1, $assessment2, $diminfo, $settings), 0);
 211      }
 212  
 213      public function test_assessments_distance_equals() {
 214          /*
 215          // fixture set-up
 216          $diminfo = array(
 217              3 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567),
 218              4 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567),
 219          );
 220          $assessment1 = (object)array('dimgrades' => array(3 => 25, 4 => 4));
 221          $assessment2 = (object)array('dimgrades' => array(3 => 75, 4 => 2));
 222          $referential = (object)array('dimgrades' => array(3 => 50, 4 => 3));
 223          $settings = (object)array('comparison' => 5);
 224          // exercise SUT and validate
 225          $this->assertEquals($this->evaluator->assessments_distance($assessment1, $referential, $diminfo, $settings),
 226                             $this->evaluator->assessments_distance($assessment2, $referential, $diminfo, $settings));
 227          */
 228          // fixture set-up
 229          $diminfo = array(
 230              1 => (object)array('min' => 0, 'max' => 2, 'weight' => 1, 'variance' => 625),
 231              2 => (object)array('min' => 0, 'max' => 3, 'weight' => 1, 'variance' => 277.7778888889),
 232          );
 233          $assessment1 = (object)array('dimgrades' => array(1 => 0,  2 => 66.66667));
 234          $assessment2 = (object)array('dimgrades' => array(1 => 50, 2 => 33.33333));
 235          $referential = (object)array('dimgrades' => array(1 => 25, 2 => 50));
 236          $settings = (object)array('comparison' => 9);
 237          // exercise SUT and validate
 238          $this->assertEquals($this->evaluator->assessments_distance($assessment1, $referential, $diminfo, $settings),
 239              $this->evaluator->assessments_distance($assessment2, $referential, $diminfo, $settings));
 240  
 241      }
 242  
 243      public function test_assessments_distance_zero_variance() {
 244          // Fixture set-up: an assessment form of the strategy "Number of errors",
 245          // three assertions, same weight.
 246          $diminfo = array(
 247              1 => (object)array('min' => 0, 'max' => 1, 'weight' => 1),
 248              2 => (object)array('min' => 0, 'max' => 1, 'weight' => 1),
 249              3 => (object)array('min' => 0, 'max' => 1, 'weight' => 1),
 250          );
 251  
 252          // Simulate structure returned by {@link workshop_best_evaluation::prepare_data_from_recordset()}
 253          $assessments = array(
 254              // The first assessment has weight 0 and the assessment was No, No, No.
 255              10 => (object)array(
 256                  'assessmentid' => 10,
 257                  'weight' => 0,
 258                  'reviewerid' => 56,
 259                  'gradinggrade' => null,
 260                  'submissionid' => 99,
 261                  'dimgrades' => array(
 262                      1 => 0,
 263                      2 => 0,
 264                      3 => 0,
 265                  ),
 266              ),
 267              // The second assessment has weight 1 and assessments was Yes, Yes, Yes.
 268              20 => (object)array(
 269                  'assessmentid' => 20,
 270                  'weight' => 1,
 271                  'reviewerid' => 76,
 272                  'gradinggrade' => null,
 273                  'submissionid' => 99,
 274                  'dimgrades' => array(
 275                      1 => 1,
 276                      2 => 1,
 277                      3 => 1,
 278                  ),
 279              ),
 280              // The third assessment has weight 1 and assessments was Yes, Yes, Yes too.
 281              30 => (object)array(
 282                  'assessmentid' => 30,
 283                  'weight' => 1,
 284                  'reviewerid' => 97,
 285                  'gradinggrade' => null,
 286                  'submissionid' => 99,
 287                  'dimgrades' => array(
 288                      1 => 1,
 289                      2 => 1,
 290                      3 => 1,
 291                  ),
 292              ),
 293          );
 294  
 295          // Process assessments in the same way as in the {@link workshop_best_evaluation::process_assessments()}
 296          $assessments = $this->evaluator->normalize_grades($assessments, $diminfo);
 297          $average = $this->evaluator->average_assessment($assessments);
 298          $variances = $this->evaluator->weighted_variance($assessments);
 299          foreach ($variances as $dimid => $variance) {
 300              $diminfo[$dimid]->variance = $variance;
 301          }
 302  
 303          // Simulate the chosen comparison of assessments "fair" (does not really matter here but we need something).
 304          $settings = (object)array('comparison' => 5);
 305  
 306          // Exercise SUT: for every assessment, calculate its distance from the average one.
 307          $distances = array();
 308          foreach ($assessments as $asid => $assessment) {
 309              $distances[$asid] = $this->evaluator->assessments_distance($assessment, $average, $diminfo, $settings);
 310          }
 311  
 312          // Validate: the first assessment is far far away from the average one ...
 313          $this->assertTrue($distances[10] > 0);
 314          // ... while the two others were both picked as the referential ones.
 315          $this->assertTrue($distances[20] == 0);
 316          $this->assertTrue($distances[30] == 0);
 317      }
 318  }
 319  
 320  
 321  /**
 322   * Test subclass that makes all the protected methods we want to test public.
 323   */
 324  class testable_workshop_best_evaluation extends workshop_best_evaluation {
 325  
 326      public function normalize_grades(array $assessments, array $diminfo) {
 327          return parent::normalize_grades($assessments, $diminfo);
 328      }
 329      public function average_assessment(array $assessments) {
 330          return parent::average_assessment($assessments);
 331      }
 332      public function weighted_variance(array $assessments) {
 333          return parent::weighted_variance($assessments);
 334      }
 335      public function assessments_distance(stdclass $assessment, stdclass $referential, array $diminfo, stdclass $settings) {
 336          return parent::assessments_distance($assessment, $referential, $diminfo, $settings);
 337      }
 338  }