Skip to content

Commit

Permalink
test: check all logger level case variants
Browse files Browse the repository at this point in the history
Add unit test to cover all cases of LevelFilter.

Signed-off-by: Jonathan Woollett-Light <jcawl@amazon.co.uk>
Signed-off-by: Sudan Landge <sudanl@amazon.com>
  • Loading branch information
Sudan Landge committed Nov 24, 2023
1 parent 51ab5ad commit 9e7af0a
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/vmm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
54 changes: 52 additions & 2 deletions src/vmm/src/logger/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -233,6 +237,28 @@ impl From<LevelFilter> for log::LevelFilter {
}
}
}
impl<'de> Deserialize<'de> for LevelFilter {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
use serde_json::Map;
let map = Map::<String, _>::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 [`<LevelFilter as FromStr>::from_str`].
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
Expand Down Expand Up @@ -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::<String>();

// 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::<LevelFilter>(&ex).is_ok(), "{ex}");
}
}
}
#[test]
fn levelfilter_from_str() {
assert_eq!(
LevelFilter::from_str("bad"),
Expand Down

0 comments on commit 9e7af0a

Please sign in to comment.