Permalink
Browse files

Start a spec test for xtrace.

OSH quick ref:
- Document more environment variables.  The [Call Stack] section is
  relevant to tracing.  So are PS4 and SHELLOPTS.
- Document traps.  There are four traps relevant to debugging.
  • Loading branch information...
Andy Chu
Andy Chu committed Dec 27, 2017
1 parent 934deba commit ffab9a4328b03fb3e50a19f7649612f88db0d6cb
Showing with 68 additions and 24 deletions.
  1. +2 −2 build/quick_ref.py
  2. +7 −3 core/cmd_exec.py
  3. +8 −6 core/state.py
  4. +15 −4 doc/osh-quick-ref-toc.txt
  5. +0 −8 spec/sh-options.test.sh
  6. +30 −0 spec/xtrace.test.sh
  7. +6 −1 test/spec.sh
View
@@ -17,7 +17,7 @@
# 2. lower-case or upper-case topic
# 3. Optional: A SINGLE space, then punctuation
TOPIC_RE = re.compile(r'\b(X[ ])?([a-zA-Z\-]+)([ ]\S+)?', re.VERBOSE)
TOPIC_RE = re.compile(r'\b(X[ ])?\@?([a-zA-Z0-9_\-]+)([ ]\S+)?', re.VERBOSE)
# Sections have alphabetical characters, spaces, and '/' for I/O. They are
# turned into anchors.
@@ -76,7 +76,7 @@ def HighlightLine(line):
parts.append(RED_X)
# Topics on the same line must be separated by exactly THREE spaces
if found_one and prior_piece != ' ':
if found_one and prior_piece not in (' ', ' @'):
last_end = start
break # stop linking
View
@@ -638,10 +638,14 @@ def _Dispatch(self, node, fork_external):
self._EvalEnv(node.more_env, environ)
if self.exec_opts.xtrace:
# TODO: Eval PS4. Using what evaluator? I guess the same state.
# self.ev.
log('+ %s', argv)
#print('+ %s' % argv, file=sys.stderr)
#print('+ %s' % argv, file=self.XFILE)
#os.write(2, '+ %s\n' % argv)
# TODO:
#
# This is good enough for xtrace.
# But the tracer is more general than that.
# self.tracer.BeginSimpleCommand()
status = self._RunSimpleCommand(argv, environ, fork_external)
View
@@ -103,7 +103,7 @@ def __init__(self, mem):
shellopts = self.mem.GetVar('SHELLOPTS')
assert shellopts.tag == value_e.Str, shellopts
self._InitFromEnv(shellopts.s)
self._InitOptionsFromEnv(shellopts.s)
# shopt -s / -u
self.nullglob = False
@@ -118,7 +118,7 @@ def __init__(self, mem):
# TODO: strict_bool. Some of this is covered by arithmetic, e.g. -eq.
def _InitFromEnv(self, shellopts):
def _InitOptionsFromEnv(self, shellopts):
# e.g. errexit:nounset:pipefail
lookup = set(shellopts.split(':'))
for _, name in SET_OPTIONS:
@@ -181,8 +181,7 @@ def SetOption(self, opt_name, b):
self.mem.InternalSetGlobal('SHELLOPTS', new_val)
else:
if opt_name in shellopts:
names = shellopts.split(':')
names = [n for n in names if n != opt_name]
names = [n for n in shellopts.split(':') if n != opt_name]
new_val = runtime.Str(':'.join(names))
self.mem.InternalSetGlobal('SHELLOPTS', new_val)
@@ -296,7 +295,7 @@ def __init__(self, argv0, argv, environ, arena):
self.root_pid = os.getpid()
self._InitDefaults()
self._InitEnviron(environ)
self._InitVarsFromEnv(environ)
self.arena = arena
def __repr__(self):
@@ -322,7 +321,10 @@ def _InitDefaults(self):
# For getopts builtin
SetGlobalString(self, 'OPTIND', '1')
def _InitEnviron(self, environ):
# For xtrace
SetGlobalString(self, 'PS4', '+ ')
def _InitVarsFromEnv(self, environ):
# This is the way dash and bash work -- at startup, they turn everything in
# 'environ' variable into shell variables. Bash has an export_env
# variable. Dash has a loop through environ in init.c
View
@@ -84,12 +84,23 @@ SHELL OPTIONS
[OSH Sane] TODO
ENVIRONMENT VARIABLES
X [Prompts] PS1 PS2
[Shell Options] X SHELLOPTS X BASHOPTS
[Process State] UID EUID
[Process State] X BASHPID X PPID UID EUID
[cd] PWD OLDPWD
[getopts] OPTIND OPTARG
[getopts] OPTIND OPTARG X OPTERR
[read] REPLY IFS
[Other] IFS
[Libaries] X RANDOM TODO:TIMESTAMP
X [select] PS3
[xtrace] PS4
[Platform] HOME X HOSTNAME X OSTYPE
[Functions] X RANDOM X SECONDS
X [Call Stack] @BASH_SOURCE @FUNCNAME @BASH_LINENO
@BASH_ARGV @BASH_ARGC LINENO
X [Process Stack] BASH_SUBSHELL SHLVL
X [Shell State] BASH_CMDS DIRSTACK
[Other] IFS X BASH_REMATCH @PIPESTATUS
TRAPS
X [Signals] SIGINT SIGABRT SIGTODO
X [Debugging] ERR DEBUG RETURN EXIT
View
@@ -149,14 +149,6 @@ echo 3
# stdout-json: "1\n"
# status: 0
### xtrace
echo 1
set -o xtrace
echo 2
# stdout-json: "1\n2\n"
# stderr: + echo 2
### pipefail
# NOTE: the sleeps are because osh can fail non-deterministically because of a
# bug. Same problem as PIPESTATUS.
View
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
#
# xtrace test. Test PS4 and line numbers, etc.
#
# TODO: need multiline test format
### basic xtrace
set -x
echo one >&2
echo two >&2
# stdout-json: ""
# stderr-json: "+ echo one\none\n+ echo two\ntwo\n"
### xtrace
echo 1
set -o xtrace
echo 2
# stdout-json: "1\n2\n"
# stderr: + echo 2
### PS4 is scoped
set -x
echo one
f() {
local PS4='- '
echo func;
}
f
echo two
# stderr-json: "+ echo one\n+ f\n+ local 'PS4=- '\n- echo func\n+ echo two\n"
View
@@ -412,7 +412,12 @@ var-sub-quote() {
}
sh-options() {
sh-spec spec/sh-options.test.sh --osh-failures-allowed 4 \
sh-spec spec/sh-options.test.sh --osh-failures-allowed 3 \
${REF_SHELLS[@]} $OSH "$@"
}
xtrace() {
sh-spec spec/xtrace.test.sh --osh-failures-allowed 3 \
${REF_SHELLS[@]} $OSH "$@"
}

0 comments on commit ffab9a4

Please sign in to comment.