Skip to main content

hblank_cli/
test.rs

1use std::{
2    path::{Path, PathBuf},
3    process::{Command, ExitStatus, Stdio},
4};
5
6use thiserror::Error;
7
8use crate::{Config, ConfigError, GenerationError, refresh_generated_fixtures};
9
10#[derive(Clone, Debug)]
11pub struct TestOptions {
12    pub project_root: PathBuf,
13    pub filter: Option<String>,
14}
15
16impl TestOptions {
17    #[must_use]
18    pub fn new(project_root: impl Into<PathBuf>) -> Self {
19        Self {
20            project_root: project_root.into(),
21            filter: None,
22        }
23    }
24
25    #[must_use]
26    pub fn with_filter(mut self, filter: impl Into<String>) -> Self {
27        self.filter = Some(filter.into());
28        self
29    }
30}
31
32/// Regenerates the private preview target and runs its explicit inline Rust tests.
33///
34/// # Errors
35/// Returns an error when configuration, generation, process startup, or Cargo tests fail.
36pub fn run_tests(options: &TestOptions) -> Result<(), TestError> {
37    let project_root = canonical_project_root(&options.project_root)?;
38    let config = Config::load(&project_root)?;
39    let generated = refresh_generated_fixtures(&project_root, &config)?;
40    println!(
41        "Testing {} Hblank fixture files with Cargo",
42        generated.fixture_files.len()
43    );
44
45    let manifest = project_root.join(".hblank/Cargo.toml");
46    let target = project_root.join(".hblank/target");
47    let mut command = Command::new("cargo");
48    command
49        .arg("test")
50        .arg("--manifest-path")
51        .arg(&manifest)
52        .arg("--target-dir")
53        .arg(&target)
54        .stdin(Stdio::null());
55    if let Some(filter) = &options.filter {
56        command.arg(filter);
57    }
58    let status = command.status().map_err(TestError::Process)?;
59    if status.success() {
60        Ok(())
61    } else {
62        Err(TestError::TestsFailed(status))
63    }
64}
65
66fn canonical_project_root(path: &Path) -> Result<PathBuf, TestError> {
67    path.canonicalize()
68        .map_err(|source| TestError::ProjectRoot {
69            path: path.to_path_buf(),
70            source,
71        })
72}
73
74#[derive(Debug, Error)]
75pub enum TestError {
76    #[error("could not resolve Hblank project root {path}: {source}")]
77    ProjectRoot {
78        path: PathBuf,
79        source: std::io::Error,
80    },
81    #[error(transparent)]
82    Config(#[from] ConfigError),
83    #[error(transparent)]
84    Generation(#[from] GenerationError),
85    #[error("could not run Hblank tests: {0}")]
86    Process(std::io::Error),
87    #[error("Hblank tests exited unsuccessfully: {0}")]
88    TestsFailed(ExitStatus),
89}