Commit 87784c502d7bfa4d1f98f53b39fb4ea0bbee265d

Authored by Jürgen Knödlseder
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.
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  
... ...
... ... @@ -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
... ... @@ -389,9 +389,9 @@ void GTestSuite::test_assert(const bool&amp; assert,
389 389  
390 390 // Return
391 391 return;
392   -
393 392 }
394 393  
  394 +
395 395 /***********************************************************************//**
396 396 * @brief Test a value
397 397 *
... ...