Warning, file /education/cantor/src/backends/python/testpython.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: 2015 Minh Ngo <minh@fedoraproject.org> 0004 SPDX-FileCopyrightText: 2021-2022 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 0007 #include "testpython.h" 0008 0009 #include "session.h" 0010 #include "expression.h" 0011 #include "imageresult.h" 0012 #include "defaultvariablemodel.h" 0013 #include "completionobject.h" 0014 0015 #include "settings.h" 0016 0017 QString TestPython3::backendName() 0018 { 0019 return QLatin1String("python"); 0020 } 0021 0022 void TestPython3::testSimpleCommand() 0023 { 0024 auto* e = evalExp(QLatin1String("2+2")); 0025 0026 QVERIFY(e != nullptr); 0027 QVERIFY(e->result()); 0028 QVERIFY(e->result()->data().toString() == QLatin1String("4")); 0029 } 0030 0031 void TestPython3::testMultilineCommand() 0032 { 0033 auto* e = evalExp(QLatin1String("print(2+2)\nprint(7*5)")); 0034 0035 QVERIFY(e != nullptr); 0036 QVERIFY(e->result()); 0037 QVERIFY(e->result()->data().toString() == QLatin1String("4\n35")); 0038 } 0039 0040 void TestPython3::testCommandQueue() 0041 { 0042 auto* e1 = session()->evaluateExpression(QLatin1String("0+1")); 0043 auto* e2 = session()->evaluateExpression(QLatin1String("1+1")); 0044 auto* e3 = evalExp(QLatin1String("1+2")); 0045 0046 QVERIFY(e1 != nullptr); 0047 QVERIFY(e2 != nullptr); 0048 QVERIFY(e3 != nullptr); 0049 0050 QVERIFY(e1->result()); 0051 QVERIFY(e2->result()); 0052 QVERIFY(e3->result()); 0053 0054 QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("1")); 0055 QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("2")); 0056 QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("3")); 0057 } 0058 0059 void TestPython3::testCommentExpression() 0060 { 0061 auto* e = evalExp(QLatin1String("#only comment")); 0062 0063 QVERIFY(e != nullptr); 0064 QCOMPARE(e->status(), Cantor::Expression::Status::Done); 0065 QCOMPARE(e->results().size(), 0); 0066 } 0067 0068 void TestPython3::testSimpleExpressionWithComment() 0069 { 0070 auto* e = evalExp(QLatin1String("2+2 # comment")); 0071 0072 QVERIFY(e != nullptr); 0073 QVERIFY(e->result()); 0074 QVERIFY(e->result()->data().toString() == QLatin1String("4")); 0075 } 0076 0077 void TestPython3::testMultilineCommandWithComment() 0078 { 0079 auto* e = evalExp(QLatin1String( 0080 "print(2+2) \n" 0081 "#comment in middle \n" 0082 "print(7*5)")); 0083 0084 QVERIFY(e != nullptr); 0085 QVERIFY(e->result()); 0086 QVERIFY(e->result()->data().toString() == QLatin1String("4\n35")); 0087 } 0088 0089 void TestPython3::testInvalidSyntax() 0090 { 0091 auto* e=evalExp( QLatin1String("2+2*+.") ); 0092 0093 QVERIFY( e!=nullptr ); 0094 QCOMPARE( e->status(), Cantor::Expression::Error ); 0095 } 0096 0097 void TestPython3::testCompletion() 0098 { 0099 if(session()->status()==Cantor::Session::Running) 0100 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0101 0102 auto* help = session()->completionFor(QLatin1String("p"), 1); 0103 waitForSignal(help, SIGNAL(fetchingDone())); 0104 0105 // Checks all completions for this request 0106 const auto& completions = help->completions(); 0107 QCOMPARE(completions.size(), 4); 0108 QVERIFY(completions.contains(QLatin1String("pass"))); 0109 QVERIFY(completions.contains(QLatin1String("pow"))); 0110 QVERIFY(completions.contains(QLatin1String("print"))); 0111 QVERIFY(completions.contains(QLatin1String("property"))); 0112 } 0113 0114 0115 void TestPython3::testImportStatement() 0116 { 0117 auto* e = evalExp(QLatin1String("import sys")); 0118 0119 QVERIFY(e != nullptr); 0120 QCOMPARE(e->status(), Cantor::Expression::Done); 0121 } 0122 0123 void TestPython3::testCodeWithComments() 0124 { 0125 { 0126 auto* e = evalExp(QLatin1String("#comment\n1+2")); 0127 0128 QVERIFY(e != nullptr); 0129 QVERIFY(e->result()); 0130 QVERIFY(e->result()->data().toString() == QLatin1String("3")); 0131 } 0132 0133 { 0134 auto* e = evalExp(QLatin1String(" #comment\n1+2")); 0135 0136 QVERIFY(e != nullptr); 0137 QVERIFY(e->result()); 0138 QVERIFY(e->result()->data().toString() == QLatin1String("3")); 0139 } 0140 } 0141 0142 void TestPython3::testPython3Code() 0143 { 0144 { 0145 auto* e = evalExp(QLatin1String("print 1 + 2")); 0146 0147 QVERIFY(e != nullptr); 0148 QVERIFY(!e->errorMessage().isEmpty()); 0149 } 0150 0151 { 0152 auto* e = evalExp(QLatin1String("print(1 + 2)")); 0153 0154 QVERIFY(e != nullptr); 0155 QVERIFY(e->result()); 0156 QVERIFY(e->result()->data().toString() == QLatin1String("3")); 0157 } 0158 } 0159 0160 void TestPython3::testSimplePlot() 0161 { 0162 if (!PythonSettings::integratePlots()) 0163 QSKIP("This test needs enabled plots integration in Python3 settings", SkipSingle); 0164 0165 auto* e = evalExp(QLatin1String( 0166 "import matplotlib\n" 0167 "import matplotlib.pyplot as plt\n" 0168 "import numpy as np" 0169 )); 0170 QVERIFY(e != nullptr); 0171 QVERIFY(e->errorMessage().isEmpty() == true); 0172 0173 //the variable model shouldn't have any entries after the module imports 0174 QAbstractItemModel* model = session()->variableModel(); 0175 QVERIFY(model != nullptr); 0176 QVERIFY(model->rowCount() == 0); 0177 0178 //create data for plotting 0179 e = evalExp(QLatin1String( 0180 "t = np.arange(0.0, 2.0, 0.01)\n" 0181 "s = 1 + np.sin(2 * np.pi * t)" 0182 )); 0183 QVERIFY(e != nullptr); 0184 QVERIFY(e->errorMessage().isEmpty() == true); 0185 0186 if(session()->status()==Cantor::Session::Running) 0187 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0188 //the variable model should have two entries now 0189 QVERIFY(model->rowCount() == 2); 0190 0191 //plot the data and check the results 0192 e = evalExp(QLatin1String( 0193 "plt.plot(t,s)\n" 0194 "plt.show()" 0195 )); 0196 0197 QVERIFY(e != nullptr); 0198 if (e->result() == nullptr) 0199 waitForSignal(e, SIGNAL(gotResult())); 0200 0201 QVERIFY(e->errorMessage().isEmpty() == true); 0202 QVERIFY(model->rowCount() == 2); //still only two variables 0203 0204 //there must be one single image result 0205 QVERIFY(e->results().size() == 1); 0206 const Cantor::ImageResult* result = dynamic_cast<const Cantor::ImageResult*>(e->result()); 0207 QVERIFY(result != nullptr); 0208 0209 evalExp(QLatin1String("del t; del s")); 0210 } 0211 0212 void TestPython3::testVariablesCreatingFromCode() 0213 { 0214 if (!PythonSettings::variableManagement()) 0215 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0216 0217 QAbstractItemModel* model = session()->variableModel(); 0218 QVERIFY(model != nullptr); 0219 0220 auto* e = evalExp(QLatin1String("a = 15; b = 'S';")); 0221 QVERIFY(e != nullptr); 0222 0223 if(session()->status() == Cantor::Session::Running) 0224 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0225 0226 QCOMPARE(2, model->rowCount()); 0227 0228 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a")); 0229 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15")); 0230 0231 QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b")); 0232 QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'")); 0233 0234 evalExp(QLatin1String("del a; del b")); 0235 } 0236 0237 void TestPython3::testVariableChangeSizeType() 0238 { 0239 if (!PythonSettings::variableManagement()) 0240 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0241 0242 QAbstractItemModel* model = session()->variableModel(); 0243 QVERIFY(model != nullptr); 0244 0245 // create a text variable 0246 auto* e = evalExp(QLatin1String("test = \"abcd\";")); 0247 QVERIFY(e != nullptr); 0248 0249 if(session()->status() == Cantor::Session::Running) 0250 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0251 0252 QCOMPARE(1, model->rowCount()); 0253 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test")); 0254 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("'abcd'")); 0255 QCOMPARE(model->index(0,2).data().toString(), QLatin1String("<class 'str'>")); 0256 QCOMPARE(model->index(0,3).data().toString(), QLatin1String("53")); 0257 0258 // change from string to integer 0259 e = evalExp(QLatin1String("test = 1;")); 0260 QVERIFY(e != nullptr); 0261 0262 if(session()->status() == Cantor::Session::Running) 0263 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0264 0265 QCOMPARE(1, model->rowCount()); 0266 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test")); 0267 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("1")); 0268 QCOMPARE(model->index(0,2).data().toString(), QLatin1String("<class 'int'>")); 0269 QCOMPARE(model->index(0,3).data().toString(), QLatin1String("28")); // 28 bytes for Python's int object 0270 0271 evalExp(QLatin1String("del test;")); 0272 } 0273 0274 void TestPython3::testVariableCleanupAfterRestart() 0275 { 0276 QAbstractItemModel* model = session()->variableModel(); 0277 QVERIFY(model != nullptr); 0278 0279 auto* e = evalExp(QLatin1String("a = 15; b = 'S';")); 0280 QVERIFY(e != nullptr); 0281 0282 if(session()->status()==Cantor::Session::Running) 0283 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0284 0285 QCOMPARE(2, model->rowCount()); 0286 0287 session()->logout(); 0288 session()->login(); 0289 0290 QCOMPARE(0, model->rowCount()); 0291 } 0292 0293 void TestPython3::testDictVariable() 0294 { 0295 if (!PythonSettings::variableManagement()) 0296 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0297 0298 auto* model = session()->variableModel(); 0299 QVERIFY(model != nullptr); 0300 0301 auto* e = evalExp(QLatin1String("d = {'value': 33}")); 0302 0303 QVERIFY(e != nullptr); 0304 0305 if(session()->status() == Cantor::Session::Running) 0306 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0307 0308 QCOMPARE(1, static_cast<QAbstractItemModel*>(model)->rowCount()); 0309 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d")); 0310 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}")); 0311 0312 evalExp(QLatin1String("del d")); 0313 } 0314 0315 void TestPython3::testInterrupt() 0316 { 0317 auto* e1 = session()->evaluateExpression(QLatin1String("import time; time.sleep(150)")); 0318 auto* e2 = session()->evaluateExpression(QLatin1String("2")); 0319 0320 if (e1->status() != Cantor::Expression::Queued) 0321 waitForSignal(e1, SIGNAL(statusChanged(Cantor::Expression::Status))); 0322 0323 if (e1->status() != Cantor::Expression::Computing) 0324 waitForSignal(e1, SIGNAL(statusChanged(Cantor::Expression::Status))); 0325 0326 if (e2->status() != Cantor::Expression::Queued) 0327 waitForSignal(e2, SIGNAL(statusChanged(Cantor::Expression::Status))); 0328 0329 // Without this delay, server don't interrupt even if got interrupt signal (via OS kill) 0330 // Also, if the server won't interrupt, the test will fail without reasonable reason 0331 QTest::qWait(100); 0332 0333 QCOMPARE(e1->status(), Cantor::Expression::Computing); 0334 QCOMPARE(e2->status(), Cantor::Expression::Queued); 0335 0336 while(session()->status() != Cantor::Session::Running) 0337 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0338 0339 session()->interrupt(); 0340 0341 while(session()->status() != Cantor::Session::Done) 0342 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0343 0344 QCOMPARE(e1->status(), Cantor::Expression::Interrupted); 0345 QCOMPARE(e2->status(), Cantor::Expression::Interrupted); 0346 0347 auto* e = evalExp(QLatin1String("2+2")); 0348 QVERIFY(e != nullptr); 0349 0350 qDebug()<<"### session status 1 " << session()->status(); 0351 qDebug()<<"### expression status 1 " << e->status(); 0352 if (session()->status() == Cantor::Session::Running) 0353 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0354 0355 qDebug()<<"### session status 2 " << session()->status(); 0356 qDebug()<<"### expression status 2 " << e->status(); 0357 QCOMPARE(e->status(), Cantor::Expression::Done); 0358 QVERIFY(e->result()); 0359 QCOMPARE(e->result()->data().toString(), QLatin1String("4")); 0360 } 0361 0362 void TestPython3::testWarning() 0363 { 0364 auto* e = evalExp(QLatin1String("import warnings; warnings.warn('Test')")); 0365 0366 QVERIFY(e != nullptr); 0367 0368 if (session()->status() == Cantor::Session::Running) 0369 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0370 0371 QCOMPARE(e->status(), Cantor::Expression::Status::Done); 0372 QCOMPARE(e->results().size(), 1); 0373 } 0374 0375 QTEST_MAIN(TestPython3)