summaryrefslogtreecommitdiffstats
path: root/src/lib/test-event-category-register.c
blob: 50cc0386d9a1ff622f5bc117b43da4f2ee6b2b17 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */

#include "test-lib.h"
#include "ioloop.h"
#include "time-util.h"
#include "lib-event-private.h"
#include "failures-private.h"

/* we call a generic "unregister category function; to tell it what exact
 * behavior it should expect from lib-lib, we pass in one of the following
 * values
 */
enum unreg_expectation {
	UNREG_NOT_LAST,
	UNREG_LAST,
	UNREG_NOP,
};

#define CAT_NAME_PREFIX	"test-category"

/* pointer to a category we expect to be registered/unregistered */
static struct event_category *expected_callback_cat;
static bool callback_called;

static struct event *dummy_event;

static void check_category(struct event_category *cat)
{
	callback_called = TRUE;

	/* lib-lib called a callback with a NULL? (useless and a bug) */
	test_assert(cat != NULL);

	/* callback called, but didn't expect to be called? */
	test_assert(expected_callback_cat != NULL);

	/* test_assert() doesn't terminate, so avoid NULL ptr derefs later on */
	if ((cat == NULL) || (expected_callback_cat == NULL))
		return;

	/* check that the categories have the same values */
	test_assert(strcmp(cat->name, expected_callback_cat->name) == 0);
	test_assert(cat->internal == expected_callback_cat->internal);
}

static void check_cat_registered(const char *name, bool should_exist)
{
	struct event_category *cat;

	callback_called = FALSE;
	cat = event_category_find_registered(name);
	test_assert(callback_called == FALSE);

	test_assert((cat != NULL) == should_exist);
}

static void register_cat(struct event_category *newcat,
			 struct event_category *expcat)
{
	/* start with a known state - no regs expected */
	expected_callback_cat = NULL;
	callback_called = FALSE;

	dummy_event = event_create(NULL);
	test_assert(callback_called == FALSE);

	/* we expect a registration only when adding a cat */
	expected_callback_cat = (expcat);
	event_add_category(dummy_event, (newcat));
	expected_callback_cat = NULL;

	/* check that all went well */
	test_assert(callback_called == (expcat != NULL));
	test_assert((newcat)->internal != NULL);
	test_assert(event_category_find_registered((newcat)->name) != NULL);

	/* clean up */
	event_unref(&dummy_event);
}

static void unregister_cat(struct event_category *cat,
			   enum unreg_expectation expectation)
{
	/* sanity check that cat is set up as expected */
	switch (expectation) {
	case UNREG_NOT_LAST:
		/* must be registered to unregister */
		test_assert(event_category_find_registered((cat)->name) != NULL);
		expected_callback_cat = NULL;
		break;

	case UNREG_LAST:
		/* must be registered to unregister */
		test_assert(event_category_find_registered((cat)->name) != NULL);
		expected_callback_cat = cat;
		break;

	case UNREG_NOP:
		/* must not be registered for no-op */
		/* event_category_find_registered(cat->name) should return
		   NULL, but since we don't actually unregister this lookup
		   would fail.  Therefore, we skip it. */
		expected_callback_cat = NULL;
		break;
	}

	/* Note: We don't actually have a way to unregister categories.  We
	   keep the above checks and the calls to this function as a form of
	   documentation of how unregistering should work. */
}

static void test_event_category_1ptr_null(void)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-1ptr-null"
	static struct event_category cat = { .name = CAT_NAME_0 };

	test_begin("event category rereg: same ptr, NULL parent");

	check_cat_registered(CAT_NAME_0, FALSE);
	register_cat(&cat, &cat);
	register_cat(&cat, NULL);
	check_cat_registered(CAT_NAME_0, TRUE);

	unregister_cat(&cat, UNREG_LAST);
	unregister_cat(&cat, UNREG_NOP);

	test_end();
#undef CAT_NAME_0
}

static void test_event_category_1ptr_nonnull(void)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-1ptr-nonnull-0"
#define CAT_NAME_1	CAT_NAME_PREFIX "-1ptr-nonnull-1"
	static struct event_category cat = { .name = CAT_NAME_0 };
	static struct event_category cat_with_parent = { .name = CAT_NAME_1, .parent = &cat };

	test_begin("event category rereg: same ptr, non-NULL parent");

	check_cat_registered(CAT_NAME_0, FALSE);
	check_cat_registered(CAT_NAME_1, FALSE);
	register_cat(&cat, &cat);
	register_cat(&cat_with_parent, &cat_with_parent);
	register_cat(&cat_with_parent, NULL);
	check_cat_registered(CAT_NAME_0, TRUE);
	check_cat_registered(CAT_NAME_1, TRUE);

	unregister_cat(&cat_with_parent, UNREG_LAST);
	unregister_cat(&cat_with_parent, UNREG_NOP);
	/* NOTE: we must unreg children before parent cats */
	unregister_cat(&cat, UNREG_LAST);
	unregister_cat(&cat, UNREG_NOP);

	test_end();
#undef CAT_NAME_0
#undef CAT_NAME_1
}

static void test_event_category_2ptr_null(void)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-2ptr-null"
	static struct event_category cat0 = { .name = CAT_NAME_0 };
	static struct event_category cat1 = { .name = CAT_NAME_0 };

	test_begin("event category rereg: different ptr, NULL parent");

	check_cat_registered(CAT_NAME_0, FALSE);
	register_cat(&cat0, &cat0);
	register_cat(&cat1, NULL);
	check_cat_registered(CAT_NAME_0, TRUE);

	unregister_cat(&cat0, UNREG_NOT_LAST);
	unregister_cat(&cat1, UNREG_LAST);
	unregister_cat(&cat0, UNREG_NOP);
	unregister_cat(&cat1, UNREG_NOP);

	test_end();
#undef CAT_NAME_0
}

static void test_event_category_2ptr_nonnull_same(void)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-2ptr-nonnull-same-0"
#define CAT_NAME_1	CAT_NAME_PREFIX "-2ptr-nonnull-same-1"
	static struct event_category cat = { .name = CAT_NAME_0 };
	static struct event_category cat_with_parent0 = { .name = CAT_NAME_1, .parent = &cat };
	static struct event_category cat_with_parent1 = { .name = CAT_NAME_1, .parent = &cat };

	test_begin("event category rereg: different ptr, same non-NULL parent");

	check_cat_registered(CAT_NAME_0, FALSE);
	check_cat_registered(CAT_NAME_1, FALSE);
	register_cat(&cat, &cat);
	register_cat(&cat_with_parent0, &cat_with_parent0);
	register_cat(&cat_with_parent1, NULL);
	check_cat_registered(CAT_NAME_0, TRUE);
	check_cat_registered(CAT_NAME_1, TRUE);

	unregister_cat(&cat_with_parent0, UNREG_NOT_LAST);
	unregister_cat(&cat_with_parent1, UNREG_LAST);
	unregister_cat(&cat_with_parent0, UNREG_NOP);
	unregister_cat(&cat_with_parent1, UNREG_NOP);
	/* NOTE: we must unreg children before parent cats */
	unregister_cat(&cat, UNREG_LAST);
	unregister_cat(&cat, UNREG_NOP);

	test_end();
#undef CAT_NAME_0
#undef CAT_NAME_1
}

static void test_event_category_2ptr_nonnull_similar(void)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-2ptr-nonnull-similar-0"
#define CAT_NAME_1	CAT_NAME_PREFIX "-2ptr-nonnull-similar-1"
	static struct event_category cat0 = { .name = CAT_NAME_0 };
	static struct event_category cat1 = { .name = CAT_NAME_0 };
	static struct event_category cat_with_parent0 = { .name = CAT_NAME_1, .parent = &cat0 };
	static struct event_category cat_with_parent1 = { .name = CAT_NAME_1, .parent = &cat1 };

	test_begin("event category rereg: different ptr, similar non-NULL parent");

	check_cat_registered(CAT_NAME_0, FALSE);
	check_cat_registered(CAT_NAME_1, FALSE);
	register_cat(&cat0, &cat0);
	register_cat(&cat1, NULL);
	register_cat(&cat_with_parent0, &cat_with_parent0);
	register_cat(&cat_with_parent1, NULL);
	check_cat_registered(CAT_NAME_0, TRUE);
	check_cat_registered(CAT_NAME_1, TRUE);

	unregister_cat(&cat_with_parent0, UNREG_NOT_LAST);
	unregister_cat(&cat_with_parent1, UNREG_LAST);
	unregister_cat(&cat_with_parent0, UNREG_NOP);
	unregister_cat(&cat_with_parent1, UNREG_NOP);
	/* NOTE: we must unreg children before parent cats */
	unregister_cat(&cat0, UNREG_NOT_LAST);
	unregister_cat(&cat1, UNREG_LAST);
	unregister_cat(&cat0, UNREG_NOP);
	unregister_cat(&cat1, UNREG_NOP);

	test_end();
#undef CAT_NAME_0
#undef CAT_NAME_1
}

void test_event_category_register(void)
{
	event_category_register_callback(check_category);

	/*
	 * registering/unregistering the same exact category struct (i.e.,
	 * the pointer is the same) is a no-op after the first call
	 */
	test_event_category_1ptr_null();
	test_event_category_1ptr_nonnull();

	/*
	 * registering/unregistering two different category structs (i.e.,
	 * the pointers are different) is a almost a no-op
	 */
	test_event_category_2ptr_null();
	test_event_category_2ptr_nonnull_same();
	test_event_category_2ptr_nonnull_similar();

	event_category_unregister_callback(check_category);
}

enum fatal_test_state fatal_event_category_register(unsigned int stage)
{
#define CAT_NAME_0	CAT_NAME_PREFIX "-2ptr-nonnull-different-0"
#define CAT_NAME_1	CAT_NAME_PREFIX "-2ptr-nonnull-different-1"
#define CAT_NAME_2	CAT_NAME_PREFIX "-2ptr-nonnull-different-2"
	static struct event_category cat_no_parent0 = { .name = CAT_NAME_0 };
	static struct event_category cat_parent0 = { .name = CAT_NAME_1, .parent = &cat_no_parent0 };
	static struct event_category cat_other = { .name = CAT_NAME_2 };
	static struct event_category cat_other_parent = { .name = CAT_NAME_1, .parent = &cat_other };

	/* we have only one fatal stage at this point */
	switch (stage) {
	case 0:
		event_category_register_callback(check_category);

		test_begin("event category rereg: different ptr, different non-NULL parent");

		check_cat_registered(CAT_NAME_0, FALSE);
		check_cat_registered(CAT_NAME_1, FALSE);
		check_cat_registered(CAT_NAME_2, FALSE);
		register_cat(&cat_no_parent0, &cat_no_parent0);
		register_cat(&cat_other, &cat_other);
		register_cat(&cat_parent0, &cat_parent0);

		test_expect_fatal_string("event category parent mismatch detected");
		register_cat(&cat_other_parent, NULL); /* expected panic */

		return FATAL_TEST_FAILURE;
	case 1:
		event_unref(&dummy_event);

		unregister_cat(&cat_parent0, UNREG_LAST);
		unregister_cat(&cat_parent0, UNREG_NOP);
		unregister_cat(&cat_other, UNREG_LAST);
		unregister_cat(&cat_other, UNREG_NOP);
		/* NOTE: we must unreg children before parent cats */
		unregister_cat(&cat_no_parent0, UNREG_LAST);
		unregister_cat(&cat_no_parent0, UNREG_NOP);

		test_end();

		event_category_unregister_callback(check_category);

		return FATAL_TEST_FINISHED;

	default:
		return FATAL_TEST_ABORT;
	}
#undef CAT_NAME_0
#undef CAT_NAME_1
#undef CAT_NAME_2
}