Commit 6c2fbd2757669cdf5ed8bc49e7221202a3130a93

Authored by Jürgen Knödlseder
1 parent 40223c82

Make class non-abstract and implement Python testing.

As Python version < 3 does not support abstract base classes, we had to make GTestSuite abstract so that we can derive a test suite in Python.

A special append method was added to the Python interface that takes a PyObject as argument. The PyObject pointer is stored in a void array of pointers that is kept along with the function pointers. For a C++ function, the Python pointers are NULL. For a Python function, the C++ pointers are NULL. The run method checks then whether a C++ pointer or a Python pointer exists, and performs the adequat run command.
include/GTestSuite.hpp
1 1 /***************************************************************************
2   - * GTestSuite.hpp - Test Suite class for GammaLib *
  2 + * GTestSuite.hpp - Test Suite class for GammaLib *
3 3 * ----------------------------------------------------------------------- *
4 4 * copyright (C) 2012 by Jean-Baptiste Cayrou *
5 5 * ----------------------------------------------------------------------- *
... ... @@ -45,7 +45,7 @@ typedef void (GTestSuite::*pfunction)(void);
45 45 /***********************************************************************//**
46 46 * @class GTestSuite
47 47 *
48   - * @brief Abstract test suite class for unit testing on GammaLib fixtures
  48 + * @brief Test suite base class for unit testing on GammaLib fixtures
49 49 *
50 50 * @todo Detailed explanation.
51 51 ***************************************************************************/
... ... @@ -63,13 +63,11 @@ public:
63 63 GTestCase& operator[](const int& index);
64 64 const GTestCase& operator[](const int& index) const;
65 65  
66   - // Pure virtual methods
67   - virtual void set(void) = 0;
68   -
69 66 // Other methods
70 67 void clear(void);
71 68 int size(void) const;
72 69 void append(pfunction function, const std::string& name);
  70 + virtual void set(void);
73 71 virtual bool run(void);
74 72 std::string name(void) const;
75 73 void name(const std::string& name);
... ... @@ -94,6 +92,7 @@ public:
94 92 int success(void) const;
95 93 time_t timestamp(void) const;
96 94 double duration(void) const;
  95 + std::string print(void) const;
97 96  
98 97 // Old methods (will become obsolete)
99 98 void add_test(pfunction function, const std::string& name);
... ... @@ -107,8 +106,6 @@ protected:
107 106  
108 107 // Protected members
109 108 std::string m_name; //!< Name of the test suite
110   - std::vector<pfunction> m_functions; //!< Test functions of this suite
111   - std::vector<std::string> m_names; //!< Names of test functions
112 109 std::vector<GTestCase*> m_tests; //!< List of test results
113 110 std::vector<GTestCase*> m_stack_try; //!< Stack for nested try blocks
114 111 int m_index; //!< Index of actual test function
... ... @@ -117,6 +114,11 @@ protected:
117 114 GLog m_log; //!< Log
118 115 time_t m_timestamp; //!< Timestamp
119 116  
  117 + // Public members so that the the swig extension can access them
  118 +public:
  119 + std::vector<std::string> m_names; //!< Names of test functions
  120 + std::vector<pfunction> m_functions; //!< Test functions of this suite
  121 + std::vector<void*> m_py_object; //!< Test Python functions of this suite
120 122 };
121 123  
122 124 #endif /* GTESTSUITE_HPP */
... ...
src/test/GTestSuite.cpp
... ... @@ -44,6 +44,11 @@ pthread_attr_t gomp_thread_attr;
44 44 #endif
45 45 #endif
46 46  
  47 +/* __ Python section ____________________________________________________ */
  48 +#ifdef HAVE_PYTHON
  49 +#include "Python.h"
  50 +#endif
  51 +
47 52 /* __ Method name definitions ____________________________________________ */
48 53 #define G_OP_ACCESS "GTestSuite::operator[](int&)"
49 54 #define G_TRY_SUCCESS "GTestSuite::test_try_success()"
... ... @@ -241,6 +246,7 @@ void GTestSuite::append(const pfunction function, const std::string&amp; name)
241 246 {
242 247 // Add test function pointer and name to suite
243 248 m_functions.push_back(function);
  249 + m_py_object.push_back(NULL);
244 250 m_names.push_back(name);
245 251  
246 252 // Return
... ... @@ -249,6 +255,21 @@ void GTestSuite::append(const pfunction function, const std::string&amp; name)
249 255  
250 256  
251 257 /***********************************************************************//**
  258 + * @brief Set test functions
  259 + *
  260 + * Dummy set method that does not set any test function. This method does
  261 + * only exist since Python version < 3 does not know abstract classes, but
  262 + * we want to use GTestSuite to derive Python test classes. Thus, GTestSuite
  263 + * is not abstract.
  264 + ***************************************************************************/
  265 +void GTestSuite::set(void)
  266 +{
  267 + // Return
  268 + return;
  269 +}
  270 +
  271 +
  272 +/***********************************************************************//**
252 273 * @brief Run all tests in test suite
253 274 *
254 275 * Executes all test functions that have been appended to the test suite.
... ... @@ -276,7 +297,11 @@ bool GTestSuite::run(void)
276 297 for (m_index = 0; m_index < m_functions.size(); ++m_index) {
277 298  
278 299 // Continue only if function is valid
  300 + #ifdef HAVE_PYTHON
  301 + if (m_functions[m_index] != NULL || m_py_object[m_index] != NULL) {
  302 + #else
279 303 if (m_functions[m_index] != NULL) {
  304 + #endif
280 305  
281 306 // Save the number of errors and failures before test
282 307 // execution. We use this after the test to see if
... ... @@ -303,9 +328,22 @@ bool GTestSuite::run(void)
303 328 clock_t t_start = clock();
304 329 #endif
305 330  
306   - // Execute test function
  331 + // Execute test function. If Python bindings are enabled we check
  332 + // if we have a C++ function or a Python object.
307 333 try {
  334 + #ifdef HAVE_PYTHON
  335 + if (m_functions[m_index] != NULL) {
  336 + (this->*(m_functions[m_index]))();
  337 + }
  338 + else if (m_py_object[m_index] != NULL) {
  339 + PyObject* args = Py_BuildValue("()");
  340 + PyObject* func = static_cast<PyObject*>(m_py_object[m_index]);
  341 + PyEval_CallObject(func, args);
  342 + Py_DECREF(args);
  343 + }
  344 + #else
308 345 (this->*(m_functions[m_index]))();
  346 + #endif
309 347 }
310 348 catch(std::exception& e) {
311 349  
... ... @@ -817,6 +855,27 @@ double GTestSuite::duration(void) const
817 855 }
818 856  
819 857  
  858 +/***********************************************************************//**
  859 + * @brief Print test suite information
  860 + ***************************************************************************/
  861 +std::string GTestSuite::print(void) const
  862 +{
  863 + // Initialise result string
  864 + std::string result;
  865 +
  866 + // Append header
  867 + result.append("=== GTestSuite ===");
  868 + result.append("\n"+parformat("Name")+m_name);
  869 + result.append("\n"+parformat("Number of functions")+str(m_names.size()));
  870 + result.append("\n"+parformat("Number of executed tests")+str(size()));
  871 + result.append("\n"+parformat("Number of errors")+str(errors()));
  872 + result.append("\n"+parformat("Number of failures")+str(failures()));
  873 +
  874 + // Return result
  875 + return result;
  876 +}
  877 +
  878 +
820 879 /*==========================================================================
821 880 = =
822 881 = Private methods =
... ... @@ -831,6 +890,7 @@ void GTestSuite::init_members(void)
831 890 // Initialise members
832 891 m_name = "Unnamed Test Suite";
833 892 m_functions.clear();
  893 + m_py_object.clear();
834 894 m_names.clear();
835 895 m_tests.clear();
836 896 m_stack_try.clear();
... ... @@ -860,6 +920,7 @@ void GTestSuite::copy_members(const GTestSuite&amp; suite)
860 920 // Copy members
861 921 m_name = suite.m_name;
862 922 m_functions = suite.m_functions;
  923 + m_py_object = suite.m_py_object;
863 924 m_names = suite.m_names;
864 925 m_tests = suite.m_tests;
865 926 m_stack_try = suite.m_stack_try;
... ...