1use std::{collections::HashSet, fmt, path::PathBuf, str::FromStr};
6
7use crate::{
8 core::{DevId, Device, DeviceInfo, DmFlags, 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 FeatureArg {
118 DropWrites,
123 ErrorWrites,
128 CorruptBioByte(u64, Direction, u8, u64),
141}
142
143impl fmt::Display for FeatureArg {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 match self {
146 FeatureArg::DropWrites => write!(f, "drop_writes"),
147 FeatureArg::ErrorWrites => write!(f, "error_writes"),
148 FeatureArg::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<FeatureArg>,
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<FeatureArg>,
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<FeatureArg>> {
219 let mut vals_iter = vals.iter();
220 let mut result: Vec<FeatureArg> = Vec::new();
221 while let Some(x) = vals_iter.next() {
222 match x {
223 &"drop_writes" => result.push(FeatureArg::DropWrites),
224 &"error_writes" => result.push(FeatureArg::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(FeatureArg::CorruptBioByte(offset, direction, value, flags));
259 }
260 x => {
261 let err_msg = format!("{x} is an unrecognized feature parameter");
262 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
263 }
264 }
265 }
266
267 Ok(result)
268 }
269
270 let vals = s.split(' ').collect::<Vec<_>>();
271
272 if vals.len() < 5 {
273 let err_msg = format!(
274 "expected at least five values in params string \"{}\", found {}",
275 s,
276 vals.len()
277 );
278 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
279 }
280
281 if vals[0] != FLAKEY_TARGET_NAME {
282 let err_msg = format!(
283 "Expected a flakey target entry but found target type {}",
284 vals[0]
285 );
286 return Err(DmError::Dm(ErrorEnum::Invalid, err_msg));
287 }
288
289 let device = parse_device(vals[1], "block device for flakey target")?;
290 let start_offset = Sectors(parse_value(vals[2], "physical start offset")?);
291
292 let up_interval = parse_value(vals[3], "up interval")?;
293 let down_interval = parse_value(vals[4], "down interval")?;
294
295 let feature_args = if vals.len() == 5 {
296 vec![]
297 } else {
298 parse_feature_args(
299 &vals[6..6 + parse_value::<usize>(vals[5], "number of feature args")?],
300 )?
301 };
302
303 Ok(FlakeyTargetParams::new(
304 device,
305 start_offset,
306 up_interval,
307 down_interval,
308 feature_args,
309 ))
310 }
311}
312
313impl TargetParams for FlakeyTargetParams {
314 fn param_str(&self) -> String {
315 let feature_args = if self.feature_args.is_empty() {
316 "0".to_owned()
317 } else {
318 format!(
319 "{} {}",
320 self.feature_args.len(),
321 self.feature_args
322 .iter()
323 .map(|x| x.to_string())
324 .collect::<Vec<_>>()
325 .join(" ")
326 )
327 };
328
329 format!(
330 "{} {} {} {} {}",
331 self.device, *self.start_offset, self.up_interval, self.down_interval, feature_args
332 )
333 }
334
335 fn target_type(&self) -> TargetTypeBuf {
336 TargetTypeBuf::new(FLAKEY_TARGET_NAME.into()).expect("FLAKEY_TARGET_NAME is valid")
337 }
338}
339
340#[derive(Clone, Debug, Eq, PartialEq)]
342pub enum LinearDevTargetParams {
343 Flakey(FlakeyTargetParams),
345 Linear(LinearTargetParams),
347}
348
349impl fmt::Display for LinearDevTargetParams {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 match *self {
352 LinearDevTargetParams::Flakey(ref flakey) => flakey.fmt(f),
353 LinearDevTargetParams::Linear(ref linear) => linear.fmt(f),
354 }
355 }
356}
357
358impl FromStr for LinearDevTargetParams {
359 type Err = DmError;
360
361 fn from_str(s: &str) -> DmResult<LinearDevTargetParams> {
362 let target_type = Some(s.split_once(' ').map_or(s, |x| x.0)).ok_or_else(|| {
363 DmError::Dm(
364 ErrorEnum::Invalid,
365 format!("target line string \"{s}\" did not contain any values"),
366 )
367 })?;
368 if target_type == FLAKEY_TARGET_NAME {
369 Ok(LinearDevTargetParams::Flakey(
370 s.parse::<FlakeyTargetParams>()?,
371 ))
372 } else if target_type == LINEAR_TARGET_NAME {
373 Ok(LinearDevTargetParams::Linear(
374 s.parse::<LinearTargetParams>()?,
375 ))
376 } else {
377 Err(DmError::Dm(
378 ErrorEnum::Invalid,
379 format!("unexpected target type \"{target_type}\""),
380 ))
381 }
382 }
383}
384
385impl TargetParams for LinearDevTargetParams {
386 fn param_str(&self) -> String {
387 match *self {
388 LinearDevTargetParams::Flakey(ref flakey) => flakey.param_str(),
389 LinearDevTargetParams::Linear(ref linear) => linear.param_str(),
390 }
391 }
392
393 fn target_type(&self) -> TargetTypeBuf {
394 match *self {
395 LinearDevTargetParams::Flakey(ref flakey) => flakey.target_type(),
396 LinearDevTargetParams::Linear(ref linear) => linear.target_type(),
397 }
398 }
399}
400
401#[derive(Clone, Debug, Eq, PartialEq)]
404pub struct LinearDevTargetTable {
405 pub table: Vec<TargetLine<LinearDevTargetParams>>,
407}
408
409impl LinearDevTargetTable {
410 pub fn new(table: Vec<TargetLine<LinearDevTargetParams>>) -> LinearDevTargetTable {
412 LinearDevTargetTable { table }
413 }
414}
415
416impl fmt::Display for LinearDevTargetTable {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 for line in &self.table {
419 writeln!(f, "{} {} {}", *line.start, *line.length, line.params)?;
420 }
421 Ok(())
422 }
423}
424
425impl TargetTable for LinearDevTargetTable {
426 fn from_raw_table(table: &[(u64, u64, String, String)]) -> DmResult<LinearDevTargetTable> {
427 Ok(LinearDevTargetTable {
428 table: table
429 .iter()
430 .map(|x| -> DmResult<TargetLine<LinearDevTargetParams>> {
431 Ok(TargetLine::new(
432 Sectors(x.0),
433 Sectors(x.1),
434 format!("{} {}", x.2, x.3).parse::<LinearDevTargetParams>()?,
435 ))
436 })
437 .collect::<DmResult<Vec<_>>>()?,
438 })
439 }
440
441 fn to_raw_table(&self) -> Vec<(u64, u64, String, String)> {
442 self.table
443 .iter()
444 .map(|x| {
445 (
446 *x.start,
447 *x.length,
448 x.params.target_type().to_string(),
449 x.params.param_str(),
450 )
451 })
452 .collect::<Vec<_>>()
453 }
454}
455
456#[derive(Debug)]
458pub struct LinearDev {
459 dev_info: Box<DeviceInfo>,
461 table: LinearDevTargetTable,
462}
463
464impl DmDevice<LinearDevTargetTable> for LinearDev {
465 fn device(&self) -> Device {
466 device!(self)
467 }
468
469 fn devnode(&self) -> PathBuf {
470 devnode!(self)
471 }
472
473 fn equivalent_tables(
477 left: &LinearDevTargetTable,
478 right: &LinearDevTargetTable,
479 ) -> DmResult<bool> {
480 Ok(left == right)
481 }
482
483 fn name(&self) -> &DmName {
484 name!(self)
485 }
486
487 fn size(&self) -> Sectors {
488 self.table.table.iter().map(|l| l.length).sum()
489 }
490
491 fn table(&self) -> &LinearDevTargetTable {
492 table!(self)
493 }
494
495 fn teardown(&mut self, dm: &DM) -> DmResult<()> {
496 dm.device_remove(&DevId::Name(self.name()), DmOptions::default())?;
497 Ok(())
498 }
499
500 fn uuid(&self) -> Option<&DmUuid> {
501 uuid!(self)
502 }
503}
504
505impl LinearDev {
508 pub fn setup(
527 dm: &DM,
528 name: &DmName,
529 uuid: Option<&DmUuid>,
530 table: Vec<TargetLine<LinearDevTargetParams>>,
531 ) -> DmResult<LinearDev> {
532 let table = LinearDevTargetTable::new(table);
533 let dev = if device_exists(dm, name)? {
534 let dev_info = dm.device_info(&DevId::Name(name))?;
535 let dev = LinearDev {
536 dev_info: Box::new(dev_info),
537 table,
538 };
539 device_match(dm, &dev, uuid)?;
540 dev
541 } else {
542 let dev_info = device_create(dm, name, uuid, &table, DmOptions::private())?;
543 LinearDev {
544 dev_info: Box::new(dev_info),
545 table,
546 }
547 };
548 Ok(dev)
549 }
550
551 pub fn set_table(
558 &mut self,
559 dm: &DM,
560 table: Vec<TargetLine<LinearDevTargetParams>>,
561 ) -> DmResult<()> {
562 let table = LinearDevTargetTable::new(table);
563 self.suspend(dm, DmOptions::default().set_flags(DmFlags::DM_NOFLUSH))?;
564 self.table_load(dm, &table, DmOptions::default())?;
565 self.table = table;
566 Ok(())
567 }
568
569 pub fn set_name(&mut self, dm: &DM, name: &DmName) -> DmResult<()> {
571 if self.name() == name {
572 return Ok(());
573 }
574 dm.device_rename(self.name(), &DevId::Name(name))?;
575 self.dev_info = Box::new(dm.device_info(&DevId::Name(name))?);
576 Ok(())
577 }
578}
579
580#[cfg(test)]
581mod tests {
582 use std::{clone::Clone, fs::OpenOptions, path::Path};
583
584 use crate::{
585 core::{devnode_to_devno, Device},
586 testing::{blkdev_size, test_name, test_with_spec},
587 };
588
589 use super::*;
590
591 fn test_empty(_paths: &[&Path]) {
593 assert_matches!(
594 LinearDev::setup(
595 &DM::new().unwrap(),
596 &test_name("new").expect("valid format"),
597 None,
598 vec![],
599 ),
600 Err(_)
601 );
602 }
603
604 fn test_empty_table_set(paths: &[&Path]) {
606 assert!(!paths.is_empty());
607
608 let dm = DM::new().unwrap();
609 let name = test_name("name").expect("valid format");
610 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
611 let params = LinearTargetParams::new(dev, Sectors(0));
612 let table = vec![TargetLine::new(
613 Sectors(0),
614 Sectors(1),
615 LinearDevTargetParams::Linear(params),
616 )];
617 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
618
619 assert_matches!(ld.set_table(&dm, vec![]), Err(_));
620 ld.resume(&dm).unwrap();
621 ld.teardown(&dm).unwrap();
622 }
623
624 fn test_rename_id(paths: &[&Path]) {
626 assert!(!paths.is_empty());
627
628 let dm = DM::new().unwrap();
629 let name = test_name("name").expect("valid format");
630 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
631 let params = LinearTargetParams::new(dev, Sectors(0));
632 let table = vec![TargetLine::new(
633 Sectors(0),
634 Sectors(1),
635 LinearDevTargetParams::Linear(params),
636 )];
637 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
638
639 ld.set_name(&dm, &name).unwrap();
640 assert_eq!(ld.name(), &*name);
641
642 ld.teardown(&dm).unwrap();
643 }
644
645 fn test_rename(paths: &[&Path]) {
647 assert!(!paths.is_empty());
648
649 let dm = DM::new().unwrap();
650 let name = test_name("name").expect("valid format");
651 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
652 let params = LinearTargetParams::new(dev, Sectors(0));
653 let table = vec![TargetLine::new(
654 Sectors(0),
655 Sectors(1),
656 LinearDevTargetParams::Linear(params),
657 )];
658 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
659
660 let new_name = test_name("new_name").expect("valid format");
661 ld.set_name(&dm, &new_name).unwrap();
662 assert_eq!(ld.name(), &*new_name);
663
664 ld.teardown(&dm).unwrap();
665 }
666
667 fn test_duplicate_segments(paths: &[&Path]) {
672 assert!(!paths.is_empty());
673
674 let dm = DM::new().unwrap();
675 let name = test_name("name").expect("valid format");
676 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
677 let params = LinearTargetParams::new(dev, Sectors(0));
678 let table = vec![
679 TargetLine::new(
680 Sectors(0),
681 Sectors(1),
682 LinearDevTargetParams::Linear(params.clone()),
683 ),
684 TargetLine::new(
685 Sectors(1),
686 Sectors(1),
687 LinearDevTargetParams::Linear(params),
688 ),
689 ];
690 let range: Sectors = table.iter().map(|s| s.length).sum();
691 let count = table.len();
692 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
693
694 let table = LinearDev::read_kernel_table(&dm, &DevId::Name(ld.name()))
695 .unwrap()
696 .table;
697 assert_eq!(table.len(), count);
698 assert_matches!(table[0].params, LinearDevTargetParams::Linear(ref device) if device.device == dev);
699 assert_matches!(table[1].params, LinearDevTargetParams::Linear(ref device) if device.device == dev);
700
701 assert_eq!(
702 blkdev_size(&OpenOptions::new().read(true).open(ld.devnode()).unwrap()).sectors(),
703 range
704 );
705
706 ld.teardown(&dm).unwrap();
707 }
708
709 fn test_several_segments(paths: &[&Path]) {
712 assert!(!paths.is_empty());
713
714 let dm = DM::new().unwrap();
715 let name = test_name("name").expect("valid format");
716 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
717 let table = (0..5)
718 .map(|n| {
719 TargetLine::new(
720 Sectors(n),
721 Sectors(1),
722 LinearDevTargetParams::Linear(LinearTargetParams::new(dev, Sectors(n))),
723 )
724 })
725 .collect::<Vec<_>>();
726 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
727
728 let loaded_table = LinearDev::read_kernel_table(&dm, &DevId::Name(ld.name())).unwrap();
729 assert!(
730 LinearDev::equivalent_tables(&LinearDevTargetTable::new(table), &loaded_table).unwrap()
731 );
732
733 ld.teardown(&dm).unwrap();
734 }
735
736 fn test_same_name(paths: &[&Path]) {
739 assert!(!paths.is_empty());
740
741 let dm = DM::new().unwrap();
742 let name = test_name("name").expect("valid format");
743 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
744 let params = LinearTargetParams::new(dev, Sectors(0));
745 let table = vec![TargetLine::new(
746 Sectors(0),
747 Sectors(1),
748 LinearDevTargetParams::Linear(params),
749 )];
750 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
751 let params2 = LinearTargetParams::new(dev, Sectors(1));
752 let table2 = vec![TargetLine::new(
753 Sectors(0),
754 Sectors(1),
755 LinearDevTargetParams::Linear(params2),
756 )];
757 assert_matches!(LinearDev::setup(&dm, &name, None, table2), Err(_));
758 assert_matches!(LinearDev::setup(&dm, &name, None, table), Ok(_));
759 ld.teardown(&dm).unwrap();
760 }
761
762 fn test_same_segment(paths: &[&Path]) {
764 assert!(!paths.is_empty());
765
766 let dm = DM::new().unwrap();
767 let name = test_name("name").expect("valid format");
768 let ersatz = test_name("ersatz").expect("valid format");
769 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
770 let params = LinearTargetParams::new(dev, Sectors(0));
771 let table = vec![TargetLine::new(
772 Sectors(0),
773 Sectors(1),
774 LinearDevTargetParams::Linear(params),
775 )];
776 let mut ld = LinearDev::setup(&dm, &name, None, table.clone()).unwrap();
777 let ld2 = LinearDev::setup(&dm, &ersatz, None, table);
778 assert_matches!(ld2, Ok(_));
779
780 ld2.unwrap().teardown(&dm).unwrap();
781 ld.teardown(&dm).unwrap();
782 }
783
784 fn test_suspend(paths: &[&Path]) {
786 assert!(!paths.is_empty());
787
788 let dm = DM::new().unwrap();
789 let name = test_name("name").expect("valid format");
790 let dev = Device::from(devnode_to_devno(paths[0]).unwrap().unwrap());
791 let params = LinearTargetParams::new(dev, Sectors(0));
792 let table = vec![TargetLine::new(
793 Sectors(0),
794 Sectors(1),
795 LinearDevTargetParams::Linear(params),
796 )];
797 let mut ld = LinearDev::setup(&dm, &name, None, table).unwrap();
798
799 ld.suspend(&dm, DmOptions::default().set_flags(DmFlags::DM_NOFLUSH))
800 .unwrap();
801 ld.suspend(&dm, DmOptions::default().set_flags(DmFlags::DM_NOFLUSH))
802 .unwrap();
803 ld.resume(&dm).unwrap();
804 ld.resume(&dm).unwrap();
805
806 ld.teardown(&dm).unwrap();
807 }
808
809 #[test]
810 fn test_flakey_target_params_zero() {
811 let result = "flakey 8:32 0 16 2 0"
812 .parse::<FlakeyTargetParams>()
813 .unwrap();
814 assert_eq!(result.feature_args, HashSet::new());
815 }
816
817 #[test]
818 fn test_flakey_target_params_none() {
819 let result = "flakey 8:32 0 16 2".parse::<FlakeyTargetParams>().unwrap();
820 assert_eq!(result.feature_args, HashSet::new());
821 }
822
823 #[test]
824 fn test_flakey_target_params_drop_writes() {
825 let result = "flakey 8:32 0 16 2 1 drop_writes"
826 .parse::<FlakeyTargetParams>()
827 .unwrap();
828 let expected = [FeatureArg::DropWrites]
829 .iter()
830 .cloned()
831 .collect::<HashSet<_>>();
832 assert_eq!(result.feature_args, expected);
833 }
834
835 #[test]
836 fn test_flakey_target_params_error_writes() {
837 let result = "flakey 8:32 0 16 2 1 error_writes"
838 .parse::<FlakeyTargetParams>()
839 .unwrap();
840 let expected = [FeatureArg::ErrorWrites]
841 .iter()
842 .cloned()
843 .collect::<HashSet<_>>();
844 assert_eq!(result.feature_args, expected);
845 }
846
847 #[test]
848 fn test_flakey_target_params_corrupt_bio_byte_reads() {
849 let result = "flakey 8:32 0 16 2 5 corrupt_bio_byte 32 r 1 0"
850 .parse::<FlakeyTargetParams>()
851 .unwrap();
852 let expected = [FeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0)]
853 .iter()
854 .cloned()
855 .collect::<HashSet<_>>();
856 assert_eq!(result.feature_args, expected);
857 }
858
859 #[test]
860 fn test_flakey_target_params_corrupt_bio_byte_writes() {
861 let result = "flakey 8:32 0 16 2 5 corrupt_bio_byte 224 w 0 32"
862 .parse::<FlakeyTargetParams>()
863 .unwrap();
864 let expected = [FeatureArg::CorruptBioByte(224, Direction::Writes, 0, 32)]
865 .iter()
866 .cloned()
867 .collect::<HashSet<_>>();
868 assert_eq!(result.feature_args, expected);
869 }
870
871 #[test]
872 fn test_flakey_target_params_corrupt_bio_byte_and_drop_writes() {
873 let result = "flakey 8:32 0 16 2 6 corrupt_bio_byte 32 r 1 0 drop_writes"
874 .parse::<FlakeyTargetParams>()
875 .unwrap();
876 let expected = [
877 FeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0),
878 FeatureArg::DropWrites,
879 ]
880 .iter()
881 .cloned()
882 .collect::<HashSet<_>>();
883 assert_eq!(result.feature_args, expected);
884 }
885
886 #[test]
887 fn test_flakey_target_params_drop_writes_and_corrupt_bio_byte() {
888 let result = "flakey 8:32 0 16 2 6 corrupt_bio_byte 32 r 1 0 drop_writes"
889 .parse::<FlakeyTargetParams>()
890 .unwrap();
891 let expected = [
892 FeatureArg::DropWrites,
893 FeatureArg::CorruptBioByte(32, Direction::Reads, 1, 0),
894 ]
895 .iter()
896 .cloned()
897 .collect::<HashSet<_>>();
898 assert_eq!(result.feature_args, expected);
899 }
900
901 #[test]
902 fn test_flakey_target_params_error_writes_and_drop_writes() {
903 let result = "flakey 8:32 0 16 2 2 error_writes drop_writes"
904 .parse::<FlakeyTargetParams>()
905 .unwrap();
906 let expected = [FeatureArg::ErrorWrites, FeatureArg::DropWrites]
907 .iter()
908 .cloned()
909 .collect::<HashSet<_>>();
910 assert_eq!(result.feature_args, expected);
911 }
912
913 #[test]
914 fn loop_test_duplicate_segments() {
915 test_with_spec(1, test_duplicate_segments);
916 }
917
918 #[test]
919 fn loop_test_empty() {
920 test_with_spec(0, test_empty);
921 }
922
923 #[test]
924 fn loop_test_empty_table_set() {
925 test_with_spec(1, test_empty_table_set);
926 }
927
928 #[test]
929 fn loop_test_rename() {
930 test_with_spec(1, test_rename);
931 }
932
933 #[test]
934 fn loop_test_rename_id() {
935 test_with_spec(1, test_rename_id);
936 }
937
938 #[test]
939 fn loop_test_same_name() {
940 test_with_spec(1, test_same_name);
941 }
942
943 #[test]
944 fn loop_test_segment() {
945 test_with_spec(1, test_same_segment);
946 }
947
948 #[test]
949 fn loop_test_several_segments() {
950 test_with_spec(1, test_several_segments);
951 }
952
953 #[test]
954 fn loop_test_suspend() {
955 test_with_spec(1, test_suspend);
956 }
957}