diff --git a/.gitmodules b/.gitmodules index 1220506c..973c7053 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "webserver/qhttp"] path = webserver/qhttp url = https://github.com/azadkuh/qhttp.git +[submodule "Catch2"] + path = Catch2 + url = git@github.com:catchorg/Catch2.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 731fa072..04dcb5a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ add_subdirectory(irc) add_subdirectory(cli) add_subdirectory(mobile) #add_subdirectory(webserver) +add_subdirectory(Catch2) +add_subdirectory(unittest) #qt5_use_modules() diff --git a/Catch2 b/Catch2 new file mode 160000 index 00000000..7abd7db2 --- /dev/null +++ b/Catch2 @@ -0,0 +1 @@ +Subproject commit 7abd7db2c8f03cc2e51ea49fff5247ee5e83464b diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 4a085a30..ed1692c1 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -29,6 +29,7 @@ set(MODE "cli") ADD_DEFINITIONS( -std=c++11 # Or -std=c++0x + -g # Other flags ) diff --git a/cli/displaytoolbox.h b/cli/displaytoolbox.h index 0b6cd120..783ddd69 100644 --- a/cli/displaytoolbox.h +++ b/cli/displaytoolbox.h @@ -19,5 +19,74 @@ class DisplayToolBox static QString diceResultToString(QJsonObject val); }; +struct Parsed { + int isValid; + QList diceResult; + QString result; + QString comment; + QString error; + QString scalarText; +}; + +inline +Parsed parse(DiceParser& parser, QString cmd) +{ + Parsed parsed; + + parsed.isValid = parser.parseLine(cmd); + if (!parsed.isValid) { + return parsed; + } + + parser.start(); + + bool homogeneous = true; + parser.getLastDiceResult(parsed.diceResult,homogeneous); + + QString lastScalarText; + QStringList strLst; + + if(parser.hasIntegerResultNotInFirst()) + { + auto values = parser.getLastIntegerResults(); + for(auto val : values ) + { + strLst << QString::number(val); + } + parsed.scalarText = QString("%1").arg(strLst.join(',')); + lastScalarText = strLst.last(); + } + else if(!parsed.diceResult.isEmpty()) + { + auto values = parser.getSumOfDiceResult(); + for(auto val : values ) + { + strLst << QString::number(val); + } + parsed.scalarText = QString("%1").arg(strLst.join(',')); + } + + if(parser.hasStringResult()) + { + bool ok; // FIXME: use me + parsed.result = parser.getAllStringResult(ok) + .join(" ; ") + .replace("%1",parsed.scalarText) + .replace("%3",lastScalarText); + + int i = 1; + for(auto value : strLst) + { + parsed.result.replace(QStringLiteral("$%1").arg(i),value); + ++i; + } + } + + parsed.comment = parser.getComment(); + parsed.error = parser.humanReadableError(); + + return parsed; +} + #endif // GENERATEIMAGE_H diff --git a/cli/main.cpp b/cli/main.cpp index 2078e9ef..37f1f766 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -49,7 +49,6 @@ QTextStream out(stdout, QIODevice::WriteOnly); bool markdown = false; enum EXPORTFORMAT {TERMINAL, SVG, IMAGE, MARKDOWN, JSON, BOT}; -int returnValue = 0; QString diceToMarkdown(QJsonArray array,bool withColor,bool allSameColor,bool allSameFaceCount ) @@ -208,103 +207,56 @@ int startDiceParsing(QStringList& cmds,QString& treeFile,bool withColor, EXPORTF int rt=0; for(QString cmd : cmds) { - if(parser.parseLine(cmd)) - { - parser.start(); - QList list; - bool homogeneous = true; - parser.getLastDiceResult(list,homogeneous); - bool allSameFaceCount, allSameColor; - auto array = DisplayToolBox::diceToJson(list,allSameFaceCount,allSameColor); - QString resultStr; - QString scalarText; - QString lastScalarText; - QString comment = parser.getComment(); - QString error = parser.humanReadableError(); - QStringList strLst; + Parsed parsed = parse(parser, cmd); - if(parser.hasIntegerResultNotInFirst()) - { - auto values = parser.getLastIntegerResults(); - for(auto val : values ) - { - strLst << QString::number(val); - } - scalarText = QString("%1").arg(strLst.join(',')); - lastScalarText = strLst.last(); - } - else if(!list.isEmpty()) + if(!parsed.isValid) { + rt = parsed.isValid; // FIXME: a new error can override a previous error + continue; + } + + bool allSameFaceCount, allSameColor; + auto array = DisplayToolBox::diceToJson(parsed.diceResult,allSameFaceCount,allSameColor); + + if(format == BOT) + { + if(allSameColor) { - auto values = parser.getSumOfDiceResult(); - for(auto val : values ) - { - strLst << QString::number(val); - } - scalarText = QString("%1").arg(strLst.join(',')); + format = MARKDOWN; } - - if(parser.hasStringResult()) + else { - bool ok; - QStringList allStringlist = parser.getAllStringResult(ok); - QString stringResult = allStringlist.join(" ; "); - stringResult.replace("%1",scalarText); - stringResult.replace("%3",lastScalarText); - - int i = 1; - for(auto value : strLst) - { - stringResult.replace(QStringLiteral("$%1").arg(i),value); - ++i; - } - - resultStr = stringResult; + format = IMAGE; } - if(format == BOT) + if(!parsed.error.isEmpty()) { - if(allSameColor) - { - format = MARKDOWN; - } - else - { - format = IMAGE; - } - if(!error.isEmpty()) - { - format = MARKDOWN; - } + format = MARKDOWN; } + } - switch(format) - { - case TERMINAL: - displayCommandResult(scalarText, resultStr, array, withColor, cmd, error, comment, allSameFaceCount, allSameColor); + switch(format) + { + case TERMINAL: + displayCommandResult(parsed.scalarText, parsed.result, array, withColor, cmd, parsed.error, parsed.comment, allSameFaceCount, allSameColor); break; - case SVG: - displaySVG(scalarText, resultStr, array, withColor, cmd, error, comment, allSameFaceCount, allSameColor); + case SVG: + displaySVG(parsed.scalarText, parsed.result, array, withColor, cmd, parsed.error, parsed.comment, allSameFaceCount, allSameColor); break; - case MARKDOWN: - displayMarkdown(scalarText, resultStr, array, withColor, cmd, error, comment, allSameFaceCount, allSameColor); + case MARKDOWN: + displayMarkdown(parsed.scalarText, parsed.result, array, withColor, cmd, parsed.error, parsed.comment, allSameFaceCount, allSameColor); break; - case JSON: - displayJSon(scalarText, resultStr, array, withColor, cmd, error, comment, allSameFaceCount, allSameColor); + case JSON: + displayJSon(parsed.scalarText, parsed.result, array, withColor, cmd, parsed.error, parsed.comment, allSameFaceCount, allSameColor); break; - case IMAGE: - displayImage(scalarText, resultStr, array, withColor, cmd, comment, allSameFaceCount, allSameColor); + case IMAGE: + displayImage(parsed.scalarText, parsed.result, array, withColor, cmd, parsed.comment, allSameFaceCount, allSameColor); break; - } - if(!treeFile.isEmpty()) - { - parser.writeDownDotTree(treeFile); - } - - if(!error.isEmpty()) - { - rt = 1; - } } - else + if(!treeFile.isEmpty()) + { + parser.writeDownDotTree(treeFile); + } + + if(!parsed.error.isEmpty()) { rt = 1; } @@ -404,10 +356,9 @@ int main(int argc, char *argv[]) { aliasstr = optionParser.value(alias); } - returnValue = startDiceParsing(cmdList,dotFileStr,colorb,format); if(optionParser.isSet(help)) { out << optionParser.helpText(); } - return returnValue; + return startDiceParsing(cmdList,dotFileStr,colorb,format); } diff --git a/die.cpp b/die.cpp index 417ab20b..9e26fde5 100644 --- a/die.cpp +++ b/die.cpp @@ -26,29 +26,18 @@ #include #include -Die::Die() - : m_hasValue(false),m_displayStatus(false),m_highlighted(true),m_base(1),m_color(""),m_op(Die::PLUS)//,m_mt(m_randomDevice) -{ -// uint seed = quintptr(this) + QDateTime::currentDateTime().toMSecsSinceEpoch(); - - // qsrand(seed); - - auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - m_rng = std::mt19937(quintptr(this)+seed); +std::mt19937 DefaultRandomGenerator::m_rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); +std::shared_ptr Die::m_rng = std::make_shared(); +void Die::setRandomGenerator(std::shared_ptr&& r) +{ + Die::m_rng = r; } -Die::Die(const Die& die) + +Die::Die(qint64 min, qint64 max) + : m_hasValue(false),m_displayStatus(false),m_highlighted(true),m_min(min),m_max(max),m_color(""),m_op(Die::PLUS) { - m_value = die.m_value; - m_rollResult = die.m_rollResult; - m_selected = die.m_selected; - m_hasValue = die.m_hasValue; - m_displayStatus = die.m_displayStatus; - m_maxValue = die.m_maxValue; - m_highlighted = die.m_highlighted; - m_base = die.m_base; - m_color = die.getColor(); - m_op = die.getOp(); + assert(min<=max); } void Die::setValue(qint64 r) @@ -134,26 +123,21 @@ void Die::replaceLastValue(qint64 value) void Die::roll(bool adding) { - if(m_maxValue!=0) + std::uniform_int_distribution dist(m_min,m_max); + qint64 value = dist(*m_rng); + if((adding)||(m_rollResult.isEmpty())) { - //quint64 value=(qrand()%m_faces)+m_base; - - std::uniform_int_distribution dist(m_base,m_maxValue); - qint64 value = dist(m_rng); - if((adding)||(m_rollResult.isEmpty())) - { - insertRollValue(value); - } - else - { - replaceLastValue(value); - } + insertRollValue(value); + } + else + { + replaceLastValue(value); } } quint64 Die::getFaces() const { - return abs(m_maxValue-m_base)+1; + return abs(m_max-m_min)+1; } qint64 Die::getLastRolledValue() { @@ -181,14 +165,6 @@ bool Die::isHighlighted() const { return m_highlighted; } -void Die::setBase(qint64 base) -{ - m_base = base; -} -qint64 Die::getBase() -{ - return m_base; -} QString Die::getColor() const { return m_color; @@ -201,12 +177,7 @@ void Die::setColor(const QString &color) qint64 Die::getMaxValue() const { - return m_maxValue; -} - -void Die::setMaxValue(const qint64 &maxValue) -{ - m_maxValue = maxValue; + return m_max; } Die::ArithmeticOperator Die::getOp() const diff --git a/die.h b/die.h index 10a6e8b8..1641c2fe 100644 --- a/die.h +++ b/die.h @@ -24,7 +24,52 @@ #include #include + +#include +#include #include + +struct RandomGenerator { + using result_type = uint_fast32_t; + virtual result_type operator() () = 0; + virtual result_type min () = 0; + virtual result_type max () = 0; +}; + +struct DefaultRandomGenerator: public RandomGenerator { +public: + result_type operator() () override { + return m_rng(); + } + result_type min () override { + return m_rng.min(); + } + result_type max () override { + return m_rng.max(); + } +private: + static std::mt19937 m_rng; +}; + +struct CustomRandomGenerator: public RandomGenerator { +public: + CustomRandomGenerator(result_type maxDiceValue, std::deque values): m_max{maxDiceValue}, m_values{move(values)} {} + result_type operator() () override { + auto val = m_values.front(); + m_values.pop_front(); + return val - 1; + } + result_type min () override { + return 0; + } + result_type max () override { + return m_max; + } +private: + result_type m_max; + std::deque m_values; +}; + /** * @brief The Die class implements all methods required from a die. You must set the Faces first, then you can roll it and roll it again, to add or replace the previous result. */ @@ -37,12 +82,10 @@ class Die enum ArithmeticOperator {PLUS,MINUS,DIVIDE,MULTIPLICATION}; /** * @brief Die + * @param max: Roll the die between min and max (inclusive) */ - Die(); - /** - * @brief Die - */ - Die(const Die& ); + Die(qint64 min, qint64 max); + /** * @brief setValue * @param r @@ -119,17 +162,10 @@ class Die */ bool isHighlighted() const; - /** - * @brief setBase - */ - void setBase(qint64); - qint64 getBase(); - QString getColor() const; void setColor(const QString &color); qint64 getMaxValue() const; - void setMaxValue(const qint64 &maxValue); Die::ArithmeticOperator getOp() const; void setOp(const Die::ArithmeticOperator &op); @@ -141,15 +177,20 @@ class Die bool m_hasValue; bool m_displayStatus; bool m_highlighted; - qint64 m_maxValue; - qint64 m_base; + qint64 m_min; + qint64 m_max; QString m_color; - Die::ArithmeticOperator m_op; - std::mt19937 m_rng; + static std::shared_ptr m_rng; +private: + static void setRandomGenerator(std::shared_ptr&& r); +public: + template + static void setRandomGenerator(RandomGenerator& r) { + setRandomGenerator(std::make_shared(r)); + } }; - #endif // DIE_H diff --git a/node/dicerollernode.cpp b/node/dicerollernode.cpp index 6691c38d..a69ad863 100644 --- a/node/dicerollernode.cpp +++ b/node/dicerollernode.cpp @@ -38,10 +38,8 @@ void DiceRollerNode::run(ExecutionNode* previous) for(quint64 i=0; i < m_diceCount ; ++i) { - Die* die = new Die(); + Die* die = new Die(m_min,m_max); die->setOp(m_operator); - die->setBase(m_min); - die->setMaxValue(m_max); die->roll(); if(m_unique) { diff --git a/node/explodedicenode.cpp b/node/explodedicenode.cpp index d640eaa5..f2d63d09 100644 --- a/node/explodedicenode.cpp +++ b/node/explodedicenode.cpp @@ -16,7 +16,7 @@ void ExplodeDiceNode::run(ExecutionNode* previous) { for(Die* die: previous_result->getResultList()) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*die; m_diceResult->insertResult(tmpdie); die->displayed(); diff --git a/node/filternode.cpp b/node/filternode.cpp index b2a2db9e..520373f2 100644 --- a/node/filternode.cpp +++ b/node/filternode.cpp @@ -36,7 +36,7 @@ void FilterNode::run(ExecutionNode* previous) { if(m_validator->hasValid(tmp,m_eachValue)) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*tmp; diceList2.append(tmpdie); tmp->displayed(); diff --git a/node/ifnode.cpp b/node/ifnode.cpp index 3a2cd958..f0f496df 100644 --- a/node/ifnode.cpp +++ b/node/ifnode.cpp @@ -139,10 +139,9 @@ void IfNode::run(ExecutionNode *previous) if(m_conditionType == OnScalar) { - Die* dice = new Die(); + Die* dice = new Die(1,value); dice->setValue(value); dice->insertRollValue(value); - dice->setMaxValue(value); if(m_validator->hasValid(dice,true,true)) { nextNode=m_true; diff --git a/node/jumpbackwardnode.cpp b/node/jumpbackwardnode.cpp index 2ca8874e..f0c74e23 100644 --- a/node/jumpbackwardnode.cpp +++ b/node/jumpbackwardnode.cpp @@ -132,7 +132,7 @@ void JumpBackwardNode::run(ExecutionNode* previous) { for(Die* die:diceResult->getResultList()) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*die; m_diceResult->insertResult(tmpdie); die->displayed(); diff --git a/node/keepdiceexecnode.cpp b/node/keepdiceexecnode.cpp index 89cda380..be89f18c 100644 --- a/node/keepdiceexecnode.cpp +++ b/node/keepdiceexecnode.cpp @@ -51,7 +51,7 @@ void KeepDiceExecNode::run(ExecutionNode* previous) for(Die* die : diceList3) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*die; diceList2.append(tmpdie); die->displayed(); diff --git a/node/listsetrollnode.cpp b/node/listsetrollnode.cpp index eca61686..b1ee5cb0 100644 --- a/node/listsetrollnode.cpp +++ b/node/listsetrollnode.cpp @@ -75,8 +75,7 @@ void ListSetRollNode::run(ExecutionNode* previous) QStringList rollResult; for(quint64 i=0; i < diceCount ; ++i) { - Die* die = new Die(); - computeFacesNumber(die); + Die* die = new Die(1,computeFacesNumber()); die->roll(); m_diceResult->insertResult(die); getValueFromDie(die,rollResult); @@ -102,11 +101,11 @@ void ListSetRollNode::setRangeList(QList& ranges) { m_rangeList = ranges; } -void ListSetRollNode::computeFacesNumber(Die* die) +qint64 ListSetRollNode::computeFacesNumber() { if(m_rangeList.isEmpty()) { - die->setMaxValue(m_values.size()); + return m_values.size(); } else { @@ -121,7 +120,7 @@ void ListSetRollNode::computeFacesNumber(Die* die) } ++i; } - die->setMaxValue(max); + return max; } } diff --git a/node/listsetrollnode.h b/node/listsetrollnode.h index 15d00e49..8c7983dc 100644 --- a/node/listsetrollnode.h +++ b/node/listsetrollnode.h @@ -48,7 +48,7 @@ class ListSetRollNode : public ExecutionNode private: void getValueFromDie(Die* die,QStringList& rollResult); - void computeFacesNumber(Die* die); + qint64 computeFacesNumber(); private: QStringList m_values; diff --git a/node/mergenode.cpp b/node/mergenode.cpp index f8088546..1d908f17 100644 --- a/node/mergenode.cpp +++ b/node/mergenode.cpp @@ -56,7 +56,7 @@ void MergeNode::run(ExecutionNode* previous) { if(!m_diceResult->getResultList().contains(die)&&(!die->hasBeenDisplayed())) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*die; die->displayed(); m_diceResult->getResultList().append(tmpdie); diff --git a/node/rerolldicenode.cpp b/node/rerolldicenode.cpp index 21e0dbff..b2863f2b 100644 --- a/node/rerolldicenode.cpp +++ b/node/rerolldicenode.cpp @@ -28,7 +28,7 @@ void RerollDiceNode::run(ExecutionNode* previous) { for(Die* die: previous_result->getResultList()) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(-1,-1); // yaaaaaarggl what does this block of code does? *tmpdie=*die; m_diceResult->insertResult(tmpdie); die->displayed(); diff --git a/node/sortresult.cpp b/node/sortresult.cpp index 5406eb60..e4ba85a7 100644 --- a/node/sortresult.cpp +++ b/node/sortresult.cpp @@ -49,8 +49,7 @@ void SortResultNode::run(ExecutionNode* node) for(int i = 0; idisplayed(); int j =0; diff --git a/node/splitnode.cpp b/node/splitnode.cpp index d44bad5a..292cac78 100644 --- a/node/splitnode.cpp +++ b/node/splitnode.cpp @@ -45,10 +45,8 @@ void SplitNode::run(ExecutionNode* previous) m_diceResult->setOperator(oldDie->getOp()); for(qint64 value : oldDie->getListValue()) { - Die* tmpdie = new Die(); + Die* tmpdie = new Die(*oldDie); tmpdie->insertRollValue(value); - tmpdie->setBase(oldDie->getBase()); - tmpdie->setMaxValue(oldDie->getMaxValue()); tmpdie->setValue(value); tmpdie->setOp(oldDie->getOp()); m_diceResult->insertResult(tmpdie); diff --git a/operationcondition.cpp b/operationcondition.cpp index 86c013a9..2266773a 100644 --- a/operationcondition.cpp +++ b/operationcondition.cpp @@ -63,8 +63,7 @@ qint64 OperationCondition::hasValid(Die* b,bool recursive,bool unhighlight) cons { case Modulo: { - Die die; - die.setMaxValue(b->getMaxValue()); + Die die(1,b->getMaxValue()); auto valueScalar = valueToScalar(); if(valueScalar==0) valueScalar = 1; diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt new file mode 100644 index 00000000..1570eb45 --- /dev/null +++ b/unittest/CMakeLists.txt @@ -0,0 +1,117 @@ +cmake_minimum_required(VERSION 2.8) + +option(UPDATE_TRANSLATIONS "update Translation" OFF) +MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}") + + +project(unittest) + + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +set(EXECUTABLE_OUTPUT_PATH bin/) + + +# Find the QtWidgets library +find_package(Qt5Core) +include_directories(${Qt5Core_INCLUDES} ../) +add_definitions(${Qt5Core_DEFINITIONS}) + +find_package(Qt5Gui) +include_directories(${Qt5Gui_INCLUDES} ../) +add_definitions(${Qt5Gui_DEFINITIONS}) + + +set(MODE "cli") + +ADD_DEFINITIONS( + -std=c++11 + -g + # Other flags +) + +set(dice_RESOURCES diceparser.qrc) +FIND_PACKAGE(Qt5LinguistTools) + + +IF(UPDATE_TRANSLATIONS) + MESSAGE( update Translation ) + FILE(GLOB_RECURSE translate_dice_SRCS ../*.cpp ../*.h) + SET(translate_SRCS ${translate_dice_SRCS}) + SET(dice_TS "${CMAKE_CURRENT_SOURCE_DIR}/i18n/dice_en.ts" "${CMAKE_CURRENT_SOURCE_DIR}/i18n/dice_fr.ts") +ELSE() + MESSAGE( NO updates for translations) + FILE(GLOB dice_TS "${CMAKE_CURRENT_SOURCE_DIR}/i18n/*.ts") +ENDIF(UPDATE_TRANSLATIONS) + +if(Qt5Core_FOUND) + + IF(UPDATE_TRANSLATIONS) + MESSAGE(status "find" ${dice_TS} ${translate_SRCS} ) + QT5_CREATE_TRANSLATION(dice_QM ${translate_SRCS} ${dice_TS}) + ELSE() + QT5_ADD_TRANSLATION(dice_QM ${dice_TS}) + ENDIF() + + QT5_ADD_RESOURCES(dice_RESOURCES_RCC ${dice_RESOURCES}) + + # guess plugins and libraries directory + set(QT_PLUGINS_DIR "${Qt5Core_DIR}/../../../plugins") + get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) + get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH) +endif() + +include_directories(../Catch2/single_include) +include_directories(../cli) + +SET( unittest_sources + unittest.cpp + + ../diceparser.cpp + ../range.cpp + ../booleancondition.cpp + ../validator.cpp + ../compositevalidator.cpp + ../operationcondition.cpp + ../die.cpp + ../parsingtoolbox.cpp + ../dicealias.cpp + ../result/result.cpp + ../result/scalarresult.cpp + ../result/stringresult.cpp + ../result/diceresult.cpp + ../node/countexecutenode.cpp + ../node/dicerollernode.cpp + ../node/executionnode.cpp + ../node/explodedicenode.cpp + ../node/helpnode.cpp + ../node/mergenode.cpp + ../node/jumpbackwardnode.cpp + ../node/keepdiceexecnode.cpp + ../node/listaliasnode.cpp + ../node/listsetrollnode.cpp + ../node/numbernode.cpp + ../node/parenthesesnode.cpp + ../node/paintnode.cpp + ../node/rerolldicenode.cpp + ../node/scalaroperatornode.cpp + ../node/sortresult.cpp + ../node/startingnode.cpp + ../node/filternode.cpp + ../node/stringnode.cpp + ../node/ifnode.cpp + ../node/splitnode.cpp + ../node/groupnode.cpp + ../highlightdice.cpp + ../node/variablenode.cpp +) + +add_executable(unittest ${unittest_sources} ${dice_QM} ) + +target_link_libraries(unittest ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) +INSTALL_TARGETS(/bin unittest) + +#qt5_use_modules() diff --git a/unittest/unittest.cpp b/unittest/unittest.cpp new file mode 100644 index 00000000..525da173 --- /dev/null +++ b/unittest/unittest.cpp @@ -0,0 +1,115 @@ +#include "die.h" +#include "displaytoolbox.h" + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +#include +#include +#include + +#include "QList" + +std::string roll(const char* command, RandomGenerator::result_type maxDiceValue, std::vector numbers) { + std::deque q; + q.assign(begin(numbers), end(numbers)); + auto gen = CustomRandomGenerator(maxDiceValue, q); + Die::setRandomGenerator(gen); + + DiceParser parser; + auto ret = parse(parser, command); + return ret.scalarText.toStdString(); +} + +TEST_CASE("keep", "operator") { + CHECK( roll("4d10k1", 10, {3,4,6,1}) == "6" ); + CHECK( roll("4d10k2", 10, {3,4,6,1}) == "4,6" ); + CHECK( roll("4d10k[>5]", 10, {3,4,6,1}) == "6" ); + CHECK( roll("4d10k[>2]", 10, {3,4,6,1}) == "3,4,6" ); + CHECK( roll("4d10k[>7]", 10, {3,4,6,1}) == "0" ); + + // k require a range + CHECK_THROWS( roll("4d10ks", 10, {3,4,6,1})); +} + +// deprecate Keep and explode + +TEST_CASE("keep lower", "operator") { + CHECK( roll("4d10kl1", 10, {3,4,6,1}) == "1" ); + CHECK( roll("4d10kl2", 10, {3,4,6,1}) == "1,3" ); +} + +TEST_CASE("sort", "operator") { + CHECK( roll("4d10s", 10, {3,4,6,1}) == "1,3,4,6" ); + CHECK( roll("4d10s", 10, {1,3,4,6}) == "1,3,4,6" ); +} + +TEST_CASE("count", "operator") { + CHECK( roll("4d10c[>5]", 10, {3,4,6,1}) == "1" ); + CHECK( roll("4d10c[>2]", 10, {3,4,6,1}) == "3" ); + + // interaction with other operators + CHECK( roll("4d10k[>7]c", 10, {3,4,6,1}) == "0" ); +} + +TEST_CASE("reroll", "operator") { + CHECK( roll("4d10r6", 10, {3,4,6,1,7}) == "3,4,7,1" ); + CHECK( roll("4d10r6", 10, {3,4,6,1,6}) == "3,4,6,1" ); // only one reroll + CHECK( roll("4d10r[>5]", 10, {3,4,6,1,7}) == "3,4,7,1" ); + CHECK( roll("4d10r[>2]", 10, {3,4,6,1,1,2,3}) == "1,2,6,3" ); +} + +TEST_CASE("reroll until", "operator") { + CHECK( roll("4d10R6", 10, {3,4,6,1,7}) == "3,4,7,1" ); + CHECK( roll("4d10R6", 10, {3,4,6,1,6,6,5}) == "3,4,5,1" ); + CHECK( roll("4d10R[>5]", 10, {3,4,6,1,7}) == "3,4,7,1" ); + CHECK( roll("4d10R[>2]", 10, {3,4,6,1,1,2,3}) == "1,2,6,3" ); + CHECK( roll("4d10R[>2]", 10, {3,4,6,1,1,6,2,6,6,3}) == "1,2,6,3" ); +} + +TEST_CASE("explode", "operator") { + CHECK( roll("4d10e", 10, {3,4,6,1}) == "3,4,6,1" ); + CHECK( roll("4d10e", 10, {3,4,6,10,1}) == "3,4,6,11" ); + CHECK( roll("4d10e6", 10, {3,4,6,1,5}) == "3,4,11,1" ); + //CHECK( roll("4d10e6", 10, {3,4,6,1,6,5}) == "3,4,17,1" ); + //CHECK( roll("4d10e[>5]", 10, {3,4,6,1,5}) == "3,4,11,1" ); + //CHECK( roll("4d10e[>2]", 10, {3,4,6,1,5,1,1,1}) == "9,5,7,1" ); +} + +TEST_CASE("backward jump", "operator") { + CHECK( roll("4d10c[>2]c+@c[>=4]", 10, {3,4,6,1}) == "5" ); + CHECK( roll("6d10kl4c[>2]c+@c[>=4]", 10, {3,4,6,1,9,8}) == "5" ); +} + +/* not testable +TEST_CASE("paint", "operator") { +} +*/ + +/* not testable +TEST_CASE("merge", "operator") { + CHECK( roll("4d10m1", 10, {3,4,6,1}) == "1" ); + CHECK( roll("4d10m2", 10, {3,4,6,1}) == "1,3" ); +} +*/ + +TEST_CASE("if", "operator") { + CHECK( roll("4d10i[>2]{\"x\"", 10, {3,4,6,1}) == "x" ); + CHECK( roll("4d10i2", 10, {3,4,6,1}) == "1,3" ); +} + +TEST_CASE("group", "operator") { + CHECK( roll("5d10g7", 10, {3,4,6,1,2}) == "2" ); + CHECK( roll("3d10g10", 10, {9,9,2}) == "1" ); + CHECK( roll("4d10g10", 10, {9,9,2,2}) == "2" ); + CHECK( roll("4d10g10", 10, {2,2,9,9}) == "2" ); + CHECK( roll("4d10g10", 10, {7,4,3,6}) == "2" ); + CHECK( roll("4d10g10", 10, {8,4,3,7}) == "2" ); + CHECK( roll("13d100g100", 100, {/* group A = 100 */ 50, 25, 25, /* group B = 100 */ 33, 34, 32, 1, /* group C = 100 */ 10, 11, 12, 13, 54}) == "3" ); + // CHECK( roll("12d100g100", 100, {/* group A = 103 */ 50, 28, 25, /* group B = 104 */ 33, 34, 32, 5, /* group C = 101 */ 9, 11, 12, 13, 56}) == "3" ); +} + +TEST_CASE("comment", "operator") { + CHECK( roll("4d10#comment", 10, {3,4,6,1}) == "comment\n3,4,6,1" ); +} +