bach icon indicating copy to clipboard operation
bach copied to clipboard

`env: 'bash': No such file or directory`

Open rogard opened this issue 9 months ago • 2 comments

Here's a commit that contains the code for the following output. For reproducibility, include the current bach.sh (3db30af).

 $ ./er317_clone_test.sh
 1..1
 not ok 1 - clone
 
 env: 'bash': No such file or directory
 '  EXIT
 export  unique_dir=/tmp/tmp.iR7GSRCV41/unique
 export  trash_dir=/tmp/tmp.iR7GSRCV41/trash
 Assert Failed:
      Expected: 0
       But got: 1
 
 # -----
 # All tests: 1, failed: 1, skipped: 0

Sharing this in case it helps improve the robustness of the framework.

rogard avatar Jul 21 '25 19:07 rogard

Hi @rogard

Thank you for your suggestion!

I think puttting source bach.sh in every test case file is OK. It's very common in other programming languages, like in Java, every test file has import org.junit.* at the begining. So, source bach.sh is like that.

I agree that having a test runner, like bach.sh your-script.test.sh can be useful. But for now, we can run the test by bash your-script.test.sh. I might implement this feature in the future.

chaifeng avatar Jul 23 '25 03:07 chaifeng

I write some test cases for your script, and I want to explain a few things.

  1. About checking if files/directories exist In your script, you use [[ -f path/to/file ]] to check if a file exists. In Bach, it's better to use [ -f path/to/file ] or test -f path/to/file. The reason is that [[ ... ]] is a shell keyword and cannot be mocked. But [ ... ] and test are commands can be mocked in tests.
  2. About simulation user input In your script, you use read command to get user input. In the test, we can use @stdout y y to simulate user inputs.
  3. About env vars and command mocking The script uses the env var HOME, we must set it manually. There are also three sed comands that need to run for real. We can allow them with @allow-real API. I also mocked all the necessary commands that are called in the script in the @setup-test. This helps making write tests easier. In each test case, we only need to change the mock for a command if we want a different result.

Patch:

--- a/er317_clone.sh    2025-07-23 11:40:35.648572148 +0800
+++ b/er317_clone.sh    2025-07-23 11:06:36.608360435 +0800
@@ -120,10 +120,10 @@
 fi

-[[ -f "${source_file}" ]] || {
+test -f "${source_file}" || {
     msg_fun "$?" "$source_file is not a file"
     exit 1
 }

-if [[ ! -d "$unique_dir" ]]; then
+if test ! -d "$unique_dir"; then
     msg='Create unique directory %s? [y/n] '
     read -p "$(printf "$msg" "$unique_dir")" answer
@@ -144,5 +144,5 @@
 fi

-if [[ ! -f "$unique_log" ]]; then
+if test ! -f "$unique_log"; then
     msg='Create unique log %s? [y/n] '
     read -p "$(printf "$msg" "$unique_log")" answer

Test er317_clone.test.sh:

#!/usr/bin/env bash
script_dir=$(realpath $(dirname "${BASH_SOURCE[0]}"))
source "$script_dir"/bach.sh

@setup-test {
    HOME=/home/bach
    @allow-real sed 's/^fallback_msg_fun/msg_fun/'
    @allow-real sed 's/^fallback_recurse_fun/recurse_fun/'
    @allow-real sed 's/^fallback_unique_fun/unique_fun/'

    @allow-real basename trash.me

    @mocktrue test -f trash.me

    @mocktrue  test ! -d /home/bach/unique
    @mocktrue mkdir  -p /home/bach/unique

    @mocktrue test ! -f /home/bach/.local/share/unique.log
    @mocktrue touch /home/bach/.local/share/unique.log

    @mock mktemp -u === @stdout /tmp/tmp.aabbcc
    @mock exec er317_clone.sh --dry-run=false --extension=me trash.me

    @mock echo "/home/bach/unique/aabbcc.me"
}

test-clone() {
    @stdout y y | @run er317_clone.sh trash.me
}
test-clone-assert() {
    cp trash.me /home/bach/unique/aabbcc.me
    mv trash.me /home/bach/.local/share/Trash/files/trash.me

    @dryrun echo "/home/bach/unique/aabbcc.me"
}


test-clone-if-cannot-create-unique-dir() {
    @mockfalse mkdir  -p /home/bach/unique

    @stdout y | @run er317_clone.sh trash.me
}
test-clone-if-cannot-create-unique-dir-assert() {
    @false
}

test-clone-if-cannot-create-unique-log-file() {
    @mockfalse touch /home/bach/.local/share/unique.log

    @stdout y y | @run er317_clone.sh trash.me
}
test-clone-if-cannot-create-unique-log-file-assert() {
    @false
}


test-clone-if-do-not-create-unique-log-file() {
    @mockfalse touch /home/bach/.local/share/unique.log

    @stdout y N | @run er317_clone.sh trash.me
}
test-clone-if-do-not-create-unique-log-file-assert() {
    @false
}

chaifeng avatar Jul 23 '25 03:07 chaifeng