Files
2026-08-16 18:24:52 +07:00

214 lines
7.7 KiB
C++

// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#if ENABLE_UNIT_TESTS
#include "../../lib/vstguifwd.h"
#include <string>
#include <string_view>
#include <list>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <stdexcept>
#include <any>
/** @page How-to write tests
How-to write tests:
1) include this file
2) optional: put the test code into namespaces: namespace VSTGUI { namespace UnitTest {
3) write tests with the function macro TEST_CASE(SuiteName, TestName) { TESTCODE }
Simple Example:
#include "unittests.h"
TEST_CASE (Example, OnePlusOneIsTwo)
{
auto result = 1+1;
EXPECT_EQ (result, 2)
}
Using a setup and teardown function and custom variable storage:
#include "unittests.h"
TEST_SUITE_SETUP (CViewContainerTest)
{
SharedPointer<CViewContainer> container = makeOwned<CViewContainer> (CRect (0, 0, 200, 200));
TEST_SUITE_SET_STORAGE (SharedPointer<CViewContainer>, container);
}
TEST_SUITE_TEARDOWN (CViewContainerTest)
{
TEST_SUITE_GET_STORAGE (SharedPointer<CViewContainer>) = nullptr;
}
TEST_CASE (CViewContainerTest, ChangeViewZOrder)
{
SharedPointer<CViewContainer>& container = TEST_SUITE_GET_STORAGE (SharedPointer<CViewContainer>);
...
}
*/
namespace VSTGUI {
namespace UnitTest {
//----------------------------------------------------------------------------------------------------
class error : public std::logic_error
{
public:
error (const char* file, size_t line, const char* str)
: std::logic_error (str), filePath (file), lineNo (line)
{
}
std::string filePath;
size_t lineNo;
};
#define VSTGUI_UNITTEST_MAKE_STRING_PRIVATE_DONT_USE(x) # x
#define VSTGUI_UNITTEST_MAKE_STRING(x) VSTGUI_UNITTEST_MAKE_STRING_PRIVATE_DONT_USE(x)
//----------------------------------------------------------------------------------------------------
#define TEST_CASE(suite, name) \
static void test##suite##name (VSTGUI::UnitTest::Context* context); \
static VSTGUI::UnitTest::TestRegistrar register##suite##name (VSTGUI_UNITTEST_MAKE_STRING(suite), \
VSTGUI_UNITTEST_MAKE_STRING(name), [](VSTGUI::UnitTest::Context* context) {\
test##suite##name (context); \
});\
void test##suite##name (VSTGUI::UnitTest::Context* context)
#define TEST_SUITE_SETUP(suite) \
static void setup##suite (VSTGUI::UnitTest::Context* context); \
static VSTGUI::UnitTest::TestRegistrar registerSetup##suite (VSTGUI_UNITTEST_MAKE_STRING(suite), \
[](VSTGUI::UnitTest::Context* context) { setup##suite (context); }, true);\
void setup##suite (VSTGUI::UnitTest::Context* context)
#define TEST_SUITE_TEARDOWN(suite) \
static void teardown##suite (VSTGUI::UnitTest::Context* context); \
static VSTGUI::UnitTest::TestRegistrar registerTeardown##suite (VSTGUI_UNITTEST_MAKE_STRING(suite), \
[](VSTGUI::UnitTest::Context* context) { teardown##suite (context); }, false);\
void teardown##suite (VSTGUI::UnitTest::Context* context)
#define TEST_SUITE_SET_STORAGE(Type, value) context->storage () = std::make_any<Type> (value)
#define TEST_SUITE_GET_STORAGE(Type) *std::any_cast<Type> (&context->storage ())
//----------------------------------------------------------------------------------------------------
#define EXPECT(condition) if (!(condition)) { throw VSTGUI::UnitTest::error (__FILE__, __LINE__, "Expected: " VSTGUI_UNITTEST_MAKE_STRING(condition)); }
#define FAIL(reason) { throw VSTGUI::UnitTest::error (__FILE__, __LINE__, "Failure: " reason); }
#define EXPECT_EXCEPTION(call, name) \
{ \
bool b = false; \
try \
{ \
call; \
} \
catch (const std::exception& error) \
{ \
EXPECT (std::string_view (error.what ()) == std::string_view (name)); \
b = true; \
} \
EXPECT (b); \
}
#define EXPECT_TRUE(condition) EXPECT(condition)
#define EXPECT_FALSE(condition) EXPECT(!condition)
#define EXPECT_EQ(var1, var2) EXPECT ((var1) == (var2))
#define EXPECT_NE(var1, var2) EXPECT ((var1) != (var2))
//----------------------------------------------------------------------------------------------------
class Context;
class TestSuite;
//----------------------------------------------------------------------------------------------------
using TestFunction = std::function<void(Context*)>;
using SetupFunction = std::function<void(Context*)>;
using TeardownFunction = std::function<void(Context*)>;
using TestSuiteFunction = std::function<void(TestSuite*)>;
//----------------------------------------------------------------------------------------------------
class UnitTestRegistry
{
using TestSuites = std::list<TestSuite>;
using Iterator = TestSuites::const_iterator;
public:
static UnitTestRegistry& instance ();
void registerTestSuite (TestSuite&& testSuite);
Iterator begin () const { return testSuites.begin (); }
Iterator end () const { return testSuites.end (); }
TestSuite* find (const std::string& name);
private:
TestSuites testSuites;
};
//----------------------------------------------------------------------------------------------------
class TestSuite
{
using TestPair = std::pair<std::string, TestFunction>;
using Tests = std::list<TestPair>;
using Iterator = Tests::const_iterator;
public:
TestSuite (std::string&& name, TestSuiteFunction&& testCase);
TestSuite (TestSuite&& tc) noexcept;
void setSetupFunction (SetupFunction&& setupFunction);
void setTeardownFunction (TeardownFunction&& teardownFunction);
void setStorage (std::any&& s);
void registerTest (std::string&& name, TestFunction&& function);
const std::string& getName () const { return name; }
std::any& getStorage () const { return storage; }
Iterator begin () const { return tests.begin (); }
Iterator end () const { return tests.end (); }
const SetupFunction& setup () const { return setupFunction; }
const TeardownFunction& teardown () const { return teardownFunction; }
TestSuite& operator= (TestSuite&& tc) noexcept;
private:
Tests tests;
std::string name;
TestSuiteFunction tcf;
SetupFunction setupFunction;
TeardownFunction teardownFunction;
mutable std::any storage;
};
//----------------------------------------------------------------------------------------------------
class TestRegistrar
{
public:
TestRegistrar (std::string&& suite, TestSuiteFunction&& testSuite);
TestRegistrar (std::string&& suite, std::string&& testName, TestFunction&& testFunction);
TestRegistrar (std::string&& suite, SetupFunction&& setupFunction, bool isSetupFunc = true);
};
//----------------------------------------------------------------------------------------------------
class Context
{
public:
void print (const char* fmt, ...);
virtual void printRaw (const char* str) = 0;
virtual std::any& storage () = 0;
};
}} // namespaces
#else
#define TESTCASE(x,y)
#endif