Cram test errors and **** UNREACHABLE ****

Some kinds of errors in Dune Cram tests lead to a ***** UNREACHABLE ***** message, which makes it hard to debug what’s wrong.

For instance:

dune init proj foo
cd foo
mkdir cram.t
echo "  $ source foo.sh" > cram.t/run.t
touch cram.t/foo.sh
dune runtest

It outputs:

File "cram.t/run.t", line 1, characters 0-0:
diff --git 1/_build/default/cram.t/run.t 2/_build/default/cram.t/run.t.corrected
index 7e8203e..84e2e4d 100644
--- 1/_build/default/cram.t/run.t
+++ 2/_build/default/cram.t/run.t.corrected
@@ -1 +1,2 @@
   $ source foo.sh
+  ***** UNREACHABLE *****

If we add other commands to debug, such as cat, we see the file is visible for the shell script running it.

Another case in which UNREACHABLE appears is if we add an exit to run.t, as in:

  $ exit 1

And I think I had other cases in which it happened.

Sometimes it’s hard to find the exact cause. Is there a way to debug such scripts to get more information? Other than trying to manually emulate what Dune does, that is, executing the commands manually.

1 Like

If you replace it by source ./foo.sh, I don’t have errors and variables are loaded (imagine FOO=42, I can do echo $FOO and it shows 42). More generally, I prefer not to rely on the shell or tools (such as wc) in a cram test and prefer to recode certain utilities in OCaml (because I know that, at least, OCaml is available :slight_smile: ).

1 Like

When a cram test says ***** UNREACHABLE ***** that means that the command directly above exited the shell. The question then becomes why is it that source ./foo.sh does not exit the shell but source foo.sh does?

Luckily the man page for bash has the answers:

       source [-p path] filename [arguments]
              The  .  command  (source)  reads and execute commands
              from filename in the current  shell  environment  and
              returns  the exit status of the last command executed
              from filename.

              If filename does not contain a slash, . searches  for
              it.  If the -p option is supplied, . treats path as a
              colon-separated  list of directories in which to find
              filename; otherwise, . uses the entries  in  PATH  to
              find  the  directory  containing  filename.  filename
              does not need to be executable.  When bash is not  in
              posix  mode,  it  searches  the  current directory if
              filename is not found in PATH, but  does  not  search
              the  current  directory  if  -p  is supplied.  If the
              sourcepath option to the  shopt  builtin  command  is
              turned off, . does not search PATH.

So source goes and searches the whole of PATH to find it. When it ultimately fails to find it, it exits the non-interactive shell due to the fact that it is a built-in command. This is why Dune tells you that the output below is unreachable, since the test runner has exited the shell.

Requiring something to be sourced at the top of a cram test is a very common pattern. The nature of bash means there are a lot of footguns when it comes to writing a source statement as you have discovered. This is why in Dune we’ve added a new setup_scripts field to the (cram) stanza, allowing you to put (cram (applies_to mytest) (setup_scripts foo.sh)) without any tears. This will probably appear in 3.22, but you can start using it if you look at the main branch or use the nightly release https://nightly.dune.build/.

2 Likes

Is that version dependent? A failing source doesn’t exit the shell for me.

Just a note that searching for unreachable in the dune docs turns up no results: Search - Dune documentation

I should have said non-interactive shell:

$ bash -c 'source foo.sh ; echo "foo"'
bash: line 1: foo.sh: No such file or directory
foo

$ sh -c 'source foo.sh ; echo "foo"'
sh: line 1: source: foo.sh: file not found

Here sh is typically bash --posix for linux users.

1 Like

Thanks for the reminder, I’ll get that included as soon as possible.

Here: doc(cram): document early shell exits by Alizter · Pull Request #13326 · ocaml/dune · GitHub

3 Likes