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 * Bug fix Version 0.8.1 released 3 * Bug fix Version 0.8.1 released
4 ================================ 4 ================================
5 5
6 Added missing hyphens in pfiles synchronisation section (#1091) 6 Added missing hyphens in pfiles synchronisation section (#1091)
7 Add configuration test script (#1091) 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 Add GChatter typedef to Python interface 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 Added vo module and GVOClient class to Python interface 11 Added vo module and GVOClient class to Python interface
  12 + Track results of Python unit tests (#1119)
  13 +
13 14
14 2014-01-18 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 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,7 +2,7 @@ New Features and Important Changes in GammaLib 00-08-01
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -25 January 2014 5 +29 January 2014
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -100,7 +100,12 @@ None @@ -100,7 +100,12 @@ None
100 100
101 17. Test module 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 18. Multi-wavelength interface 111 18. Multi-wavelength interface
pyext/GTestSuite.i
1 /*************************************************************************** 1 /***************************************************************************
2 * GTestSuite.i - Abstract test suite base class * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -94,6 +94,8 @@ public: @@ -94,6 +94,8 @@ public:
94 // Constructor 94 // Constructor
95 GPythonTestSuite(void) { 95 GPythonTestSuite(void) {
96 m_py_objects.clear(); 96 m_py_objects.clear();
  97 + m_py_names.clear();
  98 + m_py_suites.clear();
97 } 99 }
98 100
99 // Destructor 101 // Destructor
@@ -107,34 +109,78 @@ public: @@ -107,34 +109,78 @@ public:
107 void set(void) { 109 void set(void) {
108 } 110 }
109 111
110 - // Clone method 112 + // Clone method (implementation makes this class non-abstract)
111 GPythonTestSuite* clone(void) const { 113 GPythonTestSuite* clone(void) const {
112 return new GPythonTestSuite(*this); 114 return new GPythonTestSuite(*this);
113 } 115 }
114 116
115 // Generic test function for Python callback 117 // Generic test function for Python callback
116 void test(void) { 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 PyObject* args = Py_BuildValue("()"); 126 PyObject* args = Py_BuildValue("()");
118 PyObject* func = static_cast<PyObject*>(m_py_objects[m_index]); 127 PyObject* func = static_cast<PyObject*>(m_py_objects[m_index]);
119 PyObject* res = PyEval_CallObject(func, args); 128 PyObject* res = PyEval_CallObject(func, args);
120 Py_DECREF(args); 129 Py_DECREF(args);
  130 +
  131 + // Handle any error that occured during the test
121 if (res == NULL) { 132 if (res == NULL) {
122 throw GPythonException::test_error("GPythonTestSuite::test"); 133 throw GPythonException::test_error("GPythonTestSuite::test");
123 } 134 }
124 else { 135 else {
125 Py_DECREF(res); 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 return; 167 return;
128 } 168 }
129 169
130 // Append Python callback to function lists 170 // Append Python callback to function lists
131 void append(PyObject* function, const std::string& name) { 171 void append(PyObject* function, const std::string& name) {
132 m_py_objects.push_back(function); 172 m_py_objects.push_back(function);
  173 + m_py_names.push_back(name);
  174 + m_py_suites.push_back(this);
133 Py_INCREF(function); 175 Py_INCREF(function);
134 this->GTestSuite::append(static_cast<pfunction>(&GPythonTestSuite::test), 176 this->GTestSuite::append(static_cast<pfunction>(&GPythonTestSuite::test),
135 name); 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,11 +240,6 @@ public:
194 * @brief GTestSuite class extension 240 * @brief GTestSuite class extension
195 ***************************************************************************/ 241 ***************************************************************************/
196 %extend GTestSuite { 242 %extend GTestSuite {
197 - /*  
198 - char *__str__() {  
199 - return gammalib::tochar(self->print());  
200 - }  
201 - */  
202 GTestCase& __getitem__(const int& index) { 243 GTestCase& __getitem__(const int& index) {
203 if (index >= 0 && index < self->size()) { 244 if (index >= 0 && index < self->size()) {
204 return (*self)[index]; 245 return (*self)[index];
src/test/GTestSuite.cpp
@@ -389,9 +389,9 @@ void GTestSuite::test_assert(const bool&amp; assert, @@ -389,9 +389,9 @@ void GTestSuite::test_assert(const bool&amp; assert,
389 389
390 // Return 390 // Return
391 return; 391 return;
392 -  
393 } 392 }
394 393
  394 +
395 /***********************************************************************//** 395 /***********************************************************************//**
396 * @brief Test a value 396 * @brief Test a value
397 * 397 *