From f6dfd9a4d91b985de190d15f39c7e4d1a62c8dbe Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Thu, 2 Jan 2025 17:02:46 +0100 Subject: [PATCH] refactor!(test): use builder pattern --- rocks-bin/src/test.rs | 24 +++------ rocks-lib/src/operations/test.rs | 85 ++++++++++++++++++++++++++++++-- rocks-lib/tests/test.rs | 16 +----- 3 files changed, 89 insertions(+), 36 deletions(-) diff --git a/rocks-bin/src/test.rs b/rocks-bin/src/test.rs index fcd6459..2792fc4 100644 --- a/rocks-bin/src/test.rs +++ b/rocks-bin/src/test.rs @@ -2,10 +2,8 @@ use clap::Args; use eyre::{OptionExt, Result}; use rocks_lib::{ config::Config, - operations::{ensure_busted, ensure_dependencies, run_tests, TestEnv}, - progress::MultiProgress, + operations::{self, TestEnv}, project::Project, - tree::Tree, }; #[derive(Args)] @@ -20,26 +18,16 @@ pub struct Test { pub async fn test(test: Test, config: Config) -> Result<()> { let project = Project::current()? .ok_or_eyre("'rocks test' must be run in a project root, with a 'project.rockspec'")?; - let rockspec = project.rockspec(); - let lua_version = match rockspec.lua_version_from_config(&config) { - Ok(lua_version) => Ok(lua_version), - Err(_) => rockspec.test_lua_version().ok_or_eyre("lua version not set! Please provide a version through `--lua-version ` or add it to your rockspec's dependencies"), - }?; - let test_config = config.with_lua_version(lua_version); - let tree = Tree::new( - test_config.tree().clone(), - test_config.lua_version().unwrap().clone(), - )?; - let progress = MultiProgress::new_arc(); - // TODO(#204): Only ensure busted if running with busted (e.g. a .busted directory exists) - ensure_busted(&tree, &test_config, progress.clone()).await?; - ensure_dependencies(rockspec, &tree, &test_config, progress).await?; let test_args = test.test_args.unwrap_or_default(); let test_env = if test.impure { TestEnv::Impure } else { TestEnv::Pure }; - run_tests(project, test_args, test_env, test_config).await?; + operations::Test::new(project, &config) + .args(test_args) + .env(test_env) + .run() + .await?; Ok(()) } diff --git a/rocks-lib/src/operations/test.rs b/rocks-lib/src/operations/test.rs index 831198e..2255ce4 100644 --- a/rocks-lib/src/operations/test.rs +++ b/rocks-lib/src/operations/test.rs @@ -15,13 +15,86 @@ use thiserror::Error; use super::{Install, InstallError}; +pub struct Test<'a> { + project: Project, + config: &'a Config, + args: Vec, + env: TestEnv, + progress: Option>>, +} + +/// A rocks project test runner, providing fine-grained control +/// over how tests should be run. +impl<'a> Test<'a> { + /// Construct a new test runner. + pub fn new(project: Project, config: &'a Config) -> Self { + Self { + project, + config, + args: Vec::new(), + env: TestEnv::default(), + progress: None, + } + } + + /// Pass arguments to the test executable. + pub fn args(self, args: I) -> Self + where + I: IntoIterator + Send, + { + Self { + args: self.args.into_iter().chain(args).collect_vec(), + ..self + } + } + + /// Pass an argument to the test executable. + pub fn arg(self, arg: String) -> Self { + self.args(std::iter::once(arg)) + } + + /// Define the environment in which to run the tests. + pub fn env(self, env: TestEnv) -> Self { + Self { env, ..self } + } + + /// Pass a `MultiProgress` to this runner. + /// By default, a new one will be created. + pub fn progress(self, progress: Arc>) -> Self { + Self { + progress: Some(progress), + ..self + } + } + + /// Run the test suite + pub async fn run(self) -> Result<(), RunTestsError> { + let progress = match self.progress { + Some(p) => p, + None => MultiProgress::new_arc(), + }; + run_tests(self.project, self.args, self.env, self.config, progress).await + } +} + pub enum TestEnv { + /// An environment that is isolated from `HOME` and `XDG` base directories (default). Pure, + /// An impure environment in which `HOME` and `XDG` base directories can influence + /// the test results. Impure, } +impl Default for TestEnv { + fn default() -> Self { + Self::Pure + } +} + #[derive(Error, Debug)] pub enum RunTestsError { + #[error(transparent)] + InstallTestDependencies(#[from] InstallTestDependenciesError), #[error("tests failed!")] TestFailure, #[error("failed to execute `{0}`: {1}")] @@ -32,23 +105,27 @@ pub enum RunTestsError { Io(#[from] io::Error), } -pub async fn run_tests( +async fn run_tests( project: Project, test_args: I, env: TestEnv, - config: Config, + config: &Config, + progress: Arc>, ) -> Result<(), RunTestsError> where I: IntoIterator + Send, { let rockspec = project.rockspec(); - let lua_version = match rockspec.lua_version_from_config(&config) { + let lua_version = match rockspec.lua_version_from_config(config) { Ok(lua_version) => Ok(lua_version), Err(_) => rockspec .test_lua_version() .ok_or(RunTestsError::LuaVersionUnset), }?; let tree = Tree::new(config.tree().clone(), lua_version)?; + // TODO(#204): Only ensure busted if running with busted (e.g. a .busted directory exists) + ensure_busted(&tree, config, progress.clone()).await?; + ensure_dependencies(rockspec, &tree, config, progress).await?; let tree_root = &tree.root().clone(); let paths = Paths::from_tree(tree)?; let mut command = Command::new("busted"); @@ -117,7 +194,7 @@ pub async fn ensure_busted( /// Ensure dependencies and test dependencies are installed /// This defaults to the local project tree if cwd is a project root. -pub async fn ensure_dependencies( +async fn ensure_dependencies( rockspec: &Rockspec, tree: &Tree, config: &Config, diff --git a/rocks-lib/tests/test.rs b/rocks-lib/tests/test.rs index 02222b1..fe45df1 100644 --- a/rocks-lib/tests/test.rs +++ b/rocks-lib/tests/test.rs @@ -1,12 +1,6 @@ use std::path::PathBuf; -use rocks_lib::{ - config::ConfigBuilder, - operations::{ensure_busted, run_tests, TestEnv}, - progress::MultiProgress, - project::Project, - tree::Tree, -}; +use rocks_lib::{config::ConfigBuilder, operations::Test, project::Project}; #[tokio::test] async fn run_busted_test() { @@ -16,11 +10,5 @@ async fn run_busted_test() { let tree_root = project.root().to_path_buf().join(".rocks"); let _ = std::fs::remove_dir_all(&tree_root); let config = ConfigBuilder::new().tree(Some(tree_root)).build().unwrap(); - let tree = Tree::new(config.tree().clone(), config.lua_version().unwrap().clone()).unwrap(); - ensure_busted(&tree, &config, MultiProgress::new_arc()) - .await - .unwrap(); - run_tests(project, Vec::new(), TestEnv::Pure, config) - .await - .unwrap() + Test::new(project, &config).run().await.unwrap(); }