#[cfg(test)]
mod tests {
use std::{
env, fs,
io::{self, Write},
path::{Path, PathBuf},
process::{Command, Stdio},
};
struct ExecResult {
exit_code: i32,
stdout: String,
stderr: String,
}
#[test]
fn print_version() {
let result = exec(vec!["-v"]);
assert_eq!(env!("CARGO_PKG_VERSION"), result.stdout.trim());
}
#[test]
fn stdin_in_stdout_out() {
let html = "
Hello
";
let result = exec_with_input(Some(html), vec![]);
assert_eq!(result.exit_code, 0);
assert_eq!("# Hello", result.stdout);
}
#[test]
fn stdin_in_stdout_out_with_explicit_input() {
let html = "Hello
";
let result = exec_with_input(Some(html), vec!["--input", "-"]);
assert_eq!(result.exit_code, 0);
assert_eq!("# Hello", result.stdout);
}
#[test]
fn stdin_in_file_out() {
let html = "Hello
";
let result =
exec_with_temp_fs_and_input(Some(html), vec!["--output", "output.md"], |dir| {
let output = dir.join("output.md");
assert!(output.exists());
assert_eq!("# Hello", fs::read_to_string(output).unwrap());
});
assert_eq!(result.exit_code, 0);
}
#[test]
fn stdin_in_folder_out() {
let html = "Hello
";
let result = exec_with_input(Some(html), vec!["-", "--output", "./"]);
assert_eq!(result.exit_code, 1);
assert!(result.stderr.contains("Output cannot be a directory."));
}
#[test]
fn unnamed_file_in_folder_out() {
let result = exec_with_temp_fs(vec!["hello.html", "--output", "./"], |dir| {
assert!(dir.join("hello.md").exists());
});
assert_eq!(result.exit_code, 0);
assert!(result.stdout.contains("Converted"));
}
#[test]
fn file_in_with_input_option() {
let result = exec_with_temp_fs(vec!["--input", "hello.html", "--output", "./"], |dir| {
assert!(dir.join("hello.md").exists());
});
assert_eq!(result.exit_code, 0);
}
#[test]
fn file_in_not_found() {
let result = exec_with_temp_fs(vec!["404.html"], |_| {});
assert_eq!(result.exit_code, 1);
assert!(result.stderr.contains("File or directory does not exist"));
}
#[test]
fn file_in_file_out() {
let result = exec_with_temp_fs(
vec!["hello.html", "--output", "hello_converted.md"],
|dir| {
let file = dir.join("hello_converted.md");
assert!(file.exists());
assert!(file.is_file());
},
);
assert_eq!(result.exit_code, 0);
}
#[test]
fn file_in_folder_out() {
let result = exec_with_temp_fs(vec!["hello.html", "--output", "converted"], |dir| {
let file = dir.join("converted").join("hello.md");
assert!(file.exists());
assert!(file.is_file());
});
assert_eq!(result.exit_code, 0);
}
#[test]
fn folder_in_folder_out() {
let result = exec_with_temp_fs(vec!["./sub-folder2", "--output", "./"], |dir| {
let html_count = count_dir_file_count(&dir.join("./sub-folder2"), "html", true);
let md_count = count_dir_file_count(&dir, "md", true);
assert_eq!(html_count, md_count);
});
assert_eq!(result.exit_code, 0);
}
#[test]
fn folder_in_default_out() {
let result = exec_with_temp_fs(vec!["./**/*.html"], |_| {});
assert_eq!(result.exit_code, 1);
assert!(result
.stderr
.contains("Output to stdout doesn't support multiple files as the input."))
}
#[test]
fn folder_in_stdout_out() {
let result = exec_with_temp_fs(vec!["./**/*.html", "--output", "-"], |_| {});
assert_eq!(result.exit_code, 1);
assert!(result
.stderr
.contains("Output to stdout doesn't support multiple files as the input."))
}
#[test]
fn glob_in_folder_out_hierarchy() {
let result = exec_with_temp_fs(vec!["**/*.html", "--output", "./"], |dir| {
let sub_folders = vec!["./", "sub-folder", "sub-folder2"];
for sub_folder in sub_folders {
let html_count = count_dir_file_count(&dir.join(sub_folder), "html", false);
let md_count = count_dir_file_count(&dir.join(sub_folder), "md", false);
assert_eq!(html_count, md_count);
}
});
assert_eq!(result.exit_code, 0);
}
#[test]
fn glob_in_folder_out_flatten() {
let result = exec_with_temp_fs(
vec!["**/*.html", "--flatten-output", "--output", "./"],
|dir| {
let html_count = count_dir_file_count(&dir, "html", true);
let md_count = count_dir_file_count(&dir, "md", false);
assert_eq!(html_count, md_count);
},
);
assert_eq!(result.exit_code, 0);
}
#[test]
fn read_options_toml() {
let result = exec_with_temp_fs(
vec!["hello.html", "--options-file", "cli-options.toml"],
|_| {},
);
assert_eq!(result.exit_code, 0);
assert!(result.stdout.contains("Hello World!\n============"))
}
fn exec(args: Vec<&str>) -> ExecResult {
exec_with_input(None, args)
}
fn exec_with_input(input_text: Option<&str>, args: Vec<&str>) -> ExecResult {
let mut child = Command::new("cargo")
.arg("run")
.arg("--")
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to spawn child process");
if let Some(input_text) = input_text {
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(input_text.as_bytes())
.expect("Failed to write to stdin");
}
}
let output = child.wait_with_output().expect("Failed to read stdout");
ExecResult {
exit_code: output.status.code().unwrap(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
}
}
fn exec_with_temp_fs(args: Vec<&str>, verify: impl FnOnce(PathBuf) -> ()) -> ExecResult {
exec_with_temp_fs_and_input(None, args, verify)
}
fn exec_with_temp_fs_and_input(
input_text: Option<&str>,
args: Vec<&str>,
verify: impl FnOnce(PathBuf) -> (),
) -> ExecResult {
let tests_dir = env::current_dir().unwrap().join("tests");
let html_dir = tests_dir.join("html");
let temp_dir = tests_dir
.join("temp")
.join(format!("{}", uuid::Uuid::new_v4().to_string()));
// Copy html dir temp dir
copy_dir(&html_dir, &temp_dir)
.expect(format!("Cannot setup temp dir: {:?}", temp_dir).as_str());
fs::copy(
tests_dir.join("cli-options.toml"),
&temp_dir.join("cli-options.toml"),
)
.unwrap();
let mut child = Command::new("cargo")
.arg("run")
.arg("--")
.args(args)
.current_dir(&temp_dir)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to spawn child process");
if let Some(input_text) = input_text {
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(input_text.as_bytes())
.expect("Failed to write to stdin");
}
}
let output = child.wait_with_output().expect("Failed to read stdout");
let result = ExecResult {
exit_code: output.status.code().unwrap(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
};
verify(temp_dir.clone());
fs::remove_dir_all(temp_dir.clone())
.expect(format!("Cannot delete temp dir: {}", temp_dir.to_str().unwrap()).as_str());
result
}
fn copy_dir(source: &Path, target: &Path) -> io::Result<()> {
if !target.exists() {
fs::create_dir_all(target)?;
}
for entry in fs::read_dir(source)? {
let entry = entry?;
let entry_path = entry.path();
let file_name = entry_path.file_name().unwrap();
let target_path = target.join(file_name);
if entry_path.is_dir() {
copy_dir(&entry_path, &target_path)?;
} else {
fs::copy(&entry_path, &target_path)?;
}
}
Ok(())
}
fn count_dir_file_count(dir: &PathBuf, ext: &str, recursively: bool) -> usize {
let mut count: usize = 0;
for entry in fs::read_dir(dir).unwrap() {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_dir() {
if recursively {
count += count_dir_file_count(&path, ext, true);
}
} else {
if path.extension().unwrap().to_str().unwrap() == ext {
count += 1;
}
}
}
}
count
}
}