virmock.h 11.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * virmock.h: helper for mocking C functions
 *
 * Copyright (C) 2014 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

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
#pragma once

#if HAVE_DLFCN_H
# include <dlfcn.h>
#endif

#include "internal.h"

#define VIR_MOCK_COUNT_ARGS(...) VIR_MOCK_ARG21(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define VIR_MOCK_ARG21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) _21
#define VIR_MOCK_ARG_PASTE(a, b, ...) a##b(__VA_ARGS__)

#define VIR_MOCK_ARGNAME(a, b) b
#define VIR_MOCK_ARGTYPE(a, b) a
#define VIR_MOCK_ARGTYPENAME(a, b) a b
#define VIR_MOCK_ARGTYPENAME_UNUSED(a, b) a b ATTRIBUTE_UNUSED

#define VIR_MOCK_GET_ARG2(z, a, b) z(a, b)
#define VIR_MOCK_GET_ARG3(z, a, b, c) z(a, b)
#define VIR_MOCK_GET_ARG4(z, a, b, c, d) z(a, b),  z(c, d)
#define VIR_MOCK_GET_ARG5(z, a, b, c, d, e) z(a, b),  z(c, d)
#define VIR_MOCK_GET_ARG6(z, a, b, c, d, e, f) z(a, b),  z(c, d), z(e, f)
#define VIR_MOCK_GET_ARG7(z, a, b, c, d, e, f, g) z(a, b),  z(c, d), z(e, f)
#define VIR_MOCK_GET_ARG8(z, a, b, c, d, e, f, g, h) z(a, b),  z(c, d), z(e, f), z(g, h)
#define VIR_MOCK_GET_ARG9(z, a, b, c, d, e, f, g, h, i) z(a, b),  z(c, d), z(e, f), z(g, h)
#define VIR_MOCK_GET_ARG10(z, a, b, c, d, e, f, g, h, i, j) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j)
#define VIR_MOCK_GET_ARG11(z, a, b, c, d, e, f, g, h, i, j, k) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j)
#define VIR_MOCK_GET_ARG12(z, a, b, c, d, e, f, g, h, i, j, k, l) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l)
#define VIR_MOCK_GET_ARG13(z, a, b, c, d, e, f, g, h, i, j, k, l, m) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l)
#define VIR_MOCK_GET_ARG14(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n)
#define VIR_MOCK_GET_ARG15(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n)
#define VIR_MOCK_GET_ARG16(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p)
#define VIR_MOCK_GET_ARG17(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p)
#define VIR_MOCK_GET_ARG18(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r)
#define VIR_MOCK_GET_ARG19(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r)
#define VIR_MOCK_GET_ARG20(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r), z(s, t)
#define VIR_MOCK_GET_ARG21(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) z(a, b),  z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r), z(s, t)


#define VIR_MOCK_ARGNAMES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__)
#define VIR_MOCK_ARGNAMES(...) \
63 64
    VIR_MOCK_ARGNAMES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGNAME, __VA_ARGS__)

65 66
#define VIR_MOCK_ARGTYPES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__)
#define VIR_MOCK_ARGTYPES(...) \
67 68
    VIR_MOCK_ARGTYPES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPE, __VA_ARGS__)

69 70
#define VIR_MOCK_ARGTYPENAMES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__)
#define VIR_MOCK_ARGTYPENAMES(...) \
71 72
    VIR_MOCK_ARGTYPENAMES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPENAME, __VA_ARGS__)

73 74
#define VIR_MOCK_ARGTYPENAMES_UNUSED_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__)
#define VIR_MOCK_ARGTYPENAMES_UNUSED(...) \
75 76 77 78 79 80 81 82 83 84
    VIR_MOCK_ARGTYPENAMES_UNUSED_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPENAME_UNUSED, __VA_ARGS__)


/*
 * The VIR_MOCK_LINK_NNN_MMM() macros are intended for use in
 * LD_PRELOAD based wrappers. They provide a replacement for
 * for an existing shared library symbol export. They will
 * then lookup the same symbol name but with 'wrap_' prefixed
 * on it, and call that.
 *
85
 * The actual test suite should provide the implementation of
86 87 88 89 90 91 92 93 94 95 96 97 98 99
 * the wrap_XXXX symbol, using the VIR_MOCK_WRAP_NNN_MMM
 * macros.
 */


/**
 * VIR_MOCK_LINK_RET_ARGS:
 * @name: the symbol name to replace
 * @rettype: the return type
 * @...: pairs of parameter type and parameter name
 *
 * Define a replacement for @name which invokes wrap_@name
 * forwarding on all args, and passing back the return value.
 */
100
#define VIR_MOCK_LINK_RET_ARGS(name, rettype, ...) \
101 102 103 104 105 106 107 108 109 110 111
    rettype name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)) \
    { \
        static rettype (*wrap_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
        if (wrap_##name == NULL && \
            !(wrap_##name = dlsym(RTLD_DEFAULT, \
                                  "wrap_" #name))) { \
            fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \
            abort(); \
        } \
 \
        return wrap_##name(VIR_MOCK_ARGNAMES(__VA_ARGS__)); \
112 113 114 115 116 117 118 119 120 121
    }

/**
 * VIR_MOCK_LINK_RET_VOID:
 * @name: the symbol name to replace
 * @rettype: the return type
 *
 * Define a replacement for @name which invokes wrap_@name
 * with no arguments, and passing back the return value.
 */
122
#define VIR_MOCK_LINK_RET_VOID(name, rettype) \
123 124 125 126 127 128 129 130 131 132 133
    rettype name(void) \
    { \
        static rettype (*wrap_##name)(void); \
        if (wrap_##name == NULL && \
            !(wrap_##name = dlsym(RTLD_DEFAULT, \
                                  "wrap_" #name))) { \
            fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \
            abort(); \
        } \
 \
        return wrap_##name(); \
134 135 136 137 138 139 140 141 142 143
    }

/**
 * VIR_MOCK_LINK_VOID_ARGS:
 * @name: the symbol name to replace
 * @...: pairs of parameter type and parameter name
 *
 * Define a replacement for @name which invokes wrap_@name
 * forwarding on all args, but with no return value.
 */
144
#define VIR_MOCK_LINK_VOID_ARGS(name, ...) \
145 146
    void name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)) \
    { \
147
        static void (*wrap_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
148 149 150 151 152 153 154 155
        if (wrap_##name == NULL && \
            !(wrap_##name = dlsym(RTLD_DEFAULT, \
                                  "wrap_" #name))) { \
            fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \
            abort(); \
        } \
 \
        wrap_##name(VIR_MOCK_ARGNAMES(__VA_ARGS__)); \
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    }



/*
 * The VIR_MOCK_STUB_NNN_MMM() macros are intended for use in
 * LD_PRELOAD based wrappers. They provide a replacement for
 * for an existing shared library symbol export. They will
 * be a pure no-op, optionally returning a dummy value.
 */


/**
 * VIR_MOCK_STUB_RET_ARGS:
 * @name: the symbol name to replace
 * @rettype: the return type
 * @retval: the return value
 * @...: pairs of parameter type and parameter name
 *
175 176
 * Define a replacement for @name which doesn't invoke anything, just
 * returns @retval.
177
 */
178
#define VIR_MOCK_STUB_RET_ARGS(name, rettype, retval, ...) \
179 180 181
    rettype name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) \
    { \
        return retval; \
182 183 184 185 186 187
    }

/**
 * VIR_MOCK_STUB_RET_VOID:
 * @name: the symbol name to replace
 * @rettype: the return type
188
 * @retval: value to return
189
 *
190 191
 * Define a replacement for @name which doesn't invoke anything, just
 * returns @retval.
192
 */
193
#define VIR_MOCK_STUB_RET_VOID(name, rettype, retval) \
194 195 196
    rettype name(void) \
    { \
        return retval; \
197 198 199 200 201 202 203
    }

/**
 * VIR_MOCK_STUB_VOID_ARGS:
 * @name: the symbol name to replace
 * @...: pairs of parameter type and parameter name
 *
204 205
 * Define a replacement for @name which doesn't invoke or return
 * anything.
206
 */
207
#define VIR_MOCK_STUB_VOID_ARGS(name, ...) \
208 209
    void name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) \
    { \
210 211 212 213 214 215 216 217
    }



/**
 * VIR_MOCK_STUB_VOID_VOID:
 * @name: the symbol name to replace
 *
218 219
 * Define a replacement for @name which doesn't invoke or return
 * anything.
220
 */
221
#define VIR_MOCK_STUB_VOID_VOID(name) \
222 223
    void name(void) \
    { \
224 225 226 227 228 229 230 231 232 233
    }


/*
 * The VIR_MOCK_IMPL_NNN_MMM() macros are intended for use in the
 * individual test suites. The define a stub implementation of
 * the wrapped method and insert the caller provided code snippet
 * as the body of the method.
 */

234
#define VIR_MOCK_IMPL_RET_ARGS(name, rettype, ...) \
235 236
    rettype name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \
    static rettype (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
237 238
    rettype name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__))

239
#define VIR_MOCK_IMPL_RET_VOID(name, rettype) \
240 241
    rettype name(void); \
    static rettype (*real_##name)(void); \
242 243
    rettype name(void)

244
#define VIR_MOCK_IMPL_VOID_ARGS(name, ...) \
245 246
    void name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \
    static void (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
247 248
    void name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__))

249
#define VIR_MOCK_IMPL_VOID_VOID(name) \
250 251
    void name(void); \
    static void (*real_##name)(void); \
252 253 254 255 256 257 258 259 260
    void name(void)

/*
 * The VIR_MOCK_WRAP_NNN_MMM() macros are intended for use in the
 * individual test suites. The define a stub implementation of
 * the wrapped method and insert the caller provided code snippet
 * as the body of the method.
 */

261
#define VIR_MOCK_WRAP_RET_ARGS(name, rettype, ...) \
262 263
    rettype wrap_##name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \
    static rettype (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
264 265
    rettype wrap_##name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__))

266
#define VIR_MOCK_WRAP_RET_VOID(name, rettype) \
267 268
    rettype wrap_##name(void); \
    static rettype (*real_##name)(void); \
269 270
    rettype wrap_##name(void)

271
#define VIR_MOCK_WRAP_VOID_ARGS(name, ...) \
272 273
    void wrap_##name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \
    static void (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \
274 275
    void wrap_##name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__))

276
#define VIR_MOCK_WRAP_VOID_VOID(name) \
277 278
    void wrap_##name(void); \
    static void (*real_##name)(void); \
279 280
    void wrap_##name(void)

281

282
#define VIR_MOCK_REAL_INIT(name) \
283 284 285 286 287 288 289
    do { \
        if (real_##name == NULL && \
            !(real_##name = dlsym(RTLD_NEXT, \
                                  #name))) { \
            fprintf(stderr, "Missing symbol '" #name "'\n"); \
            abort(); \
        } \
290
    } while (0)
291 292 293 294 295 296 297 298 299 300

#define VIR_MOCK_REAL_INIT_ALIASED(name, alias) \
    do { \
        if (real_##name == NULL && \
            !(real_##name = dlsym(RTLD_NEXT, \
                                  alias))) { \
            fprintf(stderr, "Missing symbol '" alias "'\n"); \
            abort(); \
        } \
    } while (0)