#!/usr/bin/env bash # # Usage: # ./spec-file.sh set -o nounset set -o pipefail set -o errexit const DASH = $[which dash !2 >/dev/null || echo /bin/sh] const BASH = $[which bash] const MKSH = $[which mksh] const ZSH = $[which zsh] const BUSYBOX_ASH = '_tmp/shells/ash' const OSH_PYTHON = $(OSH_PYTHON:-bin/osh) const OSH_OVM = $(OSH_OVM:-_bin/osh) if test -e $OSH_OVM { # TODO: Does it make sense to copy the binary to an unrelated to directory, # like /tmp? /tmp/{oil.ovm,osh}. # HACK that relies on word splitting. TODO: Use ${OSH[@]} everywhere const OSH = ""$OSH_PYTHON $OSH_OVM"" } else { const OSH = $OSH_PYTHON } # ash and dash are similar, so not including ash by default. zsh is not quite # POSIX. const REF_SHELLS = '('$DASH $BASH $MKSH) # # Setup # proc link-busybox-ash { mkdir -p $[dirname $BUSYBOX_ASH] ln -s -f --verbose $[which busybox] $BUSYBOX_ASH } # dash and bash should be there by default on Ubuntu. proc install-shells { sudo apt-get install busybox-static mksh zsh link-busybox-ash } # TODO: Maybe do this before running all tests. proc check-shells { for sh in [$(REF_SHELLS[@]) $ZSH $OSH] { test -e $sh || do { echo "ERROR: $sh does not exist"; break; } test -x $sh || do { echo "ERROR: $sh isn't executable"; break; } } } proc _wget { wget --no-clobber --directory _tmp/src @Argv } # As of March 2017 proc download-shell-source { mkdir -p _tmp/src # https://tiswww.case.edu/php/chet/bash/bashtop.html - 9/2016 release # https://ftp.gnu.org/gnu/bash/ _wget https://ftp.gnu.org/gnu/bash/bash-4.4.tar.gz # https://www.mirbsd.org/mksh.htm - no dates given _wget https://www.mirbsd.org/MirOS/dist/mir/mksh/mksh-R54.tgz # https://tracker.debian.org/pkg/dash -- old versions # http://www.linuxfromscratch.org/blfs/view/svn/postlfs/dash.html # Site seems down now. # _wget http://gondor.apana.org.au/~herbert/dash/files/dash-0.5.9.1.tar.gz # http://zsh.sourceforge.net/News/ - 12/2016 release _wget https://downloads.sourceforge.net/project/zsh/zsh/5.3.1/zsh-5.3.1.tar.xz } proc maybe-show { var path = $1 if test -e $path { echo "--- $path ---" cat $path echo } } proc version-text { date echo if test -d .git { var branch = $[git rev-parse --abbrev-ref HEAD] var hash = $[git rev-parse $branch] echo "oil repo: $hash on branch $branch" } else { echo "(not running from git repository)" } echo for bin in [$OSH] { echo "\$ $bin --version" $bin --version echo } python --version !2 > !1 echo $BASH --version | head -n 1 echo $ZSH --version | head -n 1 echo # These don't have versions dpkg -s dash | egrep '^Package|Version' echo dpkg -s mksh | egrep '^Package|Version' echo # Need || true because of pipefail do { busybox || true; } | head -n 1 echo maybe-show /etc/debian_version maybe-show /etc/lsb-release } # # Helpers # proc sh-spec { var this_dir = $[cd $[dirname $0] && pwd] var tmp_env = "$this_dir/../_tmp/spec-tmp" mkdir -p $tmp_env test/sh_spec.py \ --tmp-env $tmp_env \ --path-env "$this_dir/../spec/bin:$PATH" \ @Argv } # # Misc # # Really what I want is enter(func) and exit(func), and filter by regex? proc trace-var-sub { var out = '_tmp/coverage' mkdir -p $out # This creates *.cover files, with line counts. #python -m trace --count -C $out \ # This prints trace with line numbers to stdout. #python -m trace --trace -C $out \ python -m trace --trackcalls -C $out \ test/sh_spec.py spec/var-sub.test.sh $DASH $BASH @Argv ls -l $out head $out/*.cover } # # Run All tests # proc all { test/spec-runner.sh all-parallel @Argv } # # Invidual tests. # # We configure the shells they run on and the number of allowed failures (to # prevent regressions.) # proc smoke { sh-spec spec/smoke.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc osh-only { sh-spec spec/osh-only.test.sh $OSH @Argv } # Regress bugs proc bugs { sh-spec spec/bugs.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc blog1 { sh-spec spec/blog1.test.sh \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc blog2 { sh-spec spec/blog2.test.sh \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc blog-other1 { sh-spec spec/blog-other1.test.sh \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc alias { sh-spec spec/alias.test.sh --osh-failures-allowed 10 \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc comments { sh-spec spec/comments.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc word-split { sh-spec spec/word-split.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $OSH @Argv } proc word-eval { sh-spec spec/word-eval.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } # 'do' -- detected statically as syntax error? hm. proc assign { sh-spec spec/assign.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $OSH @Argv } proc background { sh-spec spec/background.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc subshell { sh-spec spec/subshell.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc quote { sh-spec spec/quote.test.sh \ $(REF_SHELLS[@]) $BUSYBOX_ASH $OSH @Argv } proc loop { sh-spec spec/loop.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } # Not implemented in osh at all. Need glob matching of words. proc case_ { sh-spec spec/case_.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $OSH @Argv } proc if_ { sh-spec spec/if_.test.sh --osh-failures-allowed 1 \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc builtins { sh-spec spec/builtins.test.sh --osh-failures-allowed 1 \ $(REF_SHELLS[@]) $OSH @Argv } proc builtin-io { sh-spec spec/builtin-io.test.sh \ $(REF_SHELLS[@]) $ZSH $BUSYBOX_ASH $OSH @Argv } proc builtins2 { sh-spec spec/builtins2.test.sh $(REF_SHELLS[@]) $ZSH $OSH @Argv } # dash and mksh don't implement 'dirs' proc builtin-dirs { sh-spec spec/builtin-dirs.test.sh $BASH $ZSH $OSH @Argv } proc builtin-vars { sh-spec spec/builtin-vars.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $OSH @Argv } proc builtin-getopts { sh-spec spec/builtin-getopts.test.sh --osh-failures-allowed 1 \ $(REF_SHELLS[@]) $BUSYBOX_ASH $OSH @Argv } proc builtin-test { sh-spec spec/builtin-test.test.sh --osh-failures-allowed 1 \ $(REF_SHELLS[@]) $OSH @Argv } proc builtin-trap { sh-spec spec/builtin-trap.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $OSH @Argv } # Bash implements type -t, but no other shell does. For Nix. # zsh/mksh/dash don't have the 'help' builtin. proc builtin-bash { sh-spec spec/builtin-bash.test.sh \ $BASH $OSH @Argv } proc builtins-special { sh-spec spec/builtins-special.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $OSH @Argv } proc command-parsing { sh-spec spec/command-parsing.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc func-parsing { sh-spec spec/func-parsing.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc func { sh-spec spec/func.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc glob { sh-spec spec/glob.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $BUSYBOX_ASH $OSH @Argv } proc arith { sh-spec spec/arith.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc command-sub { sh-spec spec/command-sub.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $OSH @Argv } proc command_ { sh-spec spec/command_.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc pipeline { sh-spec spec/pipeline.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc explore-parsing { sh-spec spec/explore-parsing.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc parse-errors { sh-spec spec/parse-errors.test.sh --osh-failures-allowed 5 \ $(REF_SHELLS[@]) $OSH @Argv } proc here-doc { # NOTE: The last two tests, 31 and 32, have different behavior on my Ubuntu # and Debian machines. # - On Ubuntu, read_from_fd.py fails with Errno 9 -- bad file descriptor. # - On Debian, the whole process hangs. # Is this due to Python 3.2 vs 3.4? Either way osh doesn't implement the # functionality, so it's probably best to just implement it. sh-spec spec/here-doc.test.sh --osh-failures-allowed 1 --range 0-30 \ $(REF_SHELLS[@]) $OSH @Argv } proc redirect { sh-spec spec/redirect.test.sh --osh-failures-allowed 5 \ $(REF_SHELLS[@]) $OSH @Argv } proc posix { sh-spec spec/posix.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc special-vars { sh-spec spec/special-vars.test.sh --osh-failures-allowed 4 \ $(REF_SHELLS[@]) $OSH @Argv } # dash/mksh don't implement this. proc introspect { sh-spec spec/introspect.test.sh --osh-failures-allowed 3 \ $BASH $OSH @Argv } # DONE -- pysh is the most conformant! proc tilde { sh-spec spec/tilde.test.sh $(REF_SHELLS[@]) $OSH @Argv } proc var-op-test { sh-spec spec/var-op-test.test.sh --osh-failures-allowed 5 \ $(REF_SHELLS[@]) $OSH @Argv } proc var-op-other { sh-spec spec/var-op-other.test.sh --osh-failures-allowed 2 \ $(REF_SHELLS[@]) $OSH @Argv } proc var-op-strip { sh-spec spec/var-op-strip.test.sh --osh-failures-allowed 1 \ $(REF_SHELLS[@]) $ZSH $OSH @Argv } proc var-sub { # NOTE: ZSH has interesting behavior, like echo hi > "$@" can write to TWO # FILES! But ultimately we don't really care, so I disabled it. sh-spec spec/var-sub.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc var-num { sh-spec spec/var-num.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc var-sub-quote { sh-spec spec/var-sub-quote.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc sh-options { sh-spec spec/sh-options.test.sh --osh-failures-allowed 3 \ $(REF_SHELLS[@]) $OSH @Argv } proc xtrace { sh-spec spec/xtrace.test.sh --osh-failures-allowed 5 \ $(REF_SHELLS[@]) $OSH @Argv } proc strict-options { sh-spec spec/strict-options.test.sh \ $(REF_SHELLS[@]) $OSH @Argv } proc errexit { sh-spec spec/errexit.test.sh \ $(REF_SHELLS[@]) $BUSYBOX_ASH $OSH @Argv } proc errexit-strict { sh-spec spec/errexit-strict.test.sh \ $(REF_SHELLS[@]) $BUSYBOX_ASH $OSH @Argv } # # Non-POSIX extensions: arrays, brace expansion, [[, ((, etc. # # There as many non-POSIX arithmetic contexts. proc arith-context { sh-spec spec/arith-context.test.sh --osh-failures-allowed 3 \ $BASH $MKSH $ZSH $OSH @Argv } proc array { sh-spec spec/array.test.sh --osh-failures-allowed 8 \ $BASH $MKSH $OSH @Argv } proc array-compat { sh-spec spec/array-compat.test.sh --osh-failures-allowed 7 \ $BASH $MKSH $OSH @Argv } proc type-compat { sh-spec spec/type-compat.test.sh $BASH @Argv } # += is not POSIX and not in dash. proc append { sh-spec spec/append.test.sh --osh-failures-allowed 4 \ $BASH $MKSH $OSH @Argv } # associative array -- mksh implements different associative arrays. proc assoc { sh-spec spec/assoc.test.sh $BASH @Argv } # ZSH also has associative arrays, which means we probably need them proc assoc-zsh { sh-spec spec/assoc-zsh.test.sh $ZSH @Argv } # NOTE: zsh passes about half and fails about half. It supports a subset of [[ # I guess. proc dbracket { sh-spec spec/dbracket.test.sh --osh-failures-allowed 2 \ $BASH $MKSH $OSH @Argv #sh-spec spec/dbracket.test.sh $BASH $MKSH $OSH $ZSH "$@" } proc dparen { sh-spec spec/dparen.test.sh \ $BASH $MKSH $ZSH $OSH @Argv } proc brace-expansion { # TODO for osh: implement num ranges, mark char ranges unimplemented? sh-spec spec/brace-expansion.test.sh --osh-failures-allowed 12 \ $BASH $MKSH $ZSH $OSH @Argv } proc regex { sh-spec spec/regex.test.sh --osh-failures-allowed 3 \ $BASH $ZSH $OSH @Argv } proc process-sub { # mksh and dash don't support it sh-spec spec/process-sub.test.sh \ $BASH $ZSH $OSH @Argv } proc extended-glob { # Do NOT use dash here. Brace sub breaks things. sh-spec spec/extended-glob.test.sh $BASH $MKSH @Argv } # ${!var} syntax -- oil should replace this with associative arrays. proc var-ref { sh-spec spec/var-ref.test.sh --osh-failures-allowed 5 \ $BASH $MKSH $OSH @Argv } proc let { sh-spec spec/let.test.sh $BASH $MKSH $ZSH @Argv } proc for-expr { sh-spec spec/for-expr.test.sh \ $MKSH $BASH $OSH @Argv } # TODO: This is for the ANTLR grammars, in the oil-sketch repo. # osh has infinite loop? proc shell-grammar { sh-spec spec/shell-grammar.test.sh $BASH $MKSH $ZSH @Argv } @Argv