File indexing completed on 2023-05-30 09:03:11
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com> 0004 SPDX-FileCopyrightText: 2018-2023 by Alexander Semke (alexander.semke@web.de) 0005 */ 0006 0007 #include "testmaxima.h" 0008 0009 #include "session.h" 0010 #include "backend.h" 0011 #include "expression.h" 0012 #include "result.h" 0013 #include "textresult.h" 0014 #include "imageresult.h" 0015 #include "epsresult.h" 0016 #include "syntaxhelpobject.h" 0017 #include "completionobject.h" 0018 #include "defaultvariablemodel.h" 0019 0020 #include <config-cantorlib.h> 0021 0022 QString TestMaxima::backendName() 0023 { 0024 return QLatin1String("maxima"); 0025 } 0026 0027 void TestMaxima::initTestCase() { 0028 if (QStandardPaths::findExecutable(QLatin1String("maxima")).isEmpty()) 0029 QSKIP("Maxima executable not found"); 0030 BackendTest::initTestCase(); 0031 } 0032 0033 void TestMaxima::testSimpleCommand() 0034 { 0035 auto* e = evalExp( QLatin1String("2+2") ); 0036 0037 QVERIFY( e!=nullptr ); 0038 QVERIFY( e->result()!=nullptr ); 0039 0040 QCOMPARE( cleanOutput( e->result()->data().toString() ), QLatin1String("4") ); 0041 } 0042 0043 void TestMaxima::testMultilineCommand01() 0044 { 0045 auto* e = evalExp( QLatin1String("2+2;3+3") ); 0046 0047 QVERIFY(e != nullptr); 0048 QVERIFY(e->results().size() == 2); 0049 0050 QCOMPARE(e->results().at(0)->data().toString(), QLatin1String("4")); 0051 QCOMPARE(e->results().at(1)->data().toString(), QLatin1String("6")); 0052 } 0053 0054 /* 0055 * test multiple variable assignments, separated by ';'. 0056 */ 0057 void TestMaxima::testMultilineCommand02() 0058 { 0059 auto* e = evalExp( QLatin1String( 0060 "var1:1;\n" 0061 "var2:2;\n" 0062 "var3:3;" 0063 ) ); 0064 0065 QVERIFY(e != nullptr); 0066 QVERIFY(e->results().size() == 3); 0067 0068 QCOMPARE(e->results().at(0)->data().toString(), QLatin1String("1")); 0069 QCOMPARE(e->results().at(1)->data().toString(), QLatin1String("2")); 0070 QCOMPARE(e->results().at(2)->data().toString(), QLatin1String("3")); 0071 0072 e = evalExp(QLatin1String("kill(var1, var2, var3)")); 0073 QVERIFY(e != nullptr); 0074 } 0075 0076 /* 0077 * test multiple variable assignments with a supressed output. 0078 */ 0079 void TestMaxima::testMultilineCommand03() 0080 { 0081 auto* e = evalExp( QLatin1String( 0082 "var1:1$\n" 0083 "var2:2;\n" 0084 ) ); 0085 0086 QVERIFY(e != nullptr); 0087 QVERIFY(e->results().size() == 1); 0088 QCOMPARE(e->results().at(0)->data().toString(), QLatin1String("2")); 0089 0090 e = evalExp(QLatin1String("kill(var1, var2)")); 0091 QVERIFY(e != nullptr); 0092 } 0093 0094 //WARNING: for this test to work, Integration of Plots must be enabled 0095 //and CantorLib must be compiled with EPS-support 0096 void TestMaxima::testPlot() 0097 { 0098 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0099 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0100 0101 auto* e = evalExp( QLatin1String("plot2d(sin(x), [x, -10,10])") ); 0102 0103 QVERIFY( e!=nullptr ); 0104 QVERIFY( e->result()!=nullptr ); 0105 0106 if(session()->status() == Cantor::Session::Running) 0107 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0108 0109 QCOMPARE( e->result()->type(), (int)Cantor::ImageResult::Type ); 0110 QVERIFY( !e->result()->data().isNull() ); 0111 QVERIFY( e->errorMessage().isNull() ); 0112 } 0113 0114 void TestMaxima::testPlotMultiline() 0115 { 0116 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0117 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0118 0119 auto* e = evalExp(QLatin1String( 0120 "plot2d (x^2-y^3+3*y=2,\n" 0121 "[x,-2.5,2.5],\n" 0122 "[y,-2.5,2.5])" 0123 )); 0124 0125 QVERIFY(e != nullptr); 0126 QVERIFY(e->result() != nullptr); 0127 0128 if(session()->status() == Cantor::Session::Running) 0129 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0130 0131 QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type); 0132 QVERIFY(!e->result()->data().isNull()); 0133 QVERIFY(e->errorMessage().isNull()); 0134 } 0135 0136 void TestMaxima::testPlotWithWhitespaces() 0137 { 0138 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0139 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0140 0141 auto* e = evalExp(QLatin1String( 0142 "\n" 0143 "plot2d (x^2-y^3+3*y=2,\n" 0144 "[x,-2.5,2.5],\n" 0145 "[y,-2.5,2.5])" 0146 "\n" 0147 )); 0148 0149 QVERIFY(e != nullptr); 0150 QVERIFY(e->result() != nullptr); 0151 0152 if(session()->status() == Cantor::Session::Running) 0153 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0154 0155 QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type); 0156 QVERIFY(!e->result()->data().isNull()); 0157 QVERIFY(e->errorMessage().isNull()); 0158 } 0159 0160 void TestMaxima::testPlotWithAnotherTextResults() 0161 { 0162 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0163 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0164 0165 auto* e = evalExp( QLatin1String( 0166 "2*2; \n" 0167 "plot2d(sin(x), [x, -10,10]); \n" 0168 "4*4;" 0169 )); 0170 0171 if(session()->status() == Cantor::Session::Running) 0172 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0173 0174 QVERIFY( e!=nullptr ); 0175 QVERIFY( e->errorMessage().isNull() ); 0176 QCOMPARE(e->results().size(), 3); 0177 0178 QCOMPARE(e->results().at(0)->data().toString(), QLatin1String("4")); 0179 0180 QCOMPARE( e->results().at(1)->type(), (int)Cantor::ImageResult::Type ); 0181 QVERIFY( !e->results().at(1)->data().isNull() ); 0182 0183 QCOMPARE(e->results().at(2)->data().toString(), QLatin1String("16")); 0184 } 0185 0186 void TestMaxima::testDraw() 0187 { 0188 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0189 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0190 0191 auto* e = evalExp( QLatin1String("draw3d(explicit(x^2+y^2,x,-1,1,y,-1,1))") ); 0192 0193 QVERIFY(e != nullptr); 0194 QVERIFY(e->result() != nullptr); 0195 0196 if(!e->result()) 0197 waitForSignal(e, SIGNAL(gotResult())); 0198 0199 QCOMPARE( e->result()->type(), (int)Cantor::ImageResult::Type ); 0200 QVERIFY( !e->result()->data().isNull() ); 0201 QVERIFY( e->errorMessage().isNull() ); 0202 } 0203 0204 void TestMaxima::testDrawMultiline() 0205 { 0206 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0207 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0208 0209 auto* e = evalExp( QLatin1String( 0210 "draw(\n" 0211 "gr2d(\n" 0212 "key=\"sin (x)\",grid=[2,2],\n" 0213 "explicit(\n" 0214 "sin(x),\n" 0215 "x,0,2*%pi\n" 0216 ")\n" 0217 "),\n" 0218 "gr2d(\n" 0219 "key=\"cos (x)\",grid=[2,2],\n" 0220 "explicit(\n" 0221 "cos(x),\n" 0222 "x,0,2*%pi\n" 0223 ")\n" 0224 "))" 0225 )); 0226 0227 0228 QVERIFY(e != nullptr); 0229 QVERIFY(e->result() != nullptr); 0230 0231 if(!e->result()) 0232 waitForSignal(e, SIGNAL(gotResult())); 0233 0234 QCOMPARE(e->result()->type(), (int)Cantor::ImageResult::Type); 0235 QVERIFY(!e->result()->data().isNull()); 0236 QVERIFY(e->errorMessage().isNull()); 0237 } 0238 0239 void TestMaxima::testDrawWithAnotherTextResults() 0240 { 0241 if(QStandardPaths::findExecutable(QLatin1String("gnuplot")).isNull()) 0242 QSKIP("gnuplot not found, maxima needs it for plotting", SkipSingle); 0243 0244 auto* e = evalExp( QLatin1String( 0245 "2*2; \n" 0246 "draw3d(explicit(x^2+y^2,x,-1,1,y,-1,1)); \n" 0247 "4*4;" 0248 )); 0249 0250 if (e->results().at(1)->type() == Cantor::TextResult::Type) 0251 waitForSignal(e, SIGNAL(resultReplaced)); 0252 0253 QVERIFY( e!=nullptr ); 0254 QVERIFY( e->errorMessage().isNull() ); 0255 QCOMPARE(e->results().size(), 3); 0256 0257 QCOMPARE(e->results().at(0)->data().toString(), QLatin1String("4")); 0258 0259 QCOMPARE( e->results().at(1)->type(), (int)Cantor::ImageResult::Type ); 0260 QVERIFY( !e->results().at(1)->data().isNull() ); 0261 0262 QCOMPARE(e->results().at(2)->data().toString(), QLatin1String("16")); 0263 } 0264 0265 void TestMaxima::testInvalidSyntax() 0266 { 0267 auto* e = evalExp( QLatin1String("2+2*(") ); 0268 0269 QVERIFY( e!=nullptr ); 0270 QVERIFY( e->status()==Cantor::Expression::Error ); 0271 QVERIFY( !e->errorMessage().isNull() ); 0272 QCOMPARE(e->results().size(), 0); 0273 } 0274 0275 void TestMaxima::testWarning01() 0276 { 0277 auto* e = evalExp( QLatin1String("rat(0.75*10)") ); 0278 0279 QVERIFY(e != nullptr); 0280 QVERIFY(e->results().size() == 2); //two results, the warning and the actual result of the calculation 0281 0282 //the actual warning string "rat: replaced 7.5 by 15/2 = 7.5" which we don't checked since it's translated, 0283 //we just check it's existance. 0284 auto* result = dynamic_cast<Cantor::TextResult*>(e->results().at(0)); 0285 QVERIFY(e != nullptr); 0286 QVERIFY(result->data().toString().isEmpty() == false); 0287 QVERIFY(result->isWarning() == true); 0288 0289 //the result of the calculation 0290 QCOMPARE(e->results().at(1)->data().toString(), QLatin1String("15/2")); 0291 } 0292 0293 /*! 0294 * test the output of the tex() function which is similarly formatted as other functions producing warning 0295 * but which shouldn't be treated as a warning. 0296 * */ 0297 void TestMaxima::testWarning02() 0298 { 0299 auto* e = evalExp( QLatin1String("tex(\"sin(x)\")") ); 0300 0301 QVERIFY(e != nullptr); 0302 QVERIFY(e->results().size() == 2); //two results, the TeX output and an additional 'false' 0303 0304 //the actual TeX string is of no interest for us, we just check it's existance. 0305 auto* result = dynamic_cast<Cantor::TextResult*>(e->results().at(0)); 0306 QVERIFY(e != nullptr); 0307 QVERIFY(result->data().toString().isEmpty() == false); 0308 QVERIFY(result->isWarning() == false); 0309 } 0310 0311 void TestMaxima::testExprNumbering() 0312 { 0313 auto* e = evalExp( QLatin1String("kill(labels)") ); //first reset the labels 0314 0315 e=evalExp( QLatin1String("2+2") ); 0316 QVERIFY( e!=nullptr ); 0317 int id=e->id(); 0318 QCOMPARE( id, 1 ); 0319 0320 e=evalExp( QString::fromLatin1("%o%1+1" ).arg( id ) ); 0321 QVERIFY( e != nullptr ); 0322 QVERIFY( e->result()!=nullptr ); 0323 QCOMPARE( cleanOutput( e->result()->data().toString() ), QLatin1String( "5" ) ); 0324 } 0325 0326 void TestMaxima::testCommandQueue() 0327 { 0328 //only wait for the last Expression to return, so the queue gets 0329 //actually filled 0330 0331 auto* e1 = session()->evaluateExpression(QLatin1String("0+1")); 0332 auto* e2 = session()->evaluateExpression(QLatin1String("1+1")); 0333 auto* e3 = evalExp(QLatin1String("1+2")); 0334 0335 QVERIFY(e1!=nullptr); 0336 QVERIFY(e2!=nullptr); 0337 QVERIFY(e3!=nullptr); 0338 0339 QVERIFY(e1->result()); 0340 QVERIFY(e2->result()); 0341 QVERIFY(e3->result()); 0342 0343 QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("1")); 0344 QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("2")); 0345 QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("3")); 0346 } 0347 0348 void TestMaxima::testSimpleExpressionWithComment() 0349 { 0350 auto* e = evalExp(QLatin1String("/*this is a comment*/2+2")); 0351 QVERIFY(e!=nullptr); 0352 QVERIFY(e->result()!=nullptr); 0353 0354 QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("4")); 0355 } 0356 0357 void TestMaxima::testCommentExpression() 0358 { 0359 auto* e = evalExp(QLatin1String("/*this is a comment*/")); 0360 QVERIFY(e!=nullptr); 0361 QVERIFY(e->result()==nullptr||e->result()->data().toString().isEmpty()); 0362 } 0363 0364 void TestMaxima::testNestedComment() 0365 { 0366 auto* e = evalExp(QLatin1String("/*/*this is still a comment*/*/2+2/*still/*a*/comment*//**/")); 0367 QVERIFY(e!=nullptr); 0368 QVERIFY(e->result()!=nullptr); 0369 0370 QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("4")); 0371 } 0372 0373 void TestMaxima::testUnmatchedComment() 0374 { 0375 auto* e = evalExp(QLatin1String("/*this comment doesn't end here!")); 0376 QVERIFY(e!=nullptr); 0377 QVERIFY(e->result()==nullptr); 0378 QVERIFY(e->status()==Cantor::Expression::Error); 0379 } 0380 0381 void TestMaxima::testInvalidAssignment() 0382 { 0383 auto* e = evalExp(QLatin1String("0:a")); 0384 QVERIFY(e!=nullptr); 0385 //QVERIFY(e->result()==0); 0386 //QVERIFY(e->status()==Cantor::Expression::Error); 0387 0388 if(session()->status() == Cantor::Session::Running) 0389 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0390 0391 //make sure we didn't screw up the session 0392 auto* e2=evalExp(QLatin1String("2+2")); 0393 QVERIFY(e2!=nullptr); 0394 QVERIFY(e2->result()!=nullptr); 0395 0396 QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("4")); 0397 } 0398 0399 void TestMaxima::testInformationRequest() 0400 { 0401 auto* e = session()->evaluateExpression(QLatin1String("integrate(x^n,x)")); 0402 QVERIFY(e!=nullptr); 0403 waitForSignal(e, SIGNAL(needsAdditionalInformation(QString))); 0404 e->addInformation(QLatin1String("N")); 0405 0406 waitForSignal(e, SIGNAL(statusChanged(Cantor::Expression::Status))); 0407 QVERIFY(e->result()!=nullptr); 0408 0409 QCOMPARE(cleanOutput(e->result()->data().toString()), QLatin1String("x^(n+1)/(n+1)")); 0410 } 0411 0412 void TestMaxima::testHelpRequest() 0413 { 0414 QSKIP("TODO: failing on CI"); 0415 //execute "??print" 0416 auto* e = session()->evaluateExpression(QLatin1String("??print")); 0417 QVERIFY(e != nullptr); 0418 0419 //help result will be shown, but maxima still expects further input 0420 waitForSignal(e, SIGNAL(needsAdditionalInformation(QString))); 0421 QVERIFY(e->status() != Cantor::Expression::Done); 0422 QVERIFY(e->results().size() == 1); // one HelpResult showing the possible options for the answer 0423 0424 //ask for help for the first flag of the print command 0425 e->addInformation(QLatin1String("0")); 0426 0427 //no further input is required, we're done 0428 if (e->status() == Cantor::Expression::Computing) 0429 waitForSignal(e, SIGNAL(statusChanged(Cantor::Expression::Status))); 0430 0431 QVERIFY(e->status() == Cantor::Expression::Done); 0432 QVERIFY(e->results().size() == 1); // final HelpResult 0433 } 0434 0435 void TestMaxima::testSyntaxHelp() 0436 { 0437 auto* help = session()->syntaxHelpFor(QLatin1String("simplify_sum")); 0438 help->fetchSyntaxHelp(); 0439 waitForSignal(help, SIGNAL(done())); 0440 0441 bool trueHelpMessage= help->toHtml().contains(QLatin1String("simplify_sum")); 0442 bool problemsWithMaximaDocs = help->toHtml().contains(QLatin1String("INTERNAL-SIMPLE-FILE-ERROR")); 0443 QVERIFY(trueHelpMessage || problemsWithMaximaDocs); 0444 } 0445 0446 void TestMaxima::testCompletion() 0447 { 0448 auto* help = session()->completionFor(QLatin1String("ask"), 3); 0449 waitForSignal(help, SIGNAL(fetchingDone())); 0450 0451 // Checks all completions for this request 0452 // This correct for Maxima 5.41.0 0453 const QStringList& completions = help->completions(); 0454 QVERIFY(completions.contains(QLatin1String("asksign"))); 0455 QVERIFY(completions.contains(QLatin1String("askinteger"))); 0456 QVERIFY(completions.contains(QLatin1String("askexp"))); 0457 } 0458 0459 void TestMaxima::testTextQuotes() 0460 { 0461 // check simple string 0462 auto* e1 = evalExp(QLatin1String("t1: \"test string\"")); 0463 QVERIFY(e1 != nullptr); 0464 0465 if(session()->status() == Cantor::Session::Running) 0466 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0467 0468 QVERIFY(e1->result() != nullptr); 0469 QCOMPARE(e1->result()->type(), (int)Cantor::TextResult::Type ); 0470 QCOMPARE(e1->result()->data().toString(), QLatin1String("test string")); 0471 0472 // check string with quotes inside 0473 auto* e2 = evalExp(QLatin1String("t2: \"this is a \\\"quoted string\\\"\"")); 0474 QVERIFY(e2 != nullptr); 0475 0476 if(session()->status() == Cantor::Session::Running) 0477 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0478 0479 QVERIFY(e2->result() != nullptr); 0480 QCOMPARE(e2->result()->type(), (int)Cantor::TextResult::Type ); 0481 QCOMPARE(e2->result()->data().toString(), QLatin1String("this is a \"quoted string\"")); 0482 } 0483 0484 void TestMaxima::testVariableModel() 0485 { 0486 QAbstractItemModel* model = session()->variableModel(); 0487 QVERIFY(model != nullptr); 0488 0489 auto* e1 = evalExp(QLatin1String("a: 15")); 0490 auto* e2 = evalExp(QLatin1String("b: \"Hello, world!\"")); 0491 auto* e3 = evalExp(QLatin1String("l: [1,2,3]")); 0492 auto* e4 = evalExp(QLatin1String("t: \"this is a \\\"quoted string\\\"\"")); 0493 QVERIFY(e1 != nullptr); 0494 QVERIFY(e2 != nullptr); 0495 QVERIFY(e3 != nullptr); 0496 QVERIFY(e4 != nullptr); 0497 0498 if(session()->status() == Cantor::Session::Running) 0499 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0500 0501 QCOMPARE(4, model->rowCount()); 0502 0503 QVariant name = model->index(0,0).data(); 0504 QCOMPARE(name.toString(),QLatin1String("a")); 0505 0506 QVariant value = model->index(0,1).data(); 0507 QCOMPARE(value.toString(),QLatin1String("15")); 0508 0509 QVariant name1 = model->index(1,0).data(); 0510 QCOMPARE(name1.toString(),QLatin1String("b")); 0511 0512 QVariant value1 = model->index(1,1).data(); 0513 QCOMPARE(value1.toString(),QLatin1String("Hello, world!")); 0514 0515 QVariant name2 = model->index(2,0).data(); 0516 QCOMPARE(name2.toString(),QLatin1String("l")); 0517 0518 QVariant value2 = model->index(2,1).data(); 0519 QCOMPARE(value2.toString(),QLatin1String("[1,2,3]")); 0520 0521 QVariant name3 = model->index(3,0).data(); 0522 QCOMPARE(name3.toString(),QLatin1String("t")); 0523 0524 QVariant value3 = model->index(3,1).data(); 0525 QCOMPARE(value3.toString(),QLatin1String("this is a \"quoted string\"")); 0526 } 0527 0528 void TestMaxima::testLispMode01() 0529 { 0530 //switch to the Lisp-mode 0531 auto* e1 = evalExp(QLatin1String("to_lisp();")); 0532 QVERIFY(e1 != nullptr); 0533 0534 //evaluate a Lisp command and check the result 0535 auto* e2 = evalExp(QLatin1String("(cons 'a 'b)")); 0536 QVERIFY(e2 != nullptr); 0537 QVERIFY(e2->result() != nullptr); 0538 QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("(A . B)")); 0539 0540 //switch back to Maxima mode 0541 auto* e3 = evalExp(QLatin1String("(to-maxima)")); 0542 QVERIFY(e3 != nullptr); 0543 0544 //evaluate a simple Maxima command 0545 auto* e4 = evalExp(QLatin1String("5+5")); 0546 QVERIFY(e4 != nullptr); 0547 0548 //TODO: doesn't work in the test, works in Cantor though... 0549 // QVERIFY(e4->result() != nullptr); 0550 // QCOMPARE(cleanOutput(e4->result()->data().toString()), QLatin1String("10")); 0551 } 0552 0553 void TestMaxima::testLoginLogout() 0554 { 0555 // Logout from session twice and all must works fine 0556 session()->logout(); 0557 session()->logout(); 0558 0559 // Login in session twice and all must works fine 0560 session()->login(); 0561 session()->login(); 0562 } 0563 0564 void TestMaxima::testRestartWhileRunning() 0565 { 0566 QSKIP("This test is not working yet.", SkipSingle); 0567 auto* e1 = session()->evaluateExpression(QLatin1String(":lisp (sleep 5)")); 0568 QVERIFY(e1 != nullptr); 0569 0570 session()->logout(); 0571 0572 QCOMPARE(e1->status(), Cantor::Expression::Interrupted); 0573 session()->login(); 0574 0575 auto* e2=evalExp( QLatin1String("2+2") ); 0576 0577 QVERIFY(e2 != nullptr); 0578 QVERIFY(e2->result() != nullptr); 0579 0580 QCOMPARE(cleanOutput(e2->result()->data().toString() ), QLatin1String("4")); 0581 } 0582 0583 QTEST_MAIN( TestMaxima ) 0584