diff --git a/kimap/tests/kimaptest/fakeserver.h b/kimap/tests/kimaptest/fakeserver.h index cc309608b..3ce16fc5c 100644 --- a/kimap/tests/kimaptest/fakeserver.h +++ b/kimap/tests/kimaptest/fakeserver.h @@ -1,75 +1,214 @@ /* Copyright (C) 2008 Omat Holding B.V. Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FAKESERVER_H #define FAKESERVER_H #include #include #include #include #include namespace KIMAP { class ImapStreamParser; } Q_DECLARE_METATYPE( QList ) +/** + * Pretends to be an IMAP server for the purposes of unit tests. + * + * FakeServer does not really understand the IMAP protocol. Instead, + * you give it a script, or scenario, that lists how an IMAP session + * exchange should go. When it receives the client parts of the + * scenario, it will respond with the following server parts. + * + * The server can be furnished with several scenarios. The first + * scenario will be played out to the first client that connects, the + * second scenario to the second client connection and so on. + * + * The fake server runs as a separate thread in the same process it + * is started from, and listens for connections on port 5989 on the + * local machine. + * + * Scenarios are in the form of protocol messages, with a tag at the + * start to indicate whether it is message that will be sent by the + * client ("C:") or a response that should be sent by the server + * ("S:"). For example: + * @code + * C: A000001 LIST "" * + * S: * LIST ( \HasChildren ) / INBOX + * S: * LIST ( \HasNoChildren ) / INBOX/&AOQ- &APY- &APw- @ &IKw- + * S: * LIST ( \HasChildren ) / INBOX/lost+found + * S: * LIST ( \HasNoChildren ) / "INBOX/lost+found/Calendar Public-20080128" + * S: A000001 OK LIST completed + * @endcode + * + * A line starting with X indicates that the connection should be + * closed by the server. This should be the last line in the + * scenario. For example, the following simulates the server closing + * the connection after receiving too many bad commands: + * @code + * C: A000001 madhatter + * S: A000001 BAD Command madhatter + * X + * @endcode + * + * FakeServer::preauth() and FakeServer::greeting() provide standard + * PREAUTH and OK responses, respectively, that can be used (unmodified) + * as the first line of a scenario. + * + * A typical usage is something like + * @code + * QList scenario; + * scenario << FakeServer::preauth() + * << "C: A000001 CAPABILITY" + * << "S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI" + * << "S: A000001 OK CAPABILITY completed"; + * + * FakeServer fakeServer; + * fakeServer.setScenario( scenario ); + * fakeServer.start(); + * + * KIMAP::Session session( "127.0.0.1", 5989 ); + * KIMAP::CapabilitiesJob *job = new KIMAP::CapabilitiesJob(&session); + * QVERIFY( job->exec() ); + * // check the returned capabilities + * + * fakeServer.quit(); + * @endcode + */ class FakeServer : public QThread { Q_OBJECT public: + /** + * Get the default PREAUTH response + * + * This is the initial PREAUTH message that the server + * sends at the start of a session to indicate that the + * user is already authenticated by some other mechanism. + * + * Can be used as the first line in a scenario where + * you want to skip the LOGIN stage of the protocol. + */ static QByteArray preauth(); + /** + * Get the default greeting + * + * This is the initial OK message that the server sends at the + * start of a session to indicate that a LOGIN is required. + * + * Can be used as the first line in a scenario where + * you want to use the LOGIN command. + */ static QByteArray greeting(); FakeServer( QObject* parent = 0 ); ~FakeServer(); + + /** + * Starts the fake IMAP server + * + * You should not call this directly. Use start() instead. + * + * @reimp + */ virtual void run(); + /** + * Removes any previously-added scenarios, and adds a new one + * + * After this, there will only be one scenario, and so the fake + * server will only be able to service a single request. More + * scenarios can be added with addScenario, though. + * + * @see addScenario()\n + * addScenarioFromFile() + */ void setScenario( const QList &scenario ); + /** + * Adds a new scenario + * + * Note that scenarios will be used in the order that clients + * connect. If this is the 5th scenario that has been added + * (bearing in mind that setScenario() resets the scenario + * count), it will be used to service the 5th client that + * connects. + * + * @see addScenarioFromFile() + * + * @param scenario the scenario as a list of messages + */ void addScenario( const QList &scenario ); + /** + * Adds a new scenario from a local file + * + * Note that scenarios will be used in the order that clients + * connect. If this is the 5th scenario that has been added + * (bearing in mind that setScenario() resets the scenario + * count), it will be used to service the 5th client that + * connects. + * + * @see addScenario() + * + * @param fileName the name of the file that contains the + * scenario; it will be split at line + * boundaries, and excess whitespace will + * be trimmed from the start and end of lines + */ void addScenarioFromFile( const QString &fileName ); + /** + * Checks whether a particular scenario has completed + * + * @param scenarioNumber the number of the scenario to check, + * in order of addition/client connection + */ bool isScenarioDone( int scenarioNumber ) const; + /** + * Whether all the scenarios that were added to the fake + * server have been completed. + */ bool isAllScenarioDone() const; private Q_SLOTS: void newConnection(); void dataAvailable(); private: void writeServerPart( int scenarioNumber ); void readClientPart( int scenarioNumber ); QList< QList > m_scenarios; QTcpServer *m_tcpServer; mutable QMutex m_mutex; QList m_clientSockets; QList m_clientParsers; }; #endif diff --git a/kimap/tests/kimaptest/mockjob.h b/kimap/tests/kimaptest/mockjob.h index c6f28fd82..c5eb4deb2 100644 --- a/kimap/tests/kimaptest/mockjob.h +++ b/kimap/tests/kimaptest/mockjob.h @@ -1,50 +1,75 @@ /* This file is part of the KDE project Copyright (C) 2008 Kevin Ottens Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOCKJOB_H #define MOCKJOB_H #include class MockJobPrivate; +/** + * Provides an easy way to send an arbitrary IMAP client command. + */ class MockJob : public KIMAP::Job { Q_OBJECT Q_DECLARE_PRIVATE(MockJob) public: MockJob(KIMAP::Session *session); + /** + * Sets the command to execute. + * + * This should not include the command tag. + */ void setCommand(const QByteArray &command); + /** + * The command that will be sent. + */ QByteArray command() const; + /** + * Whether the command is empty. + * + * Equivalent to command().isEmpty(). + * + * @return @c true if no command is set, @c false otherwise + */ bool isNull() const; + /** + * Starts the job. + * + * Do not call this directly. Use start() instead. + * + * @reimp + */ virtual void doStart(); private slots: void done(); }; #endif