File indexing completed on 2024-05-05 11:55:59

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2019 Sirgienko Nikita <warquark@gmail.com>
0004 */
0005 
0006 #include "testr.h"
0007 
0008 #include "session.h"
0009 #include "backend.h"
0010 #include "expression.h"
0011 #include "result.h"
0012 #include "imageresult.h"
0013 #include "helpresult.h"
0014 #include "syntaxhelpobject.h"
0015 #include "completionobject.h"
0016 #include "defaultvariablemodel.h"
0017 
0018 #include <QDebug>
0019 #include <QTextDocument>
0020 #include <QSyntaxHighlighter>
0021 
0022 QString TestR::backendName()
0023 {
0024     return QLatin1String("R");
0025 }
0026 
0027 void TestR::testSimpleCommand()
0028 {
0029     Cantor::Expression* e=evalExp( QLatin1String("2+2") );
0030 
0031     QVERIFY( e!=nullptr );
0032     QVERIFY( e->result()!=nullptr );
0033 
0034     QCOMPARE( cleanOutput( e->result()->data().toString() ), QLatin1String("[1] 4") );
0035 }
0036 
0037 void TestR::testMultilineCommand()
0038 {
0039     Cantor::Expression* e = evalExp(QLatin1String("print(2+2)\nprint(7*5)"));
0040 
0041     QVERIFY(e != nullptr);
0042     QVERIFY(e->result() != nullptr);
0043     QCOMPARE(cleanOutput( e->result()->data().toString() ), QLatin1String("[1] 4\n[1] 35"));
0044 }
0045 
0046 void TestR::testCodeWithComments()
0047 {
0048     Cantor::Expression* e=evalExp( QLatin1String("2+2 #comment") );
0049 
0050     QVERIFY( e!=nullptr );
0051     QVERIFY( e->result()!=nullptr );
0052 
0053     QCOMPARE( cleanOutput( e->result()->data().toString() ), QLatin1String("[1] 4") );
0054 }
0055 
0056 void TestR::testCommandQueue()
0057 {
0058     Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("0+1"));
0059     Cantor::Expression* e2=session()->evaluateExpression(QLatin1String("1+1"));
0060     Cantor::Expression* e3=evalExp(QLatin1String("1+2"));
0061 
0062     qDebug() << e1 << e1->command() << e1->status();
0063     qDebug() << e2 << e2->command() << e2->status();
0064     qDebug() << e3 << e3->command() << e3->status();
0065 
0066     QVERIFY(e1!=nullptr);
0067     QVERIFY(e2!=nullptr);
0068     QVERIFY(e3!=nullptr);
0069 
0070     QVERIFY(e1->result());
0071     QVERIFY(e2->result());
0072     QVERIFY(e3->result());
0073 
0074     QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("[1] 1"));
0075     QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("[1] 2"));
0076     QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("[1] 3"));
0077 }
0078 
0079 void TestR::testVariablesCreatingFromCode()
0080 {
0081     QAbstractItemModel* model = session()->variableModel();
0082     QVERIFY(model != nullptr);
0083 
0084     Cantor::Expression* e=evalExp(QLatin1String("a1 = 15; b1 = 'S'; d1 = c(1,2,3)"));
0085 
0086     QVERIFY(e!=nullptr);
0087     QVERIFY(e->result() != nullptr);
0088 
0089     while (session()->status() != Cantor::Session::Done)
0090         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0091 
0092     QCOMPARE(model->rowCount(), 3);
0093 
0094     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a1"));
0095     QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15"));
0096 
0097     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b1"));
0098     QCOMPARE(model->index(1,1).data().toString(), QLatin1String("S"));
0099 
0100     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("d1"));
0101     QCOMPARE(model->index(2,1).data().toString(), QLatin1String("1, 2, 3"));
0102 
0103     evalExp(QLatin1String("rm(a1,b1,d1)"));
0104 }
0105 
0106 void TestR::testVariableCleanupAfterRestart()
0107 {
0108     QAbstractItemModel* model = session()->variableModel();
0109     QVERIFY(model != nullptr);
0110 
0111     while(session()->status() != Cantor::Session::Done)
0112         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0113 
0114     QCOMPARE(model->rowCount(), 0);
0115 
0116     Cantor::Expression* e=evalExp(QLatin1String("h1 = 15; h2 = 'S';"));
0117     QVERIFY(e!=nullptr);
0118 
0119     while (session()->status() != Cantor::Session::Done)
0120         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0121 
0122     QCOMPARE(model->rowCount(), 2);
0123 
0124     session()->logout();
0125     session()->login();
0126 
0127     QCOMPARE(model->rowCount(), 0);
0128 }
0129 
0130 void TestR::testVariableDefinition()
0131 {
0132     Cantor::Expression* e = evalExp(QLatin1String("testvar <- \"value\"; testvar"));
0133 
0134     QVERIFY(e != nullptr);
0135     QCOMPARE(e->status(), Cantor::Expression::Done);
0136     QVERIFY(e->result() != nullptr);
0137     QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("[1] \"value\""));
0138 
0139     evalExp(QLatin1String("rm(testvar)"));
0140 }
0141 
0142 void TestR::testMatrixDefinition()
0143 {
0144     Cantor::Expression* e = evalExp(QLatin1String("matrix(1:9, nrow = 3, ncol = 3)"));
0145 
0146     QVERIFY(e != nullptr);
0147     QCOMPARE(e->status(), Cantor::Expression::Done);
0148     QVERIFY(e->result() != nullptr);
0149     QCOMPARE(e->result()->data().toString(), QLatin1String(
0150          "     [,1] [,2] [,3]\n"
0151          "[1,]    1    4    7\n"
0152          "[2,]    2    5    8\n"
0153          "[3,]    3    6    9"
0154     ));
0155 }
0156 
0157 void TestR::testCommentExpression()
0158 {
0159     Cantor::Expression* e = evalExp(QLatin1String("#only comment"));
0160 
0161     QVERIFY(e != nullptr);
0162     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0163     QCOMPARE(e->results().size(), 0);
0164 }
0165 
0166 void TestR::testMultilineCommandWithComment()
0167 {
0168     Cantor::Expression* e = evalExp(QLatin1String(
0169         "print(2+2) \n"
0170         "#comment in middle \n"
0171         "print(7*5)"));
0172 
0173     QVERIFY(e != nullptr);
0174     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0175     QVERIFY(e->result() != nullptr);
0176     QVERIFY(e->result()->data().toString() == QLatin1String("[1] 4\n[1] 35"));
0177 }
0178 
0179 void TestR::testInvalidSyntax()
0180 {
0181     Cantor::Expression* e=evalExp( QLatin1String("2+2*+?!<") );
0182 
0183     QVERIFY( e!=nullptr );
0184     QCOMPARE( e->status(), Cantor::Expression::Error );
0185 }
0186 
0187 void TestR::testCompletion()
0188 {
0189     // Actual completion needs session in Done state
0190     while (session()->status() != Cantor::Session::Done)
0191         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0192 
0193     Cantor::CompletionObject* help = session()->completionFor(QLatin1String("pi"), 2);
0194     waitForSignal(help, SIGNAL(fetchingDone()));
0195 
0196     // Checks all completions for this request
0197     // This correct for R 3.4.4
0198     const QStringList& completions = help->completions();
0199     qDebug() << completions;
0200     QCOMPARE(completions.size(), 5);
0201     QVERIFY(completions.contains(QLatin1String("pi")));
0202     QVERIFY(completions.contains(QLatin1String("pico")));
0203     QVERIFY(completions.contains(QLatin1String("pictex")));
0204     QVERIFY(completions.contains(QLatin1String("pie")));
0205     QVERIFY(completions.contains(QLatin1String("pipe")));
0206 }
0207 
0208 void TestR::testSimplePlot()
0209 {
0210     Cantor::Expression* e = evalExp(QLatin1String("plot(c(1, 4, 9, 16))"));
0211 
0212     QVERIFY(e != nullptr);
0213     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0214     QVERIFY(e->result() != nullptr);
0215     QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type);
0216 }
0217 
0218 void TestR::testComplexPlot()
0219 {
0220     const QLatin1String cmd(
0221         "# Define 2 vectors\n"
0222         "cars <- c(1, 3, 6, 4, 9)\n"
0223         "trucks <- c(2, 5, 4, 5, 12)\n"
0224         "\n"
0225         "# Calculate range from 0 to max value of cars and trucks\n"
0226         "g_range <- range(0, cars, trucks)\n"
0227         "\n"
0228         "# Graph autos using y axis that ranges from 0 to max \n"
0229         "# value in cars or trucks vector.  Turn off axes and \n"
0230         "# annotations (axis labels) so we can specify them ourself\n"
0231         "plot(cars, type=\"o\", col=\"blue\", ylim=g_range, \n"
0232         "   axes=FALSE, ann=FALSE)\n"
0233         "\n"
0234         "# Make x axis using Mon-Fri labels\n"
0235         "axis(1, at=1:5, lab=c(\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\"))\n"
0236         "\n"
0237         "# Make y axis with horizontal labels that display ticks at \n"
0238         "# every 4 marks. 4*0:g_range[2] is equivalent to c(0,4,8,12).\n"
0239         "axis(2, las=1, at=4*0:g_range[2])\n"
0240         "\n"
0241         "# Create box around plot\n"
0242         "box()\n"
0243         "\n"
0244         "# Graph trucks with red dashed line and square points\n"
0245         "lines(trucks, type=\"o\", pch=22, lty=2, col=\"red\")\n"
0246         "\n"
0247         "# Create a title with a red, bold/italic font\n"
0248         "title(main=\"Autos\", col.main=\"red\", font.main=4)\n"
0249         "\n"
0250         "# Label the x and y axes with dark green text\n"
0251         "title(xlab=\"Days\", col.lab=rgb(0,0.5,0))\n"
0252         "title(ylab=\"Total\", col.lab=rgb(0,0.5,0))\n"
0253         "\n"
0254         "# Create a legend at (1, g_range[2]) that is slightly smaller \n"
0255         "# (cex) and uses the same line colors and points used by \n"
0256         "# the actual plots \n"
0257         "legend(1, g_range[2], c(\"cars\",\"trucks\"), cex=0.8, \n"
0258         "   col=c(\"blue\",\"red\"), pch=21:22, lty=1:2);\n"
0259     );
0260 
0261     Cantor::Expression* e = evalExp(cmd);
0262 
0263     QVERIFY(e != nullptr);
0264     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0265     QVERIFY(e->result() != nullptr);
0266     QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type);
0267     evalExp(QLatin1String("rm(cars, trucks, g_range)"));
0268 }
0269 
0270 void TestR::testHelpRequest()
0271 {
0272     QSKIP("Skip, until moving R help to help panel insteadof scripteditor");
0273     Cantor::Expression* e = evalExp(QLatin1String("?print"));
0274 
0275     QVERIFY(e != nullptr);
0276     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0277     QVERIFY(e->result() != nullptr);
0278     QCOMPARE(e->result()->type(), (int)Cantor::HelpResult::Type);
0279 }
0280 
0281 void TestR::testSyntaxHelp()
0282 {
0283     QSKIP("Skip, until adding this feature to R backend");
0284     Cantor::SyntaxHelpObject* help = session()->syntaxHelpFor(QLatin1String("filter"));
0285     help->fetchSyntaxHelp();
0286     waitForSignal(help, SIGNAL(done()));
0287 
0288     QVERIFY(help->toHtml().contains(QLatin1String("filter")));
0289 }
0290 
0291 void TestR::testInformationRequest()
0292 {
0293     Cantor::Expression* e=session()->evaluateExpression(QLatin1String("readline(prompt=\"Enter number: \")"));
0294     QVERIFY(e!=nullptr);
0295     waitForSignal(e, SIGNAL(needsAdditionalInformation(QString)));
0296     e->addInformation(QLatin1String("12"));
0297 
0298     waitForSignal(e, SIGNAL(statusChanged(Cantor::Expression::Status)));
0299     QVERIFY(e->result()!=nullptr);
0300 
0301     QCOMPARE(e->result()->data().toString(), QLatin1String("[1] \"12\""));
0302 }
0303 
0304 void TestR::testLoginLogout()
0305 {
0306     // Logout from session twice and all must works fine
0307     session()->logout();
0308     session()->logout();
0309 
0310     // Login in session twice and all must works fine
0311     session()->login();
0312     session()->login();
0313 }
0314 
0315 void TestR::testRestartWhileRunning()
0316 {
0317     Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("Sys.sleep(5)"));
0318 
0319     session()->logout();
0320     QCOMPARE(e1->status(), Cantor::Expression::Interrupted);
0321     session()->login();
0322 
0323     Cantor::Expression* e2=evalExp( QLatin1String("2+2") );
0324 
0325     QVERIFY(e2 != nullptr);
0326     QVERIFY(e2->result() != nullptr);
0327 
0328     QCOMPARE(cleanOutput(e2->result()->data().toString() ), QLatin1String("[1] 4"));
0329 }
0330 
0331 QTEST_MAIN( TestR )
0332