Skip to content

Commit

Permalink
fix: inconsistent tests, failing symlink deletion on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
elkowar committed Jan 4, 2025
1 parent a9af6c6 commit dc7f021
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 92 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ whoami = "1.5.2"
winnow = { version = "0.6.20", features = ["unstable-recover"] }
cov-mark = "2.0.0"
arbitrary = { version = "1.4.1", features = ["derive"] }
symlink = "0.1.0"
# rhai-autodocs = { version = "0.7.0", path = "../../clones/rhai-autodocs" }

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion src/eggs_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ mod test {

use crate::{
eggs_config::{DeploymentStrategy, EggConfig},
util::TestResult,
util::test_util::TestResult,
};

use rstest::rstest;
Expand Down
2 changes: 1 addition & 1 deletion src/script/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ impl Module {

#[cfg(test)]
mod test {
use crate::util::TestResult;
use crate::util::test_util::TestResult;
use miette::IntoDiagnostic as _;
use rhai::Variant;

Expand Down
15 changes: 7 additions & 8 deletions src/snapshots/yolk__yolk__test__deployment_error.snap
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
---
source: src/yolk.rs
expression: "miette::Report::from(yolk.sync_egg_deployment(&egg).unwrap_err())"
snapshot_kind: text
expression: render_error(yolk.sync_egg_deployment(&egg).unwrap_err())
---
× Failed to deploy egg bar
╰─▶ × Something went wrong

Error:
× Failed to deploy ~/yolk/eggs/bar/[filename]
─▶ failed to symlink file from /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
to /tmp/[tmp-dir]/[filename]: File exists (os error 17)
× Failed to deploy /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
─▶ Failed to create symlink at /tmp/[tmp-dir]/[filename] -> /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
╰─▶ File exists (os error 17)

Error:
× Failed to deploy ~/yolk/eggs/bar/[filename]
─▶ failed to symlink file from /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
to /tmp/[tmp-dir]/[filename]: File exists (os error 17)
× Failed to deploy /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
─▶ Failed to create symlink at /tmp/[tmp-dir]/[filename] -> /tmp/[tmp-dir]/yolk/eggs/bar/[filename]
╰─▶ File exists (os error 17)
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
source: src/yolk.rs
expression: "yolk.prepare_eval_ctx_for_templates(crate::yolk::EvalMode::Local).map_err(|e|\ncreate_regex(r\"\\[.*.rhai:\\d+:\\d+]\").unwrap().replace(&format!(\"{:?}\", e),\n\"[no-filename-in-test]\").to_string()).unwrap_err()"
snapshot_kind: text
expression: "yolk.prepare_eval_ctx_for_templates(crate::yolk::EvalMode::Local).map_err(|e|\ncreate_regex(r\"\\[.*.rhai:\\d+:\\d+]\").unwrap().replace(&render_report(e),\n\"[no-filename-in-test]\").to_string()).unwrap_err()"
---
× Failed to execute yolk.rhai
╰─▶ Syntax error: Expecting ')' to close the parameters list of function
'foo' (line 2, position 1)
╰─▶ Syntax error: Expecting ')' to close the parameters list of function 'foo' (line 2, position 1)
╭─[no-filename-in-test]
1 │ fn foo(
· ┬
Expand Down
2 changes: 1 addition & 1 deletion src/templating/comment_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl CommentStyle {

#[cfg(test)]
mod test {
use crate::util::TestResult;
use crate::util::test_util::TestResult;

use crate::templating::element::Element;

Expand Down
2 changes: 1 addition & 1 deletion src/templating/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ impl<T: Parser<I, O, E> + Sized, I: Stream + Location, O, E> ParserExt<I, O, E>

#[cfg(test)]
mod test {
use crate::util::{render_error, TestResult};
use crate::util::test_util::{render_error, TestResult};
use insta::assert_debug_snapshot;
use winnow::Parser as _;

Expand Down
2 changes: 1 addition & 1 deletion src/templating/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rstest::{fixture, rstest};

use crate::util::TestResult;
use crate::util::test_util::TestResult;

use crate::script::eval_ctx::EvalCtx;
use crate::templating::document::Document;
Expand Down
150 changes: 85 additions & 65 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,15 @@ pub fn create_symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> mie
let link = link.as_ref();
let original = original.as_ref();
tracing::trace!("Creating symlink at {} -> {}", link.abbr(), original.abbr());
#[cfg(unix)]
fs_err::os::unix::fs::symlink(original, link).into_diagnostic()?;
#[cfg(target_os = "windows")]
{
if original.is_dir() {
fs_err::os::windows::fs::symlink_dir(original, link)
.into_diagnostic()
.wrap_err("Failed to create symlink")?;
} else {
std::os::windows::fs::symlink_file(original, link)
.into_diagnostic()
.wrap_err("Failed to create symlink")?;
}
}
symlink::symlink_auto(original, link)
.into_diagnostic()
.wrap_err_with(|| {
format!(
"Failed to create symlink at {} -> {}",
link.abbr(),
original.abbr()
)
})?;
Ok(())
}

Expand Down Expand Up @@ -77,7 +72,7 @@ impl Path {
return self.to_path_buf();
};
#[cfg(test)]
let home = PathBuf::from(std::env::var("HOME").unwrap());
let home = test_util::get_home_dir();

if let Some(first) = self.components().next() {
if first.as_os_str().to_string_lossy() == "~" {
Expand All @@ -88,24 +83,6 @@ impl Path {
}
}

/// like <https://crates.io/crates/testresult>, but shows the debug output instead of display.
#[cfg(test)]
pub type TestResult<T = ()> = std::result::Result<T, TestError>;

#[cfg(test)]
#[derive(Debug)]
pub enum TestError {}

#[cfg(test)]
impl<T: std::fmt::Debug + std::fmt::Display> From<T> for TestError {
#[track_caller] // Will show the location of the caller in test failure messages
fn from(error: T) -> Self {
// Use alternate format for rich error message for anyhow
// See: https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
panic!("error: {} - {:?}", std::any::type_name::<T>(), error);
}
}

pub fn create_regex(s: impl AsRef<str>) -> miette::Result<Regex> {
cached::cached_key! {
REGEXES: UnboundCache<String, Result<Regex, regex::Error>> = UnboundCache::new();
Expand All @@ -118,38 +95,81 @@ pub fn create_regex(s: impl AsRef<str>) -> miette::Result<Regex> {
}

#[cfg(test)]
pub fn setup_and_init_test_yolk() -> miette::Result<(
assert_fs::TempDir,
crate::yolk::Yolk,
assert_fs::fixture::ChildPath,
)> {
use assert_fs::prelude::PathChild as _;

let home = assert_fs::TempDir::new().into_diagnostic()?;
let paths = crate::yolk_paths::YolkPaths::new(home.join("yolk"), home.to_path_buf());
let yolk = crate::yolk::Yolk::new(paths);
std::env::set_var("HOME", home.to_string_lossy().to_string());
let eggs = home.child("yolk/eggs");
yolk.init_yolk()?;
Ok((home, yolk, eggs))
}
pub mod test_util {
use std::cell::RefCell;
use std::path::PathBuf;
use std::thread_local;

#[cfg(test)]
pub fn render_error(e: impl miette::Diagnostic) -> String {
use miette::GraphicalReportHandler;

let mut out = String::new();
GraphicalReportHandler::new()
.with_theme(miette::GraphicalTheme::unicode_nocolor())
.render_report(&mut out, &e)
.unwrap();
out
}
use miette::IntoDiagnostic as _;

#[cfg(test)]
pub fn miette_no_color() {
miette::set_hook(Box::new(|_| {
Box::new(miette::MietteHandlerOpts::new().color(false).build())
}))
.unwrap();
thread_local! {
static HOME_DIR: RefCell<Option<PathBuf>> = RefCell::new(None);
}

pub fn set_home_dir(path: PathBuf) {
HOME_DIR.with(|home_dir| {
*home_dir.borrow_mut() = Some(path);
});
}

pub fn get_home_dir() -> PathBuf {
HOME_DIR.with_borrow(|x| x.clone()).expect(
"Home directory not set in this test. Use `set_home_dir` to set the home directory.",
)
}

/// like <https://crates.io/crates/testresult>, but shows the debug output instead of display.
pub type TestResult<T = ()> = std::result::Result<T, TestError>;

#[derive(Debug)]
pub enum TestError {}

impl<T: std::fmt::Debug + std::fmt::Display> From<T> for TestError {
#[track_caller] // Will show the location of the caller in test failure messages
fn from(error: T) -> Self {
// Use alternate format for rich error message for anyhow
// See: https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
panic!("error: {} - {:?}", std::any::type_name::<T>(), error);
}
}

pub fn setup_and_init_test_yolk() -> miette::Result<(
assert_fs::TempDir,
crate::yolk::Yolk,
assert_fs::fixture::ChildPath,
)> {
use assert_fs::prelude::PathChild as _;

let home = assert_fs::TempDir::new().into_diagnostic()?;
let paths = crate::yolk_paths::YolkPaths::new(home.join("yolk"), home.to_path_buf());
let yolk = crate::yolk::Yolk::new(paths);
std::env::set_var("HOME", "/tmp/TEST_HOMEDIR_SHOULD_NOT_BE_USED");
set_home_dir(home.to_path_buf());

let eggs = home.child("yolk/eggs");
yolk.init_yolk()?;
Ok((home, yolk, eggs))
}

pub fn render_error(e: impl miette::Diagnostic) -> String {
use miette::GraphicalReportHandler;

let mut out = String::new();
GraphicalReportHandler::new()
.with_theme(miette::GraphicalTheme::unicode_nocolor())
.render_report(&mut out, &e)
.unwrap();
out
}

pub fn render_report(e: miette::Report) -> String {
use miette::GraphicalReportHandler;

let mut out = String::new();
GraphicalReportHandler::new()
.with_theme(miette::GraphicalTheme::unicode_nocolor())
.render_report(&mut out, e.as_ref())
.unwrap();
out
}
}
21 changes: 12 additions & 9 deletions src/yolk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl Yolk {
.is_deployed()
.with_context(|| format!("Failed to check deployment state for egg {}", egg.name()))?;
tracing::debug!(
egg.name = egg.name(),
egg.deployed = deployed,
egg.enabled = egg.config().enabled,
"Syncing egg deployment"
Expand Down Expand Up @@ -420,7 +421,9 @@ fn remove_symlink_recursive(
link_path.abbr(),
actual_path.abbr(),
);
fs_err::remove_file(link_path).into_diagnostic()?;
symlink::remove_symlink_auto(link_path)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to remove symlink at {}", link_path.abbr(),))?;
} else if link_path.is_dir() && actual_path.is_dir() {
for entry in actual_path.fs_err_read_dir().into_diagnostic()? {
let entry = entry.into_diagnostic()?;
Expand Down Expand Up @@ -450,7 +453,10 @@ mod test {

use crate::{
eggs_config::DeploymentStrategy,
util::{create_regex, miette_no_color, setup_and_init_test_yolk, TestResult},
util::{
create_regex,
test_util::{render_error, render_report, setup_and_init_test_yolk, TestResult},
},
yolk::EvalMode,
yolk_paths::Egg,
};
Expand Down Expand Up @@ -556,7 +562,7 @@ mod test {
home.child(".config").assert(is_dir());
home.child(".config/thing.toml").assert(exists().not());
assert!(format!("{:?}", miette::Report::from(result.unwrap_err()))
.contains("failed to symlink file"));
.contains("Failed to create symlink"));
Ok(())
}

Expand Down Expand Up @@ -694,7 +700,6 @@ mod test {

#[test]
fn test_sync_eggs_continues_after_failure() -> TestResult {
miette_no_color();
let (home, yolk, eggs) = setup_and_init_test_yolk()?;
home.child("yolk/yolk.rhai").write_str(indoc::indoc! {r#"
export let eggs = #{
Expand All @@ -708,7 +713,7 @@ mod test {
eggs.child("foo/foo").assert(r#"{< invalid rhai >}"#);
eggs.child("bar/bar")
.assert(r#"#<yolk> foo # {<if false>}"#);
assert!(format!("{:?}", miette::Report::from(result.unwrap_err())).contains("Syntax error"));
assert!(render_error(result.unwrap_err()).contains("Syntax error"));
Ok(())
}

Expand Down Expand Up @@ -763,7 +768,6 @@ mod test {

#[test]
pub fn test_syntax_error_in_yolk_rhai() -> TestResult {
miette_no_color();
let (home, yolk, _) = setup_and_init_test_yolk()?;
home.child("yolk/yolk.rhai").write_str(indoc::indoc! {r#"
fn foo(
Expand All @@ -772,7 +776,7 @@ mod test {
.prepare_eval_ctx_for_templates(crate::yolk::EvalMode::Local)
.map_err(|e| create_regex(r"\[.*.rhai:\d+:\d+]")
.unwrap()
.replace(&format!("{:?}", e), "[no-filename-in-test]")
.replace(&render_report(e), "[no-filename-in-test]")
.to_string())
.unwrap_err());

Expand All @@ -781,7 +785,6 @@ mod test {

#[test]
pub fn test_deployment_error() -> TestResult {
miette_no_color();
let (home, yolk, eggs) = setup_and_init_test_yolk()?;
eggs.child("bar/file1").write_str("")?;
eggs.child("bar/file2").write_str("")?;
Expand All @@ -798,7 +801,7 @@ mod test {
(r"\.tmp[a-zA-Z0-9]{6}", "[tmp-dir]"),
(r"file\d", "[filename]")
]}, {
insta::assert_compact_debug_snapshot!(miette::Report::from(
insta::assert_snapshot!(render_error(
yolk.sync_egg_deployment(&egg).unwrap_err()
));
});
Expand Down
2 changes: 1 addition & 1 deletion src/yolk_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ impl Iterator for TraverseDeployment {
#[cfg(test)]
mod test {
use crate::{
util::{setup_and_init_test_yolk, TestResult},
util::test_util::{setup_and_init_test_yolk, TestResult},
yolk_paths::{Egg, DEFAULT_YOLK_RHAI},
};
use assert_fs::{
Expand Down

0 comments on commit dc7f021

Please sign in to comment.