File indexing completed on 2024-05-12 05:52:21

0001 /*
0002     SPDX-FileCopyrightText: 2023 Gabriel Barrantes <gabriel.barrantes.dev@outlook.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "../kcalc_core.h"
0007 #include "../kcalc_parser.h"
0008 
0009 #include <QQueue>
0010 #include <QTest>
0011 
0012 #define QS(s) QStringLiteral(s)
0013 
0014 /**
0015  * The main test class.
0016  */
0017 class KCalcParserCoreTest : public QObject
0018 {
0019     Q_OBJECT
0020 
0021 private Q_SLOTS:
0022     void initTestCase();
0023 
0024     void testParserCore_data();
0025     void testParserCore();
0026 
0027     void testParserError_data();
0028     void testParserError();
0029 
0030     void testCalculationError_data();
0031     void testCalculationError();
0032 
0033     void cleanupTestCase();
0034 
0035 private:
0036     QQueue<KCalcToken> token_Queue_;
0037     CalcEngine core;
0038     KCalcParser parser;
0039 
0040     int input_error_index_;
0041 };
0042 
0043 void KCalcParserCoreTest::initTestCase()
0044 {
0045     // load constants into parser
0046 }
0047 
0048 void KCalcParserCoreTest::testParserCore_data()
0049 {
0050     QTest::addColumn<QString>("input");
0051     QTest::addColumn<int>("base");
0052     QTest::addColumn<QString>("expectedResult");
0053 
0054     // Simple operations //
0055     QTest::newRow("addition test") << QS("1+2") << 10 << QS("3");
0056     QTest::newRow("subtaction test") << QS("7-2") << 10 << QS("5");
0057     QTest::newRow("multiplication test 1") << QS("50*2") << 10 << QS("100");
0058     QTest::newRow("multiplication test 2") << QS("50×2") << 10 << QS("100");
0059     QTest::newRow("division test") << QS("50÷12") << 10 << QS("25/6");
0060     QTest::newRow("div test using slash") << QS("1/2") << 10 << QS("1/2");
0061     QTest::newRow("percentage") << QS("10×50%") << 10 << QS("5");
0062     QTest::newRow("inverse test 1") << QS("10⁻¹") << 10 << QS("1/10");
0063     QTest::newRow("inverse test 2") << QS("10⁻¹⁻¹") << 10 << QS("10");
0064     QTest::newRow("sqrt test 2") << QS("√5") << 10 << QS("2.2360679775");
0065     QTest::newRow("square test 1") << QS("2.2360679775²") << 10 << QS("5");
0066     QTest::newRow("square test 2") << QS("2.2360679775²²") << 10 << QS("25");
0067     QTest::newRow("power test 1") << QS("5^5") << 10 << QS("3125");
0068     QTest::newRow("power test 2") << QS("2^2^2") << 10 << QS("16");
0069     QTest::newRow("sin test") << QS("sin(90)") << 10 << QS("1");
0070     QTest::newRow("sin test2") << QS("sin(45)") << 10 << QS("0.707106781187");
0071     QTest::newRow("cos test") << QS("cos(90)") << 10 << QS("0");
0072     QTest::newRow("tan test") << QS("tan(45)") << 10 << QS("1");
0073     QTest::newRow("asin test") << QS("asin(0.707106781187)") << 10 << QS("45");
0074     QTest::newRow("asin test") << QS("asin(1)") << 10 << QS("90");
0075     QTest::newRow("acos test") << QS("acos(0)") << 10 << QS("90");
0076     QTest::newRow("atan test") << QS("atan(1)") << 10 << QS("45");
0077 
0078     QTest::newRow("sinh test 1") << QS("sinh(1)") << 10 << QS("1.17520119364");
0079     QTest::newRow("cosh test 1") << QS("cosh(1)") << 10 << QS("1.54308063482");
0080     QTest::newRow("tanh test 1") << QS("tanh(2000.90645)") << 10 << QS("1");
0081     QTest::newRow("tanh test 2") << QS("tanh(0.5)") << 10 << QS("0.46211715726");
0082     QTest::newRow("asinh test 1") << QS("asinh(1.17520119364)") << 10 << QS("0.999999999998");
0083     QTest::newRow("acosh test 2") << QS("acosh(1.54308063481)") << 10 << QS("0.999999999996");
0084     QTest::newRow("atanh test 3") << QS("atanh(0.46211715726)") << 10 << QS("0.5");
0085 
0086     QTest::newRow("ln test 1") << QS("ln(2.71)") << 10 << QS("0.996948634892");
0087     QTest::newRow("ln test 2") << QS("ln(2.718281828460)") << 10 << QS("1");
0088     QTest::newRow("log test 1") << QS("log(10)") << 10 << QS("1");
0089     QTest::newRow("log test 2") << QS("log(100)") << 10 << QS("2");
0090     QTest::newRow("log test 3") << QS("log(1000)") << 10 << QS("3");
0091 
0092     QTest::newRow("exp test") << QS("exp(1)") << 10 << QS("2.71828182846");
0093     QTest::newRow("exp10 test") << QS("5⏨2") << 10 << QS("500");
0094 
0095     QTest::newRow("- test 1") << QS("-590") << 10 << QS("-590");
0096     QTest::newRow("- test 2") << QS("-1cos(45)") << 10 << QS("-0.707106781187");
0097     QTest::newRow("- test 3") << QS("-cos(45)") << 10 << QS("-0.707106781187");
0098     QTest::newRow("- test 4") << QS("-20-cos(45)") << 10 << QS("-20.7071067812");
0099     QTest::newRow("- test 5") << QS("-1×-1") << 10 << QS("1");
0100     QTest::newRow("- test 6") << QS("-1÷-1") << 10 << QS("1");
0101     QTest::newRow("- test 7") << QS("-7 mod 5") << 10 << QS("-2");
0102     QTest::newRow("- test 8") << QS("5×--5") << 10 << QS("25");
0103     QTest::newRow("- test 9") << QS("5×---5") << 10 << QS("-25");
0104     QTest::newRow("- test 10") << QS("7 mod -5") << 10 << QS("2");
0105     QTest::newRow("-- test 1") << QS("--5") << 10 << QS("5");
0106     QTest::newRow("-- test 2") << QS("--5+6") << 10 << QS("11");
0107     QTest::newRow("-- test 3") << QS("--4--9+100") << 10 << QS("113");
0108     QTest::newRow("-- test 4") << QS("--4--9-100") << 10 << QS("-87");
0109     QTest::newRow("-- test 5") << QS("--4--9×100") << 10 << QS("904");
0110     QTest::newRow("-- test 6") << QS("--4×45--9×100") << 10 << QS("1080");
0111     QTest::newRow("-- test 7") << QS("--4×45--9×100") << 10 << QS("1080");
0112 
0113     QTest::newRow("+ test 1") << QS("+7") << 10 << QS("7");
0114     QTest::newRow("++ test 1") << QS("++7") << 10 << QS("7");
0115     QTest::newRow("+++ test 1") << QS("+++7") << 10 << QS("7");
0116     QTest::newRow("+++ test 2") << QS("+++7-7²") << 10 << QS("-42");
0117     QTest::newRow("+++ test 3") << QS("+++7--7²") << 10 << QS("56");
0118     QTest::newRow("+ test 2") << QS("+7--1") << 10 << QS("8");
0119     QTest::newRow("+ test 3") << QS("+7--20") << 10 << QS("27");
0120     QTest::newRow("+ test 4") << QS("+7-20") << 10 << QS("-13");
0121     QTest::newRow("+ test 5") << QS("+7-20+30") << 10 << QS("17");
0122     QTest::newRow("+ test 6") << QS("+7----1") << 10 << QS("8");
0123 
0124     QTest::newRow("& test 1") << QS("0b10 & 0b100") << 10 << QS("0");
0125     QTest::newRow("& test 2") << QS("0b10 & 0b11 & 0b110") << 10 << QS("2");
0126     QTest::newRow("| test 1") << QS("0b10 | 0b1") << 10 << QS("3");
0127     QTest::newRow("~ test 1") << QS("~~0b10") << 10 << QS("2");
0128     QTest::newRow("<< test 1") << QS("0b10<<1") << 10 << QS("4");
0129     QTest::newRow(">> test 1") << QS("0b10>>1") << 10 << QS("1");
0130 
0131     QTest::newRow("mod test 1") << QS("15 mod 10") << 10 << QS("5");
0132     QTest::newRow("mod test 2") << QS("100 mod 101 mod 90 mod 8") << 10 << QS("2");
0133     QTest::newRow("div test 1") << QS("15 div 10") << 10 << QS("1");
0134     QTest::newRow("div test 2") << QS("25 div 10") << 10 << QS("2");
0135     QTest::newRow("factorial test 1") << QS("5!") << 10 << QS("120");
0136 
0137     QTest::newRow("0x test 1") << QS("0xA+10") << 10 << QS("20");
0138     QTest::newRow("0b test 1") << QS("0b100+1") << 10 << QS("5");
0139     QTest::newRow("0 test 1") << QS("010+8") << 10 << QS("16");
0140     QTest::newRow("base test 1") << QS("0xFF & 0b1111 & 07") << 10 << QS("7");
0141     // parenthesis input
0142     QTest::newRow("parenthesis test 1") << QS("(√5)²") << 10 << QS("5");
0143     QTest::newRow("parenthesis test 2") << QS("(1+3)") << 10 << QS("4");
0144     QTest::newRow("parenthesis test 3") << QS("(1+3)²") << 10 << QS("16");
0145     QTest::newRow("parenthesis test 4") << QS("5×(1+3)") << 10 << QS("20");
0146     QTest::newRow("parenthesis test 5") << QS("5×(1+3)+2") << 10 << QS("22");
0147     QTest::newRow("parenthesis test 6") << QS("5×(1+3)-2") << 10 << QS("18");
0148     QTest::newRow("parenthesis test 7") << QS("2(5×(1+3)-2)") << 10 << QS("36");
0149     QTest::newRow("parenthesis test 8") << QS("5×(1+3)²") << 10 << QS("80");
0150     QTest::newRow("parenthesis test 9") << QS("cos(1+2+87)") << 10 << QS("0");
0151     QTest::newRow("parenthesis test 10") << QS("(1)(2)(3)") << 10 << QS("6");
0152     QTest::newRow("parenthesis test 11") << QS("(((((-1)))))") << 10 << QS("-1");
0153     QTest::newRow("parenthesis test 12") << QS("(((((-1)))))+2") << 10 << QS("1");
0154     QTest::newRow("parenthesis test 13") << QS("√(1+2+3!+91)") << 10 << QS("10");
0155     QTest::newRow("parenthesis test 14") << QS("sin(sin(sin(sin(0))))") << 10 << QS("0");
0156     QTest::newRow("parenthesis test 15") << QS("ln(exp(1))") << 10 << QS("1");
0157     QTest::newRow("parenthesis test 16") << QS("ln(exp(100.45))") << 10 << QS("100.45");
0158 }
0159 
0160 void KCalcParserCoreTest::testParserCore()
0161 {
0162     QFETCH(QString, input);
0163     QFETCH(int, base);
0164     QFETCH(QString, expectedResult);
0165 
0166     int parsing_result, calculation_result, errorIndex;
0167     parsing_result = parser.stringToTokenQueue(input, base, token_Queue_, errorIndex);
0168     QCOMPARE(parsing_result, 0); // successful parsing
0169 
0170     calculation_result = core.calculate(token_Queue_, errorIndex);
0171 
0172     QCOMPARE(calculation_result, 0); // successful calculation
0173 
0174     QString coreResult = core.getResult().toQString(12, -1);
0175     coreResult.replace(KNumber::decimalSeparator().at(0), QLatin1Char('.'));
0176     coreResult.replace(QLatin1Char(','), QLatin1Char('.'));
0177     QCOMPARE(coreResult, expectedResult);
0178 }
0179 
0180 void KCalcParserCoreTest::testParserError_data()
0181 {
0182     QTest::addColumn<QString>("input");
0183     QTest::addColumn<int>("expectedErrorIndex");
0184 
0185     // invalid characters in input //
0186     QTest::newRow("parsing error test 1") << QS("1+s2") << 2;
0187     QTest::newRow("parsing error test 2") << QS("126d") << 3;
0188     QTest::newRow("parsing error test 3") << QS("1+1+1+1+1+m") << 10;
0189 }
0190 
0191 void KCalcParserCoreTest::testParserError()
0192 {
0193     QFETCH(QString, input);
0194     QFETCH(int, expectedErrorIndex);
0195 
0196     int parsing_result, errorIndex;
0197     parsing_result = parser.stringToTokenQueue(input, 10, token_Queue_, errorIndex);
0198 
0199     QCOMPARE_NE(parsing_result, 0); // fail on parsing
0200     QCOMPARE(errorIndex, expectedErrorIndex); // index indicating error position
0201 }
0202 
0203 void KCalcParserCoreTest::testCalculationError_data()
0204 {
0205     QTest::addColumn<QString>("input");
0206     QTest::addColumn<int>("expectedErrorIndex");
0207 
0208     // invalid input //
0209     QTest::newRow("calculation error test 1") << QS("1××5") << 3;
0210     QTest::newRow("calculation error test 2") << QS("1+8×!+2") << 5;
0211     QTest::newRow("calculation error test 3") << QS("tan(%)+2+3") << 5;
0212 }
0213 
0214 void KCalcParserCoreTest::testCalculationError()
0215 {
0216     QFETCH(QString, input);
0217     QFETCH(int, expectedErrorIndex);
0218 
0219     int parsing_result, calculation_result, errorIndex;
0220     parsing_result = parser.stringToTokenQueue(input, 10, token_Queue_, errorIndex);
0221 
0222     QCOMPARE(parsing_result, 0); // successful parsing
0223 
0224     calculation_result = core.calculate(token_Queue_, errorIndex);
0225 
0226     QCOMPARE_NE(calculation_result, 0); // fail on calculation
0227 
0228     errorIndex = token_Queue_.at(errorIndex).getStringIndex();
0229 
0230     QCOMPARE(errorIndex, expectedErrorIndex); // index indicating error position
0231 }
0232 
0233 void KCalcParserCoreTest::cleanupTestCase()
0234 {
0235 }
0236 
0237 QTEST_GUILESS_MAIN(KCalcParserCoreTest)
0238 
0239 #include "kcalc_parser_core_test.moc"