From a83930f64af8e71b0ba56d64a8493f8a54ded5d8 Mon Sep 17 00:00:00 2001 From: Sudan Landge Date: Thu, 23 Nov 2023 23:09:49 +0000 Subject: [PATCH] test: check all logger level case variants Add unit test to cover all cases of LevelFilter. Signed-off-by: Sudan Landge --- Cargo.lock | 1 + src/vmm/Cargo.toml | 1 + src/vmm/src/logger/logging.rs | 54 +++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e9ff79121e0..cc99b0ff7c2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1494,6 +1494,7 @@ dependencies = [ "device_tree", "displaydoc", "event-manager", + "itertools 0.12.0", "kvm-bindings", "kvm-ioctls", "lazy_static", diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index e637e18e14fe..a0d4b535fa64 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -50,6 +50,7 @@ vm-fdt = "0.2.0" criterion = { version = "0.5.0", default-features = false } device_tree = "1.1.0" proptest = { version = "1.0.0", default-features = false, features = ["std"] } +itertools = "0.12.0" [features] tracing = ["log-instrument"] diff --git a/src/vmm/src/logger/logging.rs b/src/vmm/src/logger/logging.rs index d111994363a4..c0f09503bcab 100644 --- a/src/vmm/src/logger/logging.rs +++ b/src/vmm/src/logger/logging.rs @@ -10,9 +10,10 @@ use std::sync::{Mutex, OnceLock}; use std::thread; use log::{Log, Metadata, Record}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use utils::time::LocalTime; +// use serde::de::IntoDeserializer; use super::metrics::{IncMetric, METRICS}; /// Default level filter for logger matching the swagger specification @@ -200,7 +201,10 @@ pub struct LoggerConfig { /// the log level filter. It would be a breaking change to no longer support this. In the next /// breaking release this should be removed (replaced with `log::LevelFilter` and only supporting /// its default deserialization). -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +// #[serde(rename_all = "lowercase")] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all(deserialize = "lowercase"))] +// #[serde(remote = "LevelFilter")] pub enum LevelFilter { /// [`log::LevelFilter:Off`] #[serde(alias = "OFF")] @@ -233,6 +237,28 @@ impl From for log::LevelFilter { } } } +impl<'de> Deserialize<'de> for LevelFilter { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + use serde_json::Map; + let map = Map::::deserialize(deserializer)?; + for key in map.keys() { + return match key.to_lowercase().as_str() { + "off" => Ok(LevelFilter::Off), + "trace" => Ok(LevelFilter::Trace), + "debug" => Ok(LevelFilter::Debug), + "info" => Ok(LevelFilter::Info), + "warn" | "warning" => Ok(LevelFilter::Warn), + "error" => Ok(LevelFilter::Error), + _ => Err(D::Error::custom("Invalid LevelFilter")), + }; + } + Err(D::Error::custom("Could not deserialize")) + } +} /// Error type for [`::from_str`]. #[derive(Debug, PartialEq, Eq, thiserror::Error)] @@ -288,6 +314,30 @@ mod tests { ); } #[test] + fn levelfilter_from_str_all_variants() { + use itertools::Itertools; + + for level in ["off", "trace", "debug", "info", "warn", "warning", "error"] { + let multi = level.chars().map(|_| 0..=1).multi_cartesian_product(); + for combination in multi { + let variant = level + .chars() + .zip_eq(combination) + .map(|(c, v)| match v { + 0 => c.to_ascii_lowercase(), + 1 => c.to_ascii_uppercase(), + _ => unreachable!(), + }) + .collect::(); + + // to deserialize with serde_json we need json string in this format: {"Unit":0} + let ex = format!("{} \"{}\": \"{}\" {}", "{", variant, "0", "}"); + assert!(LevelFilter::from_str(&variant).is_ok(), "{variant}"); + assert!(serde_json::from_str::(&ex).is_ok(), "{ex}"); + } + } + } + #[test] fn levelfilter_from_str() { assert_eq!( LevelFilter::from_str("bad"),