| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtSerialBus module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtSerialBus/qcanbusdevice.h> |
| #include <QtSerialBus/qcanbusframe.h> |
| |
| #include <QtCore/qscopedpointer.h> |
| #include <QtCore/qtimer.h> |
| #include <QtTest/qsignalspy.h> |
| #include <QtTest/qtest.h> |
| |
| #include <memory> |
| |
| Q_DECLARE_METATYPE(QCanBusDevice::Filter) |
| |
| class tst_Backend : public QCanBusDevice |
| { |
| Q_OBJECT |
| public: |
| tst_Backend() |
| { |
| referenceFrame.setFrameId(5); |
| referenceFrame.setPayload(QByteArray("FOOBAR")); |
| referenceFrame.setTimeStamp({ 22, 23 }); |
| referenceFrame.setExtendedFrameFormat(1); |
| } |
| |
| bool triggerNewFrame() |
| { |
| if (state() != QCanBusDevice::ConnectedState) |
| return false; |
| |
| // the line below triggers the framesReceived() signal |
| enqueueReceivedFrames({referenceFrame}); |
| |
| return true; |
| } |
| |
| bool open() |
| { |
| if (firstOpen) { |
| firstOpen = false; |
| return false; |
| } |
| setState(QCanBusDevice::ConnectedState); |
| return true; |
| } |
| |
| void close() |
| { |
| setState(QCanBusDevice::UnconnectedState); |
| } |
| |
| bool writeFrame(const QCanBusFrame &data) |
| { |
| if (state() != QCanBusDevice::ConnectedState) { |
| setError(QStringLiteral("Cannot write frame as device is not connected"), |
| QCanBusDevice::OperationError); |
| return false; |
| } |
| |
| if (writeBufferUsed) { |
| enqueueOutgoingFrame(data); |
| QTimer::singleShot(2000, this, [this](){ triggerDelayedWrites(); }); |
| } else { |
| emit framesWritten(1); |
| } |
| return true; |
| } |
| |
| void emulateError(const QString &text, QCanBusDevice::CanBusError e) |
| { |
| setError(text, e); |
| } |
| |
| QString interpretErrorFrame(const QCanBusFrame &/*errorFrame*/) |
| { |
| return QString(); |
| } |
| |
| bool isWriteBuffered() const { return writeBufferUsed; } |
| void setWriteBuffered(bool isBuffered) |
| { |
| // allows switching between buffered and unbuffered write mode |
| writeBufferUsed = isBuffered; |
| } |
| |
| public slots: |
| void triggerDelayedWrites() |
| { |
| if (framesToWrite() == 0) |
| return; |
| |
| dequeueOutgoingFrame(); |
| emit framesWritten(1); |
| |
| if (framesToWrite() > 0) |
| QTimer::singleShot(2000, this, [this](){ triggerDelayedWrites(); }); |
| } |
| |
| private: |
| QCanBusFrame referenceFrame; |
| bool firstOpen = true; |
| bool writeBufferUsed = true; |
| }; |
| |
| class tst_QCanBusDevice : public QObject |
| { |
| Q_OBJECT |
| public: |
| explicit tst_QCanBusDevice(); |
| |
| private slots: |
| void initTestCase(); |
| void conf(); |
| void write(); |
| void read(); |
| void readAll(); |
| void clearInputBuffer(); |
| void clearOutputBuffer(); |
| void error(); |
| void cleanupTestCase(); |
| void tst_filtering(); |
| void filterEqual_data(); |
| void filterEqual(); |
| void tst_bufferingAttribute(); |
| |
| void tst_waitForFramesReceived(); |
| void tst_waitForFramesWritten(); |
| private: |
| QScopedPointer<tst_Backend> device; |
| }; |
| |
| tst_QCanBusDevice::tst_QCanBusDevice() |
| { |
| qRegisterMetaType<QCanBusDevice::CanBusDeviceState>(); |
| qRegisterMetaType<QCanBusDevice::CanBusError>(); |
| qRegisterMetaType<QCanBusDevice::Filter>(); |
| } |
| |
| void tst_QCanBusDevice::initTestCase() |
| { |
| device.reset(new tst_Backend()); |
| QVERIFY(device); |
| |
| QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); |
| |
| QVERIFY(!device->connectDevice()); // first connect triggered to fail |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QVERIFY(device->connectDevice()); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| QCOMPARE(stateSpy.count(), 4); |
| QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectingState); |
| QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::UnconnectedState); |
| QCOMPARE(stateSpy.at(2).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectingState); |
| QCOMPARE(stateSpy.at(3).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectedState); |
| } |
| |
| void tst_QCanBusDevice::conf() |
| { |
| QVERIFY(device->configurationKeys().isEmpty()); |
| |
| // invalid QVariant ignored |
| device->setConfigurationParameter(QCanBusDevice::RawFilterKey, QVariant()); |
| QVERIFY(device->configurationKeys().isEmpty()); |
| |
| QCanBusFrame::FrameErrors error = |
| (QCanBusFrame::LostArbitrationError | QCanBusFrame::BusError); |
| |
| device->setConfigurationParameter( |
| QCanBusDevice::ErrorFilterKey, QVariant::fromValue(error)); |
| QVariant value = device->configurationParameter(QCanBusDevice::ErrorFilterKey); |
| QVERIFY(value.isValid()); |
| |
| QVector<int> keys = device->configurationKeys(); |
| QCOMPARE(keys.size(), 1); |
| QVERIFY(keys.at(0) == QCanBusDevice::ErrorFilterKey); |
| |
| QCOMPARE(value.value<QCanBusFrame::FrameErrors>(), |
| QCanBusFrame::LostArbitrationError | QCanBusFrame::BusError); |
| |
| device->setConfigurationParameter(QCanBusDevice::ErrorFilterKey, QVariant()); |
| QVERIFY(device->configurationKeys().isEmpty()); |
| } |
| |
| void tst_QCanBusDevice::write() |
| { |
| // we assume unbuffered writing in this function |
| device->setWriteBuffered(false); |
| QVERIFY(!device->isWriteBuffered()); |
| |
| QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten); |
| QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); |
| |
| QCanBusFrame frame; |
| frame.setPayload(QByteArray("testData")); |
| |
| device->disconnectDevice(); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); |
| QCOMPARE(stateSpy.count(), 2); |
| QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ClosingState); |
| QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::UnconnectedState); |
| stateSpy.clear(); |
| QVERIFY(stateSpy.isEmpty()); |
| |
| QVERIFY(!device->writeFrame(frame)); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| QCOMPARE(spy.count(), 0); |
| |
| device->connectDevice(); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| QCOMPARE(stateSpy.count(), 2); |
| QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectingState); |
| QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectedState); |
| |
| QVERIFY(device->writeFrame(frame)); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QCOMPARE(spy.count(), 1); |
| } |
| |
| void tst_QCanBusDevice::read() |
| { |
| QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); |
| |
| device->disconnectDevice(); |
| QCOMPARE(device->state(), QCanBusDevice::UnconnectedState); |
| stateSpy.clear(); |
| |
| const QCanBusFrame frame1 = device->readFrame(); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| QCOMPARE(stateSpy.count(), 2); |
| QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectingState); |
| QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), |
| QCanBusDevice::ConnectedState); |
| |
| device->triggerNewFrame(); |
| const QCanBusFrame frame2 = device->readFrame(); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QVERIFY(!frame1.frameId()); |
| QVERIFY(!frame1.isValid()); |
| QVERIFY(frame2.frameId()); |
| QVERIFY(frame2.isValid()); |
| } |
| |
| void tst_QCanBusDevice::readAll() |
| { |
| enum { FrameNumber = 10 }; |
| device->disconnectDevice(); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); |
| |
| const QVector<QCanBusFrame> empty = device->readAllFrames(); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| QVERIFY(empty.isEmpty()); |
| |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| |
| for (int i = 0; i < FrameNumber; ++i) |
| device->triggerNewFrame(); |
| |
| const QVector<QCanBusFrame> frames = device->readAllFrames(); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QCOMPARE(FrameNumber, frames.size()); |
| QVERIFY(!device->framesAvailable()); |
| } |
| |
| void tst_QCanBusDevice::clearInputBuffer() |
| { |
| device->disconnectDevice(); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); |
| |
| device->clear(QCanBusDevice::Input); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| |
| device->clear(QCanBusDevice::Input); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| |
| for (int i = 0; i < 10; ++i) |
| device->triggerNewFrame(); |
| |
| device->clear(QCanBusDevice::Input); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| |
| QVERIFY(!device->framesAvailable()); |
| } |
| |
| void tst_QCanBusDevice::clearOutputBuffer() |
| { |
| // this test requires buffered writing |
| device->setWriteBuffered(true); |
| device->disconnectDevice(); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); |
| |
| device->clear(QCanBusDevice::Output); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| |
| device->clear(QCanBusDevice::Output); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| |
| // first test buffered writing, frames will be written after some delay |
| QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten); |
| for (int i = 0; i < 10; ++i) |
| device->writeFrame(QCanBusFrame(0x123, "output")); |
| QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 10, 5000); |
| |
| // now test clearing the buffer before the frames are actually written |
| spy.clear(); |
| for (int i = 0; i < 10; ++i) |
| device->writeFrame(QCanBusFrame(0x123, "output")); |
| |
| device->clear(QCanBusDevice::Output); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 0, 5000); |
| } |
| |
| void tst_QCanBusDevice::error() |
| { |
| QSignalSpy spy(device.data(), &QCanBusDevice::errorOccurred); |
| QString testString(QStringLiteral("testString")); |
| |
| auto backend = qobject_cast<tst_Backend *>(device.data()); |
| QVERIFY(backend); |
| |
| // NoError |
| QVERIFY(device->errorString().isEmpty()); |
| |
| // ReadError |
| backend->emulateError(testString + QStringLiteral("a"), QCanBusDevice::ReadError); |
| QCOMPARE(testString + QStringLiteral("a"), device->errorString()); |
| QCOMPARE(device->error(), 1); |
| QCOMPARE(spy.count(), 1); |
| |
| // WriteError |
| backend->emulateError(testString + QStringLiteral("b"), QCanBusDevice::WriteError); |
| QCOMPARE(testString + QStringLiteral("b"), device->errorString()); |
| QCOMPARE(device->error(), 2); |
| QCOMPARE(spy.count(), 2); |
| |
| // ConnectionError |
| backend->emulateError(testString + QStringLiteral("c"), QCanBusDevice::ConnectionError); |
| QCOMPARE(testString + QStringLiteral("c"), device->errorString()); |
| QCOMPARE(device->error(), 3); |
| QCOMPARE(spy.count(), 3); |
| |
| // ConfigurationError |
| backend->emulateError(testString + QStringLiteral("d"), QCanBusDevice::ConfigurationError); |
| QCOMPARE(testString + QStringLiteral("d"), device->errorString()); |
| QCOMPARE(device->error(), 4); |
| QCOMPARE(spy.count(), 4); |
| |
| // UnknownError |
| backend->emulateError(testString + QStringLiteral("e"), QCanBusDevice::UnknownError); |
| QCOMPARE(testString + QStringLiteral("e"), device->errorString()); |
| QCOMPARE(device->error(), 5); |
| QCOMPARE(spy.count(), 5); |
| } |
| |
| void tst_QCanBusDevice::cleanupTestCase() |
| { |
| device->disconnectDevice(); |
| QCOMPARE(device->state(), QCanBusDevice::UnconnectedState); |
| QCanBusFrame frame = device->readFrame(); |
| QVERIFY(!frame.frameId()); |
| } |
| |
| void tst_QCanBusDevice::tst_filtering() |
| { |
| QCanBusDevice::Filter defaultFilter; |
| QCOMPARE(defaultFilter.frameId, 0x0u); |
| QCOMPARE(defaultFilter.frameIdMask, 0x0u); |
| QCOMPARE(defaultFilter.type, QCanBusFrame::InvalidFrame); |
| QCOMPARE(defaultFilter.format, QCanBusDevice::Filter::MatchBaseAndExtendedFormat); |
| |
| QList<QCanBusDevice::Filter> filters; |
| QCanBusDevice::Filter f; |
| f.frameId = 0x1; |
| f.type = QCanBusFrame::DataFrame; |
| f.frameIdMask = 0xFF; |
| f.format = QCanBusDevice::Filter::MatchBaseAndExtendedFormat; |
| filters.append(f); |
| |
| f.frameId = 0x2; |
| f.type = QCanBusFrame::RemoteRequestFrame; |
| f.frameIdMask = 0x0; |
| f.format = QCanBusDevice::Filter::MatchBaseFormat; |
| filters.append(f); |
| |
| const QVariant wrapper = QVariant::fromValue(filters); |
| const auto newFilter = wrapper.value<QList<QCanBusDevice::Filter> >(); |
| QCOMPARE(newFilter.count(), 2); |
| |
| QCOMPARE(newFilter.at(0).type, QCanBusFrame::DataFrame); |
| QCOMPARE(newFilter.at(0).frameId, 0x1u); |
| QCOMPARE(newFilter.at(0).frameIdMask, 0xFFu); |
| QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat); |
| QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchBaseFormat); |
| QVERIFY(newFilter.at(0).format & QCanBusDevice::Filter::MatchExtendedFormat); |
| |
| QCOMPARE(newFilter.at(1).type, QCanBusFrame::RemoteRequestFrame); |
| QCOMPARE(newFilter.at(1).frameId, 0x2u); |
| QCOMPARE(newFilter.at(1).frameIdMask, 0x0u); |
| QVERIFY((newFilter.at(1).format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat) |
| != QCanBusDevice::Filter::MatchBaseAndExtendedFormat); |
| QVERIFY(newFilter.at(1).format & QCanBusDevice::Filter::MatchBaseFormat); |
| QVERIFY(!(newFilter.at(1).format & QCanBusDevice::Filter::MatchExtendedFormat)); |
| } |
| |
| void tst_QCanBusDevice::filterEqual_data() |
| { |
| using Filter = QCanBusDevice::Filter; |
| using Frame = QCanBusFrame; |
| |
| QTest::addColumn<QCanBusDevice::Filter>("first"); |
| QTest::addColumn<QCanBusDevice::Filter>("second"); |
| QTest::addColumn<bool>("isEqual"); |
| |
| auto filter = [](quint32 frameId, quint32 frameIdMask, |
| Frame::FrameType type, |
| Filter::FormatFilter format) { |
| Filter result; |
| result.frameId = frameId; |
| result.frameIdMask = frameIdMask; |
| result.type = type; |
| result.format = format; |
| return result; |
| }; |
| |
| QTest::newRow("empty-equal") |
| << Filter() |
| << Filter() |
| << true; |
| QTest::newRow("empty-default-equal") |
| << Filter() |
| << filter(0, 0, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << true; |
| QTest::newRow("empty-non-default-different") |
| << Filter() |
| << filter(1, 2, Frame::ErrorFrame, Filter::MatchBaseFormat) |
| << false; |
| |
| QTest::newRow("frame-id-equal") |
| << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) |
| << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) |
| << true; |
| QTest::newRow("frame-id-different") |
| << filter(0x345, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) |
| << filter(0x346, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) |
| << false; |
| |
| QTest::newRow("frame-mask-equal") |
| << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << true; |
| QTest::newRow("frame-mask-different") |
| << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << filter(0x123, 0x7FE, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << false; |
| |
| QTest::newRow("frame-type-equal") |
| << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) |
| << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) |
| << true; |
| QTest::newRow("frame-type-different") |
| << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) |
| << filter(0xFFF, 0xBFF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) |
| << false; |
| |
| QTest::newRow("filter-equal") |
| << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) |
| << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) |
| << true; |
| QTest::newRow("filter-different") |
| << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) |
| << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchBaseAndExtendedFormat) |
| << false; |
| } |
| |
| void tst_QCanBusDevice::filterEqual() |
| { |
| QFETCH(QCanBusDevice::Filter, first); |
| QFETCH(QCanBusDevice::Filter, second); |
| QFETCH(bool, isEqual); |
| |
| if (isEqual) { |
| QCOMPARE(first, second); |
| } else { |
| QVERIFY(first != second); |
| } |
| } |
| |
| void tst_QCanBusDevice::tst_bufferingAttribute() |
| { |
| std::unique_ptr<tst_Backend> canDevice(new tst_Backend); |
| QVERIFY(canDevice != nullptr); |
| // by default buffered set to true |
| QVERIFY(canDevice->isWriteBuffered()); |
| |
| canDevice->setWriteBuffered(false); |
| QVERIFY(!canDevice->isWriteBuffered()); |
| canDevice->setWriteBuffered(true); |
| QVERIFY(canDevice->isWriteBuffered()); |
| } |
| |
| void tst_QCanBusDevice::tst_waitForFramesReceived() |
| { |
| device->disconnectDevice(); |
| QVERIFY(!device->waitForFramesReceived(100)); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| if (device->state() != QCanBusDevice::ConnectedState) { |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| } |
| |
| QVERIFY(!device->framesAvailable()); |
| QVERIFY(device->triggerNewFrame()); |
| QVERIFY(device->framesAvailable()); |
| |
| // frame is already available, but no new frame comes in |
| // while function blocks, therefore times out |
| QVERIFY(!device->waitForFramesReceived(2000)); |
| QCOMPARE(device->error(), QCanBusDevice::TimeoutError); |
| |
| QCanBusFrame frame = device->readFrame(); |
| QVERIFY(frame.isValid()); |
| QCOMPARE(frame.payload(), QByteArray("FOOBAR")); |
| QVERIFY(!device->framesAvailable()); |
| |
| QElapsedTimer elapsed; |
| elapsed.start(); |
| // no pending frame (should trigger active wait & timeout) |
| QVERIFY(!device->waitForFramesReceived(5000)); |
| QVERIFY(elapsed.hasExpired(4000)); // should have caused time elapse |
| QCOMPARE(device->error(), QCanBusDevice::TimeoutError); |
| |
| QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); }); |
| elapsed.restart(); |
| // frame will be inserted after 2s |
| QVERIFY(device->waitForFramesReceived(8000)); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QVERIFY(!elapsed.hasExpired(8000)); |
| |
| frame = device->readFrame(); |
| QVERIFY(frame.isValid()); |
| QCOMPARE(frame.payload(), QByteArray("FOOBAR")); |
| QVERIFY(!device->framesAvailable()); |
| |
| QTimer::singleShot(2000, [&]() { |
| device->emulateError(QStringLiteral("TriggerError"), QCanBusDevice::ReadError); |
| }); |
| elapsed.restart(); |
| // error will be inserted after 2s |
| QVERIFY(!device->waitForFramesReceived(8000)); |
| QVERIFY(!elapsed.hasExpired(8000)); |
| QCOMPARE(device->errorString(), QStringLiteral("TriggerError")); |
| QCOMPARE(device->error(), QCanBusDevice::ReadError); |
| |
| // test recursive calling of waitForFramesReceived() behavior |
| int handleCounter = 0; |
| QTimer::singleShot(1000, [&]() { |
| device->triggerNewFrame(); |
| device->triggerNewFrame(); |
| }); |
| QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); }); |
| QObject::connect(device.data(), &QCanBusDevice::framesReceived, [this, &handleCounter]() { |
| handleCounter++; |
| // this should trigger a recursion which we want to catch |
| QVERIFY(!device->waitForFramesReceived(5000)); |
| // Only the first two frames create a recursion, as the outer |
| // waitForFramesReceived() will immediately exit once at least |
| // one frame was received. Therefore the third frame here leads |
| // to TimeoutError, as no further frame is received. |
| if (handleCounter < 3) { |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| } else { |
| QCOMPARE(device->error(), QCanBusDevice::TimeoutError); |
| } |
| }); |
| QVERIFY(device->waitForFramesReceived(8000)); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000); |
| } |
| |
| void tst_QCanBusDevice::tst_waitForFramesWritten() |
| { |
| device->disconnectDevice(); |
| QVERIFY(!device->waitForFramesWritten(100)); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| if (device->state() != QCanBusDevice::ConnectedState) { |
| QVERIFY(!device->waitForFramesWritten(100)); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| |
| QVERIFY(device->connectDevice()); |
| QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); |
| } |
| |
| device->setWriteBuffered(false); |
| QVERIFY(!device->waitForFramesWritten(1000)); // no buffer, waiting not possible |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| |
| device->setWriteBuffered(true); |
| |
| QVERIFY(device->framesToWrite() == 0); |
| QVERIFY(!device->waitForFramesWritten(1000)); // nothing in buffer, nothing to wait for |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| |
| QCanBusFrame frame; |
| frame.setPayload(QByteArray("testData")); |
| |
| // test error case |
| QTimer::singleShot(500, [&]() { |
| device->emulateError(QStringLiteral("TriggerWriteError"), QCanBusDevice::WriteError); |
| }); |
| device->writeFrame(frame); |
| QElapsedTimer elapsed; |
| elapsed.start(); |
| |
| // error will be triggered |
| QVERIFY(!device->waitForFramesWritten(8000)); |
| QVERIFY(!elapsed.hasExpired(8000)); |
| QCOMPARE(device->errorString(), QStringLiteral("TriggerWriteError")); |
| QCOMPARE(device->error(), QCanBusDevice::WriteError); |
| |
| // flush remaining frames out to reset the test |
| QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); |
| |
| // test timeout |
| device->writeFrame(frame); |
| QVERIFY(!device->waitForFramesWritten(500)); |
| QCOMPARE(device->error(), QCanBusDevice::TimeoutError); |
| QVERIFY(elapsed.hasExpired(500)); |
| |
| // flush remaining frames out to reset the test |
| QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); |
| |
| device->writeFrame(frame); |
| device->writeFrame(frame); |
| elapsed.restart(); |
| QVERIFY(device->waitForFramesWritten(8000)); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QVERIFY(!elapsed.hasExpired(8000)); |
| |
| // flush remaining frames out to reset the test |
| QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); |
| |
| // test recursive calling of waitForFramesWritten() behavior |
| int handleCounter = 0; |
| device->writeFrame(frame); |
| QTimer::singleShot(1000, [&]() { device->writeFrame(frame); }); |
| QTimer::singleShot(2000, [&]() { device->writeFrame(frame); }); |
| QObject::connect(device.data(), &QCanBusDevice::framesWritten, [this, &handleCounter]() { |
| handleCounter++; |
| // this should trigger a recursion which we want to catch |
| QVERIFY(!device->waitForFramesWritten(5000)); |
| QCOMPARE(device->error(), QCanBusDevice::OperationError); |
| }); |
| QVERIFY(device->waitForFramesWritten(8000)); |
| QCOMPARE(device->error(), QCanBusDevice::NoError); |
| QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000); |
| |
| device->setWriteBuffered(false); |
| } |
| |
| QTEST_MAIN(tst_QCanBusDevice) |
| |
| #include "tst_qcanbusdevice.moc" |