package com.github.donkirkby.vograbulary.ultraghost;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import com.github.donkirkby.vograbulary.Scheduler;
import com.github.donkirkby.vograbulary.VograbularyPreferences;
import com.github.donkirkby.vograbulary.ultraghost.DummyScreen.Focus;
public class ControllerTest {
private static final int MATCH_SCORE = 10;
private Controller controller;
private DummyRandom random;
private Puzzle startPuzzle;
private DummyScreen screen;
private Student student;
private Student student2;
private ComputerStudent computerStudent;
private VograbularyPreferences preferences;
private Runnable scoreTask;
private Runnable searchTask;
private Scheduler scheduler = new Scheduler() {
@Override
public void scheduleRepeating(Runnable task, int periodMilliseconds) {
if (scoreTask == null) {
scoreTask = task;
}
else {
searchTask = task;
}
}
@Override
public void cancel(Runnable task) {
if (scoreTask == task) {
scoreTask = null;
}
else {
searchTask = null;
}
}
};
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() {
random = new DummyRandom();
random.setPuzzles("AAA", "AAA", "AAA");
screen = new DummyScreen();
WordList wordList = new WordList();
wordList.read(Arrays.asList("ROPE", "PIECE", "PIPE"));
preferences = mock(VograbularyPreferences.class);
when(preferences.getComputerStudentVocabularySize()).thenReturn(
wordList.size());
student = new Student("Student");
student2 = new Student("Student 2");
computerStudent = new ComputerStudent(preferences);
controller = new Controller();
scoreTask = null;
searchTask = null;
controller.setPreferences(preferences);
controller.setScheduler(scheduler);
controller.setWordList(wordList);
controller.setRandom(random);
controller.setScreen(screen);
startPuzzle = new Puzzle("RPE", student, wordList);
createMatch(student, student2);
controller.watchPuzzle(startPuzzle);
}
private void createMatch(Student... students) {
Match match = new Match(MATCH_SCORE, students);
match.setPuzzle(startPuzzle);
match.setRandom(random);
controller.clearStudents();
for (Student student : students) {
controller.addStudent(student);
student.setMatch(match);
}
screen.setMatch(match);
}
@Test
public void startPuzzle() {
String expectedLetters = "PIE";
random.setPuzzles(expectedLetters);
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat(
"puzzle",
puzzle,
not(anyOf(sameInstance(startPuzzle), nullValue())));
assertThat("letters", puzzle.getLetters(), is(expectedLetters));
}
@Test
public void searchTask() {
controller.start();
assertThat("search task", searchTask, notNullValue());
}
@Test
public void cancelMatch() {
controller.start();
boolean isScheduledBefore = searchTask != null;
controller.cancelMatch();
boolean isScheduledAfter = searchTask != null;
assertThat("is scheduled before cancel", isScheduledBefore, is(true));
assertThat("is scheduled after cancel", isScheduledAfter, is(false));
}
@Test
public void cancelMatchBeforeStart() {
// This does nothing, but we should check that it doesn't throw an
// exception.
controller.cancelMatch();
}
@Test
public void startFirstStudent() {
random.setStartingStudent(0);
Student expectedStudent = student;
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat("owner", puzzle.getOwner(), is(expectedStudent));
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void startOtherStudent() {
random.setStartingStudent(1);
Student expectedStudent = student2;
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat("owner", puzzle.getOwner(), is(expectedStudent));
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void startComputerStudent() {
createMatch(computerStudent, student);
random.setStartingStudent(0);
Student expectedStudent = computerStudent;
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat("owner", puzzle.getOwner(), is(expectedStudent));
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Thinking));
assertThat("letters", computerStudent.getCurrentPuzzle(), is(puzzle));
}
@Test
public void computerStudentSolve() {
random.setPuzzles("RPE");
computerStudent.setMaxSearchBatchCount(1);
createMatch(computerStudent, student);
random.setStartingStudent(0);
controller.start();
int previousRefreshCount = screen.getRefreshCount();
searchTask.run();
Puzzle puzzle = screen.getPuzzle();
assertThat("solution", puzzle.getSolution(), is("ROPE"));
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Response));
assertThat(
"refresh count",
screen.getRefreshCount(),
is(previousRefreshCount+1));
assertThat("isScheduled", searchTask, nullValue());
}
@Test
public void computerStudentSolveAfter2Batches() {
random.setPuzzles("PIE");
computerStudent.setMaxSearchBatchCount(2);
createMatch(computerStudent, student);
random.setStartingStudent(0);
controller.start();
Puzzle puzzle = screen.getPuzzle();
searchTask.run();
String solutionAfterSearch1 = puzzle.getSolution();
boolean isScheduledAfterSearch1 = searchTask != null;
searchTask.run();
String solutionAfterSearch2 = puzzle.getSolution();
boolean isScheduledAfterSearch2 = searchTask != null;
assertThat("solution1", solutionAfterSearch1, nullValue());
assertThat("solution2", solutionAfterSearch2, is("PIECE"));
assertThat("is scheduled 1", isScheduledAfterSearch1, is(true));
assertThat("is scheduled 2", isScheduledAfterSearch2, is(false));
}
@Test
public void startSecondPuzzle() {
random.setStartingStudent(0);
Student expectedStudent = student2;
controller.start();
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat("owner", puzzle.getOwner(), is(expectedStudent));
}
@Test
public void startSecondPuzzleWrapAround() {
random.setStartingStudent(1);
Student expectedStudent = student;
controller.start();
controller.start();
Puzzle puzzle = screen.getPuzzle();
assertThat("owner", puzzle.getOwner(), is(expectedStudent));
}
@Test
public void solveWithHumanOwnerAgainstHuman() {
startPuzzle.setSolution("");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Response));
}
@Test
public void solutionNotAMatch() {
startPuzzle.setSolution("pipe");
int startRefreshCount = screen.getRefreshCount();
controller.solve();
WordResult result = startPuzzle.getResult();
assertThat("result", result, is(WordResult.NOT_A_MATCH));
assertThat(
"refresh count",
screen.getRefreshCount(),
is(startRefreshCount+1));
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void solutionNotAWord() {
startPuzzle.setSolution("pixe");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void solutionTooShort() {
startPuzzle.setMinimumWordLength(5);
startPuzzle.setSolution("rope");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void solutionTooSoon() {
startPuzzle.setPreviousWord("ROPE");
startPuzzle.setSolution("rope");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Solution));
}
@Test
public void solutionTimesOut() {
startPuzzle.adjustScore(45);
startPuzzle.setSolution("pixe");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Result));
}
@Test
public void solveWithHumanOwnerAgainstComputer() {
createMatch(student, computerStudent);
startPuzzle.setSolution("");
int startRefreshCount = screen.getRefreshCount();
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Result));
assertThat("response", startPuzzle.getResponse(), is(""));
assertThat(
"refresh count",
screen.getRefreshCount(),
greaterThan(startRefreshCount));
assertThat("score", student.getScore(), is(33));
}
@Test
public void noResponseFromActiveStudent() {
createMatch(computerStudent, student);
random.setStartingStudent(1);
startPuzzle.setSolution("");
controller.solve();
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Result));
}
@Test
public void cancelSearchTask() {
controller.clearStudents();
controller.addStudent(student);
controller.addStudent(computerStudent);
random.setPuzzles("RPE");
controller.start();
Puzzle puzzle = screen.getPuzzle();
puzzle.setSolution("");
controller.solve();
assertThat("search task", searchTask, nullValue());
assertThat("score task", scoreTask, nullValue());
}
@Test
public void cancelScoreTask() {
random.setPuzzles("RPE");
controller.start();
Puzzle puzzle = screen.getPuzzle();
puzzle.setSolution("");
controller.solve();
puzzle.setResponse("");
assertThat("search task", searchTask, nullValue());
assertThat("score task", scoreTask, nullValue());
}
@Test
public void keepScoreTaskAfterInvalidResponse() {
random.setPuzzles("RPE");
controller.start();
Puzzle puzzle = screen.getPuzzle();
puzzle.setSolution("");
controller.solve();
puzzle.setResponse("RICE");
assertThat("score task", scoreTask, notNullValue());
}
@Test
public void focusAfterInvalidResponse() {
random.setPuzzles("RPE");
controller.start();
Puzzle puzzle = screen.getPuzzle();
puzzle.setSolution("");
controller.solve();
screen.focusSolution(); // just to check that it gets updated.
puzzle.setResponse("RICE");
assertThat(
"focus",
screen.getCurrentFocus(),
is(DummyScreen.Focus.Response));
}
@Test
public void respond() {
startPuzzle.setSolution("");
int startRefreshCount = screen.getRefreshCount();
startPuzzle.setResponse("rope");
Focus focus = screen.getCurrentFocus();
assertThat("focus", focus, is(Focus.Result));
assertThat("result", startPuzzle.getResult(), is(WordResult.WORD_FOUND));
assertThat("refresh count", screen.getRefreshCount(), greaterThan(startRefreshCount));
}
@Test
public void summary() {
startPuzzle.setSolution("");
startPuzzle.setResponse("rope");
assertThat("score", student.getScore(), is(-33));
}
@Test
public void adjustScore() {
controller.start();
Puzzle puzzle = screen.getPuzzle();
int seconds = 10;
int loopCount = seconds * 1000 / Controller.SCORE_MILLISECONDS;
for (int i = 0; i < loopCount; i++) {
scoreTask.run();
}
assertThat("score", puzzle.getScore(WordResult.NOT_IMPROVED), is(98));
assertThat("refresh count", screen.getScoreRefreshCount(), is(loopCount));
}
@Test
public void adjustScoreOnResponseUntilTimeout() {
random.setPuzzles("RPE");
controller.start();
Puzzle puzzle = screen.getPuzzle();
puzzle.setSolution("ROPE");
controller.solve();
int seconds = (int) Puzzle.MAX_DELAY/2;
int loopCount = seconds * 1000 / Controller.SCORE_MILLISECONDS;
for (int i = 0; i < loopCount; i++) {
scoreTask.run();
}
assertThat("score", puzzle.getScore(WordResult.NOT_IMPROVED), is(100));
}
@Test
public void newMatch() {
controller.start();
Match match1 = controller.getMatch();
int expectedWordLength = 7;
when(preferences.getUltraghostMinimumWordLength()).thenReturn(
expectedWordLength);
controller.clearStudents();
controller.addStudent(student);
controller.addStudent(student2);
controller.start();
Match match2 = controller.getMatch();
assertThat("match1", match1, notNullValue());
assertThat("match2", match2, allOf(notNullValue(), not(match1)));
assertThat(
"word length",
match2.getPuzzle().getMinimumWordLength(),
is(expectedWordLength));
}
@Test
public void changeWordList() {
WordList newWordList = new WordList();
newWordList.read(Arrays.asList("NEW", "WORD", "LIST"));
controller.setWordList(newWordList);
assertThat("student's word list", student.getWordList(), is(newWordList));
}
@Test
public void hint() {
startPuzzle.setSolution("");
startPuzzle.setResponse("");
assertThat("hint", startPuzzle.getHint(), is("hint: ROPE"));
}
@Test
public void noHint() {
startPuzzle.setSolution("rope");
startPuzzle.setResponse("");
assertThat("hint", startPuzzle.getHint(), is("Perfect!"));
}
@Test
public void hintAfterInvalid() {
startPuzzle.setSolution("r");
float solutionSeconds = 1000;
startPuzzle.adjustScore(solutionSeconds);
assertThat("hint", startPuzzle.getHint(), is("hint: ROPE"));
}
@Test
public void addStudent() {
controller = new Controller();
controller.setPreferences(preferences);
controller.setScreen(new DummyScreen());
controller.addStudent(student);
controller.addStudent(student2);
assertThat(
"students",
controller.getMatch().getStudents(),
containsInAnyOrder(student, student2));
}
@Test
public void addStudentAfterGetMatch() {
controller = new Controller();
controller.setPreferences(preferences);
controller.setScreen(new DummyScreen());
controller.getMatch();
controller.addStudent(student);
controller.addStudent(student2);
assertThat(
"students",
controller.getMatch().getStudents(),
containsInAnyOrder(student, student2));
}
}