Skip to content

Commit 477fe67

Browse files
committed
check: Start a check script, in Bash
This provides the guts of an implementation of #60. I'd have liked to write this in Dart, rather than in shell. But when running this script interactively (vs. in CI), a key ingredient for a good developer experience is that the child processes like `flutter test` should have their stdout and stderr connected directly to those of the parent script, i.e. to the developer's terminal. Crucially, that lets those processes know to print their output in a form that takes advantage of terminal features to get a good interactive experience. The gold standard for a CLI tool is to have a way to control that choice directly, but `flutter test` and some other Dart-based tools lack that capability. And in Dart, as far as I can tell, there simply is no way to start a child process that inherits any of the parent's file handles; instead the child's output will always be wired up to pipes for the parent to consume. There's advice for how the parent can copy the output, chunk by chunk, to its own output: dart-lang/sdk#44573 dart-lang/sdk#5607 https://stackoverflow.com/questions/72183086/dart-dont-buffer-process-stdout-tell-process-that-it-is-running-from-a-termina but that doesn't solve the problem of the child's behavior changing when it sees a pipe instead of a terminal. The nastiest consequence of this behavior is that the output of `flutter test` is hundreds of lines long, even for our small codebase, even when only a single test case fails or none at all. That's fine in CI, but pretty much intolerable for a development workflow. So, shell it is. (Python, or Javascript with Node, or many other languages would also handle this just fine, but would mean an extra system of dependencies to manage.) Specifically, Bash. --- Fortunately, using Bash does mean we get to reuse the work that's already gone into the `tools/test` script in zulip-mobile. This commit introduces a bare-bones version, with most of the features (notably --diff and its friends) stripped out, and the test suites replaced with a first two for our codebase. This version also uses `set -u` and `set -o pipefail`, unlike the one in zulip-mobile, in addition to `set -e`.
1 parent e5d1e3b commit 477fe67

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

tools/check

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env bash
2+
3+
# Careful! `set -e` doesn't do everything you'd think it does. In
4+
# fact, we don't get its benefit in any of the `run_foo` functions.
5+
#
6+
# This is because it has an effect only when it can exit the whole shell.
7+
# (Its full name is `set -o errexit`, and it means "exit" literally.) See:
8+
# https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
9+
#
10+
# When one test suite fails, we want to go on to run the other suites, so
11+
# we use `||` to prevent the whole script from exiting there, and that
12+
# defeats `set -e`.
13+
#
14+
# For now our workaround is to put `|| return` in the `run_foo` just
15+
# after each nontrivial command that isn't the final command in the
16+
# function.
17+
set -euo pipefail
18+
19+
20+
## CLI PARSING
21+
22+
default_suites=(analyze test)
23+
extra_suites=(
24+
)
25+
26+
usage() {
27+
cat >&2 <<EOF
28+
usage: tools/check [SUITE]...
29+
30+
Run our tests.
31+
32+
By default, run ${#default_suites[@]} suite(s):
33+
${default_suites[*]}
34+
and skip ${#extra_suites[@]} suite(s):
35+
${extra_suites[*]}
36+
EOF
37+
exit 2
38+
}
39+
40+
opt_suites=()
41+
while (( $# )); do
42+
case "$1" in
43+
analyze|test)
44+
opt_suites+=("$1"); shift;;
45+
*) usage;;
46+
esac
47+
done
48+
49+
if (( ! "${#opt_suites[@]}" )); then
50+
opt_suites=( "${default_suites[@]}" )
51+
fi
52+
53+
54+
## EXECUTION
55+
56+
rootdir=$(git rev-parse --show-toplevel)
57+
cd "$rootdir"
58+
59+
run_analyze() {
60+
flutter analyze
61+
}
62+
63+
run_test() {
64+
flutter test
65+
}
66+
67+
failed=()
68+
for suite in "${opt_suites[@]}"; do
69+
echo "Running $suite..."
70+
case "$suite" in
71+
analyze) run_analyze ;;
72+
test) run_test ;;
73+
*) echo >&2 "Internal error: unknown suite $suite" ;;
74+
esac || failed+=( "$suite" )
75+
done
76+
77+
if (( ${#failed[@]} )); then
78+
cat >&2 <<EOF
79+
80+
FAILED: ${failed[*]}
81+
82+
To rerun the suites that failed, run:
83+
$ tools/check ${failed[*]}
84+
EOF
85+
exit 1
86+
fi
87+
88+
echo "Passed!"

0 commit comments

Comments
 (0)