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 | |
47 | Q_DECLARE_METATYPE(QCanBusDevice::Filter) |
48 | |
49 | class tst_Backend : public QCanBusDevice |
50 | { |
51 | Q_OBJECT |
52 | public: |
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 | |
121 | public 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 | |
134 | private: |
135 | QCanBusFrame referenceFrame; |
136 | bool firstOpen = true; |
137 | bool writeBufferUsed = true; |
138 | }; |
139 | |
140 | class tst_QCanBusDevice : public QObject |
141 | { |
142 | Q_OBJECT |
143 | public: |
144 | explicit tst_QCanBusDevice(); |
145 | |
146 | private 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(); |
163 | private: |
164 | QScopedPointer<tst_Backend> device; |
165 | }; |
166 | |
167 | tst_QCanBusDevice::tst_QCanBusDevice() |
168 | { |
169 | qRegisterMetaType<QCanBusDevice::CanBusDeviceState>(); |
170 | qRegisterMetaType<QCanBusDevice::CanBusError>(); |
171 | qRegisterMetaType<QCanBusDevice::Filter>(); |
172 | } |
173 | |
174 | void 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 | |
197 | void 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 | |
224 | void 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 | |
263 | void 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 | |
291 | void tst_QCanBusDevice::readAll() |
292 | { |
293 | enum { = 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 | |
313 | void 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 | |
336 | void 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 | |
368 | void 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 | |
410 | void tst_QCanBusDevice::cleanupTestCase() |
411 | { |
412 | device->disconnectDevice(); |
413 | QCOMPARE(device->state(), QCanBusDevice::UnconnectedState); |
414 | QCanBusFrame frame = device->readFrame(); |
415 | QVERIFY(!frame.frameId()); |
416 | } |
417 | |
418 | void 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 | |
460 | void 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 | |
530 | void 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 | |
543 | void 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 | |
556 | void 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 | |
636 | void 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 | |
717 | QTEST_MAIN(tst_QCanBusDevice) |
718 | |
719 | #include "tst_qcanbusdevice.moc" |
720 | |