diff --git a/common/test/data/graded/roots/2012_Fall.xml b/common/test/data/graded/roots/2012_Fall.xml
index b71528809b..b046a28d3b 100644
--- a/common/test/data/graded/roots/2012_Fall.xml
+++ b/common/test/data/graded/roots/2012_Fall.xml
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/lms/djangoapps/courseware/tests/tests.py b/lms/djangoapps/courseware/tests/tests.py
index f3b086748d..c0932eee0b 100644
--- a/lms/djangoapps/courseware/tests/tests.py
+++ b/lms/djangoapps/courseware/tests/tests.py
@@ -10,8 +10,9 @@ from pprint import pprint
from urlparse import urlsplit, urlunsplit
from django.contrib.auth.models import User, Group
+from django.core.handlers.wsgi import WSGIRequest
from django.test import TestCase
-from django.test.client import Client
+from django.test.client import Client, RequestFactory
from django.conf import settings
from django.core.urlresolvers import reverse
from mock import patch, Mock
@@ -20,7 +21,9 @@ from override_settings import override_settings
import xmodule.modulestore.django
# Need access to internal func to put users in the right group
+from courseware import grades
from courseware.access import _course_staff_group_name
+from courseware.models import StudentModuleCache
from student.models import Registration
from xmodule.modulestore.django import modulestore
@@ -640,3 +643,130 @@ class RealCoursesLoadTestCase(PageLoader):
# ========= TODO: check ajax interaction here too?
+
+
+@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
+class TestCourseGrader(PageLoader):
+ """Check that a course gets graded properly"""
+
+ # NOTE: setUpClass() runs before override_settings takes effect, so
+ # can't do imports there without manually hacking settings.
+
+ def setUp(self):
+ xmodule.modulestore.django._MODULESTORES = {}
+ courses = modulestore().get_courses()
+
+ def find_course(course_id):
+ """Assumes the course is present"""
+ return [c for c in courses if c.id==course_id][0]
+
+ self.graded_course = find_course("edX/graded/2012_Fall")
+
+ # create a test student
+ self.student = 'view@test.com'
+ self.password = 'foo'
+ self.create_account('u1', self.student, self.password)
+ self.activate_user(self.student)
+ self.enroll(self.graded_course)
+
+ self.student_user = user(self.student)
+
+ self.factory = RequestFactory()
+
+ def check_grade_percent(self, percent):
+
+ student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
+ self.graded_course.id, self.student_user, self.graded_course)
+
+ fake_request = self.factory.get(reverse('progress',
+ kwargs={'course_id': self.graded_course.id}))
+
+ grade_summary = grades.grade(self.student_user, fake_request,
+ self.graded_course, student_module_cache)
+ self.assertEqual(grade_summary['percent'], percent)
+
+ def submit_question_answer(self, problem_url_name, responses):
+ """
+ The field names of a problem are hard to determine. This method only works
+ for the problems used in the edX/graded course, which has fields named in the
+ following form:
+ input_i4x-edX-graded-problem-H1P3_2_1
+ input_i4x-edX-graded-problem-H1P3_2_2
+ """
+
+
+ problem_location = "i4x://edX/graded/problem/{0}".format(problem_url_name)
+
+ modx_url = reverse('modx_dispatch',
+ kwargs={
+ 'course_id' : self.graded_course.id,
+ 'location' : problem_location,
+ 'dispatch' : 'problem_check', }
+ )
+
+ resp = self.client.post(modx_url, {
+ 'input_i4x-edX-graded-problem-{0}_2_1'.format(problem_url_name): responses[0],
+ 'input_i4x-edX-graded-problem-{0}_2_2'.format(problem_url_name): responses[1],
+ })
+ print "modx_url" , modx_url, "responses" , responses
+ print "resp" , resp
+
+ return resp
+
+ def reset_question_answer(self, problem_url_name):
+ problem_location = "i4x://edX/graded/problem/{0}".format(problem_url_name)
+
+ modx_url = reverse('modx_dispatch',
+ kwargs={
+ 'course_id' : self.graded_course.id,
+ 'location' : problem_location,
+ 'dispatch' : 'problem_reset', }
+ )
+
+ resp = self.client.post(modx_url)
+ return resp
+
+
+ def test_get_graded(self):
+ #### Check that the grader shows we have 0% in the course
+ self.check_grade_percent(0)
+
+
+ #### Submit the answers to a few problems as ajax calls
+
+ # Only get half of the first problem correct
+ self.submit_question_answer('H1P1', ['Correct', 'Incorrect'])
+ self.check_grade_percent(0.06)
+
+ # Get both parts of the first problem correct
+ self.reset_question_answer('H1P1')
+ self.submit_question_answer('H1P1', ['Correct', 'Correct'])
+ self.check_grade_percent(0.13)
+
+ # This problem is shown in an ABTest
+ self.submit_question_answer('H1P2', ['Correct', 'Correct'])
+ self.check_grade_percent(0.25)
+
+ # This problem is hidden in an ABTest. Getting it correct doesn't change total grade
+ self.submit_question_answer('H1P3', ['Correct', 'Correct'])
+ self.check_grade_percent(0.25)
+
+
+ # On the second homework, we only answer half of the questions.
+ # Then it will be dropped when homework three becomes the higher percent
+ self.submit_question_answer('H2P1', ['Correct', 'Correct'])
+ self.check_grade_percent(0.38)
+
+
+ # Third homework
+ self.submit_question_answer('H3P1', ['Correct', 'Correct'])
+ self.check_grade_percent(0.38) # Score didn't change
+
+ self.submit_question_answer('H3P2', ['Correct', 'Correct'])
+ self.check_grade_percent(0.5) # Now homework2 dropped. Score changes
+
+
+ # Now we answer the final question (worth half of the grade)
+ self.submit_question_answer('FinalQuestion', ['Correct', 'Correct'])
+ self.check_grade_percent(1.0) # Hooray! We got 100%
+