1use serde::{Deserialize, Serialize};
7use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
8use std::sync::Arc;
9use std::time::{Duration, SystemTime, UNIX_EPOCH};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub enum ClockState {
14 Running,
16 Paused,
18 System,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ClockOptions {
25 pub time_ms: u64,
27 pub paused: bool,
29}
30
31impl ClockOptions {
32 #[must_use]
34 pub fn now() -> Self {
35 let time_ms = SystemTime::now()
36 .duration_since(UNIX_EPOCH)
37 .map(|d| d.as_millis() as u64)
38 .unwrap_or(0);
39
40 Self {
41 time_ms,
42 paused: false,
43 }
44 }
45
46 #[must_use]
48 pub fn fixed(time_ms: u64) -> Self {
49 Self {
50 time_ms,
51 paused: true,
52 }
53 }
54
55 pub fn from_iso(iso: &str) -> Result<Self, ClockError> {
61 let time_ms = parse_iso_to_ms(iso)?;
64 Ok(Self {
65 time_ms,
66 paused: false,
67 })
68 }
69
70 #[must_use]
72 pub fn paused(mut self, paused: bool) -> Self {
73 self.paused = paused;
74 self
75 }
76}
77
78impl Default for ClockOptions {
79 fn default() -> Self {
80 Self::now()
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
86pub enum ClockError {
87 InvalidFormat(String),
89 NotInstalled,
91 AlreadyInstalled,
93}
94
95impl std::fmt::Display for ClockError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::InvalidFormat(s) => write!(f, "Invalid datetime format: {s}"),
99 Self::NotInstalled => write!(f, "Clock not installed"),
100 Self::AlreadyInstalled => write!(f, "Clock already installed"),
101 }
102 }
103}
104
105impl std::error::Error for ClockError {}
106
107#[derive(Debug)]
109pub struct FakeClock {
110 current_ms: AtomicU64,
112 paused: AtomicBool,
114 installed: AtomicBool,
116 install_real_ms: AtomicU64,
118 install_fake_ms: AtomicU64,
120}
121
122impl FakeClock {
123 #[must_use]
125 pub fn new() -> Self {
126 Self {
127 current_ms: AtomicU64::new(0),
128 paused: AtomicBool::new(false),
129 installed: AtomicBool::new(false),
130 install_real_ms: AtomicU64::new(0),
131 install_fake_ms: AtomicU64::new(0),
132 }
133 }
134
135 pub fn install(&self, options: ClockOptions) -> Result<(), ClockError> {
141 if self.installed.swap(true, Ordering::SeqCst) {
142 return Err(ClockError::AlreadyInstalled);
143 }
144
145 let real_ms = SystemTime::now()
146 .duration_since(UNIX_EPOCH)
147 .map(|d| d.as_millis() as u64)
148 .unwrap_or(0);
149
150 self.install_real_ms.store(real_ms, Ordering::SeqCst);
151 self.install_fake_ms
152 .store(options.time_ms, Ordering::SeqCst);
153 self.current_ms.store(options.time_ms, Ordering::SeqCst);
154 self.paused.store(options.paused, Ordering::SeqCst);
155
156 Ok(())
157 }
158
159 pub fn uninstall(&self) {
161 self.installed.store(false, Ordering::SeqCst);
162 self.paused.store(false, Ordering::SeqCst);
163 }
164
165 #[must_use]
167 pub fn is_installed(&self) -> bool {
168 self.installed.load(Ordering::SeqCst)
169 }
170
171 #[must_use]
173 pub fn is_paused(&self) -> bool {
174 self.paused.load(Ordering::SeqCst)
175 }
176
177 #[must_use]
179 pub fn now_ms(&self) -> u64 {
180 if !self.is_installed() {
181 return SystemTime::now()
182 .duration_since(UNIX_EPOCH)
183 .map(|d| d.as_millis() as u64)
184 .unwrap_or(0);
185 }
186
187 if self.is_paused() {
188 return self.current_ms.load(Ordering::SeqCst);
189 }
190
191 let real_now = SystemTime::now()
193 .duration_since(UNIX_EPOCH)
194 .map(|d| d.as_millis() as u64)
195 .unwrap_or(0);
196 let real_elapsed = real_now.saturating_sub(self.install_real_ms.load(Ordering::SeqCst));
197 let current = self.current_ms.load(Ordering::SeqCst);
198
199 current + real_elapsed
200 }
201
202 #[must_use]
204 pub fn now(&self) -> Duration {
205 Duration::from_millis(self.now_ms())
206 }
207
208 pub fn pause(&self) {
210 if self.is_installed() && !self.is_paused() {
211 let current = self.now_ms();
213 self.current_ms.store(current, Ordering::SeqCst);
214 self.paused.store(true, Ordering::SeqCst);
215 }
216 }
217
218 pub fn resume(&self) {
220 if self.is_installed() && self.is_paused() {
221 let real_now = SystemTime::now()
223 .duration_since(UNIX_EPOCH)
224 .map(|d| d.as_millis() as u64)
225 .unwrap_or(0);
226 self.install_real_ms.store(real_now, Ordering::SeqCst);
227 self.paused.store(false, Ordering::SeqCst);
228 }
229 }
230
231 pub fn set_fixed_time(&self, time_ms: u64) {
233 self.current_ms.store(time_ms, Ordering::SeqCst);
234 self.paused.store(true, Ordering::SeqCst);
235 }
236
237 pub fn set_fixed_time_iso(&self, iso: &str) -> Result<(), ClockError> {
243 let time_ms = parse_iso_to_ms(iso)?;
244 self.set_fixed_time(time_ms);
245 Ok(())
246 }
247
248 pub fn fast_forward(&self, duration: Duration) {
250 let current = self.current_ms.load(Ordering::SeqCst);
251 let new_time = current + duration.as_millis() as u64;
252 self.current_ms.store(new_time, Ordering::SeqCst);
253 }
254
255 pub fn fast_forward_ms(&self, ms: u64) {
257 self.fast_forward(Duration::from_millis(ms));
258 }
259
260 pub fn pause_at(&self, time_ms: u64) {
262 self.current_ms.store(time_ms, Ordering::SeqCst);
263 self.paused.store(true, Ordering::SeqCst);
264 }
265
266 #[must_use]
268 pub fn state(&self) -> ClockState {
269 if !self.is_installed() {
270 ClockState::System
271 } else if self.is_paused() {
272 ClockState::Paused
273 } else {
274 ClockState::Running
275 }
276 }
277}
278
279impl Default for FakeClock {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285impl Clone for FakeClock {
286 fn clone(&self) -> Self {
287 Self {
288 current_ms: AtomicU64::new(self.current_ms.load(Ordering::SeqCst)),
289 paused: AtomicBool::new(self.paused.load(Ordering::SeqCst)),
290 installed: AtomicBool::new(self.installed.load(Ordering::SeqCst)),
291 install_real_ms: AtomicU64::new(self.install_real_ms.load(Ordering::SeqCst)),
292 install_fake_ms: AtomicU64::new(self.install_fake_ms.load(Ordering::SeqCst)),
293 }
294 }
295}
296
297pub type Clock = Arc<FakeClock>;
299
300#[must_use]
302pub fn create_clock() -> Clock {
303 Arc::new(FakeClock::new())
304}
305
306fn parse_iso_to_ms(iso: &str) -> Result<u64, ClockError> {
308 let iso = iso.trim().trim_end_matches('Z');
314
315 let parts: Vec<&str> = if iso.contains('T') {
316 iso.split('T').collect()
317 } else {
318 vec![iso, "00:00:00"]
319 };
320
321 if parts.is_empty() {
322 return Err(ClockError::InvalidFormat(iso.to_string()));
323 }
324
325 let date_parts: Vec<u32> = parts[0]
326 .split('-')
327 .map(|s| s.parse().unwrap_or(0))
328 .collect();
329
330 if date_parts.len() < 3 {
331 return Err(ClockError::InvalidFormat(iso.to_string()));
332 }
333
334 let year = date_parts[0];
335 let month = date_parts[1];
336 let day = date_parts[2];
337
338 let (hour, minute, second) = if parts.len() > 1 {
339 let time_parts: Vec<u32> = parts[1]
340 .split(':')
341 .map(|s| s.parse().unwrap_or(0))
342 .collect();
343 (
344 *time_parts.first().unwrap_or(&0),
345 *time_parts.get(1).unwrap_or(&0),
346 *time_parts.get(2).unwrap_or(&0),
347 )
348 } else {
349 (0, 0, 0)
350 };
351
352 let days_since_epoch = days_since_unix_epoch(year, month, day);
354 let seconds = days_since_epoch * 86400
355 + u64::from(hour) * 3600
356 + u64::from(minute) * 60
357 + u64::from(second);
358
359 Ok(seconds * 1000)
360}
361
362fn days_since_unix_epoch(year: u32, month: u32, day: u32) -> u64 {
364 let mut days: i64 = 0;
365
366 for y in 1970..year {
368 days += if is_leap_year(y) { 366 } else { 365 };
369 }
370
371 let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
373 for m in 1..month {
374 days += i64::from(month_days[(m - 1) as usize]);
375 if m == 2 && is_leap_year(year) {
376 days += 1;
377 }
378 }
379
380 days += i64::from(day - 1);
382
383 days.max(0) as u64
384}
385
386fn is_leap_year(year: u32) -> bool {
388 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
389}
390
391#[derive(Debug, Clone)]
393pub struct ClockController {
394 clock: Clock,
395}
396
397impl ClockController {
398 #[must_use]
400 pub fn new() -> Self {
401 Self {
402 clock: create_clock(),
403 }
404 }
405
406 #[must_use]
408 pub fn with_clock(clock: Clock) -> Self {
409 Self { clock }
410 }
411
412 pub fn install(&self, options: ClockOptions) -> Result<(), ClockError> {
418 self.clock.install(options)
419 }
420
421 pub fn uninstall(&self) {
423 self.clock.uninstall();
424 }
425
426 pub fn fast_forward(&self, duration: Duration) {
428 self.clock.fast_forward(duration);
429 }
430
431 pub fn set_fixed_time(&self, time_ms: u64) {
433 self.clock.set_fixed_time(time_ms);
434 }
435
436 pub fn set_fixed_time_iso(&self, iso: &str) -> Result<(), ClockError> {
442 self.clock.set_fixed_time_iso(iso)
443 }
444
445 pub fn pause_at(&self, time_ms: u64) {
447 self.clock.pause_at(time_ms);
448 }
449
450 pub fn pause(&self) {
452 self.clock.pause();
453 }
454
455 pub fn resume(&self) {
457 self.clock.resume();
458 }
459
460 #[must_use]
462 pub fn now_ms(&self) -> u64 {
463 self.clock.now_ms()
464 }
465
466 #[must_use]
468 pub fn state(&self) -> ClockState {
469 self.clock.state()
470 }
471
472 #[must_use]
474 pub fn inner(&self) -> &Clock {
475 &self.clock
476 }
477}
478
479impl Default for ClockController {
480 fn default() -> Self {
481 Self::new()
482 }
483}
484
485#[cfg(test)]
486#[allow(clippy::unwrap_used, clippy::expect_used)]
487mod tests {
488 use super::*;
489
490 #[test]
495 fn h0_clock_01_new() {
496 let clock = FakeClock::new();
497 assert!(!clock.is_installed());
498 assert!(!clock.is_paused());
499 }
500
501 #[test]
502 fn h0_clock_02_state_system_when_not_installed() {
503 let clock = FakeClock::new();
504 assert_eq!(clock.state(), ClockState::System);
505 }
506
507 #[test]
512 fn h0_clock_03_install_success() {
513 let clock = FakeClock::new();
514 let options = ClockOptions::fixed(1_000_000);
515 clock.install(options).unwrap();
516
517 assert!(clock.is_installed());
518 assert!(clock.is_paused());
519 }
520
521 #[test]
522 fn h0_clock_04_install_already_installed() {
523 let clock = FakeClock::new();
524 clock.install(ClockOptions::now()).unwrap();
525
526 let result = clock.install(ClockOptions::now());
527 assert!(matches!(result, Err(ClockError::AlreadyInstalled)));
528 }
529
530 #[test]
531 fn h0_clock_05_uninstall() {
532 let clock = FakeClock::new();
533 clock.install(ClockOptions::now()).unwrap();
534 clock.uninstall();
535
536 assert!(!clock.is_installed());
537 }
538
539 #[test]
544 fn h0_clock_06_now_ms_when_paused() {
545 let clock = FakeClock::new();
546 clock
547 .install(ClockOptions::fixed(1_705_312_800_000))
548 .unwrap(); let time = clock.now_ms();
551 assert_eq!(time, 1_705_312_800_000);
552 }
553
554 #[test]
555 fn h0_clock_07_now_returns_duration() {
556 let clock = FakeClock::new();
557 clock.install(ClockOptions::fixed(1000)).unwrap();
558
559 let duration = clock.now();
560 assert_eq!(duration.as_millis(), 1000);
561 }
562
563 #[test]
568 fn h0_clock_08_fast_forward() {
569 let clock = FakeClock::new();
570 clock.install(ClockOptions::fixed(1000)).unwrap();
571
572 clock.fast_forward(Duration::from_secs(60));
573
574 assert_eq!(clock.now_ms(), 61_000);
575 }
576
577 #[test]
578 fn h0_clock_09_fast_forward_ms() {
579 let clock = FakeClock::new();
580 clock.install(ClockOptions::fixed(0)).unwrap();
581
582 clock.fast_forward_ms(5000);
583
584 assert_eq!(clock.now_ms(), 5000);
585 }
586
587 #[test]
592 fn h0_clock_10_pause() {
593 let clock = FakeClock::new();
594 clock
595 .install(ClockOptions {
596 time_ms: 1000,
597 paused: false,
598 })
599 .unwrap();
600
601 clock.pause();
602
603 assert!(clock.is_paused());
604 assert_eq!(clock.state(), ClockState::Paused);
605 }
606
607 #[test]
608 fn h0_clock_11_resume() {
609 let clock = FakeClock::new();
610 clock.install(ClockOptions::fixed(1000)).unwrap();
611
612 clock.resume();
613
614 assert!(!clock.is_paused());
615 assert_eq!(clock.state(), ClockState::Running);
616 }
617
618 #[test]
619 fn h0_clock_12_pause_at() {
620 let clock = FakeClock::new();
621 clock.install(ClockOptions::now()).unwrap();
622
623 clock.pause_at(5000);
624
625 assert!(clock.is_paused());
626 assert_eq!(clock.now_ms(), 5000);
627 }
628
629 #[test]
634 fn h0_clock_13_set_fixed_time() {
635 let clock = FakeClock::new();
636 clock.install(ClockOptions::now()).unwrap();
637
638 clock.set_fixed_time(9999);
639
640 assert!(clock.is_paused());
641 assert_eq!(clock.now_ms(), 9999);
642 }
643
644 #[test]
645 fn h0_clock_14_set_fixed_time_iso() {
646 let clock = FakeClock::new();
647 clock.install(ClockOptions::now()).unwrap();
648
649 clock.set_fixed_time_iso("2024-01-15T12:00:00Z").unwrap();
650
651 assert!(clock.is_paused());
652 assert!(clock.now_ms() > 1_705_000_000_000);
654 }
655
656 #[test]
661 fn h0_clock_15_options_now() {
662 let options = ClockOptions::now();
663 assert!(!options.paused);
664 assert!(options.time_ms > 0);
665 }
666
667 #[test]
668 fn h0_clock_16_options_fixed() {
669 let options = ClockOptions::fixed(1234);
670 assert!(options.paused);
671 assert_eq!(options.time_ms, 1234);
672 }
673
674 #[test]
675 fn h0_clock_17_options_from_iso() {
676 let options = ClockOptions::from_iso("2024-01-01T00:00:00Z").unwrap();
677 assert!(options.time_ms > 1_704_000_000_000);
678 }
679
680 #[test]
681 fn h0_clock_18_options_paused_builder() {
682 let options = ClockOptions::now().paused(true);
683 assert!(options.paused);
684 }
685
686 #[test]
691 fn h0_clock_19_parse_iso_full() {
692 let ms = parse_iso_to_ms("2024-01-15T10:30:00Z").unwrap();
693 assert!(ms > 1_705_000_000_000);
695 }
696
697 #[test]
698 fn h0_clock_20_parse_iso_date_only() {
699 let ms = parse_iso_to_ms("2024-01-15").unwrap();
700 assert!(ms > 1_705_000_000_000);
701 }
702
703 #[test]
704 fn h0_clock_21_parse_iso_invalid() {
705 let result = parse_iso_to_ms("invalid");
706 assert!(result.is_err());
707 }
708
709 #[test]
714 fn h0_clock_22_controller_new() {
715 let controller = ClockController::new();
716 assert_eq!(controller.state(), ClockState::System);
717 }
718
719 #[test]
720 fn h0_clock_23_controller_install() {
721 let controller = ClockController::new();
722 controller.install(ClockOptions::fixed(1000)).unwrap();
723
724 assert_eq!(controller.state(), ClockState::Paused);
725 assert_eq!(controller.now_ms(), 1000);
726 }
727
728 #[test]
729 fn h0_clock_24_controller_fast_forward() {
730 let controller = ClockController::new();
731 controller.install(ClockOptions::fixed(0)).unwrap();
732
733 controller.fast_forward(Duration::from_secs(30));
734
735 assert_eq!(controller.now_ms(), 30_000);
736 }
737
738 #[test]
739 fn h0_clock_25_controller_pause_resume() {
740 let controller = ClockController::new();
741 controller
742 .install(ClockOptions {
743 time_ms: 1000,
744 paused: false,
745 })
746 .unwrap();
747
748 controller.pause();
749 assert_eq!(controller.state(), ClockState::Paused);
750
751 controller.resume();
752 assert_eq!(controller.state(), ClockState::Running);
753 }
754
755 #[test]
760 fn h0_clock_26_clone() {
761 let clock = FakeClock::new();
762 clock.install(ClockOptions::fixed(5000)).unwrap();
763
764 let cloned = clock;
765
766 assert!(cloned.is_installed());
767 assert_eq!(cloned.now_ms(), 5000);
768 }
769
770 #[test]
775 fn h0_clock_27_is_leap_year() {
776 assert!(is_leap_year(2000));
777 assert!(is_leap_year(2024));
778 assert!(!is_leap_year(2023));
779 assert!(!is_leap_year(1900));
780 }
781
782 #[test]
787 fn h0_clock_28_error_display() {
788 let err = ClockError::InvalidFormat("bad".to_string());
789 assert!(err.to_string().contains("Invalid datetime"));
790
791 let err = ClockError::NotInstalled;
792 assert!(err.to_string().contains("not installed"));
793
794 let err = ClockError::AlreadyInstalled;
795 assert!(err.to_string().contains("already installed"));
796 }
797
798 #[test]
803 fn h0_clock_29_create_clock() {
804 let clock = create_clock();
805 assert!(!clock.is_installed());
806 }
807
808 #[test]
809 fn h0_clock_30_controller_with_clock() {
810 let clock = create_clock();
811 clock.install(ClockOptions::fixed(1234)).unwrap();
812
813 let controller = ClockController::with_clock(clock);
814 assert_eq!(controller.now_ms(), 1234);
815 }
816
817 #[test]
822 fn h0_clock_31_options_default() {
823 let options = ClockOptions::default();
824 assert!(options.time_ms > 0 || !options.paused);
827 }
828
829 #[test]
834 fn test_clock_controller_inner() {
835 let controller = ClockController::new();
836 let inner = controller.inner();
837 assert!(!inner.is_installed());
838 }
839
840 #[test]
841 fn test_clock_controller_uninstall() {
842 let controller = ClockController::new();
843 controller.install(ClockOptions::fixed(1000)).unwrap();
844 assert_eq!(controller.state(), ClockState::Paused);
845
846 controller.uninstall();
847 assert_eq!(controller.state(), ClockState::System);
848 }
849
850 #[test]
851 fn test_clock_controller_pause_at() {
852 let controller = ClockController::new();
853 controller.install(ClockOptions::now()).unwrap();
854
855 controller.pause_at(5000);
856 assert_eq!(controller.state(), ClockState::Paused);
857 assert_eq!(controller.now_ms(), 5000);
858 }
859
860 #[test]
861 fn test_clock_controller_set_fixed_time() {
862 let controller = ClockController::new();
863 controller.install(ClockOptions::now()).unwrap();
864
865 controller.set_fixed_time(9999);
866 assert_eq!(controller.now_ms(), 9999);
867 }
868
869 #[test]
870 fn test_clock_controller_set_fixed_time_iso() {
871 let controller = ClockController::new();
872 controller.install(ClockOptions::now()).unwrap();
873
874 controller
875 .set_fixed_time_iso("2024-06-15T12:30:00Z")
876 .unwrap();
877 assert!(controller.now_ms() > 1_718_000_000_000);
879 }
880
881 #[test]
882 fn test_clock_controller_set_fixed_time_iso_error() {
883 let controller = ClockController::new();
884 controller.install(ClockOptions::now()).unwrap();
885
886 let result = controller.set_fixed_time_iso("invalid");
887 assert!(result.is_err());
888 }
889
890 #[test]
891 fn test_fake_clock_now_ms_not_installed() {
892 let clock = FakeClock::new();
893 let time = clock.now_ms();
895 let system_time = SystemTime::now()
896 .duration_since(UNIX_EPOCH)
897 .map(|d| d.as_millis() as u64)
898 .unwrap_or(0);
899
900 assert!((time as i64 - system_time as i64).abs() < 1000);
902 }
903
904 #[test]
905 fn test_fake_clock_now_ms_running() {
906 let clock = FakeClock::new();
907 clock
908 .install(ClockOptions {
909 time_ms: 1000,
910 paused: false, })
912 .unwrap();
913
914 std::thread::sleep(std::time::Duration::from_millis(50));
916 let time = clock.now_ms();
917 assert!(time >= 1040);
919 }
920
921 #[test]
922 fn test_fake_clock_pause_not_installed() {
923 let clock = FakeClock::new();
924 clock.pause();
926 assert!(!clock.is_paused());
927 }
928
929 #[test]
930 fn test_fake_clock_resume_not_installed() {
931 let clock = FakeClock::new();
932 clock.resume();
934 assert!(!clock.is_paused());
935 }
936
937 #[test]
938 fn test_fake_clock_pause_already_paused() {
939 let clock = FakeClock::new();
940 clock.install(ClockOptions::fixed(1000)).unwrap(); clock.pause(); assert!(clock.is_paused());
944 assert_eq!(clock.now_ms(), 1000);
945 }
946
947 #[test]
948 fn test_fake_clock_resume_not_paused() {
949 let clock = FakeClock::new();
950 clock
951 .install(ClockOptions {
952 time_ms: 1000,
953 paused: false,
954 })
955 .unwrap();
956
957 clock.resume(); assert!(!clock.is_paused());
959 }
960
961 #[test]
962 fn test_fake_clock_clone() {
963 let clock = FakeClock::new();
964 clock.install(ClockOptions::fixed(5000)).unwrap();
965
966 let cloned = clock;
967 assert!(cloned.is_installed());
968 assert!(cloned.is_paused());
969 assert_eq!(cloned.now_ms(), 5000);
970 }
971
972 #[test]
973 fn test_clock_options_serialize_deserialize() {
974 let options = ClockOptions {
975 time_ms: 1234567890,
976 paused: true,
977 };
978
979 let json = serde_json::to_string(&options).unwrap();
980 let deserialized: ClockOptions = serde_json::from_str(&json).unwrap();
981
982 assert_eq!(deserialized.time_ms, 1234567890);
983 assert!(deserialized.paused);
984 }
985
986 #[test]
987 fn test_clock_state_serialize_deserialize() {
988 let state = ClockState::Paused;
989 let json = serde_json::to_string(&state).unwrap();
990 let deserialized: ClockState = serde_json::from_str(&json).unwrap();
991 assert_eq!(deserialized, ClockState::Paused);
992
993 let state2 = ClockState::Running;
994 let json2 = serde_json::to_string(&state2).unwrap();
995 let deserialized2: ClockState = serde_json::from_str(&json2).unwrap();
996 assert_eq!(deserialized2, ClockState::Running);
997 }
998
999 #[test]
1000 fn test_clock_error_display_invalid_format() {
1001 let err = ClockError::InvalidFormat("bad date".to_string());
1002 let display = err.to_string();
1003 assert!(display.contains("Invalid datetime format"));
1004 assert!(display.contains("bad date"));
1005 }
1006
1007 #[test]
1008 fn test_clock_error_is_error() {
1009 let err: &dyn std::error::Error = &ClockError::NotInstalled;
1010 assert!(err.to_string().contains("not installed"));
1011 }
1012
1013 #[test]
1014 fn test_parse_iso_date_only_without_t() {
1015 let ms = parse_iso_to_ms("2024-03-15").unwrap();
1016 assert!(ms > 1_710_000_000_000);
1018 }
1019
1020 #[test]
1021 fn test_parse_iso_with_trailing_z() {
1022 let ms1 = parse_iso_to_ms("2024-01-15T10:30:00Z").unwrap();
1023 let ms2 = parse_iso_to_ms("2024-01-15T10:30:00").unwrap();
1024 assert_eq!(ms1, ms2);
1025 }
1026
1027 #[test]
1028 fn test_parse_iso_with_whitespace() {
1029 let ms = parse_iso_to_ms(" 2024-01-15T10:30:00Z ").unwrap();
1030 assert!(ms > 1_705_000_000_000);
1031 }
1032
1033 #[test]
1034 fn test_parse_iso_partial_time() {
1035 let ms = parse_iso_to_ms("2024-01-15T10").unwrap();
1037 assert!(ms > 1_705_000_000_000);
1038 }
1039
1040 #[test]
1041 fn test_days_since_unix_epoch() {
1042 let days = days_since_unix_epoch(1970, 1, 1);
1044 assert_eq!(days, 0);
1045
1046 let days = days_since_unix_epoch(1970, 1, 2);
1048 assert_eq!(days, 1);
1049
1050 let days = days_since_unix_epoch(1971, 1, 1);
1052 assert_eq!(days, 365);
1053 }
1054
1055 #[test]
1056 fn test_days_since_unix_epoch_leap_year() {
1057 let days_2000 = days_since_unix_epoch(2000, 12, 31);
1059 let days_2001 = days_since_unix_epoch(2001, 1, 1);
1060 assert_eq!(days_2001 - days_2000, 1);
1061
1062 let start_2000 = days_since_unix_epoch(2000, 1, 1);
1064 let start_2001 = days_since_unix_epoch(2001, 1, 1);
1065 assert_eq!(start_2001 - start_2000, 366);
1066 }
1067
1068 #[test]
1069 fn test_is_leap_year_comprehensive() {
1070 assert!(is_leap_year(2020));
1072 assert!(is_leap_year(2024));
1073 assert!(is_leap_year(2028));
1074
1075 assert!(!is_leap_year(2019));
1077 assert!(!is_leap_year(2021));
1078 assert!(!is_leap_year(2023));
1079
1080 assert!(!is_leap_year(1900)); assert!(!is_leap_year(2100));
1083 assert!(is_leap_year(2000)); }
1085
1086 #[test]
1087 fn test_clock_controller_default() {
1088 let controller = ClockController::default();
1089 assert_eq!(controller.state(), ClockState::System);
1090 }
1091
1092 #[test]
1093 fn test_fake_clock_default() {
1094 let clock = FakeClock::default();
1095 assert!(!clock.is_installed());
1096 assert!(!clock.is_paused());
1097 }
1098
1099 #[test]
1100 fn test_fast_forward_preserves_paused_state() {
1101 let clock = FakeClock::new();
1102 clock.install(ClockOptions::fixed(1000)).unwrap();
1103 assert!(clock.is_paused());
1104
1105 clock.fast_forward_ms(500);
1106 assert_eq!(clock.now_ms(), 1500);
1107 assert!(clock.is_paused());
1109 }
1110
1111 #[test]
1112 fn test_set_fixed_time_pauses_clock() {
1113 let clock = FakeClock::new();
1114 clock
1115 .install(ClockOptions {
1116 time_ms: 1000,
1117 paused: false,
1118 })
1119 .unwrap();
1120 assert!(!clock.is_paused());
1121
1122 clock.set_fixed_time(5000);
1123 assert!(clock.is_paused());
1124 assert_eq!(clock.now_ms(), 5000);
1125 }
1126
1127 #[test]
1128 fn test_clock_state_equality() {
1129 assert_eq!(ClockState::Running, ClockState::Running);
1130 assert_eq!(ClockState::Paused, ClockState::Paused);
1131 assert_eq!(ClockState::System, ClockState::System);
1132 assert_ne!(ClockState::Running, ClockState::Paused);
1133 }
1134
1135 #[test]
1136 fn test_parse_iso_short_date() {
1137 let result = parse_iso_to_ms("2024-01");
1139 assert!(result.is_err());
1140 }
1141}