File indexing completed on 2025-01-05 04:37:24
0001 #ifndef QT_GUI_LIB 0002 #define QT_GUI_LIB 0003 #endif 0004 0005 #include <QLocale> 0006 #include <QObject> 0007 #include <QRandomGenerator> 0008 #include <QtTest> 0009 0010 #include <ctime> 0011 #include <interfaces/queuemanagerinterface.h> 0012 #include <testlib/dummytorrentcreator.h> 0013 #include <torrent/torrentcontrol.h> 0014 #include <torrent/torrentfilestream.h> 0015 #include <unistd.h> 0016 #include <util/error.h> 0017 #include <util/fileops.h> 0018 #include <util/log.h> 0019 #include <util/sha1hashgen.h> 0020 0021 using namespace bt; 0022 0023 const bt::Uint32 TEST_FILE_SIZE = 5 * 1024 * 1024; 0024 0025 class TorrentFileStreamTest : public QEventLoop, public bt::QueueManagerInterface 0026 { 0027 Q_OBJECT 0028 public: 0029 TorrentFileStreamTest(QObject *parent = nullptr) 0030 : QEventLoop(parent) 0031 { 0032 } 0033 0034 bool alreadyLoaded(const bt::SHA1Hash &ih) const override 0035 { 0036 Q_UNUSED(ih); 0037 return false; 0038 } 0039 0040 void mergeAnnounceList(const bt::SHA1Hash &ih, const bt::TrackerTier *trk) override 0041 { 0042 Q_UNUSED(ih); 0043 Q_UNUSED(trk); 0044 } 0045 0046 private Q_SLOTS: 0047 void initTestCase() 0048 { 0049 QLocale::setDefault(QLocale("main")); 0050 bt::InitLog("torrentfilestreamtest.log", false, false); 0051 QVERIFY(creator.createSingleFileTorrent(TEST_FILE_SIZE, "test.avi")); 0052 QVERIFY(creator2.createSingleFileTorrent(TEST_FILE_SIZE, "test2.avi")); 0053 0054 Out(SYS_GEN | LOG_DEBUG) << "Created " << creator.torrentPath() << endl; 0055 Out(SYS_GEN | LOG_DEBUG) << "Created " << creator2.torrentPath() << endl; 0056 try { 0057 tc.init(this, bt::LoadFile(creator.torrentPath()), creator.tempPath() + "tor0", creator.tempPath() + "data/"); 0058 tc.createFiles(); 0059 QVERIFY(tc.hasExistingFiles()); 0060 tc.startDataCheck(false, 0, tc.getStats().total_chunks); 0061 do { 0062 processEvents(AllEvents, 1000); 0063 } while (tc.getStats().status == bt::CHECKING_DATA); 0064 QVERIFY(tc.getStats().completed); 0065 0066 incomplete_tc.init(this, bt::LoadFile(creator2.torrentPath()), creator2.tempPath() + "tor0", creator2.tempPath() + "data/"); 0067 incomplete_tc.createFiles(); 0068 } catch (bt::Error &err) { 0069 Out(SYS_GEN | LOG_DEBUG) << "Failed to load torrent: " << creator.torrentPath() << endl; 0070 QFAIL("Torrent load failure"); 0071 } 0072 } 0073 0074 void cleanupTestCase() 0075 { 0076 } 0077 0078 void testSimple() 0079 { 0080 Out(SYS_GEN | LOG_DEBUG) << "Begin: testSimple() " << endl; 0081 bt::TorrentFileStream::Ptr stream = tc.createTorrentFileStream(0, false, this); 0082 QVERIFY(stream); 0083 QVERIFY(!stream->open(QIODevice::ReadWrite)); 0084 QVERIFY(stream->open(QIODevice::ReadOnly)); 0085 // Simple test read per chunk and verify if it works 0086 QByteArray tmp(tc.getStats().chunk_size, 0); 0087 bt::Uint64 written = 0; 0088 bt::Uint32 idx = 0; 0089 while (written < TEST_FILE_SIZE) { 0090 qint64 ret = stream->read(tmp.data(), tc.getStats().chunk_size); 0091 QVERIFY(ret == tc.getStats().chunk_size); 0092 written += tc.getStats().chunk_size; 0093 0094 // Verify the hash 0095 bt::SHA1Hash hash = bt::SHA1Hash::generate((const bt::Uint8 *)tmp.data(), tmp.size()); 0096 QVERIFY(hash == tc.getTorrent().getHash(idx)); 0097 idx++; 0098 } 0099 0100 stream->close(); 0101 Out(SYS_GEN | LOG_DEBUG) << "End: testSimple() " << endl; 0102 } 0103 0104 void testSeek() 0105 { 0106 Out(SYS_GEN | LOG_DEBUG) << "Begin: testSeek() " << endl; 0107 bt::TorrentFileStream::Ptr stream = tc.createTorrentFileStream(0, false, this); 0108 QVERIFY(stream); 0109 QVERIFY(!stream->open(QIODevice::ReadWrite)); 0110 QVERIFY(stream->open(QIODevice::ReadOnly)); 0111 // Simple test read per chunk and seek somewhat 0112 bt::Uint32 chunk_size = tc.getStats().chunk_size; 0113 QByteArray tmp(chunk_size, 0); 0114 bt::Uint64 written = 0; 0115 bt::Uint32 idx = 0; 0116 while (written < TEST_FILE_SIZE) { 0117 // Chunk size of last chunk can be smaller 0118 if (idx == tc.getStats().total_chunks - 1) { 0119 chunk_size = tc.getStats().chunk_size % tc.getStats().total_bytes; 0120 if (chunk_size == 0) 0121 chunk_size = tc.getStats().chunk_size; 0122 } 0123 0124 // Lets read in two times, first at the back and then at the front 0125 qint64 split = QRandomGenerator::global()->bounded(chunk_size); 0126 while (split == 0) 0127 split = QRandomGenerator::global()->bounded(chunk_size); 0128 0129 QVERIFY(stream->seek(idx * tc.getStats().chunk_size + split)); 0130 qint64 ret = stream->read(tmp.data() + split, chunk_size - split); 0131 QVERIFY(ret == (chunk_size - split)); 0132 written += ret; 0133 0134 QVERIFY(stream->seek(idx * tc.getStats().chunk_size)); 0135 ret = stream->read(tmp.data(), split); 0136 QVERIFY(ret == split); 0137 written += ret; 0138 0139 // Verify the hash 0140 bt::SHA1Hash hash = bt::SHA1Hash::generate((const bt::Uint8 *)tmp.data(), tmp.size()); 0141 QVERIFY(hash == tc.getTorrent().getHash(idx)); 0142 idx++; 0143 } 0144 0145 stream->close(); 0146 Out(SYS_GEN | LOG_DEBUG) << "End: testSeek() " << endl; 0147 } 0148 0149 void testRandomAccess() 0150 { 0151 Out(SYS_GEN | LOG_DEBUG) << "Begin: testRandomAccess() " << endl; 0152 bt::TorrentFileStream::Ptr stream = tc.createTorrentFileStream(0, false, this); 0153 QVERIFY(stream); 0154 QVERIFY(!stream->open(QIODevice::ReadWrite)); 0155 QVERIFY(stream->open(QIODevice::ReadOnly)); 0156 0157 // Read an area of around 5 chunks in at a random location 0158 // And verify the 3 middle chunks 0159 qint64 range_size = tc.getStats().chunk_size * 5; 0160 qint64 off = QRandomGenerator::global()->bounded(100) / 100.0 * (tc.getStats().total_bytes - range_size); 0161 QVERIFY(stream->seek(off)); 0162 0163 Out(SYS_GEN | LOG_DEBUG) << "Reading random range" << endl; 0164 Out(SYS_GEN | LOG_DEBUG) << "range_size = " << range_size << endl; 0165 Out(SYS_GEN | LOG_DEBUG) << "off = " << off << endl; 0166 0167 QByteArray range(range_size, 0); 0168 qint64 bytes_read = 0; 0169 while (bytes_read < range_size) { 0170 qint64 ret = stream->read(range.data() + bytes_read, range_size - bytes_read); 0171 Out(SYS_GEN | LOG_DEBUG) << "ret = " << ret << endl; 0172 Out(SYS_GEN | LOG_DEBUG) << "read = " << bytes_read << endl; 0173 QVERIFY(ret > 0); 0174 bytes_read += ret; 0175 } 0176 0177 { 0178 QFile fptr(stream->path()); 0179 QVERIFY(fptr.open(QIODevice::ReadOnly)); 0180 QByteArray tmp(range_size, 0); 0181 fptr.seek(off); 0182 QVERIFY(fptr.read(tmp.data(), range_size) == range_size); 0183 QVERIFY(tmp == range); 0184 } 0185 0186 QVERIFY(bytes_read == range_size); 0187 0188 // Calculate the offset of the first chunk in range 0189 qint64 chunk_idx = off / tc.getStats().chunk_size; 0190 if (off % tc.getStats().chunk_size != 0) 0191 chunk_idx++; 0192 qint64 chunk_off = chunk_idx * tc.getStats().chunk_size - off; 0193 0194 Out(SYS_GEN | LOG_DEBUG) << "chunk_idx = " << chunk_idx << endl; 0195 Out(SYS_GEN | LOG_DEBUG) << "chunk_off = " << chunk_off << endl; 0196 0197 // Verify the hashes 0198 for (int i = 0; i < 3; i++) { 0199 bt::SHA1Hash hash = bt::SHA1Hash::generate((const bt::Uint8 *)range.data() + chunk_off, tc.getStats().chunk_size); 0200 0201 Out(SYS_GEN | LOG_DEBUG) << "chash = " << hash.toString() << endl; 0202 Out(SYS_GEN | LOG_DEBUG) << "whash = " << tc.getTorrent().getHash(chunk_idx).toString() << endl; 0203 QVERIFY(hash == tc.getTorrent().getHash(chunk_idx)); 0204 chunk_idx++; 0205 chunk_off += tc.getStats().chunk_size; 0206 } 0207 0208 stream->close(); 0209 Out(SYS_GEN | LOG_DEBUG) << "End: testRandomAccess() " << endl; 0210 } 0211 0212 void testMultiSeek() 0213 { 0214 Out(SYS_GEN | LOG_DEBUG) << "Begin: testMultiSeek() " << endl; 0215 bt::TorrentFileStream::Ptr stream = tc.createTorrentFileStream(0, false, this); 0216 QVERIFY(stream); 0217 QVERIFY(!stream->open(QIODevice::ReadWrite)); 0218 QVERIFY(stream->open(QIODevice::ReadOnly)); 0219 0220 QFile fptr(stream->path()); 0221 QVERIFY(fptr.open(QIODevice::ReadOnly)); 0222 for (int i = 0; i < 20; i++) { 0223 qint64 off = QRandomGenerator::global()->bounded(TEST_FILE_SIZE - 100); 0224 // Seek to a random location 0225 QVERIFY(stream->seek(off)); 0226 QByteArray tmp(100, 0); 0227 QVERIFY(stream->read(tmp.data(), 100) == 100); 0228 0229 // Verify those 0230 QByteArray tmp2(100, 0); 0231 fptr.seek(off); 0232 QVERIFY(fptr.read(tmp2.data(), 100) == 100); 0233 QVERIFY(tmp == tmp2); 0234 } 0235 0236 stream->close(); 0237 Out(SYS_GEN | LOG_DEBUG) << "End: testMultiSeek() " << endl; 0238 } 0239 0240 void testStreamingCreate() 0241 { 0242 bt::TorrentFileStream::Ptr a = tc.createTorrentFileStream(0, true, this); 0243 QVERIFY(a); 0244 bt::TorrentFileStream::Ptr b = tc.createTorrentFileStream(0, true, this); 0245 QVERIFY(!b); 0246 a.clear(); 0247 b = tc.createTorrentFileStream(0, true, this); 0248 QVERIFY(b); 0249 } 0250 0251 void testSeekToUndownloadedSection() 0252 { 0253 bt::TorrentFileStream::Ptr a = incomplete_tc.createTorrentFileStream(0, true, this); 0254 QVERIFY(incomplete_tc.getStats().completed == false); 0255 QVERIFY(a->seek(TEST_FILE_SIZE / 2)); 0256 QVERIFY(a->bytesAvailable() == 0); 0257 } 0258 0259 private: 0260 DummyTorrentCreator creator; 0261 DummyTorrentCreator creator2; 0262 bt::TorrentControl tc; 0263 bt::TorrentControl incomplete_tc; 0264 }; 0265 0266 QTEST_MAIN(TorrentFileStreamTest) 0267 0268 #include "torrentfilestreamtest.moc"