SessionTest.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "generated/SessionDriver.h"
00025 #include "generated/SessionMock.h" // \todo for SessionStatusMock
00026 #include "Imports.cpp"
00027 
00028 namespace Protocols {
00029 namespace BitTorrent {
00030 namespace Transfers {
00031 namespace Testing {
00032 
00034 
00036 class SessionTest : public CppUnit::TestFixture
00037 {
00038     CPPUNIT_TEST_SUITE(SessionTest);
00039     CPPUNIT_TEST (testEstablishSessionSucceedsDirectly);
00040     CPPUNIT_TEST (testEstablishSessionSucceedsAfterOneReadyRead);
00041     CPPUNIT_TEST (testEstablishSessionSucceedsAfterTwoReadyRead);
00042     CPPUNIT_TEST (testEstablishSessionSucceedsAfterThreeReadyRead);
00043     CPPUNIT_TEST (testAcceptConnectionSucceedsDirectly);
00044     CPPUNIT_TEST (testAcceptConnectionSucceedsAfterOneReadyRead);
00045     CPPUNIT_TEST (testAcceptConnectionSucceedsAfterTwoReadyReads);
00046     CPPUNIT_TEST (testAcceptConnectionSucceedsAfterThreeReadyReads);
00047     //CPPUNIT_TEST (testEstablishSessionFailsDirectly);
00048     //CPPUNIT_TEST (testEstablishSessionFailsAfterOneReadyRead);
00049     //CPPUNIT_TEST (testEstablishSessionFailsAfterTwoReadyRead);
00050     // protocol error (e.g. fileId not matched, corrupt message header)
00051     //CPPUNIT_TEST (testSessionChoked);
00052     //CPPUNIT_TEST (testSessionUnchoked);
00053     //CPPUNIT_TEST (testSessionInterested);
00054     //CPPUNIT_TEST (testSessionNotInterested);
00055     //CPPUNIT_TEST (testChokeSession);
00056     //CPPUNIT_TEST (testUnchokeSession);
00057     CPPUNIT_TEST_SUITE_END();
00058 
00059     PeerInfo                        myPeerInfo;
00060     PeerInfo                        remotePeerInfo;
00061     FileId                          fileId;
00062     auto_ptr <TransportMock>        transport;
00063     auto_ptr <SessionStatusMock>    status;
00064     auto_ptr <Session>              session_real;
00065     auto_ptr <SessionDriver>        session;
00066 
00067 public:
00068     void setUp()
00069     {
00070         // \todo set PeerInfo.nodeAddress ???
00071         myPeerInfo.id = QByteArray (20, '1');
00072         remotePeerInfo.id = QByteArray (20, '2');
00073         fileId = QByteArray  (20, '3');
00074 
00075         transport.reset (new TransportMock());
00076         status.reset (new SessionStatusMock());
00077         session_real.reset (new Session (transport->imp, status->imp,
00078                                          myPeerInfo));
00079         session.reset (new SessionDriver (*session_real.get()));
00080     }
00081 
00082     void tearDown()
00083     {
00084         session.reset();
00085         session_real.reset();
00086         status.reset();
00087         transport.reset();
00088     }
00089 
00090     QByteArray makeHandshakeBytes (const FileId &fileId,
00091                                    const PeerInfo &peerInfo)
00092     {
00093         QByteArray handshake;
00094         handshake.append ('\x13');
00095         handshake.append (QByteArray ("BitTorrent protocol"));
00096         handshake.append (QByteArray ("\0\0\0\0\0\0\0\0", 8));
00097         handshake.append (fileId);
00098         handshake.append (peerInfo.id);
00099         return handshake;
00100     }
00101 
00103     void testEstablishSessionSucceedsDirectly()
00104     {
00105         // Prepare the handshakes:
00106         QByteArray outHandshake = makeHandshakeBytes (fileId, myPeerInfo);
00107         QByteArray inHandshake = makeHandshakeBytes (fileId, remotePeerInfo);
00108         // We send our handshake after establishing the transport connection:
00109         transport->calls (session->establish (myPeerInfo, fileId))
00110             .willCall (status->establishingSession())
00111             .returns()
00112             .willCall (transport->write (outHandshake))
00113             .willReturn (true)
00114             .willCall (transport->read (inHandshake.length()))
00115             .willReturn (inHandshake)
00116             .willCall (status->sessionEstablished())
00117             .returns()
00118         .returns();
00119     }
00120 
00121     void scenarioEstablishSessionSucceedsAfterANumberOfReadyReads (int count)
00122     {
00123         Q_ASSERT (count > 0);
00124         // Prepare the handshake bytes:
00125         QByteArray inHandshake = makeHandshakeBytes (fileId, remotePeerInfo);
00126         QByteArray outHandshake = makeHandshakeBytes (fileId, myPeerInfo);
00127         // The transport connection is established:
00128         transport->calls (session->establish (myPeerInfo, fileId))
00129             .willCall (status->establishingSession())
00130             .returns()
00131             .willCall (transport->write (outHandshake))
00132             .willReturn (true)
00133             .willCall (transport->read (inHandshake.length()))
00134             .willReturn (QByteArray())
00135         .returns();
00136         // readyRead() may be called even if not all bytes are buffered:
00137         for (; count > 1; --count) // the last readyRead() will succeed below
00138         {
00139             transport->calls (session->readyRead())
00140                 .willCall (transport->read (inHandshake.length()))
00141                 .willReturn (QByteArray())
00142             .returns();
00143         }
00144         // some bytes have been read by transport, retry reading the handshake:
00145         transport->calls (session->readyRead())
00146             // read complete handshake directly:
00147             .willCall (transport->read (inHandshake.length()))
00148             .willReturn (inHandshake)
00149             // done, connection established:
00150             .willCall (status->sessionEstablished())
00151             .returns()
00152         .returns();
00153     }
00154 
00156     void testEstablishSessionSucceedsAfterOneReadyRead()
00157     {
00158         scenarioEstablishSessionSucceedsAfterANumberOfReadyReads (1);
00159     }
00160 
00162     void testEstablishSessionSucceedsAfterTwoReadyRead()
00163     {
00164         scenarioEstablishSessionSucceedsAfterANumberOfReadyReads (2);
00165     }
00166 
00168     void testEstablishSessionSucceedsAfterThreeReadyRead()
00169     {
00170         scenarioEstablishSessionSucceedsAfterANumberOfReadyReads (3);
00171     }
00172 
00174 
00180     void testAcceptConnectionSucceedsDirectly()
00181     {
00182         QByteArray inHandshake = makeHandshakeBytes (fileId, remotePeerInfo);
00183         QByteArray outHandshake = makeHandshakeBytes (fileId, myPeerInfo);
00184 
00185         call (session->waitRequest())
00186             // report status:
00187             .willCall (status->establishingSession())
00188             .returns()
00189             // read complete handshake directly:
00190             .willCall (transport->read (inHandshake.length()))
00191             .willReturn (inHandshake)
00192             .willCall (status->sessionRequestedFor (fileId))
00193             .returns()
00194         .returns();
00195 
00196         call (session->accept())
00197             // write complete handshake directly:
00198             .willCall (transport->write (outHandshake))
00199             .willReturn (true)
00200             // done, connection established:
00201             .willCall (status->sessionEstablished())
00202             .returns()
00203         .returns();
00204     }
00205 
00207 
00211     void scenarioAcceptConnectionSucceedsAfterANumberOfReadyReads (int count)
00212     {
00213         Q_ASSERT (count > 0);
00214         // Prepare the handshakes:
00215         QByteArray inHandshake = makeHandshakeBytes (fileId, remotePeerInfo);
00216         QByteArray outHandshake = makeHandshakeBytes (fileId, myPeerInfo);
00217 
00218         call (session->waitRequest())
00219             // report status:
00220             .willCall (status->establishingSession())
00221             .returns()
00222             // handshake cannot be read completely:
00223             .willCall (transport->read (inHandshake.length()))
00224             .willReturn (QByteArray())
00225         .returns();
00226         // readyRead() may be called even if not all bytes are buffered:
00227         for (; count > 1; --count) // the last readyRead() will succeed below
00228         {
00229             transport->calls (session->readyRead())
00230                 .willCall (transport->read (inHandshake.length()))
00231                 .willReturn (QByteArray())
00232             .returns();
00233         }
00234         // Some bytes have been read by transport, retry reading the handshake:
00235         transport->calls (session->readyRead())
00236             // read complete handshake directly:
00237             .willCall (transport->read (inHandshake.length()))
00238             .willReturn (inHandshake)
00239             .willCall (status->sessionRequestedFor (fileId))
00240             .returns()
00241         .returns();
00242         // Accept the session for the requested fileId:
00243         call (session->accept())
00244             // write complete handshake directly:
00245             .willCall (transport->write (outHandshake))
00246             .willReturn (true)
00247             // done, connection established:
00248             .willCall (status->sessionEstablished())
00249             .returns()
00250         .returns();
00251     }
00252 
00254     void testAcceptConnectionSucceedsAfterOneReadyRead()
00255     {
00256         scenarioAcceptConnectionSucceedsAfterANumberOfReadyReads (1);
00257     }
00258 
00260     void testAcceptConnectionSucceedsAfterTwoReadyReads()
00261     {
00262         scenarioAcceptConnectionSucceedsAfterANumberOfReadyReads (2);
00263     }
00264 
00266     void testAcceptConnectionSucceedsAfterThreeReadyReads()
00267     {
00268         scenarioAcceptConnectionSucceedsAfterANumberOfReadyReads (3);
00269     }
00270 
00271     // testAcceptConnectionFailsDueToInvalidHandshake()
00272 
00273     void stateEstablished()
00274     {
00275         testEstablishSessionSucceedsDirectly();
00276     }
00277 
00278     /*
00279     template <typename T>
00280     void scenarioSessionStateChangedByPeer (
00281                 ExpectationPair <ExpectedFunction <void ()> > expectation)
00282     {
00283         stateEstablished();
00284 
00285         call (session->readyRead())
00286             .willCall (packetFactory->readPacket(transport->imp))
00287             .willReturn (SharedPointer <Packet> (new T()))
00288             .willCall (expectation)
00289             .returns()
00290         .returns();
00291     }
00292 
00293     void testSessionChoked()
00294     {
00295         scenarioSessionStateChangedByPeer <Choke> (status->sessionChoked());
00296     }
00297 
00298     void testSessionUnchoked()
00299     {
00300         scenarioSessionStateChangedByPeer <Unchoke> (status->sessionUnchoked());
00301     }
00302 
00303     void testSessionInterested()
00304     {
00305         scenarioSessionStateChangedByPeer <Interested>
00306                                             (status->sessionInterested());
00307     }
00308 
00309     void testSessionNotInterested()
00310     {
00311         scenarioSessionStateChangedByPeer <NotInterested>
00312                                             (status->sessionNotInterested());
00313     }
00314 
00315     void testChokeSession()
00316     {
00317         CPPUNIT_ASSERT (false);
00318     }
00319 
00320     void testUnchokeSession()
00321     {
00322         CPPUNIT_ASSERT (false);
00323     }
00324     */
00325 };
00326 
00327 CPPUNIT_TEST_SUITE_REGISTRATION(SessionTest);
00328 
00329 } // namespace Testing
00330 } // namespace Transfers
00331 } // namespace BitTorrent
00332 } // namespace Protocols