aboutsummaryrefslogtreecommitdiff
path: root/src/lib/testsupport/testsupport.h
blob: 165c497f710a4b63eb362710f2631058835e5a13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* 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 <b>func</b> (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 <b>func</b> (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) */