aboutsummaryrefslogtreecommitdiff
path: root/src/lib/testsupport/testsupport.h
blob: 9363a9ba661c8d295a841256335473f472eb7f6a (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
/* Copyright (c) 2013-2019, 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

#ifdef TOR_UNIT_TESTS
/** The "STATIC" macro marks a function or variable that is static when
 * building Tor for production, but non-static when building the unit
 * tests. */
#define STATIC
#define EXTERN(type, name) extern type name;
#else
#define STATIC static
#define EXTERN(type, name)
#endif /* defined(TOR_UNIT_TESTS) */

/** 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
#define MOCK_DECL(rv, funcname, arglist)     \
  rv funcname ##__real arglist;              \
  extern rv(*funcname) arglist
#define MOCK_IMPL(rv, funcname, arglist)     \
  rv(*funcname) arglist = funcname ##__real; \
  rv funcname ##__real arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
  rv funcname ##__real arglist attr;                \
  extern rv(*funcname) arglist
#define MOCK_IMPL(rv, funcname, arglist)     \
  rv(*funcname) arglist = funcname ##__real; \
  rv funcname ##__real arglist
#define MOCK(func, replacement)                 \
  do {                                          \
    (func) = (replacement);                     \
  } while (0)
#define UNMOCK(func)                            \
  do {                                          \
    func = func ##__real;                       \
  } while (0)
#else /* !(defined(TOR_UNIT_TESTS)) */
#define MOCK_DECL(rv, funcname, arglist) \
  rv funcname arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
  rv funcname arglist attr
#define MOCK_IMPL(rv, funcname, arglist) \
  rv funcname arglist
#endif /* defined(TOR_UNIT_TESTS) */
/** @} */

#endif /* !defined(TOR_TESTSUPPORT_H) */