/*! * \brief Unit tests for \ref RemoteDispatcher * * \copyright Copyright (c) 2017-2018 Governikus GmbH & Co. KG, Germany */ #include "RemoteDispatcherClient.h" #include "RemoteDispatcherServer.h" #include "messages/IfdConnect.h" #include "messages/IfdDisconnect.h" #include "messages/IfdEstablishContext.h" #include "messages/IfdEstablishContextResponse.h" #include "messages/IfdTransmit.h" #include "MockDataChannel.h" #include #include using namespace governikus; class RemoteDispatcherSpy : public QObject { Q_OBJECT private: const QSharedPointer mRemoteDispatcher; bool mClosed; GlobalStatus::Code mCloseCode; QVector mReceivedMessageTypes; QVector mReceivedMessages; QVector mReceivedSignalSenders; public: RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher); virtual ~RemoteDispatcherSpy(); bool isClosed() const; GlobalStatus::Code getCloseCode() const; const QVector& getReceivedMessageTypes() const; const QVector& getReceivedMessages() const; const QVector& getReceivedSignalSenders() const; private Q_SLOTS: void onClosed(GlobalStatus::Code pCloseCode, const QString& pId); void onReceived(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId); }; RemoteDispatcherSpy::RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher) : mRemoteDispatcher(pRemoteDispatcher) , mClosed(false) , mCloseCode(GlobalStatus::Code::RemoteReader_CloseCode_Undefined) { const auto client = mRemoteDispatcher.objectCast(); if (client) { connect(client.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteDispatcherSpy::onClosed); connect(client.data(), &RemoteDispatcherClient::fireReceived, this, &RemoteDispatcherSpy::onReceived); return; } const auto server = mRemoteDispatcher.objectCast(); if (server) { connect(server.data(), &RemoteDispatcherServer::fireClosed, this, &RemoteDispatcherSpy::onClosed); connect(server.data(), &RemoteDispatcherServer::fireReceived, this, &RemoteDispatcherSpy::onReceived); return; } } RemoteDispatcherSpy::~RemoteDispatcherSpy() { const auto client = mRemoteDispatcher.objectCast(); if (client) { disconnect(client.data(), &RemoteDispatcherClient::fireClosed, this, &RemoteDispatcherSpy::onClosed); disconnect(client.data(), &RemoteDispatcherClient::fireReceived, this, &RemoteDispatcherSpy::onReceived); return; } const auto server = mRemoteDispatcher.objectCast(); if (server) { disconnect(server.data(), &RemoteDispatcherServer::fireClosed, this, &RemoteDispatcherSpy::onClosed); disconnect(server.data(), &RemoteDispatcherServer::fireReceived, this, &RemoteDispatcherSpy::onReceived); return; } } bool RemoteDispatcherSpy::isClosed() const { return mClosed; } GlobalStatus::Code RemoteDispatcherSpy::getCloseCode() const { return mCloseCode; } const QVector& RemoteDispatcherSpy::getReceivedMessageTypes() const { return mReceivedMessageTypes; } const QVector& RemoteDispatcherSpy::getReceivedMessages() const { return mReceivedMessages; } const QVector& RemoteDispatcherSpy::getReceivedSignalSenders() const { return mReceivedSignalSenders; } void RemoteDispatcherSpy::onClosed(GlobalStatus::Code pCloseCode, const QString& pId) { mClosed = true; mCloseCode = pCloseCode; mReceivedSignalSenders += pId; } void RemoteDispatcherSpy::onReceived(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject, const QString& pId) { qDebug() << "RemoteDispatcherSpy::onReceived() -" << pMessageType; mReceivedMessageTypes += pMessageType; mReceivedMessages += pJsonObject; mReceivedSignalSenders += pId; } class test_RemoteDisp : public QObject { Q_OBJECT private Q_SLOTS: void channelClosedNormallyClient() { const QSharedPointer channel(new MockDataChannel()); const QSharedPointer dispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, channel)); RemoteDispatcherSpy spy(dispatcher); channel->close(); QVERIFY(spy.isClosed()); QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); QCOMPARE(senders.first(), dispatcher->getId()); } void channelClosedNormallyServer() { const QSharedPointer channel(new MockDataChannel()); const QSharedPointer dispatcher(new RemoteDispatcherServer(channel)); RemoteDispatcherSpy spy(dispatcher); channel->close(); QVERIFY(spy.isClosed()); QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); QCOMPARE(senders.first(), dispatcher->getId()); } void channelClosedAbnormallyClient() { const QSharedPointer channel(new MockDataChannel()); const QSharedPointer dispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, channel)); RemoteDispatcherSpy spy(dispatcher); channel->closeAbnormal(); QVERIFY(spy.isClosed()); QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); QCOMPARE(senders.first(), dispatcher->getId()); } void channelClosedAbnormallyServer() { const QSharedPointer channel(new MockDataChannel()); const QSharedPointer dispatcher(new RemoteDispatcherServer(channel)); RemoteDispatcherSpy spy(dispatcher); channel->closeAbnormal(); QVERIFY(spy.isClosed()); QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); QCOMPARE(senders.first(), dispatcher->getId()); } void messagesAreDelivered() { const QSharedPointer clientChannel(new MockDataChannel()); const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); const QSharedPointer serverChannel(new MockDataChannel()); const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); RemoteDispatcherSpy spy(serverDispatcher); clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); clientDispatcher->send(QSharedPointer(new IfdConnect(QStringLiteral("NFC Reader")))); clientDispatcher->send(QSharedPointer(new IfdTransmit(QStringLiteral("NFC Reader"), QByteArray::fromHex("00A402022F00")))); clientDispatcher->send(QSharedPointer(new IfdDisconnect(QStringLiteral("NFC Reader")))); const QVector receivedMessageTypes = spy.getReceivedMessageTypes(); const QVector receivedMessages = spy.getReceivedMessages(); QCOMPARE(receivedMessageTypes.size(), 3); QCOMPARE(receivedMessages.size(), 3); QCOMPARE(receivedMessageTypes.at(0), RemoteCardMessageType::IFDConnect); const IfdConnect ifdConnect(receivedMessages.at(0)); QCOMPARE(ifdConnect.getType(), RemoteCardMessageType::IFDConnect); QCOMPARE(ifdConnect.getSlotName(), QStringLiteral("NFC Reader")); QCOMPARE(receivedMessageTypes.at(1), RemoteCardMessageType::IFDTransmit); const IfdTransmit ifdTransmit(receivedMessages.at(1)); QCOMPARE(ifdTransmit.getType(), RemoteCardMessageType::IFDTransmit); QCOMPARE(ifdTransmit.getSlotHandle(), QStringLiteral("NFC Reader")); QCOMPARE(ifdTransmit.getInputApdu(), QByteArray::fromHex("00A402022F00")); QCOMPARE(receivedMessageTypes.at(2), RemoteCardMessageType::IFDDisconnect); const IfdDisconnect ifdDisconnect(receivedMessages.at(2)); QCOMPARE(ifdDisconnect.getType(), RemoteCardMessageType::IFDDisconnect); QCOMPARE(ifdDisconnect.getSlotHandle(), QStringLiteral("NFC Reader")); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 3); QCOMPARE(senders.at(0), serverDispatcher->getId()); QCOMPARE(senders.at(1), serverDispatcher->getId()); QCOMPARE(senders.at(2), serverDispatcher->getId()); } void channelIsClosedWhenRemoteDispatcherIsDestroyed() { const QSharedPointer clientChannel(new MockDataChannel()); QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); const QSharedPointer serverChannel(new MockDataChannel()); const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); connect(clientChannel.data(), &DataChannel::fireClosed, serverChannel.data(), &DataChannel::close, Qt::DirectConnection); RemoteDispatcherSpy spy(serverDispatcher); QVERIFY(!spy.isClosed()); // Destroying a remote dispatcher should close the underlying channel. clientDispatcher.reset(); QVERIFY(spy.isClosed()); QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); QCOMPARE(senders.at(0), serverDispatcher->getId()); } void repeatedIfdEstablishContextGenerateCorrectErrorMessage() { const QSharedPointer clientChannel(new MockDataChannel()); const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v0, clientChannel)); const QSharedPointer serverChannel(new MockDataChannel()); const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v0, DeviceInfo::getName()))); const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); QCOMPARE(clientReceivedDataBlocks.size(), 2); const IfdEstablishContextResponse message1(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(0))); QVERIFY(!message1.isIncomplete()); QCOMPARE(message1.getType(), RemoteCardMessageType::IFDEstablishContextResponse); QCOMPARE(message1.resultHasError(), false); QCOMPARE(message1.getResultMinor(), ECardApiResult::Minor::null); const IfdEstablishContextResponse message2(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(1))); QVERIFY(!message2.isIncomplete()); QCOMPARE(message2.getType(), RemoteCardMessageType::IFDEstablishContextResponse); QCOMPARE(message2.resultHasError(), true); QCOMPARE(message2.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); } void ifdEstablishContextWithWrongProtocolGeneratesCorrectErrorMessage() { const QSharedPointer clientChannel(new MockDataChannel()); const QSharedPointer clientDispatcher(new RemoteDispatcherClient(IfdVersion::Version::v_test, clientChannel)); const QSharedPointer serverChannel(new MockDataChannel()); const QSharedPointer serverDispatcher(new RemoteDispatcherServer(serverChannel)); connect(clientChannel.data(), &MockDataChannel::fireSend, serverChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); connect(serverChannel.data(), &MockDataChannel::fireSend, clientChannel.data(), &MockDataChannel::onReceived, Qt::DirectConnection); clientDispatcher->send(QSharedPointer(new IfdEstablishContext(IfdVersion::Version::v_test, DeviceInfo::getName()))); const QVector& clientReceivedDataBlocks = clientChannel->getReceivedDataBlocks(); QCOMPARE(clientReceivedDataBlocks.size(), 1); const IfdEstablishContextResponse message(RemoteMessage::parseByteArray(clientReceivedDataBlocks.at(0))); QVERIFY(!message.isIncomplete()); QCOMPARE(message.getType(), RemoteCardMessageType::IFDEstablishContextResponse); QCOMPARE(message.resultHasError(), true); QCOMPARE(message.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); } }; QTEST_GUILESS_MAIN(test_RemoteDisp) #include "test_RemoteDisp.moc"