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"