From 4b04598a714100bff5fcbc2d189d47f47968ff20 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Fri, 26 May 2023 00:26:28 +0200 Subject: [PATCH] DNS-SD mock for checking platform implementations (#26143) * DNS-SD mock for checking platform implementations * Mark POSIX event loop as ready for run if not-internaly managed * Prevent SEGFAULT in test in case of resolve error * Document mDNS test cases * Apply suggestions from code review Co-authored-by: Boris Zbarsky --------- Co-authored-by: Boris Zbarsky --- .../GenericPlatformManagerImpl_POSIX.ipp | 1 + src/platform/tests/BUILD.gn | 5 +- src/platform/tests/TestDnssd.cpp | 146 +++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp index 1b09d298eb2867..5c9061a901a3f2 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp @@ -163,6 +163,7 @@ void GenericPlatformManagerImpl_POSIX::_RunEventLoop() { mChipTask = pthread_self(); mState.store(State::kRunning, std::memory_order_relaxed); + mShouldRunEventLoop.store(true, std::memory_order_relaxed); } pthread_mutex_unlock(&mStateLock); diff --git a/src/platform/tests/BUILD.gn b/src/platform/tests/BUILD.gn index 1de55b7be409ce..70d096556ed579 100644 --- a/src/platform/tests/BUILD.gn +++ b/src/platform/tests/BUILD.gn @@ -49,7 +49,10 @@ if (chip_device_platform != "none" && chip_device_platform != "fake") { if (chip_mdns != "none" && chip_enable_dnssd_tests && (chip_device_platform == "linux" || chip_device_platform == "darwin")) { test_sources += [ "TestDnssd.cpp" ] - public_deps += [ "${chip_root}/src/lib/dnssd" ] + public_deps += [ + "${chip_root}/src/lib/dnssd", + "${chip_root}/src/lib/dnssd/minimal_mdns", + ] } # These tests appear to be broken on Mac. diff --git a/src/platform/tests/TestDnssd.cpp b/src/platform/tests/TestDnssd.cpp index d7e4ed18554f74..21f46388ff5730 100644 --- a/src/platform/tests/TestDnssd.cpp +++ b/src/platform/tests/TestDnssd.cpp @@ -21,7 +21,18 @@ #include "lib/dnssd/platform/Dnssd.h" #include "platform/CHIPDeviceLayer.h" +#include "platform/ConnectivityManager.h" #include "platform/PlatformManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -44,6 +55,35 @@ struct DnssdContext bool mEndOfInput = false; }; +class TestDnssdResolveServerDelegate : public mdns::Minimal::ServerDelegate, public mdns::Minimal::ParserDelegate +{ +public: + TestDnssdResolveServerDelegate(mdns::Minimal::ResponseSender * responder) : mResponder(responder) {} + virtual ~TestDnssdResolveServerDelegate() = default; + + // Implementation of mdns::Minimal::ServerDelegate + + void OnResponse(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override {} + void OnQuery(const mdns::Minimal::BytesRange & data, const chip::Inet::IPPacketInfo * info) override + { + mCurSrc = info; + mdns::Minimal::ParsePacket(data, this); + mCurSrc = nullptr; + } + + // Implementation of mdns::Minimal::ParserDelegate + + void OnHeader(mdns::Minimal::ConstHeaderRef & header) override { mMsgId = header.GetMessageId(); } + void OnQuery(const mdns::Minimal::QueryData & data) override { mResponder->Respond(mMsgId, data, mCurSrc, mRespConfig); } + void OnResource(mdns::Minimal::ResourceType type, const mdns::Minimal::ResourceData & data) override {} + +private: + mdns::Minimal::ResponseSender * mResponder; + mdns::Minimal::ResponseConfiguration mRespConfig; + const chip::Inet::IPPacketInfo * mCurSrc = nullptr; + uint16_t mMsgId = 0; +}; + } // namespace static void Timeout(chip::System::Layer * systemLayer, void * context) @@ -64,6 +104,10 @@ static void HandleResolve(void * context, DnssdService * result, const chip::Spa NL_TEST_ASSERT(suite, result != nullptr); NL_TEST_ASSERT(suite, error == CHIP_NO_ERROR); + // The NL_TEST_ASSERT above will not abort the test, so we need to + // explicitly abort it here to avoid dereferencing a null pointer. + VerifyOrReturn(result != nullptr, ); + if (!addresses.empty()) { addresses.data()[0].ToString(addrBuf, sizeof(addrBuf)); @@ -122,13 +166,96 @@ static void DnssdErrorCallback(void * context, CHIP_ERROR error) NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR); } +void TestDnssdBrowse_DnssdInitCallback(void * context, CHIP_ERROR error) +{ + auto * ctx = static_cast(context); + NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR); + auto * suite = ctx->mTestSuite; + + NL_TEST_ASSERT(suite, + ChipDnssdBrowse("_mock", DnssdServiceProtocol::kDnssdProtocolUdp, chip::Inet::IPAddressType::kAny, + chip::Inet::InterfaceId::Null(), HandleBrowse, context, + &ctx->mBrowseIdentifier) == CHIP_NO_ERROR); +} + +// Verify that platform DNS-SD implementation can browse and resolve services. +// +// This test case uses platform-independent mDNS server implementation based on +// minimal mdns library. The server is configured to respond to PTR, SRV, TXT, +// A and AAAA queries without additional records. In order to pass this test, +// the platform DNS-SD client implementation must be able to browse and resolve +// services by querying for all of these records separately. +void TestDnssdBrowse(nlTestSuite * inSuite, void * inContext) +{ + DnssdContext context; + context.mTestSuite = inSuite; + + mdns::Minimal::SetDefaultAddressPolicy(); + + mdns::Minimal::Server<10> server; + mdns::Minimal::QNamePart serverName[] = { "resolve-tester", "_mock", chip::Dnssd::kCommissionProtocol, + chip::Dnssd::kLocalDomain }; + mdns::Minimal::ResponseSender responseSender(&server); + + mdns::Minimal::QueryResponder<16> queryResponder; + responseSender.AddQueryResponder(&queryResponder); + + // Respond to PTR queries for _mock._udp.local + mdns::Minimal::QNamePart serviceName[] = { "_mock", chip::Dnssd::kCommissionProtocol, chip::Dnssd::kLocalDomain }; + mdns::Minimal::QNamePart serverServiceName[] = { "INSTANCE", chip::Dnssd::kCommissionableServiceName, + chip::Dnssd::kCommissionProtocol, chip::Dnssd::kLocalDomain }; + mdns::Minimal::PtrResponder ptrUdpResponder(serviceName, serverServiceName); + queryResponder.AddResponder(&ptrUdpResponder); + + // Respond to SRV queries for INSTANCE._matterc._udp.local + mdns::Minimal::SrvResponder srvResponder(mdns::Minimal::SrvResourceRecord(serverServiceName, serverName, CHIP_PORT)); + queryResponder.AddResponder(&srvResponder); + + // Respond to TXT queries for INSTANCE._matterc._udp.local + const char * txtEntries[] = { "key=val" }; + mdns::Minimal::TxtResponder txtResponder(mdns::Minimal::TxtResourceRecord(serverServiceName, txtEntries)); + queryResponder.AddResponder(&txtResponder); + + // Respond to A queries + mdns::Minimal::IPv4Responder ipv4Responder(serverName); + queryResponder.AddResponder(&ipv4Responder); + + // Respond to AAAA queries + mdns::Minimal::IPv6Responder ipv6Responder(serverName); + queryResponder.AddResponder(&ipv6Responder); + + TestDnssdResolveServerDelegate delegate(&responseSender); + server.SetDelegate(&delegate); + + auto endpoints = mdns::Minimal::GetAddressPolicy()->GetListenEndpoints(); + NL_TEST_ASSERT(inSuite, server.Listen(chip::DeviceLayer::UDPEndPointManager(), endpoints.get(), 5353) == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, + chip::Dnssd::ChipDnssdInit(TestDnssdBrowse_DnssdInitCallback, DnssdErrorCallback, &context) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(5), Timeout, &context) == + CHIP_NO_ERROR); + + ChipLogProgress(DeviceLayer, "Start EventLoop"); + chip::DeviceLayer::PlatformMgr().RunEventLoop(); + ChipLogProgress(DeviceLayer, "End EventLoop"); + + NL_TEST_ASSERT(inSuite, context.mResolvedServicesCount > 0); + NL_TEST_ASSERT(inSuite, !context.mTimeoutExpired); + + // Stop browsing so we can safely shutdown DNS-SD + chip::Dnssd::ChipDnssdStopBrowse(context.mBrowseIdentifier); + + chip::Dnssd::ChipDnssdShutdown(); +} + static void HandlePublish(void * context, const char * type, const char * instanceName, CHIP_ERROR error) { auto * ctx = static_cast(context); NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR); } -static void TestDnssdPubSub_DnssdInitCallback(void * context, CHIP_ERROR error) +static void TestDnssdPublishService_DnssdInitCallback(void * context, CHIP_ERROR error) { auto * ctx = static_cast(context); NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR); @@ -157,13 +284,19 @@ static void TestDnssdPubSub_DnssdInitCallback(void * context, CHIP_ERROR error) &ctx->mBrowseIdentifier) == CHIP_NO_ERROR); } -void TestDnssdPubSub(nlTestSuite * inSuite, void * inContext) +// Verify that the platform DNS-SD implementation can publish services. +// +// This test uses platform implementation of DNS-SD server and client. Since +// client implementation should be verified by the TestDnssdBrowse test case, +// here we only verify that the server implementation can publish services. +void TestDnssdPublishService(nlTestSuite * inSuite, void * inContext) { DnssdContext context; context.mTestSuite = inSuite; NL_TEST_ASSERT(inSuite, - chip::Dnssd::ChipDnssdInit(TestDnssdPubSub_DnssdInitCallback, DnssdErrorCallback, &context) == CHIP_NO_ERROR); + chip::Dnssd::ChipDnssdInit(TestDnssdPublishService_DnssdInitCallback, DnssdErrorCallback, &context) == + CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(5), Timeout, &context) == CHIP_NO_ERROR); @@ -172,6 +305,7 @@ void TestDnssdPubSub(nlTestSuite * inSuite, void * inContext) chip::DeviceLayer::PlatformMgr().RunEventLoop(); ChipLogProgress(DeviceLayer, "End EventLoop"); + NL_TEST_ASSERT(inSuite, context.mResolvedServicesCount > 0); NL_TEST_ASSERT(inSuite, !context.mTimeoutExpired); // Stop browsing so we can safely shutdown DNS-SD @@ -180,7 +314,11 @@ void TestDnssdPubSub(nlTestSuite * inSuite, void * inContext) chip::Dnssd::ChipDnssdShutdown(); } -static const nlTest sTests[] = { NL_TEST_DEF("Test Dnssd::PubSub", TestDnssdPubSub), NL_TEST_SENTINEL() }; +static const nlTest sTests[] = { + NL_TEST_DEF("Test ChipDnssdBrowse", TestDnssdBrowse), + NL_TEST_DEF("Test ChipDnssdPublishService", TestDnssdPublishService), + NL_TEST_SENTINEL(), +}; int TestDnssd_Setup(void * inContext) {