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