From 83cae246941e5b2c95d5c9a12746983a18f6c157 Mon Sep 17 00:00:00 2001 From: chenjiayi Date: Wed, 15 Nov 2023 15:29:02 +0800 Subject: [PATCH 061/103] test(device): add UT for device enumerator --- libs/device/src/device_enumerator.rs | 720 ++++++++++++++------------- libs/device/src/error.rs | 6 +- 2 files changed, 365 insertions(+), 361 deletions(-) diff --git a/libs/device/src/device_enumerator.rs b/libs/device/src/device_enumerator.rs index 55f8681d..29391e0b 100644 --- a/libs/device/src/device_enumerator.rs +++ b/libs/device/src/device_enumerator.rs @@ -12,10 +12,11 @@ //! enumerate /sys to collect devices //! -use crate::{device::Device, error::Error, utils::*}; +use crate::{device::Device, error::*, utils::*, TAGS_BASE_DIR}; use bitflags::bitflags; use fnmatch_sys::fnmatch; use nix::errno::Errno; +use snafu::ResultExt; use std::{ cell::RefCell, collections::{HashMap, HashSet}, @@ -45,7 +46,6 @@ impl Default for MatchInitializedType { } /// enumerate devices or subsystems under /sys -#[derive(Default)] pub struct DeviceEnumerator { /// enumerator type pub(crate) etype: RefCell, @@ -91,6 +91,40 @@ pub struct DeviceEnumerator { /// how to match device pub(crate) match_initialized: RefCell, + + /// the base directory path to contain runtime temporary files of device database, tags, etc. + pub(crate) base_path: RefCell, +} + +impl Default for DeviceEnumerator { + fn default() -> Self { + Self::new() + } +} + +impl DeviceEnumerator { + /// create a default instance of DeviceEnumerator + pub fn new() -> Self { + Self { + etype: RefCell::new(DeviceEnumerationType::All), + devices_by_syspath: RefCell::new(HashMap::new()), + devices: RefCell::new(Vec::new()), + scan_up_to_date: RefCell::new(false), + sorted: RefCell::new(false), + prioritized_subsystems: RefCell::new(Vec::new()), + match_subsystem: RefCell::new(HashSet::new()), + not_match_subsystem: RefCell::new(HashSet::new()), + match_sysattr: RefCell::new(HashMap::new()), + not_match_sysattr: RefCell::new(HashMap::new()), + match_property: RefCell::new(HashMap::new()), + match_sysname: RefCell::new(HashSet::new()), + not_match_sysname: RefCell::new(HashSet::new()), + match_tag: RefCell::new(HashSet::new()), + match_parent: RefCell::new(HashSet::new()), + match_initialized: RefCell::new(MatchInitializedType::ALL), + base_path: RefCell::new(crate::DEFAULT_BASE_DIR.to_string()), + } + } } /// decide enumerate devices or subsystems @@ -186,11 +220,6 @@ bitflags! { /// public methods impl DeviceEnumerator { - /// create a default instance of DeviceEnumerator - pub fn new() -> DeviceEnumerator { - DeviceEnumerator::default() - } - /// set the enumerator type pub fn set_enumerator_type(&mut self, etype: DeviceEnumerationType) { if *self.etype.borrow() != etype { @@ -426,23 +455,11 @@ impl DeviceEnumerator { for (property_pattern, value_pattern) in self.match_property.borrow().iter() { for (property, value) in &device.property_iter() { - if !self - .pattern_match(property_pattern, property) - .map_err(|e| Error::Nix { - msg: format!("match_property failed: {}", e), - source: e.get_errno(), - })? - { + if !self.pattern_match(property_pattern, property) { continue; } - if self - .pattern_match(value_pattern, value) - .map_err(|e| Error::Nix { - msg: format!("match_property failed: {}", e), - source: e.get_errno(), - })? - { + if self.pattern_match(value_pattern, value) { return Ok(true); } } @@ -458,7 +475,7 @@ impl DeviceEnumerator { } /// check whether the sysname of a device matches - pub(crate) fn match_sysname(&self, sysname: &str) -> Result { + pub(crate) fn match_sysname(&self, sysname: &str) -> bool { self.set_pattern_match( &self.match_sysname.borrow(), &self.not_match_sysname.borrow(), @@ -473,7 +490,7 @@ impl DeviceEnumerator { } /// check whether the subsystem of a device matches - pub(crate) fn match_subsystem(&self, subsystem: &str) -> Result { + pub(crate) fn match_subsystem(&self, subsystem: &str) -> bool { self.set_pattern_match( &self.match_subsystem.borrow(), &self.not_match_subsystem.borrow(), @@ -529,7 +546,7 @@ impl DeviceEnumerator { return Ok(true); } - self.pattern_match(patterns, &value) + Ok(self.pattern_match(patterns, &value)) } /// check whether a device matches conditions according to flags @@ -538,19 +555,8 @@ impl DeviceEnumerator { device: &mut Device, flags: MatchFlag, ) -> Result { - if (flags & MatchFlag::SYSNAME).bits() != 0 { - match self.match_sysname(&device.get_sysname()?) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match sysname failed: {}", e), - source: e.get_errno(), - }) - } - } + if (flags & MatchFlag::SYSNAME).bits() != 0 && !self.match_sysname(&device.get_sysname()?) { + return Ok(false); } if (flags & MatchFlag::SUBSYSTEM).bits() != 0 { @@ -568,87 +574,29 @@ impl DeviceEnumerator { } }; - match self.match_subsystem(&subsystem) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match_subsystem ({})", e), - source: e.get_errno(), - }); - } + if !self.match_subsystem(&subsystem) { + return Ok(false); } } - if (flags & MatchFlag::PARENT).bits() != 0 { - match self.match_parent(device) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match parent failed: {}", e), - source: e.get_errno(), - }); - } - } + if (flags & MatchFlag::PARENT).bits() != 0 && !self.match_parent(device)? { + return Ok(false); } - if (flags & MatchFlag::TAG).bits() != 0 { - match self.match_tag(device) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match tag failed: {}", e), - source: e.get_errno(), - }); - } - } + if (flags & MatchFlag::TAG).bits() != 0 && !self.match_tag(device)? { + return Ok(false); } - match self.match_initialized(device) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match_initialized ({})", e), - source: e.get_errno(), - }); - } + if !self.match_initialized(device)? { + return Ok(false); } - match self.match_property(device) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match property failed: {}", e), - source: e.get_errno(), - }); - } + if !self.match_property(device)? { + return Ok(false); } - match self.match_sysattr(device) { - Ok(ret) => match ret { - true => {} - false => return Ok(false), - }, - Err(e) => { - return Err(Error::Nix { - msg: format!("test_matches failed: match sysattr failed: {}", e), - source: e.get_errno(), - }); - } + if !self.match_sysattr(device)? { + return Ok(false); } Ok(true) @@ -679,20 +627,11 @@ impl DeviceEnumerator { d = parent.clone(); - if !self - .test_matches(&mut parent.borrow_mut(), flags) - .map_err(|e| Error::Nix { - msg: format!("add_parent_devices failed: {}", e), - source: e.get_errno(), - })? - { + if !self.test_matches(&mut parent.borrow_mut(), flags)? { continue; } - if !self.add_device(parent.clone()).map_err(|e| Error::Nix { - msg: format!("add_parent_devices failed: {}", e), - source: e.get_errno(), - })? { + if !self.add_device(parent.clone())? { break; } } @@ -711,12 +650,8 @@ impl DeviceEnumerator { let mut path: Vec = vec!["/sys".to_string(), basedir]; path.append(&mut subdirs); let path = path.join("/"); - let path = match Path::new(&path).canonicalize().map_err(|e| Error::Nix { - msg: format!( - "scan_dir_and_add_devices failed: canonicalize {} ({})", - path, e - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), + let path = match Path::new(&path).canonicalize().context(Io { + msg: format!("failed to canonicalize '{}'", path), }) { Ok(p) => p, Err(e) => { @@ -729,37 +664,31 @@ impl DeviceEnumerator { let entries = std::fs::read_dir(path).unwrap(); for entry in entries { - if let Err(e) = entry { - ret = Err(Error::Nix { - msg: format!("scan_dir_and_add_devices failed: read entries ({})", e), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); - continue; - } - - let entry = entry.unwrap(); + let entry = match entry.context(Io { + msg: "failed to read directory entry".to_string(), + }) { + Ok(i) => i, + Err(e) => { + ret = Err(e); + continue; + } + }; if !relevant_sysfs_subdir(&entry) { continue; } - if !self.match_sysname(entry.file_name().to_str().unwrap_or_default())? { + if !self.match_sysname(entry.file_name().to_str().unwrap_or_default()) { continue; } - let syspath = match entry.path().canonicalize() { + let syspath = match entry.path().canonicalize().context(Io { + msg: format!("failed to canonicalize '{:?}'", entry.path()), + }) { Ok(ret) => ret, Err(e) => { - if let Some(errno) = e.raw_os_error() { - if errno != libc::ENODEV { - ret = Err(Error::Nix { - msg: format!( - "scan_dir_and_add_devices failed: can't canonicalize '{:?}': {}", - entry, e - ), - source: Errno::from_i32(errno), - }); - } + if e.get_errno() != Errno::ENODEV { + ret = Err(e); } continue; } @@ -787,10 +716,7 @@ impl DeviceEnumerator { continue; } Err(e) => { - ret = Err(Error::Nix { - msg: format!("scan_dir_and_add_devices failed: {}", e), - source: e.get_errno(), - }); + ret = Err(e); continue; } }; @@ -806,15 +732,11 @@ impl DeviceEnumerator { }; // also include all potentially matching parent devices. - match self.add_parent_devices(device.clone(), MatchFlag::ALL) { - Ok(_) => {} - Err(e) => { - ret = Err(Error::Nix { - msg: format!("scan_dir_and_add_devices failed: {}", e), - source: e.get_errno(), - }); - } - }; + let _ = self + .add_parent_devices(device.clone(), MatchFlag::ALL) + .map_err(|e| { + ret = Err(e); + }); } ret @@ -828,15 +750,9 @@ impl DeviceEnumerator { subsystem: Option, ) -> Result<(), Error> { let path_str = "/sys/".to_string() + basedir.as_str(); - let path = match Path::new(&path_str).canonicalize() { - Ok(ret) => ret, - Err(e) => { - return Err(Error::Nix { - msg: format!("scan_dir failed: can't canonicalize '{}': {}", basedir, e), - source: Errno::EINVAL, - }); - } - }; + let path = Path::new(&path_str).canonicalize().context(Io { + msg: format!("fail to canonicalize '{}'", path_str), + })?; let dir = std::fs::read_dir(path); if let Err(e) = dir { @@ -853,16 +769,12 @@ impl DeviceEnumerator { let mut ret = Result::<(), Error>::Ok(()); for entry in dir.unwrap() { - let entry = match entry { + let entry = match entry.context(Io { + msg: format!("failed to read entry under '{}'", path_str), + }) { Ok(e) => e, Err(e) => { - ret = Err(Error::Nix { - msg: format!( - "scan_dir failed: can't read entries from directory '{}'", - path_str - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); + ret = Err(e); continue; } }; @@ -871,14 +783,13 @@ impl DeviceEnumerator { continue; } - match self.match_subsystem( + if !self.match_subsystem( subsystem .clone() .unwrap_or_else(|| entry.file_name().to_str().unwrap_or_default().to_string()) .as_str(), ) { - Ok(false) | Err(_) => continue, - Ok(true) => {} + continue; } let mut subdirs = vec![entry.file_name().to_str().unwrap_or_default().to_string()]; @@ -887,30 +798,30 @@ impl DeviceEnumerator { subdirs.push(subdir.clone().unwrap()); } - if let Err(e) = self.scan_dir_and_add_devices(basedir.clone(), subdirs) { - ret = Err(Error::Nix { - msg: format!("scan_dir failed: {}", e), - source: e.get_errno(), + let _ = self + .scan_dir_and_add_devices(basedir.clone(), subdirs) + .map_err(|e| { + ret = Err(e); }); - } } ret } /// scan devices for a single tag pub(crate) fn scan_devices_tag(&self, tag: &str) -> Result<(), Error> { - let path = Path::new("/run/devmaster/tags/").join(tag); - let dir = std::fs::read_dir(path); - let tag_dir = match dir { + let path = Path::new(self.base_path.borrow().as_str()) + .join(TAGS_BASE_DIR) + .join(tag); + let dir = std::fs::read_dir(&path); + let tag_dir = match dir.context(Io { + msg: format!("failed to read '{:?}'", path), + }) { Ok(d) => d, Err(e) => { - if e.raw_os_error().unwrap_or_default() == libc::ENOENT { + if e.get_errno() == Errno::ENOENT { return Ok(()); } else { - return Err(Error::Nix { - msg: format!("scan_dir failed: can't read directory '{}'", tag), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); + return Err(e); } } }; @@ -919,16 +830,12 @@ impl DeviceEnumerator { let mut ret = Ok(()); for entry in tag_dir { - let entry = match entry { + let entry = match entry.context(Io { + msg: format!("failed to read entry under '{:?}'", path), + }) { Ok(e) => e, Err(e) => { - ret = Err(Error::Nix { - msg: format!( - "scan_dir failed: can't read entries from directory '{}'", - tag - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); + ret = Err(e); continue; } }; @@ -977,10 +884,7 @@ impl DeviceEnumerator { for tag in self.match_tag.borrow().iter() { if let Err(e) = self.scan_devices_tag(tag) { - ret = Err(Error::Nix { - msg: format!("scan_devices_tags failed: {}", e), - source: e.get_errno(), - }); + ret = Err(e); } } ret @@ -1012,40 +916,31 @@ impl DeviceEnumerator { path: &str, stack: &mut HashSet, ) -> Result<(), Error> { - let entries = match std::fs::read_dir(path) { + let entries = match std::fs::read_dir(path).context(Io { + msg: format!("failed to read '{}'", path), + }) { Ok(ret) => ret, Err(e) => { - let errno = e.raw_os_error().unwrap_or_default(); - if errno == libc::ENOENT { + if e.get_errno() == Errno::ENOENT { return Ok(()); } else { - return Err(Error::Nix { - msg: format!( - "parent_crawl_children failed: can't read directory '{}'", - path - ), - source: Errno::from_i32(errno), - }); + return Err(e); } } }; let mut ret = Result::<(), Error>::Ok(()); for entry in entries { - let entry = match entry { + let entry = match entry.context(Io { + msg: format!("failed to read entry under'{:?}'", path), + }) { Ok(e) => e, Err(e) => { - ret = Err(Error::Nix { - msg: format!( - "parent_crawl_children failed: can't read entries under '{}'", - path - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); + ret = Err(e); continue; } }; - let file_name = entry.file_name(); - let file_name = file_name.to_str().unwrap_or_default(); + + let file_name = entry.file_name().to_str().unwrap_or_default().to_string(); if file_name.is_empty() || file_name.starts_with('.') { continue; } @@ -1055,29 +950,22 @@ impl DeviceEnumerator { continue; } - let entry_path = match entry.path().canonicalize() { + let entry_path = match entry.path().canonicalize().context(Io { + msg: format!("fail to canonicalize '{:?}'", entry.path()), + }) { Ok(p) => p.to_str().unwrap_or_default().to_string(), Err(e) => { - ret = Err(Error::Nix { - msg: format!( - "parent_crawl_children failed: can't canonicalize '{:?}'", - entry - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), - }); + ret = Err(e); continue; } }; - if let Ok(true) = self.match_sysname(file_name) { + if self.match_sysname(&file_name) { if let Err(e) = self.parent_add_child( &entry_path, MatchFlag::ALL & !(MatchFlag::SYSNAME | MatchFlag::PARENT), ) { - ret = Err(Error::Nix { - msg: format!("parent_crawl_children failed: {}", e), - source: e.get_errno(), - }); + ret = Err(e); }; } @@ -1099,17 +987,11 @@ impl DeviceEnumerator { .parent_add_child(path, MatchFlag::ALL & !MatchFlag::PARENT) .map(|_| ()) { - ret = Err(Error::Nix { - msg: format!("scan_devices_children failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e) } if let Err(e) = self.parent_crawl_children(path, &mut stack) { - ret = Err(Error::Nix { - msg: format!("scan_devices_children failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e) } } @@ -1117,10 +999,7 @@ impl DeviceEnumerator { stack.remove(&path); if let Err(e) = self.parent_crawl_children(&path, &mut stack) { - ret = Err(Error::Nix { - msg: format!("scan_devices_children failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e) } } @@ -1132,17 +1011,11 @@ impl DeviceEnumerator { let mut ret = Result::<(), Error>::Ok(()); if let Err(e) = self.scan_dir("bus".to_string(), Some("devices".to_string()), None) { - ret = Err(Error::Nix { - msg: format!("scan_devices_all failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e); } if let Err(e) = self.scan_dir("class".to_string(), None, None) { - ret = Err(Error::Nix { - msg: format!("scan_devices_all failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e); } ret @@ -1152,44 +1025,35 @@ impl DeviceEnumerator { pub(crate) fn scan_subsystems_all(&mut self) -> Result<(), Error> { let mut ret = Result::<(), Error>::Ok(()); - if self.match_subsystem("module").map_err(|e| Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - })? { - if let Err(e) = self.scan_dir_and_add_devices("module".to_string(), vec![]) { - ret = Err(Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - }) - } + if self.match_subsystem("module") { + let _ = self + .scan_dir_and_add_devices("module".to_string(), vec![]) + .map_err(|e| { + ret = Err(e); + log::error!("Enumerator: failed to scan modules"); + }); } - if self.match_subsystem("subsystem").map_err(|e| Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - })? { - if let Err(e) = self.scan_dir_and_add_devices("bus".to_string(), vec![]) { - ret = Err(Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - }) - } + if self.match_subsystem("subsystem") { + let _ = self + .scan_dir_and_add_devices("bus".to_string(), vec![]) + .map_err(|e| { + ret = Err(e); + log::error!("Enumerator: failed to scan subsystems"); + }); } - if self.match_subsystem("drivers").map_err(|e| Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - })? { - if let Err(e) = self.scan_dir( - "bus".to_string(), - Some("drivers".to_string()), - Some("drivers".to_string()), - ) { - ret = Err(Error::Nix { - msg: format!("scan_subsystems_all failed: {}", e), - source: e.get_errno(), - }) - } + if self.match_subsystem("drivers") { + let _ = self + .scan_dir( + "bus".to_string(), + Some("drivers".to_string()), + Some("drivers".to_string()), + ) + .map_err(|e| { + ret = Err(e); + log::error!("Enumerator: failed to scan drivers"); + }); } ret @@ -1209,24 +1073,11 @@ impl DeviceEnumerator { let mut ret = Result::<(), Error>::Ok(()); if !self.match_tag.borrow().is_empty() { - if let Err(e) = self.scan_devices_tags() { - ret = Err(Error::Nix { - msg: format!("scan_devices failed: {}", e), - source: e.get_errno(), - }) - } + let _ = self.scan_devices_tags().map_err(|e| ret = Err(e)); } else if !self.match_parent.borrow().is_empty() { - if let Err(e) = self.scan_devices_children() { - ret = Err(Error::Nix { - msg: format!("scan_devices failed: {}", e), - source: e.get_errno(), - }) - } + let _ = self.scan_devices_children().map_err(|e| ret = Err(e)); } else if let Err(e) = self.scan_devices_all() { - ret = Err(Error::Nix { - msg: format!("scan_devices failed: {}", e), - source: e.get_errno(), - }) + ret = Err(e); } self.scan_up_to_date.replace(true); @@ -1247,10 +1098,7 @@ impl DeviceEnumerator { self.devices_by_syspath.borrow_mut().clear(); self.devices.borrow_mut().clear(); - let ret = self.scan_subsystems_all().map_err(|e| Error::Nix { - msg: format!("scan_subsystems failed: {}", e), - source: e.get_errno(), - }); + let ret = self.scan_subsystems_all(); self.scan_up_to_date.replace(true); self.etype.replace(DeviceEnumerationType::Subsystems); @@ -1271,33 +1119,18 @@ impl DeviceEnumerator { let mut ret = Result::<(), Error>::Ok(()); if !self.match_tag.borrow().is_empty() { - if let Err(e) = self.scan_devices_tags() { - ret = Err(Error::Nix { - msg: format!("scan_devices_and_subsystems failed: {}", e), - source: e.get_errno(), - }) - } + ret = self.scan_devices_tags(); } else if !self.match_parent.borrow().is_empty() { - if let Err(e) = self.scan_devices_children() { - ret = Err(Error::Nix { - msg: format!("scan_devices_and_subsystems failed: {}", e), - source: e.get_errno(), - }) - } + ret = self.scan_devices_children(); } else { - if let Err(e) = self.scan_devices_all() { - ret = Err(Error::Nix { - msg: format!("scan_devices_and_subsystems failed: {}", e), - source: e.get_errno(), - }) - } + let _ = self.scan_devices_all().map_err(|e| { + ret = Err(e); + log::error!("Failed to scan devices."); + }); - if let Err(e) = self.scan_subsystems_all() { - ret = Err(Error::Nix { - msg: format!("scan_devices_and_subsystems failed: {}", e), - source: e.get_errno(), - }) - } + let _ = self.scan_subsystems_all().map_err(|e| { + ret = Err(e); + }); } self.scan_up_to_date.replace(true); @@ -1306,20 +1139,18 @@ impl DeviceEnumerator { ret } - /// pattern match - /// if the enumerator filter is of Glob type, use unix glob-fnmatch to check whether match - /// if the enumerator filter is of Regular type, use typical regular expression to check whether match - pub(crate) fn pattern_match(&self, pattern: &str, value: &str) -> Result { + /// Pattern match based on glob style pattern + pub(crate) fn pattern_match(&self, pattern: &str, value: &str) -> bool { let pattern = format!("{}\0", pattern); let value = format!("{}\0", value); - Ok(unsafe { + unsafe { fnmatch( pattern.as_ptr() as *const c_char, value.as_ptr() as *const c_char, 0, - ) - } == 0) + ) == 0 + } } /// if any exclude pattern matches, return false @@ -1330,38 +1161,32 @@ impl DeviceEnumerator { include_pattern_set: &HashSet, exclude_pattern_set: &HashSet, value: &str, - ) -> Result { + ) -> bool { for pattern in exclude_pattern_set.iter() { - if self.pattern_match(pattern, value).map_err(|e| Error::Nix { - msg: format!("set_pattern_match failed: pattern_match exclude ({})", e), - source: e.get_errno(), - })? { - return Ok(false); + if self.pattern_match(pattern, value) { + return false; } } if include_pattern_set.is_empty() { - return Ok(true); + return true; } for pattern in include_pattern_set.iter() { - if self.pattern_match(pattern, value).map_err(|e| Error::Nix { - msg: format!("set_pattern_match failed: pattern_match include ({})", e), - source: e.get_errno(), - })? { - return Ok(true); + if self.pattern_match(pattern, value) { + return true; } } - Ok(false) + false } } #[cfg(test)] mod tests { - use crate::device_enumerator::DeviceEnumerationType; + use crate::{device_enumerator::DeviceEnumerationType, Device}; - use super::DeviceEnumerator; + use super::{DeviceEnumerator, MatchInitializedType}; #[test] fn test_enumerator_inialize() { @@ -1374,6 +1199,17 @@ mod tests { enumerator.add_match_property("DEVTYPE", "char").unwrap(); enumerator.add_match_sysname("sda", true).unwrap(); enumerator.add_match_sysname("sdb", false).unwrap(); + enumerator.add_prioritized_subsystem("net").unwrap(); + enumerator.add_match_tag("devmaster").unwrap(); + + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + + enumerator.add_match_parent_incremental(&dev).unwrap(); + enumerator.add_match_parent(&dev).unwrap(); + enumerator.allow_uninitialized().unwrap(); + enumerator + .add_match_is_initialized(MatchInitializedType::ALL) + .unwrap(); } #[test] @@ -1409,6 +1245,176 @@ mod tests { #[test] fn test_pattern_match() { let enumerator = DeviceEnumerator::new(); - assert!(enumerator.pattern_match("hello*", "hello world").unwrap()); + assert!(enumerator.pattern_match("hello*", "hello world")); + assert!(!enumerator.pattern_match("hello*", "world")); + } + + trait State { + fn trans(self: Box, s: &str) -> Box; + } + + struct StateMachine { + state: Option>, + } + + impl StateMachine { + fn trans(&mut self, s: &str) { + if let Some(state) = self.state.take() { + self.state = Some(state.trans(s)); + } + } + } + + struct Init; + + impl State for Init { + fn trans(self: Box, s: &str) -> Box { + match s { + "net" => Box::new(Front {}), + _ => panic!(), + } + } + } + + struct Front; + + impl State for Front { + fn trans(self: Box, s: &str) -> Box { + match s { + "net" => Box::new(Front {}), + _ => Box::new(Tail {}), + } + } + } + + struct Tail; + + impl State for Tail { + fn trans(self: Box, s: &str) -> Box { + match s { + "net" => panic!(), + _ => Box::new(Tail {}), + } + } + } + + #[test] + fn test_priority_subsystem() { + /* If the prioritized subsystem is set, e.g., "net", + * the "net" devices should be ordered in the front of the scanned devices. + */ + let mut ert = DeviceEnumerator::new(); + ert.set_enumerator_type(DeviceEnumerationType::Devices); + ert.add_prioritized_subsystem("net").unwrap(); + ert.add_prioritized_subsystem("block").unwrap(); + + let mut sm = StateMachine { + state: Some(Box::new(Init {})), + }; + + for dev in ert.iter() { + sm.trans(&dev.borrow().get_subsystem().unwrap()); + } + + ert.sort_devices().unwrap(); + } + + #[test] + fn test_match_property() { + let mut ert = DeviceEnumerator::new(); + + let mut dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + + dev.add_property("helloxxx", "worldxxx").unwrap(); + + ert.add_match_property("hello*", "world*").unwrap(); + assert!(ert.match_property(&mut dev).unwrap()); + } + + #[test] + fn test_match_subsystem() { + let mut ert = DeviceEnumerator::new(); + + ert.add_match_subsystem("net", true).unwrap(); + ert.add_match_subsystem("block", false).unwrap(); + + assert!(ert.match_subsystem("net")); + assert!(!ert.match_subsystem("block")); + } + + #[test] + fn test_match_sysattr() { + let mut ert = DeviceEnumerator::new(); + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + + ert.add_match_sysattr("ifindex", "1", true).unwrap(); + ert.add_match_sysattr("address", "aa:aa:aa:aa:aa:aa", false) + .unwrap(); + + assert!(ert.match_sysattr(&dev).unwrap()); + } + + #[test] + fn test_match_sysname() { + let mut ert = DeviceEnumerator::new(); + ert.add_match_sysname("loop*", true).unwrap(); + ert.add_match_sysname("sd*", false).unwrap(); + assert!(ert.match_sysname("loop1")); + assert!(!ert.match_sysname("sda")); + } + + #[test] + fn test_match_tag() { + let mut ert = DeviceEnumerator::new(); + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + dev.set_base_path("/tmp/devmaster"); + dev.add_tag("devmaster", true); + dev.update_tag("devmaster", true).unwrap(); + ert.add_match_tag("devmaster").unwrap(); + assert!(ert.match_tag(&dev).unwrap()); + + ert.set_enumerator_type(DeviceEnumerationType::Devices); + ert.scan_devices().unwrap(); + + dev.update_tag("devmaster", false).unwrap(); + } + + #[test] + fn test_match_parent_incremental() { + let mut ert = DeviceEnumerator::new(); + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + assert!(ert.match_parent(&dev).unwrap()); + ert.add_match_parent_incremental(&dev).unwrap(); + assert!(ert.match_parent(&dev).unwrap()); + let dev_1 = Device::from_subsystem_sysname("drivers", "usb:usb").unwrap(); + assert!(!ert.match_parent(&dev_1).unwrap()); + + ert.set_enumerator_type(DeviceEnumerationType::Devices); + ert.scan_devices().unwrap(); + } + + #[test] + fn test_match_parent() { + let mut ert = DeviceEnumerator::new(); + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + ert.add_match_parent_incremental(&dev).unwrap(); + assert!(ert.match_parent(&dev).unwrap()); + } + + #[test] + fn test_match_is_initialized() { + let mut ert = DeviceEnumerator::new(); + let dev = Device::from_subsystem_sysname("net", "lo").unwrap(); + ert.add_match_is_initialized(MatchInitializedType::ALL) + .unwrap(); + assert!(ert.match_initialized(&dev).unwrap()); + } + + #[test] + fn test_scan_subsystems_all() { + let mut ert = DeviceEnumerator::new(); + ert.add_match_subsystem("module", true).unwrap(); + ert.add_match_subsystem("subsystem", true).unwrap(); + ert.add_match_subsystem("drivers", true).unwrap(); } } diff --git a/libs/device/src/error.rs b/libs/device/src/error.rs index 2cea6f4b..3d7307b7 100644 --- a/libs/device/src/error.rs +++ b/libs/device/src/error.rs @@ -12,6 +12,7 @@ //! Error definition of device //! +use basic::IN_SET; use nix::errno::Errno; use snafu::prelude::Snafu; @@ -77,10 +78,7 @@ impl Error { /// check whether the device error indicates the device is absent pub fn is_absent(&self) -> bool { - matches!( - self.get_errno(), - Errno::ENODEV | Errno::ENXIO | Errno::ENOENT - ) + IN_SET!(self.get_errno(), Errno::ENODEV, Errno::ENXIO, Errno::ENOENT) } pub(crate) fn replace_errno(self, from: Errno, to: Errno) -> Self { -- 2.33.0