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("&nbsp;"));
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