xref: /aosp_15_r20/external/openscreen/cast/common/discovery/e2e_test/tests.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <atomic>
6 #include <functional>
7 #include <map>
8 #include <string>
9 
10 // NOTE: although we use gtest here, prefer OSP_CHECKs to
11 // ASSERTS due to asynchronous concerns around test failures.
12 // Although this causes the entire test binary to fail instead of
13 // just a single test, it makes debugging easier/possible.
14 #include "cast/common/public/receiver_info.h"
15 #include "discovery/common/config.h"
16 #include "discovery/common/reporting_client.h"
17 #include "discovery/public/dns_sd_service_factory.h"
18 #include "discovery/public/dns_sd_service_publisher.h"
19 #include "discovery/public/dns_sd_service_watcher.h"
20 #include "gtest/gtest.h"
21 #include "platform/api/udp_socket.h"
22 #include "platform/base/interface_info.h"
23 #include "platform/impl/network_interface.h"
24 #include "platform/impl/platform_client_posix.h"
25 #include "platform/impl/task_runner.h"
26 #include "testing/util/task_util.h"
27 #include "util/chrono_helpers.h"
28 #include "util/osp_logging.h"
29 
30 namespace openscreen {
31 namespace cast {
32 namespace {
33 
34 // Maximum amount of time needed for a query to be received.
35 constexpr seconds kMaxQueryDuration{3};
36 
37 // Total wait time = 4 seconds.
38 constexpr milliseconds kWaitLoopSleepTime(500);
39 constexpr int kMaxWaitLoopIterations = 8;
40 
41 // Total wait time = 2.5 seconds.
42 // NOTE: This must be less than the above wait time.
43 constexpr milliseconds kCheckLoopSleepTime(100);
44 constexpr int kMaxCheckLoopIterations = 25;
45 
46 // Publishes new service instances.
47 class Publisher : public discovery::DnsSdServicePublisher<ReceiverInfo> {
48  public:
Publisher(discovery::DnsSdService * service)49   explicit Publisher(discovery::DnsSdService* service)  // NOLINT
50       : DnsSdServicePublisher<ReceiverInfo>(service,
51                                             kCastV2ServiceId,
52                                             ReceiverInfoToDnsSdInstance) {
53     OSP_LOG_INFO << "Initializing Publisher...\n";
54   }
55 
56   ~Publisher() override = default;
57 
IsInstanceIdClaimed(const std::string & requested_id)58   bool IsInstanceIdClaimed(const std::string& requested_id) {
59     auto it =
60         std::find(instance_ids_.begin(), instance_ids_.end(), requested_id);
61     return it != instance_ids_.end();
62   }
63 
64  private:
65   // DnsSdPublisher::Client overrides.
OnInstanceClaimed(const std::string & requested_id)66   void OnInstanceClaimed(const std::string& requested_id) override {
67     instance_ids_.push_back(requested_id);
68   }
69 
70   std::vector<std::string> instance_ids_;
71 };
72 
73 // Receives incoming services and outputs their results to stdout.
74 class ServiceReceiver : public discovery::DnsSdServiceWatcher<ReceiverInfo> {
75  public:
ServiceReceiver(discovery::DnsSdService * service)76   explicit ServiceReceiver(discovery::DnsSdService* service)  // NOLINT
77       : discovery::DnsSdServiceWatcher<ReceiverInfo>(
78             service,
79             kCastV2ServiceId,
80             DnsSdInstanceEndpointToReceiverInfo,
81             [this](
82                 std::vector<std::reference_wrapper<const ReceiverInfo>> infos) {
83               ProcessResults(std::move(infos));
84             }) {
85     OSP_LOG_INFO << "Initializing ServiceReceiver...";
86   }
87 
IsServiceFound(const ReceiverInfo & check_service)88   bool IsServiceFound(const ReceiverInfo& check_service) {
89     return std::find_if(receiver_infos_.begin(), receiver_infos_.end(),
90                         [&check_service](const ReceiverInfo& info) {
91                           return info.friendly_name ==
92                                  check_service.friendly_name;
93                         }) != receiver_infos_.end();
94   }
95 
EraseReceivedServices()96   void EraseReceivedServices() { receiver_infos_.clear(); }
97 
98  private:
ProcessResults(std::vector<std::reference_wrapper<const ReceiverInfo>> infos)99   void ProcessResults(
100       std::vector<std::reference_wrapper<const ReceiverInfo>> infos) {
101     receiver_infos_.clear();
102     for (const ReceiverInfo& info : infos) {
103       receiver_infos_.push_back(info);
104     }
105   }
106 
107   std::vector<ReceiverInfo> receiver_infos_;
108 };
109 
110 class FailOnErrorReporting : public discovery::ReportingClient {
OnFatalError(Error error)111   void OnFatalError(Error error) override {
112     OSP_LOG_FATAL << "Fatal error received: '" << error << "'";
113     OSP_NOTREACHED();
114   }
115 
OnRecoverableError(Error error)116   void OnRecoverableError(Error error) override {
117     // Pending resolution of openscreen:105, logging recoverable errors is
118     // disabled, as this will end up polluting the output with logs related to
119     // mDNS messages received from non-loopback network interfaces over which
120     // we have no control.
121   }
122 };
123 
GetConfigSettings()124 discovery::Config GetConfigSettings() {
125   // Get the loopback interface to run on.
126   InterfaceInfo loopback = GetLoopbackInterfaceForTesting().value();
127   OSP_LOG_INFO << "Selected network interface for testing: " << loopback;
128   return discovery::Config{{std::move(loopback)}};
129 }
130 
131 class DiscoveryE2ETest : public testing::Test {
132  public:
DiscoveryE2ETest()133   DiscoveryE2ETest() {
134     // Sleep to let any packets clear off the network before further tests.
135     std::this_thread::sleep_for(milliseconds(500));
136 
137     PlatformClientPosix::Create(milliseconds(50));
138     task_runner_ = PlatformClientPosix::GetInstance()->GetTaskRunner();
139   }
140 
~DiscoveryE2ETest()141   ~DiscoveryE2ETest() {
142     OSP_LOG_INFO << "TEST COMPLETE!";
143     dnssd_service_.reset();
144     PlatformClientPosix::ShutDown();
145   }
146 
147  protected:
GetInfo(int id)148   ReceiverInfo GetInfo(int id) {
149     ReceiverInfo hosted_service;
150     hosted_service.port = 1234;
151     hosted_service.unique_id = "id" + std::to_string(id);
152     hosted_service.model_name = "openscreen-Model" + std::to_string(id);
153     hosted_service.friendly_name = "Demo" + std::to_string(id);
154     return hosted_service;
155   }
156 
SetUpService(const discovery::Config & config)157   void SetUpService(const discovery::Config& config) {
158     OSP_DCHECK(!dnssd_service_.get());
159     std::atomic_bool done{false};
160     task_runner_->PostTask([this, &config, &done]() {
161       dnssd_service_ = discovery::CreateDnsSdService(
162           task_runner_, &reporting_client_, config);
163       receiver_ = std::make_unique<ServiceReceiver>(dnssd_service_.get());
164       publisher_ = std::make_unique<Publisher>(dnssd_service_.get());
165       done = true;
166     });
167     WaitForCondition([&done]() { return done.load(); }, kWaitLoopSleepTime,
168                      kMaxWaitLoopIterations);
169     OSP_CHECK(done);
170   }
171 
StartDiscovery()172   void StartDiscovery() {
173     OSP_DCHECK(dnssd_service_.get());
174     task_runner_->PostTask([this]() { receiver_->StartDiscovery(); });
175   }
176 
177   template <typename... RecordTypes>
UpdateRecords(RecordTypes...records)178   void UpdateRecords(RecordTypes... records) {
179     OSP_DCHECK(dnssd_service_.get());
180     OSP_DCHECK(publisher_.get());
181 
182     std::vector<ReceiverInfo> record_set{std::move(records)...};
183     for (ReceiverInfo& record : record_set) {
184       task_runner_->PostTask([this, r = std::move(record)]() {
185         auto error = publisher_->UpdateRegistration(r);
186         OSP_CHECK(error.ok()) << "\tFailed to update service instance '"
187                               << r.friendly_name << "': " << error << "!";
188       });
189     }
190   }
191 
192   template <typename... RecordTypes>
PublishRecords(RecordTypes...records)193   void PublishRecords(RecordTypes... records) {
194     OSP_DCHECK(dnssd_service_.get());
195     OSP_DCHECK(publisher_.get());
196 
197     std::vector<ReceiverInfo> record_set{std::move(records)...};
198     for (ReceiverInfo& record : record_set) {
199       task_runner_->PostTask([this, r = std::move(record)]() {
200         auto error = publisher_->Register(r);
201         OSP_CHECK(error.ok()) << "\tFailed to publish service instance '"
202                               << r.friendly_name << "': " << error << "!";
203       });
204     }
205   }
206 
207   template <typename... AtomicBoolPtrs>
WaitUntilSeen(bool should_be_seen,AtomicBoolPtrs...bools)208   void WaitUntilSeen(bool should_be_seen, AtomicBoolPtrs... bools) {
209     OSP_DCHECK(dnssd_service_.get());
210     std::vector<std::atomic_bool*> atomic_bools{bools...};
211 
212     int waiting_on = atomic_bools.size();
213     for (int i = 0; i < kMaxWaitLoopIterations; i++) {
214       waiting_on = atomic_bools.size();
215       for (std::atomic_bool* atomic : atomic_bools) {
216         if (*atomic) {
217           OSP_CHECK(should_be_seen) << "Found service instance!";
218           waiting_on--;
219         }
220       }
221 
222       if (waiting_on) {
223         OSP_LOG_INFO << "\tWaiting on " << waiting_on << "...";
224         std::this_thread::sleep_for(kWaitLoopSleepTime);
225         continue;
226       }
227       return;
228     }
229     OSP_CHECK(!should_be_seen)
230         << "Could not find " << waiting_on << " service instances!";
231   }
232 
CheckForClaimedIds(ReceiverInfo receiver_info,std::atomic_bool * has_been_seen)233   void CheckForClaimedIds(ReceiverInfo receiver_info,
234                           std::atomic_bool* has_been_seen) {
235     OSP_DCHECK(dnssd_service_.get());
236     task_runner_->PostTask(
237         [this, info = std::move(receiver_info), has_been_seen]() mutable {
238           CheckForClaimedIds(std::move(info), has_been_seen, 0);
239         });
240   }
241 
CheckForPublishedService(ReceiverInfo receiver_info,std::atomic_bool * has_been_seen)242   void CheckForPublishedService(ReceiverInfo receiver_info,
243                                 std::atomic_bool* has_been_seen) {
244     OSP_DCHECK(dnssd_service_.get());
245     task_runner_->PostTask(
246         [this, info = std::move(receiver_info), has_been_seen]() mutable {
247           CheckForPublishedService(std::move(info), has_been_seen, 0, true);
248         });
249   }
250 
251   // TODO(issuetracker.google.com/159256503): Change this to use a polling
252   // method to wait until the service disappears rather than immediately failing
253   // if it exists, so waits throughout this file can be removed.
CheckNotPublishedService(ReceiverInfo receiver_info,std::atomic_bool * has_been_seen)254   void CheckNotPublishedService(ReceiverInfo receiver_info,
255                                 std::atomic_bool* has_been_seen) {
256     OSP_DCHECK(dnssd_service_.get());
257     task_runner_->PostTask(
258         [this, info = std::move(receiver_info), has_been_seen]() mutable {
259           CheckForPublishedService(std::move(info), has_been_seen, 0, false);
260         });
261   }
262   TaskRunner* task_runner_;
263   FailOnErrorReporting reporting_client_;
264   SerialDeletePtr<discovery::DnsSdService> dnssd_service_;
265   std::unique_ptr<ServiceReceiver> receiver_;
266   std::unique_ptr<Publisher> publisher_;
267 
268  private:
CheckForClaimedIds(ReceiverInfo receiver_info,std::atomic_bool * has_been_seen,int attempts)269   void CheckForClaimedIds(ReceiverInfo receiver_info,
270                           std::atomic_bool* has_been_seen,
271                           int attempts) {
272     if (publisher_->IsInstanceIdClaimed(receiver_info.GetInstanceId())) {
273       // TODO(crbug.com/openscreen/110): Log the published service instance.
274       *has_been_seen = true;
275       return;
276     }
277 
278     OSP_CHECK_LE(attempts++, kMaxCheckLoopIterations)
279         << "Service " << receiver_info.friendly_name << " publication failed.";
280     task_runner_->PostTaskWithDelay(
281         [this, info = std::move(receiver_info), has_been_seen,
282          attempts]() mutable {
283           CheckForClaimedIds(std::move(info), has_been_seen, attempts);
284         },
285         kCheckLoopSleepTime);
286   }
287 
CheckForPublishedService(ReceiverInfo receiver_info,std::atomic_bool * has_been_seen,int attempts,bool expect_to_be_present)288   void CheckForPublishedService(ReceiverInfo receiver_info,
289                                 std::atomic_bool* has_been_seen,
290                                 int attempts,
291                                 bool expect_to_be_present) {
292     if (!receiver_->IsServiceFound(receiver_info)) {
293       if (attempts++ > kMaxCheckLoopIterations) {
294         OSP_CHECK(!expect_to_be_present)
295             << "Service " << receiver_info.friendly_name
296             << " discovery failed.";
297         return;
298       }
299       task_runner_->PostTaskWithDelay(
300           [this, info = std::move(receiver_info), has_been_seen, attempts,
301            expect_to_be_present]() mutable {
302             CheckForPublishedService(std::move(info), has_been_seen, attempts,
303                                      expect_to_be_present);
304           },
305           kCheckLoopSleepTime);
306     } else if (expect_to_be_present) {
307       // TODO(crbug.com/openscreen/110): Log the discovered service instance.
308       *has_been_seen = true;
309     } else {
310       OSP_LOG_FATAL << "Found instance '" << receiver_info.friendly_name
311                     << "'!";
312     }
313   }
314 };
315 
316 // The below runs an E2E tests. These test requires no user interaction and is
317 // intended to perform a set series of actions to validate that discovery is
318 // functioning as intended.
319 //
320 // Known issues:
321 // - The ipv6 socket in discovery/mdns/service_impl.cc fails to bind to an ipv6
322 //   address on the loopback interface. Investigating this issue is pending
323 //   resolution of bug
324 //   https://bugs.chromium.org/p/openscreen/issues/detail?id=105.
325 //
326 // In this test, the following operations are performed:
327 // 1) Start up the Cast platform for a posix system.
328 // 2) Publish 3 CastV2 service instances to the loopback interface using mDNS,
329 //    with record announcement disabled.
330 // 3) Wait for the probing phase to successfully complete.
331 // 4) Query for records published over the loopback interface, and validate that
332 //    all 3 previously published services are discovered.
TEST_F(DiscoveryE2ETest,ValidateQueryFlow)333 TEST_F(DiscoveryE2ETest, ValidateQueryFlow) {
334   // Set up demo infra.
335   auto discovery_config = GetConfigSettings();
336   discovery_config.new_record_announcement_count = 0;
337   SetUpService(discovery_config);
338 
339   auto instance1 = GetInfo(1);
340   auto instance2 = GetInfo(2);
341   auto instance3 = GetInfo(3);
342 
343   // Start discovery and publication.
344   StartDiscovery();
345   PublishRecords(instance1, instance2, instance3);
346 
347   // Wait until all probe phases complete and all instance ids are claimed. At
348   // this point, all records should be published.
349   OSP_LOG_INFO << "Service publication in progress...";
350   std::atomic_bool found1{false};
351   std::atomic_bool found2{false};
352   std::atomic_bool found3{false};
353   CheckForClaimedIds(instance1, &found1);
354   CheckForClaimedIds(instance2, &found2);
355   CheckForClaimedIds(instance3, &found3);
356   WaitUntilSeen(true, &found1, &found2, &found3);
357   OSP_LOG_INFO << "\tAll services successfully published!\n";
358 
359   // Make sure all services are found through discovery.
360   OSP_LOG_INFO << "Service discovery in progress...";
361   found1 = false;
362   found2 = false;
363   found3 = false;
364   CheckForPublishedService(instance1, &found1);
365   CheckForPublishedService(instance2, &found2);
366   CheckForPublishedService(instance3, &found3);
367   WaitUntilSeen(true, &found1, &found2, &found3);
368 }
369 
370 // In this test, the following operations are performed:
371 // 1) Start up the Cast platform for a posix system.
372 // 2) Start service discovery and new queries, with no query messages being
373 //    sent.
374 // 3) Publish 3 CastV2 service instances to the loopback interface using mDNS,
375 //    with record announcement enabled.
376 // 4) Ensure the correct records were published over the loopback interface.
377 // 5) De-register all services.
378 // 6) Ensure that goodbye records are received for all service instances.
TEST_F(DiscoveryE2ETest,ValidateAnnouncementFlow)379 TEST_F(DiscoveryE2ETest, ValidateAnnouncementFlow) {
380   // Set up demo infra.
381   auto discovery_config = GetConfigSettings();
382   discovery_config.new_query_announcement_count = 0;
383   SetUpService(discovery_config);
384 
385   auto instance1 = GetInfo(1);
386   auto instance2 = GetInfo(2);
387   auto instance3 = GetInfo(3);
388 
389   // Start discovery and publication.
390   StartDiscovery();
391   PublishRecords(instance1, instance2, instance3);
392 
393   // Wait until all probe phases complete and all instance ids are claimed. At
394   // this point, all records should be published.
395   OSP_LOG_INFO << "Service publication in progress...";
396   std::atomic_bool found1{false};
397   std::atomic_bool found2{false};
398   std::atomic_bool found3{false};
399   CheckForClaimedIds(instance1, &found1);
400   CheckForClaimedIds(instance2, &found2);
401   CheckForClaimedIds(instance3, &found3);
402   WaitUntilSeen(true, &found1, &found2, &found3);
403   OSP_LOG_INFO << "\tAll services successfully published and announced!\n";
404 
405   // Make sure all services are found through discovery.
406   OSP_LOG_INFO << "Service discovery in progress...";
407   found1 = false;
408   found2 = false;
409   found3 = false;
410   CheckForPublishedService(instance1, &found1);
411   CheckForPublishedService(instance2, &found2);
412   CheckForPublishedService(instance3, &found3);
413   WaitUntilSeen(true, &found1, &found2, &found3);
414   OSP_LOG_INFO << "\tAll services successfully discovered!\n";
415 
416   // Deregister all service instances.
417   OSP_LOG_INFO << "Deregister all services...";
418   task_runner_->PostTask([this]() {
419     ErrorOr<int> result = publisher_->DeregisterAll();
420     ASSERT_FALSE(result.is_error());
421     ASSERT_EQ(result.value(), 3);
422   });
423   std::this_thread::sleep_for(seconds(3));
424   found1 = false;
425   found2 = false;
426   found3 = false;
427   CheckNotPublishedService(instance1, &found1);
428   CheckNotPublishedService(instance2, &found2);
429   CheckNotPublishedService(instance3, &found3);
430   WaitUntilSeen(false, &found1, &found2, &found3);
431 }
432 
433 // In this test, the following operations are performed:
434 // 1) Start up the Cast platform for a posix system.
435 // 2) Publish one service and ensure it is NOT received.
436 // 3) Start service discovery and new queries.
437 // 4) Ensure above published service IS received.
438 // 5) Stop the started query.
439 // 6) Update a service, and ensure that no callback is received.
440 // 7) Restart the query and ensure that only the expected callbacks are
441 // received.
TEST_F(DiscoveryE2ETest,ValidateRecordsOnlyReceivedWhenQueryRunning)442 TEST_F(DiscoveryE2ETest, ValidateRecordsOnlyReceivedWhenQueryRunning) {
443   // Set up demo infra.
444   auto discovery_config = GetConfigSettings();
445   discovery_config.new_record_announcement_count = 1;
446   SetUpService(discovery_config);
447 
448   auto instance = GetInfo(1);
449 
450   // Start discovery and publication.
451   PublishRecords(instance);
452 
453   // Wait until all probe phases complete and all instance ids are claimed. At
454   // this point, all records should be published.
455   OSP_LOG_INFO << "Service publication in progress...";
456   std::atomic_bool found{false};
457   CheckForClaimedIds(instance, &found);
458   WaitUntilSeen(true, &found);
459 
460   // And ensure stopped discovery does not find the records.
461   OSP_LOG_INFO
462       << "Validating no service discovery occurs when discovery stopped...";
463   found = false;
464   CheckNotPublishedService(instance, &found);
465   WaitUntilSeen(false, &found);
466 
467   // Make sure all services are found through discovery.
468   StartDiscovery();
469   OSP_LOG_INFO << "Service discovery in progress...";
470   found = false;
471   CheckForPublishedService(instance, &found);
472   WaitUntilSeen(true, &found);
473 
474   // Update discovery and ensure that the updated service is seen.
475   OSP_LOG_INFO << "Updating service and waiting for discovery...";
476   auto updated_instance = instance;
477   updated_instance.friendly_name = "OtherName";
478   found = false;
479   UpdateRecords(updated_instance);
480   CheckForPublishedService(updated_instance, &found);
481   WaitUntilSeen(true, &found);
482 
483   // And ensure the old service has been removed.
484   found = false;
485   CheckNotPublishedService(instance, &found);
486   WaitUntilSeen(false, &found);
487 
488   // Stop discovery.
489   OSP_LOG_INFO << "Stopping discovery...";
490   task_runner_->PostTask([this]() { receiver_->StopDiscovery(); });
491 
492   // Update discovery and ensure that the updated service is NOT seen.
493   OSP_LOG_INFO
494       << "Updating service and validating the change isn't received...";
495   found = false;
496   instance.friendly_name = "ThirdName";
497   UpdateRecords(instance);
498   CheckNotPublishedService(instance, &found);
499   WaitUntilSeen(false, &found);
500 
501   StartDiscovery();
502   std::this_thread::sleep_for(kMaxQueryDuration);
503 
504   OSP_LOG_INFO << "Service discovery in progress...";
505   found = false;
506   CheckNotPublishedService(updated_instance, &found);
507   WaitUntilSeen(false, &found);
508 
509   found = false;
510   CheckForPublishedService(instance, &found);
511   WaitUntilSeen(true, &found);
512 }
513 
514 // In this test, the following operations are performed:
515 // 1) Start up the Cast platform for a posix system.
516 // 2) Start service discovery and new queries.
517 // 3) Publish one service and ensure it is received.
518 // 4) Hard reset discovery
519 // 5) Ensure the same service is discovered
520 // 6) Soft reset the service, and ensure that a callback is received.
TEST_F(DiscoveryE2ETest,ValidateRefreshFlow)521 TEST_F(DiscoveryE2ETest, ValidateRefreshFlow) {
522   // Set up demo infra.
523   // NOTE: This configuration assumes that packets cannot be lost over the
524   // loopback interface.
525   auto discovery_config = GetConfigSettings();
526   discovery_config.new_record_announcement_count = 0;
527   discovery_config.new_query_announcement_count = 2;
528   SetUpService(discovery_config);
529 
530   auto instance = GetInfo(1);
531 
532   // Start discovery and publication.
533   StartDiscovery();
534   PublishRecords(instance);
535 
536   // Wait until all probe phases complete and all instance ids are claimed. At
537   // this point, all records should be published.
538   OSP_LOG_INFO << "Service publication in progress...";
539   std::atomic_bool found{false};
540   CheckForClaimedIds(instance, &found);
541   WaitUntilSeen(true, &found);
542 
543   // Make sure all services are found through discovery.
544   OSP_LOG_INFO << "Service discovery in progress...";
545   found = false;
546   CheckForPublishedService(instance, &found);
547   WaitUntilSeen(true, &found);
548 
549   // Force refresh discovery, then ensure that the published service is
550   // re-discovered.
551   OSP_LOG_INFO << "Force refresh discovery...";
552   task_runner_->PostTask([this]() { receiver_->EraseReceivedServices(); });
553   std::this_thread::sleep_for(kMaxQueryDuration);
554   found = false;
555   CheckNotPublishedService(instance, &found);
556   WaitUntilSeen(false, &found);
557   task_runner_->PostTask([this]() { receiver_->ForceRefresh(); });
558 
559   OSP_LOG_INFO << "Ensure that the published service is re-discovered...";
560   found = false;
561   CheckForPublishedService(instance, &found);
562   WaitUntilSeen(true, &found);
563 
564   // Soft refresh discovery, then ensure that the published service is NOT
565   // re-discovered.
566   OSP_LOG_INFO << "Call DiscoverNow on discovery...";
567   task_runner_->PostTask([this]() { receiver_->EraseReceivedServices(); });
568   std::this_thread::sleep_for(kMaxQueryDuration);
569   found = false;
570   CheckNotPublishedService(instance, &found);
571   WaitUntilSeen(false, &found);
572   task_runner_->PostTask([this]() { receiver_->DiscoverNow(); });
573 
574   OSP_LOG_INFO << "Ensure that the published service is re-discovered...";
575   found = false;
576   CheckForPublishedService(instance, &found);
577   WaitUntilSeen(true, &found);
578 }
579 
580 }  // namespace
581 }  // namespace cast
582 }  // namespace openscreen
583