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