// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include <QtTest/QtTest>
#include <QtSerialBus/QModbusReply>

class tst_QModbusReply : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();
    void tst_ctor();
    void tst_setFinished();
    void tst_setError_data();
    void tst_setError();
    void tst_setResult();
};

void tst_QModbusReply::initTestCase()
{
    qRegisterMetaType<QModbusDevice::Error>();
}

void tst_QModbusReply::tst_ctor()
{
    QModbusReply r(QModbusReply::Common, 1, this);
    QCOMPARE(r.type(), QModbusReply::Common);
    QCOMPARE(r.serverAddress(), 1);
    QCOMPARE(r.isFinished(), false);
    QCOMPARE(r.result().isValid(), false);
    QCOMPARE(r.rawResult().isValid(), false);
    QCOMPARE(r.errorString(), QString());
    QCOMPARE(r.error(), QModbusDevice::NoError);

    QModbusReply r2(QModbusReply::Raw, 2, this);
    QCOMPARE(r2.type(), QModbusReply::Raw);
    QCOMPARE(r2.serverAddress(), 2);
    QCOMPARE(r2.isFinished(), false);
    QCOMPARE(r2.result().isValid(), false);
    QCOMPARE(r2.rawResult().isValid(), false);
    QCOMPARE(r2.errorString(), QString());
    QCOMPARE(r2.error(), QModbusDevice::NoError);
}

void tst_QModbusReply::tst_setFinished()
{
    QModbusReply replyTest(QModbusReply::Common, 1);
    QCOMPARE(replyTest.serverAddress(), 1);
    QSignalSpy finishedSpy(&replyTest, &QModbusReply::finished);
    QSignalSpy errorSpy(&replyTest, &QModbusReply::errorOccurred);

    QCOMPARE(replyTest.serverAddress(), 1);
    QCOMPARE(replyTest.isFinished(), false);
    QCOMPARE(replyTest.result().isValid(), false);
    QCOMPARE(replyTest.rawResult().isValid(), false);
    QCOMPARE(replyTest.errorString(), QString());
    QCOMPARE(replyTest.error(), QModbusDevice::NoError);

    QVERIFY(finishedSpy.isEmpty());
    QVERIFY(errorSpy.isEmpty());

    replyTest.setFinished(true);
    QVERIFY(finishedSpy.size() == 1);
    QVERIFY(errorSpy.isEmpty());
    QCOMPARE(replyTest.serverAddress(), 1);
    QCOMPARE(replyTest.isFinished(), true);
    QCOMPARE(replyTest.result().isValid(), false);
    QCOMPARE(replyTest.rawResult().isValid(), false);
    QCOMPARE(replyTest.errorString(), QString());
    QCOMPARE(replyTest.error(), QModbusDevice::NoError);

    replyTest.setFinished(false);
    QVERIFY(finishedSpy.size() == 1); // no further signal
    QVERIFY(errorSpy.isEmpty());
    QCOMPARE(replyTest.serverAddress(), 1);
    QCOMPARE(replyTest.isFinished(), false);
    QCOMPARE(replyTest.result().isValid(), false);
    QCOMPARE(replyTest.rawResult().isValid(), false);
    QCOMPARE(replyTest.errorString(), QString());
    QCOMPARE(replyTest.error(), QModbusDevice::NoError);
}

void tst_QModbusReply::tst_setError_data()
{
    QTest::addColumn<QModbusDevice::Error>("error");
    QTest::addColumn<QString>("errorString");

    QTest::newRow("ProtocolError") << QModbusDevice::ProtocolError << QString("ProtocolError");
    QTest::newRow("NoError") << QModbusDevice::NoError << QString("NoError");
    QTest::newRow("NoError-empty") << QModbusDevice::NoError << QString();
    QTest::newRow("TimeoutError") << QModbusDevice::TimeoutError << QString("TimeoutError");
    QTest::newRow("ReplyAbortedError") << QModbusDevice::ReplyAbortedError << QString("AbortedError");
}

void tst_QModbusReply::tst_setError()
{
    QFETCH(QModbusDevice::Error, error);
    QFETCH(QString, errorString);

    QModbusReply replyTest(QModbusReply::Common, 1);
    QCOMPARE(replyTest.serverAddress(), 1);
    QSignalSpy finishedSpy(&replyTest, &QModbusReply::finished);
    QSignalSpy errorSpy(&replyTest, &QModbusReply::errorOccurred);

    QVERIFY(finishedSpy.isEmpty());
    QVERIFY(errorSpy.isEmpty());

    replyTest.setError(error, errorString);
    QCOMPARE(finishedSpy.size(), 1);
    QCOMPARE(errorSpy.size(), 1);
    QCOMPARE(replyTest.rawResult().isValid(), false);
    QCOMPARE(replyTest.error(), error);
    QCOMPARE(replyTest.errorString(), errorString);
    QCOMPARE(errorSpy.at(0).at(0).value<QModbusDevice::Error>(), error);

    replyTest.setError(error, errorString);
    replyTest.setFinished(true);
    QCOMPARE(finishedSpy.size(), 3); //setError() implies call to setFinished()
    QCOMPARE(errorSpy.size(), 2);
}

void tst_QModbusReply::tst_setResult()
{
    QModbusDataUnit unit(QModbusDataUnit::Coils, 5, {4,5,6});
    QCOMPARE(unit.startAddress(), 5);
    QCOMPARE(unit.valueCount(), 3);
    QCOMPARE(unit.registerType(), QModbusDataUnit::Coils);
    QCOMPARE(unit.isValid(), true);
    QList<quint16> reference = { 4, 5, 6 };
    QCOMPARE(unit.values(), reference);

    QModbusReply replyTest(QModbusReply::Common, 1);
    QCOMPARE(replyTest.serverAddress(), 1);
    QSignalSpy finishedSpy(&replyTest, &QModbusReply::finished);
    QSignalSpy errorSpy(&replyTest, &QModbusReply::errorOccurred);

    QVERIFY(finishedSpy.isEmpty());
    QVERIFY(errorSpy.isEmpty());

    QCOMPARE(replyTest.result().startAddress(), -1);
    QCOMPARE(replyTest.result().valueCount(), 0);
    QCOMPARE(replyTest.result().registerType(), QModbusDataUnit::Invalid);
    QCOMPARE(replyTest.result().isValid(), false);
    QCOMPARE(replyTest.rawResult().isValid(), false);
    QCOMPARE(replyTest.result().values(), QList<quint16>());

    QModbusResponse response(QModbusResponse::ReadExceptionStatus, quint16(0x0000));
    replyTest.setResult(unit);
    replyTest.setRawResult(response);
    QCOMPARE(finishedSpy.size(), 0);
    QCOMPARE(errorSpy.size(), 0);
    QCOMPARE(replyTest.result().startAddress(), 5);
    QCOMPARE(replyTest.result().valueCount(), 3);
    QCOMPARE(replyTest.result().registerType(), QModbusDataUnit::Coils);
    QCOMPARE(replyTest.result().isValid(), true);
    QCOMPARE(replyTest.result().values(), reference);
    QCOMPARE(replyTest.rawResult().isValid(), true);

    auto tmp = replyTest.rawResult();
    QCOMPARE(tmp.functionCode(), QModbusResponse::ReadExceptionStatus);
    QCOMPARE(tmp.data(), QByteArray::fromHex("0000"));

    QModbusReply replyRawTest(QModbusReply::Raw, 1);
    QCOMPARE(replyRawTest.serverAddress(), 1);
    QSignalSpy finishedSpyRaw(&replyRawTest, &QModbusReply::finished);
    QSignalSpy errorSpyRaw(&replyRawTest, &QModbusReply::errorOccurred);

    QVERIFY(finishedSpyRaw.isEmpty());
    QVERIFY(errorSpyRaw.isEmpty());

    QCOMPARE(replyRawTest.result().startAddress(), -1);
    QCOMPARE(replyRawTest.result().valueCount(), 0);
    QCOMPARE(replyRawTest.result().registerType(), QModbusDataUnit::Invalid);
    QCOMPARE(replyRawTest.result().isValid(), false);
    QCOMPARE(replyRawTest.rawResult().isValid(), false);
    QCOMPARE(replyRawTest.result().values(), QList<quint16>());

    replyRawTest.setResult(unit);
    replyRawTest.setRawResult(response);
    QCOMPARE(finishedSpy.size(), 0);
    QCOMPARE(errorSpyRaw.size(), 0);
    QCOMPARE(replyRawTest.result().isValid(), true);
    QCOMPARE(replyRawTest.rawResult().isValid(), true);

    tmp = replyRawTest.rawResult();
    QCOMPARE(tmp.functionCode(), QModbusResponse::ReadExceptionStatus);
    QCOMPARE(tmp.data(), QByteArray::fromHex("0000"));
}

QTEST_MAIN(tst_QModbusReply)

#include "tst_qmodbusreply.moc"
