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)
124125static 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+
158265cleanup :
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