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

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com>
0004     SPDX-FileCopyrightText: 2021-2023 Alexander Semke <alexander.semke@web.de>
0005 */
0006 
0007 #include "testoctave.h"
0008 
0009 #include "session.h"
0010 #include "backend.h"
0011 #include "expression.h"
0012 #include "result.h"
0013 #include "imageresult.h"
0014 #include "textresult.h"
0015 #include "helpresult.h"
0016 #include "epsresult.h"
0017 #include "completionobject.h"
0018 #include "syntaxhelpobject.h"
0019 #include "defaultvariablemodel.h"
0020 
0021 #include "octaveexpression.h"
0022 #include "settings.h"
0023 
0024 #include <QDebug>
0025 
0026 QString TestOctave::backendName()
0027 {
0028     return QLatin1String("octave");
0029 }
0030 
0031 void TestOctave::initTestCase() {
0032     QSKIP("Octave tests are failing on CI, deactivating for now until the problem is understood and fixed.");
0033 /*
0034     if (QStandardPaths::findExecutable(QLatin1String("octave")).isEmpty())
0035         QSKIP("Octave executable not found");
0036     BackendTest::initTestCase();
0037 */
0038 }
0039 
0040 void TestOctave::testSimpleCommand()
0041 {
0042     auto* e = evalExp( QLatin1String("2+2") );
0043 
0044     QVERIFY( e != nullptr );
0045     QVERIFY( e->result()!=nullptr );
0046 
0047     QCOMPARE( cleanOutput( e->result()->data().toString() ), QLatin1String("ans = 4") );
0048 }
0049 void TestOctave::testMultilineCommand()
0050 {
0051     auto* e = evalExp( QLatin1String("a = 2+2, b = 3+3") );
0052 
0053     QVERIFY( e != nullptr );
0054     QVERIFY( e->result()!=nullptr );
0055 
0056     QString result=e->result()->data().toString();
0057 
0058     QCOMPARE( cleanOutput(result ), QLatin1String("a = 4\nb = 6") );
0059 }
0060 
0061 void TestOctave::testCommandQueue()
0062 {
0063     auto* e1=session()->evaluateExpression(QLatin1String("0+1"));
0064     auto* e2=session()->evaluateExpression(QLatin1String("1+1"));
0065     auto* e3=evalExp(QLatin1String("1+2"));
0066 
0067     QVERIFY(e1!=nullptr);
0068     QVERIFY(e2!=nullptr);
0069     QVERIFY(e3!=nullptr);
0070 
0071     QVERIFY(e1->result());
0072     QVERIFY(e2->result());
0073     QVERIFY(e3->result());
0074 
0075     QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("ans = 1"));
0076     QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("ans = 2"));
0077     QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("ans = 3"));
0078 }
0079 
0080 void TestOctave::testVariableDefinition()
0081 {
0082     auto* e = evalExp(QLatin1String("testvar = 1"));
0083 
0084     QVERIFY(e != nullptr);
0085     QVERIFY(e->result() != nullptr);
0086     QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("testvar = 1"));
0087 }
0088 
0089 void TestOctave::testMatrixDefinition()
0090 {
0091     auto* e = evalExp(QLatin1String(
0092         "M = [1, 2, 3;"\
0093         "     4, 5, 6;"\
0094         "     7, 8, 9;]"
0095     ));
0096 
0097     QVERIFY(e != nullptr);
0098     QVERIFY(e->result() != nullptr);
0099     QCOMPARE(e->result()->type(), (int) Cantor::TextResult::Type);
0100 
0101     Cantor::TextResult* result = static_cast<Cantor::TextResult*>(e->result());
0102     QCOMPARE(result->plain(), QLatin1String(
0103         "M =\n"\
0104         "\n"
0105         "   1   2   3\n"\
0106         "   4   5   6\n"\
0107         "   7   8   9"
0108     ));
0109 }
0110 
0111 //Comments
0112 void TestOctave::testComment00()
0113 {
0114     auto* e = evalExp(QLatin1String("s = 1234 #This is comment"));
0115 
0116     QVERIFY(e != nullptr);
0117     QVERIFY(e->result() != nullptr);
0118     QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("s = 1234"));
0119 }
0120 
0121 /*!
0122  * simple command containing one single comment only
0123  */
0124 void TestOctave::testComment01()
0125 {
0126     auto* e = evalExp(QLatin1String("#Only comment"));
0127 
0128     QVERIFY(e != nullptr);
0129     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0130     QCOMPARE(e->results().size(), 0);
0131 }
0132 
0133 /*!
0134  * multi-line command with lines containing comments only
0135  */
0136 void TestOctave::testComment02()
0137 {
0138     auto* e = evalExp(QLatin1String(
0139         "# comment 1 \n"
0140         "5 + 5\n"
0141         "# comment 2\n"
0142         "a = 10"
0143     ));
0144 
0145     QVERIFY(e != nullptr);
0146     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0147     QVERIFY(e->result() != nullptr);
0148 
0149     Cantor::TextResult* result = static_cast<Cantor::TextResult*>(e->result());
0150     QVERIFY(result != nullptr);
0151     QCOMPARE(cleanOutput(result->plain()), QLatin1String(
0152         "ans = 10\n"
0153         "a = 10"
0154     ));
0155 }
0156 
0157 /*!
0158  * multi-line command with comments within the line containing also the actual expression
0159  * */
0160 void TestOctave::testComment03()
0161 {
0162     auto* e = evalExp(QLatin1String(
0163         "a = 2+4 \n"
0164         "6/2 % comment\n"
0165         "q = 'Str' # comment\n"
0166         "b = 4"
0167     ));
0168 
0169     QVERIFY(e != nullptr);
0170     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0171     QVERIFY(e->result() != nullptr);
0172 
0173     Cantor::TextResult* result = static_cast<Cantor::TextResult*>(e->result());
0174     QVERIFY(result != nullptr);
0175     QCOMPARE(cleanOutput(result->plain()), QLatin1String(
0176         "a = 6\n"
0177         "ans = 3\n"
0178         "q = Str\n"
0179         "b = 4"
0180     ));
0181 }
0182 
0183 /*!
0184  * multi-line command with comments within the line containing also the actual expression
0185  * and with ' used to transpose the vector.
0186  * */
0187 void TestOctave::testComment04()
0188 {
0189     QAbstractItemModel* model = session()->variableModel();
0190     QVERIFY(model != nullptr);
0191 
0192     evalExp(QLatin1String("clear();"));
0193 
0194     auto* e = evalExp(QLatin1String(
0195         "x=[1,2,3];\n"
0196         "xT=x';\n"
0197         "% comment\n"
0198         "y=1;"
0199     ));
0200 
0201     QVERIFY(e != nullptr);
0202 
0203    if(session()->status()==Cantor::Session::Running)
0204         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0205 
0206     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0207     QVERIFY(e->result() == nullptr); // empty result output
0208 
0209     QCOMPARE(model->rowCount(), 3);
0210     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x"));
0211     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("xT"));
0212     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("y"));
0213 }
0214 
0215 /*!
0216  * multi-line command with comments within the line containing also the actual expression
0217  * and with ' used to transpose the vector.
0218  * */
0219 void TestOctave::testComment05()
0220 {
0221     QAbstractItemModel* model = session()->variableModel();
0222     QVERIFY(model != nullptr);
0223 
0224     evalExp(QLatin1String("clear();"));
0225 
0226     auto* e = evalExp(QLatin1String(
0227         "x=[1,2,3];\n"
0228         "xT=x';\n"
0229         "# comment\n"
0230         "y=1;"
0231     ));
0232 
0233     QVERIFY(e != nullptr);
0234 
0235    if(session()->status()==Cantor::Session::Running)
0236         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0237 
0238     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0239     QVERIFY(e->result() == nullptr); // empty result output
0240 
0241     QCOMPARE(model->rowCount(), 3);
0242     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x"));
0243     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("xT"));
0244     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("y"));
0245 }
0246 
0247 /*!
0248  * multi-line command with comments within the line containing also the actual expression
0249  * and with ' used to transpose the vector.
0250  * */
0251 void TestOctave::testComment06()
0252 {
0253     QAbstractItemModel* model = session()->variableModel();
0254     QVERIFY(model != nullptr);
0255 
0256     evalExp(QLatin1String("clear();"));
0257 
0258     auto* e = evalExp(QLatin1String(
0259         "x=[1,2,3];\n"
0260         "xT=x'; % comment\n"
0261         "y=1;"
0262     ));
0263 
0264     QVERIFY(e != nullptr);
0265 
0266    if(session()->status()==Cantor::Session::Running)
0267         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0268 
0269     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0270     QVERIFY(e->result() == nullptr); // empty result output
0271 
0272     QCOMPARE(model->rowCount(), 3);
0273     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x"));
0274     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("xT"));
0275     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("y"));
0276 }
0277 
0278 /*!
0279  * multi-line command with comments within the line containing also the actual expression
0280  * and with ' used to transpose the vector.
0281  * */
0282 void TestOctave::testComment07()
0283 {
0284     QAbstractItemModel* model = session()->variableModel();
0285     QVERIFY(model != nullptr);
0286 
0287     evalExp(QLatin1String("clear();"));
0288 
0289     auto* e = evalExp(QLatin1String(
0290         "x=[1,2,3];\n"
0291         "xT=x'; # comment\n"
0292         "y=1;"
0293     ));
0294 
0295     QVERIFY(e != nullptr);
0296 
0297    if(session()->status()==Cantor::Session::Running)
0298         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0299 
0300     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0301     QVERIFY(e->result() == nullptr); // empty result output
0302 
0303     QCOMPARE(model->rowCount(), 3);
0304     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x"));
0305     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("xT"));
0306     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("y"));
0307 }
0308 
0309 /*!
0310  * multi-line command with comments within the line containing also the actual expression
0311  * and with ' used to transpose the vector and to quote a string
0312  * */
0313 void TestOctave::testComment08()
0314 {
0315     QAbstractItemModel* model = session()->variableModel();
0316     QVERIFY(model != nullptr);
0317 
0318     evalExp(QLatin1String("clear();"));
0319 
0320     auto* e = evalExp(QLatin1String(
0321         "x=[1,2,3];\n"
0322         "xT=x';\n"
0323         "% comment\n"
0324         "text = 'bla#blub';\n"
0325         "y=1;"
0326     ));
0327 
0328     QVERIFY(e != nullptr);
0329 
0330    if(session()->status()==Cantor::Session::Running)
0331         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0332 
0333     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0334     QVERIFY(e->result() == nullptr); // empty result output
0335 
0336     QCOMPARE(model->rowCount(), 4);
0337     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("text"));
0338     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("x"));
0339     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("xT"));
0340     QCOMPARE(model->index(3,0).data().toString(), QLatin1String("y"));
0341 }
0342 
0343 /*!
0344  * multi-line command with comments within the line containing also the actual expression
0345  * and with ' used to transpose the vector and to quote a string
0346  * */
0347 void TestOctave::testComment09()
0348 {
0349     QAbstractItemModel* model = session()->variableModel();
0350     QVERIFY(model != nullptr);
0351 
0352     evalExp(QLatin1String("clear();"));
0353 
0354     auto* e = evalExp(QLatin1String(
0355         "x=[1,2,3];\n"
0356         "xT=x';\n"
0357         "% comment\n"
0358         "text = 'bla%blub';\n"
0359         "y=1;"
0360     ));
0361 
0362     QVERIFY(e != nullptr);
0363 
0364    if(session()->status()==Cantor::Session::Running)
0365         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0366 
0367     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0368     QVERIFY(e->result() == nullptr); // empty result output
0369 
0370     QCOMPARE(model->rowCount(), 4);
0371     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("text"));
0372     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("x"));
0373     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("xT"));
0374     QCOMPARE(model->index(3,0).data().toString(), QLatin1String("y"));
0375 }
0376 
0377 void TestOctave::testCompletion()
0378 {
0379     Cantor::CompletionObject* help = session()->completionFor(QLatin1String("as"), 2);
0380     waitForSignal(help, SIGNAL(fetchingDone()));
0381 
0382     // Checks some completions for this request (but not all)
0383     // This correct for Octave 4.2.2 at least (and another versions, I think)
0384     const QStringList& completions = help->completions();
0385     QVERIFY(completions.contains(QLatin1String("asin")));
0386     QVERIFY(completions.contains(QLatin1String("asctime")));
0387     QVERIFY(completions.contains(QLatin1String("asec")));
0388     QVERIFY(completions.contains(QLatin1String("assert")));
0389 }
0390 
0391 void TestOctave::testVariablesCreatingFromCode()
0392 {
0393     QAbstractItemModel* model = session()->variableModel();
0394     QVERIFY(model != nullptr);
0395 
0396     evalExp(QLatin1String("clear();"));
0397 
0398     auto* e = evalExp(QLatin1String("a = 15; b = 'S';"));
0399     QVERIFY(e != nullptr);
0400 
0401     if(session()->status()==Cantor::Session::Running)
0402         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0403 
0404     QCOMPARE(2, model->rowCount());
0405 
0406     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a"));
0407     QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15"));
0408 
0409     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b"));
0410     QCOMPARE(model->index(1,1).data().toString(), QLatin1String("S"));
0411 }
0412 
0413 void TestOctave::testVariablesMultiRowValues()
0414 {
0415     QAbstractItemModel* model = session()->variableModel();
0416     QVERIFY(model != nullptr);
0417 
0418     evalExp(QLatin1String("clear();"));
0419 
0420     auto* e1 = evalExp(QLatin1String("rowVec = [1 2 3];"));
0421     QVERIFY(e1 != nullptr);
0422 
0423     auto* e2 = evalExp(QLatin1String("columnVec = [1; 2; 3];"));
0424     QVERIFY(e2 != nullptr);
0425 
0426     auto* e3 = evalExp(QLatin1String("mat = columnVec * rowVec;"));
0427     QVERIFY(e3 != nullptr);
0428 
0429     if(session()->status() == Cantor::Session::Running)
0430         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0431 
0432     QCOMPARE(3, model->rowCount());
0433 
0434     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("rowVec"));
0435     QCOMPARE(model->index(0,1).data().toString(), QLatin1String("1 2 3"));
0436 
0437     QCOMPARE(model->index(1,0).data().toString(), QLatin1String("columnVec"));
0438     QCOMPARE(model->index(1,1).data().toString(), QLatin1String("1; 2; 3"));
0439 
0440     QCOMPARE(model->index(2,0).data().toString(), QLatin1String("mat"));
0441     QCOMPARE(model->index(2,1).data().toString(), QLatin1String("1 2 3; 2 4 6; 3 6 9"));
0442 }
0443 
0444 void TestOctave::testVariableChangeSizeType()
0445 {
0446     QAbstractItemModel* model = session()->variableModel();
0447     QVERIFY(model != nullptr);
0448 
0449     evalExp(QLatin1String("clear();"));
0450 
0451     // create a text variable
0452     auto* e1 = evalExp(QLatin1String("test = \"abcd\";"));
0453     QVERIFY(e1 != nullptr);
0454 
0455     if(session()->status() == Cantor::Session::Running)
0456         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0457 
0458     QCOMPARE(1, model->rowCount());
0459     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test"));
0460     QCOMPARE(model->index(0,1).data().toString(), QLatin1String("abcd"));
0461     QCOMPARE(model->index(0,2).data().toString(), QLatin1String("string"));
0462     QCOMPARE(model->index(0,3).data().toInt(), 4); // 4 bytes for 4 characters
0463 
0464     // change from string to integer
0465     auto* e2 = evalExp(QLatin1String("test = 1;"));
0466     QVERIFY(e2 != nullptr);
0467 
0468     if(session()->status() == Cantor::Session::Running)
0469         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0470 
0471     QCOMPARE(1, model->rowCount());
0472     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test"));
0473     QCOMPARE(model->index(0,1).data().toString(), QLatin1String("1"));
0474     QCOMPARE(model->index(0,2).data().toString(), QLatin1String("scalar"));
0475     QCOMPARE(model->index(0,3).data().toInt(), 8); // 8 bytes for a scalar value
0476 }
0477 
0478 void TestOctave::testVariableCreatingFromCodeWithPlot()
0479 {
0480     QAbstractItemModel* model = session()->variableModel();
0481     QVERIFY(model != nullptr);
0482 
0483     evalExp(QLatin1String("clear();"));
0484 
0485     auto* e = evalExp(QLatin1String(
0486         "x = -10:0.1:10;\n"
0487         "plot (x, sin (x));\n"
0488         "xlabel (\"x\");\n"
0489         "ylabel (\"sin (x)\");\n"
0490         "title (\"Simple 2-D Plot\");\n"
0491     ));
0492     QVERIFY(e != nullptr);
0493 
0494     if(session()->status()==Cantor::Session::Running)
0495         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0496 
0497     QCOMPARE(e->status(), Cantor::Expression::Done);
0498     QVERIFY(e->result() != nullptr);
0499 
0500     bool eps = (OctaveExpression::plotExtensions[OctaveSettings::inlinePlotFormat()] == QLatin1String("eps"));
0501     int plotType = eps ? (int)Cantor::EpsResult::Type : (int)Cantor::ImageResult::Type;
0502     QVERIFY(e->result()->type() == plotType);
0503 
0504     QCOMPARE(1, model->rowCount());
0505     QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x"));
0506 }
0507 
0508 void TestOctave::testVariableCleanupAfterRestart()
0509 {
0510     Cantor::DefaultVariableModel* model = session()->variableModel();
0511     QVERIFY(model != nullptr);
0512 
0513     evalExp(QLatin1String("clear();"));
0514     auto* e = evalExp(QLatin1String("a = 15; b = 'S';"));
0515     QVERIFY(e != nullptr);
0516 
0517     if(session()->status()==Cantor::Session::Running)
0518         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0519 
0520     QCOMPARE(2, static_cast<QAbstractItemModel*>(model)->rowCount());
0521 
0522     session()->logout();
0523     session()->login();
0524 
0525     QCOMPARE(0, static_cast<QAbstractItemModel*>(model)->rowCount());
0526 }
0527 
0528 void TestOctave::testPlot()
0529 {
0530     auto* e = evalExp(QLatin1String("x=-10:0.1:10; plot(x,sin(x));"));
0531     QVERIFY(e != nullptr);
0532 
0533     if(session()->status() == Cantor::Session::Running)
0534         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0535 
0536     QVERIFY(e->result() != nullptr);
0537     QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type );
0538     QVERIFY(!e->result()->data().isNull());
0539     QVERIFY(e->errorMessage().isNull());
0540 }
0541 
0542 void TestOctave::testCantorPlot2d()
0543 {
0544     auto* e = evalExp( QLatin1String("cantor_plot2d('sin(x)', 'x', -10,10);") );
0545     QVERIFY(e != nullptr);
0546 
0547     if(session()->status() == Cantor::Session::Running)
0548         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0549 
0550     QVERIFY(e->result() != nullptr);
0551     QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type );
0552     QVERIFY(!e->result()->data().isNull());
0553     QVERIFY(e->errorMessage().isNull());
0554 }
0555 
0556 void TestOctave::testCantorPlot3d()
0557 {
0558     auto* e = evalExp( QLatin1String("cantor_plot3d('cos(x)*sin(y)', 'x', -10,10, 'y', -10,10);") );
0559     QVERIFY(e != nullptr);
0560 
0561     if(session()->status() == Cantor::Session::Running)
0562         waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
0563 
0564     QVERIFY(e->result() != nullptr);
0565     QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type );
0566     QVERIFY(!e->result()->data().isNull());
0567     QVERIFY(e->errorMessage().isNull());
0568 }
0569 
0570 void TestOctave::testInvalidSyntax()
0571 {
0572     auto* e = evalExp( QLatin1String("2+2*+.") );
0573 
0574     QVERIFY( e != nullptr );
0575     QCOMPARE( e->status(), Cantor::Expression::Error );
0576 }
0577 
0578 void TestOctave::testHelpRequest()
0579 {
0580     auto* e = evalExp(QLatin1String("help printf"));
0581 
0582     QVERIFY(e != nullptr);
0583     QCOMPARE(e->status(), Cantor::Expression::Status::Done);
0584     QVERIFY(e->result() != nullptr);
0585     QCOMPARE(e->result()->type(), (int)Cantor::HelpResult::Type);
0586     QString text = QString::fromLatin1("Print optional arguments under the control of the template").toHtmlEscaped();
0587     text.replace(QLatin1Char(' '), QLatin1String("&nbsp;"));
0588     QVERIFY(e->result()->toHtml().contains(text));
0589 }
0590 
0591 void TestOctave::testSyntaxHelp()
0592 {
0593     Cantor::SyntaxHelpObject* help = session()->syntaxHelpFor(QLatin1String("abs"));
0594     help->fetchSyntaxHelp();
0595     waitForSignal(help, SIGNAL(done()));
0596 
0597     QString text = QString::fromLatin1("Compute the magnitude").toHtmlEscaped();
0598     text.replace(QLatin1Char(' '), QLatin1String("&nbsp;"));
0599     QVERIFY(help->toHtml().contains(text));
0600 }
0601 
0602 void TestOctave::testLoginLogout()
0603 {
0604     // Logout from session twice and all must works fine
0605     session()->logout();
0606     session()->logout();
0607 
0608     // Login in session twice and all must works fine
0609     session()->login();
0610     session()->login();
0611 }
0612 
0613 void TestOctave::testRestartWhileRunning()
0614 {
0615     auto* e1=session()->evaluateExpression(QLatin1String("sleep(5)"));
0616 
0617     session()->logout();
0618     QCOMPARE(e1->status(), Cantor::Expression::Interrupted);
0619     session()->login();
0620 
0621     auto* e2=evalExp( QLatin1String("2+2") );
0622 
0623     QVERIFY(e2 != nullptr);
0624     QVERIFY(e2->result() != nullptr);
0625 
0626     QCOMPARE(cleanOutput(e2->result()->data().toString() ), QLatin1String("ans = 4"));
0627 }
0628 
0629 QTEST_MAIN( TestOctave )
0630