//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #ifndef UNITLIB_H #define UNITLIB_H #ifdef _WIN32 #pragma once #endif #include "tier0/platform.h" #include "tier1/interface.h" #include "appframework/IAppSystem.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)) \ { \ LoggingResponse_t ret = Log_Assert( "%s (%d) : %s\n", __TFILE__, __LINE__, _msg ); \ _executeExp; \ if ( ret == LR_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