From 4c6062ea6552d786e12f14c1e948c44f438ae17b Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Tue, 3 Apr 2012 14:50:35 +0200 Subject: [PATCH] MDL-32149 scale generator and workshop tests --- lib/phpunit/generatorlib.php | 53 +++ .../random/tests/allocator_test.php | 323 ++++++++++++++ mod/workshop/eval/best/tests/lib_test.php | 262 +++++++++++ .../form/accumulative/tests/lib_test.php | 207 +++++++++ .../form/numerrors/tests/lib_test.php | 257 +++++++++++ mod/workshop/form/rubric/tests/lib_test.php | 146 ++++++ mod/workshop/tests/locallib_test.php | 419 ++++++++++++++++++ 7 files changed, 1667 insertions(+) create mode 100644 mod/workshop/allocation/random/tests/allocator_test.php create mode 100644 mod/workshop/eval/best/tests/lib_test.php create mode 100644 mod/workshop/form/accumulative/tests/lib_test.php create mode 100644 mod/workshop/form/numerrors/tests/lib_test.php create mode 100644 mod/workshop/form/rubric/tests/lib_test.php create mode 100644 mod/workshop/tests/locallib_test.php diff --git a/lib/phpunit/generatorlib.php b/lib/phpunit/generatorlib.php index ce369a8ee1b..f01893e6e1e 100644 --- a/lib/phpunit/generatorlib.php +++ b/lib/phpunit/generatorlib.php @@ -34,6 +34,7 @@ class phpunit_data_generator { protected $coursecount = 0; protected $blockcount = 0; protected $modulecount = 0; + protected $scalecount = 0; /** * To be called from data reset code only, @@ -46,6 +47,7 @@ class phpunit_data_generator { $this->coursecount = 0; $this->blockcount = 0; $this->modulecount = 0; + $this->scalecount = 0; } /** @@ -414,4 +416,55 @@ class phpunit_data_generator { return $instance; } + + /** + * Create a test scale + * @param array|stdClass $record + * @param array $options + * @return stdClass block instance record + */ + public function create_scale($record=null, array $options=null) { + global $DB; + + $this->scalecount++; + $i = $this->scalecount; + + $record = (array)$record; + + if (!isset($record['name'])) { + $record['name'] = 'Test scale '.$i; + } + + if (!isset($record['scale'])) { + $record['scale'] = 'A,B,C,D,F'; + } + + if (!isset($record['courseid'])) { + $record['courseid'] = 0; + } + + if (!isset($record['userid'])) { + $record['userid'] = 0; + } + + if (!isset($record['description'])) { + $record['description'] = 'Test scale description '.$i; + } + + if (!isset($record['descriptionformat'])) { + $record['descriptionformat'] = FORMAT_MOODLE; + } + + $record['timemodified'] = time(); + + if (isset($record['id'])) { + $DB->import_record('scale', $record); + $DB->get_manager()->reset_sequence('scale'); + $id = $record['id']; + } else { + $id = $DB->insert_record('scale', $record); + } + + return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST); + } } diff --git a/mod/workshop/allocation/random/tests/allocator_test.php b/mod/workshop/allocation/random/tests/allocator_test.php new file mode 100644 index 00000000000..55d79486146 --- /dev/null +++ b/mod/workshop/allocation/random/tests/allocator_test.php @@ -0,0 +1,323 @@ +. + +/** + * Unit tests for Random allocation + * + * @package workshopallocation_random + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); +require_once($CFG->dirroot . '/mod/workshop/allocation/random/lib.php'); + + +class workshopallocation_random_testcase extends basic_testcase { + + /** workshop instance emulation */ + protected $workshop; + + /** allocator instance */ + protected $allocator; + + protected function setUp() { + parent::setUp(); + + $cm = new stdclass(); + $course = new stdclass(); + $context = new stdclass(); + $workshop = (object)array('id' => 42); + $this->workshop = new workshop($workshop, $cm, $course, $context); + $this->allocator = new testable_workshop_random_allocator($this->workshop); + } + + protected function tearDown() { + $this->allocator = null; + $this->workshop = null; + parent::tearDown(); + } + + public function test_self_allocation_empty_values() { + // fixture setup & exercise SUT & verify + $this->assertEquals(array(), $this->allocator->self_allocation()); + } + + public function test_self_allocation_equal_user_groups() { + // fixture setup + $authors = array(0 => array_fill_keys(array(4, 6, 10), new stdclass())); + $reviewers = array(0 => array_fill_keys(array(4, 6, 10), new stdclass())); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers); + // verify + $this->assertEquals(array(array(4 => 4), array(6 => 6), array(10 => 10)), $newallocations); + } + + public function test_self_allocation_different_user_groups() { + // fixture setup + $authors = array(0 => array_fill_keys(array(1, 4, 5, 10, 13), new stdclass())); + $reviewers = array(0 => array_fill_keys(array(4, 7, 10), new stdclass())); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers); + // verify + $this->assertEquals(array(array(4 => 4), array(10 => 10)), $newallocations); + } + + public function test_self_allocation_skip_existing() { + // fixture setup + $authors = array(0 => array_fill_keys(array(3, 4, 10), new stdclass())); + $reviewers = array(0 => array_fill_keys(array(3, 4, 10), new stdclass())); + $assessments = array(23 => (object)array('authorid' => 3, 'reviewerid' => 3)); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers, $assessments); + // verify + $this->assertEquals(array(array(4 => 4), array(10 => 10)), $newallocations); + } + + public function test_get_author_ids() { + // fixture setup + $newallocations = array(array(1 => 3), array(2 => 1), array(3 => 1)); + // exercise SUT & verify + $this->assertEquals(array(3, 1), $this->allocator->get_author_ids($newallocations)); + } + + public function test_index_submissions_by_authors() { + // fixture setup + $submissions = array( + 676 => (object)array('id' => 676, 'authorid' => 23), + 121 => (object)array('id' => 121, 'authorid' => 56), + ); + // exercise SUT + $submissions = $this->allocator->index_submissions_by_authors($submissions); + // verify + $this->assertEquals(array( + 23 => (object)array('id' => 676, 'authorid' => 23), + 56 => (object)array('id' => 121, 'authorid' => 56), + ), $submissions); + } + + public function test_index_submissions_by_authors_duplicate_author() { + // fixture setup + $submissions = array( + 14 => (object)array('id' => 676, 'authorid' => 3), + 87 => (object)array('id' => 121, 'authorid' => 3), + ); + // set expectation + $this->setExpectedException('moodle_exception'); + // exercise SUT + $submissions = $this->allocator->index_submissions_by_authors($submissions); + } + + public function test_get_unique_allocations() { + // fixture setup + $newallocations = array(array(4 => 5), array(6 => 6), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $newallocations = $this->allocator->get_unique_allocations($newallocations); + // verify + $this->assertEquals(array(array(4 => 5), array(6 => 6), array(1 => 16), array(16 => 1)), $newallocations); + } + + public function test_get_unkept_assessments_no_keep_selfassessments() { + // fixture setup + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 6, 'reviewerid' => 3), + ); + $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, false); + // verify + // we want to keep $assessments[45] because it has been re-allocated + $this->assertEquals(array(23, 12), $delassessments); + } + + public function test_get_unkept_assessments_keep_selfassessments() { + // fixture setup + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 6, 'reviewerid' => 3), + ); + $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, true); + // verify + // we want to keep $assessments[45] because it has been re-allocated + // we want to keep $assessments[23] because if is self assessment + $this->assertEquals(array(12), $delassessments); + } + + /** + * Aggregates assessment info per author and per reviewer + */ + public function test_convert_assessments_to_links() { + // fixture setup + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 5, 'reviewerid' => 3), + ); + // exercise SUT + list($authorlinks, $reviewerlinks) = $this->allocator->convert_assessments_to_links($assessments); + // verify + $this->assertEquals(array(3 => array(3), 5 => array(11, 3)), $authorlinks); + $this->assertEquals(array(3 => array(3, 5), 11 => array(5)), $reviewerlinks); + } + + /** + * Trivial case + */ + public function test_convert_assessments_to_links_empty() { + // fixture setup + $assessments = array(); + // exercise SUT + list($authorlinks, $reviewerlinks) = $this->allocator->convert_assessments_to_links($assessments); + // verify + $this->assertEquals(array(), $authorlinks); + $this->assertEquals(array(), $reviewerlinks); + } + + /** + * If there is a single element with the lowest workload, it should be chosen + */ + public function test_get_element_with_lowest_workload_deterministic() { + // fixture setup + $workload = array(4 => 6, 9 => 1, 10 => 2); + // exercise SUT + $chosen = $this->allocator->get_element_with_lowest_workload($workload); + // verify + $this->assertEquals(9, $chosen); + } + + /** + * If there are no elements available, must return false + */ + public function test_get_element_with_lowest_workload_impossible() { + // fixture setup + $workload = array(); + // exercise SUT + $chosen = $this->allocator->get_element_with_lowest_workload($workload); + // verify + $this->assertTrue($chosen === false); + } + + /** + * If there are several elements with the lowest workload, one of them should be chosen randomly + */ + public function test_get_element_with_lowest_workload_random() { + // fixture setup + $workload = array(4 => 6, 9 => 2, 10 => 2); + // exercise SUT + $elements = $this->allocator->get_element_with_lowest_workload($workload); + // verify + // in theory, this test can fail even if the function works well. However, the probability of getting + // a row of a hundred same ids in this use case is 1/pow(2, 100) + // also, this just tests that each of the two elements has been chosen at least once. this is not to + // measure the quality or randomness of the algorithm + $counts = array(4 => 0, 9 => 0, 10 => 0); + for ($i = 0; $i < 100; $i++) { + $chosen = $this->allocator->get_element_with_lowest_workload($workload); + if (!in_array($chosen, array(4, 9, 10))) { + $this->fail('Invalid element ' . var_export($chosen, true) . ' chosen'); + break; + } else { + $counts[$this->allocator->get_element_with_lowest_workload($workload)]++; + } + } + $this->assertTrue(($counts[9] > 0) && ($counts[10] > 0)); + } + + /** + * Floats should be rounded before they are compared + * + * This should test + */ + public function test_get_element_with_lowest_workload_random_floats() { + // fixture setup + $workload = array(1 => 1/13, 2 => 0.0769230769231); // should be considered as the same value + // exercise SUT + $elements = $this->allocator->get_element_with_lowest_workload($workload); + // verify + $counts = array(1 => 0, 2 => 0); + for ($i = 0; $i < 100; $i++) { + $chosen = $this->allocator->get_element_with_lowest_workload($workload); + if (!in_array($chosen, array(1, 2))) { + $this->fail('Invalid element ' . var_export($chosen, true) . ' chosen'); + break; + } else { + $counts[$this->allocator->get_element_with_lowest_workload($workload)]++; + } + } + $this->assertTrue(($counts[1] > 0) && ($counts[2] > 0)); + + } + + /** + * Filter new assessments so they do not contain existing + */ + public function test_filter_current_assessments() { + // fixture setup + $newallocations = array(array(3 => 5), array(11 => 5), array(2 => 9), array(3 => 5)); + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 5, 'reviewerid' => 3), + ); + // exercise SUT + $this->allocator->filter_current_assessments($newallocations, $assessments); + // verify + $this->assertEquals(array_values($newallocations), array(array(2 => 9))); + } + + +} + + +/** + * Make protected methods we want to test public + */ +class testable_workshop_random_allocator extends workshop_random_allocator { + public function self_allocation($authors=array(), $reviewers=array(), $assessments=array()) { + return parent::self_allocation($authors, $reviewers, $assessments); + } + public function get_author_ids($newallocations) { + return parent::get_author_ids($newallocations); + } + public function index_submissions_by_authors($submissions) { + return parent::index_submissions_by_authors($submissions); + } + public function get_unique_allocations($newallocations) { + return parent::get_unique_allocations($newallocations); + } + public function get_unkept_assessments($assessments, $newallocations, $keepselfassessments) { + return parent::get_unkept_assessments($assessments, $newallocations, $keepselfassessments); + } + public function convert_assessments_to_links($assessments) { + return parent::convert_assessments_to_links($assessments); + } + public function get_element_with_lowest_workload($workload) { + return parent::get_element_with_lowest_workload($workload); + } + public function filter_current_assessments(&$newallocations, $assessments) { + return parent::filter_current_assessments($newallocations, $assessments); + } +} diff --git a/mod/workshop/eval/best/tests/lib_test.php b/mod/workshop/eval/best/tests/lib_test.php new file mode 100644 index 00000000000..9b31e36288f --- /dev/null +++ b/mod/workshop/eval/best/tests/lib_test.php @@ -0,0 +1,262 @@ +. + +/** + * Unit tests for grading evaluation method "best" + * + * @package workshopeval_best + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); +require_once($CFG->dirroot . '/mod/workshop/eval/best/lib.php'); +require_once($CFG->libdir . '/gradelib.php'); + + +class workshopeval_best_evaluation_testcase extends basic_testcase { + + /** workshop instance emulation */ + protected $workshop; + + /** instance of the grading evaluator being tested */ + protected $evaluator; + + /** + * Setup testing environment + */ + protected function setUp() { + parent::setUp(); + + $cm = new stdclass(); + $course = new stdclass(); + $context = new stdclass(); + $workshop = (object)array('id' => 42, 'evaluation' => 'best'); + $this->workshop = new workshop($workshop, $cm, $course, $context); + $this->evaluator = new testable_workshop_best_evaluation($this->workshop); + } + + protected function tearDown() { + $this->workshop = null; + $this->evaluator = null; + parent::tearDown(); + } + + public function test_normalize_grades() { + // fixture set-up + $assessments = array(); + $assessments[1] = (object)array( + 'dimgrades' => array(3 => 1.0000, 4 => 13.42300), + ); + $assessments[3] = (object)array( + 'dimgrades' => array(3 => 2.0000, 4 => 19.1000), + ); + $assessments[7] = (object)array( + 'dimgrades' => array(3 => 3.0000, 4 => 0.00000), + ); + $diminfo = array( + 3 => (object)array('min' => 1, 'max' => 3), + 4 => (object)array('min' => 0, 'max' => 20), + ); + // exercise SUT + $norm = $this->evaluator->normalize_grades($assessments, $diminfo); + // validate + $this->assertEquals(gettype($norm), 'array'); + // the following grades from a scale + $this->assertEquals($norm[1]->dimgrades[3], 0); + $this->assertEquals($norm[3]->dimgrades[3], 50); + $this->assertEquals($norm[7]->dimgrades[3], 100); + // the following grades from an interval 0 - 20 + $this->assertEquals($norm[1]->dimgrades[4], grade_floatval(13.423 / 20 * 100)); + $this->assertEquals($norm[3]->dimgrades[4], grade_floatval(19.1 / 20 * 100)); + $this->assertEquals($norm[7]->dimgrades[4], 0); + } + + public function test_normalize_grades_max_equals_min() { + // fixture set-up + $assessments = array(); + $assessments[1] = (object)array( + 'dimgrades' => array(3 => 100.0000), + ); + $diminfo = array( + 3 => (object)array('min' => 100, 'max' => 100), + ); + // exercise SUT + $norm = $this->evaluator->normalize_grades($assessments, $diminfo); + // validate + $this->assertEquals(gettype($norm), 'array'); + $this->assertEquals($norm[1]->dimgrades[3], 100); + } + + public function test_average_assessment_same_weights() { + // fixture set-up + $assessments = array(); + $assessments[18] = (object)array( + 'weight' => 1, + 'dimgrades' => array(1 => 50, 2 => 33.33333), + ); + $assessments[16] = (object)array( + 'weight' => 1, + 'dimgrades' => array(1 => 0, 2 => 66.66667), + ); + // exercise SUT + $average = $this->evaluator->average_assessment($assessments); + // validate + $this->assertEquals(gettype($average->dimgrades), 'array'); + $this->assertEquals(grade_floatval($average->dimgrades[1]), grade_floatval(25)); + $this->assertEquals(grade_floatval($average->dimgrades[2]), grade_floatval(50)); + } + + public function test_average_assessment_different_weights() { + // fixture set-up + $assessments = array(); + $assessments[11] = (object)array( + 'weight' => 1, + 'dimgrades' => array(3 => 10.0, 4 => 13.4, 5 => 95.0), + ); + $assessments[13] = (object)array( + 'weight' => 3, + 'dimgrades' => array(3 => 11.0, 4 => 10.1, 5 => 92.0), + ); + $assessments[17] = (object)array( + 'weight' => 1, + 'dimgrades' => array(3 => 11.0, 4 => 8.1, 5 => 88.0), + ); + // exercise SUT + $average = $this->evaluator->average_assessment($assessments); + // validate + $this->assertEquals(gettype($average->dimgrades), 'array'); + $this->assertEquals(grade_floatval($average->dimgrades[3]), grade_floatval((10.0 + 11.0*3 + 11.0)/5)); + $this->assertEquals(grade_floatval($average->dimgrades[4]), grade_floatval((13.4 + 10.1*3 + 8.1)/5)); + $this->assertEquals(grade_floatval($average->dimgrades[5]), grade_floatval((95.0 + 92.0*3 + 88.0)/5)); + } + + public function test_average_assessment_noweight() { + // fixture set-up + $assessments = array(); + $assessments[11] = (object)array( + 'weight' => 0, + 'dimgrades' => array(3 => 10.0, 4 => 13.4, 5 => 95.0), + ); + $assessments[17] = (object)array( + 'weight' => 0, + 'dimgrades' => array(3 => 11.0, 4 => 8.1, 5 => 88.0), + ); + // exercise SUT + $average = $this->evaluator->average_assessment($assessments); + // validate + $this->assertNull($average); + } + + public function test_weighted_variance() { + // fixture set-up + $assessments[11] = (object)array( + 'weight' => 1, + 'dimgrades' => array(3 => 11, 4 => 2), + ); + $assessments[13] = (object)array( + 'weight' => 3, + 'dimgrades' => array(3 => 11, 4 => 4), + ); + $assessments[17] = (object)array( + 'weight' => 2, + 'dimgrades' => array(3 => 11, 4 => 5), + ); + $assessments[20] = (object)array( + 'weight' => 1, + 'dimgrades' => array(3 => 11, 4 => 7), + ); + $assessments[25] = (object)array( + 'weight' => 1, + 'dimgrades' => array(3 => 11, 4 => 9), + ); + // exercise SUT + $variance = $this->evaluator->weighted_variance($assessments); + // validate + // dimension [3] have all the grades equal to 11 + $this->assertEquals($variance[3], 0); + // dimension [4] represents data 2, 4, 4, 4, 5, 5, 7, 9 having stdev=2 (stdev is sqrt of variance) + $this->assertEquals($variance[4], 4); + } + + public function test_assessments_distance_zero() { + // fixture set-up + $diminfo = array( + 3 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567), + 4 => (object)array('weight' => 1, 'min' => 1, 'max' => 5, 'variance' => 98.76543), + ); + $assessment1 = (object)array('dimgrades' => array(3 => 15, 4 => 2)); + $assessment2 = (object)array('dimgrades' => array(3 => 15, 4 => 2)); + $settings = (object)array('comparison' => 5); + // exercise SUT and validate + $this->assertEquals($this->evaluator->assessments_distance($assessment1, $assessment2, $diminfo, $settings), 0); + } + + public function test_assessments_distance_equals() { + /* + // fixture set-up + $diminfo = array( + 3 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567), + 4 => (object)array('weight' => 1, 'min' => 0, 'max' => 100, 'variance' => 12.34567), + ); + $assessment1 = (object)array('dimgrades' => array(3 => 25, 4 => 4)); + $assessment2 = (object)array('dimgrades' => array(3 => 75, 4 => 2)); + $referential = (object)array('dimgrades' => array(3 => 50, 4 => 3)); + $settings = (object)array('comparison' => 5); + // exercise SUT and validate + $this->assertEquals($this->evaluator->assessments_distance($assessment1, $referential, $diminfo, $settings), + $this->evaluator->assessments_distance($assessment2, $referential, $diminfo, $settings)); + */ + // fixture set-up + $diminfo = array( + 1 => (object)array('min' => 0, 'max' => 2, 'weight' => 1, 'variance' => 625), + 2 => (object)array('min' => 0, 'max' => 3, 'weight' => 1, 'variance' => 277.7778888889), + ); + $assessment1 = (object)array('dimgrades' => array(1 => 0, 2 => 66.66667)); + $assessment2 = (object)array('dimgrades' => array(1 => 50, 2 => 33.33333)); + $referential = (object)array('dimgrades' => array(1 => 25, 2 => 50)); + $settings = (object)array('comparison' => 9); + // exercise SUT and validate + $this->assertEquals($this->evaluator->assessments_distance($assessment1, $referential, $diminfo, $settings), + $this->evaluator->assessments_distance($assessment2, $referential, $diminfo, $settings)); + + } +} + + +/** + * Test subclass that makes all the protected methods we want to test public. + */ +class testable_workshop_best_evaluation extends workshop_best_evaluation { + + public function normalize_grades(array $assessments, array $diminfo) { + return parent::normalize_grades($assessments, $diminfo); + } + public function average_assessment(array $assessments) { + return parent::average_assessment($assessments); + } + public function weighted_variance(array $assessments) { + return parent::weighted_variance($assessments); + } + public function assessments_distance(stdclass $assessment, stdclass $referential, array $diminfo, stdclass $settings) { + return parent::assessments_distance($assessment, $referential, $diminfo, $settings); + } +} diff --git a/mod/workshop/form/accumulative/tests/lib_test.php b/mod/workshop/form/accumulative/tests/lib_test.php new file mode 100644 index 00000000000..f5ee5f723f9 --- /dev/null +++ b/mod/workshop/form/accumulative/tests/lib_test.php @@ -0,0 +1,207 @@ +. + +/** + * Unit tests for Accumulative grading strategy logic + * + * @package workshopform_accumulative + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); +require_once($CFG->dirroot . '/mod/workshop/form/accumulative/lib.php'); + + +class workshop_accumulative_strategy_testcase extends advanced_testcase { + /** workshop instance emulation */ + protected $workshop; + + /** @var testable_workshop_accumulative_strategy instance of the strategy logic class being tested */ + protected $strategy; + + /** + * Setup testing environment + */ + protected function setUp() { + parent::setUp(); + + $cm = new stdclass(); + $course = new stdclass(); + $context = new stdclass(); + $workshop = (object)array('id' => 42, 'strategy' => 'accumulative'); + $this->workshop = new workshop($workshop, $cm, $course, $context); + $this->strategy = new testable_workshop_accumulative_strategy($this->workshop); + } + + protected function tearDown() { + $this->workshop = null; + $this->strategy = null; + parent::tearDown(); + } + + public function test_calculate_peer_grade_null_grade() { + // fixture set-up + $this->strategy->dimensions = array(); + $grades = array(); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertNull($suggested); + } + + public function test_calculate_peer_grade_one_numerical() { + // fixture set-up + $this->strategy->dimensions[1003] = (object)array('grade' => '20', 'weight' => '1'); + $grades[] = (object)array('dimensionid' => 1003, 'grade' => '5.00000'); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals(grade_floatval(5/20 * 100), $suggested); + } + + public function test_calculate_peer_grade_negative_weight() { + // fixture set-up + $this->strategy->dimensions[1003] = (object)array('grade' => '20', 'weight' => '-1'); + $grades[] = (object)array('dimensionid' => 1003, 'grade' => '20'); + $this->setExpectedException('coding_exception'); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + } + + public function test_calculate_peer_grade_one_numerical_weighted() { + // fixture set-up + $this->strategy->dimensions[1003] = (object)array('grade' => '20', 'weight' => '3'); + $grades[] = (object)array('dimensionid' => '1003', 'grade' => '5'); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals(grade_floatval(5/20 * 100), $suggested); + } + + public function test_calculate_peer_grade_three_numericals_same_weight() { + // fixture set-up + $this->strategy->dimensions[1003] = (object)array('grade' => '20', 'weight' => '2'); + $this->strategy->dimensions[1004] = (object)array('grade' => '100', 'weight' => '2'); + $this->strategy->dimensions[1005] = (object)array('grade' => '10', 'weight' => '2'); + $grades[] = (object)array('dimensionid' => 1003, 'grade' => '11.00000'); + $grades[] = (object)array('dimensionid' => 1004, 'grade' => '87.00000'); + $grades[] = (object)array('dimensionid' => 1005, 'grade' => '10.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + + // validate + $this->assertEquals(grade_floatval((11/20 + 87/100 + 10/10)/3 * 100), $suggested); + } + + public function test_calculate_peer_grade_three_numericals_different_weights() { + // fixture set-up + $this->strategy->dimensions[1003] = (object)array('grade' => '15', 'weight' => 3); + $this->strategy->dimensions[1004] = (object)array('grade' => '80', 'weight' => 1); + $this->strategy->dimensions[1005] = (object)array('grade' => '5', 'weight' => 2); + $grades[] = (object)array('dimensionid' => 1003, 'grade' => '7.00000'); + $grades[] = (object)array('dimensionid' => 1004, 'grade' => '66.00000'); + $grades[] = (object)array('dimensionid' => 1005, 'grade' => '4.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + + // validate + $this->assertEquals(grade_floatval((7/15*3 + 66/80*1 + 4/5*2)/6 * 100), $suggested); + } + + public function test_calculate_peer_grade_one_scale_max() { + $this->resetAfterTest(true); + + // fixture set-up + $scale11 = $this->getDataGenerator()->create_scale(array('scale'=>'E,D,C,B,A', 'id'=>11)); + $this->strategy->dimensions[1008] = (object)array('grade' => (-$scale11->id), 'weight' => 1); + $grades[] = (object)array('dimensionid' => 1008, 'grade' => '5.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + + // validate + $this->assertEquals(100.00000, $suggested); + } + + public function test_calculate_peer_grade_one_scale_min_with_scale_caching() { + $this->resetAfterTest(true); + + // fixture set-up + $scale11 = $this->getDataGenerator()->create_scale(array('scale'=>'E,D,C,B,A', 'id'=>11)); + $this->strategy->dimensions[1008] = (object)array('grade' => (-$scale11->id), 'weight' => 1); + $grades[] = (object)array('dimensionid' => 1008, 'grade' => '1.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + + // validate + $this->assertEquals(0.00000, $suggested); + } + + public function test_calculate_peer_grade_two_scales_weighted() { + $this->resetAfterTest(true); + // fixture set-up + $scale13 = $this->getDataGenerator()->create_scale(array('scale'=>'Poor,Good,Excellent', 'id'=>13)); + $scale17 = $this->getDataGenerator()->create_scale(array('scale'=>'-,*,**,***,****,*****,******', 'id'=>17)); + $this->strategy->dimensions[1012] = (object)array('grade' => (-$scale13->id), 'weight' => 2); + $this->strategy->dimensions[1019] = (object)array('grade' => (-$scale17->id), 'weight' => 3); + $grades[] = (object)array('dimensionid' => 1012, 'grade' => '2.00000'); // "Good" + $grades[] = (object)array('dimensionid' => 1019, 'grade' => '5.00000'); // "****" + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + + // validate + $this->assertEquals(grade_floatval((1/2*2 + 4/6*3)/5 * 100), $suggested); + } + + public function test_calculate_peer_grade_scale_exception() { + $this->resetAfterTest(true); + // fixture set-up + $scale13 = $this->getDataGenerator()->create_scale(array('scale'=>'Poor,Good,Excellent', 'id'=>13)); + $this->strategy->dimensions[1012] = (object)array('grade' => (-$scale13->id), 'weight' => 1); + $grades[] = (object)array('dimensionid' => 1012, 'grade' => '4.00000'); // exceeds the number of scale items + + // exercise SUT + $this->setExpectedException('coding_exception'); + $suggested = $this->strategy->calculate_peer_grade($grades); + } +} + + +/** + * Test subclass that makes all the protected methods we want to test public + */ +class testable_workshop_accumulative_strategy extends workshop_accumulative_strategy { + + /** allows to set dimensions manually */ + public $dimensions = array(); + + /** + * This is where the calculation of suggested grade for submission is done + */ + public function calculate_peer_grade(array $grades) { + return parent::calculate_peer_grade($grades); + } +} diff --git a/mod/workshop/form/numerrors/tests/lib_test.php b/mod/workshop/form/numerrors/tests/lib_test.php new file mode 100644 index 00000000000..77f32dbf153 --- /dev/null +++ b/mod/workshop/form/numerrors/tests/lib_test.php @@ -0,0 +1,257 @@ +. + +/** + * Unit tests for Number of errors grading logic + * + * @package workshopform_numerrors + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); +require_once($CFG->dirroot . '/mod/workshop/form/numerrors/lib.php'); + + +class workshopform_numerrors_strategy_testcase extends advanced_testcase { + + /** workshop instance emulation */ + protected $workshop; + + /** instance of the strategy logic class being tested */ + protected $strategy; + + /** + * Setup testing environment + */ + protected function setUp() { + parent::setUp(); + + $cm = new stdclass(); + $course = new stdclass(); + $context = new stdclass(); + $workshop = (object)array('id' => 42, 'strategy' => 'numerrors'); + $this->workshop = new workshop($workshop, $cm, $course, $context); + $this->strategy = new testable_workshop_numerrors_strategy($this->workshop); + } + + protected function tearDown() { + $this->workshop = null; + $this->strategy = null; + parent::tearDown(); + } + + public function test_calculate_peer_grade_null_grade() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->mappings = array(); + $grades = array(); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertNull($suggested); + } + + public function test_calculate_peer_grade_no_error() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1'); + $this->strategy->dimensions[109] = (object)array('weight' => '1'); + $this->strategy->dimensions[111] = (object)array('weight' => '1'); + $this->strategy->mappings = array(); + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '1.00000'); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 100.00000); + } + + public function test_calculate_peer_grade_one_error() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1'); + $this->strategy->dimensions[109] = (object)array('weight' => '1'); + $this->strategy->dimensions[111] = (object)array('weight' => '1'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '80.00000'), + 2 => (object)array('grade' => '60.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '1.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 80.00000); + } + + public function test_calculate_peer_grade_three_errors_same_weight_a() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1.00000'); + $this->strategy->dimensions[109] = (object)array('weight' => '1.00000'); + $this->strategy->dimensions[111] = (object)array('weight' => '1.00000'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '80.00000'), + 2 => (object)array('grade' => '60.00000'), + 3 => (object)array('grade' => '10.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '0.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 10.00000); + } + + public function test_calculate_peer_grade_three_errors_same_weight_b() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1.00000'); + $this->strategy->dimensions[109] = (object)array('weight' => '1.00000'); + $this->strategy->dimensions[111] = (object)array('weight' => '1.00000'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '80.00000'), + 2 => (object)array('grade' => '60.00000'), + 3 => (object)array('grade' => '0.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '0.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 0.00000); + } + + public function test_calculate_peer_grade_one_error_weighted() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1'); + $this->strategy->dimensions[109] = (object)array('weight' => '2'); + $this->strategy->dimensions[111] = (object)array('weight' => '0'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '66.00000'), + 2 => (object)array('grade' => '33.00000'), + 3 => (object)array('grade' => '0.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '0.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 33.00000); + } + + public function test_calculate_peer_grade_zero_weight() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1'); + $this->strategy->dimensions[109] = (object)array('weight' => '2'); + $this->strategy->dimensions[111] = (object)array('weight' => '0'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '66.00000'), + 2 => (object)array('grade' => '33.00000'), + 3 => (object)array('grade' => '0.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '1.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '1.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 100.00000); + } + + public function test_calculate_peer_grade_sum_weight() { + // fixture set-up + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => '1'); + $this->strategy->dimensions[109] = (object)array('weight' => '2'); + $this->strategy->dimensions[111] = (object)array('weight' => '3'); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => '90.00000'), + 2 => (object)array('grade' => '80.00000'), + 3 => (object)array('grade' => '70.00000'), + 4 => (object)array('grade' => '60.00000'), + 5 => (object)array('grade' => '30.00000'), + 6 => (object)array('grade' => '5.00000'), + 7 => (object)array('grade' => '0.00000'), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 111, 'grade' => '0.00000'); + $grades[] = (object)array('dimensionid' => 109, 'grade' => '0.00000'); + + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals($suggested, 5.00000); + } +} + + +/** + * Test subclass that makes all the protected methods we want to test public + */ +class testable_workshop_numerrors_strategy extends workshop_numerrors_strategy { + + /** allows to set dimensions manually */ + public $dimensions = array(); + + /** allow to set mappings manually */ + public $mappings = array(); + + /** + * This is where the calculation of suggested grade for submission is done + */ + public function calculate_peer_grade(array $grades) { + return parent::calculate_peer_grade($grades); + } +} diff --git a/mod/workshop/form/rubric/tests/lib_test.php b/mod/workshop/form/rubric/tests/lib_test.php new file mode 100644 index 00000000000..1137e0e5b64 --- /dev/null +++ b/mod/workshop/form/rubric/tests/lib_test.php @@ -0,0 +1,146 @@ +. + +/** + * Unit tests for Rubric grading strategy logic + * + * @package workshopform_rubric + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); +require_once($CFG->dirroot . '/mod/workshop/form/rubric/lib.php'); + + +class workshopform_rubric_strategy_test extends advanced_testcase { + + /** workshop instance emulation */ + protected $workshop; + + /** instance of the strategy logic class being tested */ + protected $strategy; + + /** + * Setup testing environment + */ + protected function setUp() { + parent::setUser(); + $cm = new stdclass(); + $course = new stdclass(); + $context = new stdclass(); + $workshop = (object)array('id' => 42, 'strategy' => 'rubric'); + $this->workshop = new workshop($workshop, $cm, $course, $context); + $this->strategy = new testable_workshop_rubric_strategy($this->workshop); + + // prepare dimensions definition + $dim = new stdclass(); + $dim->id = 6; + $dim->levels[10] = (object)array('id' => 10, 'grade' => 0); + $dim->levels[13] = (object)array('id' => 13, 'grade' => 2); + $dim->levels[14] = (object)array('id' => 14, 'grade' => 6); + $dim->levels[16] = (object)array('id' => 16, 'grade' => 8); + $this->strategy->dimensions[$dim->id] = $dim; + + $dim = new stdclass(); + $dim->id = 8; + $dim->levels[17] = (object)array('id' => 17, 'grade' => 0); + $dim->levels[18] = (object)array('id' => 18, 'grade' => 1); + $dim->levels[19] = (object)array('id' => 19, 'grade' => 2); + $dim->levels[20] = (object)array('id' => 20, 'grade' => 3); + $this->strategy->dimensions[$dim->id] = $dim; + + $dim = new stdclass(); + $dim->id = 10; + $dim->levels[27] = (object)array('id' => 27, 'grade' => 10); + $dim->levels[28] = (object)array('id' => 28, 'grade' => 20); + $dim->levels[29] = (object)array('id' => 29, 'grade' => 30); + $dim->levels[30] = (object)array('id' => 30, 'grade' => 40); + $this->strategy->dimensions[$dim->id] = $dim; + + } + + protected function tearDown() { + $this->strategy = null; + $this->workshop = null; + parent::tearDown(); + } + + public function test_calculate_peer_grade_null_grade() { + // fixture set-up + $grades = array(); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertNull($suggested); + } + + public function test_calculate_peer_grade_worst_possible() { + // fixture set-up + $grades[6] = (object)array('dimensionid' => 6, 'grade' => 0); + $grades[8] = (object)array('dimensionid' => 8, 'grade' => 0); + $grades[10] = (object)array('dimensionid' => 10, 'grade' => 10); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals(grade_floatval($suggested), 0.00000); + } + + public function test_calculate_peer_grade_best_possible() { + // fixture set-up + $grades[6] = (object)array('dimensionid' => 6, 'grade' => 8); + $grades[8] = (object)array('dimensionid' => 8, 'grade' => 3); + $grades[10] = (object)array('dimensionid' => 10, 'grade' => 40); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + $this->assertEquals(grade_floatval($suggested), 100.00000); + } + + public function test_calculate_peer_grade_something() { + // fixture set-up + $grades[6] = (object)array('dimensionid' => 6, 'grade' => 2); + $grades[8] = (object)array('dimensionid' => 8, 'grade' => 2); + $grades[10] = (object)array('dimensionid' => 10, 'grade' => 30); + // exercise SUT + $suggested = $this->strategy->calculate_peer_grade($grades); + // validate + // minimal rubric score is 10, maximal is 51. We have 34 here + $this->assertEquals(grade_floatval($suggested), grade_floatval(100 * 24 / 41)); + } +} + + +/** + * Test subclass that makes all the protected methods we want to test public + */ +class testable_workshop_rubric_strategy extends workshop_rubric_strategy { + + /** allows to set dimensions manually */ + public $dimensions = array(); + + /** + * This is where the calculation of suggested grade for submission is done + */ + public function calculate_peer_grade(array $grades) { + return parent::calculate_peer_grade($grades); + } +} diff --git a/mod/workshop/tests/locallib_test.php b/mod/workshop/tests/locallib_test.php new file mode 100644 index 00000000000..44e6cd5868c --- /dev/null +++ b/mod/workshop/tests/locallib_test.php @@ -0,0 +1,419 @@ +. + +/** + * Unit tests for workshop api class defined in mod/workshop/locallib.php + * + * @package mod_workshop + * @category phpunit + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); // Include the code to test + + +/** + * Test cases for the internal workshop api + */ +class mod_workshop_internal_api_testcase extends advanced_testcase { + + /** workshop instance emulation */ + protected $workshop; + + /** setup testing environment */ + protected function setUp() { + parent::setUp(); + + $this->workshop = new testable_workshop(); + } + + protected function tearDown() { + $this->workshop = null; + parent::tearDown(); + } + + public function test_aggregate_submission_grades_process_notgraded() { + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => null); + //$DB->expectNever('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_single() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.12345); + $expected = 10.12345; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_null_doesnt_influence() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => 45.54321); + $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => null); + $expected = 45.54321; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_weighted_single() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 4, 'grade' => 14.00012); + $expected = 14.00012; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_mean() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 56.12000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 12.59000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 0.00000); + $expected = 19.67750; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_mean_changed() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 56.12000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 12.59000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 10.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 0.00000); + $expected = 19.67750; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_mean_nochange() { + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 56.12000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 12.59000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 10.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 0.00000); + //$DB->expectNever('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_rounding() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 4.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 2.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 1.00000); + $expected = 2.33333; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_submission_grades_process_weighted_mean() { + $this->resetAfterTest(true); + + // fixture set-up + $batch = array(); // batch of a submission's assessments + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 3, 'grade' => 12.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 2, 'grade' => 30.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.00000); + $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 0, 'grade' => 1000.00000); + $expected = 17.66667; + //$DB->expectOnce('update_record'); + // exercise SUT + $this->workshop->aggregate_submission_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_nograding() { + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>2, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null); + // expectation + //$DB->expectNever('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_single_grade_new() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>82.87670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null); + // expectation + $now = time(); + $expected = new stdclass(); + $expected->workshopid = $this->workshop->id; + $expected->userid = 3; + $expected->gradinggrade = 82.87670; + $expected->timegraded = $now; + //$DB->expectOnce('insert_record', array('workshop_aggregations', $expected)); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch, $now); + } + + public function test_aggregate_grading_grades_process_single_grade_update() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>82.87670); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_single_grade_uptodate() { + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>90.00000); + // expectation + //$DB->expectNever('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_single_grade_overridden() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>4, 'gradinggrade'=>91.56700, 'gradinggradeover'=>82.32105, 'aggregationid'=>2, 'aggregatedgrade'=>91.56700); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_multiple_grades_new() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>99.45670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null); + // expectation + $now = time(); + $expected = new stdclass(); + $expected->workshopid = $this->workshop->id; + $expected->userid = 5; + $expected->gradinggrade = 79.3066; + $expected->timegraded = $now; + //$DB->expectOnce('insert_record', array('workshop_aggregations', $expected)); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch, $now); + } + + public function test_aggregate_grading_grades_process_multiple_grades_update() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_multiple_grades_overriden() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>99.45670, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904); + $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_multiple_grades_one_missing() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_aggregate_grading_grades_process_multiple_grades_missing_overridden() { + $this->resetAfterTest(true); + // fixture set-up + $batch = array(); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>69.00000, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000); + // expectation + //$DB->expectOnce('update_record'); + // excersise SUT + $this->workshop->aggregate_grading_grades_process($batch); + } + + public function test_percent_to_value() { + // fixture setup + $total = 185; + $percent = 56.6543; + // exercise SUT + $part = workshop::percent_to_value($percent, $total); + // verify + $this->assertEquals($part, $total * $percent / 100); + } + + public function test_percent_to_value_negative() { + // fixture setup + $total = 185; + $percent = -7.098; + // set expectation + $this->setExpectedException('coding_exception'); + // exercise SUT + $part = workshop::percent_to_value($percent, $total); + } + + public function test_percent_to_value_over_hundred() { + // fixture setup + $total = 185; + $percent = 121.08; + // set expectation + $this->setExpectedException('coding_exception'); + // exercise SUT + $part = workshop::percent_to_value($percent, $total); + } + + public function test_lcm() { + // fixture setup + exercise SUT + verify in one step + $this->assertEquals(workshop::lcm(1,4), 4); + $this->assertEquals(workshop::lcm(2,4), 4); + $this->assertEquals(workshop::lcm(4,2), 4); + $this->assertEquals(workshop::lcm(2,3), 6); + $this->assertEquals(workshop::lcm(6,4), 12); + } + + public function test_lcm_array() { + // fixture setup + $numbers = array(5,3,15); + // excersise SUT + $lcm = array_reduce($numbers, 'workshop::lcm', 1); + // verify + $this->assertEquals($lcm, 15); + } + + public function test_prepare_example_assessment() { + // fixture setup + $fakerawrecord = (object)array( + 'id' => 42, + 'submissionid' => 56, + 'weight' => 0, + 'timecreated' => time() - 10, + 'timemodified' => time() - 5, + 'grade' => null, + 'gradinggrade' => null, + 'gradinggradeover' => null, + ); + // excersise SUT + $a = $this->workshop->prepare_example_assessment($fakerawrecord); + // verify + $this->assertTrue($a instanceof workshop_example_assessment); + $this->assertTrue($a->url instanceof moodle_url); + + // modify setup + $fakerawrecord->weight = 1; + $this->setExpectedException('coding_exception'); + // excersise SUT + $a = $this->workshop->prepare_example_assessment($fakerawrecord); + } + + public function test_prepare_example_reference_assessment() { + global $USER; + // fixture setup + $fakerawrecord = (object)array( + 'id' => 38, + 'submissionid' => 56, + 'weight' => 1, + 'timecreated' => time() - 100, + 'timemodified' => time() - 50, + 'grade' => 0.75000, + 'gradinggrade' => 1.00000, + 'gradinggradeover' => null, + ); + // excersise SUT + $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord); + // verify + $this->assertTrue($a instanceof workshop_example_reference_assessment); + + // modify setup + $fakerawrecord->weight = 0; + $this->setExpectedException('coding_exception'); + // excersise SUT + $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord); + } +} + + +/** + * Test subclass that makes all the protected methods we want to test public. + */ +class testable_workshop extends workshop { + + public function __construct() { + $this->id = 16; + $this->cm = new stdclass(); + $this->course = new stdclass(); + $this->context = new stdclass(); + } + + public function aggregate_submission_grades_process(array $assessments) { + parent::aggregate_submission_grades_process($assessments); + } + + public function aggregate_grading_grades_process(array $assessments, $timegraded = null) { + parent::aggregate_grading_grades_process($assessments, $timegraded); + } + +}