Merge fix for logging of mode changes by charly.
[jelmer/ctrlproxy.git] / testsuite / test-linestack.c
1 /*
2         (c) 2006 Jelmer Vernooij <jelmer@nl.linux.org>
3
4         This program is free software; you can redistribute it and/or modify
5         it under the terms of the GNU General Public License as published by
6         the Free Software Foundation; either version 3 of the License, or
7         (at your option) any later version.
8
9         This program is distributed in the hope that it will be useful,
10         but WITHOUT ANY WARRANTY; without even the implied warranty of
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12         GNU General Public License for more details.
13
14         You should have received a copy of the GNU General Public License
15         along with this program; if not, write to the Free Software
16         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <check.h>
22 #include "ctrlproxy.h"
23 #include "torture.h"
24 #include "internals.h"
25
26 void stack_process(struct linestack_context *ctx, struct irc_network_state *ns, const char *line)
27 {
28         struct irc_line *l;
29         l = irc_parse_line(line);
30         g_assert(l);
31         g_assert(state_handle_data(ns, l));
32         g_assert(linestack_insert_line(ctx, l, FROM_SERVER, ns));
33         free_line(l);
34 }
35
36 #define null_equal(a,b) { \
37         if ((a) == NULL && (b) == NULL) \
38                 return TRUE; \
39         if ((a) == NULL || (b) == NULL) \
40         return FALSE; \
41 }
42
43 static gboolean list_equal(GList *list1, GList *list2, GEqualFunc eq)
44 {
45         GList *gl1, *gl2;
46         null_equal(list1, list2);
47
48         for (gl1 = list1, gl2 = list2; gl1 && gl2; gl1 = gl1->next, gl2 = gl2->next) {
49                 if (!eq(gl1->data, gl2->data))
50                         return FALSE;
51         }
52
53         if (gl1 != NULL || gl2 != NULL)
54                 return FALSE;
55
56         return TRUE;
57 }
58
59 static gboolean modes_equal(const irc_modes_t a, const irc_modes_t b)
60 {
61         return modes_cmp(a, b) == 0;
62 }
63
64 static gboolean str_equal(const char *a, const char *b)
65 {
66         null_equal(a, b);
67
68         return g_str_equal(a, b);
69 }
70
71 static gboolean channel_nick_equal(const struct channel_nick *nick1, const struct channel_nick *nick2)
72 {
73         null_equal(nick1, nick2);
74
75         return modes_equal(nick1->modes, nick2->modes) &&
76                    str_equal(nick1->global_nick->nick, nick2->global_nick->nick) &&
77                    str_equal(nick1->channel->name, nick2->channel->name);
78 }
79
80 static gboolean banlist_entry_equal(const struct nicklist_entry *entry1, const struct nicklist_entry *entry2)
81 {
82         null_equal(entry1, entry2);
83
84         return str_equal(entry1->hostmask, entry2->hostmask) &&
85                    str_equal(entry1->by, entry2->by) &&
86                    entry1->time_set == entry2->time_set;
87 }
88
89 static gboolean channel_state_equal(const struct irc_channel_state *channel1, const struct irc_channel_state *channel2)
90 {
91         int i;
92         null_equal(channel1, channel2);
93         
94         for (i = 0; i < MAXMODES; i++) {
95                 if (!str_equal(channel1->chanmode_option[i], channel2->chanmode_option[i]))
96                         return FALSE;
97
98                 if (!list_equal(channel1->chanmode_nicklist[i], channel2->chanmode_nicklist[i], (GEqualFunc)banlist_entry_equal))
99                         return FALSE;
100         }
101
102         return str_equal(channel1->name, channel2->name) &&
103                    str_equal(channel1->topic, channel2->topic) &&
104                    channel1->mode == channel2->mode &&
105                    !memcmp(channel1->modes, channel2->modes, 255) &&
106                    channel1->namreply_started == channel2->namreply_started &&
107                    channel1->invitelist_started == channel2->invitelist_started &&
108                    channel1->exceptlist_started == channel2->exceptlist_started &&
109                    channel1->banlist_started == channel2->banlist_started &&
110                    list_equal(channel1->nicks, channel2->nicks, (GEqualFunc)channel_nick_equal);
111 }
112
113 static gboolean network_info_equal(const struct irc_network_info *info1, const struct irc_network_info *info2)
114 {
115         null_equal(info1, info2);
116
117         return str_equal(info1->name, info2->name) &&
118                    str_equal(info1->server, info2->server) &&
119                    str_equal(info1->supported_user_modes, info2->supported_user_modes) &&
120                    str_equal(info1->supported_channel_modes, info2->supported_channel_modes) &&
121                    str_equal(info1->prefix, info2->prefix) &&
122                    str_equal(info1->chantypes, info2->chantypes) &&
123                    str_equal(info1->charset, info2->charset) &&
124                    ((info1->chanmodes == NULL && info2->chanmodes == NULL) ||
125                    (str_equal(info1->chanmodes[0], info2->chanmodes[0]) &&
126                    str_equal(info1->chanmodes[1], info2->chanmodes[1]) &&
127                    str_equal(info1->chanmodes[2], info2->chanmodes[2]) &&
128                    str_equal(info1->chanmodes[3], info2->chanmodes[3]))) &&
129                    info1->keylen == info2->keylen &&
130                    info1->silence == info2->silence &&
131                    info1->channellen == info2->channellen &&
132                    info1->awaylen == info2->awaylen &&
133                    info1->maxtargets == info2->maxtargets &&
134                    info1->nicklen == info2->nicklen &&
135                    info1->userlen == info2->userlen &&
136                    info1->hostlen == info2->hostlen &&
137                    info1->maxchannels == info2->maxchannels &&
138                    info1->topiclen == info2->topiclen &&
139                    info1->maxbans == info2->maxbans &&
140                    info1->maxmodes == info2->maxmodes &&
141                    info1->wallchops == info2->wallchops &&
142                    info1->wallvoices == info2->wallvoices &&
143                    info1->rfc2812 == info2->rfc2812 &&
144                    info1->penalty == info2->penalty &&
145                    info1->forced_nick_changes == info2->forced_nick_changes &&
146                    info1->safelist == info2->safelist &&
147                    info1->userip == info2->userip &&
148                    info1->cprivmsg == info2->cprivmsg &&
149                    info1->cnotice == info2->cnotice &&
150                    info1->knock == info2->knock &&
151                    info1->vchannels == info2->vchannels &&
152                    info1->whox == info2->whox &&
153                    info1->callerid == info2->callerid &&
154                    info1->accept == info2->accept &&
155                    info1->capab == info2->capab &&
156                    info1->casemapping == info2->casemapping;
157 }
158
159 static gboolean network_nick_equal(const struct network_nick *nick1, const struct network_nick *nick2)
160 {
161         null_equal(nick1, nick2);
162
163         return nick1->query == nick2->query &&
164                    str_equal(nick1->nick, nick2->nick) &&
165                    str_equal(nick1->fullname, nick2->fullname) &&
166                    str_equal(nick1->username, nick2->username) &&
167                    str_equal(nick1->hostname, nick2->hostname) &&
168                    !memcmp(nick1->modes, nick2->modes, 255) &&
169                    list_equal(nick1->channel_nicks, nick2->channel_nicks, 
170                                           (GEqualFunc)channel_nick_equal);
171 }
172
173 static gboolean network_state_equal(const struct irc_network_state *state1, 
174                                                                         const struct irc_network_state *state2)
175 {
176         null_equal(state1, state2);
177
178         return network_nick_equal(&state1->me, &state2->me) &&
179                    network_info_equal(state1->info, state2->info) &&
180                    list_equal(state1->channels, state2->channels, 
181                                           (GEqualFunc)channel_state_equal) &&
182                    list_equal(state1->nicks, state2->nicks, 
183                                           (GEqualFunc)network_nick_equal);
184 }
185
186 const char *get_linestack_tempdir(const char *base)
187 {
188         return g_build_filename("/tmp", base, NULL);
189 }
190
191 START_TEST(test_empty)
192         struct irc_network_state *ns1, *ns2;
193         struct linestack_context *ctx;
194         
195         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
196         ctx = create_linestack(get_linestack_tempdir("test_empty"), TRUE, ns1);
197
198         ns2 = linestack_get_state(ctx, NULL);
199
200         fail_unless (network_state_equal(ns1, ns2), 
201                                  "Network state returned not equal");
202 END_TEST
203
204 START_TEST(test_msg)
205         struct irc_network_state *ns1;
206         struct linestack_context *ctx;
207         linestack_marker lm;
208         struct irc_client *cl;
209
210         GIOChannel *ch1, *ch2;
211         char *raw;
212         
213         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
214         ctx = create_linestack(get_linestack_tempdir("msg"), TRUE, ns1);
215
216         lm = linestack_get_marker(ctx);
217
218         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
219         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi");
220
221         g_io_channel_pair(&ch1, &ch2);
222         g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
223         g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
224         cl = client_init_iochannel(NULL, ch1, "test");
225         g_io_channel_unref(ch1);
226
227         linestack_send(ctx, lm, NULL, cl, FALSE, FALSE, 0);
228         client_disconnect(cl, "foo");
229
230         g_io_channel_read_to_end(ch2, &raw, NULL, NULL);
231
232         fail_unless(!strcmp(raw, ":bla!Gebruikersnaam@Computernaam JOIN #bla\r\n"
233                                                      ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi\r\n"
234                                                          "ERROR :foo\r\n"));
235 END_TEST
236
237 START_TEST(test_join_part)
238         struct irc_network_state *ns1;
239         struct linestack_context *ctx;
240         linestack_marker lm;
241         struct irc_client *cl;
242
243         GIOChannel *ch1, *ch2;
244         char *raw;
245         
246         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
247         ctx = create_linestack(get_linestack_tempdir("join_part"), TRUE, ns1);
248
249         lm = linestack_get_marker(ctx);
250
251         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
252         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam PART #bla :hihi");
253
254         g_io_channel_pair(&ch1, &ch2);
255         g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
256         g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
257         cl = client_init_iochannel(NULL, ch1, "test");
258         g_io_channel_unref(ch1);
259
260         linestack_send(ctx, lm, NULL, cl, FALSE, FALSE, 0);
261         g_main_iteration(FALSE);
262         client_disconnect(cl, "foo");
263         g_main_iteration(FALSE);
264         g_io_channel_read_to_end(ch2, &raw, NULL, NULL);
265
266         fail_unless(!strcmp(raw, ":bla!Gebruikersnaam@Computernaam JOIN #bla\r\n"
267                                                      ":bla!Gebruikersnaam@Computernaam PART #bla :hihi\r\n"
268                                                          "ERROR :foo\r\n"), "Got %s", raw);
269 END_TEST
270
271
272
273 START_TEST(test_skip_msg)
274         struct irc_network_state *ns1;
275         struct linestack_context *ctx;
276         linestack_marker lm;
277         struct irc_client *cl;
278
279         GIOChannel *ch1, *ch2;
280         char *raw;
281         
282         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
283         ctx = create_linestack(get_linestack_tempdir("skip_msg"), TRUE, ns1);
284
285         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :haha");
286
287         lm = linestack_get_marker(ctx);
288
289         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
290         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi");
291
292         g_io_channel_pair(&ch1, &ch2);
293         g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
294         g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
295         cl = client_init_iochannel(NULL, ch1, "test");
296         g_io_channel_unref(ch1);
297
298         linestack_send(ctx, lm, NULL, cl, FALSE, FALSE, 0);
299         client_disconnect(cl, "foo");
300
301         g_io_channel_read_to_end(ch2, &raw, NULL, NULL);
302
303         fail_unless(!strcmp(raw, ":bla!Gebruikersnaam@Computernaam JOIN #bla\r\n"
304                                                      ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi\r\n"
305                                                          "ERROR :foo\r\n"));
306 END_TEST
307
308 START_TEST(test_object_msg)
309         struct irc_network_state *ns1;
310         struct linestack_context *ctx;
311         linestack_marker lm;
312         struct irc_client *cl;
313
314         GIOChannel *ch1, *ch2;
315         char *raw;
316         
317         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
318         ctx = create_linestack(get_linestack_tempdir("get_object_msg"), TRUE, ns1);
319
320         lm = linestack_get_marker(ctx);
321
322         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #foo");
323         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
324         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #foo :hihi");
325         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bar :hihi");
326         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #blablie :hihi");
327         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi");
328
329         g_io_channel_pair(&ch1, &ch2);
330         g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
331         g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
332         cl = client_init_iochannel(NULL, ch1, "test");
333         g_io_channel_unref(ch1);
334
335         linestack_send_object(ctx, "#bla", lm, NULL, cl, FALSE, FALSE, 0);
336         client_disconnect(cl, "foo");
337
338         g_io_channel_read_to_end(ch2, &raw, NULL, NULL);
339
340         fail_unless(!strcmp(raw, ":bla!Gebruikersnaam@Computernaam JOIN #bla\r\n"
341                                                      ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi\r\n"
342                                                          "ERROR :foo\r\n"));
343 END_TEST
344
345
346 START_TEST(test_object_open)
347         struct irc_network_state *ns1;
348         struct linestack_context *ctx;
349         linestack_marker lm;
350         struct irc_client *cl;
351
352         GIOChannel *ch1, *ch2;
353         char *raw;
354
355         int j;
356         
357         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
358         ctx = create_linestack(get_linestack_tempdir("test_object_open"), TRUE, ns1);
359
360         lm = linestack_get_marker(ctx);
361
362         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #foo");
363         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
364         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #foo :hihi");
365         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bar :hihi");
366         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #blablie :hihi");
367         stack_process(ctx, ns1, ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi");
368
369         for (j = 0; j < 4; j++) {
370                 g_io_channel_pair(&ch1, &ch2);
371                 g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
372                 g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
373                 cl = client_init_iochannel(NULL, ch1, "test");
374                 g_io_channel_unref(ch1);
375
376                 linestack_send_object(ctx, "#bla", NULL, NULL, cl, FALSE, FALSE, 0);
377                 client_disconnect(cl, "foo");
378
379                 g_io_channel_read_to_end(ch2, &raw, NULL, NULL);
380
381                 fail_unless(!strcmp(raw, ":bla!Gebruikersnaam@Computernaam JOIN #bla\r\n"
382                                                 ":bloe!Gebruikersnaam@Computernaam PRIVMSG #bla :hihi\r\n"
383                                                 "ERROR :foo\r\n"));
384         }
385 END_TEST
386
387 START_TEST(test_join)
388         struct irc_network_state *ns1, *ns2;
389         struct linestack_context *ctx;
390         
391         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
392         ctx = create_linestack(get_linestack_tempdir("test_join"), TRUE, ns1);
393
394         stack_process(ctx, ns1, ":bla!Gebruikersnaam@Computernaam JOIN #bla");
395
396         ns2 = linestack_get_state(ctx, linestack_get_marker(ctx));
397
398         fail_unless (network_state_equal(ns1, ns2), "Network state returned not equal");
399 END_TEST
400
401 int seen = 0;
402
403 static gboolean line_track(struct irc_line *l, time_t t, void *data)
404 {
405         fail_unless(!strcmp(l->args[0], "PRIVMSG"));
406         fail_unless(seen == atoi(l->args[1]));
407         seen++;
408         return TRUE;
409 }
410
411 START_TEST(bench_lots_of_lines)
412         struct irc_network_state *ns1;
413         struct linestack_context *ctx;
414         linestack_marker marker;
415         int i;
416
417         ns1 = network_state_init("bla", "Gebruikersnaam", "Computernaam");
418         ctx = create_linestack(get_linestack_tempdir("lots of lines"), TRUE, ns1);
419         marker = linestack_get_marker(ctx);
420
421         seen = 0;
422
423         for (i = 0; i < 10000; i++) 
424                 linestack_insert_line(ctx, irc_parse_linef("PRIVMSG :%d", i), 
425                                                           TO_SERVER, ns1);
426         
427         linestack_traverse(ctx, marker, NULL, line_track, stderr);
428 END_TEST
429
430 Suite *linestack_suite()
431 {
432         Suite *s = suite_create("linestack");
433         TCase *tc_core = tcase_create("core");
434         suite_add_tcase(s, tc_core);
435         tcase_add_test(tc_core, test_empty);
436         tcase_add_test(tc_core, test_join);
437         tcase_add_test(tc_core, test_msg);
438         tcase_add_test(tc_core, test_skip_msg);
439         tcase_add_test(tc_core, test_object_msg);
440         tcase_add_test(tc_core, test_object_open);
441         tcase_add_test(tc_core, test_join_part);
442         tcase_add_test(tc_core, bench_lots_of_lines);
443         return s;
444 }