File indexing completed on 2023-05-30 09:03:13
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 QSKIP("doesn't work on CI", SkipSingle); 0163 0164 if (!PythonSettings::integratePlots()) 0165 QSKIP("This test needs enabled plots integration in Python3 settings", SkipSingle); 0166 0167 auto* e = evalExp(QLatin1String( 0168 "import matplotlib\n" 0169 "import matplotlib.pyplot as plt\n" 0170 "import numpy as np" 0171 )); 0172 QVERIFY(e != nullptr); 0173 QVERIFY(e->errorMessage().isEmpty() == true); 0174 0175 //the variable model shouldn't have any entries after the module imports 0176 QAbstractItemModel* model = session()->variableModel(); 0177 QVERIFY(model != nullptr); 0178 QVERIFY(model->rowCount() == 0); 0179 0180 //create data for plotting 0181 e = evalExp(QLatin1String( 0182 "t = np.arange(0.0, 2.0, 0.01)\n" 0183 "s = 1 + np.sin(2 * np.pi * t)" 0184 )); 0185 QVERIFY(e != nullptr); 0186 QVERIFY(e->errorMessage().isEmpty() == true); 0187 0188 if(session()->status()==Cantor::Session::Running) 0189 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0190 //the variable model should have two entries now 0191 QVERIFY(model->rowCount() == 2); 0192 0193 //plot the data and check the results 0194 e = evalExp(QLatin1String( 0195 "plt.plot(t,s)\n" 0196 "plt.show()" 0197 )); 0198 0199 QVERIFY(e != nullptr); 0200 if (e->result() == nullptr) 0201 waitForSignal(e, SIGNAL(gotResult())); 0202 0203 QVERIFY(e->errorMessage().isEmpty() == true); 0204 QVERIFY(model->rowCount() == 2); //still only two variables 0205 0206 //there must be one single image result 0207 QVERIFY(e->results().size() == 1); 0208 const Cantor::ImageResult* result = dynamic_cast<const Cantor::ImageResult*>(e->result()); 0209 QVERIFY(result != nullptr); 0210 0211 evalExp(QLatin1String("del t; del s")); 0212 } 0213 0214 void TestPython3::testVariablesCreatingFromCode() 0215 { 0216 if (!PythonSettings::variableManagement()) 0217 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0218 0219 QAbstractItemModel* model = session()->variableModel(); 0220 QVERIFY(model != nullptr); 0221 0222 auto* e = evalExp(QLatin1String("a = 15; b = 'S';")); 0223 QVERIFY(e != nullptr); 0224 0225 if(session()->status() == Cantor::Session::Running) 0226 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0227 0228 QCOMPARE(2, model->rowCount()); 0229 0230 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a")); 0231 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15")); 0232 0233 QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b")); 0234 QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'")); 0235 0236 evalExp(QLatin1String("del a; del b")); 0237 } 0238 0239 void TestPython3::testVariableChangeSizeType() 0240 { 0241 if (!PythonSettings::variableManagement()) 0242 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0243 0244 QAbstractItemModel* model = session()->variableModel(); 0245 QVERIFY(model != nullptr); 0246 0247 // create a text variable 0248 auto* e = evalExp(QLatin1String("test = \"abcd\";")); 0249 QVERIFY(e != nullptr); 0250 0251 if(session()->status() == Cantor::Session::Running) 0252 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0253 0254 QCOMPARE(1, model->rowCount()); 0255 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test")); 0256 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("'abcd'")); 0257 QCOMPARE(model->index(0,2).data().toString(), QLatin1String("<class 'str'>")); 0258 QCOMPARE(model->index(0,3).data().toString(), QLatin1String("53")); 0259 0260 // change from string to integer 0261 e = evalExp(QLatin1String("test = 1;")); 0262 QVERIFY(e != nullptr); 0263 0264 if(session()->status() == Cantor::Session::Running) 0265 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0266 0267 QCOMPARE(1, model->rowCount()); 0268 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("test")); 0269 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("1")); 0270 QCOMPARE(model->index(0,2).data().toString(), QLatin1String("<class 'int'>")); 0271 QCOMPARE(model->index(0,3).data().toString(), QLatin1String("28")); // 28 bytes for Python's int object 0272 0273 evalExp(QLatin1String("del test;")); 0274 } 0275 0276 void TestPython3::testVariableCleanupAfterRestart() 0277 { 0278 QAbstractItemModel* model = session()->variableModel(); 0279 QVERIFY(model != nullptr); 0280 0281 auto* e = evalExp(QLatin1String("a = 15; b = 'S';")); 0282 QVERIFY(e != nullptr); 0283 0284 if(session()->status()==Cantor::Session::Running) 0285 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0286 0287 QCOMPARE(2, model->rowCount()); 0288 0289 session()->logout(); 0290 session()->login(); 0291 0292 QCOMPARE(0, model->rowCount()); 0293 } 0294 0295 void TestPython3::testDictVariable() 0296 { 0297 if (!PythonSettings::variableManagement()) 0298 QSKIP("This test needs enabled variable management in Python3 settings", SkipSingle); 0299 0300 auto* model = session()->variableModel(); 0301 QVERIFY(model != nullptr); 0302 0303 auto* e = evalExp(QLatin1String("d = {'value': 33}")); 0304 0305 QVERIFY(e != nullptr); 0306 0307 if(session()->status() == Cantor::Session::Running) 0308 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0309 0310 QCOMPARE(1, static_cast<QAbstractItemModel*>(model)->rowCount()); 0311 QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d")); 0312 QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}")); 0313 0314 evalExp(QLatin1String("del d")); 0315 } 0316 0317 void TestPython3::testInterrupt() 0318 { 0319 QSKIP("doesn't work on CI", SkipSingle); 0320 0321 auto* e1 = session()->evaluateExpression(QLatin1String("import time; time.sleep(150)")); 0322 auto* e2 = session()->evaluateExpression(QLatin1String("2")); 0323 0324 if (e1->status() != Cantor::Expression::Queued) 0325 waitForSignal(e1, SIGNAL(statusChanged(Cantor::Expression::Status))); 0326 0327 if (e1->status() != Cantor::Expression::Computing) 0328 waitForSignal(e1, SIGNAL(statusChanged(Cantor::Expression::Status))); 0329 0330 if (e2->status() != Cantor::Expression::Queued) 0331 waitForSignal(e2, SIGNAL(statusChanged(Cantor::Expression::Status))); 0332 0333 // Without this delay, server don't interrupt even if got interrupt signal (via OS kill) 0334 // Also, if the server won't interrupt, the test will fail without reasonable reason 0335 QTest::qWait(100); 0336 0337 QCOMPARE(e1->status(), Cantor::Expression::Computing); 0338 QCOMPARE(e2->status(), Cantor::Expression::Queued); 0339 0340 while(session()->status() != Cantor::Session::Running) 0341 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0342 0343 session()->interrupt(); 0344 0345 while(session()->status() != Cantor::Session::Done) 0346 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0347 0348 QCOMPARE(e1->status(), Cantor::Expression::Interrupted); 0349 QCOMPARE(e2->status(), Cantor::Expression::Interrupted); 0350 0351 auto* e = evalExp(QLatin1String("2+2")); 0352 QVERIFY(e != nullptr); 0353 0354 if (session()->status() == Cantor::Session::Running) 0355 waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); 0356 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)