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 <QtCore/QCoreApplication> |
30 | #include <QtNetwork/QtNetwork> |
31 | #include <QtTest/QtTest> |
32 | |
33 | #include "../../../network-settings.h" |
34 | |
35 | class tst_QIODevice : public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | private slots: |
40 | void initTestCase(); |
41 | void cleanupTestCase(); |
42 | void getSetCheck(); |
43 | void constructing_QTcpSocket(); |
44 | void constructing_QFile(); |
45 | void read_QByteArray(); |
46 | void unget(); |
47 | void peek(); |
48 | void peekAndRead(); |
49 | |
50 | void readLine_data(); |
51 | void readLine(); |
52 | |
53 | void readLine2_data(); |
54 | void readLine2(); |
55 | |
56 | void readAllKeepPosition(); |
57 | void writeInTextMode(); |
58 | void skip_data(); |
59 | void skip(); |
60 | void skipAfterPeek_data(); |
61 | void skipAfterPeek(); |
62 | |
63 | void transaction_data(); |
64 | void transaction(); |
65 | |
66 | private: |
67 | QSharedPointer<QTemporaryDir> m_tempDir; |
68 | QString m_previousCurrent; |
69 | }; |
70 | |
71 | void tst_QIODevice::initTestCase() |
72 | { |
73 | #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) |
74 | QVERIFY(QFileInfo(QStringLiteral("./tst_qiodevice.cpp" )).exists() |
75 | || QFile::copy(QStringLiteral(":/tst_qiodevice.cpp" ), QStringLiteral("./tst_qiodevice.cpp" ))); |
76 | #endif |
77 | m_previousCurrent = QDir::currentPath(); |
78 | m_tempDir = QSharedPointer<QTemporaryDir>::create(); |
79 | QVERIFY2(!m_tempDir.isNull(), qPrintable("Could not create temporary directory." )); |
80 | QVERIFY2(QDir::setCurrent(m_tempDir->path()), qPrintable("Could not switch current directory" )); |
81 | } |
82 | |
83 | void tst_QIODevice::cleanupTestCase() |
84 | { |
85 | QDir::setCurrent(m_previousCurrent); |
86 | } |
87 | |
88 | // Testing get/set functions |
89 | void tst_QIODevice::getSetCheck() |
90 | { |
91 | // OpenMode QIODevice::openMode() |
92 | // void QIODevice::setOpenMode(OpenMode) |
93 | class MyIODevice : public QTcpSocket { |
94 | public: |
95 | using QTcpSocket::setOpenMode; |
96 | }; |
97 | MyIODevice var1; |
98 | var1.setOpenMode(QIODevice::OpenMode(QIODevice::NotOpen)); |
99 | QCOMPARE(QIODevice::OpenMode(QIODevice::NotOpen), var1.openMode()); |
100 | var1.setOpenMode(QIODevice::OpenMode(QIODevice::ReadWrite)); |
101 | QCOMPARE(QIODevice::OpenMode(QIODevice::ReadWrite), var1.openMode()); |
102 | } |
103 | |
104 | //---------------------------------------------------------------------------------- |
105 | void tst_QIODevice::constructing_QTcpSocket() |
106 | { |
107 | #if defined(Q_OS_WINRT) |
108 | QSKIP("Synchronous socket calls are broken on winrt. See QTBUG-40922" ); |
109 | #endif |
110 | if (!QtNetworkSettings::verifyTestNetworkSettings()) |
111 | QSKIP("No network test server available" ); |
112 | |
113 | QTcpSocket socket; |
114 | QIODevice *device = &socket; |
115 | |
116 | QVERIFY(!device->isOpen()); |
117 | |
118 | socket.connectToHost(hostName: QtNetworkSettings::serverName(), port: 143); |
119 | QVERIFY(socket.waitForConnected(30000)); |
120 | QVERIFY(device->isOpen()); |
121 | QCOMPARE(device->readChannelCount(), 1); |
122 | QCOMPARE(device->writeChannelCount(), 1); |
123 | |
124 | while (!device->canReadLine()) |
125 | QVERIFY(device->waitForReadyRead(30000)); |
126 | |
127 | char buf[1024]; |
128 | memset(s: buf, c: 0, n: sizeof(buf)); |
129 | qlonglong lineLength = device->readLine(data: buf, maxlen: sizeof(buf)); |
130 | QVERIFY(lineLength > 0); |
131 | QCOMPARE(socket.pos(), qlonglong(0)); |
132 | |
133 | socket.close(); |
134 | QCOMPARE(socket.readChannelCount(), 0); |
135 | QCOMPARE(socket.writeChannelCount(), 0); |
136 | socket.connectToHost(hostName: QtNetworkSettings::serverName(), port: 143); |
137 | QVERIFY(socket.waitForConnected(30000)); |
138 | QVERIFY(device->isOpen()); |
139 | |
140 | while (!device->canReadLine()) |
141 | QVERIFY(device->waitForReadyRead(30000)); |
142 | |
143 | char buf2[1024]; |
144 | memset(s: buf2, c: 0, n: sizeof(buf2)); |
145 | QCOMPARE(socket.readLine(buf2, sizeof(buf2)), lineLength); |
146 | |
147 | char *c1 = buf; |
148 | char *c2 = buf2; |
149 | while (*c1 && *c2) { |
150 | QCOMPARE(*c1, *c2); |
151 | ++c1; |
152 | ++c2; |
153 | } |
154 | QCOMPARE(*c1, *c2); |
155 | } |
156 | |
157 | //---------------------------------------------------------------------------------- |
158 | void tst_QIODevice::constructing_QFile() |
159 | { |
160 | QFile file; |
161 | QIODevice *device = &file; |
162 | |
163 | QVERIFY(!device->isOpen()); |
164 | |
165 | file.setFileName(QFINDTESTDATA("tst_qiodevice.cpp" )); |
166 | QVERIFY(file.open(QFile::ReadOnly)); |
167 | QVERIFY(device->isOpen()); |
168 | QCOMPARE((int) device->openMode(), (int) QFile::ReadOnly); |
169 | QCOMPARE(device->readChannelCount(), 1); |
170 | QCOMPARE(device->writeChannelCount(), 0); |
171 | |
172 | char buf[1024]; |
173 | memset(s: buf, c: 0, n: sizeof(buf)); |
174 | qlonglong lineLength = device->readLine(data: buf, maxlen: sizeof(buf)); |
175 | QVERIFY(lineLength > 0); |
176 | QCOMPARE(file.pos(), lineLength); |
177 | |
178 | file.seek(offset: 0); |
179 | char buf2[1024]; |
180 | memset(s: buf2, c: 0, n: sizeof(buf2)); |
181 | QCOMPARE(file.readLine(buf2, sizeof(buf2)), lineLength); |
182 | |
183 | char *c1 = buf; |
184 | char *c2 = buf2; |
185 | while (*c1 && *c2) { |
186 | QCOMPARE(*c1, *c2); |
187 | ++c1; |
188 | ++c2; |
189 | } |
190 | QCOMPARE(*c1, *c2); |
191 | } |
192 | |
193 | |
194 | void tst_QIODevice::read_QByteArray() |
195 | { |
196 | QFile f(QFINDTESTDATA("tst_qiodevice.cpp" )); |
197 | f.open(flags: QIODevice::ReadOnly); |
198 | |
199 | QByteArray b = f.read(maxlen: 10); |
200 | QCOMPARE(b.length(), 10); |
201 | |
202 | b = f.read(maxlen: 256); |
203 | QCOMPARE(b.length(), 256); |
204 | |
205 | b = f.read(maxlen: 0); |
206 | QCOMPARE(b.length(), 0); |
207 | } |
208 | |
209 | //-------------------------------------------------------------------- |
210 | void tst_QIODevice::unget() |
211 | { |
212 | QBuffer buffer; |
213 | buffer.open(openMode: QBuffer::ReadWrite); |
214 | buffer.write(data: "ZXCV" ); |
215 | buffer.seek(off: 0); |
216 | QCOMPARE(buffer.read(4), QByteArray("ZXCV" )); |
217 | QCOMPARE(buffer.pos(), qint64(4)); |
218 | |
219 | buffer.ungetChar(c: 'a'); |
220 | buffer.ungetChar(c: 'b'); |
221 | buffer.ungetChar(c: 'c'); |
222 | buffer.ungetChar(c: 'd'); |
223 | |
224 | QCOMPARE(buffer.pos(), qint64(0)); |
225 | |
226 | char buf[6]; |
227 | QCOMPARE(buffer.readLine(buf, 5), qint64(4)); |
228 | QCOMPARE(buffer.pos(), qint64(4)); |
229 | QCOMPARE(static_cast<const char*>(buf), "dcba" ); |
230 | |
231 | buffer.ungetChar(c: 'a'); |
232 | buffer.ungetChar(c: 'b'); |
233 | buffer.ungetChar(c: 'c'); |
234 | buffer.ungetChar(c: 'd'); |
235 | |
236 | QCOMPARE(buffer.pos(), qint64(0)); |
237 | |
238 | for (int i = 0; i < 5; ++i) { |
239 | buf[0] = '@'; |
240 | buf[1] = '@'; |
241 | QTest::ignoreMessage(type: QtWarningMsg, |
242 | message: "QIODevice::readLine (QBuffer): Called with maxSize < 2" ); |
243 | QCOMPARE(buffer.readLine(buf, 1), qint64(-1)); |
244 | QCOMPARE(buffer.readLine(buf, 2), qint64(i < 4 ? 1 : -1)); |
245 | switch (i) { |
246 | case 0: QCOMPARE(buf[0], 'd'); break; |
247 | case 1: QCOMPARE(buf[0], 'c'); break; |
248 | case 2: QCOMPARE(buf[0], 'b'); break; |
249 | case 3: QCOMPARE(buf[0], 'a'); break; |
250 | case 4: QCOMPARE(buf[0], '\0'); break; |
251 | } |
252 | QCOMPARE(buf[1], i < 4 ? '\0' : '@'); |
253 | } |
254 | |
255 | buffer.ungetChar(c: '\n'); |
256 | QCOMPARE(buffer.readLine(), QByteArray("\n" )); |
257 | |
258 | buffer.seek(off: 1); |
259 | buffer.readLine(data: buf, maxlen: 3); |
260 | QCOMPARE(static_cast<const char*>(buf), "XC" ); |
261 | |
262 | buffer.seek(off: 4); |
263 | buffer.ungetChar(c: 'Q'); |
264 | QCOMPARE(buffer.readLine(buf, 3), qint64(1)); |
265 | |
266 | #if defined(Q_OS_WINRT) |
267 | QSKIP("Synchronous socket calls are broken on winrt. See QTBUG-40922" ); |
268 | #endif |
269 | for (int i = 0; i < 2; ++i) { |
270 | QTcpSocket socket; |
271 | QIODevice *dev; |
272 | QByteArray result; |
273 | const char *lineResult; |
274 | if (i == 0) { |
275 | dev = &buffer; |
276 | result = QByteArray("ZXCV" ); |
277 | lineResult = "ZXCV" ; |
278 | } else { |
279 | if (!QtNetworkSettings::verifyTestNetworkSettings()) |
280 | QSKIP("No network test server available" ); |
281 | socket.connectToHost(hostName: QtNetworkSettings::serverName(), port: 80); |
282 | socket.write(data: "GET / HTTP/1.0\r\n\r\n" ); |
283 | QVERIFY(socket.waitForReadyRead()); |
284 | dev = &socket; |
285 | result = QByteArray("HTTP" ); |
286 | lineResult = "Date" ; |
287 | } |
288 | char ch, ch2; |
289 | dev->seek(pos: 0); |
290 | dev->getChar(c: &ch); |
291 | dev->ungetChar(c: ch); |
292 | QCOMPARE(dev->peek(4), result); |
293 | dev->getChar(c: &ch); |
294 | dev->getChar(c: &ch2); |
295 | dev->ungetChar(c: ch2); |
296 | dev->ungetChar(c: ch); |
297 | QCOMPARE(dev->read(1), result.left(1)); |
298 | QCOMPARE(dev->read(3), result.right(3)); |
299 | |
300 | if (i == 0) |
301 | dev->seek(pos: 0); |
302 | else |
303 | dev->readLine(); |
304 | dev->getChar(c: &ch); |
305 | dev->ungetChar(c: ch); |
306 | dev->readLine(data: buf, maxlen: 5); |
307 | QCOMPARE(static_cast<const char*>(buf), lineResult); |
308 | |
309 | if (i == 1) |
310 | socket.close(); |
311 | } |
312 | } |
313 | |
314 | //-------------------------------------------------------------------- |
315 | void tst_QIODevice::peek() |
316 | { |
317 | QBuffer buffer; |
318 | QFile::remove(fileName: "peektestfile" ); |
319 | QFile file("peektestfile" ); |
320 | |
321 | for (int i = 0; i < 2; ++i) { |
322 | QIODevice *device = i ? (QIODevice *)&file : (QIODevice *)&buffer; |
323 | |
324 | device->open(mode: QBuffer::ReadWrite); |
325 | device->write(data: "ZXCV" ); |
326 | |
327 | device->seek(pos: 0); |
328 | QCOMPARE(device->peek(4), QByteArray("ZXCV" )); |
329 | QCOMPARE(device->pos(), qint64(0)); |
330 | device->write(data: "ABCDE" ); |
331 | device->seek(pos: 3); |
332 | QCOMPARE(device->peek(1), QByteArray("D" )); |
333 | QCOMPARE(device->peek(5), QByteArray("DE" )); |
334 | device->seek(pos: 0); |
335 | QCOMPARE(device->read(4), QByteArray("ABCD" )); |
336 | QCOMPARE(device->pos(), qint64(4)); |
337 | |
338 | device->seek(pos: 0); |
339 | device->write(data: "ZXCV" ); |
340 | device->seek(pos: 0); |
341 | char buf[5]; |
342 | buf[4] = 0; |
343 | device->peek(data: buf, maxlen: 4); |
344 | QCOMPARE(static_cast<const char *>(buf), "ZXCV" ); |
345 | QCOMPARE(device->pos(), qint64(0)); |
346 | device->read(data: buf, maxlen: 4); |
347 | QCOMPARE(static_cast<const char *>(buf), "ZXCV" ); |
348 | QCOMPARE(device->pos(), qint64(4)); |
349 | } |
350 | QFile::remove(fileName: "peektestfile" ); |
351 | } |
352 | |
353 | void tst_QIODevice::peekAndRead() |
354 | { |
355 | QByteArray originalData; |
356 | for (int i=0;i<1000;i++) |
357 | originalData += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; |
358 | QBuffer buffer; |
359 | QFile::remove(fileName: "peektestfile" ); |
360 | QFile file("peektestfile" ); |
361 | |
362 | for (int i = 0; i < 2; ++i) { |
363 | QByteArray readData; |
364 | QIODevice *device = i ? (QIODevice *)&file : (QIODevice *)&buffer; |
365 | device->open(mode: QBuffer::ReadWrite); |
366 | device->write(data: originalData); |
367 | device->seek(pos: 0); |
368 | while (!device->atEnd()) { |
369 | char peekIn[26]; |
370 | device->peek(data: peekIn, maxlen: 26); |
371 | readData += device->read(maxlen: 26); |
372 | } |
373 | QCOMPARE(readData, originalData); |
374 | } |
375 | QFile::remove(fileName: "peektestfile" ); |
376 | } |
377 | |
378 | void tst_QIODevice::readLine_data() |
379 | { |
380 | QTest::addColumn<QByteArray>(name: "data" ); |
381 | |
382 | QTest::newRow(dataTag: "0" ) << QByteArray("\nAA" ); |
383 | QTest::newRow(dataTag: "1" ) << QByteArray("A\nAA" ); |
384 | |
385 | QByteArray data(9000, 'A'); |
386 | data[8193] = '\n'; |
387 | QTest::newRow(dataTag: "8194" ) << data; |
388 | data[8193] = 'A'; |
389 | data[8192] = '\n'; |
390 | QTest::newRow(dataTag: "8193" ) << data; |
391 | data[8192] = 'A'; |
392 | data[8191] = '\n'; |
393 | QTest::newRow(dataTag: "8192" ) << data; |
394 | data[8191] = 'A'; |
395 | data[8190] = '\n'; |
396 | QTest::newRow(dataTag: "8191" ) << data; |
397 | |
398 | data[5999] = '\n'; |
399 | QTest::newRow(dataTag: "6000" ) << data; |
400 | |
401 | data[4095] = '\n'; |
402 | QTest::newRow(dataTag: "4096" ) << data; |
403 | |
404 | data[4094] = '\n'; |
405 | data[4095] = 'A'; |
406 | QTest::newRow(dataTag: "4095" ) << data; |
407 | } |
408 | |
409 | void tst_QIODevice::readLine() |
410 | { |
411 | QFETCH(QByteArray, data); |
412 | QBuffer buffer(&data); |
413 | QVERIFY(buffer.open(QIODevice::ReadWrite)); |
414 | QVERIFY(buffer.canReadLine()); |
415 | |
416 | int linelen = data.indexOf(c: '\n') + 1; |
417 | QByteArray line; |
418 | line.reserve(asize: linelen + 100); |
419 | |
420 | int result = buffer.readLine(data: line.data(), maxlen: linelen + 100); |
421 | QCOMPARE(result, linelen); |
422 | |
423 | // try the exact length of the line (plus terminating \0) |
424 | QVERIFY(buffer.seek(0)); |
425 | result = buffer.readLine(data: line.data(), maxlen: linelen + 1); |
426 | QCOMPARE(result, linelen); |
427 | |
428 | // try with a line length limit |
429 | QVERIFY(buffer.seek(0)); |
430 | line = buffer.readLine(maxlen: linelen + 100); |
431 | QCOMPARE(line.size(), linelen); |
432 | |
433 | // try without a length limit |
434 | QVERIFY(buffer.seek(0)); |
435 | line = buffer.readLine(); |
436 | QCOMPARE(line.size(), linelen); |
437 | } |
438 | |
439 | void tst_QIODevice::readLine2_data() |
440 | { |
441 | QTest::addColumn<QByteArray>(name: "line" ); |
442 | |
443 | QTest::newRow(dataTag: "1024 - 4" ) << QByteArray(1024 - 4, 'x'); |
444 | QTest::newRow(dataTag: "1024 - 3" ) << QByteArray(1024 - 3, 'x'); |
445 | QTest::newRow(dataTag: "1024 - 2" ) << QByteArray(1024 - 2, 'x'); |
446 | QTest::newRow(dataTag: "1024 - 1" ) << QByteArray(1024 - 1, 'x'); |
447 | QTest::newRow(dataTag: "1024" ) << QByteArray(1024 , 'x'); |
448 | QTest::newRow(dataTag: "1024 + 1" ) << QByteArray(1024 + 1, 'x'); |
449 | QTest::newRow(dataTag: "1024 + 2" ) << QByteArray(1024 + 2, 'x'); |
450 | |
451 | QTest::newRow(dataTag: "4096 - 4" ) << QByteArray(4096 - 4, 'x'); |
452 | QTest::newRow(dataTag: "4096 - 3" ) << QByteArray(4096 - 3, 'x'); |
453 | QTest::newRow(dataTag: "4096 - 2" ) << QByteArray(4096 - 2, 'x'); |
454 | QTest::newRow(dataTag: "4096 - 1" ) << QByteArray(4096 - 1, 'x'); |
455 | QTest::newRow(dataTag: "4096" ) << QByteArray(4096 , 'x'); |
456 | QTest::newRow(dataTag: "4096 + 1" ) << QByteArray(4096 + 1, 'x'); |
457 | QTest::newRow(dataTag: "4096 + 2" ) << QByteArray(4096 + 2, 'x'); |
458 | |
459 | QTest::newRow(dataTag: "8192 - 4" ) << QByteArray(8192 - 4, 'x'); |
460 | QTest::newRow(dataTag: "8192 - 3" ) << QByteArray(8192 - 3, 'x'); |
461 | QTest::newRow(dataTag: "8192 - 2" ) << QByteArray(8192 - 2, 'x'); |
462 | QTest::newRow(dataTag: "8192 - 1" ) << QByteArray(8192 - 1, 'x'); |
463 | QTest::newRow(dataTag: "8192" ) << QByteArray(8192 , 'x'); |
464 | QTest::newRow(dataTag: "8192 + 1" ) << QByteArray(8192 + 1, 'x'); |
465 | QTest::newRow(dataTag: "8192 + 2" ) << QByteArray(8192 + 2, 'x'); |
466 | |
467 | QTest::newRow(dataTag: "16384 - 4" ) << QByteArray(16384 - 4, 'x'); |
468 | QTest::newRow(dataTag: "16384 - 3" ) << QByteArray(16384 - 3, 'x'); |
469 | QTest::newRow(dataTag: "16384 - 2" ) << QByteArray(16384 - 2, 'x'); |
470 | QTest::newRow(dataTag: "16384 - 1" ) << QByteArray(16384 - 1, 'x'); |
471 | QTest::newRow(dataTag: "16384" ) << QByteArray(16384 , 'x'); |
472 | QTest::newRow(dataTag: "16384 + 1" ) << QByteArray(16384 + 1, 'x'); |
473 | QTest::newRow(dataTag: "16384 + 2" ) << QByteArray(16384 + 2, 'x'); |
474 | |
475 | QTest::newRow(dataTag: "20000" ) << QByteArray(20000, 'x'); |
476 | |
477 | QTest::newRow(dataTag: "32768 - 4" ) << QByteArray(32768 - 4, 'x'); |
478 | QTest::newRow(dataTag: "32768 - 3" ) << QByteArray(32768 - 3, 'x'); |
479 | QTest::newRow(dataTag: "32768 - 2" ) << QByteArray(32768 - 2, 'x'); |
480 | QTest::newRow(dataTag: "32768 - 1" ) << QByteArray(32768 - 1, 'x'); |
481 | QTest::newRow(dataTag: "32768" ) << QByteArray(32768 , 'x'); |
482 | QTest::newRow(dataTag: "32768 + 1" ) << QByteArray(32768 + 1, 'x'); |
483 | QTest::newRow(dataTag: "32768 + 2" ) << QByteArray(32768 + 2, 'x'); |
484 | |
485 | QTest::newRow(dataTag: "40000" ) << QByteArray(40000, 'x'); |
486 | } |
487 | |
488 | void tst_QIODevice::readLine2() |
489 | { |
490 | QFETCH(QByteArray, line); |
491 | |
492 | int length = line.size(); |
493 | |
494 | QByteArray data("First line.\r\n" ); |
495 | data.append(a: line); |
496 | data.append(s: "\r\n" ); |
497 | data.append(a: line); |
498 | data.append(s: "\r\n" ); |
499 | data.append(s: "\r\n0123456789" ); |
500 | |
501 | { |
502 | QBuffer buffer(&data); |
503 | buffer.open(openMode: QIODevice::ReadOnly); |
504 | |
505 | buffer.seek(off: 0); |
506 | QByteArray temp; |
507 | temp.resize(size: 64536); |
508 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(13)); |
509 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(length + 2)); |
510 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(length + 2)); |
511 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(2)); |
512 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(10)); |
513 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(-1)); |
514 | |
515 | buffer.seek(off: 0); |
516 | QCOMPARE(buffer.readLine().size(), 13); |
517 | QCOMPARE(buffer.readLine().size(), length + 2); |
518 | QCOMPARE(buffer.readLine().size(), length + 2); |
519 | QCOMPARE(buffer.readLine().size(), 2); |
520 | QCOMPARE(buffer.readLine().size(), 10); |
521 | QVERIFY(buffer.readLine().isNull()); |
522 | } |
523 | |
524 | { |
525 | QBuffer buffer(&data); |
526 | buffer.open(openMode: QIODevice::ReadOnly | QIODevice::Text); |
527 | |
528 | buffer.seek(off: 0); |
529 | QByteArray temp; |
530 | temp.resize(size: 64536); |
531 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(12)); |
532 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(length + 1)); |
533 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(length + 1)); |
534 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(1)); |
535 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(10)); |
536 | QCOMPARE(buffer.readLine(temp.data(), temp.size()), qint64(-1)); |
537 | |
538 | buffer.seek(off: 0); |
539 | QCOMPARE(buffer.readLine().size(), 12); |
540 | QCOMPARE(buffer.readLine().size(), length + 1); |
541 | QCOMPARE(buffer.readLine().size(), length + 1); |
542 | QCOMPARE(buffer.readLine().size(), 1); |
543 | QCOMPARE(buffer.readLine().size(), 10); |
544 | QVERIFY(buffer.readLine().isNull()); |
545 | } |
546 | } |
547 | |
548 | class SequentialReadBuffer : public QIODevice |
549 | { |
550 | public: |
551 | SequentialReadBuffer(const char *data) |
552 | : QIODevice(), buf(new QByteArray(data)), offset(0), ownbuf(true) { } |
553 | SequentialReadBuffer(QByteArray *byteArray) |
554 | : QIODevice(), buf(byteArray), offset(0), ownbuf(false) { } |
555 | virtual ~SequentialReadBuffer() { if (ownbuf) delete buf; } |
556 | |
557 | bool isSequential() const override { return true; } |
558 | const QByteArray &buffer() const { return *buf; } |
559 | |
560 | protected: |
561 | qint64 readData(char *data, qint64 maxSize) override |
562 | { |
563 | maxSize = qMin(a: maxSize, b: qint64(buf->size() - offset)); |
564 | if (maxSize > 0) |
565 | memcpy(dest: data, src: buf->constData() + offset, n: maxSize); |
566 | offset += maxSize; |
567 | return maxSize; |
568 | } |
569 | qint64 writeData(const char * /* data */, qint64 /* maxSize */) override |
570 | { |
571 | return -1; |
572 | } |
573 | |
574 | private: |
575 | QByteArray *buf; |
576 | int offset; |
577 | bool ownbuf; |
578 | }; |
579 | |
580 | // Test readAll() on position change for sequential device |
581 | void tst_QIODevice::readAllKeepPosition() |
582 | { |
583 | SequentialReadBuffer buffer("Hello world!" ); |
584 | buffer.open(mode: QIODevice::ReadOnly); |
585 | char c; |
586 | |
587 | QCOMPARE(buffer.readChannelCount(), 1); |
588 | QCOMPARE(buffer.writeChannelCount(), 0); |
589 | QVERIFY(buffer.getChar(&c)); |
590 | QCOMPARE(buffer.pos(), qint64(0)); |
591 | buffer.ungetChar(c); |
592 | QCOMPARE(buffer.pos(), qint64(0)); |
593 | |
594 | QByteArray resultArray = buffer.readAll(); |
595 | QCOMPARE(buffer.pos(), qint64(0)); |
596 | QCOMPARE(resultArray, buffer.buffer()); |
597 | } |
598 | |
599 | class RandomAccessBuffer : public QIODevice |
600 | { |
601 | public: |
602 | RandomAccessBuffer(const char *data) : QIODevice(), buf(data) { } |
603 | |
604 | protected: |
605 | qint64 readData(char *data, qint64 maxSize) override |
606 | { |
607 | maxSize = qMin(a: maxSize, b: qint64(buf.size() - pos())); |
608 | if (maxSize > 0) |
609 | memcpy(dest: data, src: buf.constData() + pos(), n: maxSize); |
610 | return maxSize; |
611 | } |
612 | qint64 writeData(const char *data, qint64 maxSize) override |
613 | { |
614 | maxSize = qMin(a: maxSize, b: qint64(buf.size() - pos())); |
615 | if (maxSize > 0) |
616 | memcpy(dest: buf.data() + pos(), src: data, n: maxSize); |
617 | return maxSize; |
618 | } |
619 | |
620 | private: |
621 | QByteArray buf; |
622 | }; |
623 | |
624 | // Test write() on skipping correct number of bytes in read buffer |
625 | void tst_QIODevice::writeInTextMode() |
626 | { |
627 | // Unlike other platforms, Windows implementation expands '\n' into |
628 | // "\r\n" sequence in write(). Ensure that write() properly works with |
629 | // a read buffer on random-access devices. |
630 | #ifndef Q_OS_WIN |
631 | QSKIP("This is a Windows-only test" ); |
632 | #else |
633 | RandomAccessBuffer buffer("one\r\ntwo\r\nthree\r\n" ); |
634 | buffer.open(QBuffer::ReadWrite | QBuffer::Text); |
635 | QCOMPARE(buffer.readLine(), QByteArray("one\n" )); |
636 | QCOMPARE(buffer.write("two\n" ), 4); |
637 | QCOMPARE(buffer.readLine(), QByteArray("three\n" )); |
638 | #endif |
639 | } |
640 | |
641 | void tst_QIODevice::skip_data() |
642 | { |
643 | QTest::addColumn<bool>(name: "sequential" ); |
644 | QTest::addColumn<QByteArray>(name: "data" ); |
645 | QTest::addColumn<int>(name: "read" ); |
646 | QTest::addColumn<int>(name: "skip" ); |
647 | QTest::addColumn<int>(name: "skipped" ); |
648 | QTest::addColumn<char>(name: "expect" ); |
649 | |
650 | QByteArray bigData; |
651 | bigData.fill(c: 'a', size: 20000); |
652 | bigData[10001] = 'x'; |
653 | |
654 | bool sequential = true; |
655 | do { |
656 | QByteArray devName(sequential ? "sequential" : "random-access" ); |
657 | |
658 | QTest::newRow(qPrintable(devName + "-small_data" )) << true << QByteArray("abcdefghij" ) |
659 | << 3 << 6 << 6 << 'j'; |
660 | QTest::newRow(qPrintable(devName + "-big_data" )) << true << bigData |
661 | << 1 << 10000 << 10000 << 'x'; |
662 | QTest::newRow(qPrintable(devName + "-beyond_the_end" )) << true << bigData |
663 | << 1 << 20000 << 19999 << '\0'; |
664 | |
665 | sequential = !sequential; |
666 | } while (!sequential); |
667 | } |
668 | |
669 | void tst_QIODevice::skip() |
670 | { |
671 | QFETCH(bool, sequential); |
672 | QFETCH(QByteArray, data); |
673 | QFETCH(int, read); |
674 | QFETCH(int, skip); |
675 | QFETCH(int, skipped); |
676 | QFETCH(char, expect); |
677 | char lastChar = 0; |
678 | |
679 | QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data) |
680 | : (QIODevice *) new QBuffer(&data)); |
681 | dev->open(mode: QIODevice::ReadOnly); |
682 | |
683 | for (int i = 0; i < read; ++i) |
684 | dev->getChar(c: nullptr); |
685 | |
686 | QCOMPARE(dev->skip(skip), skipped); |
687 | dev->getChar(c: &lastChar); |
688 | QCOMPARE(lastChar, expect); |
689 | } |
690 | |
691 | void tst_QIODevice::skipAfterPeek_data() |
692 | { |
693 | QTest::addColumn<bool>(name: "sequential" ); |
694 | QTest::addColumn<QByteArray>(name: "data" ); |
695 | |
696 | QByteArray bigData; |
697 | for (int i = 0; i < 1000; ++i) |
698 | bigData += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; |
699 | |
700 | QTest::newRow(dataTag: "sequential" ) << true << bigData; |
701 | QTest::newRow(dataTag: "random-access" ) << false << bigData; |
702 | } |
703 | |
704 | void tst_QIODevice::skipAfterPeek() |
705 | { |
706 | QFETCH(bool, sequential); |
707 | QFETCH(QByteArray, data); |
708 | |
709 | QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data) |
710 | : (QIODevice *) new QBuffer(&data)); |
711 | int readSoFar = 0; |
712 | qint64 bytesToSkip = 1; |
713 | |
714 | dev->open(mode: QIODevice::ReadOnly); |
715 | forever { |
716 | QByteArray chunk = dev->peek(maxlen: bytesToSkip); |
717 | if (chunk.isEmpty()) |
718 | break; |
719 | |
720 | QCOMPARE(dev->skip(bytesToSkip), qint64(chunk.size())); |
721 | QCOMPARE(chunk, data.mid(readSoFar, chunk.size())); |
722 | readSoFar += chunk.size(); |
723 | bytesToSkip <<= 1; |
724 | } |
725 | QCOMPARE(readSoFar, data.size()); |
726 | } |
727 | |
728 | void tst_QIODevice::transaction_data() |
729 | { |
730 | QTest::addColumn<bool>(name: "sequential" ); |
731 | QTest::addColumn<qint8>(name: "i8Data" ); |
732 | QTest::addColumn<qint16>(name: "i16Data" ); |
733 | QTest::addColumn<qint32>(name: "i32Data" ); |
734 | QTest::addColumn<qint64>(name: "i64Data" ); |
735 | QTest::addColumn<bool>(name: "bData" ); |
736 | QTest::addColumn<float>(name: "fData" ); |
737 | QTest::addColumn<double>(name: "dData" ); |
738 | QTest::addColumn<QByteArray>(name: "strData" ); |
739 | |
740 | bool sequential = true; |
741 | do { |
742 | QByteArray devName(sequential ? "sequential" : "random-access" ); |
743 | |
744 | QTest::newRow(qPrintable(devName + '1')) << sequential << qint8(1) << qint16(2) |
745 | << qint32(3) << qint64(4) << true |
746 | << 5.0f << double(6.0) |
747 | << QByteArray("Hello world!" ); |
748 | QTest::newRow(qPrintable(devName + '2')) << sequential << qint8(1 << 6) << qint16(1 << 14) |
749 | << qint32(1 << 30) << (qint64(1) << 62) << false |
750 | << 123.0f << double(234.0) |
751 | << QByteArray("abcdefghijklmnopqrstuvwxyz" ); |
752 | QTest::newRow(qPrintable(devName + '3')) << sequential << qint8(-1) << qint16(-2) |
753 | << qint32(-3) << qint64(-4) << true |
754 | << -123.0f << double(-234.0) |
755 | << QByteArray("Qt rocks!" ); |
756 | sequential = !sequential; |
757 | } while (!sequential); |
758 | } |
759 | |
760 | // Test transaction integrity |
761 | void tst_QIODevice::transaction() |
762 | { |
763 | QByteArray testBuffer; |
764 | |
765 | QFETCH(bool, sequential); |
766 | QFETCH(qint8, i8Data); |
767 | QFETCH(qint16, i16Data); |
768 | QFETCH(qint32, i32Data); |
769 | QFETCH(qint64, i64Data); |
770 | QFETCH(bool, bData); |
771 | QFETCH(float, fData); |
772 | QFETCH(double, dData); |
773 | QFETCH(QByteArray, strData); |
774 | |
775 | { |
776 | QDataStream stream(&testBuffer, QIODevice::WriteOnly); |
777 | |
778 | stream << i8Data << i16Data << i32Data << i64Data |
779 | << bData << fData << dData << strData.constData(); |
780 | } |
781 | |
782 | for (int splitPos = 0; splitPos <= testBuffer.size(); ++splitPos) { |
783 | QByteArray readBuffer(testBuffer.left(len: splitPos)); |
784 | QIODevice *dev = sequential ? (QIODevice *) new SequentialReadBuffer(&readBuffer) |
785 | : (QIODevice *) new QBuffer(&readBuffer); |
786 | dev->open(mode: QIODevice::ReadOnly); |
787 | QDataStream stream(dev); |
788 | |
789 | qint8 i8; |
790 | qint16 i16; |
791 | qint32 i32; |
792 | qint64 i64; |
793 | bool b; |
794 | float f; |
795 | double d; |
796 | char *str; |
797 | |
798 | forever { |
799 | QVERIFY(!dev->isTransactionStarted()); |
800 | dev->startTransaction(); |
801 | QVERIFY(dev->isTransactionStarted()); |
802 | |
803 | // Try to read all data in one go. If the status of the data stream |
804 | // indicates an unsuccessful operation, restart a read transaction |
805 | // on the completed buffer. |
806 | stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> str; |
807 | |
808 | QVERIFY(stream.atEnd()); |
809 | if (stream.status() == QDataStream::Ok) { |
810 | dev->commitTransaction(); |
811 | break; |
812 | } |
813 | |
814 | dev->rollbackTransaction(); |
815 | QVERIFY(splitPos == 0 || !stream.atEnd()); |
816 | QCOMPARE(dev->pos(), Q_INT64_C(0)); |
817 | QCOMPARE(dev->bytesAvailable(), qint64(readBuffer.size())); |
818 | QVERIFY(readBuffer.size() < testBuffer.size()); |
819 | delete [] str; |
820 | readBuffer.append(a: testBuffer.right(len: testBuffer.size() - splitPos)); |
821 | stream.resetStatus(); |
822 | } |
823 | |
824 | QVERIFY(!dev->isTransactionStarted()); |
825 | QVERIFY(stream.atEnd()); |
826 | QCOMPARE(i8, i8Data); |
827 | QCOMPARE(i16, i16Data); |
828 | QCOMPARE(i32, i32Data); |
829 | QCOMPARE(i64, i64Data); |
830 | QCOMPARE(b, bData); |
831 | QCOMPARE(f, fData); |
832 | QCOMPARE(d, dData); |
833 | QVERIFY(strData == str); |
834 | delete [] str; |
835 | stream.setDevice(0); |
836 | delete dev; |
837 | } |
838 | } |
839 | |
840 | QTEST_MAIN(tst_QIODevice) |
841 | #include "tst_qiodevice.moc" |
842 | |