/* Copyright (c) 2013-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file testsupport.h * * \brief Macros to implement mocking and selective exposure for the test code. * * Each Tor source file is built twice: once with TOR_UNIT_TESTS defined, and * once with it undefined. The only difference between these configurations * should be that when building for the tests, more functions are exposed as * non-static, and a number of functions are declared as mockable. **/ #ifndef TOR_TESTSUPPORT_H #define TOR_TESTSUPPORT_H /** The "STATIC" macro marks a function or variable that is static when * building Tor for production, but non-static when building the unit * tests. * * For example, a function declared as: * * STATIC int internal_function(void); * * should be only visible for the file on which it is declared, and in the * unit tests. */ #ifdef TOR_UNIT_TESTS #define STATIC #else /* !defined(TOR_UNIT_TESTS) */ #define STATIC static #endif /* defined(TOR_UNIT_TESTS) */ /** The "EXTERN" macro is used along with "STATIC" for variables declarations: * it expands to an extern declaration when Tor building unit tests, and to * nothing otherwise. * * For example, to declare a variable as visible only visible in one * file and in the unit tests, you would put this in the header: * * EXTERN(int, local_variable) * * and this in the source: * * STATIC int local_variable; */ #ifdef TOR_UNIT_TESTS #define EXTERN(type, name) extern type name; #else #define EXTERN(type, name) #endif /** Quick and dirty macros to implement test mocking. * * To use them, suppose that you have a function you'd like to mock * with the signature "void writebuf(size_t n, char *buf)". You can then * declare the function as: * * MOCK_DECL(void, writebuf, (size_t n, char *buf)); * * and implement it as: * * MOCK_IMPL(void, * writebuf,(size_t n, char *buf)) * { * ... * } * * For the non-testing build, this will expand simply into: * * void writebuf(size_t n, char *buf); * void * writebuf(size_t n, char *buf) * { * ... * } * * But for the testing case, it will expand into: * * void writebuf__real(size_t n, char *buf); * extern void (*writebuf)(size_t n, char *buf); * * void (*writebuf)(size_t n, char *buf) = writebuf__real; * void * writebuf__real(size_t n, char *buf) * { * ... * } * * This is not a great mocking system! It is deliberately "the simplest * thing that could work", and pays for its simplicity in its lack of * features, and in its uglification of the Tor code. Replacing it with * something clever would be a fine thing. * * @{ */ #ifdef TOR_UNIT_TESTS /** Declare a mocked function. For use in headers. */ #define MOCK_DECL(rv, funcname, arglist) \ rv funcname ##__real arglist; \ extern rv(*funcname) arglist /** Define the implementation of a mocked function. */ #define MOCK_IMPL(rv, funcname, arglist) \ rv(*funcname) arglist = funcname ##__real; \ rv funcname ##__real arglist /** As MOCK_DECL(), but allow attributes. */ #define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \ rv funcname ##__real arglist attr; \ extern rv(*funcname) arglist /** * Replace func (a mockable function) with a replacement function. * * Only usable when Tor has been built for unit tests. */ #define MOCK(func, replacement) \ do { \ (func) = (replacement); \ } while (0) /** Replace func (a mockable function) with its original value. * * Only usable when Tor has been built for unit tests. */ #define UNMOCK(func) \ do { \ func = func ##__real; \ } while (0) #else /* !defined(TOR_UNIT_TESTS) */ /** Declare a mocked function. For use in headers. */ #define MOCK_DECL(rv, funcname, arglist) \ rv funcname arglist /** As MOCK_DECL(), but allow */ #define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \ rv funcname arglist attr /** Define the implementation of a mocked function. */ #define MOCK_IMPL(rv, funcname, arglist) \ rv funcname arglist #endif /* defined(TOR_UNIT_TESTS) */ /** @} */ #endif /* !defined(TOR_TESTSUPPORT_H) */