use insta::assert_snapshot;
use logforth_core::Filter;
use logforth_core::filter::FilterResult;
use logforth_core::record::FilterCriteria;
use logforth_core::record::Level;
use logforth_core::record::LevelFilter;
use crate::Directive;
use crate::ParseResult;
use crate::RustLogFilter;
use crate::RustLogFilterBuilder;
use crate::parse_spec;
impl RustLogFilter {
fn rejected(&self, level: Level, target: &str) -> bool {
let criteria = FilterCriteria::builder()
.level(level)
.target(target)
.build();
matches!(Filter::enabled(self, &criteria, &[]), FilterResult::Reject)
}
}
#[test]
fn parse_spec_valid() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
assert_eq!(dirs.len(), 3);
assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Error));
assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
assert_eq!(dirs[1].level, LevelFilter::All);
assert_eq!(dirs[2].name, Some("crate2".to_owned()));
assert_eq!(dirs[2].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert!(errors.is_empty());
}
#[test]
fn parse_spec_invalid_crate() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=warn=info,crate2=debug");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert_eq!(errors.len(), 1);
assert_snapshot!(
&errors[0],
@"malformed logging spec 'crate1::mod1=warn=info'"
);
}
#[test]
fn parse_spec_invalid_level() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=noNumber,crate2=debug");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert_eq!(errors.len(), 1);
assert_snapshot!(&errors[0], @"malformed logging spec 'noNumber'");
}
#[test]
fn parse_spec_string_level() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=wrong,crate2=warn");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
assert_eq!(errors.len(), 1);
assert_snapshot!(&errors[0], @"malformed logging spec 'wrong'");
}
#[test]
fn parse_spec_empty_level() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=wrong,crate2=");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::All);
assert_eq!(errors.len(), 1);
assert_snapshot!(&errors[0], @"malformed logging spec 'wrong'");
}
#[test]
fn parse_spec_empty_level_isolated() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec(""); assert_eq!(dirs.len(), 0);
assert!(errors.is_empty());
}
#[test]
fn parse_spec_blank_level_isolated() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec(" "); assert_eq!(dirs.len(), 0);
assert!(errors.is_empty());
}
#[test]
fn parse_spec_blank_level_isolated_comma_only() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec(","); assert_eq!(dirs.len(), 0);
assert!(errors.is_empty());
}
#[test]
fn parse_spec_blank_level_isolated_comma_blank() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec(", "); assert_eq!(dirs.len(), 0);
assert!(errors.is_empty());
}
#[test]
fn parse_spec_blank_level_isolated_blank_comma() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec(" ,"); assert_eq!(dirs.len(), 0);
assert!(errors.is_empty());
}
#[test]
fn parse_spec_global() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("warn,crate2=debug");
assert_eq!(dirs.len(), 2);
assert_eq!(dirs[0].name, None);
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
assert_eq!(dirs[1].name, Some("crate2".to_owned()));
assert_eq!(dirs[1].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert!(errors.is_empty());
}
#[test]
fn parse_spec_global_bare_warn_lc() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("warn");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, None);
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
assert!(errors.is_empty());
}
#[test]
fn parse_spec_global_bare_warn_uc() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("WARN");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, None);
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
assert!(errors.is_empty());
}
#[test]
fn parse_spec_global_bare_warn_mixed() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("wArN");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, None);
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Warn));
assert!(errors.is_empty());
}
#[test]
fn parse_spec_multiple_invalid_crates() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=warn=info,crate2=debug,crate3=error=error");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert_eq!(errors.len(), 2);
assert_snapshot!(
&errors[0],
@"malformed logging spec 'crate1::mod1=warn=info'"
);
assert_snapshot!(
&errors[1],
@"malformed logging spec 'crate3=error=error'"
);
}
#[test]
fn parse_spec_multiple_invalid_levels() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=noNumber,crate2=debug,crate3=invalid");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert_eq!(errors.len(), 2);
assert_snapshot!(&errors[0], @"malformed logging spec 'noNumber'");
assert_snapshot!(&errors[1], @"malformed logging spec 'invalid'");
}
#[test]
fn parse_spec_invalid_crate_and_level() {
let ParseResult {
directives: dirs,
errors,
} = parse_spec("crate1::mod1=debug=info,crate2=debug,crate3=invalid");
assert_eq!(dirs.len(), 1);
assert_eq!(dirs[0].name, Some("crate2".to_owned()));
assert_eq!(dirs[0].level, LevelFilter::MoreSevereEqual(Level::Debug));
assert_eq!(errors.len(), 2);
assert_snapshot!(
&errors[0],
@"malformed logging spec 'crate1::mod1=debug=info'"
);
assert_snapshot!(&errors[1], @"malformed logging spec 'invalid'");
}
#[test]
fn parse_error_message_single_error() {
let ParseResult { errors, .. } = parse_spec("crate1::mod1=debug=info,crate2=debug");
assert_snapshot!(
&errors[0],
@"malformed logging spec 'crate1::mod1=debug=info'"
);
}
#[test]
fn parse_error_message_multiple_errors() {
let ParseResult { errors, .. } =
parse_spec("crate1::mod1=debug=info,crate2=debug,crate3=invalid");
assert_snapshot!(
&errors[0],
@"malformed logging spec 'crate1::mod1=debug=info'"
);
}
#[test]
fn filter_info() {
let logger = RustLogFilterBuilder::default()
.filter_level(LevelFilter::MoreSevereEqual(Level::Info))
.build();
assert!(!logger.rejected(Level::Info, "crate1"));
assert!(logger.rejected(Level::Debug, "crate1"));
}
#[test]
fn filter_beginning_longest_match() {
let logger = RustLogFilterBuilder::default()
.filter_module("crate2", LevelFilter::MoreSevereEqual(Level::Info))
.filter_module("crate2::mod", LevelFilter::MoreSevereEqual(Level::Debug))
.filter_module("crate1::mod1", LevelFilter::MoreSevereEqual(Level::Warn))
.build();
assert!(!logger.rejected(Level::Debug, "crate2::mod1"));
assert!(logger.rejected(Level::Debug, "crate2"));
}
#[test]
fn ensure_tests_cover_level_universe() {
let level_universe: Level = Level::Trace; match level_universe {
Level::Trace
| Level::Trace2
| Level::Trace3
| Level::Trace4
| Level::Debug
| Level::Debug2
| Level::Debug3
| Level::Debug4
| Level::Info
| Level::Info2
| Level::Info3
| Level::Info4
| Level::Warn
| Level::Warn2
| Level::Warn3
| Level::Warn4
| Level::Error
| Level::Error2
| Level::Error3
| Level::Error4
| Level::Fatal
| Level::Fatal2
| Level::Fatal3
| Level::Fatal4 => (),
}
}
#[test]
fn parse_default() {
let logger = RustLogFilterBuilder::from_spec("info,crate1::mod1=warn").build();
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
assert!(!logger.rejected(Level::Info, "crate2::mod2"));
}
#[test]
fn parse_default_bare_level_off_lc() {
let logger = RustLogFilterBuilder::from_spec("off").build();
assert!(logger.rejected(Level::Error, ""));
assert!(logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_off_uc() {
let logger = RustLogFilterBuilder::from_spec("OFF").build();
assert!(logger.rejected(Level::Error, ""));
assert!(logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_error_lc() {
let logger = RustLogFilterBuilder::from_spec("error").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_error_uc() {
let logger = RustLogFilterBuilder::from_spec("ERROR").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_warn_lc() {
let logger = RustLogFilterBuilder::from_spec("warn").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_warn_uc() {
let logger = RustLogFilterBuilder::from_spec("WARN").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_info_lc() {
let logger = RustLogFilterBuilder::from_spec("info").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_info_uc() {
let logger = RustLogFilterBuilder::from_spec("INFO").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_debug_lc() {
let logger = RustLogFilterBuilder::from_spec("debug").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_debug_uc() {
let logger = RustLogFilterBuilder::from_spec("DEBUG").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_trace_lc() {
let logger = RustLogFilterBuilder::from_spec("trace").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(!logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_trace_uc() {
let logger = RustLogFilterBuilder::from_spec("TRACE").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(!logger.rejected(Level::Trace, ""));
}
#[test]
fn parse_default_bare_level_debug_mixed() {
{
let logger = RustLogFilterBuilder::from_spec("Debug").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
{
let logger = RustLogFilterBuilder::from_spec("debuG").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
{
let logger = RustLogFilterBuilder::from_spec("deBug").build();
assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
{
let logger = RustLogFilterBuilder::from_spec("DeBuG").build(); assert!(!logger.rejected(Level::Error, ""));
assert!(!logger.rejected(Level::Warn, ""));
assert!(!logger.rejected(Level::Info, ""));
assert!(!logger.rejected(Level::Debug, ""));
assert!(logger.rejected(Level::Trace, ""));
}
}
#[test]
fn try_parse_valid_filter() {
let logger = RustLogFilterBuilder::try_from_spec("info,crate1::mod1=warn")
.expect("valid filter returned error")
.build();
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
assert!(!logger.rejected(Level::Info, "crate2::mod2"));
}
#[test]
fn try_parse_invalid_filter() {
let error = RustLogFilterBuilder::try_from_spec("info,crate1=invalid").unwrap_err();
assert_snapshot!(error.to_string(), @"malformed logging spec 'invalid'");
}
#[test]
fn match_full_path() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: Some("crate2".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Warn),
},
]);
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
assert!(logger.rejected(Level::Info, "crate1::mod1"));
assert!(!logger.rejected(Level::Info, "crate2"));
assert!(logger.rejected(Level::Debug, "crate2"));
}
#[test]
fn no_match() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: Some("crate2".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Warn),
},
]);
assert!(logger.rejected(Level::Warn, "crate3"));
}
#[test]
fn match_beginning() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: Some("crate2".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Warn),
},
]);
assert!(!logger.rejected(Level::Info, "crate2::mod1"));
}
#[test]
fn match_beginning_longest_match() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: Some("crate2".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate2::mod".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Debug),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Warn),
},
]);
assert!(!logger.rejected(Level::Debug, "crate2::mod1"));
assert!(logger.rejected(Level::Debug, "crate2"));
}
#[test]
fn match_default() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: None,
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::MoreSevereEqual(Level::Warn),
},
]);
assert!(!logger.rejected(Level::Warn, "crate1::mod1"));
assert!(!logger.rejected(Level::Info, "crate2::mod2"));
}
#[test]
fn zero_level() {
let logger = RustLogFilter::from_directives(vec![
Directive {
name: None,
level: LevelFilter::MoreSevereEqual(Level::Info),
},
Directive {
name: Some("crate1::mod1".to_owned()),
level: LevelFilter::Off,
},
]);
assert!(logger.rejected(Level::Error, "crate1::mod1"));
assert!(!logger.rejected(Level::Info, "crate2::mod2"));
}