233 lines
7.7 KiB
C++
233 lines
7.7 KiB
C++
// Copyright 2021 Nemanja Trifunovic
|
|
|
|
/*
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
do so, all subject to the following:
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
the above license grant, this restriction and the following disclaimer,
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
all derivative works of the Software, unless such copies or derivative
|
|
works are solely in the form of machine-executable object code generated by
|
|
a source language processor.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#ifndef F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
|
#define F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
namespace ftest
|
|
{
|
|
|
|
enum TestStatus {None, Success, Failed};
|
|
|
|
class Test
|
|
{
|
|
public:
|
|
Test(const char* name, const char* test_case_name) : m_name(name),
|
|
m_status(None), m_test_case_name(test_case_name) {}
|
|
virtual ~Test() {}
|
|
const std::string& name() const { return m_name; }
|
|
const std::string& case_name() const { return m_test_case_name; }
|
|
TestStatus status() const { return m_status; }
|
|
virtual void run()
|
|
{
|
|
std::cout << "[ RUN ] " << m_test_case_name << "." << m_name << std::endl;
|
|
m_status = Success;
|
|
run_internal(m_status);
|
|
if (m_status == Success) {
|
|
std::cout << "[ OK ] " << m_test_case_name << "." << m_name << std::endl;
|
|
} else {
|
|
std::cout << "[ FAILED ] " << m_test_case_name << "." << m_name << std::endl;
|
|
}
|
|
}
|
|
protected:
|
|
virtual void run_internal(TestStatus& status) = 0;
|
|
private:
|
|
std::string m_name;
|
|
TestStatus m_status;
|
|
std::string m_test_case_name;
|
|
};
|
|
|
|
class TestCase
|
|
{
|
|
public:
|
|
explicit TestCase(const char* name) : m_name(name) {}
|
|
const std::string& name() const { return m_name; }
|
|
void add_test(Test* test) { m_tests.push_back(test); }
|
|
size_t tests_count() const { return m_tests.size(); }
|
|
size_t status_count(TestStatus status) const
|
|
{
|
|
size_t ret = 0;
|
|
for (size_t i = 0; i < m_tests.size(); ++i)
|
|
if (m_tests[i]->status() == status)
|
|
++ret;
|
|
return ret;
|
|
}
|
|
void run()
|
|
{
|
|
std::cout << "[----------] " << m_tests.size() << " tests from " << name() << std::endl;
|
|
for (size_t i = 0; i < m_tests.size(); ++i)
|
|
m_tests[i]->run();
|
|
std::cout << "[----------] " << m_tests.size() << " tests from " << name() << std::endl;
|
|
}
|
|
void print_failed()
|
|
{
|
|
for (size_t i = 0; i < m_tests.size(); ++i)
|
|
if (m_tests[i]->status() == Failed)
|
|
std::cout << "[ FAILED ] " << m_name << "." << m_tests[i]->name() << std::endl;
|
|
}
|
|
private:
|
|
std::string m_name;
|
|
std::vector<Test*> m_tests;
|
|
};
|
|
|
|
inline TestCase* find_test_case(const std::string& name, const std::vector<TestCase*>& test_cases)
|
|
{
|
|
for (size_t i = 0; i < test_cases.size(); ++i) {
|
|
if (test_cases[i]->name() == name)
|
|
return test_cases[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
class TestDriver
|
|
{
|
|
public:
|
|
bool add_test(Test* test)
|
|
{
|
|
m_tests.push_back(test);
|
|
TestCase* test_case = find_test_case(test->case_name(), m_test_cases);
|
|
if (!test_case) {
|
|
test_case = new TestCase(test->case_name().c_str());
|
|
m_test_cases.push_back(test_case);
|
|
}
|
|
test_case->add_test(test);
|
|
return true;
|
|
}
|
|
|
|
int run_tests()
|
|
{
|
|
std::cout << "[==========] Running " << m_tests.size() <<
|
|
" tests from " << m_test_cases.size() << " test cases.\n";
|
|
|
|
for (size_t i = 0; i < m_test_cases.size(); ++i)
|
|
m_test_cases[i]->run();
|
|
|
|
std::cout << "[==========] " << m_tests.size() <<
|
|
" tests from " << m_test_cases.size() << " test cases ran.\n";
|
|
size_t passed_count = 0, failed_count = 0;
|
|
for (size_t i = 0; i < m_test_cases.size(); ++i) {
|
|
passed_count += m_test_cases[i]->status_count(Success);
|
|
failed_count += m_test_cases[i]->status_count(Failed);
|
|
}
|
|
if (passed_count)
|
|
std::cout << "[ PASSED ] " << passed_count << " tests.\n";
|
|
if (failed_count) {
|
|
std::cout << "[ FAILED ] " << failed_count << " tests, listed below:\n";
|
|
for (size_t i = 0; i < m_test_cases.size(); ++i)
|
|
m_test_cases[i]->print_failed();
|
|
std::cout << "\n " << failed_count << " FAILED TESTS\n";
|
|
}
|
|
|
|
return static_cast<int>(failed_count);
|
|
}
|
|
|
|
const std::vector<TestCase*>& test_cases() const { return m_test_cases; }
|
|
|
|
~TestDriver()
|
|
{
|
|
for (size_t i = 0; i < m_test_cases.size(); ++i)
|
|
delete m_test_cases[i];
|
|
for (size_t i = 0; i < m_tests.size(); ++i)
|
|
delete m_tests[i];
|
|
}
|
|
private:
|
|
std::vector<TestCase*> m_test_cases;
|
|
std::vector<Test*> m_tests;
|
|
};
|
|
|
|
#ifdef F_TEST_NO_MAIN
|
|
extern TestDriver testdriver;
|
|
#else
|
|
TestDriver testdriver;
|
|
#endif
|
|
|
|
} // namespace ftest
|
|
|
|
#define EXPECT_TRUE(what) \
|
|
do { \
|
|
if (!(what)) {\
|
|
std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
|
|
std::cout << " Expected : true\n Actual : "; \
|
|
std::cout << (what ? "true" : "false"); \
|
|
std::cout << std::endl; \
|
|
status = ftest::Failed; \
|
|
} \
|
|
} while (false)
|
|
|
|
#define EXPECT_FALSE(what) \
|
|
do { \
|
|
if (what) {\
|
|
std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
|
|
std::cout << " Expected : false\n Actual : "; \
|
|
std::cout << (what ? "true" : "false"); \
|
|
std::cout << std::endl; \
|
|
status = ftest::Failed; \
|
|
} \
|
|
} while (false)
|
|
|
|
#define FTEST_EXPECT_PREDICATE(a, b, predicate_operator) \
|
|
do { \
|
|
if (!(a predicate_operator b)) { \
|
|
std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
|
|
status = ftest::Failed; \
|
|
} \
|
|
} while (false)
|
|
|
|
#define EXPECT_EQ(a, b) FTEST_EXPECT_PREDICATE(a, b, ==)
|
|
#define EXPECT_NE(a, b) FTEST_EXPECT_PREDICATE(a, b, !=)
|
|
#define EXPECT_LT(a, b) FTEST_EXPECT_PREDICATE(a, b, <)
|
|
#define EXPECT_LE(a, b) FTEST_EXPECT_PREDICATE(a, b, <=)
|
|
#define EXPECT_GT(a, b) FTEST_EXPECT_PREDICATE(a, b, >)
|
|
#define EXPECT_GE(a, b) FTEST_EXPECT_PREDICATE(a, b, >=)
|
|
|
|
#define TEST(f_test_case_name, f_test_name) \
|
|
class F_TEST_##f_test_case_name##f_test_name : public ftest::Test \
|
|
{ \
|
|
public: \
|
|
F_TEST_##f_test_case_name##f_test_name(const char* test_name, const char* test_case_name) : \
|
|
ftest::Test(test_name, test_case_name) {} \
|
|
void run_internal(ftest::TestStatus& status); \
|
|
static bool m_registered; \
|
|
}; \
|
|
bool F_TEST_##f_test_case_name##f_test_name::m_registered = \
|
|
ftest::testdriver.add_test \
|
|
(new F_TEST_##f_test_case_name##f_test_name(#f_test_name, #f_test_case_name)); \
|
|
void F_TEST_##f_test_case_name##f_test_name::run_internal(ftest::TestStatus& status)
|
|
|
|
#ifndef F_TEST_NO_MAIN
|
|
int main (int /*argc*/, const char** /*argv*/)
|
|
{
|
|
return ftest::testdriver.run_tests();
|
|
}
|
|
#endif //F_TEST_NO_MAIN
|
|
|
|
#endif // F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|