1 /*
2 * Copyright (C) 2008-2020 The Communi Project
3 *
4 * This test is free, and not covered by the BSD license. There is no
5 * restriction applied to their modification, redistribution, using and so on.
6 * You can study them, modify them, use them in your own program - either
7 * completely or partially.
8 */
9
10 #include "irc.h"
11 #include "irccommand.h"
12 #include "ircprotocol.h"
13 #include "ircconnection.h"
14 #include "ircmessage.h"
15 #include "ircfilter.h"
16 #include <QtTest/QtTest>
17 #include <QTextCodec>
18 #include <QtCore/QScopedPointer>
19 #ifndef QT_NO_SSL
20 #include <QtNetwork/QSslSocket>
21 #endif
22
23 #include "tst_ircdata.h"
24 #include "tst_ircclientserver.h"
25
26 class FriendlyConnection : public IrcConnection
27 {
28 friend class tst_IrcConnection;
29 };
30
31 class TestProtocol : public IrcProtocol
32 {
33 public:
TestProtocol(IrcConnection * connection)34 TestProtocol(IrcConnection* connection) : IrcProtocol(connection)
35 {
36 }
37
write(const QByteArray & data)38 bool write(const QByteArray& data) override
39 {
40 written = data;
41 return IrcProtocol::write(data);
42 }
43
44 QByteArray written;
45 };
46
47 class tst_IrcConnection : public tst_IrcClientServer
48 {
49 Q_OBJECT
50
51 private slots:
52 void testDefaults();
53
54 void testHost_data();
55 void testHost();
56
57 void testPort_data();
58 void testPort();
59
60 void testUserName_data();
61 void testUserName();
62
63 void testNickName_data();
64 void testNickName();
65
66 void testRealName_data();
67 void testRealName();
68
69 void testPassword_data();
70 void testPassword();
71
72 void testDisplayName_data();
73 void testDisplayName();
74
75 void testEncoding_data();
76 void testEncoding();
77
78 void testSocket_data();
79 void testSocket();
80
81 void testSecure();
82 void testSasl();
83 void testNoSasl();
84 void testSsl();
85
86 void testOpen();
87 void testEnabled();
88
89 void testStatus();
90 void testConnection();
91 void testMessages();
92 void testMessageFlags();
93 void testStatusPrefixes();
94 void testMessageComposer();
95 void testMessageComposerCrash_data();
96 void testMessageComposerCrash();
97 void testBatch();
98 void testServerTime();
99
100 void testSendCommand();
101 void testSendData();
102
103 void testMessageFilter();
104 void testCommandFilter();
105
106 void testDebug();
107 void testWarnings();
108
109 void testCtcp();
110 void testClone();
111 void testSaveRestore();
112 void testSignals();
113 void testServers();
114 };
115
testDefaults()116 void tst_IrcConnection::testDefaults()
117 {
118 IrcConnection connection;
119 QVERIFY(connection.host().isNull());
120 QCOMPARE(connection.port(), 6667);
121 QVERIFY(connection.userName().isNull());
122 QVERIFY(connection.nickName().isNull());
123 QVERIFY(connection.realName().isNull());
124 QVERIFY(connection.password().isNull());
125 QVERIFY(connection.displayName().isNull());
126 QCOMPARE(connection.encoding(), QByteArray("ISO-8859-15"));
127 QCOMPARE(connection.status(), IrcConnection::Inactive);
128 QVERIFY(!connection.isActive());
129 QVERIFY(!connection.isConnected());
130 QVERIFY(connection.isEnabled());
131 QCOMPARE(connection.reconnectDelay(), 0);
132 QVERIFY(connection.socket());
133 QVERIFY(!connection.isSecure());
134 QVERIFY(connection.saslMechanism().isNull());
135 QVERIFY(!IrcConnection::supportedSaslMechanisms().isEmpty());
136 QVERIFY(connection.network());
137 }
138
testHost_data()139 void tst_IrcConnection::testHost_data()
140 {
141 QTest::addColumn<QString>("host");
142
143 QTest::newRow("null") << QString();
144 QTest::newRow("empty") << QString("");
145 QTest::newRow("space") << QString(" ");
146 QTest::newRow("invalid") << QString("invalid");
147 QTest::newRow("local") << QString("127.0.0.1");
148 }
149
testHost()150 void tst_IrcConnection::testHost()
151 {
152 QFETCH(QString, host);
153
154 IrcConnection connection;
155 QSignalSpy spy(&connection, SIGNAL(hostChanged(QString)));
156 QVERIFY(spy.isValid());
157 connection.setHost(host);
158 QCOMPARE(connection.host(), host);
159 QCOMPARE(spy.count(), !host.isEmpty() ? 1 : 0);
160 if (!spy.isEmpty())
161 QCOMPARE(spy.first().first().toString(), host);
162
163 IrcConnection another(host);
164 QCOMPARE(another.host(), host);
165 }
166
testPort_data()167 void tst_IrcConnection::testPort_data()
168 {
169 QTest::addColumn<int>("port");
170
171 QTest::newRow("-1") << -1;
172 QTest::newRow("0") << 0;
173 QTest::newRow("6666") << 6666;
174 QTest::newRow("6667") << 6667;
175 QTest::newRow("6668") << 6668;
176 }
177
testPort()178 void tst_IrcConnection::testPort()
179 {
180 QFETCH(int, port);
181
182 IrcConnection connection;
183 QSignalSpy spy(&connection, SIGNAL(portChanged(int)));
184 QVERIFY(spy.isValid());
185 connection.setPort(port);
186 QCOMPARE(connection.port(), port);
187 QCOMPARE(spy.count(), port != 6667 ? 1 : 0);
188 if (!spy.isEmpty())
189 QCOMPARE(spy.first().first().toInt(), port);
190 }
191
testUserName_data()192 void tst_IrcConnection::testUserName_data()
193 {
194 QTest::addColumn<QString>("name");
195 QTest::addColumn<QString>("result");
196
197 QTest::newRow("null") << QString() << QString();
198 QTest::newRow("empty") << QString("") << QString("");
199 QTest::newRow("space") << QString(" ") << QString("");
200 QTest::newRow("spaces") << QString(" foo bar ") << QString("foo");
201 }
202
testUserName()203 void tst_IrcConnection::testUserName()
204 {
205 QFETCH(QString, name);
206 QFETCH(QString, result);
207
208 IrcConnection connection;
209 QSignalSpy spy(&connection, SIGNAL(userNameChanged(QString)));
210 QVERIFY(spy.isValid());
211 connection.setUserName(name);
212 QCOMPARE(connection.userName(), result);
213 QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0);
214 if (!spy.isEmpty())
215 QCOMPARE(spy.first().first().toString(), result);
216 }
217
testNickName_data()218 void tst_IrcConnection::testNickName_data()
219 {
220 QTest::addColumn<QString>("name");
221 QTest::addColumn<QString>("result");
222
223 QTest::newRow("null") << QString() << QString();
224 QTest::newRow("empty") << QString("") << QString("");
225 QTest::newRow("space") << QString(" ") << QString("");
226 QTest::newRow("spaces") << QString(" foo bar ") << QString("foo");
227 }
228
testNickName()229 void tst_IrcConnection::testNickName()
230 {
231 QFETCH(QString, name);
232 QFETCH(QString, result);
233
234 IrcConnection connection;
235 QSignalSpy spy(&connection, SIGNAL(nickNameChanged(QString)));
236 QVERIFY(spy.isValid());
237 connection.setNickName(name);
238 QCOMPARE(connection.nickName(), result);
239 QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0);
240 if (!spy.isEmpty())
241 QCOMPARE(spy.first().first().toString(), result);
242 }
243
testRealName_data()244 void tst_IrcConnection::testRealName_data()
245 {
246 QTest::addColumn<QString>("name");
247 QTest::addColumn<QString>("result");
248
249 QTest::newRow("null") << QString() << QString();
250 QTest::newRow("empty") << QString("") << QString("");
251 QTest::newRow("space") << QString(" ") << QString(" ");
252 QTest::newRow("spaces") << QString(" foo bar ") << QString(" foo bar ");
253 }
254
testRealName()255 void tst_IrcConnection::testRealName()
256 {
257 QFETCH(QString, name);
258 QFETCH(QString, result);
259
260 IrcConnection connection;
261 QSignalSpy spy(&connection, SIGNAL(realNameChanged(QString)));
262 QVERIFY(spy.isValid());
263 connection.setRealName(name);
264 QCOMPARE(connection.realName(), result);
265 QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0);
266 if (!spy.isEmpty())
267 QCOMPARE(spy.first().first().toString(), result);
268 }
269
testPassword_data()270 void tst_IrcConnection::testPassword_data()
271 {
272 QTest::addColumn<QString>("passwd");
273 QTest::addColumn<QString>("result");
274
275 QTest::newRow("null") << QString() << QString();
276 QTest::newRow("empty") << QString("") << QString("");
277 QTest::newRow("space") << QString(" ") << QString(" ");
278 QTest::newRow("spaces") << QString(" foo bar ") << QString(" foo bar ");
279 }
280
testPassword()281 void tst_IrcConnection::testPassword()
282 {
283 QFETCH(QString, passwd);
284 QFETCH(QString, result);
285
286 IrcConnection connection;
287 QSignalSpy spy(&connection, SIGNAL(passwordChanged(QString)));
288 QVERIFY(spy.isValid());
289 connection.setPassword(passwd);
290 QCOMPARE(connection.password(), result);
291 QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0);
292 if (!spy.isEmpty())
293 QCOMPARE(spy.first().first().toString(), result);
294 }
295
testDisplayName_data()296 void tst_IrcConnection::testDisplayName_data()
297 {
298 QTest::addColumn<QString>("host");
299 QTest::addColumn<QString>("name");
300 QTest::addColumn<QString>("result");
301
302 QTest::newRow("null") << QString() << QString() << QString();
303 QTest::newRow("empty") << QString() << QString("") << QString("");
304 QTest::newRow("space") << QString() << QString(" ") << QString(" ");
305
306 QTest::newRow("host") << QString("host") << QString() << QString("host");
307 QTest::newRow("name") << QString() << QString("name") << QString("name");
308 QTest::newRow("explicit") << QString("host") << QString("name") << QString("name");
309 }
310
testDisplayName()311 void tst_IrcConnection::testDisplayName()
312 {
313 QFETCH(QString, host);
314 QFETCH(QString, name);
315 QFETCH(QString, result);
316
317 IrcConnection connection;
318 connection.setHost(host);
319 connection.setDisplayName(name);
320 QCOMPARE(connection.displayName(), result);
321 }
322
testEncoding_data()323 void tst_IrcConnection::testEncoding_data()
324 {
325 QTest::addColumn<QByteArray>("encoding");
326 QTest::addColumn<QByteArray>("actual");
327 QTest::addColumn<bool>("supported");
328
329 QTest::newRow("null") << QByteArray() << QByteArray("ISO-8859-15") << false;
330 QTest::newRow("empty") << QByteArray("") << QByteArray("ISO-8859-15") << false;
331 QTest::newRow("space") << QByteArray(" ") << QByteArray("ISO-8859-15") << false;
332 QTest::newRow("invalid") << QByteArray("invalid") << QByteArray("ISO-8859-15") << false;
333 foreach (const QByteArray& codec, QTextCodec::availableCodecs())
334 QTest::newRow(codec) << codec << codec << true;
335 }
336
testEncoding()337 void tst_IrcConnection::testEncoding()
338 {
339 QFETCH(QByteArray, encoding);
340 QFETCH(QByteArray, actual);
341 QFETCH(bool, supported);
342
343 if (!supported)
344 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setEncoding(): unsupported encoding \"" + encoding + "\" ");
345
346 IrcConnection connection;
347 connection.setEncoding(encoding);
348 QCOMPARE(connection.encoding(), actual);
349 }
350
Q_DECLARE_METATYPE(QAbstractSocket *)351 Q_DECLARE_METATYPE(QAbstractSocket*)
352 void tst_IrcConnection::testSocket_data()
353 {
354 QTest::addColumn<QAbstractSocket*>("socket");
355
356 QTest::newRow("null") << static_cast<QAbstractSocket*>(nullptr);
357 QTest::newRow("tcp") << static_cast<QAbstractSocket*>(new QTcpSocket(this));
358 #ifndef QT_NO_SSL
359 QTest::newRow("ssl") << static_cast<QAbstractSocket*>(new QSslSocket(this));
360 #endif
361 }
362
testSocket()363 void tst_IrcConnection::testSocket()
364 {
365 QFETCH(QAbstractSocket*, socket);
366
367 IrcConnection connection;
368 connection.setSocket(socket);
369 QCOMPARE(connection.socket(), socket);
370 QCOMPARE(connection.isSecure(), socket && socket->inherits("QSslSocket"));
371 }
372
testSecure()373 void tst_IrcConnection::testSecure()
374 {
375 IrcConnection connection;
376 QSignalSpy spy(&connection, SIGNAL(secureChanged(bool)));
377 QVERIFY(spy.isValid());
378 QVERIFY(!connection.isSecure());
379
380 #ifdef QT_NO_SSL
381 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSecure(): the Qt build does not support SSL");
382 #endif
383
384 connection.setSecure(true);
385
386 #ifndef QT_NO_SSL
387 QVERIFY(connection.isSecure());
388 QVERIFY(connection.socket()->inherits("QSslSocket"));
389 QCOMPARE(spy.count(), 1);
390 QVERIFY(spy.first().first().toBool());
391 #else
392 QVERIFY(!connection.isSecure());
393 QVERIFY(!connection.socket()->inherits("QSslSocket"));
394 QCOMPARE(spy.count(), 0);
395 #endif
396
397 connection.setSecure(false);
398 QVERIFY(!connection.isSecure());
399 QVERIFY(!connection.socket()->inherits("QSslSocket"));
400 #ifndef QT_NO_SSL
401 QCOMPARE(spy.count(), 2);
402 QVERIFY(!spy.last().last().toBool());
403 #else
404 QCOMPARE(spy.count(), 0);
405 #endif
406 }
407
testSasl()408 void tst_IrcConnection::testSasl()
409 {
410 QVERIFY(!IrcConnection::supportedSaslMechanisms().contains("UNKNOWN"));
411 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism(): unsupported mechanism: 'UNKNOWN'");
412 connection->setSaslMechanism("UNKNOWN");
413 QVERIFY(connection->saslMechanism().isEmpty());
414
415 IrcProtocol* protocol = static_cast<FriendlyConnection*>(connection.data())->protocol();
416 QVERIFY(protocol);
417
418 QVERIFY(IrcConnection::supportedSaslMechanisms().contains("PLAIN"));
419 connection->setSaslMechanism("PLAIN");
420 QCOMPARE(connection->saslMechanism(), QString("PLAIN"));
421
422 connection->open();
423 QVERIFY(waitForOpened());
424
425 QVERIFY(clientSocket->waitForBytesWritten(1000));
426 QVERIFY(serverSocket->waitForReadyRead(1000));
427 QByteArray written = serverSocket->readAll();
428 QVERIFY(written.contains("CAP LS"));
429 QVERIFY(written.contains("NICK nick"));
430 QVERIFY(!written.contains("PASS secret"));
431 QVERIFY(!written.contains("CAP REQ :sasl"));
432
433 QVERIFY(waitForWritten(":irc.freenode.net CAP * LS :sasl"));
434 QVERIFY(clientSocket->waitForBytesWritten(1000));
435 QVERIFY(serverSocket->waitForReadyRead(1000));
436 written = serverSocket->readAll();
437 QVERIFY(!written.contains("CAP LS"));
438 QVERIFY(!written.contains("NICK nick"));
439 QVERIFY(!written.contains("PASS secret"));
440 QVERIFY(written.contains("CAP REQ :sasl"));
441
442 // do not resume handshake too early
443 QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall);
444 QVERIFY(!clientSocket->waitForBytesWritten(1000));
445
446 QVERIFY(waitForWritten(":irc.freenode.net CAP user ACK :sasl"));
447 QVERIFY(clientSocket->waitForBytesWritten(1000));
448 QVERIFY(serverSocket->waitForReadyRead(1000));
449 QVERIFY(serverSocket->readAll().contains("AUTHENTICATE PLAIN"));
450
451 QVERIFY(waitForWritten("AUTHENTICATE +"));
452 QVERIFY(clientSocket->waitForBytesWritten(1000));
453 QVERIFY(serverSocket->waitForReadyRead(1000));
454
455 QByteArray response = serverSocket->readAll();
456 int index = response.indexOf("AUTHENTICATE");
457 QVERIFY(index != -1);
458 QByteArray secret = response.mid(index + 13);
459 index = secret.indexOf("\r\n");
460 QVERIFY(index != -1);
461 secret.truncate(index + 1);
462 secret = QByteArray::fromBase64(secret);
463 QByteArray expected = connection->userName().toUtf8() + '\0' +
464 connection->userName().toUtf8() + '\0' +
465 connection->password().toUtf8();
466 QCOMPARE(secret, expected);
467
468 // resume handshake
469 QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall);
470
471 QVERIFY(clientSocket->waitForBytesWritten(1000));
472 QVERIFY(serverSocket->waitForReadyRead(1000));
473 QVERIFY(serverSocket->readAll().contains("CAP END"));
474
475 // TODO:
476 QVERIFY(waitForWritten(":irc.freenode.net 900 user nick!user@host nick :You are now logged in as user."));
477 QVERIFY(waitForWritten(":irc.freenode.net 903 user :SASL authentication successful"));
478 QVERIFY(waitForWritten(":irc.freenode.net 001 user :Welcome to the freenode Internet Relay Chat Network user"));
479 }
480
testNoSasl()481 void tst_IrcConnection::testNoSasl()
482 {
483 QVERIFY(!IrcConnection::supportedSaslMechanisms().contains("UNKNOWN"));
484 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism(): unsupported mechanism: 'UNKNOWN'");
485 connection->setSaslMechanism("UNKNOWN");
486 QVERIFY(connection->saslMechanism().isEmpty());
487
488 IrcProtocol* protocol = static_cast<FriendlyConnection*>(connection.data())->protocol();
489 QVERIFY(protocol);
490
491 QVERIFY(IrcConnection::supportedSaslMechanisms().contains("PLAIN"));
492 connection->setSaslMechanism("PLAIN");
493 QCOMPARE(connection->saslMechanism(), QString("PLAIN"));
494
495 connection->open();
496 QVERIFY(waitForOpened());
497
498 QVERIFY(clientSocket->waitForBytesWritten(1000));
499 QVERIFY(serverSocket->waitForReadyRead(1000));
500 QByteArray written = serverSocket->readAll();
501 QVERIFY(written.contains("CAP LS"));
502 QVERIFY(written.contains("NICK nick"));
503 QVERIFY(!written.contains("PASS secret"));
504 QVERIFY(!written.contains("CAP REQ :sasl"));
505
506 QVERIFY(waitForWritten(":irc.freenode.net CAP * LS :no s-a-s-l here"));
507 QVERIFY(!clientSocket->waitForBytesWritten(1000));
508 QVERIFY(!serverSocket->waitForReadyRead(1000));
509 QVERIFY(serverSocket->readAll().isEmpty());
510
511 // resume handshake
512 QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall);
513 QVERIFY(clientSocket->waitForBytesWritten(1000));
514 QVERIFY(serverSocket->waitForReadyRead(1000));
515 written = serverSocket->readAll();
516 QVERIFY(written.contains("PASS :secret"));
517 QVERIFY(written.contains("CAP END"));
518 }
519
520 #ifndef QT_NO_SSL
521 class SslSocket : public QSslSocket
522 {
523 Q_OBJECT
524
525 public:
SslSocket(QObject * parent)526 SslSocket(QObject* parent) : QSslSocket(parent), clientEncryptionStarted(false) { }
527 bool clientEncryptionStarted;
528
529 public slots:
startClientEncryption()530 void startClientEncryption()
531 {
532 clientEncryptionStarted = true;
533 QSslSocket::startClientEncryption();
534 }
535 };
536 #endif // !QT_NO_SSL
537
testSsl()538 void tst_IrcConnection::testSsl()
539 {
540 #ifndef QT_NO_SSL
541 SslSocket* socket = new SslSocket(connection);
542 connection->setSocket(socket);
543 QCOMPARE(connection->socket(), socket);
544
545 connection->open();
546 QVERIFY(waitForOpened());
547
548 QVERIFY(socket->clientEncryptionStarted);
549 #endif // !QT_NO_SSL
550 }
551
testOpen()552 void tst_IrcConnection::testOpen()
553 {
554 IrcConnection connection;
555 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): host is empty!");
556 connection.open();
557 QCOMPARE(connection.status(), IrcConnection::Inactive);
558
559 connection.setHost("irc.ser.ver");
560 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): userName is empty!");
561 connection.open();
562 QCOMPARE(connection.status(), IrcConnection::Inactive);
563
564 connection.setUserName("user");
565 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): nickNames is empty!");
566 connection.open();
567 QCOMPARE(connection.status(), IrcConnection::Inactive);
568
569 connection.setNickName("nick");
570 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): realName is empty!");
571 connection.open();
572 QCOMPARE(connection.status(), IrcConnection::Inactive);
573
574 connection.setRealName("real");
575 connection.open();
576 QVERIFY(connection.status() != IrcConnection::Inactive);
577
578 connection.close();
579 QCOMPARE(connection.status(), IrcConnection::Closed);
580
581 connection.setEnabled(false);
582 connection.open();
583 QCOMPARE(connection.status(), IrcConnection::Closed);
584 }
585
testEnabled()586 void tst_IrcConnection::testEnabled()
587 {
588 IrcConnection connection;
589 QVERIFY(connection.isEnabled());
590
591 QSignalSpy spy(&connection, SIGNAL(enabledChanged(bool)));
592 QVERIFY(spy.isValid());
593
594 connection.setEnabled(false);
595 QVERIFY(!connection.isEnabled());
596 QCOMPARE(spy.count(), 1);
597 QCOMPARE(spy.last().at(0).toBool(), false);
598
599 connection.setDisabled(true);
600 QVERIFY(!connection.isEnabled());
601 QCOMPARE(spy.count(), 1);
602
603 connection.setDisabled(false);
604 QVERIFY(connection.isEnabled());
605 QCOMPARE(spy.count(), 2);
606 QCOMPARE(spy.last().at(0).toBool(), true);
607
608 connection.setEnabled(true);
609 QVERIFY(connection.isEnabled());
610 QCOMPARE(spy.count(), 2);
611 }
612
testStatus()613 void tst_IrcConnection::testStatus()
614 {
615 Irc::registerMetaTypes();
616
617 QSignalSpy statusSpy(connection, SIGNAL(statusChanged(IrcConnection::Status)));
618 QSignalSpy connectingSpy(connection, SIGNAL(connecting()));
619 QSignalSpy connectedSpy(connection, SIGNAL(connected()));
620 QSignalSpy disconnectedSpy(connection, SIGNAL(disconnected()));
621
622 QVERIFY(statusSpy.isValid());
623 QVERIFY(connectingSpy.isValid());
624 QVERIFY(connectedSpy.isValid());
625 QVERIFY(disconnectedSpy.isValid());
626
627 int statusCount = 0;
628 int connectingCount = 0;
629 int connectedCount = 0;
630 int disconnectedCount = 0;
631
632 connection->open();
633 QVERIFY(waitForOpened());
634 QVERIFY(connection->isActive());
635 QVERIFY(!connection->isConnected());
636 QCOMPARE(connection->status(), IrcConnection::Connecting);
637 QCOMPARE(statusSpy.count(), ++statusCount);
638 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connecting);
639 QCOMPARE(connectingSpy.count(), ++connectingCount);
640
641 QVERIFY(waitForWritten(tst_IrcData::welcome()));
642 QVERIFY(connection->isActive());
643 QVERIFY(connection->isConnected());
644 QCOMPARE(connection->status(), IrcConnection::Connected);
645 QCOMPARE(statusSpy.count(), ++statusCount);
646 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connected);
647 QCOMPARE(connectedSpy.count(), ++connectedCount);
648
649 clientSocket->close();
650 QVERIFY(connection->isActive());
651 QVERIFY(!connection->isConnected());
652 QCOMPARE(connection->status(), IrcConnection::Closing);
653 QCOMPARE(statusSpy.count(), ++statusCount);
654 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Closing);
655
656 connection->close();
657 QVERIFY(!connection->isActive());
658 QVERIFY(!connection->isConnected());
659 QCOMPARE(connection->status(), IrcConnection::Closed);
660 QCOMPARE(statusSpy.count(), ++statusCount);
661 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Closed);
662 QCOMPARE(disconnectedSpy.count(), ++disconnectedCount);
663
664 connection->open();
665 QVERIFY(waitForOpened());
666 QVERIFY(connection->isActive());
667 QVERIFY(!connection->isConnected());
668 QCOMPARE(connection->status(), IrcConnection::Connecting);
669 QCOMPARE(statusSpy.count(), ++statusCount);
670 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connecting);
671 QCOMPARE(connectingSpy.count(), ++connectingCount);
672
673 QVERIFY(waitForWritten(tst_IrcData::welcome()));
674 QVERIFY(connection->isActive());
675 QVERIFY(connection->isConnected());
676 QCOMPARE(connection->status(), IrcConnection::Connected);
677 QCOMPARE(statusSpy.count(), ++statusCount);
678 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connected);
679 QCOMPARE(connectedSpy.count(), ++connectedCount);
680
681 // trigger an error
682 serverSocket->close();
683 QVERIFY(clientSocket->waitForDisconnected(100));
684 QVERIFY(!connection->isConnected());
685 QVERIFY(!connection->isActive());
686
687 QCOMPARE(statusSpy.at(statusCount++).at(0).value<IrcConnection::Status>(), IrcConnection::Error);
688 QCOMPARE(statusSpy.count(), statusCount);
689
690 connection->close();
691 QVERIFY(!connection->isActive());
692 QVERIFY(!connection->isConnected());
693 QCOMPARE(connection->status(), IrcConnection::Closed);
694 QCOMPARE(statusSpy.count(), ++statusCount);
695 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Closed);
696 QCOMPARE(disconnectedSpy.count(), ++disconnectedCount);
697
698 connection->open();
699 QVERIFY(waitForOpened());
700 QVERIFY(connection->isActive());
701 QVERIFY(!connection->isConnected());
702 QCOMPARE(connection->status(), IrcConnection::Connecting);
703 QCOMPARE(statusSpy.count(), ++statusCount);
704 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connecting);
705 QCOMPARE(connectingSpy.count(), ++connectingCount);
706
707 QVERIFY(waitForWritten(tst_IrcData::welcome()));
708 QVERIFY(connection->isActive());
709 QVERIFY(connection->isConnected());
710 QCOMPARE(connection->status(), IrcConnection::Connected);
711 QCOMPARE(statusSpy.count(), ++statusCount);
712 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connected);
713 QCOMPARE(connectedSpy.count(), ++connectedCount);
714
715 // trigger an error - automatic reconnect
716 connection->setReconnectDelay(1);
717 serverSocket->close();
718 QVERIFY(clientSocket->waitForDisconnected(100));
719 QVERIFY(!connection->isConnected());
720 QVERIFY(!connection->isActive());
721
722 QCOMPARE(statusSpy.at(statusCount++).at(0).value<IrcConnection::Status>(), IrcConnection::Error);
723 QCOMPARE(statusSpy.at(statusCount++).at(0).value<IrcConnection::Status>(), IrcConnection::Waiting);
724 QCOMPARE(statusSpy.count(), statusCount);
725
726 QEventLoop reconnectLoop;
727 QTimer::singleShot(2000, &reconnectLoop, SLOT(quit()));
728 connect(connection, SIGNAL(statusChanged(IrcConnection::Status)), &reconnectLoop, SLOT(quit()));
729 reconnectLoop.exec();
730
731 QVERIFY(connection->isActive());
732 QVERIFY(!connection->isConnected());
733 QCOMPARE(connection->status(), IrcConnection::Connecting);
734 QCOMPARE(statusSpy.count(), ++statusCount);
735 QCOMPARE(statusSpy.last().at(0).value<IrcConnection::Status>(), IrcConnection::Connecting);
736
737 QVERIFY(waitForOpened());
738 QCOMPARE(connectingSpy.count(), ++connectingCount);
739
740 // trigger an error _after_ quit -> no automatic reconnect
741 connection->quit();
742 serverSocket->close();
743 QVERIFY(!connection->isConnected());
744 QVERIFY(!connection->isActive());
745 QCOMPARE(statusSpy.at(statusCount++).at(0).value<IrcConnection::Status>(), IrcConnection::Closed);
746 QCOMPARE(statusSpy.count(), statusCount);
747 }
748
testConnection()749 void tst_IrcConnection::testConnection()
750 {
751 Irc::registerMetaTypes();
752
753 TestProtocol* protocol = new TestProtocol(connection);
754 FriendlyConnection* friendly = static_cast<FriendlyConnection*>(connection.data());
755 friendly->setProtocol(protocol);
756 QCOMPARE(friendly->protocol(), protocol);
757 QCOMPARE(protocol->connection(), connection.data());
758
759 connection->open();
760 QVERIFY(waitForOpened());
761
762 QVERIFY(connection->isActive());
763 QVERIFY(!connection->isConnected());
764 QCOMPARE(connection->status(), IrcConnection::Connecting);
765
766 QVERIFY(waitForWritten(":irc.ser.ver 001 nick :Welcome to the Internet Relay Chat Network nick"));
767 QVERIFY(connection->isActive());
768 QVERIFY(connection->isConnected());
769 QCOMPARE(connection->status(), IrcConnection::Connected);
770
771 connection->close();
772 QVERIFY(!connection->isActive());
773 QVERIFY(!connection->isConnected());
774 QCOMPARE(connection->status(), IrcConnection::Closed);
775
776 // don't open when disabled
777 connection->setEnabled(false);
778 connection->open();
779 QVERIFY(!connection->isActive());
780 QVERIFY(!connection->isConnected());
781 QCOMPARE(connection->status(), IrcConnection::Closed);
782
783 // re-enable
784 connection->setEnabled(true);
785 connection->open();
786 QVERIFY(connection->isActive());
787 QVERIFY(!connection->isConnected());
788 QCOMPARE(connection->status(), IrcConnection::Connecting);
789
790 QVERIFY(waitForOpened());
791
792 protocol->written.clear();
793 connection->network()->requestCapability("identify-msg");
794 QVERIFY(protocol->written.contains("CAP REQ"));
795 QVERIFY(protocol->written.contains("identify-msg"));
796
797 protocol->written.clear();
798 connection->network()->requestCapabilities(QStringList() << "sasl" << "communi");
799 QVERIFY(protocol->written.contains("CAP REQ"));
800 QVERIFY(protocol->written.contains("sasl"));
801 QVERIFY(protocol->written.contains("communi"));
802
803 QVERIFY(waitForWritten(":irc.ser.ver 001 nick :Welcome to the Internet Relay Chat Network nick"));
804 QVERIFY(connection->isActive());
805 QVERIFY(connection->isConnected());
806 QCOMPARE(connection->status(), IrcConnection::Connected);
807
808 protocol->written.clear();
809 connection->setNickName("communi");
810 QVERIFY(protocol->written.contains("NICK"));
811 QVERIFY(protocol->written.contains("communi"));
812
813 protocol->written.clear();
814 connection->quit();
815 QVERIFY(protocol->written.contains("QUIT"));
816
817 connection->close();
818 QVERIFY(!connection->isActive());
819 QVERIFY(!connection->isConnected());
820 QCOMPARE(connection->status(), IrcConnection::Closed);
821 }
822
823 class NickChanger : public QObject
824 {
825 Q_OBJECT
826
827 public:
NickChanger(IrcConnection * connection)828 NickChanger(IrcConnection* connection) : QObject(connection)
829 {
830 connect(connection, SIGNAL(nickNameReserved(QString*)), SLOT(onNickNameReserved(QString*)));
831 }
832
833 QString setAlternate;
834 QString passedAlternate;
835
836 public slots:
onNickNameReserved(QString * alternate)837 void onNickNameReserved(QString* alternate)
838 {
839 Q_ASSERT(alternate);
840 passedAlternate = *alternate;
841 *alternate = setAlternate;
842 }
843 };
844
Q_DECLARE_METATYPE(QString *)845 Q_DECLARE_METATYPE(QString*)
846 void tst_IrcConnection::testMessages()
847 {
848 Irc::registerMetaTypes();
849 qRegisterMetaType<QString*>();
850
851 QSignalSpy messageSpy(connection, SIGNAL(messageReceived(IrcMessage*)));
852 QSignalSpy accountMessageSpy(connection, SIGNAL(accountMessageReceived(IrcAccountMessage*)));
853 QSignalSpy awayMessageSpy(connection, SIGNAL(awayMessageReceived(IrcAwayMessage*)));
854 QSignalSpy batchMessageSpy(connection, SIGNAL(batchMessageReceived(IrcBatchMessage*)));
855 QSignalSpy capabilityMessageSpy(connection, SIGNAL(capabilityMessageReceived(IrcCapabilityMessage*)));
856 QSignalSpy errorMessageSpy(connection, SIGNAL(errorMessageReceived(IrcErrorMessage*)));
857 QSignalSpy hostChangeMessageSpy(connection, SIGNAL(hostChangeMessageReceived(IrcHostChangeMessage*)));
858 QSignalSpy inviteMessageSpy(connection, SIGNAL(inviteMessageReceived(IrcInviteMessage*)));
859 QSignalSpy joinMessageSpy(connection, SIGNAL(joinMessageReceived(IrcJoinMessage*)));
860 QSignalSpy kickMessageSpy(connection, SIGNAL(kickMessageReceived(IrcKickMessage*)));
861 QSignalSpy modeMessageSpy(connection, SIGNAL(modeMessageReceived(IrcModeMessage*)));
862 QSignalSpy namesMessageSpy(connection, SIGNAL(namesMessageReceived(IrcNamesMessage*)));
863 QSignalSpy nickMessageSpy(connection, SIGNAL(nickMessageReceived(IrcNickMessage*)));
864 QSignalSpy noticeMessageSpy(connection, SIGNAL(noticeMessageReceived(IrcNoticeMessage*)));
865 QSignalSpy numericMessageSpy(connection, SIGNAL(numericMessageReceived(IrcNumericMessage*)));
866 QSignalSpy motdMessageSpy(connection, SIGNAL(motdMessageReceived(IrcMotdMessage*)));
867 QSignalSpy partMessageSpy(connection, SIGNAL(partMessageReceived(IrcPartMessage*)));
868 QSignalSpy pingMessageSpy(connection, SIGNAL(pingMessageReceived(IrcPingMessage*)));
869 QSignalSpy pongMessageSpy(connection, SIGNAL(pongMessageReceived(IrcPongMessage*)));
870 QSignalSpy privateMessageSpy(connection, SIGNAL(privateMessageReceived(IrcPrivateMessage*)));
871 QSignalSpy quitMessageSpy(connection, SIGNAL(quitMessageReceived(IrcQuitMessage*)));
872 QSignalSpy topicMessageSpy(connection, SIGNAL(topicMessageReceived(IrcTopicMessage*)));
873 QSignalSpy whoisMessageSpy(connection, SIGNAL(whoisMessageReceived(IrcWhoisMessage*)));
874 QSignalSpy whowasMessageSpy(connection, SIGNAL(whowasMessageReceived(IrcWhowasMessage*)));
875 QSignalSpy whoReplyMessageSpy(connection, SIGNAL(whoReplyMessageReceived(IrcWhoReplyMessage*)));
876
877 QVERIFY(messageSpy.isValid());
878 QVERIFY(accountMessageSpy.isValid());
879 QVERIFY(awayMessageSpy.isValid());
880 QVERIFY(batchMessageSpy.isValid());
881 QVERIFY(capabilityMessageSpy.isValid());
882 QVERIFY(errorMessageSpy.isValid());
883 QVERIFY(hostChangeMessageSpy.isValid());
884 QVERIFY(inviteMessageSpy.isValid());
885 QVERIFY(joinMessageSpy.isValid());
886 QVERIFY(kickMessageSpy.isValid());
887 QVERIFY(modeMessageSpy.isValid());
888 QVERIFY(namesMessageSpy.isValid());
889 QVERIFY(nickMessageSpy.isValid());
890 QVERIFY(noticeMessageSpy.isValid());
891 QVERIFY(numericMessageSpy.isValid());
892 QVERIFY(motdMessageSpy.isValid());
893 QVERIFY(partMessageSpy.isValid());
894 QVERIFY(pingMessageSpy.isValid());
895 QVERIFY(pongMessageSpy.isValid());
896 QVERIFY(privateMessageSpy.isValid());
897 QVERIFY(quitMessageSpy.isValid());
898 QVERIFY(topicMessageSpy.isValid());
899 QVERIFY(whoisMessageSpy.isValid());
900 QVERIFY(whowasMessageSpy.isValid());
901 QVERIFY(whoReplyMessageSpy.isValid());
902
903 int messageCount = 0;
904 int numericMessageCount = 0;
905
906 connection->open();
907 QVERIFY(waitForOpened());
908
909 QVERIFY(waitForWritten(":moorcock.freenode.net CAP * LS :account-notify extended-join identify-msg multi-prefix sasl"));
910 QCOMPARE(messageSpy.count(), ++messageCount);
911 QCOMPARE(capabilityMessageSpy.count(), 1);
912
913 QVERIFY(waitForWritten(":moorcock.freenode.net 001 communi :Welcome to the freenode Internet Relay Chat Network communi"));
914 QCOMPARE(messageSpy.count(), ++messageCount);
915 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
916
917 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server"));
918 QCOMPARE(messageSpy.count(), ++messageCount);
919 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
920
921 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server"));
922 QCOMPARE(messageSpy.count(), ++messageCount);
923 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
924
925 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"));
926 QCOMPARE(messageSpy.count(), ++messageCount);
927 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
928
929 QVERIFY(waitForWritten(":moorcock.freenode.net 375 communi :- moorcock.freenode.net Message of the Day -"));
930 QCOMPARE(messageSpy.count(), ++messageCount);
931 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
932
933 QVERIFY(waitForWritten(":moorcock.freenode.net 372 communi :- Welcome to moorcock.freenode.net in ..."));
934 QCOMPARE(messageSpy.count(), ++messageCount);
935 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
936
937 QVERIFY(waitForWritten(":moorcock.freenode.net 376 communi :End of /MOTD command."));
938 messageCount += 2; // RPL_ENDOFMOTD + IrcMotdMessage
939 QCOMPARE(messageSpy.count(), messageCount);
940 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
941 QCOMPARE(motdMessageSpy.count(), 1);
942
943 QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #freenode"));
944 QCOMPARE(messageSpy.count(), ++messageCount);
945 QCOMPARE(joinMessageSpy.count(), 1);
946
947 QVERIFY(waitForWritten(":moorcock.freenode.net 332 communi #freenode :Welcome to #freenode | Staff are voiced; some may also be on /stats p -- feel free to /msg us at any time | FAQ: http://freenode.net/faq.shtml | Unwelcome queries? Use /mode your_nick +R to block them. | Channel guidelines: http://freenode.net/poundfreenode.shtml | Blog: http://blog.freenode.net | Please don't comment on spam/trolls."));
948 messageCount += 2; // RPL_TOPIC & IrcTopicMessage
949 QCOMPARE(messageSpy.count(), messageCount);
950 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
951 QCOMPARE(topicMessageSpy.count(), 1);
952
953 QVERIFY(waitForWritten(":moorcock.freenode.net 333 communi #freenode erry 1379357591"));
954 QCOMPARE(messageSpy.count(), ++messageCount);
955 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
956
957 QVERIFY(waitForWritten(":moorcock.freenode.net 353 communi = #freenode :communi straterra absk007 pefn xlys Gromit TooCool Sambler gat0rs KarneAsada danis_963 Kiryx chrismeller deefloo black_male sxlnxdx bjork Kinny phobos_anomaly T13|sleeps JuxTApose Kolega2357 rorx techhelper1 hermatize Azimi iqualfragile fwilson skasturi mwallacesd mayday Guest76549 mcjohansen MangaKaDenza ARISTIDES ketas `- claptor ylluminate Cooky Brand3n cheater_1 Kirito digitaloktay Will| Iarfen abrotman smurfy Inaunt +mist Karol RougeR_"));
958 QCOMPARE(messageSpy.count(), ++messageCount);
959 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
960
961 QVERIFY(waitForWritten(":moorcock.freenode.net 353 communi = #freenode :publickeating An_Ony_Moose michagogo Guest915` davidfg4 Ragnor s1lent_1 keee GingerGeek[Away] hibari derp S_T_A_N anonymuse asantoni road|runner LLckfan neoian2 aviancarrier nipples danieldaniel Pyrus Bry8Star shadowm_desktop furtardo rdymac TTSDA seaworthy Chiyo yscc Zombiebaron redpill f4cl3y Boohbah applebloom zorael kameloso^ Zetetic XAMPP wheels_up Cuppy-Cake mindlessjohnny Kymru mquin_ Rodja babilen kirin` David Affix jshyeung_ DarkAceZ karakedi"));
962 QCOMPARE(messageSpy.count(), ++messageCount);
963 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
964
965 QVERIFY(waitForWritten(":moorcock.freenode.net 366 communi #freenode :End of /NAMES list."));
966 messageCount += 2; // RPL_ENDOFNAMES & IrcNamesMessage
967 QCOMPARE(messageSpy.count(), messageCount);
968 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
969 QCOMPARE(namesMessageSpy.count(), 1);
970
971 QVERIFY(waitForWritten(":ChanServ!ChanServ@services. NOTICE communi :[#freenode] Welcome to #freenode. All network staff are voiced in here, but may not always be around - type /stats p to get a list of on call staff. Others may be hiding so do feel free to ping and /msg us at will! Also please read the channel guidelines at http://freenode.net/poundfreenode.shtml - thanks."));
972 QCOMPARE(messageSpy.count(), ++messageCount);
973 QCOMPARE(noticeMessageSpy.count(), 1);
974
975 QVERIFY(waitForWritten(":services. 328 communi #freenode :http://freenode.net/"));
976 QCOMPARE(messageSpy.count(), ++messageCount);
977 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
978
979 QVERIFY(waitForWritten("PING :moorcock.freenode.net"));
980 QCOMPARE(messageSpy.count(), ++messageCount);
981 QCOMPARE(pingMessageSpy.count(), 1);
982
983 QVERIFY(waitForWritten("PONG :moorcock.freenode.net"));
984 QCOMPARE(messageSpy.count(), ++messageCount);
985 QCOMPARE(pongMessageSpy.count(), 1);
986
987 QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi INVITE Communi84194 :#communi"));
988 QCOMPARE(messageSpy.count(), ++messageCount);
989 QCOMPARE(inviteMessageSpy.count(), 1);
990
991 QVERIFY(waitForWritten(":moorcock.freenode.net 341 jpnurmi Communi84194 #communi"));
992 messageCount += 2; // RPL_INVITING + IrcInviteMessage
993 QCOMPARE(messageSpy.count(), messageCount);
994 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
995 QCOMPARE(inviteMessageSpy.count(), 2);
996 QVERIFY(inviteMessageSpy.last().last().value<IrcInviteMessage*>()->property("reply").toBool());
997
998 QVERIFY(waitForWritten(":Communi84194!ident@host NICK :communi"));
999 QCOMPARE(messageSpy.count(), ++messageCount);
1000 QCOMPARE(nickMessageSpy.count(), 1);
1001
1002 // own nick name changes
1003 QSignalSpy nickNameChangedSpy(connection, SIGNAL(nickNameChanged(QString)));
1004 QVERIFY(nickNameChangedSpy.isValid());
1005 QVERIFY(waitForWritten(":communi!user@host NICK :own"));
1006 QCOMPARE(messageSpy.count(), ++messageCount);
1007 QCOMPARE(nickMessageSpy.count(), 2);
1008 QCOMPARE(connection->nickName(), QString("own"));
1009 QCOMPARE(nickNameChangedSpy.count(), 1);
1010 QCOMPARE(nickNameChangedSpy.last().at(0).toString(), QString("own"));
1011
1012 // nick in use
1013 QString prevNick = connection->nickName();
1014 NickChanger changer(connection);
1015 changer.setAlternate = "communi_";
1016 QSignalSpy nickNameReservedSpy(connection, SIGNAL(nickNameReserved(QString*)));
1017 QVERIFY(nickNameReservedSpy.isValid());
1018 QVERIFY(waitForWritten(":moorcock.freenode.net 433 * communi :Nickname is already in use."));
1019 QCOMPARE(messageSpy.count(), ++messageCount);
1020 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
1021 QCOMPARE(nickNameReservedSpy.count(), 1);
1022 QCOMPARE(changer.passedAlternate, prevNick);
1023
1024 QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi MODE #communi +v communi"));
1025 QCOMPARE(messageSpy.count(), ++messageCount);
1026 QCOMPARE(modeMessageSpy.count(), 1);
1027
1028 QVERIFY(waitForWritten(":moorcock.freenode.net 324 communi #communi +ms"));
1029 messageCount += 2; // RPL_CHANNELMODEIS + IrcModeMessage
1030 QCOMPARE(messageSpy.count(), messageCount);
1031 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
1032 QCOMPARE(modeMessageSpy.count(), 2);
1033
1034 QVERIFY(waitForWritten(":qtassistant!jpnurmi@qt/jpnurmi/bot/qtassistant PART #communi"));
1035 QCOMPARE(messageSpy.count(), ++messageCount);
1036 QCOMPARE(partMessageSpy.count(), 1);
1037
1038 QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi PRIVMSG #communi :hello"));
1039 QCOMPARE(messageSpy.count(), ++messageCount);
1040 QCOMPARE(privateMessageSpy.count(), 1);
1041
1042 QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi QUIT :Client Quit"));
1043 QCOMPARE(messageSpy.count(), ++messageCount);
1044 QCOMPARE(quitMessageSpy.count(), 1);
1045
1046 QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi KICK #communi communi"));
1047 QCOMPARE(messageSpy.count(), ++messageCount);
1048 QCOMPARE(kickMessageSpy.count(), 1);
1049
1050 QVERIFY(waitForWritten("ERROR :just testing..."));
1051 QCOMPARE(messageSpy.count(), ++messageCount);
1052 QCOMPARE(errorMessageSpy.count(), 1);
1053
1054 QVERIFY(waitForWritten(":hobana.freenode.net 352 communi #communi ChanServ services. services. ChanServ H@ :0 Channel Services" ));
1055 messageCount += 2; // RPL_WHOREPLY + IrcWhoReplyMessage
1056 QCOMPARE(messageSpy.count(), messageCount);
1057 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
1058 QCOMPARE(whoReplyMessageSpy.count(), 1);
1059
1060 QVERIFY(waitForWritten(":hobana.freenode.net 315 communi #communi :End of /WHO list."));
1061 QCOMPARE(messageSpy.count(), ++messageCount);
1062 QCOMPARE(numericMessageSpy.count(), ++numericMessageCount);
1063 QCOMPARE(whoReplyMessageSpy.count(), 1);
1064
1065 QVERIFY(waitForWritten(":nick!user@host CHGHOST newuser newhost"));
1066 QCOMPARE(messageSpy.count(), ++messageCount);
1067 QCOMPARE(hostChangeMessageSpy.count(), 1);
1068
1069 QVERIFY(waitForWritten(":nick!user@host AWAY :reason"));
1070 QCOMPARE(messageSpy.count(), ++messageCount);
1071 QCOMPARE(awayMessageSpy.count(), 1);
1072
1073 QVERIFY(waitForWritten(":nick!user@host ACCOUNT account"));
1074 QCOMPARE(messageSpy.count(), ++messageCount);
1075 QCOMPARE(accountMessageSpy.count(), 1);
1076
1077 QVERIFY(waitForWritten(":asimov.freenode.net 311 jpnurmi qtassistant jpnurmi qt/jpnurmi/bot/qtassistant * :http://doc.qt.io/qt-5"));
1078 QVERIFY(waitForWritten(":asimov.freenode.net 318 jpnurmi qtassistant :End of /WHOIS list."));
1079 messageCount += 3; // RPL_WHOISUSER + RPL_ENDOFWHOIS + IrcWhoisMessage
1080 numericMessageCount += 2; // RPL_WHOISUSER + RPL_ENDOFWHOIS
1081 QCOMPARE(messageSpy.count(), messageCount);
1082 QCOMPARE(numericMessageSpy.count(), numericMessageCount);
1083 QCOMPARE(whoisMessageSpy.count(), 1);
1084
1085 QVERIFY(waitForWritten(":asimov.freenode.net 314 jpnurmi jirssi ~jpnurmi 88.95.51.136 * :J-P Nurmi"));
1086 QVERIFY(waitForWritten(":asimov.freenode.net 369 jpnurmi jirssi :End of WHOWAS"));
1087 messageCount += 3; // RPL_WHOWASUSER + RPL_ENDOFWHOWAS + IrcWhowasMessage
1088 numericMessageCount += 2; // RPL_WHOWASUSER + RPL_ENDOFWHOWAS
1089 QCOMPARE(messageSpy.count(), messageCount);
1090 QCOMPARE(numericMessageSpy.count(), numericMessageCount);
1091 QCOMPARE(whowasMessageSpy.count(), 1);
1092 }
1093
1094 class MsgFilter : public QObject, public IrcMessageFilter
1095 {
1096 Q_OBJECT
1097 Q_INTERFACES(IrcMessageFilter)
1098
1099 public:
MsgFilter()1100 MsgFilter() : flags(IrcMessage::None)
1101 {
1102 }
1103
reset(const QByteArray & p="",int c=0)1104 void reset(const QByteArray& p = "", int c = 0)
1105 {
1106 count = c;
1107 properties = p;
1108 flags = IrcMessage::None;
1109 type = IrcMessage::Unknown;
1110 values.clear();
1111 }
1112
messageFilter(IrcMessage * message)1113 bool messageFilter(IrcMessage* message) override
1114 {
1115 ++count;
1116 type = message->type();
1117 flags = message->flags();
1118 foreach (const QByteArray& property, properties.split(',')) {
1119 QVariant value = message->property(property);
1120 if (!value.isNull())
1121 values[property] = value;
1122 }
1123 return false;
1124 }
1125
1126 public:
1127 int count = 0;
1128 QVariantMap values;
1129 QByteArray properties;
1130 IrcMessage::Type type = IrcMessage::Unknown;
1131 IrcMessage::Flags flags;
1132 };
1133
testMessageFlags()1134 void tst_IrcConnection::testMessageFlags()
1135 {
1136 connection->open();
1137 QVERIFY(waitForOpened());
1138
1139 int count = 0;
1140 MsgFilter filter;
1141 connection->installMessageFilter(&filter);
1142
1143 QVERIFY(waitForWritten(":server CAP * LS :identify-msg"));
1144 QCOMPARE(filter.count, ++count);
1145 QCOMPARE(filter.type, IrcMessage::Capability);
1146 QCOMPARE(filter.flags, IrcMessage::None);
1147
1148 QVERIFY(waitForWritten(":server CAP communi ACK :identify-msg"));
1149 QCOMPARE(filter.count, ++count);
1150 QCOMPARE(filter.type, IrcMessage::Capability);
1151 QCOMPARE(filter.flags, IrcMessage::None);
1152
1153 QVERIFY(waitForWritten(":server 001 communi :Welcome..."));
1154 QCOMPARE(filter.count, ++count);
1155 QCOMPARE(filter.type, IrcMessage::Numeric);
1156 QCOMPARE(filter.flags, IrcMessage::None);
1157
1158 QVERIFY(waitForWritten(":server 005 communi CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=fake KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server"));
1159 QCOMPARE(filter.count, ++count);
1160 QCOMPARE(filter.type, IrcMessage::Numeric);
1161 QCOMPARE(filter.flags, IrcMessage::None);
1162
1163 QVERIFY(waitForWritten(":server 005 communi CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server"));
1164 QCOMPARE(filter.count, ++count);
1165 QCOMPARE(filter.type, IrcMessage::Numeric);
1166 QCOMPARE(filter.flags, IrcMessage::None);
1167
1168 QVERIFY(waitForWritten(":server 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"));
1169 QCOMPARE(filter.count, ++count);
1170 QCOMPARE(filter.type, IrcMessage::Numeric);
1171 QCOMPARE(filter.flags, IrcMessage::None);
1172
1173 filter.reset("content", count);
1174 QVERIFY(waitForWritten(":communi!ident@host PRIVMSG #communi :hi all"));
1175 QCOMPARE(filter.count, ++count);
1176 QCOMPARE(filter.type, IrcMessage::Private);
1177 QCOMPARE(filter.flags, IrcMessage::Own);
1178 QCOMPARE(filter.values.value("content").toString(), QString("hi all"));
1179
1180 filter.reset("content", count);
1181 QVERIFY(waitForWritten(":jpnurmi!ident@host PRIVMSG #communi :hello there, communi"));
1182 QCOMPARE(filter.count, ++count);
1183 QCOMPARE(filter.type, IrcMessage::Private);
1184 QCOMPARE(filter.values.value("content").toString(), QString("hello there, communi"));
1185
1186 filter.reset("content", count);
1187 QVERIFY(waitForWritten(":Guest1234!ident@host PRIVMSG #communi :hi communi"));
1188 QCOMPARE(filter.count, ++count);
1189 QCOMPARE(filter.type, IrcMessage::Private);
1190 QCOMPARE(filter.values.value("content").toString(), QString("hi communi"));
1191
1192 filter.reset("content", count);
1193 QVERIFY(waitForWritten(":communi!ident@host NOTICE #communi :hi all"));
1194 QCOMPARE(filter.count, ++count);
1195 QCOMPARE(filter.type, IrcMessage::Notice);
1196 QCOMPARE(filter.flags, IrcMessage::Own);
1197 QCOMPARE(filter.values.value("content").toString(), QString("hi all"));
1198
1199 filter.reset("content", count);
1200 QVERIFY(waitForWritten(":jpnurmi!ident@host NOTICE #communi :hello there, communi"));
1201 QCOMPARE(filter.count, ++count);
1202 QCOMPARE(filter.type, IrcMessage::Notice);
1203 QCOMPARE(filter.values.value("content").toString(), QString("hello there, communi"));
1204
1205 filter.reset("content", count);
1206 QVERIFY(waitForWritten(":Guest1234!ident@host NOTICE #communi :hi communi"));
1207 QCOMPARE(filter.count, ++count);
1208 QCOMPARE(filter.type, IrcMessage::Notice);
1209 QCOMPARE(filter.values.value("content").toString(), QString("hi communi"));
1210 }
1211
testStatusPrefixes()1212 void tst_IrcConnection::testStatusPrefixes()
1213 {
1214 connection->open();
1215 QVERIFY(waitForOpened());
1216
1217 int count = 0;
1218 MsgFilter filter;
1219 connection->installMessageFilter(&filter);
1220
1221 QVERIFY(waitForWritten(":server 001 communi :Welcome..."));
1222 QCOMPARE(filter.count, ++count);
1223 QCOMPARE(filter.type, IrcMessage::Numeric);
1224 QCOMPARE(filter.flags, IrcMessage::None);
1225
1226 QVERIFY(waitForWritten(":server 005 communi STATUSMSG=@+"));
1227 QCOMPARE(filter.count, ++count);
1228 QCOMPARE(filter.type, IrcMessage::Numeric);
1229 QCOMPARE(filter.flags, IrcMessage::None);
1230
1231 QVERIFY(waitForWritten(":server 375 communi :MOTD"));
1232 QVERIFY(waitForWritten(":server 376 communi :End of /MOTD command."));
1233 count += 3; // RPL_MOTDSTART, RPL_ENDOFMOTD, IrcMotdMessage
1234 QCOMPARE(filter.count, count);
1235 QCOMPARE(filter.type, IrcMessage::Motd);
1236 QCOMPARE(filter.flags, IrcMessage::None);
1237
1238 filter.reset("target,statusPrefix,content", count);
1239 QVERIFY(waitForWritten(":Guest1234!ident@host PRIVMSG +#communi :hi communi"));
1240 QCOMPARE(filter.count, ++count);
1241 QCOMPARE(filter.type, IrcMessage::Private);
1242 QCOMPARE(filter.values.value("target").toString(), QString("#communi"));
1243 QCOMPARE(filter.values.value("statusPrefix").toString(), QString("+"));
1244 QCOMPARE(filter.values.value("content").toString(), QString("hi communi"));
1245
1246 filter.reset("target,statusPrefix,content", count);
1247 QVERIFY(waitForWritten(":Guest1234!ident@host NOTICE +#communi :hi communi"));
1248 QCOMPARE(filter.count, ++count);
1249 QCOMPARE(filter.type, IrcMessage::Notice);
1250 QCOMPARE(filter.values.value("target").toString(), QString("#communi"));
1251 QCOMPARE(filter.values.value("statusPrefix").toString(), QString("+"));
1252 QCOMPARE(filter.values.value("content").toString(), QString("hi communi"));
1253 }
1254
testMessageComposer()1255 void tst_IrcConnection::testMessageComposer()
1256 {
1257 connection->open();
1258 QVERIFY(waitForOpened());
1259
1260 MsgFilter filter;
1261 connection->installMessageFilter(&filter);
1262
1263 QVERIFY(waitForWritten(":my.irc.ser.ver 001 communi :Welcome..."));
1264 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=fake KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server"));
1265 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server"));
1266 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"));
1267
1268 filter.reset("mask,ident,host,server,nick,away,servOp,realName,composed");
1269 QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi"));
1270 QCOMPARE(filter.count, 2); // RPL_WHOREPLY + IrcWhoReply
1271 QCOMPARE(filter.values.value("mask").toString(), QString("#communi"));
1272 QCOMPARE(filter.values.value("ident").toString(), QString("~jpnurmi"));
1273 QCOMPARE(filter.values.value("host").toString(), QString("qt/jpnurmi"));
1274 QCOMPARE(filter.values.value("server").toString(), QString("his.irc.ser.ver"));
1275 QCOMPARE(filter.values.value("nick").toString(), QString("jpnurmi"));
1276 QCOMPARE(filter.values.value("away").toBool(), true);
1277 QCOMPARE(filter.values.value("servOp").toBool(), true);
1278 QCOMPARE(filter.values.value("realName").toString(), QString("J-P Nurmi"));
1279 QCOMPARE(filter.values.value("composed").toBool(), true);
1280
1281 filter.reset("realName");
1282 QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0"));
1283 QCOMPARE(filter.values.value("realName").toString(), QString());
1284
1285 filter.reset("content,nick,reply,away,composed");
1286 QVERIFY(waitForWritten(":my.irc.ser.ver 301 communi nick :gone far away"));
1287 QCOMPARE(filter.values.value("content").toString(), QString("gone far away"));
1288 QCOMPARE(filter.values.value("nick").toString(), QString("nick"));
1289 QVERIFY(filter.values.value("reply").toBool());
1290 QVERIFY(filter.values.value("away").toBool());
1291 QVERIFY(filter.values.value("composed").toBool());
1292 QCOMPARE(filter.type, IrcMessage::Away);
1293
1294 filter.reset("content,nick,reply,away,composed");
1295 QVERIFY(waitForWritten(":my.irc.ser.ver 301 communi nick"));
1296 QCOMPARE(filter.values.value("nick").toString(), QString("nick"));
1297 QCOMPARE(filter.values.value("content").toString(), QString());
1298 QVERIFY(filter.values.value("reply").toBool());
1299 QVERIFY(filter.values.value("away").toBool());
1300 QVERIFY(filter.values.value("composed").toBool());
1301 QCOMPARE(filter.type, IrcMessage::Away);
1302
1303 filter.reset("content,nick,reply,away,composed");
1304 QVERIFY(waitForWritten(":my.irc.ser.ver 305 communi :You are no longer marked as being away"));
1305 QCOMPARE(filter.values.value("nick").toString(), QString("communi"));
1306 QCOMPARE(filter.values.value("content").toString(), QString("You are no longer marked as being away"));
1307 QVERIFY(filter.values.value("reply").toBool());
1308 QVERIFY(!filter.values.value("away").toBool());
1309 QVERIFY(filter.values.value("composed").toBool());
1310 QCOMPARE(filter.type, IrcMessage::Away);
1311
1312 filter.reset("content,nick,reply,away,composed");
1313 QVERIFY(waitForWritten(":my.irc.ser.ver 306 communi :You have been marked as being away"));
1314 QCOMPARE(filter.values.value("nick").toString(), QString("communi"));
1315 QCOMPARE(filter.values.value("content").toString(), QString("You have been marked as being away"));
1316 QVERIFY(filter.values.value("reply").toBool());
1317 QVERIFY(filter.values.value("away").toBool());
1318 QVERIFY(filter.values.value("composed").toBool());
1319 QCOMPARE(filter.type, IrcMessage::Away);
1320
1321 filter.reset("realName,server,info,account,address,since,idle,secure,from,channels,awayReason,valid");
1322 QVERIFY(waitForWritten(":asimov.freenode.net 311 jipsu qtassistant jpnurmi qt/jpnurmi/bot/qtassistant * :http://doc.qt.io/qt-5"));
1323 QVERIFY(waitForWritten(":asimov.freenode.net 319 jipsu qtassistant :+#jpnurmi"));
1324 QVERIFY(waitForWritten(":asimov.freenode.net 312 jipsu qtassistant leguin.freenode.net :Umeå, SE, EU"));
1325 QVERIFY(waitForWritten(":asimov.freenode.net 671 jipsu qtassistant :is using a secure connection"));
1326 QVERIFY(waitForWritten(":asimov.freenode.net 301 jipsu qtassistant :gone fishing"));
1327 QVERIFY(waitForWritten(":asimov.freenode.net 330 jipsu qtassistant qtaccountant :is logged in as"));
1328 QVERIFY(waitForWritten(":asimov.freenode.net 378 jipsu qtassistant :is connecting from *@88.95.51.136 88.95.51.136"));
1329 QVERIFY(waitForWritten(":asimov.freenode.net 317 jipsu qtassistant 15 1440706032 :seconds idle, signon time"));
1330 QVERIFY(waitForWritten(":asimov.freenode.net 318 jipsu qtassistant :End of /WHOIS list."));
1331 QCOMPARE(filter.values.value("realName").toString(), QString("http://doc.qt.io/qt-5"));
1332 QCOMPARE(filter.values.value("server").toString(), QString("leguin.freenode.net"));
1333 QCOMPARE(filter.values.value("info").toString(), QString::fromUtf8("Umeå, SE, EU"));
1334 QCOMPARE(filter.values.value("account").toString(), QString("qtaccountant"));
1335 QEXPECT_FAIL("", "RPL_WHOISHOST :is connecting from *@88.95.51.136 88.95.51.136", Continue);
1336 QCOMPARE(filter.values.value("address").toString(), QString("88.95.51.136"));
1337 QCOMPARE(filter.values.value("since").toDateTime(), QDateTime::fromTime_t(1440706032));
1338 QCOMPARE(filter.values.value("idle").toInt(), 15);
1339 QCOMPARE(filter.values.value("secure").toBool(), true);
1340 QCOMPARE(filter.values.value("channels").toStringList(), QStringList() << "+#jpnurmi");
1341 QCOMPARE(filter.values.value("awayReason").toString(), QString("gone fishing"));
1342 QVERIFY(filter.values.value("valid").toBool());
1343 QCOMPARE(filter.type, IrcMessage::Whois);
1344
1345 filter.reset("realName,server,info,account,valid");
1346 QVERIFY(waitForWritten(":asimov.freenode.net 314 jipsu jirssi ~jpnurmi 88.95.51.136 * :J-P Nurmi"));
1347 QVERIFY(waitForWritten(":asimov.freenode.net 312 jipsu jirssi wolfe.freenode.net :Wed Aug 26 22:11:42 2015"));
1348 QVERIFY(waitForWritten(":asimov.freenode.net 330 jipsu jirssi jaccount :is logged in as"));
1349 QVERIFY(waitForWritten(":asimov.freenode.net 369 jipsu jirssi :End of WHOWAS"));
1350 QCOMPARE(filter.values.value("realName").toString(), QString("J-P Nurmi"));
1351 QCOMPARE(filter.values.value("server").toString(), QString("wolfe.freenode.net"));
1352 QCOMPARE(filter.values.value("info").toString(), QString("Wed Aug 26 22:11:42 2015"));
1353 QCOMPARE(filter.values.value("account").toString(), QString("jaccount"));
1354 QVERIFY(filter.values.value("valid").toBool());
1355 QCOMPARE(filter.type, IrcMessage::Whowas);
1356 }
1357
testMessageComposerCrash_data()1358 void tst_IrcConnection::testMessageComposerCrash_data()
1359 {
1360 QTest::addColumn<QByteArray>("data");
1361
1362 // unexpected replies - don't crash
1363 QList<Irc::Code> codes;
1364 codes << Irc::RPL_WHOISSERVER << Irc::RPL_WHOISACCOUNT << Irc::RPL_WHOISHOST << Irc::RPL_WHOISIDLE << Irc::RPL_WHOISSECURE << Irc::RPL_WHOISCHANNELS;
1365 foreach (Irc::Code code, codes)
1366 QTest::newRow(qPrintable(Irc::codeToString(code))) << QByteArray(":server ") + QByteArray::number(code);
1367 }
1368
testMessageComposerCrash()1369 void tst_IrcConnection::testMessageComposerCrash()
1370 {
1371 QFETCH(QByteArray, data);
1372
1373 connection->open();
1374 QVERIFY(waitForOpened());
1375 QVERIFY(waitForWritten(data));
1376 }
1377
testBatch()1378 void tst_IrcConnection::testBatch()
1379 {
1380 connection->open();
1381 QVERIFY(waitForOpened());
1382
1383 QVERIFY(waitForWritten(":my.irc.ser.ver 001 communi :Welcome..."));
1384 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=fake KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server"));
1385 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server"));
1386 QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"));
1387
1388 QSignalSpy messageSpy(connection, SIGNAL(messageReceived(IrcMessage*)));
1389 QSignalSpy batchMessageSpy(connection, SIGNAL(batchMessageReceived(IrcBatchMessage*)));
1390 QVERIFY(messageSpy.isValid());
1391 QVERIFY(batchMessageSpy.isValid());
1392
1393 QVERIFY(waitForWritten(":irc.host BATCH +yXNAbvnRHTRBv netsplit irc.hub other.host"));
1394 QVERIFY(waitForWritten("@batch=yXNAbvnRHTRBv :aji!a@a QUIT :irc.hub other.host"));
1395 QVERIFY(waitForWritten("@batch=yXNAbvnRHTRBv :nenolod!a@a QUIT :irc.hub other.host"));
1396 QVERIFY(waitForWritten(":nick!user@host PRIVMSG #channel :This is not in batch, so processed immediately"));
1397 QVERIFY(waitForWritten("@batch=yXNAbvnRHTRBv :jilles!a@a QUIT :irc.hub other.host"));
1398 QVERIFY(waitForWritten(":irc.host BATCH -yXNAbvnRHTRBv"));
1399
1400 QCOMPARE(messageSpy.count(), 2); // BATCH + NICK
1401 QCOMPARE(batchMessageSpy.count(), 1);
1402
1403 IrcBatchMessage* batch = batchMessageSpy.last().last().value<IrcBatchMessage*>();
1404 QVERIFY(batch);
1405 QVERIFY(batch->isValid());
1406 QCOMPARE(batch->tag(), QString("yXNAbvnRHTRBv"));
1407 QCOMPARE(batch->batch(), QString("netsplit"));
1408 QCOMPARE(batch->messages().count(), 3);
1409
1410 IrcQuitMessage* q1 = qobject_cast<IrcQuitMessage*>(batch->messages().at(0));
1411 QVERIFY(q1);
1412 QCOMPARE(q1->nick(), QString("aji"));
1413 QCOMPARE(q1->reason(), QString("irc.hub other.host"));
1414
1415 IrcQuitMessage* q2 = qobject_cast<IrcQuitMessage*>(batch->messages().at(1));
1416 QVERIFY(q2);
1417 QCOMPARE(q2->nick(), QString("nenolod"));
1418 QCOMPARE(q2->reason(), QString("irc.hub other.host"));
1419
1420 IrcQuitMessage* q3 = qobject_cast<IrcQuitMessage*>(batch->messages().at(2));
1421 QVERIFY(q3);
1422 QCOMPARE(q3->nick(), QString("jilles"));
1423 QCOMPARE(q3->reason(), QString("irc.hub other.host"));
1424 }
1425
testServerTime()1426 void tst_IrcConnection::testServerTime()
1427 {
1428 connection->open();
1429 QVERIFY(waitForOpened());
1430
1431 QSignalSpy messageSpy(connection, SIGNAL(numericMessageReceived(IrcNumericMessage*)));
1432 QVERIFY(messageSpy.isValid());
1433
1434 QVERIFY(waitForWritten("@time=2011-10-19T16:40:51.620Z :my.irc.ser.ver 001 communi :Welcome..."));
1435
1436 QCOMPARE(messageSpy.count(), 1);
1437 IrcNumericMessage* message = messageSpy.last().last().value<IrcNumericMessage*>();
1438 QVERIFY(message);
1439 QVERIFY(message->isValid());
1440 QCOMPARE(message->timeStamp(), QDateTime(QDate(2011, 10, 19), QTime(16, 40, 51, 620), Qt::UTC));
1441 }
1442
testSendCommand()1443 void tst_IrcConnection::testSendCommand()
1444 {
1445 IrcConnection conn;
1446 QVERIFY(!conn.sendCommand(nullptr));
1447 QVERIFY(!conn.sendCommand(IrcCommand::createQuit()));
1448
1449 TestProtocol* protocol = new TestProtocol(connection);
1450 FriendlyConnection* friendly = static_cast<FriendlyConnection*>(connection.data());
1451 friendly->setProtocol(protocol);
1452 QCOMPARE(friendly->protocol(), protocol);
1453 QCOMPARE(protocol->connection(), connection.data());
1454
1455 connection->open();
1456 QVERIFY(waitForOpened());
1457
1458 QVERIFY(connection->sendCommand(IrcCommand::createQuit()));
1459 QVERIFY(!connection->sendCommand(nullptr));
1460 QVERIFY(protocol->written.contains("QUIT"));
1461 }
1462
testSendData()1463 void tst_IrcConnection::testSendData()
1464 {
1465 IrcConnection conn;
1466 QVERIFY(!conn.sendData("QUIT"));
1467
1468 TestProtocol* protocol = new TestProtocol(connection);
1469 FriendlyConnection* friendly = static_cast<FriendlyConnection*>(connection.data());
1470 friendly->setProtocol(protocol);
1471 QCOMPARE(friendly->protocol(), protocol);
1472 QCOMPARE(protocol->connection(), connection.data());
1473
1474 connection->open();
1475 QVERIFY(waitForOpened());
1476
1477 QVERIFY(connection->sendData("QUIT"));
1478 QVERIFY(protocol->written.contains("QUIT"));
1479 }
1480
1481 class TestFilter : public QObject, public IrcMessageFilter, public IrcCommandFilter
1482 {
1483 Q_OBJECT
1484 Q_INTERFACES(IrcMessageFilter IrcCommandFilter)
1485
1486 public:
clear()1487 void clear()
1488 {
1489 commitSuicide = false;
1490 messageFiltered = 0;
1491 commandFiltered = 0;
1492 messageFilterEnabled = false;
1493 commandFilterEnabled = false;
1494 }
1495
messageFilter(IrcMessage *)1496 bool messageFilter(IrcMessage*) override
1497 {
1498 ++messageFiltered;
1499 if (commitSuicide)
1500 delete this;
1501 return messageFilterEnabled;
1502 }
1503
commandFilter(IrcCommand *)1504 bool commandFilter(IrcCommand*) override
1505 {
1506 ++commandFiltered;
1507 if (commitSuicide)
1508 delete this;
1509 return commandFilterEnabled;
1510 }
1511
1512 bool commitSuicide;
1513 int messageFiltered;
1514 int commandFiltered;
1515 bool messageFilterEnabled;
1516 bool commandFilterEnabled;
1517 };
1518
testMessageFilter()1519 void tst_IrcConnection::testMessageFilter()
1520 {
1521 Irc::registerMetaTypes();
1522
1523 QSignalSpy messageSpy(connection, SIGNAL(messageReceived(IrcMessage*)));
1524 QVERIFY(messageSpy.isValid());
1525 int messageCount = 0;
1526
1527 TestFilter filter1;
1528 QScopedPointer<TestFilter> filter2(new TestFilter);
1529 QScopedPointer<TestFilter> filter3(new TestFilter);
1530
1531 filter1.clear(); filter2->clear(); filter3->clear();
1532
1533 connection->installMessageFilter(&filter1);
1534 connection->installMessageFilter(filter2.data());
1535 connection->installMessageFilter(filter3.data());
1536
1537 connection->open();
1538 QVERIFY(waitForOpened());
1539
1540 QVERIFY(waitForWritten(":moorcock.freenode.net 001 communi :Welcome to the freenode Internet Relay Chat Network communi"));
1541 QCOMPARE(filter1.messageFiltered, 1);
1542 QCOMPARE(filter2->messageFiltered, 1);
1543 QCOMPARE(filter3->messageFiltered, 1);
1544 QCOMPARE(messageSpy.count(), ++messageCount);
1545
1546 filter1.clear(); filter2->clear(); filter3->clear();
1547 filter3->messageFilterEnabled = true;
1548
1549 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server"));
1550 QCOMPARE(filter1.messageFiltered, 0);
1551 QCOMPARE(filter2->messageFiltered, 0);
1552 QCOMPARE(filter3->messageFiltered, 1);
1553 QCOMPARE(messageSpy.count(), messageCount);
1554
1555 filter1.clear(); filter2->clear(); filter3->clear();
1556 filter2->messageFilterEnabled = true;
1557
1558 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server"));
1559 QCOMPARE(filter1.messageFiltered, 0);
1560 QCOMPARE(filter2->messageFiltered, 1);
1561 QCOMPARE(filter3->messageFiltered, 1);
1562 QCOMPARE(messageSpy.count(), messageCount);
1563
1564 filter1.clear(); filter2->clear(); filter3->clear();
1565 filter1.messageFilterEnabled = true;
1566
1567 QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"));
1568 QCOMPARE(filter1.messageFiltered, 1);
1569 QCOMPARE(filter2->messageFiltered, 1);
1570 QCOMPARE(filter3->messageFiltered, 1);
1571 QCOMPARE(messageSpy.count(), messageCount);
1572
1573 filter1.clear(); filter2->clear(); filter3->clear();
1574
1575 QVERIFY(waitForWritten(":moorcock.freenode.net 375 communi :- moorcock.freenode.net Message of the Day -"));
1576 QCOMPARE(filter1.messageFiltered, 1);
1577 QCOMPARE(filter2->messageFiltered, 1);
1578 QCOMPARE(filter3->messageFiltered, 1);
1579 QCOMPARE(messageSpy.count(), ++messageCount);
1580
1581 // a deleted filter gets removed
1582 filter2.reset();
1583 filter1.clear(); filter3->clear();
1584
1585 QVERIFY(waitForWritten(":moorcock.freenode.net 372 communi :- Welcome to moorcock.freenode.net in ..."));
1586 QCOMPARE(filter1.messageFiltered, 1);
1587 QCOMPARE(filter3->messageFiltered, 1);
1588 QCOMPARE(messageSpy.count(), ++messageCount);
1589
1590 QVERIFY(waitForWritten(":moorcock.freenode.net 376 communi :End of /MOTD command."));
1591 messageCount += 2; // RPL_ENDOFMOTD + IrcMotdMessage
1592 QCOMPARE(messageSpy.count(), messageCount);
1593
1594 // double filters
1595 connection->installMessageFilter(&filter1);
1596 connection->installMessageFilter(filter3.data());
1597 filter1.clear(); filter3->clear();
1598
1599 QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #freenode"));
1600 QCOMPARE(filter1.messageFiltered, 2);
1601 QCOMPARE(filter3->messageFiltered, 2);
1602 QCOMPARE(messageSpy.count(), ++messageCount);
1603
1604 // remove & enable double filter
1605 filter1.clear(); filter3->clear();
1606 filter1.messageFilterEnabled = true;
1607 connection->removeMessageFilter(filter3.data());
1608
1609 QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #communi"));
1610 QCOMPARE(filter1.messageFiltered, 1);
1611 QCOMPARE(filter3->messageFiltered, 0);
1612 QCOMPARE(messageSpy.count(), messageCount);
1613
1614 // remove & delete
1615 filter3.reset();
1616 filter1.clear();
1617 connection->removeMessageFilter(&filter1);
1618
1619 QVERIFY(waitForWritten(":communi!~communi@hidd.en PART #communi"));
1620 QCOMPARE(filter1.messageFiltered, 0);
1621 QCOMPARE(messageSpy.count(), ++messageCount);
1622
1623 // commit a suicide & filter
1624 QPointer<TestFilter> suicidal1 = new TestFilter;
1625 connection->installMessageFilter(suicidal1);
1626 suicidal1->clear();
1627 suicidal1->messageFilterEnabled = true;
1628 suicidal1->commitSuicide = true;
1629
1630 QVERIFY(waitForWritten(":communi!~communi@hidd.en PART #freenode"));
1631 QCOMPARE(messageSpy.count(), messageCount);
1632 QVERIFY(!suicidal1);
1633
1634 // commit a suicide & don't filter
1635 QPointer<TestFilter> suicidal2 = new TestFilter;
1636 connection->installMessageFilter(suicidal2);
1637 suicidal2->clear();
1638 suicidal2->commitSuicide = true;
1639
1640 QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #qt"));
1641 QVERIFY(!suicidal2);
1642 }
1643
testCommandFilter()1644 void tst_IrcConnection::testCommandFilter()
1645 {
1646 TestProtocol* protocol = new TestProtocol(connection);
1647 FriendlyConnection* friendly = static_cast<FriendlyConnection*>(connection.data());
1648 friendly->setProtocol(protocol);
1649 QCOMPARE(friendly->protocol(), protocol);
1650 QCOMPARE(protocol->connection(), connection.data());
1651
1652 TestFilter filter1;
1653 QScopedPointer<TestFilter> filter2(new TestFilter);
1654 QScopedPointer<TestFilter> filter3(new TestFilter);
1655
1656 filter1.clear(); filter2->clear(); filter3->clear();
1657
1658 connection->installCommandFilter(&filter1);
1659 connection->installCommandFilter(filter2.data());
1660 connection->installCommandFilter(filter3.data());
1661
1662 connection->open();
1663 QVERIFY(waitForOpened());
1664
1665 connection->sendCommand(IrcCommand::createJoin("#freenode"));
1666 QCOMPARE(filter1.commandFiltered, 1);
1667 QCOMPARE(filter2->commandFiltered, 1);
1668 QCOMPARE(filter3->commandFiltered, 1);
1669 QVERIFY(!protocol->written.isEmpty());
1670
1671 protocol->written.clear();
1672 filter1.clear(); filter2->clear(); filter3->clear();
1673 filter3->commandFilterEnabled = true;
1674
1675 connection->sendCommand(IrcCommand::createJoin("#communi"));
1676 QCOMPARE(filter1.commandFiltered, 0);
1677 QCOMPARE(filter2->commandFiltered, 0);
1678 QCOMPARE(filter3->commandFiltered, 1);
1679 QVERIFY(protocol->written.isEmpty());
1680
1681 protocol->written.clear();
1682 filter1.clear(); filter2->clear(); filter3->clear();
1683 filter2->commandFilterEnabled = true;
1684
1685 connection->sendCommand(IrcCommand::createJoin("#qt"));
1686 QCOMPARE(filter1.commandFiltered, 0);
1687 QCOMPARE(filter2->commandFiltered, 1);
1688 QCOMPARE(filter3->commandFiltered, 1);
1689 QVERIFY(protocol->written.isEmpty());
1690
1691 protocol->written.clear();
1692 filter1.clear(); filter2->clear(); filter3->clear();
1693 filter1.commandFilterEnabled = true;
1694
1695 connection->sendCommand(IrcCommand::createPart("#freenode"));
1696 QCOMPARE(filter1.commandFiltered, 1);
1697 QCOMPARE(filter2->commandFiltered, 1);
1698 QCOMPARE(filter3->commandFiltered, 1);
1699 QVERIFY(protocol->written.isEmpty());
1700
1701 protocol->written.clear();
1702 filter1.clear(); filter2->clear(); filter3->clear();
1703
1704 connection->sendCommand(IrcCommand::createPart("#communi"));
1705 QCOMPARE(filter1.commandFiltered, 1);
1706 QCOMPARE(filter2->commandFiltered, 1);
1707 QCOMPARE(filter3->commandFiltered, 1);
1708 QVERIFY(!protocol->written.isEmpty());
1709
1710 // a deleted filter gets removed
1711 filter2.reset();
1712 filter1.clear(); filter3->clear();
1713 protocol->written.clear();
1714
1715 connection->sendCommand(IrcCommand::createPart("#qt"));
1716 QCOMPARE(filter1.commandFiltered, 1);
1717 QCOMPARE(filter3->commandFiltered, 1);
1718 QVERIFY(!protocol->written.isEmpty());
1719
1720 // double filters
1721 connection->installCommandFilter(&filter1);
1722 connection->installCommandFilter(filter3.data());
1723 filter1.clear(); filter3->clear();
1724 protocol->written.clear();
1725
1726 connection->sendCommand(IrcCommand::createJoin("#freenode"));
1727 QCOMPARE(filter1.commandFiltered, 2);
1728 QCOMPARE(filter3->commandFiltered, 2);
1729 QVERIFY(!protocol->written.isEmpty());
1730
1731 // remove & enable double filter
1732 filter1.clear(); filter3->clear();
1733 filter1.commandFilterEnabled = true;
1734 connection->removeCommandFilter(filter3.data());
1735 protocol->written.clear();
1736
1737 connection->sendCommand(IrcCommand::createJoin("#communi"));
1738 QCOMPARE(filter1.commandFiltered, 1);
1739 QCOMPARE(filter3->commandFiltered, 0);
1740 QVERIFY(protocol->written.isEmpty());
1741
1742 // remove & delete
1743 filter3.reset();
1744 filter1.clear();
1745 connection->removeCommandFilter(&filter1);
1746 protocol->written.clear();
1747
1748 connection->sendCommand(IrcCommand::createJoin("#qt"));
1749 QCOMPARE(filter1.commandFiltered, 0);
1750 QVERIFY(!protocol->written.isEmpty());
1751
1752 // commit a suicide
1753 QPointer<TestFilter> suicidal = new TestFilter;
1754 connection->installCommandFilter(suicidal);
1755 suicidal->commitSuicide = true;
1756
1757 connection->sendCommand(IrcCommand::createPart("#qt"));
1758 QVERIFY(!suicidal);
1759 }
1760
testDebug()1761 void tst_IrcConnection::testDebug()
1762 {
1763 QString str;
1764 QDebug dbg(&str);
1765
1766 dbg << static_cast<IrcConnection*>(nullptr);
1767 QCOMPARE(str.trimmed(), QString::fromLatin1("IrcConnection(0x0)"));
1768 str.clear();
1769
1770 IrcConnection connection;
1771 dbg << &connection;
1772 QVERIFY(QRegularExpression("IrcConnection\\(0x[0-9A-Fa-f]+\\) ").match(str).hasMatch());
1773 str.clear();
1774
1775 connection.setHost("irc.freenode.net");
1776 dbg << &connection;
1777 QVERIFY(QRegularExpression("IrcConnection\\(0x[0-9A-Fa-f]+, irc.freenode.net\\) ").match(str).hasMatch());
1778 str.clear();
1779
1780 connection.setDisplayName("Freenode");
1781 dbg << &connection;
1782 QVERIFY(QRegularExpression("IrcConnection\\(0x[0-9A-Fa-f]+, Freenode\\) ").match(str).hasMatch());
1783 str.clear();
1784
1785 dbg << IrcConnection::Connected;
1786 QCOMPARE(str.trimmed(), QString::fromLatin1("Connected"));
1787 str.clear();
1788 }
1789
testWarnings()1790 void tst_IrcConnection::testWarnings()
1791 {
1792 connection->open();
1793 QVERIFY(waitForOpened());
1794
1795 QVERIFY(connection->isActive());
1796
1797 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setHost() has no effect until re-connect");
1798 connection->setHost("foo");
1799
1800 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setPort() has no effect until re-connect");
1801 connection->setPort(1234);
1802
1803 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setUserName() has no effect until re-connect");
1804 connection->setUserName("foo");
1805
1806 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setRealName() has no effect until re-connect");
1807 connection->setRealName("foo");
1808
1809 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setPassword() has no effect until re-connect");
1810 connection->setPassword("foo");
1811
1812 QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism() has no effect until re-connect");
1813 connection->setSaslMechanism("PLAIN");
1814 }
1815
1816 class FakeQmlConnection : public IrcConnection
1817 {
1818 Q_OBJECT
1819 friend class tst_IrcConnection;
1820
1821 public slots:
1822 // -Wno-overloaded-virtual
createCtcpReply(const QVariant & request)1823 QVariant createCtcpReply(const QVariant& request)
1824 {
1825 return QVariant::fromValue(IrcConnection::createCtcpReply(request.value<IrcPrivateMessage*>()));
1826 }
1827 };
1828
testCtcp()1829 void tst_IrcConnection::testCtcp()
1830 {
1831 FriendlyConnection* friendly = static_cast<FriendlyConnection*>(connection.data());
1832
1833 QVariantMap replies;
1834 replies.insert("FOO", "bar");
1835 connection->setCtcpReplies(replies);
1836 QCOMPARE(connection->ctcpReplies(), replies);
1837
1838 // PING
1839 IrcMessage* msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1PING timestamp\1", connection);
1840 QScopedPointer<IrcPrivateMessage> pingRequest(qobject_cast<IrcPrivateMessage*>(msg));
1841 QVERIFY(pingRequest.data());
1842
1843 QScopedPointer<IrcCommand> pingReply(friendly->createCtcpReply(pingRequest.data()));
1844 QVERIFY(pingReply.data());
1845 QCOMPARE(pingReply->type(), IrcCommand::CtcpReply);
1846 QCOMPARE(pingReply->toString(), QString("NOTICE nick :\1PING timestamp\1"));
1847
1848 // TIME
1849 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1TIME\1", connection);
1850 QScopedPointer<IrcPrivateMessage> timeRequest(qobject_cast<IrcPrivateMessage*>(msg));
1851 QVERIFY(timeRequest);
1852
1853 QScopedPointer<IrcCommand> timeReply(friendly->createCtcpReply(timeRequest.data()));
1854 QVERIFY(timeReply.data());
1855 QCOMPARE(timeReply->type(), IrcCommand::CtcpReply);
1856 QCOMPARE(timeReply->toString(), QString("NOTICE nick :\1TIME %1\1").arg(QLocale().toString(QDateTime::currentDateTime(), QLocale::ShortFormat)));
1857
1858 // VERSION
1859 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1VERSION\1", connection);
1860 QScopedPointer<IrcPrivateMessage> versionRequest(qobject_cast<IrcPrivateMessage*>(msg));
1861 QVERIFY(versionRequest.data());
1862
1863 QScopedPointer<IrcCommand> versionReply(friendly->createCtcpReply(versionRequest.data()));
1864 QVERIFY(versionReply.data());
1865 QCOMPARE(versionReply->type(), IrcCommand::CtcpReply);
1866 QVERIFY(versionReply->toString().startsWith("NOTICE nick :\1VERSION "));
1867 QVERIFY(versionReply->toString().contains(Irc::version()));
1868 QVERIFY(versionReply->toString().endsWith("\1"));
1869
1870 // SOURCE
1871 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1SOURCE\1", connection);
1872 QScopedPointer<IrcPrivateMessage> sourceRequest(qobject_cast<IrcPrivateMessage*>(msg));
1873 QVERIFY(sourceRequest.data());
1874
1875 QScopedPointer<IrcCommand> sourceReply(friendly->createCtcpReply(sourceRequest.data()));
1876 QVERIFY(sourceReply.data());
1877 QCOMPARE(sourceReply->type(), IrcCommand::CtcpReply);
1878 QVERIFY(sourceReply->toString().startsWith("NOTICE nick :\1SOURCE "));
1879 QVERIFY(sourceReply->toString().contains("https://"));
1880 QVERIFY(sourceReply->toString().endsWith("\1"));
1881
1882 // CLIENTINFO
1883 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1CLIENTINFO\1", connection);
1884 QScopedPointer<IrcPrivateMessage> infoRequest(qobject_cast<IrcPrivateMessage*>(msg));
1885 QVERIFY(infoRequest.data());
1886
1887 QScopedPointer<IrcCommand> infoReply(friendly->createCtcpReply(infoRequest.data()));
1888 QVERIFY(infoReply.data());
1889 QCOMPARE(infoReply->type(), IrcCommand::CtcpReply);
1890 QVERIFY(infoReply->toString().startsWith("NOTICE nick :\1CLIENTINFO "));
1891 QVERIFY(infoReply->toString().contains("PING"));
1892 QVERIFY(infoReply->toString().contains("TIME"));
1893 QVERIFY(infoReply->toString().contains("VERSION"));
1894 QVERIFY(infoReply->toString().contains("SOURCE"));
1895 QVERIFY(infoReply->toString().endsWith("\1"));
1896
1897 // FOO
1898 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1FOO\1", connection);
1899 QScopedPointer<IrcPrivateMessage> fooRequest(qobject_cast<IrcPrivateMessage*>(msg));
1900 QVERIFY(fooRequest.data());
1901
1902 QScopedPointer<IrcCommand> fooReply(friendly->createCtcpReply(fooRequest.data()));
1903 QVERIFY(fooReply.data());
1904 QCOMPARE(fooReply->type(), IrcCommand::CtcpReply);
1905 QCOMPARE(fooReply->toString(), QString("NOTICE nick :\1FOO bar\1"));
1906
1907 // override
1908 replies.insert("VERSION", "none");
1909 connection->setCtcpReplies(replies);
1910 QCOMPARE(connection->ctcpReplies(), replies);
1911
1912 msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1VERSION\1", connection);
1913 QScopedPointer<IrcPrivateMessage> overrideRequest(qobject_cast<IrcPrivateMessage*>(msg));
1914 QVERIFY(overrideRequest.data());
1915
1916 QScopedPointer<IrcCommand> overrideReply(friendly->createCtcpReply(overrideRequest.data()));
1917 QVERIFY(overrideReply.data());
1918 QCOMPARE(overrideReply->type(), IrcCommand::CtcpReply);
1919 QCOMPARE(overrideReply->toString(), QString("NOTICE nick :\1VERSION none\1"));
1920
1921 connection->setCtcpReplies(QVariantMap());
1922
1923 // QML compatibility
1924 FakeQmlConnection qmlConnection;
1925 qmlConnection.setUserName("user");
1926 qmlConnection.setNickName("nick");
1927 qmlConnection.setRealName("real");
1928 qmlConnection.setPassword("secret");
1929 qmlConnection.setHost("127.0.0.1");
1930 qmlConnection.setPort(server->serverPort());
1931
1932 TestProtocol* qmlProtocol = new TestProtocol(&qmlConnection);
1933 qmlConnection.setProtocol(qmlProtocol);
1934 qmlConnection.open();
1935
1936 QVERIFY(server->waitForNewConnection(200));
1937 QAbstractSocket* qmlServerSocket = server->nextPendingConnection();
1938 QVERIFY(qmlServerSocket);
1939 QAbstractSocket* qmlClientSocket = qmlConnection.socket();
1940 QVERIFY(qmlClientSocket);
1941 QVERIFY(qmlClientSocket->waitForConnected(200));
1942
1943 qmlProtocol->written.clear();
1944 qmlServerSocket->write(":nick!user@host PRIVMSG communi :\1PING qml\1\r\n");
1945 QVERIFY(qmlServerSocket->waitForBytesWritten(1000));
1946 QVERIFY(qmlClientSocket->waitForReadyRead(1000));
1947 QCOMPARE(qmlProtocol->written, QByteArray("NOTICE nick :\1PING qml\1"));
1948
1949 connection->open();
1950 QVERIFY(waitForOpened());
1951
1952 TestProtocol* protocol = new TestProtocol(friendly);
1953 friendly->setProtocol(protocol);
1954 QCOMPARE(friendly->protocol(), protocol);
1955 QCOMPARE(protocol->connection(), friendly);
1956
1957 // PING
1958 protocol->written.clear();
1959 QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1PING timestamp\1\r\n"));
1960 QCOMPARE(protocol->written, QByteArray("NOTICE nick :\1PING timestamp\1"));
1961
1962 // TIME
1963 protocol->written.clear();
1964 QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1TIME\1\r\n"));
1965 QVERIFY(protocol->written.startsWith("NOTICE nick :\1TIME "));
1966 QVERIFY(protocol->written.endsWith("\1"));
1967
1968 // VERSION
1969 protocol->written.clear();
1970 QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1VERSION\1\r\n"));
1971 QVERIFY(protocol->written.startsWith("NOTICE nick :\1VERSION "));
1972 QVERIFY(protocol->written.contains(Irc::version().toUtf8()));
1973 QVERIFY(protocol->written.endsWith("\1"));
1974
1975 // SOURCE
1976 protocol->written.clear();
1977 QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1SOURCE\1\r\n"));
1978 QVERIFY(protocol->written.startsWith("NOTICE nick :\1SOURCE "));
1979 QVERIFY(protocol->written.contains("https://"));
1980 QVERIFY(protocol->written.endsWith("\1"));
1981
1982 // CLIENTINFO
1983 protocol->written.clear();
1984 QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1CLIENTINFO\1\r\n"));
1985 QVERIFY(protocol->written.startsWith("NOTICE nick :\1CLIENTINFO "));
1986 QVERIFY(protocol->written.contains("PING"));
1987 QVERIFY(protocol->written.contains("TIME"));
1988 QVERIFY(protocol->written.contains("VERSION"));
1989 QVERIFY(protocol->written.contains("SOURCE"));
1990 QVERIFY(protocol->written.endsWith("\1"));
1991 }
1992
testClone()1993 void tst_IrcConnection::testClone()
1994 {
1995 QVariantMap ud;
1996 ud.insert("foo", "bar");
1997
1998 IrcConnection c1;
1999 c1.setHost("host");
2000 c1.setPort(123);
2001 c1.setServers(QStringList() << "s1" << "s2" << "s3");
2002 c1.setUserName("user");
2003 c1.setNickName("nick");
2004 c1.setRealName("real");
2005 c1.setPassword("pass");
2006 c1.setNickNames(QStringList() << "n1" << "n2" << "n3");
2007 c1.setDisplayName("display");
2008 c1.setUserData(ud);
2009 c1.setEncoding("UTF-8");
2010 c1.setEnabled(false);
2011 c1.setReconnectDelay(10);
2012 c1.setSecure(true);
2013 c1.setSaslMechanism("PLAIN");
2014
2015 IrcConnection* c2 = c1.clone(&c1);
2016 QCOMPARE(c2->parent(), &c1);
2017
2018 QCOMPARE(c2->host(), QString("host"));
2019 QCOMPARE(c2->port(), 123);
2020 QCOMPARE(c2->servers(), QStringList() << "s1" << "s2" << "s3");
2021 QCOMPARE(c2->userName(), QString("user"));
2022 QCOMPARE(c2->nickName(), QString("nick"));
2023 QCOMPARE(c2->realName(), QString("real"));
2024 QCOMPARE(c2->password(), QString("pass"));
2025 QCOMPARE(c2->nickNames(), QStringList() << "n1" << "n2" << "n3");
2026 QCOMPARE(c2->displayName(), QString("display"));
2027 QCOMPARE(c2->userData(), ud);
2028 QCOMPARE(c2->encoding(), QByteArray("UTF-8"));
2029 QVERIFY(!c2->isEnabled());
2030 QCOMPARE(c2->reconnectDelay(), 10);
2031 QVERIFY(c2->isSecure());
2032 QCOMPARE(c2->saslMechanism(), QString("PLAIN"));
2033 }
2034
testSaveRestore()2035 void tst_IrcConnection::testSaveRestore()
2036 {
2037 QVariantMap ud;
2038 ud.insert("foo", "bar");
2039
2040 IrcConnection c1;
2041 c1.setHost("host");
2042 c1.setPort(123);
2043 c1.setServers(QStringList() << "s1" << "s2" << "s3");
2044 c1.setUserName("user");
2045 c1.setNickName("nick");
2046 c1.setRealName("real");
2047 c1.setPassword("pass");
2048 c1.setNickNames(QStringList() << "n1" << "n2" << "n3");
2049 c1.setDisplayName("display");
2050 c1.setUserData(ud);
2051 c1.setEncoding("UTF-8");
2052 c1.setEnabled(false);
2053 c1.setReconnectDelay(10);
2054 c1.setSecure(true);
2055 c1.setSaslMechanism("PLAIN");
2056
2057 IrcConnection c2;
2058 c2.restoreState(c1.saveState());
2059
2060 QCOMPARE(c2.host(), QString("host"));
2061 QCOMPARE(c2.port(), 123);
2062 QCOMPARE(c2.servers(), QStringList() << "s1" << "s2" << "s3");
2063 QCOMPARE(c2.userName(), QString("user"));
2064 QEXPECT_FAIL("", "TODO", Continue);
2065 QCOMPARE(c2.nickName(), QString("nick"));
2066 QCOMPARE(c2.realName(), QString("real"));
2067 QCOMPARE(c2.password(), QString("pass"));
2068 QCOMPARE(c2.nickNames(), QStringList() << "n1" << "n2" << "n3");
2069 QCOMPARE(c2.displayName(), QString("display"));
2070 QCOMPARE(c2.userData(), ud);
2071 QCOMPARE(c2.encoding(), QByteArray("UTF-8"));
2072 QVERIFY(!c2.isEnabled());
2073 QCOMPARE(c2.reconnectDelay(), 10);
2074 QVERIFY(c2.isSecure());
2075 QCOMPARE(c2.saslMechanism(), QString("PLAIN"));
2076 }
2077
testSignals()2078 void tst_IrcConnection::testSignals()
2079 {
2080 connection->open();
2081 QVERIFY(waitForOpened());
2082
2083 QSignalSpy channelKeyRequiredSpy(connection, SIGNAL(channelKeyRequired(QString,QString*)));
2084 QVERIFY(channelKeyRequiredSpy.isValid());
2085
2086 QVERIFY(waitForWritten(":hobana.freenode.net 475 jpnurmi #communi :Cannot join channel (+k) - bad key"));
2087 QCOMPARE(channelKeyRequiredSpy.count(), 1);
2088 QCOMPARE(channelKeyRequiredSpy.last().first().toString(), QString("#communi"));
2089
2090 QSignalSpy nickNameRequiredSpy(connection, SIGNAL(nickNameRequired(QString,QString*)));
2091 QVERIFY(nickNameRequiredSpy.isValid());
2092
2093 QVERIFY(waitForWritten(":sinisalo.freenode.net 433 * jpnurmi :Nickname is already in use."));
2094 QCOMPARE(nickNameRequiredSpy.count(), 1);
2095 QCOMPARE(nickNameRequiredSpy.last().first().toString(), QString("jpnurmi"));
2096 }
2097
testServers()2098 void tst_IrcConnection::testServers()
2099 {
2100 QVERIFY(IrcConnection::isValidServer("irc.freenode.net"));
2101 QVERIFY(IrcConnection::isValidServer("irc.freenode.net 6667"));
2102 QVERIFY(IrcConnection::isValidServer("irc.freenode.net +6697"));
2103
2104 QVERIFY(!IrcConnection::isValidServer(""));
2105 QVERIFY(!IrcConnection::isValidServer("irc.freenode.net foobar"));
2106 QVERIFY(!IrcConnection::isValidServer("irc.freenode.net 6667 foobar"));
2107 }
2108
2109 QTEST_MAIN(tst_IrcConnection)
2110
2111 #include "tst_ircconnection.moc"
2112