make test code public to simplify creation of tests in other projects

This commit is contained in:
Martchus 2016-02-06 02:52:06 +01:00
parent df9c0573bf
commit 9abe04ce06
7 changed files with 207 additions and 59 deletions

View File

@ -26,6 +26,8 @@ set(HEADER_FILES
math/math.h math/math.h
misc/memory.h misc/memory.h
misc/random.h misc/random.h
tests/testutils.h
tests/cppunit.h
) )
set(SRC_FILES set(SRC_FILES
application/argumentparser.cpp application/argumentparser.cpp
@ -45,6 +47,7 @@ set(SRC_FILES
io/path.cpp io/path.cpp
math/math.cpp math/math.cpp
misc/random.cpp misc/random.cpp
tests/testutils.cpp
) )
set(TEST_HEADER_FILES set(TEST_HEADER_FILES
@ -126,7 +129,7 @@ if(NOT TARGET check)
enable_testing() enable_testing()
endif() endif()
add_executable(${META_PROJECT_NAME}_tests EXCLUDE_FROM_ALL ${TEST_HEADER_FILES} ${TEST_SRC_FILES}) add_executable(${META_PROJECT_NAME}_tests EXCLUDE_FROM_ALL ${TEST_HEADER_FILES} ${TEST_SRC_FILES})
target_link_libraries(${META_PROJECT_NAME}_tests c++utilities cppunit) target_link_libraries(${META_PROJECT_NAME}_tests ${META_PROJECT_NAME} cppunit)
set_target_properties(${META_PROJECT_NAME}_tests PROPERTIES CXX_STANDARD 11) set_target_properties(${META_PROJECT_NAME}_tests PROPERTIES CXX_STANDARD 11)
add_test(NAME ${META_PROJECT_NAME}_cppunit COMMAND ${META_PROJECT_NAME}_tests -p "${CMAKE_CURRENT_SOURCE_DIR}/testfiles") add_test(NAME ${META_PROJECT_NAME}_cppunit COMMAND ${META_PROJECT_NAME}_tests -p "${CMAKE_CURRENT_SOURCE_DIR}/testfiles")
add_dependencies(check ${META_PROJECT_NAME}_tests) add_dependencies(check ${META_PROJECT_NAME}_tests)

View File

@ -42,7 +42,9 @@ HEADERS += \
io/path.h \ io/path.h \
math/math.h \ math/math.h \
misc/memory.h \ misc/memory.h \
misc/random.h misc/random.h \
tests/testutils.h \
tests/cppunit.h \
SOURCES += \ SOURCES += \
application/argumentparser.cpp \ application/argumentparser.cpp \
@ -61,7 +63,8 @@ SOURCES += \
io/inifile.cpp \ io/inifile.cpp \
io/path.cpp \ io/path.cpp \
math/math.cpp \ math/math.cpp \
misc/random.cpp misc/random.cpp \
tests/testutils.cpp
OTHER_FILES += \ OTHER_FILES += \
README.md \ README.md \
@ -82,7 +85,7 @@ mingw-w64-install {
target.path = $$(INSTALL_ROOT)/lib target.path = $$(INSTALL_ROOT)/lib
INSTALLS += target INSTALLS += target
} }
for(dir, $$list(application io conversion chrono math misc)) { for(dir, $$list(application io conversion chrono math misc tests)) {
eval(inc_$${dir} = $${dir}) eval(inc_$${dir} = $${dir})
inc_$${dir}.path = $$(INSTALL_ROOT)/include/$$projectname/$${dir} inc_$${dir}.path = $$(INSTALL_ROOT)/include/$$projectname/$${dir}
inc_$${dir}.files = $${dir}/*.h inc_$${dir}.files = $${dir}/*.h

View File

@ -1,49 +1 @@
#include "../application/argumentparser.h" #include "cppunit.h"
#include "../application/failure.h"
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <iostream>
using namespace std;
using namespace ApplicationUtilities;
using namespace CPPUNIT_NS;
namespace UnitTests {
string testFilesPath("tests");
}
int main(int argc, char **argv)
{
// setup argument parser
ArgumentParser parser;
HelpArgument helpArg(parser);
Argument testFilesPathArg("test-files-path", "p", "specifies the path to the directory with test files");
testFilesPathArg.setRequiredValueCount(1);
testFilesPathArg.setValueNames({"path"});
testFilesPathArg.setCombinable(true);
parser.setMainArguments({&testFilesPathArg, &helpArg});
try {
// parse arguments
parser.parseArgs(argc, argv);
if(testFilesPathArg.isPresent()) {
UnitTests::testFilesPath = testFilesPathArg.values().front();
}
cerr << "Direcoty for test files: " << UnitTests::testFilesPath << endl;
// run tests
TextUi::TestRunner runner;
TestFactoryRegistry &registry = TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
return !runner.run(string(), false);
} catch(const Failure &failure) {
cerr << "Invalid arguments specified: " << failure.what() << endl;
return -1;
}
}

32
tests/cppunit.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef CPPUNIT_H
#define CPPUNIT_H
#include "./testutils.h"
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <iostream>
using namespace std;
using namespace TestUtilities;
using namespace CPPUNIT_NS;
/*!
* \brief Performs unit tests using cppunit.
*/
int main(int argc, char **argv)
{
TestApplication testApp(argc, argv);
if(testApp) {
// run tests
TextUi::TestRunner runner;
TestFactoryRegistry &registry = TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
return !runner.run(string(), false);
} else {
return -1;
}
}
#endif // CPPUNIT_H

View File

@ -1,3 +1,5 @@
#include "./testutils.h"
#include "../io/binaryreader.h" #include "../io/binaryreader.h"
#include "../io/binarywriter.h" #include "../io/binarywriter.h"
#include "../io/bitreader.h" #include "../io/bitreader.h"
@ -71,7 +73,7 @@ void IoTests::testBinaryReader()
{ {
// read test file // read test file
fstream testFile; fstream testFile;
testFile.open(UnitTests::testFilesPath + "/some_data", ios_base::in | ios_base::binary); testFile.open(TestUtilities::testFilePath("some_data"), ios_base::in | ios_base::binary);
BinaryReader reader(&testFile); BinaryReader reader(&testFile);
CPPUNIT_ASSERT(reader.readUInt16LE() == 0x0102u); CPPUNIT_ASSERT(reader.readUInt16LE() == 0x0102u);
CPPUNIT_ASSERT(reader.readUInt16BE() == 0x0102u); CPPUNIT_ASSERT(reader.readUInt16BE() == 0x0102u);
@ -116,7 +118,7 @@ void IoTests::testBinaryWriter()
{ {
// prepare reading expected data // prepare reading expected data
fstream testFile; fstream testFile;
testFile.open(UnitTests::testFilesPath + "/some_data", ios_base::in | ios_base::binary); testFile.open(TestUtilities::testFilePath("some_data"), ios_base::in | ios_base::binary);
// prepare output stream // prepare output stream
stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary); stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
@ -214,7 +216,7 @@ void IoTests::testIniFile()
// prepare reading test file // prepare reading test file
fstream inputFile; fstream inputFile;
inputFile.exceptions(ios_base::failbit | ios_base::badbit); inputFile.exceptions(ios_base::failbit | ios_base::badbit);
inputFile.open(UnitTests::testFilesPath + "/test.ini", ios_base::in); inputFile.open(TestUtilities::testFilePath("test.ini"), ios_base::in);
IniFile ini; IniFile ini;
ini.parse(inputFile); ini.parse(inputFile);
@ -236,12 +238,12 @@ void IoTests::testIniFile()
// write values to another file // write values to another file
fstream outputFile; fstream outputFile;
outputFile.exceptions(ios_base::failbit | ios_base::badbit); outputFile.exceptions(ios_base::failbit | ios_base::badbit);
outputFile.open(UnitTests::testFilesPath + "/output.ini", ios_base::out | ios_base::trunc); outputFile.open(TestUtilities::testFilePath("output.ini"), ios_base::out | ios_base::trunc);
ini.make(outputFile); ini.make(outputFile);
// parse written values (again) // parse written values (again)
outputFile.close(); outputFile.close();
outputFile.open(UnitTests::testFilesPath + "/output.ini", ios_base::in); outputFile.open(TestUtilities::testFilePath("output.ini"), ios_base::in);
IniFile ini2; IniFile ini2;
ini2.parse(outputFile); ini2.parse(outputFile);
CPPUNIT_ASSERT(ini.data() == ini2.data()); CPPUNIT_ASSERT(ini.data() == ini2.data());
@ -254,7 +256,7 @@ void IoTests::testCopy()
{ {
// prepare streams // prepare streams
fstream testFile; fstream testFile;
testFile.open(UnitTests::testFilesPath + "/some_data", ios_base::in | ios_base::binary); testFile.open(TestUtilities::testFilePath("some_data"), ios_base::in | ios_base::binary);
testFile.exceptions(ios_base::failbit | ios_base::badbit); testFile.exceptions(ios_base::failbit | ios_base::badbit);
stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary); stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
outputStream.exceptions(ios_base::failbit | ios_base::badbit); outputStream.exceptions(ios_base::failbit | ios_base::badbit);

99
tests/testutils.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "./testutils.h"
#include "../application/failure.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;
using namespace ApplicationUtilities;
namespace TestUtilities {
TestApplication *TestApplication::m_instance = nullptr;
/*!
* \class TestApplication
* \brief The TestApplication class simplifies writing test applications.
* \remarks Only one instance is allowed at a time (singletone class).
*/
/*!
* \brief Constructs a TestApplication instance.
* \throws Throws std::runtime_error if an instance has already been created.
*/
TestApplication::TestApplication(int argc, char **argv) :
m_helpArg(m_parser),
m_testFilesPathArg("test-files-path", "p", "specifies the path of the directory with test files")
{
if(m_instance) {
throw runtime_error("only one TestApplication instance allowed at a time");
}
m_instance = this;
if(const char *testFilesPathEnv = getenv("TEST_FILE_PATH")) {
if(const auto len = strlen(testFilesPathEnv)) {
m_testFilesPathEnvValue.reserve(len + 1);
m_testFilesPathEnvValue += testFilesPathEnv;
m_testFilesPathEnvValue += '/';
}
}
m_testFilesPathArg.setRequiredValueCount(1);
m_testFilesPathArg.setValueNames({"path"});
m_testFilesPathArg.setCombinable(true);
m_parser.setMainArguments({&m_testFilesPathArg, &m_helpArg});
try {
m_parser.parseArgs(argc, argv);
cerr << "Directories used to search for testfiles: " << endl;
if(m_testFilesPathArg.isPresent()) {
if(!m_testFilesPathArg.values().front().empty()) {
cerr << (m_testFilesPathArgValue = m_testFilesPathArg.values().front() + '/') << endl;
} else {
cerr << (m_testFilesPathArgValue = "./") << endl;
}
}
if(!m_testFilesPathEnvValue.empty()) {
cerr << m_testFilesPathEnvValue << endl;
}
cerr << "./testfiles/" << endl << endl;
m_valid = true;
cerr << "Executing test cases ..." << endl;
} catch(const Failure &failure) {
cerr << "Invalid arguments specified: " << failure.what() << endl;
m_valid = false;
}
}
/*!
* \brief Destroys the TestApplication.
*/
TestApplication::~TestApplication()
{
m_instance = nullptr;
}
/*!
* \brief Returns the full path of tbe test file with the specified \a name.
*/
string TestApplication::testFilePath(const string &name) const
{
string path;
fstream file;
if(m_testFilesPathArg.isPresent()) {
file.open(path = m_testFilesPathArgValue + name, ios_base::in);
if(file.good()) {
return path;
}
}
if(!m_testFilesPathEnvValue.empty()) {
file.clear();
file.open(path = m_testFilesPathEnvValue + name, ios_base::in);
if(file.good()) {
return path;
}
}
return "./testfiles/" + name;
}
}

57
tests/testutils.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef TESTUTILS_H
#define TESTUTILS_H
#include "../application/argumentparser.h"
#include <string>
namespace TestUtilities {
class LIB_EXPORT TestApplication
{
public:
TestApplication(int argc, char **argv);
~TestApplication();
operator bool() const;
std::string testFilePath(const std::string &name) const;
static const TestApplication *instance();
private:
ApplicationUtilities::ArgumentParser m_parser;
ApplicationUtilities::HelpArgument m_helpArg;
ApplicationUtilities::Argument m_testFilesPathArg;
std::string m_testFilesPathArgValue;
std::string m_testFilesPathEnvValue;
bool m_valid;
static TestApplication *m_instance;
};
/*!
* \brief Returns whether the TestApplication instance is valid.
*/
inline TestApplication::operator bool() const
{
return m_valid;
}
/*!
* \brief Returns the current TestApplication instance.
*/
inline const TestApplication *TestApplication::instance()
{
return TestApplication::m_instance;
}
/*!
* \brief Convenience function which returns the full path of the test file with the specified \a name.
* \remarks A TestApplication must be present.
*/
inline LIB_EXPORT std::string testFilePath(const std::string &name)
{
return TestApplication::instance()->testFilePath(name);
}
}
#endif // TESTUTILS_H