Commit 6c2fbd2757669cdf5ed8bc49e7221202a3130a93
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.
Showing
2 changed files
with
71 additions
and
8 deletions
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& 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& 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& 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; | ... | ... |