Improve command error messages and unit test coverage
This commit is contained in:
parent
b6002b635d
commit
0c56e837d4
@ -78,3 +78,7 @@ Once you have that installed, just run
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
and you will find an executable in `target/release`.
|
and you will find an executable in `target/release`.
|
||||||
|
|
||||||
|
### Unit tests
|
||||||
|
|
||||||
|
In order for the unit tests to pass, you will need `python3`
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use crate::logger::{LogLevel, Logger};
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::iter::once;
|
||||||
use std::process::{Command, ExitStatus, Stdio};
|
use std::process::{Command, ExitStatus, Stdio};
|
||||||
|
|
||||||
pub trait LogRunnable {
|
pub trait LogRunnable {
|
||||||
@ -47,12 +48,20 @@ impl Display for SpecificExecutionError<'_> {
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Failed to execute command {:?}: {}",
|
"Failed to execute command '{}': {}",
|
||||||
self.command, self.error
|
command_to_string(self.command),
|
||||||
|
self.error
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn command_to_string(command: &Command) -> String {
|
||||||
|
once(command.get_program().to_string_lossy())
|
||||||
|
.chain(command.get_args().map(|arg| arg.to_string_lossy()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
impl Error for SpecificExecutionError<'_> {}
|
impl Error for SpecificExecutionError<'_> {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -76,9 +85,9 @@ impl From<ExitStatus> for ExecutionError {
|
|||||||
impl Display for ExecutionError {
|
impl Display for ExecutionError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ExecutionError::StartError(e) => write!(f, "failed to start command: {}", e),
|
ExecutionError::StartError(e) => write!(f, "Failed to start command: {}", e),
|
||||||
ExecutionError::BadExitStatus(status) => {
|
ExecutionError::BadExitStatus(status) => {
|
||||||
write!(f, "command failed with exit status: {}", status)
|
write!(f, "Command failed with {}", status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,23 +99,40 @@ impl Error for ExecutionError {}
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::command::{ExecutionError, LogRunnable, SpecificExecutionError};
|
use crate::command::{ExecutionError, LogRunnable, SpecificExecutionError};
|
||||||
use crate::logger::Logger;
|
use crate::logger::Logger;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown_command() {
|
fn test_unknown_command() {
|
||||||
let mut command = Command::new("a_command_which_def_doesnt_exist");
|
let mut command = Command::new("python7");
|
||||||
let Err(
|
let Err(
|
||||||
e @ SpecificExecutionError {
|
e @ SpecificExecutionError {
|
||||||
error: ExecutionError::StartError(_),
|
error: ExecutionError::StartError(_),
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
) = command.args(["foo", "bar"]).run(&Logger::default())
|
) = command
|
||||||
|
.args([PathBuf::from("test-ressources/python/exit_1.py")])
|
||||||
|
.run(&Logger::default())
|
||||||
else {
|
else {
|
||||||
panic!("command shouldn't exist");
|
panic!("command shouldn't exist");
|
||||||
};
|
};
|
||||||
println!("{e}");
|
assert_eq!(e.to_string(), "Failed to execute command 'python7': Failed to start command: No such file or directory (os error 2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_error() {}
|
fn test_error() {
|
||||||
|
let mut command = Command::new("python3");
|
||||||
|
let Err(
|
||||||
|
e @ SpecificExecutionError {
|
||||||
|
error: ExecutionError::BadExitStatus(_),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) = command
|
||||||
|
.arg("test-ressources/python/exit_1.py")
|
||||||
|
.run(&Logger::default())
|
||||||
|
else {
|
||||||
|
panic!("command should return exit-code 1")
|
||||||
|
};
|
||||||
|
assert_eq!(e.to_string(), "Failed to execute command 'python3 test-ressources/python/exit_1.py': Command failed with exit status: 1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ macro_rules! log {
|
|||||||
$logger.$level(format!($($args)*));
|
$logger.$level(format!($($args)*));
|
||||||
};
|
};
|
||||||
($logger:expr, $($args:tt)*) => {
|
($logger:expr, $($args:tt)*) => {
|
||||||
log!($logger, info, $($args)*);
|
log!($logger, info, $($args)*); //TODO better use default level with log function instead of assuming info as default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
test-ressources/python/exit_1.py
Normal file
1
test-ressources/python/exit_1.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
exit(1)
|
||||||
Loading…
Reference in New Issue
Block a user