diff --git a/README.md b/README.md index 8e64837..9c6ce5e 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,14 @@ are outside `make check`. Neither opens the popup from the toolbar button: both load its page in an ordinary tab, so what the toolbar popup itself adds, its size and its closing when it loses focus, is covered by neither. +Each run builds its image under a tag of its own, `autistmask-e2e-chrome:` or +`autistmask-e2e-firefox:` followed by twelve random hex digits, so two clones +can run the same suite at once without one build taking the other's image away. +The run removes that image, and its container if one is still running, after the +suite passes or fails, on Ctrl-C, on `kill`, and when the terminal or SSH +session it runs in closes. A run killed outright (`kill -9`) cannot clean up: +its image stays under its tag until removed by hand with `docker image rm`. + ### Chrome (`make test-e2e`) `make test-e2e` builds `dist/chrome/` and drives the **real popup in a real diff --git a/script/test-e2e b/script/test-e2e index 2246905..2763bb6 100755 --- a/script/test-e2e +++ b/script/test-e2e @@ -22,13 +22,29 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" -IMAGE="$("$SCRIPT_DIR/projectname")-e2e-chrome" - -IIDFILE="" +# Each run builds, runs and then removes an image under a tag of its own. +# Under one tag shared by every run, a second clone's build moves the tag +# off this run's image, and the image store can then delete that untagged +# image before or while this run uses it. The tag is random rather than +# $$ because CI jobs share one docker daemon, but each job's container +# numbers its processes from scratch. +SUFFIX="$(od -An -N6 -tx1 /dev/urandom | tr -d ' \n')" +NAME="$("$SCRIPT_DIR/projectname")-e2e-chrome" +IMAGE="$NAME:$SUFFIX" +# Both docker runs below use this container name: docker run --rm returns +# only once its container is removed, so the second never meets the first. +CONTAINER="$NAME-$SUFFIX" cleanup() { - if [ -n "$IIDFILE" ]; then - rm -f "$IIDFILE" + # After a third Ctrl-C docker's client exits whether or not the + # container has stopped, and a container still running keeps the image + # in use. + if docker container inspect "$CONTAINER" >/dev/null 2>&1; then + docker container rm -f "$CONTAINER" >/dev/null + fi + # No image to remove when the build failed or was interrupted. + if docker image inspect "$IMAGE" >/dev/null 2>&1; then + docker image rm "$IMAGE" >/dev/null fi } @@ -40,22 +56,16 @@ main() { exit 1 fi - IIDFILE="$(mktemp)" trap cleanup EXIT - trap 'cleanup; exit 130' INT TERM + # HUP: the terminal or SSH session running the suite closed. + trap 'cleanup; exit 130' HUP INT TERM echo "Building the Chrome e2e image (extension included)..." # --no-cache: the image build runs make build and its checks, and a # cached layer is a check that did not run. - docker build --no-cache --iidfile "$IIDFILE" -t "$IMAGE" \ - -f tests/e2e/Dockerfile . + docker build --no-cache -t "$IMAGE" -f tests/e2e/Dockerfile . echo "Running e2e suite in the pinned Playwright container..." - # The image is run by ID, not by tag: where two clones of this repo run - # the suite at once, the other build can move the tag between this - # build and this run, and the suite would then silently test the other - # checkout. - # # --ipc=host: Chromium's shared-memory needs more than the default # 64MB /dev/shm or renderers crash. # HOME=/tmp: the image's root home is not a reliable place for the @@ -75,11 +85,12 @@ main() { # probe. The image is pinned by digest, so this can only ever bite # on a deliberate bump. docker run --rm \ + --name "$CONTAINER" \ --ipc=host \ -e HOME=/tmp \ -e PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 \ -e "E2E_TRACE_NETWORK=${E2E_TRACE_NETWORK:-0}" \ - "$(cat "$IIDFILE")" \ + "$IMAGE" \ node tests/e2e/run.js # Where chrome.storage.local lives, and what moves it: two unpacked loads @@ -93,9 +104,10 @@ main() { # --host-resolver-rules=MAP * ~NOTFOUND so nothing it does can leave. echo "Running the extension-id and storage-partition observations..." docker run --rm \ + --name "$CONTAINER" \ --ipc=host \ -e HOME=/tmp \ - "$(cat "$IIDFILE")" \ + "$IMAGE" \ node tests/e2e/storagePartition.js } diff --git a/script/test-e2e-firefox b/script/test-e2e-firefox index 892e7e1..884a710 100755 --- a/script/test-e2e-firefox +++ b/script/test-e2e-firefox @@ -21,13 +21,23 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" -IMAGE="$("$SCRIPT_DIR/projectname")-e2e-firefox" - -IIDFILE="" +# A tag and a container name of this run's own, as in script/test-e2e, +# which says why. +SUFFIX="$(od -An -N6 -tx1 /dev/urandom | tr -d ' \n')" +NAME="$("$SCRIPT_DIR/projectname")-e2e-firefox" +IMAGE="$NAME:$SUFFIX" +CONTAINER="$NAME-$SUFFIX" cleanup() { - if [ -n "$IIDFILE" ]; then - rm -f "$IIDFILE" + # After a third Ctrl-C docker's client exits whether or not the + # container has stopped, and a container still running keeps the image + # in use. + if docker container inspect "$CONTAINER" >/dev/null 2>&1; then + docker container rm -f "$CONTAINER" >/dev/null + fi + # No image to remove when the build failed or was interrupted. + if docker image inspect "$IMAGE" >/dev/null 2>&1; then + docker image rm "$IMAGE" >/dev/null fi } @@ -39,22 +49,16 @@ main() { exit 1 fi - IIDFILE="$(mktemp)" trap cleanup EXIT - trap 'cleanup; exit 130' INT TERM + # HUP: the terminal or SSH session running the suite closed. + trap 'cleanup; exit 130' HUP INT TERM echo "Building the pinned Firefox e2e image (extension included)..." # --no-cache: the image build runs make build and its checks, and a # cached layer is a check that did not run. - docker build --no-cache --iidfile "$IIDFILE" -t "$IMAGE" \ - -f tests/e2e/firefox/Dockerfile . + docker build --no-cache -t "$IMAGE" -f tests/e2e/firefox/Dockerfile . echo "Running the Firefox e2e suite..." - # The image is run by ID, not by tag: where two clones of this repo run - # the suite at once, the other build can move the tag between this - # build and this run, and the suite would then silently test the other - # checkout. - # # --shm-size=1g: Firefox needs more than the default 64MB /dev/shm. # --network none: the suite stubs nothing, so this is what keeps the # run offline and deterministic. The extension swallows its own @@ -69,10 +73,11 @@ main() { # "CanCreateUserNamespace() clone() failure: EPERM" on startup here; # it is cosmetic and headless Firefox runs fine without it. docker run --rm \ + --name "$CONTAINER" \ --shm-size=1g \ --network none \ -e HOME=/tmp \ - "$(cat "$IIDFILE")" \ + "$IMAGE" \ node tests/e2e/firefox/run.js dist/firefox # The install/uninstall/re-install property, against the packaged XPI @@ -82,10 +87,11 @@ main() { # and the suite above shares one session throughout. echo "Running the Firefox re-install suite against the packaged XPI..." docker run --rm \ + --name "$CONTAINER" \ --shm-size=1g \ --network none \ -e HOME=/tmp \ - "$(cat "$IIDFILE")" \ + "$IMAGE" \ node tests/e2e/firefox/reinstall.js }