summaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/Card/Card_GenerateAnswers_test.cpp66
-rw-r--r--tests/unit/Card/Card_GenerateAnswers_test.h34
-rw-r--r--tests/unit/Card/Card_test.cpp36
-rw-r--r--tests/unit/Card/Card_test.h18
-rw-r--r--tests/unit/Card/Card_test_QuestionAnswer.cpp30
-rw-r--r--tests/unit/Card/card.pri10
-rw-r--r--tests/unit/CardPack/CardPack_GenerateCards_test.cpp43
-rw-r--r--tests/unit/CardPack/CardPack_GenerateCards_test.h26
-rw-r--r--tests/unit/CardPack/CardPack_test.cpp80
-rw-r--r--tests/unit/CardPack/CardPack_test.h31
-rw-r--r--tests/unit/CardPack/cPack.pri7
-rw-r--r--tests/unit/CardSideView/CardSideView_test.cpp49
-rw-r--r--tests/unit/CardSideView/CardSideView_test.h21
-rw-r--r--tests/unit/CardSideView/csView.pri8
-rw-r--r--tests/unit/RandomGenerator/RandomGenerator_test.cpp79
-rw-r--r--tests/unit/RandomGenerator/rndGen.pri6
-rw-r--r--tests/unit/Settings/FieldStyleFactory_test.cpp88
-rw-r--r--tests/unit/Settings/FieldStyleFactory_test.h29
-rw-r--r--tests/unit/Settings/StudySettings_test.cpp42
-rw-r--r--tests/unit/Settings/StudySettings_test.h22
-rw-r--r--tests/unit/Settings/TestSettings.cpp16
-rw-r--r--tests/unit/Settings/TestSettings.h11
-rw-r--r--tests/unit/Settings/set.pri10
-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
-rw-r--r--tests/unit/cards.pri24
-rw-r--r--tests/unit/common.pri3
-rw-r--r--tests/unit/main.cpp9
-rw-r--r--tests/unit/random.pri1
-rw-r--r--tests/unit/studySets.pri9
-rw-r--r--tests/unit/unit_tests.pro29
38 files changed, 1791 insertions, 0 deletions
diff --git a/tests/unit/Card/Card_GenerateAnswers_test.cpp b/tests/unit/Card/Card_GenerateAnswers_test.cpp
new file mode 100644
index 0000000..f381daf
--- /dev/null
+++ b/tests/unit/Card/Card_GenerateAnswers_test.cpp
@@ -0,0 +1,66 @@
+#include "Card_GenerateAnswers_test.h"
+
+#include <initializer_list>
+
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/IDictionary.h"
+#include "../../../src/dictionary/Field.h"
+#include "../../../src/dictionary/DicRecord.h"
+#include "../../../src/dictionary/Card.h"
+
+INSTANTIATE_TEST_CASE_P(, GenerateAnswersTest,
+ testing::ValuesIn(RecordsParam::createParams()) );
+
+void GenerateAnswersTest::TearDown()
+{
+ for(Field* field: fields)
+ delete field;
+}
+
+TEST_P(GenerateAnswersTest, generateAnswers)
+ {
+ auto param = GetParam();
+ dict.addRecords(param.records);
+ for(int i: param.packFields)
+ addFieldToPack(i);
+
+ unsigned i = 0;
+ for(vector<string> expectedCardFields: param.answers)
+ {
+ SCOPED_TRACE(i);
+ Card card(&pack, expectedCardFields[0].c_str());
+ for(unsigned j = 1; j < expectedCardFields.size(); j++)
+ {
+ SCOPED_TRACE(j);
+ ASSERT_EQ_QSTR(QString(expectedCardFields[j].c_str()),
+ card.getAnswers()[j - 1]);
+ }
+ i++;
+ }
+}
+
+TEST_F(GenerateAnswersTest, dropAnswers)
+ {
+ const vector<string> fieldValues {"table", "стол"};
+
+ DicRecord* record = new DicRecord;
+ dict.addRecord(record);
+ for(unsigned i = 0; i < fieldValues.size(); i++)
+ {
+ addFieldToPack(i);
+ record->setField(RecordsParam::fieldNames[i].c_str(), fieldValues[i].c_str());
+ }
+ Card card(&pack, fieldValues[0].c_str());
+
+ ASSERT_EQ(QString(fieldValues[1].c_str()), card.getAnswers().first());
+
+ record->setField("Russian", "кровать");
+ ASSERT_EQ(QString("кровать"), card.getAnswers().first());
+}
+
+void GenerateAnswersTest::addFieldToPack(unsigned fieldId)
+{
+ Field* field = new Field(RecordsParam::fieldNames[fieldId].c_str(), "Normal");
+ fields.push_back(field);
+ pack.addField(field);
+}
diff --git a/tests/unit/Card/Card_GenerateAnswers_test.h b/tests/unit/Card/Card_GenerateAnswers_test.h
new file mode 100644
index 0000000..1b69cba
--- /dev/null
+++ b/tests/unit/Card/Card_GenerateAnswers_test.h
@@ -0,0 +1,34 @@
+#include <vector>
+#include <string>
+#include <gtest/gtest.h>
+
+#include "../../../src/dictionary/CardPack.h"
+#include "../../mocks/Dictionary_mock.h"
+#include "../../common/RecordsParam.h"
+
+class Field;
+
+using std::vector;
+using std::string;
+
+class GenerateAnswersTest: public testing::TestWithParam<RecordsParam>
+{
+public:
+ GenerateAnswersTest():
+ pack(&dict) {}
+
+protected:
+ void TearDown();
+ void addFieldToPack(unsigned fieldId);
+
+public:
+ static const vector<string> fieldNames;
+
+protected:
+ vector<Field*> fields;
+
+protected:
+ MockDictionary dict;
+ CardPack pack;
+};
+
diff --git a/tests/unit/Card/Card_test.cpp b/tests/unit/Card/Card_test.cpp
new file mode 100644
index 0000000..bfae243
--- /dev/null
+++ b/tests/unit/Card/Card_test.cpp
@@ -0,0 +1,36 @@
+#include <gtest/gtest.h>
+#include <QtCore>
+
+#include "Card_test.h"
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../../src/dictionary/ICardPack.h"
+#include "../../mocks/CardPack_mock.h"
+
+void CardTest::SetUp()
+ {
+ defaultPack = new CardPackMock;
+ }
+
+void CardTest::TearDown()
+ {
+ delete defaultPack;
+ }
+
+TEST_F(CardTest, Create)
+ {
+ Card card(defaultPack);
+
+ ASSERT_EQ(defaultPack, card.getCardPack());
+ ASSERT_TRUE(card.getQuestion().isEmpty());
+ ASSERT_TRUE(card.getAnswers().isEmpty());
+ }
+
+TEST_F(CardTest, Create_NullPack)
+ {
+ Card card(NULL);
+
+ ASSERT_EQ(NULL, card.getCardPack());
+ ASSERT_TRUE(card.getQuestion().isEmpty());
+ ASSERT_TRUE(card.getAnswers().isEmpty());
+ }
diff --git a/tests/unit/Card/Card_test.h b/tests/unit/Card/Card_test.h
new file mode 100644
index 0000000..e9a0891
--- /dev/null
+++ b/tests/unit/Card/Card_test.h
@@ -0,0 +1,18 @@
+#ifndef CARD_TEST_H
+#define CARD_TEST_H
+
+#include <gtest/gtest.h>
+
+class ICardPack;
+
+class CardTest: public testing::Test
+{
+public:
+ void SetUp();
+ void TearDown();
+
+protected:
+ ICardPack* defaultPack;
+};
+
+#endif
diff --git a/tests/unit/Card/Card_test_QuestionAnswer.cpp b/tests/unit/Card/Card_test_QuestionAnswer.cpp
new file mode 100644
index 0000000..6100f75
--- /dev/null
+++ b/tests/unit/Card/Card_test_QuestionAnswer.cpp
@@ -0,0 +1,30 @@
+#include <gtest/gtest.h>
+
+#include "Card_test.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../mocks/CardPack_mock.h"
+
+using testing::Combine;
+using testing::Values;
+using testing::WithParamInterface;
+
+class QuestionCardTest: public CardTest,
+ public WithParamInterface<const char*>
+{};
+
+INSTANTIATE_TEST_CASE_P(, QuestionCardTest,
+ Values("", "Question"));
+
+TEST_P(QuestionCardTest, getName)
+ {
+ QString question = GetParam();
+ Card card(defaultPack, question);
+ ASSERT_EQ(question, card.getName());
+ }
+
+TEST_P(QuestionCardTest, getQuestion)
+ {
+ QString question = GetParam();
+ Card card(defaultPack, question);
+ ASSERT_EQ(question, card.getQuestion());
+ }
diff --git a/tests/unit/Card/card.pri b/tests/unit/Card/card.pri
new file mode 100644
index 0000000..2eae07d
--- /dev/null
+++ b/tests/unit/Card/card.pri
@@ -0,0 +1,10 @@
+HEADERS += \
+ $$PWD/Card_test.h \
+ $$PWD/Card_GenerateAnswers_test.h \
+ $$TESTS/mocks/CardPack_mock.h
+
+SOURCES += \
+ $$PWD/Card_test.cpp \
+ $$PWD/Card_test_QuestionAnswer.cpp \
+ $$PWD/Card_GenerateAnswers_test.cpp \
+ $$TESTS/mocks/CardPack_mock.cpp
diff --git a/tests/unit/CardPack/CardPack_GenerateCards_test.cpp b/tests/unit/CardPack/CardPack_GenerateCards_test.cpp
new file mode 100644
index 0000000..c7e7fcb
--- /dev/null
+++ b/tests/unit/CardPack/CardPack_GenerateCards_test.cpp
@@ -0,0 +1,43 @@
+#include "CardPack_GenerateCards_test.h"
+
+#include <initializer_list>
+
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/IDictionary.h"
+#include "../../../src/dictionary/Field.h"
+#include "../../../src/dictionary/DicRecord.h"
+
+vector<Field*> GenerateCardsTest::fields;
+
+INSTANTIATE_TEST_CASE_P(, GenerateCardsTest,
+ testing::ValuesIn(RecordsParam::createParams()) );
+
+vector<Field*> GenerateCardsTest::getFields()
+{
+ static vector<string> names {"English", "Russian"};
+ if(fields.empty())
+ for(string name: names)
+ fields.push_back(new Field(name.c_str(), "Normal"));
+ return fields;
+}
+
+void GenerateCardsTest::TearDownTestCase()
+{
+ for(Field* field: fields)
+ delete field;
+}
+
+TEST_P(GenerateCardsTest, generateQuestions)
+ {
+ auto param = GetParam();
+ dict.addRecords(param.records);
+ for(int fieldId: param.packFields)
+ pack.addField(getFields()[ fieldId ]);
+ pack.generateQuestions();
+
+ QStringList questions;
+ for(string question: param.questions)
+ questions << question.c_str();
+
+ ASSERT_EQ(questions, pack.getCardQuestions());
+}
diff --git a/tests/unit/CardPack/CardPack_GenerateCards_test.h b/tests/unit/CardPack/CardPack_GenerateCards_test.h
new file mode 100644
index 0000000..299ee66
--- /dev/null
+++ b/tests/unit/CardPack/CardPack_GenerateCards_test.h
@@ -0,0 +1,26 @@
+#include <iostream>
+#include <vector>
+#include <string>
+#include <gtest/gtest.h>
+#include <QtCore>
+
+#include "CardPack_test.h"
+#include "../../common/RecordsParam.h"
+
+class DicRecord;
+
+using std::vector;
+using std::string;
+using std::ostream;
+
+class GenerateCardsTest: public CardPackTest,
+ public testing::WithParamInterface<RecordsParam>
+{
+public:
+ static void TearDownTestCase();
+ static vector<Field*> getFields();
+
+private:
+ static vector<Field*> fields;
+
+};
diff --git a/tests/unit/CardPack/CardPack_test.cpp b/tests/unit/CardPack/CardPack_test.cpp
new file mode 100644
index 0000000..47b2387
--- /dev/null
+++ b/tests/unit/CardPack/CardPack_test.cpp
@@ -0,0 +1,80 @@
+#include "CardPack_test.h"
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/IDictionary.h"
+#include "../../../src/dictionary/Field.h"
+#include "../../../src/dictionary/DicRecord.h"
+
+ostream& operator<<(ostream& os, const Field* field)
+{
+ return os << field->name().toStdString();
+}
+
+CardPackTest::CardPackTest():
+ pack(&dict),
+ qstField("Question", "Normal"),
+ ansField1("Answer1", "Normal"),
+ ansField2("Answer2", "Normal")
+{}
+
+TEST_F(CardPackTest, empty_QuestionAnswers)
+ {
+ ASSERT_EQ(NULL, pack.getQuestionField());
+ ASSERT_EQ(QList<const Field*>(), pack.getAnswerFields());
+ }
+
+TEST_F(CardPackTest, empty_Id)
+ {
+ ASSERT_EQ("(empty pack)", pack.id());
+ }
+
+TEST_F(CardPackTest, empty_cardQuestions)
+ {
+ ASSERT_EQ(QStringList(), pack.getCardQuestions());
+ }
+
+TEST_F(CardPackTest, setQstField)
+ {
+ pack.setQstField(&qstField);
+ ASSERT_EQ(&qstField, pack.getQuestionField());
+ }
+
+TEST_F(CardPackTest, setAnsField_1)
+ {
+ ansFields << &ansField1;
+
+ pack.setAnsFields(ansFields);
+ ASSERT_EQ(ansFields, pack.getAnswerFields());
+ }
+
+TEST_F(CardPackTest, setAnsFields_2)
+ {
+ ansFields << &ansField1 << &ansField2;
+
+ pack.setAnsFields(ansFields);
+ ASSERT_EQ(ansFields, pack.getAnswerFields());
+ }
+
+TEST_F(CardPackTest, addFields)
+ {
+ QList<const Field*> allFields;
+ allFields << &qstField << &ansField1 << &ansField2;
+ ansFields << &ansField1 << &ansField2;
+
+ pack.addField(&qstField);
+ pack.addField(&ansField1);
+ pack.addField(&ansField2);
+
+ ASSERT_EQ(allFields, pack.getFields());
+ ASSERT_EQ(&qstField, pack.getQuestionField());
+ ASSERT_EQ(ansFields, pack.getAnswerFields());
+ }
+
+TEST_F(CardPackTest, id)
+ {
+ ansFields << &ansField1 << &ansField2;
+
+ pack.setQstField(&qstField);
+ pack.setAnsFields(ansFields);
+
+ ASSERT_EQ("Question - Answer1, Answer2", pack.id());
+ }
diff --git a/tests/unit/CardPack/CardPack_test.h b/tests/unit/CardPack/CardPack_test.h
new file mode 100644
index 0000000..e25c75d
--- /dev/null
+++ b/tests/unit/CardPack/CardPack_test.h
@@ -0,0 +1,31 @@
+#ifndef CARDPACK_TEST_H
+#define CARDPACK_TEST_H
+
+#include <gtest/gtest.h>
+#include <iostream>
+#include <QtCore>
+
+#include "../../../src/dictionary/CardPack.h"
+#include "../../mocks/Dictionary_mock.h"
+
+using std::ostream;
+
+class Field;
+
+class CardPackTest: public testing::Test
+{
+public:
+ CardPackTest();
+
+protected:
+ MockDictionary dict;
+ CardPack pack;
+ Field qstField;
+ Field ansField1;
+ Field ansField2;
+ QList<const Field*> ansFields;
+};
+
+ostream& operator<<(ostream& os, const Field* field);
+
+#endif
diff --git a/tests/unit/CardPack/cPack.pri b/tests/unit/CardPack/cPack.pri
new file mode 100644
index 0000000..e3befe3
--- /dev/null
+++ b/tests/unit/CardPack/cPack.pri
@@ -0,0 +1,7 @@
+HEADERS += \
+ $$PWD/CardPack_test.h \
+ $$PWD/CardPack_GenerateCards_test.h
+
+SOURCES += \
+ $$PWD/CardPack_test.cpp \
+ $$PWD/CardPack_GenerateCards_test.cpp
diff --git a/tests/unit/CardSideView/CardSideView_test.cpp b/tests/unit/CardSideView/CardSideView_test.cpp
new file mode 100644
index 0000000..9b08dc0
--- /dev/null
+++ b/tests/unit/CardSideView/CardSideView_test.cpp
@@ -0,0 +1,49 @@
+#include <gtest/gtest.h>
+#include <QtCore>
+#include <QtWidgets>
+
+#include "CardSideView_test.h"
+#include "../../common/printQtTypes.h"
+#include "../../../src/dictionary/Card.h"
+#include "../../../src/study/CardSideView.h"
+
+void CardSideViewTest::SetUp()
+ {
+ cardPack.addField(new Field("English", "Normal"));
+ cardPack.addField(new Field("Example", "Example"));
+ cardPack.addField(new Field("Russian", "Normal"));
+ }
+
+TEST_F(CardSideViewTest, getFormattedQuestion)
+ {
+ CardSideView view;
+ view.setPack(&cardPack);
+ view.setQstAnsr("First", QStringList());
+ ASSERT_EQ("<span style=\"font-family:'Times New Roman'; "\
+ "font-size:18pt; font-weight:bold\">First</span>",
+ view.getFormattedText());
+ }
+
+TEST_F(CardSideViewTest, getFormattedAnswer)
+ {
+ CardSideView view(CardSideView::AnsMode);
+ view.setPack(&cardPack);
+ QStringList answers = QStringList() << "First example" << "Pervyj";
+ view.setQstAnsr("First", answers);
+ ASSERT_EQ("<span style=\"font-family:'Times New Roman'; "\
+ "font-size:14pt\"><span style=\"; color:#0000ff\">First</span> example</span><br/><br/>"\
+ "<span style=\"font-family:'Times New Roman'; font-size:18pt; "\
+ "font-weight:bold\">Pervyj</span>",
+ view.getFormattedText());
+ }
+
+TEST_F(CardSideViewTest, getFormattedAnswer_1missing)
+ {
+ CardSideView view(CardSideView::AnsMode);
+ view.setPack(&cardPack);
+ QStringList answers = QStringList() << "" << "Pervyj";
+ view.setQstAnsr("First", answers);
+ ASSERT_EQ("<br/><br/><span style=\"font-family:'Times New Roman'; font-size:18pt; "\
+ "font-weight:bold\">Pervyj</span>",
+ view.getFormattedText());
+ }
diff --git a/tests/unit/CardSideView/CardSideView_test.h b/tests/unit/CardSideView/CardSideView_test.h
new file mode 100644
index 0000000..491ebe4
--- /dev/null
+++ b/tests/unit/CardSideView/CardSideView_test.h
@@ -0,0 +1,21 @@
+#ifndef CARDSIDEVIEW_TEST_H
+#define CARDSIDEVIEW_TEST_H
+
+#include <gtest/gtest.h>
+
+#include "../../mocks/Dictionary_mock.h"
+#include "../../../src/dictionary/CardPack.h"
+
+class CardSideViewTest: public testing::Test
+{
+public:
+ CardSideViewTest():
+ cardPack(&dict) {}
+ void SetUp();
+
+protected:
+ MockDictionary dict;
+ CardPack cardPack;
+};
+
+#endif
diff --git a/tests/unit/CardSideView/csView.pri b/tests/unit/CardSideView/csView.pri
new file mode 100644
index 0000000..6eeae5f
--- /dev/null
+++ b/tests/unit/CardSideView/csView.pri
@@ -0,0 +1,8 @@
+HEADERS += \
+ $$PWD/CardSideView_test.h \
+ $$SRC/study/CardSideView.h
+
+SOURCES += \
+ $$PWD/CardSideView_test.cpp \
+ $$SRC/study/CardSideView.cpp
+ \ No newline at end of file
diff --git a/tests/unit/RandomGenerator/RandomGenerator_test.cpp b/tests/unit/RandomGenerator/RandomGenerator_test.cpp
new file mode 100644
index 0000000..d334c9b
--- /dev/null
+++ b/tests/unit/RandomGenerator/RandomGenerator_test.cpp
@@ -0,0 +1,79 @@
+#include <gtest/gtest.h>
+#include <cmath>
+#include "../../../src/utils/RandomGenerator.h"
+
+class RandomGeneratorTest: public testing::Test
+{
+protected:
+ void generateRandoms();
+ void checkStats();
+
+private:
+ void checkMinMeanMax(double min, double mean, double max);
+ void minMeanMax(double& min, double& mean, double& max);
+ double stddev(double mean);
+
+protected:
+ static const int Num = 10000;
+
+protected:
+ RandomGenerator random;
+ double x[Num];
+};
+
+TEST_F(RandomGeneratorTest, Stats)
+{
+ generateRandoms();
+ checkStats();
+}
+
+TEST_F(RandomGeneratorTest, getArray)
+{
+ QByteArray ba = random.getArray();
+}
+
+void RandomGeneratorTest::generateRandoms()
+{
+ for(int i = 0; i < Num; i++)
+ x[i] = random.getInRange_11();
+}
+
+void RandomGeneratorTest::checkStats()
+{
+ double min;
+ double mean;
+ double max;
+ minMeanMax(min, mean, max);
+
+ checkMinMeanMax(min, mean, max);
+ ASSERT_GT(stddev(mean), 0.55);
+}
+
+void RandomGeneratorTest::checkMinMeanMax(double min, double mean, double max)
+{
+ ASSERT_NEAR(-1, min, 0.01);
+ ASSERT_NEAR(0, mean, 0.2);
+ ASSERT_NEAR(1, max, 0.01);
+}
+
+void RandomGeneratorTest::minMeanMax(double& min, double& mean, double& max)
+{
+ double sum = 0;
+ min = 1000;
+ max = -1000;
+ for(int i = 0; i < Num; i++)
+ {
+ min = fmin(x[i], min);
+ max = fmax(x[i], max);
+ sum += x[i];
+ }
+ mean = sum / Num;
+}
+
+double RandomGeneratorTest::stddev(double mean)
+{
+ double varSum = 0;
+ for(int i = 0; i < Num; i++)
+ varSum += pow((mean - x[i]), 2);
+ return sqrt(varSum / Num);
+}
diff --git a/tests/unit/RandomGenerator/rndGen.pri b/tests/unit/RandomGenerator/rndGen.pri
new file mode 100644
index 0000000..dbc9300
--- /dev/null
+++ b/tests/unit/RandomGenerator/rndGen.pri
@@ -0,0 +1,6 @@
+HEADERS += \
+ $$SRC/utils/RandomGenerator.h
+
+SOURCES += \
+ $$PWD/RandomGenerator_test.cpp \
+ $$SRC/utils/RandomGenerator.cpp
diff --git a/tests/unit/Settings/FieldStyleFactory_test.cpp b/tests/unit/Settings/FieldStyleFactory_test.cpp
new file mode 100644
index 0000000..be53e7c
--- /dev/null
+++ b/tests/unit/Settings/FieldStyleFactory_test.cpp
@@ -0,0 +1,88 @@
+#include "FieldStyleFactory_test.h"
+#include "../../common/printQtTypes.h"
+#include "TestSettings.h"
+
+void FieldStyleFactoryTest::SetUp()
+{
+ TestSettings::init();
+}
+
+FieldStyleFactory FieldStyleFactoryTest::getDefaults()
+{
+ FieldStyleFactory factory;
+ factory.cardBgColor.setNamedColor("#ffffff");
+ factory.setStyle(FieldStyleFactory::DefaultStyle,
+ {"Times New Roman", 18, true, false, "black" , "", ""});
+ factory.setStyle("Example",
+ {"Times New Roman", 14, false, false, "black", "", "", true, "blue"});
+ factory.setStyle("Transcription",
+ {"Arial", 18, false, false, "black", "/", "/"});
+ factory.setStyle("Big", {"Arial", 26, true, false, "black"});
+ factory.setStyle("Color1", {"Times New Roman", 18, true, false, "red"});
+ factory.setStyle("Color2", {"Times New Roman", 18, true, false, "blue"});
+ return factory;
+}
+
+FieldStyleFactory FieldStyleFactoryTest::getUserSettings()
+{
+ FieldStyleFactory factory;
+ factory.cardBgColor.setNamedColor("#ffff00");
+ factory.setStyle(FieldStyleFactory::DefaultStyle,
+ {"Georgia", 19, false, true, "#550000", "", ""});
+ factory.setStyle("Example",
+ {"Times New Roman", 15, true, false, "black", "", ""});
+ factory.setStyle("Transcription",
+ {"Verdana", 18, true, false, "black", "[", "]"});
+ factory.setStyle("Big",
+ {"Arial", 27, false, false, "black", "", "", true, "blue"});
+ factory.setStyle("Color1", {"Times New Roman", 18, true, false, "green"});
+ factory.setStyle("Color2", {"Times New Roman", 18, true, false, "#00FFFF"});
+ return factory;
+}
+
+TEST_F(FieldStyleFactoryTest, DefaultValues)
+{
+ TestFieldStyleFactory factory;
+ SCOPED_TRACE("Default values");
+ factory.check(getDefaults());
+}
+
+TEST_F(FieldStyleFactoryTest, UserValues)
+{
+ FieldStyleFactory::inst()->load();
+ SCOPED_TRACE("User values");
+ static_cast<TestFieldStyleFactory*>(FieldStyleFactory::inst())->
+ check(getUserSettings());
+}
+
+void TestFieldStyleFactory::check(const FieldStyleFactory& expFactory)
+{
+ ASSERT_EQ(expFactory.cardBgColor.name(), cardBgColor.name());
+ checkStyles(expFactory);
+}
+
+void TestFieldStyleFactory::checkStyles(const FieldStyleFactory& expFactory)
+{
+ ASSERT_EQ(expFactory.getStyleNames(), getStyleNames());
+ foreach(QString styleName, getStyleNames())
+ {
+ SCOPED_TRACE(styleName.toStdString());
+ checkStyle(expFactory.getStyle(styleName), styleName);
+ }
+}
+
+void TestFieldStyleFactory::checkStyle(const FieldStyle& expStyle,
+ const QString& actualStyleName)
+{
+ FieldStyle actualStyle = getStyle(actualStyleName);
+ ASSERT_EQ(expStyle.font.family(), actualStyle.font.family());
+ ASSERT_EQ(expStyle.font.pointSize(), actualStyle.font.pointSize());
+ ASSERT_EQ(expStyle.font.bold(), actualStyle.font.bold());
+ ASSERT_EQ(expStyle.font.italic(), actualStyle.font.italic());
+ ASSERT_EQ(expStyle.color.name(), actualStyle.color.name());
+ ASSERT_EQ(expStyle.prefix, actualStyle.prefix);
+ ASSERT_EQ(expStyle.suffix, actualStyle.suffix);
+ ASSERT_EQ(expStyle.hasKeyword, actualStyle.hasKeyword);
+ if(expStyle.hasKeyword)
+ ASSERT_EQ(expStyle.keywordColor.name(), actualStyle.keywordColor.name());
+}
diff --git a/tests/unit/Settings/FieldStyleFactory_test.h b/tests/unit/Settings/FieldStyleFactory_test.h
new file mode 100644
index 0000000..9b7d410
--- /dev/null
+++ b/tests/unit/Settings/FieldStyleFactory_test.h
@@ -0,0 +1,29 @@
+#ifndef FIELD_STYLE_FACTORY_TEST_H
+#define FIELD_STYLE_FACTORY_TEST_H
+
+#include <gtest/gtest.h>
+#include <QtCore>
+
+#include "../../../src/field-styles/FieldStyleFactory.h"
+
+class TestFieldStyleFactory: public FieldStyleFactory
+{
+public:
+ void check(const FieldStyleFactory& expFactory);
+
+private:
+ void checkStyles(const FieldStyleFactory& expFactory);
+ void checkStyle(const FieldStyle& expStyle, const QString& actualStyleName);
+};
+
+class FieldStyleFactoryTest: public testing::Test
+{
+public:
+ static FieldStyleFactory getDefaults();
+ static FieldStyleFactory getUserSettings();
+
+public:
+ void SetUp();
+};
+
+#endif
diff --git a/tests/unit/Settings/StudySettings_test.cpp b/tests/unit/Settings/StudySettings_test.cpp
new file mode 100644
index 0000000..d66b8a1
--- /dev/null
+++ b/tests/unit/Settings/StudySettings_test.cpp
@@ -0,0 +1,42 @@
+#include "StudySettings_test.h"
+#include "../../common/printQtTypes.h"
+#include "TestSettings.h"
+
+void StudySettingsTest::SetUp()
+{
+ TestSettings::init();
+}
+
+StudySettings StudySettingsTest::getDefaults()
+{
+ StudySettings defaults;
+ defaults.dayShift = 3;
+ return defaults;
+}
+
+StudySettings StudySettingsTest::getUserSettings()
+{
+
+ StudySettings user;
+ user.dayShift = 4;
+ return user;
+}
+
+TEST_F(StudySettingsTest, DefaultValues)
+{
+ StudySettings settings;
+ SCOPED_TRACE("Default values");
+ check(getDefaults(), settings);
+}
+
+TEST_F(StudySettingsTest, UserValues)
+{
+ StudySettings::inst()->load();
+ SCOPED_TRACE("User values");
+ check(getUserSettings(), *StudySettings::inst());
+}
+
+void StudySettingsTest::check(const StudySettings& expected, const StudySettings& actual)
+{
+ ASSERT_EQ(expected.dayShift, actual.dayShift);
+}
diff --git a/tests/unit/Settings/StudySettings_test.h b/tests/unit/Settings/StudySettings_test.h
new file mode 100644
index 0000000..aa45ead
--- /dev/null
+++ b/tests/unit/Settings/StudySettings_test.h
@@ -0,0 +1,22 @@
+#ifndef STUDY_SETTINGS_TEST_H
+#define STUDY_SETTINGS_TEST_H
+
+#include <gtest/gtest.h>
+#include <QtCore>
+
+#include "../../../src/study/StudySettings.h"
+
+class StudySettingsTest: public testing::Test
+{
+public:
+ static StudySettings getDefaults();
+ static StudySettings getUserSettings();
+
+protected:
+ void check(const StudySettings& expected, const StudySettings& actual);
+
+public:
+ void SetUp();
+};
+
+#endif
diff --git a/tests/unit/Settings/TestSettings.cpp b/tests/unit/Settings/TestSettings.cpp
new file mode 100644
index 0000000..1f3fbb4
--- /dev/null
+++ b/tests/unit/Settings/TestSettings.cpp
@@ -0,0 +1,16 @@
+#include <QtCore>
+#include <gtest/gtest.h>
+
+#include "TestSettings.h"
+
+void TestSettings::init()
+{
+ QSettings::setDefaultFormat(QSettings::IniFormat);
+ QCoreApplication::setOrganizationName("freshmemory");
+ QCoreApplication::setApplicationName("freshmemory");
+ QString appDir = qApp->applicationDirPath();
+ QString userSettingsDir = appDir + "/../common";
+ QString systemSettingsDir = appDir + "/../../config";
+ QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, userSettingsDir);
+ QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, systemSettingsDir);
+}
diff --git a/tests/unit/Settings/TestSettings.h b/tests/unit/Settings/TestSettings.h
new file mode 100644
index 0000000..60b8be8
--- /dev/null
+++ b/tests/unit/Settings/TestSettings.h
@@ -0,0 +1,11 @@
+#ifndef TEST_SETTINGS_H
+#define TEST_SETTINGS_H
+
+class TestSettings
+{
+public:
+ static void init();
+
+};
+
+#endif
diff --git a/tests/unit/Settings/set.pri b/tests/unit/Settings/set.pri
new file mode 100644
index 0000000..df1871f
--- /dev/null
+++ b/tests/unit/Settings/set.pri
@@ -0,0 +1,10 @@
+HEADERS += \
+ $$PWD/TestSettings.h \
+ $$PWD/StudySettings_test.h \
+ $$PWD/FieldStyleFactory_test.h
+
+SOURCES += \
+ $$PWD/TestSettings.cpp \
+ $$PWD/StudySettings_test.cpp \
+ $$PWD/FieldStyleFactory_test.cpp
+ \ No newline at end of file
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
diff --git a/tests/unit/cards.pri b/tests/unit/cards.pri
new file mode 100644
index 0000000..e3c563e
--- /dev/null
+++ b/tests/unit/cards.pri
@@ -0,0 +1,24 @@
+HEADERS += \
+ $$SRC/dictionary/Card.h \
+ $$SRC/dictionary/ICardPack.h \
+ $$SRC/dictionary/CardPack.h \
+ $$SRC/dictionary/IDictionary.h \
+ $$SRC/dictionary/Field.h \
+ $$SRC/dictionary/DicRecord.h \
+ $$SRC/study/StudyRecord.h \
+ $$SRC/utils/TimeProvider.h \
+ $$TESTS/mocks/Dictionary_mock.h \
+ $$TESTS/common/RecordsParam.h
+
+SOURCES += \
+ $$SRC/dictionary/Card.cpp \
+ $$SRC/dictionary/ICardPack.cpp \
+ $$SRC/dictionary/CardPack.cpp \
+ $$SRC/dictionary/IDictionary.cpp \
+ $$SRC/dictionary/Field.cpp \
+ $$SRC/dictionary/DicRecord.cpp \
+ $$SRC/study/StudyRecord.cpp \
+ $$TESTS/mocks/Dictionary_mock.cpp \
+ $$TESTS/mocks/TimeProvider_mock.cpp \
+ $$TESTS/common/RecordsParam.cpp \
+ $$TESTS/common/RecordsParam_create.cpp
diff --git a/tests/unit/common.pri b/tests/unit/common.pri
new file mode 100644
index 0000000..482dab7
--- /dev/null
+++ b/tests/unit/common.pri
@@ -0,0 +1,3 @@
+SOURCES += \
+ main.cpp \
+ $$TESTS/common/printQtTypes.cpp
diff --git a/tests/unit/main.cpp b/tests/unit/main.cpp
new file mode 100644
index 0000000..eb8f2b9
--- /dev/null
+++ b/tests/unit/main.cpp
@@ -0,0 +1,9 @@
+#include <gtest/gtest.h>
+#include <QtWidgets>
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/unit/random.pri b/tests/unit/random.pri
new file mode 100644
index 0000000..003c663
--- /dev/null
+++ b/tests/unit/random.pri
@@ -0,0 +1 @@
+HEADERS += $$SRC/utils/IRandomGenerator.h
diff --git a/tests/unit/studySets.pri b/tests/unit/studySets.pri
new file mode 100644
index 0000000..50c6bd1
--- /dev/null
+++ b/tests/unit/studySets.pri
@@ -0,0 +1,9 @@
+HEADERS += \
+ $$SRC/field-styles/FieldStyleFactory.h \
+ $$SRC/field-styles/FieldStyle.h \
+ $$SRC/study/StudySettings.h
+
+SOURCES += \
+ $$SRC/field-styles/FieldStyleFactory.cpp \
+ $$SRC/field-styles/FieldStyle.cpp \
+ $$SRC/study/StudySettings.cpp \ No newline at end of file
diff --git a/tests/unit/unit_tests.pro b/tests/unit/unit_tests.pro
new file mode 100644
index 0000000..49f74d0
--- /dev/null
+++ b/tests/unit/unit_tests.pro
@@ -0,0 +1,29 @@
+TEMPLATE = app
+TARGET = unittest
+QT += widgets
+LIBS += -lgtest -lgtest_main
+QMAKE_CXXFLAGS += -std=gnu++11
+DESTDIR = ./
+
+win32: {
+ QMAKE_LIBS_QT_ENTRY =
+ CONFIG += console
+ }
+
+MOC_DIR = moc
+OBJECTS_DIR = obj
+
+SRC = $$PWD/../../src
+TESTS = $$PWD/..
+
+include(common.pri)
+include(cards.pri)
+include(studySets.pri)
+include(random.pri)
+
+include(Card/card.pri)
+include(CardPack/cPack.pri)
+include(CardSideView/csView.pri)
+include(RandomGenerator/rndGen.pri)
+include(Settings/set.pri)
+include(SpacedRepetitionModel/srModel.pri)