src: Yearly copyright bump
[quassel.git] / tests / common / ircencodertest.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 "ircencoder.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 std::string write(const IrcMessage& message)
68 {
69     QHash<IrcTagKey, QString> tags;
70     QByteArray prefix = QByteArray::fromStdString(message.prefix);
71     QByteArray cmd = QByteArray::fromStdString(message.cmd);
72     QList<QByteArray> params;
73
74     for (const auto& pair : message.tags) {
75         tags[pair.first] = QString::fromStdString(pair.second);
76     }
77
78     for (const std::string& param : message.params) {
79         params += QByteArray::fromStdString(param);
80     }
81
82     return IrcEncoder::writeMessage(tags, prefix, cmd, params).toStdString();
83 }
84
85 TEST(IrcEncoderTest, simple_test_with_verb_and_params)
86 {
87     EXPECT_STRCASEEQ(
88         "foo bar baz asdf",
89         write(IrcMessage("",
90                          "foo",
91                          {"bar", "baz", "asdf"})).data());
92 }
93
94 TEST(IrcEncoderTest, simple_test_with_source_and_no_params)
95 {
96     EXPECT_STRCASEEQ(
97         ":src AWAY",
98         write(IrcMessage("src",
99                          "AWAY")).data());
100 }
101
102 TEST(IrcEncoderTest, simple_test_with_source_and_empty_trailing_param)
103 {
104     EXPECT_STRCASEEQ(
105         ":src AWAY :",
106         write(IrcMessage("src",
107                          "AWAY",
108                          {""})).data());
109 }
110
111 TEST(IrcEncoderTest, simple_test_with_source)
112 {
113     EXPECT_STRCASEEQ(
114         ":coolguy foo bar baz asdf",
115         write(IrcMessage("coolguy",
116                          "foo",
117                          {"bar", "baz", "asdf"})).data());
118 }
119
120 TEST(IrcEncoderTest, simple_test_with_trailing_param)
121 {
122     EXPECT_STRCASEEQ(
123         "foo bar baz :asdf quux",
124         write(IrcMessage("",
125                          "foo",
126                          {"bar", "baz", "asdf quux"})).data());
127 }
128
129 TEST(IrcEncoderTest, simple_test_with_empty_trailing_param)
130 {
131     EXPECT_STRCASEEQ(
132         "foo bar baz :",
133         write(IrcMessage("",
134                          "foo",
135                          {"bar", "baz", ""})).data());
136 }
137
138 TEST(IrcEncoderTest, simple_test_with_trailing_param_containing_colon)
139 {
140     EXPECT_STRCASEEQ(
141         "foo bar baz ::asdf",
142         write(IrcMessage("",
143                          "foo",
144                          {"bar", "baz", ":asdf"})).data());
145 }
146
147 TEST(IrcEncoderTest, test_with_source_and_trailing_param)
148 {
149     EXPECT_STRCASEEQ(
150         ":coolguy foo bar baz :asdf quux",
151         write(IrcMessage("coolguy",
152                          "foo",
153                          {"bar", "baz", "asdf quux"})).data());
154 }
155
156 TEST(IrcEncoderTest, test_with_trailing_containing_beginning_end_whitespace)
157 {
158     EXPECT_STRCASEEQ(
159         ":coolguy foo bar baz :  asdf quux ",
160         write(IrcMessage("coolguy",
161                          "foo",
162                          {"bar", "baz", "  asdf quux "})).data());
163 }
164
165 TEST(IrcEncoderTest, test_with_trailing_containing_what_looks_like_another_trailing_param)
166 {
167     EXPECT_STRCASEEQ(
168         ":coolguy PRIVMSG bar :lol :) ",
169         write(IrcMessage("coolguy",
170                          "PRIVMSG",
171                          {"bar", "lol :) "})).data());
172 }
173
174 TEST(IrcEncoderTest, simple_test_with_source_and_empty_trailing)
175 {
176     EXPECT_STRCASEEQ(
177         ":coolguy foo bar baz :",
178         write(IrcMessage("coolguy",
179                          "foo",
180                          {"bar", "baz", ""})).data());
181 }
182
183 TEST(IrcEncoderTest, trailing_contains_only_spaces)
184 {
185     EXPECT_STRCASEEQ(
186         ":coolguy foo bar baz :  ",
187         write(IrcMessage("coolguy",
188                          "foo",
189                          {"bar", "baz", "  "})).data());
190 }
191
192 TEST(IrcEncoderTest, param_containing_tab_tab_is_not_considered_SPACE_for_message_splitting)
193 {
194     EXPECT_STRCASEEQ(
195         ":coolguy foo b\tar baz",
196         write(IrcMessage("coolguy",
197                          "foo",
198                          {"b\tar", "baz"})).data());
199 }
200
201 TEST(IrcEncoderTest, tags_with_no_value_and_space_filled_trailing)
202 {
203     EXPECT_STRCASEEQ(
204         "@asd :coolguy foo bar baz :  ",
205         write(IrcMessage({{IrcTagKey("asd"), ""}},
206                          "coolguy",
207                          "foo",
208                          {"bar", "baz", "  "})).data());
209 }
210
211 TEST(IrcEncoderTest, tags_with_escaped_values)
212 {
213     std::vector<std::string> expected{
214         R"(@d=gh\:764;a=b\\and\nk foo)",
215         R"(@a=b\\and\nk;d=gh\:764 foo)",
216     };
217     EXPECT_THAT(expected, testing::Contains(testing::StrCaseEq(
218         write(IrcMessage({{IrcTagKey("a"), "b\\and\nk"},
219                           {IrcTagKey("d"), "gh;764"}},
220                          "",
221                          "foo")))));
222 }
223
224 TEST(IrcEncoderTest, tags_with_escaped_values_and_params)
225 {
226     std::vector<std::string> expected{
227         R"(@d=gh\:764;a=b\\and\nk foo par1 par2)",
228         R"(@a=b\\and\nk;d=gh\:764 foo par1 par2)",
229     };
230     EXPECT_THAT(expected, testing::Contains(testing::StrCaseEq(
231         write(IrcMessage({{IrcTagKey("a"), "b\\and\nk"},
232                           {IrcTagKey("d"), "gh;764"}},
233                          "",
234                          "foo",
235                          {"par1", "par2"})))));
236 }
237
238 TEST(IrcEncoderTest, tags_with_long_strange_values)
239 {
240     EXPECT_STRCASEEQ(
241         R"(@foo=\\\\\:\\s\s\r\n COMMAND)",
242         write(IrcMessage({{IrcTagKey("foo"), "\\\\;\\s \r\n"}},
243                          "",
244                          "COMMAND")).data());
245 }