Skip to content

Commit c6dd6af

Browse files
committed
tests: Add unit test for stalled client index reassignment
1 parent 9852a19 commit c6dd6af

5 files changed

Lines changed: 193 additions & 4 deletions

File tree

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ AC_CONFIG_MACRO_DIR([m4])
1010
AC_CONFIG_AUX_DIR([build-aux])
1111

1212
AC_CONFIG_HEADERS([imaptest-config.h])
13-
AM_INIT_AUTOMAKE
13+
AM_INIT_AUTOMAKE([serial-tests])
1414
AM_SILENT_RULES([yes])
1515

1616
AM_MAINTAINER_MODE

src/Makefile.am

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
bin_PROGRAMS = imaptest
22
noinst_PROGRAMS = test-test-parser
3+
check_PROGRAMS = test-client-stalled
4+
TESTS = $(check_PROGRAMS)
35

46
AM_CPPFLAGS = $(LIBDOVECOT_INCLUDE) -DSTATIC_OPENSSL=1
57
imaptest_LDFLAGS = $(AM_LDFLAGS)
68

7-
imaptest_SOURCES = \
9+
common_sources = \
810
checkpoint.c \
911
client.c \
1012
client-state.c \
1113
commands.c \
1214
imap-client.c \
13-
imaptest.c \
1415
imaptest-lmtp.c \
1516
mailbox.c \
1617
mailbox-source.c \
@@ -26,10 +27,20 @@ imaptest_SOURCES = \
2627
test-parser.c \
2728
user.c
2829

30+
imaptest_SOURCES = \
31+
$(common_sources) \
32+
imaptest.c
33+
2934
test_test_parser_SOURCES = test-test-parser.c test-parser.c settings.c
3035
test_test_parser_LDADD = $(imaptest_LDADD)
3136
test_test_parser_CFLAGS = $(imaptest_CFLAGS)
3237

38+
test_client_stalled_SOURCES = \
39+
$(common_sources) \
40+
test-client-stalled.c
41+
test_client_stalled_LDADD = $(imaptest_LDADD)
42+
test_client_stalled_CFLAGS = $(imaptest_CFLAGS)
43+
3344
noinst_HEADERS = \
3445
checkpoint.h \
3546
client.h \

src/client.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
int clients_count = 0;
3131
unsigned int total_disconnects = 0;
3232
ARRAY_TYPE(client) clients;
33-
ARRAY(unsigned int) stalled_clients;
33+
ARRAY_TYPE(unsigned_int) stalled_clients;
3434
bool stalled = FALSE, disconnect_clients = FALSE, scripted_tests_running = FALSE;
3535

3636
static unsigned int client_min_free_idx = 0;

src/client.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ struct client {
5050
bool idling:1;
5151
};
5252
ARRAY_DEFINE_TYPE(client, struct client *);
53+
ARRAY_DEFINE_TYPE(unsigned_int, unsigned int);
5354

5455
extern int clients_count;
5556
extern unsigned int total_disconnects;
5657
extern ARRAY_TYPE(client) clients;
58+
extern ARRAY_TYPE(unsigned_int) stalled_clients;
5759
extern bool stalled, disconnect_clients, scripted_tests_running;
5860

5961
struct client *client_new_user(struct user *user);

src/test-client-stalled.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/* Copyright (c) ImapTest authors, see the included COPYING file */
2+
3+
#include "lib.h"
4+
#include "ioloop.h"
5+
#include "array.h"
6+
#include "settings.h"
7+
#include "client.h"
8+
#include "imap-client.h"
9+
#include "user.h"
10+
#include "mailbox.h"
11+
#include "mailbox-source.h"
12+
#include "test-common.h"
13+
14+
struct settings conf;
15+
bool profile_running = FALSE;
16+
17+
/* Mock net_connect_ip to return a real (but unconnected) socket.
18+
This allows using real i_stream_create_fd() etc. without real network.
19+
Since we link against libdovecot, we can provide our own version of this
20+
function to override the one in the library for this test. */
21+
int net_connect_ip(const struct ip_addr *ip, in_port_t port, const struct ip_addr *my_ip)
22+
{
23+
return socket(AF_INET, SOCK_STREAM, 0);
24+
}
25+
26+
/* These are needed by imap-client.c and other files but are defined in imaptest.c */
27+
bool imaptest_has_clients(void) { return FALSE; }
28+
void sig_die(int signo ATTR_UNUSED, void *context ATTR_UNUSED) { exit(1); }
29+
void error_quit(void) { exit(1); }
30+
31+
static void test_stalled_client_reassignment(void)
32+
{
33+
struct user *user;
34+
struct imap_client *c1, *c2, *c3;
35+
struct ioloop *ioloop;
36+
struct mailbox_source *source;
37+
38+
test_begin("stalled client index reassignment");
39+
40+
ioloop = io_loop_create();
41+
42+
conf.clients_count = 10;
43+
conf.ips_count = 1;
44+
conf.ips = i_new(struct ip_addr, 1);
45+
conf.mailbox = "INBOX";
46+
47+
clients_init();
48+
mailboxes_init();
49+
50+
source = mailbox_source_new_random(1024);
51+
users_init(NULL, source);
52+
user = user_get("testuser", source);
53+
54+
/* 1. Add a client at index 0 */
55+
if (imap_client_new(0, user, NULL, &c1) < 0)
56+
i_fatal("imap_client_new 1 failed");
57+
58+
test_assert(array_count(&clients) == 1);
59+
test_assert(*(struct client **)array_idx(&clients, 0) == &c1->client);
60+
61+
/* 2. Set stalled = TRUE, then try to init another client at index 0.
62+
This simulates clients_unstalled() trying to reuse index 0. */
63+
stalled = TRUE;
64+
/* In the fixed code, client_init should have found that index 0 is occupied,
65+
found index 1 is free, and appended index 1 to stalled_clients. */
66+
if (imap_client_new(0, user, NULL, &c2) != -1)
67+
i_fatal("imap_client_new 2 should have stalled and returned -1");
68+
69+
const unsigned int *stalled_idxs;
70+
unsigned int stalled_count;
71+
stalled_idxs = array_get(&stalled_clients, &stalled_count);
72+
test_assert(stalled_count == 1);
73+
test_assert(stalled_idxs[0] == 1);
74+
75+
/* 3. Now let's occupy index 1 as well */
76+
stalled = FALSE;
77+
if (imap_client_new(1, user, NULL, &c3) < 0)
78+
i_fatal("imap_client_new 3 failed");
79+
80+
test_assert(array_count(&clients) == 2);
81+
test_assert(*(struct client **)array_idx(&clients, 1) == &c3->client);
82+
83+
/* 4. Now try to stall index 0 again. It should find index 2. */
84+
stalled = TRUE;
85+
if (imap_client_new(0, user, NULL, &c2) != -1)
86+
i_fatal("imap_client_new 4 should have stalled and returned -1");
87+
88+
stalled_idxs = array_get(&stalled_clients, &stalled_count);
89+
test_assert(stalled_count == 2);
90+
test_assert(stalled_idxs[1] == 2);
91+
92+
client_unref(&c1->client, FALSE);
93+
client_unref(&c3->client, FALSE);
94+
95+
users_deinit();
96+
mailbox_source_unref(&source);
97+
mailboxes_deinit();
98+
clients_deinit();
99+
array_free(&clients);
100+
i_free(conf.ips);
101+
102+
io_loop_destroy(&ioloop);
103+
104+
test_end();
105+
}
106+
107+
static void test_nonstalled_client_reassignment(void)
108+
{
109+
struct user *user;
110+
struct imap_client *c1, *c2;
111+
struct ioloop *ioloop;
112+
struct mailbox_source *source;
113+
114+
test_begin("non-stalled client index reassignment");
115+
116+
ioloop = io_loop_create();
117+
118+
conf.clients_count = 10;
119+
conf.ips_count = 1;
120+
conf.ips = i_new(struct ip_addr, 1);
121+
conf.mailbox = "INBOX";
122+
123+
clients_init();
124+
mailboxes_init();
125+
126+
source = mailbox_source_new_random(1024);
127+
users_init(NULL, source);
128+
user = user_get("testuser", source);
129+
130+
/* 1. Add a client at index 0 */
131+
if (imap_client_new(0, user, NULL, &c1) < 0)
132+
i_fatal("imap_client_new 1 failed");
133+
134+
test_assert(array_count(&clients) == 1);
135+
test_assert(c1->client.idx == 0);
136+
137+
/* 2. With stalled = FALSE, request index 0 which is occupied.
138+
client_init should find index 1 free and create the client there. */
139+
stalled = FALSE;
140+
if (imap_client_new(0, user, NULL, &c2) < 0)
141+
i_fatal("imap_client_new 2 failed");
142+
143+
test_assert(array_count(&clients) == 2);
144+
test_assert(c2->client.idx == 1);
145+
test_assert(*(struct client **)array_idx(&clients, 1) == &c2->client);
146+
147+
/* 3. Verify stalled_clients array is empty - nothing was stalled */
148+
const unsigned int *stalled_idxs;
149+
unsigned int stalled_count;
150+
stalled_idxs = array_get(&stalled_clients, &stalled_count);
151+
test_assert(stalled_count == 0);
152+
153+
client_unref(&c1->client, FALSE);
154+
client_unref(&c2->client, FALSE);
155+
156+
users_deinit();
157+
mailbox_source_unref(&source);
158+
mailboxes_deinit();
159+
clients_deinit();
160+
array_free(&clients);
161+
i_free(conf.ips);
162+
163+
io_loop_destroy(&ioloop);
164+
165+
test_end();
166+
}
167+
168+
int main(void)
169+
{
170+
static void (*const test_functions[])(void) = {
171+
test_stalled_client_reassignment,
172+
test_nonstalled_client_reassignment,
173+
NULL
174+
};
175+
return test_run(test_functions);
176+
}

0 commit comments

Comments
 (0)