diff --git a/test/macro.h b/test/macro.h index 4ef88c4..f013ed9 100644 --- a/test/macro.h +++ b/test/macro.h @@ -9,10 +9,12 @@ printf("FAIL: %s:%d: EXPECT(%s) failed\n", __FILE__, __LINE__, #x); \ } -#define EXPECT_EQ(a, b) \ - if (a != b) { \ - std::cout << "FAIL: " << __FILE__ << ":" << __LINE__ \ - << ": EXPECT_EQ(" << #a << ", " << #b ") failed with lhs=" << a << " and rhs=" << b << std::endl; \ +#define EXPECT_EQ(a, b) \ + if (a != b) { \ + std::cout << "FAIL: " << __FILE__ << ":" << __LINE__ \ + << ": EXPECT_EQ(" << #a << ", " << #b ") failed with" \ + << " lhs='" << a << "' and rhs='" << b << "'" << std::endl; \ + Test::TestSuite::the().currentTestCaseFailed(); \ } #endif // TEST_H diff --git a/test/main.cpp b/test/main.cpp index 74220eb..f378b2f 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,120 +1,8 @@ -#include // fopen, printf, stdout -#include -#include - -#include "macro.h" -#include "util/argparser.h" - -FILE* output; -FILE* null; - -bool runParser(std::vector arguments, std::function initializer = {}) -{ - stdout = null; - - Util::ArgParser parser; - if (initializer) { - initializer(parser); - } - - arguments.insert(arguments.begin(), "app"); - auto result = parser.parse(arguments.size(), arguments.data()); - - stdout = output; - return result; -} +#include "testsuite.h" int main(int, const char*[]) { - output = stdout; - null = fopen("/dev/null", "w"); // Windows: nul - - printf("Test project\n"); - - // No arguments - { - auto result = runParser({}); - EXPECT_EQ(result, true); - } - - // Bool options - { - // Short option - bool boolOpt1 = false; - auto result = runParser({ "-b" }, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - } - { - // Short option, not given - bool boolOpt1 = false; - auto result = runParser({}, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, false); - } - { - // Long option - bool boolOpt1 = false; - auto result = runParser({ "--bool" }, [&](auto& parser) { - parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - } - { - // Long option, not given - bool boolOpt1 = false; - auto result = runParser({}, [&](auto& parser) { - parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, false); - } - { - // Allow both short and long option, provide short - bool boolOpt1 = false; - auto result = runParser({ "-b" }, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - } - { - // Allow both short and long option, provide long - bool boolOpt1 = false; - auto result = runParser({ "--bool" }, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - } - { - // Allow both short and long option, provide both - bool boolOpt1 = false; - auto result = runParser({ "-b", "--bool" }, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - } - - // .. - { - // - bool boolOpt1 = false; - std::string stringOpt1 = ""; - auto result = runParser({ "-b", "something", "-s", "my-value" }, [&](auto& parser) { - parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr); - parser.addOption(stringOpt1, 's', nullptr, nullptr, nullptr, nullptr, Util::ArgParser::Required::Yes); - }); - EXPECT_EQ(result, true); - EXPECT_EQ(boolOpt1, true); - EXPECT_EQ(stringOpt1, ""); - } + Test::TestSuite::the().run(); // // bool tests // test('o', "option", { "-o" }, true); @@ -150,8 +38,5 @@ int main(int, const char*[]) // ./help -o something -a my-value // -a has required argument, but something should stop option parsing - printf("Completed running tests\n"); - - fclose(null); return 0; } diff --git a/test/testcase.h b/test/testcase.h new file mode 100644 index 0000000..fd7ed6c --- /dev/null +++ b/test/testcase.h @@ -0,0 +1,45 @@ +#ifndef TEST_CASE_H +#define TEST_CASE_H + +#include +#include + +#define __TEST_CASE_FUNCTION(x) __test##x +#define __TEST_CASE_STRUCT(x) __testStruct##x + +#define TEST_CASE(x) \ + static void __TEST_CASE_FUNCTION(x)(); \ + struct __TEST_CASE_STRUCT(x) { \ + __TEST_CASE_STRUCT(x) \ + () \ + { \ + Test::TestSuite::the().addCase( \ + *new Test::TestCase(#x, __TEST_CASE_FUNCTION(x))); \ + } \ + }; \ + static struct __TEST_CASE_STRUCT(x) __TEST_CASE_STRUCT(x); \ + static void __TEST_CASE_FUNCTION(x)() + +namespace Test { + +using testFunction = std::function; + +class TestCase { +public: + TestCase(const char* name, testFunction&& function) + : m_name(name) + , m_function(function) + { + } + + const char* name() const { return m_name; } + const testFunction& function() const { return m_function; } + +private: + const char* m_name { nullptr }; + testFunction m_function; +}; + +} // namespace Test + +#endif // TEST_CASE_H diff --git a/test/testsuite.cpp b/test/testsuite.cpp new file mode 100644 index 0000000..3b73e43 --- /dev/null +++ b/test/testsuite.cpp @@ -0,0 +1,40 @@ +#include // size_t +#include // fclose, fopen, printf, stdout + +#include "testsuite.h" + +namespace Test { + +TestSuite::TestSuite(s) +{ + m_outputStd = stdout; + m_outputNull = fopen("/dev/null", "w"); // Windows: nul +} + +TestSuite::~TestSuite() +{ + fclose(m_outputNull); +} + +void TestSuite::run() +{ + printf("TestSuite: %d cases have been added!\n", (int)m_cases.size()); + + size_t caseFailedCount = 0; + + for(auto& testCase : m_cases) { + printf("%s\n", testCase.name()); + m_currentTestCasePassed = true; + + testCase.function()(); + + if (!m_currentTestCasePassed) { + caseFailedCount++; + } + } + + int percentagePassed = (1 - caseFailedCount / (float)m_cases.size()) * 100; + printf("Passed %d%% of tests\n", percentagePassed); +} + +} // namespace Test diff --git a/test/testsuite.h b/test/testsuite.h new file mode 100644 index 0000000..9ab8ea0 --- /dev/null +++ b/test/testsuite.h @@ -0,0 +1,34 @@ +#ifndef TEST_SUITE_H +#define TEST_SUITE_H + +#include // FILE +#include + +#include "testcase.h" +#include "util/singleton.h" + +namespace Test { + +class TestSuite final : public Util::Singleton { +public: + TestSuite(s); + virtual ~TestSuite(); + + void run(); + void addCase(const TestCase& testCase) { m_cases.push_back(testCase); } + void currentTestCaseFailed() { m_currentTestCasePassed = false; } + + FILE* outputStd() const { return m_outputStd; } + FILE* outputNull() const { return m_outputNull; } + +private: + bool m_currentTestCasePassed { true }; + FILE* m_outputStd { nullptr }; + FILE* m_outputNull { nullptr }; + + std::vector m_cases; +}; + +} // namespace Test + +#endif // TEST_SUITE_H