/***************************************************************************
* GTestSuite.i - Abstract test suite base class *
* ----------------------------------------------------------------------- *
* copyright (C) 2012-2015 Jean-Baptiste Cayrou *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GTestSuite.i
* @brief Abstract test suite base class definition
* @author Jean-Baptiste Cayrou
*/
%{
/* Put headers and other declarations here that are needed for compilation */
#include "GTestSuite.hpp"
#include "GException.hpp"
/***********************************************************************//**
* @class GPythonException
*
* @brief Exception for Python tests
*
* This class implements an exception for the Python tests. It allows to
* catch any error message that occurs during Python unit testing.
***************************************************************************/
class GPythonException : public GExceptionHandler {
public:
class test_error : public GExceptionHandler {
public:
test_error(const std::string& origin) {
// Set origin and default message
m_origin = origin;
m_message = "";
// Set message
if (PyErr_Occurred()) {
// Fetch error type, value and traceback
PyObject *type = 0;
PyObject *value = 0;
PyObject *traceback = 0;
PyErr_Fetch(&type, &value, &traceback);
// Clear error message
PyErr_Clear();
// Extract message strings
PyObject *py_type = PyObject_Str(type);
PyObject *py_value = PyObject_Str(value);
char *c_type = PyString_AsString(py_type);
char *c_value = PyString_AsString(py_value);
m_message += std::string(c_type);
m_message += "\n";
m_message += std::string(c_value);
Py_DECREF(py_type);
Py_DECREF(py_value);
}
}
};
};
/***********************************************************************//**
* @class GPythonTestSuite
*
* @brief Test suite for Python tests
*
* This is the test suite that should be used for Python tests. It derives
* from the abstract GTestSuite base class and implements a generic test
* method that is used as callback for Python testing. The class keeps an
* array of PyObject pointers that is used to call the Python callbacks.
* The generic test method profits from the fact that the function index is
* a class member, hence the test method knows which Python callback function
* it should execute.
***************************************************************************/
class GPythonTestSuite : public GTestSuite {
public:
// Constructor
GPythonTestSuite(void) {
m_py_objects.clear();
m_py_names.clear();
m_py_suites.clear();
}
// Destructor
virtual ~GPythonTestSuite(void) {
for (size_t i = 0; i < m_py_objects.size(); ++i) {
Py_DECREF(m_py_objects[i]);
}
}
// Dummy set method (implementation makes this class non-abstract)
void set(void) {
}
// Clone method (implementation makes this class non-abstract)
GPythonTestSuite* clone(void) const {
return new GPythonTestSuite(*this);
}
// Classname method (implementation makes this class non-abstract)
std::string classname(void) const {
return ("GPythonTestSuite");
}
// Generic test function for Python callback
void test(void) {
// Initialise failures, errors and test cases before testing
int failures = m_py_suites[m_index]->failures();
int errors = m_py_suites[m_index]->errors();
int cases = m_py_suites[m_index]->size();
// Execute unit test suite
PyObject* args = Py_BuildValue("()");
PyObject* func = static_cast(m_py_objects[m_index]);
PyObject* res = PyEval_CallObject(func, args);
Py_DECREF(args);
// Handle any error that occured during the test
if (res == NULL) {
throw GPythonException::test_error("GPythonTestSuite::test");
}
else {
Py_DECREF(res);
}
// Determine failures and errors after testing
m_failures += (m_py_suites[m_index]->failures() - failures);
m_errors += (m_py_suites[m_index]->errors() - errors);
// Recover test cases
for (int i = cases; i < m_py_suites[m_index]->size(); ++i) {
// Get actual test case
GTestCase& test = (*m_py_suites[m_index])[i];
// Kluge to set the test name correctly. For some reason, the
// m_index points to the first test case when the Python
// function is called, hence the name of the first test case
// gets prepended. The name is thus replaced by the correct
// name for all test cases following the first test case.
if (m_index > 0) {
std::string name = test.name();
name.erase(0, m_names[0].length());
name = m_names[m_index] + name;
test.name(name);
}
// Clone the test case and push it on stack
m_tests.push_back(new GTestCase(test));
} // endfor: looped over all test cases
// Return
return;
}
// Append Python callback to function lists
void append(PyObject* function, const std::string& name) {
m_py_objects.push_back(function);
m_py_names.push_back(name);
m_py_suites.push_back(this);
Py_INCREF(function);
this->GTestSuite::append(static_cast(&GPythonTestSuite::test),
name);
}
// Callback information
std::vector m_py_objects; //!< Python callback function list
std::vector m_py_names; //!< Python callback function names
std::vector m_py_suites; //!< Python callback function pointers
};
%}
/***********************************************************************//**
* @class GTestSuite
*
* @brief Abstract test suite Python interface definition
***************************************************************************/
class GTestSuite : public GBase {
public:
// Constructors and destructors
GTestSuite(void);
GTestSuite(const GTestSuite& testsuite);
GTestSuite(const std::string& name);
virtual ~GTestSuite(void);
// Pure virtual methods
virtual GTestSuite* clone(void) const = 0;
virtual std::string classname(void) const = 0;
virtual void set(void) = 0;
// Methods
void clear(void);
int size(void) const;
bool run(void);
const std::string& name(void) const;
void name(const std::string& name);
void cout(const bool& flag);
void test_assert(const bool& result,
const std::string& name,
const std::string& message = "");
void test_value(const int& value,
const int& expected,
const std::string& name="",
const std::string& message="");
void test_value(const double& value,
const double& expected,
const double& eps = 1.0e-10,
const std::string& name="",
const std::string& message="");
void test_try(const std::string& name);
void test_try_success(void);
void test_try_failure(const std::string& message = "",
const std::string& type = "");
void test_try_failure(const std::exception& e);
GException::test_failure& exception_failure(const std::string& message);
GException::test_error& exception_error(const std::string& message);
const int& errors(void) const;
const int& failures(void) const;
int success(void) const;
const time_t& timestamp(void) const;
double duration(void) const;
};
/***********************************************************************//**
* @brief GTestSuite class extension
***************************************************************************/
%extend GTestSuite {
GTestCase& __getitem__(const int& index) {
if (index >= 0 && index < self->size()) {
return (*self)[index];
}
else {
throw GException::out_of_range("__getitem__(int)", index, self->size());
}
}
void __setitem__(const int& index, const GTestCase& val) {
if (index>=0 && index < self->size()) {
(*self)[index] = val;
return;
}
else {
throw GException::out_of_range("__setitem__(int)", index, self->size());
}
}
};
/***********************************************************************//**
* @class GTestSuite
*
* @brief Test suite Python interface definition
***************************************************************************/
class GPythonTestSuite : public GTestSuite {
public:
// Constructors and destructors
GPythonTestSuite(void);
virtual ~GPythonTestSuite(void);
// Methods
void set(void);
GPythonTestSuite* clone(void) const;
std::string classname(void) const;
void append(PyObject* function, const std::string& name);
};