Add MDNS e2e test
This commit is contained in:
parent
4c13af95c3
commit
fd25318a20
5 changed files with 87 additions and 22 deletions
|
@ -98,12 +98,12 @@ static int query_callback(int sock, const struct sockaddr* from, size_t addrlen,
|
||||||
|
|
||||||
switch (record_type) {
|
switch (record_type) {
|
||||||
case MDNS_RECORDTYPE_PTR: {
|
case MDNS_RECORDTYPE_PTR: {
|
||||||
// We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full "<service-name>.<_service-type>._tcp.local." string
|
// We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full "<instance-name>._<service-type>._tcp.local." string
|
||||||
mdns_string_pair_t serviceNamePos = mdns_get_next_substring(data, size, record_offset);
|
mdns_string_pair_t instanceNamePos = mdns_get_next_substring(data, size, record_offset);
|
||||||
discoveredService->name = QString::fromLatin1((char *)data + serviceNamePos.offset, serviceNamePos.length);
|
discoveredService->name = QString::fromLatin1((char *)data + instanceNamePos.offset, instanceNamePos.length);
|
||||||
//static char serviceNameBuffer[256];
|
//static char instanceNameBuffer[256];
|
||||||
//mdns_string_t serviceName = mdns_record_parse_ptr(data, size, record_offset, record_length, serviceNameBuffer, sizeof(serviceNameBuffer));
|
//mdns_string_t instanceName = mdns_record_parse_ptr(data, size, record_offset, record_length, instanceNameBuffer, sizeof(instanceNameBuffer));
|
||||||
//discoveredService->name = QString::fromLatin1(serviceName.str, serviceName.length);
|
//discoveredService->name = QString::fromLatin1(instanceName.str, instanceName.length);
|
||||||
if (discoveredService->address == QHostAddress::Null) {
|
if (discoveredService->address == QHostAddress::Null) {
|
||||||
discoveredService->address = QHostAddress(from); // In case we don't receive a A record, use from as address
|
discoveredService->address = QHostAddress(from); // In case we don't receive a A record, use from as address
|
||||||
}
|
}
|
||||||
|
@ -309,11 +309,11 @@ static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self,
|
||||||
answer.rclass = 0;
|
answer.rclass = 0;
|
||||||
answer.ttl = 0;
|
answer.ttl = 0;
|
||||||
switch (record_type) {
|
switch (record_type) {
|
||||||
case MDNS_RECORDTYPE_PTR: // maps "<_service-type>._tcp.local." to "<service-name>.<_service-type>._tcp.local."
|
case MDNS_RECORDTYPE_PTR: // maps "_<service-type>._tcp.local." to "<instance-name>._<service-type>._tcp.local."
|
||||||
answer.name = createMdnsString(self.serviceType);
|
answer.name = createMdnsString(self.serviceType);
|
||||||
answer.data.ptr.name = createMdnsString(self.serviceInstance);
|
answer.data.ptr.name = createMdnsString(self.serviceInstance);
|
||||||
break;
|
break;
|
||||||
case MDNS_RECORDTYPE_SRV: // maps "<service-name>.<_service-type>._tcp.local." to "<hostname>.local." and port
|
case MDNS_RECORDTYPE_SRV: // maps "<instance-name>._<service-type>._tcp.local." to "<hostname>.local." and port
|
||||||
answer.name = createMdnsString(self.serviceInstance);
|
answer.name = createMdnsString(self.serviceInstance);
|
||||||
answer.data.srv.name = createMdnsString(self.hostname);
|
answer.data.srv.name = createMdnsString(self.hostname);
|
||||||
answer.data.srv.port = self.port;
|
answer.data.srv.port = self.port;
|
||||||
|
@ -544,14 +544,14 @@ int Announcer::listenForQueries()
|
||||||
return numSockets;
|
return numSockets;
|
||||||
}
|
}
|
||||||
|
|
||||||
Announcer::Announcer(const QString &serviceName, const QString &serviceType, uint16_t port)
|
Announcer::Announcer(const QString &instanceName, const QString &serviceType, uint16_t port)
|
||||||
{
|
{
|
||||||
self.serviceType = serviceType.toLatin1();
|
self.serviceType = serviceType.toLatin1();
|
||||||
if (!self.serviceType.endsWith('.')) {
|
if (!self.serviceType.endsWith('.')) {
|
||||||
// mdns.h needs all the qualified names to end with dot for some reason
|
// mdns.h needs all the qualified names to end with dot for some reason
|
||||||
self.serviceType.append('.');
|
self.serviceType.append('.');
|
||||||
}
|
}
|
||||||
self.serviceInstance = serviceName.toLatin1() + '.' + self.serviceType;
|
self.serviceInstance = instanceName.toLatin1() + '.' + self.serviceType;
|
||||||
self.hostname = QHostInfo::localHostName().toLatin1() + ".local.";
|
self.hostname = QHostInfo::localHostName().toLatin1() + ".local.";
|
||||||
self.port = port;
|
self.port = port;
|
||||||
detectHostAddresses();
|
detectHostAddresses();
|
||||||
|
|
|
@ -13,30 +13,32 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
/*
|
#include "kdeconnectcore_export.h"
|
||||||
|
|
||||||
|
/**
|
||||||
* A Qt wrapper for the mdns.h header-only library
|
* A Qt wrapper for the mdns.h header-only library
|
||||||
* from https://github.com/mjansson/mdns
|
* from https://github.com/mjansson/mdns
|
||||||
*/
|
*/
|
||||||
namespace MdnsWrapper
|
namespace MdnsWrapper
|
||||||
{
|
{
|
||||||
|
|
||||||
class Discoverer : public QObject
|
class KDECONNECTCORE_EXPORT Discoverer : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct MdnsService {
|
struct MdnsService {
|
||||||
QString name;
|
QString name; // The instance-name part in "<instance-name>._<service-type>._tcp.local."
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
QHostAddress address;
|
QHostAddress address; // An IPv4 address (IPv6 addresses are ignored)
|
||||||
QMap<QString, QString> txtRecords;
|
QMap<QString, QString> txtRecords;
|
||||||
};
|
};
|
||||||
|
|
||||||
// serviceType must be of the form "_<name>._<tcp/udp>.local"
|
// serviceType must be of the form "_<service-type>._<tcp/udp>.local"
|
||||||
void startDiscovering(const QString &serviceType);
|
void startDiscovering(const QString &serviceType);
|
||||||
void stopDiscovering();
|
void stopDiscovering();
|
||||||
|
|
||||||
void sendQuery(const QString &serviceName);
|
void sendQuery(const QString &serviceType);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void serviceFound(const MdnsWrapper::Discoverer::MdnsService &service);
|
void serviceFound(const MdnsWrapper::Discoverer::MdnsService &service);
|
||||||
|
@ -48,14 +50,14 @@ private:
|
||||||
QVector<QSocketNotifier *> responseSocketNotifiers;
|
QVector<QSocketNotifier *> responseSocketNotifiers;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Announcer : public QObject
|
class KDECONNECTCORE_EXPORT Announcer : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct AnnouncedInfo {
|
struct AnnouncedInfo {
|
||||||
QByteArray serviceType; // ie: "<_service-type>._tcp.local."
|
QByteArray serviceType; // ie: "_<service-type>._tcp.local."
|
||||||
QByteArray serviceInstance; // ie: "<service-name>.<_service-type>._tcp.local."
|
QByteArray serviceInstance; // ie: "<instance-name>._<service-type>._tcp.local."
|
||||||
QByteArray hostname; // ie: "<hostname>.local."
|
QByteArray hostname; // ie: "<hostname>.local."
|
||||||
QVector<QHostAddress> addressesV4;
|
QVector<QHostAddress> addressesV4;
|
||||||
QVector<QHostAddress> addressesV6;
|
QVector<QHostAddress> addressesV6;
|
||||||
|
@ -63,8 +65,8 @@ public:
|
||||||
QHash<QByteArray, QByteArray> txtRecords;
|
QHash<QByteArray, QByteArray> txtRecords;
|
||||||
};
|
};
|
||||||
|
|
||||||
// serviceType must be of the form "_<name>._<tcp/udp>.local"
|
// serviceType must be of the form "_<service-type>._<tcp/udp>.local"
|
||||||
Announcer(const QString &serviceName, const QString &serviceType, uint16_t port);
|
Announcer(const QString &instanceName, const QString &serviceType, uint16_t port);
|
||||||
|
|
||||||
void putTxtRecord(const QString &key, const QString &value)
|
void putTxtRecord(const QString &key, const QString &value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,7 +23,7 @@ MdnsDiscovery::MdnsDiscovery(LanLinkProvider *lanLinkProvider)
|
||||||
mdnsAnnouncer.putTxtRecord(QStringLiteral("type"), config.deviceType().toString());
|
mdnsAnnouncer.putTxtRecord(QStringLiteral("type"), config.deviceType().toString());
|
||||||
mdnsAnnouncer.putTxtRecord(QStringLiteral("protocol"), QString::number(NetworkPacket::s_protocolVersion));
|
mdnsAnnouncer.putTxtRecord(QStringLiteral("protocol"), QString::number(NetworkPacket::s_protocolVersion));
|
||||||
|
|
||||||
connect(&mdnsDiscoverer, &MdnsWrapper::Discoverer::serviceFound, [lanLinkProvider](const MdnsWrapper::Discoverer::MdnsService &service) {
|
connect(&mdnsDiscoverer, &MdnsWrapper::Discoverer::serviceFound, this, [lanLinkProvider](const MdnsWrapper::Discoverer::MdnsService &service) {
|
||||||
if (KdeConnectConfig::instance().deviceId() == service.name) {
|
if (KdeConnectConfig::instance().deviceId() == service.name) {
|
||||||
qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring";
|
qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring";
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -12,3 +12,4 @@ set(kdeconnect_libraries
|
||||||
|
|
||||||
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
||||||
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
||||||
|
ecm_add_test(mdnstest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
|
||||||
|
|
62
tests/mdnstest.cpp
Normal file
62
tests/mdnstest.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include "core/backends/lan/mdns_wrapper.h"
|
||||||
|
#include "kdeconnect-version.h"
|
||||||
|
|
||||||
|
class TestMdns : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TestMdns()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testAnounceAndDiscover()
|
||||||
|
{
|
||||||
|
QString instanceName = QStringLiteral("myinstance");
|
||||||
|
uint16_t instancePort = 1716;
|
||||||
|
QString serviceType = QStringLiteral("_test._udp.local");
|
||||||
|
QString txtKey = QStringLiteral("keyerino");
|
||||||
|
QString txtValue = QStringLiteral("valuerino");
|
||||||
|
|
||||||
|
MdnsWrapper::Announcer announcer(instanceName, serviceType, instancePort);
|
||||||
|
announcer.putTxtRecord(txtKey, txtValue);
|
||||||
|
|
||||||
|
MdnsWrapper::Discoverer discoverer;
|
||||||
|
|
||||||
|
QSignalSpy spy(&discoverer, &MdnsWrapper::Discoverer::serviceFound);
|
||||||
|
|
||||||
|
connect(&discoverer, &MdnsWrapper::Discoverer::serviceFound, this, [instanceName, instancePort, txtKey, txtValue](const MdnsWrapper::Discoverer::MdnsService &service) {
|
||||||
|
QCOMPARE(instanceName, service.name);
|
||||||
|
QCOMPARE(instancePort, service.port);
|
||||||
|
QVERIFY(service.txtRecords.size() == 1);
|
||||||
|
QVERIFY(service.txtRecords.contains(txtKey));
|
||||||
|
QCOMPARE(txtValue, service.txtRecords.value(txtKey));
|
||||||
|
});
|
||||||
|
|
||||||
|
announcer.startAnnouncing();
|
||||||
|
discoverer.startDiscovering(serviceType);
|
||||||
|
|
||||||
|
QVERIFY(spy.wait(2000));
|
||||||
|
QVERIFY(spy.count() > 0);
|
||||||
|
|
||||||
|
discoverer.stopDiscovering();
|
||||||
|
announcer.stopAnnouncing();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(TestMdns);
|
||||||
|
|
||||||
|
#include "mdnstest.moc"
|
Loading…
Reference in a new issue