argc 1.24.0

A bash cli framework, also a bash-based command runner
Documentation
use assert_cmd::prelude::*;
use assert_fs::fixture::PathChild;
use std::{process::Command, time::Instant};

use crate::fixtures::{
    get_path_env_var, locate_script, tmpdir, tmpdir_argcfiles, tmpdir_path, SCRIPT_PATHS,
};

#[test]
fn version() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-version")
        .assert()
        .stdout(predicates::str::contains(format!(
            "argc {}",
            env!("CARGO_PKG_VERSION")
        )))
        .success();
}

#[test]
fn help() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-help")
        .assert()
        .stdout(predicates::str::contains(env!("CARGO_PKG_DESCRIPTION")))
        .success();
}

#[test]
fn create() {
    let tmpdir = tmpdir();
    let path_env_var = get_path_env_var();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir.path())
        .env("PATH", path_env_var.clone())
        .arg("--argc-create")
        .assert()
        .success();
    assert!(tmpdir.path().join("Argcfile.sh").exists());
    Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir.path())
        .env("PATH", path_env_var)
        .assert()
        .success();
}

#[test]
fn create_with_tasks() {
    let tmpdir = tmpdir();
    let path_env_var = get_path_env_var();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir.path())
        .env("PATH", path_env_var.clone())
        .arg("--argc-create")
        .args(["foo", "bar"])
        .assert()
        .success();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir.path())
        .env("PATH", path_env_var)
        .arg("bar")
        .assert()
        .stdout(predicates::str::contains("TODO bar"))
        .success();
}

#[test]
fn run() {
    let path_env_var = get_path_env_var();
    let path = locate_script("examples/demo.sh");
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-run")
        .arg(path)
        .env("PATH", path_env_var)
        .assert()
        .stderr(predicates::str::contains("USAGE: demo"))
        .success();
}

#[test]
fn build_stdout() {
    let path = locate_script("examples/demo.sh");
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-build")
        .arg(path)
        .assert()
        .stdout(predicates::str::contains("# ARGC-BUILD"))
        .success();
}

#[test]
fn run_build() {
    let path = locate_script("examples/strict.sh");
    let tmpdir = tmpdir();
    let outpath = tmpdir.join("strict.sh");
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-build")
        .arg(&path)
        .arg(&outpath)
        .assert()
        .success();

    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-run")
        .arg(&outpath)
        .args([
            "--fa",
            "--oa",
            "oa1",
            "--of=of1,of2",
            "--oca=a",
            "--ofa",
            "abc",
        ])
        .assert()
        .stdout(predicates::str::contains("argc__fn=main"))
        .success();
}

#[test]
fn mangen() {
    let path = locate_script("examples/demo.sh");
    let tmpdir = tmpdir();
    let outdir = tmpdir.to_path_buf();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-mangen")
        .arg(&path)
        .arg(&outdir)
        .assert()
        .success();
    let manpath = outdir.join("demo.1");
    let script = std::fs::read_to_string(manpath).unwrap();
    assert!(script.contains(".TH DEMO 1"));
}

#[test]
fn completions() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .args(["--argc-completions", "bash", "mycmd1", "mycmd2"])
        .assert()
        .stdout(predicates::str::contains(r#"argc mycmd1 mycmd2"#))
        .success();
}

#[test]
fn compgen_args() {
    let path = locate_script("examples/args.sh");
    let path_env_var = get_path_env_var();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-compgen")
        .arg("fish")
        .arg(path)
        .args(["args", "cmd_arg_with_choice_fn", ""])
        .env("PATH", path_env_var)
        .assert()
        .stdout(predicates::str::contains("abc\ndef\nghi"))
        .success();
}

#[test]
fn compgen_options() {
    let path = locate_script("examples/options.sh");
    let path_env_var = get_path_env_var();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-compgen")
        .arg("fish")
        .arg(path)
        .args(["args", "test1", "--cc", ""])
        .env("PATH", path_env_var)
        .assert()
        .stdout(predicates::str::contains("abc\ndef\nghi"))
        .success();
}

#[test]
fn compgen_argc() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .args(["--argc-compgen", "fish", "", "argc", "--argc-compgen", ""])
        .assert()
        .stdout(predicates::str::contains("zsh"))
        .success();
}

#[test]
fn compgen_kind() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .args([
            "--argc-compgen",
            "fish",
            argc::COMPGEN_KIND_SYMBOL,
            "shell",
            "",
        ])
        .assert()
        .stdout(predicates::str::contains("zsh"))
        .success();
}

#[test]
fn export() {
    let path = locate_script("examples/options.sh");
    let output = Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-export")
        .arg(path)
        .output()
        .unwrap();
    let stdout = std::str::from_utf8(&output.stdout).unwrap();
    insta::assert_snapshot!(stdout);
}

#[test]
fn parallel() {
    let tmpdir = tmpdir_argcfiles();
    let path_env_var = get_path_env_var();
    let args = ["task2", "--oa", "3"];
    let start_time = Instant::now();
    let output = Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir_path(&tmpdir, "dir1"))
        .env("PATH", path_env_var)
        .args(args)
        .output()
        .unwrap();

    let end_time = Instant::now();
    let elapsed_time = end_time.duration_since(start_time);

    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);
    let text = format!(
        r###"ARGS: {args:?}

STDOUT:
{stdout}

STDERR:
{stderr}

"###
    );
    assert!(elapsed_time.as_millis() < 3999);
    insta::assert_snapshot!(text);
}

#[test]
fn script_path() {
    let tmpdir = tmpdir_argcfiles();
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-script-path")
        .current_dir(tmpdir.child("dir1").path())
        .assert()
        .stdout(predicates::str::contains(
            tmpdir_path(&tmpdir, "dir1/Argcfile.sh")
                .display()
                .to_string(),
        ))
        .success();
}

#[test]
fn shell_path() {
    Command::new(assert_cmd::cargo::cargo_bin!())
        .arg("--argc-shell-path")
        .assert()
        .stdout(predicates::str::contains("bash"))
        .success();
}

#[test]
fn run_argcfile() {
    let tmpdir = tmpdir_argcfiles();
    let path_env_var = get_path_env_var();
    for path in SCRIPT_PATHS {
        if path.ends_with("EMPTY") {
            continue;
        }
        Command::new(assert_cmd::cargo::cargo_bin!())
            .current_dir(tmpdir_path(&tmpdir, path).path().parent().unwrap())
            .env("PATH", path_env_var.clone())
            .assert()
            .stdout(predicates::str::contains(path))
            .success();
    }

    Command::new(assert_cmd::cargo::cargo_bin!())
        .current_dir(tmpdir_path(&tmpdir, "dir1/subdir1/subdirdir1"))
        .env("PATH", path_env_var)
        .assert()
        .stdout(predicates::str::contains("dir1/subdir1/Argcfile.sh"))
        .success();
}