src: Yearly copyright bump
[quassel.git] / tests / common / ircdecodertest.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program 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         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <ostream>
22
23 #include "testglobal.h"
24 #include "ircdecoder.h"
25 #include "irctag.h"
26
27 struct IrcMessage
28 {
29     std::map<IrcTagKey, std::string> tags;
30     std::string prefix;
31     std::string cmd;
32     std::vector<std::string> params;
33
34     explicit IrcMessage(std::map<IrcTagKey, std::string> tags, std::string prefix, std::string cmd, std::vector<std::string> params = {})
35         : tags(std::move(tags)), prefix(std::move(prefix)), cmd(std::move(cmd)), params(std::move(params)) {}
36
37     explicit IrcMessage(std::initializer_list<std::pair<const IrcTagKey, std::string>> tags, std::string prefix, std::string cmd, std::vector<std::string> params = {})
38         : tags(tags), prefix(std::move(prefix)), cmd(std::move(cmd)), params(std::move(params)) {}
39
40     explicit IrcMessage(std::string prefix, std::string cmd, std::vector<std::string> params = {})
41         : tags({}), prefix(std::move(prefix)), cmd(std::move(cmd)), params(std::move(params)) {}
42
43     friend bool operator==(const IrcMessage& a, const IrcMessage& b)
44     {
45         return a.tags == b.tags &&
46                a.prefix == b.prefix &&
47                a.cmd == b.cmd &&
48                a.params == b.params;
49     }
50
51     friend std::ostream& operator<<(std::ostream& o, const IrcMessage& m)
52     {
53         o << "(tags={";
54         for (const std::pair<IrcTagKey, std::string> entry: m.tags) {
55             o << entry.first << "='" << entry.second << "', ";
56         }
57         o << "}, prefix=" << m.prefix << ", cmd=" << m.cmd << ", params=[";
58         for (const std::string& param : m.params) {
59             o << "'" << param << "', ";
60         }
61         o << "])";
62         return o;
63     }
64 };
65
66
67 IrcMessage parse(const std::string& message)
68 {
69     QHash<IrcTagKey, QString> tags;
70     QString prefix;
71     QString cmd;
72     QList<QByteArray> params;
73
74     IrcDecoder::parseMessage([](const QByteArray& data) {
75         return QString::fromUtf8(data);
76     }, QByteArray::fromStdString(message), tags, prefix, cmd, params);
77
78     std::map<IrcTagKey, std::string> actualTags;
79     for (const IrcTagKey& key : tags.keys()) {
80         actualTags[key] = tags[key].toStdString();
81     }
82     std::string actualPrefix = prefix.toStdString();
83     std::string actualCmd = cmd.toStdString();
84     std::vector<std::string> actualParams;
85     for (const QByteArray& param : params) {
86         actualParams.push_back(param.toStdString());
87     }
88
89     return IrcMessage{actualTags, actualPrefix, actualCmd, actualParams};
90 }
91
92 TEST(IrcDecoderTest, simple)
93 {
94     EXPECT_EQ(parse("foo bar baz asdf"),
95               IrcMessage("",
96                          "foo",
97                          {"bar", "baz", "asdf"}));
98 }
99
100 TEST(IrcDecoderTest, with_source)
101 {
102     EXPECT_EQ(parse(":coolguy foo bar baz asdf"),
103               IrcMessage("coolguy",
104                          "foo",
105                          {"bar", "baz", "asdf"}));
106 }
107
108 TEST(IrcDecoderTest, with_trailing_param)
109 {
110     EXPECT_EQ(parse("foo bar baz :asdf quux"),
111               IrcMessage("",
112                          "foo",
113                          {"bar", "baz", "asdf quux"}));
114     EXPECT_EQ(parse("foo bar baz :"),
115               IrcMessage("",
116                          "foo",
117                          {"bar", "baz", ""}));
118     EXPECT_EQ(parse("foo bar baz ::asdf"),
119               IrcMessage("",
120                          "foo",
121                          {"bar", "baz", ":asdf"}));
122 }
123
124 TEST(IrcDecoderTest, with_source_and_trailing_param)
125 {
126     EXPECT_EQ(parse(":coolguy foo bar baz :asdf quux"),
127               IrcMessage("coolguy",
128                          "foo",
129                          {"bar", "baz", "asdf quux"}));
130     EXPECT_EQ(parse(":coolguy foo bar baz :  asdf quux "),
131               IrcMessage("coolguy",
132                          "foo",
133                          {"bar", "baz", "  asdf quux "}));
134     EXPECT_EQ(parse(":coolguy PRIVMSG bar :lol :) "),
135               IrcMessage("coolguy",
136                          "PRIVMSG",
137                          {"bar", "lol :) "}));
138     EXPECT_EQ(parse(":coolguy foo bar baz :  "),
139               IrcMessage("coolguy",
140                          "foo",
141                          {"bar", "baz", "  "}));
142 }
143
144 TEST(IrcDecoderTest, with_tags)
145 {
146     EXPECT_EQ(parse("@a=b;c=32;k;rt=ql7 foo"),
147               IrcMessage({{IrcTagKey("a"),  "b"},
148                           {IrcTagKey("c"),  "32"},
149                           {IrcTagKey("k"),  ""},
150                           {IrcTagKey("rt"), "ql7"}},
151                          "",
152                          "foo"));
153 }
154
155 TEST(IrcDecoderTest, with_escaped_tags)
156 {
157     EXPECT_EQ(parse("@a=b\\\\and\\nk;c=72\\s45;d=gh\\:764 foo"),
158               IrcMessage({{IrcTagKey("a"), "b\\and\nk"},
159                           {IrcTagKey("c"), "72 45"},
160                           {IrcTagKey("d"), "gh;764"}},
161                          "",
162                          "foo"));
163 }
164
165 TEST(IrcDecoderTest, with_tags_and_source)
166 {
167     EXPECT_EQ(parse("@c;h=;a=b :quux ab cd"),
168               IrcMessage({{IrcTagKey("c"), ""},
169                           {IrcTagKey("h"), ""},
170                           {IrcTagKey("a"), "b"}},
171                          "quux",
172                          "ab",
173                          {"cd"}));
174 }
175
176 TEST(IrcDecoderTest, different_forms_of_last_param)
177 {
178     EXPECT_EQ(parse(":src JOIN #chan"),
179               IrcMessage("src",
180                          "JOIN",
181                          {"#chan"}));
182     EXPECT_EQ(parse(":src JOIN :#chan"),
183               IrcMessage("src",
184                          "JOIN",
185                          {"#chan"}));
186 }
187
188 TEST(IrcDecoderTest, with_and_without_last_param)
189 {
190     EXPECT_EQ(parse(":src AWAY"),
191               IrcMessage("src",
192                          "AWAY"));
193     EXPECT_EQ(parse(":src AWAY "),
194               IrcMessage("src",
195                          "AWAY"));
196 }
197
198 TEST(IrcDecoderTest, tab_is_not_considered_SPACE)
199 {
200     EXPECT_EQ(parse(":cool\tguy foo bar baz"),
201               IrcMessage("cool\tguy",
202                          "foo",
203                          {"bar", "baz"}));
204 }
205
206 TEST(IrcDecoderTest, with_weird_control_codes_in_the_source)
207 {
208     EXPECT_EQ(parse(":coolguy!ag@net""\x03""5w""\x03""ork.admin PRIVMSG foo :bar baz"),
209               IrcMessage("coolguy!ag@net""\x03""5w""\x03""ork.admin",
210                          "PRIVMSG",
211                          {"foo", "bar baz"}));
212     EXPECT_EQ(parse(":coolguy!~ag@n""\x02""et""\x03""05w""\x0f""ork.admin PRIVMSG foo :bar baz"),
213               IrcMessage("coolguy!~ag@n""\x02""et""\x03""05w""\x0f""ork.admin",
214                          "PRIVMSG",
215                          {"foo", "bar baz"}));
216 }
217
218 TEST(IrcDecoderTest, with_tags_source_and_params)
219 {
220     EXPECT_EQ(parse("@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 :irc.example.com COMMAND param1 param2 :param3 param3"),
221               IrcMessage({{IrcTagKey("tag1"),            "value1"},
222                           {IrcTagKey("tag2"),            ""},
223                           {IrcTagKey("vendor1", "tag3"), "value2"},
224                           {IrcTagKey("vendor2", "tag4"), ""}},
225                          "irc.example.com",
226                          "COMMAND",
227                          {"param1", "param2", "param3 param3"}));
228     EXPECT_EQ(parse(":irc.example.com COMMAND param1 param2 :param3 param3"),
229               IrcMessage("irc.example.com",
230                          "COMMAND",
231                          {"param1", "param2", "param3 param3"}));
232     EXPECT_EQ(parse("@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 COMMAND param1 param2 :param3 param3"),
233               IrcMessage({{IrcTagKey("tag1"),            "value1"},
234                           {IrcTagKey("tag2"),            ""},
235                           {IrcTagKey("vendor1", "tag3"), "value2"},
236                           {IrcTagKey("vendor2", "tag4"), ""}},
237                          "",
238                          "COMMAND",
239                          {"param1", "param2", "param3 param3"}));
240     EXPECT_EQ(parse("COMMAND"),
241               IrcMessage("",
242                          "COMMAND"));
243     EXPECT_EQ(parse("@foo=\\\\\\\\\\:\\\\s\\s\\r\\n COMMAND"),
244               IrcMessage({{IrcTagKey("foo"), "\\\\;\\s \r\n"}},
245                          "",
246                          "COMMAND"));
247 }
248
249 TEST(IrcDecoderTest, broken_messages_from_unreal)
250 {
251     EXPECT_EQ(parse(":gravel.mozilla.org 432  #momo :Erroneous Nickname: Illegal characters"),
252               IrcMessage("gravel.mozilla.org",
253                          "432",
254                          {"#momo", "Erroneous Nickname: Illegal characters"}));
255     EXPECT_EQ(parse(":gravel.mozilla.org MODE #tckk +n "),
256               IrcMessage("gravel.mozilla.org",
257                          "MODE",
258                          {"#tckk", "+n"}));
259     EXPECT_EQ(parse(":services.esper.net MODE #foo-bar +o foobar  "),
260               IrcMessage("services.esper.net",
261                          "MODE",
262                          {"#foo-bar", "+o", "foobar"}));
263 }
264
265 TEST(IrcDecoderTest, tag_values)
266 {
267     EXPECT_EQ(parse("@tag1=value\\\\ntest COMMAND"),
268               IrcMessage({{IrcTagKey("tag1"), "value\\ntest"}},
269                          "",
270                          "COMMAND"));
271     EXPECT_EQ(parse("@tag1=value\\1 COMMAND"),
272               IrcMessage({{IrcTagKey("tag1"), "value1"}},
273                          "",
274                          "COMMAND"));
275     EXPECT_EQ(parse("@tag1=value1\\ COMMAND"),
276               IrcMessage({{IrcTagKey("tag1"), "value1"}},
277                          "",
278                          "COMMAND"));
279 }