diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-test/test-common.h | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-test/test-common.h')
-rw-r--r-- | src/lib-test/test-common.h | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/lib-test/test-common.h b/src/lib-test/test-common.h new file mode 100644 index 0000000..f845c0d --- /dev/null +++ b/src/lib-test/test-common.h @@ -0,0 +1,165 @@ +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +struct istream *test_istream_create(const char *data); +struct istream *test_istream_create_data(const void *data, size_t size); +void test_istream_set_size(struct istream *input, uoff_t size); +void test_istream_set_allow_eof(struct istream *input, bool allow); +void test_istream_set_max_buffer_size(struct istream *input, size_t size); + +struct ostream *test_ostream_create(buffer_t *output); +struct ostream *test_ostream_create_nonblocking(buffer_t *output, + size_t max_internal_buffer_size); +/* When output->used reaches max_size, start buffering output internally. + When internal buffer reaches max_internal_buffer_size, start returning 0 for + o_stream_send*(). */ +void test_ostream_set_max_output_size(struct ostream *output, size_t max_size); + +void test_begin(const char *name); +#define test_assert(code) STMT_START { \ + if (!(code)) test_assert_failed(#code, __FILE__, __LINE__); \ + } STMT_END +/* Additional parameter may be int or unsigned int, to indicate which of + * a barrage of tests have failed (such as in a loop). + */ +#define test_assert_idx(code, i) STMT_START { \ + if (!(code)) test_assert_failed_idx(#code, __FILE__, __LINE__, i); \ + } STMT_END +/* Additional parameters are s1 (source) and s2 (destination) string + * in strcmp(). + */ +#define test_assert_strcmp(s1, s2) STMT_START { \ + test_assert_strcmp_idx(s1, s2, LLONG_MIN); \ + } STMT_END + +/* Same as test_assert_strcmp expect that it takes an additional i as input. + * When i is greater than or equals 0 it is used to identify the barrage of + * tests failed like in test_assert_idx. +*/ +#define test_assert_strcmp_idx(_s1, _s2, i) STMT_START { \ + const char *_temp_s1 = (_s1); \ + const char *_temp_s2 = (_s2); \ + if ((null_strcmp(_temp_s1,_temp_s2) != 0)) \ + test_assert_failed_strcmp_idx("strcmp(" #_s1 "," #_s2 ")", \ + __FILE__, __LINE__, _temp_s1, _temp_s2, i); \ + } STMT_END + +#define test_assert_cmp(_value1, _op, _value2) \ + test_assert_cmp_idx(_value1, _op, _value2, LLONG_MIN) +#define test_assert_cmp_idx(_value1, _op, _value2, _idx) STMT_START { \ + intmax_t _temp_value1 = (_value1); \ + intmax_t _temp_value2 = (_value2); \ + if (!(_value1 _op _value2)) \ + test_assert_failed_cmp_intmax_idx( \ + #_value1 " " #_op " " #_value2, \ + __FILE__, __LINE__, _temp_value1, _temp_value2, \ + #_op, _idx); \ + } STMT_END + +#define test_assert_ucmp(_value1, _op, _value2) \ + test_assert_ucmp_idx(_value1, _op, _value2, LLONG_MIN) +#define test_assert_ucmp_idx(_value1, _op, _value2, _idx) STMT_START { \ + uintmax_t _temp_value1 = (_value1); \ + uintmax_t _temp_value2 = (_value2); \ + if (!(_value1 _op _value2)) \ + test_assert_failed_ucmp_intmax_idx( \ + #_value1 " " #_op " " #_value2, \ + __FILE__, __LINE__, _temp_value1, _temp_value2, \ + #_op, _idx); \ + } STMT_END + +#ifdef STATIC_CHECKER +# define ATTR_STATIC_CHECKER_NORETURN ATTR_NORETURN +#else +# define ATTR_STATIC_CHECKER_NORETURN +#endif + +void test_assert_failed(const char *code, const char *file, unsigned int line) + ATTR_STATIC_CHECKER_NORETURN; +void test_assert_failed_idx(const char *code, const char *file, unsigned int line, long long i) + ATTR_STATIC_CHECKER_NORETURN; +void test_assert_failed_strcmp_idx(const char *code, const char *file, unsigned int line, + const char * src, const char * dst, long long i) + ATTR_STATIC_CHECKER_NORETURN; +void test_assert_failed_cmp_intmax_idx(const char *code, const char *file, + unsigned int line, + intmax_t src, intmax_t dst, + const char *op, long long i) + ATTR_STATIC_CHECKER_NORETURN; +void test_assert_failed_ucmp_intmax_idx(const char *code, const char *file, + unsigned int line, + uintmax_t src, uintmax_t dst, + const char *op, long long i) + ATTR_STATIC_CHECKER_NORETURN; +bool test_has_failed(void); +/* If you're testing nasty cases which you want to warn, surround the noisy op with these */ +void test_expect_errors(unsigned int expected); +void test_expect_error_string(const char *substr); /* expect just 1 message matching the printf format */ +void test_expect_error_string_n_times(const char *substr, unsigned int times); /* expect just n messages matching the printf format */ +void test_expect_no_more_errors(void); +/* Note that test_expect_error{s,_string}() effectively begin with a check equivalent + to test_expect_no_more_errors(), so you don't need the latter explicitly if following + it with either of the former.*/ + +void test_end(void); + +void test_out(const char *name, bool success); +void test_out_quiet(const char *name, bool success); /* only prints failures */ +void test_out_reason(const char *name, bool success, const char *reason) + ATTR_NULL(3); + +int test_run(void (*const test_functions[])(void)) ATTR_WARN_UNUSED_RESULT; +struct named_test { + const char *name; + void (*func)(void); +}; +int test_run_named(const struct named_test tests[], const char *match) ATTR_WARN_UNUSED_RESULT; + +#define TEST_DECL(x) void x(void); +#define TEST_NAMELESS(x) x, /* Were you to want to use the X trick but not name the tests */ +#define TEST_NAMED(x) { .name = #x , .func = x }, + +enum fatal_test_state { + FATAL_TEST_FINISHED, /* no more test stages, don't call again */ + FATAL_TEST_FAILURE, /* single stage has failed, continue */ + FATAL_TEST_ABORT, /* something's gone horrifically wrong */ +}; +/* The fatal function is called first with stage=0. After each call the stage + is increased by 1. The idea is that each stage would be running an + individual test that is supposed to crash. The function is called until + FATAL_TEST_FINISHED or FATAL_TEST_ABORT is returned. */ +typedef enum fatal_test_state test_fatal_func_t(unsigned int stage); + +typedef void test_fatal_callback_t(void *context); + +struct named_fatal { + const char *name; + test_fatal_func_t *func; +}; +int test_run_with_fatals(void (*const test_functions[])(void), + test_fatal_func_t *const fatal_functions[]); +int test_run_named_with_fatals(const char *match, const struct named_test tests[], + const struct named_fatal fatals[]); + +/* Require the Fatal/Panic string to match this or the fatal test fails. */ +void test_expect_fatal_string(const char *substr); +/* Call the specified callback when a fatal is being triggered. This is mainly + intended to allow freeing memory so valgrind won't complain about memory + leaks. */ +void test_fatal_set_callback(test_fatal_callback_t *callback, void *context); +#define test_fatal_set_callback(callback, context) \ + test_fatal_set_callback(1 ? (test_fatal_callback_t *)callback : \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + context) + +#define FATAL_DECL(x) enum fatal_test_state x(unsigned int); +#define FATAL_NAMELESS(x) x, /* Were you to want to use the X trick but not name the tests */ +#define FATAL_NAMED(x) { .name = #x , .func = x }, + +/* If a child is forked within test context, but wants to operate outside it, + then this will avoid valgrind leak errors */ +void test_forked_end(void); +/* If a fork() wants to exit(), then this will avoid valgrind leak errors */ +void test_exit(int status) ATTR_NORETURN; + +#endif |