1pub use crate::shm::CLOCKBOUND_SHM_CLIENT_DEFAULT_PATH;
4pub use crate::shm::ClockStatus;
5use crate::shm::ShmReader;
6use crate::shm::{ClockBoundNowResult, ClockBoundSnapshot, ClockErrorBound, ShmError};
7pub use crate::vmclock::shm::VMCLOCK_SHM_DEFAULT_PATH;
8use crate::vmclock::shm_reader::VMClockShmReader;
9use errno::Errno;
10use std::ffi::CString;
11use std::path::Path;
12
13pub struct ClockBoundClient {
17 clockbound_shm: ClockBoundSHM,
18 vmclock_shm: VMClockSHM,
19}
20
21impl ClockBoundClient {
22 pub fn new() -> Result<ClockBoundClient, ClockBoundError> {
32 Self::new_with_path(CLOCKBOUND_SHM_CLIENT_DEFAULT_PATH)
33 }
34
35 pub fn new_with_path(clockbound_shm_path: &str) -> Result<ClockBoundClient, ClockBoundError> {
46 Self::new_with_paths(clockbound_shm_path, VMCLOCK_SHM_DEFAULT_PATH)
47 }
48
49 pub fn new_with_paths(
59 clockbound_shm_path: &str,
60 vmclock_shm_path: &str,
61 ) -> Result<ClockBoundClient, ClockBoundError> {
62 let mut clockbound_shm = ClockBoundSHM::new(clockbound_shm_path)?;
64
65 let cb_snapshot = clockbound_shm.snapshot()?;
68
69 let vmclock_shm = VMClockSHM::new(
71 vmclock_shm_path,
72 cb_snapshot.clock_disruption_support_enabled(),
73 )?;
74
75 Ok(ClockBoundClient {
76 clockbound_shm,
77 vmclock_shm,
78 })
79 }
80
81 pub fn now(&mut self) -> Result<ClockBoundNowResult, ClockBoundError> {
93 let cb_snap = self.clockbound_shm.snapshot()?;
96 let mut clock_bound_now_result = cb_snap.now()?;
97
98 if self.vmclock_shm.vmclock_shm_reader.is_none()
102 && cb_snap.clock_disruption_support_enabled()
103 {
104 self.vmclock_shm.vmclock_shm_reader = Some(VMClockShmReader::new(
105 self.vmclock_shm.vmclock_shm_path.as_str(),
106 )?);
107 }
108
109 let is_disrupted = match self.vmclock_shm.disruption_marker()? {
115 Some(marker) => marker != cb_snap.disruption_marker(),
116 None => false,
117 };
118
119 if is_disrupted {
121 clock_bound_now_result.clock_status = ClockStatus::Disrupted;
122 }
123
124 Ok(clock_bound_now_result)
125 }
126}
127
128struct ClockBoundSHM {
130 #[expect(dead_code)]
131 clockbound_shm_path: String,
132 clockbound_shm_reader: ShmReader,
133}
134
135impl ClockBoundSHM {
136 fn new(clockbound_shm_path: &str) -> Result<ClockBoundSHM, ClockBoundError> {
142 if !Path::new(clockbound_shm_path).exists() {
144 let detail = format!(
145 "Path to clockbound daemon shared memory segment does not exist: {clockbound_shm_path}"
146 );
147 let error = ClockBoundError {
148 kind: ClockBoundErrorKind::SegmentNotInitialized,
149 detail,
150 errno: Errno(0),
151 };
152 return Err(error);
153 }
154
155 let shm_path = CString::new(clockbound_shm_path).expect("CString::new failed");
156 let shm_reader = ShmReader::new(shm_path.as_c_str())?;
157
158 Ok(ClockBoundSHM {
159 clockbound_shm_path: String::from(clockbound_shm_path),
160 clockbound_shm_reader: shm_reader,
161 })
162 }
163
164 fn snapshot(&mut self) -> Result<&ClockErrorBound, ShmError> {
166 self.clockbound_shm_reader.snapshot()
167 }
168}
169
170struct VMClockSHM {
172 vmclock_shm_path: String,
173 vmclock_shm_reader: Option<VMClockShmReader>,
174}
175
176impl VMClockSHM {
177 fn new(
183 vmclock_shm_path: &str,
184 clock_disruption_support_enabled: bool,
185 ) -> Result<VMClockSHM, ClockBoundError> {
186 let mut vmclock_shm_reader: Option<VMClockShmReader> = None;
189 if clock_disruption_support_enabled {
190 if !Path::new(vmclock_shm_path).exists() {
192 let detail = format!(
193 "Path to VMClock device shared memory segment does not exist: {vmclock_shm_path}"
194 );
195 let error = ClockBoundError {
196 kind: ClockBoundErrorKind::SegmentNotInitialized,
197 detail,
198 errno: Errno(0),
199 };
200 return Err(error);
201 }
202 vmclock_shm_reader = Some(VMClockShmReader::new(vmclock_shm_path)?);
203 }
204
205 Ok(VMClockSHM {
206 vmclock_shm_path: String::from(vmclock_shm_path),
207 vmclock_shm_reader,
208 })
209 }
210
211 fn disruption_marker(&mut self) -> Result<Option<u64>, ShmError> {
219 if let Some(ref mut vmclock_shm_reader) = self.vmclock_shm_reader {
220 let snap = vmclock_shm_reader.snapshot()?;
221 return Ok(Some(snap.disruption_marker));
222 }
223
224 Ok(None)
226 }
227}
228
229#[derive(Debug)]
230pub struct ClockBoundError {
231 pub kind: ClockBoundErrorKind,
232 pub errno: Errno,
233 pub detail: String,
234}
235
236impl From<ShmError> for ClockBoundError {
237 fn from(value: ShmError) -> Self {
238 let (kind, detail, errno) = match value {
239 ShmError::SyscallError(detail, errno) => (ClockBoundErrorKind::Syscall, detail, errno),
240 ShmError::SegmentNotInitialized(detail) => {
241 (ClockBoundErrorKind::SegmentNotInitialized, detail, Errno(0))
242 }
243 ShmError::SegmentMalformed(detail) => {
244 (ClockBoundErrorKind::SegmentMalformed, detail, Errno(0))
245 }
246 ShmError::CausalityBreach(detail) => {
247 (ClockBoundErrorKind::CausalityBreach, detail, Errno(0))
248 }
249 ShmError::SegmentVersionNotSupported(detail) => (
250 ClockBoundErrorKind::SegmentVersionNotSupported,
251 detail,
252 Errno(0),
253 ),
254 };
255
256 ClockBoundError {
257 kind,
258 errno,
259 detail,
260 }
261 }
262}
263
264#[derive(Hash, PartialEq, Eq, Clone, Debug)]
265pub enum ClockBoundErrorKind {
266 Syscall,
269 SegmentNotInitialized,
270 SegmentMalformed,
271 CausalityBreach,
272 SegmentVersionNotSupported,
273}
274
275#[cfg(test)]
276mod lib_tests {
277 use super::*;
278 use crate::shm::{ClockErrorBound, ShmWrite, ShmWriter};
279 use crate::shm::{ClockErrorBoundGeneric, ClockErrorBoundLayoutVersion};
280 use crate::vmclock::shm::{VMClockClockStatus, VMClockShmBody};
281 use crate::vmclock::shm_writer::{VMClockShmWrite, VMClockShmWriter};
282
283 use byteorder::{NativeEndian, WriteBytesExt};
284 use nix::sys::time::TimeSpec;
285 use std::fs::{File, OpenOptions};
286 use std::io::Write;
287 use std::path::Path;
288 use tempfile::NamedTempFile;
292
293 macro_rules! write_clockbound_memory_segment {
297 ($file:ident,
298 $magic_0:literal,
299 $magic_1:literal,
300 $segsize:literal,
301 $version:literal,
302 $generation:literal) => {
303 let ceb = ClockErrorBoundGeneric::builder()
305 .clock_disruption_support_enabled(true)
306 .build(ClockErrorBoundLayoutVersion::V2);
307
308 let slice = unsafe {
311 ::core::slice::from_raw_parts(
312 (&ceb as *const ClockErrorBound) as *const u8,
313 ::core::mem::size_of::<ClockErrorBound>(),
314 )
315 };
316
317 $file
318 .write_u32::<NativeEndian>($magic_0)
319 .expect("Write failed magic_0");
320 $file
321 .write_u32::<NativeEndian>($magic_1)
322 .expect("Write failed magic_1");
323 $file
324 .write_u32::<NativeEndian>($segsize)
325 .expect("Write failed segsize");
326 $file
327 .write_u16::<NativeEndian>($version)
328 .expect("Write failed version");
329 $file
330 .write_u16::<NativeEndian>($generation)
331 .expect("Write failed generation");
332 $file
333 .write_all(slice)
334 .expect("Write failed ClockErrorBound");
335 $file.sync_all().expect("Sync to disk failed");
336 };
337 }
338
339 macro_rules! vmclockshmbody {
340 () => {
341 VMClockShmBody {
342 disruption_marker: 10,
343 flags: 0_u64,
344 _padding: [0x00, 0x00],
345 clock_status: VMClockClockStatus::Unknown,
346 leap_second_smearing_hint: 0,
347 tai_offset_sec: 37_i16,
348 leap_indicator: 0,
349 counter_period_shift: 0,
350 counter_value: 0,
351 counter_period_frac_sec: 0,
352 counter_period_esterror_rate_frac_sec: 0,
353 counter_period_maxerror_rate_frac_sec: 0,
354 time_sec: 0,
355 time_frac_sec: 0,
356 time_esterror_nanosec: 0,
357 time_maxerror_nanosec: 0,
358 }
359 };
360 }
361
362 #[repr(C)]
364 #[derive(Debug, Copy, Clone, PartialEq)]
365 struct VMClockContent {
366 magic: u32,
367 size: u32,
368 version: u16,
369 counter_id: u8,
370 time_type: u8,
371 seq_count: u32,
372 disruption_marker: u64,
373 flags: u64,
374 _padding: [u8; 2],
375 clock_status: VMClockClockStatus,
376 leap_second_smearing_hint: u8,
377 tai_offset_sec: i16,
378 leap_indicator: u8,
379 counter_period_shift: u8,
380 counter_value: u64,
381 counter_period_frac_sec: u64,
382 counter_period_esterror_rate_frac_sec: u64,
383 counter_period_maxerror_rate_frac_sec: u64,
384 time_sec: u64,
385 time_frac_sec: u64,
386 time_esterror_nanosec: u64,
387 time_maxerror_nanosec: u64,
388 }
389
390 fn write_vmclock_content(file: &mut File, vmclock_content: &VMClockContent) {
391 let slice = unsafe {
394 ::core::slice::from_raw_parts(
395 (vmclock_content as *const VMClockContent) as *const u8,
396 ::core::mem::size_of::<VMClockContent>(),
397 )
398 };
399
400 file.write_all(slice).expect("Write failed VMClockContent");
401 file.sync_all().expect("Sync to disk failed");
402 }
403
404 fn remove_file_or_directory(path: &str) {
406 let p = Path::new(&path);
408 while p.exists() {
409 if p.is_dir() {
410 std::fs::remove_dir_all(&path).expect("failed to remove file");
411 } else {
412 std::fs::remove_file(&path).expect("failed to remove file");
413 }
414 }
415 }
416
417 #[test]
420 fn test_vmclock_now_with_clock_disruption_support_enabled_success() {
421 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
422 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
423 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
424 remove_file_or_directory(&vmclock_shm_path);
425
426 let vmclock_shm_body = vmclockshmbody!();
428 let mut vmclock_shm_writer = VMClockShmWriter::new(Path::new(&vmclock_shm_path))
429 .expect("Failed to create a VMClockShmWriter");
430 vmclock_shm_writer.write(&vmclock_shm_body);
431
432 let vmclock_new_result = VMClockSHM::new(&vmclock_shm_path, true);
434 match vmclock_new_result {
435 Ok(mut vmclock) => {
436 let marker_result = vmclock.disruption_marker();
438 assert!(marker_result.is_ok());
439 assert!(marker_result.unwrap() == Some(10_u64));
440 }
441 Err(_) => {
442 assert!(false);
443 }
444 }
445 }
446
447 #[test]
450 fn test_vmclock_now_with_clock_disruption_support_enabled_failure() {
451 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
452 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
453 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
454 remove_file_or_directory(&vmclock_shm_path);
455
456 let vmclock_new_result = VMClockSHM::new(&vmclock_shm_path, true);
458 assert!(vmclock_new_result.is_err());
459 }
460
461 #[test]
464 fn test_vmclock_now_with_clock_disruption_support_not_enabled() {
465 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
466 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
467 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
468 remove_file_or_directory(&vmclock_shm_path);
469
470 let vmclock_new_result = VMClockSHM::new(&vmclock_shm_path, false);
473 match vmclock_new_result {
474 Ok(mut vmclock) => {
475 let marker_result = vmclock.disruption_marker();
477 assert!(marker_result.is_ok());
478 assert!(marker_result.unwrap() == None)
479 }
480 Err(_) => {
481 assert!(false);
482 }
483 }
484 }
485
486 #[test]
487 fn test_new_with_path_does_not_exist() {
488 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
489 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
490 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
491 remove_file_or_directory(clockbound_shm_path);
492 let result = ClockBoundClient::new_with_path(clockbound_shm_path);
493 assert!(result.is_err());
494 }
495
496 #[test]
498 fn test_new_with_paths_sanity_check() {
499 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
500 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
501 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
502 let mut clockbound_shm_file = OpenOptions::new()
503 .write(true)
504 .open(clockbound_shm_path)
505 .expect("open clockbound file failed");
506 write_clockbound_memory_segment!(
507 clockbound_shm_file,
508 0x414D5A4E,
509 0x43420200,
510 800,
511 0x0303,
512 10
513 );
514
515 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
516 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
517 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
518 let mut vmclock_shm_file = OpenOptions::new()
519 .write(true)
520 .open(vmclock_shm_path)
521 .expect("open vmclock file failed");
522 let vmclock_content = VMClockContent {
523 magic: 0x4B4C4356,
524 size: 104_u32,
525 version: 1_u16,
526 counter_id: 1_u8,
527 time_type: 0_u8,
528 seq_count: 10_u32,
529 disruption_marker: 888888_u64,
530 flags: 0_u64,
531 _padding: [0x00, 0x00],
532 clock_status: VMClockClockStatus::Synchronized,
533 leap_second_smearing_hint: 0_u8,
534 tai_offset_sec: 0_i16,
535 leap_indicator: 0_u8,
536 counter_period_shift: 0_u8,
537 counter_value: 123456_u64,
538 counter_period_frac_sec: 0_u64,
539 counter_period_esterror_rate_frac_sec: 0_u64,
540 counter_period_maxerror_rate_frac_sec: 0_u64,
541 time_sec: 0_u64,
542 time_frac_sec: 0_u64,
543 time_esterror_nanosec: 0_u64,
544 time_maxerror_nanosec: 0_u64,
545 };
546 write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
547
548 let mut clockbound =
549 match ClockBoundClient::new_with_paths(clockbound_shm_path, vmclock_shm_path) {
550 Ok(c) => c,
551 Err(e) => {
552 eprintln!("{:?}", e);
553 panic!("ClockBoundClient::new_with_paths() failed");
554 }
555 };
556
557 let now_result = match clockbound.now() {
558 Ok(result) => result,
559 Err(e) => {
560 eprintln!("{:?}", e);
561 panic!("ClockBoundClient::now() failed");
562 }
563 };
564
565 assert_eq!(now_result.clock_status, ClockStatus::Disrupted);
566 }
567
568 #[test]
569 fn test_new_with_paths_does_not_exist() {
570 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
572 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
573 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
574 remove_file_or_directory(clockbound_shm_path);
575 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
576 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
577 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
578 remove_file_or_directory(vmclock_shm_path);
579 let result = ClockBoundClient::new_with_paths(clockbound_shm_path, vmclock_shm_path);
580 assert!(result.is_err());
581
582 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
584 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
585 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
586 let mut clockbound_shm_file = OpenOptions::new()
587 .write(true)
588 .open(clockbound_shm_path)
589 .expect("open clockbound file failed");
590 write_clockbound_memory_segment!(clockbound_shm_file, 0x414D5A4E, 0x43420200, 800, 2, 10);
591 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
592 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
593 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
594 remove_file_or_directory(vmclock_shm_path);
595 let result = ClockBoundClient::new_with_paths(clockbound_shm_path, vmclock_shm_path);
596 assert!(result.is_err());
597 remove_file_or_directory(clockbound_shm_path);
598
599 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
601 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
602 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
603 remove_file_or_directory(clockbound_shm_path);
604 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
605 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
606 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
607 let mut vmclock_shm_file = OpenOptions::new()
608 .write(true)
609 .open(vmclock_shm_path)
610 .expect("open vmclock file failed");
611 let vmclock_content = VMClockContent {
612 magic: 0x4B4C4356,
613 size: 104_u32,
614 version: 1_u16,
615 counter_id: 1_u8,
616 time_type: 0_u8,
617 seq_count: 10_u32,
618 disruption_marker: 888888_u64,
619 flags: 0_u64,
620 _padding: [0x00, 0x00],
621 clock_status: VMClockClockStatus::Synchronized,
622 leap_second_smearing_hint: 0_u8,
623 tai_offset_sec: 0_i16,
624 leap_indicator: 0_u8,
625 counter_period_shift: 0_u8,
626 counter_value: 123456_u64,
627 counter_period_frac_sec: 0_u64,
628 counter_period_esterror_rate_frac_sec: 0_u64,
629 counter_period_maxerror_rate_frac_sec: 0_u64,
630 time_sec: 0_u64,
631 time_frac_sec: 0_u64,
632 time_esterror_nanosec: 0_u64,
633 time_maxerror_nanosec: 0_u64,
634 };
635 write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
636
637 let result = ClockBoundClient::new_with_paths(clockbound_shm_path, vmclock_shm_path);
638 assert!(result.is_err());
639 }
640
641 #[test]
646 #[ignore = "can fail if daemon has run previously with root privs"]
647 fn test_new_sanity_check() {
648 let result = ClockBoundClient::new();
649 if Path::new(CLOCKBOUND_SHM_CLIENT_DEFAULT_PATH).exists() {
650 assert!(result.is_ok());
651 } else {
652 assert!(result.is_err());
653 }
654 }
655
656 #[test]
657 #[ignore = "daemon version mismatch"]
660 fn test_now_clock_error_bound_now_error() {
661 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
662 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
663 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
664 let mut clockbound_shm_file = OpenOptions::new()
665 .write(true)
666 .open(clockbound_shm_path)
667 .expect("open clockbound file failed");
668 write_clockbound_memory_segment!(
670 clockbound_shm_file,
671 0x414D5A4E,
672 0x43420200,
673 800,
674 0x0002,
675 10
676 );
677
678 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
679 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
680 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
681 let mut vmclock_shm_file = OpenOptions::new()
682 .write(true)
683 .open(vmclock_shm_path)
684 .expect("open vmclock file failed");
685 let vmclock_content = VMClockContent {
686 magic: 0x4B4C4356,
687 size: 104_u32,
688 version: 1_u16,
689 counter_id: 1_u8,
690 time_type: 0_u8,
691 seq_count: 10_u32,
692 disruption_marker: 888888_u64,
693 flags: 0_u64,
694 _padding: [0x00, 0x00],
695 clock_status: VMClockClockStatus::Synchronized,
696 leap_second_smearing_hint: 0_u8,
697 tai_offset_sec: 0_i16,
698 leap_indicator: 0_u8,
699 counter_period_shift: 0_u8,
700 counter_value: 123456_u64,
701 counter_period_frac_sec: 0_u64,
702 counter_period_esterror_rate_frac_sec: 0_u64,
703 counter_period_maxerror_rate_frac_sec: 0_u64,
704 time_sec: 0_u64,
705 time_frac_sec: 0_u64,
706 time_esterror_nanosec: 0_u64,
707 time_maxerror_nanosec: 0_u64,
708 };
709 write_vmclock_content(&mut vmclock_shm_file, &vmclock_content);
710
711 let mut writer = ShmWriter::new(
712 Path::new(clockbound_shm_path),
713 ClockErrorBoundLayoutVersion::V2,
714 ClockErrorBoundLayoutVersion::V2,
715 )
716 .expect("Failed to create a writer");
717
718 let ceb = ClockErrorBoundGeneric::builder().build(ClockErrorBoundLayoutVersion::V3);
719 writer.write(&ceb);
720
721 let mut clockbound =
722 match ClockBoundClient::new_with_paths(clockbound_shm_path, vmclock_shm_path) {
723 Ok(c) => c,
724 Err(e) => {
725 eprintln!("{:?}", e);
726 panic!("ClockBoundClient::new_with_paths() failed");
727 }
728 };
729
730 let now_result = clockbound.now();
732 assert!(now_result.is_ok());
733
734 let ceb = ClockErrorBoundGeneric::builder()
737 .as_of(TimeSpec::new(100, 0))
738 .void_after(TimeSpec::new(10, 0))
739 .max_drift_ppb(1_000_000_000)
740 .clock_status(ClockStatus::Synchronized)
741 .clock_disruption_support_enabled(true)
742 .build(ClockErrorBoundLayoutVersion::V3);
743 writer.write(&ceb);
744
745 let now_result = clockbound.now();
747 assert!(now_result.is_err());
748 }
749
750 #[test]
753 fn test_shmerror_clockbounderror_conversion_syscallerror() {
754 let errno = Errno(1);
755 let detail = String::from("test detail");
756 let shm_error = ShmError::SyscallError(detail.clone(), errno);
757 let clockbounderror = ClockBoundError::from(shm_error);
759 assert_eq!(ClockBoundErrorKind::Syscall, clockbounderror.kind);
760 assert_eq!(errno, clockbounderror.errno);
761 assert_eq!(detail, clockbounderror.detail);
762 }
763
764 #[test]
765 fn test_shmerror_clockbounderror_conversion_segmentnotinitialized() {
766 let detail = String::from("test detail");
767 let shm_error = ShmError::SegmentNotInitialized(detail.clone());
768 let clockbounderror = ClockBoundError::from(shm_error);
770 assert_eq!(
771 ClockBoundErrorKind::SegmentNotInitialized,
772 clockbounderror.kind
773 );
774 assert_eq!(Errno(0), clockbounderror.errno);
775 assert_eq!(detail, clockbounderror.detail);
776 }
777
778 #[test]
779 fn test_shmerror_clockbounderror_conversion_segmentmalformed() {
780 let detail = String::from("test detail");
781 let shm_error = ShmError::SegmentMalformed(detail.clone());
782 let clockbounderror = ClockBoundError::from(shm_error);
784 assert_eq!(ClockBoundErrorKind::SegmentMalformed, clockbounderror.kind);
785 assert_eq!(Errno(0), clockbounderror.errno);
786 assert_eq!(detail, clockbounderror.detail);
787 }
788
789 #[test]
790 fn test_shmerror_clockbounderror_conversion_causalitybreach() {
791 let detail = String::from("test detail");
792 let shm_error = ShmError::CausalityBreach(detail.clone());
793 let clockbounderror = ClockBoundError::from(shm_error);
795 assert_eq!(ClockBoundErrorKind::CausalityBreach, clockbounderror.kind);
796 assert_eq!(Errno(0), clockbounderror.errno);
797 assert_eq!(detail, clockbounderror.detail);
798 }
799}