1use std::{collections::HashSet, fmt, path::PathBuf, str::FromStr};
6
7use crate::{
8 core::{DevId, Device, DeviceInfo, DmName, DmOptions, DmUuid, DM},
9 result::{DmError, DmResult, ErrorEnum},
10 shared::{
11 device_create, device_exists, device_match, parse_device, parse_value, DmDevice,
12 TargetLine, TargetParams, TargetTable, TargetTypeBuf,
13 },
14 units::Sectors,
15};
16
17const FLAKEY_TARGET_NAME: &str = "flakey";
18const LINEAR_TARGET_NAME: &str = "linear";
19
20#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct LinearTargetParams {
23 pub device: Device,
25 pub start_offset: Sectors,
27}
28
29impl LinearTargetParams {
30 pub fn new(device: Device, start_offset: Sectors) -> LinearTargetParams {
32 LinearTargetParams {
33 device,
34 start_offset,
35 }
36 }
37}
38
39impl fmt::Display for LinearTargetParams {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "{} {}", LINEAR_TARGET_NAME, self.param_str())
42 }
43}
44
45impl FromStr for LinearTargetParams {
46 type Err = DmError;
47
48 fn from_str(s: &str) -> DmResult<LinearTargetParams> {
49 let vals = s.split(' ').collect::<Vec<_>>();
50 if vals.len() != 3 {
51 let err_msg = format!(
52 "expected 3 values in params string \"{}\", found {}",
53 s,
54 vals.len()
55 );
56 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
57 }
58
59 if vals[0] != LINEAR_TARGET_NAME {
60 let err_msg = format!(
61 "Expected a linear target entry but found target type {}",
62 vals[0]
63 );
64 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
65 }
66
67 let device = parse_device(vals[1], "block device for linear target")?;
68 let start = Sectors(parse_value(vals[2], "physical start offset")?);
69
70 Ok(LinearTargetParams::new(device, start))
71 }
72}
73
74impl TargetParams for LinearTargetParams {
75 fn param_str(&self) -> String {
76 format!("{} {}", self.device, *self.start_offset)
77 }
78
79 fn target_type(&self) -> TargetTypeBuf {
80 TargetTypeBuf::new(LINEAR_TARGET_NAME.into()).expect("LINEAR_TARGET_NAME is valid")
81 }
82}
83
84#[derive(Debug, Hash, Clone, Eq, PartialEq)]
85pub enum Direction {
86 Reads,
87 Writes,
88}
89
90impl fmt::Display for Direction {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Direction::Reads => write!(f, "r"),
94 Direction::Writes => write!(f, "w"),
95 }
96 }
97}
98
99impl FromStr for Direction {
100 type Err = DmError;
101 fn from_str(s: &str) -> DmResult<Direction> {
102 if s == "r" {
103 Ok(Direction::Reads)
104 } else if s == "w" {
105 Ok(Direction::Writes)
106 } else {
107 let err_msg = format!("Expected r or w, found {s}");
108 Err(DmError::Dm(ErrorEnum::Invalid, err_msg))
109 }
110 }
111}
112
113#[derive(Debug, Hash, Clone, Eq, PartialEq)]
117pub enum FlakeyFeatureArg {
118 DropWrites,
123 ErrorWrites,
128 CorruptBioByte(u64, Direction, u8, u64),
141}
142
143impl fmt::Display for FlakeyFeatureArg {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 match self {
146 FlakeyFeatureArg::DropWrites => write!(f, "drop_writes"),
147 FlakeyFeatureArg::ErrorWrites => write!(f, "error_writes"),
148 FlakeyFeatureArg::CorruptBioByte(offset, direction, value, flags) => {
149 write!(f, "corrupt_bio_byte {offset} {direction} {value} {flags}")
150 }
151 }
152 }
153}
154
155#[derive(Clone, Debug, Eq, PartialEq)]
157pub struct FlakeyTargetParams {
158 pub device: Device,
160 pub start_offset: Sectors,
162 pub up_interval: u32,
165 pub down_interval: u32,
168 pub feature_args: HashSet<FlakeyFeatureArg>,
170}
171
172impl FlakeyTargetParams {
173 pub fn new(
175 device: Device,
176 start_offset: Sectors,
177 up_interval: u32,
178 down_interval: u32,
179 feature_args: Vec<FlakeyFeatureArg>,
180 ) -> FlakeyTargetParams {
181 FlakeyTargetParams {
182 device,
183 start_offset,
184 up_interval,
185 down_interval,
186 feature_args: feature_args.into_iter().collect::<HashSet<_>>(),
187 }
188 }
189}
190
191impl fmt::Display for FlakeyTargetParams {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 write!(f, "{} {}", FLAKEY_TARGET_NAME, self.param_str())
211 }
212}
213
214impl FromStr for FlakeyTargetParams {
215 type Err = DmError;
216
217 fn from_str(s: &str) -> DmResult<FlakeyTargetParams> {
218 fn parse_feature_args(vals: &[&str]) -> DmResult<Vec<FlakeyFeatureArg>> {
219 let mut vals_iter = vals.iter();
220 let mut result: Vec<FlakeyFeatureArg> = Vec::new();
221 while let Some(x) = vals_iter.next() {
222 match x {
223 &"drop_writes" => result.push(FlakeyFeatureArg::DropWrites),
224 &"error_writes" => result.push(FlakeyFeatureArg::ErrorWrites),
225 &"corrupt_bio_byte" => {
226 let offset = vals_iter
227 .next()
228 .ok_or({
229 let err_msg = "corrupt_bio_byte takes 4 parameters";
230 DmError::Dm(ErrorEnum::Invalid, err_msg.to_string())
231 })
232 .and_then(|s| parse_value::<u64>(s, "offset"))?;
233
234 let direction = vals_iter
235 .next()
236 .ok_or({
237 let err_msg = "corrupt_bio_byte takes 4 parameters";
238 DmError::Dm(ErrorEnum::Invalid, err_msg.to_string())
239 })
240 .and_then(|s| parse_value::<Direction>(s, "direction"))?;
241
242 let value = vals_iter
243 .next()
244 .ok_or({
245 let err_msg = "corrupt_bio_byte takes 4 parameters";
246 DmError::Dm(ErrorEnum::Invalid, err_msg.to_string())
247 })
248 .and_then(|s| parse_value::<u8>(s, "value"))?;
249
250 let flags = vals_iter
251 .next()
252 .ok_or({
253 let err_msg = "corrupt_bio_byte takes 4 parameters";
254 DmError::Dm(ErrorEnum::Invalid, err_msg.to_string())
255 })
256 .and_then(|s| parse_value::<u64>(s, "flags"))?;
257
258 result.push(FlakeyFeatureArg::CorruptBioByte(
259 offset, direction, value, flags,
260 ));
261 }
262 x => {
263 let err_msg = format!("{x} is an unrecognized feature parameter");
264 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
265 }
266 }
267 }
268
269 Ok(result)
270 }
271
272 let vals = s.split(' ').collect::<Vec<_>>();
273
274 if vals.len() < 5 {
275 let err_msg = format!(
276 "expected at least five values in params string \"{}\", found {}",
277 s,
278 vals.len()
279 );
280 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
281 }
282
283 if vals[0] != FLAKEY_TARGET_NAME {
284 let err_msg = format!(
285 "Expected a flakey target entry but found target type {}",
286 vals[0]
287 );
288 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
289 }
290
291 let device = parse_device(vals[1], "block device for flakey target")?;
292 let start_offset = Sectors(parse_value(vals[2], "physical start offset")?);
293
294 let up_interval = parse_value(vals[3], "up interval")?;
295 let down_interval = parse_value(vals[4], "down interval")?;
296
297 let feature_args = if vals.len() == 5 {
298 vec![]
299 } else {
300 parse_feature_args(
301 &vals[6..6 + parse_value::<usize>(vals[5], "number of feature args")?],
302 )?
303 };
304
305 Ok(FlakeyTargetParams::new(
306 device,
307 start_offset,
308 up_interval,
309 down_interval,
310 feature_args,
311 ))
312 }
313}
314
315impl TargetParams for FlakeyTargetParams {
316 fn param_str(&self) -> String {
317 let feature_args = if self.feature_args.is_empty() {
318 "0".to_owned()
319 } else {
320 format!(
321 "{} {}",
322 self.feature_args.len(),
323 self.feature_args
324 .iter()
325 .map(|x| x.to_string())
326 .collect::<Vec<_>>()
327 .join(" ")
328 )
329 };
330
331 format!(
332 "{} {} {} {} {}",
333 self.device, *self.start_offset, self.up_interval, self.down_interval, feature_args
334 )
335 }
336
337 fn target_type(&self) -> TargetTypeBuf {
338 TargetTypeBuf::new(FLAKEY_TARGET_NAME.into()).expect("FLAKEY_TARGET_NAME is valid")
339 }
340}
341
342#[derive(Clone, Debug, Eq, PartialEq)]
344pub enum LinearDevTargetParams {
345 Flakey(FlakeyTargetParams),
347 Linear(LinearTargetParams),
349}
350
351impl fmt::Display for LinearDevTargetParams {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 match *self {
354 LinearDevTargetParams::Flakey(ref flakey) => flakey.fmt(f),
355 LinearDevTargetParams::Linear(ref linear) => linear.fmt(f),
356 }
357 }
358}
359
360impl FromStr for LinearDevTargetParams {
361 type Err = DmError;
362
363 fn from_str(s: &str) -> DmResult<LinearDevTargetParams> {
364 let target_type = Some(s.split_once(' ').map_or(s, |x| x.0)).ok_or_else(|| {
365 DmError::Dm(
366 ErrorEnum::Invalid,
367 format!("target line string \"{s}\" did not contain any values"),
368 )
369 })?;
370 if target_type == FLAKEY_TARGET_NAME {
371 Ok(LinearDevTargetParams::Flakey(
372 s.parse::<FlakeyTargetParams>()?,
373 ))
374 } else if target_type == LINEAR_TARGET_NAME {
375 Ok(LinearDevTargetParams::Linear(
376 s.parse::<LinearTargetParams>()?,
377 ))
378 } else {
379 Err(DmError::Dm(
380 ErrorEnum::Invalid,
381 format!("unexpected target type \"{target_type}\""),
382 ))
383 }
384 }
385}
386
387impl TargetParams for LinearDevTargetParams {
388 fn param_str(&self) -> String {
389 match *self {
390 LinearDevTargetParams::Flakey(ref flakey) => flakey.param_str(),
391 LinearDevTargetParams::Linear(ref linear) => linear.param_str(),
392 }
393 }
394
395 fn target_type(&self) -> TargetTypeBuf {
396 match *self {
397 LinearDevTargetParams::Flakey(ref flakey) => flakey.target_type(),
398 LinearDevTargetParams::Linear(ref linear) => linear.target_type(),
399 }
400 }
401}
402
403#[derive(Clone, Debug, Eq, PartialEq)]
406pub struct LinearDevTargetTable {
407 pub table: Vec<TargetLine<LinearDevTargetParams>>,
409}
410
411impl LinearDevTargetTable {
412 pub fn new(table: Vec<TargetLine<LinearDevTargetParams>>) -> LinearDevTargetTable {
414 LinearDevTargetTable { table }
415 }
416}
417
418impl fmt::Display for LinearDevTargetTable {
419 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420 for line in &self.table {
421 writeln!(f, "{} {} {}", *line.start, *line.length, line.params)?;
422 }
423 Ok(())
424 }
425}
426
427impl TargetTable for LinearDevTargetTable {
428 fn from_raw_table(table: &[(u64, u64, String, String)]) -> DmResult<LinearDevTargetTable> {
429 Ok(LinearDevTargetTable {
430 table: table
431 .iter()
432 .map(|x| -> DmResult<TargetLine<LinearDevTargetParams>> {
433 Ok(TargetLine::new(
434 Sectors(x.0),
435 Sectors(x.1),
436 format!("{} {}", x.2, x.3).parse::<LinearDevTargetParams>()?,
437 ))
438 })
439 .collect::<DmResult<Vec<_>>>()?,
440 })
441 }
442
443 fn to_raw_table(&self) -> Vec<(u64, u64, String, String)> {
444 self.table
445 .iter()
446 .map(|x| {
447 (
448 *x.start,
449 *x.length,
450 x.params.target_type().to_string(),
451 x.params.param_str(),
452 )
453 })
454 .collect::<Vec<_>>()
455 }
456}
457
458#[derive(Debug)]
460pub struct LinearDev {
461 dev_info: Box<DeviceInfo>,
463 table: LinearDevTargetTable,
464}
465
466impl DmDevice<LinearDevTargetTable> for LinearDev {
467 fn device(&self) -> Device {
468 device!(self)
469 }
470
471 fn devnode(&self) -> PathBuf {
472 devnode!(self)
473 }
474
475 fn equivalent_tables(
479 left: &LinearDevTargetTable,
480 right: &LinearDevTargetTable,
481 ) -> DmResult<bool> {
482 Ok(left == right)
483 }
484
485 fn name(&self) -> &DmName {
486 name!(self)
487 }
488
489 fn size(&self) -> Sectors {
490 self.table.table.iter().map(|l| l.length).sum()
491 }
492
493 fn table(&self) -> &LinearDevTargetTable {
494 table!(self)
495 }
496
497 fn teardown(&mut self, dm: &DM) -> DmResult<()> {
498 dm.device_remove(&DevId::Name(self.name()), DmOptions::default())?;
499 Ok(())
500 }
501
502 fn uuid(&self) -> Option<&DmUuid> {
503 uuid!(self)
504 }
505}
506
507impl LinearDev {
510 pub fn setup(
529 dm: &DM,
530 name: &DmName,
531 uuid: Option<&DmUuid>,
532 table: Vec<TargetLine<LinearDevTargetParams>>,
533 ) -> DmResult<LinearDev> {
534 let table = LinearDevTargetTable::new(table);
535 let dev = if device_exists(dm, name)? {
536 let dev_info = dm.device_info(&DevId::Name(name))?;
537 let dev = LinearDev {
538 dev_info: Box::new(dev_info),
539 table,
540 };
541 device_match(dm, &dev, uuid)?;
542 dev
543 } else {
544 let dev_info = device_create(dm, name, uuid, &table, DmOptions::private())?;
545 LinearDev {
546 dev_info: Box::new(dev_info),
547 table,
548 }
549 };
550 Ok(dev)
551 }
552
553 pub fn set_table(
560 &mut self,
561 dm: &DM,
562 table: Vec<TargetLine<LinearDevTargetParams>>,
563 ) -> DmResult<()> {
564 let table = LinearDevTargetTable::new(table);
565 self.table_load(dm, &table, DmOptions::default())?;
566 self.table = table;
567 Ok(())
568 }
569
570 pub fn set_name(&mut self, dm: &DM, name: &DmName) -> DmResult<()> {
572 if self.name() == name {
573 return Ok(());
574 }
575 dm.device_rename(self.name(), &DevId::Name(name))?;
576 *self.dev_info = dm.device_info(&DevId::Name(name))?;
577 Ok(())
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use std::{clone::Clone, fs::OpenOptions, path::Path};
584
585 use crate::{
586 core::{devnode_to_devno, Device, DmFlags},
587 testing::{blkdev_size, test_name, test_with_spec},
588 };
589
590 use super::*;
591
592 fn test_empty(_paths: &[&Path]) {
594 assert_matches!(
595 LinearDev::setup(
596 &DM::new().unwrap(),
597 &test_name("new").expect("valid format"),
598 None,
599 vec![],
600 ),
601 Err(_)
602 );
603 }
604
605 fn test_empty_table_set(paths: &[&Path]) {
607 assert!(!paths.is_empty());
608
609 let dm = DM::new().unwrap();
610 let name = test_name("name").expect("valid format");
611 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
612 let params = LinearTargetParams::new(dev, Sectors(0));
613 let table = vec![TargetLine::new(
614 Sectors(0),
615 Sectors(1),
616 LinearDevTargetParams::Linear(params),
617 )];
618 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
619
620 assert_matches!(ld.set_table(&dm, vec![]), Err(_));
621 ld.resume(&dm).unwrap();
622 ld.teardown(&dm).unwrap();
623 }
624
625 fn test_rename_id(paths: &[&Path]) {
627 assert!(!paths.is_empty());
628
629 let dm = DM::new().unwrap();
630 let name = test_name("name").expect("valid format");
631 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
632 let params = LinearTargetParams::new(dev, Sectors(0));
633 let table = vec![TargetLine::new(
634 Sectors(0),
635 Sectors(1),
636 LinearDevTargetParams::Linear(params),
637 )];
638 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
639
640 ld.set_name(&dm, &name).unwrap();
641 assert_eq!(ld.name(), &*name);
642
643 ld.teardown(&dm).unwrap();
644 }
645
646 fn test_rename(paths: &[&Path]) {
648 assert!(!paths.is_empty());
649
650 let dm = DM::new().unwrap();
651 let name = test_name("name").expect("valid format");
652 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
653 let params = LinearTargetParams::new(dev, Sectors(0));
654 let table = vec![TargetLine::new(
655 Sectors(0),
656 Sectors(1),
657 LinearDevTargetParams::Linear(params),
658 )];
659 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
660
661 let new_name = test_name("new_name").expect("valid format");
662 ld.set_name(&dm, &new_name).unwrap();
663 assert_eq!(ld.name(), &*new_name);
664
665 ld.teardown(&dm).unwrap();
666 }
667
668 fn test_duplicate_segments(paths: &[&Path]) {
673 assert!(!paths.is_empty());
674
675 let dm = DM::new().unwrap();
676 let name = test_name("name").expect("valid format");
677 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
678 let params = LinearTargetParams::new(dev, Sectors(0));
679 let table = vec![
680 TargetLine::new(
681 Sectors(0),
682 Sectors(1),
683 LinearDevTargetParams::Linear(params.clone()),
684 ),
685 TargetLine::new(
686 Sectors(1),
687 Sectors(1),
688 LinearDevTargetParams::Linear(params),
689 ),
690 ];
691 let range: Sectors = table.iter().map(|s| s.length).sum();
692 let count = table.len();
693 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
694
695 let table = LinearDev::read_kernel_table(&dm, &DevId::Name(ld.name()))
696 .unwrap()
697 .table;
698 assert_eq!(table.len(), count);
699 assert_matches!(table[0].params, LinearDevTargetParams::Linear(ref device) if device.device == dev);
700 assert_matches!(table[1].params, LinearDevTargetParams::Linear(ref device) if device.device == dev);
701
702 assert_eq!(
703 blkdev_size(&OpenOptions::new().read(true).open(ld.devnode()).unwrap()).sectors(),
704 range
705 );
706
707 ld.teardown(&dm).unwrap();
708 }
709
710 fn test_several_segments(paths: &[&Path]) {
713 assert!(!paths.is_empty());
714
715 let dm = DM::new().unwrap();
716 let name = test_name("name").expect("valid format");
717 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
718 let table = (0..5)
719 .map(|n| {
720 TargetLine::new(
721 Sectors(n),
722 Sectors(1),
723 LinearDevTargetParams::Linear(LinearTargetParams::new(dev, Sectors(n))),
724 )
725 })
726 .collect::<Vec<_>>();
727 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
728
729 let loaded_table = LinearDev::read_kernel_table(&dm, &DevId::Name(ld.name())).unwrap();
730 assert!(
731 LinearDev::equivalent_tables(&LinearDevTargetTable::new(table), &loaded_table).unwrap()
732 );
733
734 ld.teardown(&dm).unwrap();
735 }
736
737 fn test_same_name(paths: &[&Path]) {
740 assert!(!paths.is_empty());
741
742 let dm = DM::new().unwrap();
743 let name = test_name("name").expect("valid format");
744 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
745 let params = LinearTargetParams::new(dev, Sectors(0));
746 let table = vec![TargetLine::new(
747 Sectors(0),
748 Sectors(1),
749 LinearDevTargetParams::Linear(params),
750 )];
751 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
752 let params2 = LinearTargetParams::new(dev, Sectors(1));
753 let table2 = vec![TargetLine::new(
754 Sectors(0),
755 Sectors(1),
756 LinearDevTargetParams::Linear(params2),
757 )];
758 assert_matches!(LinearDev::setup(&dm, &name, None, table2), Err(_));
759 assert_matches!(LinearDev::setup(&dm, &name, None, table), Ok(_));
760 ld.teardown(&dm).unwrap();
761 }
762
763 fn test_same_segment(paths: &[&Path]) {
765 assert!(!paths.is_empty());
766
767 let dm = DM::new().unwrap();
768 let name = test_name("name").expect("valid format");
769 let ersatz = test_name("ersatz").expect("valid format");
770 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
771 let params = LinearTargetParams::new(dev, Sectors(0));
772 let table = vec![TargetLine::new(
773 Sectors(0),
774 Sectors(1),
775 LinearDevTargetParams::Linear(params),
776 )];
777 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
778 let ld2 = LinearDev::setup(&dm, &ersatz, None, table);
779 assert_matches!(ld2, Ok(_));
780
781 ld2.unwrap().teardown(&dm).unwrap();
782 ld.teardown(&dm).unwrap();
783 }
784
785 fn test_suspend(paths: &[&Path]) {
787 assert!(!paths.is_empty());
788
789 let dm = DM::new().unwrap();
790 let name = test_name("name").expect("valid format");
791 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
792 let params = LinearTargetParams::new(dev, Sectors(0));
793 let table = vec![TargetLine::new(
794 Sectors(0),
795 Sectors(1),
796 LinearDevTargetParams::Linear(params),
797 )];
798 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
799
800 ld.suspend(&dm, DmOptions::default().set_flags(DmFlags::DM_NOFLUSH))
801 .unwrap();
802 ld.suspend(&dm, DmOptions::default().set_flags(DmFlags::DM_NOFLUSH))
803 .unwrap();
804 ld.resume(&dm).unwrap();
805 ld.resume(&dm).unwrap();
806
807 ld.teardown(&dm).unwrap();
808 }
809
810 #[test]
811 fn test_flakey_target_params_zero() {
812 let result = "flakey 8:32 0 16 2 0"
813 .parse::<FlakeyTargetParams>()
814 .unwrap();
815 assert_eq!(result.feature_args, HashSet::new());
816 }
817
818 #[test]
819 fn test_flakey_target_params_none() {
820 let result = "flakey 8:32 0 16 2".parse::<FlakeyTargetParams>().unwrap();
821 assert_eq!(result.feature_args, HashSet::new());
822 }
823
824 #[test]
825 fn test_flakey_target_params_drop_writes() {
826 let result = "flakey 8:32 0 16 2 1 drop_writes"
827 .parse::<FlakeyTargetParams>()
828 .unwrap();
829 let expected = [FlakeyFeatureArg::DropWrites]
830 .iter()
831 .cloned()
832 .collect::<HashSet<_>>();
833 assert_eq!(result.feature_args, expected);
834 }
835
836 #[test]
837 fn test_flakey_target_params_error_writes() {
838 let result = "flakey 8:32 0 16 2 1 error_writes"
839 .parse::<FlakeyTargetParams>()
840 .unwrap();
841 let expected = [FlakeyFeatureArg::ErrorWrites]
842 .iter()
843 .cloned()
844 .collect::<HashSet<_>>();
845 assert_eq!(result.feature_args, expected);
846 }
847
848 #[test]
849 fn test_flakey_target_params_corrupt_bio_byte_reads() {
850 let result = "flakey 8:32 0 16 2 5 corrupt_bio_byte 32 r 1 0"
851 .parse::<FlakeyTargetParams>()
852 .unwrap();
853 let expected = [FlakeyFeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0)]
854 .iter()
855 .cloned()
856 .collect::<HashSet<_>>();
857 assert_eq!(result.feature_args, expected);
858 }
859
860 #[test]
861 fn test_flakey_target_params_corrupt_bio_byte_writes() {
862 let result = "flakey 8:32 0 16 2 5 corrupt_bio_byte 224 w 0 32"
863 .parse::<FlakeyTargetParams>()
864 .unwrap();
865 let expected = [FlakeyFeatureArg::CorruptBioByte(
866 224,
867 Direction::Writes,
868 0,
869 32,
870 )]
871 .iter()
872 .cloned()
873 .collect::<HashSet<_>>();
874 assert_eq!(result.feature_args, expected);
875 }
876
877 #[test]
878 fn test_flakey_target_params_corrupt_bio_byte_and_drop_writes() {
879 let result = "flakey 8:32 0 16 2 6 corrupt_bio_byte 32 r 1 0 drop_writes"
880 .parse::<FlakeyTargetParams>()
881 .unwrap();
882 let expected = [
883 FlakeyFeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0),
884 FlakeyFeatureArg::DropWrites,
885 ]
886 .iter()
887 .cloned()
888 .collect::<HashSet<_>>();
889 assert_eq!(result.feature_args, expected);
890 }
891
892 #[test]
893 fn test_flakey_target_params_drop_writes_and_corrupt_bio_byte() {
894 let result = "flakey 8:32 0 16 2 6 corrupt_bio_byte 32 r 1 0 drop_writes"
895 .parse::<FlakeyTargetParams>()
896 .unwrap();
897 let expected = [
898 FlakeyFeatureArg::DropWrites,
899 FlakeyFeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0),
900 ]
901 .iter()
902 .cloned()
903 .collect::<HashSet<_>>();
904 assert_eq!(result.feature_args, expected);
905 }
906
907 #[test]
908 fn test_flakey_target_params_error_writes_and_drop_writes() {
909 let result = "flakey 8:32 0 16 2 2 error_writes drop_writes"
910 .parse::<FlakeyTargetParams>()
911 .unwrap();
912 let expected = [FlakeyFeatureArg::ErrorWrites, FlakeyFeatureArg::DropWrites]
913 .iter()
914 .cloned()
915 .collect::<HashSet<_>>();
916 assert_eq!(result.feature_args, expected);
917 }
918
919 #[test]
920 fn loop_test_duplicate_segments() {
921 test_with_spec(1, test_duplicate_segments);
922 }
923
924 #[test]
925 fn loop_test_empty() {
926 test_with_spec(0, test_empty);
927 }
928
929 #[test]
930 fn loop_test_empty_table_set() {
931 test_with_spec(1, test_empty_table_set);
932 }
933
934 #[test]
935 fn loop_test_rename() {
936 test_with_spec(1, test_rename);
937 }
938
939 #[test]
940 fn loop_test_rename_id() {
941 test_with_spec(1, test_rename_id);
942 }
943
944 #[test]
945 fn loop_test_same_name() {
946 test_with_spec(1, test_same_name);
947 }
948
949 #[test]
950 fn loop_test_segment() {
951 test_with_spec(1, test_same_segment);
952 }
953
954 #[test]
955 fn loop_test_several_segments() {
956 test_with_spec(1, test_several_segments);
957 }
958
959 #[test]
960 fn loop_test_suspend() {
961 test_with_spec(1, test_suspend);
962 }
963}