ctkAbstractPythonManagerTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. // Qt includes
  2. #include <QTemporaryFile>
  3. #include <QTextStream>
  4. // CTK includes
  5. #include "ctkAbstractPythonManager.h"
  6. #include "ctkTest.h"
  7. // PythonQt includes
  8. #include <PythonQt.h>
  9. // STD includes
  10. #include <cstdlib>
  11. #include <iostream>
  12. //-----------------------------------------------------------------------------
  13. #if QT_VERSION < 0x040700
  14. Q_DECLARE_METATYPE(QVariant)
  15. #endif
  16. //-----------------------------------------------------------------------------
  17. class ctkAbstractPythonManagerTester: public QObject
  18. {
  19. Q_OBJECT
  20. private:
  21. ctkAbstractPythonManager PythonManager;
  22. private Q_SLOTS:
  23. void testDefaults();
  24. void testIsPythonInitialized();
  25. void testSetInitializationFlags();
  26. void testSetSystemExitExceptionHandlerEnabled();
  27. void testPythonErrorOccured();
  28. void testPythonErrorOccured_data();
  29. void testInitialize();
  30. void testMainContext();
  31. void testAddObjectToPythonMain();
  32. //void testRegisterPythonQtDecorator(); // TODO
  33. //void testRegisterClassForPythonQt(); // TODO
  34. void testExecuteString();
  35. void testExecuteString_data();
  36. void testExecuteFile();
  37. void testExecuteFile_data();
  38. void testPythonAttributes();
  39. void testPythonAttributes_data();
  40. void testPythonModule();
  41. void testPythonModule_data();
  42. void testPythonObject();
  43. void testPythonObject_data();
  44. };
  45. Q_DECLARE_METATYPE(PyObject*)
  46. // ----------------------------------------------------------------------------
  47. void ctkAbstractPythonManagerTester::testDefaults()
  48. {
  49. QCOMPARE(this->PythonManager.pythonErrorOccured(), false);
  50. this->PythonManager.resetErrorFlag();
  51. this->PythonManager.registerPythonQtDecorator(0);
  52. this->PythonManager.registerClassForPythonQt(0);
  53. this->PythonManager.registerCPPClassForPythonQt(0);
  54. this->PythonManager.addWrapperFactory(0);
  55. }
  56. // ----------------------------------------------------------------------------
  57. void ctkAbstractPythonManagerTester::testIsPythonInitialized()
  58. {
  59. QCOMPARE(this->PythonManager.isPythonInitialized(), false);
  60. }
  61. // ----------------------------------------------------------------------------
  62. void ctkAbstractPythonManagerTester::testSetInitializationFlags()
  63. {
  64. QCOMPARE(this->PythonManager.initializationFlags(), PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut);
  65. int flagsToSetPreInit = PythonQt::RedirectStdOut;
  66. int expectedFlagsPreInit = PythonQt::RedirectStdOut;
  67. this->PythonManager.setInitializationFlags(flagsToSetPreInit);
  68. QCOMPARE(this->PythonManager.initializationFlags(), expectedFlagsPreInit);
  69. this->PythonManager.mainContext();
  70. int flagsToSetPostInit = 0;
  71. int expectedFlagsPostInit = PythonQt::RedirectStdOut;
  72. this->PythonManager.setInitializationFlags(flagsToSetPostInit);
  73. QCOMPARE(this->PythonManager.initializationFlags(), expectedFlagsPostInit);
  74. QCOMPARE(this->PythonManager.isPythonInitialized(), true);
  75. }
  76. // ----------------------------------------------------------------------------
  77. void ctkAbstractPythonManagerTester::testSetSystemExitExceptionHandlerEnabled()
  78. {
  79. QCOMPARE(this->PythonManager.systemExitExceptionHandlerEnabled(), false);
  80. this->PythonManager.setSystemExitExceptionHandlerEnabled(true);
  81. QCOMPARE(this->PythonManager.systemExitExceptionHandlerEnabled(), true);
  82. }
  83. // ----------------------------------------------------------------------------
  84. void ctkAbstractPythonManagerTester::testInitialize()
  85. {
  86. QVERIFY(this->PythonManager.initialize());
  87. this->testDefaults();
  88. }
  89. // ----------------------------------------------------------------------------
  90. void ctkAbstractPythonManagerTester::testMainContext()
  91. {
  92. QVERIFY(this->PythonManager.mainContext());
  93. this->testDefaults();
  94. }
  95. // ----------------------------------------------------------------------------
  96. void ctkAbstractPythonManagerTester::testPythonErrorOccured()
  97. {
  98. QFETCH(QString, pythonCode);
  99. QFETCH(bool, errorOccured);
  100. this->PythonManager.executeString(pythonCode);
  101. QCOMPARE(this->PythonManager.pythonErrorOccured(), errorOccured);
  102. if(errorOccured)
  103. {
  104. this->PythonManager.resetErrorFlag();
  105. }
  106. }
  107. // ----------------------------------------------------------------------------
  108. void ctkAbstractPythonManagerTester::testPythonErrorOccured_data()
  109. {
  110. QTest::addColumn<QString>("pythonCode");
  111. QTest::addColumn<bool>("errorOccured");
  112. QTest::newRow("0") << QString("2 + 2") << false;
  113. QTest::newRow("1") << QString("raise Exception('This exception is expected')") << true;
  114. }
  115. // ----------------------------------------------------------------------------
  116. void ctkAbstractPythonManagerTester::testAddObjectToPythonMain()
  117. {
  118. QObject * object = new QObject(this);
  119. object->setProperty("happy", true);
  120. this->PythonManager.addObjectToPythonMain("testAddObjectToPythonMain", object);
  121. QVariant returnValue = this->PythonManager.executeString("testAddObjectToPythonMain.happy",
  122. ctkAbstractPythonManager::EvalInput);
  123. this->PythonManager.resetErrorFlag();
  124. QCOMPARE(returnValue, QVariant(true));
  125. }
  126. // ----------------------------------------------------------------------------
  127. void ctkAbstractPythonManagerTester::testExecuteString()
  128. {
  129. QFETCH(QString, stringToExecute);
  130. QFETCH(int, executeStringMode);
  131. QFETCH(bool, errorOccured);
  132. QFETCH(QVariant, expectedReturnValue);
  133. QFETCH(QString, expectedVariableName);
  134. QFETCH(QVariant, expectedVariableValue);
  135. QVariant returnValue = this->PythonManager.executeString(
  136. stringToExecute,
  137. static_cast<ctkAbstractPythonManager::ExecuteStringMode>(executeStringMode));
  138. QCOMPARE(this->PythonManager.pythonErrorOccured(), errorOccured);
  139. if (errorOccured)
  140. {
  141. this->PythonManager.resetErrorFlag();
  142. return;
  143. }
  144. QCOMPARE(returnValue, expectedReturnValue);
  145. QCOMPARE(this->PythonManager.getVariable(expectedVariableName), expectedVariableValue);
  146. }
  147. // ----------------------------------------------------------------------------
  148. void ctkAbstractPythonManagerTester::testExecuteString_data()
  149. {
  150. QTest::addColumn<QString>("stringToExecute");
  151. QTest::addColumn<int>("executeStringMode");
  152. QTest::addColumn<bool>("errorOccured");
  153. QTest::addColumn<QVariant>("expectedReturnValue");
  154. QTest::addColumn<QString>("expectedVariableName");
  155. QTest::addColumn<QVariant>("expectedVariableValue");
  156. QTest::newRow("0") << QString("a = 6542")
  157. << static_cast<int>(ctkAbstractPythonManager::FileInput)
  158. << false
  159. << QVariant() << QString("a") << QVariant(6542);
  160. QTest::newRow("1") << QString("6543")
  161. << static_cast<int>(ctkAbstractPythonManager::FileInput)
  162. << false
  163. << QVariant() << QString("a") << QVariant(6542);
  164. QTest::newRow("2") << QString("b = 6544")
  165. << static_cast<int>(ctkAbstractPythonManager::EvalInput)
  166. << true
  167. << QVariant() << QString("b") << QVariant();
  168. QTest::newRow("3") << QString("7")
  169. << static_cast<int>(ctkAbstractPythonManager::EvalInput)
  170. << false
  171. << QVariant(7) << QString("b") << QVariant();
  172. QTest::newRow("4") << QString("sys.getrecursionlimit()")
  173. << static_cast<int>(ctkAbstractPythonManager::FileInput)
  174. << false
  175. << QVariant() << QString() << QVariant();
  176. // This assume the default 'recursionlimit' has not been changed
  177. QTest::newRow("5") << QString("sys.getrecursionlimit()")
  178. << static_cast<int>(ctkAbstractPythonManager::EvalInput)
  179. << false
  180. << QVariant(1000) << QString() << QVariant();
  181. }
  182. // ----------------------------------------------------------------------------
  183. void ctkAbstractPythonManagerTester::testExecuteFile()
  184. {
  185. QFETCH(QString, stringToExecute);
  186. QFETCH(bool, pythonErrorExpected);
  187. QTemporaryFile pythonFile("testExecuteFile-XXXXXX.py");
  188. QVERIFY(pythonFile.open());
  189. QTextStream out(&pythonFile);
  190. out << stringToExecute;
  191. pythonFile.close();
  192. this->PythonManager.executeFile(pythonFile.fileName());
  193. QCOMPARE(this->PythonManager.pythonErrorOccured(), pythonErrorExpected);
  194. }
  195. // ----------------------------------------------------------------------------
  196. void ctkAbstractPythonManagerTester::testExecuteFile_data()
  197. {
  198. QTest::addColumn<QString>("stringToExecute");
  199. QTest::addColumn<bool>("pythonErrorExpected");
  200. QTest::newRow("0-emptyfile") << QString("")
  201. << false;
  202. QTest::newRow("1-helloworld") << QString("print 'Hello world'")
  203. << false;
  204. QTest::newRow("2-syntaxerror") << QString("print '") // SyntaxError
  205. << true;
  206. QTest::newRow("3-check __file__ attribute") << QString("print 'This file is: %s' % __file__")
  207. << false;
  208. }
  209. // ----------------------------------------------------------------------------
  210. void ctkAbstractPythonManagerTester::testPythonAttributes()
  211. {
  212. QFETCH(QString, pythonVariableName);
  213. QFETCH(QStringList, expectedAttributeList);
  214. QString test_path = __FILE__; // return the local path of this source file
  215. // replace "ctkAbstractPythonManagerTest.py" by "PythonAttributes-test.py"
  216. test_path.lastIndexOf("/");
  217. test_path.replace(test_path.lastIndexOf("/"),
  218. test_path.size() - test_path.lastIndexOf("/"),
  219. "/PythonAttributes-test.py");
  220. this->PythonManager.executeFile(test_path);
  221. QStringList AttributeList = this->PythonManager.pythonAttributes(pythonVariableName, QString("__main__").toLatin1(), false);
  222. foreach (const QString& expectedAttribute, expectedAttributeList)
  223. {
  224. QVERIFY(AttributeList.contains(expectedAttribute));
  225. }
  226. }
  227. // ----------------------------------------------------------------------------
  228. void ctkAbstractPythonManagerTester::testPythonAttributes_data()
  229. {
  230. QTest::addColumn<QString>("pythonVariableName");
  231. QTest::addColumn<QStringList>("expectedAttributeList");
  232. QTest::newRow("d.foo_class()")
  233. << "d.foo_class()"
  234. << (QStringList()
  235. << "FOO_CLASS_MEMBER"
  236. << "foo_class_method"
  237. << "foo_instance_member"
  238. << "foo_instance_method"
  239. << "instantiate_bar");
  240. QTest::newRow("d.foo_class().instantiate_bar()")
  241. << "d.foo_class().instantiate_bar()"
  242. << (QStringList()
  243. << "BAR_CLASS_MEMBER"
  244. << "bar_class_method"
  245. << "bar_instance_member"
  246. << "bar_instance_method");
  247. QTest::newRow("d.foo_class().instantiate_bar().bar_maths(5)")
  248. << "d.foo_class().instantiate_bar().bar_maths(5)"
  249. << (QStringList()
  250. << "MATHS_CLASS_MEMBER"
  251. << "maths_instance_member" // TODO: verify result is 5
  252. << "maths_instance_method");
  253. QTest::newRow("MultipleArg( 5 + 5 , '(')")
  254. << "MultipleArg( 5 + 5 , '(')"
  255. << (QStringList()
  256. << "multipleArg_instance_member_num" // TODO: verify result is 10
  257. << "multipleArg_instance_member_str" // TODO: verify result is '('
  258. << "multipleArg_instance_member_other"); // TODO: verify result is 0
  259. QTest::newRow("MultipleArg( 5 % 5 + 1, '\"', 0.1)")
  260. << "MultipleArg( 5 + 5 , '\"', 0.1)"
  261. << (QStringList()
  262. << "multipleArg_instance_member_num" // TODO: verify result is 1.1
  263. << "multipleArg_instance_member_str" // TODO: verify result is '"'
  264. << "multipleArg_instance_member_other"); // TODO: verify result is 0.1
  265. }
  266. // ----------------------------------------------------------------------------
  267. void ctkAbstractPythonManagerTester::testPythonModule()
  268. {
  269. QFETCH(QString, pythonCode);
  270. QFETCH(QString, inputModuleList);
  271. QFETCH(QString, expectedReturnedString);
  272. this->PythonManager.executeString(pythonCode);
  273. PyObject* returnedPyObject = this->PythonManager.pythonModule(inputModuleList);
  274. PyObject* returnedPyString;
  275. if(returnedPyObject)
  276. {
  277. returnedPyString = PyObject_GetAttrString(returnedPyObject, "__name__");
  278. }
  279. else
  280. {
  281. returnedPyString = PyString_FromString("");
  282. }
  283. QString returnedString = PyString_AsString(returnedPyString);
  284. QCOMPARE(returnedString, expectedReturnedString);
  285. }
  286. // ----------------------------------------------------------------------------
  287. void ctkAbstractPythonManagerTester::testPythonModule_data()
  288. {
  289. QTest::addColumn<QString>("pythonCode");
  290. QTest::addColumn<QString>("inputModuleList");
  291. QTest::addColumn<QString>("expectedReturnedString");
  292. QTest::newRow("0") << ""
  293. << "__main__"
  294. << "__main__";
  295. QTest::newRow("1") << ""
  296. << "__main__.__builtins__"
  297. << "__builtin__";
  298. QTest::newRow("2") << "class foo: pass"
  299. << "__main__.foo"
  300. << "foo";
  301. QTest::newRow("3") << ""
  302. << "__main__.NOT_A_MODULE"
  303. << "";
  304. }
  305. //-----------------------------------------------------------------------------
  306. void ctkAbstractPythonManagerTester::testPythonObject()
  307. {
  308. QFETCH(QString, pythonCode);
  309. QFETCH(QString, inputPythonVariableNameAndFunction);
  310. QFETCH(QString, expectedReturnedString);
  311. this->PythonManager.executeString(pythonCode);
  312. PyObject* returnedPyObject = this->PythonManager.pythonObject(inputPythonVariableNameAndFunction);
  313. PyObject* returnedPyObjectString;
  314. if (returnedPyObject)
  315. {
  316. returnedPyObjectString = PyObject_GetAttrString(returnedPyObject, "__name__");
  317. }
  318. else
  319. {
  320. returnedPyObjectString = PyString_FromString("");
  321. }
  322. QString returnedString = PyString_AsString(returnedPyObjectString);
  323. QCOMPARE(returnedString, expectedReturnedString);
  324. }
  325. //-----------------------------------------------------------------------------
  326. void ctkAbstractPythonManagerTester::testPythonObject_data()
  327. {
  328. QTest::addColumn<QString>("pythonCode");
  329. QTest::addColumn<QString>("inputPythonVariableNameAndFunction");
  330. QTest::addColumn<QString>("expectedReturnedString");
  331. QTest::newRow("0") << "foo = []"
  332. << "__main__.foo.append"
  333. << "append";
  334. QTest::newRow("1") << ""
  335. << "__main__.__builtins__.dir"
  336. << "dir";
  337. QTest::newRow("2") << "class foo: bar = []"
  338. << "__main__.foo.bar.reverse"
  339. << "reverse";
  340. QTest::newRow("3") << ""
  341. << "__main__.__builtins__.NOT_A_FUNCTION"
  342. << "";
  343. }
  344. // ----------------------------------------------------------------------------
  345. CTK_TEST_MAIN(ctkAbstractPythonManagerTest)
  346. #include "moc_ctkAbstractPythonManagerTest.cpp"