summaryrefslogtreecommitdiff
path: root/src/test.cpp
blob: 79fcd0bc2f7e5655710674c34decd545e51b3573 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
/**
 * Just a very simple test suite, by hand, using assert()
 */

#include <xmpp/xmpp_component.hpp>
#include <utils/timed_events.hpp>
#include <database/database.hpp>
#include <xmpp/xmpp_parser.hpp>
#include <utils/encoding.hpp>
#include <logger/logger.hpp>
#include <config/config.hpp>
#include <bridge/colors.hpp>
#include <utils/tolower.hpp>
#include <utils/revstr.hpp>
#include <irc/irc_user.hpp>
#include <utils/string.hpp>
#include <utils/split.hpp>
#include <utils/xdg.hpp>
#include <xmpp/jid.hpp>
#include <irc/iid.hpp>
#include <unistd.h>
#include <string.h>

#include <iostream>
#include <thread>
#include <vector>

#include "biboumi.h"

#undef NDEBUG
#include <assert.h>

static const std::string color("");
static const std::string reset("");

int main()
{

  /**
   * Config
   */
  std::cout << color << "Testing config…" << reset << std::endl;
  Config::filename = "test.cfg";
  Config::file_must_exist = false;
  Config::set("coucou", "bonjour", true);
  Config::close();

  bool error = false;
  try
    {
      Config::file_must_exist = true;
      assert(Config::get("coucou", "") == "bonjour");
      assert(Config::get("does not exist", "default") == "default");
      Config::close();
    }
  catch (const std::ios::failure& e)
    {
      error = true;
    }
  assert(error == false);

  Config::set("log_level", "2");
  Config::set("log_file", "");

  std::cout << color << "Testing logging…" << reset << std::endl;
  log_debug("If you see this, the test FAILED.");
  log_info("If you see this, the test FAILED.");
  log_warning("You must see this message. And the next one too.");
  log_error("It’s not an error, don’t worry, the test passed.");


  /**
   * Timed events
   */
  std::cout << color << "Testing timed events…" << reset << std::endl;
  // No event.
  assert(TimedEventsManager::instance().get_timeout() == utils::no_timeout);
  assert(TimedEventsManager::instance().execute_expired_events() == 0);

  // Add a single event
  TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 50ms, [](){ std::cout << "Timeout expired" << std::endl; }));
  // The event should not yet be expired
  assert(TimedEventsManager::instance().get_timeout() > 0ms);
  assert(TimedEventsManager::instance().execute_expired_events() == 0);
  std::chrono::milliseconds timoute = TimedEventsManager::instance().get_timeout();
  std::cout << "Sleeping for " << timoute.count() << "ms" << std::endl;
  std::this_thread::sleep_for(timoute);

  // Event is now expired
  assert(TimedEventsManager::instance().execute_expired_events() == 1);
  assert(TimedEventsManager::instance().get_timeout() == utils::no_timeout);

  // Test canceling events
  TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 100ms, [](){ }, "un"));
  TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 200ms, [](){ }, "deux"));
  TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 300ms, [](){ }, "deux"));
  assert(TimedEventsManager::instance().get_timeout() > 0ms);
  assert(TimedEventsManager::instance().size() == 3);
  assert(TimedEventsManager::instance().cancel("un") == 1);
  assert(TimedEventsManager::instance().size() == 2);
  assert(TimedEventsManager::instance().cancel("deux") == 2);
  assert(TimedEventsManager::instance().get_timeout() == utils::no_timeout);

  /**
   * Encoding
   */
  std::cout << color << "Testing encoding…" << reset << std::endl;
  const char* valid = "C̡͔͕̩͙̽ͫ̈́ͥ̿̆ͧ̚r̸̩̘͍̻͖̆͆͛͊̉̕͡o͇͈̳̤̱̊̈͢q̻͍̦̮͕ͥͬͬ̽ͭ͌̾ͅǔ͉͕͇͚̙͉̭͉̇̽ȇ͈̮̼͍͔ͣ͊͞͝ͅ ͫ̾ͪ̓ͥ̆̋̔҉̢̦̠͈͔̖̲̯̦ụ̶̯͐̃̋ͮ͆͝n̬̱̭͇̻̱̰̖̤̏͛̏̿̑͟ë́͐҉̸̥̪͕̹̻̙͉̰ ̹̼̱̦̥ͩ͑̈́͑͝ͅt͍̥͈̹̝ͣ̃̔̈̔ͧ̕͝ḙ̸̖̟̙͙ͪ͢ų̯̞̼̲͓̻̞͛̃̀́b̮̰̗̩̰̊̆͗̾̎̆ͯ͌͝.̗̙͎̦ͫ̈́ͥ͌̈̓ͬ";
  assert(utils::is_valid_utf8(valid) == true);
  const char* invalid = "\xF0\x0F";
  assert(utils::is_valid_utf8(invalid) == false);
  const char* invalid2 = "\xFE\xFE\xFF\xFF";
  assert(utils::is_valid_utf8(invalid2) == false);

  std::string in = "Biboumi ╯°□°)╯︵ ┻━┻";
  std::cout << in << std::endl;
  assert(utils::is_valid_utf8(in.c_str()) == true);
  std::string res = utils::convert_to_utf8(in, "UTF-8");
  assert(utils::is_valid_utf8(res.c_str()) == true && res == in);

  std::string original_utf8("couc¥ou");
  std::string original_latin1("couc\xa5ou");

  // When converting back to utf-8
  std::string from_latin1 = utils::convert_to_utf8(original_latin1.c_str(), "ISO-8859-1");
  assert(from_latin1 == original_utf8);

  // Check the behaviour when the decoding fails (here because we provide a
  // wrong charset)
  std::string from_ascii = utils::convert_to_utf8(original_latin1, "US-ASCII");
  assert(from_ascii == "couc�ou");
  std::cout << from_ascii << std::endl;

  std::string without_ctrl_char("𤭢€¢$");
  assert(utils::remove_invalid_xml_chars(without_ctrl_char) == without_ctrl_char);
  assert(utils::remove_invalid_xml_chars(in) == in);
  assert(utils::remove_invalid_xml_chars("\acouco\u0008u\uFFFEt\uFFFFe\r\n♥") == "coucoute\r\n♥");

  /**
   * Id generation
   */
  std::cout << color << "Testing id generation…" << reset << std::endl;
  const std::string first_uuid = XmppComponent::next_id();
  const std::string second_uuid = XmppComponent::next_id();
  std::cout << first_uuid << std::endl;
  std::cout << second_uuid << std::endl;
  assert(first_uuid.size() == 36);
  assert(second_uuid.size() == 36);
  assert(first_uuid != second_uuid);

  /**
   * Utils
   */
  std::cout << color << "Testing utils…" << reset << std::endl;
  std::vector<std::string> splitted = utils::split("a::a", ':', false);
  assert(splitted.size() == 2);
  splitted = utils::split("a::a", ':', true);
  assert(splitted.size() == 3);
  assert(splitted[0] == "a");
  assert(splitted[1] == "");
  assert(splitted[2] == "a");
  splitted = utils::split("\na", '\n', true);
  assert(splitted.size() == 2);
  assert(splitted[0] == "");
  assert(splitted[1] == "a");

  const std::string lowercase = utils::tolower("CoUcOu LeS CoPaiNs ♥");
  std::cout << lowercase << std::endl;
  assert(lowercase == "coucou les copains ♥");

  const std::string ltr = "coucou";
  assert(utils::revstr(ltr) == "uocuoc");

  assert(to_bool("true"));
  assert(!to_bool("trou"));
  assert(to_bool("1"));
  assert(!to_bool("0"));
  assert(!to_bool("-1"));
  assert(!to_bool("false"));

  /**
   * XML parsing
   */
  std::cout << color << "Testing XML parsing…" << reset << std::endl;
  XmppParser xml;
  const std::string doc = "<stream xmlns='stream_ns'><stanza b='c'>inner<child1><grandchild/></child1><child2 xmlns='child2_ns'/>tail</stanza></stream>";
  auto check_stanza = [](const Stanza& stanza)
    {
      assert(stanza.get_name() == "stanza");
      assert(stanza.get_tag("xmlns") == "stream_ns");
      assert(stanza.get_tag("b") == "c");
      assert(stanza.get_inner() == "inner");
      assert(stanza.get_tail() == "");
      assert(stanza.get_child("child1", "stream_ns") != nullptr);
      assert(stanza.get_child("child2", "stream_ns") == nullptr);
      assert(stanza.get_child("child2", "child2_ns") != nullptr);
      assert(stanza.get_child("child2", "child2_ns")->get_tail() == "tail");
    };
  xml.add_stanza_callback([check_stanza](const Stanza& stanza)
      {
        std::cout << stanza.to_string() << std::endl;
        check_stanza(stanza);
        // Do the same checks on a copy of that stanza.
        Stanza copy(stanza);
        check_stanza(copy);
      });
  xml.feed(doc.data(), doc.size(), true);

  const std::string doc2 = "<stream xmlns='s'><stanza>coucou\r\n\a</stanza></stream>";
  xml.add_stanza_callback([](const Stanza& stanza)
      {
        std::cout << stanza.to_string() << std::endl;
        assert(stanza.get_inner() == "coucou\r\n");
      });
  xml.feed(doc2.data(), doc.size(), true);

  /**
   * XML escape/escape
   */
  std::cout << color << "Testing XML escaping…" << reset << std::endl;
  const std::string unescaped = "'coucou'<cc>/&\"gaga\"";
  assert(xml_escape(unescaped) == "&apos;coucou&apos;&lt;cc&gt;/&amp;&quot;gaga&quot;");
  assert(xml_unescape(xml_escape(unescaped)) == unescaped);

  /**
   * Irc user parsing
   */
  const std::map<char, char> prefixes{{'!', 'a'}, {'@', 'o'}};

  IrcUser user1("!nick!~some@host.bla", prefixes);
  assert(user1.nick == "nick");
  assert(user1.host == "~some@host.bla");
  assert(user1.modes.size() == 1);
  assert(user1.modes.find('a') != user1.modes.end());
  IrcUser user2("coucou!~other@host.bla", prefixes);
  assert(user2.nick == "coucou");
  assert(user2.host == "~other@host.bla");
  assert(user2.modes.empty());
  assert(user2.modes.find('a') == user2.modes.end());

  /**
   * Colors conversion
   */
  std::cout << color << "Testing IRC colors conversion…" << reset << std::endl;
  std::unique_ptr<XmlNode> xhtml;
  std::string cleaned_up;

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("bold");
  std::cout << xhtml->to_string() << std::endl;
  assert(xhtml && xhtml->to_string() == "<body xmlns='http://www.w3.org/1999/xhtml'><span style='font-weight:bold;'>bold</span></body>");

  std::tie(cleaned_up, xhtml) =
    irc_format_to_xhtmlim("normalboldunder-and-boldbold normal"
                          "5red,5default-on-red10,2cyan-on-blue");
  assert(xhtml);
  assert(xhtml->to_string() == "<body xmlns='http://www.w3.org/1999/xhtml'>normal<span style='font-weight:bold;'>bold</span><span style='font-weight:bold;text-decoration:underline;'>under-and-bold</span><span style='font-weight:bold;'>bold</span> normal<span style='color:red;'>red</span><span style='background-color:red;'>default-on-red</span><span style='color:cyan;background-color:blue;'>cyan-on-blue</span></body>");
  assert(cleaned_up == "normalboldunder-and-boldbold normalreddefault-on-redcyan-on-blue");

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("normal");
  assert(!xhtml && cleaned_up == "normal");

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("");
  assert(xhtml && !xhtml->has_children() && cleaned_up.empty());

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim(",a");
  assert(xhtml && !xhtml->has_children() && cleaned_up == "a");

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim(",");
  assert(xhtml && !xhtml->has_children() && cleaned_up.empty());

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("[\x1D13dolphin-emu/dolphin\x1D] 03foo commented on #283 (Add support for the guide button to XInput): 02http://example.com");
  assert(xhtml->to_string() == "<body xmlns='http://www.w3.org/1999/xhtml'>[<span style='font-style:italic;'/><span style='font-style:italic;color:lightmagenta;'>dolphin-emu/dolphin</span><span style='color:lightmagenta;'>] </span><span style='color:green;'>foo</span> commented on #283 (Add support for the guide button to XInput): <span style='text-decoration:underline;'/><span style='text-decoration:underline;color:blue;'>http://example.com</span><span style='text-decoration:underline;'/></body>");
  assert(cleaned_up == "[dolphin-emu/dolphin] foo commented on #283 (Add support for the guide button to XInput): http://example.com");

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("0e46ab by 03Pierre Dindon [090|091|040] 02http://example.net/Ojrh4P media: avoid pop-in effect when loading thumbnails by specifying an explicit size");
  assert(cleaned_up == "0e46ab by Pierre Dindon [0|1|0] http://example.net/Ojrh4P media: avoid pop-in effect when loading thumbnails by specifying an explicit size");
  assert(xhtml->to_string() == "<body xmlns='http://www.w3.org/1999/xhtml'>0e46ab by <span style='color:green;'>Pierre Dindon</span> [<span style='color:lightgreen;'>0</span>|<span style='color:lightgreen;'>1</span>|<span style='color:indianred;'>0</span>] <span style='text-decoration:underline;'/><span style='text-decoration:underline;color:blue;'>http://example.net/Ojrh4P</span><span style='text-decoration:underline;'/> media: avoid pop-in effect when loading thumbnails by specifying an explicit size</body>");

  std::tie(cleaned_up, xhtml) = irc_format_to_xhtmlim("test\ncoucou");
  assert(cleaned_up == "test\ncoucou");
  assert(xhtml->to_string() == "<body xmlns='http://www.w3.org/1999/xhtml'>test<br/>coucou</body>");

  /**
   * JID parsing
   */
  std::cout << color << "Testing JID parsing…" << reset << std::endl;
  // Full JID
  Jid jid1("♥@ツ.coucou/coucou@coucou/coucou");
  std::cout << jid1.local << "@" << jid1.domain << "/" << jid1.resource << std::endl;
  assert(jid1.local == "♥");
  assert(jid1.domain == "ツ.coucou");
  assert(jid1.resource == "coucou@coucou/coucou");

  // Domain and resource
  Jid jid2("ツ.coucou/coucou@coucou/coucou");
  std::cout << jid2.local << "@" << jid2.domain << "/" << jid2.resource << std::endl;
  assert(jid2.local == "");
  assert(jid2.domain == "ツ.coucou");
  assert(jid2.resource == "coucou@coucou/coucou");

  // Jidprep
  const std::string& badjid("~zigougou™@EpiK-7D9D1FDE.poez.io/Boujour/coucou/slt™");
  const std::string correctjid = jidprep(badjid);
  std::cout << correctjid << std::endl;
  assert(correctjid == "~zigougoutm@epik-7d9d1fde.poez.io/Boujour/coucou/sltTM");
  // Check that the cache do not break things when we prep the same string
  // multiple times
  assert(jidprep(badjid) == "~zigougoutm@epik-7d9d1fde.poez.io/Boujour/coucou/sltTM");
  assert(jidprep(badjid) == "~zigougoutm@epik-7d9d1fde.poez.io/Boujour/coucou/sltTM");

  const std::string badjid2("Zigougou@poez.io");
  const std::string correctjid2 = jidprep(badjid2);
  std::cout << correctjid2 << std::endl;
  assert(correctjid2 == "zigougou@poez.io");

  const std::string crappy("~Bisous@7ea8beb1:c5fd2849:da9a048e:ip");
  const std::string fixed_crappy = jidprep(crappy);
  std::cout << fixed_crappy << std::endl;
  assert(fixed_crappy == "~bisous@7ea8beb1-c5fd2849-da9a048e-ip");

  /**
   * IID parsing
   */
  {
    std::cout << color << "Testing IID parsing…" << reset << std::endl;
    Iid iid1("foo!irc.example.org");
    std::cout << std::to_string(iid1) << std::endl;
    assert(std::to_string(iid1) == "foo!irc.example.org");
    assert(iid1.get_local() == "foo");
    assert(iid1.get_server() == "irc.example.org");
    assert(!iid1.is_channel);
    assert(iid1.is_user);

    Iid iid2("#test%irc.example.org");
    std::cout << std::to_string(iid2) << std::endl;
    assert(std::to_string(iid2) == "#test%irc.example.org");
    assert(iid2.get_local() == "#test");
    assert(iid2.get_server() == "irc.example.org");
    assert(iid2.is_channel);
    assert(!iid2.is_user);

    Iid iid3("%irc.example.org");
    std::cout << std::to_string(iid3) << std::endl;
    assert(std::to_string(iid3) == "%irc.example.org");
    assert(iid3.get_local() == "");
    assert(iid3.get_server() == "irc.example.org");
    assert(iid3.is_channel);
    assert(!iid3.is_user);

    Iid iid4("irc.example.org");
    std::cout << std::to_string(iid4) << std::endl;
    assert(std::to_string(iid4) == "irc.example.org");
    assert(iid4.get_local() == "");
    assert(iid4.get_server() == "irc.example.org");
    assert(!iid4.is_channel);
    assert(!iid4.is_user);

    Iid iid5("nick!");
    std::cout << std::to_string(iid5) << std::endl;
    assert(std::to_string(iid5) == "nick!");
    assert(iid5.get_local() == "nick");
    assert(iid5.get_server() == "");
    assert(!iid5.is_channel);
    assert(iid5.is_user);

    Iid iid6("##channel%");
    std::cout << std::to_string(iid6) << std::endl;
    assert(std::to_string(iid6) == "##channel%");
    assert(iid6.get_local() == "##channel");
    assert(iid6.get_server() == "");
    assert(iid6.is_channel);
    assert(!iid6.is_user);
  }

  {
    std::cout << color << "Testing IID parsing with a fixed server configured…" << reset << std::endl;
    // Now do the same tests, but with a configured fixed_irc_server
    Config::set("fixed_irc_server", "fixed.example.com", false);

    Iid iid1("foo!irc.example.org");
    std::cout << std::to_string(iid1) << std::endl;
    assert(std::to_string(iid1) == "foo!");
    assert(iid1.get_local() == "foo");
    assert(iid1.get_server() == "fixed.example.com");
    assert(!iid1.is_channel);
    assert(iid1.is_user);

    Iid iid2("#test%irc.example.org");
    std::cout << std::to_string(iid2) << std::endl;
    assert(std::to_string(iid2) == "#test%irc.example.org");
    assert(iid2.get_local() == "#test%irc.example.org");
    assert(iid2.get_server() == "fixed.example.com");
    assert(iid2.is_channel);
    assert(!iid2.is_user);

    // Note that it is impossible to adress the IRC server directly, or to
    // use the virtual channel, in that mode

    // Iid iid3("%irc.example.org");
    // Iid iid4("irc.example.org");

    Iid iid5("nick!");
    std::cout << std::to_string(iid5) << std::endl;
    assert(std::to_string(iid5) == "nick!");
    assert(iid5.get_local() == "nick");
    assert(iid5.get_server() == "fixed.example.com");
    assert(!iid5.is_channel);
    assert(iid5.is_user);

    Iid iid6("##channel%");
    std::cout << std::to_string(iid6) << std::endl;
    assert(std::to_string(iid6) == "##channel%");
    assert(iid6.get_local() == "##channel%");
    assert(iid6.get_server() == "fixed.example.com");
    assert(iid6.is_channel);
    assert(!iid6.is_user);
  }
#ifdef USE_DATABASE
  {
    std::cout << color << "Testing the Database…" << reset << std::endl;
    // Remove any potential existing db
    unlink("./test.db");
    Config::set("db_name", "test.db");
    Database::set_verbose(true);
    auto o = Database::get_irc_server_options("zouzou@example.com", "irc.example.com");
    o.requireTls = false;
    o.update();
    auto a = Database::get_irc_server_options("zouzou@example.com", "irc.example.com");
    assert(a.requireTls == false);
    auto b = Database::get_irc_server_options("moumou@example.com", "irc.example.com");
    assert(b.requireTls == true);

    // b does not yet exist in the db, the object is created but not yet
    // inserted
    assert(1 == Database::count<db::IrcServerOptions>());

    b.update();
    assert(2 == Database::count<db::IrcServerOptions>());

    assert(b.pass == "");
    assert(b.pass.value() == "");

    std::vector<litesql::FieldType> ftypes;
    db::IrcServerOptions::getFieldTypes(ftypes);
    for (const auto& type: ftypes)
      {
        std::cout << type.type() << std::endl;
      }
  }
#endif
  {
    std::cout << color << "Testing the xdg_path function…" << reset << std::endl;
    std::string res;

    ::unsetenv("XDG_CONFIG_HOME");
    ::unsetenv("HOME");
    res = xdg_config_path("coucou.txt");
    std::cout << res << std::endl;
    assert(res == "coucou.txt");

    ::setenv("HOME", "/home/user", 1);
    res = xdg_config_path("coucou.txt");
    std::cout << res << std::endl;
    assert(res == "/home/user/.config/biboumi/coucou.txt");

    ::setenv("XDG_CONFIG_HOME", "/some_weird_dir", 1);
    res = xdg_config_path("coucou.txt");
    std::cout << res << std::endl;
    assert(res == "/some_weird_dir/biboumi/coucou.txt");

    ::setenv("XDG_DATA_HOME", "/datadir", 1);
    res = xdg_data_path("bonjour.txt");
    std::cout << res << std::endl;
    assert(res == "/datadir/biboumi/bonjour.txt");

  }

  return 0;
}