BDecoderTest.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2006-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 "../BDecoder.h"
00025 #include "../BDictionary.h"
00026 #include "../BErrorItem.h"
00027 #include "../BList.h"
00028 #include "../BInt.h"
00029 #include "../BString.h"
00030 #include "cppunit/extensions/HelperMacros.h"
00031 #include <sstream>
00032 #include <limits>
00033 
00034 namespace Protocols {
00035 namespace BitTorrent {
00036 namespace Bencoding {
00037 namespace Testing {
00038 
00040 class BDecoderTest : public CppUnit::TestFixture
00041 {
00042     CPPUNIT_TEST_SUITE(BDecoderTest);
00043     CPPUNIT_TEST(testCtors);
00044     CPPUNIT_TEST(testReadTorrent);
00045     CPPUNIT_TEST(testMalFormedRead);
00046     CPPUNIT_TEST(testReadDictionary);
00047     CPPUNIT_TEST(testReadInvalidList);
00048     CPPUNIT_TEST(testReadValidQInt64);
00049     CPPUNIT_TEST(testReadOverflowingQInt64);
00050     CPPUNIT_TEST(testReadQInt64Max);
00051     CPPUNIT_TEST(testReadQInt64Min);
00052     CPPUNIT_TEST(testReadQuint64Max);
00053     CPPUNIT_TEST(testReadZero);
00054     CPPUNIT_TEST(testReadNegative);
00055     CPPUNIT_TEST(testReadValidListOneElement);
00056     CPPUNIT_TEST(testReadStringWithTooManyElements);
00057     CPPUNIT_TEST(testReadStringWithNoElement);
00058     CPPUNIT_TEST(testReadStringWithOneElement);
00059     CPPUNIT_TEST(testReadStringWithWrongCount);
00060     CPPUNIT_TEST(testReadDoubleAsInt);
00061     CPPUNIT_TEST_SUITE_END();
00062 
00063     auto_ptr <BItem> m_item;
00064 
00065 private:
00066     QByteArray getBytes(const char *rawData) const
00067     {
00068         size_t len = strlen (rawData);
00069         return QByteArray::fromRawData (rawData, len);
00070     }
00071 
00072     template<class T>
00073     QByteArray stringifyNum(T num) const
00074     {
00075         std::stringstream ss;
00076         ss << "i"
00077            << num
00078            << "e";
00079         std::string s = ss.str();
00080         return QByteArray (s.c_str());
00081     }
00082 
00083     const BInt *getBInt(const BItem *item) const
00084     {
00085         const BInt *bInt = dynamic_cast <const BInt *> (item);
00086         return bInt;
00087     }
00088 
00089     const BInt *getBInt() const
00090     {
00091         return getBInt(m_item.get());
00092     }
00093 
00094     const BString *getBString(const BItem *item) const
00095     {
00096         const BString *str = dynamic_cast <const BString *> (item);
00097         return str;
00098     }
00099 
00100     const BList *getBList(const BItem *item) const
00101     {
00102         const BList *list = dynamic_cast <const BList *> (item);
00103         return list;
00104     }
00105 
00106     const BDictionary *getBDictionary(const BItem *item) const
00107     {
00108         const BDictionary *dict = dynamic_cast <const BDictionary *>(item);
00109         return dict;
00110     }
00111 
00112     QByteArray readTorrentFile (const char *fileName)
00113     {
00114         QFile torrentFile (fileName);
00115         torrentFile.open (QIODevice::ReadOnly);
00116         QByteArray buffer = torrentFile.readAll();
00117         return buffer;
00118     }
00119 
00120 public:
00121     BDecoderTest();
00122 
00123     void testCtors()
00124     {
00125         QByteArray emptyBuffer;
00126         {
00127             BDecoder bDecoder (emptyBuffer);
00128             CPPUNIT_ASSERT (bDecoder.hasReadAll());
00129             CPPUNIT_ASSERT (!bDecoder.hasReadPassEnd());
00130         }
00131         QByteArray erronousBuffer (4, '\0');
00132         {
00133             BDecoder bDecoder (erronousBuffer);
00134             CPPUNIT_ASSERT (!bDecoder.hasReadAll());
00135             CPPUNIT_ASSERT (!bDecoder.hasReadPassEnd());
00136             m_item = bDecoder.readNext();
00137             CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00138         }
00139     }
00140 
00141     /* "d3:stri0e4:key2l7:calitkoi20ei-240eee" should indent as.. */
00142     /*               "d
00143                         3:str
00144                         i0e
00145                         4:key2
00146                         l
00147                             7:calitko
00148                             i20e
00149                             i-240e
00150                         e
00151                       e"        ;*/
00152     void testReadDictionary()
00153     {
00154         BDecoder bDecoder (getBytes("d3:stri0e4:key2l7:calitkoi20ei-240eee"));
00155         CPPUNIT_ASSERT (!bDecoder.hasReadAll());
00156         CPPUNIT_ASSERT (!bDecoder.hasReadPassEnd());
00157 
00158         m_item = bDecoder.readNext();
00159         CPPUNIT_ASSERT (bDecoder.hasReadAll());
00160         CPPUNIT_ASSERT (!bDecoder.hasReadPassEnd());
00161         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BDictionary));
00162 
00163         const BDictionary *dict = getBDictionary(m_item.get());
00164         CPPUNIT_ASSERT (dict);
00165 
00166         const BInt *bint = getBInt(dict->item("str"));
00167         CPPUNIT_ASSERT (bint);
00168         CPPUNIT_ASSERT (bint->value() == 0);
00169 
00170         const BList *blist = getBList(dict->item ("key2"));
00171         CPPUNIT_ASSERT (blist);
00172         const BString *bstr = getBString(blist->item (0));
00173         CPPUNIT_ASSERT (bstr);
00174         CPPUNIT_ASSERT (bstr->value() == "calitko");
00175 
00176         bint = getBInt(blist->item (1));
00177         CPPUNIT_ASSERT (bint);
00178         CPPUNIT_ASSERT (bint->value() == 20);
00179 
00180         bint = getBInt(blist->item (2));
00181         CPPUNIT_ASSERT (bint);
00182         CPPUNIT_ASSERT (bint->value() == -240);
00183     }
00184 
00185     void testMalFormedRead()
00186     {
00187         BDecoder bDecoder (getBytes ("d3:sti0e4:key2l7:calitkoi20ei-240eee"));
00188         CPPUNIT_ASSERT (!bDecoder.hasReadAll());
00189         CPPUNIT_ASSERT (!bDecoder.hasReadPassEnd());
00190         m_item = bDecoder.readNext();
00191         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00192     }
00193 
00194     void testReadStringWithTooManyElements()
00195     {
00196         BDecoder bDecoder (getBytes ("33333:sti0e4:key2l7:calitkoi20ei-240eee"));
00197         m_item = bDecoder.readNext();
00198         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00199     }
00200 
00201     void testReadStringWithNoElement()
00202     {
00203         BDecoder bDecoder (getBytes ("0:0eee"));
00204         m_item = bDecoder.readNext();
00205         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00206     }
00207 
00208     void testReadStringWithOneElement()
00209     {
00210         BDecoder bDecoder (getBytes ("1:1eee"));
00211         m_item = bDecoder.readNext();
00212         const BString* str = getBString(m_item.get());
00213         CPPUNIT_ASSERT (str);
00214         CPPUNIT_ASSERT (str->value() == "1");
00215     }
00216 
00217     void testReadStringWithWrongCount()
00218     {
00219         BDecoder bDecoder (getBytes("5:1eee"));
00220         m_item = bDecoder.readNext();
00221         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00222     }
00223 
00224     void testReadValidQInt64()
00225     {
00226         BDecoder bDecoder (getBytes ("i33333e"));
00227         m_item = bDecoder.readNext();
00228         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BInt));
00229     }
00230 
00231     void testReadOverflowingQInt64()
00232     {
00233         BDecoder bDecoder (getBytes ("i33333333333333333333333333333333333e"));
00234         m_item = bDecoder.readNext();
00235         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00236     }
00237 
00238     void testReadQInt64Max()
00239     {
00240         BDecoder bDecoder (stringifyNum (std::numeric_limits<qint64>::max()));
00241         m_item = bDecoder.readNext();
00242         CPPUNIT_ASSERT (getBInt());
00243         CPPUNIT_ASSERT (getBInt()->value() == std::numeric_limits<qint64>::max());
00244     }
00245 
00246     void testReadQInt64Min()
00247     {
00248         BDecoder bDecoder (stringifyNum (std::numeric_limits<qint64>::min()));
00249         m_item = bDecoder.readNext();
00250         CPPUNIT_ASSERT (getBInt());
00251         CPPUNIT_ASSERT (getBInt()->value() == std::numeric_limits<qint64>::min());
00252     }
00253 
00254     void testReadQuint64Max()
00255     {
00256         BDecoder bDecoder (stringifyNum (std::numeric_limits<quint64>::max()));
00257         m_item = bDecoder.readNext();
00258         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00259     }
00260 
00261     void testReadZero()
00262     {
00263         BDecoder bDecoder (stringifyNum (0));
00264         m_item = bDecoder.readNext();
00265         CPPUNIT_ASSERT (getBInt());
00266         CPPUNIT_ASSERT (getBInt()->value() == 0);
00267     }
00268 
00269     void testReadNegative()
00270     {
00271         BDecoder bDecoder (stringifyNum (-1));
00272         m_item = bDecoder.readNext();
00273         CPPUNIT_ASSERT (getBInt());
00274         CPPUNIT_ASSERT (getBInt()->value() == -1);
00275     }
00276 
00277     void testReadDoubleAsInt()
00278     {
00279         BDecoder bDecoder (stringifyNum (1.23));
00280         m_item = bDecoder.readNext();
00281         CPPUNIT_ASSERT (typeid (*m_item) == typeid (BErrorItem));
00282     }
00283 
00284     void testReadValidListOneElement()
00285     {
00286         BDecoder bDecoder (getBytes ("li123ee"));
00287         m_item = bDecoder.readNext();
00288         const BList *pList = getBList(m_item.get());
00289         CPPUNIT_ASSERT (pList);
00290         CPPUNIT_ASSERT (pList->size() == 1);
00291         const BInt *bInt = getBInt(pList->item(0));
00292         CPPUNIT_ASSERT (bInt);
00293         CPPUNIT_ASSERT (bInt->value() == 123);
00294     }
00295 
00296     void testReadInvalidList()
00297     {
00298         const char list[] = "li1ei1ei1ei1ei1ei1ei1ei1rei1ei1ei1e";
00299         BDecoder bDecoder (getBytes (list));
00300         m_item = bDecoder.readNext();
00301         CPPUNIT_ASSERT( typeid (*m_item) == typeid (BErrorItem));
00302     }
00303 
00304     void testReadTorrent()
00305     {
00306         QByteArray buffer = readTorrentFile ("Protocols/BitTorrent/Torrents/Testing/Samples/sample.torrent");
00307         BDecoder bDecoder (buffer);
00308         CPPUNIT_ASSERT (!bDecoder.hasReadAll());
00309         m_item = bDecoder.readNext();
00310 
00311         CPPUNIT_ASSERT (bDecoder.hasReadAllCorrectly());
00312         const BDictionary *dict = getBDictionary (m_item.get());
00313         CPPUNIT_ASSERT (dict);
00314 
00315         const BString *str = getBString (dict->item ("announce"));
00316         CPPUNIT_ASSERT (str);
00317         CPPUNIT_ASSERT (str->value() == "http://tracker.calitko.org/torrents.php?passkey=d7c034325e7274bb2s346c6fc5b71307");
00318 
00319         str = getBString (dict->item ("created by"));
00320         CPPUNIT_ASSERT (str);
00321         CPPUNIT_ASSERT (str->value() == "BitComet/0.54");
00322 
00323         const BInt *bint = getBInt (dict->item ("creation date"));
00324         CPPUNIT_ASSERT (bint);
00325         CPPUNIT_ASSERT (bint->value() == 1135459991);
00326 
00327         const BDictionary *info_dict = getBDictionary (dict->item ("info"));
00328         CPPUNIT_ASSERT (info_dict);
00329 
00330         const BList *list = getBList (info_dict->item ("files"));
00331         CPPUNIT_ASSERT (list);
00332 
00333         const BDictionary *dict1 = getBDictionary (list->item (1));
00334         CPPUNIT_ASSERT (dict1);
00335 
00336         list = getBList (dict1->item ("path.utf-8"));
00337         CPPUNIT_ASSERT (list);
00338 
00339         str = getBString (list->item (0));
00340         CPPUNIT_ASSERT (str->value() == "Calitko_doc.zip");
00341 
00342         str = getBString (info_dict->item ("pieces"));
00343         CPPUNIT_ASSERT (str);
00344         CPPUNIT_ASSERT (str->value().length() == 6540);
00345     }
00346 };
00347 
00348 CPPUNIT_TEST_SUITE_REGISTRATION(BDecoderTest);
00349 
00351 BDecoderTest::BDecoderTest()
00352     : m_item()
00353 {
00354 }
00355 
00356 } // namespace Testing
00357 } // namespace Bencoding
00358 } // namespace BitTorrent
00359 } // namespace Protocols