From 0bae5d9fad3196c79f88a9d47eba38e89d5f291f Mon Sep 17 00:00:00 2001
From: chenjiayi <chenjiayi22@huawei.com>
Date: Wed, 8 Nov 2023 05:18:36 +0800
Subject: [PATCH 049/103] test(device): compress closures and add some unit
 cases

Some unnecessary closures may decrease line coverage, thus compress
them.

Also drop Clone trait implementation for Device as it is not necessary.
---
 .../src/bin/devctl/subcmds/devctl_info.rs     |   4 +-
 exts/devmaster/src/lib/builtin/hwdb.rs        |  12 +-
 exts/devmaster/src/lib/framework/devmaster.rs |   1 -
 exts/devmaster/src/lib/framework/job_queue.rs |   2 -
 .../src/lib/framework/uevent_monitor.rs       |   1 -
 libs/device/examples/device_from.rs           |   4 +-
 libs/device/src/device.rs                     | 689 +++++++-----------
 libs/device/src/device_enumerator.rs          |  10 +-
 libs/device/src/device_monitor.rs             |   2 +-
 libs/device/src/error.rs                      |  65 +-
 10 files changed, 324 insertions(+), 466 deletions(-)

diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
index 71c9254d..d4fcb6c4 100644
--- a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
@@ -286,7 +286,7 @@ fn print_device_chain(device: Device) -> Result<()> {
     while let Ok(parent) = child.get_parent() {
         print_all_attributes(&parent.borrow(), true)?;
 
-        child = parent.borrow().clone();
+        child = parent.borrow().shallow_clone().unwrap();
     }
 
     Ok(())
@@ -489,7 +489,7 @@ fn export_devices() -> Result<()> {
     }
 
     for device in e.iter() {
-        print_record(device.borrow().clone(), "");
+        print_record(device.borrow().shallow_clone().unwrap(), "");
     }
 
     Ok(())
diff --git a/exts/devmaster/src/lib/builtin/hwdb.rs b/exts/devmaster/src/lib/builtin/hwdb.rs
index 63308e37..0b797874 100644
--- a/exts/devmaster/src/lib/builtin/hwdb.rs
+++ b/exts/devmaster/src/lib/builtin/hwdb.rs
@@ -123,7 +123,7 @@ impl Hwdb {
         let mut last = false;
         let mut src_dev = match srcdev {
             Some(d) => d,
-            None => dev.borrow_mut().clone(),
+            None => dev.borrow_mut().shallow_clone().unwrap(),
         };
 
         loop {
@@ -131,7 +131,7 @@ impl Hwdb {
                 Ok(str_subsystem) => str_subsystem,
                 Err(_) => {
                     src_dev = match src_dev.get_parent() {
-                        Ok(d) => d.borrow_mut().clone(),
+                        Ok(d) => d.borrow_mut().shallow_clone().unwrap(),
                         Err(_) => break,
                     };
                     continue;
@@ -142,7 +142,7 @@ impl Hwdb {
             if let Some(str_subsystem) = subsystem {
                 if &dsubsys != str_subsystem {
                     src_dev = match src_dev.get_parent() {
-                        Ok(d) => d.borrow_mut().clone(),
+                        Ok(d) => d.borrow_mut().shallow_clone().unwrap(),
                         Err(_) => break,
                     };
                     continue;
@@ -170,7 +170,7 @@ impl Hwdb {
 
             if modalias.is_empty() {
                 src_dev = match src_dev.get_parent() {
-                    Ok(d) => d.borrow_mut().clone(),
+                    Ok(d) => d.borrow_mut().shallow_clone().unwrap(),
                     Err(_) => break,
                 };
                 continue;
@@ -189,7 +189,7 @@ impl Hwdb {
             }
 
             src_dev = match src_dev.get_parent() {
-                Ok(d) => d.borrow_mut().clone(),
+                Ok(d) => d.borrow_mut().shallow_clone().unwrap(),
                 Err(_) => break,
             };
         }
@@ -258,7 +258,7 @@ impl Builtin for Hwdb {
                 Ok(srcdev) => Some(srcdev),
                 Err(e) => {
                     return Err(Error::Other {
-                        msg: format!("Failed to create sd_device object '{:?}'", dev),
+                        msg: format!("Failed to create device object '{}'", device_id),
                         errno: e.get_errno(),
                     });
                 }
diff --git a/exts/devmaster/src/lib/framework/devmaster.rs b/exts/devmaster/src/lib/framework/devmaster.rs
index 370f4e84..94b93ca0 100644
--- a/exts/devmaster/src/lib/framework/devmaster.rs
+++ b/exts/devmaster/src/lib/framework/devmaster.rs
@@ -24,7 +24,6 @@ use std::{
 };
 
 /// encapsulate all submanagers
-#[derive(Debug)]
 pub struct Devmaster {
     /// reference to events
     pub(crate) events: Rc<Events>,
diff --git a/exts/devmaster/src/lib/framework/job_queue.rs b/exts/devmaster/src/lib/framework/job_queue.rs
index e3e6e90b..7c6d6a8d 100644
--- a/exts/devmaster/src/lib/framework/job_queue.rs
+++ b/exts/devmaster/src/lib/framework/job_queue.rs
@@ -51,7 +51,6 @@ impl Display for JobState {
 }
 
 /// device job
-#[derive(Debug)]
 pub struct DeviceJob {
     /// internal device
     pub device: Device,
@@ -132,7 +131,6 @@ impl PartialEq for DeviceJob {
 }
 
 /// job queue
-#[derive(Debug)]
 pub struct JobQueue {
     /// internal container of jobs
     pub(crate) jobs: RefCell<VecDeque<Rc<DeviceJob>>>,
diff --git a/exts/devmaster/src/lib/framework/uevent_monitor.rs b/exts/devmaster/src/lib/framework/uevent_monitor.rs
index fe7d19c3..809071d1 100644
--- a/exts/devmaster/src/lib/framework/uevent_monitor.rs
+++ b/exts/devmaster/src/lib/framework/uevent_monitor.rs
@@ -22,7 +22,6 @@ use std::os::unix::io::RawFd;
 use std::rc::Rc;
 
 /// uevent monitor
-#[derive(Debug)]
 pub struct UeventMonitor {
     /// receive uevent from netlink socket
     device_monitor: DeviceMonitor,
diff --git a/libs/device/examples/device_from.rs b/libs/device/examples/device_from.rs
index 29e24bbc..146543ee 100644
--- a/libs/device/examples/device_from.rs
+++ b/libs/device/examples/device_from.rs
@@ -30,11 +30,11 @@ fn main() {
         let dev = Device::from_subsystem_sysname("drivers", "usb:hub").unwrap();
         println!("{}", dev.get_sysname().unwrap());
         println!("{}", dev.get_subsystem().unwrap());
-        println!("{:?}", dev);
+        println!("{}", dev.get_device_id().unwrap());
     }
 
     {
         let dev = Device::from_ifindex(2).unwrap();
-        println!("{:?}", dev);
+        println!("{}", dev.get_device_id().unwrap());
     }
 }
diff --git a/libs/device/src/device.rs b/libs/device/src/device.rs
index 6673d823..284cd6e9 100644
--- a/libs/device/src/device.rs
+++ b/libs/device/src/device.rs
@@ -12,7 +12,6 @@
 
 //! struct Device
 //!
-use crate::err_wrapper;
 use crate::utils::readlink_value;
 use crate::{error::*, DeviceAction};
 use basic::fs_util::{chmod, open_temporary, touch_file};
@@ -49,7 +48,7 @@ pub const DB_BASE_DIR: &str = "data";
 pub const TAGS_BASE_DIR: &str = "tags";
 
 /// Device
-#[derive(Debug, Clone)]
+#[derive(Debug)]
 pub struct Device {
     /// inotify handler
     pub watch_handle: RefCell<i32>,
@@ -355,33 +354,16 @@ impl Device {
 
         let buf_trans: &[u8] = unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const _, 16) };
 
-        let ifname = String::from_utf8(buf_trans.to_vec()).map_err(|e| Error::Nix {
-            msg: format!("from_ifindex failed: from_utf8 {:?} ({})", buf_trans, e),
-            source: Errno::EINVAL,
+        let ifname = String::from_utf8(buf_trans.to_vec()).context(FromUtf8 {
+            msg: format!("invalid utf-8 string {:?}", buf_trans),
         })?;
 
         let syspath = format!("/sys/class/net/{}", ifname.trim_matches(char::from(0)));
-        let dev = Self::from_syspath(&syspath, true).map_err(|e| Error::Nix {
-            msg: format!("from_ifindex failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        let dev = Self::from_syspath(&syspath, true)?;
 
-        let i = match dev.get_ifindex() {
-            Ok(i) => i,
-            Err(e) => {
-                if e.get_errno() == Errno::ENOENT {
-                    return Err(Error::Nix {
-                        msg: format!("from_ifindex failed: {}", e),
-                        source: Errno::ENXIO,
-                    });
-                }
-
-                return Err(Error::Nix {
-                    msg: format!("from_ifindex failed: {}", e),
-                    source: e.get_errno(),
-                });
-            }
-        };
+        let i = dev
+            .get_ifindex()
+            .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENXIO))?;
 
         if i != ifindex {
             return Err(Error::Nix {
@@ -393,8 +375,9 @@ impl Device {
         Ok(dev)
     }
 
-    /// create a Device instance from subsystem and sysname
-    /// if subsystem is 'drivers', sysname should be like 'xxx:yyy'
+    /// Create a Device instance from subsystem and sysname.
+    ///
+    /// If subsystem is 'drivers', sysname should be like 'xxx:yyy'
     pub fn from_subsystem_sysname(subsystem: &str, sysname: &str) -> Result<Device, Error> {
         let sysname = sysname.replace('/', "!");
         if subsystem == "subsystem" {
@@ -506,10 +489,12 @@ impl Device {
         })
     }
 
-    /// set sysattr value
+    /// Set sysattr value.
+    ///
+    /// If the sysattr is not 'uevent', the value will be cached.
     pub fn set_sysattr_value(&self, sysattr: &str, value: Option<&str>) -> Result<(), Error> {
         if value.is_none() {
-            self.remove_cached_sysattr_value(sysattr)?;
+            self.remove_cached_sysattr_value(sysattr);
             return Ok(());
         }
 
@@ -529,7 +514,7 @@ impl Device {
         };
 
         if let Err(e) = file.write(value.unwrap().as_bytes()) {
-            self.remove_cached_sysattr_value(sysattr)?;
+            self.remove_cached_sysattr_value(sysattr);
             return Err(Error::Nix {
                 msg: format!(
                     "set_sysattr_value failed: can't write sysattr '{}'",
@@ -559,28 +544,18 @@ impl Device {
 
         match id.chars().next() {
             Some('b') | Some('c') => {
-                let devnum = parse_devnum(&id[1..]).map_err(|_| Error::Nix {
+                let devnum = parse_devnum(&id[1..]).context(Basic {
                     msg: format!("from_device_id failed: parse_devnum '{}' failed", id),
-                    source: Errno::EINVAL,
                 })?;
 
-                return Device::from_devnum(id.chars().next().unwrap(), devnum).map_err(|e| {
-                    Error::Nix {
-                        msg: format!("from_device_id failed: {}", e),
-                        source: e.get_errno(),
-                    }
-                });
+                Device::from_devnum(id.chars().next().unwrap(), devnum)
             }
             Some('n') => {
-                let ifindex = parse_ifindex(&id[1..]).map_err(|_| Error::Nix {
+                let ifindex = parse_ifindex(&id[1..]).context(Basic {
                     msg: format!("from_device_id failed: parse_ifindex '{}' failed", id),
-                    source: Errno::EINVAL,
                 })?;
 
-                Device::from_ifindex(ifindex).map_err(|e| Error::Nix {
-                    msg: format!("from_device_id failed: {}", e),
-                    source: e.get_errno(),
-                })
+                Device::from_ifindex(ifindex)
             }
             Some('+') => {
                 let sep = match id.find(':') {
@@ -604,10 +579,7 @@ impl Device {
 
                 let subsystem = id[1..sep].to_string();
                 let sysname = id[sep + 1..].to_string();
-                Device::from_subsystem_sysname(&subsystem, &sysname).map_err(|e| Error::Nix {
-                    msg: format!("from_device_id failed: {}", e),
-                    source: e.get_errno(),
-                })
+                Device::from_subsystem_sysname(&subsystem, &sysname)
             }
             _ => Err(Error::Nix {
                 msg: format!("from_device_id failed: invalid id '{}'", id),
@@ -648,7 +620,7 @@ impl Device {
     /// get the sysname of the device
     pub fn get_sysname(&self) -> Result<String, Error> {
         if self.sysname.borrow().is_empty() {
-            err_wrapper!(self.set_sysname_and_sysnum(), "get_sysname")?;
+            self.set_sysname_and_sysnum()?;
         }
 
         Ok(self.sysname.borrow().clone())
@@ -729,10 +701,7 @@ impl Device {
             // e.g. /sys/devices/pci0000:00/0000:00:10.0/host2/target2:0:1/2:0:1:0/block/sda/subsystem -> ../../../../../../../../class/block
             // get `block`
             let filename = if Path::exists(Path::new(subsystem_path)) {
-                readlink_value(subsystem_path).map_err(|e| Error::Nix {
-                    msg: format!("get_subsystem failed: {}", e),
-                    source: e.get_errno(),
-                })?
+                readlink_value(subsystem_path)?
             } else {
                 "".to_string()
             };
@@ -766,10 +735,7 @@ impl Device {
 
     /// get the ifindex of device
     pub fn get_ifindex(&self) -> Result<u32, Error> {
-        self.read_uevent_file().map_err(|e| Error::Nix {
-            msg: format!("get_ifindex failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_uevent_file()?;
 
         if *self.ifindex.borrow() == 0 {
             return Err(Error::Nix {
@@ -802,12 +768,7 @@ impl Device {
 
     /// get devnum
     pub fn get_devnum(&self) -> Result<u64, Error> {
-        match self.read_uevent_file() {
-            Ok(_) => {}
-            Err(e) => {
-                return Err(e);
-            }
-        }
+        self.read_uevent_file()?;
 
         if major(*self.devnum.borrow()) == 0 {
             return Err(Error::Nix {
@@ -870,10 +831,7 @@ impl Device {
     /// get device sysnum
     pub fn get_sysnum(&self) -> Result<String, Error> {
         if self.sysname.borrow().is_empty() {
-            self.set_sysname_and_sysnum().map_err(|e| Error::Nix {
-                msg: format!("get_sysnum failed: {}", e),
-                source: e.get_errno(),
-            })?;
+            self.set_sysname_and_sysnum()?;
         }
 
         if self.sysnum.borrow().is_empty() {
@@ -915,10 +873,7 @@ impl Device {
 
     /// get device diskseq
     pub fn get_diskseq(&self) -> Result<u64, Error> {
-        self.read_uevent_file().map_err(|e| Error::Nix {
-            msg: format!("get_diskseq failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_uevent_file()?;
 
         if *self.diskseq.borrow() == 0 {
             return Err(Error::Nix {
@@ -935,7 +890,6 @@ impl Device {
 
     /// get is initialized
     pub fn get_is_initialized(&self) -> Result<bool, Error> {
-        // match self.read_db
         match self.read_db() {
             Ok(_) => {}
             Err(e) => {
@@ -979,30 +933,21 @@ impl Device {
 
     /// check whether the device has the tag
     pub fn has_tag(&self, tag: &str) -> Result<bool, Error> {
-        self.read_db().map_err(|e| Error::Nix {
-            msg: format!("has_tag failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_db()?;
 
         Ok(self.all_tags.borrow().contains(tag))
     }
 
     /// check whether the device has the current tag
     pub fn has_current_tag(&self, tag: &str) -> Result<bool, Error> {
-        self.read_db().map_err(|e| Error::Nix {
-            msg: format!("has_tag failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_db()?;
 
         Ok(self.current_tags.borrow().contains(tag))
     }
 
     /// get the value of specific device property
     pub fn get_property_value(&self, key: &str) -> Result<String, Error> {
-        self.properties_prepare().map_err(|e| Error::Nix {
-            msg: format!("get_property_value failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.properties_prepare()?;
 
         match self.properties.borrow().get(key) {
             Some(v) => Ok(v.clone()),
@@ -1083,27 +1028,22 @@ impl Device {
                     let mut file = std::fs::OpenOptions::new()
                         .read(true)
                         .open(&sysattr_path)
-                        .map_err(|e| Error::Nix {
+                        .context(Io {
                             msg: format!(
-                                "get_sysattr_value failed: can't open sysattr '{}': {}",
-                                sysattr, e
+                                "get_sysattr_value failed: can't open sysattr '{}'",
+                                sysattr
                             ),
-                            source: Errno::from_i32(e.raw_os_error().unwrap_or_default()),
                         })?;
                     let mut value = String::new();
-                    file.read_to_string(&mut value).map_err(|e| Error::Nix {
-                        msg: format!(
-                            "get_sysattr_value failed: can't read sysattr '{}': {}",
-                            sysattr, e
-                        ),
-                        source: Errno::from_i32(e.raw_os_error().unwrap_or_default()),
+                    file.read_to_string(&mut value).context(Io {
+                        msg: format!("get_sysattr_value failed: can't read sysattr '{}'", sysattr),
                     })?;
                     value.trim_end().to_string()
                 }
             }
 
             Err(e) => {
-                self.remove_cached_sysattr_value(sysattr).unwrap();
+                self.remove_cached_sysattr_value(sysattr);
                 return Err(Error::Nix {
                     msg: format!("get_sysattr_value failed: can't lstat '{}'", sysattr_path),
                     source: e,
@@ -1111,11 +1051,7 @@ impl Device {
             }
         };
 
-        self.cache_sysattr_value(sysattr, &value)
-            .map_err(|e| Error::Nix {
-                msg: format!("get_sysattr_value failed: {}", e),
-                source: e.get_errno(),
-            })?;
+        self.cache_sysattr_value(sysattr, &value)?;
 
         Ok(value)
     }
@@ -1133,15 +1069,9 @@ impl Device {
 
         let s = format!("{}", action);
 
-        let id = match randomize() {
-            Ok(id) => id,
-            Err(e) => {
-                return Err(Error::Nix {
-                    msg: "Failed to randomize".to_string(),
-                    source: e,
-                })
-            }
-        };
+        let id = randomize().context(Nix {
+            msg: "Failed to randomize".to_string(),
+        })?;
 
         let j = s + " " + &id.to_string();
 
@@ -1152,33 +1082,12 @@ impl Device {
 
     /// open device
     pub fn open(&self, oflags: OFlag) -> Result<File, Error> {
-        let devname = self.get_devname().map_err(|e| {
-            if e.get_errno() == Errno::ENOENT {
-                Error::Nix {
-                    msg: format!("open failed: {}", e),
-                    source: Errno::ENOEXEC,
-                }
-            } else {
-                Error::Nix {
-                    msg: format!("open failed: {}", e),
-                    source: e.get_errno(),
-                }
-            }
-        })?;
-
-        let devnum = self.get_devnum().map_err(|e| {
-            if e.get_errno() == Errno::ENOENT {
-                Error::Nix {
-                    msg: format!("open failed: {}", e),
-                    source: Errno::ENOEXEC,
-                }
-            } else {
-                Error::Nix {
-                    msg: format!("open failed: {}", e),
-                    source: e.get_errno(),
-                }
-            }
-        })?;
+        let devname = self
+            .get_devname()
+            .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENOEXEC))?;
+        let devnum = self
+            .get_devnum()
+            .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENOEXEC))?;
 
         let subsystem = match self.get_subsystem() {
             Ok(s) => s,
@@ -1212,19 +1121,13 @@ impl Device {
             }
         };
 
-        let stat = match nix::sys::stat::fstat(file.as_raw_fd()) {
-            Ok(s) => s,
-            Err(e) => {
-                return Err(Error::Nix {
-                    msg: format!(
-                        "open failed: can't fstat fd {} for '{}'",
-                        file.as_raw_fd(),
-                        devname
-                    ),
-                    source: e,
-                })
-            }
-        };
+        let stat = nix::sys::stat::fstat(file.as_raw_fd()).context(Nix {
+            msg: format!(
+                "open failed: can't fstat fd {} for '{}'",
+                file.as_raw_fd(),
+                devname
+            ),
+        })?;
 
         if stat.st_rdev != devnum {
             return Err(Error::Nix {
@@ -1265,18 +1168,11 @@ impl Device {
 
         let mut diskseq: u64 = 0;
 
-        if self.get_is_initialized().map_err(|e| Error::Nix {
-            msg: format!("open failed: {}", e),
-            source: e.get_errno(),
-        })? {
+        if self.get_is_initialized()? {
             match self.get_property_value("ID_IGNORE_DISKSEQ") {
                 Ok(value) => {
-                    if !value.parse::<bool>().map_err(|e| Error::Nix {
-                        msg: format!(
-                            "open failed: failed to parse value '{}' to boolean: {}",
-                            value, e
-                        ),
-                        source: Errno::EINVAL,
+                    if !value.parse::<bool>().context(ParseBool {
+                        msg: format!("invalid value '{}'", value),
                     })? {
                         match self.get_diskseq() {
                             Ok(n) => diskseq = n,
@@ -1302,28 +1198,16 @@ impl Device {
             }
         }
 
-        let file2 =
-            basic::fd_util::fd_reopen(file.as_raw_fd(), oflags).map_err(|e| Error::Nix {
-                msg: format!("open failed: {}", e),
-                source: match e {
-                    basic::Error::Nix { source } => source,
-                    _ => Errno::EINVAL,
-                },
-            })?;
+        let file2 = basic::fd_util::fd_reopen(file.as_raw_fd(), oflags).context(Basic {
+            msg: format!("failed to open {}", file.as_raw_fd()),
+        })?;
 
         if diskseq == 0 {
             return Ok(file2);
         }
 
-        let q = basic::fd_util::fd_get_diskseq(file2.as_raw_fd()).map_err(|e| Error::Nix {
-            msg: format!(
-                "open failed: failed to get diskseq on fd {}",
-                file2.as_raw_fd()
-            ),
-            source: match e {
-                basic::Error::Nix { source } => source,
-                _ => Errno::EINVAL,
-            },
+        let q = basic::fd_util::fd_get_diskseq(file2.as_raw_fd()).context(Basic {
+            msg: format!("failed to get diskseq on fd {}", file2.as_raw_fd()),
         })?;
 
         if q != diskseq {
@@ -1352,18 +1236,9 @@ impl Device {
 
     /// shadow clone a device object and import properties from db
     pub fn clone_with_db(&self) -> Result<Device, Error> {
-        let device = self.shallow_clone().map_err(|e| Error::Nix {
-            msg: format!("clone_with_db failed: {}", e),
-            source: e.get_errno(),
-        })?;
-
-        device.read_db().map_err(|e| Error::Nix {
-            msg: format!("clone_with_db failed: {}", e),
-            source: e.get_errno(),
-        })?;
-
+        let device = self.shallow_clone()?;
+        device.read_db()?;
         device.sealed.replace(true);
-
         Ok(device)
     }
 
@@ -1427,22 +1302,14 @@ impl Device {
     ///
     /// The format is like:
     ///
-    ///     character device:       c<major>:<minor>
-    ///
-    ///     block device:           b<major>:<minor>
-    ///
-    ///     network interface:      n<ifindex>
-    ///
-    ///     drivers:                +drivers:<driver subsystem>:<sysname>
-    ///
-    ///     other subsystems:       +<subsystem>:<sysname>
-    ///
+    /// - character device:       c<major>:<minor>
+    /// - block device:           b<major>:<minor>
+    /// - network interface:      n<ifindex>
+    /// - drivers:                +drivers:<driver subsystem>:<sysname>
+    /// - other subsystems:       +<subsystem>:<sysname>
     pub fn get_device_id(&self) -> Result<String, Error> {
         if self.device_id.borrow().is_empty() {
-            let subsystem = self.get_subsystem().map_err(|e| Error::Nix {
-                msg: format!("get_device_id failed: {}", e),
-                source: e.get_errno(),
-            })?;
+            let subsystem = self.get_subsystem()?;
 
             let id: String;
             if let Ok(devnum) = self.get_devnum() {
@@ -1455,10 +1322,7 @@ impl Device {
             } else if let Ok(ifindex) = self.get_ifindex() {
                 id = format!("n{}", ifindex);
             } else {
-                let sysname = self.get_sysname().map_err(|e| Error::Nix {
-                    msg: format!("get_device_id failed: {}", e),
-                    source: e.get_errno(),
-                })?;
+                let sysname = self.get_sysname()?;
 
                 if subsystem == "drivers" {
                     id = format!("+drivers:{}:{}", self.driver_subsystem.borrow(), sysname);
@@ -1571,21 +1435,14 @@ impl Device {
         let db_path = format!("{}/{}/{}", self.base_path.borrow(), DB_BASE_DIR, id);
 
         if !has_info && *self.devnum.borrow() == 0 && *self.ifindex.borrow() == 0 {
-            unlink(db_path.as_str()).map_err(|e| Error::Nix {
+            unlink(db_path.as_str()).context(Nix {
                 msg: format!("update_db failed: can't unlink db '{}'", db_path),
-                source: e,
             })?;
-
             return Ok(());
         }
 
-        create_dir_all(&format!("{}/{}", self.base_path.borrow(), DB_BASE_DIR)).map_err(|e| {
-            Error::Nix {
-                msg: "update_db failed: can't create db directory".to_string(),
-                source: e
-                    .raw_os_error()
-                    .map_or_else(|| nix::Error::EIO, nix::Error::from_i32),
-            }
+        create_dir_all(&format!("{}/{}", self.base_path.borrow(), DB_BASE_DIR)).context(Io {
+            msg: "failed to create db directory".to_string(),
         })?;
 
         if let Err(e) = chmod(
@@ -1595,22 +1452,14 @@ impl Device {
             log::error!("Failed to set permission for /run/devmaster/data/: {}", e);
         }
 
-        let (mut file, tmp_file) = open_temporary(&db_path).map_err(|e| {
-            let errno = match e {
-                basic::error::Error::Nix { source } => source,
-                _ => nix::Error::EINVAL,
-            };
-            Error::Nix {
-                msg: "update_db failed: can't open temporary file".to_string(),
-                source: errno,
-            }
+        let (mut file, tmp_file) = open_temporary(&db_path).context(Basic {
+            msg: "can't open temporary file".to_string(),
         })?;
 
-        self.atomic_create_db(&mut file, tmp_file.as_str(), db_path.as_str())
-            .map_err(|e| {
-                Self::cleanup(&db_path, &tmp_file);
-                e
-            })?;
+        if let Err(e) = self.atomic_create_db(&mut file, tmp_file.as_str(), db_path.as_str()) {
+            Self::cleanup(&db_path, &tmp_file);
+            return Err(e);
+        }
 
         Ok(())
     }
@@ -1713,9 +1562,8 @@ impl Device {
         );
 
         if add {
-            touch_file(&tag_path, true, Some(0o444), None, None).map_err(|e| Error::Nix {
-                msg: format!("tag_persist failed: can't touch file '{}': {}", tag_path, e),
-                source: nix::Error::EINVAL,
+            touch_file(&tag_path, true, Some(0o444), None, None).context(Basic {
+                msg: format!("can't touch file '{}'", tag_path),
             })?;
 
             if let Err(e) = chmod(
@@ -1765,10 +1613,7 @@ impl Device {
 
     /// read database
     pub fn read_db(&self) -> Result<(), Error> {
-        self.read_db_internal(false).map_err(|e| Error::Nix {
-            msg: format!("read_db failed: {}", e),
-            source: e.get_errno(),
-        })
+        self.read_db_internal(false)
     }
 
     /// read database internally
@@ -1777,18 +1622,11 @@ impl Device {
             return Ok(());
         }
 
-        let id = self.get_device_id().map_err(|e| Error::Nix {
-            msg: format!("read_db_internal failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        let id = self.get_device_id()?;
 
         let path = format!("{}/{}/{}", self.base_path.borrow(), DB_BASE_DIR, id);
 
         self.read_db_internal_filename(&path)
-            .map_err(|e| Error::Nix {
-                msg: format!("read_db_internal failed: {}", e),
-                source: e.get_errno(),
-            })
     }
 
     /// get properties nulstr, if it is out of date, update it
@@ -1836,10 +1674,7 @@ impl Device {
         }
 
         // verify subsystem
-        let subsystem_ret = device.get_subsystem().map_err(|e| Error::Nix {
-            msg: format!("from_mode_and_devnum failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        let subsystem_ret = device.get_subsystem()?;
         if (subsystem_ret == "block") != ((mode & S_IFMT) == S_IFBLK) {
             return Err(Error::Nix {
                 msg: "from_mode_and_devnum failed: inconsistent subsystem".to_string(),
@@ -1942,19 +1777,9 @@ impl Device {
 
     /// set the sysname and sysnum of device object
     pub fn set_sysname_and_sysnum(&self) -> Result<(), Error> {
-        let sysname = match self.devpath.borrow().rfind('/') {
-            Some(i) => String::from(&self.devpath.borrow()[i + 1..]),
-            None => {
-                return Err(Error::Nix {
-                    msg: format!(
-                        "set_sysname_and_sysnum failed: invalid devpath '{}'",
-                        self.devpath.borrow()
-                    ),
-                    source: Errno::EINVAL,
-                });
-            }
-        };
-
+        /* The devpath is validated to begin with '/' when setting syspath. */
+        let idx = self.devpath.borrow().rfind('/').unwrap();
+        let sysname = String::from(&self.devpath.borrow()[idx + 1..]);
         let sysname = sysname.replace('!', "/");
 
         let mut ridx = sysname.len();
@@ -2146,15 +1971,10 @@ impl Device {
     /// set ifindex
     pub fn set_ifindex(&self, ifindex: &str) -> Result<(), Error> {
         self.add_property_internal("IFINDEX", ifindex).unwrap();
-        self.ifindex.replace(match ifindex.parse::<u32>() {
-            Ok(idx) => idx,
-            Err(e) => {
-                return Err(Error::Nix {
-                    msg: format!("set_ifindex failed: {}", e),
-                    source: Errno::EINVAL,
-                });
-            }
-        });
+        self.ifindex
+            .replace(ifindex.parse::<u32>().context(ParseInt {
+                msg: format!("invalid integer '{}'", ifindex),
+            })?);
         Ok(())
     }
 
@@ -2172,12 +1992,8 @@ impl Device {
 
     /// set devmode
     pub fn set_devmode(&self, devmode: &str) -> Result<(), Error> {
-        let m = Some(mode_t::from_str_radix(devmode, 8).map_err(|e| Error::Nix {
-            msg: format!(
-                "set_devmode failed: can't change '{}' to mode: {}",
-                devmode, e
-            ),
-            source: Errno::EINVAL,
+        let m = Some(mode_t::from_str_radix(devmode, 8).context(ParseInt {
+            msg: format!("invalid octal mode '{}'", devmode),
         })?);
 
         self.devmode.replace(m);
@@ -2189,9 +2005,8 @@ impl Device {
 
     /// set device uid
     pub fn set_devuid(&self, devuid: &str) -> Result<(), Error> {
-        let uid = devuid.parse::<uid_t>().map_err(|e| Error::Nix {
-            msg: format!("set_devuid failed: can't change '{}' to uid: {}", devuid, e),
-            source: Errno::EINVAL,
+        let uid = devuid.parse::<uid_t>().context(ParseInt {
+            msg: format!("invalid uid '{}'", devuid),
         })?;
 
         self.devuid.replace(Some(Uid::from_raw(uid)));
@@ -2203,9 +2018,8 @@ impl Device {
 
     /// set device gid
     pub fn set_devgid(&self, devgid: &str) -> Result<(), Error> {
-        let gid = devgid.parse::<gid_t>().map_err(|e| Error::Nix {
-            msg: format!("set_devgid failed: can't change '{}' to gid: {}", devgid, e),
-            source: Errno::EINVAL,
+        let gid = devgid.parse::<gid_t>().context(ParseInt {
+            msg: format!("invalid gid '{}'", devgid),
         })?;
 
         self.devgid.replace(Some(Gid::from_raw(gid)));
@@ -2217,15 +2031,10 @@ impl Device {
 
     /// set devnum
     pub fn set_devnum(&self, major: &str, minor: &str) -> Result<(), Error> {
-        let major_num: u64 = match major.parse() {
-            Ok(n) => n,
-            Err(e) => {
-                return Err(Error::Nix {
-                    msg: format!("set_devnum failed: invalid major number '{}': {}", major, e),
-                    source: Errno::EINVAL,
-                });
-            }
-        };
+        let major_num: u64 = major.parse().context(ParseInt {
+            msg: format!("invalid major number '{}'", major),
+        })?;
+
         let minor_num: u64 = match minor.parse() {
             Ok(n) => n,
             Err(e) => {
@@ -2247,15 +2056,9 @@ impl Device {
     pub fn set_diskseq(&self, diskseq: &str) -> Result<(), Error> {
         self.add_property_internal("DISKSEQ", diskseq).unwrap();
 
-        let diskseq_num: u64 = match diskseq.parse() {
-            Ok(n) => n,
-            Err(e) => {
-                return Err(Error::Nix {
-                    msg: format!("set_diskseq failed: invalid diskseq '{}': {}", diskseq, e),
-                    source: Errno::EINVAL,
-                });
-            }
-        };
+        let diskseq_num: u64 = diskseq.parse().context(ParseInt {
+            msg: format!("invalid diskseq '{}'", diskseq),
+        })?;
 
         self.diskseq.replace(diskseq_num);
 
@@ -2271,18 +2074,7 @@ impl Device {
 
     /// set action from string
     pub fn set_action_from_string(&self, action_s: &str) -> Result<(), Error> {
-        let action = match action_s.parse::<DeviceAction>() {
-            Ok(a) => a,
-            Err(_) => {
-                return Err(Error::Nix {
-                    msg: format!(
-                        "set_action_from_string failed: invalid action '{}'",
-                        action_s
-                    ),
-                    source: Errno::EINVAL,
-                });
-            }
-        };
+        let action = action_s.parse::<DeviceAction>()?;
 
         self.set_action(action);
 
@@ -2291,19 +2083,9 @@ impl Device {
 
     /// set seqnum from string
     pub fn set_seqnum_from_string(&self, seqnum_s: &str) -> Result<(), Error> {
-        let seqnum: u64 = match seqnum_s.parse() {
-            Ok(n) => n,
-            Err(_) => {
-                return Err(Error::Nix {
-                    msg: format!(
-                        "set_seqnum_from_string failed: invalid seqnum '{}'",
-                        seqnum_s
-                    ),
-                    source: Errno::EINVAL,
-                });
-            }
-        };
-
+        let seqnum: u64 = seqnum_s.parse().context(ParseInt {
+            msg: format!("invalid seqnum '{}'", seqnum_s),
+        })?;
         self.set_seqnum(seqnum);
         Ok(())
     }
@@ -2325,7 +2107,7 @@ impl Device {
     /// cache sysattr value
     pub fn cache_sysattr_value(&self, sysattr: &str, value: &str) -> Result<(), Error> {
         if value.is_empty() {
-            self.remove_cached_sysattr_value(sysattr)?;
+            self.remove_cached_sysattr_value(sysattr);
         } else {
             self.sysattr_values
                 .borrow_mut()
@@ -2336,34 +2118,27 @@ impl Device {
     }
 
     /// remove cached sysattr value
-    pub fn remove_cached_sysattr_value(&self, sysattr: &str) -> Result<(), Error> {
+    pub fn remove_cached_sysattr_value(&self, sysattr: &str) {
         self.sysattr_values.borrow_mut().remove(sysattr);
-
-        Ok(())
     }
 
     /// get cached sysattr value
     pub fn get_cached_sysattr_value(&self, sysattr: &str) -> Result<String, Error> {
         if !self.sysattr_values.borrow().contains_key(sysattr) {
             return Err(Error::Nix {
-                msg: format!(
-                    "get_cached_sysattr_value failed: no cached sysattr '{}'",
-                    sysattr
-                ),
+                msg: format!("no cached sysattr '{}'", sysattr),
                 source: Errno::ESTALE,
             });
         }
 
-        match self.sysattr_values.borrow().get(sysattr) {
-            Some(value) => Ok(value.clone()),
-            None => Err(Error::Nix {
-                msg: format!(
-                    "get_cached_sysattr_value failed: non-existing sysattr '{}'",
-                    sysattr
-                ),
+        self.sysattr_values
+            .borrow()
+            .get(sysattr)
+            .cloned()
+            .ok_or(Error::Nix {
+                msg: format!("non-existing sysattr '{}'", sysattr),
                 source: Errno::ENOENT,
-            }),
-        }
+            })
     }
 
     /// new from child
@@ -2378,8 +2153,7 @@ impl Device {
                 Some(p) => {
                     if p == Path::new("/sys") {
                         return Err(Error::Nix {
-                            msg: "new_from_child failed: no available parent device until /sys"
-                                .to_string(),
+                            msg: "no available parent device".to_string(),
                             source: Errno::ENODEV,
                         });
                     }
@@ -2387,7 +2161,7 @@ impl Device {
                     let path = p
                         .to_str()
                         .ok_or(Error::Nix {
-                            msg: format!("new_from_child failed: invalid path '{:?}'", p),
+                            msg: format!("invalid path '{:?}'", p),
                             source: Errno::ENODEV,
                         })?
                         .to_string();
@@ -2444,15 +2218,9 @@ impl Device {
     /// 3. if self devlinks are outdated, add to internal property
     /// 4. if self tags are outdated ,add to internal property
     pub fn properties_prepare(&self) -> Result<(), Error> {
-        self.read_uevent_file().map_err(|e| Error::Nix {
-            msg: format!("properties_prepare failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_uevent_file()?;
 
-        self.read_db().map_err(|e| Error::Nix {
-            msg: format!("properties_prepare failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        self.read_db()?;
 
         let property_devlinks_outdated = *self.property_devlinks_outdated.borrow();
         if property_devlinks_outdated {
@@ -2518,15 +2286,8 @@ impl Device {
         };
 
         let mut buf = String::new();
-        file.read_to_string(&mut buf).map_err(|e| Error::Nix {
-            msg: format!(
-                "read_db_internal_filename failed: can't read db '{}': {}",
-                filename, e
-            ),
-            source: e
-                .raw_os_error()
-                .map(nix::Error::from_i32)
-                .unwrap_or(nix::Error::EIO),
+        file.read_to_string(&mut buf).context(Io {
+            msg: format!("can't read db '{}'", filename),
         })?;
 
         self.is_initialized.replace(true);
@@ -2540,10 +2301,7 @@ impl Device {
             let key = &line[0..1];
             let value = &line[2..];
 
-            self.handle_db_line(key, value).map_err(|e| Error::Nix {
-                msg: format!("read_db_internal_filename failed: {}", e),
-                source: e.get_errno(),
-            })?;
+            self.handle_db_line(key, value)?;
         }
 
         Ok(())
@@ -2572,29 +2330,18 @@ impl Device {
 
                 let (k, v) = (tokens[0], tokens[1]);
 
-                self.add_property_internal(k, v).map_err(|e| Error::Nix {
-                    msg: format!("handle_db_line failed: {}", e),
-                    source: e.get_errno(),
-                })?;
+                self.add_property_internal(k, v)?;
             }
             "I" => {
-                let time = value.parse::<u64>().map_err(|e| Error::Nix {
-                    msg: format!(
-                        "handle_db_line failed: invalid initialized time '{}': {}",
-                        value, e
-                    ),
-                    source: Errno::EINVAL,
+                let time = value.parse::<u64>().context(ParseInt {
+                    msg: format!("invalid usec integer '{}'", value),
                 })?;
 
                 self.set_usec_initialized(time);
             }
             "L" => {
-                let priority = value.parse::<i32>().map_err(|e| Error::Nix {
-                    msg: format!(
-                        "handle_db_line failed: failed to parse devlink priority '{}': {}",
-                        value, e
-                    ),
-                    source: Errno::EINVAL,
+                let priority = value.parse::<i32>().context(ParseInt {
+                    msg: format!("invalid link priority integer '{}'", value),
                 })?;
 
                 self.devlink_priority.replace(priority);
@@ -2603,12 +2350,8 @@ impl Device {
                 log::debug!("watch handle in database is deprecated.");
             }
             "V" => {
-                let version = value.parse::<u32>().map_err(|e| Error::Nix {
-                    msg: format!(
-                        "handle_db_line failed: failed to parse database version '{}': {}",
-                        value, e
-                    ),
-                    source: Errno::EINVAL,
+                let version = value.parse::<u32>().context(ParseInt {
+                    msg: format!("invalid db version integer '{}'", value),
                 })?;
 
                 self.database_version.replace(version);
@@ -2625,22 +2368,11 @@ impl Device {
     pub fn shallow_clone(&self) -> Result<Device, Error> {
         let device = Self::default();
 
-        let syspath = self.get_syspath().map_err(|e| Error::Nix {
-            msg: format!("shallow_clone failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        let syspath = self.get_syspath()?;
 
-        device
-            .set_syspath(&syspath, false)
-            .map_err(|e| Error::Nix {
-                msg: format!("shallow_clone failed: {}", e),
-                source: e.get_errno(),
-            })?;
+        device.set_syspath(&syspath, false)?;
 
-        let subsystem = self.get_subsystem().map_err(|e| Error::Nix {
-            msg: format!("shallow_clone failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        let subsystem = self.get_subsystem()?;
 
         device.set_subsystem(&subsystem);
 
@@ -2651,24 +2383,15 @@ impl Device {
         }
 
         if let Ok(ifindex) = self.get_property_value("IFINDEX") {
-            device.set_ifindex(&ifindex).map_err(|e| Error::Nix {
-                msg: format!("shallow_clone failed: failed to set_ifindex ({})", e),
-                source: e.get_errno(),
-            })?;
+            device.set_ifindex(&ifindex)?;
         }
 
         if let Ok(major) = self.get_property_value("MAJOR") {
             let minor = self.get_property_value("MINOR")?;
-            device.set_devnum(&major, &minor).map_err(|e| Error::Nix {
-                msg: format!("shallow_clone failed: {}", e),
-                source: e.get_errno(),
-            })?;
+            device.set_devnum(&major, &minor)?;
         }
 
-        device.read_uevent_file().map_err(|e| Error::Nix {
-            msg: format!("shallow_clone failed: {}", e),
-            source: e.get_errno(),
-        })?;
+        device.read_uevent_file()?;
 
         Ok(device)
     }
@@ -2685,12 +2408,8 @@ impl Device {
             "DRIVER" => self.set_driver(value),
             "IFINDEX" => self.set_ifindex(value)?,
             "USEC_INITIALIZED" => {
-                self.set_usec_initialized(value.parse::<u64>().map_err(|e| Error::Nix {
-                    msg: format!(
-                        "amend_key_value failed: failed to parse initialized time '{}': {}",
-                        value, e
-                    ),
-                    source: Errno::EINVAL,
+                self.set_usec_initialized(value.parse::<u64>().context(ParseInt {
+                    msg: format!("invalid usec integer '{}'", value),
                 })?);
             }
             "DEVMODE" => self.set_devmode(value)?,
@@ -2814,9 +2533,8 @@ impl Device {
             format!("{}/{}", syspath, subdir)
         };
 
-        std::fs::read_dir(&dir).map_err(|e| Error::Nix {
+        std::fs::read_dir(&dir).context(Io {
             msg: format!("Failed to read directory '{}'", &dir),
-            source: nix::Error::from_i32(e.raw_os_error().unwrap_or_default()),
         })
     }
 
@@ -3017,7 +2735,7 @@ impl Device {
             log::debug!(
                 "failed to read db of '{}': {}",
                 self.get_device_id()
-                    .unwrap_or(self.devpath.borrow().clone()),
+                    .unwrap_or_else(|_| self.devpath.borrow().clone()),
                 e
             )
         }
@@ -3033,7 +2751,7 @@ impl Device {
             log::error!(
                 "failed to read db of '{}': {}",
                 self.get_device_id()
-                    .unwrap_or(self.devpath.borrow().clone()),
+                    .unwrap_or_else(|_| self.devpath.borrow().clone()),
                 e
             )
         }
@@ -3049,7 +2767,7 @@ impl Device {
             log::debug!(
                 "failed to read db of '{}': {}",
                 self.get_device_id()
-                    .unwrap_or(self.devpath.borrow().clone()),
+                    .unwrap_or_else(|_| self.devpath.borrow().clone()),
                 e
             )
         }
@@ -3065,7 +2783,7 @@ impl Device {
             log::debug!(
                 "failed to prepare properties of '{}': {}",
                 self.get_device_id()
-                    .unwrap_or(self.devpath.borrow().clone()),
+                    .unwrap_or_else(|_| self.devpath.borrow().clone()),
                 e
             )
         }
@@ -3081,7 +2799,7 @@ impl Device {
             log::debug!(
                 "failed to enumerate children of '{}': {}",
                 self.get_device_id()
-                    .unwrap_or(self.devpath.borrow().clone()),
+                    .unwrap_or_else(|_| self.devpath.borrow().clone()),
                 e
             )
         }
@@ -3097,7 +2815,8 @@ impl Device {
             if let Err(e) = self.read_all_sysattrs() {
                 log::debug!(
                     "{}: failed to read all sysattrs: {}",
-                    self.get_sysname().unwrap_or(self.devpath.borrow().clone()),
+                    self.get_sysname()
+                        .unwrap_or_else(|_| self.devpath.borrow().clone()),
                     e
                 );
             }
@@ -3117,6 +2836,7 @@ impl PartialEq for Device {
 
 #[cfg(test)]
 mod tests {
+    use std::fs::OpenOptions;
     use std::panic::catch_unwind;
 
     use crate::{
@@ -3426,6 +3146,11 @@ mod tests {
             nix::Error::EINVAL
         );
 
+        /* Test other add_* methods. */
+        device.add_property("A", "AA").unwrap();
+        device.add_property("B", "BB").unwrap();
+        device.add_tags("A:B:C", true);
+
         device.update_db().unwrap();
 
         /* Test enumerating child devices */
@@ -3902,8 +3627,8 @@ Q:devmaster
 V:100
 ";
             touch_file("/tmp/tmp_db", false, None, None, None).unwrap();
-            let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap();
-            f.write(content.as_bytes()).unwrap();
+            let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap();
+            f.write_all(content.as_bytes()).unwrap();
             let device = Device::new();
             device.read_db_internal_filename("/tmp/tmp_db").unwrap();
         }
@@ -3912,8 +3637,8 @@ V:100
         {
             let content = "error
 ";
-            let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap();
-            f.write(content.as_bytes()).unwrap();
+            let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap();
+            f.write_all(content.as_bytes()).unwrap();
             let device = Device::new();
             device.read_db_internal_filename("/tmp/tmp_db").unwrap();
         }
@@ -3922,13 +3647,13 @@ V:100
         {
             let content = "I:invalid
 ";
-            let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap();
-            f.write(content.as_bytes()).unwrap();
+            let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap();
+            f.write_all(content.as_bytes()).unwrap();
             let device = Device::new();
             assert!(device.read_db_internal_filename("/tmp/tmp_db").is_err());
         }
 
-        /* DB shoud be readable. */
+        /* DB should be readable. */
         {
             touch_file("/tmp/tmp_db_writeonly", false, Some(0o222), None, None).unwrap();
             let device = Device::new();
@@ -3973,4 +3698,98 @@ V:100
         })
         .is_err());
     }
+
+    #[test]
+    fn test_set() {
+        let device = Device::from_subsystem_sysname("net", "lo").unwrap();
+        device.set_devuid("1").unwrap();
+        device.set_devgid("1").unwrap();
+        device.set_devmode("666").unwrap();
+        device.set_diskseq("1").unwrap();
+        device.set_action_from_string("change").unwrap();
+        device.set_sysattr_value("ifalias", Some("test")).unwrap();
+
+        assert_eq!(&device.get_property_value("DEVUID").unwrap(), "1");
+        assert_eq!(&device.get_property_value("DEVGID").unwrap(), "1");
+        assert_eq!(&device.get_property_value("DEVMODE").unwrap(), "666");
+        assert_eq!(&device.get_property_value("DISKSEQ").unwrap(), "1");
+        assert_eq!(&device.get_property_value("ACTION").unwrap(), "change");
+        assert_eq!(&device.get_cached_sysattr_value("ifalias").unwrap(), "test");
+
+        assert!(device.set_devuid("invalid").is_err());
+        assert!(device.set_devgid("invalid").is_err());
+        assert!(device.set_devmode("invalid").is_err());
+        assert!(device.set_diskseq("invalid").is_err());
+        assert!(device.set_action_from_string("invalid").is_err());
+        assert!(device.set_sysattr_value("nonexist", Some("test")).is_err());
+
+        assert!(device.set_sysattr_value("nonexist", None).is_ok());
+        assert!(device.set_sysattr_value("ifalias", None).is_ok());
+    }
+
+    #[test]
+    fn test_from_device_id() {
+        assert!(Device::from_device_id("invalid").is_err());
+        assert!(Device::from_device_id("b").is_err());
+        assert!(Device::from_device_id("+drivers").is_err());
+        assert!(Device::from_device_id("+drivers:").is_err());
+        assert!(Device::from_device_id("+drivers::usb").is_err());
+
+        let dev = Device::from_device_id("+drivers:usb:usb").unwrap();
+        println!("{}", dev.get_device_id().unwrap());
+
+        let dev = Device::from_syspath("/sys/bus/usb/drivers/usb", true).unwrap();
+        println!("{}", dev.get_device_id().unwrap());
+
+        let _ = unlink("/tmp/devmaster/data/+drivers:usb:usb");
+        dev.set_base_path("/tmp/devmaster");
+        assert!(dev.update_db().is_err());
+        dev.add_property("hello", "world").unwrap();
+        dev.update_db().unwrap();
+        assert!(Path::new("/tmp/devmaster/data/+drivers:usb:usb").exists());
+    }
+
+    #[test]
+    fn test_get_err() {
+        let device = Device::new();
+        assert!(device.get_syspath().is_err());
+        assert!(device.get_devpath().is_err());
+        assert!(device.get_parent().is_err());
+        assert!(device.get_devtype().is_err());
+        assert!(!device.get_is_initialized().unwrap());
+    }
+
+    #[test]
+    fn test_cleanup() {
+        let _ = touch_file("/tmp/devmaster/a", false, None, None, None);
+        let _ = touch_file("/tmp/devmaster/b", false, None, None, None);
+        Device::cleanup("/tmp/devmaster/a", "/tmp/devmaster/b");
+    }
+
+    #[test]
+    fn test_fmt() {
+        let device = Device::from_subsystem_sysname("net", "lo").unwrap();
+        println!("{:?}", device);
+    }
+
+    #[test]
+    fn test_set_syspath_no_verify() {
+        let device = Device::new();
+        device.set_syspath("/sys/test", false).unwrap();
+
+        assert!(device.set_sysname_and_sysnum().is_ok());
+    }
+
+    #[test]
+    fn test_partial_eq_trait() {
+        let dev1 = Device::from_syspath("/sys/class/net/lo", true).unwrap();
+        let dev2 = Device::from_subsystem_sysname("net", "lo").unwrap();
+
+        assert!(dev1 == dev2);
+    }
+
+    #[test]
+    fn test_from_devnum_err() {
+        assert!(Device::from_devnum('x', 100).is_err());
+    }
 }
diff --git a/libs/device/src/device_enumerator.rs b/libs/device/src/device_enumerator.rs
index 96cac2d9..55f8681d 100644
--- a/libs/device/src/device_enumerator.rs
+++ b/libs/device/src/device_enumerator.rs
@@ -12,7 +12,7 @@
 
 //! enumerate /sys to collect devices
 //!
-use crate::{device::Device, err_wrapper, error::Error, utils::*};
+use crate::{device::Device, error::Error, utils::*};
 use bitflags::bitflags;
 use fnmatch_sys::fnmatch;
 use nix::errno::Errno;
@@ -45,7 +45,7 @@ impl Default for MatchInitializedType {
 }
 
 /// enumerate devices or subsystems under /sys
-#[derive(Debug, Default)]
+#[derive(Default)]
 pub struct DeviceEnumerator {
     /// enumerator type
     pub(crate) etype: RefCell<DeviceEnumerationType>,
@@ -289,7 +289,7 @@ impl DeviceEnumerator {
 
     /// add match parent
     pub fn add_match_parent_incremental(&mut self, parent: &Device) -> Result<(), Error> {
-        let syspath = err_wrapper!(parent.get_syspath(), "add_match_parent_incremental")?;
+        let syspath = parent.get_syspath()?;
         self.match_parent.borrow_mut().insert(syspath);
         self.scan_up_to_date.replace(false);
         Ok(())
@@ -364,7 +364,7 @@ impl DeviceEnumerator {
                 // remove already sorted devices from the hashmap (self.devices_by_syspath)
                 // avoid get repeated devices from the hashmap later
                 for device in devices.iter().skip(m) {
-                    let syspath = err_wrapper!(device.borrow().get_syspath(), "sort_devices")?;
+                    let syspath = device.borrow().get_syspath()?;
 
                     self.devices_by_syspath.borrow_mut().remove(&syspath);
                 }
@@ -400,7 +400,7 @@ impl DeviceEnumerator {
 
     /// add device
     pub(crate) fn add_device(&self, device: Rc<RefCell<Device>>) -> Result<bool, Error> {
-        let syspath = err_wrapper!(device.borrow().get_syspath(), "add_device")?;
+        let syspath = device.borrow().get_syspath()?;
 
         match self.devices_by_syspath.borrow_mut().insert(syspath, device) {
             Some(_) => {
diff --git a/libs/device/src/device_monitor.rs b/libs/device/src/device_monitor.rs
index 729a7dcb..db51c40f 100644
--- a/libs/device/src/device_monitor.rs
+++ b/libs/device/src/device_monitor.rs
@@ -250,7 +250,7 @@ mod tests {
         ///
         fn dispatch(&self, e: &Events) -> i32 {
             let device = self.device_monitor.receive_device().unwrap();
-            println!("{:?}", device);
+            println!("{}", device.get_device_id().unwrap());
             e.set_exit();
             0
         }
diff --git a/libs/device/src/error.rs b/libs/device/src/error.rs
index 40589c21..10410301 100644
--- a/libs/device/src/error.rs
+++ b/libs/device/src/error.rs
@@ -38,6 +38,24 @@ pub enum Error {
 
     #[snafu(context, display("Basic error: {}", msg))]
     Basic { msg: String, source: basic::Error },
+
+    #[snafu(context, display("Failed to parse boolean: {}", msg))]
+    ParseBool {
+        msg: String,
+        source: std::str::ParseBoolError,
+    },
+
+    #[snafu(context, display("Failed to parse integer: {}", msg))]
+    ParseInt {
+        msg: String,
+        source: std::num::ParseIntError,
+    },
+
+    #[snafu(context, display("Failed to parse utf-8: {}", msg))]
+    FromUtf8 {
+        msg: String,
+        source: std::string::FromUtf8Error,
+    },
 }
 
 impl Error {
@@ -53,21 +71,13 @@ impl Error {
                 source: errno,
             } => Errno::from_i32(errno.raw_os_error().unwrap_or_default()),
             Self::Basic { msg: _, source } => Errno::from_i32(source.get_errno()),
+            Self::ParseBool { msg: _, source: _ } => nix::Error::EINVAL,
+            Self::ParseInt { msg: _, source: _ } => nix::Error::EINVAL,
+            Self::FromUtf8 { msg: _, source: _ } => nix::Error::EINVAL,
         }
     }
 }
 
-/// append current function and inherit the errno
-#[macro_export]
-macro_rules! err_wrapper {
-    ($e:expr, $s:expr) => {
-        $e.map_err(|e| Error::Nix {
-            msg: format!("$s failed: {}", e),
-            source: e.get_errno(),
-        })
-    };
-}
-
 impl Error {
     /// check whether the device error belongs to specific errno
     pub fn is_errno(&self, errno: nix::Error) -> bool {
@@ -81,4 +91,37 @@ impl Error {
             Errno::ENODEV | Errno::ENXIO | Errno::ENOENT
         )
     }
+
+    pub(crate) fn replace_errno(self, from: Errno, to: Errno) -> Self {
+        let n = self.get_errno();
+
+        if n == from {
+            Self::Nix {
+                msg: self.to_string(),
+                source: to,
+            }
+        } else {
+            self
+        }
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use nix::errno::Errno;
+
+    #[test]
+    fn test_replace_errno() {
+        let e = Error::Nix {
+            msg: "test".to_string(),
+            source: Errno::ENOENT,
+        };
+
+        assert_eq!(
+            Errno::ENOEXEC,
+            e.replace_errno(Errno::ENOENT, Errno::ENOEXEC).get_errno(),
+        );
+    }
 }
-- 
2.33.0

