Skip to content

Commit ff0763b

Browse files
committed
test_mail_stress: Add IMAP stress tests.
Extend the existing mail stress tests by adding some IMAP tests that should have linear running time with respect to the number of messages in the mailbox. This includes: * LIST-STATUS with STATUS=SIZE * SEARCH The COPY tests previously failed, prior to commit 8ec4fbc.
1 parent 8ec4fbc commit ff0763b

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

tests/configs/mod_mail.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[general]
22
maildir=/tmp/test_lbbs/maildir
3+
quota=40000000 ; lot of quota needed for test_mail_stres
34

45
[aliases]
56
aliasuser = testuser

tests/test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ int test_client_drain(int fd, int ms);
151151
#define CLIENT_EXPECT(fd, s) if (test_client_expect(fd, SEC_MS(DEFAULT_WAIT_SEC), s, __LINE__)) { goto cleanup; }
152152
#define CLIENT_EXPECT_BUF(fd, s, buf) if (test_client_expect_buf(fd, SEC_MS(DEFAULT_WAIT_SEC), s, __LINE__, buf, sizeof(buf))) { goto cleanup; }
153153
#define CLIENT_EXPECT_EVENTUALLY(fd, s) if (test_client_expect_eventually(fd, SEC_MS(DEFAULT_WAIT_SEC), s, __LINE__)) { goto cleanup; }
154+
#define CLIENT_EXPECT_EVENTUALLY_SEC(fd, sec, s) if (test_client_expect_eventually(fd, SEC_MS(sec), s, __LINE__)) { goto cleanup; }
154155
/* We really may need up to 150ms when under valgrind */
155156
#define CLIENT_DRAIN(fd) test_client_drain(fd, 150)
156157

tests/test_mail_stress.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <string.h>
2727
#include <time.h>
2828
#include <sys/stat.h>
29+
#include <sys/time.h>
2930
#include <pthread.h>
3031
#include <sys/socket.h> /* use SOMAXCONN */
3132
#include <netinet/in.h> /* use sockaddr_in */
@@ -124,6 +125,17 @@ static void *send_thread(void *varg)
124125
static int run(void)
125126
{
126127
int i, res = -1;
128+
int imapfd = -1;
129+
struct timeval tv_start, tv_end;
130+
long t_smtp, t_list_status, t_select, t_copy1, t_copy4, t_copy5, t_copy20, t_copy40;
131+
long t2_list_status, t2_select, t2_search;
132+
133+
#define START_TIMER() gettimeofday(&tv_start, NULL)
134+
#define END_TIMER(var) \
135+
gettimeofday(&tv_end, NULL); \
136+
var = (1000000 * tv_end.tv_sec + tv_end.tv_usec) - (1000000 * tv_start.tv_sec + tv_start.tv_usec)
137+
#define PRINT_TIME(var, name) \
138+
fprintf(stderr, "%-25s | %5ld.%03d ms\n", name, var / 1000, (int) (var % 1000))
127139

128140
/* Initialization */
129141
for (i = 0; i < NUM_SMTP_THREADS; i++) {
@@ -132,6 +144,7 @@ static int run(void)
132144
}
133145

134146
/* First, send all the test messages */
147+
START_TIMER();
135148
for (i = 0; i < NUM_SMTP_THREADS; i++) {
136149
int pres = pthread_create(&smtp_threads[i], NULL, send_thread, &smtp_index[i]);
137150
if (pres) {
@@ -149,19 +162,114 @@ static int run(void)
149162
smtp_threads[i] = 0;
150163
}
151164
}
165+
END_TIMER(t_smtp);
152166

153167
/* Verify that the email messages were all sent properly. */
154168
DIRECTORY_EXPECT_FILE_COUNT(TEST_MAIL_DIR "/1/new", TARGET_MESSAGES);
155169

170+
/* If this test is being run from a console, clear the screen at this point, since the above SMTP output is lengthy.
171+
* That way, we can focus on the IMAP stuff from here on out. */
172+
fprintf(stderr, TERM_CLEAR_SCROLLBACK);
173+
174+
/* Log in via IMAP to look at some of the messages */
175+
imapfd = test_make_socket(143);
176+
REQUIRE_FD(imapfd);
177+
CLIENT_EXPECT(imapfd, "OK");
178+
179+
SWRITE(imapfd, "a1 LOGIN \"" TEST_USER "\" \"" TEST_PASS "\"" ENDL);
180+
CLIENT_EXPECT(imapfd, "a1 OK");
181+
182+
/* Perform some operations that are linear with respect to the current mailbox,
183+
* things that would likely perform better with some kind of caching mechanism. */
184+
185+
/* First, LIST-STATUS with STATUS=SIZE. */
186+
START_TIMER();
187+
SWRITE(imapfd, "a2 LIST \"\" \"*\" RETURN (CHILDREN STATUS (MESSAGES RECENT UNSEEN SIZE))" ENDL);
188+
CLIENT_EXPECT_EVENTUALLY(imapfd, "a2 OK");
189+
END_TIMER(t_list_status);
190+
191+
/* Select the INBOX (where all the messages are) */
192+
START_TIMER();
193+
SWRITE(imapfd, "a3 SELECT \"INBOX\"" ENDL);
194+
CLIENT_EXPECT_EVENTUALLY(imapfd, "a3 OK");
195+
END_TIMER(t_select);
196+
197+
/* Copy the initial 10k messages a few times so we end up with 100k messages for IMAP load testing.
198+
* Start by copying small batches, and move up towards increasingly large batches.
199+
* It's MUCH faster to create new messages this way than using SMTP transactions. */
200+
START_TIMER();
201+
SWRITE(imapfd, "b1 UID COPY 1:1000 \"INBOX\"" ENDL); /* End with 11,000 */
202+
CLIENT_EXPECT_EVENTUALLY(imapfd, "b1 OK");
203+
END_TIMER(t_copy1);
204+
205+
START_TIMER();
206+
SWRITE(imapfd, "b2 UID COPY 1001:5000 \"INBOX\"" ENDL); /* End with 15,000 */
207+
CLIENT_EXPECT_EVENTUALLY(imapfd, "b2 OK");
208+
END_TIMER(t_copy4);
209+
210+
START_TIMER();
211+
SWRITE(imapfd, "b3 UID COPY 5001:10000 \"INBOX\"" ENDL); /* End with 20,000 */
212+
CLIENT_EXPECT_EVENTUALLY(imapfd, "b3 OK");
213+
END_TIMER(t_copy5);
214+
215+
START_TIMER();
216+
SWRITE(imapfd, "b4 UID COPY 1:20000 \"INBOX\"" ENDL); /* End with 40,000 */
217+
CLIENT_EXPECT_EVENTUALLY_SEC(imapfd, 15, "b4 OK"); /* May need more time with high debug */
218+
END_TIMER(t_copy20);
219+
220+
START_TIMER();
221+
SWRITE(imapfd, "b5 UID COPY 1:40000 \"INBOX\"" ENDL); /* End with 80,000 */
222+
CLIENT_EXPECT_EVENTUALLY_SEC(imapfd, 30, "b5 OK"); /* May need more time with high debug */
223+
END_TIMER(t_copy40);
224+
225+
SWRITE(imapfd, "b6 UNSELECT" ENDL);
226+
CLIENT_EXPECT(imapfd, "b6 OK");
227+
228+
/* Now that the mailbox is somewhat large (80k messages), repeat some of the initial tests. */
229+
START_TIMER();
230+
SWRITE(imapfd, "c1 LIST \"\" \"*\" RETURN (CHILDREN STATUS (MESSAGES RECENT UNSEEN SIZE))" ENDL);
231+
CLIENT_EXPECT_EVENTUALLY(imapfd, "c1 OK");
232+
END_TIMER(t2_list_status);
233+
234+
START_TIMER();
235+
SWRITE(imapfd, "c2 SELECT \"INBOX\"" ENDL);
236+
CLIENT_EXPECT_EVENTUALLY(imapfd, "c2 OK");
237+
END_TIMER(t2_select);
238+
239+
/* Search for messages with subject containing "1337".
240+
* There should be exactly 8 (the original one from SMTP, and the 7 copies we made).
241+
* This should take a little more time, given we actually need to OPEN 80,000 messages
242+
* and parse all their headers. */
243+
START_TIMER();
244+
SWRITE(imapfd, "c3 SEARCH HEADER \"From\" \"1337\"" ENDL);
245+
/* We can't expect exact sequence numbers, as depending on the order in which the initial messages delivered
246+
* via SMTP were processed, the last 4 digits may vary, i.e. 1ABC, 11ABC, 21ABC, 31ABC, etc.
247+
* Manually run the test, can confirm the last 4 digits, i.e. 1ABC are the same for all the results. */
248+
CLIENT_EXPECT(imapfd, "* SEARCH 1"); /* This just matches the beginning (i.e. search result is non-empty and first sequence # starts with 1 */
249+
END_TIMER(t2_search);
250+
156251
res = 0;
157252

253+
PRINT_TIME(t_smtp, "SMTP send 10k");
254+
PRINT_TIME(t_list_status, "LIST-STATUS 10k");
255+
PRINT_TIME(t_select, "SELECT 10k");
256+
PRINT_TIME(t_copy1, "COPY 1k");
257+
PRINT_TIME(t_copy4, "COPY 4k");
258+
PRINT_TIME(t_copy5, "COPY 5k");
259+
PRINT_TIME(t_copy20, "COPY 20k");
260+
PRINT_TIME(t_copy40, "COPY 40k");
261+
PRINT_TIME(t2_list_status, "LIST-STATUS 80k");
262+
PRINT_TIME(t2_select, "SELECT 80k");
263+
PRINT_TIME(t2_search, "SEARCH 80k");
264+
158265
cleanup:
159266
for (i = 0; i < NUM_SMTP_THREADS; i++) {
160267
if (smtp_threads[i]) {
161268
pthread_join(smtp_threads[i], NULL);
162269
smtp_threads[i] = 0;
163270
}
164271
}
272+
close_if(imapfd);
165273
return res;
166274
}
167275

0 commit comments

Comments
 (0)