Commit 87784c502d7bfa4d1f98f53b39fb4ea0bbee265d
1 parent
c0ca6bc6
Track results of Python unit tests (#1119)
The results of the Python unit test were not correctly tracked due to some subtle problems related to the way how C++ code is called through the Python interface. A kluge has been found and added to GTestSuite.i to retrieve the Python test results and to propagate them to the C++ test suite, so now Python test results are also written into the XML file, and the Python tests signal failure in case that something goes wrong.
Showing
4 changed files
with
62 additions
and
15 deletions
ChangeLog
| 1 | -2014-01-25 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> | |
| 1 | +2014-01-29 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> | |
| 2 | 2 | |
| 3 | 3 | * Bug fix Version 0.8.1 released |
| 4 | 4 | ================================ |
| 5 | 5 | |
| 6 | 6 | Added missing hyphens in pfiles synchronisation section (#1091) |
| 7 | 7 | Add configuration test script (#1091) |
| 8 | - GModelSky, GCTAModelBackground and GCTAModelRadialAcceptance write | |
| 9 | - id attribute into XML file (#1092) | |
| 8 | + GModelSky, GCTAModelBackground and GCTAModelRadialAcceptance write id attribute into XML file (#1092) | |
| 10 | 9 | Add GChatter typedef to Python interface |
| 11 | - GAMMALIB_CALDB takes precedence over CALDB environment var (#1094) | |
| 10 | + GAMMALIB_CALDB takes precedence over CALDB environment variable (#1094) | |
| 12 | 11 | Added vo module and GVOClient class to Python interface |
| 12 | + Track results of Python unit tests (#1119) | |
| 13 | + | |
| 13 | 14 | |
| 14 | 15 | 2014-01-18 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> |
| 15 | 16 | ... | ... |
NEWS
| ... | ... | @@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 00-08-01 |
| 2 | 2 | |
| 3 | 3 | Author(s) : Juergen Knoedlseder |
| 4 | 4 | |
| 5 | -25 January 2014 | |
| 5 | +29 January 2014 | |
| 6 | 6 | |
| 7 | 7 | |
| 8 | 8 | 1. Introduction |
| ... | ... | @@ -100,7 +100,12 @@ None |
| 100 | 100 | |
| 101 | 101 | 17. Test module |
| 102 | 102 | --------------- |
| 103 | -None | |
| 103 | +The results of the Python unit test were not correctly tracked due to | |
| 104 | +some subtle problems related to the way how C++ code is called through the | |
| 105 | +Python interface. A kluge has been found and added to GTestSuite.i to | |
| 106 | +retrieve the Python test results and to propagate them to the C++ test | |
| 107 | +suite, so now Python test results are also written into the XML file, and | |
| 108 | +the Python tests signal failure in case that something goes wrong. | |
| 104 | 109 | |
| 105 | 110 | |
| 106 | 111 | 18. Multi-wavelength interface | ... | ... |
pyext/GTestSuite.i
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * GTestSuite.i - Abstract test suite base class * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2012-2013 Jean-Baptiste Cayrou * | |
| 4 | + * copyright (C) 2012-2014 Jean-Baptiste Cayrou * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -94,6 +94,8 @@ public: |
| 94 | 94 | // Constructor |
| 95 | 95 | GPythonTestSuite(void) { |
| 96 | 96 | m_py_objects.clear(); |
| 97 | + m_py_names.clear(); | |
| 98 | + m_py_suites.clear(); | |
| 97 | 99 | } |
| 98 | 100 | |
| 99 | 101 | // Destructor |
| ... | ... | @@ -107,34 +109,78 @@ public: |
| 107 | 109 | void set(void) { |
| 108 | 110 | } |
| 109 | 111 | |
| 110 | - // Clone method | |
| 112 | + // Clone method (implementation makes this class non-abstract) | |
| 111 | 113 | GPythonTestSuite* clone(void) const { |
| 112 | 114 | return new GPythonTestSuite(*this); |
| 113 | 115 | } |
| 114 | 116 | |
| 115 | 117 | // Generic test function for Python callback |
| 116 | 118 | void test(void) { |
| 119 | + | |
| 120 | + // Initialise failures, errors and test cases before testing | |
| 121 | + int failures = m_py_suites[m_index]->failures(); | |
| 122 | + int errors = m_py_suites[m_index]->errors(); | |
| 123 | + int cases = m_py_suites[m_index]->size(); | |
| 124 | + | |
| 125 | + // Execute unit test suite | |
| 117 | 126 | PyObject* args = Py_BuildValue("()"); |
| 118 | 127 | PyObject* func = static_cast<PyObject*>(m_py_objects[m_index]); |
| 119 | 128 | PyObject* res = PyEval_CallObject(func, args); |
| 120 | 129 | Py_DECREF(args); |
| 130 | + | |
| 131 | + // Handle any error that occured during the test | |
| 121 | 132 | if (res == NULL) { |
| 122 | 133 | throw GPythonException::test_error("GPythonTestSuite::test"); |
| 123 | 134 | } |
| 124 | 135 | else { |
| 125 | 136 | Py_DECREF(res); |
| 126 | 137 | } |
| 138 | + | |
| 139 | + // Determine failures and errors after testing | |
| 140 | + m_failures += (m_py_suites[m_index]->failures() - failures); | |
| 141 | + m_errors += (m_py_suites[m_index]->errors() - errors); | |
| 142 | + | |
| 143 | + // Recover test cases | |
| 144 | + for (int i = cases; i < m_py_suites[m_index]->size(); ++i) { | |
| 145 | + | |
| 146 | + // Get actual test case | |
| 147 | + GTestCase& test = (*m_py_suites[m_index])[i]; | |
| 148 | + | |
| 149 | + // Kluge to set the test name correctly. For some reason, the | |
| 150 | + // m_index points to the first test case when the Python | |
| 151 | + // function is called, hence the name of the first test case | |
| 152 | + // gets prepended. The name is thus replaced by the correct | |
| 153 | + // name for all test cases following the first test case. | |
| 154 | + if (m_index > 0) { | |
| 155 | + std::string name = test.name(); | |
| 156 | + name.erase(0, m_names[0].length()); | |
| 157 | + name = m_names[m_index] + name; | |
| 158 | + test.name(name); | |
| 159 | + } | |
| 160 | + | |
| 161 | + // Clone the test case and push it on stack | |
| 162 | + m_tests.push_back(new GTestCase(test)); | |
| 163 | + | |
| 164 | + } // endfor: looped over all test cases | |
| 165 | + | |
| 166 | + // Return | |
| 127 | 167 | return; |
| 128 | 168 | } |
| 129 | 169 | |
| 130 | 170 | // Append Python callback to function lists |
| 131 | 171 | void append(PyObject* function, const std::string& name) { |
| 132 | 172 | m_py_objects.push_back(function); |
| 173 | + m_py_names.push_back(name); | |
| 174 | + m_py_suites.push_back(this); | |
| 133 | 175 | Py_INCREF(function); |
| 134 | 176 | this->GTestSuite::append(static_cast<pfunction>(&GPythonTestSuite::test), |
| 135 | 177 | name); |
| 136 | 178 | } |
| 137 | - std::vector<PyObject*> m_py_objects; //!< Python callback function list | |
| 179 | + | |
| 180 | + // Callback information | |
| 181 | + std::vector<PyObject*> m_py_objects; //!< Python callback function list | |
| 182 | + std::vector<std::string> m_py_names; //!< Python callback function names | |
| 183 | + std::vector<GTestSuite*> m_py_suites; //!< Python callback function pointers | |
| 138 | 184 | }; |
| 139 | 185 | %} |
| 140 | 186 | |
| ... | ... | @@ -194,11 +240,6 @@ public: |
| 194 | 240 | * @brief GTestSuite class extension |
| 195 | 241 | ***************************************************************************/ |
| 196 | 242 | %extend GTestSuite { |
| 197 | - /* | |
| 198 | - char *__str__() { | |
| 199 | - return gammalib::tochar(self->print()); | |
| 200 | - } | |
| 201 | - */ | |
| 202 | 243 | GTestCase& __getitem__(const int& index) { |
| 203 | 244 | if (index >= 0 && index < self->size()) { |
| 204 | 245 | return (*self)[index]; | ... | ... |
src/test/GTestSuite.cpp