infertux / bashcov

Code coverage tool for Bash
MIT License
150 stars 19 forks source link

POSIX sh support? #24

Open ghost opened 7 years ago

ghost commented 7 years ago

Hi, I'm looking for a tool to test a rather large sh script (it's around a 1000 lines atm). I have one main script for which I want to generate coverage (preferably also for the large command strings that are passed to xargs to get around read not supporting \0-separation in most implementations), but I also have a collection of test scripts also written in sh.

Does bashcov have support for pure sh scripts? Would you recommend I use bashcov for this?

infertux commented 7 years ago

Great question! Bashcov relies on the xtrace ability and the $LINENO special variable to get the current line number. I just tried that on a Debian machine (which uses Dash as the POSIX shell):

$ cat test.sh
#!/bin/sh -x

echo $LINENO

$ ./test.sh
+ echo

So -x (i.e. xtrace) is supported but not $LINENO. It seems it's disabled at compilation time.

Are you using a *BSD variant and does it support $LINENO by default?

infertux commented 7 years ago

https://lists.ubuntu.com/archives/ubuntu-users/2008-July/153242.html

infertux commented 7 years ago

As expected, it fails badly without $LINENO support: https://travis-ci.org/infertux/bashcov/jobs/191911884

It'd be interesting to compile Dash with $LINENO support or try with KornShell...

tomeon commented 7 years ago

@infertux -- dash 0.5.9.1 on Arch Linux comes with support for $LINENO, and I was able to get bashcov working with a few changes:

Happy to take a crack at updating bashcov to support shells that recognize $LINENO . Maybe we could check during the initialization proces whether the program provided as --bash-path expands $LINENO to an integer?

infertux commented 7 years ago

That's great news. Thanks a lot @BaxterStockman.

$0 behaves slightly differently than $BASH_SOURCE so we'll need to make sure it doesn't introduce regressions.

I see sh is just a symlink to bash on Arch:

% ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec 10 18:55 /bin/sh -> bash

I wonder if Bash runs in "pseudo" POSIX-compliant mode when called that way. The man page says you can pass --posix to Bash.

I should have some time to work on this next week but please feel free to take a crack at it! That would be most welcomed :)

tomeon commented 7 years ago

So it looks like it will be difficult to support POSIX shells in all but the most basic scenario of generating coverage stats for a single script. As far as I have been able to determine, dash, ksh, and mksh provide no means of globally enabling execution tracing a la SHELLOPTS=xtrace -- the only way to turn on tracing is with set -x, which isn't inherited by scripts invoked from the "parent" script.

One possiblity is to provide "shim" scripts -- something like <bashcov root>/exe/{sh,dash,ksh,mksh} -- that simply exec the correct shell with the -x option, passing through the contents of ARGV. By prepending the exe dir to PATH, scripts with shebangs like #!/usr/bin/env sh will use the shim scripts and therefore have execution tracing enabled. Of course, this approach won't work for scripts that use shebangs like #!/bin/sh.

infertux commented 7 years ago

which isn't inherited by scripts invoked from the "parent" script

Same result for me. Bummer.

One possiblity is to provide "shim" scripts [...] Of course, this approach won't work for scripts that use shebangs like #!/bin/sh.

Great idea but yeah I'm afraid most sh scripts out there use #!/bin/sh and not #!/usr/bin/env sh. That being said, they should be able to switch to #!/usr/bin/env sh without breaking anything in theory if they really want to use Bashcov. It looks like it's the only reliable option we have anyway. Would this be helpful in your case @WillemMali?

PS: Thanks again for your help @BaxterStockman :)

infertux commented 6 years ago

Ping @WillemMali

ghost commented 6 years ago

Sorry for not responding, I guess I missed the email.

Yes, that would be helpful. I could then wrap bashcov to run on a copy of my script with the #! modified, if I were really stubborn about wanting to use #!/bin/sh :^)

bashcov() {
    $tmp="$(mktemp)"
    { echo "#!/usr/bin/env sh"; tail -n+2 "$0"; } | cat > "$tmp"
    shift
    status=0
    exec bashcov "$tmp" "$@" || status="$?"
    rm "$tmp"
    exit "$status"
}

... I'm not sure if I'm spending too much or too little of my time writing shell scripts.

tomeon commented 5 years ago

Holy moly, TIL that kcov is using some LD_PRELOAD trickery to override execve calls and inject the -x flag into the argument vector when the command name is /bin/bash, /bin/sh, /bin/ash or /bin/dash. That's... brilliant(/depraved). Not sure it's worth supporting something similar for Bashcov, but it's a neat idea.

infertux commented 5 years ago

Nice find! Though I feel like overriding LD_PRELOAD is more depraved than brilliant IMHO... unless you're doing it for debugging purposes.