Documentation
#[cfg(test)]
mod tests {
    use assert_cmd::{cargo, prelude::*}; // Add methods on commands
    use predicates::prelude::*; // Used for writing assertions
    use std::{env, path::Path, process::Command}; // Run programs

    fn get_path() -> std::path::PathBuf {
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
            .join("tests")
            .join("tests_makefile")
    }

    #[test]
    fn test_main_base() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("make");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("ls"))
            .stdout(predicate::str::contains("folder"))
            .stdout(predicate::str::contains("Makefile"));
    }

    #[test]
    fn test_main_multi_arg() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("make");
        cmd.arg("basic");

        let output = ["echo 'basic task'", "basic task"].join("\n") + "\n";
        cmd.assert()
            .success()
            .stdout(predicate::str::contains(output));
    }

    #[test]
    fn test_main_with_env() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.env("SLEEP_TIME", "0.5");
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("--capture-environ");
        cmd.arg("make");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("sleep 0.5"));
    }

    #[test]
    fn test_main_with_env_in_cmd() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("--capture-environ");
        cmd.arg("SLEEP_TIME=0.6");
        cmd.arg("make");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("sleep 0.6"));
    }

    #[test]
    fn test_main_with_make_env() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("make");
        cmd.arg("SLEEP_TIME=0.5");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("sleep 0.5"));
    }

    #[test]
    fn test_main_one_arg() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());

        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("make basic");

        let output = ["echo 'basic task'", "basic task"].join("\n") + "\n";
        cmd.assert()
            .success()
            .stdout(predicate::str::contains(output));
    }

    #[test]
    fn test_main_json_output() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("--json");
        cmd.arg("make basic");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("\": {"));
    }

    #[test]
    fn test_main_not_found() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());

        cmd.arg("-dd");
        cmd.arg("run");
        cmd.arg("my_bin_not_found.sh"); // not found

        cmd.assert().failure().stderr(predicate::str::contains(
            "Binary 'my_bin_not_found.sh' not found in path",
        ));
    }

    #[test]
    fn test_main_non_exec() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.arg("-dd");
        cmd.arg("run");
        cmd.arg("Cargo.toml"); // not executable

        cmd.assert()
            .failure()
            .stderr(predicate::str::contains("is not an executable"));
    }

    #[test]
    fn test_main_not_found_path_option() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.arg("-dd");
        cmd.arg("run");
        cmd.arg("--path");
        cmd.arg("src");
        cmd.arg("--path");
        cmd.arg("tests");
        cmd.arg("Cargo.toml"); // not executable

        cmd.assert()
            .failure()
            .stderr(predicate::str::contains(
                "Binary 'Cargo.toml' not found in path",
            ))
            .stderr(predicate::str::contains("(PATH is 'src:tests')"));
    }

    #[test]
    fn test_main_with_capture_environ() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("--json");
        cmd.arg("--capture-environ");
        cmd.arg("make");
        cmd.arg("basic");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("\"PATH\""))
            .stdout(predicate::str::contains("\"SHELL\""))
            .stdout(predicate::str::contains("\"HOME\""))
            .stdout(predicate::str::contains("\"USER\""));
    }

    #[test]
    fn test_main_with_capture_status() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.current_dir(get_path());
        cmd.arg("run");
        cmd.arg("--json");
        cmd.arg("--capture-status");
        cmd.arg("make");
        cmd.arg("basic");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Pid:"))
            .stdout(predicate::str::contains("PPid:"));
    }

    #[test]
    fn test_main_with_cwd() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("--cwd");
        cmd.arg(get_path());
        cmd.arg("make");
        cmd.arg("basic");

        let output = ["echo 'basic task'", "basic task"].join("\n") + "\n";
        cmd.assert()
            .success()
            .stdout(predicate::str::contains(output));
    }

    #[test]
    fn test_main_with_pwd() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.arg("run");
        cmd.arg("-dd");
        cmd.arg("--pwd");
        cmd.arg(get_path().join("folder"));
        cmd.arg("make");
        cmd.arg("all");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("I am a Makefile in a folder"));
    }

    #[test]
    fn test_main_with_relative_pid() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        cmd.arg("run");
        cmd.arg("--json");
        cmd.arg("--relative-pid");
        cmd.arg("make");
        cmd.arg("a");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("\"1\": {"));
    }

    #[test]
    fn test_main_wrap_shell_1() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run -vv --wrap-shell echo 1 '&&' echo 2
        cmd.arg("run");
        cmd.arg("-vv");
        cmd.arg("--wrap-shell");
        cmd.arg("echo");
        cmd.arg("1");
        cmd.arg("&&");
        cmd.arg("echo");
        cmd.arg("2");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("1\n2"));
    }

    #[test]
    fn test_main_wrap_shell_2() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run -vv --wrap-shell 'echo 1 && echo 2'
        cmd.arg("run");
        cmd.arg("-vv");
        cmd.arg("--wrap-shell");
        cmd.arg("echo 1 && echo 2");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains("1\n2"));
    }

    #[test]
    fn test_main_multiple_exec() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run -vv path/to/recursive_exec.sh a
        let current_file = Path::new(file!());
        let dir = current_file.parent().unwrap();
        let test_script = dir.join("recursive_exec.sh");
        cmd.arg("run");
        cmd.arg("-o=-");
        cmd.arg(test_script);
        cmd.arg("a");

        cmd.assert()
            .success()
            .stdout(predicate::str::contains(
                "\"after_exec\": [
        [",
            ))
            .stdout(predicate::str::contains(
                "recursive_exec.sh\",
          \"aa\"\n",
            ))
            .stdout(predicate::str::contains(
                "recursive_exec.sh\",
          \"aaa\"\n",
            ));
    }

    #[test]
    fn test_exclude_args() {
        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run -o=- --write-on-insert make
        cmd.arg("run");
        cmd.arg("-o=-");
        cmd.arg("--write-on-insert");
        cmd.arg("make");

        cmd.assert().failure().stderr(predicate::str::contains(
            "Cannot have both --write-on-insert and --output-file=-",
        ));

        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run --output=- --write-on-insert make
        cmd.arg("run");
        cmd.arg("--output=-");
        cmd.arg("--write-on-insert");
        cmd.arg("make");

        cmd.assert().failure().stderr(predicate::str::contains(
            "Cannot have both --write-on-insert and --output-file=-",
        ));

        let mut cmd = Command::new(cargo::cargo_bin!("meka"));
        // meka run --output - --write-on-insert make
        cmd.arg("run");
        cmd.arg("--output");
        cmd.arg("-");
        cmd.arg("--write-on-insert");
        cmd.arg("make");

        cmd.assert().failure().stderr(predicate::str::contains(
            "Cannot have both --write-on-insert and --output-file=-",
        ));
    }
}