mirror of
https://github.com/tealdeer-rs/tealdeer.git
synced 2026-08-09 09:49:10 +02:00
686 lines
19 KiB
Rust
686 lines
19 KiB
Rust
//! Integration tests.
|
|
|
|
use std::fs::{create_dir_all, File};
|
|
use std::io::Write;
|
|
use std::process::Command;
|
|
use std::time::{Duration, SystemTime};
|
|
|
|
use assert_cmd::prelude::*;
|
|
use predicates::boolean::PredicateBooleanExt;
|
|
use predicates::prelude::predicate::str::{contains, diff, is_empty};
|
|
use tempfile::{Builder, TempDir};
|
|
|
|
// TODO: Should be 'cache::CACHE_DIR_ENV_VAR'. This requires to have a library crate for the logic.
|
|
static CACHE_DIR_ENV_VAR: &str = "TEALDEER_CACHE_DIR";
|
|
|
|
struct TestEnv {
|
|
pub cache_dir: TempDir,
|
|
pub custom_pages_dir: TempDir,
|
|
pub config_dir: TempDir,
|
|
pub input_dir: TempDir,
|
|
pub default_features: bool,
|
|
pub features: Vec<String>,
|
|
}
|
|
|
|
impl TestEnv {
|
|
fn new() -> Self {
|
|
TestEnv {
|
|
cache_dir: Builder::new().prefix(".tldr.test.cache").tempdir().unwrap(),
|
|
config_dir: Builder::new().prefix(".tldr.test.conf").tempdir().unwrap(),
|
|
custom_pages_dir: Builder::new()
|
|
.prefix(".tldr.test.custom-pages")
|
|
.tempdir()
|
|
.unwrap(),
|
|
input_dir: Builder::new().prefix(".tldr.test.input").tempdir().unwrap(),
|
|
default_features: true,
|
|
features: vec![],
|
|
}
|
|
}
|
|
|
|
/// Write `content` to "config.toml" in the `config_dir` directory
|
|
fn write_config(&self, content: impl AsRef<str>) {
|
|
let config_file_name = self.config_dir.path().join("config.toml");
|
|
println!("Config path: {:?}", config_file_name);
|
|
|
|
let mut config_file = File::create(&config_file_name).unwrap();
|
|
config_file.write_all(content.as_ref().as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Add entry for that environment to the "common" pages.
|
|
fn add_entry(&self, name: &str, contents: &str) {
|
|
self.add_os_entry("common", name, contents);
|
|
}
|
|
|
|
/// Add entry for that environment to an OS-specific subfolder.
|
|
fn add_os_entry(&self, os: &str, name: &str, contents: &str) {
|
|
let dir = self
|
|
.cache_dir
|
|
.path()
|
|
.join("tldr-master")
|
|
.join("pages")
|
|
.join(os);
|
|
create_dir_all(&dir).unwrap();
|
|
|
|
let mut file = File::create(&dir.join(format!("{}.md", name))).unwrap();
|
|
file.write_all(contents.as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Add custom patch entry to the custom_pages_dir
|
|
fn add_page_entry(&self, name: &str, contents: &str) {
|
|
let dir = self.custom_pages_dir.path();
|
|
create_dir_all(dir).unwrap();
|
|
let mut file = File::create(&dir.join(format!("{}.page", name))).unwrap();
|
|
file.write_all(contents.as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Add custom patch entry to the custom_pages_dir
|
|
fn add_patch_entry(&self, name: &str, contents: &str) {
|
|
let dir = self.custom_pages_dir.path();
|
|
create_dir_all(dir).unwrap();
|
|
let mut file = File::create(&dir.join(format!("{}.patch", name))).unwrap();
|
|
file.write_all(contents.as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Disable default features.
|
|
#[allow(dead_code)] // Might be useful in the future
|
|
fn no_default_features(mut self) -> Self {
|
|
self.default_features = false;
|
|
self
|
|
}
|
|
|
|
/// Add the specified feature.
|
|
#[allow(dead_code)] // Might be useful in the future
|
|
fn with_feature<S: Into<String>>(mut self, feature: S) -> Self {
|
|
self.features.push(feature.into());
|
|
self
|
|
}
|
|
|
|
/// Return a new `Command` with env vars set.
|
|
fn command(&self) -> Command {
|
|
let mut build = escargot::CargoBuild::new()
|
|
.bin("tldr")
|
|
.current_release()
|
|
.current_target();
|
|
if !self.default_features {
|
|
build = build.arg("--no-default-features");
|
|
}
|
|
if !self.features.is_empty() {
|
|
build = build.arg(&format!("--feature {}", self.features.join(",")));
|
|
}
|
|
let run = build.run().unwrap();
|
|
let mut cmd = run.command();
|
|
cmd.env(CACHE_DIR_ENV_VAR, self.cache_dir.path().to_str().unwrap());
|
|
cmd.env(
|
|
"TEALDEER_CONFIG_DIR",
|
|
self.config_dir.path().to_str().unwrap(),
|
|
);
|
|
cmd
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_cache() {
|
|
TestEnv::new()
|
|
.command()
|
|
.args(["sl"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("Cache not found. Please run `tldr --update`."));
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_cache() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["sl"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("Cache not found. Please run `tldr --update`."));
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--update"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("Successfully updated cache."));
|
|
|
|
testenv.command().args(["sl"]).assert().success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_quiet_cache() {
|
|
let testenv = TestEnv::new();
|
|
testenv
|
|
.command()
|
|
.args(["--update", "--quiet"])
|
|
.assert()
|
|
.success()
|
|
.stdout(is_empty());
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--clear-cache", "--quiet"])
|
|
.assert()
|
|
.success()
|
|
.stdout(is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_quiet_failures() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--update", "-q"])
|
|
.assert()
|
|
.success()
|
|
.stdout(is_empty());
|
|
|
|
testenv
|
|
.command()
|
|
.args(["fakeprogram", "-q"])
|
|
.assert()
|
|
.failure()
|
|
.stdout(is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_quiet_old_cache() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--update", "-q"])
|
|
.assert()
|
|
.success()
|
|
.stdout(is_empty());
|
|
|
|
filetime::set_file_mtime(
|
|
testenv.cache_dir.path().join("tldr-master"),
|
|
filetime::FileTime::from_unix_time(1, 0),
|
|
)
|
|
.unwrap();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["tldr"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("The cache hasn't been updated for more than "));
|
|
|
|
testenv
|
|
.command()
|
|
.args(["tldr", "--quiet"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("The cache hasn't been updated for more than ").not());
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_cache_directory_path() {
|
|
let testenv = TestEnv::new();
|
|
let cache_dir = testenv.cache_dir.path();
|
|
let internal_cache_dir = cache_dir.join("internal");
|
|
|
|
let mut command = testenv.command();
|
|
command.env(CACHE_DIR_ENV_VAR, internal_cache_dir.to_str().unwrap());
|
|
|
|
assert!(!internal_cache_dir.exists());
|
|
|
|
command
|
|
.arg("-u")
|
|
.assert()
|
|
.success()
|
|
.stderr(contains(format!(
|
|
"Successfully created cache directory path `{}`.",
|
|
internal_cache_dir.to_str().unwrap()
|
|
)))
|
|
.stderr(contains("Successfully updated cache."));
|
|
|
|
assert!(internal_cache_dir.is_dir());
|
|
}
|
|
|
|
#[test]
|
|
fn test_cache_location_not_a_directory() {
|
|
let testenv = TestEnv::new();
|
|
let cache_dir = testenv.cache_dir.path();
|
|
let internal_file = cache_dir.join("internal");
|
|
File::create(&internal_file).unwrap();
|
|
|
|
let mut command = testenv.command();
|
|
command.env(CACHE_DIR_ENV_VAR, internal_file.to_str().unwrap());
|
|
|
|
command
|
|
.arg("-u")
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains(format!(
|
|
"Path specified by ${} is not a directory.",
|
|
CACHE_DIR_ENV_VAR
|
|
)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_setup_seed_config() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--seed-config"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("Successfully created seed config file here"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_show_paths() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--show-paths"])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains(format!(
|
|
"Config dir: {}",
|
|
testenv.config_dir.path().to_str().unwrap(),
|
|
)))
|
|
.stdout(contains(format!(
|
|
"Config path: {}",
|
|
testenv
|
|
.config_dir
|
|
.path()
|
|
.join("config.toml")
|
|
.to_str()
|
|
.unwrap(),
|
|
)))
|
|
.stdout(contains(format!(
|
|
"Cache dir: {}",
|
|
testenv.cache_dir.path().to_str().unwrap(),
|
|
)))
|
|
.stdout(contains(format!(
|
|
"Pages dir: {}",
|
|
testenv
|
|
.cache_dir
|
|
.path()
|
|
.join("tldr-master")
|
|
.to_str()
|
|
.unwrap(),
|
|
)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_os_specific_page() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv.add_os_entry("sunos", "truss", "contents");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--os", "sunos", "truss"])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_markdown_rendering() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv.add_entry("which", include_str!("which-markdown.expected"));
|
|
|
|
let expected = include_str!("which-markdown.expected");
|
|
testenv
|
|
.command()
|
|
.args(["-m", "which"])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
fn _test_correct_rendering(
|
|
input_file: &str,
|
|
filename: &str,
|
|
expected: &'static str,
|
|
color_option: &str,
|
|
) {
|
|
let testenv = TestEnv::new();
|
|
|
|
// Create input file
|
|
let file_path = testenv.input_dir.path().join(filename);
|
|
println!("Testfile path: {:?}", file_path);
|
|
let mut file = File::create(&file_path).unwrap();
|
|
file.write_all(input_file.as_bytes()).unwrap();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--color", color_option, "-f", file_path.to_str().unwrap()])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
/// An end-to-end integration test for direct file rendering (v1 syntax).
|
|
#[test]
|
|
fn test_correct_rendering_v1() {
|
|
_test_correct_rendering(
|
|
include_str!("inkscape-v1.md"),
|
|
"inkscape-v1.md",
|
|
include_str!("inkscape-default.expected"),
|
|
"always",
|
|
);
|
|
}
|
|
|
|
/// An end-to-end integration test for direct file rendering (v2 syntax).
|
|
#[test]
|
|
fn test_correct_rendering_v2() {
|
|
_test_correct_rendering(
|
|
include_str!("inkscape-v2.md"),
|
|
"inkscape-v2.md",
|
|
include_str!("inkscape-default.expected"),
|
|
"always",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// An end-to-end integration test for direct file rendering with the `--color auto` option. This
|
|
/// will not use styling since output is not stdout.
|
|
fn test_rendering_color_auto() {
|
|
_test_correct_rendering(
|
|
include_str!("inkscape-v2.md"),
|
|
"inkscape-v2.md",
|
|
include_str!("inkscape-default-no-color.expected"),
|
|
"auto",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// An end-to-end integration test for direct file rendering with the `--color never` option.
|
|
fn test_rendering_color_never() {
|
|
_test_correct_rendering(
|
|
include_str!("inkscape-v2.md"),
|
|
"inkscape-v2.md",
|
|
include_str!("inkscape-default-no-color.expected"),
|
|
"never",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rendering_i18n() {
|
|
_test_correct_rendering(
|
|
include_str!("chmod.ru.md"),
|
|
"chmod.ru.md",
|
|
include_str!("chmod.ru.expected"),
|
|
"always",
|
|
);
|
|
}
|
|
|
|
/// An end-to-end integration test for rendering with custom syntax config.
|
|
#[test]
|
|
fn test_correct_rendering_with_config() {
|
|
let testenv = TestEnv::new();
|
|
|
|
// Setup config file
|
|
// TODO should be config::CONFIG_FILE_NAME
|
|
let config_file_path = testenv.config_dir.path().join("config.toml");
|
|
println!("Config path: {:?}", config_file_path);
|
|
|
|
let mut config_file = File::create(&config_file_path).unwrap();
|
|
config_file
|
|
.write_all(include_bytes!("config.toml"))
|
|
.unwrap();
|
|
|
|
// Create input file
|
|
let file_path = testenv.input_dir.path().join("inkscape-v2.md");
|
|
println!("Testfile path: {:?}", file_path);
|
|
|
|
let mut file = File::create(&file_path).unwrap();
|
|
file.write_all(include_bytes!("inkscape-v2.md")).unwrap();
|
|
|
|
// Load expected output
|
|
let expected = include_str!("inkscape-with-config.expected");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--color", "always", "-f", file_path.to_str().unwrap()])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
#[test]
|
|
fn test_spaces_find_command() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--update"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("Successfully updated cache."));
|
|
|
|
testenv
|
|
.command()
|
|
.args(["git", "checkout"])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_pager_flag_enable() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--update"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("Successfully updated cache."));
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--pager", "which"])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_list_flag_rendering() {
|
|
let testenv = TestEnv::new();
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--list"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("Cache not found. Please run `tldr --update`."));
|
|
|
|
testenv.add_entry("foo", "");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--list"])
|
|
.assert()
|
|
.success()
|
|
.stdout("foo\n");
|
|
|
|
testenv.add_entry("bar", "");
|
|
testenv.add_entry("baz", "");
|
|
testenv.add_entry("qux", "");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["--list"])
|
|
.assert()
|
|
.success()
|
|
.stdout("bar\nbaz\nfoo\nqux\n");
|
|
}
|
|
|
|
#[test]
|
|
fn test_autoupdate_cache() {
|
|
let testenv = TestEnv::new();
|
|
|
|
// The first time, if automatic updates are disabled, the cache should not be found
|
|
testenv
|
|
.command()
|
|
.args(["--list"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("Cache not found. Please run `tldr --update`."));
|
|
|
|
let config_file_path = testenv.config_dir.path().join("config.toml");
|
|
let cache_file_path = testenv.cache_dir.path().join("tldr-master");
|
|
|
|
// Activate automatic updates, set the auto-update interval to 24 hours
|
|
let mut config_file = File::create(&config_file_path).unwrap();
|
|
config_file
|
|
.write_all(b"[updates]\nauto_update = true\nauto_update_interval_hours = 24")
|
|
.unwrap();
|
|
config_file.flush().unwrap();
|
|
|
|
// Helper function that runs `tldr --list` and asserts that the cache is automatically updated
|
|
// or not, depending on the value of `expected`.
|
|
let check_cache_updated = |expected| {
|
|
let assert = testenv.command().args(["--list"]).assert().success();
|
|
let pred = contains("Successfully updated cache");
|
|
if expected {
|
|
assert.stderr(pred)
|
|
} else {
|
|
assert.stderr(pred.not())
|
|
};
|
|
};
|
|
|
|
// The cache is updated the first time we run `tldr --list`
|
|
check_cache_updated(true);
|
|
|
|
// The cache is not updated with a subsequent call
|
|
check_cache_updated(false);
|
|
|
|
// We update the modification and access times such that they are about 23 hours from now.
|
|
// auto-update interval is 24 hours, the cache should not be updated
|
|
let new_mtime = SystemTime::now() - Duration::from_secs(82_800);
|
|
filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
|
|
check_cache_updated(false);
|
|
|
|
// We update the modification and access times such that they are about 25 hours from now.
|
|
// auto-update interval is 24 hours, the cache should be updated
|
|
let new_mtime = SystemTime::now() - Duration::from_secs(90_000);
|
|
filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
|
|
check_cache_updated(true);
|
|
|
|
// The cache is not updated with a subsequent call
|
|
check_cache_updated(false);
|
|
}
|
|
|
|
/// End-end test to ensure .page files overwrite pages in cache_dir
|
|
#[test]
|
|
fn test_custom_page_overwrites() {
|
|
let testenv = TestEnv::new();
|
|
|
|
// set custom pages directory
|
|
testenv.write_config(format!(
|
|
"[directories]\ncustom_pages_dir = '{}'",
|
|
testenv.custom_pages_dir.path().to_str().unwrap()
|
|
));
|
|
|
|
// Add file that should be ignored to the cache dir
|
|
testenv.add_entry("inkscape-v2", "");
|
|
// Add .page file to custome_pages_dir
|
|
testenv.add_page_entry("inkscape-v2", include_str!("inkscape-v2.md"));
|
|
|
|
// Load expected output
|
|
let expected = include_str!("inkscape-default-no-color.expected");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["inkscape-v2", "--color", "never"])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
/// End-End test to ensure that .patch files are appened to pages in the cache_dir
|
|
#[test]
|
|
fn test_custom_patch_appends_to_common() {
|
|
let testenv = TestEnv::new();
|
|
|
|
// set custom pages directory
|
|
testenv.write_config(format!(
|
|
"[directories]\ncustom_pages_dir = '{}'",
|
|
testenv.custom_pages_dir.path().to_str().unwrap()
|
|
));
|
|
|
|
// Add page to the cache dir
|
|
testenv.add_entry("inkscape-v2", include_str!("inkscape-v2.md"));
|
|
// Add .page file to custome_pages_dir
|
|
testenv.add_patch_entry("inkscape-v2", include_str!("inkscape-v2.patch"));
|
|
|
|
// Load expected output
|
|
let expected = include_str!("inkscape-patched-no-color.expected");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["inkscape-v2", "--color", "never"])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
/// End-End test to ensure that .patch files are not appended to .page files in the custom_pages_dir
|
|
/// Maybe this interaction should change but I put this test here for the coverage
|
|
#[test]
|
|
fn test_custom_patch_does_not_append_to_custom() {
|
|
let testenv = TestEnv::new();
|
|
|
|
// set custom pages directory
|
|
testenv.write_config(format!(
|
|
"[directories]\ncustom_pages_dir = '{}'",
|
|
testenv.custom_pages_dir.path().to_str().unwrap()
|
|
));
|
|
|
|
testenv.add_entry("test", "");
|
|
|
|
// Add page to the cache dir
|
|
testenv.add_page_entry("inkscape-v2", include_str!("inkscape-v2.md"));
|
|
// Add .page file to custome_pages_dir
|
|
testenv.add_patch_entry("inkscape-v2", include_str!("inkscape-v2.patch"));
|
|
|
|
// Load expected output
|
|
let expected = include_str!("inkscape-default-no-color.expected");
|
|
|
|
testenv
|
|
.command()
|
|
.args(["inkscape-v2", "--color", "never"])
|
|
.assert()
|
|
.success()
|
|
.stdout(diff(expected));
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(target_os = "windows")]
|
|
fn test_pager_warning() {
|
|
let testenv = TestEnv::new();
|
|
testenv
|
|
.command()
|
|
.args(["--update"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("Successfully updated cache."));
|
|
|
|
// Regular call should not show a "pager flag not available on windows" warning
|
|
testenv
|
|
.command()
|
|
.args(["which"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("pager flag not available on Windows").not());
|
|
|
|
// But it should be shown if the pager flag is true
|
|
testenv
|
|
.command()
|
|
.args(["which", "-p"])
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("pager flag not available on Windows"));
|
|
}
|