Skip to content

Commit a141353

Browse files
Set up automated bash tests with bats (#1721)
Resolves #1714. Related #1710. Stacked onto #1720. This PR sets up bash tests with [bats](https://bats-core.readthedocs.io/en/stable/), and adds a first test suite for [the `strip-marker-sections` script](#1720). ## Notes - Similar to how we do it with other test files, I put the `.bats` test file next to the script under test. The directive in `.dockerignore` [removes it from the bundle / `.deb` file](https://github.com/tiny-pilot/tinypilot/assets/83721279/7f52ca3e-17d9-486a-83a5-ea1c6132eb36). - The `build_bash` dev script is called “build” for consistency with `build_python` and `build_javascript`, even though we technically don’t build something. I’ve created #1716 for us to potentially reconsider this overall. - In `build_bash`, it somehow felt reasonable to me to search all places that are likely to contain `.bats` files, even though we currently only have them in `/opt/tinypilot-privileged/scripts/`. I don’t feel strongly about this, though. - For running the tests, there theoretically would be [an official bats Docker image](https://bats-core.readthedocs.io/en/stable/installation.html#running-bats-in-docker), however that doesn’t play nicely with our bash files: the docker image doesn’t have a symlink at `/bin/bash`, so it fails to execute any of our bash scripts that have a `#!/bin/bash` shebang (which is effectively all of them). We instead would either have to change all our shebangs to the (most portable) `#!/usr/bin/env bash` shebang, or we just install bats manually like done here (in the Circle conf). <a data-ca-tag href="https://codeapprove.com/pr/tiny-pilot/tinypilot/1721"><img src="https://codeapprove.com/external/github-tag-allbg.png" alt="Review on CodeApprove" /></a> --------- Co-authored-by: Jan Heuermann <[email protected]>
1 parent 8dadc66 commit a141353

File tree

5 files changed

+227
-0
lines changed

5 files changed

+227
-0
lines changed

.circleci/config.yml

+15
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ jobs:
9090
- run:
9191
name: Run build script
9292
command: ./dev-scripts/build-javascript
93+
build_bash:
94+
docker:
95+
- image: cimg/base:2024.01
96+
steps:
97+
- checkout
98+
- run:
99+
name: Install dependencies
100+
command: |
101+
git clone --depth 1 --branch v1.10.0 https://github.com/bats-core/bats-core.git
102+
cd bats-core
103+
sudo ./install.sh /usr/local
104+
- run:
105+
name: Run build script
106+
command: ./dev-scripts/build-bash
93107
e2e:
94108
docker:
95109
# To run tests against the dev server, Playwright needs a CircleCI image
@@ -263,6 +277,7 @@ workflows:
263277
- decode_edid
264278
- build_python
265279
- build_javascript
280+
- build_bash
266281
- build_debian_package
267282
- e2e
268283
- lint_debian_package:

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Test files.
66
**/*_test.py
77
**/*.test.js
8+
**/*.bats
89

910
/debian-pkg/Dockerfile
1011
/debian-pkg/releases/

CONTRIBUTING.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The steps below show you how to quickly set up a development environment for Tin
1111
- Python 3.9 or higher
1212
- Node.js 18.16.1 or higher
1313
- [shellcheck](https://github.com/koalaman/shellcheck#installing)
14+
- [bats](https://bats-core.readthedocs.io/en/stable/installation.html)
1415
- Docker 20.10.x or higher
1516

1617
### Install packages
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/bin/bash
2+
3+
{
4+
# Silence shellcheck for global bats variables.
5+
# https://github.com/tiny-pilot/tinypilot/issues/1718
6+
# shellcheck disable=SC2154
7+
echo "${output}" "${status}" "${lines}" >/dev/null
8+
}
9+
10+
# Wrapper for invoking the script under test as command.
11+
strip-marker-sections() {
12+
bash "${BATS_TEST_DIRNAME}/strip-marker-sections" "$@"
13+
}
14+
15+
prints-help() { #@test
16+
run strip-marker-sections --help
17+
expected_output="$(cat << EOF
18+
Usage: strip-marker-sections [--help] TARGET_FILE
19+
Strips TinyPilot marker sections from a file.
20+
TARGET_FILE Path to file with marker sections.
21+
--help Display this help and exit.
22+
EOF
23+
)"
24+
25+
[[ "${status}" == 0 ]]
26+
[[ "${output}" == "${expected_output}" ]]
27+
}
28+
29+
rejects-missing-input-arg() { #@test
30+
run strip-marker-sections
31+
32+
[[ "${status}" == 1 ]]
33+
[[ "${output}" == 'Input parameter missing: TARGET_FILE' ]]
34+
}
35+
36+
rejects-illegal-flag() { #@test
37+
run strip-marker-sections --foo
38+
39+
[[ "${status}" == 1 ]]
40+
[[ "${output}" == 'Illegal option: --foo' ]]
41+
}
42+
43+
rejects-non-existing-file() { #@test
44+
run strip-marker-sections foo-file.txt
45+
46+
[[ "${status}" == 1 ]]
47+
[[ "${output}" == 'Not a file: foo-file.txt' ]]
48+
}
49+
50+
rejects-non-file() { #@test
51+
tmp_dir="$(mktemp --directory)"
52+
run strip-marker-sections "${tmp_dir}"
53+
54+
[[ "${status}" == 1 ]]
55+
[[ "${output}" == "Not a file: ${tmp_dir}" ]]
56+
}
57+
58+
noop-if-file-has-no-markers() { #@test
59+
target_file="$(mktemp)"
60+
cat << EOF > "${target_file}"
61+
line 1
62+
line 2
63+
line 3
64+
EOF
65+
run strip-marker-sections "${target_file}"
66+
actual_contents="$(<"${target_file}")"
67+
expected_contents="$(cat << EOF
68+
line 1
69+
line 2
70+
line 3
71+
EOF
72+
)"
73+
74+
[[ "${status}" == 0 ]]
75+
[[ "${output}" == "" ]]
76+
[[ "${actual_contents}" == "${expected_contents}" ]]
77+
}
78+
79+
preserves-whitespace() { #@test
80+
target_file="$(mktemp)"
81+
cat << EOF > "${target_file}"
82+
x y
83+
84+
1
85+
EOF
86+
run strip-marker-sections "${target_file}"
87+
actual_contents="$(<"${target_file}")"
88+
expected_contents="$(cat << EOF
89+
x y
90+
91+
1
92+
EOF
93+
)"
94+
95+
[[ "${status}" == 0 ]]
96+
[[ "${output}" == "" ]]
97+
[[ "${actual_contents}" == "${expected_contents}" ]]
98+
}
99+
100+
strips-marker-section() { #@test
101+
target_file="$(mktemp)"
102+
cat << EOF > "${target_file}"
103+
some line
104+
some other line
105+
# --- AUTOGENERATED BY TINYPILOT - START ---
106+
to be stripped
107+
# --- AUTOGENERATED BY TINYPILOT - END ---
108+
final line
109+
EOF
110+
run strip-marker-sections "${target_file}"
111+
actual_contents="$(<"${target_file}")"
112+
expected_contents="$(cat << EOF
113+
some line
114+
some other line
115+
final line
116+
EOF
117+
)"
118+
119+
[[ "${status}" == 0 ]]
120+
[[ "${output}" == "" ]]
121+
[[ "${actual_contents}" == "${expected_contents}" ]]
122+
}
123+
124+
strips-multiple-marker-sections() { #@test
125+
target_file="$(mktemp)"
126+
cat << EOF > "${target_file}"
127+
some line
128+
some other line
129+
# --- AUTOGENERATED BY TINYPILOT - START ---
130+
to be stripped
131+
# --- AUTOGENERATED BY TINYPILOT - END ---
132+
intermediate line
133+
# --- AUTOGENERATED BY TINYPILOT - START ---
134+
to be stripped too
135+
# --- AUTOGENERATED BY TINYPILOT - END ---
136+
final line
137+
EOF
138+
run strip-marker-sections "${target_file}"
139+
actual_contents="$(<"${target_file}")"
140+
expected_contents="$(cat << EOF
141+
some line
142+
some other line
143+
intermediate line
144+
final line
145+
EOF
146+
)"
147+
148+
[[ "${status}" == 0 ]]
149+
[[ "${output}" == "" ]]
150+
[[ "${actual_contents}" == "${expected_contents}" ]]
151+
}
152+
153+
fails-for-unmatched-start-marker() { #@test
154+
target_file="$(mktemp)"
155+
cat << EOF > "${target_file}"
156+
some line
157+
# --- AUTOGENERATED BY TINYPILOT - START ---
158+
to be stripped
159+
EOF
160+
run strip-marker-sections "${target_file}"
161+
actual_contents="$(<"${target_file}")"
162+
expected_contents="$(cat << EOF
163+
some line
164+
# --- AUTOGENERATED BY TINYPILOT - START ---
165+
to be stripped
166+
EOF
167+
)"
168+
169+
[[ "${status}" == 1 ]]
170+
[[ "${output}" == "Unmatched start marker" ]]
171+
[[ "${actual_contents}" == "${expected_contents}" ]]
172+
}
173+
174+
fails-for-unmatched-end-marker() { #@test
175+
target_file="$(mktemp)"
176+
cat << EOF > "${target_file}"
177+
some line
178+
# --- AUTOGENERATED BY TINYPILOT - END ---
179+
final line
180+
EOF
181+
run strip-marker-sections "${target_file}"
182+
actual_contents="$(<"${target_file}")"
183+
expected_contents="$(cat << EOF
184+
some line
185+
# --- AUTOGENERATED BY TINYPILOT - END ---
186+
final line
187+
EOF
188+
)"
189+
190+
[[ "${status}" == 1 ]]
191+
[[ "${output}" == 'Unmatched end marker' ]]
192+
[[ "${actual_contents}" == "${expected_contents}" ]]
193+
}

dev-scripts/build-bash

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/bash
2+
3+
# Exit on first failure.
4+
set -e
5+
6+
# Echo commands before executing them, by default to stderr.
7+
set -x
8+
9+
# Exit on unset variable.
10+
set -u
11+
12+
# Run bats tests.
13+
bats \
14+
--recursive \
15+
scripts/ \
16+
debian-pkg/ \
17+
dev-scripts/

0 commit comments

Comments
 (0)