logger works, but only half tests

This commit is contained in:
Christoph J. Scherr 2023-07-07 21:05:37 +02:00
parent 2564eee991
commit 88de9b0fe0
Signed by: PlexSheep
GPG Key ID: 25B4ACF7D88186CC
10 changed files with 186 additions and 32 deletions

View File

@ -24,5 +24,7 @@ path = "src/bin/mod.rs"
[dependencies] [dependencies]
clap = "4.3.11" clap = "4.3.11"
env_logger = "0.10.0" env_logger = "0.10.0"
log = { version = "0.4.19", features = ["max_level_debug", "release_max_level_info"] } gag = "1.0.0"
log = { version = "0.4.19", features = ["max_level_trace", "release_max_level_trace"] }
pyo3 = "0.18.1" pyo3 = "0.18.1"
testing_logger = "0.1.1"

View File

@ -14,3 +14,5 @@ classifiers = [
[tool.maturin] [tool.maturin]
features = ["pyo3/extension-module"] features = ["pyo3/extension-module"]
python-source = "python"
module-name = "libpt._libpt"

0
python/libpt/libpt.pyi Normal file
View File

0
python/libpt/py-typed Normal file
View File

View File

@ -13,48 +13,79 @@
//// ATTRIBUTES //////////////////////////////////////////////////////////////////////////////////// //// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::{fmt, str::FromStr}; use std::{
fmt,
sync::atomic::{AtomicBool, Ordering},
};
use log::{debug, error, info, trace, warn}; use env_logger::{Env, Target};
use log::{debug, error, info, trace, warn, Level};
use env_logger;
use pyo3::prelude::*; use pyo3::prelude::*;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified /// The log level used when none is specified
const DEFAULT_LOG_LEVEL: log::Level = log::Level::Info; pub const DEFAULT_LOG_LEVEL: Level = Level::Info;
/// Register your level to this ENVAR to override the used level
pub const LOGGER_ENV_KEY: &'static str = "LIBPT_LOGLEVEL";
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
static INITIALIZED: AtomicBool = AtomicBool::new(false);
//// STRUCTS /////////////////////////////////////////////////////////////////////////////////////// //// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## Logger for [`libpt`](crate::libpt) /// ## Logger for [`libpt`](crate::libpt)
/// ///
/// This struct exists mainly for the python module, so that we can use the same logger with both /// This struct exists mainly for the python module, so that we can use the same logger with both
/// python and rust. /// python and rust.
///
/// ### Setting a [`Level`](log::Level)
///
/// To set a [`Level`](log::Level), you need to set the ENVAR `LIBPT_LOGLEVEL` to either of
///
/// - `Trace`
/// - `Debug`
/// - `Info`
/// - `Warn`
/// - `Error`
#[pyclass] #[pyclass]
pub struct Logger { pub struct Logger {}
/// keeps track of if the logger was initialized
pub initialized: bool,
}
//// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl Logger { impl Logger {
/// ## create a `Logger` /// ## create a `Logger`
pub fn new(level: log::Level) -> Self { ///
let mut l = Logger { initialized: false }; /// Creates a new uninitialized [`Logger`] object.
l.init(level); pub fn new() -> Self {
let l = Logger {};
l l
} }
pub fn init(&mut self, level: log::Level) { /// ## initializes the logger
///
/// Will enable the logger to be used.
pub fn init() {
// only init if no init has been performed yet // only init if no init has been performed yet
if self.initialized { if INITIALIZED.load(Ordering::Relaxed) {
self.warn("trying to reinitialize the logger, ignoring"); warn!("trying to reinitialize the logger, ignoring");
return; return;
} else {
let env = Env::default().filter_or(LOGGER_ENV_KEY, DEFAULT_LOG_LEVEL.to_string());
env_logger::init_from_env(env);
INITIALIZED.store(true, Ordering::Relaxed);
}
}
/// ## initializes the logger to log to a target
///
/// Will enable the logger to be used.
pub fn init_target(test: bool, target: Target) {
// only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring");
return;
} else {
let env = Env::default().filter_or(LOGGER_ENV_KEY, DEFAULT_LOG_LEVEL.to_string());
env_logger::Builder::from_env(env).is_test(test).target(target).init();
INITIALIZED.store(true, Ordering::Relaxed);
} }
#[allow(unused_imports)]
use log::log_enabled;
// TODO check if level is valid!
std::env::set_var("RUST_LOG", level.as_str());
env_logger::init();
self.initialized = true;
} }
/// ## logging at [`Level::Error`] /// ## logging at [`Level::Error`]
@ -99,17 +130,14 @@ impl Logger {
impl Logger { impl Logger {
/// ## Python version of [`new()`](Logger::new) /// ## Python version of [`new()`](Logger::new)
#[new] #[new]
pub fn py_new(level: String) -> PyResult<Self> { pub fn py_new() -> PyResult<Self> {
Ok(Self::new(log::Level::from_str(level.as_str()).expect( Ok(Logger::new())
format!("could not get log level for '{}'", level).as_str(),
)))
} }
/// ## Python version of [`init()`](Logger::init) /// ## Python version of [`init()`](Logger::init)
#[pyo3(name = "init")] #[pyo3(name = "init")]
pub fn py_init(&mut self, level: String) { #[staticmethod]
Self::init(self, log::Level::from_str(level.as_str()).expect( pub fn py_init() {
format!("could not get log level for '{}'", level).as_str(), Self::init()
))
} }
/// ## Python version of [`error()`](Logger::error) /// ## Python version of [`error()`](Logger::error)
#[pyo3(name = "error")] #[pyo3(name = "error")]
@ -142,7 +170,11 @@ impl Logger {
impl fmt::Debug for Logger { impl fmt::Debug for Logger {
/// ## Debug representation for [`Logger`] /// ## Debug representation for [`Logger`]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Logger") write!(
f,
"Logger: {{initialized: {}}} ",
INITIALIZED.load(Ordering::Relaxed)
)
} }
} }

38
src/template.rs Normal file
View File

@ -0,0 +1,38 @@
//! # very short description
//!
//! Short description
//!
//! Details
//!
//! ## Section 1
//!
//! ## Section 2
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

0
tests/python/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,80 @@
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
//// HELPERS ///////////////////////////////////////////////////////////////////////////////////////
/// ## checks if the expected thing was printed to stdout
///
/// Source: [users.rust-lang.org](https://users.rust-lang.org/t/how-to-test-functions-that-use-
/// println/67188/5)
macro_rules! assert_stdout_eq {
($test:expr, $expected:literal) => {{
use gag::BufferRedirect;
use std::io::Read;
let mut buf = BufferRedirect::stdout().unwrap();
$test;
let mut output = String::new();
buf.read_to_string(&mut output).unwrap();
drop(buf);
assert_eq!(&output, $expected);
}};
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
/// ## Tests for basic logging functionality
mod test_logger_struct {
use libpt::logger::*;
fn setup() {
// we don't want to log messages during our tests!
Logger::init_target(false, env_logger::Target::Stdout)
}
/// ## Tests for basic logging
///
/// This test tests if the loggers basic logging functionality works, that is it's methods:
///
/// - [`Logger::trace`]
/// - [`Logger::debug`]
/// - [`Logger::info`]
/// - [`Logger::warn`]
/// - [`Logger::error`]
#[test]
#[ignore]
fn test_log_basic() {
std::env::set_var(LOGGER_ENV_KEY, "Trace");
setup();
let l = Logger::new();
l.error("HELP");
assert_stdout_eq!(l.trace("hello world"), "\u{1b}[0m\u{1b}[38;5;8m[\u{1b}[0m2023-07-07T1\
8:59:03Z \u{1b}[0m\u{1b}[36mTRACE\u{1b}[0m libpt::logger\u{1b}[0m\u{1b}[38;5;8m]\u{1b}[\
0m hello world\n");
assert_stdout_eq!(l.debug("hello world"), "\u{1b}[0m\u{1b}[38;5;8m[\u{1b} [0m2023-07-07T1\
8:59:03Z \u{1b}[0m\u{1b}[34mDEBUG\u{1b}[0m libpt::logger\u{1b}[0m\u{1b}[38;5;8m]\u{1b}[\
0m hello world\n");
assert_stdout_eq!(l.info("hello world"), "\u{1b}[0m\u{1b}[38;5;8m[\u{1b} [0m2023-07-07T1\
8:59:03Z \u{1b}[0m\u{1b}[34mINFO\u{1b}[0m libpt::logger\u{1b}[0m\u{1b}[38;5;8m]\u{1b}[\
0m hello world\n");
assert_stdout_eq!(l.warn("hello world"), "\u{1b}[0m\u{1b}[38;5;8m[\u{1b} [0m2023-07-07T1\
8:59:03Z \u{1b}[0m\u{1b}[34mWARN\u{1b}[0m libpt::logger\u{1b}[0m\u{1b}[38;5;8m]\u{1b}[\
0m hello world\n");
assert_stdout_eq!(l.error("hello world"), "\u{1b}[0m\u{1b}[38;5;8m[\u{1b} [0m2023-07-07T1\
8:59:03Z \u{1b}[0m\u{1b}[34mERROR\u{1b}[0m libpt::logger\u{1b}[0m\u{1b}[38;5;8m]\u{1b}[\
0m hello world\n");
}
#[test]
fn test_multi_initialize() {
setup();
let l = Logger::new();
// these should be ignored due to the global flag
Logger::init();
Logger::init();
Logger::init();
Logger::init();
l.info("Successfully ignored extra init");
}
}