Skip to content

Commit 6ec5f81

Browse files
authored
Fix TestDnssd exit after last HandleResolved call (#26138)
* Fix TestDnssd exit after last HandleResolved call * Fix typo in comment * Use dedicated context instead of global state * Use Setup/Teardown for DNS-SD test suite * Test timeout in case of Avahi not running * Use ChipDnssdStopBrowse before exit * Fix typo in comment * Cancel timer before stopping event loop * Avoid deadlock when stopping event loop on non-matter thread * Update src/platform/tests/TestDnssd.cpp * Remove unnecessary comment
1 parent f1096a5 commit 6ec5f81

6 files changed

+125
-110
lines changed

src/platform/tests/TestConfigurationMgr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ int TestConfigurationMgr()
499499
{
500500
nlTestSuite theSuite = { "ConfigurationMgr tests", &sTests[0], TestConfigurationMgr_Setup, TestConfigurationMgr_Teardown };
501501

502-
// Run test suit againt one context.
502+
// Run test suite against one context.
503503
nlTestRunner(&theSuite, nullptr);
504504
return nlTestRunnerStats(&theSuite);
505505
}

src/platform/tests/TestConnectivityMgr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ int TestConnectivityMgr()
9999
{
100100
nlTestSuite theSuite = { "ConfigurationMgr tests", &sTests[0], TestConnectivityMgr_Setup, TestConnectivityMgr_Teardown };
101101

102-
// Run test suit againt one context.
102+
// Run test suite against one context.
103103
nlTestRunner(&theSuite, nullptr);
104104
return nlTestRunnerStats(&theSuite);
105105
}

src/platform/tests/TestDnssd.cpp

+120-105
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
1-
#include <condition_variable>
2-
#include <mutex>
3-
#include <thread>
1+
/*
2+
*
3+
* Copyright (c) 2021 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include <atomic>
419

520
#include <nlunit-test.h>
621

@@ -14,47 +29,80 @@ using chip::Dnssd::DnssdService;
1429
using chip::Dnssd::DnssdServiceProtocol;
1530
using chip::Dnssd::TextEntry;
1631

17-
static unsigned int gBrowsedServicesCount = 0;
18-
static unsigned int gResolvedServicesCount = 0;
19-
static bool gEndOfInput = false;
32+
namespace {
33+
34+
struct DnssdContext
35+
{
36+
nlTestSuite * mTestSuite;
37+
38+
std::atomic<bool> mTimeoutExpired{ false };
39+
40+
intptr_t mBrowseIdentifier = 0;
41+
42+
unsigned int mBrowsedServicesCount = 0;
43+
unsigned int mResolvedServicesCount = 0;
44+
bool mEndOfInput = false;
45+
};
46+
47+
} // namespace
48+
49+
static void Timeout(chip::System::Layer * systemLayer, void * context)
50+
{
51+
auto * ctx = static_cast<DnssdContext *>(context);
52+
ChipLogError(DeviceLayer, "mDNS test timeout, is avahi daemon running?");
53+
ctx->mTimeoutExpired = true;
54+
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
55+
}
2056

2157
static void HandleResolve(void * context, DnssdService * result, const chip::Span<chip::Inet::IPAddress> & addresses,
2258
CHIP_ERROR error)
2359
{
60+
auto * ctx = static_cast<DnssdContext *>(context);
61+
auto * suite = ctx->mTestSuite;
2462
char addrBuf[100];
25-
nlTestSuite * suite = static_cast<nlTestSuite *>(context);
2663

2764
NL_TEST_ASSERT(suite, result != nullptr);
2865
NL_TEST_ASSERT(suite, error == CHIP_NO_ERROR);
2966

3067
if (!addresses.empty())
3168
{
3269
addresses.data()[0].ToString(addrBuf, sizeof(addrBuf));
33-
printf("Service[%u] at [%s]:%u\n", gResolvedServicesCount, addrBuf, result->mPort);
70+
printf("Service[%u] at [%s]:%u\n", ctx->mResolvedServicesCount, addrBuf, result->mPort);
3471
}
3572

3673
NL_TEST_ASSERT(suite, result->mTextEntrySize == 1);
3774
NL_TEST_ASSERT(suite, strcmp(result->mTextEntries[0].mKey, "key") == 0);
3875
NL_TEST_ASSERT(suite, strcmp(reinterpret_cast<const char *>(result->mTextEntries[0].mData), "val") == 0);
3976

40-
if (gBrowsedServicesCount == ++gResolvedServicesCount)
77+
if (ctx->mBrowsedServicesCount == ++ctx->mResolvedServicesCount)
4178
{
42-
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
43-
chip::DeviceLayer::PlatformMgr().Shutdown();
44-
exit(0);
79+
chip::DeviceLayer::SystemLayer().CancelTimer(Timeout, context);
80+
// StopEventLoopTask can be called from any thread, but when called from
81+
// non-Matter one it will lock the Matter stack. The same locking rules
82+
// are required when the resolve callback (this one) is called. In order
83+
// to avoid deadlocks, we need to call the StopEventLoopTask from inside
84+
// the Matter event loop by scheduling a lambda.
85+
chip::DeviceLayer::SystemLayer().ScheduleLambda([]() {
86+
// After last service is resolved, stop the event loop,
87+
// so the test case can gracefully exit.
88+
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
89+
});
4590
}
4691
}
4792

4893
static void HandleBrowse(void * context, DnssdService * services, size_t servicesSize, bool finalBrowse, CHIP_ERROR error)
4994
{
50-
nlTestSuite * suite = static_cast<nlTestSuite *>(context);
95+
auto * ctx = static_cast<DnssdContext *>(context);
96+
auto * suite = ctx->mTestSuite;
5197

5298
// Make sure that we will not be called again after end-of-input is set
53-
NL_TEST_ASSERT(suite, gEndOfInput == false);
54-
NL_TEST_ASSERT(suite, error == CHIP_NO_ERROR);
99+
NL_TEST_ASSERT(suite, ctx->mEndOfInput == false);
100+
// Cancelled error is expected when the browse is stopped with
101+
// ChipDnssdStopBrowse(), so we will not assert on it.
102+
NL_TEST_ASSERT(suite, error == CHIP_NO_ERROR || error == CHIP_ERROR_CANCELLED);
55103

56-
gBrowsedServicesCount += servicesSize;
57-
gEndOfInput = finalBrowse;
104+
ctx->mBrowsedServicesCount += servicesSize;
105+
ctx->mEndOfInput = finalBrowse;
58106

59107
if (servicesSize > 0)
60108
{
@@ -63,22 +111,31 @@ static void HandleBrowse(void * context, DnssdService * services, size_t service
63111
{
64112
printf("Service[%u] name %s\n", i, services[i].mName);
65113
printf("Service[%u] type %s\n", i, services[i].mType);
66-
NL_TEST_ASSERT(suite, ChipDnssdResolve(&services[i], services[i].mInterface, HandleResolve, suite) == CHIP_NO_ERROR);
114+
NL_TEST_ASSERT(suite, ChipDnssdResolve(&services[i], services[i].mInterface, HandleResolve, context) == CHIP_NO_ERROR);
67115
}
68116
}
69117
}
70118

71-
static void HandlePublish(void * context, const char * type, const char * instanceName, CHIP_ERROR error) {}
119+
static void DnssdErrorCallback(void * context, CHIP_ERROR error)
120+
{
121+
auto * ctx = static_cast<DnssdContext *>(context);
122+
NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR);
123+
}
124+
125+
static void HandlePublish(void * context, const char * type, const char * instanceName, CHIP_ERROR error)
126+
{
127+
auto * ctx = static_cast<DnssdContext *>(context);
128+
NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR);
129+
}
72130

73-
static void InitCallback(void * context, CHIP_ERROR error)
131+
static void TestDnssdPubSub_DnssdInitCallback(void * context, CHIP_ERROR error)
74132
{
75-
DnssdService service;
76-
TextEntry entry;
77-
char key[] = "key";
78-
char val[] = "val";
79-
nlTestSuite * suite = static_cast<nlTestSuite *>(context);
133+
auto * ctx = static_cast<DnssdContext *>(context);
134+
NL_TEST_ASSERT(ctx->mTestSuite, error == CHIP_NO_ERROR);
135+
auto * suite = ctx->mTestSuite;
80136

81-
NL_TEST_ASSERT(suite, error == CHIP_NO_ERROR);
137+
DnssdService service{};
138+
TextEntry entry{ "key", reinterpret_cast<const uint8_t *>("val"), 3 };
82139

83140
service.mInterface = chip::Inet::InterfaceId::Null();
84141
service.mPort = 80;
@@ -87,107 +144,65 @@ static void InitCallback(void * context, CHIP_ERROR error)
87144
strcpy(service.mType, "_mock");
88145
service.mAddressType = chip::Inet::IPAddressType::kAny;
89146
service.mProtocol = DnssdServiceProtocol::kDnssdProtocolTcp;
90-
entry.mKey = key;
91-
entry.mData = reinterpret_cast<const uint8_t *>(val);
92-
entry.mDataSize = strlen(reinterpret_cast<const char *>(entry.mData));
93147
service.mTextEntries = &entry;
94148
service.mTextEntrySize = 1;
95149
service.mSubTypes = nullptr;
96150
service.mSubTypeSize = 0;
97151

98-
NL_TEST_ASSERT(suite, ChipDnssdPublishService(&service, HandlePublish) == CHIP_NO_ERROR);
99-
intptr_t browseIdentifier;
100-
ChipDnssdBrowse("_mock", DnssdServiceProtocol::kDnssdProtocolTcp, chip::Inet::IPAddressType::kAny,
101-
chip::Inet::InterfaceId::Null(), HandleBrowse, suite, &browseIdentifier);
102-
}
152+
NL_TEST_ASSERT(suite, ChipDnssdPublishService(&service, HandlePublish, context) == CHIP_NO_ERROR);
103153

104-
static void ErrorCallback(void * context, CHIP_ERROR error)
105-
{
106-
VerifyOrDieWithMsg(error == CHIP_NO_ERROR, DeviceLayer, "Mdns error: %" CHIP_ERROR_FORMAT "\n", error.Format());
154+
NL_TEST_ASSERT(suite,
155+
ChipDnssdBrowse("_mock", DnssdServiceProtocol::kDnssdProtocolTcp, chip::Inet::IPAddressType::kAny,
156+
chip::Inet::InterfaceId::Null(), HandleBrowse, context,
157+
&ctx->mBrowseIdentifier) == CHIP_NO_ERROR);
107158
}
108159

109160
void TestDnssdPubSub(nlTestSuite * inSuite, void * inContext)
110161
{
111-
chip::Platform::MemoryInit();
112-
chip::DeviceLayer::PlatformMgr().InitChipStack();
113-
NL_TEST_ASSERT(inSuite, chip::Dnssd::ChipDnssdInit(InitCallback, ErrorCallback, inSuite) == CHIP_NO_ERROR);
162+
DnssdContext context;
163+
context.mTestSuite = inSuite;
164+
165+
NL_TEST_ASSERT(inSuite,
166+
chip::Dnssd::ChipDnssdInit(TestDnssdPubSub_DnssdInitCallback, DnssdErrorCallback, &context) == CHIP_NO_ERROR);
167+
NL_TEST_ASSERT(inSuite,
168+
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(5), Timeout, &context) ==
169+
CHIP_NO_ERROR);
114170

115171
ChipLogProgress(DeviceLayer, "Start EventLoop");
116172
chip::DeviceLayer::PlatformMgr().RunEventLoop();
117173
ChipLogProgress(DeviceLayer, "End EventLoop");
118-
}
119-
120-
static const nlTest sTests[] = { NL_TEST_DEF("Test Dnssd::PubSub", TestDnssdPubSub), NL_TEST_SENTINEL() };
121-
122-
int TestDnssd()
123-
{
124-
std::mutex mtx;
125174

126-
std::condition_variable readyCondition;
127-
bool ready = false;
175+
NL_TEST_ASSERT(inSuite, !context.mTimeoutExpired);
128176

129-
std::condition_variable doneCondition;
130-
bool done = false;
131-
bool shutdown = false;
177+
// Stop browsing so we can safely shutdown DNS-SD
178+
chip::Dnssd::ChipDnssdStopBrowse(context.mBrowseIdentifier);
132179

133-
int retVal = EXIT_FAILURE;
134-
135-
std::thread t([&]() {
136-
{
137-
std::lock_guard<std::mutex> lock(mtx);
138-
ready = true;
139-
readyCondition.notify_one();
140-
}
141-
142-
nlTestSuite theSuite = { "CHIP DeviceLayer mdns tests", &sTests[0], nullptr, nullptr };
143-
144-
nlTestRunner(&theSuite, nullptr);
145-
retVal = nlTestRunnerStats(&theSuite);
146-
147-
{
148-
std::lock_guard<std::mutex> lock(mtx);
149-
done = true;
150-
doneCondition.notify_all();
151-
}
152-
});
153-
154-
{
155-
std::unique_lock<std::mutex> lock(mtx);
156-
readyCondition.wait(lock, [&] { return ready; });
180+
chip::Dnssd::ChipDnssdShutdown();
181+
}
157182

158-
doneCondition.wait_for(lock, std::chrono::seconds(5));
159-
if (!done)
160-
{
161-
fprintf(stderr, "mDNS test timeout, is avahi daemon running?\n");
183+
static const nlTest sTests[] = { NL_TEST_DEF("Test Dnssd::PubSub", TestDnssdPubSub), NL_TEST_SENTINEL() };
162184

163-
//
164-
// This will stop the event loop above, and wait till it has actually stopped
165-
// (i.e exited RunEventLoop()).
166-
//
167-
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
168-
chip::Dnssd::ChipDnssdShutdown();
169-
chip::DeviceLayer::PlatformMgr().Shutdown();
170-
shutdown = true;
171-
172-
doneCondition.wait_for(lock, std::chrono::seconds(1));
173-
if (!done)
174-
{
175-
fprintf(stderr, "Orderly shutdown of the platform main loop failed as well.\n");
176-
}
177-
retVal = EXIT_FAILURE;
178-
}
179-
}
180-
t.join();
185+
int TestDnssd_Setup(void * inContext)
186+
{
187+
VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE);
188+
VerifyOrReturnError(chip::DeviceLayer::PlatformMgr().InitChipStack() == CHIP_NO_ERROR, FAILURE);
189+
return SUCCESS;
190+
}
181191

182-
if (!shutdown)
183-
{
184-
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
185-
chip::Dnssd::ChipDnssdShutdown();
186-
chip::DeviceLayer::PlatformMgr().Shutdown();
187-
}
192+
int TestDnssd_Teardown(void * inContext)
193+
{
194+
chip::DeviceLayer::PlatformMgr().Shutdown();
188195
chip::Platform::MemoryShutdown();
196+
return SUCCESS;
197+
}
198+
199+
int TestDnssd()
200+
{
201+
nlTestSuite theSuite = { "CHIP DeviceLayer mDNS tests", &sTests[0], TestDnssd_Setup, TestDnssd_Teardown };
189202

190-
return retVal;
203+
// Run test suite against one context.
204+
nlTestRunner(&theSuite, nullptr);
205+
return nlTestRunnerStats(&theSuite);
191206
}
192207

193208
CHIP_REGISTER_TEST_SUITE(TestDnssd);

src/platform/tests/TestKeyValueStoreMgr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ int TestKeyValueStoreMgr()
353353
{
354354
nlTestSuite theSuite = { "KeyValueStoreMgr tests", &sTests[0], TestKeyValueStoreMgr_Setup, TestKeyValueStoreMgr_Teardown };
355355

356-
// Run test suit againt one context.
356+
// Run test suite against one context.
357357
nlTestRunner(&theSuite, nullptr);
358358
return nlTestRunnerStats(&theSuite);
359359
}

src/platform/tests/TestPlatformMgr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ int TestPlatformMgr()
287287
{
288288
nlTestSuite theSuite = { "PlatformMgr tests", &sTests[0], TestPlatformMgr_Setup, TestPlatformMgr_Teardown };
289289

290-
// Run test suit againt one context.
290+
// Run test suite against one context.
291291
nlTestRunner(&theSuite, nullptr);
292292
return nlTestRunnerStats(&theSuite);
293293
}

src/platform/tests/TestPlatformTime.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int TestPlatformTime()
125125
{
126126
nlTestSuite theSuite = { "PlatformTime tests", &sTests[0], nullptr, nullptr };
127127

128-
// Run test suit againt one context.
128+
// Run test suite against one context.
129129
nlTestRunner(&theSuite, nullptr);
130130
return nlTestRunnerStats(&theSuite);
131131
}

0 commit comments

Comments
 (0)