OILS / test / gold / errexit-confusion.sh View on Github | oilshell.org

139 lines, 61 significant
1#!/usr/bin/env bash
2#
3# Show problems with errexit.
4#
5# Usage:
6# ./errexit-confusion.sh <function name>
7
8set -o nounset
9set -o pipefail
10set -o errexit
11
12log() {
13 echo "$@" 1>&2
14}
15
16die() {
17 log "$@"
18 exit 1
19}
20
21all-passing() {
22 bin/osh --parse-and-print-arena foo # This fails
23 echo status=$? # succeeds
24}
25
26# Copied from test/common.sh, to show the bug.
27
28# The bug is that errexit is disabled within if. Fixes tried:
29#
30# 1) Putting it on its own line: Then errexit triggers and you don't get the
31# failure message.
32#
33# $func_name 2>&1
34#
35# 2) Disabling with
36#
37# set +o errexit
38# $func_name 2>&1
39# status=$?
40# set -o errexit
41#
42# The problem is that the LAST line of all-passing succeeds. We want it to
43# fail in the middle.
44
45run-other-suite-for-release-OLD() {
46 local suite_name=$1
47 local func_name=$2
48
49 local out=_tmp/other/${suite_name}.txt
50 mkdir -p $(dirname $out)
51
52 echo
53 echo "*** Running test suite '$suite_name' ***"
54 echo
55
56 if $func_name 2>&1; then
57 echo
58 log "Test suite '$suite_name' ran without errors. Wrote $out"
59 else
60 echo
61 die "Test suite '$suite_name' failed (running $func_name)"
62 fi
63}
64
65# This is an awkward rewrite that works.
66#
67# Again the argv dispatch pattern saves the day! You can test if a function
68# failed while preserving its own errexit semantics!
69#
70# This composes!
71
72run-other-suite-for-release-FIXED() {
73 local suite_name=$1
74 local func_name=$2
75
76 local out=_tmp/other/${suite_name}.txt
77 mkdir -p $(dirname $out)
78
79 echo
80 echo "*** Running test suite '$suite_name' ***"
81 echo
82
83 local status=0
84
85 # Run in a separate SHELL, not just in a separate process. ( $func_name )
86 # doesn't work.
87 $0 $func_name 2>&1 | tee $out || status=$?
88
89 if test $status -eq 0; then
90 echo
91 log "Test suite '$suite_name' ran without errors. Wrote '$out'"
92 else
93 echo
94 die "Test suite '$suite_name' failed with status $status (running '$func_name', wrote '$out')"
95 fi
96}
97
98run-for-release-OLD() {
99 run-other-suite-for-release-OLD example-failure all-passing
100}
101
102run-for-release-FIXED() {
103 run-other-suite-for-release-FIXED example-failure all-passing
104}
105
106# This could be a blog post:
107#
108# Conditions for the problem:
109# - using errexit (pipefail)
110# - but you want to test if a FUNCTION failed. If you disable errexit, you are
111# changing the semantics of the function!
112#
113# Solution:
114#
115# 1. Use the argv dispatch pattern
116# 2. Use $0 $func_name || status=$?
117
118# -----------------------------------------------------------------------------
119
120# Another different that went away with the FIXED: A case where the stricter
121# behavior of OSH's errexit was triggered.
122
123# Can't set 'errexit' in a context where it's disabled (if, !, && ||,
124# while/until conditions)
125#
126# Arguably this is exposing a bug? errexit is already disabled? May have to
127# revisit this.
128
129test-case-that-sets-errexit() {
130 set +o errexit
131 echo hi
132}
133
134osh-stricter() {
135 run-other-suite-for-release-OLD osh-stricter test-case-that-sets-errexit
136}
137
138"$@"
139