1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include <private/qringbuffer_p.h>
32#include <qvector.h>
33
34class tst_QRingBuffer : public QObject
35{
36 Q_OBJECT
37private slots:
38 void constructing();
39 void usingInVector();
40 void readPointerAtPositionWriteRead();
41 void readPointerAtPositionEmptyRead();
42 void readPointerAtPositionWithHead();
43 void readPointerAtPositionReadTooMuch();
44 void sizeWhenReservedAndChopped();
45 void sizeWhenReserved();
46 void free();
47 void reserveAndRead();
48 void reserveAndReadInPacketMode();
49 void reserveFrontAndRead();
50 void chop();
51 void readPointerValidity();
52 void ungetChar();
53 void indexOf();
54 void appendAndRead();
55 void peek();
56 void readLine();
57};
58
59void tst_QRingBuffer::constructing()
60{
61 QRingBuffer ringBuffer;
62
63 const int chunkSize = ringBuffer.chunkSize();
64 ringBuffer.setChunkSize(0);
65 QCOMPARE(ringBuffer.chunkSize(), Q_INT64_C(0));
66 ringBuffer.setChunkSize(chunkSize);
67 QCOMPARE(ringBuffer.chunkSize(), chunkSize);
68
69 QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
70 QVERIFY(ringBuffer.isEmpty());
71 QCOMPARE(ringBuffer.nextDataBlockSize(), Q_INT64_C(0));
72 QVERIFY(ringBuffer.readPointer() == nullptr);
73 QCOMPARE(ringBuffer.skip(5), Q_INT64_C(0));
74 QCOMPARE(ringBuffer.read(), QByteArray());
75 QCOMPARE(ringBuffer.getChar(), -1);
76 QVERIFY(!ringBuffer.canReadLine());
77
78 char buf[5];
79 QCOMPARE(ringBuffer.peek(buf, sizeof(buf)), Q_INT64_C(0));
80}
81
82void tst_QRingBuffer::usingInVector()
83{
84 QRingBuffer ringBuffer;
85 QVector<QRingBuffer> buffers;
86
87 ringBuffer.reserve(bytes: 5);
88 buffers.append(t: ringBuffer);
89 QCOMPARE(buffers[0].size(), Q_INT64_C(5));
90}
91
92void tst_QRingBuffer::sizeWhenReserved()
93{
94 QRingBuffer ringBuffer;
95 ringBuffer.reserve(bytes: 5);
96
97 QCOMPARE(ringBuffer.size(), Q_INT64_C(5));
98}
99
100void tst_QRingBuffer::sizeWhenReservedAndChopped()
101{
102 QRingBuffer ringBuffer;
103 ringBuffer.reserve(bytes: 31337);
104 ringBuffer.chop(bytes: 31337);
105
106 QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
107}
108
109void tst_QRingBuffer::readPointerAtPositionReadTooMuch()
110{
111 QRingBuffer ringBuffer;
112
113 qint64 length;
114 const char *buf = ringBuffer.readPointerAtPosition(pos: 42, length);
115 QVERIFY(buf == 0);
116 QCOMPARE(length, Q_INT64_C(0));
117}
118
119void tst_QRingBuffer::readPointerAtPositionWithHead()
120{
121 QRingBuffer ringBuffer;
122 char *buf = ringBuffer.reserve(bytes: 4);
123 memcpy (dest: buf, src: "0123", n: 4);
124 ringBuffer.free(bytes: 2);
125
126 // ringBuffer should have stayed the same except
127 // its head it had moved to position 2
128 qint64 length;
129 const char* buf2 = ringBuffer.readPointerAtPosition(pos: 0, length);
130
131 QCOMPARE(length, Q_INT64_C(2));
132 QCOMPARE(*buf2, '2');
133 QCOMPARE(*(buf2 + 1), '3');
134
135 // advance 2 more, ringBuffer should be empty then
136 ringBuffer.free(bytes: 2);
137 buf2 = ringBuffer.readPointerAtPosition(pos: 0, length);
138 QCOMPARE(length, Q_INT64_C(0));
139 QVERIFY(buf2 == 0);
140
141 // check buffer with 2 blocks
142 memcpy(dest: ringBuffer.reserve(bytes: 4), src: "0123", n: 4);
143 ringBuffer.append(qba: QByteArray("45678", 5));
144 ringBuffer.free(bytes: 3);
145 buf2 = ringBuffer.readPointerAtPosition(Q_INT64_C(1), length);
146 QCOMPARE(length, Q_INT64_C(5));
147}
148
149void tst_QRingBuffer::readPointerAtPositionEmptyRead()
150{
151 QRingBuffer ringBuffer;
152
153 qint64 length;
154 const char *buf = ringBuffer.readPointerAtPosition(pos: 0, length);
155 QVERIFY(buf == 0);
156 QCOMPARE(length, Q_INT64_C(0));
157}
158
159void tst_QRingBuffer::readPointerAtPositionWriteRead()
160{
161 //create some data
162 QBuffer inData;
163 inData.open(openMode: QIODevice::ReadWrite);
164 inData.putChar(c: 0x42);
165 inData.putChar(c: 0x23);
166 inData.write(data: "Qt rocks!");
167 for (int i = 0; i < 5000; i++)
168 inData.write(data: "Number " + QByteArray::number(i));
169 inData.reset();
170 QVERIFY(inData.size() > 0);
171
172 //put the inData in the QRingBuffer
173 QRingBuffer ringBuffer;
174 qint64 remaining = inData.size();
175 while (remaining > 0) {
176 // write in chunks of 50 bytes
177 // this ensures there will be multiple QByteArrays inside the QRingBuffer
178 // since QRingBuffer is then only using individual arrays of around 4000 bytes
179 qint64 thisWrite = qMin(a: remaining, Q_INT64_C(50));
180 char *pos = ringBuffer.reserve(bytes: thisWrite);
181 inData.read(data: pos, maxlen: thisWrite);
182 remaining -= thisWrite;
183 }
184 // was data put into it?
185 QVERIFY(ringBuffer.size() > 0);
186 QCOMPARE(ringBuffer.size(), inData.size());
187
188 //read from the QRingBuffer in loop, put back into another QBuffer
189 QBuffer outData;
190 outData.open(openMode: QIODevice::ReadWrite);
191 remaining = ringBuffer.size();
192 while (remaining > 0) {
193 qint64 thisRead;
194 // always try to read as much as possible
195 const char *buf = ringBuffer.readPointerAtPosition(pos: ringBuffer.size() - remaining, length&: thisRead);
196 outData.write(data: buf, len: thisRead);
197 remaining -= thisRead;
198 }
199 outData.reset();
200
201 QVERIFY(outData.size() > 0);
202
203 // was the data read from the QRingBuffer the same as the one written into it?
204 QCOMPARE(outData.size(), inData.size());
205 QVERIFY(outData.buffer().startsWith(inData.buffer()));
206}
207
208void tst_QRingBuffer::free()
209{
210 QRingBuffer ringBuffer;
211 // make three byte arrays with different sizes
212 ringBuffer.reserve(bytes: 4096);
213 ringBuffer.reserve(bytes: 2048);
214 ringBuffer.append(qba: QByteArray("01234", 5));
215
216 ringBuffer.free(bytes: 1);
217 QCOMPARE(ringBuffer.size(), Q_INT64_C(4095) + 2048 + 5);
218 ringBuffer.free(bytes: 4096);
219 QCOMPARE(ringBuffer.size(), Q_INT64_C(2047) + 5);
220 ringBuffer.free(bytes: 48);
221 ringBuffer.free(bytes: 2000);
222 QCOMPARE(ringBuffer.size(), Q_INT64_C(4));
223 QVERIFY(memcmp(ringBuffer.readPointer(), "1234", 4) == 0);
224}
225
226void tst_QRingBuffer::reserveAndRead()
227{
228 QRingBuffer ringBuffer;
229 // fill buffer with an arithmetic progression
230 for (int i = 1; i < 256; ++i) {
231 QByteArray ba(i, char(i));
232 char *ringPos = ringBuffer.reserve(bytes: i);
233 QVERIFY(ringPos);
234 memcpy(dest: ringPos, src: ba.constData(), n: i);
235 }
236
237 // readback and check stored data
238 for (int i = 1; i < 256; ++i) {
239 QByteArray ba;
240 ba.resize(size: i);
241 qint64 thisRead = ringBuffer.read(data: ba.data(), maxLength: i);
242 QCOMPARE(thisRead, qint64(i));
243 QCOMPARE(ba.count(char(i)), i);
244 }
245 QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
246}
247
248void tst_QRingBuffer::reserveAndReadInPacketMode()
249{
250 QRingBuffer ringBuffer(0);
251 // try to allocate 255 buffers
252 for (int i = 1; i < 256; ++i) {
253 char *ringPos = ringBuffer.reserve(bytes: i);
254 QVERIFY(ringPos);
255 }
256
257 // count and check the size of stored buffers
258 int buffersCount = 0;
259 while (!ringBuffer.isEmpty()) {
260 QByteArray ba = ringBuffer.read();
261 ++buffersCount;
262 QCOMPARE(ba.size(), buffersCount);
263 }
264 QCOMPARE(buffersCount, 255);
265}
266
267void tst_QRingBuffer::reserveFrontAndRead()
268{
269 QRingBuffer ringBuffer;
270 // fill buffer with an arithmetic progression
271 for (int i = 1; i < 256; ++i) {
272 QByteArray ba(i, char(i));
273 char *ringPos = ringBuffer.reserveFront(bytes: i);
274 QVERIFY(ringPos);
275 memcpy(dest: ringPos, src: ba.constData(), n: i);
276 }
277
278 // readback and check stored data
279 for (int i = 255; i > 0; --i) {
280 QByteArray ba;
281 ba.resize(size: i);
282 qint64 thisRead = ringBuffer.read(data: ba.data(), maxLength: i);
283 QCOMPARE(thisRead, qint64(i));
284 QCOMPARE(ba.count(char(i)), i);
285 }
286 QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
287}
288
289void tst_QRingBuffer::chop()
290{
291 QRingBuffer ringBuffer;
292 // make three byte arrays with different sizes
293 ringBuffer.append(qba: QByteArray("01234", 5));
294 ringBuffer.reserve(bytes: 2048);
295 ringBuffer.reserve(bytes: 4096);
296
297 ringBuffer.chop(bytes: 1);
298 QCOMPARE(ringBuffer.size(), Q_INT64_C(5) + 2048 + 4095);
299 ringBuffer.chop(bytes: 4096);
300 QCOMPARE(ringBuffer.size(), Q_INT64_C(5) + 2047);
301 ringBuffer.chop(bytes: 48);
302 ringBuffer.chop(bytes: 2000);
303 QCOMPARE(ringBuffer.size(), Q_INT64_C(4));
304 QVERIFY(memcmp(ringBuffer.readPointer(), "0123", 4) == 0);
305}
306
307void tst_QRingBuffer::readPointerValidity()
308{
309 QRingBuffer ringBuffer(16);
310 QByteArray ba("Hello world!");
311
312 ringBuffer.append(qba: ba);
313 const char *ptr = ringBuffer.readPointer();
314 ba.clear();
315 ringBuffer.reserve(bytes: 32);
316 QVERIFY(ptr == ringBuffer.readPointer());
317 ringBuffer.reserveFront(bytes: 32);
318 qint64 dummy;
319 QVERIFY(ptr == ringBuffer.readPointerAtPosition(32, dummy));
320}
321
322void tst_QRingBuffer::ungetChar()
323{
324 QRingBuffer ringBuffer(16);
325 for (int i = 1; i < 32; ++i)
326 ringBuffer.putChar(c: char(i));
327
328 for (int i = 1; i < 31; ++i) {
329 int c = ringBuffer.getChar();
330 QCOMPARE(c, 1);
331 ringBuffer.getChar();
332 ringBuffer.ungetChar(c: char(c)); // unget first char
333 }
334 QCOMPARE(ringBuffer.size(), Q_INT64_C(1));
335}
336
337void tst_QRingBuffer::indexOf()
338{
339 QRingBuffer ringBuffer(16);
340 for (int i = 1; i < 256; ++i)
341 ringBuffer.putChar(c: char(i));
342
343 for (int i = 1; i < 256; ++i) {
344 qint64 index = ringBuffer.indexOf(c: char(i));
345 QCOMPARE(index, qint64(i - 1));
346 QCOMPARE(ringBuffer.indexOf(char(i), i, i >> 1), index);
347 QCOMPARE(ringBuffer.indexOf(char(i), 256, i), Q_INT64_C(-1));
348 QCOMPARE(ringBuffer.indexOf(char(i), i - 1), -1); // test for absent char
349 }
350}
351
352void tst_QRingBuffer::appendAndRead()
353{
354 QRingBuffer ringBuffer;
355 QByteArray ba1("Hello world!");
356 QByteArray ba2("Test string.");
357 QByteArray ba3("0123456789");
358 ringBuffer.append(qba: ba1);
359 ringBuffer.append(qba: ba2);
360 ringBuffer.append(qba: ba3);
361
362 QCOMPARE(ringBuffer.read(), ba1);
363 QCOMPARE(ringBuffer.read(), ba2);
364 QCOMPARE(ringBuffer.read(), ba3);
365}
366
367void tst_QRingBuffer::peek()
368{
369 QRingBuffer ringBuffer;
370 QByteArray testBuffer;
371 // fill buffer with an arithmetic progression
372 for (int i = 1; i < 256; ++i) {
373 char *ringPos = ringBuffer.reserve(bytes: i);
374 QVERIFY(ringPos);
375 memset(s: ringPos, c: i, n: i);
376 testBuffer.append(s: ringPos, len: i);
377 }
378
379 // check stored data
380 QByteArray resultBuffer;
381 int peekPosition = testBuffer.size();
382 for (int i = 1; i < 256; ++i) {
383 QByteArray ba(i, 0);
384 peekPosition -= i;
385 qint64 thisPeek = ringBuffer.peek(data: ba.data(), maxLength: i, pos: peekPosition);
386 QCOMPARE(thisPeek, qint64(i));
387 resultBuffer.prepend(a: ba);
388 }
389 QCOMPARE(resultBuffer, testBuffer);
390}
391
392void tst_QRingBuffer::readLine()
393{
394 QRingBuffer ringBuffer;
395 QByteArray ba1("Hello world!\n", 13);
396 QByteArray ba2("\n", 1);
397 QByteArray ba3("Test string.", 12);
398 QByteArray ba4("0123456789", 10);
399 ringBuffer.append(qba: ba1);
400 ringBuffer.append(qba: ba2);
401 ringBuffer.append(qba: ba3 + ba4 + ba2);
402
403 char stringBuf[102];
404 stringBuf[101] = 0; // non-crash terminator
405 QCOMPARE(ringBuffer.readLine(stringBuf, sizeof(stringBuf) - 2), qint64(ba1.size()));
406 QCOMPARE(QByteArray(stringBuf, int(strlen(stringBuf))), ba1);
407
408 // check first empty string reading
409 stringBuf[0] = char(0xFF);
410 QCOMPARE(ringBuffer.readLine(stringBuf, int(sizeof(stringBuf)) - 2), qint64(ba2.size()));
411 QCOMPARE(stringBuf[0], ba2.at(0));
412
413 QCOMPARE(ringBuffer.readLine(stringBuf, int(sizeof(stringBuf)) - 2),
414 qint64(ba3.size() + ba4.size() + ba2.size()));
415 QCOMPARE(QByteArray(stringBuf, int(strlen(stringBuf))), ba3 + ba4 + ba2);
416 QCOMPARE(ringBuffer.size(), Q_INT64_C(0));
417}
418
419QTEST_APPLESS_MAIN(tst_QRingBuffer)
420#include "tst_qringbuffer.moc"
421

source code of qtbase/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp