From d24f813f3f2a05c112e803e4256b53535895fc98 Mon Sep 17 00:00:00 2001 From: Jedidiah Barber Date: Wed, 14 Jul 2021 11:49:10 +1200 Subject: Initial mirror commit --- .../SRModel_pickCard_test.cpp | 420 +++++++++++++++++++++ .../SpacedRepetitionModel/SRModel_pickCard_test.h | 27 ++ .../SRModel_schedule_test.cpp | 304 +++++++++++++++ .../SpacedRepetitionModel/SRModel_schedule_test.h | 24 ++ .../SRModel_showGrades_test.cpp | 36 ++ .../SRModel_showGrades_test.h | 14 + tests/unit/SpacedRepetitionModel/SRModel_test.cpp | 67 ++++ tests/unit/SpacedRepetitionModel/SRModel_test.h | 46 +++ tests/unit/SpacedRepetitionModel/srModel.pri | 16 + 9 files changed, 954 insertions(+) create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_test.h create mode 100644 tests/unit/SpacedRepetitionModel/srModel.pri (limited to 'tests/unit/SpacedRepetitionModel') 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& expCards) +{ + vector 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 + +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& 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 expGrades = {4, 5}; + ASSERT_EQ(expGrades, model.getAvailableGrades()); + } + +TEST_F(SRModel_showGrades_Test, learning) + { + setStudy({StudyRecord::ShortLearning, 4, ss->initEasiness, ss->unknownInterval}); + QList expGrades = {1, 2, 4, 5}; + ASSERT_EQ(expGrades, model.getAvailableGrades()); + } + +TEST_F(SRModel_showGrades_Test, repeating) + { + setStudy({StudyRecord::Repeating, 4, ss->initEasiness, 2}); + QList 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 +#include +#include + +#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 -- cgit