From d24f813f3f2a05c112e803e4256b53535895fc98 Mon Sep 17 00:00:00 2001 From: Jedidiah Barber Date: Wed, 14 Jul 2021 11:49:10 +1200 Subject: Initial mirror commit --- tests/common/RecordsParam.cpp | 51 +++ tests/common/RecordsParam.h | 58 +++ tests/common/RecordsParam_create.cpp | 62 +++ tests/common/printQtTypes.cpp | 31 ++ tests/common/printQtTypes.h | 23 ++ tests/fute/charts/charts.pro | 35 ++ tests/fute/charts/charts_test.cpp | 52 +++ tests/fute/charts/charts_test.h | 31 ++ tests/fute/charts/main.cpp | 12 + tests/fute/pieCharts/main.cpp | 12 + tests/fute/pieCharts/pieCharts.pro | 29 ++ tests/fute/pieCharts/pieCharts_test.cpp | 52 +++ tests/fute/pieCharts/pieCharts_test.h | 34 ++ tests/fute/timeCharts/main.cpp | 12 + tests/fute/timeCharts/timeCharts.pro | 35 ++ tests/fute/timeCharts/timeCharts_test.cpp | 58 +++ tests/fute/timeCharts/timeCharts_test.h | 30 ++ tests/mocks/CardPack_mock.cpp | 31 ++ tests/mocks/CardPack_mock.h | 23 ++ tests/mocks/Dictionary_mock.cpp | 6 + tests/mocks/Dictionary_mock.h | 32 ++ tests/mocks/RandomGenerator_mock.h | 33 ++ tests/mocks/TimeProvider_mock.cpp | 9 + tests/unit/Card/Card_GenerateAnswers_test.cpp | 66 ++++ tests/unit/Card/Card_GenerateAnswers_test.h | 34 ++ tests/unit/Card/Card_test.cpp | 36 ++ tests/unit/Card/Card_test.h | 18 + tests/unit/Card/Card_test_QuestionAnswer.cpp | 30 ++ tests/unit/Card/card.pri | 10 + .../unit/CardPack/CardPack_GenerateCards_test.cpp | 43 +++ tests/unit/CardPack/CardPack_GenerateCards_test.h | 26 ++ tests/unit/CardPack/CardPack_test.cpp | 80 ++++ tests/unit/CardPack/CardPack_test.h | 31 ++ tests/unit/CardPack/cPack.pri | 7 + tests/unit/CardSideView/CardSideView_test.cpp | 49 +++ tests/unit/CardSideView/CardSideView_test.h | 21 ++ tests/unit/CardSideView/csView.pri | 8 + .../unit/RandomGenerator/RandomGenerator_test.cpp | 79 ++++ tests/unit/RandomGenerator/rndGen.pri | 6 + tests/unit/Settings/FieldStyleFactory_test.cpp | 88 +++++ tests/unit/Settings/FieldStyleFactory_test.h | 29 ++ tests/unit/Settings/StudySettings_test.cpp | 42 +++ tests/unit/Settings/StudySettings_test.h | 22 ++ tests/unit/Settings/TestSettings.cpp | 16 + tests/unit/Settings/TestSettings.h | 11 + tests/unit/Settings/set.pri | 10 + .../SRModel_pickCard_test.cpp | 420 +++++++++++++++++++++ .../SpacedRepetitionModel/SRModel_pickCard_test.h | 27 ++ .../SRModel_schedule_test.cpp | 304 +++++++++++++++ .../SpacedRepetitionModel/SRModel_schedule_test.h | 24 ++ .../SRModel_showGrades_test.cpp | 36 ++ .../SRModel_showGrades_test.h | 14 + tests/unit/SpacedRepetitionModel/SRModel_test.cpp | 67 ++++ tests/unit/SpacedRepetitionModel/SRModel_test.h | 46 +++ tests/unit/SpacedRepetitionModel/srModel.pri | 16 + tests/unit/cards.pri | 24 ++ tests/unit/common.pri | 3 + tests/unit/main.cpp | 9 + tests/unit/random.pri | 1 + tests/unit/studySets.pri | 9 + tests/unit/unit_tests.pro | 29 ++ 61 files changed, 2542 insertions(+) create mode 100644 tests/common/RecordsParam.cpp create mode 100644 tests/common/RecordsParam.h create mode 100644 tests/common/RecordsParam_create.cpp create mode 100644 tests/common/printQtTypes.cpp create mode 100644 tests/common/printQtTypes.h create mode 100644 tests/fute/charts/charts.pro create mode 100644 tests/fute/charts/charts_test.cpp create mode 100644 tests/fute/charts/charts_test.h create mode 100644 tests/fute/charts/main.cpp create mode 100644 tests/fute/pieCharts/main.cpp create mode 100644 tests/fute/pieCharts/pieCharts.pro create mode 100644 tests/fute/pieCharts/pieCharts_test.cpp create mode 100644 tests/fute/pieCharts/pieCharts_test.h create mode 100644 tests/fute/timeCharts/main.cpp create mode 100644 tests/fute/timeCharts/timeCharts.pro create mode 100644 tests/fute/timeCharts/timeCharts_test.cpp create mode 100644 tests/fute/timeCharts/timeCharts_test.h create mode 100644 tests/mocks/CardPack_mock.cpp create mode 100644 tests/mocks/CardPack_mock.h create mode 100644 tests/mocks/Dictionary_mock.cpp create mode 100644 tests/mocks/Dictionary_mock.h create mode 100644 tests/mocks/RandomGenerator_mock.h create mode 100644 tests/mocks/TimeProvider_mock.cpp create mode 100644 tests/unit/Card/Card_GenerateAnswers_test.cpp create mode 100644 tests/unit/Card/Card_GenerateAnswers_test.h create mode 100644 tests/unit/Card/Card_test.cpp create mode 100644 tests/unit/Card/Card_test.h create mode 100644 tests/unit/Card/Card_test_QuestionAnswer.cpp create mode 100644 tests/unit/Card/card.pri create mode 100644 tests/unit/CardPack/CardPack_GenerateCards_test.cpp create mode 100644 tests/unit/CardPack/CardPack_GenerateCards_test.h create mode 100644 tests/unit/CardPack/CardPack_test.cpp create mode 100644 tests/unit/CardPack/CardPack_test.h create mode 100644 tests/unit/CardPack/cPack.pri create mode 100644 tests/unit/CardSideView/CardSideView_test.cpp create mode 100644 tests/unit/CardSideView/CardSideView_test.h create mode 100644 tests/unit/CardSideView/csView.pri create mode 100644 tests/unit/RandomGenerator/RandomGenerator_test.cpp create mode 100644 tests/unit/RandomGenerator/rndGen.pri create mode 100644 tests/unit/Settings/FieldStyleFactory_test.cpp create mode 100644 tests/unit/Settings/FieldStyleFactory_test.h create mode 100644 tests/unit/Settings/StudySettings_test.cpp create mode 100644 tests/unit/Settings/StudySettings_test.h create mode 100644 tests/unit/Settings/TestSettings.cpp create mode 100644 tests/unit/Settings/TestSettings.h create mode 100644 tests/unit/Settings/set.pri create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_test.cpp create mode 100644 tests/unit/SpacedRepetitionModel/SRModel_test.h create mode 100644 tests/unit/SpacedRepetitionModel/srModel.pri create mode 100644 tests/unit/cards.pri create mode 100644 tests/unit/common.pri create mode 100644 tests/unit/main.cpp create mode 100644 tests/unit/random.pri create mode 100644 tests/unit/studySets.pri create mode 100644 tests/unit/unit_tests.pro (limited to 'tests') diff --git a/tests/common/RecordsParam.cpp b/tests/common/RecordsParam.cpp new file mode 100644 index 0000000..74ad802 --- /dev/null +++ b/tests/common/RecordsParam.cpp @@ -0,0 +1,51 @@ +#include "RecordsParam.h" + +#include "../../src/dictionary/Field.h" +#include "../../src/dictionary/DicRecord.h" + +const vector RecordsParam::fieldNames {"English", "Russian", "Finnish"}; + +RecordsParam::RecordsParam( + vector packFields, + vector > records, + vector questions, + vector > answers): + packFields(packFields), + questions(questions), + answers(answers) +{ + for(vector fieldValues: records) + { + DicRecord* record = new DicRecord; + for(unsigned i = 0; i < fieldValues.size(); i++) + record->setField(fieldNames[i].c_str(), fieldValues[i].c_str()); + this->records << record; + } +} + +vector RecordsParam::hashToStrVector(const QHash& hash, + const vector& keys) +{ + vector res; + for(unsigned i = 0; i < keys.size(); i++) + res.push_back( hash[keys[i].c_str()].toStdString() ); + return res; +} + +vector RecordsParam::recordsToStr() const +{ + vector res; + for(DicRecord* record: records) + { + vector fieldValues = hashToStrVector(record->getFields(), fieldNames); + res.push_back(string("(") + vectorToStr(fieldValues) + ")"); + } + return res; +} + +ostream& operator<<(ostream& os, const RecordsParam& param) +{ + os << "Fields(" << param.vectorToStr(param.packFields) << ") "; + os << "{" << param.vectorToStr( param.recordsToStr() ) << "}"; + return os; +} diff --git a/tests/common/RecordsParam.h b/tests/common/RecordsParam.h new file mode 100644 index 0000000..19d4532 --- /dev/null +++ b/tests/common/RecordsParam.h @@ -0,0 +1,58 @@ +#ifndef RECORDS_PARAM_H +#define RECORDS_PARAM_H + +#include +#include +#include +#include +#include + +class DicRecord; + +using std::vector; +using std::string; +using std::ostream; + +struct RecordsParam +{ +public: + static vector createParams(); + + template + static string vectorToStr(const vector& v); + + static vector hashToStrVector(const QHash& hash, + const vector& keys); + +public: + RecordsParam(vector packFields, vector > records, + vector questions, vector > answers); + + vector recordsToStr() const; + +public: + static const vector fieldNames; + +public: + QList records; + vector packFields; + vector questions; + vector > answers; +}; + +ostream& operator<<(ostream& os, const RecordsParam& param); + +template +string RecordsParam::vectorToStr(const vector& v) +{ + std::stringstream ss; + for(unsigned i = 0; i < v.size(); i++) + { + if(i != 0) + ss << ", "; + ss << v[i]; + } + return ss.str(); +} + +#endif diff --git a/tests/common/RecordsParam_create.cpp b/tests/common/RecordsParam_create.cpp new file mode 100644 index 0000000..b247f35 --- /dev/null +++ b/tests/common/RecordsParam_create.cpp @@ -0,0 +1,62 @@ +#include "RecordsParam.h" + +typedef RecordsParam RP; + +vector RecordsParam::createParams() +{ +return + { + RP({0, 1}, + {{"table", "стол"}, {"window", "окно"}}, + {"table", "window"}, + {{"table", "стол"}, {"window", "окно"}}), + RP({0, 1}, + {{"table", "стол"}, {"window", "окно"}, {"world", "мир"}}, + {"table", "window", "world"}, + {{"table", "стол"}, {"window", "окно"}, {"world", "мир"}}), + RP({1, 0}, + {{"table", "стол"}, {"window", "окно"}}, + {"стол", "окно"}, + {{"стол", "table"}, {"окно", "window"}}), + RP({0, 1}, + {{"table", "стол"}, {"table", "стол"}}, + {"table"}, + {{"table", "стол"}}), + RP({0, 1}, + {{"table", "стол"}, {"table", "таблица"}}, + {"table"}, + {{"table", "стол; таблица"}}), + RP({1, 0}, + {{"world", "мир"}, {"peace", "мир"}}, + {"мир"}, + {{"мир", "world; peace"}}), + RP({1, 0}, + {{"man", "человек; мужчина"}, {"man", "мужик"}}, + {"человек", "мужчина", "мужик"}, + {{"человек", "man"}, {"мужчина", "man"}, {"мужик", "man"}}), + RP({0, 1}, + {{"better; best", "лучший"}}, + {"better", "best"}, + {{"better", "лучший"}, {"best", "лучший"}}), + RP({0, 1}, + {{"table", ""},}, + {}, + {}), + RP({0, 1}, + {{"", "стол"},}, + {}, + {}), + RP({0, 1, 2}, + {{"table", "стол", "pöytä"}, {"window", "окно", "ikkuna"}}, + {"table", "window"}, + {{"table", "стол", "pöytä"}, {"window", "окно", "ikkuna"}}), + RP({0, 2, 1}, + {{"table", "стол", "pöytä"}, {"window", "окно", "ikkuna"}}, + {"table", "window"}, + {{"table", "pöytä", "стол"}, {"window", "ikkuna", "окно"}}), + RP({0, 1, 2}, + {{"table", "", "pöytä"}, {"window", "окно", ""}}, + {"table", "window"}, + {{"table", "", "pöytä"}, {"window", "окно", ""}}), + }; +} diff --git a/tests/common/printQtTypes.cpp b/tests/common/printQtTypes.cpp new file mode 100644 index 0000000..72975b7 --- /dev/null +++ b/tests/common/printQtTypes.cpp @@ -0,0 +1,31 @@ +#include "printQtTypes.h" + +void PrintTo(const QString& str, ::std::ostream* os) +{ + *os << "\"" << str.toStdString() << "\""; +} + +void PrintTo(const QStringList& list, ::std::ostream* os) +{ + *os << "(" << list.join(", ").toStdString() << ")"; +} + +void PrintTo(const QDateTime& time, ::std::ostream* os) +{ + *os << time.toString("yyyy-MM-dd HH:mm:ss").toStdString(); +} + +void PrintTo(const QByteArray& array, ::std::ostream* os) +{ + *os << "\""; + const char* hex = array.toHex().constData(); + for(int i = 0; i < array.size(); i++) + { + unsigned char ch = array.constData()[i]; + if(ch >= 32 && ch <= 126) + *os << ch; + else + *os << "\\x" << hex[i * 2] << hex[i * 2 + 1] << " "; + } + *os << "\""; +} diff --git a/tests/common/printQtTypes.h b/tests/common/printQtTypes.h new file mode 100644 index 0000000..20c23d6 --- /dev/null +++ b/tests/common/printQtTypes.h @@ -0,0 +1,23 @@ +#ifndef PRINT_QT_TYPES_H +#define PRINT_QT_TYPES_H + +#include +#include +#include + +using std::ostream; +using std::vector; + +void PrintTo(const QString& str, ::std::ostream* os); +void PrintTo(const QStringList& list, ::std::ostream* os); + +#define ASSERT_EQ_QSTR(x, y) ASSERT_EQ(x, y) << "\"" << x.toStdString() << "\"" << \ + " != " << "\"" << y.toStdString() << "\""; + +#define ASSERT_EQ_QSTRLIST(x, y) ASSERT_EQ(x, y) << "(" << x.join(", ").toStdString() << ")" << \ + " != " << "(" << y.join(", ").toStdString() << ")"; + +void PrintTo(const QDateTime& time, ::std::ostream* os); +void PrintTo(const QByteArray& array, ::std::ostream* os); + +#endif diff --git a/tests/fute/charts/charts.pro b/tests/fute/charts/charts.pro new file mode 100644 index 0000000..613fee8 --- /dev/null +++ b/tests/fute/charts/charts.pro @@ -0,0 +1,35 @@ +TEMPLATE = app +QT += widgets +TARGET = charts_test +DEPENDPATH += . +INCLUDEPATH += . +QMAKE_CXXFLAGS += -std=gnu++11 +DESTDIR = ./ + +win32: { + CONFIG += console + } + +HEADERS = \ + charts_test.h \ + ../../../src/charts/Chart.h \ + ../../../src/charts/TimeChart.h \ + ../../../src/charts/DataPoint.h \ + ../../../src/charts/ChartScene.h \ + ../../../src/charts/ChartView.h \ + ../../../src/charts/ChartAxes.h \ + ../../../src/charts/ChartDataLine.h \ + ../../../src/charts/ChartMarker.h \ + ../../../src/charts/ChartToolTip.h + +SOURCES = \ + main.cpp \ + charts_test.cpp \ + ../../../src/charts/Chart.cpp \ + ../../../src/charts/TimeChart.cpp \ + ../../../src/charts/ChartScene.cpp \ + ../../../src/charts/ChartView.cpp \ + ../../../src/charts/ChartAxes.cpp \ + ../../../src/charts/ChartDataLine.cpp \ + ../../../src/charts/ChartMarker.cpp \ + ../../../src/charts/ChartToolTip.cpp diff --git a/tests/fute/charts/charts_test.cpp b/tests/fute/charts/charts_test.cpp new file mode 100644 index 0000000..0870027 --- /dev/null +++ b/tests/fute/charts/charts_test.cpp @@ -0,0 +1,52 @@ +#include "charts_test.h" +#include "../../../src/charts/Chart.h" + +#include +#include + +ChartsTest::ChartsTest() +{ + srand(time(NULL)); + createUi(); + changeDataSet(); +} + +void ChartsTest::changeDataSet() +{ + const int daysNum = 7; + yValuesStr = "Values: "; + dataSet.clear(); + for (int i = 0; i < daysNum; i++) + addDataPoint(i); + chart->setDataSet(dataSet); + valuesLabel->setText(yValuesStr); +} + +void ChartsTest::addDataPoint(int index) +{ + const int firstDay = 15; + QString xLabel = QString::number(firstDay + index) + ".11"; + int yValue = rand() % 70; + dataSet << DataPoint(xLabel, yValue, xLabel); + yValuesStr += QString::number(yValue) + ", "; +} + +void ChartsTest::createUi() +{ + QPushButton* newBtn = new QPushButton(tr("New chart")); + connect(newBtn, SIGNAL(clicked()), SLOT(changeDataSet())); + + valuesLabel = new QLabel; + chart = new Chart; + chart->setLabels("Date", "Value"); + + QHBoxLayout* controlLt = new QHBoxLayout; + controlLt->addWidget(valuesLabel); + controlLt->addWidget(newBtn); + + QVBoxLayout* mainLt = new QVBoxLayout; + mainLt->addLayout(controlLt); + mainLt->addWidget(chart); + setLayout(mainLt); + resize(800, 500); +} diff --git a/tests/fute/charts/charts_test.h b/tests/fute/charts/charts_test.h new file mode 100644 index 0000000..3975444 --- /dev/null +++ b/tests/fute/charts/charts_test.h @@ -0,0 +1,31 @@ +#ifndef CHARTS_TEST_H +#define CHARTS_TEST_H + +#include +#include + +#include "../../../src/charts/DataPoint.h" + +class Chart; + +class ChartsTest: public QWidget +{ + Q_OBJECT +public: + ChartsTest(); + +private: + void createUi(); + void addDataPoint(int index); + +private slots: + void changeDataSet(); + +private: + QString yValuesStr; + Chart* chart; + QList dataSet; + QLabel* valuesLabel; +}; + +#endif diff --git a/tests/fute/charts/main.cpp b/tests/fute/charts/main.cpp new file mode 100644 index 0000000..346133d --- /dev/null +++ b/tests/fute/charts/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "charts_test.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + ChartsTest mainWin; + mainWin.show(); + return app.exec(); +} + diff --git a/tests/fute/pieCharts/main.cpp b/tests/fute/pieCharts/main.cpp new file mode 100644 index 0000000..ed7ce6e --- /dev/null +++ b/tests/fute/pieCharts/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "pieCharts_test.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + PieChartsTest mainWin; + mainWin.show(); + return app.exec(); +} + diff --git a/tests/fute/pieCharts/pieCharts.pro b/tests/fute/pieCharts/pieCharts.pro new file mode 100644 index 0000000..960dd59 --- /dev/null +++ b/tests/fute/pieCharts/pieCharts.pro @@ -0,0 +1,29 @@ +TEMPLATE = app +QT += widgets +TARGET = piecharts_test +DEPENDPATH += . +INCLUDEPATH += . +QMAKE_CXXFLAGS += -std=gnu++11 +DESTDIR = ./ + +win32: { + CONFIG += console + } + +HEADERS = \ + pieCharts_test.h \ + ../../../src/charts/PieChart.h \ + ../../../src/charts/DataPoint.h \ + ../../../src/charts/PieChartScene.h \ + ../../../src/charts/ChartView.h \ + ../../../src/charts/PieRound.h \ + ../../../src/charts/PieLegend.h + +SOURCES = \ + main.cpp \ + pieCharts_test.cpp \ + ../../../src/charts/PieChart.cpp \ + ../../../src/charts/PieChartScene.cpp \ + ../../../src/charts/ChartView.cpp \ + ../../../src/charts/PieRound.cpp \ + ../../../src/charts/PieLegend.cpp diff --git a/tests/fute/pieCharts/pieCharts_test.cpp b/tests/fute/pieCharts/pieCharts_test.cpp new file mode 100644 index 0000000..d88981a --- /dev/null +++ b/tests/fute/pieCharts/pieCharts_test.cpp @@ -0,0 +1,52 @@ +#include "pieCharts_test.h" +#include "../../../src/charts/PieChart.h" + +#include +#include + +const QStringList PieChartsTest::Labels = + {"Studied", "Scheduled for today", "New"}; + +PieChartsTest::PieChartsTest() +{ + srand(time(NULL)); + createUi(); + changeDataSet(); +} + +void PieChartsTest::changeDataSet() +{ + yValuesStr = "Values: "; + dataSet.clear(); + for (int i = 0; i < 3; i++) + addDataPoint(i); + chart->setDataSet(dataSet); + valuesLabel->setText(yValuesStr); +} + +void PieChartsTest::addDataPoint(int index) +{ + int yValue = rand() % 200; + dataSet << DataPoint(Labels[index], yValue, ""); + yValuesStr += QString::number(yValue) + ", "; +} + +void PieChartsTest::createUi() +{ + QPushButton* newBtn = new QPushButton(tr("New chart")); + connect(newBtn, SIGNAL(clicked()), SLOT(changeDataSet())); + + valuesLabel = new QLabel; + chart = new PieChart; + chart->setColors({"#39c900", "#ece900", "#ff0000"}); + + QHBoxLayout* controlLt = new QHBoxLayout; + controlLt->addWidget(valuesLabel); + controlLt->addWidget(newBtn); + + QVBoxLayout* mainLt = new QVBoxLayout; + mainLt->addLayout(controlLt); + mainLt->addWidget(chart); + setLayout(mainLt); + resize(800, 500); +} diff --git a/tests/fute/pieCharts/pieCharts_test.h b/tests/fute/pieCharts/pieCharts_test.h new file mode 100644 index 0000000..835ff72 --- /dev/null +++ b/tests/fute/pieCharts/pieCharts_test.h @@ -0,0 +1,34 @@ +#ifndef PIE_CHARTS_TEST_H +#define PIE_CHARTS_TEST_H + +#include +#include + +#include "../../../src/charts/DataPoint.h" + +class PieChart; + +class PieChartsTest: public QWidget +{ + Q_OBJECT +public: + static const QStringList Labels; + +public: + PieChartsTest(); + +private: + void createUi(); + void addDataPoint(int index); + +private slots: + void changeDataSet(); + +private: + QString yValuesStr; + PieChart* chart; + QList dataSet; + QLabel* valuesLabel; +}; + +#endif diff --git a/tests/fute/timeCharts/main.cpp b/tests/fute/timeCharts/main.cpp new file mode 100644 index 0000000..3abb753 --- /dev/null +++ b/tests/fute/timeCharts/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "timeCharts_test.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + TimeChartsTest mainWin; + mainWin.show(); + return app.exec(); +} + diff --git a/tests/fute/timeCharts/timeCharts.pro b/tests/fute/timeCharts/timeCharts.pro new file mode 100644 index 0000000..a570829 --- /dev/null +++ b/tests/fute/timeCharts/timeCharts.pro @@ -0,0 +1,35 @@ +TEMPLATE = app +QT += widgets +TARGET = timeCharts_test +DEPENDPATH += . +INCLUDEPATH += . +QMAKE_CXXFLAGS += -std=gnu++11 +DESTDIR = ./ + +win32: { + CONFIG += console + } + +HEADERS = \ + timeCharts_test.h \ + ../../../src/charts/Chart.h \ + ../../../src/charts/TimeChart.h \ + ../../../src/charts/DataPoint.h \ + ../../../src/charts/ChartScene.h \ + ../../../src/charts/ChartView.h \ + ../../../src/charts/ChartAxes.h \ + ../../../src/charts/ChartDataLine.h \ + ../../../src/charts/ChartMarker.h \ + ../../../src/charts/ChartToolTip.h + +SOURCES = \ + main.cpp \ + timeCharts_test.cpp \ + ../../../src/charts/Chart.cpp \ + ../../../src/charts/TimeChart.cpp \ + ../../../src/charts/ChartScene.cpp \ + ../../../src/charts/ChartView.cpp \ + ../../../src/charts/ChartAxes.cpp \ + ../../../src/charts/ChartDataLine.cpp \ + ../../../src/charts/ChartMarker.cpp \ + ../../../src/charts/ChartToolTip.cpp diff --git a/tests/fute/timeCharts/timeCharts_test.cpp b/tests/fute/timeCharts/timeCharts_test.cpp new file mode 100644 index 0000000..53f0921 --- /dev/null +++ b/tests/fute/timeCharts/timeCharts_test.cpp @@ -0,0 +1,58 @@ +#include "../../../src/charts/TimeChart.h" + +#include +#include +#include "timeCharts_test.h" + +TimeChartsTest::TimeChartsTest() +{ + srand(time(NULL)); + createUi(); + changeDataSet(); +} + +void TimeChartsTest::changeDataSet() +{ + int daysNum = periodBox->value(); + yValuesStr = "Values: "; + dates.clear(); + for (int i = 0; i < daysNum; i++) + addDataPoint(i); + chart->setDates(dates, daysNum, 1); + valuesLabel->setText(yValuesStr); +} + +void TimeChartsTest::addDataPoint(int index) +{ + QDateTime date = QDateTime::currentDateTime().addDays(index); + int dayCardsNum = rand() % 50; + for(int i = 0; i < dayCardsNum; i++) + dates << date; + yValuesStr += QString::number(dayCardsNum) + ", "; +} + +void TimeChartsTest::createUi() +{ + QPushButton* newBtn = new QPushButton(tr("New chart")); + connect(newBtn, SIGNAL(clicked()), SLOT(changeDataSet())); + + valuesLabel = new QLabel; + valuesLabel->setMaximumWidth(750); + + periodBox = new QSpinBox; + periodBox->setRange(7, 5000); + + chart = new TimeChart; + chart->setLabels(tr("Date"), tr("Cards")); + + QHBoxLayout* controlLt = new QHBoxLayout; + controlLt->addWidget(periodBox); + controlLt->addWidget(newBtn); + + QVBoxLayout* mainLt = new QVBoxLayout; + mainLt->addLayout(controlLt); + mainLt->addWidget(valuesLabel); + mainLt->addWidget(chart); + setLayout(mainLt); + resize(800, 500); +} diff --git a/tests/fute/timeCharts/timeCharts_test.h b/tests/fute/timeCharts/timeCharts_test.h new file mode 100644 index 0000000..49d6346 --- /dev/null +++ b/tests/fute/timeCharts/timeCharts_test.h @@ -0,0 +1,30 @@ +#ifndef CHARTS_TEST_H +#define CHARTS_TEST_H + +#include +#include + +class TimeChart; + +class TimeChartsTest: public QWidget +{ + Q_OBJECT +public: + TimeChartsTest(); + +private: + void createUi(); + void addDataPoint(int index); + +private slots: + void changeDataSet(); + +private: + QString yValuesStr; + TimeChart* chart; + QList dates; + QLabel* valuesLabel; + QSpinBox* periodBox; +}; + +#endif diff --git a/tests/mocks/CardPack_mock.cpp b/tests/mocks/CardPack_mock.cpp new file mode 100644 index 0000000..159e0de --- /dev/null +++ b/tests/mocks/CardPack_mock.cpp @@ -0,0 +1,31 @@ +#include "CardPack_mock.h" + +void CardPackMock::addStudyRecord(const QString cardId, const StudyRecord& studyRecord) +{ + studyRecords.insert(cardId, studyRecord); +} + +QList CardPackMock::getStudyRecords(QString cardId) const +{ + return studyRecords.values(cardId); +} + +StudyRecord CardPackMock::getStudyRecord(QString cardId) const +{ + return studyRecords.values(cardId).first(); +} + +QList CardPackMock::getRecords() const +{ + return QList(); +} + +const Field* CardPackMock::getQuestionField() const +{ + return NULL; +} + +QList CardPackMock::getAnswerFields() const +{ + return QList(); +} diff --git a/tests/mocks/CardPack_mock.h b/tests/mocks/CardPack_mock.h new file mode 100644 index 0000000..50e6c13 --- /dev/null +++ b/tests/mocks/CardPack_mock.h @@ -0,0 +1,23 @@ +#ifndef CARDPACK_MOCK_H +#define CARDPACK_MOCK_H + +#include + +#include "../../src/dictionary/ICardPack.h" + +class CardPackMock: public ICardPack +{ +public: + void addStudyRecord(const QString cardId, const StudyRecord& studyRecord); + QList getStudyRecords(QString cardId) const; + StudyRecord getStudyRecord(QString cardId) const; + + QList getRecords() const; + const Field* getQuestionField() const; + QList getAnswerFields() const; + +private: + QMultiHash< QString, StudyRecord > studyRecords; +}; + +#endif diff --git a/tests/mocks/Dictionary_mock.cpp b/tests/mocks/Dictionary_mock.cpp new file mode 100644 index 0000000..905a2f2 --- /dev/null +++ b/tests/mocks/Dictionary_mock.cpp @@ -0,0 +1,6 @@ +#include "Dictionary_mock.h" + +void MockDictionary::addCardPack(CardPack* aCardPack) +{ + m_cardPacks << aCardPack; +} diff --git a/tests/mocks/Dictionary_mock.h b/tests/mocks/Dictionary_mock.h new file mode 100644 index 0000000..0b29fbb --- /dev/null +++ b/tests/mocks/Dictionary_mock.h @@ -0,0 +1,32 @@ +#ifndef DICTIONARY_MOCK_H +#define DICTIONARY_MOCK_H + +#include + +#include "../../src/dictionary/IDictionary.h" +#include "../../src/dictionary/TreeItem.h" + +class MockDictionary: public TreeItem, public IDictionary +{ +Q_OBJECT +public: + const TreeItem* parent() const { return NULL; } + const TreeItem* child(int) const { return NULL; } + int childCount() const { return 0; } + int columnCount() const { return 0; } + QVariant data(int) const { return QVariant(); } + int row() const { return 0; } + int topParentRow() const { return 0; } + + const Field* field(int) const { return NULL; } + const Field* field(const QString) const { return NULL; } + int indexOfCardPack(CardPack*) const { return 0; } + + void addCardPack(CardPack* aCardPack); + +signals: + void entryChanged( int aEntryIx, int aFieldIx ); + void entriesRemoved( int aIndex, int aNum ); +}; + +#endif diff --git a/tests/mocks/RandomGenerator_mock.h b/tests/mocks/RandomGenerator_mock.h new file mode 100644 index 0000000..0d3ef97 --- /dev/null +++ b/tests/mocks/RandomGenerator_mock.h @@ -0,0 +1,33 @@ +#ifndef RANDOM_GENERATOR_MOCK_H +#define RANDOM_GENERATOR_MOCK_H + +#include "../../src/utils/IRandomGenerator.h" + +class MockRandomGenerator: public IRandomGenerator +{ +public: + MockRandomGenerator(): + dRandom(0), rand(0) {} + double getInRange_11() const { return dRandom; } + double getInRange_01() const { return dRandom; } + int getRand() const { return rand; } + int getRand(int maxNum) const + { + int r = rand; + if(r >= maxNum) + r = maxNum -1; + return r; + } + QByteArray getArray() const { return array; } + + void setDouble(double dRandom) { this->dRandom = dRandom; } + void setRand(int rand) { this->rand = rand; } + void setArray(const QByteArray& array) { this->array = array; } + +private: + double dRandom; + int rand; + QByteArray array; +}; + +#endif diff --git a/tests/mocks/TimeProvider_mock.cpp b/tests/mocks/TimeProvider_mock.cpp new file mode 100644 index 0000000..22cfb43 --- /dev/null +++ b/tests/mocks/TimeProvider_mock.cpp @@ -0,0 +1,9 @@ +#include "../../src/utils/TimeProvider.h" + +// TODO: Make interface with virtual function. Use Time provider as singleton. + +QDateTime TimeProvider::get() +{ + static QDateTime time = QDateTime::currentDateTime(); + return time; +} 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 + +#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 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 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 +#include +#include + +#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 +{ +public: + GenerateAnswersTest(): + pack(&dict) {} + +protected: + void TearDown(); + void addFieldToPack(unsigned fieldId); + +public: + static const vector fieldNames; + +protected: + vector 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 +#include + +#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 + +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 + +#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 +{}; + +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 + +#include "../../common/printQtTypes.h" +#include "../../../src/dictionary/IDictionary.h" +#include "../../../src/dictionary/Field.h" +#include "../../../src/dictionary/DicRecord.h" + +vector GenerateCardsTest::fields; + +INSTANTIATE_TEST_CASE_P(, GenerateCardsTest, + testing::ValuesIn(RecordsParam::createParams()) ); + +vector GenerateCardsTest::getFields() +{ + static vector 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 +#include +#include +#include +#include + +#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 +{ +public: + static void TearDownTestCase(); + static vector getFields(); + +private: + static vector 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(), 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 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 +#include +#include + +#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 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 +#include +#include + +#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("First", + 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("First example

"\ + "Pervyj", + view.getFormattedText()); + } + +TEST_F(CardSideViewTest, getFormattedAnswer_1missing) + { + CardSideView view(CardSideView::AnsMode); + view.setPack(&cardPack); + QStringList answers = QStringList() << "" << "Pervyj"; + view.setQstAnsr("First", answers); + ASSERT_EQ("

Pervyj", + 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 + +#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 +#include +#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(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 +#include + +#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 +#include + +#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 +#include + +#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& expCards) +{ + vector pickedCards; + for(unsigned i = 0; i < expCards.size(); i++) + { + if(i == 0) + model.testPickCard(); + else + model.scheduleCard(StudyRecord::Good); + if(model.getCurCard()) + pickedCards.push_back(model.getCurCard()->getQuestion().toInt()); + else + pickedCards.push_back(-1); + } + ASSERT_EQ(expCards, pickedCards); +} diff --git a/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h new file mode 100644 index 0000000..aacb59a --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_pickCard_test.h @@ -0,0 +1,27 @@ +#include "SRModel_test.h" + +#include + +using namespace std; + +class SRModel_pickCard_Test: public SRModelTest +{ +public: + static const int NoCard = -1; + static const int Now = 0; + static const double HourAgo; + +protected: + static QDateTime timeFromDelta(double daysDelta); + +protected: + void SetUp(); + void addStudied(double daysDelta, double interval, int grade = 4, + int level = StudyRecord::Repeating); + void addUnknown(double daysDelta); + void addIncorrect(double daysDelta); + void addLearning(double daysDelta); + void addNew(); + void assertCurCard(int expCard); + void testPicked(const vector& expCards); +}; diff --git a/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp new file mode 100644 index 0000000..cd65154 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.cpp @@ -0,0 +1,304 @@ +#include "SRModel_schedule_test.h" +#include "../../../src/utils/TimeProvider.h" + +const double SRModel_schedule_Test::startE = StudySettings().initEasiness; + +void SRModel_schedule_Test::SetUp() +{ + StudySettings::inst()->schedRandomness = StudySettings().schedRandomness; + randomGenerator->setDouble(0.3); + cardName = createCard(); +} + +static const StudySettings* ss = StudySettings::inst(); + + +// New +// Grades 1, 2, 3 not used + +TEST_F(SRModel_schedule_Test, new_good) // Levels: 1 -> 2 + { + setNewCard(); + schedule(4); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, new_easy) // Levels: 1 -> 3 + { + setNewCard(); + schedule(5); + checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval); + } + + +// Short Learning + +TEST_F(SRModel_schedule_Test, short_unknown) // same level + { + setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}); + schedule(1); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, short_incor) // same level with longer interval + { + setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}); + schedule(2); + checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval); + } + + // grade 3 not used + +TEST_F(SRModel_schedule_Test, short_good) // Levels: 2 -> 3 + { + setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}); + schedule(4); + checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval); + } + +TEST_F(SRModel_schedule_Test, short_easy) // Levels: 2 -> repeat next day + { + setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}); + schedule(5); + checkStudy(StudyRecord::Repeating, startE, ss->nextDayInterval); + } + + +// Short learning from previous day: +// Is promoted directly to next day repeating + +TEST_F(SRModel_schedule_Test, short_prevDay_difficult) + { + setStudy({StudyRecord::ShortLearning, 4, 2.5, ss->unknownInterval}, -1); + schedule(3); + checkStudy(StudyRecord::Repeating, 2.36, 2.18772); + } + +TEST_F(SRModel_schedule_Test, short_prevDay_good) + { + setStudy({StudyRecord::ShortLearning, 4, startE, ss->unknownInterval}, -1); + schedule(4); + checkStudy(StudyRecord::Repeating, startE, 2.3175); + } + +TEST_F(SRModel_schedule_Test, short_prevDay_easy) + { + setStudy({StudyRecord::ShortLearning, 4, 2.5, ss->unknownInterval}, -1); + schedule(5); + checkStudy(StudyRecord::Repeating, 2.6, 2.4102); + } + + +// Long Learning + +TEST_F(SRModel_schedule_Test, long_unknown) // Levels: 3 -> 2 + { + setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}); + schedule(1); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, long_incor) // Levels: 3 -> 2 + { + setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}); + schedule(2); + checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval); + } + + // grade 3 not used + +TEST_F(SRModel_schedule_Test, long_good) // Levels: 3 -> repeat next day + { + setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}); + schedule(4); + checkStudy(StudyRecord::Repeating, startE, ss->nextDayInterval); + } + +TEST_F(SRModel_schedule_Test, long_easy) // Levels: 3 -> repeat in 2 days + { + setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}); + schedule(5); + checkStudy(StudyRecord::Repeating, startE, ss->twoDaysInterval); + } + + +// Long learning from previous day: +// Is promoted directly to next day repeating + +TEST_F(SRModel_schedule_Test, long_prevDay_difficult) + { + setStudy({StudyRecord::LongLearning, 4, 2.5, ss->learningInterval}, -1); + schedule(3); + checkStudy(StudyRecord::Repeating, 2.36, 2.18772); + } + +TEST_F(SRModel_schedule_Test, long_prevDay_good) + { + setStudy({StudyRecord::LongLearning, 4, startE, ss->learningInterval}, -1); + schedule(4); + checkStudy(StudyRecord::Repeating, startE, 2.3175); + } + +TEST_F(SRModel_schedule_Test, long_prevDay_easy) + { + setStudy({StudyRecord::LongLearning, 4, 2.5, ss->learningInterval}, -1); + schedule(5); + checkStudy(StudyRecord::Repeating, 2.6, 2.4102); + } + + +// First repeating day + +TEST_F(SRModel_schedule_Test, firstRep_unknown) // -> 2 + { + setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval}); + schedule(1); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, firstRep_incor) // -> 2 + { + setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval}); + schedule(2); + checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval); + } + +TEST_F(SRModel_schedule_Test, firstRep_difficult) + { + setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval}); + schedule(3); + checkStudy(StudyRecord::Repeating, 2.36, 2.18772); + } + +TEST_F(SRModel_schedule_Test, firstRep_good) + { + setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval}); + schedule(4); + checkStudy(StudyRecord::Repeating, startE, 2.3175); + } + +TEST_F(SRModel_schedule_Test, firstRep_easy) + { + setStudy({StudyRecord::Repeating, 4, startE, ss->nextDayInterval}); + schedule(5); + checkStudy(StudyRecord::Repeating, 2.6, 2.4102); + } + + +// Normal repeating + +TEST_F(SRModel_schedule_Test, repeat_unknown) + { + setStudy({StudyRecord::Repeating, 4, startE, 5}); + schedule(1); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, repeat_incor) + { + setStudy({StudyRecord::Repeating, 4, startE, 5}); + schedule(2); + checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval); + } + +TEST_F(SRModel_schedule_Test, repeat_difficult) + { + setStudy({StudyRecord::Repeating, 4, 2.5, 5}); + schedule(3); + checkStudy(StudyRecord::Repeating, 2.36, 12.1540); + } + +TEST_F(SRModel_schedule_Test, repeat_good) + { + setStudy({StudyRecord::Repeating, 4, startE, 5}); + schedule(4); + checkStudy(StudyRecord::Repeating, startE, 12.875); + } + +TEST_F(SRModel_schedule_Test, repeat_easy) + { + setStudy({StudyRecord::Repeating, 4, 2.5, 5}); + schedule(5); + checkStudy(StudyRecord::Repeating, 2.6, 13.39); + } + + +// Mature repeating + +TEST_F(SRModel_schedule_Test, mature_difficult) + { + setStudy({StudyRecord::Repeating, 4, 3.1, 23}); + schedule(3); + checkStudy(StudyRecord::Repeating, 2.96, 70.1224); + } + +TEST_F(SRModel_schedule_Test, mature_good) + { + setStudy({StudyRecord::Repeating, 4, 3.1, 23}); + schedule(4); + checkStudy(StudyRecord::Repeating, 3.1, 73.439); + } + + +// Re-learning unknown and incorrect cards + +TEST_F(SRModel_schedule_Test, unknown_incor) + { + setStudy({StudyRecord::ShortLearning, 1, startE, ss->unknownInterval}); + schedule(2); + checkStudy(StudyRecord::ShortLearning, startE, ss->incorrectInterval); + } + +TEST_F(SRModel_schedule_Test, incor_unknown) + { + setStudy({StudyRecord::ShortLearning, 2, startE, ss->incorrectInterval}); + schedule(1); + checkStudy(StudyRecord::ShortLearning, startE, ss->unknownInterval); + } + +TEST_F(SRModel_schedule_Test, unknown_good) + { + setStudy({StudyRecord::ShortLearning, 1, startE, ss->unknownInterval}); + schedule(4); + checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval); + } + +TEST_F(SRModel_schedule_Test, incor_good) + { + setStudy({StudyRecord::ShortLearning, 2, startE, ss->incorrectInterval}); + schedule(4); + checkStudy(StudyRecord::LongLearning, startE, ss->learningInterval); + } + + +void SRModel_schedule_Test::setNewCard() +{ + pack.addStudyRecord(cardName, StudyRecord()); +} + +void SRModel_schedule_Test::setStudy(StudyRecord study, double daysDelta) +{ + study.date = timeFromDelta(daysDelta); + pack.addStudyRecord(cardName, study); +} + +QDateTime SRModel_schedule_Test::timeFromDelta(double daysDelta) +{ + return TimeProvider::get().addSecs((int)(daysDelta * 24 * 60 * 60)); +} + +void SRModel_schedule_Test::schedule(int grade) +{ + this->grade = grade; + model.scheduleCard(grade); +} + +void SRModel_schedule_Test::checkStudy(int expLevel, double expEasiness, + double expInterval) +{ + StudyRecord newStudy = pack.getStudyRecord(cardName); + ASSERT_EQ(grade, newStudy.grade); + ASSERT_EQ(expLevel, newStudy.level); + ASSERT_DOUBLE_EQ(expEasiness, newStudy.easiness); + ASSERT_NEAR(expInterval, newStudy.interval, 0.00000001); +} diff --git a/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h new file mode 100644 index 0000000..6da4365 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_schedule_test.h @@ -0,0 +1,24 @@ +#include "SRModel_test.h" +#include "../../../src/dictionary/Card.h" +#include "../../../src/study/StudySettings.h" +#include "../../mocks/RandomGenerator_mock.h" + +class SRModel_schedule_Test: public SRModelTest +{ +protected: + static const double startE; + +protected: + void SetUp(); + void setNewCard(); + void setStudy(StudyRecord study, double daysDelta = 0); + void schedule(int grade); + void checkStudy(int expLevel, double expEasiness, double expInterval); + +private: + static QDateTime timeFromDelta(double daysDelta); + +protected: + QString cardName; + int grade; +}; diff --git a/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp new file mode 100644 index 0000000..2fd0675 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.cpp @@ -0,0 +1,36 @@ +#include "SRModel_showGrades_test.h" + +static const StudySettings* ss = StudySettings::inst(); + +void SRModel_showGrades_Test::SetUp() +{ + StudySettings::inst()->schedRandomness = StudySettings().schedRandomness; + randomGenerator->setDouble(0.3); + cardName = createCard(); +} + +TEST_F(SRModel_showGrades_Test, new) + { + setStudy({StudyRecord::New, 4, ss->initEasiness, 0}); + QList expGrades = {4, 5}; + ASSERT_EQ(expGrades, model.getAvailableGrades()); + } + +TEST_F(SRModel_showGrades_Test, learning) + { + setStudy({StudyRecord::ShortLearning, 4, ss->initEasiness, ss->unknownInterval}); + QList expGrades = {1, 2, 4, 5}; + ASSERT_EQ(expGrades, model.getAvailableGrades()); + } + +TEST_F(SRModel_showGrades_Test, repeating) + { + setStudy({StudyRecord::Repeating, 4, ss->initEasiness, 2}); + QList expGrades = {1, 2, 3, 4, 5}; + ASSERT_EQ(expGrades, model.getAvailableGrades()); + } + +void SRModel_showGrades_Test::setStudy(const StudyRecord& study) +{ + pack.addStudyRecord(cardName, study); +} diff --git a/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h new file mode 100644 index 0000000..c45a760 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_showGrades_test.h @@ -0,0 +1,14 @@ +#include "SRModel_test.h" +#include "../../../src/dictionary/Card.h" +#include "../../../src/study/StudySettings.h" +#include "../../mocks/RandomGenerator_mock.h" + +class SRModel_showGrades_Test: public SRModelTest +{ +protected: + void SetUp(); + void setStudy(const StudyRecord& study); + +protected: + QString cardName; +}; diff --git a/tests/unit/SpacedRepetitionModel/SRModel_test.cpp b/tests/unit/SpacedRepetitionModel/SRModel_test.cpp new file mode 100644 index 0000000..420dfa7 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_test.cpp @@ -0,0 +1,67 @@ +#include "SRModel_test.h" +#include "../../common/printQtTypes.h" +#include "../../../src/dictionary/DicRecord.h" +#include "../../../src/dictionary/Card.h" +#include "../../../src/study/StudySettings.h" +#include "../../mocks/RandomGenerator_mock.h" + +SRModelTest::SRModelTest(): + recordId(0), + pack(&dict), + randomGenerator(new MockRandomGenerator), + model(&pack, randomGenerator) +{ + field1 = new Field("English", "Normal"); + field2 = new Field("Russian", "Normal"); + pack.addField(field1); + pack.addField(field2); + dict.addCardPack(&pack); +} + +void SRModelTest::SetUp() +{ + QCoreApplication::setOrganizationName("freshmemory"); + QCoreApplication::setApplicationName("freshmemory"); + QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, ""); + StudySettings::inst()->load(); +} + +void SRModelTest::TearDown() +{ + delete field1; + delete field2; +} + +TEST_F(SRModelTest, EmptyStats) + { + ASSERT_EQ(0, model.estimatedNewReviewedCardsToday()); + ASSERT_EQ(0, model.countTodayRemainingCards()); + } + +TEST_F(SRModelTest, FirstStudy) + { + QString cardName = createCard(); + StudyRecord newStudy = pack.getStudyRecord(cardName); + + ASSERT_EQ(StudyRecord::Unknown, newStudy.grade); + ASSERT_DOUBLE_EQ(StudySettings::inst()->initEasiness, newStudy.easiness); + ASSERT_DOUBLE_EQ(0, newStudy.interval); + } + +QString SRModelTest::createCard() +{ + addRecord(); + pack.generateQuestions(); + return model.getCurCard()->getQuestion(); +} + +QString SRModelTest::addRecord() +{ + DicRecord* record = new DicRecord; + QString recordName = QString::number(recordId); + record->setField("English", recordName); + record->setField("Russian", "Odin"); + recordId++; + dict.addRecord(record); + return recordName; +} diff --git a/tests/unit/SpacedRepetitionModel/SRModel_test.h b/tests/unit/SpacedRepetitionModel/SRModel_test.h new file mode 100644 index 0000000..9e5d669 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/SRModel_test.h @@ -0,0 +1,46 @@ +#ifndef SPACED_REPETITION_MODEL_TEST_H +#define SPACED_REPETITION_MODEL_TEST_H + +#include +#include +#include + +#include "../../../src/study/SpacedRepetitionModel.h" +#include "../../../src/dictionary/CardPack.h" +#include "../../mocks/Dictionary_mock.h" + +using std::ostream; + +class MockRandomGenerator; +class Field; + +class TestSpacedRepetitionModel: public SpacedRepetitionModel +{ +public: + TestSpacedRepetitionModel(CardPack* pack, IRandomGenerator* random): + SpacedRepetitionModel(pack, random) {} + void testPickCard() { pickNextCardAndNotify(); } +}; + +class SRModelTest: public testing::Test +{ +public: + SRModelTest(); + +protected: + void SetUp(); + void TearDown(); + QString createCard(); + QString addRecord(); + +protected: + MockDictionary dict; + int recordId; + Field* field1; + Field* field2; + CardPack pack; + MockRandomGenerator* randomGenerator; + TestSpacedRepetitionModel model; +}; + +#endif diff --git a/tests/unit/SpacedRepetitionModel/srModel.pri b/tests/unit/SpacedRepetitionModel/srModel.pri new file mode 100644 index 0000000..19daca1 --- /dev/null +++ b/tests/unit/SpacedRepetitionModel/srModel.pri @@ -0,0 +1,16 @@ +HEADERS += \ + $$PWD/SRModel_test.h \ + $$PWD/SRModel_schedule_test.h \ + $$PWD/SRModel_pickCard_test.h \ + $$PWD/SRModel_showGrades_test.h \ + $$SRC/study/SpacedRepetitionModel.h \ + $$SRC/study/IStudyModel.h \ + $$TESTS/mocks/RandomGenerator_mock.h + +SOURCES += \ + $$PWD/SRModel_test.cpp \ + $$PWD/SRModel_schedule_test.cpp \ + $$PWD/SRModel_pickCard_test.cpp \ + $$PWD/SRModel_showGrades_test.cpp \ + $$SRC/study/SpacedRepetitionModel.cpp \ + $$SRC/study/IStudyModel.cpp 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 +#include + +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) -- cgit