feat: add ffmpeg audio plugin and migrate verbosity to tracing levels
Release / check-release (push) Successful in 59s
Release / build_gnu (push) Skipped
Release / build_musl (push) Skipped
Release / build_windows (push) Skipped
Release / package_gnu (deb) (push) Skipped
Release / package_gnu (rpm) (push) Skipped
Release / package_musl (deb) (push) Skipped
Release / package_musl (rpm) (push) Skipped
Release / publish-release (push) Skipped
CI / Test (push) Successful in 1m44s
CI / Build Linux (push) Successful in 2m3s
Release / check-release (push) Successful in 59s
Release / build_gnu (push) Skipped
Release / build_musl (push) Skipped
Release / build_windows (push) Skipped
Release / package_gnu (deb) (push) Skipped
Release / package_gnu (rpm) (push) Skipped
Release / package_musl (deb) (push) Skipped
Release / package_musl (rpm) (push) Skipped
Release / publish-release (push) Skipped
CI / Test (push) Successful in 1m44s
CI / Build Linux (push) Successful in 2m3s
This commit is contained in:
@@ -42,3 +42,4 @@ tempfile = "3.27.0"
|
||||
thiserror = "2.0.18"
|
||||
tokio = { version = "1.52.3", features = ["rt", "rt-multi-thread"] }
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.23"
|
||||
|
||||
@@ -77,7 +77,7 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=plugins");
|
||||
|
||||
// Add build metadata for versioning
|
||||
let version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.1.0".to_string());
|
||||
let version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "dev-unknown".to_string());
|
||||
|
||||
let build_time = run_command("date", &["-u", "+%Y-%m-%d %H:%M:%S UTC"])
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
@@ -93,9 +93,13 @@ fn main() {
|
||||
.map(|s| s.split('\n').next().unwrap_or("").to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
|
||||
let host = env::var("HOST").unwrap_or_else(|_| "Unknown".to_string());
|
||||
let authors = env::var("CARGO_PKG_AUTHORS").unwrap_or_else(|_| "Unknown".to_string());
|
||||
let repository = env::var("CARGO_PKG_REPOSITORY").unwrap_or_else(|_| "Unknown".to_string());
|
||||
|
||||
let version_string = format!(
|
||||
"{}\nbuild-time: {}\ncommit: {}\ntarget: {}\nprofile: {}\nrustc: {}",
|
||||
version, build_time, git_commit, target, profile, rustc_version
|
||||
"{}\nbuild-time: {}\ncommit: {}\ntarget: {}\nhost: {}\nprofile: {}\nrustc: {}\nauthors: {}\nrepository: {}",
|
||||
version, build_time, git_commit, target, host, profile, rustc_version, authors, repository
|
||||
);
|
||||
|
||||
let version_path = Path::new(&out_dir).join("version.txt");
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 exclusively.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use rust_ffmpeg::FFmpegBuilder;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct PluginImpl;
|
||||
|
||||
impl Plugin for PluginImpl {
|
||||
fn name(&self) -> &'static str {
|
||||
"ffmpeg_audio"
|
||||
}
|
||||
fn from_formats(&self) -> Vec<&'static str> {
|
||||
vec![
|
||||
"mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "wma", "amr", "aiff", "aiff", "au",
|
||||
]
|
||||
}
|
||||
fn to_formats(&self) -> Vec<&'static str> {
|
||||
vec![
|
||||
"mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "wma", "amr", "aiff", "aiff", "au",
|
||||
]
|
||||
}
|
||||
fn familiarity(&self, _from: &str, to: &str) -> u8 {
|
||||
match to {
|
||||
"mp3" | "wav" => 255,
|
||||
"flac" | "m4a" | "aac" => 240,
|
||||
"ogg" | "opus" => 220,
|
||||
"wma" | "amr" => 150,
|
||||
"aiff" | "au" => 120,
|
||||
_ => 128,
|
||||
}
|
||||
}
|
||||
fn quality(&self, _from: &str, to: &str) -> u8 {
|
||||
match to {
|
||||
"flac" | "wav" | "aiff" | "au" => 255,
|
||||
"opus" | "ogg" | "aac" | "m4a" => 220,
|
||||
"mp3" => 200,
|
||||
"wma" | "amr" => 150,
|
||||
_ => 128,
|
||||
}
|
||||
}
|
||||
fn speed(&self, _from: &str, to: &str) -> u8 {
|
||||
match to {
|
||||
"wav" | "aiff" | "au" => 255,
|
||||
"flac" => 200,
|
||||
"mp3" | "aac" | "m4a" | "ogg" => 180,
|
||||
"opus" | "wma" | "amr" => 150,
|
||||
_ => 128,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert(
|
||||
&self,
|
||||
input: &[u8],
|
||||
_from: &str,
|
||||
to: &str,
|
||||
temp_dir: &Path,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let mut temp_in = tempfile::Builder::new()
|
||||
.suffix(&format!(".{}", _from))
|
||||
.tempfile_in(temp_dir)
|
||||
.map_err(|e| e.to_string())?;
|
||||
temp_in.write_all(input).map_err(|e| e.to_string())?;
|
||||
|
||||
let temp_out = tempfile::Builder::new()
|
||||
.suffix(&format!(".{}", to))
|
||||
.tempfile_in(temp_dir)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let temp_out_path = temp_out.into_temp_path();
|
||||
|
||||
let in_path = temp_in.path().to_path_buf();
|
||||
|
||||
let mut raw_args = vec![];
|
||||
match to {
|
||||
"mp3" => {
|
||||
raw_args.extend(vec!["-c:a", "libmp3lame", "-q:a", "2"]);
|
||||
}
|
||||
"ogg" => {
|
||||
raw_args.extend(vec!["-c:a", "libvorbis", "-q:a", "4"]);
|
||||
}
|
||||
"flac" => {
|
||||
raw_args.extend(vec!["-c:a", "flac"]);
|
||||
}
|
||||
"aac" | "m4a" => {
|
||||
raw_args.extend(vec!["-c:a", "aac", "-b:a", "192k"]);
|
||||
}
|
||||
"opus" => {
|
||||
raw_args.extend(vec!["-c:a", "libopus", "-b:a", "128k"]);
|
||||
}
|
||||
"wma" => {
|
||||
raw_args.extend(vec!["-c:a", "wmav2", "-b:a", "192k"]);
|
||||
}
|
||||
"amr" => {
|
||||
raw_args.extend(vec!["-ar", "8000", "-c:a", "libopencore_amrnb", "-b:a", "12.2k"]);
|
||||
}
|
||||
"wav" | "aiff" | "au" => {
|
||||
raw_args.extend(vec!["-c:a", "pcm_s16le"]);
|
||||
}
|
||||
_ => {
|
||||
raw_args.extend(vec!["-c:a", "copy"]);
|
||||
}
|
||||
}
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
|
||||
rt.block_on(async {
|
||||
FFmpegBuilder::new()
|
||||
.map_err(|e| e.to_string())?
|
||||
.input_path(in_path)
|
||||
.output_path(temp_out_path.to_path_buf())
|
||||
.raw_args(raw_args)
|
||||
.overwrite()
|
||||
.run()
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
})?;
|
||||
|
||||
std::fs::read(&temp_out_path).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
+26
-41
@@ -18,10 +18,15 @@ use std::path::PathBuf;
|
||||
pub const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version = env!("CARGO_PKG_VERSION"), long_version = VERSION, about = "A file converter program.", long_about = None)]
|
||||
#[command(version = VERSION, about = "A file converter program.", long_about = None)]
|
||||
pub struct Args {
|
||||
#[arg(short = 'v', action = clap::ArgAction::Count, help = "Output information about the conversion. Use -v for path taken, -vv for step info, -vvv for timing, and -vvvv for plugin logs.")]
|
||||
pub verbose: u8,
|
||||
#[arg(
|
||||
short = 'v',
|
||||
long,
|
||||
default_value = "warn",
|
||||
help = "Output information about the conversion (trace, debug, info, warn, error)"
|
||||
)]
|
||||
pub verbose: Verbosity,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
@@ -67,44 +72,24 @@ pub struct Args {
|
||||
pub temp_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn verbosity_level(&self) -> u8 {
|
||||
self.verbose
|
||||
#[derive(clap::ValueEnum, Clone, Debug, Copy, PartialEq, Eq)]
|
||||
pub enum Verbosity {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl From<Verbosity> for tracing::Level {
|
||||
fn from(v: Verbosity) -> Self {
|
||||
match v {
|
||||
Verbosity::Trace => tracing::Level::TRACE,
|
||||
Verbosity::Debug => tracing::Level::DEBUG,
|
||||
Verbosity::Info => tracing::Level::INFO,
|
||||
Verbosity::Warn => tracing::Level::WARN,
|
||||
Verbosity::Error => tracing::Level::ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! test_args_verbosity {
|
||||
($name:ident, $verbose:expr, $expected:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let args = Args {
|
||||
verbose: $verbose,
|
||||
test: false,
|
||||
quiet: false,
|
||||
write_to_console: false,
|
||||
priority: "fqs".to_string(),
|
||||
input_path: "test".to_string(),
|
||||
output_path: None,
|
||||
temp_dir: std::path::PathBuf::from("/dev/shm"),
|
||||
};
|
||||
assert_eq!(args.verbosity_level(), $expected);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_args_verbosity!(test_verbosity_0, 0, 0);
|
||||
test_args_verbosity!(test_verbosity_1, 1, 1);
|
||||
test_args_verbosity!(test_verbosity_2, 2, 2);
|
||||
test_args_verbosity!(test_verbosity_3, 3, 3);
|
||||
test_args_verbosity!(test_verbosity_4, 4, 4);
|
||||
test_args_verbosity!(test_verbosity_5, 5, 5);
|
||||
test_args_verbosity!(test_verbosity_6, 10, 10);
|
||||
test_args_verbosity!(test_verbosity_7, 100, 100);
|
||||
test_args_verbosity!(test_verbosity_8, 255, 255);
|
||||
test_args_verbosity!(test_verbosity_9, 128, 128);
|
||||
test_args_verbosity!(test_verbosity_10, 50, 50);
|
||||
}
|
||||
|
||||
+35
-14
@@ -12,39 +12,56 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn identify_format(path: &str) -> Option<&'static str> {
|
||||
pub fn identify_format(path: &str, plugins: &[Box<dyn Plugin>]) -> Option<&'static str> {
|
||||
let p = Path::new(path);
|
||||
let ext = p
|
||||
.extension()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(path)
|
||||
.to_lowercase();
|
||||
match ext.as_str() {
|
||||
"jpg" | "jpeg" => Some("jpeg"),
|
||||
"png" => Some("png"),
|
||||
"mp4" => Some("mp4"),
|
||||
"webm" => Some("webm"),
|
||||
"mkv" => Some("mkv"),
|
||||
"avi" => Some("avi"),
|
||||
"mov" => Some("mov"),
|
||||
"wmv" => Some("wmv"),
|
||||
"flv" => Some("flv"),
|
||||
"gif" => Some("gif"),
|
||||
_ => None,
|
||||
let ext_str = match ext.as_str() {
|
||||
"jpg" => "jpeg",
|
||||
other => other,
|
||||
};
|
||||
|
||||
for plugin in plugins {
|
||||
if let Some(&f) = plugin.from_formats().iter().find(|&&f| f == ext_str) {
|
||||
return Some(f);
|
||||
}
|
||||
if let Some(&f) = plugin.to_formats().iter().find(|&&f| f == ext_str) {
|
||||
return Some(f);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
struct MockPlugin;
|
||||
impl Plugin for MockPlugin {
|
||||
fn name(&self) -> &'static str { "mock" }
|
||||
fn from_formats(&self) -> Vec<&'static str> {
|
||||
vec!["jpeg", "png", "mp4", "webm", "mkv", "avi", "mov", "wmv", "flv", "gif", "mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "wma", "amr", "aiff", "au"]
|
||||
}
|
||||
fn to_formats(&self) -> Vec<&'static str> { vec![] }
|
||||
fn familiarity(&self, _: &str, _: &str) -> u8 { 0 }
|
||||
fn quality(&self, _: &str, _: &str) -> u8 { 0 }
|
||||
fn speed(&self, _: &str, _: &str) -> u8 { 0 }
|
||||
fn convert(&self, _: &[u8], _: &str, _: &str, _: &Path) -> Result<Vec<u8>, String> { Ok(vec![]) }
|
||||
}
|
||||
|
||||
macro_rules! test_ident {
|
||||
($name:ident, $ext:expr, $expected:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
assert_eq!(identify_format($ext), $expected);
|
||||
let mock = Box::new(MockPlugin);
|
||||
let plugins: Vec<Box<dyn Plugin>> = vec![mock];
|
||||
assert_eq!(identify_format($ext, &plugins), $expected);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -74,4 +91,8 @@ mod tests {
|
||||
test_ident!(test_identify_format_19, "test_file_without_extension", None);
|
||||
test_ident!(test_identify_format_20, ".jpg", None);
|
||||
test_ident!(test_identify_format_21, "", None);
|
||||
test_ident!(test_identify_format_22, "test.mp3", Some("mp3"));
|
||||
test_ident!(test_identify_format_23, "test.WAV", Some("wav"));
|
||||
test_ident!(test_identify_format_24, "audio.flac", Some("flac"));
|
||||
test_ident!(test_identify_format_25, "music.ogg", Some("ogg"));
|
||||
}
|
||||
|
||||
+29
-47
@@ -29,36 +29,40 @@ use std::process;
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let level_filter = if args.quiet {
|
||||
tracing_subscriber::filter::LevelFilter::OFF
|
||||
} else {
|
||||
tracing::Level::from(args.verbose).into()
|
||||
};
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(level_filter)
|
||||
.init();
|
||||
|
||||
// Read input file
|
||||
let input_bytes = match fs::read(&args.input_path) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
if !args.quiet {
|
||||
eprintln!("Error reading input file: {}", e);
|
||||
}
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let from_format = match identifier::identify_format(&args.input_path) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
if !args.quiet {
|
||||
eprintln!("Error: Unknown input format.");
|
||||
}
|
||||
tracing::error!("Error reading input file: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let plugins = get_plugins();
|
||||
|
||||
let to_format = match &args.output_path {
|
||||
Some(out) => match identifier::identify_format(out) {
|
||||
let from_format = match identifier::identify_format(&args.input_path, &plugins) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
if !args.quiet {
|
||||
eprintln!("Error: Unknown output format.");
|
||||
tracing::error!("Unknown input format.");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let to_format = match &args.output_path {
|
||||
Some(out) => match identifier::identify_format(out, &plugins) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
tracing::error!("Unknown output format.");
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
@@ -74,11 +78,7 @@ fn main() {
|
||||
match default_target {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
if !args.quiet {
|
||||
eprintln!(
|
||||
"Error: No output path provided, and no available conversions found."
|
||||
);
|
||||
}
|
||||
tracing::error!("No output path provided, and no available conversions found.");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -88,59 +88,41 @@ fn main() {
|
||||
let path = match pathfinder::find_best_path(&plugins, from_format, to_format, &args.priority) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
if !args.quiet {
|
||||
eprintln!(
|
||||
"Error: No conversion path found from {} to {}.",
|
||||
from_format, to_format
|
||||
);
|
||||
}
|
||||
tracing::error!("No conversion path found from {} to {}.", from_format, to_format);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if args.test {
|
||||
if !args.quiet {
|
||||
let path_str: Vec<_> = path.iter().map(|(p, _, _)| p.name()).collect();
|
||||
println!("Test successful. Path: {}", path_str.join(" -> "));
|
||||
}
|
||||
tracing::info!("Test successful. Path: {}", path_str.join(" -> "));
|
||||
return;
|
||||
}
|
||||
|
||||
let verbosity = args.verbosity_level();
|
||||
let result = runner::run_conversion(&path, &input_bytes, verbosity, &args.temp_dir);
|
||||
let result = runner::run_conversion(&path, &input_bytes, &args.temp_dir);
|
||||
|
||||
let output_bytes = match result {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
if !args.quiet {
|
||||
eprintln!("Conversion error: {}", e);
|
||||
}
|
||||
tracing::error!("Conversion error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(out_path) = &args.output_path {
|
||||
if let Err(e) = fs::write(out_path, &output_bytes) {
|
||||
if !args.quiet {
|
||||
eprintln!("Error writing output file: {}", e);
|
||||
}
|
||||
tracing::error!("Error writing output file: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let is_piped = !io::stdout().is_terminal();
|
||||
if !is_piped && !args.write_to_console {
|
||||
if !args.quiet {
|
||||
eprintln!(
|
||||
"Warning: Outputting directly to console. Use -c to suppress this warning, or redirect to a file."
|
||||
);
|
||||
}
|
||||
tracing::warn!("Outputting directly to console. Use -c to suppress this warning, or redirect to a file.");
|
||||
}
|
||||
|
||||
let mut stdout = io::stdout();
|
||||
if let Err(e) = stdout.write_all(&output_bytes) {
|
||||
if !args.quiet {
|
||||
eprintln!("Error writing to console: {}", e);
|
||||
}
|
||||
tracing::error!("Error writing to console: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-29
@@ -19,49 +19,40 @@ use std::time::Instant;
|
||||
pub fn run_conversion(
|
||||
path: &[(&dyn Plugin, &str, &str)],
|
||||
input: &[u8],
|
||||
verbosity: u8,
|
||||
temp_dir: &Path,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
if verbosity >= 1 {
|
||||
let path_str: Vec<_> = path.iter().map(|(p, _, _)| p.name()).collect();
|
||||
eprintln!("Path taken: {}", path_str.join(" -> "));
|
||||
}
|
||||
tracing::info!("Path taken: {}", path_str.join(" -> "));
|
||||
|
||||
let mut current_data = input.to_vec();
|
||||
|
||||
for (plugin, from_format, to_format) in path {
|
||||
if verbosity >= 2 {
|
||||
eprintln!(
|
||||
tracing::info!(
|
||||
"Converting {} to {} using plugin {}...",
|
||||
from_format,
|
||||
to_format,
|
||||
plugin.name()
|
||||
);
|
||||
}
|
||||
|
||||
if verbosity >= 4 {
|
||||
eprintln!("[Plugin Log] Running plugin: {}", plugin.name());
|
||||
eprintln!("[Plugin Log] Source format: {}", from_format);
|
||||
eprintln!("[Plugin Log] Target format: {}", to_format);
|
||||
eprintln!(
|
||||
tracing::debug!("[Plugin Log] Running plugin: {}", plugin.name());
|
||||
tracing::debug!("[Plugin Log] Source format: {}", from_format);
|
||||
tracing::debug!("[Plugin Log] Target format: {}", to_format);
|
||||
tracing::debug!(
|
||||
"[Plugin Log] Metrics - Familiarity: {}, Quality: {}, Speed: {}",
|
||||
plugin.familiarity(*from_format, *to_format),
|
||||
plugin.quality(*from_format, *to_format),
|
||||
plugin.speed(*from_format, *to_format)
|
||||
);
|
||||
}
|
||||
|
||||
let start_time = Instant::now();
|
||||
current_data = plugin.convert(¤t_data, from_format, to_format, temp_dir)?;
|
||||
let elapsed = start_time.elapsed();
|
||||
|
||||
if verbosity >= 3 {
|
||||
eprintln!(
|
||||
"Step '{} -> {}' completed in {:.2?}",
|
||||
tracing::info!(
|
||||
"Step '{} -> {}' completed in {:?}",
|
||||
from_format, to_format, elapsed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(current_data)
|
||||
}
|
||||
@@ -114,7 +105,7 @@ mod tests {
|
||||
}
|
||||
|
||||
macro_rules! test_runner {
|
||||
($name:ident, $verbosity:expr, $fail:expr, $expected_res:expr, $expected_len:expr) => {
|
||||
($name:ident, $fail:expr, $expected_res:expr, $expected_len:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let plugin = MockPlugin {
|
||||
@@ -128,7 +119,7 @@ mod tests {
|
||||
let input = vec![0];
|
||||
|
||||
let result =
|
||||
run_conversion(&path, &input, $verbosity, std::path::Path::new("/dev/shm"));
|
||||
run_conversion(&path, &input, std::path::Path::new("/dev/shm"));
|
||||
assert_eq!(result.is_ok(), $expected_res);
|
||||
if let Ok(res) = result {
|
||||
assert_eq!(res.len(), $expected_len);
|
||||
@@ -137,14 +128,6 @@ mod tests {
|
||||
};
|
||||
}
|
||||
|
||||
test_runner!(test_runner_verb_0, 0, false, true, 2);
|
||||
test_runner!(test_runner_verb_1, 1, false, true, 2);
|
||||
test_runner!(test_runner_verb_2, 2, false, true, 2);
|
||||
test_runner!(test_runner_verb_3, 3, false, true, 2);
|
||||
test_runner!(test_runner_verb_4, 4, false, true, 2);
|
||||
test_runner!(test_runner_verb_5, 5, false, true, 2);
|
||||
test_runner!(test_runner_verb_0_fail, 0, true, false, 0);
|
||||
test_runner!(test_runner_verb_1_fail, 1, true, false, 0);
|
||||
test_runner!(test_runner_verb_4_fail, 4, true, false, 0);
|
||||
test_runner!(test_runner_verb_255, 255, false, true, 2);
|
||||
test_runner!(test_runner_verb_0, false, true, 2);
|
||||
test_runner!(test_runner_verb_0_fail, true, false, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user