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>
4
19
5
20
#include < nlunit-test.h>
6
21
@@ -14,47 +29,80 @@ using chip::Dnssd::DnssdService;
14
29
using chip::Dnssd::DnssdServiceProtocol;
15
30
using chip::Dnssd::TextEntry;
16
31
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
+ }
20
56
21
57
static void HandleResolve (void * context, DnssdService * result, const chip::Span<chip::Inet::IPAddress> & addresses,
22
58
CHIP_ERROR error)
23
59
{
60
+ auto * ctx = static_cast <DnssdContext *>(context);
61
+ auto * suite = ctx->mTestSuite ;
24
62
char addrBuf[100 ];
25
- nlTestSuite * suite = static_cast <nlTestSuite *>(context);
26
63
27
64
NL_TEST_ASSERT (suite, result != nullptr );
28
65
NL_TEST_ASSERT (suite, error == CHIP_NO_ERROR);
29
66
30
67
if (!addresses.empty ())
31
68
{
32
69
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 );
34
71
}
35
72
36
73
NL_TEST_ASSERT (suite, result->mTextEntrySize == 1 );
37
74
NL_TEST_ASSERT (suite, strcmp (result->mTextEntries [0 ].mKey , " key" ) == 0 );
38
75
NL_TEST_ASSERT (suite, strcmp (reinterpret_cast <const char *>(result->mTextEntries [0 ].mData ), " val" ) == 0 );
39
76
40
- if (gBrowsedServicesCount == ++gResolvedServicesCount )
77
+ if (ctx-> mBrowsedServicesCount == ++ctx-> mResolvedServicesCount )
41
78
{
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
+ });
45
90
}
46
91
}
47
92
48
93
static void HandleBrowse (void * context, DnssdService * services, size_t servicesSize, bool finalBrowse, CHIP_ERROR error)
49
94
{
50
- nlTestSuite * suite = static_cast <nlTestSuite *>(context);
95
+ auto * ctx = static_cast <DnssdContext *>(context);
96
+ auto * suite = ctx->mTestSuite ;
51
97
52
98
// 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);
55
103
56
- gBrowsedServicesCount += servicesSize;
57
- gEndOfInput = finalBrowse;
104
+ ctx-> mBrowsedServicesCount += servicesSize;
105
+ ctx-> mEndOfInput = finalBrowse;
58
106
59
107
if (servicesSize > 0 )
60
108
{
@@ -63,22 +111,31 @@ static void HandleBrowse(void * context, DnssdService * services, size_t service
63
111
{
64
112
printf (" Service[%u] name %s\n " , i, services[i].mName );
65
113
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);
67
115
}
68
116
}
69
117
}
70
118
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
+ }
72
130
73
- static void InitCallback (void * context, CHIP_ERROR error)
131
+ static void TestDnssdPubSub_DnssdInitCallback (void * context, CHIP_ERROR error)
74
132
{
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 ;
80
136
81
- NL_TEST_ASSERT (suite, error == CHIP_NO_ERROR);
137
+ DnssdService service{};
138
+ TextEntry entry{ " key" , reinterpret_cast <const uint8_t *>(" val" ), 3 };
82
139
83
140
service.mInterface = chip::Inet::InterfaceId::Null ();
84
141
service.mPort = 80 ;
@@ -87,107 +144,65 @@ static void InitCallback(void * context, CHIP_ERROR error)
87
144
strcpy (service.mType , " _mock" );
88
145
service.mAddressType = chip::Inet::IPAddressType::kAny ;
89
146
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 ));
93
147
service.mTextEntries = &entry;
94
148
service.mTextEntrySize = 1 ;
95
149
service.mSubTypes = nullptr ;
96
150
service.mSubTypeSize = 0 ;
97
151
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);
103
153
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);
107
158
}
108
159
109
160
void TestDnssdPubSub (nlTestSuite * inSuite, void * inContext)
110
161
{
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);
114
170
115
171
ChipLogProgress (DeviceLayer, " Start EventLoop" );
116
172
chip::DeviceLayer::PlatformMgr ().RunEventLoop ();
117
173
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;
125
174
126
- std::condition_variable readyCondition;
127
- bool ready = false ;
175
+ NL_TEST_ASSERT (inSuite, !context.mTimeoutExpired );
128
176
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 );
132
179
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
+ }
157
182
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 () };
162
184
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
+ }
181
191
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 ();
188
195
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 };
189
202
190
- return retVal;
203
+ // Run test suite against one context.
204
+ nlTestRunner (&theSuite, nullptr );
205
+ return nlTestRunnerStats (&theSuite);
191
206
}
192
207
193
208
CHIP_REGISTER_TEST_SUITE (TestDnssd);
0 commit comments