//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #ifndef UNITLIB_H #define UNITLIB_H #ifdef _WIN32 #pragma once #endif #include "appframework/IAppSystem.h" #include "tier0/platform.h" #include "tier1/interface.h" //----------------------------------------------------------------------------- // Usage model for the UnitTest library // // The general methodology here is that clients are expected to create unit // test DLLs that statically link to the unit test DLL and which implement // tests of various libraries. The unit test application will dynamically // load all DLLs in a directory, which causes them to install their test cases // into the unit test system. The application then runs through all tests and // executes them all, displaying the results. // // *** NOTE: The test suites are compiled in both debug and release builds, // even though it's expected to only be useful in debug builds. This is //because // I couldn't come up with a good way of disabling the code in release builds. // (The only options I could come up with would still compile in the //functions, just not install them into the unit test library, or would make it //so that you couldn't step through the unit test code). // // Even though this is the case, there's no reason not to add test cases // directly into your shipping DLLs, as long as you surround the code with // #ifdef _DEBUG. To error check a project to make sure it's not compiling // in unit tests in Release build, just don't link in unitlib.lib in //Release. You can of course also put your test suites into separate DLLs. // // All tests inherit from the ITestCase interface. There are two major //kinds of tests; the first is a single test case meant to run a piece of code //and check its results match expected values using the Assert macros. The //second kind is a test suite which is simply a list of other tests. // // The following classes and macros are used to easily create unit test //cases and suites: // // Use DEFINE_TESTSUITE to define a particular test suite, and DEFINE_TESTCASE // to add as many test cases as you like to that test suite, as follows: // // DEFINE_TESTSUITE( VectorTestSuite ) // // DEFINE_TESTCASE( VectorAdditionTest, VectorTestSuite ) // { // .. test code here .. // } // // Note that the definition of the test suite can occur in a different file // as the test case. A link error will occur if the test suite to which a // test case is added has not been defined. // // To create a test case that is not part of a suite, use... // // DEFINE_TESTCASE_NOSUITE( VectorAdditionTest ) // { // .. test code here .. // } // // You can also create a suite which is a child of another suite using // // DEFINE_SUBSUITE( VectorTestSuite, MathTestSuite ) // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // dll export stuff //----------------------------------------------------------------------------- #ifdef UNITLIB_DLL_EXPORT #define UNITLIB_INTERFACE DLL_EXPORT #define UNITLIB_CLASS_INTERFACE DLL_CLASS_EXPORT #define UNITLIB_GLOBAL_INTERFACE DLL_GLOBAL_EXPORT #else #define UNITLIB_INTERFACE DLL_IMPORT #define UNITLIB_CLASS_INTERFACE DLL_CLASS_IMPORT #define UNITLIB_GLOBAL_INTERFACE DLL_GLOBAL_IMPORT #endif //----------------------------------------------------------------------------- // All unit test libraries can be asked for a unit test // AppSystem to perform connection //----------------------------------------------------------------------------- #define UNITTEST_INTERFACE_VERSION "UnitTestV001" //----------------------------------------------------------------------------- // // NOTE: All classes and interfaces below you shouldn't use directly. // Use the DEFINE_TESTSUITE and DEFINE_TESTCASE macros instead. // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Test case + suite interface //----------------------------------------------------------------------------- class ITestCase { public: // This returns the test name virtual char const* GetName() = 0; // This runs the test virtual void RunTest() = 0; }; class ITestSuite : public ITestCase { public: // Add a test to the suite... virtual void AddTest(ITestCase* pTest) = 0; }; //----------------------------------------------------------------------------- // This is the main function exported by the unit test library used by // unit test DLLs to install their test cases into a list to be run //----------------------------------------------------------------------------- UNITLIB_INTERFACE void UnitTestInstallTestCase(ITestCase* pTest); //----------------------------------------------------------------------------- // These are the methods used by the unit test running program to run all tests //----------------------------------------------------------------------------- UNITLIB_INTERFACE int UnitTestCount(); UNITLIB_INTERFACE ITestCase* GetUnitTest(int i); //----------------------------------------------------------------------------- // Helper for unit test DLLs to expose IAppSystems //----------------------------------------------------------------------------- #define USE_UNITTEST_APPSYSTEM(_className) \ static _className s_UnitTest##_className; \ EXPOSE_SINGLE_INTERFACE_GLOBALVAR(_className, IAppSystem, \ UNITTEST_INTERFACE_VERSION, \ s_UnitTest##_className); //----------------------------------------------------------------------------- // Base class for test cases //----------------------------------------------------------------------------- class UNITLIB_CLASS_INTERFACE CTestCase : public ITestCase { public: CTestCase(char const* pName, ITestSuite* pParent = 0); ~CTestCase(); // Returns the test name char const* GetName(); private: char* m_pName; }; //----------------------------------------------------------------------------- // Test suite class //----------------------------------------------------------------------------- class UNITLIB_CLASS_INTERFACE CTestSuite : public ITestSuite { public: CTestSuite(char const* pName, ITestSuite* pParent = 0); ~CTestSuite(); // This runs the test void RunTest(); // Add a test to the suite... void AddTest(ITestCase* pTest); // Returns the test name char const* GetName(); protected: int m_TestCount; ITestCase** m_ppTestCases; char* m_pName; }; #define TESTSUITE_CLASS(_suite) \ class CTS##_suite : public CTestSuite { \ public: \ CTS##_suite(); \ }; #define TESTSUITE_ACCESSOR(_suite) \ CTS##_suite* GetTS##_suite() { \ static CTS##_suite s_TS##_suite; \ return &s_TS##_suite; \ } #define FWD_DECLARE_TESTSUITE(_suite) \ class CTS##_suite; \ CTS##_suite* GetTS##_suite(); #define DEFINE_TESTSUITE(_suite) \ TESTSUITE_CLASS(_suite) \ TESTSUITE_ACCESSOR(_suite) \ CTS##_suite::CTS##_suite() : CTestSuite(#_suite) {} #define DEFINE_SUBSUITE(_suite, _parent) \ TESTSUITE_CLASS(_suite) \ TESTSUITE_ACCESSOR(_suite) \ FWD_DECLARE_TESTSUITE(_parent) \ CTS##_suite::CTS##_suite() : CTestSuite(#_suite, GetTS##_parent()) {} #define TESTCASE_CLASS(_case) \ class CTC##_case : public CTestCase { \ public: \ CTC##_case(); \ void RunTest(); \ }; #define DEFINE_TESTCASE_NOSUITE(_case) \ TESTCASE_CLASS(_case) \ CTC##_case::CTC##_case() : CTestCase(#_case) {} \ \ CTC##_case s_TC##_case; \ \ void CTC##_case ::RunTest() #define DEFINE_TESTCASE(_case, _suite) \ TESTCASE_CLASS(_case) \ FWD_DECLARE_TESTSUITE(_suite) \ CTC##_case::CTC##_case() : CTestCase(#_case, GetTS##_suite()) {} \ \ CTC##_case s_TC##_case; \ \ void CTC##_case ::RunTest() #define _Shipping_AssertMsg(_exp, _msg, _executeExp, _bFatal) \ do { \ if (!(_exp)) { \ _SpewInfo(SPEW_ASSERT, __TFILE__, __LINE__); \ SpewRetval_t ret = _SpewMessage(_msg); \ _executeExp; \ if (ret == SPEW_DEBUGGER) { \ if (!ShouldUseNewAssertDialog() || \ DoNewAssertDialog(__TFILE__, __LINE__, _msg)) \ DebuggerBreak(); \ if (_bFatal) _ExitOnFatalAssert(__TFILE__, __LINE__); \ } \ } \ } while (0) #define Shipping_Assert(_exp) \ _Shipping_AssertMsg(_exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), \ false) #endif // UNITLIB_H