/* * Copyright (C) 2008-2014 The Communi Project * * This test is free, and not covered by the BSD license. There is no * restriction applied to their modification, redistribution, using and so on. * You can study them, modify them, use them in your own program - either * completely or partially. */ #include "irc.h" #include "irccommand.h" #include "ircprotocol.h" #include "ircconnection.h" #include "ircmessage.h" #include "ircfilter.h" #include #include #include #include #ifndef QT_NO_OPENSSL #include #endif #include "tst_ircdata.h" #include "tst_ircclientserver.h" class FriendlyConnection : public IrcConnection { friend class tst_IrcConnection; }; class TestProtocol : public IrcProtocol { public: TestProtocol(IrcConnection* connection) : IrcProtocol(connection) { } virtual bool write(const QByteArray& data) { written = data; return IrcProtocol::write(data); } QByteArray written; }; class tst_IrcConnection : public tst_IrcClientServer { Q_OBJECT private slots: void testDefaults(); void testHost_data(); void testHost(); void testPort_data(); void testPort(); void testUserName_data(); void testUserName(); void testNickName_data(); void testNickName(); void testRealName_data(); void testRealName(); void testPassword_data(); void testPassword(); void testDisplayName_data(); void testDisplayName(); void testEncoding_data(); void testEncoding(); void testSocket_data(); void testSocket(); void testSecure(); void testSasl(); void testNoSasl(); void testSsl(); void testOpen(); void testEnabled(); void testStatus(); void testConnection(); void testMessages(); void testMessageFlags(); void testMessageBuilder(); void testSendCommand(); void testSendData(); void testMessageFilter(); void testCommandFilter(); void testDebug(); void testWarnings(); void testCtcp(); }; void tst_IrcConnection::testDefaults() { IrcConnection connection; QVERIFY(connection.host().isNull()); QCOMPARE(connection.port(), 6667); QVERIFY(connection.userName().isNull()); QVERIFY(connection.nickName().isNull()); QVERIFY(connection.realName().isNull()); QVERIFY(connection.password().isNull()); QVERIFY(connection.displayName().isNull()); QCOMPARE(connection.encoding(), QByteArray("ISO-8859-15")); QCOMPARE(connection.status(), IrcConnection::Inactive); QVERIFY(!connection.isActive()); QVERIFY(!connection.isConnected()); QVERIFY(connection.isEnabled()); QCOMPARE(connection.reconnectDelay(), 0); QVERIFY(connection.socket()); QVERIFY(!connection.isSecure()); QVERIFY(connection.saslMechanism().isNull()); QVERIFY(!IrcConnection::supportedSaslMechanisms().isEmpty()); QVERIFY(connection.network()); } void tst_IrcConnection::testHost_data() { QTest::addColumn("host"); QTest::newRow("null") << QString(); QTest::newRow("empty") << QString(""); QTest::newRow("space") << QString(" "); QTest::newRow("invalid") << QString("invalid"); QTest::newRow("local") << QString("127.0.0.1"); } void tst_IrcConnection::testHost() { QFETCH(QString, host); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(hostChanged(QString))); QVERIFY(spy.isValid()); connection.setHost(host); QCOMPARE(connection.host(), host); QCOMPARE(spy.count(), !host.isEmpty() ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toString(), host); IrcConnection another(host); QCOMPARE(another.host(), host); } void tst_IrcConnection::testPort_data() { QTest::addColumn("port"); QTest::newRow("-1") << -1; QTest::newRow("0") << 0; QTest::newRow("6666") << 6666; QTest::newRow("6667") << 6667; QTest::newRow("6668") << 6668; } void tst_IrcConnection::testPort() { QFETCH(int, port); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(portChanged(int))); QVERIFY(spy.isValid()); connection.setPort(port); QCOMPARE(connection.port(), port); QCOMPARE(spy.count(), port != 6667 ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toInt(), port); } void tst_IrcConnection::testUserName_data() { QTest::addColumn("name"); QTest::addColumn("result"); QTest::newRow("null") << QString() << QString(); QTest::newRow("empty") << QString("") << QString(""); QTest::newRow("space") << QString(" ") << QString(""); QTest::newRow("spaces") << QString(" foo bar ") << QString("foo"); } void tst_IrcConnection::testUserName() { QFETCH(QString, name); QFETCH(QString, result); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(userNameChanged(QString))); QVERIFY(spy.isValid()); connection.setUserName(name); QCOMPARE(connection.userName(), result); QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toString(), result); } void tst_IrcConnection::testNickName_data() { QTest::addColumn("name"); QTest::addColumn("result"); QTest::newRow("null") << QString() << QString(); QTest::newRow("empty") << QString("") << QString(""); QTest::newRow("space") << QString(" ") << QString(""); QTest::newRow("spaces") << QString(" foo bar ") << QString("foo"); } void tst_IrcConnection::testNickName() { QFETCH(QString, name); QFETCH(QString, result); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(nickNameChanged(QString))); QVERIFY(spy.isValid()); connection.setNickName(name); QCOMPARE(connection.nickName(), result); QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toString(), result); } void tst_IrcConnection::testRealName_data() { QTest::addColumn("name"); QTest::addColumn("result"); QTest::newRow("null") << QString() << QString(); QTest::newRow("empty") << QString("") << QString(""); QTest::newRow("space") << QString(" ") << QString(" "); QTest::newRow("spaces") << QString(" foo bar ") << QString(" foo bar "); } void tst_IrcConnection::testRealName() { QFETCH(QString, name); QFETCH(QString, result); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(realNameChanged(QString))); QVERIFY(spy.isValid()); connection.setRealName(name); QCOMPARE(connection.realName(), result); QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toString(), result); } void tst_IrcConnection::testPassword_data() { QTest::addColumn("passwd"); QTest::addColumn("result"); QTest::newRow("null") << QString() << QString(); QTest::newRow("empty") << QString("") << QString(""); QTest::newRow("space") << QString(" ") << QString(" "); QTest::newRow("spaces") << QString(" foo bar ") << QString(" foo bar "); } void tst_IrcConnection::testPassword() { QFETCH(QString, passwd); QFETCH(QString, result); IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(passwordChanged(QString))); QVERIFY(spy.isValid()); connection.setPassword(passwd); QCOMPARE(connection.password(), result); QCOMPARE(spy.count(), !result.isEmpty() ? 1 : 0); if (!spy.isEmpty()) QCOMPARE(spy.first().first().toString(), result); } void tst_IrcConnection::testDisplayName_data() { QTest::addColumn("host"); QTest::addColumn("name"); QTest::addColumn("result"); QTest::newRow("null") << QString() << QString() << QString(); QTest::newRow("empty") << QString() << QString("") << QString(""); QTest::newRow("space") << QString() << QString(" ") << QString(" "); QTest::newRow("host") << QString("host") << QString() << QString("host"); QTest::newRow("name") << QString() << QString("name") << QString("name"); QTest::newRow("explicit") << QString("host") << QString("name") << QString("name"); } void tst_IrcConnection::testDisplayName() { QFETCH(QString, host); QFETCH(QString, name); QFETCH(QString, result); IrcConnection connection; connection.setHost(host); connection.setDisplayName(name); QCOMPARE(connection.displayName(), result); } void tst_IrcConnection::testEncoding_data() { QTest::addColumn("encoding"); QTest::addColumn("actual"); QTest::addColumn("supported"); QTest::newRow("null") << QByteArray() << QByteArray("ISO-8859-15") << false; QTest::newRow("empty") << QByteArray("") << QByteArray("ISO-8859-15") << false; QTest::newRow("space") << QByteArray(" ") << QByteArray("ISO-8859-15") << false; QTest::newRow("invalid") << QByteArray("invalid") << QByteArray("ISO-8859-15") << false; foreach (const QByteArray& codec, QTextCodec::availableCodecs()) QTest::newRow(codec) << codec << codec << true; } void tst_IrcConnection::testEncoding() { QFETCH(QByteArray, encoding); QFETCH(QByteArray, actual); QFETCH(bool, supported); if (!supported) QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setEncoding(): unsupported encoding \"" + encoding + "\" "); IrcConnection connection; connection.setEncoding(encoding); QCOMPARE(connection.encoding(), actual); } Q_DECLARE_METATYPE(QAbstractSocket*) void tst_IrcConnection::testSocket_data() { QTest::addColumn("socket"); QTest::newRow("null") << static_cast(0); QTest::newRow("tcp") << static_cast(new QTcpSocket(this)); #ifndef QT_NO_OPENSSL QTest::newRow("ssl") << static_cast(new QSslSocket(this)); #endif } void tst_IrcConnection::testSocket() { QFETCH(QAbstractSocket*, socket); IrcConnection connection; connection.setSocket(socket); QCOMPARE(connection.socket(), socket); QCOMPARE(connection.isSecure(), socket && socket->inherits("QSslSocket")); } void tst_IrcConnection::testSecure() { IrcConnection connection; QSignalSpy spy(&connection, SIGNAL(secureChanged(bool))); QVERIFY(spy.isValid()); QVERIFY(!connection.isSecure()); #ifdef QT_NO_OPENSSL QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSecure(): the Qt build does not support SSL"); #endif connection.setSecure(true); #ifndef QT_NO_OPENSSL QVERIFY(connection.isSecure()); QVERIFY(connection.socket()->inherits("QSslSocket")); QCOMPARE(spy.count(), 1); QVERIFY(spy.first().first().toBool()); #else QVERIFY(!connection.isSecure()); QVERIFY(!connection.socket()->inherits("QSslSocket")); QCOMPARE(spy.count(), 0); #endif connection.setSecure(false); QVERIFY(!connection.isSecure()); QVERIFY(!connection.socket()->inherits("QSslSocket")); #ifndef QT_NO_OPENSSL QCOMPARE(spy.count(), 2); QVERIFY(!spy.last().last().toBool()); #else QCOMPARE(spy.count(), 0); #endif } void tst_IrcConnection::testSasl() { QVERIFY(!IrcConnection::supportedSaslMechanisms().contains("UNKNOWN")); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism(): unsupported mechanism: 'UNKNOWN'"); connection->setSaslMechanism("UNKNOWN"); QVERIFY(connection->saslMechanism().isEmpty()); IrcProtocol* protocol = static_cast(connection.data())->protocol(); QVERIFY(protocol); QVERIFY(IrcConnection::supportedSaslMechanisms().contains("PLAIN")); connection->setSaslMechanism("PLAIN"); QCOMPARE(connection->saslMechanism(), QString("PLAIN")); connection->open(); QVERIFY(waitForOpened()); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); QByteArray written = serverSocket->readAll(); QVERIFY(written.contains("CAP LS")); QVERIFY(written.contains("NICK nick")); QVERIFY(!written.contains("PASS secret")); QVERIFY(!written.contains("CAP REQ :sasl")); QVERIFY(waitForWritten(":irc.freenode.net CAP * LS :sasl")); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); written = serverSocket->readAll(); QVERIFY(!written.contains("CAP LS")); QVERIFY(!written.contains("NICK nick")); QVERIFY(!written.contains("PASS secret")); QVERIFY(written.contains("CAP REQ :sasl")); // do not resume handshake too early QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall); QVERIFY(!clientSocket->waitForBytesWritten(1000)); QVERIFY(waitForWritten(":irc.freenode.net CAP user ACK :sasl")); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); QVERIFY(serverSocket->readAll().contains("AUTHENTICATE PLAIN")); QVERIFY(waitForWritten("AUTHENTICATE +")); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); QByteArray response = serverSocket->readAll(); int index = response.indexOf("AUTHENTICATE"); QVERIFY(index != -1); QByteArray secret = response.mid(index + 13); index = secret.indexOf("\r\n"); QVERIFY(index != -1); secret.truncate(index + 1); secret = QByteArray::fromBase64(secret); QByteArray expected = connection->userName().toUtf8() + '\0' + connection->userName().toUtf8() + '\0' + connection->password().toUtf8(); QCOMPARE(secret, expected); // resume handshake QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); QVERIFY(serverSocket->readAll().contains("CAP END")); // TODO: QVERIFY(waitForWritten(":irc.freenode.net 900 user nick!user@host nick :You are now logged in as user.")); QVERIFY(waitForWritten(":irc.freenode.net 903 user :SASL authentication successful")); QVERIFY(waitForWritten(":irc.freenode.net 001 user :Welcome to the freenode Internet Relay Chat Network user")); } void tst_IrcConnection::testNoSasl() { QVERIFY(!IrcConnection::supportedSaslMechanisms().contains("UNKNOWN")); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism(): unsupported mechanism: 'UNKNOWN'"); connection->setSaslMechanism("UNKNOWN"); QVERIFY(connection->saslMechanism().isEmpty()); IrcProtocol* protocol = static_cast(connection.data())->protocol(); QVERIFY(protocol); QVERIFY(IrcConnection::supportedSaslMechanisms().contains("PLAIN")); connection->setSaslMechanism("PLAIN"); QCOMPARE(connection->saslMechanism(), QString("PLAIN")); connection->open(); QVERIFY(waitForOpened()); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); QByteArray written = serverSocket->readAll(); QVERIFY(written.contains("CAP LS")); QVERIFY(written.contains("NICK nick")); QVERIFY(!written.contains("PASS secret")); QVERIFY(!written.contains("CAP REQ :sasl")); QVERIFY(waitForWritten(":irc.freenode.net CAP * LS :no s-a-s-l here")); QVERIFY(!clientSocket->waitForBytesWritten(1000)); QVERIFY(!serverSocket->waitForReadyRead(1000)); QVERIFY(serverSocket->readAll().isEmpty()); // resume handshake QCoreApplication::sendPostedEvents(protocol, QEvent::MetaCall); QVERIFY(clientSocket->waitForBytesWritten(1000)); QVERIFY(serverSocket->waitForReadyRead(1000)); written = serverSocket->readAll(); QVERIFY(written.contains("PASS secret")); QVERIFY(written.contains("CAP END")); } #ifndef QT_NO_OPENSSL class SslSocket : public QSslSocket { Q_OBJECT public: SslSocket(QObject* parent) : QSslSocket(parent), clientEncryptionStarted(false) { } bool clientEncryptionStarted; public slots: void startClientEncryption() { clientEncryptionStarted = true; QSslSocket::startClientEncryption(); } }; #endif // !QT_NO_OPENSSL void tst_IrcConnection::testSsl() { #ifndef QT_NO_OPENSSL SslSocket* socket = new SslSocket(connection); connection->setSocket(socket); QCOMPARE(connection->socket(), socket); connection->open(); QVERIFY(waitForOpened()); QVERIFY(socket->clientEncryptionStarted); #endif // !QT_NO_OPENSSL } void tst_IrcConnection::testOpen() { IrcConnection connection; QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): host is empty!"); connection.open(); QCOMPARE(connection.status(), IrcConnection::Inactive); connection.setHost("irc.ser.ver"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): userName is empty!"); connection.open(); QCOMPARE(connection.status(), IrcConnection::Inactive); connection.setUserName("user"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): nickName is empty!"); connection.open(); QCOMPARE(connection.status(), IrcConnection::Inactive); connection.setNickName("nick"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::open(): realName is empty!"); connection.open(); QCOMPARE(connection.status(), IrcConnection::Inactive); connection.setRealName("real"); connection.open(); QVERIFY(connection.status() != IrcConnection::Inactive); connection.close(); QCOMPARE(connection.status(), IrcConnection::Closed); connection.setEnabled(false); connection.open(); QCOMPARE(connection.status(), IrcConnection::Closed); } void tst_IrcConnection::testEnabled() { IrcConnection connection; QVERIFY(connection.isEnabled()); QSignalSpy spy(&connection, SIGNAL(enabledChanged(bool))); QVERIFY(spy.isValid()); connection.setEnabled(false); QVERIFY(!connection.isEnabled()); QCOMPARE(spy.count(), 1); QCOMPARE(spy.last().at(0).toBool(), false); connection.setDisabled(true); QVERIFY(!connection.isEnabled()); QCOMPARE(spy.count(), 1); connection.setDisabled(false); QVERIFY(connection.isEnabled()); QCOMPARE(spy.count(), 2); QCOMPARE(spy.last().at(0).toBool(), true); connection.setEnabled(true); QVERIFY(connection.isEnabled()); QCOMPARE(spy.count(), 2); } void tst_IrcConnection::testStatus() { Irc::registerMetaTypes(); QSignalSpy statusSpy(connection, SIGNAL(statusChanged(IrcConnection::Status))); QSignalSpy connectingSpy(connection, SIGNAL(connecting())); QSignalSpy connectedSpy(connection, SIGNAL(connected())); QSignalSpy disconnectedSpy(connection, SIGNAL(disconnected())); QVERIFY(statusSpy.isValid()); QVERIFY(connectingSpy.isValid()); QVERIFY(connectedSpy.isValid()); QVERIFY(disconnectedSpy.isValid()); int statusCount = 0; int connectingCount = 0; int connectedCount = 0; int disconnectedCount = 0; connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connecting); QCOMPARE(connectingSpy.count(), ++connectingCount); QVERIFY(waitForWritten(tst_IrcData::welcome())); QVERIFY(connection->isActive()); QVERIFY(connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connected); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connected); QCOMPARE(connectedSpy.count(), ++connectedCount); clientSocket->close(); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closing); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Closing); connection->close(); QVERIFY(!connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closed); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Closed); QCOMPARE(disconnectedSpy.count(), ++disconnectedCount); connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connecting); QCOMPARE(connectingSpy.count(), ++connectingCount); QVERIFY(waitForWritten(tst_IrcData::welcome())); QVERIFY(connection->isActive()); QVERIFY(connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connected); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connected); QCOMPARE(connectedSpy.count(), ++connectedCount); // trigger an error serverSocket->close(); QVERIFY(clientSocket->waitForDisconnected(100)); QVERIFY(!connection->isConnected()); QVERIFY(!connection->isActive()); QCOMPARE(statusSpy.at(statusCount++).at(0).value(), IrcConnection::Error); QCOMPARE(statusSpy.count(), statusCount); connection->close(); QVERIFY(!connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closed); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Closed); QCOMPARE(disconnectedSpy.count(), ++disconnectedCount); connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connecting); QCOMPARE(connectingSpy.count(), ++connectingCount); QVERIFY(waitForWritten(tst_IrcData::welcome())); QVERIFY(connection->isActive()); QVERIFY(connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connected); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connected); QCOMPARE(connectedSpy.count(), ++connectedCount); // trigger an error - automatic reconnect connection->setReconnectDelay(1); serverSocket->close(); QVERIFY(clientSocket->waitForDisconnected(100)); QVERIFY(!connection->isConnected()); QVERIFY(!connection->isActive()); QCOMPARE(statusSpy.at(statusCount++).at(0).value(), IrcConnection::Error); QCOMPARE(statusSpy.at(statusCount++).at(0).value(), IrcConnection::Waiting); QCOMPARE(statusSpy.count(), statusCount); QEventLoop reconnectLoop; QTimer::singleShot(2000, &reconnectLoop, SLOT(quit())); connect(connection, SIGNAL(statusChanged(IrcConnection::Status)), &reconnectLoop, SLOT(quit())); reconnectLoop.exec(); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QCOMPARE(statusSpy.count(), ++statusCount); QCOMPARE(statusSpy.last().at(0).value(), IrcConnection::Connecting); QVERIFY(waitForOpened()); QCOMPARE(connectingSpy.count(), ++connectingCount); // trigger an error _after_ quit -> no automatic reconnect connection->quit(); serverSocket->close(); QVERIFY(clientSocket->waitForDisconnected(100)); QVERIFY(!connection->isConnected()); QVERIFY(!connection->isActive()); QCOMPARE(statusSpy.at(statusCount++).at(0).value(), IrcConnection::Closing); QCOMPARE(statusSpy.at(statusCount++).at(0).value(), IrcConnection::Closed); QCOMPARE(statusSpy.count(), statusCount); } void tst_IrcConnection::testConnection() { Irc::registerMetaTypes(); TestProtocol* protocol = new TestProtocol(connection); FriendlyConnection* friendly = static_cast(connection.data()); friendly->setProtocol(protocol); QCOMPARE(friendly->protocol(), protocol); QCOMPARE(protocol->connection(), connection.data()); connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QVERIFY(waitForWritten(":irc.ser.ver 001 nick :Welcome to the Internet Relay Chat Network nick")); QVERIFY(connection->isActive()); QVERIFY(connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connected); connection->close(); QVERIFY(!connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closed); // don't open when disabled connection->setEnabled(false); connection->open(); QVERIFY(!connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closed); // re-enable connection->setEnabled(true); connection->open(); QVERIFY(connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connecting); QVERIFY(waitForOpened()); protocol->written.clear(); connection->network()->requestCapability("identify-msg"); QVERIFY(protocol->written.contains("CAP REQ")); QVERIFY(protocol->written.contains("identify-msg")); protocol->written.clear(); connection->network()->requestCapabilities(QStringList() << "sasl" << "communi"); QVERIFY(protocol->written.contains("CAP REQ")); QVERIFY(protocol->written.contains("sasl")); QVERIFY(protocol->written.contains("communi")); QVERIFY(waitForWritten(":irc.ser.ver 001 nick :Welcome to the Internet Relay Chat Network nick")); QVERIFY(connection->isActive()); QVERIFY(connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Connected); protocol->written.clear(); connection->setNickName("communi"); QVERIFY(protocol->written.contains("NICK")); QVERIFY(protocol->written.contains("communi")); protocol->written.clear(); connection->quit(); QVERIFY(protocol->written.contains("QUIT")); connection->close(); QVERIFY(!connection->isActive()); QVERIFY(!connection->isConnected()); QCOMPARE(connection->status(), IrcConnection::Closed); } class NickChanger : public QObject { Q_OBJECT public: NickChanger(IrcConnection* connection) : QObject(connection) { connect(connection, SIGNAL(nickNameReserved(QString*)), SLOT(onNickNameReserved(QString*))); } QString setAlternate; QString passedAlternate; public slots: void onNickNameReserved(QString* alternate) { Q_ASSERT(alternate); passedAlternate = *alternate; *alternate = setAlternate; } }; Q_DECLARE_METATYPE(QString*) void tst_IrcConnection::testMessages() { Irc::registerMetaTypes(); qRegisterMetaType(); QSignalSpy messageSpy(connection, SIGNAL(messageReceived(IrcMessage*))); QSignalSpy capabilityMessageSpy(connection, SIGNAL(capabilityMessageReceived(IrcCapabilityMessage*))); QSignalSpy errorMessageSpy(connection, SIGNAL(errorMessageReceived(IrcErrorMessage*))); QSignalSpy inviteMessageSpy(connection, SIGNAL(inviteMessageReceived(IrcInviteMessage*))); QSignalSpy joinMessageSpy(connection, SIGNAL(joinMessageReceived(IrcJoinMessage*))); QSignalSpy kickMessageSpy(connection, SIGNAL(kickMessageReceived(IrcKickMessage*))); QSignalSpy modeMessageSpy(connection, SIGNAL(modeMessageReceived(IrcModeMessage*))); QSignalSpy namesMessageSpy(connection, SIGNAL(namesMessageReceived(IrcNamesMessage*))); QSignalSpy nickMessageSpy(connection, SIGNAL(nickMessageReceived(IrcNickMessage*))); QSignalSpy noticeMessageSpy(connection, SIGNAL(noticeMessageReceived(IrcNoticeMessage*))); QSignalSpy numericMessageSpy(connection, SIGNAL(numericMessageReceived(IrcNumericMessage*))); QSignalSpy motdMessageSpy(connection, SIGNAL(motdMessageReceived(IrcMotdMessage*))); QSignalSpy partMessageSpy(connection, SIGNAL(partMessageReceived(IrcPartMessage*))); QSignalSpy pingMessageSpy(connection, SIGNAL(pingMessageReceived(IrcPingMessage*))); QSignalSpy pongMessageSpy(connection, SIGNAL(pongMessageReceived(IrcPongMessage*))); QSignalSpy privateMessageSpy(connection, SIGNAL(privateMessageReceived(IrcPrivateMessage*))); QSignalSpy quitMessageSpy(connection, SIGNAL(quitMessageReceived(IrcQuitMessage*))); QSignalSpy topicMessageSpy(connection, SIGNAL(topicMessageReceived(IrcTopicMessage*))); QSignalSpy whoReplyMessageSpy(connection, SIGNAL(whoReplyMessageReceived(IrcWhoReplyMessage*))); QVERIFY(messageSpy.isValid()); QVERIFY(capabilityMessageSpy.isValid()); QVERIFY(errorMessageSpy.isValid()); QVERIFY(inviteMessageSpy.isValid()); QVERIFY(joinMessageSpy.isValid()); QVERIFY(kickMessageSpy.isValid()); QVERIFY(modeMessageSpy.isValid()); QVERIFY(namesMessageSpy.isValid()); QVERIFY(nickMessageSpy.isValid()); QVERIFY(noticeMessageSpy.isValid()); QVERIFY(numericMessageSpy.isValid()); QVERIFY(motdMessageSpy.isValid()); QVERIFY(partMessageSpy.isValid()); QVERIFY(pingMessageSpy.isValid()); QVERIFY(pongMessageSpy.isValid()); QVERIFY(privateMessageSpy.isValid()); QVERIFY(quitMessageSpy.isValid()); QVERIFY(topicMessageSpy.isValid()); QVERIFY(whoReplyMessageSpy.isValid()); int messageCount = 0; int numericMessageCount = 0; connection->open(); QVERIFY(waitForOpened()); QVERIFY(waitForWritten(":moorcock.freenode.net CAP * LS :account-notify extended-join identify-msg multi-prefix sasl")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(capabilityMessageSpy.count(), 1); QVERIFY(waitForWritten(":moorcock.freenode.net 001 communi :Welcome to the freenode Internet Relay Chat Network communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); 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")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); 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")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 375 communi :- moorcock.freenode.net Message of the Day -")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 372 communi :- Welcome to moorcock.freenode.net in ...")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 376 communi :End of /MOTD command.")); messageCount += 2; // RPL_ENDOFMOTD + IrcMotdMessage QCOMPARE(messageSpy.count(), messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(motdMessageSpy.count(), 1); QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #freenode")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(joinMessageSpy.count(), 1); 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.")); messageCount += 2; // RPL_TOPIC & IrcTopicMessage QCOMPARE(messageSpy.count(), messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(topicMessageSpy.count(), 1); QVERIFY(waitForWritten(":moorcock.freenode.net 333 communi #freenode erry 1379357591")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); 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_")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); 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")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 366 communi #freenode :End of /NAMES list.")); messageCount += 2; // RPL_ENDOFNAMES & IrcNamesMessage QCOMPARE(messageSpy.count(), messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(namesMessageSpy.count(), 1); 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.")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(noticeMessageSpy.count(), 1); QVERIFY(waitForWritten(":services. 328 communi #freenode :http://freenode.net/")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QVERIFY(waitForWritten("PING :moorcock.freenode.net")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(pingMessageSpy.count(), 1); QVERIFY(waitForWritten("PONG :moorcock.freenode.net")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(pongMessageSpy.count(), 1); QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi INVITE Communi84194 :#communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(inviteMessageSpy.count(), 1); QVERIFY(waitForWritten(":Communi84194!ident@host NICK :communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(nickMessageSpy.count(), 1); // own nick name changes QSignalSpy nickNameChangedSpy(connection, SIGNAL(nickNameChanged(QString))); QVERIFY(nickNameChangedSpy.isValid()); QVERIFY(waitForWritten(":communi!user@host NICK :own")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(nickMessageSpy.count(), 2); QCOMPARE(connection->nickName(), QString("own")); QCOMPARE(nickNameChangedSpy.count(), 1); QCOMPARE(nickNameChangedSpy.last().at(0).toString(), QString("own")); // nick in use QString prevNick = connection->nickName(); NickChanger changer(connection); changer.setAlternate = "communi_"; QSignalSpy nickNameReservedSpy(connection, SIGNAL(nickNameReserved(QString*))); QVERIFY(nickNameReservedSpy.isValid()); QVERIFY(waitForWritten(":moorcock.freenode.net 433 * communi :Nickname is already in use.")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(nickNameReservedSpy.count(), 1); QCOMPARE(changer.passedAlternate, prevNick); QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi MODE #communi +v communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(modeMessageSpy.count(), 1); QVERIFY(waitForWritten(":moorcock.freenode.net 324 communi #communi +ms")); messageCount += 2; // RPL_CHANNELMODEIS + IrcModeMessage QCOMPARE(messageSpy.count(), messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(modeMessageSpy.count(), 2); QVERIFY(waitForWritten(":qtassistant!jpnurmi@qt/jpnurmi/bot/qtassistant PART #communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(partMessageSpy.count(), 1); QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi PRIVMSG #communi :hello")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(privateMessageSpy.count(), 1); QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi QUIT :Client Quit")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(quitMessageSpy.count(), 1); QVERIFY(waitForWritten(":jpnurmi!jpnurmi@qt/jpnurmi KICK #communi communi")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(kickMessageSpy.count(), 1); QVERIFY(waitForWritten("ERROR :just testing...")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(errorMessageSpy.count(), 1); QVERIFY(waitForWritten(":hobana.freenode.net 352 communi #communi ChanServ services. services. ChanServ H@ :0 Channel Services" )); messageCount += 2; // RPL_WHOREPLY + IrcWhoReplyMessage QCOMPARE(messageSpy.count(), messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(whoReplyMessageSpy.count(), 1); QVERIFY(waitForWritten(":hobana.freenode.net 315 communi #communi :End of /WHO list.")); QCOMPARE(messageSpy.count(), ++messageCount); QCOMPARE(numericMessageSpy.count(), ++numericMessageCount); QCOMPARE(whoReplyMessageSpy.count(), 1); } class MsgFilter : public QObject, public IrcMessageFilter { Q_OBJECT Q_INTERFACES(IrcMessageFilter) public: MsgFilter() : count(0), type(IrcMessage::Unknown), flags(IrcMessage::None) { } bool messageFilter(IrcMessage* message) { ++count; type = message->type(); flags = message->flags(); value = message->property(property); return false; } public: int count; QVariant value; QByteArray property; IrcMessage::Type type; IrcMessage::Flags flags; }; void tst_IrcConnection::testMessageFlags() { connection->open(); QVERIFY(waitForOpened()); int count = 0; MsgFilter filter; connection->installMessageFilter(&filter); QVERIFY(waitForWritten(":server CAP * LS :identify-msg")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Capability); QCOMPARE(filter.flags, IrcMessage::None); QVERIFY(waitForWritten(":server CAP communi ACK :identify-msg")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Capability); QCOMPARE(filter.flags, IrcMessage::None); QVERIFY(waitForWritten(":server 001 communi :Welcome...")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Numeric); QCOMPARE(filter.flags, IrcMessage::None); 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")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Numeric); QCOMPARE(filter.flags, IrcMessage::None); 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")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Numeric); QCOMPARE(filter.flags, IrcMessage::None); QVERIFY(waitForWritten(":server 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Numeric); QCOMPARE(filter.flags, IrcMessage::None); filter.property = "content"; QVERIFY(waitForWritten(":communi!ident@host PRIVMSG #communi :hi all")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Private); QCOMPARE(filter.flags, IrcMessage::Own); QCOMPARE(filter.value.toString(), QString("hi all")); filter.property = "content"; QVERIFY(waitForWritten(":jpnurmi!ident@host PRIVMSG #communi :+hello there, communi")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Private); QCOMPARE(filter.flags, IrcMessage::Identified); QCOMPARE(filter.value.toString(), QString("hello there, communi")); filter.property = "content"; QVERIFY(waitForWritten(":Guest1234!ident@host PRIVMSG #communi :-hi communi")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Private); QCOMPARE(filter.flags, IrcMessage::Unidentified); QCOMPARE(filter.value.toString(), QString("hi communi")); filter.property = "content"; QVERIFY(waitForWritten(":communi!ident@host NOTICE #communi :hi all")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Notice); QCOMPARE(filter.flags, IrcMessage::Own); QCOMPARE(filter.value.toString(), QString("hi all")); filter.property = "content"; QVERIFY(waitForWritten(":jpnurmi!ident@host NOTICE #communi :+hello there, communi")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Notice); QCOMPARE(filter.flags, IrcMessage::Identified); QCOMPARE(filter.value.toString(), QString("hello there, communi")); filter.property = "content"; QVERIFY(waitForWritten(":Guest1234!ident@host NOTICE #communi :-hi communi")); QCOMPARE(filter.count, ++count); QCOMPARE(filter.type, IrcMessage::Notice); QCOMPARE(filter.flags, IrcMessage::Unidentified); QCOMPARE(filter.value.toString(), QString("hi communi")); } void tst_IrcConnection::testMessageBuilder() { connection->open(); QVERIFY(waitForOpened()); MsgFilter filter; connection->installMessageFilter(&filter); QVERIFY(waitForWritten(":my.irc.ser.ver 001 communi :Welcome...")); 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")); 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")); QVERIFY(waitForWritten(":my.irc.ser.ver 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server")); filter.property = "mask"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("#communi")); filter.property = "ident"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("~jpnurmi")); filter.property = "host"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("qt/jpnurmi")); filter.property = "server"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("his.irc.ser.ver")); filter.property = "nick"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("jpnurmi")); filter.property = "away"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toBool(), true); filter.property = "servOp"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toBool(), true); filter.property = "realName"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0 J-P Nurmi")); QCOMPARE(filter.value.toString(), QString("J-P Nurmi")); filter.property = "realName"; QVERIFY(waitForWritten(":my.irc.ser.ver 352 communi #communi ~jpnurmi qt/jpnurmi his.irc.ser.ver jpnurmi G*@ :0")); QCOMPARE(filter.value.toString(), QString()); } void tst_IrcConnection::testSendCommand() { IrcConnection conn; QVERIFY(!conn.sendCommand(0)); QVERIFY(!conn.sendCommand(IrcCommand::createQuit())); TestProtocol* protocol = new TestProtocol(connection); FriendlyConnection* friendly = static_cast(connection.data()); friendly->setProtocol(protocol); QCOMPARE(friendly->protocol(), protocol); QCOMPARE(protocol->connection(), connection.data()); connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->sendCommand(IrcCommand::createQuit())); QVERIFY(!connection->sendCommand(0)); QVERIFY(protocol->written.contains("QUIT")); } void tst_IrcConnection::testSendData() { IrcConnection conn; QVERIFY(!conn.sendData("QUIT")); TestProtocol* protocol = new TestProtocol(connection); FriendlyConnection* friendly = static_cast(connection.data()); friendly->setProtocol(protocol); QCOMPARE(friendly->protocol(), protocol); QCOMPARE(protocol->connection(), connection.data()); connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->sendData("QUIT")); QVERIFY(protocol->written.contains("QUIT")); } class TestFilter : public QObject, public IrcMessageFilter, public IrcCommandFilter { Q_OBJECT Q_INTERFACES(IrcMessageFilter IrcCommandFilter) public: void clear() { commitSuicide = false; messageFiltered = 0; commandFiltered = 0; messageFilterEnabled = false; commandFilterEnabled = false; } bool messageFilter(IrcMessage*) { ++messageFiltered; if (commitSuicide) delete this; return messageFilterEnabled; } bool commandFilter(IrcCommand*) { ++commandFiltered; if (commitSuicide) delete this; return commandFilterEnabled; } bool commitSuicide; int messageFiltered; int commandFiltered; bool messageFilterEnabled; bool commandFilterEnabled; }; void tst_IrcConnection::testMessageFilter() { Irc::registerMetaTypes(); QSignalSpy messageSpy(connection, SIGNAL(messageReceived(IrcMessage*))); QVERIFY(messageSpy.isValid()); int messageCount = 0; TestFilter filter1; QScopedPointer filter2(new TestFilter); QScopedPointer filter3(new TestFilter); filter1.clear(); filter2->clear(); filter3->clear(); connection->installMessageFilter(&filter1); connection->installMessageFilter(filter2.data()); connection->installMessageFilter(filter3.data()); connection->open(); QVERIFY(waitForOpened()); QVERIFY(waitForWritten(":moorcock.freenode.net 001 communi :Welcome to the freenode Internet Relay Chat Network communi")); QCOMPARE(filter1.messageFiltered, 1); QCOMPARE(filter2->messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), ++messageCount); filter1.clear(); filter2->clear(); filter3->clear(); filter3->messageFilterEnabled = true; 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")); QCOMPARE(filter1.messageFiltered, 0); QCOMPARE(filter2->messageFiltered, 0); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), messageCount); filter1.clear(); filter2->clear(); filter3->clear(); filter2->messageFilterEnabled = true; 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")); QCOMPARE(filter1.messageFiltered, 0); QCOMPARE(filter2->messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), messageCount); filter1.clear(); filter2->clear(); filter3->clear(); filter1.messageFilterEnabled = true; QVERIFY(waitForWritten(":moorcock.freenode.net 005 communi EXTBAN=$,arxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server")); QCOMPARE(filter1.messageFiltered, 1); QCOMPARE(filter2->messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), messageCount); filter1.clear(); filter2->clear(); filter3->clear(); QVERIFY(waitForWritten(":moorcock.freenode.net 375 communi :- moorcock.freenode.net Message of the Day -")); QCOMPARE(filter1.messageFiltered, 1); QCOMPARE(filter2->messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), ++messageCount); // a deleted filter gets removed filter2.reset(); filter1.clear(); filter3->clear(); QVERIFY(waitForWritten(":moorcock.freenode.net 372 communi :- Welcome to moorcock.freenode.net in ...")); QCOMPARE(filter1.messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 1); QCOMPARE(messageSpy.count(), ++messageCount); QVERIFY(waitForWritten(":moorcock.freenode.net 376 communi :End of /MOTD command.")); messageCount += 2; // RPL_ENDOFMOTD + IrcMotdMessage QCOMPARE(messageSpy.count(), messageCount); // double filters connection->installMessageFilter(&filter1); connection->installMessageFilter(filter3.data()); filter1.clear(); filter3->clear(); QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #freenode")); QCOMPARE(filter1.messageFiltered, 2); QCOMPARE(filter3->messageFiltered, 2); QCOMPARE(messageSpy.count(), ++messageCount); // remove & enable double filter filter1.clear(); filter3->clear(); filter1.messageFilterEnabled = true; connection->removeMessageFilter(filter3.data()); QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #communi")); QCOMPARE(filter1.messageFiltered, 1); QCOMPARE(filter3->messageFiltered, 0); QCOMPARE(messageSpy.count(), messageCount); // remove & delete filter3.reset(); filter1.clear(); connection->removeMessageFilter(&filter1); QVERIFY(waitForWritten(":communi!~communi@hidd.en PART #communi")); QCOMPARE(filter1.messageFiltered, 0); QCOMPARE(messageSpy.count(), ++messageCount); // commit a suicide & filter QPointer suicidal1 = new TestFilter; connection->installMessageFilter(suicidal1); suicidal1->clear(); suicidal1->messageFilterEnabled = true; suicidal1->commitSuicide = true; QVERIFY(waitForWritten(":communi!~communi@hidd.en PART #freenode")); QCOMPARE(messageSpy.count(), messageCount); QVERIFY(!suicidal1); // commit a suicide & don't filter QPointer suicidal2 = new TestFilter; connection->installMessageFilter(suicidal2); suicidal2->clear(); suicidal2->commitSuicide = true; QVERIFY(waitForWritten(":communi!~communi@hidd.en JOIN #qt")); QVERIFY(!suicidal2); } void tst_IrcConnection::testCommandFilter() { TestProtocol* protocol = new TestProtocol(connection); FriendlyConnection* friendly = static_cast(connection.data()); friendly->setProtocol(protocol); QCOMPARE(friendly->protocol(), protocol); QCOMPARE(protocol->connection(), connection.data()); TestFilter filter1; QScopedPointer filter2(new TestFilter); QScopedPointer filter3(new TestFilter); filter1.clear(); filter2->clear(); filter3->clear(); connection->installCommandFilter(&filter1); connection->installCommandFilter(filter2.data()); connection->installCommandFilter(filter3.data()); connection->open(); QVERIFY(waitForOpened()); connection->sendCommand(IrcCommand::createJoin("#freenode")); QCOMPARE(filter1.commandFiltered, 1); QCOMPARE(filter2->commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(!protocol->written.isEmpty()); protocol->written.clear(); filter1.clear(); filter2->clear(); filter3->clear(); filter3->commandFilterEnabled = true; connection->sendCommand(IrcCommand::createJoin("#communi")); QCOMPARE(filter1.commandFiltered, 0); QCOMPARE(filter2->commandFiltered, 0); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(protocol->written.isEmpty()); protocol->written.clear(); filter1.clear(); filter2->clear(); filter3->clear(); filter2->commandFilterEnabled = true; connection->sendCommand(IrcCommand::createJoin("#qt")); QCOMPARE(filter1.commandFiltered, 0); QCOMPARE(filter2->commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(protocol->written.isEmpty()); protocol->written.clear(); filter1.clear(); filter2->clear(); filter3->clear(); filter1.commandFilterEnabled = true; connection->sendCommand(IrcCommand::createPart("#freenode")); QCOMPARE(filter1.commandFiltered, 1); QCOMPARE(filter2->commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(protocol->written.isEmpty()); protocol->written.clear(); filter1.clear(); filter2->clear(); filter3->clear(); connection->sendCommand(IrcCommand::createPart("#communi")); QCOMPARE(filter1.commandFiltered, 1); QCOMPARE(filter2->commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(!protocol->written.isEmpty()); // a deleted filter gets removed filter2.reset(); filter1.clear(); filter3->clear(); protocol->written.clear(); connection->sendCommand(IrcCommand::createPart("#qt")); QCOMPARE(filter1.commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 1); QVERIFY(!protocol->written.isEmpty()); // double filters connection->installCommandFilter(&filter1); connection->installCommandFilter(filter3.data()); filter1.clear(); filter3->clear(); protocol->written.clear(); connection->sendCommand(IrcCommand::createJoin("#freenode")); QCOMPARE(filter1.commandFiltered, 2); QCOMPARE(filter3->commandFiltered, 2); QVERIFY(!protocol->written.isEmpty()); // remove & enable double filter filter1.clear(); filter3->clear(); filter1.commandFilterEnabled = true; connection->removeCommandFilter(filter3.data()); protocol->written.clear(); connection->sendCommand(IrcCommand::createJoin("#communi")); QCOMPARE(filter1.commandFiltered, 1); QCOMPARE(filter3->commandFiltered, 0); QVERIFY(protocol->written.isEmpty()); // remove & delete filter3.reset(); filter1.clear(); connection->removeCommandFilter(&filter1); protocol->written.clear(); connection->sendCommand(IrcCommand::createJoin("#qt")); QCOMPARE(filter1.commandFiltered, 0); QVERIFY(!protocol->written.isEmpty()); // commit a suicide QPointer suicidal = new TestFilter; connection->installCommandFilter(suicidal); suicidal->commitSuicide = true; connection->sendCommand(IrcCommand::createPart("#qt")); QVERIFY(!suicidal); } void tst_IrcConnection::testDebug() { QString str; QDebug dbg(&str); dbg << static_cast(0); QCOMPARE(str.trimmed(), QString::fromLatin1("IrcConnection(0x0)")); str.clear(); IrcConnection connection; dbg << &connection; QVERIFY(QRegExp("IrcConnection\\(0x[0-9A-Fa-f]+\\) ").exactMatch(str)); str.clear(); connection.setHost("irc.freenode.net"); dbg << &connection; QVERIFY(QRegExp("IrcConnection\\(0x[0-9A-Fa-f]+, irc.freenode.net\\) ").exactMatch(str)); str.clear(); connection.setDisplayName("Freenode"); dbg << &connection; QVERIFY(QRegExp("IrcConnection\\(0x[0-9A-Fa-f]+, Freenode\\) ").exactMatch(str)); str.clear(); dbg << IrcConnection::Connected; QCOMPARE(str.trimmed(), QString::fromLatin1("Connected")); str.clear(); } void tst_IrcConnection::testWarnings() { connection->open(); QVERIFY(waitForOpened()); QVERIFY(connection->isActive()); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setHost() has no effect until re-connect"); connection->setHost("foo"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setPort() has no effect until re-connect"); connection->setPort(1234); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setUserName() has no effect until re-connect"); connection->setUserName("foo"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setRealName() has no effect until re-connect"); connection->setRealName("foo"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setPassword() has no effect until re-connect"); connection->setPassword("foo"); QTest::ignoreMessage(QtWarningMsg, "IrcConnection::setSaslMechanism() has no effect until re-connect"); connection->setSaslMechanism("PLAIN"); } class FakeQmlConnection : public IrcConnection { Q_OBJECT friend class tst_IrcConnection; public slots: // -Wno-overloaded-virtual QVariant createCtcpReply(const QVariant& request) { return QVariant::fromValue(IrcConnection::createCtcpReply(request.value())); } }; void tst_IrcConnection::testCtcp() { FriendlyConnection* friendly = static_cast(connection.data()); // PING IrcMessage* msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1PING timestamp\1", connection); QScopedPointer pingRequest(qobject_cast(msg)); QVERIFY(pingRequest.data()); QScopedPointer pingReply(friendly->createCtcpReply(pingRequest.data())); QVERIFY(pingReply.data()); QCOMPARE(pingReply->type(), IrcCommand::CtcpReply); QCOMPARE(pingReply->toString(), QString("NOTICE nick :\1PING timestamp\1")); // TIME msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1TIME\1", connection); QScopedPointer timeRequest(qobject_cast(msg)); QVERIFY(timeRequest); QScopedPointer timeReply(friendly->createCtcpReply(timeRequest.data())); QVERIFY(timeReply.data()); QCOMPARE(timeReply->type(), IrcCommand::CtcpReply); QCOMPARE(timeReply->toString(), QString("NOTICE nick :\1TIME %1\1").arg(QLocale().toString(QDateTime::currentDateTime(), QLocale::ShortFormat))); // VERSION msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1VERSION\1", connection); QScopedPointer versionRequest(qobject_cast(msg)); QVERIFY(versionRequest.data()); QScopedPointer versionReply(friendly->createCtcpReply(versionRequest.data())); QVERIFY(versionReply.data()); QCOMPARE(versionReply->type(), IrcCommand::CtcpReply); QVERIFY(versionReply->toString().startsWith("NOTICE nick :\1VERSION ")); QVERIFY(versionReply->toString().contains(Irc::version())); QVERIFY(versionReply->toString().endsWith("\1")); // SOURCE msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1SOURCE\1", connection); QScopedPointer sourceRequest(qobject_cast(msg)); QVERIFY(sourceRequest.data()); QScopedPointer sourceReply(friendly->createCtcpReply(sourceRequest.data())); QVERIFY(sourceReply.data()); QCOMPARE(sourceReply->type(), IrcCommand::CtcpReply); QVERIFY(sourceReply->toString().startsWith("NOTICE nick :\1SOURCE ")); QVERIFY(sourceReply->toString().contains("http://")); QVERIFY(sourceReply->toString().endsWith("\1")); // CLIENTINFO msg = IrcMessage::fromData(":nick!user@host PRIVMSG communi :\1CLIENTINFO\1", connection); QScopedPointer infoRequest(qobject_cast(msg)); QVERIFY(infoRequest.data()); QScopedPointer infoReply(friendly->createCtcpReply(infoRequest.data())); QVERIFY(infoReply.data()); QCOMPARE(infoReply->type(), IrcCommand::CtcpReply); QVERIFY(infoReply->toString().startsWith("NOTICE nick :\1CLIENTINFO ")); QVERIFY(infoReply->toString().contains("PING")); QVERIFY(infoReply->toString().contains("TIME")); QVERIFY(infoReply->toString().contains("VERSION")); QVERIFY(infoReply->toString().contains("SOURCE")); QVERIFY(infoReply->toString().endsWith("\1")); // QML compatibility FakeQmlConnection qmlConnection; qmlConnection.setUserName("user"); qmlConnection.setNickName("nick"); qmlConnection.setRealName("real"); qmlConnection.setPassword("secret"); qmlConnection.setHost("127.0.0.1"); qmlConnection.setPort(server->serverPort()); TestProtocol* qmlProtocol = new TestProtocol(&qmlConnection); qmlConnection.setProtocol(qmlProtocol); qmlConnection.open(); QVERIFY(server->waitForNewConnection(200)); QAbstractSocket* qmlServerSocket = server->nextPendingConnection(); QVERIFY(qmlServerSocket); QAbstractSocket* qmlClientSocket = qmlConnection.socket(); QVERIFY(qmlClientSocket); QVERIFY(qmlClientSocket->waitForConnected(200)); qmlProtocol->written.clear(); qmlServerSocket->write(":nick!user@host PRIVMSG communi :\1PING qml\1\r\n"); QVERIFY(qmlServerSocket->waitForBytesWritten(1000)); QVERIFY(qmlClientSocket->waitForReadyRead(1000)); QCOMPARE(qmlProtocol->written, QByteArray("NOTICE nick :\1PING qml\1")); connection->open(); QVERIFY(waitForOpened()); TestProtocol* protocol = new TestProtocol(friendly); friendly->setProtocol(protocol); QCOMPARE(friendly->protocol(), protocol); QCOMPARE(protocol->connection(), friendly); // PING protocol->written.clear(); QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1PING timestamp\1\r\n")); QCOMPARE(protocol->written, QByteArray("NOTICE nick :\1PING timestamp\1")); // TIME protocol->written.clear(); QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1TIME\1\r\n")); QVERIFY(protocol->written.startsWith("NOTICE nick :\1TIME ")); QVERIFY(protocol->written.endsWith("\1")); // VERSION protocol->written.clear(); QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1VERSION\1\r\n")); QVERIFY(protocol->written.startsWith("NOTICE nick :\1VERSION ")); QVERIFY(protocol->written.contains(Irc::version().toUtf8())); QVERIFY(protocol->written.endsWith("\1")); // SOURCE protocol->written.clear(); QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1SOURCE\1\r\n")); QVERIFY(protocol->written.startsWith("NOTICE nick :\1SOURCE ")); QVERIFY(protocol->written.contains("http://")); QVERIFY(protocol->written.endsWith("\1")); // CLIENTINFO protocol->written.clear(); QVERIFY(waitForWritten(":nick!user@host PRIVMSG communi :\1CLIENTINFO\1\r\n")); QVERIFY(protocol->written.startsWith("NOTICE nick :\1CLIENTINFO ")); QVERIFY(protocol->written.contains("PING")); QVERIFY(protocol->written.contains("TIME")); QVERIFY(protocol->written.contains("VERSION")); QVERIFY(protocol->written.contains("SOURCE")); QVERIFY(protocol->written.endsWith("\1")); } QTEST_MAIN(tst_IrcConnection) #include "tst_ircconnection.moc"