File indexing completed on 2024-04-28 11:20:31
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com> 0004 */ 0005 #include "testjulia.h" 0006 0007 #include "session.h" 0008 #include "backend.h" 0009 #include "expression.h" 0010 #include "result.h" 0011 #include "textresult.h" 0012 #include "helpresult.h" 0013 #include "imageresult.h" 0014 #include "defaultvariablemodel.h" 0015 #include "completionobject.h" 0016 0017 #include "settings.h" 0018 0019 QString TestJulia::backendName() 0020 { 0021 return QLatin1String("julia"); 0022 } 0023 0024 void TestJulia::testOneLine() 0025 { 0026 Cantor::Expression *e = evalExp(QLatin1String("2 + 3")); 0027 QVERIFY(e != nullptr); 0028 QCOMPARE(e->status(), Cantor::Expression::Done); 0029 QVERIFY(e->result()->type() == Cantor::TextResult::Type); 0030 QCOMPARE(e->result()->data().toString(), QLatin1String("5")); 0031 QVERIFY(e->errorMessage().isEmpty()); 0032 } 0033 0034 void TestJulia::testOneLineWithPrint() 0035 { 0036 Cantor::Expression *e = evalExp(QLatin1String("print(2 + 3)")); 0037 QVERIFY(e != nullptr); 0038 QCOMPARE(e->status(), Cantor::Expression::Done); 0039 QVERIFY(e->result()->type() == Cantor::TextResult::Type); 0040 QCOMPARE(e->result()->data().toString(), QLatin1String("5")); 0041 QVERIFY(e->errorMessage().isEmpty()); 0042 } 0043 0044 void TestJulia::testException() 0045 { 0046 Cantor::Expression *e = evalExp(QLatin1String("sqrt(-1)")); 0047 QVERIFY(e != nullptr); 0048 QCOMPARE(e->status(), Cantor::Expression::Error); 0049 QCOMPARE(e->results().size(), 0); 0050 QVERIFY( 0051 !e->errorMessage().isEmpty() 0052 && e->errorMessage().contains(QLatin1String( 0053 "sqrt will only return a complex result if called with a " 0054 "complex argument." 0055 )) 0056 ); 0057 } 0058 0059 void TestJulia::testSyntaxError() 0060 { 0061 Cantor::Expression *e = evalExp(QLatin1String("for i = 1:10\nprint(i)")); 0062 QVERIFY(e != nullptr); 0063 QCOMPARE(e->status(), Cantor::Expression::Error); 0064 QCOMPARE(e->results().size(), 0); 0065 QVERIFY( 0066 !e->errorMessage().isEmpty() 0067 && e->errorMessage().contains(QLatin1String( 0068 "syntax: incomplete: \"for\" at none:1 requires end" 0069 )) 0070 ); 0071 } 0072 0073 void TestJulia::testMultilineCode() 0074 { 0075 Cantor::Expression *e = evalExp(QLatin1String( 0076 "q = 0; # comment\n" 0077 "# sdfsdf\n" 0078 "for i = 1:10\n" 0079 " global q\n" 0080 " q += i\n" 0081 "end\n" 0082 "print(q)" 0083 )); 0084 QVERIFY(e != nullptr); 0085 QCOMPARE(e->status(), Cantor::Expression::Done); 0086 QVERIFY(e->result()->type() == Cantor::TextResult::Type); 0087 QCOMPARE(e->result()->data().toString(), QLatin1String("55")); 0088 QVERIFY(e->errorMessage().isEmpty()); 0089 } 0090 0091 void TestJulia::testPartialResultOnException() 0092 { 0093 Cantor::Expression *e = evalExp(QLatin1String( 0094 "for i = 1:5\n" 0095 " print(i)\n" 0096 "end\n" 0097 "sqrt(-1)\n" 0098 )); 0099 QVERIFY(e != nullptr); 0100 QCOMPARE(e->status(), Cantor::Expression::Error); 0101 QVERIFY(e->result()->type() == Cantor::TextResult::Type); 0102 QCOMPARE(e->result()->data().toString(), QLatin1String("12345")); 0103 QVERIFY( 0104 !e->errorMessage().isEmpty() 0105 && e->errorMessage().contains(QLatin1String( 0106 "sqrt will only return a complex result if called with a " 0107 "complex argument." 0108 )) 0109 ); 0110 } 0111 0112 void TestJulia::testInlinePlot() 0113 { 0114 if (!JuliaSettings::integratePlots()) 0115 QSKIP("This test needs enabled plots integration in Julia settings", SkipSingle); 0116 0117 Cantor::Expression* testGR = evalExp(QLatin1String("import GR")); 0118 if (testGR->status() == Cantor::Expression::Error) 0119 QSKIP("GR module not found, so skip tests for graphic", SkipSingle); 0120 0121 Cantor::Expression *e = evalExp(QLatin1String( 0122 "import GR\n" 0123 "GR.plot([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], sin)" 0124 )); 0125 QVERIFY(e != nullptr); 0126 QCOMPARE(e->status(), Cantor::Expression::Done); 0127 QVERIFY(e->result()->type() == Cantor::ImageResult::Type); 0128 } 0129 0130 void TestJulia::testInlinePlotWithExceptionAndPartialResult() 0131 { 0132 if (!JuliaSettings::integratePlots()) 0133 QSKIP("This test needs enabled plots integration in Julia settings", SkipSingle); 0134 0135 Cantor::Expression* testGR = evalExp(QLatin1String("import GR")); 0136 if (testGR->status() == Cantor::Expression::Error) 0137 QSKIP("GR module not found, so skip tests for graphic", SkipSingle); 0138 0139 Cantor::Expression *e = evalExp(QLatin1String( 0140 "import GR\n" 0141 "print(\"gonna plot\")\n" 0142 "sqrt(-1)\n" 0143 "GR.plot([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], sin)\n" 0144 )); 0145 QVERIFY(e != nullptr); 0146 QCOMPARE(e->status(), Cantor::Expression::Error); 0147 QVERIFY(e->result()); 0148 QVERIFY(e->result()->type() == Cantor::TextResult::Type); 0149 QCOMPARE(e->result()->data().toString(), QLatin1String("gonna plot")); 0150 QVERIFY( 0151 !e->errorMessage().isEmpty() 0152 && e->errorMessage().contains(QLatin1String( 0153 "sqrt will only return a complex result if called with a " 0154 "complex argument." 0155 )) 0156 ); 0157 } 0158 0159 void TestJulia::testAddVariablesFromCode() 0160 { 0161 evalExp(QLatin1String("a = 0; b = 1; c = 2; d = 3\n")); 0162 0163 auto variableModel = session()->variableModel(); 0164 QStringList variableNames = 0165 QString::fromLatin1("a b c d").split(QLatin1String(" ")); 0166 0167 for (int i = 0; i < variableNames.size(); i++) { 0168 QModelIndexList matchedVariables = variableModel->match( 0169 variableModel->index(0, 0), 0170 Qt::DisplayRole, 0171 QVariant::fromValue(variableNames[i]), 0172 -1, 0173 Qt::MatchExactly 0174 ); 0175 QCOMPARE(matchedVariables.size(), 1); 0176 auto match = matchedVariables[0]; 0177 auto valueIndex = 0178 variableModel->index(match.row(), match.column() + 1); 0179 QVERIFY( 0180 valueIndex.data(Qt::DisplayRole) == 0181 QVariant::fromValue(QString::number(i)) 0182 ); 0183 } 0184 } 0185 0186 void TestJulia::testAddVariablesFromManager() 0187 { 0188 Cantor::DefaultVariableModel* variableModel = session()->variableModel(); 0189 QStringList variableNames = 0190 QString::fromLatin1("a b c d").split(QLatin1String(" ")); 0191 0192 for (int i = 0; i < variableNames.size(); i++) { 0193 variableModel->addVariable(variableNames[i], QString::number(i)); 0194 0195 QModelIndexList matchedVariables = variableModel->match( 0196 variableModel->index(0, 0), 0197 Qt::DisplayRole, 0198 QVariant::fromValue(variableNames[i]), 0199 -1, 0200 Qt::MatchExactly 0201 ); 0202 QCOMPARE(matchedVariables.size(), 1); 0203 auto match = matchedVariables[0]; 0204 auto valueIndex = 0205 variableModel->index(match.row(), match.column() + 1); 0206 QVERIFY( 0207 valueIndex.data(Qt::DisplayRole) == 0208 QVariant::fromValue(QString::number(i)) 0209 ); 0210 } 0211 } 0212 0213 void TestJulia::testRemoveVariables() 0214 { 0215 Cantor::DefaultVariableModel* variableModel = session()->variableModel(); 0216 QStringList variableNames = 0217 QString::fromLatin1("a b c d").split(QLatin1String(" ")); 0218 0219 for (int i = 0; i < variableNames.size(); i++) { 0220 variableModel->addVariable(variableNames[i], QString::number(i)); 0221 } 0222 for (int i = 0; i < variableNames.size(); i += 2) { 0223 variableModel->removeVariable(variableNames[i]); 0224 } 0225 0226 for (int i = 0; i < variableNames.size(); i++) { 0227 QModelIndexList matchedVariables = variableModel->match( 0228 variableModel->index(0, 0), 0229 Qt::DisplayRole, 0230 QVariant::fromValue(variableNames[i]), 0231 -1, 0232 Qt::MatchExactly 0233 ); 0234 if (i % 2 == 0) { 0235 QVERIFY(matchedVariables.isEmpty()); 0236 } else { 0237 QCOMPARE(matchedVariables.size(), 1); 0238 auto match = matchedVariables[0]; 0239 auto valueIndex = 0240 variableModel->index(match.row(), match.column() + 1); 0241 QVERIFY( 0242 valueIndex.data(Qt::DisplayRole) == 0243 QVariant::fromValue(QString::number(i)) 0244 ); 0245 } 0246 } 0247 } 0248 0249 void TestJulia::testAutoCompletion() 0250 { 0251 auto prefix = QLatin1String("ex"); 0252 auto completionObject = session()->completionFor(prefix); 0253 waitForSignal(completionObject, SIGNAL(fetchingDone())); 0254 auto completions = completionObject->completions(); 0255 0256 QStringList completionsToCheck; 0257 completionsToCheck << QLatin1String("exit"); 0258 completionsToCheck << QLatin1String("exponent"); 0259 completionsToCheck << QLatin1String("exp"); 0260 0261 for (auto completion : completionsToCheck) { 0262 QVERIFY(completions.contains(completion)); 0263 } 0264 0265 for (auto completion : completions) { 0266 QVERIFY(completion.startsWith(prefix)); 0267 } 0268 } 0269 0270 void TestJulia::testComplexAutocompletion() 0271 { 0272 auto prefix = QLatin1String("Base.Ma"); 0273 auto completionObject = session()->completionFor(prefix); 0274 waitForSignal(completionObject, SIGNAL(fetchingDone())); 0275 auto completions = completionObject->completions(); 0276 0277 QStringList completionsToCheck; 0278 completionsToCheck << QLatin1String("Base.MainInclude"); 0279 completionsToCheck << QLatin1String("Base.Math"); 0280 completionsToCheck << QLatin1String("Base.Matrix"); 0281 0282 for (auto completion : completionsToCheck) { 0283 QVERIFY(completions.contains(completion)); 0284 } 0285 0286 for (auto completion : completions) { 0287 QVERIFY(completion.startsWith(prefix)); 0288 } 0289 } 0290 0291 void TestJulia::testExpressionQueue() 0292 { 0293 Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("0+1")); 0294 Cantor::Expression* e2=session()->evaluateExpression(QLatin1String("1+1")); 0295 QVERIFY(session()->status() == Cantor::Session::Running); 0296 Cantor::Expression* e3=evalExp(QLatin1String("1+2")); 0297 0298 QVERIFY(e1!=nullptr); 0299 QVERIFY(e2!=nullptr); 0300 QVERIFY(e3!=nullptr); 0301 0302 QVERIFY(e1->result()); 0303 QVERIFY(e2->result()); 0304 QVERIFY(e3->result()); 0305 0306 QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("1")); 0307 QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("2")); 0308 QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("3")); 0309 } 0310 0311 void TestJulia::testHelpRequest() 0312 { 0313 QSKIP("Skip, until we add this functionality to Julia backend", SkipSingle); 0314 Cantor::Expression* e = evalExp(QLatin1String("?print")); 0315 0316 QVERIFY(e != nullptr); 0317 QCOMPARE(e->status(), Cantor::Expression::Status::Done); 0318 QVERIFY(e->result() != nullptr); 0319 QCOMPARE(e->result()->type(), (int)Cantor::HelpResult::Type); 0320 QString text = QString::fromLatin1("Write to io (or to the default output stream stdout").toHtmlEscaped(); 0321 text.replace(QLatin1Char(' '), QLatin1String(" ")); 0322 QVERIFY(e->result()->toHtml().contains(text)); 0323 } 0324 0325 void TestJulia::testLoginLogout() 0326 { 0327 // Logout from session twice and all must works fine 0328 session()->logout(); 0329 session()->logout(); 0330 0331 // Login in session twice and all must works fine 0332 session()->login(); 0333 session()->login(); 0334 } 0335 0336 void TestJulia::testRestartWhileRunning() 0337 { 0338 Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("sleep(5)")); 0339 0340 session()->logout(); 0341 QCOMPARE(session()->status(), Cantor::Session::Disable); 0342 QCOMPARE(e1->status(), Cantor::Expression::Interrupted); 0343 session()->login(); 0344 0345 Cantor::Expression* e2=evalExp( QLatin1String("2+2") ); 0346 0347 QVERIFY(e2 != nullptr); 0348 QVERIFY(e2->result() != nullptr); 0349 0350 QCOMPARE(cleanOutput(e2->result()->data().toString() ), QLatin1String("4")); 0351 } 0352 0353 QTEST_MAIN(TestJulia) 0354