| |
1 /* |
| |
2 * Purple - Internet Messaging Library |
| |
3 * Copyright (C) Pidgin Developers <devel@pidgin.im> |
| |
4 * |
| |
5 * This library is free software; you can redistribute it and/or |
| |
6 * modify it under the terms of the GNU Lesser General Public |
| |
7 * License as published by the Free Software Foundation; either |
| |
8 * version 2 of the License, or (at your option) any later version. |
| |
9 * |
| |
10 * This library is distributed in the hope that it will be useful, |
| |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| |
13 * Lesser General Public License for more details. |
| |
14 * |
| |
15 * You should have received a copy of the GNU Lesser General Public |
| |
16 * License along with this library; if not, see <https://www.gnu.org/licenses/>. |
| |
17 */ |
| |
18 |
| |
19 #include <glib.h> |
| |
20 |
| |
21 #include <purple.h> |
| |
22 |
| |
23 #include "../purpleircv3constants.h" |
| |
24 #include "../purpleircv3message.h" |
| |
25 #include "../purpleircv3parser.h" |
| |
26 |
| |
27 #define TEST_IRCV3_PARSER_DOMAIN (g_quark_from_static_string("test-ircv3-parser")) |
| |
28 |
| |
29 typedef struct { |
| |
30 GHashTable *tags; |
| |
31 gchar *source; |
| |
32 gchar *command; |
| |
33 guint n_params; |
| |
34 const gchar * const params[16]; |
| |
35 } TestPurpleIRCv3ParserData; |
| |
36 |
| |
37 /****************************************************************************** |
| |
38 * Handlers |
| |
39 *****************************************************************************/ |
| |
40 static gboolean |
| |
41 test_purple_ircv3_test_handler(PurpleIRCv3Message *message, |
| |
42 G_GNUC_UNUSED GError **error, |
| |
43 gpointer data) |
| |
44 { |
| |
45 TestPurpleIRCv3ParserData *d = data; |
| |
46 GHashTable *tags = NULL; |
| |
47 GHashTableIter iter; |
| |
48 GStrv params = NULL; |
| |
49 const char *command = NULL; |
| |
50 const char *source = NULL; |
| |
51 guint n_params = 0; |
| |
52 |
| |
53 command = purple_ircv3_message_get_command(message); |
| |
54 params = purple_ircv3_message_get_params(message); |
| |
55 source = purple_ircv3_message_get_source(message); |
| |
56 tags = purple_ircv3_message_get_tags(message); |
| |
57 |
| |
58 if(params != NULL) { |
| |
59 n_params = g_strv_length(params); |
| |
60 } |
| |
61 |
| |
62 /* Make sure we have an expected tags hash table before checking them. */ |
| |
63 if(d->tags != NULL) { |
| |
64 gpointer expected_key; |
| |
65 gpointer expected_value; |
| |
66 guint actual_size; |
| |
67 guint expected_size; |
| |
68 |
| |
69 /* Make sure the tag hash tables have the same size. */ |
| |
70 expected_size = g_hash_table_size(d->tags); |
| |
71 actual_size = g_hash_table_size(tags); |
| |
72 g_assert_cmpuint(actual_size, ==, expected_size); |
| |
73 |
| |
74 /* Since the tables have the same size, we can walk through the expected |
| |
75 * table and use it to verify the actual table. |
| |
76 */ |
| |
77 g_hash_table_iter_init(&iter, d->tags); |
| |
78 while(g_hash_table_iter_next(&iter, &expected_key, &expected_value)) { |
| |
79 gpointer actual_value = NULL; |
| |
80 gboolean found = FALSE; |
| |
81 |
| |
82 found = g_hash_table_lookup_extended(tags, expected_key, NULL, |
| |
83 &actual_value); |
| |
84 g_assert_true(found); |
| |
85 g_assert_cmpstr(actual_value, ==, expected_value); |
| |
86 } |
| |
87 } |
| |
88 |
| |
89 /* Walk through the params checking against the expected values. */ |
| |
90 if(d->n_params > 0) { |
| |
91 g_assert_cmpuint(n_params, ==, d->n_params); |
| |
92 |
| |
93 for(guint i = 0; i < d->n_params; i++) { |
| |
94 g_assert_cmpstr(params[i], ==, d->params[i]); |
| |
95 } |
| |
96 } |
| |
97 |
| |
98 /* Validate all the string parameters. */ |
| |
99 g_assert_cmpstr(source, ==, d->source); |
| |
100 g_assert_cmpstr(command, ==, d->command); |
| |
101 |
| |
102 /* Cleanup everything the caller allocated. */ |
| |
103 g_clear_pointer(&d->tags, g_hash_table_destroy); |
| |
104 |
| |
105 /* Return the return value the caller asked for. */ |
| |
106 return TRUE; |
| |
107 } |
| |
108 |
| |
109 static gboolean |
| |
110 test_purple_ircv3_test_handler_error(G_GNUC_UNUSED PurpleIRCv3Message *message, |
| |
111 GError **error, |
| |
112 G_GNUC_UNUSED gpointer data) |
| |
113 { |
| |
114 g_set_error(error, TEST_IRCV3_PARSER_DOMAIN, 0, "test error"); |
| |
115 |
| |
116 return FALSE; |
| |
117 } |
| |
118 |
| |
119 /****************************************************************************** |
| |
120 * Helpers |
| |
121 *****************************************************************************/ |
| |
122 static void |
| |
123 test_purple_ircv3_parser(const gchar *source, TestPurpleIRCv3ParserData *d) { |
| |
124 PurpleIRCv3Parser *parser = purple_ircv3_parser_new(); |
| |
125 GError *error = NULL; |
| |
126 gboolean result = FALSE; |
| |
127 |
| |
128 purple_ircv3_parser_set_fallback_handler(parser, |
| |
129 test_purple_ircv3_test_handler); |
| |
130 |
| |
131 result = purple_ircv3_parser_parse(parser, source, &error, d); |
| |
132 |
| |
133 g_assert_no_error(error); |
| |
134 g_assert_true(result); |
| |
135 |
| |
136 g_clear_object(&parser); |
| |
137 } |
| |
138 |
| |
139 /****************************************************************************** |
| |
140 * Tests |
| |
141 *****************************************************************************/ |
| |
142 static void |
| |
143 test_purple_ircv3_parser_propagates_errors(void) { |
| |
144 PurpleIRCv3Parser *parser = purple_ircv3_parser_new(); |
| |
145 GError *error = NULL; |
| |
146 gboolean result = FALSE; |
| |
147 |
| |
148 purple_ircv3_parser_set_fallback_handler(parser, |
| |
149 test_purple_ircv3_test_handler_error); |
| |
150 |
| |
151 result = purple_ircv3_parser_parse(parser, "COMMAND", &error, NULL); |
| |
152 g_assert_error(error, TEST_IRCV3_PARSER_DOMAIN, 0); |
| |
153 g_clear_error(&error); |
| |
154 |
| |
155 g_assert_false(result); |
| |
156 |
| |
157 g_clear_object(&parser); |
| |
158 } |
| |
159 |
| |
160 static void |
| |
161 test_purple_ircv3_parser_simple(void) { |
| |
162 TestPurpleIRCv3ParserData data = { |
| |
163 .command = "foo", |
| |
164 .n_params = 3, |
| |
165 .params = {"bar", "baz", "asdf"}, |
| |
166 }; |
| |
167 |
| |
168 test_purple_ircv3_parser("foo bar baz asdf", &data); |
| |
169 } |
| |
170 |
| |
171 static void |
| |
172 test_purple_ircv3_parser_with_source(void) { |
| |
173 TestPurpleIRCv3ParserData data = { |
| |
174 .source = "coolguy", |
| |
175 .command = "foo", |
| |
176 .n_params = 3, |
| |
177 .params = {"bar", "baz", "asdf"}, |
| |
178 }; |
| |
179 |
| |
180 test_purple_ircv3_parser(":coolguy foo bar baz asdf", &data); |
| |
181 } |
| |
182 |
| |
183 static void |
| |
184 test_purple_ircv3_parser_with_trailing(void) { |
| |
185 TestPurpleIRCv3ParserData data = { |
| |
186 .command = "foo", |
| |
187 .n_params = 3, |
| |
188 .params = {"bar", "baz", "asdf quux"}, |
| |
189 }; |
| |
190 |
| |
191 test_purple_ircv3_parser("foo bar baz :asdf quux", &data); |
| |
192 } |
| |
193 |
| |
194 static void |
| |
195 test_purple_ircv3_parser_with_empty_trailing(void) { |
| |
196 TestPurpleIRCv3ParserData data = { |
| |
197 .command = "foo", |
| |
198 .n_params = 3, |
| |
199 .params = {"bar", "baz", ""}, |
| |
200 }; |
| |
201 |
| |
202 test_purple_ircv3_parser("foo bar baz :", &data); |
| |
203 } |
| |
204 |
| |
205 static void |
| |
206 test_purple_ircv3_parser_with_trailing_starting_colon(void) { |
| |
207 TestPurpleIRCv3ParserData data = { |
| |
208 .command = "foo", |
| |
209 .n_params = 3, |
| |
210 .params = {"bar", "baz", ":asdf"}, |
| |
211 }; |
| |
212 |
| |
213 test_purple_ircv3_parser("foo bar baz ::asdf", &data); |
| |
214 } |
| |
215 |
| |
216 static void |
| |
217 test_purple_ircv3_parser_with_source_and_trailing(void) { |
| |
218 TestPurpleIRCv3ParserData data = { |
| |
219 .source = "coolguy", |
| |
220 .command = "foo", |
| |
221 .n_params = 3, |
| |
222 .params = {"bar", "baz", "asdf quux"}, |
| |
223 }; |
| |
224 |
| |
225 test_purple_ircv3_parser(":coolguy foo bar baz :asdf quux", &data); |
| |
226 } |
| |
227 |
| |
228 static void |
| |
229 test_purple_ircv3_parser_with_source_and_trailing_whitespace(void) { |
| |
230 TestPurpleIRCv3ParserData data = { |
| |
231 .source = "coolguy", |
| |
232 .command = "foo", |
| |
233 .n_params = 3, |
| |
234 .params = {"bar", "baz", " asdf quux "}, |
| |
235 }; |
| |
236 |
| |
237 test_purple_ircv3_parser(":coolguy foo bar baz : asdf quux ", &data); |
| |
238 } |
| |
239 |
| |
240 static void |
| |
241 test_purple_ircv3_parser_with_source_and_trailing_colon(void) { |
| |
242 TestPurpleIRCv3ParserData data = { |
| |
243 .source = "coolguy", |
| |
244 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
245 .n_params = 2, |
| |
246 .params = {"bar", "lol :) "}, |
| |
247 }; |
| |
248 |
| |
249 test_purple_ircv3_parser(":coolguy PRIVMSG bar :lol :) ", &data); |
| |
250 } |
| |
251 |
| |
252 static void |
| |
253 test_purple_ircv3_parser_with_source_and_empty_trailing(void) { |
| |
254 TestPurpleIRCv3ParserData data = { |
| |
255 .source = "coolguy", |
| |
256 .command = "foo", |
| |
257 .n_params = 3, |
| |
258 .params = {"bar", "baz", ""}, |
| |
259 }; |
| |
260 |
| |
261 test_purple_ircv3_parser(":coolguy foo bar baz :", &data); |
| |
262 } |
| |
263 |
| |
264 static void |
| |
265 test_purple_ircv3_parser_with_source_and_trailing_only_whitespace(void) { |
| |
266 TestPurpleIRCv3ParserData data = { |
| |
267 .source = "coolguy", |
| |
268 .command = "foo", |
| |
269 .n_params = 3, |
| |
270 .params = {"bar", "baz", " "}, |
| |
271 }; |
| |
272 |
| |
273 test_purple_ircv3_parser(":coolguy foo bar baz : ", &data); |
| |
274 } |
| |
275 |
| |
276 static void |
| |
277 test_purple_ircv3_parser_with_tags(void) { |
| |
278 TestPurpleIRCv3ParserData data = { |
| |
279 .command = "foo", |
| |
280 }; |
| |
281 |
| |
282 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
283 g_hash_table_insert(data.tags, "a", "b"); |
| |
284 g_hash_table_insert(data.tags, "c", "32"); |
| |
285 g_hash_table_insert(data.tags, "k", ""); |
| |
286 g_hash_table_insert(data.tags, "rt", "ql7"); |
| |
287 |
| |
288 test_purple_ircv3_parser("@a=b;c=32;k;rt=ql7 foo", &data); |
| |
289 } |
| |
290 |
| |
291 static void |
| |
292 test_purple_ircv3_parser_with_escaped_tags(void) { |
| |
293 TestPurpleIRCv3ParserData data = { |
| |
294 .command = "foo", |
| |
295 }; |
| |
296 |
| |
297 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
298 g_hash_table_insert(data.tags, "a", "b\\and\nk"); |
| |
299 g_hash_table_insert(data.tags, "c", "72 45"); |
| |
300 g_hash_table_insert(data.tags, "d", "gh;764"); |
| |
301 |
| |
302 test_purple_ircv3_parser("@a=b\\\\and\\nk;c=72\\s45;d=gh\\:764 foo", |
| |
303 &data); |
| |
304 } |
| |
305 |
| |
306 static void |
| |
307 test_purple_ircv3_with_tags_and_source(void) { |
| |
308 TestPurpleIRCv3ParserData data = { |
| |
309 .source = "quux", |
| |
310 .command = "ab", |
| |
311 .n_params = 1, |
| |
312 .params = {"cd"}, |
| |
313 }; |
| |
314 |
| |
315 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
316 g_hash_table_insert(data.tags, "c", ""); |
| |
317 g_hash_table_insert(data.tags, "h", ""); |
| |
318 g_hash_table_insert(data.tags, "a", "b"); |
| |
319 |
| |
320 test_purple_ircv3_parser("@c;h=;a=b :quux ab cd", &data); |
| |
321 } |
| |
322 |
| |
323 static void |
| |
324 test_purple_ircv3_last_param_no_colon(void) { |
| |
325 TestPurpleIRCv3ParserData data = { |
| |
326 .source = "src", |
| |
327 .command = PURPLE_IRCV3_MSG_JOIN, |
| |
328 .n_params = 1, |
| |
329 .params = {"#chan"}, |
| |
330 }; |
| |
331 |
| |
332 test_purple_ircv3_parser(":src JOIN #chan", &data); |
| |
333 } |
| |
334 |
| |
335 static void |
| |
336 test_purple_ircv3_last_param_with_colon(void) { |
| |
337 TestPurpleIRCv3ParserData data = { |
| |
338 .source = "src", |
| |
339 .command = PURPLE_IRCV3_MSG_JOIN, |
| |
340 .n_params = 1, |
| |
341 .params = {"#chan"}, |
| |
342 }; |
| |
343 |
| |
344 test_purple_ircv3_parser(":src JOIN :#chan", &data); |
| |
345 } |
| |
346 |
| |
347 static void |
| |
348 test_purple_ircv3_without_last_param(void) { |
| |
349 TestPurpleIRCv3ParserData data = { |
| |
350 .source = "src", |
| |
351 .command = "AWAY", |
| |
352 }; |
| |
353 |
| |
354 test_purple_ircv3_parser(":src AWAY", &data); |
| |
355 } |
| |
356 |
| |
357 static void |
| |
358 test_purple_ircv3_with_last_param(void) { |
| |
359 TestPurpleIRCv3ParserData data = { |
| |
360 .source = "src", |
| |
361 .command = "AWAY", |
| |
362 }; |
| |
363 |
| |
364 test_purple_ircv3_parser(":src AWAY ", &data); |
| |
365 } |
| |
366 |
| |
367 static void |
| |
368 test_purple_ircv3_tab_is_not_space(void) { |
| |
369 TestPurpleIRCv3ParserData data = { |
| |
370 .source = "cool\tguy", |
| |
371 .command = "foo", |
| |
372 .n_params = 2, |
| |
373 .params = {"bar", "baz"}, |
| |
374 }; |
| |
375 |
| |
376 test_purple_ircv3_parser(":cool\tguy foo bar baz", &data); |
| |
377 } |
| |
378 |
| |
379 static void |
| |
380 test_purple_ircv3_source_control_characters_1(void) { |
| |
381 /* Break each string after the hex escape as they are supposed to only be |
| |
382 * a single byte, but the c compiler will keep unescaping unless we break |
| |
383 * the string. |
| |
384 */ |
| |
385 TestPurpleIRCv3ParserData data = { |
| |
386 .source = "coolguy!ag@net\x03" "5w\x03" "ork.admin", |
| |
387 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
388 .n_params = 2, |
| |
389 .params = {"foo", "bar baz"}, |
| |
390 }; |
| |
391 const gchar *msg = NULL; |
| |
392 |
| |
393 msg = ":coolguy!ag@net\x03" "5w\x03" "ork.admin PRIVMSG foo :bar baz"; |
| |
394 |
| |
395 test_purple_ircv3_parser(msg, &data); |
| |
396 } |
| |
397 |
| |
398 static void |
| |
399 test_purple_ircv3_source_control_characters_2(void) { |
| |
400 /* Break each string after the hex escape as they are supposed to only be |
| |
401 * a single byte, but the c compiler will keep unescaping unless we break |
| |
402 * the string. |
| |
403 */ |
| |
404 TestPurpleIRCv3ParserData data = { |
| |
405 .source = "coolguy!~ag@n\x02" "et\x03" "05w\x0f" "ork.admin", |
| |
406 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
407 .n_params = 2, |
| |
408 .params = {"foo", "bar baz"}, |
| |
409 }; |
| |
410 const gchar *msg = NULL; |
| |
411 |
| |
412 msg = ":coolguy!~ag@n\x02" "et\x03" "05w\x0f" "ork.admin PRIVMSG foo :bar " |
| |
413 "baz"; |
| |
414 |
| |
415 test_purple_ircv3_parser(msg, &data); |
| |
416 } |
| |
417 |
| |
418 static void |
| |
419 test_purple_ircv3_everything(void) { |
| |
420 TestPurpleIRCv3ParserData data = { |
| |
421 .source = "irc.example.com", |
| |
422 .command = "COMMAND", |
| |
423 .n_params = 3, |
| |
424 .params = {"param1", "param2", "param3 param3"}, |
| |
425 }; |
| |
426 const gchar *msg = NULL; |
| |
427 |
| |
428 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
429 g_hash_table_insert(data.tags, "tag1", "value1"); |
| |
430 g_hash_table_insert(data.tags, "tag2", ""); |
| |
431 g_hash_table_insert(data.tags, "vendor1/tag3", "value2"); |
| |
432 g_hash_table_insert(data.tags, "vendor2/tag4", ""); |
| |
433 |
| |
434 msg = "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4= " |
| |
435 ":irc.example.com COMMAND param1 param2 :param3 param3"; |
| |
436 |
| |
437 test_purple_ircv3_parser(msg, &data); |
| |
438 } |
| |
439 |
| |
440 static void |
| |
441 test_purple_ircv3_everything_but_tags(void) { |
| |
442 TestPurpleIRCv3ParserData data = { |
| |
443 .source = "irc.example.com", |
| |
444 .command = "COMMAND", |
| |
445 .n_params = 3, |
| |
446 .params = {"param1", "param2", "param3 param3"}, |
| |
447 }; |
| |
448 const gchar *msg = NULL; |
| |
449 |
| |
450 msg = ":irc.example.com COMMAND param1 param2 :param3 param3"; |
| |
451 |
| |
452 test_purple_ircv3_parser(msg, &data); |
| |
453 } |
| |
454 |
| |
455 static void |
| |
456 test_purple_ircv3_everything_but_source(void) { |
| |
457 TestPurpleIRCv3ParserData data = { |
| |
458 .command = "COMMAND", |
| |
459 .n_params = 3, |
| |
460 .params = {"param1", "param2", "param3 param3"}, |
| |
461 }; |
| |
462 const gchar *msg = NULL; |
| |
463 |
| |
464 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
465 g_hash_table_insert(data.tags, "tag1", "value1"); |
| |
466 g_hash_table_insert(data.tags, "tag2", ""); |
| |
467 g_hash_table_insert(data.tags, "vendor1/tag3", "value2"); |
| |
468 g_hash_table_insert(data.tags, "vendor2/tag4", ""); |
| |
469 |
| |
470 msg = "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 " |
| |
471 "COMMAND param1 param2 :param3 param3"; |
| |
472 |
| |
473 test_purple_ircv3_parser(msg, &data); |
| |
474 } |
| |
475 |
| |
476 static void |
| |
477 test_purple_ircv3_command_only(void) { |
| |
478 TestPurpleIRCv3ParserData data = { |
| |
479 .command = "COMMAND", |
| |
480 }; |
| |
481 |
| |
482 test_purple_ircv3_parser("COMMAND", &data); |
| |
483 } |
| |
484 |
| |
485 static void |
| |
486 test_purple_ircv3_slashes_are_fun(void) { |
| |
487 TestPurpleIRCv3ParserData data = { |
| |
488 .command = "COMMAND", |
| |
489 }; |
| |
490 |
| |
491 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
492 g_hash_table_insert(data.tags, "foo", "\\\\;\\s \r\n"); |
| |
493 |
| |
494 test_purple_ircv3_parser("@foo=\\\\\\\\\\:\\\\s\\s\\r\\n COMMAND", &data); |
| |
495 } |
| |
496 |
| |
497 static void |
| |
498 test_purple_ircv3_unreal_broken_1(void) { |
| |
499 TestPurpleIRCv3ParserData data = { |
| |
500 .source = "gravel.mozilla.org", |
| |
501 .command = "432", |
| |
502 .n_params = 2, |
| |
503 .params = {"#momo", "Erroneous Nickname: Illegal characters"}, |
| |
504 }; |
| |
505 const gchar *msg = NULL; |
| |
506 |
| |
507 msg = ":gravel.mozilla.org 432 #momo :Erroneous Nickname: Illegal " |
| |
508 "characters"; |
| |
509 |
| |
510 test_purple_ircv3_parser(msg, &data); |
| |
511 } |
| |
512 |
| |
513 static void |
| |
514 test_purple_ircv3_unreal_broken_2(void) { |
| |
515 TestPurpleIRCv3ParserData data = { |
| |
516 .source = "gravel.mozilla.org", |
| |
517 .command = "MODE", |
| |
518 .n_params = 2, |
| |
519 .params = {"#tckk", "+n"}, |
| |
520 }; |
| |
521 |
| |
522 test_purple_ircv3_parser(":gravel.mozilla.org MODE #tckk +n ", &data); |
| |
523 } |
| |
524 |
| |
525 static void |
| |
526 test_purple_ircv3_unreal_broken_3(void) { |
| |
527 TestPurpleIRCv3ParserData data = { |
| |
528 .source = "services.esper.net", |
| |
529 .command = "MODE", |
| |
530 .n_params = 3, |
| |
531 .params = {"#foo-bar", "+o", "foobar"}, |
| |
532 }; |
| |
533 |
| |
534 test_purple_ircv3_parser(":services.esper.net MODE #foo-bar +o foobar ", |
| |
535 &data); |
| |
536 } |
| |
537 |
| |
538 static void |
| |
539 test_purple_ircv3_tag_escape_char_at_a_time(void) { |
| |
540 TestPurpleIRCv3ParserData data = { |
| |
541 .command = "COMMAND", |
| |
542 }; |
| |
543 |
| |
544 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
545 g_hash_table_insert(data.tags, "tag1", "value\\ntest"); |
| |
546 |
| |
547 test_purple_ircv3_parser("@tag1=value\\\\ntest COMMAND", &data); |
| |
548 } |
| |
549 |
| |
550 static void |
| |
551 test_purple_ircv3_tag_drop_unnecessary_escapes(void) { |
| |
552 TestPurpleIRCv3ParserData data = { |
| |
553 .command = "COMMAND", |
| |
554 }; |
| |
555 |
| |
556 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
557 g_hash_table_insert(data.tags, "tag1", "value1"); |
| |
558 |
| |
559 test_purple_ircv3_parser("@tag1=value\\1 COMMAND", &data); |
| |
560 } |
| |
561 |
| |
562 static void |
| |
563 test_purple_ircv3_tag_drop_trailing_slash(void) { |
| |
564 TestPurpleIRCv3ParserData data = { |
| |
565 .command = "COMMAND", |
| |
566 }; |
| |
567 |
| |
568 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
569 g_hash_table_insert(data.tags, "tag1", "value1"); |
| |
570 |
| |
571 test_purple_ircv3_parser("@tag1=value1\\ COMMAND", &data); |
| |
572 } |
| |
573 |
| |
574 static void |
| |
575 test_purple_ircv3_duplicate_tags(void) { |
| |
576 TestPurpleIRCv3ParserData data = { |
| |
577 .command = "COMMAND", |
| |
578 }; |
| |
579 |
| |
580 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
581 g_hash_table_insert(data.tags, "tag1", "5"); |
| |
582 g_hash_table_insert(data.tags, "tag2", "3"); |
| |
583 g_hash_table_insert(data.tags, "tag3", "4"); |
| |
584 |
| |
585 test_purple_ircv3_parser("@tag1=1;tag2=3;tag3=4;tag1=5 COMMAND", &data); |
| |
586 } |
| |
587 |
| |
588 static void |
| |
589 test_purple_ircv3_vendor_tags_are_namespaced(void) { |
| |
590 TestPurpleIRCv3ParserData data = { |
| |
591 .command = "COMMAND", |
| |
592 }; |
| |
593 const gchar *msg = NULL; |
| |
594 |
| |
595 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
596 g_hash_table_insert(data.tags, "tag1", "5"); |
| |
597 g_hash_table_insert(data.tags, "tag2", "3"); |
| |
598 g_hash_table_insert(data.tags, "tag3", "4"); |
| |
599 g_hash_table_insert(data.tags, "vendor/tag2", "8"); |
| |
600 |
| |
601 msg = "@tag1=1;tag2=3;tag3=4;tag1=5;vendor/tag2=8 COMMAND"; |
| |
602 |
| |
603 test_purple_ircv3_parser(msg, &data); |
| |
604 } |
| |
605 |
| |
606 static void |
| |
607 test_purple_ircv3_special_mode_1(void) { |
| |
608 TestPurpleIRCv3ParserData data = { |
| |
609 .source = "SomeOp", |
| |
610 .command = "MODE", |
| |
611 .n_params = 2, |
| |
612 .params = {"#channel", "+i"}, |
| |
613 }; |
| |
614 |
| |
615 test_purple_ircv3_parser(":SomeOp MODE #channel :+i", &data); |
| |
616 } |
| |
617 |
| |
618 static void |
| |
619 test_purple_ircv3_special_mode_2(void) { |
| |
620 TestPurpleIRCv3ParserData data = { |
| |
621 .source = "SomeOp", |
| |
622 .command = "MODE", |
| |
623 .n_params = 4, |
| |
624 .params = {"#channel", "+oo", "SomeUser", "AnotherUser"}, |
| |
625 }; |
| |
626 |
| |
627 test_purple_ircv3_parser(":SomeOp MODE #channel +oo SomeUser :AnotherUser", |
| |
628 &data); |
| |
629 } |
| |
630 |
| |
631 /****************************************************************************** |
| |
632 * Message tags examples |
| |
633 *****************************************************************************/ |
| |
634 static void |
| |
635 test_purple_ircv3_parser_message_tags_none(void) { |
| |
636 TestPurpleIRCv3ParserData data = { |
| |
637 .source = "nick!ident@host.com", |
| |
638 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
639 .n_params = 2, |
| |
640 .params = {"me", "Hello"}, |
| |
641 }; |
| |
642 const char *msg = NULL; |
| |
643 |
| |
644 msg = ":nick!ident@host.com PRIVMSG me :Hello"; |
| |
645 |
| |
646 test_purple_ircv3_parser(msg, &data); |
| |
647 } |
| |
648 |
| |
649 static void |
| |
650 test_purple_ircv3_parser_message_tags_3_tags(void) { |
| |
651 TestPurpleIRCv3ParserData data = { |
| |
652 .source = "nick!ident@host.com", |
| |
653 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
654 .n_params = 2, |
| |
655 .params = {"me", "Hello"}, |
| |
656 }; |
| |
657 const char *msg = NULL; |
| |
658 |
| |
659 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
660 g_hash_table_insert(data.tags, "aaa", "bbb"); |
| |
661 g_hash_table_insert(data.tags, "ccc", ""); |
| |
662 g_hash_table_insert(data.tags, "example.com/ddd", "eee"); |
| |
663 |
| |
664 msg = "@aaa=bbb;ccc;example.com/ddd=eee :nick!ident@host.com PRIVMSG me " |
| |
665 ":Hello"; |
| |
666 |
| |
667 test_purple_ircv3_parser(msg, &data); |
| |
668 } |
| |
669 |
| |
670 static void |
| |
671 test_purple_ircv3_parser_message_tags_client_only(void) { |
| |
672 TestPurpleIRCv3ParserData data = { |
| |
673 .source = "url_bot!bot@example.com", |
| |
674 .command = PURPLE_IRCV3_MSG_PRIVMSG, |
| |
675 .n_params = 2, |
| |
676 .params = {"#channel", "Example.com: A News Story"}, |
| |
677 }; |
| |
678 const char *msg = NULL; |
| |
679 |
| |
680 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
681 g_hash_table_insert(data.tags, "+icon", "https://example.com/favicon.png"); |
| |
682 |
| |
683 msg = "@+icon=https://example.com/favicon.png :url_bot!bot@example.com " |
| |
684 "PRIVMSG #channel :Example.com: A News Story"; |
| |
685 |
| |
686 test_purple_ircv3_parser(msg, &data); |
| |
687 } |
| |
688 |
| |
689 static void |
| |
690 test_purple_ircv3_parser_message_tags_labeled_response(void) { |
| |
691 TestPurpleIRCv3ParserData data = { |
| |
692 .source = "nick!user@example.com", |
| |
693 .command = "TAGMSG", |
| |
694 .n_params = 1, |
| |
695 .params = {"#channel"}, |
| |
696 }; |
| |
697 const char *msg = NULL; |
| |
698 |
| |
699 data.tags = g_hash_table_new(g_str_hash, g_str_equal); |
| |
700 g_hash_table_insert(data.tags, "label", "123"); |
| |
701 g_hash_table_insert(data.tags, "msgid", "abc"); |
| |
702 g_hash_table_insert(data.tags, "+example-client-tag", "example-value"); |
| |
703 |
| |
704 msg = "@label=123;msgid=abc;+example-client-tag=example-value " |
| |
705 ":nick!user@example.com TAGMSG #channel"; |
| |
706 |
| |
707 test_purple_ircv3_parser(msg, &data); |
| |
708 |
| |
709 } |
| |
710 /****************************************************************************** |
| |
711 * Main |
| |
712 *****************************************************************************/ |
| |
713 gint |
| |
714 main(gint argc, gchar *argv[]) { |
| |
715 g_test_init(&argc, &argv, NULL); |
| |
716 |
| |
717 /* Make sure an error in the handler is propagated back up to the calling |
| |
718 * function. |
| |
719 */ |
| |
720 g_test_add_func("/ircv3/parser/propagates-errors", |
| |
721 test_purple_ircv3_parser_propagates_errors); |
| |
722 |
| |
723 /* These tests are based on the msg-split tests from |
| |
724 * https://github.com/ircdocs/parser-tests/blob/master/tests/msg-split.yaml |
| |
725 */ |
| |
726 g_test_add_func("/ircv3/parser/simple", |
| |
727 test_purple_ircv3_parser_simple); |
| |
728 g_test_add_func("/ircv3/parser/with-source", |
| |
729 test_purple_ircv3_parser_with_source); |
| |
730 g_test_add_func("/ircv3/parser/with-trailing", |
| |
731 test_purple_ircv3_parser_with_trailing); |
| |
732 g_test_add_func("/ircv3/parser/with-empty-trailing", |
| |
733 test_purple_ircv3_parser_with_empty_trailing); |
| |
734 g_test_add_func("/ircv3/parser/with-trailing-starting-colon", |
| |
735 test_purple_ircv3_parser_with_trailing_starting_colon); |
| |
736 g_test_add_func("/ircv3/parser/with-source-and-trailing", |
| |
737 test_purple_ircv3_parser_with_source_and_trailing); |
| |
738 g_test_add_func("/ircv3/parser/with-source-and-trailing-whitespace", |
| |
739 test_purple_ircv3_parser_with_source_and_trailing_whitespace); |
| |
740 g_test_add_func("/ircv3/parser/with-source-and-trailing-colon", |
| |
741 test_purple_ircv3_parser_with_source_and_trailing_colon); |
| |
742 g_test_add_func("/ircv3/parser/with-source-and-empty-trailing", |
| |
743 test_purple_ircv3_parser_with_source_and_empty_trailing); |
| |
744 g_test_add_func("/ircv3/parser/with-source-and-trailing-only-whitespace", |
| |
745 test_purple_ircv3_parser_with_source_and_trailing_only_whitespace); |
| |
746 |
| |
747 g_test_add_func("/ircv3/parser/with-tags", |
| |
748 test_purple_ircv3_parser_with_tags); |
| |
749 g_test_add_func("/ircv3/parser/with-escaped-tags", |
| |
750 test_purple_ircv3_parser_with_escaped_tags); |
| |
751 g_test_add_func("/ircv3/parser/with-tags-and-source", |
| |
752 test_purple_ircv3_with_tags_and_source); |
| |
753 |
| |
754 g_test_add_func("/ircv3/parser/last-param-no-colon", |
| |
755 test_purple_ircv3_last_param_no_colon); |
| |
756 g_test_add_func("/ircv3/parser/last-param-with-colon", |
| |
757 test_purple_ircv3_last_param_with_colon); |
| |
758 |
| |
759 g_test_add_func("/ircv3/parser/without-last-param", |
| |
760 test_purple_ircv3_without_last_param); |
| |
761 g_test_add_func("/ircv3/parser/with-last-parsm", |
| |
762 test_purple_ircv3_with_last_param); |
| |
763 |
| |
764 g_test_add_func("/ircv3/parser/tab-is-not-space", |
| |
765 test_purple_ircv3_tab_is_not_space); |
| |
766 |
| |
767 g_test_add_func("/ircv3/parser/source_control_characters_1", |
| |
768 test_purple_ircv3_source_control_characters_1); |
| |
769 g_test_add_func("/ircv3/parser/source_control_characters_2", |
| |
770 test_purple_ircv3_source_control_characters_2); |
| |
771 |
| |
772 g_test_add_func("/ircv3/parser/everything", |
| |
773 test_purple_ircv3_everything); |
| |
774 g_test_add_func("/ircv3/parser/everything-but-tags", |
| |
775 test_purple_ircv3_everything_but_tags); |
| |
776 g_test_add_func("/ircv3/parser/everything-but-source", |
| |
777 test_purple_ircv3_everything_but_source); |
| |
778 |
| |
779 g_test_add_func("/ircv3/parser/command-only", |
| |
780 test_purple_ircv3_command_only); |
| |
781 |
| |
782 g_test_add_func("/ircv3/parser/slashes-are-fun", |
| |
783 test_purple_ircv3_slashes_are_fun); |
| |
784 |
| |
785 g_test_add_func("/ircv3/parser/unreal-broken-1", |
| |
786 test_purple_ircv3_unreal_broken_1); |
| |
787 g_test_add_func("/ircv3/parser/unreal-broken-2", |
| |
788 test_purple_ircv3_unreal_broken_2); |
| |
789 g_test_add_func("/ircv3/parser/unreal-broken-3", |
| |
790 test_purple_ircv3_unreal_broken_3); |
| |
791 |
| |
792 g_test_add_func("/ircv3/parser/tag-escape-char-at-a-time", |
| |
793 test_purple_ircv3_tag_escape_char_at_a_time); |
| |
794 g_test_add_func("/ircv3/parser/tag-drop-unnecessary-escapes", |
| |
795 test_purple_ircv3_tag_drop_unnecessary_escapes); |
| |
796 g_test_add_func("/ircv3/parser/tag-drop-trailing-slash", |
| |
797 test_purple_ircv3_tag_drop_trailing_slash); |
| |
798 |
| |
799 g_test_add_func("/ircv3/parser/duplicate-tags", |
| |
800 test_purple_ircv3_duplicate_tags); |
| |
801 g_test_add_func("/ircv3/parser/vendor-tags-are-namespaced", |
| |
802 test_purple_ircv3_vendor_tags_are_namespaced); |
| |
803 |
| |
804 g_test_add_func("/ircv3/parser/special-mode-1", |
| |
805 test_purple_ircv3_special_mode_1); |
| |
806 g_test_add_func("/ircv3/parser/special-mode-2", |
| |
807 test_purple_ircv3_special_mode_2); |
| |
808 |
| |
809 /* These tests are the examples from the message-tags specification, |
| |
810 * https://ircv3.net/specs/extensions/message-tags.html. |
| |
811 */ |
| |
812 g_test_add_func("/ircv3/parser/message-tags/none", |
| |
813 test_purple_ircv3_parser_message_tags_none); |
| |
814 g_test_add_func("/ircv3/parser/message-tags/3-tags", |
| |
815 test_purple_ircv3_parser_message_tags_3_tags); |
| |
816 g_test_add_func("/ircv3/parser/message-tags/client-only", |
| |
817 test_purple_ircv3_parser_message_tags_client_only); |
| |
818 g_test_add_func("/ircv3/parser/message-tags/labeled-message", |
| |
819 test_purple_ircv3_parser_message_tags_labeled_response); |
| |
820 |
| |
821 return g_test_run(); |
| |
822 } |