1 /*
2     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
3     SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
4 */
5 
6 #include <QTest>
7 
8 #include <future>
9 #include <thread>
10 
11 #include "transfer.h"
12 
13 class TransferTest : public QObject
14 {
15     Q_OBJECT
16 private Q_SLOTS:
testSegmentOnSmallFile()17     void testSegmentOnSmallFile()
18     {
19         // Files smaller than our minimal segment size ought to be transferred in one go
20         // otherwise we have a chance of degrading performance.
21         QCOMPARE(TransferSegment(1).buf.size(), 1);
22     }
23 
testMaxSegment()24     void testMaxSegment()
25     {
26         // Large files may only use up to a given maximum.
27         QCOMPARE(TransferSegment(512 * 1024 * 1024).buf.size(), c_maxSegmentSize);
28     }
29 
testIdealSegmentSize()30     void testIdealSegmentSize()
31     {
32         QCOMPARE(TransferSegment(64 * 1024 * 1024).buf.size(), 1342177);
33     }
34 
testSegment()35     void testSegment()
36     {
37         TransferSegment s(8);
38         QCOMPARE(s.buf.size(), 8);
39         memset(s.buf.data(), 1, 8);
40         QCOMPARE(s.buf.data()[0], 1);
41     }
42 
testRing()43     void testRing()
44     {
45         TransferRingBuffer ring(8);
46         for (auto i = 0; i <= 32; ++i) {
47             {
48                 auto s = ring.nextFree();
49                 memset(s->buf.data(), i, 8);
50                 ring.push();
51             }
52             {
53                 auto s = ring.pop();
54                 QCOMPARE(s->buf.data()[0], static_cast<char>(i));
55                 ring.unpop();
56             }
57         }
58     }
59 
testRingThreadedSlowPush()60     void testRingThreadedSlowPush()
61     {
62         const auto runs = 127;
63         const auto fileSize = 8;
64         TransferRingBuffer ring(fileSize);
65 
66         std::atomic<bool> abort(false);
67 
68         auto pullFuture = std::async(std::launch::async, [&ring, &abort]() -> bool {
69             for (auto i = 0; i <= runs && !abort; ++i) {
70                 auto s = ring.pop();
71                 if (!QTest::qCompare(s->buf.data()[0], static_cast<char>(i),
72                                      qPrintable(QStringLiteral("On pull iteration %1").arg(i)), "",
73                                      __FILE__, __LINE__)) {
74                     abort = true;
75                     return false;
76                 }
77                 ring.unpop();
78             }
79             return true;
80         });
81 
82         auto pushFuture = std::async(std::launch::async, [&ring, &abort]() -> bool {
83             for (auto i = 0; i <= runs && !abort; ++i) {
84                 auto s = ring.nextFree();
85                 memset(s->buf.data(), i, fileSize);
86                 ring.push();
87                 if (abort) {
88                     ring.done();
89                     return false;
90                 }
91                 // Slow down this thread to simulate slow network reads.
92                 std::this_thread::sleep_for(std::chrono::milliseconds(5));
93             }
94             ring.done();
95             return true;
96         });
97 
98         pushFuture.wait();
99         pullFuture.wait();
100 
101         QVERIFY(pushFuture.get());
102         QVERIFY(pullFuture.get());
103     }
104 
testRingThreadedSlowPull()105     void testRingThreadedSlowPull()
106     {
107         const auto runs = 127;
108         const auto fileSize = 8;
109         TransferRingBuffer ring(fileSize);
110 
111         std::atomic<bool> abort(false);
112 
113         auto pullFuture = std::async(std::launch::async, [&ring, &abort]() -> bool {
114             for (auto i = 0; i <= runs && !abort; ++i) {
115                 auto s = ring.pop();
116                 if (!QTest::qCompare(s->buf.data()[0], static_cast<char>(i),
117                                      qPrintable(QStringLiteral("On pull iteration %1").arg(i)), "",
118                                      __FILE__, __LINE__)) {
119                     abort = true;
120                 }
121                 // Slow down this thread to simulate slow local writes.
122                 std::this_thread::sleep_for(std::chrono::milliseconds(5));
123                 ring.unpop();
124             }
125             return true;
126         });
127 
128         auto pushFuture = std::async(std::launch::async, [&ring, &abort]() -> bool {
129             for (auto i = 0; i <= runs && !abort; ++i) {
130                 auto s = ring.nextFree();
131                 memset(s->buf.data(), i, fileSize);
132                 if (abort) {
133                     ring.done();
134                     return false;
135                 }
136                 ring.push();
137             }
138             ring.done();
139             return true;
140         });
141 
142         pushFuture.wait();
143         pullFuture.wait();
144 
145         QVERIFY(pushFuture.get());
146         QVERIFY(pullFuture.get());
147     }
148 };
149 
150 QTEST_GUILESS_MAIN(TransferTest)
151 
152 #include "transfertest.moc"
153