001package edu.pdx.cs.joy.grader;
002
003import edu.pdx.cs.joy.grader.gradebook.Assignment;
004import edu.pdx.cs.joy.grader.gradebook.GradeBook;
005import edu.pdx.cs.joy.grader.gradebook.Student;
006import org.junit.jupiter.api.Test;
007import org.mockito.ArgumentCaptor;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011import java.io.Reader;
012import java.io.StringReader;
013import java.util.regex.Matcher;
014
015import static org.hamcrest.MatcherAssert.assertThat;
016import static org.hamcrest.Matchers.containsString;
017import static org.hamcrest.Matchers.equalTo;
018import static org.junit.jupiter.api.Assertions.assertThrows;
019import static org.mockito.Mockito.*;
020
021public class ProjectGradesImporterTest {
022
023  private Logger logger = LoggerFactory.getLogger(this.getClass().getPackage().getName());
024
025  @Test
026  public void gradedProjectWithNoGradeThrowsScoreNotFoundException() throws ProjectGradesImporter.ScoreNotFoundException {
027    GradedProject project = new GradedProject();
028    project.addLine("asdfhjkl");
029    project.addLine("iadguow");
030
031    assertThrows(ProjectGradesImporter.ScoreNotFoundException.class, () ->
032      ProjectGradesImporter.getScoreFrom(project.getReader())
033    );
034  }
035
036  @Test
037  public void scoreRegularExpressionWorkWithSimpleCase() {
038    Matcher matcher = ProjectGradesImporter.scorePattern.matcher("3.0 out of 4.5");
039    assertThat(matcher.find(), equalTo(true));
040    assertThat(matcher.groupCount(), equalTo(2));
041    assertThat(matcher.group(1), equalTo("3.0"));
042    assertThat(matcher.group(2), equalTo("4.5"));
043  }
044
045  @Test
046  public void gradedProjectWithOutOfHasValidScore() throws ProjectGradesImporter.ScoreNotFoundException {
047    GradedProject project = new GradedProject();
048    project.addLine("3.4 out of 3.5");
049    project.addLine("iadguow");
050
051    ProjectGradesImporter.ProjectScore score = ProjectGradesImporter.getScoreFrom(project.getReader());
052    assertThat(score.getScore(), equalTo(3.4));
053    assertThat(score.getTotalPoints(), equalTo(3.5));
054  }
055
056  @Test
057  public void scoreMatchesRegardlessOfCase() {
058    Matcher matcher = ProjectGradesImporter.scorePattern.matcher("4.0 OUT OF 5.0");
059    assertThat(matcher.find(), equalTo(true));
060    assertThat(matcher.groupCount(), equalTo(2));
061    assertThat(matcher.group(1), equalTo("4.0"));
062    assertThat(matcher.group(2), equalTo("5.0"));
063  }
064
065  @Test
066  public void scoreMatchesIntegerPoints() {
067    Matcher matcher = ProjectGradesImporter.scorePattern.matcher("4 out of 5");
068    assertThat(matcher.find(), equalTo(true));
069    assertThat(matcher.groupCount(), equalTo(2));
070    assertThat(matcher.group(1), equalTo("4"));
071    assertThat(matcher.group(2), equalTo("5"));
072  }
073
074  @Test
075  public void scoreMatchesInTheMiddleOfOtherText() {
076    Matcher matcher = ProjectGradesImporter.scorePattern.matcher("You got 4 out of 5 points");
077    assertThat(matcher.find(), equalTo(true));
078    assertThat(matcher.groupCount(), equalTo(2));
079    assertThat(matcher.group(1), equalTo("4"));
080    assertThat(matcher.group(2), equalTo("5"));
081  }
082
083  @Test
084  public void gradedProjectWithIntegerPoints() throws ProjectGradesImporter.ScoreNotFoundException {
085    GradedProject project = new GradedProject();
086    project.addLine("3 out of 5");
087    project.addLine("iadguow");
088
089    ProjectGradesImporter.ProjectScore score = ProjectGradesImporter.getScoreFrom(project.getReader());
090    assertThat(score.getScore(), equalTo(3.0));
091    assertThat(score.getTotalPoints(), equalTo(5.0));
092  }
093
094  private class GradedProject {
095    private StringBuilder sb = new StringBuilder();
096
097    public void addLine(String line) {
098      sb.append(line);
099      sb.append("\n");
100    }
101
102    public Reader getReader() {
103      return new StringReader(sb.toString());
104    }
105  }
106
107  @Test
108  public void onlyFirstScoreIsReturned() throws ProjectGradesImporter.ScoreNotFoundException {
109    GradedProject project = new GradedProject();
110    project.addLine("3.4 out of 3.5");
111    project.addLine("iadguow");
112    project.addLine("3.3 out of 3.4");
113
114    ProjectGradesImporter.ProjectScore score = ProjectGradesImporter.getScoreFrom(project.getReader());
115    assertThat(score.getScore(), equalTo(3.4));
116    assertThat(score.getTotalPoints(), equalTo(3.5));
117  }
118
119  @Test
120  public void scoreIsRecordedInGradeBook() throws ProjectGradesImporter.ScoreNotFoundException {
121    String studentId = "student";
122    Assignment assignment = new Assignment("project", 6.0);
123
124    GradeBook gradeBook = new GradeBook("test");
125    gradeBook.addStudent(new Student(studentId));
126    gradeBook.addAssignment(assignment);
127
128    String score = "5.8";
129    GradedProject project = new GradedProject();
130    project.addLine(score + " out of 6.0");
131    project.addLine("");
132    project.addLine("asdfasd");
133
134    Logger logger = mock(Logger.class);
135    ProjectGradesImporter importer = new ProjectGradesImporter(gradeBook, assignment, logger);
136    importer.recordScoreFromProjectReport(studentId, project.getReader());
137
138    assertThat(gradeBook.getStudent(studentId).get().getGrade(assignment.getName()).getScore(), equalTo(5.8));
139    assertThat(gradeBook.isDirty(), equalTo(true));
140
141    ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
142    verify(logger).info(message.capture());
143    assertThat(message.getValue(), containsString("Recorded grade of " + score + " for " + studentId));
144  }
145
146  @Test
147  public void throwIllegalStateExceptionWhenTotalPointsInReportDoesNotMatchGradeBook() throws ProjectGradesImporter.ScoreNotFoundException {
148    String studentId = "student";
149    Assignment assignment = new Assignment("project", 8.0);
150
151    GradeBook gradeBook = new GradeBook("test");
152    gradeBook.addStudent(new Student(studentId));
153    gradeBook.addAssignment(assignment);
154
155    GradedProject project = new GradedProject();
156    project.addLine("5.8 out of 6.0");
157    project.addLine("");
158    project.addLine("asdfasd");
159
160    ProjectGradesImporter importer = new ProjectGradesImporter(gradeBook, assignment, logger);
161    assertThrows(IllegalStateException.class, () ->
162      importer.recordScoreFromProjectReport(studentId, project.getReader())
163    );
164  }
165
166  @Test
167  public void logWarningWhenStudentDoesNotExistInGradeBook() throws ProjectGradesImporter.ScoreNotFoundException {
168    String studentId = "student";
169    Assignment assignment = new Assignment("project", 6.0);
170
171    GradeBook gradeBook = new GradeBook("test");
172    gradeBook.addAssignment(assignment);
173
174    GradedProject project = new GradedProject();
175    project.addLine("5.8 out of 6.0");
176    project.addLine("");
177    project.addLine("asdfasd");
178
179    Logger logger = mock(Logger.class);
180    ProjectGradesImporter importer = new ProjectGradesImporter(gradeBook, assignment, logger);
181
182    importer.recordScoreFromProjectReport(studentId, project.getReader());
183
184    assertThat(gradeBook.containsStudent(studentId), equalTo(false));
185
186    ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
187    verify(logger, times(1)).warn(message.capture());
188    assertThat(message.getValue(), containsString("Student \"" + studentId + "\" not found in gradebook"));
189  }
190
191}