1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtSerialBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include <QtSerialBus/qcanbusdevice.h>
38#include <QtSerialBus/qcanbusframe.h>
39
40#include <QtCore/qscopedpointer.h>
41#include <QtCore/qtimer.h>
42#include <QtTest/qsignalspy.h>
43#include <QtTest/qtest.h>
44
45#include <memory>
46
47Q_DECLARE_METATYPE(QCanBusDevice::Filter)
48
49class tst_Backend : public QCanBusDevice
50{
51 Q_OBJECT
52public:
53 tst_Backend()
54 {
55 referenceFrame.setFrameId(5);
56 referenceFrame.setPayload(QByteArray("FOOBAR"));
57 referenceFrame.setTimeStamp({ 22, 23 });
58 referenceFrame.setExtendedFrameFormat(1);
59 }
60
61 bool triggerNewFrame()
62 {
63 if (state() != QCanBusDevice::ConnectedState)
64 return false;
65
66 // the line below triggers the framesReceived() signal
67 enqueueReceivedFrames(newFrames: {referenceFrame});
68
69 return true;
70 }
71
72 bool open()
73 {
74 if (firstOpen) {
75 firstOpen = false;
76 return false;
77 }
78 setState(QCanBusDevice::ConnectedState);
79 return true;
80 }
81
82 void close()
83 {
84 setState(QCanBusDevice::UnconnectedState);
85 }
86
87 bool writeFrame(const QCanBusFrame &data)
88 {
89 if (state() != QCanBusDevice::ConnectedState) {
90 setError(QStringLiteral("Cannot write frame as device is not connected"),
91 QCanBusDevice::OperationError);
92 return false;
93 }
94
95 if (writeBufferUsed) {
96 enqueueOutgoingFrame(newFrame: data);
97 QTimer::singleShot(interval: 2000, context: this, slot: [this](){ triggerDelayedWrites(); });
98 } else {
99 emit framesWritten(framesCount: 1);
100 }
101 return true;
102 }
103
104 void emulateError(const QString &text, QCanBusDevice::CanBusError e)
105 {
106 setError(errorText: text, e);
107 }
108
109 QString interpretErrorFrame(const QCanBusFrame &/*errorFrame*/)
110 {
111 return QString();
112 }
113
114 bool isWriteBuffered() const { return writeBufferUsed; }
115 void setWriteBuffered(bool isBuffered)
116 {
117 // allows switching between buffered and unbuffered write mode
118 writeBufferUsed = isBuffered;
119 }
120
121public slots:
122 void triggerDelayedWrites()
123 {
124 if (framesToWrite() == 0)
125 return;
126
127 dequeueOutgoingFrame();
128 emit framesWritten(framesCount: 1);
129
130 if (framesToWrite() > 0)
131 QTimer::singleShot(interval: 2000, context: this, slot: [this](){ triggerDelayedWrites(); });
132 }
133
134private:
135 QCanBusFrame referenceFrame;
136 bool firstOpen = true;
137 bool writeBufferUsed = true;
138};
139
140class tst_QCanBusDevice : public QObject
141{
142 Q_OBJECT
143public:
144 explicit tst_QCanBusDevice();
145
146private slots:
147 void initTestCase();
148 void conf();
149 void write();
150 void read();
151 void readAll();
152 void clearInputBuffer();
153 void clearOutputBuffer();
154 void error();
155 void cleanupTestCase();
156 void tst_filtering();
157 void filterEqual_data();
158 void filterEqual();
159 void tst_bufferingAttribute();
160
161 void tst_waitForFramesReceived();
162 void tst_waitForFramesWritten();
163private:
164 QScopedPointer<tst_Backend> device;
165};
166
167tst_QCanBusDevice::tst_QCanBusDevice()
168{
169 qRegisterMetaType<QCanBusDevice::CanBusDeviceState>();
170 qRegisterMetaType<QCanBusDevice::CanBusError>();
171 qRegisterMetaType<QCanBusDevice::Filter>();
172}
173
174void tst_QCanBusDevice::initTestCase()
175{
176 device.reset(other: new tst_Backend());
177 QVERIFY(device);
178
179 QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged);
180
181 QVERIFY(!device->connectDevice()); // first connect triggered to fail
182 QCOMPARE(device->error(), QCanBusDevice::NoError);
183 QVERIFY(device->connectDevice());
184 QCOMPARE(device->error(), QCanBusDevice::NoError);
185 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
186 QCOMPARE(stateSpy.count(), 4);
187 QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(),
188 QCanBusDevice::ConnectingState);
189 QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(),
190 QCanBusDevice::UnconnectedState);
191 QCOMPARE(stateSpy.at(2).at(0).value<QCanBusDevice::CanBusDeviceState>(),
192 QCanBusDevice::ConnectingState);
193 QCOMPARE(stateSpy.at(3).at(0).value<QCanBusDevice::CanBusDeviceState>(),
194 QCanBusDevice::ConnectedState);
195}
196
197void tst_QCanBusDevice::conf()
198{
199 QVERIFY(device->configurationKeys().isEmpty());
200
201 // invalid QVariant ignored
202 device->setConfigurationParameter(key: QCanBusDevice::RawFilterKey, value: QVariant());
203 QVERIFY(device->configurationKeys().isEmpty());
204
205 QCanBusFrame::FrameErrors error =
206 (QCanBusFrame::LostArbitrationError | QCanBusFrame::BusError);
207
208 device->setConfigurationParameter(
209 key: QCanBusDevice::ErrorFilterKey, value: QVariant::fromValue(value: error));
210 QVariant value = device->configurationParameter(key: QCanBusDevice::ErrorFilterKey);
211 QVERIFY(value.isValid());
212
213 QVector<int> keys = device->configurationKeys();
214 QCOMPARE(keys.size(), 1);
215 QVERIFY(keys.at(0) == QCanBusDevice::ErrorFilterKey);
216
217 QCOMPARE(value.value<QCanBusFrame::FrameErrors>(),
218 QCanBusFrame::LostArbitrationError | QCanBusFrame::BusError);
219
220 device->setConfigurationParameter(key: QCanBusDevice::ErrorFilterKey, value: QVariant());
221 QVERIFY(device->configurationKeys().isEmpty());
222}
223
224void tst_QCanBusDevice::write()
225{
226 // we assume unbuffered writing in this function
227 device->setWriteBuffered(false);
228 QVERIFY(!device->isWriteBuffered());
229
230 QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten);
231 QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged);
232
233 QCanBusFrame frame;
234 frame.setPayload(QByteArray("testData"));
235
236 device->disconnectDevice();
237 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000);
238 QCOMPARE(stateSpy.count(), 2);
239 QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(),
240 QCanBusDevice::ClosingState);
241 QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(),
242 QCanBusDevice::UnconnectedState);
243 stateSpy.clear();
244 QVERIFY(stateSpy.isEmpty());
245
246 QVERIFY(!device->writeFrame(frame));
247 QCOMPARE(device->error(), QCanBusDevice::OperationError);
248 QCOMPARE(spy.count(), 0);
249
250 device->connectDevice();
251 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
252 QCOMPARE(stateSpy.count(), 2);
253 QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(),
254 QCanBusDevice::ConnectingState);
255 QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(),
256 QCanBusDevice::ConnectedState);
257
258 QVERIFY(device->writeFrame(frame));
259 QCOMPARE(device->error(), QCanBusDevice::NoError);
260 QCOMPARE(spy.count(), 1);
261}
262
263void tst_QCanBusDevice::read()
264{
265 QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged);
266
267 device->disconnectDevice();
268 QCOMPARE(device->state(), QCanBusDevice::UnconnectedState);
269 stateSpy.clear();
270
271 const QCanBusFrame frame1 = device->readFrame();
272 QCOMPARE(device->error(), QCanBusDevice::OperationError);
273
274 QVERIFY(device->connectDevice());
275 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
276 QCOMPARE(stateSpy.count(), 2);
277 QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(),
278 QCanBusDevice::ConnectingState);
279 QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(),
280 QCanBusDevice::ConnectedState);
281
282 device->triggerNewFrame();
283 const QCanBusFrame frame2 = device->readFrame();
284 QCOMPARE(device->error(), QCanBusDevice::NoError);
285 QVERIFY(!frame1.frameId());
286 QVERIFY(!frame1.isValid());
287 QVERIFY(frame2.frameId());
288 QVERIFY(frame2.isValid());
289}
290
291void tst_QCanBusDevice::readAll()
292{
293 enum { FrameNumber = 10 };
294 device->disconnectDevice();
295 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000);
296
297 const QVector<QCanBusFrame> empty = device->readAllFrames();
298 QCOMPARE(device->error(), QCanBusDevice::OperationError);
299 QVERIFY(empty.isEmpty());
300
301 QVERIFY(device->connectDevice());
302 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
303
304 for (int i = 0; i < FrameNumber; ++i)
305 device->triggerNewFrame();
306
307 const QVector<QCanBusFrame> frames = device->readAllFrames();
308 QCOMPARE(device->error(), QCanBusDevice::NoError);
309 QCOMPARE(FrameNumber, frames.size());
310 QVERIFY(!device->framesAvailable());
311}
312
313void tst_QCanBusDevice::clearInputBuffer()
314{
315 device->disconnectDevice();
316 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000);
317
318 device->clear(direction: QCanBusDevice::Input);
319 QCOMPARE(device->error(), QCanBusDevice::OperationError);
320
321 QVERIFY(device->connectDevice());
322 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
323
324 device->clear(direction: QCanBusDevice::Input);
325 QCOMPARE(device->error(), QCanBusDevice::NoError);
326
327 for (int i = 0; i < 10; ++i)
328 device->triggerNewFrame();
329
330 device->clear(direction: QCanBusDevice::Input);
331 QCOMPARE(device->error(), QCanBusDevice::NoError);
332
333 QVERIFY(!device->framesAvailable());
334}
335
336void tst_QCanBusDevice::clearOutputBuffer()
337{
338 // this test requires buffered writing
339 device->setWriteBuffered(true);
340 device->disconnectDevice();
341 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000);
342
343 device->clear(direction: QCanBusDevice::Output);
344 QCOMPARE(device->error(), QCanBusDevice::OperationError);
345
346 QVERIFY(device->connectDevice());
347 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
348
349 device->clear(direction: QCanBusDevice::Output);
350 QCOMPARE(device->error(), QCanBusDevice::NoError);
351
352 // first test buffered writing, frames will be written after some delay
353 QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten);
354 for (int i = 0; i < 10; ++i)
355 device->writeFrame(data: QCanBusFrame(0x123, "output"));
356 QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 10, 5000);
357
358 // now test clearing the buffer before the frames are actually written
359 spy.clear();
360 for (int i = 0; i < 10; ++i)
361 device->writeFrame(data: QCanBusFrame(0x123, "output"));
362
363 device->clear(direction: QCanBusDevice::Output);
364 QCOMPARE(device->error(), QCanBusDevice::NoError);
365 QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 0, 5000);
366}
367
368void tst_QCanBusDevice::error()
369{
370 QSignalSpy spy(device.data(), &QCanBusDevice::errorOccurred);
371 QString testString(QStringLiteral("testString"));
372
373 auto backend = qobject_cast<tst_Backend *>(object: device.data());
374 QVERIFY(backend);
375
376 // NoError
377 QVERIFY(device->errorString().isEmpty());
378
379 // ReadError
380 backend->emulateError(text: testString + QStringLiteral("a"), e: QCanBusDevice::ReadError);
381 QCOMPARE(testString + QStringLiteral("a"), device->errorString());
382 QCOMPARE(device->error(), 1);
383 QCOMPARE(spy.count(), 1);
384
385 // WriteError
386 backend->emulateError(text: testString + QStringLiteral("b"), e: QCanBusDevice::WriteError);
387 QCOMPARE(testString + QStringLiteral("b"), device->errorString());
388 QCOMPARE(device->error(), 2);
389 QCOMPARE(spy.count(), 2);
390
391 // ConnectionError
392 backend->emulateError(text: testString + QStringLiteral("c"), e: QCanBusDevice::ConnectionError);
393 QCOMPARE(testString + QStringLiteral("c"), device->errorString());
394 QCOMPARE(device->error(), 3);
395 QCOMPARE(spy.count(), 3);
396
397 // ConfigurationError
398 backend->emulateError(text: testString + QStringLiteral("d"), e: QCanBusDevice::ConfigurationError);
399 QCOMPARE(testString + QStringLiteral("d"), device->errorString());
400 QCOMPARE(device->error(), 4);
401 QCOMPARE(spy.count(), 4);
402
403 // UnknownError
404 backend->emulateError(text: testString + QStringLiteral("e"), e: QCanBusDevice::UnknownError);
405 QCOMPARE(testString + QStringLiteral("e"), device->errorString());
406 QCOMPARE(device->error(), 5);
407 QCOMPARE(spy.count(), 5);
408}
409
410void tst_QCanBusDevice::cleanupTestCase()
411{
412 device->disconnectDevice();
413 QCOMPARE(device->state(), QCanBusDevice::UnconnectedState);
414 QCanBusFrame frame = device->readFrame();
415 QVERIFY(!frame.frameId());
416}
417
418void tst_QCanBusDevice::tst_filtering()
419{
420 QCanBusDevice::Filter defaultFilter;
421 QCOMPARE(defaultFilter.frameId, 0x0u);
422 QCOMPARE(defaultFilter.frameIdMask, 0x0u);
423 QCOMPARE(defaultFilter.type, QCanBusFrame::InvalidFrame);
424 QCOMPARE(defaultFilter.format, QCanBusDevice::Filter::MatchBaseAndExtendedFormat);
425
426 QList<QCanBusDevice::Filter> filters;
427 QCanBusDevice::Filter f;
428 f.frameId = 0x1;
429 f.type = QCanBusFrame::DataFrame;
430 f.frameIdMask = 0xFF;
431 f.format = QCanBusDevice::Filter::MatchBaseAndExtendedFormat;
432 filters.append(t: f);
433
434 f.frameId = 0x2;
435 f.type = QCanBusFrame::RemoteRequestFrame;
436 f.frameIdMask = 0x0;
437 f.format = QCanBusDevice::Filter::MatchBaseFormat;
438 filters.append(t: f);
439
440 const QVariant wrapper = QVariant::fromValue(value: filters);
441 const auto newFilter = wrapper.value<QList<QCanBusDevice::Filter> >();
442 QCOMPARE(newFilter.count(), 2);
443
444 QCOMPARE(newFilter.at(0).type, QCanBusFrame::DataFrame);
445 QCOMPARE(newFilter.at(0).frameId, 0x1u);
446 QCOMPARE(newFilter.at(0).frameIdMask, 0xFFu);
447 QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat);
448 QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchBaseFormat);
449 QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchExtendedFormat);
450
451 QCOMPARE(newFilter.at(1).type, QCanBusFrame::RemoteRequestFrame);
452 QCOMPARE(newFilter.at(1).frameId, 0x2u);
453 QCOMPARE(newFilter.at(1).frameIdMask, 0x0u);
454 QVERIFY((newFilter.at(1).format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat)
455 != QCanBusDevice::Filter::MatchBaseAndExtendedFormat);
456 QVERIFY(newFilter.at(1).format & QCanBusDevice::Filter::MatchBaseFormat);
457 QVERIFY(!(newFilter.at(1).format & QCanBusDevice::Filter::MatchExtendedFormat));
458}
459
460void tst_QCanBusDevice::filterEqual_data()
461{
462 using Filter = QCanBusDevice::Filter;
463 using Frame = QCanBusFrame;
464
465 QTest::addColumn<QCanBusDevice::Filter>(name: "first");
466 QTest::addColumn<QCanBusDevice::Filter>(name: "second");
467 QTest::addColumn<bool>(name: "isEqual");
468
469 auto filter = [](quint32 frameId, quint32 frameIdMask,
470 Frame::FrameType type,
471 Filter::FormatFilter format) {
472 Filter result;
473 result.frameId = frameId;
474 result.frameIdMask = frameIdMask;
475 result.type = type;
476 result.format = format;
477 return result;
478 };
479
480 QTest::newRow(dataTag: "empty-equal")
481 << Filter()
482 << Filter()
483 << true;
484 QTest::newRow(dataTag: "empty-default-equal")
485 << Filter()
486 << filter(0, 0, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
487 << true;
488 QTest::newRow(dataTag: "empty-non-default-different")
489 << Filter()
490 << filter(1, 2, Frame::ErrorFrame, Filter::MatchBaseFormat)
491 << false;
492
493 QTest::newRow(dataTag: "frame-id-equal")
494 << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat)
495 << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat)
496 << true;
497 QTest::newRow(dataTag: "frame-id-different")
498 << filter(0x345, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat)
499 << filter(0x346, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat)
500 << false;
501
502 QTest::newRow(dataTag: "frame-mask-equal")
503 << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
504 << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
505 << true;
506 QTest::newRow(dataTag: "frame-mask-different")
507 << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
508 << filter(0x123, 0x7FE, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
509 << false;
510
511 QTest::newRow(dataTag: "frame-type-equal")
512 << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat)
513 << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat)
514 << true;
515 QTest::newRow(dataTag: "frame-type-different")
516 << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat)
517 << filter(0xFFF, 0xBFF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat)
518 << false;
519
520 QTest::newRow(dataTag: "filter-equal")
521 << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat)
522 << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat)
523 << true;
524 QTest::newRow(dataTag: "filter-different")
525 << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat)
526 << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchBaseAndExtendedFormat)
527 << false;
528}
529
530void tst_QCanBusDevice::filterEqual()
531{
532 QFETCH(QCanBusDevice::Filter, first);
533 QFETCH(QCanBusDevice::Filter, second);
534 QFETCH(bool, isEqual);
535
536 if (isEqual) {
537 QCOMPARE(first, second);
538 } else {
539 QVERIFY(first != second);
540 }
541}
542
543void tst_QCanBusDevice::tst_bufferingAttribute()
544{
545 std::unique_ptr<tst_Backend> canDevice(new tst_Backend);
546 QVERIFY(canDevice != nullptr);
547 // by default buffered set to true
548 QVERIFY(canDevice->isWriteBuffered());
549
550 canDevice->setWriteBuffered(false);
551 QVERIFY(!canDevice->isWriteBuffered());
552 canDevice->setWriteBuffered(true);
553 QVERIFY(canDevice->isWriteBuffered());
554}
555
556void tst_QCanBusDevice::tst_waitForFramesReceived()
557{
558 device->disconnectDevice();
559 QVERIFY(!device->waitForFramesReceived(100));
560 QCOMPARE(device->error(), QCanBusDevice::OperationError);
561
562 if (device->state() != QCanBusDevice::ConnectedState) {
563 QVERIFY(device->connectDevice());
564 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
565 }
566
567 QVERIFY(!device->framesAvailable());
568 QVERIFY(device->triggerNewFrame());
569 QVERIFY(device->framesAvailable());
570
571 // frame is already available, but no new frame comes in
572 // while function blocks, therefore times out
573 QVERIFY(!device->waitForFramesReceived(2000));
574 QCOMPARE(device->error(), QCanBusDevice::TimeoutError);
575
576 QCanBusFrame frame = device->readFrame();
577 QVERIFY(frame.isValid());
578 QCOMPARE(frame.payload(), QByteArray("FOOBAR"));
579 QVERIFY(!device->framesAvailable());
580
581 QElapsedTimer elapsed;
582 elapsed.start();
583 // no pending frame (should trigger active wait & timeout)
584 QVERIFY(!device->waitForFramesReceived(5000));
585 QVERIFY(elapsed.hasExpired(4000)); // should have caused time elapse
586 QCOMPARE(device->error(), QCanBusDevice::TimeoutError);
587
588 QTimer::singleShot(interval: 2000, slot: [&]() { device->triggerNewFrame(); });
589 elapsed.restart();
590 // frame will be inserted after 2s
591 QVERIFY(device->waitForFramesReceived(8000));
592 QCOMPARE(device->error(), QCanBusDevice::NoError);
593 QVERIFY(!elapsed.hasExpired(8000));
594
595 frame = device->readFrame();
596 QVERIFY(frame.isValid());
597 QCOMPARE(frame.payload(), QByteArray("FOOBAR"));
598 QVERIFY(!device->framesAvailable());
599
600 QTimer::singleShot(interval: 2000, slot: [&]() {
601 device->emulateError(QStringLiteral("TriggerError"), e: QCanBusDevice::ReadError);
602 });
603 elapsed.restart();
604 // error will be inserted after 2s
605 QVERIFY(!device->waitForFramesReceived(8000));
606 QVERIFY(!elapsed.hasExpired(8000));
607 QCOMPARE(device->errorString(), QStringLiteral("TriggerError"));
608 QCOMPARE(device->error(), QCanBusDevice::ReadError);
609
610 // test recursive calling of waitForFramesReceived() behavior
611 int handleCounter = 0;
612 QTimer::singleShot(interval: 1000, slot: [&]() {
613 device->triggerNewFrame();
614 device->triggerNewFrame();
615 });
616 QTimer::singleShot(interval: 2000, slot: [&]() { device->triggerNewFrame(); });
617 QObject::connect(sender: device.data(), signal: &QCanBusDevice::framesReceived, slot: [this, &handleCounter]() {
618 handleCounter++;
619 // this should trigger a recursion which we want to catch
620 QVERIFY(!device->waitForFramesReceived(5000));
621 // Only the first two frames create a recursion, as the outer
622 // waitForFramesReceived() will immediately exit once at least
623 // one frame was received. Therefore the third frame here leads
624 // to TimeoutError, as no further frame is received.
625 if (handleCounter < 3) {
626 QCOMPARE(device->error(), QCanBusDevice::OperationError);
627 } else {
628 QCOMPARE(device->error(), QCanBusDevice::TimeoutError);
629 }
630 });
631 QVERIFY(device->waitForFramesReceived(8000));
632 QCOMPARE(device->error(), QCanBusDevice::NoError);
633 QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000);
634}
635
636void tst_QCanBusDevice::tst_waitForFramesWritten()
637{
638 device->disconnectDevice();
639 QVERIFY(!device->waitForFramesWritten(100));
640 QCOMPARE(device->error(), QCanBusDevice::OperationError);
641
642 if (device->state() != QCanBusDevice::ConnectedState) {
643 QVERIFY(!device->waitForFramesWritten(100));
644 QCOMPARE(device->error(), QCanBusDevice::OperationError);
645
646 QVERIFY(device->connectDevice());
647 QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
648 }
649
650 device->setWriteBuffered(false);
651 QVERIFY(!device->waitForFramesWritten(1000)); // no buffer, waiting not possible
652 QCOMPARE(device->error(), QCanBusDevice::NoError);
653
654 device->setWriteBuffered(true);
655
656 QVERIFY(device->framesToWrite() == 0);
657 QVERIFY(!device->waitForFramesWritten(1000)); // nothing in buffer, nothing to wait for
658 QCOMPARE(device->error(), QCanBusDevice::NoError);
659
660 QCanBusFrame frame;
661 frame.setPayload(QByteArray("testData"));
662
663 // test error case
664 QTimer::singleShot(interval: 500, slot: [&]() {
665 device->emulateError(QStringLiteral("TriggerWriteError"), e: QCanBusDevice::WriteError);
666 });
667 device->writeFrame(data: frame);
668 QElapsedTimer elapsed;
669 elapsed.start();
670
671 // error will be triggered
672 QVERIFY(!device->waitForFramesWritten(8000));
673 QVERIFY(!elapsed.hasExpired(8000));
674 QCOMPARE(device->errorString(), QStringLiteral("TriggerWriteError"));
675 QCOMPARE(device->error(), QCanBusDevice::WriteError);
676
677 // flush remaining frames out to reset the test
678 QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
679
680 // test timeout
681 device->writeFrame(data: frame);
682 QVERIFY(!device->waitForFramesWritten(500));
683 QCOMPARE(device->error(), QCanBusDevice::TimeoutError);
684 QVERIFY(elapsed.hasExpired(500));
685
686 // flush remaining frames out to reset the test
687 QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
688
689 device->writeFrame(data: frame);
690 device->writeFrame(data: frame);
691 elapsed.restart();
692 QVERIFY(device->waitForFramesWritten(8000));
693 QCOMPARE(device->error(), QCanBusDevice::NoError);
694 QVERIFY(!elapsed.hasExpired(8000));
695
696 // flush remaining frames out to reset the test
697 QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
698
699 // test recursive calling of waitForFramesWritten() behavior
700 int handleCounter = 0;
701 device->writeFrame(data: frame);
702 QTimer::singleShot(interval: 1000, slot: [&]() { device->writeFrame(data: frame); });
703 QTimer::singleShot(interval: 2000, slot: [&]() { device->writeFrame(data: frame); });
704 QObject::connect(sender: device.data(), signal: &QCanBusDevice::framesWritten, slot: [this, &handleCounter]() {
705 handleCounter++;
706 // this should trigger a recursion which we want to catch
707 QVERIFY(!device->waitForFramesWritten(5000));
708 QCOMPARE(device->error(), QCanBusDevice::OperationError);
709 });
710 QVERIFY(device->waitForFramesWritten(8000));
711 QCOMPARE(device->error(), QCanBusDevice::NoError);
712 QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000);
713
714 device->setWriteBuffered(false);
715}
716
717QTEST_MAIN(tst_QCanBusDevice)
718
719#include "tst_qcanbusdevice.moc"
720

source code of qtserialbus/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp