use garden::cmd;
use garden::display;
use garden::errors;
use garden::model;
use garden::string;
use anyhow::Result;
use escargot::CURRENT_TARGET;
fn initialize_environment() {
std::env::set_var("HOME", "/home/test");
std::env::set_var("PATH", "/usr/bin:/bin");
std::env::set_var("EMPTY", "");
std::env::remove_var("PYTHONPATH");
}
pub fn garden_context_from_string(
string: &str,
) -> Result<garden::model::ApplicationContext, errors::GardenError> {
initialize_environment();
model::ApplicationContext::from_string(string)
}
pub fn garden_context() -> Result<garden::model::ApplicationContext, errors::GardenError> {
let string = string!(
r#"
garden:
root: ${root}
variables:
echo_cmd: echo cmd
echo_cmd_exec: $ ${echo_cmd}
test: TEST
local: ${test}/local
src: src
root: ~/${src}
environment:
EXAMPLE_VALUE=: ${GARDEN_ROOT}
PATH: /home/test/bin
templates:
makefile:
variables:
prefix: ${TREE_PATH}/local
commands:
build: make -j prefix=${prefix} all
install: make -j prefix=${prefix} install
test: make test
python:
environment:
PYTHONPATH: ${TREE_PATH}
local:
url: ${local}/${TREE_NAME}
trees:
git:
url: https://github.com/git/git
templates: makefile
variables:
prefix: ~/.local
gitconfig:
user.name: A U Thor
user.email: author@example.com
cola:
url: https://github.com/git-cola/git-cola
path: git-cola
templates: [makefile, python]
variables:
prefix: ${TREE_PATH}/local
environment:
PATH:
- ${prefix}/bin
- ${TREE_PATH}/bin
PYTHONPATH: ${GARDEN_ROOT}/python/send2trash
commands:
test:
- git status --short
- make tox
remotes:
davvid: git@github.com:davvid/git-cola.git
python/qtpy:
url: https://github.com/spider-ide/qtpy.git
templates: python
tmp:
environment:
EMPTY: [a, b]
${TREE_NAME}_VALUE=: ${TREE_PATH}
path: /tmp
templates: local
annex/data:
url: git@example.com:git-annex/data.git
gitconfig:
remote.origin.annex-ignore: true
remotes:
local: ${GARDEN_ROOT}/annex/local
annex/local:
extend: annex/data
oneline: git@example.com:example/oneline.git
groups:
cola: [git, cola, python/qtpy]
test: [a, b, c]
reverse: [cola, git]
annex: annex/*
annex-1: annex/data
annex-2: annex/local
gardens:
cola:
groups: cola
variables:
prefix: ~/apps/git-cola/current
environment:
GIT_COLA_TRACE=: full
PATH+: ${prefix}/bin
commands:
summary:
- git branch
- git status --short
git:
groups: cola
trees: gitk
gitconfig:
user.name: A U Thor
user.email: author@example.com
annex/group:
groups: annex
annex/wildcard-groups:
groups: annex-*
annex/wildcard-trees:
trees: annex/*
"#
);
garden_context_from_string(&string)
}
pub fn exec_garden(args: &[&str]) -> Result<()> {
let mut argv: Vec<&str> = vec!["garden"];
argv.extend(args);
display::print_command_vec(&argv);
let mut exec = cargo_bin_cmd("garden").expect("garden command");
exec.args(args);
assert!(exec.status().expect("garden returned an error").success());
Ok(())
}
pub fn garden_capture(args: &[&str]) -> String {
let mut argv: Vec<&str> = vec!["garden"];
argv.extend(args);
display::print_command_vec(&argv);
let mut exec = cargo_bin_cmd("garden").expect("garden command");
exec.args(args);
let capture = exec.output();
assert!(capture.is_ok());
let utf8_result = String::from_utf8(capture.unwrap().stdout);
assert!(utf8_result.is_ok());
utf8_result.unwrap().trim_end().into()
}
pub fn garden_exec(args: &[&str]) -> (i32, String, String) {
let mut argv: Vec<&str> = vec!["garden"];
argv.extend(args);
display::print_command_vec(&argv);
let mut exec = cargo_bin_cmd("garden").expect("garden command");
exec.args(args);
let output_result = exec.output();
assert!(output_result.is_ok());
let status = exec.status().unwrap().code().unwrap_or(0);
let output = output_result.unwrap();
let utf8_stdout = String::from_utf8(output.stdout);
let utf8_stderr = String::from_utf8(output.stderr);
assert!(utf8_stdout.is_ok());
assert!(utf8_stderr.is_ok());
(
status,
utf8_stdout.unwrap().trim_end().into(),
utf8_stderr.unwrap().trim_end().into(),
)
}
pub fn assert_cmd_status(cmd: &[&str], directory: &str, status: i32) {
display::print_command_vec(cmd);
let exec = cmd::exec_in_dir(cmd, directory);
let cmd_status = cmd::status(exec);
assert_eq!(cmd_status, status);
}
pub fn assert_cmd(cmd: &[&str], directory: &str) {
assert_cmd_status(cmd, directory, errors::EX_OK);
}
pub fn assert_cmd_capture(cmd: &[&str], directory: &str) -> String {
display::print_command_vec(cmd);
let exec = cmd::exec_in_dir(cmd, directory);
let capture = cmd::stdout_to_string(exec);
assert!(capture.is_ok());
capture.unwrap()
}
pub fn assert_path(path: &str) {
let pathbuf = std::path::PathBuf::from(path);
assert!(pathbuf.exists());
}
pub fn assert_git_worktree(path: &str) {
assert_path(&format!("{path}/.git"));
}
pub fn assert_ref(repository: &str, refname: &str) {
let cmd = ["git", "rev-parse", "--quiet", "--verify", refname];
assert_cmd(&cmd, repository);
}
pub fn assert_ref_missing(repository: &str, refname: &str) {
let cmd = ["git", "rev-parse", "--quiet", "--verify", refname];
assert_cmd_status(&cmd, repository, 1);
}
fn setup_tmp_bare_repo(name: &str, path: &str) {
let cmd = ["../integration/setup.sh", name];
assert_cmd(&cmd, path);
}
fn teardown_tmp_test_data(path: &str) {
if let Err(err) = std::fs::remove_dir_all(path) {
panic!("unable to remove '{path}': {err}");
}
}
pub struct BareRepoFixture<'a> {
name: &'a str,
}
impl<'a> BareRepoFixture<'a> {
pub fn new(name: &'a str) -> Self {
setup_tmp_bare_repo(name, "tests/tmp");
Self { name }
}
pub fn root(&self) -> String {
format!("tests/tmp/{}", self.name)
}
pub fn root_pathbuf(&self) -> std::path::PathBuf {
std::path::PathBuf::from(self.root())
}
pub fn root_worktree_pathbuf(&self) -> std::path::PathBuf {
let worktree = self.root();
self.assert_worktree(&worktree);
worktree.into()
}
pub fn assert_worktree(&self, path: &str) {
assert_git_worktree(path);
}
pub fn path(&self, path: &str) -> String {
let fixture_path = format!("{}/{}", self.root(), path);
assert_path(&fixture_path);
fixture_path
}
pub fn pathbuf(&self, path: &str) -> std::path::PathBuf {
std::path::PathBuf::from(self.path(path))
}
pub fn worktree(&self, path: &str) -> String {
let worktree = self.path(path);
assert_git_worktree(&worktree);
worktree
}
pub fn worktree_pathbuf(&self, path: &str) -> std::path::PathBuf {
std::path::PathBuf::from(self.worktree(path))
}
}
impl Drop for BareRepoFixture<'_> {
fn drop(&mut self) {
teardown_tmp_test_data(&self.root());
}
}
fn cargo_bin_cmd<S: AsRef<str>>(name: S) -> Result<std::process::Command, String> {
let path = cargo_bin(name);
if path.is_file() {
if let Some(runner) = cargo_runner() {
let mut cmd = std::process::Command::new(&runner[0]);
cmd.args(&runner[1..]).arg(path);
Ok(cmd)
} else {
Ok(std::process::Command::new(path))
}
} else {
Err(format!("error: file not found: {path:?}"))
}
}
fn cargo_runner() -> Option<Vec<String>> {
let runner_env = format!(
"CARGO_TARGET_{}_RUNNER",
CURRENT_TARGET.replace('-', "_").to_uppercase()
);
let runner = std::env::var(runner_env).ok()?;
Some(runner.split(' ').map(str::to_string).collect())
}
fn cargo_bin<S: AsRef<str>>(name: S) -> std::path::PathBuf {
cargo_bin_str(name.as_ref())
}
fn cargo_bin_str(name: &str) -> std::path::PathBuf {
let env_var = format!("CARGO_BIN_EXE_{name}");
std::env::var_os(env_var)
.map(|p| p.into())
.unwrap_or_else(|| target_dir().join(format!("{}{}", name, std::env::consts::EXE_SUFFIX)))
}
fn target_dir() -> std::path::PathBuf {
std::env::current_exe()
.ok()
.map(|mut path| {
path.pop();
if path.ends_with("deps") {
path.pop();
}
path
})
.expect("this should only be used where a `current_exe` can be set")
}