From 948d46da34350256f264ed513fe0519c2d12d7d6 Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Sun, 8 Apr 2012 11:30:44 +0200 Subject: [PATCH] MDL-32323 add grade tests --- lib/grade/tests/fixtures/lib.php | 773 ++++++++++++++++++++++++ lib/grade/tests/grade_category_test.php | 646 ++++++++++++++++++++ lib/grade/tests/grade_grade_test.php | 196 ++++++ lib/grade/tests/grade_item_test.php | 547 +++++++++++++++++ lib/grade/tests/grade_outcome_test.php | 106 ++++ lib/grade/tests/grade_scale_test.php | 130 ++++ phpunit.xml.dist | 9 +- 7 files changed, 2403 insertions(+), 4 deletions(-) create mode 100644 lib/grade/tests/fixtures/lib.php create mode 100644 lib/grade/tests/grade_category_test.php create mode 100644 lib/grade/tests/grade_grade_test.php create mode 100644 lib/grade/tests/grade_item_test.php create mode 100644 lib/grade/tests/grade_outcome_test.php create mode 100644 lib/grade/tests/grade_scale_test.php diff --git a/lib/grade/tests/fixtures/lib.php b/lib/grade/tests/fixtures/lib.php new file mode 100644 index 00000000000..7ca25207d7e --- /dev/null +++ b/lib/grade/tests/fixtures/lib.php @@ -0,0 +1,773 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/gradelib.php'); + + +/** + * Shared code for all grade related tests. + * + * Here is a brief explanation of the test data set up in these unit tests. + * category1 => array(category2 => array(grade_item1, grade_item2), category3 => array(grade_item3)) + * 3 users for 3 grade_items + */ +class grade_base_testcase extends advanced_testcase { + + protected $course; + protected $activities = array(); + protected $grade_items = array(); + protected $grade_categories = array(); + protected $grade_grades = array(); + protected $grade_outcomes = array(); + protected $scale = array(); + protected $scalemax = array(); + + protected $courseid; + protected $userid; + + protected function setUp() { + global $CFG; + parent::setup(); + + $this->resetAfterTest(true); + + $CFG->grade_droplow = -1; + $CFG->grade_keephigh = -1; + $CFG->grade_aggregation = -1; + $CFG->grade_aggregateonlygraded = -1; + $CFG->grade_aggregateoutcomes = -1; + $CFG->grade_aggregatesubcats = -1; + + $this->course = $this->getDataGenerator()->create_course(); + $this->courseid = $this->course->id; + + $this->user[0] = $this->getDataGenerator()->create_user(); + $this->user[1] = $this->getDataGenerator()->create_user(); + $this->user[2] = $this->getDataGenerator()->create_user(); + $this->user[3] = $this->getDataGenerator()->create_user(); + $this->userid = $this->user[0]->id; + + $this->load_modules(); + + $this->load_scales(); + $this->load_grade_categories(); + $this->load_grade_items(); + $this->load_grade_grades(); + $this->load_grade_outcomes(); + } + + public function test_void () { + // empty method to keep PHPUnit happy + } + + private function load_modules() { + $this->activities[0] = $this->getDataGenerator()->create_module('assignment', array('course'=>$this->course->id)); + $this->course_module[0] = get_coursemodule_from_instance('assignment', $this->activities[0]->id); + + $this->activities[1] = $this->getDataGenerator()->create_module('assignment', array('course'=>$this->course->id)); + $this->course_module[1] = get_coursemodule_from_instance('assignment', $this->activities[1]->id); + + $this->activities[2] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id)); + $this->course_module[2] = get_coursemodule_from_instance('forum', $this->activities[2]->id); + + $this->activities[3] = $this->getDataGenerator()->create_module('page', array('course'=>$this->course->id)); + $this->course_module[3] = get_coursemodule_from_instance('page', $this->activities[3]->id); + + $this->activities[4] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id)); + $this->course_module[4] = get_coursemodule_from_instance('forum', $this->activities[4]->id); + + $this->activities[5] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id)); + $this->course_module[5] = get_coursemodule_from_instance('forum', $this->activities[5]->id); + + $this->activities[6] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id)); + $this->course_module[6] = get_coursemodule_from_instance('forum', $this->activities[6]->id); + } + + private function load_scales() { + $scale = new stdClass(); + $scale->name = 'unittestscale1'; + $scale->courseid = $this->course->id; + $scale->userid = $this->user[0]->id; + $scale->scale = 'Way off topic, Not very helpful, Fairly neutral, Fairly helpful, Supportive, Some good information, Perfect answer!'; + $scale->description = 'This scale defines some of qualities that make posts helpful within the Moodle help forums.\n Your feedback will help others see how their posts are being received.'; + + $this->scale[0] = $this->getDataGenerator()->create_scale($scale); + $this->scalemax[0] = substr_count($scale->scale, ','); + + $scale = new stdClass(); + $scale->name = 'unittestscale2'; + $scale->courseid = $this->course->id; + $scale->userid = $this->user[0]->id; + $scale->scale = 'Distinction, Very Good, Good, Pass, Fail'; + $scale->description = 'This scale is used to mark standard assignments.'; + + $this->scale[1] = $this->getDataGenerator()->create_scale($scale); + $this->scalemax[1] = substr_count($scale->scale, ','); + + $scale = new stdClass(); + $scale->name = 'unittestscale3'; + $scale->courseid = $this->course->id; + $scale->userid = $this->user[0]->id; + $scale->scale = 'Loner, Contentious, Disinterested, Participative, Follower, Leader'; + $scale->description = 'Describes the level of teamwork of a student.'; + $temp = explode(',', $scale->scale); + $scale->max = count($temp) -1; + + $this->scale[2] = $this->getDataGenerator()->create_scale($scale); + $this->scalemax[2] = substr_count($scale->scale, ','); + + $scale = new stdClass(); + $scale->name = 'unittestscale4'; + $scale->courseid = $this->course->id; + $scale->userid = $this->user[0]->id; + $scale->scale = 'Does not understand theory, Understands theory but fails practice, Manages through, Excels'; + $scale->description = 'Level of expertise at a technical task, with a theoretical framework.'; + $temp = explode(',', $scale->scale); + $scale->max = count($temp) -1; + + $this->scale[3] = $this->getDataGenerator()->create_scale($scale); + $this->scalemax[3] = substr_count($scale->scale, ','); + + $scale = new stdClass(); + $scale->name = 'unittestscale5'; + $scale->courseid = $this->course->id; + $scale->userid = $this->user[0]->id; + $scale->scale = 'Insufficient, Acceptable, Excellent.'; + $scale->description = 'Description of skills.'; + + $this->scale[4] = $this->getDataGenerator()->create_scale($scale); + $this->scalemax[4] = substr_count($scale->scale, ','); + } + + /** + * Load grade_category data into the database, and adds the corresponding objects to this class' variable. + * category structure: + course category + | + +--------+-------------+ + | | + unittestcategory1 level1category + | + +--------+-------------+ + | | + unittestcategory2 unittestcategory3 + */ + private function load_grade_categories() { + global $DB; + + $course_category = grade_category::fetch_course_category($this->course->id); + + $grade_category = new stdClass(); + + $grade_category->fullname = 'unittestcategory1'; + $grade_category->courseid = $this->course->id; + $grade_category->aggregation = GRADE_AGGREGATE_MEAN; + $grade_category->aggregateonlygraded = 1; + $grade_category->keephigh = 0; + $grade_category->droplow = 0; + $grade_category->parent = $course_category->id; + $grade_category->timecreated = time(); + $grade_category->timemodified = time(); + $grade_category->depth = 2; + + $grade_category->id = $DB->insert_record('grade_categories', $grade_category); + $grade_category->path = '/'.$course_category->id.'/'.$grade_category->id.'/'; + $DB->update_record('grade_categories', $grade_category); + $this->grade_categories[0] = $grade_category; + + $grade_category = new stdClass(); + + $grade_category->fullname = 'unittestcategory2'; + $grade_category->courseid = $this->course->id; + $grade_category->aggregation = GRADE_AGGREGATE_MEAN; + $grade_category->aggregateonlygraded = 1; + $grade_category->keephigh = 0; + $grade_category->droplow = 0; + $grade_category->parent = $this->grade_categories[0]->id; + $grade_category->timecreated = time(); + $grade_category->timemodified = time(); + $grade_category->depth = 3; + + $grade_category->id = $DB->insert_record('grade_categories', $grade_category); + $grade_category->path = $this->grade_categories[0]->path.$grade_category->id.'/'; + $DB->update_record('grade_categories', $grade_category); + $this->grade_categories[1] = $grade_category; + + $grade_category = new stdClass(); + + $grade_category->fullname = 'unittestcategory3'; + $grade_category->courseid = $this->course->id; + $grade_category->aggregation = GRADE_AGGREGATE_MEAN; + $grade_category->aggregateonlygraded = 1; + $grade_category->keephigh = 0; + $grade_category->droplow = 0; + $grade_category->parent = $this->grade_categories[0]->id; + $grade_category->timecreated = time(); + $grade_category->timemodified = time(); + $grade_category->depth = 3; + + $grade_category->id = $DB->insert_record('grade_categories', $grade_category); + $grade_category->path = $this->grade_categories[0]->path.$grade_category->id.'/'; + $DB->update_record('grade_categories', $grade_category); + $this->grade_categories[2] = $grade_category; + + // A category with no parent, but grade_items as children + + $grade_category = new stdClass(); + + $grade_category->fullname = 'level1category'; + $grade_category->courseid = $this->course->id; + $grade_category->aggregation = GRADE_AGGREGATE_MEAN; + $grade_category->aggregateonlygraded = 1; + $grade_category->keephigh = 0; + $grade_category->droplow = 0; + $grade_category->parent = $course_category->id; + $grade_category->timecreated = time(); + $grade_category->timemodified = time(); + $grade_category->depth = 2; + + $grade_category->id = $DB->insert_record('grade_categories', $grade_category); + $grade_category->path = '/'.$course_category->id.'/'.$grade_category->id.'/'; + $DB->update_record('grade_categories', $grade_category); + $this->grade_categories[3] = $grade_category; + } + + /** + * Load grade_item data into the database, and adds the corresponding objects to this class' variable. + */ + protected function load_grade_items() { + global $DB; + + $course_category = grade_category::fetch_course_category($this->course->id); + + // id = 0 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $this->grade_categories[1]->id; + $grade_item->itemname = 'unittestgradeitem1'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = $this->course_module[0]->modname; + $grade_item->iteminstance = $this->course_module[0]->instance; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 30; + $grade_item->grademax = 110; + $grade_item->itemnumber = 1; + $grade_item->idnumber = 'item id 0'; + $grade_item->iteminfo = 'Grade item 0 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 3; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[0] = $grade_item; + + // id = 1 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $this->grade_categories[1]->id; + $grade_item->itemname = 'unittestgradeitem2'; + $grade_item->itemtype = 'import'; + $grade_item->itemmodule = $this->course_module[1]->modname; + $grade_item->iteminstance = $this->course_module[1]->instance; + $grade_item->calculation = '= ##gi'.$this->grade_items[0]->id.'## + 30 + [[item id 0]] - [[item id 0]]'; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->itemnumber = null; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Grade item 1 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 4; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[1] = $grade_item; + + // id = 2 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $this->grade_categories[2]->id; + $grade_item->itemname = 'unittestgradeitem3'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = $this->course_module[2]->modname; + $grade_item->iteminstance = $this->course_module[2]->instance; + $grade_item->gradetype = GRADE_TYPE_SCALE; + $grade_item->scaleid = $this->scale[0]->id; + $grade_item->grademin = 0; + $grade_item->grademax = $this->scalemax[0]; + $grade_item->iteminfo = 'Grade item 2 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 6; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[2] = $grade_item; + + // Load grade_items associated with the 3 categories + // id = 3 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->iteminstance = $this->grade_categories[0]->id; + $grade_item->itemname = 'unittestgradeitemcategory1'; + $grade_item->needsupdate = 0; + $grade_item->itemtype = 'category'; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Grade item 3 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 1; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[3] = $grade_item; + + // id = 4 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->iteminstance = $this->grade_categories[1]->id; + $grade_item->itemname = 'unittestgradeitemcategory2'; + $grade_item->itemtype = 'category'; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->needsupdate = 0; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Grade item 4 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 2; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[4] = $grade_item; + + // id = 5 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->iteminstance = $this->grade_categories[2]->id; + $grade_item->itemname = 'unittestgradeitemcategory3'; + $grade_item->itemtype = 'category'; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->needsupdate = true; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Grade item 5 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 5; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[5] = $grade_item; + + // Orphan grade_item + // id = 6 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $course_category->id; + $grade_item->itemname = 'unittestorphangradeitem1'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = $this->course_module[4]->modname; + $grade_item->iteminstance = $this->course_module[4]->instance; + $grade_item->itemnumber = 0; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 10; + $grade_item->grademax = 120; + $grade_item->locked = time(); + $grade_item->iteminfo = 'Orphan Grade 6 item used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 7; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[6] = $grade_item; + + // 2 grade items under level1category + // id = 7 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $this->grade_categories[3]->id; + $grade_item->itemname = 'singleparentitem1'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = $this->course_module[5]->modname; + $grade_item->iteminstance = $this->course_module[5]->instance; + $grade_item->gradetype = GRADE_TYPE_SCALE; + $grade_item->scaleid = $this->scale[0]->id; + $grade_item->grademin = 0; + $grade_item->grademax = $this->scalemax[0]; + $grade_item->iteminfo = 'Grade item 7 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 9; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[7] = $grade_item; + + // id = 8 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $this->grade_categories[3]->id; + $grade_item->itemname = 'singleparentitem2'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = $this->course_module[6]->modname; + $grade_item->iteminstance = $this->course_module[6]->instance; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Grade item 8 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 10; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[8] = $grade_item; + + // Grade_item for level1category + // id = 9 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->itemname = 'grade_item for level1 category'; + $grade_item->itemtype = 'category'; + $grade_item->iteminstance = $this->grade_categories[3]->id; + $grade_item->needsupdate = true; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Orphan Grade item 9 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + $grade_item->sortorder = 8; + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[9] = $grade_item; + + // Manual grade_item + // id = 10 + $grade_item = new stdClass(); + + $grade_item->courseid = $this->course->id; + $grade_item->categoryid = $course_category->id; + $grade_item->itemname = 'manual grade_item'; + $grade_item->itemtype = 'manual'; + $grade_item->itemnumber = 0; + $grade_item->needsupdate = false; + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->grademin = 0; + $grade_item->grademax = 100; + $grade_item->iteminfo = 'Manual grade item 10 used for unit testing'; + $grade_item->timecreated = time(); + $grade_item->timemodified = time(); + + $grade_item->id = $DB->insert_record('grade_items', $grade_item); + $this->grade_items[10] = $grade_item; + } + + /** + * Load grade_grades data into the database, and adds the corresponding objects to this class' variable. + */ + private function load_grade_grades() { + global $DB; + + //this method is called once for each test method. Avoid adding things to $this->grade_grades multiple times + $this->grade_grades = array(); + + // Grades for grade_item 1 + $grade = new stdClass(); + $grade->itemid = $this->grade_items[0]->id; + $grade->userid = $this->user[1]->id; + $grade->rawgrade = 15; // too small + $grade->finalgrade = 30; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '1 of 17 grade_grades'; + $grade->informationformat = FORMAT_PLAIN; + $grade->feedback = 'Good, but not good enough..'; + $grade->feedbackformat = FORMAT_PLAIN; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[0] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[0]->id; + $grade->userid = $this->user[2]->id; + $grade->rawgrade = 40; + $grade->finalgrade = 40; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '2 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[1] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[0]->id; + $grade->userid = $this->user[3]->id; + $grade->rawgrade = 170; // too big + $grade->finalgrade = 110; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '3 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[2] = $grade; + + + // No raw grades for grade_item 2 - it is calculated + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[1]->id; + $grade->userid = $this->user[1]->id; + $grade->finalgrade = 60; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '4 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[3] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[1]->id; + $grade->userid = $this->user[2]->id; + $grade->finalgrade = 70; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '5 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[4] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[1]->id; + $grade->userid = $this->user[3]->id; + $grade->finalgrade = 100; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '6 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[5] = $grade; + + + // Grades for grade_item 3 + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[2]->id; + $grade->userid = $this->user[1]->id; + $grade->rawgrade = 2; + $grade->finalgrade = 6; + $grade->scaleid = $this->scale[3]->id; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '7 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[6] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[2]->id; + $grade->userid = $this->user[2]->id; + $grade->rawgrade = 3; + $grade->finalgrade = 2; + $grade->scaleid = $this->scale[3]->id; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '8 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[2]->id; + $grade->userid = $this->user[3]->id; + $grade->rawgrade = 1; + $grade->finalgrade = 3; + $grade->scaleid = $this->scale[3]->id; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '9 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + // Grades for grade_item 7 + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[6]->id; + $grade->userid = $this->user[1]->id; + $grade->rawgrade = 97; + $grade->finalgrade = 69; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '10 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[6]->id; + $grade->userid = $this->user[2]->id; + $grade->rawgrade = 49; + $grade->finalgrade = 87; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '11 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[6]->id; + $grade->userid = $this->user[3]->id; + $grade->rawgrade = 67; + $grade->finalgrade = 94; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '12 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + // Grades for grade_item 8 + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[7]->id; + $grade->userid = $this->user[2]->id; + $grade->rawgrade = 3; + $grade->finalgrade = 3; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '13 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[7]->id; + $grade->userid = $this->user[3]->id; + $grade->rawgrade = 6; + $grade->finalgrade = 6; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '14 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + // Grades for grade_item 9 + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[8]->id; + $grade->userid = $this->user[1]->id; + $grade->rawgrade = 20; + $grade->finalgrade = 20; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '15 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[8]->id; + $grade->userid = $this->user[2]->id; + $grade->rawgrade = 50; + $grade->finalgrade = 50; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '16 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + + $grade = new stdClass(); + $grade->itemid = $this->grade_items[8]->id; + $grade->userid = $this->user[3]->id; + $grade->rawgrade = 100; + $grade->finalgrade = 100; + $grade->timecreated = time(); + $grade->timemodified = time(); + $grade->information = '17 of 17 grade_grades'; + + $grade->id = $DB->insert_record('grade_grades', $grade); + $this->grade_grades[] = $grade; + } + + /** + * Load grade_outcome data into the database, and adds the corresponding objects to this class' variable. + */ + private function load_grade_outcomes() { + global $DB; + + //this method is called once for each test method. Avoid adding things to $this->grade_outcomes multiple times + $this->grade_outcomes = array(); + + // Calculation for grade_item 1 + $grade_outcome = new stdClass(); + $grade_outcome->fullname = 'Team work'; + $grade_outcome->shortname = 'Team work'; + $grade_outcome->fullname = 'Team work outcome'; + $grade_outcome->timecreated = time(); + $grade_outcome->timemodified = time(); + $grade_outcome->scaleid = $this->scale[2]->id; + + $grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome); + $this->grade_outcomes[] = $grade_outcome; + + // Calculation for grade_item 2 + $grade_outcome = new stdClass(); + $grade_outcome->fullname = 'Complete circuit board'; + $grade_outcome->shortname = 'Complete circuit board'; + $grade_outcome->fullname = 'Complete circuit board'; + $grade_outcome->timecreated = time(); + $grade_outcome->timemodified = time(); + $grade_outcome->scaleid = $this->scale[3]->id; + + $grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome); + $this->grade_outcomes[] = $grade_outcome; + + // Calculation for grade_item 3 + $grade_outcome = new stdClass(); + $grade_outcome->fullname = 'Debug Java program'; + $grade_outcome->shortname = 'Debug Java program'; + $grade_outcome->fullname = 'Debug Java program'; + $grade_outcome->timecreated = time(); + $grade_outcome->timemodified = time(); + $grade_outcome->scaleid = $this->scale[4]->id; + + $grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome); + $this->grade_outcomes[] = $grade_outcome; + } +} + + diff --git a/lib/grade/tests/grade_category_test.php b/lib/grade/tests/grade_category_test.php new file mode 100644 index 00000000000..102ef8625c6 --- /dev/null +++ b/lib/grade/tests/grade_category_test.php @@ -0,0 +1,646 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/fixtures/lib.php'); + + +class grade_category_testcase extends grade_base_testcase { + + public function test_grade_category() { + $this->sub_test_grade_category_construct(); + $this->sub_test_grade_category_build_path(); + $this->sub_test_grade_category_fetch(); + $this->sub_test_grade_category_fetch_all(); + $this->sub_test_grade_category_update(); + $this->sub_test_grade_category_delete(); + $this->sub_test_grade_category_insert(); + $this->sub_test_grade_category_qualifies_for_regrading(); + $this->sub_test_grade_category_force_regrading(); + $this->sub_test_grade_category_aggregate_grades(); + $this->sub_test_grade_category_apply_limit_rules(); + $this->sub_test_grade_category_is_aggregationcoef_used(); + $this->sub_test_grade_category_fetch_course_tree(); + $this->sub_test_grade_category_get_children(); + $this->sub_test_grade_category_load_grade_item(); + $this->sub_test_grade_category_get_grade_item(); + $this->sub_test_grade_category_load_parent_category(); + $this->sub_test_grade_category_get_parent_category(); + $this->sub_test_grade_category_get_name(); + $this->sub_test_grade_category_set_parent(); + $this->sub_test_grade_category_get_final(); + $this->sub_test_grade_category_get_sortorder(); + $this->sub_test_grade_category_set_sortorder(); + $this->sub_test_grade_category_is_editable(); + $this->sub_test_grade_category_move_after_sortorder(); + $this->sub_test_grade_category_is_course_category(); + $this->sub_test_grade_category_fetch_course_category(); + $this->sub_test_grade_category_is_locked(); + $this->sub_test_grade_category_set_locked(); + $this->sub_test_grade_category_is_hidden(); + $this->sub_test_grade_category_set_hidden(); + + //this won't work until MDL-11837 is complete + //$this->sub_test_grade_category_generate_grades(); + + //do this last as adding a second course category messes up the data + $this->sub_test_grade_category_insert_course_category(); + } + + //adds 3 new grade categories at various depths + protected function sub_test_grade_category_construct() { + $course_category = grade_category::fetch_course_category($this->courseid); + + $params = new stdClass(); + + $params->courseid = $this->courseid; + $params->fullname = 'unittestcategory4'; + + $grade_category = new grade_category($params, false); + $grade_category->insert(); + $this->grade_categories[] = $grade_category; + + $this->assertEquals($params->courseid, $grade_category->courseid); + $this->assertEquals($params->fullname, $grade_category->fullname); + $this->assertEquals(2, $grade_category->depth); + $this->assertEquals("/$course_category->id/$grade_category->id/", $grade_category->path); + $parentpath = $grade_category->path; + + // Test a child category + $params->parent = $grade_category->id; + $params->fullname = 'unittestcategory5'; + $grade_category = new grade_category($params, false); + $grade_category->insert(); + $this->grade_categories[] = $grade_category; + + $this->assertEquals(3, $grade_category->depth); + $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path); + $parentpath = $grade_category->path; + + // Test a third depth category + $params->parent = $grade_category->id; + $params->fullname = 'unittestcategory6'; + $grade_category = new grade_category($params, false); + $grade_category->insert(); + $this->grade_categories[50] = $grade_category;//going to delete this one later hence the special index + + $this->assertEquals(4, $grade_category->depth); + $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path); + } + + protected function sub_test_grade_category_build_path() { + $grade_category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($grade_category, 'build_path')); + $path = grade_category::build_path($grade_category); + $this->assertEquals($grade_category->path, $path); + } + + protected function sub_test_grade_category_fetch() { + $grade_category = new grade_category(); + $this->assertTrue(method_exists($grade_category, 'fetch')); + + $grade_category = grade_category::fetch(array('id'=>$this->grade_categories[0]->id)); + $this->assertEquals($this->grade_categories[0]->id, $grade_category->id); + $this->assertEquals($this->grade_categories[0]->fullname, $grade_category->fullname); + } + + protected function sub_test_grade_category_fetch_all() { + $grade_category = new grade_category(); + $this->assertTrue(method_exists($grade_category, 'fetch_all')); + + $grade_categories = grade_category::fetch_all(array('courseid'=>$this->courseid)); + $this->assertEquals(count($this->grade_categories), count($grade_categories)-1); + } + + protected function sub_test_grade_category_update() { + global $DB; + $grade_category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($grade_category, 'update')); + + $grade_category->fullname = 'Updated info for this unittest grade_category'; + $grade_category->path = null; // path must be recalculated if missing + $grade_category->depth = null; + $grade_category->aggregation = GRADE_AGGREGATE_MAX; // should force regrading + + $grade_item = $grade_category->get_grade_item(); + $this->assertEquals(0, $grade_item->needsupdate); + + $this->assertTrue($grade_category->update()); + + $fullname = $DB->get_field('grade_categories', 'fullname', array('id' => $this->grade_categories[0]->id)); + $this->assertEquals($grade_category->fullname, $fullname); + + $path = $DB->get_field('grade_categories', 'path', array('id' => $this->grade_categories[0]->id)); + $this->assertEquals($grade_category->path, $path); + + $depth = $DB->get_field('grade_categories', 'depth', array('id' => $this->grade_categories[0]->id)); + $this->assertEquals($grade_category->depth, $depth); + + $grade_item = $grade_category->get_grade_item(); + $this->assertEquals(1, $grade_item->needsupdate); + } + + protected function sub_test_grade_category_delete() { + global $DB; + + $grade_category = new grade_category($this->grade_categories[50]); + $this->assertTrue(method_exists($grade_category, 'delete')); + + $this->assertTrue($grade_category->delete()); + $this->assertFalse($DB->get_record('grade_categories', array('id' => $grade_category->id))); + } + + protected function sub_test_grade_category_insert() { + $course_category = grade_category::fetch_course_category($this->courseid); + + $grade_category = new grade_category(); + $this->assertTrue(method_exists($grade_category, 'insert')); + + $grade_category->fullname = 'unittestcategory4'; + $grade_category->courseid = $this->courseid; + $grade_category->aggregation = GRADE_AGGREGATE_MEAN; + $grade_category->aggregateonlygraded = 1; + $grade_category->keephigh = 100; + $grade_category->droplow = 10; + $grade_category->hidden = 0; + $grade_category->parent = $this->grade_categories[1]->id; //sub_test_grade_category_delete() removed the category at 0 + + $grade_category->insert(); + + $this->assertEquals('/'.$course_category->id.'/'.$this->grade_categories[1]->parent.'/'.$this->grade_categories[1]->id.'/'.$grade_category->id.'/', $grade_category->path); + $this->assertEquals(4, $grade_category->depth); + + $last_grade_category = end($this->grade_categories); + + $this->assertFalse(empty($grade_category->grade_item)); + $this->assertEquals($grade_category->id, $grade_category->grade_item->iteminstance); + $this->assertEquals('category', $grade_category->grade_item->itemtype); + + $this->assertEquals($grade_category->id, $last_grade_category->id + 1); + $this->assertFalse(empty($grade_category->timecreated)); + $this->assertFalse(empty($grade_category->timemodified)); + } + + protected function sub_test_grade_category_qualifies_for_regrading() { + $grade_category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($grade_category, 'qualifies_for_regrading')); + $this->assertFalse($grade_category->qualifies_for_regrading()); + + $grade_category->aggregation = GRADE_AGGREGATE_MAX; + $this->assertTrue($grade_category->qualifies_for_regrading()); + + $grade_category = new grade_category($this->grade_categories[1]); + $grade_category->droplow = 99; + $this->assertTrue($grade_category->qualifies_for_regrading()); + + $grade_category = new grade_category($this->grade_categories[1]); + $grade_category->keephigh = 99; + $this->assertTrue($grade_category->qualifies_for_regrading()); + } + + protected function sub_test_grade_category_force_regrading() { + $grade_category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($grade_category, 'force_regrading')); + + $grade_category->load_grade_item(); + $this->assertEquals(0, $grade_category->grade_item->needsupdate); + + $grade_category->force_regrading(); + + $grade_category->grade_item = null; + $grade_category->load_grade_item(); + + $this->assertEquals(1, $grade_category->grade_item->needsupdate); + } + + /** + * Tests the calculation of grades using the various aggregation methods with and without hidden grades + * This will not work entirely until MDL-11837 is done + * @global type $DB + */ + protected function sub_test_grade_category_generate_grades() { + global $DB; + + //inserting some special grade items to make testing the final grade calculation easier + $params->courseid = $this->courseid; + $params->fullname = 'unittestgradecalccategory'; + $params->aggregation = GRADE_AGGREGATE_MEAN; + $params->aggregateonlygraded = 0; + $grade_category = new grade_category($params, false); + $grade_category->insert(); + + $this->assertTrue(method_exists($grade_category, 'generate_grades')); + + $grade_category->load_grade_item(); + $cgi = $grade_category->get_grade_item(); + $cgi->grademin = 0; + $cgi->grademax = 20;//3 grade items out of 10 but category is out of 20 to force scaling to occur + $cgi->update(); + + //3 grade items each with a maximum grade of 10 + $grade_items = array(); + for ($i=0; $i<3; $i++) { + $grade_items[$i] = new grade_item(); + $grade_items[$i]->courseid = $this->courseid; + $grade_items[$i]->categoryid = $grade_category->id; + $grade_items[$i]->itemname = 'manual grade_item '.$i; + $grade_items[$i]->itemtype = 'manual'; + $grade_items[$i]->itemnumber = 0; + $grade_items[$i]->needsupdate = false; + $grade_items[$i]->gradetype = GRADE_TYPE_VALUE; + $grade_items[$i]->grademin = 0; + $grade_items[$i]->grademax = 10; + $grade_items[$i]->iteminfo = 'Manual grade item used for unit testing'; + $grade_items[$i]->timecreated = time(); + $grade_items[$i]->timemodified = time(); + + //used as the weight by weighted mean and as extra credit by mean with extra credit + //Will be 0, 1 and 2 + $grade_items[$i]->aggregationcoef = $i; + + $grade_items[$i]->insert(); + } + + //a grade for each grade item + $grade_grades = array(); + for ($i=0; $i<3; $i++) { + $grade_grades[$i] = new grade_grade(); + $grade_grades[$i]->itemid = $grade_items[$i]->id; + $grade_grades[$i]->userid = $this->userid; + $grade_grades[$i]->rawgrade = ($i+1)*2;//produce grade grades of 2, 4 and 6 + $grade_grades[$i]->finalgrade = ($i+1)*2; + $grade_grades[$i]->timecreated = time(); + $grade_grades[$i]->timemodified = time(); + $grade_grades[$i]->information = '1 of 2 grade_grades'; + $grade_grades[$i]->informationformat = FORMAT_PLAIN; + $grade_grades[$i]->feedback = 'Good, but not good enough..'; + $grade_grades[$i]->feedbackformat = FORMAT_PLAIN; + + $grade_grades[$i]->insert(); + } + + //3 grade items with 1 grade_grade each. + //grade grades have the values 2, 4 and 6 + + //First correct answer is the aggregate with all 3 grades + //Second correct answer is with the first grade (value 2) hidden + + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEDIAN, 'GRADE_AGGREGATE_MEDIAN', 8, 8); + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MAX, 'GRADE_AGGREGATE_MAX', 12, 12); + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MODE, 'GRADE_AGGREGATE_MODE', 12, 12); + + //weighted mean. note grade totals are rounded to an int to prevent rounding discrepancies. correct final grade isnt actually exactly 10 + //3 items with grades 2, 4 and 6 with weights 0, 1 and 2 and all out of 10. then doubled to be out of 20. + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN, 'GRADE_AGGREGATE_WEIGHTED_MEAN', 10, 10); + + //simple weighted mean + //3 items with grades 2, 4 and 6 equally weighted and all out of 10. then doubled to be out of 20. + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN2, 'GRADE_AGGREGATE_WEIGHTED_MEAN2', 8, 10); + + //mean of grades with extra credit + //3 items with grades 2, 4 and 6 with extra credit 0, 1 and 2 equally weighted and all out of 10. then doubled to be out of 20. + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_EXTRACREDIT_MEAN, 'GRADE_AGGREGATE_EXTRACREDIT_MEAN', 10, 13); + + //aggregation tests the are affected by a hidden grade currently dont work as we dont store the altered grade in the database + //instead an in memory recalculation is done. This should be remedied by MDL-11837 + + //fails with 1 grade hidden. still reports 8 as being correct + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEAN, 'GRADE_AGGREGATE_MEAN', 8, 10); + + //fails with 1 grade hidden. still reports 4 as being correct + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MIN, 'GRADE_AGGREGATE_MIN', 4, 8); + + //fails with 1 grade hidden. still reports 12 as being correct + $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_SUM, 'GRADE_AGGREGATE_SUM', 12, 10); + } + + /** + * Test grade category aggregation using the supplied grade objects and aggregation method + * @param grade_category $grade_category the category to be tested + * @param array $grade_items array of instance of grade_item + * @param array $grade_grades array of instances of grade_grade + * @param int $aggmethod the aggregation method to apply ie GRADE_AGGREGATE_MEAN + * @param string $aggmethodname the name of the aggregation method to apply. Used to display any test failure messages + * @param int $correct1 the correct final grade for the category with NO items hidden + * @param int $correct2 the correct final grade for the category with the grade at $grade_grades[0] hidden + * @return void + */ + protected function helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, $aggmethod, $aggmethodname, $correct1, $correct2) { + global $DB; + + $grade_category->aggregation = $aggmethod; + $grade_category->update(); + + //check grade_item isnt hidden from a previous test + $grade_items[0]->set_hidden(0, true); + $this->helper_test_grade_aggregation_result($grade_category, $correct1, 'Testing aggregation method('.$aggmethodname.') with no items hidden %s'); + + //hide the grade item with grade of 2 + $grade_items[0]->set_hidden(1, true); + $this->helper_test_grade_aggregation_result($grade_category, $correct2, 'Testing aggregation method('.$aggmethodname.') with 1 item hidden %s'); + } + + /** + * Verify the value of the category grade item for $this->userid + * @param grade_category $grade_category the category to be tested + * @param int $correctgrade the expected grade + * @param string msg The message that should be displayed if the correct grade is not found + * @return void + */ + protected function helper_test_grade_aggregation_result($grade_category, $correctgrade, $msg) { + global $DB; + + $category_grade_item = $grade_category->get_grade_item(); + + //this creates all the grade_grades we need + grade_regrade_final_grades($this->courseid); + + $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid)); + $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax); + $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg); + + /* + * TODO this doesnt work as the grade_grades created by $grade_category->generate_grades(); dont + * observe the category's max grade + //delete the grade_grades for the category itself and check they get recreated correctly + $DB->delete_records('grade_grades', array('itemid'=>$category_grade_item->id)); + $grade_category->generate_grades(); + + $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid)); + $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax); + $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg); + * + */ + } + + protected function sub_test_grade_category_aggregate_grades() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'aggregate_grades')); + // tested more fully via test_grade_category_generate_grades() + } + + protected function sub_test_grade_category_apply_limit_rules() { + $items[$this->grade_items[0]->id] = new grade_item($this->grade_items[0], false); + $items[$this->grade_items[1]->id] = new grade_item($this->grade_items[1], false); + $items[$this->grade_items[2]->id] = new grade_item($this->grade_items[2], false); + $items[$this->grade_items[4]->id] = new grade_item($this->grade_items[4], false); + + $category = new grade_category(); + $category->droplow = 2; + $grades = array($this->grade_items[0]->id=>5.374, + $this->grade_items[1]->id=>9.4743, + $this->grade_items[2]->id=>2.5474, + $this->grade_items[4]->id=>7.3754); + $category->apply_limit_rules($grades, $items); + $this->assertEquals(count($grades), 2); + $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); + $this->assertEquals($grades[$this->grade_items[4]->id], 7.3754); + + $category = new grade_category(); + $category->keephigh = 1; + $category->droplow = 0; + $grades = array($this->grade_items[0]->id=>5.374, + $this->grade_items[1]->id=>9.4743, + $this->grade_items[2]->id=>2.5474, + $this->grade_items[4]->id=>7.3754); + $category->apply_limit_rules($grades, $items); + $this->assertEquals(count($grades), 1); + $grade = reset($grades); + $this->assertEquals(9.4743, $grade); + + $category = new grade_category(); + $category->droplow = 2; + $category->aggregation = GRADE_AGGREGATE_SUM; + $items[$this->grade_items[2]->id]->aggregationcoef = 1; + $grades = array($this->grade_items[0]->id=>5.374, + $this->grade_items[1]->id=>9.4743, + $this->grade_items[2]->id=>2.5474, + $this->grade_items[4]->id=>7.3754); + + $category->apply_limit_rules($grades, $items); + $this->assertEquals(count($grades), 2); + $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); + $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474); + + $category = new grade_category(); + $category->keephigh = 1; + $category->droplow = 0; + $category->aggregation = GRADE_AGGREGATE_SUM; + $items[$this->grade_items[2]->id]->aggregationcoef = 1; + $grades = array($this->grade_items[0]->id=>5.374, + $this->grade_items[1]->id=>9.4743, + $this->grade_items[2]->id=>2.5474, + $this->grade_items[4]->id=>7.3754); + $category->apply_limit_rules($grades, $items); + $this->assertEquals(count($grades), 2); + $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); + $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474); + } + + /** + * TODO implement + */ + protected function sub_test_grade_category_is_aggregationcoef_used() { + + } + + protected function sub_test_grade_category_fetch_course_tree() { + $category = new grade_category(); + $this->assertTrue(method_exists($category, 'fetch_course_tree')); + //TODO: add some tests + } + + protected function sub_test_grade_category_get_children() { + $course_category = grade_category::fetch_course_category($this->courseid); + + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'get_children')); + + $children_array = $category->get_children(0); + + $this->assertTrue(is_array($children_array)); + $this->assertFalse(empty($children_array[2])); + $this->assertFalse(empty($children_array[2]['object'])); + $this->assertFalse(empty($children_array[2]['children'])); + $this->assertEquals($this->grade_categories[1]->id, $children_array[2]['object']->id); + $this->assertEquals($this->grade_categories[2]->id, $children_array[5]['object']->id); + $this->assertEquals($this->grade_items[0]->id, $children_array[2]['children'][3]['object']->id); + $this->assertEquals($this->grade_items[1]->id, $children_array[2]['children'][4]['object']->id); + $this->assertEquals($this->grade_items[2]->id, $children_array[5]['children'][6]['object']->id); + } + + protected function sub_test_grade_category_load_grade_item() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'load_grade_item')); + $this->assertEquals(null, $category->grade_item); + $category->load_grade_item(); + $this->assertEquals($this->grade_items[3]->id, $category->grade_item->id); + } + + protected function sub_test_grade_category_get_grade_item() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'get_grade_item')); + $grade_item = $category->get_grade_item(); + $this->assertEquals($this->grade_items[3]->id, $grade_item->id); + } + + protected function sub_test_grade_category_load_parent_category() { + $category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($category, 'load_parent_category')); + $this->assertEquals(null, $category->parent_category); + $category->load_parent_category(); + $this->assertEquals($this->grade_categories[0]->id, $category->parent_category->id); + } + + protected function sub_test_grade_category_get_parent_category() { + $category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($category, 'get_parent_category')); + $parent_category = $category->get_parent_category(); + $this->assertEquals($this->grade_categories[0]->id, $parent_category->id); + } + + protected function sub_test_grade_category_get_name() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'get_name')); + $this->assertEquals($this->grade_categories[0]->fullname, $category->get_name()); + } + + protected function sub_test_grade_category_set_parent() { + $category = new grade_category($this->grade_categories[1]); + $this->assertTrue(method_exists($category, 'set_parent')); + // TODO: implement detailed tests + + $course_category = grade_category::fetch_course_category($this->courseid); + $this->assertTrue($category->set_parent($course_category->id)); + $this->assertEquals($course_category->id, $category->parent); + } + + protected function sub_test_grade_category_get_final() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'get_final')); + $category->load_grade_item(); + $this->assertEquals($category->get_final(), $category->grade_item->get_final()); + } + + protected function sub_test_grade_category_get_sortorder() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'get_sortorder')); + $category->load_grade_item(); + $this->assertEquals($category->get_sortorder(), $category->grade_item->get_sortorder()); + } + + protected function sub_test_grade_category_set_sortorder() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'set_sortorder')); + $category->load_grade_item(); + $this->assertEquals($category->set_sortorder(10), $category->grade_item->set_sortorder(10)); + } + + protected function sub_test_grade_category_move_after_sortorder() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'move_after_sortorder')); + $category->load_grade_item(); + $this->assertEquals($category->move_after_sortorder(10), $category->grade_item->move_after_sortorder(10)); + } + + protected function sub_test_grade_category_is_course_category() { + $category = grade_category::fetch_course_category($this->courseid); + $this->assertTrue(method_exists($category, 'is_course_category')); + $this->assertTrue($category->is_course_category()); + } + + protected function sub_test_grade_category_fetch_course_category() { + $category = new grade_category(); + $this->assertTrue(method_exists($category, 'fetch_course_category')); + $category = grade_category::fetch_course_category($this->courseid); + $this->assertTrue(empty($category->parent)); + } + /** + * TODO implement + */ + protected function sub_test_grade_category_is_editable() { + + } + + protected function sub_test_grade_category_is_locked() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'is_locked')); + $category->load_grade_item(); + $this->assertEquals($category->is_locked(), $category->grade_item->is_locked()); + } + + protected function sub_test_grade_category_set_locked() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'set_locked')); + + //will return false as cannot lock a grade that needs updating + $this->assertFalse($category->set_locked(1)); + grade_regrade_final_grades($this->courseid); + + //get the category from the db again + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue($category->set_locked(1)); + } + + protected function sub_test_grade_category_is_hidden() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'is_hidden')); + $category->load_grade_item(); + $this->assertEquals($category->is_hidden(), $category->grade_item->is_hidden()); + } + + protected function sub_test_grade_category_set_hidden() { + $category = new grade_category($this->grade_categories[0]); + $this->assertTrue(method_exists($category, 'set_hidden')); + $category->set_hidden(1); + $category->load_grade_item(); + $this->assertEquals(true, $category->grade_item->is_hidden()); + } + + //beware: adding a duplicate course category messes up the data in a way that's hard to recover from + protected function sub_test_grade_category_insert_course_category() { + $grade_category = new grade_category(); + $this->assertTrue(method_exists($grade_category, 'insert_course_category')); + + $id = $grade_category->insert_course_category($this->courseid); + $this->assertNotNull($id); + $this->assertEquals('?', $grade_category->fullname); + $this->assertEquals(GRADE_AGGREGATE_WEIGHTED_MEAN2, $grade_category->aggregation); + $this->assertEquals("/$id/", $grade_category->path); + $this->assertEquals(1, $grade_category->depth); + $this->assertNull($grade_category->parent); + } + + protected function generate_random_raw_grade($item, $userid) { + $grade = new grade_grade(); + $grade->itemid = $item->id; + $grade->userid = $userid; + $grade->grademin = 0; + $grade->grademax = 1; + $valuetype = "grade$item->gradetype"; + $grade->rawgrade = rand(0, 1000) / 1000; + $grade->insert(); + return $grade->rawgrade; + } +} diff --git a/lib/grade/tests/grade_grade_test.php b/lib/grade/tests/grade_grade_test.php new file mode 100644 index 00000000000..0227b23b5b6 --- /dev/null +++ b/lib/grade/tests/grade_grade_test.php @@ -0,0 +1,196 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/fixtures/lib.php'); + + +class grade_grade_testcase extends grade_base_testcase { + + public function test_grade_grade() { + $this->sub_test_grade_grade_construct(); + $this->sub_test_grade_grade_insert(); + $this->sub_test_grade_grade_update(); + $this->sub_test_grade_grade_fetch(); + $this->sub_test_grade_grade_fetch_all(); + $this->sub_test_grade_grade_load_grade_item(); + $this->sub_test_grade_grade_standardise_score(); + $this->sub_test_grade_grade_is_locked(); + $this->sub_test_grade_grade_set_hidden(); + $this->sub_test_grade_grade_is_hidden(); + } + + protected function sub_test_grade_grade_construct() { + $params = new stdClass(); + + $params->itemid = $this->grade_items[0]->id; + $params->userid = 1; + $params->rawgrade = 88; + $params->rawgrademax = 110; + $params->rawgrademin = 18; + + $grade_grade = new grade_grade($params, false); + $this->assertEquals($params->itemid, $grade_grade->itemid); + $this->assertEquals($params->rawgrade, $grade_grade->rawgrade); + } + + protected function sub_test_grade_grade_insert() { + $grade_grade = new grade_grade(); + $this->assertTrue(method_exists($grade_grade, 'insert')); + + $grade_grade->itemid = $this->grade_items[0]->id; + $grade_grade->userid = 10; + $grade_grade->rawgrade = 88; + $grade_grade->rawgrademax = 110; + $grade_grade->rawgrademin = 18; + + // Check the grade_item's needsupdate variable first + $grade_grade->load_grade_item(); + $this->assertEmpty($grade_grade->grade_item->needsupdate); + + $grade_grade->insert(); + + $last_grade_grade = end($this->grade_grades); + + $this->assertEquals($grade_grade->id, $last_grade_grade->id + 1); + + // timecreated will only be set if the grade was submitted by an activity module + $this->assertTrue(empty($grade_grade->timecreated)); + // timemodified will only be set if the grade was submitted by an activity module + $this->assertTrue(empty($grade_grade->timemodified)); + + //keep our collection the same as is in the database + $this->grade_grades[] = $grade_grade; + } + + protected function sub_test_grade_grade_update() { + $grade_grade = new grade_grade($this->grade_grades[0]); + $this->assertTrue(method_exists($grade_grade, 'update')); + } + + protected function sub_test_grade_grade_fetch() { + $grade_grade = new grade_grade(); + $this->assertTrue(method_exists($grade_grade, 'fetch')); + + $grades = grade_grade::fetch(array('id'=>$this->grade_grades[0]->id)); + $this->assertEquals($this->grade_grades[0]->id, $grades->id); + $this->assertEquals($this->grade_grades[0]->rawgrade, $grades->rawgrade); + } + + protected function sub_test_grade_grade_fetch_all() { + $grade_grade = new grade_grade(); + $this->assertTrue(method_exists($grade_grade, 'fetch_all')); + + $grades = grade_grade::fetch_all(array()); + $this->assertEquals(count($this->grade_grades), count($grades)); + } + + protected function sub_test_grade_grade_load_grade_item() { + $grade_grade = new grade_grade($this->grade_grades[0]); + $this->assertTrue(method_exists($grade_grade, 'load_grade_item')); + $this->assertNull($grade_grade->grade_item); + $this->assertNotEmpty($grade_grade->itemid); + $this->assertNotNull($grade_grade->load_grade_item()); + $this->assertNotNull($grade_grade->grade_item); + $this->assertEquals($this->grade_items[0]->id, $grade_grade->grade_item->id); + } + + + protected function sub_test_grade_grade_standardise_score() { + $this->assertEquals(4, round(grade_grade::standardise_score(6, 0, 7, 0, 5))); + $this->assertEquals(40, grade_grade::standardise_score(50, 30, 80, 0, 100)); + } + + + /* + * Disabling this test: the set_locked() arguments have been modified, rendering these tests useless until they are re-written + + protected function test_grade_grade_set_locked() { + $grade_item = new grade_item($this->grade_items[0]); + $grade = new grade_grade($grade_item->get_final(1)); + $this->assertTrue(method_exists($grade, 'set_locked')); + + $this->assertTrue(empty($grade_item->locked)); + $this->assertTrue(empty($grade->locked)); + + $this->assertTrue($grade->set_locked(true)); + $this->assertFalse(empty($grade->locked)); + $this->assertTrue($grade->set_locked(false)); + $this->assertTrue(empty($grade->locked)); + + $this->assertTrue($grade_item->set_locked(true, true)); + $grade = new grade_grade($grade_item->get_final(1)); + + $this->assertFalse(empty($grade->locked)); + $this->assertFalse($grade->set_locked(true, false)); + + $this->assertTrue($grade_item->set_locked(true, false)); + $grade = new grade_grade($grade_item->get_final(1)); + + $this->assertTrue($grade->set_locked(true, false)); + } + */ + + protected function sub_test_grade_grade_is_locked() { + $grade = new grade_grade($this->grade_grades[0]); + $this->assertTrue(method_exists($grade, 'is_locked')); + + $this->assertFalse($grade->is_locked()); + $grade->locked = time(); + $this->assertTrue($grade->is_locked()); + } + + protected function sub_test_grade_grade_set_hidden() { + $grade = new grade_grade($this->grade_grades[0]); + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade, 'set_hidden')); + + $this->assertEquals(0, $grade_item->hidden); + $this->assertEquals(0, $grade->hidden); + + $grade->set_hidden(0); + $this->assertEquals(0, $grade->hidden); + + $grade->set_hidden(1); + $this->assertEquals(1, $grade->hidden); + + $grade->set_hidden(0); + $this->assertEquals(0, $grade->hidden); + } + + protected function sub_test_grade_grade_is_hidden() { + $grade = new grade_grade($this->grade_grades[0]); + $this->assertTrue(method_exists($grade, 'is_hidden')); + + $this->assertFalse($grade->is_hidden()); + $grade->hidden = 1; + $this->assertTrue($grade->is_hidden()); + + $grade->hidden = time()-666; + $this->assertFalse($grade->is_hidden()); + + $grade->hidden = time()+666; + $this->assertTrue($grade->is_hidden()); + } +} diff --git a/lib/grade/tests/grade_item_test.php b/lib/grade/tests/grade_item_test.php new file mode 100644 index 00000000000..dda234b0280 --- /dev/null +++ b/lib/grade/tests/grade_item_test.php @@ -0,0 +1,547 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/fixtures/lib.php'); + +class grade_item_testcase extends grade_base_testcase { + public function test_grade_item() { + $this->sub_test_grade_item_construct(); + $this->sub_test_grade_item_insert(); + $this->sub_test_grade_item_delete(); + $this->sub_test_grade_item_update(); + $this->sub_test_grade_item_load_scale(); + $this->sub_test_grade_item_load_outcome(); + $this->sub_test_grade_item_qualifies_for_regrading(); + $this->sub_test_grade_item_force_regrading(); + $this->sub_test_grade_item_fetch(); + $this->sub_test_grade_item_fetch_all(); + $this->sub_test_grade_item_get_all_finals(); + $this->sub_test_grade_item_get_final(); + $this->sub_test_grade_item_get_sortorder(); + $this->sub_test_grade_item_set_sortorder(); + $this->sub_test_grade_item_move_after_sortorder(); + $this->sub_test_grade_item_get_name(); + $this->sub_test_grade_item_set_parent(); + $this->sub_test_grade_item_get_parent_category(); + $this->sub_test_grade_item_load_parent_category(); + $this->sub_test_grade_item_get_item_category(); + $this->sub_test_grade_item_load_item_category(); + $this->sub_test_grade_item_regrade_final_grades(); + $this->sub_test_grade_item_adjust_raw_grade(); + $this->sub_test_grade_item_set_locked(); + $this->sub_test_grade_item_is_locked(); + $this->sub_test_grade_item_set_hidden(); + $this->sub_test_grade_item_is_hidden(); + $this->sub_test_grade_item_is_category_item(); + $this->sub_test_grade_item_is_course_item(); + $this->sub_test_grade_item_fetch_course_item(); + $this->sub_test_grade_item_depends_on(); + $this->sub_test_grade_item_is_calculated(); + $this->sub_test_grade_item_set_calculation(); + $this->sub_test_grade_item_get_calculation(); + $this->sub_test_grade_item_compute(); + } + + protected function sub_test_grade_item_construct() { + $params = new stdClass(); + + $params->courseid = $this->courseid; + $params->categoryid = $this->grade_categories[1]->id; + $params->itemname = 'unittestgradeitem4'; + $params->itemtype = 'mod'; + $params->itemmodule = 'database'; + $params->iteminfo = 'Grade item used for unit testing'; + + $grade_item = new grade_item($params, false); + + $this->assertEquals($params->courseid, $grade_item->courseid); + $this->assertEquals($params->categoryid, $grade_item->categoryid); + $this->assertEquals($params->itemmodule, $grade_item->itemmodule); + } + + protected function sub_test_grade_item_insert() { + $grade_item = new grade_item(); + $this->assertTrue(method_exists($grade_item, 'insert')); + + $grade_item->courseid = $this->courseid; + $grade_item->categoryid = $this->grade_categories[1]->id; + $grade_item->itemname = 'unittestgradeitem4'; + $grade_item->itemtype = 'mod'; + $grade_item->itemmodule = 'quiz'; + $grade_item->iteminfo = 'Grade item used for unit testing'; + + $grade_item->insert(); + + $last_grade_item = end($this->grade_items); + + $this->assertEquals($grade_item->id, $last_grade_item->id + 1); + $this->assertEquals(11, $grade_item->sortorder); + + //keep our reference collection the same as what is in the database + $this->grade_items[] = $grade_item; + } + + protected function sub_test_grade_item_delete() { + global $DB; + $grade_item = new grade_item($this->grade_items[7]);//use a grade item not touched by previous (or future) tests + $this->assertTrue(method_exists($grade_item, 'delete')); + + $this->assertTrue($grade_item->delete()); + + $this->assertFalse($DB->get_record('grade_items', array('id' => $grade_item->id))); + + //keep our reference collection the same as the database + unset($this->grade_items[7]); + } + + protected function sub_test_grade_item_update() { + global $DB; + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'update')); + + $grade_item->iteminfo = 'Updated info for this unittest grade_item'; + + $this->assertTrue($grade_item->update()); + + $grade_item->grademin = 14; + $this->assertTrue($grade_item->qualifies_for_regrading()); + $this->assertTrue($grade_item->update()); + + $iteminfo = $DB->get_field('grade_items', 'iteminfo', array('id' => $this->grade_items[0]->id)); + $this->assertEquals($grade_item->iteminfo, $iteminfo); + } + + protected function sub_test_grade_item_load_scale() { + $grade_item = new grade_item($this->grade_items[2]); + $this->assertTrue(method_exists($grade_item, 'load_scale')); + $scale = $grade_item->load_scale(); + $this->assertFalse(empty($grade_item->scale)); + $this->assertEquals($scale->id, $this->grade_items[2]->scaleid); + } + + protected function sub_test_grade_item_load_outcome() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'load_outcome')); + //TODO: add tests + } + + protected function sub_test_grade_item_qualifies_for_regrading() { + $grade_item = new grade_item($this->grade_items[1]);//use a grade item not touched by previous tests + $this->assertTrue(method_exists($grade_item, 'qualifies_for_regrading')); + + $this->assertFalse($grade_item->qualifies_for_regrading()); + + $grade_item->iteminfo = 'Updated info for this unittest grade_item'; + + $this->assertFalse($grade_item->qualifies_for_regrading()); + + $grade_item->grademin = 14; + + $this->assertTrue($grade_item->qualifies_for_regrading()); + } + + protected function sub_test_grade_item_force_regrading() { + $grade_item = new grade_item($this->grade_items[2]);//use a grade item not touched by previous tests + $this->assertTrue(method_exists($grade_item, 'force_regrading')); + + $this->assertEquals(0, $grade_item->needsupdate); + + $grade_item->force_regrading(); + $this->assertEquals(1, $grade_item->needsupdate); + $grade_item->update_from_db(); + $this->assertEquals(1, $grade_item->needsupdate); + } + + protected function sub_test_grade_item_fetch() { + $grade_item = new grade_item(); + $this->assertTrue(method_exists($grade_item, 'fetch')); + + //not using $this->grade_items[0] as it's iteminfo was modified by sub_test_grade_item_qualifies_for_regrading() + $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id)); + $this->assertEquals($this->grade_items[1]->id, $grade_item->id); + $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo); + + $grade_item = grade_item::fetch(array('itemtype'=>$this->grade_items[1]->itemtype, 'itemmodule'=>$this->grade_items[1]->itemmodule)); + $this->assertEquals($this->grade_items[1]->id, $grade_item->id); + $this->assertEquals($this->grade_items[1]->iteminfo, $grade_item->iteminfo); + } + + protected function sub_test_grade_item_fetch_all() { + $grade_item = new grade_item(); + $this->assertTrue(method_exists($grade_item, 'fetch_all')); + + $grade_items = grade_item::fetch_all(array('courseid'=>$this->courseid)); + $this->assertEquals(count($this->grade_items), count($grade_items)-1);//-1 to account for the course grade item + } + + // Retrieve all final scores for a given grade_item. + protected function sub_test_grade_item_get_all_finals() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'get_final')); + + $final_grades = $grade_item->get_final(); + $this->assertEquals(3, count($final_grades)); + } + + + // Retrieve all final scores for a specific userid. + protected function sub_test_grade_item_get_final() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'get_final')); + $final_grade = $grade_item->get_final($this->user[1]->id); + $this->assertEquals($this->grade_grades[0]->finalgrade, $final_grade->finalgrade); + } + + protected function sub_test_grade_item_get_sortorder() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'get_sortorder')); + $sortorder = $grade_item->get_sortorder(); + $this->assertEquals($this->grade_items[0]->sortorder, $sortorder); + } + + protected function sub_test_grade_item_set_sortorder() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'set_sortorder')); + $grade_item->set_sortorder(999); + $this->assertEquals($grade_item->sortorder, 999); + } + + protected function sub_test_grade_item_move_after_sortorder() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'move_after_sortorder')); + $grade_item->move_after_sortorder(5); + $this->assertEquals($grade_item->sortorder, 6); + + $grade_item = grade_item::fetch(array('id'=>$this->grade_items[0]->id)); + $this->assertEquals($grade_item->sortorder, 6); + + $after = grade_item::fetch(array('id'=>$this->grade_items[6]->id)); + $this->assertEquals($after->sortorder, 8); + } + + protected function sub_test_grade_item_get_name() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'get_name')); + + $name = $grade_item->get_name(); + $this->assertEquals($this->grade_items[0]->itemname, $name); + } + + protected function sub_test_grade_item_set_parent() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'set_parent')); + + $old = $grade_item->get_parent_category(); + $new = new grade_category($this->grade_categories[3]); + $new_item = $new->get_grade_item(); + + $this->assertTrue($grade_item->set_parent($new->id)); + + $new_item->update_from_db(); + $grade_item->update_from_db(); + + $this->assertEquals($grade_item->categoryid, $new->id); + } + + protected function sub_test_grade_item_get_parent_category() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'get_parent_category')); + + $category = $grade_item->get_parent_category(); + $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname); + } + + protected function sub_test_grade_item_load_parent_category() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'load_parent_category')); + + $category = $grade_item->load_parent_category(); + $this->assertEquals($this->grade_categories[1]->fullname, $category->fullname); + $this->assertEquals($this->grade_categories[1]->fullname, $grade_item->parent_category->fullname); + } + + protected function sub_test_grade_item_get_item_category() { + $grade_item = new grade_item($this->grade_items[3]); + $this->assertTrue(method_exists($grade_item, 'get_item_category')); + + $category = $grade_item->get_item_category(); + $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname); + } + + protected function sub_test_grade_item_load_item_category() { + $grade_item = new grade_item($this->grade_items[3]); + $this->assertTrue(method_exists($grade_item, 'load_item_category')); + + $category = $grade_item->load_item_category(); + $this->assertEquals($this->grade_categories[0]->fullname, $category->fullname); + $this->assertEquals($this->grade_categories[0]->fullname, $grade_item->item_category->fullname); + } + + // Test update of all final grades + protected function sub_test_grade_item_regrade_final_grades() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'regrade_final_grades')); + $this->assertEquals(true, $grade_item->regrade_final_grades()); + //TODO: add more tests + } + + // Test the adjust_raw_grade method + protected function sub_test_grade_item_adjust_raw_grade() { + $grade_item = new grade_item($this->grade_items[2]); // anything but assignment module! + $this->assertTrue(method_exists($grade_item, 'adjust_raw_grade')); + + $grade_raw = new stdClass(); + $grade_raw->rawgrade = 40; + $grade_raw->grademax = 100; + $grade_raw->grademin = 0; + + $grade_item->gradetype = GRADE_TYPE_VALUE; + $grade_item->multfactor = 1; + $grade_item->plusfactor = 0; + $grade_item->grademax = 50; + $grade_item->grademin = 0; + + $original_grade_raw = clone($grade_raw); + $original_grade_item = clone($grade_item); + + $this->assertEquals(20, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Try a larger maximum grade + $grade_item->grademax = 150; + $grade_item->grademin = 0; + $this->assertEquals(60, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Try larger minimum grade + $grade_item->grademin = 50; + + $this->assertEquals(90, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Rescaling from a small scale (0-50) to a larger scale (0-100) + $grade_raw->grademax = 50; + $grade_raw->grademin = 0; + $grade_item->grademax = 100; + $grade_item->grademin = 0; + + $this->assertEquals(80, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Rescaling from a small scale (0-50) to a larger scale with offset (40-100) + $grade_item->grademax = 100; + $grade_item->grademin = 40; + + $this->assertEquals(88, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Try multfactor and plusfactor + $grade_raw = clone($original_grade_raw); + $grade_item = clone($original_grade_item); + $grade_item->multfactor = 1.23; + $grade_item->plusfactor = 3; + + $this->assertEquals(27.6, $grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax)); + + // Try multfactor below 0 and a negative plusfactor + $grade_raw = clone($original_grade_raw); + $grade_item = clone($original_grade_item); + $grade_item->multfactor = 0.23; + $grade_item->plusfactor = -3; + + $this->assertEquals(round(1.6), round($grade_item->adjust_raw_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax))); + } + + // Test locking of grade items + protected function sub_test_grade_item_set_locked() { + //getting a grade_item from the DB as set_locked() will fail if the grade items needs to be updated + //also needs to have at least one grade_grade or $grade_item->get_final(1) returns null + //$grade_item = new grade_item($this->grade_items[8]); + $grade_item = grade_item::fetch(array('id'=>$this->grade_items[8]->id)); + + $this->assertTrue(method_exists($grade_item, 'set_locked')); + + $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id)); + $this->assertTrue(empty($grade_item->locked));//not locked + $this->assertTrue(empty($grade_grade->locked));//not locked + + $this->assertTrue($grade_item->set_locked(true, true, false)); + $grade_grade = new grade_grade($grade_item->get_final($this->user[1]->id)); + + $this->assertFalse(empty($grade_item->locked));//locked + $this->assertFalse(empty($grade_grade->locked)); // individual grades should be locked too + + $this->assertTrue($grade_item->set_locked(false, true, false)); + $grade = new grade_grade($grade_item->get_final($this->user[1]->id)); + + $this->assertTrue(empty($grade_item->locked)); + $this->assertTrue(empty($grade->locked)); // individual grades should be unlocked too + } + + protected function sub_test_grade_item_is_locked() { + $grade_item = new grade_item($this->grade_items[10]); + $this->assertTrue(method_exists($grade_item, 'is_locked')); + + $this->assertFalse($grade_item->is_locked()); + $this->assertFalse($grade_item->is_locked($this->user[1]->id)); + $this->assertTrue($grade_item->set_locked(true, true, false)); + $this->assertTrue($grade_item->is_locked()); + $this->assertTrue($grade_item->is_locked($this->user[1]->id)); + } + + // Test hiding of grade items + protected function sub_test_grade_item_set_hidden() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'set_hidden')); + + $grade = new grade_grade($grade_item->get_final($this->user[1]->id)); + $this->assertEquals(0, $grade_item->hidden); + $this->assertEquals(0, $grade->hidden); + + $grade_item->set_hidden(666, true); + $grade = new grade_grade($grade_item->get_final($this->user[1]->id)); + + $this->assertEquals(666, $grade_item->hidden); + $this->assertEquals(666, $grade->hidden); + } + + protected function sub_test_grade_item_is_hidden() { + $grade_item = new grade_item($this->grade_items[0]); + $this->assertTrue(method_exists($grade_item, 'is_hidden')); + + $this->assertFalse($grade_item->is_hidden()); + $this->assertFalse($grade_item->is_hidden(1)); + + $grade_item->set_hidden(1); + $this->assertTrue($grade_item->is_hidden()); + $this->assertTrue($grade_item->is_hidden(1)); + + $grade_item->set_hidden(666); + $this->assertFalse($grade_item->is_hidden()); + $this->assertFalse($grade_item->is_hidden(1)); + + $grade_item->set_hidden(time()+666); + $this->assertTrue($grade_item->is_hidden()); + $this->assertTrue($grade_item->is_hidden(1)); + } + + protected function sub_test_grade_item_is_category_item() { + $grade_item = new grade_item($this->grade_items[3]); + $this->assertTrue(method_exists($grade_item, 'is_category_item')); + $this->assertTrue($grade_item->is_category_item()); + } + + protected function sub_test_grade_item_is_course_item() { + $grade_item = grade_item::fetch_course_item($this->courseid); + $this->assertTrue(method_exists($grade_item, 'is_course_item')); + $this->assertTrue($grade_item->is_course_item()); + } + + protected function sub_test_grade_item_fetch_course_item() { + $grade_item = grade_item::fetch_course_item($this->courseid); + $this->assertTrue(method_exists($grade_item, 'fetch_course_item')); + $this->assertEquals($grade_item->itemtype, 'course'); + } + + protected function sub_test_grade_item_depends_on() { + $grade_item = new grade_item($this->grade_items[1]); + + // calculated grade dependency + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $this->assertEquals(array($this->grade_items[0]->id), $deps); + + // simulate depends on returns none when locked + $grade_item->locked = time(); + $grade_item->update(); + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $this->assertEquals(array(), $deps); + + // category dependency + $grade_item = new grade_item($this->grade_items[3]); + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $res = array($this->grade_items[4]->id, $this->grade_items[5]->id); + $this->assertEquals($res, $deps); + } + + protected function sub_test_grade_item_is_calculated() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'is_calculated')); + $this->assertTrue($grade_item->is_calculated()); + + $grade_item = new grade_item($this->grade_items[0]); + $this->assertFalse($grade_item->is_calculated()); + } + + protected function sub_test_grade_item_set_calculation() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'set_calculation')); + $grade_itemsource = new grade_item($this->grade_items[0]); + + $grade_item->set_calculation('=[['.$grade_itemsource->idnumber.']]'); + + $this->assertTrue(!empty($grade_item->needsupdate)); + $this->assertEquals('=##gi'.$grade_itemsource->id.'##', $grade_item->calculation); + } + + protected function sub_test_grade_item_get_calculation() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'get_calculation')); + $grade_itemsource = new grade_item($this->grade_items[0]); + + $denormalizedformula = str_replace('##gi'.$grade_itemsource->id.'##', '[['.$grade_itemsource->idnumber.']]', $this->grade_items[1]->calculation); + + $formula = $grade_item->get_calculation(); + $this->assertTrue(!empty($grade_item->needsupdate)); + $this->assertEquals($denormalizedformula, $formula); + } + + public function sub_test_grade_item_compute() { + $grade_item = grade_item::fetch(array('id'=>$this->grade_items[1]->id)); + $this->assertTrue(method_exists($grade_item, 'compute')); + + //check the grade_grades in the array match those in the DB then delete $this->grade_items[1]'s grade_grades + $this->grade_grades[3] = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id)); + $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[3]->id)); + $grade_grade->delete(); + + $this->grade_grades[4] = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id)); + $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[4]->id)); + $grade_grade->delete(); + + $this->grade_grades[5] = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id)); + $grade_grade = grade_grade::fetch(array('id'=>$this->grade_grades[5]->id)); + $grade_grade->delete(); + + //recalculate the grades (its a calculation so pulls values from other grade_items) and reinsert them + $grade_item->compute(); + + $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[3]->userid, 'itemid'=>$this->grade_grades[3]->itemid)); + $this->assertEquals($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade); + + $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[4]->userid, 'itemid'=>$this->grade_grades[4]->itemid)); + $this->assertEquals($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade); + + $grade_grade = grade_grade::fetch(array('userid'=>$this->grade_grades[5]->userid, 'itemid'=>$this->grade_grades[5]->itemid)); + $this->assertEquals($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade); + } +} diff --git a/lib/grade/tests/grade_outcome_test.php b/lib/grade/tests/grade_outcome_test.php new file mode 100644 index 00000000000..0561d1802f4 --- /dev/null +++ b/lib/grade/tests/grade_outcome_test.php @@ -0,0 +1,106 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/fixtures/lib.php'); + + +class grade_outcome_testcase extends grade_base_testcase { + + public function test_grade_outcome() { + $this->sub_test_grade_outcome_construct(); + $this->sub_test_grade_outcome_insert(); + $this->sub_test_grade_outcome_update(); + $this->sub_test_grade_outcome_delete(); + //$this->sub_test_grade_outcome_fetch(); + $this->sub_test_grade_outcome_fetch_all(); + } + + protected function sub_test_grade_outcome_construct() { + $params = new stdClass(); + + $params->courseid = $this->courseid; + $params->shortname = 'Team work'; + + $grade_outcome = new grade_outcome($params, false); + $this->assertEquals($params->courseid, $grade_outcome->courseid); + $this->assertEquals($params->shortname, $grade_outcome->shortname); + } + + protected function sub_test_grade_outcome_insert() { + $grade_outcome = new grade_outcome(); + $this->assertTrue(method_exists($grade_outcome, 'insert')); + + $grade_outcome->courseid = $this->courseid; + $grade_outcome->shortname = 'tw'; + $grade_outcome->fullname = 'Team work'; + + $grade_outcome->insert(); + + $last_grade_outcome = end($this->grade_outcomes); + + $this->assertEquals($grade_outcome->id, $last_grade_outcome->id + 1); + $this->assertFalse(empty($grade_outcome->timecreated)); + $this->assertFalse(empty($grade_outcome->timemodified)); + } + + protected function sub_test_grade_outcome_update() { + global $DB; + $grade_outcome = new grade_outcome($this->grade_outcomes[0]); + $this->assertTrue(method_exists($grade_outcome, 'update')); + $grade_outcome->shortname = 'Team work'; + $this->assertTrue($grade_outcome->update()); + $shortname = $DB->get_field('grade_outcomes', 'shortname', array('id' => $this->grade_outcomes[0]->id)); + $this->assertEquals($grade_outcome->shortname, $shortname); + } + + protected function sub_test_grade_outcome_delete() { + global $DB; + $grade_outcome = new grade_outcome($this->grade_outcomes[0]); + $this->assertTrue(method_exists($grade_outcome, 'delete')); + + $this->assertTrue($grade_outcome->delete()); + $this->assertFalse($DB->get_record('grade_outcomes', array('id' => $grade_outcome->id))); + } + + protected function sub_test_grade_outcome_fetch() { + $grade_outcome = new grade_outcome(); + $this->assertTrue(method_exists($grade_outcome, 'fetch')); + + $grade_outcome = grade_outcome::fetch(array('id'=>$this->grade_outcomes[0]->id)); + $grade_outcome->load_scale(); + $this->assertEquals($this->grade_outcomes[0]->id, $grade_outcome->id); + $this->assertEquals($this->grade_outcomes[0]->shortname, $grade_outcome->shortname); + + $this->assertEquals($this->scale[2]->id, $grade_outcome->scale->id); + } + + protected function sub_test_grade_outcome_fetch_all() { + $grade_outcome = new grade_outcome(); + $this->assertTrue(method_exists($grade_outcome, 'fetch_all')); + + $grade_outcomes = grade_outcome::fetch_all(array()); + $this->assertEquals(count($this->grade_outcomes), count($grade_outcomes)); + } +} diff --git a/lib/grade/tests/grade_scale_test.php b/lib/grade/tests/grade_scale_test.php new file mode 100644 index 00000000000..0a2f21c0af9 --- /dev/null +++ b/lib/grade/tests/grade_scale_test.php @@ -0,0 +1,130 @@ +. + +/** + * @package core_grades + * @category phpunit + * @copyright nicolas@moodle.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__.'/fixtures/lib.php'); + + +class grade_scale_testcase extends grade_base_testcase { + + public function test_grade_scale() { + $this->sub_test_scale_construct(); + $this->sub_test_grade_scale_insert(); + $this->sub_test_grade_scale_update(); + $this->sub_test_grade_scale_delete(); + $this->sub_test_grade_scale_fetch(); + $this->sub_test_scale_load_items(); + $this->sub_test_scale_compact_items(); + } + + protected function sub_test_scale_construct() { + $params = new stdClass(); + $params->name = 'unittestscale3'; + $params->courseid = $this->course->id; + $params->userid = $this->userid; + $params->scale = 'Distinction, Very Good, Good, Pass, Fail'; + $params->description = 'This scale is used to mark standard assignments.'; + $params->timemodified = time(); + + $scale = new grade_scale($params, false); + + $this->assertEquals($params->name, $scale->name); + $this->assertEquals($params->scale, $scale->scale); + $this->assertEquals($params->description, $scale->description); + + } + + protected function sub_test_grade_scale_insert() { + $grade_scale = new grade_scale(); + $this->assertTrue(method_exists($grade_scale, 'insert')); + + $grade_scale->name = 'unittestscale3'; + $grade_scale->courseid = $this->courseid; + $grade_scale->userid = $this->userid; + $grade_scale->scale = 'Distinction, Very Good, Good, Pass, Fail'; + $grade_scale->description = 'This scale is used to mark standard assignments.'; + + $grade_scale->insert(); + + $last_grade_scale = end($this->scale); + + $this->assertEquals($grade_scale->id, $last_grade_scale->id + 1); + $this->assertTrue(!empty($grade_scale->timecreated)); + $this->assertTrue(!empty($grade_scale->timemodified)); + } + + protected function sub_test_grade_scale_update() { + global $DB; + $grade_scale = new grade_scale($this->scale[1]); + $this->assertTrue(method_exists($grade_scale, 'update')); + + $grade_scale->name = 'Updated info for this unittest grade_scale'; + $this->assertTrue($grade_scale->update()); + $name = $DB->get_field('scale', 'name', array('id' => $this->scale[1]->id)); + $this->assertEquals($grade_scale->name, $name); + } + + protected function sub_test_grade_scale_delete() { + global $DB; + $grade_scale = new grade_scale($this->scale[4]);//choose one we're not using elsewhere + $this->assertTrue(method_exists($grade_scale, 'delete')); + + $this->assertTrue($grade_scale->delete()); + $this->assertFalse($DB->get_record('scale', array('id' => $grade_scale->id))); + + //keep the reference collection the same as what is in the database + unset($this->scale[4]); + } + + protected function sub_test_grade_scale_fetch() { + $grade_scale = new grade_scale(); + $this->assertTrue(method_exists($grade_scale, 'fetch')); + + $grade_scale = grade_scale::fetch(array('id'=>$this->scale[0]->id)); + $this->assertEquals($this->scale[0]->id, $grade_scale->id); + $this->assertEquals($this->scale[0]->name, $grade_scale->name); + } + + protected function sub_test_scale_load_items() { + $scale = new grade_scale($this->scale[0]); + $this->assertTrue(method_exists($scale, 'load_items')); + + $scale->load_items(); + $this->assertEquals(7, count($scale->scale_items)); + $this->assertEquals('Fairly neutral', $scale->scale_items[2]); + + } + + protected function sub_test_scale_compact_items() { + $scale = new grade_scale($this->scale[0]); + $this->assertTrue(method_exists($scale, 'compact_items')); + + $scale->load_items(); + $scale->scale = null; + $scale->compact_items(); + + // The original string and the new string may have differences in whitespace around the delimiter, and that's OK + $this->assertEquals(preg_replace('/\s*,\s*/', ',', $this->scale[0]->scale), $scale->scale); + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c70d9077ea9..b847fdd5889 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -29,6 +29,11 @@ lib/ajax/tests lib/form/tests + + lib/grade/tests + grade/tests + grade/grading/tests + backup/controller/tests backup/converter/moodle1/tests @@ -40,10 +45,6 @@ course/tests - - grade/tests - grade/grading/tests -