|
1 #include "irc_chan.h" |
|
2 #include "irc_net.h" |
|
3 #include "test.h" |
|
4 |
|
5 |
|
6 static void _on_chan_self_join (struct irc_chan *chan, void *arg) |
|
7 { |
|
8 struct test_chan_ctx *ctx = arg; |
|
9 |
|
10 assert(chan == ctx->chan); |
|
11 |
|
12 ctx->on_chan_self_join = true; |
|
13 |
|
14 log_debug("on_self_join"); |
|
15 } |
|
16 |
|
17 static void _on_chan_join (struct irc_chan *chan, const struct irc_nm *source, void *arg) |
|
18 { |
|
19 struct test_chan_ctx *ctx = arg; |
|
20 |
|
21 assert(chan == ctx->chan); |
|
22 |
|
23 // XXX: verify source |
|
24 |
|
25 ctx->on_chan_join = true; |
|
26 |
|
27 log_debug("on_join"); |
|
28 } |
|
29 |
|
30 static void _on_chan_part (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg) |
|
31 { |
|
32 struct test_chan_ctx *ctx = arg; |
|
33 |
|
34 assert(chan == ctx->chan); |
|
35 |
|
36 // XXX: verify source |
|
37 // XXX: verify msg |
|
38 |
|
39 ctx->on_chan_part = true; |
|
40 |
|
41 log_debug("on_part"); |
|
42 } |
|
43 |
|
44 |
|
45 struct irc_chan_callbacks _chan_callbacks = { |
|
46 .on_self_join = _on_chan_self_join, |
|
47 .on_join = _on_chan_join, |
|
48 .on_part = _on_chan_part, |
|
49 }; |
|
50 |
|
51 |
|
52 struct irc_chan_user* check_chan_user (struct irc_chan *chan, const char *nickname, bool exists) |
|
53 { |
|
54 struct irc_chan_user *chan_user = irc_chan_get_user(chan, nickname); |
|
55 |
|
56 if (exists && chan_user == NULL) |
|
57 FATAL("user %s not found in channel %s", dump_str(nickname), dump_str(irc_chan_name(chan))); |
|
58 |
|
59 if (!exists && chan_user) |
|
60 FATAL("user %s should not be on channel %s anymore", dump_str(nickname), dump_str(irc_chan_name(chan))); |
|
61 |
|
62 log_debug("%s, exists=%d -> %p: user=%p, nickname=%s", |
|
63 nickname, exists, chan_user, chan_user ? chan_user->user : NULL, chan_user ? chan_user->user->nickname : NULL); |
|
64 |
|
65 if (chan_user) |
|
66 assert_strcmp(chan_user->user->nickname, nickname); |
|
67 |
|
68 return chan_user; |
|
69 } |
|
70 |
|
71 /** |
|
72 * Creates an irc_chan on the given irc_net, but does not check any output (useful for testing offline add). |
|
73 * |
|
74 * You must pass a test_chan_ctx for use with later operations, this will be initialized for you. |
|
75 */ |
|
76 static struct irc_chan* setup_irc_chan_raw (struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
77 { |
|
78 struct irc_chan *chan; |
|
79 struct irc_chan_info chan_info = { |
|
80 .channel = channel, |
|
81 }; |
|
82 struct error_info err; |
|
83 |
|
84 // initialize the given ctx |
|
85 memset(ctx, 0, sizeof(*ctx)); |
|
86 ctx->channel = channel; |
|
87 |
|
88 // add a channel |
|
89 assert_success(irc_net_add_chan(net, &chan, &chan_info, &err)); |
|
90 assert(!chan->joined); |
|
91 assert_success(irc_chan_add_callbacks(chan, &_chan_callbacks, ctx)); |
|
92 ctx->chan = chan; |
|
93 |
|
94 // ok |
|
95 return chan; |
|
96 } |
|
97 |
|
98 /** |
|
99 * Checks that the JOIN request for a channel was sent, and sends the basic JOIN reply |
|
100 */ |
|
101 static void do_irc_chan_join (struct transport_test *tp, struct test_chan_ctx *ctx) |
|
102 { |
|
103 // JOIN request |
|
104 assert(ctx->chan->joining); |
|
105 assert_transport_data(tp, "JOIN %s\r\n", ctx->channel); |
|
106 |
|
107 // JOIN reply |
|
108 transport_test_push_fmt(tp, ":mynick!user@host JOIN %s\r\n", ctx->channel); |
|
109 assert(!ctx->chan->joining && ctx->chan->joined); |
|
110 assert(ctx->on_chan_self_join); |
|
111 } |
|
112 |
|
113 /** |
|
114 * Sends a short RPL_NAMREPLY/RPL_ENDOFNAMES reply and checks that the users list matches |
|
115 */ |
|
116 static void do_irc_chan_namreply (struct transport_test *tp, struct test_chan_ctx *ctx) |
|
117 { |
|
118 // RPL_NAMREPLY |
|
119 transport_test_push_fmt(tp, "353 mynick = %s :mynick userA +userB @userC\r\n", ctx->channel); |
|
120 transport_test_push_fmt(tp, "353 mynick = %s :trailingspace \r\n", ctx->channel); |
|
121 transport_test_push_fmt(tp, "366 mynick %s :End of NAMES\r\n", ctx->channel); |
|
122 |
|
123 // XXX: this should be an exclusive test, i.e. these should be the only ones... |
|
124 check_chan_user(ctx->chan, "mynick", true); |
|
125 check_chan_user(ctx->chan, "userA", true); |
|
126 check_chan_user(ctx->chan, "userB", true); |
|
127 check_chan_user(ctx->chan, "userC", true); |
|
128 } |
|
129 |
|
130 /** |
|
131 * Creates an irc_chan on the given irc_net, and checks up to the JOIN reply |
|
132 */ |
|
133 static struct irc_chan* setup_irc_chan_join (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
134 { |
|
135 setup_irc_chan_raw(net, channel, ctx); |
|
136 do_irc_chan_join(tp, ctx); |
|
137 |
|
138 // ok |
|
139 return ctx->chan; |
|
140 } |
|
141 |
|
142 struct irc_chan* setup_irc_chan (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
143 { |
|
144 setup_irc_chan_raw(net, channel, ctx); |
|
145 do_irc_chan_join(tp, ctx); |
|
146 do_irc_chan_namreply(tp, ctx); |
|
147 |
|
148 // ok |
|
149 return ctx->chan; |
|
150 } |
|
151 |
|
152 |
|
153 void test_irc_chan_add_offline (void) |
|
154 { |
|
155 struct test_chan_ctx ctx; |
|
156 struct transport_test *tp = setup_transport_test(); |
|
157 struct irc_net *net = setup_irc_net_unregistered(tp); |
|
158 |
|
159 // add an offline channel |
|
160 log_info("test offline irc_net_add_chan"); |
|
161 struct irc_chan *chan = setup_irc_chan_raw(net, "#test", &ctx); |
|
162 assert(!chan->joining && !chan->joined); |
|
163 |
|
164 // send the registration reply |
|
165 log_info("test irc_conn_on_RPL_WELCOME"); |
|
166 test_irc_net_welcome(tp, net); |
|
167 |
|
168 // test the join sequence |
|
169 log_info("test irc_chan_join/irc_chan_on_JOIN"); |
|
170 do_irc_chan_join(tp, &ctx); |
|
171 |
|
172 // cleanup |
|
173 irc_net_destroy(net); |
|
174 } |
|
175 |
|
176 void test_irc_chan_namreply (void) |
|
177 { |
|
178 struct test_chan_ctx ctx; |
|
179 struct transport_test *tp = setup_transport_test(); |
|
180 struct irc_net *net = setup_irc_net(tp); |
|
181 setup_irc_chan_join(tp, net, "#test", &ctx); |
|
182 |
|
183 log_info("test irc_chan_on_RPL_NAMREPLY"); |
|
184 do_irc_chan_namreply(tp, &ctx); |
|
185 |
|
186 // cleanup |
|
187 irc_net_destroy(net); |
|
188 } |
|
189 |
|
190 void test_irc_chan_user_join (void) |
|
191 { |
|
192 struct test_chan_ctx ctx; |
|
193 struct transport_test *tp = setup_transport_test(); |
|
194 struct irc_net *net = setup_irc_net(tp); |
|
195 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
196 |
|
197 // have a user join |
|
198 log_info("test irc_chan_on_JOIN"); |
|
199 transport_test_push_fmt(tp, ":newuser!someone@somewhere JOIN %s\r\n", "#test"); |
|
200 assert(ctx.on_chan_join); |
|
201 check_chan_user(chan, "newuser", true); |
|
202 |
|
203 // cleanup |
|
204 irc_net_destroy(net); |
|
205 } |
|
206 |
|
207 void test_irc_chan_user_part (void) |
|
208 { |
|
209 struct test_chan_ctx ctx; |
|
210 struct transport_test *tp = setup_transport_test(); |
|
211 struct irc_net *net = setup_irc_net(tp); |
|
212 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
213 |
|
214 // have a user join |
|
215 log_info("test irc_chan_on_PART"); |
|
216 transport_test_push_fmt(tp, ":userA!someone@somewhere PART %s\r\n", "#test"); |
|
217 assert(ctx.on_chan_part); ctx.on_chan_part = NULL; |
|
218 check_chan_user(chan, "userA", false); |
|
219 |
|
220 // cleanup |
|
221 irc_net_destroy(net); |
|
222 } |
|
223 |
|
224 void test_irc_chan_user_kick (void) |
|
225 { |
|
226 struct test_chan_ctx ctx; |
|
227 struct transport_test *tp = setup_transport_test(); |
|
228 struct irc_net *net = setup_irc_net(tp); |
|
229 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
230 |
|
231 // kick a user |
|
232 log_info("test irc_chan_on_KICK (other)"); |
|
233 transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s userB\r\n", "#test"); |
|
234 check_chan_user(chan, "userA", true); |
|
235 check_chan_user(chan, "userB", false); |
|
236 |
|
237 // cleanup |
|
238 irc_net_destroy(net); |
|
239 } |
|
240 |
|
241 void test_irc_chan_self_kick (void) |
|
242 { |
|
243 struct test_chan_ctx ctx; |
|
244 struct transport_test *tp = setup_transport_test(); |
|
245 struct irc_net *net = setup_irc_net(tp); |
|
246 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
247 |
|
248 // kick a user |
|
249 log_info("test irc_chan_on_KICK (self)"); |
|
250 transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s mynick foobar\r\n", "#test"); |
|
251 assert(!chan->joined); |
|
252 assert(chan->kicked); |
|
253 |
|
254 // cleanup |
|
255 irc_net_destroy(net); |
|
256 } |
|
257 |
|
258 void test_irc_chan_user_nick (void) |
|
259 { |
|
260 struct test_chan_ctx ctx; |
|
261 struct transport_test *tp = setup_transport_test(); |
|
262 struct irc_net *net = setup_irc_net(tp); |
|
263 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
264 |
|
265 // rename one of the users |
|
266 log_info("test irc_net_on_chanuser"); |
|
267 transport_test_push_fmt(tp, ":userA!someone@somewhere NICK userA2\r\n"); |
|
268 check_chan_user(chan, "userA", false); |
|
269 check_chan_user(chan, "userB", true); |
|
270 |
|
271 // cleanup |
|
272 irc_net_destroy(net); |
|
273 } |
|
274 |
|
275 void test_irc_chan_user_quit (void) |
|
276 { |
|
277 struct test_chan_ctx ctx; |
|
278 struct transport_test *tp = setup_transport_test(); |
|
279 struct irc_net *net = setup_irc_net(tp); |
|
280 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
281 |
|
282 // rename one of the users |
|
283 log_info("test irc_net_on_chanuser"); |
|
284 transport_test_push_fmt(tp, ":userA!someone@somewhere QUIT foo\r\n"); |
|
285 check_chan_user(chan, "userA", false); |
|
286 |
|
287 // cleanup |
|
288 irc_net_destroy(net); |
|
289 } |
|
290 |
|
291 void _test_irc_chan_on_CTCP_ACTION (const struct irc_line *line, void *arg) |
|
292 { |
|
293 bool *flag = arg; |
|
294 |
|
295 log_debug("CTCP ACTION"); |
|
296 |
|
297 *flag = true; |
|
298 } |
|
299 |
|
300 static struct irc_cmd_handler _test_irc_chan_handlers[] = { |
|
301 { "CTCP ACTION", &_test_irc_chan_on_CTCP_ACTION }, |
|
302 { NULL, NULL } |
|
303 }; |
|
304 |
|
305 void test_irc_chan_CTCP_ACTION (void) |
|
306 { |
|
307 struct test_chan_ctx ctx; |
|
308 struct transport_test *tp = setup_transport_test(); |
|
309 struct irc_net *net = setup_irc_net(tp); |
|
310 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
311 bool cb_ok = false; |
|
312 |
|
313 // add our handler |
|
314 assert_success(irc_cmd_add(&chan->handlers, _test_irc_chan_handlers, &cb_ok)); |
|
315 |
|
316 // rename one of the users |
|
317 log_info("test irc_conn_on_CTCP_ACTION"); |
|
318 transport_test_push_fmt(tp, ":userA!someone@somewhere PRIVMSG #test \001ACTION hello world\001\r\n"); |
|
319 assert(cb_ok); |
|
320 |
|
321 // cleanup |
|
322 irc_net_destroy(net); |
|
323 } |
|
324 |
|
325 void test_irc_chan_privmsg (void) |
|
326 { |
|
327 struct test_chan_ctx ctx; |
|
328 struct transport_test *tp = setup_transport_test(); |
|
329 struct irc_net *net = setup_irc_net(tp); |
|
330 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
331 |
|
332 // rename one of the users |
|
333 log_info("test irc_chan_PRIVMSG"); |
|
334 assert_success(irc_chan_PRIVMSG(chan, "foobar quux")); |
|
335 assert_transport_data(tp, "PRIVMSG #test :foobar quux\r\n"); |
|
336 |
|
337 // cleanup |
|
338 irc_net_destroy(net); |
|
339 } |
|
340 |