summaryrefslogtreecommitdiff
path: root/tests/unit/SpacedRepetitionModel
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/SpacedRepetitionModel')
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp420
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h27
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp304
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h24
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp36
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h14
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_test.cpp67
-rw-r--r--tests/unit/SpacedRepetitionModel/SRModel_test.h46
-rw-r--r--tests/unit/SpacedRepetitionModel/srModel.pri16
9 files changed, 954 insertions, 0 deletions
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp
new file mode 100644
index 0000000..73ab023
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp
@@ -0,0 +1,420 @@
+#include "SRModel_pickCard_test.h"
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../mocks/RandomGenerator_mock.h"
+#include "../../../src/utils/TimeProvider.h"
+
+static const StudySettings* ss = StudySettings::inst();
+
+const double SRModel_pickCard_Test::HourAgo = -1. / 24;
+
+void SRModel_pickCard_Test::SetUp()
+{
+ randomGenerator->setRand(1);
+}
+
+// Time interval
+
+TEST_F(SRModel_pickCard_Test, pick_active1)
+{
+ addStudied(-1, 1);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_active2)
+{
+ addStudied(-8.5, 8.1);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_active_and_future)
+{
+ addStudied(-1, 1);
+ addStudied(-1, 2);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2active1)
+{
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+
+ randomGenerator->setRand(0);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2active2)
+{
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_3active)
+{
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ addStudied(-1.2, 1);
+ testPicked({1, 2, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_future1)
+{
+ addStudied(-1, 2);
+ testPicked({NoCard});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_future2)
+{
+ addStudied(-1, 2);
+ addStudied(-1.1, 2);
+ testPicked({NoCard});
+}
+
+
+// Priority cards with small intervals
+// Always pick first cards with small interval
+
+TEST_F(SRModel_pickCard_Test, pick_zeroInterval) // Unreal
+{
+ addStudied(HourAgo, 0);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown)
+{
+ addUnknown(HourAgo);
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown_and_active)
+{
+ addUnknown(HourAgo);
+ addStudied(-1, 1);
+ testPicked({0, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_learning3min_and_active)
+{
+ addLearning(-3./(24*60)); // 3 min back
+ addStudied(-1, 1);
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_learning10min_and_active)
+{
+ addLearning(-10./(24*60));
+ addStudied(-1, 1);
+ testPicked({0, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_learningPrevDay_and_active)
+{
+ addLearning(-1);
+ addStudied(-1, 1);
+ testPicked({0, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown_and_2actives)
+{
+ addUnknown(HourAgo);
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ testPicked({0, 2, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remembered_and_2actives)
+{
+ addStudied(-1, ss->nextDayInterval);
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ testPicked({0, 2, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_competing_unknown_and_new)
+{
+ addUnknown(HourAgo);
+ addNew();
+ addStudied(-1, 1);
+ testPicked({0, 1, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_competing_remembered_and_new)
+{
+ addStudied(-1, ss->nextDayInterval);
+ addNew();
+ addStudied(-1, 1);
+ testPicked({0, 1, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2unknowns_and_2actives)
+{
+ addUnknown(HourAgo);
+ addUnknown(2 * HourAgo);
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ testPicked({1, 0, 3, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown_and_3actives)
+{
+ addUnknown(HourAgo);
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+ addStudied(-1.2, 1);
+ testPicked({0, 2, 3, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown_and_remembered)
+{
+ addUnknown(HourAgo);
+ addStudied(-1, ss->nextDayInterval);
+ addStudied(-1, 1);
+ testPicked({0, 1, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_incorrect_and_remembered)
+{
+ addIncorrect(HourAgo);
+ addStudied(-1, ss->nextDayInterval);
+ addStudied(-1, 1);
+ testPicked({0, 1, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_unknown_and_incorrect)
+{
+ addUnknown(HourAgo);
+ addIncorrect(HourAgo);
+ addStudied(-1, 1);
+ testPicked({0, 1, 2});
+}
+
+// New cards
+
+TEST_F(SRModel_pickCard_Test, pick_new)
+{
+ addNew();
+ testPicked({0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_new_and_studied1)
+{
+ addNew();
+ addStudied(-1.1, 1);
+
+ randomGenerator->setDouble(0.3); // Pick studied first
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_new_and_studied2)
+{
+ addNew();
+ addStudied(-1.1, 1);
+
+ // Pick new first
+ testPicked({0, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2new)
+{
+ addNew();
+ addNew();
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_3new)
+{
+ for(int i = 0; i < 3; i++)
+ addNew();
+ testPicked({1, 2, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2new_and_2studied1)
+{
+ addNew();
+ addNew();
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+
+ randomGenerator->setDouble(0.3); // Pick studied first
+ testPicked({3, 2, 1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2new_and_2studied2)
+{
+ addNew();
+ addNew();
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+
+ // Pick new first, double random = 0
+ testPicked({1, 0, 3, 2});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2new_and_2studied3)
+{
+ addNew();
+ addNew();
+ addStudied(-1, 1);
+ addStudied(-1.1, 1);
+
+ randomGenerator->setDouble(0.3); // Pick studied first
+ model.testPickCard();
+ assertCurCard(3);
+ randomGenerator->setDouble(0.1); // Pick new first
+ model.scheduleCard(4);
+ assertCurCard(1);
+ randomGenerator->setDouble(0.3);
+ model.scheduleCard(4);
+ assertCurCard(2);
+ randomGenerator->setDouble(0.1);
+ model.scheduleCard(4);
+ assertCurCard(0);
+}
+
+TEST_F(SRModel_pickCard_Test, pick_incorrect_new_and_studied)
+{
+ addIncorrect(HourAgo);
+ addNew();
+ addStudied(-1, 1);
+
+ randomGenerator->setDouble(0.3); // Pick studied first
+ testPicked({0, 2, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_newAndActive_newLimitReached)
+{
+ for(int i = 0; i < ss->newCardsDayLimit; i++)
+ addStudied(HourAgo, ss->nextDayInterval);
+
+ addNew();
+ addStudied(-1, 1);
+
+ int studiedCard = ss->newCardsDayLimit + 1;
+ testPicked({studiedCard, NoCard});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_2new_newLimitReached)
+{
+ for(int i = 0; i < ss->newCardsDayLimit; i++)
+ addStudied(HourAgo, ss->nextDayInterval);
+
+ addNew();
+ addNew();
+
+ testPicked({NoCard});
+}
+
+// Remaining incorrect cards
+// Must be taken in the end (both active and new cards are finished)
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_unknown)
+{
+ addUnknown(Now);
+ addStudied(-1, 1);
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_incorrect)
+{
+ addIncorrect(Now);
+ addStudied(-1, 1);
+ testPicked({1, 0});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_remembered)
+{
+ addStudied(Now, ss->nextDayInterval);
+ addStudied(-1, 1);
+ testPicked({1, NoCard});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_unknownAndIncorrect)
+{
+ addIncorrect(HourAgo);
+ addUnknown(Now);
+ addStudied(-1, 1);
+ testPicked({0, 2, 1});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_after_new)
+{
+ addNew();
+ testPicked({0, 0, 0, NoCard});
+}
+
+TEST_F(SRModel_pickCard_Test, pick_remaining_after_2new)
+{
+ addNew();
+ addNew();
+ testPicked({1, 0, 1, 0, 1, 0, NoCard});
+}
+
+
+
+QDateTime SRModel_pickCard_Test::timeFromDelta(double daysDelta)
+{
+ return TimeProvider::get().addSecs((int)(daysDelta * 24 * 60 * 60));
+}
+
+void SRModel_pickCard_Test::addStudied(double daysDelta, double interval,
+ int grade, int level)
+{
+ QString name = addRecord();
+ pack.generateQuestions();
+ StudyRecord study(level, grade, 2.5, interval);
+ study.date = timeFromDelta(daysDelta);
+ pack.addStudyRecord(name, study);
+}
+
+void SRModel_pickCard_Test::addUnknown(double daysDelta)
+{
+ addStudied(daysDelta, ss->unknownInterval, StudyRecord::Unknown,
+ StudyRecord::ShortLearning);
+}
+
+void SRModel_pickCard_Test::addIncorrect(double daysDelta)
+{
+ addStudied(daysDelta, ss->incorrectInterval, StudyRecord::Incorrect,
+ StudyRecord::ShortLearning);
+}
+
+void SRModel_pickCard_Test::addLearning(double daysDelta)
+{
+ addStudied(daysDelta, ss->learningInterval, StudyRecord::Good,
+ StudyRecord::LongLearning);
+}
+
+void SRModel_pickCard_Test::addNew()
+{
+ addRecord();
+ pack.generateQuestions();
+}
+
+void SRModel_pickCard_Test::assertCurCard(int expCard)
+{
+ if(expCard < 0)
+ {
+ ASSERT_FALSE(model.getCurCard());
+ return;
+ }
+ ASSERT_TRUE(model.getCurCard());
+ ASSERT_EQ(expCard, model.getCurCard()->getQuestion().toInt());
+}
+
+void SRModel_pickCard_Test::testPicked(const vector<int>& expCards)
+{
+ vector<int> pickedCards;
+ for(unsigned i = 0; i < expCards.size(); i++)
+ {
+ if(i == 0)
+ model.testPickCard();
+ else
+ model.scheduleCard(StudyRecord::Good);
+ if(model.getCurCard())
+ pickedCards.push_back(model.getCurCard()->getQuestion().toInt());
+ else
+ pickedCards.push_back(-1);
+ }
+ ASSERT_EQ(expCards, pickedCards);
+}
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h
new file mode 100644
index 0000000..aacb59a
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h
@@ -0,0 +1,27 @@
+#include "SRModel_test.h"
+
+#include <vector>
+
+using namespace std;
+
+class SRModel_pickCard_Test: public SRModelTest
+{
+public:
+ static const int NoCard = -1;
+ static const int Now = 0;
+ static const double HourAgo;
+
+protected:
+ static QDateTime timeFromDelta(double daysDelta);
+
+protected:
+ void SetUp();
+ void addStudied(double daysDelta, double interval, int grade = 4,
+ int level = StudyRecord::Repeating);
+ void addUnknown(double daysDelta);
+ void addIncorrect(double daysDelta);
+ void addLearning(double daysDelta);
+ void addNew();
+ void assertCurCard(int expCard);
+ void testPicked(const vector<int>& expCards);
+};
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp
new file mode 100644
index 0000000..cd65154
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp
@@ -0,0 +1,304 @@
+#include "SRModel_schedule_test.h"
+#include "../../../src/utils/TimeProvider.h"
+
+const double SRModel_schedule_Test::startE = StudySettings().initEasiness;
+
+void SRModel_schedule_Test::SetUp()
+{
+ StudySettings::inst()->schedRandomness = StudySettings().schedRandomness;
+ randomGenerator->setDouble(0.3);
+ cardName = createCard();
+}
+
+static const StudySettings* ss = StudySettings::inst();
+
+
+// New
+// Grades 1, 2, 3 not used
+
+TEST_F(SRModel_schedule_Test, new_good) // Levels: 1 -> 2
+ {
+ setNewCard();
+ schedule(4);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, new_easy) // Levels: 1 -> 3
+ {
+ setNewCard();
+ schedule(5);
+ checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval);
+ }
+
+
+// Short Learning
+
+TEST_F(SRModel_schedule_Test, short_unknown) // same level
+ {
+ setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval});
+ schedule(1);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, short_incor) // same level with longer interval
+ {
+ setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval});
+ schedule(2);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval);
+ }
+
+ // grade 3 not used
+
+TEST_F(SRModel_schedule_Test, short_good) // Levels: 2 -> 3
+ {
+ setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval});
+ schedule(4);
+ checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, short_easy) // Levels: 2 -> repeat next day
+ {
+ setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval});
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, startE, ss->nextDayInterval);
+ }
+
+
+// Short learning from previous day:
+// Is promoted directly to next day repeating
+
+TEST_F(SRModel_schedule_Test, short_prevDay_difficult)
+ {
+ setStudy({StudyRecord::ShortLearning, 4, 2.5, ss->unknownInterval}, -1);
+ schedule(3);
+ checkStudy(StudyRecord::Repeating, 2.36, 2.18772);
+ }
+
+TEST_F(SRModel_schedule_Test, short_prevDay_good)
+ {
+ setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}, -1);
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, startE, 2.3175);
+ }
+
+TEST_F(SRModel_schedule_Test, short_prevDay_easy)
+ {
+ setStudy({StudyRecord::ShortLearning, 4, 2.5, ss->unknownInterval}, -1);
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, 2.6, 2.4102);
+ }
+
+
+// Long Learning
+
+TEST_F(SRModel_schedule_Test, long_unknown) // Levels: 3 -> 2
+ {
+ setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval});
+ schedule(1);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, long_incor) // Levels: 3 -> 2
+ {
+ setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval});
+ schedule(2);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval);
+ }
+
+ // grade 3 not used
+
+TEST_F(SRModel_schedule_Test, long_good) // Levels: 3 -> repeat next day
+ {
+ setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval});
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, startE, ss->nextDayInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, long_easy) // Levels: 3 -> repeat in 2 days
+ {
+ setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval});
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, startE, ss->twoDaysInterval);
+ }
+
+
+// Long learning from previous day:
+// Is promoted directly to next day repeating
+
+TEST_F(SRModel_schedule_Test, long_prevDay_difficult)
+ {
+ setStudy({StudyRecord::LongLearning, 4, 2.5, ss->learningInterval}, -1);
+ schedule(3);
+ checkStudy(StudyRecord::Repeating, 2.36, 2.18772);
+ }
+
+TEST_F(SRModel_schedule_Test, long_prevDay_good)
+ {
+ setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}, -1);
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, startE, 2.3175);
+ }
+
+TEST_F(SRModel_schedule_Test, long_prevDay_easy)
+ {
+ setStudy({StudyRecord::LongLearning, 4, 2.5, ss->learningInterval}, -1);
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, 2.6, 2.4102);
+ }
+
+
+// First repeating day
+
+TEST_F(SRModel_schedule_Test, firstRep_unknown) // -> 2
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval});
+ schedule(1);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, firstRep_incor) // -> 2
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval});
+ schedule(2);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, firstRep_difficult)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval});
+ schedule(3);
+ checkStudy(StudyRecord::Repeating, 2.36, 2.18772);
+ }
+
+TEST_F(SRModel_schedule_Test, firstRep_good)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval});
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, startE, 2.3175);
+ }
+
+TEST_F(SRModel_schedule_Test, firstRep_easy)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval});
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, 2.6, 2.4102);
+ }
+
+
+// Normal repeating
+
+TEST_F(SRModel_schedule_Test, repeat_unknown)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, 5});
+ schedule(1);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, repeat_incor)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, 5});
+ schedule(2);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, repeat_difficult)
+ {
+ setStudy({StudyRecord::Repeating, 4, 2.5, 5});
+ schedule(3);
+ checkStudy(StudyRecord::Repeating, 2.36, 12.1540);
+ }
+
+TEST_F(SRModel_schedule_Test, repeat_good)
+ {
+ setStudy({StudyRecord::Repeating, 4, startE, 5});
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, startE, 12.875);
+ }
+
+TEST_F(SRModel_schedule_Test, repeat_easy)
+ {
+ setStudy({StudyRecord::Repeating, 4, 2.5, 5});
+ schedule(5);
+ checkStudy(StudyRecord::Repeating, 2.6, 13.39);
+ }
+
+
+// Mature repeating
+
+TEST_F(SRModel_schedule_Test, mature_difficult)
+ {
+ setStudy({StudyRecord::Repeating, 4, 3.1, 23});
+ schedule(3);
+ checkStudy(StudyRecord::Repeating, 2.96, 70.1224);
+ }
+
+TEST_F(SRModel_schedule_Test, mature_good)
+ {
+ setStudy({StudyRecord::Repeating, 4, 3.1, 23});
+ schedule(4);
+ checkStudy(StudyRecord::Repeating, 3.1, 73.439);
+ }
+
+
+// Re-learning unknown and incorrect cards
+
+TEST_F(SRModel_schedule_Test, unknown_incor)
+ {
+ setStudy({StudyRecord::ShortLearning, 1, startE, ss->unknownInterval});
+ schedule(2);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, incor_unknown)
+ {
+ setStudy({StudyRecord::ShortLearning, 2, startE, ss->incorrectInterval});
+ schedule(1);
+ checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, unknown_good)
+ {
+ setStudy({StudyRecord::ShortLearning, 1, startE, ss->unknownInterval});
+ schedule(4);
+ checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval);
+ }
+
+TEST_F(SRModel_schedule_Test, incor_good)
+ {
+ setStudy({StudyRecord::ShortLearning, 2, startE, ss->incorrectInterval});
+ schedule(4);
+ checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval);
+ }
+
+
+void SRModel_schedule_Test::setNewCard()
+{
+ pack.addStudyRecord(cardName, StudyRecord());
+}
+
+void SRModel_schedule_Test::setStudy(StudyRecord study, double daysDelta)
+{
+ study.date = timeFromDelta(daysDelta);
+ pack.addStudyRecord(cardName, study);
+}
+
+QDateTime SRModel_schedule_Test::timeFromDelta(double daysDelta)
+{
+ return TimeProvider::get().addSecs((int)(daysDelta * 24 * 60 * 60));
+}
+
+void SRModel_schedule_Test::schedule(int grade)
+{
+ this->grade = grade;
+ model.scheduleCard(grade);
+}
+
+void SRModel_schedule_Test::checkStudy(int expLevel, double expEasiness,
+ double expInterval)
+{
+ StudyRecord newStudy = pack.getStudyRecord(cardName);
+ ASSERT_EQ(grade, newStudy.grade);
+ ASSERT_EQ(expLevel, newStudy.level);
+ ASSERT_DOUBLE_EQ(expEasiness, newStudy.easiness);
+ ASSERT_NEAR(expInterval, newStudy.interval, 0.00000001);
+}
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h
new file mode 100644
index 0000000..6da4365
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h
@@ -0,0 +1,24 @@
+#include "SRModel_test.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../../src/study/StudySettings.h"
+#include "../../mocks/RandomGenerator_mock.h"
+
+class SRModel_schedule_Test: public SRModelTest
+{
+protected:
+ static const double startE;
+
+protected:
+ void SetUp();
+ void setNewCard();
+ void setStudy(StudyRecord study, double daysDelta = 0);
+ void schedule(int grade);
+ void checkStudy(int expLevel, double expEasiness, double expInterval);
+
+private:
+ static QDateTime timeFromDelta(double daysDelta);
+
+protected:
+ QString cardName;
+ int grade;
+};
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp
new file mode 100644
index 0000000..2fd0675
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp
@@ -0,0 +1,36 @@
+#include "SRModel_showGrades_test.h"
+
+static const StudySettings* ss = StudySettings::inst();
+
+void SRModel_showGrades_Test::SetUp()
+{
+ StudySettings::inst()->schedRandomness = StudySettings().schedRandomness;
+ randomGenerator->setDouble(0.3);
+ cardName = createCard();
+}
+
+TEST_F(SRModel_showGrades_Test, new)
+ {
+ setStudy({StudyRecord::New, 4, ss->initEasiness, 0});
+ QList<int> expGrades = {4, 5};
+ ASSERT_EQ(expGrades, model.getAvailableGrades());
+ }
+
+TEST_F(SRModel_showGrades_Test, learning)
+ {
+ setStudy({StudyRecord::ShortLearning, 4, ss->initEasiness, ss->unknownInterval});
+ QList<int> expGrades = {1, 2, 4, 5};
+ ASSERT_EQ(expGrades, model.getAvailableGrades());
+ }
+
+TEST_F(SRModel_showGrades_Test, repeating)
+ {
+ setStudy({StudyRecord::Repeating, 4, ss->initEasiness, 2});
+ QList<int> expGrades = {1, 2, 3, 4, 5};
+ ASSERT_EQ(expGrades, model.getAvailableGrades());
+ }
+
+void SRModel_showGrades_Test::setStudy(const StudyRecord& study)
+{
+ pack.addStudyRecord(cardName, study);
+}
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h
new file mode 100644
index 0000000..c45a760
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h
@@ -0,0 +1,14 @@
+#include "SRModel_test.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../../src/study/StudySettings.h"
+#include "../../mocks/RandomGenerator_mock.h"
+
+class SRModel_showGrades_Test: public SRModelTest
+{
+protected:
+ void SetUp();
+ void setStudy(const StudyRecord& study);
+
+protected:
+ QString cardName;
+};
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_test.cpp
new file mode 100644
index 0000000..420dfa7
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_test.cpp
@@ -0,0 +1,67 @@
+#include "SRModel_test.h"
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/DicRecord.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../../src/study/StudySettings.h"
+#include "../../mocks/RandomGenerator_mock.h"
+
+SRModelTest::SRModelTest():
+ recordId(0),
+ pack(&dict),
+ randomGenerator(new MockRandomGenerator),
+ model(&pack, randomGenerator)
+{
+ field1 = new Field("English", "Normal");
+ field2 = new Field("Russian", "Normal");
+ pack.addField(field1);
+ pack.addField(field2);
+ dict.addCardPack(&pack);
+}
+
+void SRModelTest::SetUp()
+{
+ QCoreApplication::setOrganizationName("freshmemory");
+ QCoreApplication::setApplicationName("freshmemory");
+ QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, "");
+ StudySettings::inst()->load();
+}
+
+void SRModelTest::TearDown()
+{
+ delete field1;
+ delete field2;
+}
+
+TEST_F(SRModelTest, EmptyStats)
+ {
+ ASSERT_EQ(0, model.estimatedNewReviewedCardsToday());
+ ASSERT_EQ(0, model.countTodayRemainingCards());
+ }
+
+TEST_F(SRModelTest, FirstStudy)
+ {
+ QString cardName = createCard();
+ StudyRecord newStudy = pack.getStudyRecord(cardName);
+
+ ASSERT_EQ(StudyRecord::Unknown, newStudy.grade);
+ ASSERT_DOUBLE_EQ(StudySettings::inst()->initEasiness, newStudy.easiness);
+ ASSERT_DOUBLE_EQ(0, newStudy.interval);
+ }
+
+QString SRModelTest::createCard()
+{
+ addRecord();
+ pack.generateQuestions();
+ return model.getCurCard()->getQuestion();
+}
+
+QString SRModelTest::addRecord()
+{
+ DicRecord* record = new DicRecord;
+ QString recordName = QString::number(recordId);
+ record->setField("English", recordName);
+ record->setField("Russian", "Odin");
+ recordId++;
+ dict.addRecord(record);
+ return recordName;
+}
diff --git a/tests/unit/SpacedRepetitionModel/SRModel_test.h b/tests/unit/SpacedRepetitionModel/SRModel_test.h
new file mode 100644
index 0000000..9e5d669
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/SRModel_test.h
@@ -0,0 +1,46 @@
+#ifndef SPACED_REPETITION_MODEL_TEST_H
+#define SPACED_REPETITION_MODEL_TEST_H
+
+#include <gtest/gtest.h>
+#include <iostream>
+#include <QtCore>
+
+#include "../../../src/study/SpacedRepetitionModel.h"
+#include "../../../src/dictionary/CardPack.h"
+#include "../../mocks/Dictionary_mock.h"
+
+using std::ostream;
+
+class MockRandomGenerator;
+class Field;
+
+class TestSpacedRepetitionModel: public SpacedRepetitionModel
+{
+public:
+ TestSpacedRepetitionModel(CardPack* pack, IRandomGenerator* random):
+ SpacedRepetitionModel(pack, random) {}
+ void testPickCard() { pickNextCardAndNotify(); }
+};
+
+class SRModelTest: public testing::Test
+{
+public:
+ SRModelTest();
+
+protected:
+ void SetUp();
+ void TearDown();
+ QString createCard();
+ QString addRecord();
+
+protected:
+ MockDictionary dict;
+ int recordId;
+ Field* field1;
+ Field* field2;
+ CardPack pack;
+ MockRandomGenerator* randomGenerator;
+ TestSpacedRepetitionModel model;
+};
+
+#endif
diff --git a/tests/unit/SpacedRepetitionModel/srModel.pri b/tests/unit/SpacedRepetitionModel/srModel.pri
new file mode 100644
index 0000000..19daca1
--- /dev/null
+++ b/tests/unit/SpacedRepetitionModel/srModel.pri
@@ -0,0 +1,16 @@
+HEADERS += \
+ $$PWD/SRModel_test.h \
+ $$PWD/SRModel_schedule_test.h \
+ $$PWD/SRModel_pickCard_test.h \
+ $$PWD/SRModel_showGrades_test.h \
+ $$SRC/study/SpacedRepetitionModel.h \
+ $$SRC/study/IStudyModel.h \
+ $$TESTS/mocks/RandomGenerator_mock.h
+
+SOURCES += \
+ $$PWD/SRModel_test.cpp \
+ $$PWD/SRModel_schedule_test.cpp \
+ $$PWD/SRModel_pickCard_test.cpp \
+ $$PWD/SRModel_showGrades_test.cpp \
+ $$SRC/study/SpacedRepetitionModel.cpp \
+ $$SRC/study/IStudyModel.cpp