1use anyhow::Context;
6use chrono::{Datelike, TimeZone, Timelike};
7use fidl::endpoints::ServerEnd;
8use fidl_fuchsia_hardware_rtc::{DeviceRequest, DeviceRequestStream};
9use fidl_fuchsia_io as fio;
10use fidl_fuchsia_metrics::MetricEvent;
11use fidl_fuchsia_metrics_test::{LogMethod, MetricEventLoggerQuerierProxy};
12use fidl_fuchsia_testing::{FakeClockControlProxy, FakeClockProxy};
13use fidl_fuchsia_time::{MaintenanceRequest, MaintenanceRequestStream};
14use fidl_fuchsia_time_external::{PushSourceMarker, Status, TimeSample};
15use fidl_test_time::{TimeSourceControlRequest, TimeSourceControlRequestStream};
16use fuchsia_async as fasync;
17use fuchsia_component::server::ServiceFs;
18use fuchsia_component_test::{
19 Capability, ChildOptions, ChildRef, LocalComponentHandles, RealmBuilder, RealmInstance, Ref,
20 Route,
21};
22use fuchsia_sync::Mutex;
23use futures::channel::mpsc::Sender;
24use futures::stream::{Stream, StreamExt, TryStreamExt};
25use futures::{Future, FutureExt, SinkExt};
26use push_source::{PushSource, TestUpdateAlgorithm, Update};
27use std::ops::Deref;
28use std::sync::{Arc, LazyLock};
29use time_metrics_registry::PROJECT_ID;
30use vfs::pseudo_directory;
31use zx::{self as zx, Rights};
32
33const TIMEKEEPER_URL: &str = "#meta/timekeeper_for_integration.cm";
35const TIMEKEEPER_FAKE_TIME_URL: &str = "#meta/timekeeper_with_fake_time.cm";
37const COBALT_URL: &str = "#meta/fake_cobalt.cm";
39const FAKE_CLOCK_URL: &str = "#meta/fake_clock.cm";
41
42pub struct NestedTimekeeper {
45 _realm_instance: RealmInstance,
46}
47
48impl Into<RealmInstance> for NestedTimekeeper {
49 fn into(self) -> RealmInstance {
51 self._realm_instance
52 }
53}
54
55impl NestedTimekeeper {
56 pub async fn new(
69 clock: Arc<zx::Clock>,
70 rtc_options: RtcOptions,
71 use_fake_clock: bool,
72 ) -> (
73 Self,
74 Arc<PushSourcePuppet>,
75 RtcUpdates,
76 MetricEventLoggerQuerierProxy,
77 Option<FakeClockController>,
78 ) {
79 let push_source_puppet = Arc::new(PushSourcePuppet::new());
80
81 let builder = RealmBuilder::new().await.unwrap();
82 let fake_cobalt =
83 builder.add_child("fake_cobalt", COBALT_URL, ChildOptions::new()).await.unwrap();
84
85 let timekeeper_url = if use_fake_clock { TIMEKEEPER_FAKE_TIME_URL } else { TIMEKEEPER_URL };
86 log::trace!("using timekeeper_url: {}", timekeeper_url);
87 let timekeeper = builder
88 .add_child("timekeeper_test", timekeeper_url, ChildOptions::new().eager())
89 .await
90 .with_context(|| format!("while starting up timekeeper_test from: {timekeeper_url}"))
91 .unwrap();
92
93 let timesource_server = builder
94 .add_local_child(
95 "timesource_mock",
96 {
97 let push_source_puppet = Arc::clone(&push_source_puppet);
98 move |handles: LocalComponentHandles| {
99 Box::pin(timesource_mock_server(handles, Arc::clone(&push_source_puppet)))
100 }
101 },
102 ChildOptions::new(),
103 )
104 .await
105 .context("while starting up timesource_mock")
106 .unwrap();
107
108 let maintenance_server = builder
109 .add_local_child(
110 "maintenance_mock",
111 move |handles: LocalComponentHandles| {
112 Box::pin(maintenance_mock_server(handles, Arc::clone(&clock)))
113 },
114 ChildOptions::new(),
115 )
116 .await
117 .context("while starting up maintenance_mock")
118 .unwrap();
119
120 if use_fake_clock {
122 let fake_clock =
123 builder.add_child("fake_clock", FAKE_CLOCK_URL, ChildOptions::new()).await.unwrap();
124
125 builder
126 .add_route(
127 Route::new()
128 .capability(Capability::protocol_by_name(
129 "fuchsia.testing.FakeClockControl",
130 ))
131 .from(&fake_clock)
132 .to(Ref::parent()),
133 )
134 .await
135 .context("while setting up FakeClockControl")
136 .unwrap();
137
138 builder
139 .add_route(
140 Route::new()
141 .capability(Capability::protocol_by_name("fuchsia.testing.FakeClock"))
142 .from(&fake_clock)
143 .to(Ref::parent())
144 .to(&timekeeper),
145 )
146 .await
147 .context("while setting up FakeClock")
148 .unwrap();
149
150 builder
151 .add_route(
152 Route::new()
153 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
154 .from(Ref::parent())
155 .to(&fake_clock),
156 )
157 .await
158 .context("while setting up LogSink")
159 .unwrap();
160 };
161
162 builder
163 .add_route(
164 Route::new()
165 .capability(Capability::protocol_by_name("fuchsia.time.Maintenance"))
166 .from(&maintenance_server)
167 .to(&timekeeper),
168 )
169 .await
170 .context("while setting up Maintenance")
171 .unwrap();
172
173 builder
174 .add_route(
175 Route::new()
176 .capability(Capability::protocol_by_name("test.time.TimeSourceControl"))
177 .from(×ource_server)
178 .to(&timekeeper),
179 )
180 .await
181 .unwrap();
182
183 builder
184 .add_route(
185 Route::new()
186 .capability(Capability::protocol_by_name(
187 "fuchsia.metrics.test.MetricEventLoggerQuerier",
188 ))
189 .from(&fake_cobalt)
190 .to(Ref::parent()),
191 )
192 .await
193 .unwrap();
194
195 builder
196 .add_route(
197 Route::new()
198 .capability(Capability::protocol_by_name(
199 "fuchsia.metrics.MetricEventLoggerFactory",
200 ))
201 .from(&fake_cobalt)
202 .to(&timekeeper),
203 )
204 .await
205 .unwrap();
206
207 builder
208 .add_route(
209 Route::new()
210 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
211 .from(Ref::parent())
212 .to(&fake_cobalt)
213 .to(&timekeeper)
214 .to(×ource_server)
215 .to(&maintenance_server),
216 )
217 .await
218 .unwrap();
219
220 let parent_configs = [
221 "fuchsia.time.config.BackOffTimeBetweenPullSamplesSec",
222 "fuchsia.time.config.DisableDelays",
223 "fuchsia.time.config.HasRealTimeClock",
224 "fuchsia.time.config.MaxFrequencyErrorPpm",
225 "fuchsia.time.config.MinAcceptableBootTimeForUtcUpdateMinutes",
226 "fuchsia.time.config.MinUtcReferenceToBackstopDiffMinutes",
227 "fuchsia.time.config.PrimaryTimeSourceUrl",
228 "fuchsia.time.config.PrimaryUsesPull",
229 "fuchsia.time.config.ServeTestProtocols",
230 "fuchsia.time.config.UtcStartAtStartup",
231 "fuchsia.time.config.WritableUTCTime",
232 ];
233 builder
234 .add_route(
235 parent_configs
236 .into_iter()
237 .fold(Route::new(), |route, name| {
238 route.capability(Capability::configuration(name))
239 })
240 .from(Ref::parent())
241 .to(&timekeeper),
242 )
243 .await
244 .unwrap();
245
246 let void_configs = [
249 "fuchsia.time.config.EarlyExit",
250 "fuchsia.time.config.FirstSamplingDelaySec",
251 "fuchsia.time.config.HasAlwaysOnCounter",
252 "fuchsia.time.config.InitialFrequencyPpm",
253 "fuchsia.time.config.MonitorTimeSourceUrl",
254 "fuchsia.time.config.MonitorUsesPull",
255 "fuchsia.time.config.OscillatorErrorStdDevPpm",
256 "fuchsia.time.config.PeriodicRtcUpdateIntervalMinutes",
257 "fuchsia.time.config.PowerTopologyIntegrationEnabled",
258 "fuchsia.time.config.RtcAllowSettingPastUtc",
259 "fuchsia.time.config.ServeFuchsiaTimeAlarms",
260 "fuchsia.time.config.TimeSourceEndpointUrl",
261 "fuchsia.time.config.UseConnectivity",
262 "fuchsia.time.config.UtcMaxAllowedDeltaFutureSec",
263 "fuchsia.time.config.UtcMaxAllowedDeltaPastSec",
264 "fuchsia.time.config.UtcStartAtStartupWhenInvalidRtc",
265 ];
266 builder
267 .add_route(
268 void_configs
269 .into_iter()
270 .fold(Route::new(), |route, name| {
271 route.capability(Capability::configuration(name))
272 })
273 .from(Ref::void())
274 .to(&timekeeper),
275 )
276 .await
277 .unwrap();
278
279 let rtc_updates = setup_rtc(rtc_options, &builder, &timekeeper).await;
280 let realm_instance = builder.build().await.unwrap();
281
282 let fake_clock_control = if use_fake_clock {
283 let control_proxy = realm_instance.root.connect_to_protocol_at_exposed_dir().unwrap();
284 let clock_proxy = realm_instance.root.connect_to_protocol_at_exposed_dir().unwrap();
285 Some(FakeClockController { control_proxy, clock_proxy })
286 } else {
287 None
288 };
289
290 let cobalt_querier = realm_instance
291 .root
292 .connect_to_protocol_at_exposed_dir()
293 .expect("the connection succeeds");
294
295 let nested_timekeeper = Self { _realm_instance: realm_instance };
296
297 (nested_timekeeper, push_source_puppet, rtc_updates, cobalt_querier, fake_clock_control)
298 }
299}
300
301pub struct RemotePushSourcePuppet {
302 proxy: fidl_test_time_realm::PushSourcePuppetProxy,
303}
304
305impl RemotePushSourcePuppet {
306 pub fn new(proxy: fidl_test_time_realm::PushSourcePuppetProxy) -> Arc<Self> {
308 Arc::new(Self { proxy })
309 }
310
311 pub async fn set_sample(&self, sample: TimeSample) {
313 self.proxy.set_sample(&sample).await.expect("original API was infallible");
314 }
315
316 pub async fn set_status(&self, status: Status) {
318 self.proxy.set_status(status).await.expect("original API was infallible");
319 }
320
321 pub async fn simulate_crash(&self) {
323 self.proxy.crash().await.expect("original local API was infallible");
324 }
325
326 pub async fn lifetime_served_connections(&self) -> u32 {
329 self.proxy.get_lifetime_served_connections().await.expect("original API was infallible")
330 }
331}
332
333pub struct PushSourcePuppet {
335 inner: Mutex<PushSourcePuppetInner>,
338 cumulative_clients: Mutex<u32>,
340}
341
342impl PushSourcePuppet {
343 fn new() -> Self {
345 Self { inner: Mutex::new(PushSourcePuppetInner::new()), cumulative_clients: Mutex::new(0) }
346 }
347
348 fn serve_client(&self, server_end: ServerEnd<PushSourceMarker>) {
350 log::debug!("serve_client entry");
351 let mut inner = self.inner.lock();
352 if inner.served_client() {
356 *inner = PushSourcePuppetInner::new();
357 }
358 inner.serve_client(server_end);
359 *self.cumulative_clients.lock() += 1;
360 }
361
362 pub async fn set_sample(&self, sample: TimeSample) {
364 let mut sink = self.inner.lock().get_sink();
365 sink.send(sample.into()).await.unwrap();
366 }
367
368 pub async fn set_status(&self, status: Status) {
370 let mut sink = self.inner.lock().get_sink();
371 sink.send(status.into()).await.unwrap();
372 }
373
374 pub fn simulate_crash(&self) {
376 *self.inner.lock() = PushSourcePuppetInner::new();
377 }
379
380 pub fn lifetime_served_connections(&self) -> u32 {
383 *self.cumulative_clients.lock()
384 }
385}
386
387struct PushSourcePuppetInner {
390 push_source: Arc<PushSource<TestUpdateAlgorithm>>,
391 tasks: Vec<fasync::Task<()>>,
393 update_sink: Sender<Update>,
395}
396
397impl PushSourcePuppetInner {
398 fn new() -> Self {
399 let (update_algorithm, update_sink) = TestUpdateAlgorithm::new();
400 let push_source = Arc::new(PushSource::new(update_algorithm, Status::Ok).unwrap());
401 let push_source_clone = Arc::clone(&push_source);
402 let tasks = vec![fasync::Task::spawn(async move {
403 push_source_clone.poll_updates().await.unwrap();
404 })];
405 Self { push_source, tasks, update_sink }
406 }
407
408 fn served_client(&self) -> bool {
410 self.tasks.len() > 1
411 }
412
413 fn serve_client(&mut self, server_end: ServerEnd<PushSourceMarker>) {
415 let push_source_clone = Arc::clone(&self.push_source);
416 self.tasks.push(fasync::Task::spawn(async move {
417 push_source_clone.handle_requests_for_stream(server_end.into_stream()).await.unwrap();
418 }));
419 }
420
421 fn get_sink(&self) -> Sender<Update> {
426 self.update_sink.clone()
427 }
428}
429
430#[derive(Clone, Debug)]
432pub struct RtcUpdates(Arc<Mutex<Vec<fidl_fuchsia_hardware_rtc::Time>>>);
433
434impl RtcUpdates {
435 pub fn to_vec(&self) -> Vec<fidl_fuchsia_hardware_rtc::Time> {
437 self.0.lock().clone()
438 }
439}
440
441pub struct RemoteRtcUpdates {
444 proxy: fidl_test_time_realm::RtcUpdatesProxy,
445}
446
447impl RemoteRtcUpdates {
448 pub async fn to_vec(&self) -> Vec<fidl_fuchsia_hardware_rtc::Time> {
449 self.proxy
450 .get(fidl_test_time_realm::GetRequest::default())
451 .await
452 .expect("no errors or overflows") .unwrap()
454 .0
455 }
456 pub fn new(proxy: fidl_test_time_realm::RtcUpdatesProxy) -> Self {
457 RemoteRtcUpdates { proxy }
458 }
459}
460
461pub struct FakeClockController {
464 control_proxy: FakeClockControlProxy,
465 clock_proxy: FakeClockProxy,
466}
467
468impl Deref for FakeClockController {
469 type Target = FakeClockControlProxy;
470
471 fn deref(&self) -> &Self::Target {
472 &self.control_proxy
473 }
474}
475
476impl FakeClockController {
477 pub fn new(control_proxy: FakeClockControlProxy, clock_proxy: FakeClockProxy) -> Self {
479 FakeClockController { control_proxy, clock_proxy }
480 }
481
482 pub fn into_components(self) -> (FakeClockControlProxy, FakeClockProxy) {
484 (self.control_proxy, self.clock_proxy)
485 }
486
487 pub async fn get_monotonic(&self) -> Result<i64, fidl::Error> {
488 self.clock_proxy.get().await.map(|(_boot, mono)| mono.into_nanos())
489 }
490
491 pub async fn get_reference(&self) -> Result<zx::BootInstant, fidl::Error> {
493 self.get_monotonic().await.map(|v| zx::BootInstant::from_nanos(v))
494 }
495}
496
497pub enum RtcOptions {
499 None,
502 InitialRtcTime(zx::SyntheticInstant),
504 InjectedRtc(fio::DirectoryProxy),
520}
521
522impl From<fidl_test_time_realm::RtcOptions> for RtcOptions {
523 fn from(value: fidl_test_time_realm::RtcOptions) -> Self {
524 match value {
525 fidl_test_time_realm::RtcOptions::DevClassRtc(h) => {
526 RtcOptions::InjectedRtc(h.into_proxy())
527 }
528 fidl_test_time_realm::RtcOptions::InitialRtcTime(t) => {
529 RtcOptions::InitialRtcTime(zx::SyntheticInstant::from_nanos(t))
530 }
531 _ => unimplemented!(),
532 }
533 }
534}
535
536impl From<zx::SyntheticInstant> for RtcOptions {
537 fn from(value: zx::SyntheticInstant) -> Self {
538 RtcOptions::InitialRtcTime(value)
539 }
540}
541
542impl From<Option<zx::SyntheticInstant>> for RtcOptions {
543 fn from(value: Option<zx::SyntheticInstant>) -> Self {
544 value.map(|t| t.into()).unwrap_or(Self::None)
545 }
546}
547
548async fn setup_rtc(
559 rtc_options: RtcOptions,
560 builder: &RealmBuilder,
561 timekeeper: &ChildRef,
562) -> RtcUpdates {
563 let rtc_updates = RtcUpdates(Arc::new(Mutex::new(vec![])));
564
565 let rtc_dir = match rtc_options {
566 RtcOptions::InitialRtcTime(initial_time) => {
567 log::debug!("using fake fuchsia.hardware.rtc.Service");
568 pseudo_directory! {
569 "fuchsia.hardware.rtc.Service" => pseudo_directory! {
570 "default" => pseudo_directory! {
571 "device" => vfs::service::host({
572 let rtc_updates = rtc_updates.clone();
573 move |stream| {
574 serve_fake_rtc(initial_time, rtc_updates.clone(), stream)
575 }
576 })
577 }
578 }
579 }
580 }
581 RtcOptions::None => {
582 log::debug!("using an empty fuchsia.hardware.rtc.Service directory");
583 pseudo_directory! {
584 "fuchsia.hardware.rtc.Service" => pseudo_directory! {
585 }
586 }
587 }
588 RtcOptions::InjectedRtc(h) => {
589 log::debug!("using RTC provided by client");
590 pseudo_directory! {
591 "fuchsia.hardware.rtc.Service" => vfs::remote::remote_dir(h)
592 }
593 }
594 };
595
596 let fake_rtc_server = builder
597 .add_local_child(
598 "fake_rtc",
599 {
600 move |handles| {
601 let rtc_dir = rtc_dir.clone();
602 async move {
603 let mut fs = ServiceFs::new();
604 fs.add_remote(
605 "svc",
606 vfs::directory::serve_read_only(
607 rtc_dir,
608 vfs::execution_scope::ExecutionScope::new(),
609 ),
610 );
611 fs.serve_connection(handles.outgoing_dir)
612 .expect("failed to serve fake RTC ServiceFs");
613 fs.collect::<()>().await;
614 Ok(())
615 }
616 .boxed()
617 }
618 },
619 ChildOptions::new().eager(),
620 )
621 .await
622 .unwrap();
623
624 builder
625 .add_route(
626 Route::new()
627 .capability(Capability::service::<fidl_fuchsia_hardware_rtc::ServiceMarker>())
628 .from(&fake_rtc_server)
629 .to(&*timekeeper),
630 )
631 .await
632 .unwrap();
633
634 rtc_updates
635}
636
637async fn serve_fake_rtc(
640 initial_time: zx::SyntheticInstant,
641 rtc_updates: RtcUpdates,
642 mut stream: DeviceRequestStream,
643) {
644 let mut current = zx_time_to_rtc_time(initial_time);
645 while let Some(req) = stream.try_next().await.unwrap() {
646 match req {
647 DeviceRequest::Get { responder } => {
648 log::debug!("serve_fake_rtc: DeviceRequest::Get: {current:?}");
649 responder.send(Ok(¤t)).unwrap();
650 }
651 DeviceRequest::Set2 { rtc, responder } => {
652 log::debug!("serve_fake_rtc: DeviceRequest::Set2: {rtc:?}");
653 rtc_updates.0.lock().push(rtc);
654 current = rtc;
655 responder.send(Ok(())).unwrap();
656 }
657 DeviceRequest::_UnknownMethod { .. } => {}
658 }
659 }
660}
661
662async fn serve_test_control(puppet: &PushSourcePuppet, stream: TimeSourceControlRequestStream) {
663 stream
664 .try_for_each_concurrent(None, |req| async {
665 let _ = &req;
666 let TimeSourceControlRequest::ConnectPushSource { push_source, .. } = req;
667 puppet.serve_client(push_source);
668 Ok(())
669 })
670 .await
671 .unwrap();
672}
673
674async fn serve_maintenance(clock_handle: Arc<zx::Clock>, mut stream: MaintenanceRequestStream) {
675 while let Some(req) = stream.try_next().await.unwrap() {
676 let MaintenanceRequest::GetWritableUtcClock { responder } = req;
677 responder.send(clock_handle.duplicate_handle(Rights::SAME_RIGHTS).unwrap()).unwrap();
678 }
679}
680
681async fn timesource_mock_server(
682 handles: LocalComponentHandles,
683 push_source_puppet: Arc<PushSourcePuppet>,
684) -> Result<(), anyhow::Error> {
685 let mut fs = ServiceFs::new();
686 let mut tasks = vec![];
687
688 fs.dir("svc").add_fidl_service(move |stream: TimeSourceControlRequestStream| {
689 let puppet_clone = Arc::clone(&push_source_puppet);
690
691 tasks.push(fasync::Task::local(async move {
692 serve_test_control(&*puppet_clone, stream).await;
693 }));
694 });
695
696 fs.serve_connection(handles.outgoing_dir)?;
697 fs.collect::<()>().await;
698
699 Ok(())
700}
701
702async fn maintenance_mock_server(
703 handles: LocalComponentHandles,
704 clock: Arc<zx::Clock>,
705) -> Result<(), anyhow::Error> {
706 let mut fs = ServiceFs::new();
707 let mut tasks = vec![];
708
709 fs.dir("svc").add_fidl_service(move |stream: MaintenanceRequestStream| {
710 let clock_clone = Arc::clone(&clock);
711
712 tasks.push(fasync::Task::local(async move {
713 serve_maintenance(clock_clone, stream).await;
714 }));
715 });
716
717 fs.serve_connection(handles.outgoing_dir)?;
718 fs.collect::<()>().await;
719
720 Ok(())
721}
722
723fn from_rfc2822(date: &str) -> zx::SyntheticInstant {
724 zx::SyntheticInstant::from_nanos(
725 chrono::DateTime::parse_from_rfc2822(date).unwrap().timestamp_nanos_opt().unwrap(),
726 )
727}
728
729pub static BACKSTOP_TIME: LazyLock<zx::SyntheticInstant> =
730 LazyLock::new(|| from_rfc2822("Sun, 20 Sep 2020 01:01:01 GMT"));
731pub static VALID_RTC_TIME: LazyLock<zx::SyntheticInstant> =
732 LazyLock::new(|| from_rfc2822("Sun, 20 Sep 2020 02:02:02 GMT"));
733pub static BEFORE_BACKSTOP_TIME: LazyLock<zx::SyntheticInstant> =
734 LazyLock::new(|| from_rfc2822("Fri, 06 Mar 2020 04:04:04 GMT"));
735pub static VALID_TIME: LazyLock<zx::SyntheticInstant> =
736 LazyLock::new(|| from_rfc2822("Tue, 29 Sep 2020 02:19:01 GMT"));
737pub static VALID_TIME_2: LazyLock<zx::SyntheticInstant> =
738 LazyLock::new(|| from_rfc2822("Wed, 30 Sep 2020 14:59:59 GMT"));
739
740pub const BETWEEN_SAMPLES: zx::BootDuration = zx::BootDuration::from_seconds(5);
742
743pub const STD_DEV: zx::BootDuration = zx::BootDuration::from_millis(50);
745
746pub fn new_clock() -> Arc<zx::SyntheticClock> {
749 Arc::new(new_nonshareable_clock())
750}
751
752pub fn new_nonshareable_clock() -> zx::SyntheticClock {
754 zx::SyntheticClock::create(zx::ClockOpts::MAPPABLE, Some(*BACKSTOP_TIME)).unwrap()
755}
756
757fn zx_time_to_rtc_time(zx_time: zx::SyntheticInstant) -> fidl_fuchsia_hardware_rtc::Time {
758 let date = chrono::Utc.timestamp_nanos(zx_time.into_nanos());
759 fidl_fuchsia_hardware_rtc::Time {
760 seconds: date.second() as u8,
761 minutes: date.minute() as u8,
762 hours: date.hour() as u8,
763 day: date.day() as u8,
764 month: date.month() as u8,
765 year: date.year() as u16,
766 }
767}
768
769pub fn rtc_time_to_zx_time(rtc_time: fidl_fuchsia_hardware_rtc::Time) -> zx::SyntheticInstant {
770 let date = chrono::Utc
771 .with_ymd_and_hms(
772 rtc_time.year as i32,
773 rtc_time.month as u32,
774 rtc_time.day as u32,
775 rtc_time.hours as u32,
776 rtc_time.minutes as u32,
777 rtc_time.seconds as u32,
778 )
779 .unwrap();
780 zx::SyntheticInstant::from_nanos(date.timestamp_nanos_opt().unwrap())
781}
782
783pub fn create_cobalt_event_stream(
785 proxy: Arc<MetricEventLoggerQuerierProxy>,
786 log_method: LogMethod,
787) -> std::pin::Pin<Box<dyn Stream<Item = MetricEvent>>> {
788 async_utils::hanging_get::client::HangingGetStream::new(proxy, move |p| {
789 p.watch_logs(PROJECT_ID, log_method)
790 })
791 .map(|res| futures::stream::iter(res.expect("there should be a valid result here").0))
792 .flatten()
793 .boxed()
794}
795
796#[macro_export]
798macro_rules! poll_until_some {
799 ($condition:expr) => {
800 $crate::poll_until_some_impl(
801 $condition,
802 &$crate::SourceLocation::new(file!(), line!(), column!()),
803 )
804 };
805}
806
807#[macro_export]
810macro_rules! poll_until_some_async {
811 ($condition:expr) => {{
812 let loc = $crate::SourceLocation::new(file!(), line!(), column!());
813 log::info!("=> poll_until_some_async() for {}", &loc);
814 let mut result = None;
815 loop {
816 result = $condition.await;
817 if result.is_some() {
818 break;
819 }
820 fasync::Timer::new(fasync::MonotonicInstant::after($crate::RETRY_WAIT_DURATION)).await;
821 }
822 log::info!("=> poll_until_some_async() done for {}", &loc);
823 result.expect("we loop around while result is None")
824 }};
825}
826
827#[macro_export]
830macro_rules! poll_until_async {
831 ($condition:expr) => {
832 $crate::poll_until_async_impl(
833 $condition,
834 &$crate::SourceLocation::new(file!(), line!(), column!()),
835 )
836 };
837}
838
839#[macro_export]
841macro_rules! poll_until_async_2 {
842 ($condition:expr) => {{
843 let loc = $crate::SourceLocation::new(file!(), line!(), column!());
844 log::info!("=> poll_until_async() for {}", &loc);
845 let mut result = true;
846 loop {
847 result = $condition.await;
848 if result {
849 break;
850 }
851 fasync::Timer::new(fasync::MonotonicInstant::after($crate::RETRY_WAIT_DURATION)).await;
852 }
853 log::info!("=> poll_until_async_2() done for {}", &loc);
854 result
855 }};
856}
857
858#[macro_export]
860macro_rules! poll_until {
861 ($condition:expr) => {
862 $crate::poll_until_impl(
863 $condition,
864 &$crate::SourceLocation::new(file!(), line!(), column!()),
865 )
866 };
867}
868
869pub const RETRY_WAIT_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
871
872pub struct SourceLocation {
873 file: &'static str,
874 line: u32,
875 column: u32,
876}
877
878impl std::fmt::Display for SourceLocation {
879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
880 write!(f, "(file: {}, line: {}, column: {})", self.file, self.line, self.column)
881 }
882}
883
884impl SourceLocation {
885 pub fn new(file: &'static str, line: u32, column: u32) -> Self {
886 Self { file, line, column }
887 }
888}
889
890pub async fn poll_until_some_impl<T, F>(poll_fn: F, loc: &SourceLocation) -> T
892where
893 F: Fn() -> Option<T>,
894{
895 log::info!("=> poll_until_some() for {}", loc);
896 loop {
897 match poll_fn() {
898 Some(value) => {
899 log::info!("<= poll_until_some() for {}", loc);
900 return value;
901 }
902 None => fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await,
903 }
904 }
905}
906
907pub async fn poll_until_async_impl<F, Fut>(poll_fn: F, loc: &SourceLocation)
909where
910 F: Fn() -> Fut,
911 Fut: Future<Output = bool>,
912{
913 log::info!("=> poll_until_async() for {}", loc);
914 while !poll_fn().await {
915 fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await
916 }
917 log::info!("<= poll_until_async() for {}", loc);
918}
919
920pub async fn poll_until_impl<F: Fn() -> bool>(poll_fn: F, loc: &SourceLocation) {
922 log::info!("=> poll_until() for {}", loc);
923 while !poll_fn() {
924 fasync::Timer::new(fasync::MonotonicInstant::after(RETRY_WAIT_DURATION)).await
925 }
926 log::info!("<= poll_until() for {}", loc);
927}