new_contents: """
  Test grade calculation.
  """
  from django.http import Http404
  from django.test.utils import override_settings
  from mock import patch
  
  from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
  from student.tests.factories import UserFactory
  from xmodule.modulestore.tests.factories import CourseFactory
  from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
  
  from courseware.grades import grade, iterate_grades_for
  
  
  def _grade_with_errors(student, request, course, keep_raw_scores=False):
      """This fake grade method will throw exceptions for student3 and
      student4, but allow any other students to go through normal grading.
  
      It's meant to simulate when something goes really wrong while trying to
      grade a particular student, so we can test that we won't kill the entire
      course grading run.
      """
      if student.username in ['student3', 'student4']:
          raise Exception("I don't like {}".format(student.username))
  
      return grade(student, request, course, keep_raw_scores=keep_raw_scores)
  
  
  @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
  class TestGradeIteration(ModuleStoreTestCase):
      """
      Test iteration through student gradesets.
      """
      COURSE_NUM = "1000"
      COURSE_NAME = "grading_test_course"
  
      def setUp(self):
          """
          Create a course and a handful of users to assign grades
          """
          self.course = CourseFactory.create(
              display_name=self.COURSE_NAME,
              number=self.COURSE_NUM
          )
          self.students = [
              UserFactory.create(username='student1'),
              UserFactory.create(username='student2'),
              UserFactory.create(username='student3'),
              UserFactory.create(username='student4'),
              UserFactory.create(username='student5'),
          ]
  
      def test_empty_student_list(self):
          """If we don't pass in any students, it should return a zero-length
          iterator, but it shouldn't error."""
          gradeset_results = list(iterate_grades_for(self.course.id, []))
          self.assertEqual(gradeset_results, [])
  
      def test_nonexistent_course(self):
          """If the course we want to get grades for does not exist, a `Http404`
          should be raised. This is a horrible crossing of abstraction boundaries
          and should be fixed, but for now we're just testing the behavior. :-("""
          with self.assertRaises(Http404):
              gradeset_results = iterate_grades_for("I/dont/exist", [])
              gradeset_results.next()
  
      def test_all_empty_grades(self):
          """No students have grade entries"""
          all_gradesets, all_errors = self._gradesets_and_errors_for(self.course.id, self.students)
          self.assertEqual(len(all_errors), 0)
          for gradeset in all_gradesets.values():
              self.assertIsNone(gradeset['grade'])
              self.assertEqual(gradeset['percent'], 0.0)
  
      @patch('courseware.grades.grade', _grade_with_errors)
      def test_grading_exception(self):
          """Test that we correctly capture exception messages that bubble up from
          grading. Note that we only see errors at this level if the grading
          process for this student fails entirely due to an unexpected event --
          having errors in the problem sets will not trigger this.
  
          We patch the grade() method with our own, which will generate the errors
          for student3 and student4.
          """
          all_gradesets, all_errors = self._gradesets_and_errors_for(self.course.id, self.students)
          student1, student2, student3, student4, student5 = self.students
          self.assertEqual(
              all_errors,
              {
                  student3: "I don't like student3",
                  student4: "I don't like student4"
              }
          )
  
          # But we should still have five gradesets
          self.assertEqual(len(all_gradesets), 5)
  
          # Even though two will simply be empty
          self.assertFalse(all_gradesets[student3])
          self.assertFalse(all_gradesets[student4])
  
          # The rest will have grade information in them
          self.assertTrue(all_gradesets[student1])
          self.assertTrue(all_gradesets[student2])
          self.assertTrue(all_gradesets[student5])
  
      ################################# Helpers #################################
      def _gradesets_and_errors_for(self, course_id, students):
          """Simple helper method to iterate through student grades and give us
          two dictionaries -- one that has all students and their respective
          gradesets, and one that has only students that could not be graded and
          their respective error messages."""
          students_to_gradesets = {}
          students_to_errors = {}
  
          for student, gradeset, err_msg in iterate_grades_for(course_id, students):
              students_to_gradesets[student] = gradeset
              if err_msg:
                  students_to_errors[student] = err_msg
  
          return students_to_gradesets, students_to_errors
  
subject: Add basic tests of gradeset iteration.