diff options
Diffstat (limited to 'tests/unit')
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) |