Added catch2 unit testing framework

This commit is contained in:
Jarrett Johnson
2018-11-15 17:53:25 +01:00
committed by Thomas Holder
parent 6371955429
commit d859e67b5b
11 changed files with 304 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ REQUIREMENTS
- mmtf-cpp (for fast MMTF export, disable with --use-msgpackc=no)
- PyQt5, PyQt4, or PySide (optional, will fall back to Tk interface)
- glm
- catch2 (optional, enable with --testing)
SETUP OPTIONS

View File

@@ -19,6 +19,7 @@ Z* -------------------------------------------------------------------
#include"os_python.h"
#include"PyMOLGlobals.h"
#include"Base.h"
#include"OVLexicon.h"

View File

@@ -115,6 +115,19 @@ static int run_only_once = true;
#define API_SETUP_PYMOL_GLOBALS \
G = _api_get_pymol_globals(self)
/*
* C-level tests
*/
#ifdef _PYMOL_CTEST
#include "TestCmdTest2.h"
#else
static PyObject* CmdTest2(PyObject*, PyObject*)
{
PyErr_SetString(PyExc_NotImplementedError, "compile with --testing");
return nullptr;
}
#endif
/*
* Start a headless singleton instance in the current thread.
*
@@ -8685,6 +8698,7 @@ static PyMethodDef Cmd_methods[] = {
{"symexp", CmdSymExp, METH_VARARGS},
{"symmetry_copy", CmdSymmetryCopy, METH_VARARGS},
{"test", CmdTest, METH_VARARGS},
{"test2", CmdTest2, METH_VARARGS},
{"toggle", CmdToggle, METH_VARARGS},
{"matrix_copy", CmdMatrixCopy, METH_VARARGS},
{"transform_object", CmdTransformObject, METH_VARARGS},

22
layerCTest/Test.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include <iostream>
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
#include "Test.h"
#include "TestCmdTest2.h"
using PyMOL_TestAPI = pymol::test::PYMOL_TEST_API;
PyObject *PyMOL_TestAPI::PYMOL_TEST_SUCCESS = PConvAutoNone(Py_None);
PyObject *PyMOL_TestAPI::PYMOL_TEST_FAILURE = Py_BuildValue("i", -1);
PyObject *CmdTest2(PyObject *, PyObject *) {
int argc = 1;
char argv0[] = "pymol";
char *argv[] = {argv0};
auto result = Catch::Session().run(argc, argv);
if (!result) {
return PyMOL_TestAPI::PYMOL_TEST_SUCCESS;
} else {
return PyMOL_TestAPI::PYMOL_TEST_FAILURE;
}
}

60
layerCTest/Test.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <iostream>
#include <vector>
#include <functional>
#include <cstring>
#include <cmath>
#include "os_python.h"
#include "PConv.h"
#include <catch2/catch.hpp>
namespace pymol {
namespace test {
// Checks whether obj is zero'd out (Struct of all PoD Types without non-default
// values are 0)
template <typename T> static bool isStructZero(const T &obj) {
const auto size = sizeof(T);
std::vector<char> buffer{size, 0};
if (std::memcmp(buffer.data(), &obj, size)) {
return false;
}
return true;
}
// Checks whether array arr is zeroed
template <typename T>
static bool isArrayZero(const T *arr, const std::size_t len) {
auto bytelen = len * sizeof(T);
std::vector<char> buffer(bytelen, 0);
return std::memcmp(buffer.data(), arr, bytelen) == 0;
}
// Checks whether arrays are equal
template <typename T>
static bool isArrayEqual(const T *arr1, const T *arr2, const std::size_t len) {
return std::equal(arr1, arr1 + len, arr2);
}
// Checks whether type has all special member functions
template <typename T> static bool hasAllSpecialMemberFunctions()
{
return std::is_default_constructible<T>::value &&
std::is_copy_constructible<T>::value &&
std::is_copy_assignable<T>::value &&
std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value;
}
// Checks whether ptr is equal to nullptr
template <typename T> static bool isNullptr(const T *ptr) {
return ptr == nullptr;
}
struct PYMOL_TEST_API {
static PyObject *PYMOL_TEST_SUCCESS;
static PyObject *PYMOL_TEST_FAILURE;
};
}; // namespace test
}; // namespace pymol

View File

@@ -0,0 +1,3 @@
#include "os_python.h"
PyObject *CmdTest2(PyObject *, PyObject *);

View File

@@ -0,0 +1,10 @@
#include "MemoryDebug.h"
#include "Test.h"
TEST_CASE("VLA Classic Alloc", "[VLA_Classic]")
{
const std::size_t size = 10;
auto ptr = VLACalloc(int, size);
REQUIRE(pymol::test::isArrayZero(ptr, size));
VLAFree(ptr);
}

180
layerCTest/Test_VLA.cpp Normal file
View File

@@ -0,0 +1,180 @@
#include <array>
#include "Test.h"
#include "vla.h"
using namespace pymol::test;
using pymol::vla;
TEST_CASE("VLA Default", "[VLA]")
{
vla<int> myVLA;
REQUIRE(isNullptr(myVLA.data()));
}
TEST_CASE("VLA Alloc And Size", "[VLA]")
{
vla<int> myVLA(5);
REQUIRE(myVLA.size() == 5);
}
TEST_CASE("VLA Default Val", "[VLA]")
{
vla<int> myVLA(5, 0);
REQUIRE(myVLA.size() == 5);
REQUIRE(isArrayZero(myVLA.data(), myVLA.size()));
}
TEST_CASE("VLA Initializer List", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
const std::array<int, 5> myArr{1, 2, 3, 4, 5};
REQUIRE(isArrayEqual(myVLA.data(), myArr.data(), myVLA.size()));
}
TEST_CASE("VLA Special Member Functions", "[VLA]")
{
REQUIRE(hasAllSpecialMemberFunctions<vla<int>>());
}
TEST_CASE("VLA Copy Construct", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
auto copyVLA = myVLA;
REQUIRE(myVLA.size() == copyVLA.size());
REQUIRE(isArrayEqual(myVLA.data(), copyVLA.data(), myVLA.size()));
}
TEST_CASE("VLA Copy Assign", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
vla<int> copyVLA(1);
REQUIRE(myVLA.size() != copyVLA.size());
copyVLA = myVLA;
REQUIRE(myVLA.size() == copyVLA.size());
REQUIRE(isArrayEqual(myVLA.data(), copyVLA.data(), myVLA.size()));
}
TEST_CASE("VLA Move Construct", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
auto copyVLA = std::move(myVLA);
REQUIRE(copyVLA.size() == 5);
const std::array<int, 5> myArr{1, 2, 3, 4, 5};
REQUIRE(isArrayEqual(copyVLA.data(), myArr.data(), copyVLA.size()));
}
TEST_CASE("VLA Move Assign", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
vla<int> copyVLA(3);
copyVLA = std::move(myVLA);
REQUIRE(copyVLA.size() == 5);
const std::array<int, 5> myArr{1, 2, 3, 4, 5};
REQUIRE(isArrayEqual(copyVLA.data(), myArr.data(), copyVLA.size()));
}
TEST_CASE("Vector_Resize", "[VLA]")
{
vla<int> myVLA(5);
REQUIRE(myVLA.size() == 5);
myVLA.resize(3);
REQUIRE(myVLA.size() == 3);
vla<int> myVLA2;
myVLA2.resize(3);
REQUIRE(myVLA2.size() == 3);
}
TEST_CASE("FreeP", "[VLA]")
{
vla<int> myVLA(10);
REQUIRE(myVLA.size() == 10);
myVLA.freeP();
REQUIRE(myVLA.size() == 0);
REQUIRE(isNullptr(myVLA.data()));
}
TEST_CASE("Bool Cast", "[VLA]")
{
vla<int> myVLA;
REQUIRE(myVLA == nullptr);
vla<int> myVLA2(2);
REQUIRE(myVLA2 != nullptr);
myVLA2.freeP();
REQUIRE(myVLA2 == nullptr);
}
TEST_CASE("Index", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
REQUIRE(myVLA[2] == 3);
}
TEST_CASE("Range Based For", "[VLA]")
{
vla<int> myVLA{0, 1, 2, 3, 4};
std::size_t i{0u};
for (auto& m : myVLA) {
REQUIRE(myVLA[i] == i);
i++;
}
i = 0;
for (const auto& m : myVLA) {
REQUIRE(myVLA[i] == i);
i++;
}
}
TEST_CASE("To_StdVector", "[VLA]")
{
vla<int> myVLA{1, 2, 3, 4, 5};
auto myStdVec = myVLA.toStdVector();
REQUIRE(myStdVec.size() == myVLA.size());
REQUIRE(isArrayEqual(myStdVec.data(), myVLA.data(), myStdVec.size()));
}
TEST_CASE("From_StdVector", "[VLA]")
{
std::vector<int> myStdVec{1, 2, 3, 4, 5};
vla<int> myVLA(myStdVec);
REQUIRE(myStdVec.size() == myVLA.size());
REQUIRE(isArrayEqual(myStdVec.data(), myVLA.data(), myStdVec.size()));
}
TEST_CASE("From_VLACalloc", "[VLA]")
{
vla<int> myVLA(VLACalloc(int, 5));
REQUIRE(myVLA.size() == 5);
REQUIRE(isArrayZero(myVLA.data(), 5));
}
TEST_CASE("Classic_Copy", "[VLA]")
{
vla<int> myVLA(5, 10);
auto myVLACopy = VLACopy2(myVLA);
REQUIRE(isArrayEqual(myVLA.data(), myVLACopy.data(), myVLA.size()));
myVLACopy[1] = 100;
REQUIRE(myVLA[1] != myVLACopy[1]);
}
TEST_CASE("Classic_Check2", "[VLA]")
{
vla<int> myVLA(5);
VLACheck2(myVLA, 10);
REQUIRE(myVLA.size() >= 11);
}
TEST_CASE("Classic_Size2", "[VLA]")
{
vla<int> myVLA(5);
VLASize2(myVLA, 10);
REQUIRE(myVLA.size() == 10);
}
TEST_CASE("Classic_Free", "[VLA]")
{
vla<int> myVLA(5);
VLAFreeP(myVLA);
REQUIRE(isNullptr(myVLA.data()));
}
// vi:sw=2:expandtab

View File

@@ -439,6 +439,8 @@ def launch(args=None, block_input_hook=0):
if invocation.options.gui == 'pmg_qt':
if invocation.options.no_gui:
return _launch_no_gui()
elif invocation.options.testing:
return pymol._cmd.test2()
try:
from pmg_qt import pymol_qt_gui

View File

@@ -29,6 +29,7 @@ Options
--version display PyMOL version and exit
--retina use retina resolution (MacPyMOL) and set display_scale_factor=2
--gldebug use glDebugMessageCallback for GL debugging
--testing run pymol testing
-1 config_mouse one_button
-2 config_mouse two_button
@@ -170,6 +171,7 @@ if __name__=='pymol.invocation':
options.no_spacenav = 0
options.launch_status = 0
options.gldebug = 0
options.testing = 0
options.win_py = { 'irix':240,
'darwin': 214, # hmm...need to set to 192 for Leopard?...
@@ -281,6 +283,8 @@ if __name__=='pymol.invocation':
print(' Warning: --nospnav not available in Open-Source PyMOL')
elif a == "--gldebug":
options.gldebug = 1
elif a == "--testing":
options.testing = 1
else:
# double hypen signals end of PyMOL arguments
if python_script == None:

View File

@@ -25,6 +25,7 @@ class options:
no_glut = True
use_msgpackc = 'guess'
help_distutils = False
testing = False
try:
import argparse
@@ -47,6 +48,8 @@ try:
"shared library; no: disable fast MMTF load support")
parser.add_argument('--help-distutils', action="store_true",
help="show help for distutils options and exit")
parser.add_argument('--testing', action="store_true",
help="Build C-level tests")
options, sys.argv[1:] = parser.parse_known_args(namespace=options)
except ImportError:
print("argparse not available")
@@ -311,6 +314,10 @@ if options.no_glut:
("_PYMOL_NO_MAIN", None),
]
if options.testing:
pymol_src_dirs += ["layerCTest"]
def_macros += [("_PYMOL_CTEST", None)]
inc_dirs = list(pymol_src_dirs)
#============================================================================