Skip to content

Commit 56afd35

Browse files
authored
Merge pull request #283 from kaste/local-runner
Revive docker and add script for local headless test runs
2 parents 52dcca0 + 3aa4dfc commit 56afd35

16 files changed

Lines changed: 930 additions & 63 deletions

.gitattributes

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
# files detected as binary untouched.
33
* text=auto
44

5+
# Shell scripts used in Linux containers must stay LF in the working tree.
6+
/docker/*.sh text eol=lf
7+
/docker/ut-run-tests text eol=lf
8+
/docker/xvfb text eol=lf
9+
/sbin/*.sh text eol=lf
10+
511
# Files and directories with the attribute export-ignore won’t be added to
612
# archive files. See http://git-scm.com/docs/gitattributes for details.
713

814
# Git
9-
/.gitattributes export-ignore
1015
/.github/ export-ignore
1116
/.gitignore export-ignore
1217

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,49 @@ so that <kbd>ctrl</kbd>+<kbd>b</kbd> would invoke the testing action.
112112
```
113113

114114

115+
### Headless container runner
116+
117+
To run tests without affecting your interactive Sublime Text session,
118+
use the bundled `docker/ut-run-tests` launcher.
119+
120+
```sh
121+
# run all tests from current package root
122+
/path/to/UnitTesting/docker/ut-run-tests .
123+
124+
# run just one test file (faster)
125+
/path/to/UnitTesting/docker/ut-run-tests . --file tests/test_example.py
126+
```
127+
128+
If `UnitTesting/docker` is on your `PATH`, you can simply run:
129+
130+
```sh
131+
ut-run-tests .
132+
```
133+
134+
This launcher calls `docker/run_tests.py`, which runs tests in a Docker
135+
container (headless), streams output to stdout/stderr and keeps a cache
136+
volume so repeated runs are fast.
137+
138+
Useful options:
139+
140+
- `--file tests/test_foo.py`
141+
- `--pattern test_foo.py --tests-dir tests/subdir`
142+
- `--coverage`
143+
- `--failfast`
144+
- `--reload-package-on-testing` (default: off)
145+
- `--scheduler-delay-ms 0` (default)
146+
- `--dry-run` (only print runtime metadata and schedule)
147+
- `--refresh-cache` (re-bootstrap cached `/root` state)
148+
- `--refresh-image` (rebuild local Docker image)
149+
- `--refresh` (both cache and image refresh)
150+
- `--no-cache-volume` (run without persistent cache)
151+
152+
> [!TIP]
153+
>
154+
> This is useful for editor build systems and for AI agents,
155+
> because test runs no longer commandeer your active editor window.
156+
157+
115158
## GitHub Actions
116159

117160
Unittesting provides the following GitHub Actions, which can be combined

docker/Dockerfile

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ FROM ubuntu:latest
33
USER root
44
RUN apt-get update
55
RUN apt-get install --no-install-recommends sudo apt-utils -y
6-
RUN apt-get install --no-install-recommends python software-properties-common -y
7-
RUN apt-get install --no-install-recommends git curl xvfb -y
6+
RUN apt-get install --no-install-recommends python3 python-is-python3 software-properties-common -y
7+
RUN apt-get install --no-install-recommends git curl xvfb rsync -y
88
RUN apt-get install --no-install-recommends libglib2.0-0 libgtk-3-0 -y
99
RUN apt-get install --no-install-recommends psmisc -y
1010
RUN apt-get install --no-install-recommends locales locales-all -y
@@ -15,17 +15,15 @@ RUN if [ "$arch" = "i386" ]; then apt-get update; fi
1515
RUN if [ "$arch" = "i386" ]; then apt-get install --no-install-recommends libc6:i386 libncurses5:i386 libstdc++6:i386 -y; fi
1616
RUN if [ "$arch" = "i386" ]; then apt-get install --no-install-recommends libglib2.0-0:i386 libgtk-3-0:i386 libx11-6:i386 -y; fi
1717

18-
ENV LANG en_US.UTF-8
19-
ENV LANGUAGE en_US.UTF-8
18+
ENV LANG=en_US.UTF-8
19+
ENV LANGUAGE=en_US.UTF-8
2020

2121
ENV DISPLAY=:1
2222
COPY xvfb /etc/init.d/xvfb
23-
RUN chmod +x /etc/init.d/xvfb
2423
COPY docker.sh /docker.sh
25-
RUN chmod +x /docker.sh
2624
COPY entrypoint.sh /entrypoint.sh
27-
RUN chmod 666 /entrypoint.sh
28-
RUN chmod +x /entrypoint.sh
25+
RUN sed -i 's/\r$//' /etc/init.d/xvfb /docker.sh /entrypoint.sh
26+
RUN chmod +x /etc/init.d/xvfb /docker.sh /entrypoint.sh
2927

3028
WORKDIR /project
3129
ENTRYPOINT ["/entrypoint.sh"]

docker/README.md

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,99 @@
11
# Docker image for Sublime Text UnitTesting
22

3+
## Recommended usage
4+
5+
Use the launcher script:
6+
7+
```sh
8+
# from UnitTesting repo root
9+
./docker/ut-run-tests /path/to/package
10+
./docker/ut-run-tests /path/to/package --file tests/test_example.py
11+
```
12+
13+
Or call it via absolute path from any package directory:
14+
15+
```sh
16+
/path/to/UnitTesting/docker/ut-run-tests .
17+
```
18+
19+
If this directory is on your `PATH`, you can run `ut-run-tests` directly.
20+
21+
The launcher calls `docker/run_tests.py`, builds/uses a local image,
22+
mounts the package at `/project`, runs tests headlessly, and keeps a cache
23+
volume for fast reruns.
24+
25+
By default it:
26+
27+
- builds `unittesting-local` image from `./docker` if missing
28+
- mounts your repo as `/project`
29+
- runs UnitTesting through the same CI shell entrypoints
30+
- stores Sublime install/cache in docker volume `unittesting-home`
31+
- synchronizes only changed files into `Packages/<Package>` using `rsync`
32+
33+
## Manual docker usage
334

4-
## From Docker Hub
535
```sh
6-
# cd to package
7-
docker run --rm -it -e PACKAGE=$PACKAGE -v $PWD:/project sublimetext/unittesting
36+
# build from UnitTesting/docker
37+
docker build -t unittesting-local .
38+
39+
# run from package root
40+
docker run --rm -it \
41+
-e PACKAGE=$PACKAGE \
42+
-v $PWD:/project \
43+
-v unittesting-home:/root \
44+
unittesting-local run_tests
845
```
946

10-
## Build image from scratch
47+
## Fast reruns
48+
49+
The container entrypoint writes a marker in `/root/.cache/unittesting`.
50+
With `-v unittesting-home:/root`, bootstrap/install runs once and later runs
51+
only refresh your package files and execute tests.
52+
53+
## Refresh/update controls (without direct docker commands)
54+
55+
Use launcher flags instead of calling `docker` manually:
56+
57+
- `--refresh-cache`: recreate `unittesting-home` cache volume (forces fresh
58+
bootstrap, including Sublime Text/Package Control install path)
59+
- `--refresh-image`: rebuild local image (for Dockerfile/entrypoint changes)
60+
- `--refresh`: both `--refresh-cache` and `--refresh-image`
61+
62+
Examples:
63+
64+
```sh
65+
ut-run-tests . --refresh-image
66+
ut-run-tests . --refresh-cache
67+
ut-run-tests . --refresh
68+
```
69+
70+
## Dry run (metadata and schedule only)
71+
72+
Use `--dry-run` to print runner metadata (including detected Sublime
73+
Text and Package Control versions) plus the generated schedule.
74+
75+
```sh
76+
ut-run-tests . --dry-run
77+
```
78+
79+
## Colored output
80+
81+
Use `--color` to control ANSI colors in test output:
82+
83+
- `--color auto` (default): color only when stdout is a TTY
84+
- `--color always`: force color
85+
- `--color never`: disable color
86+
87+
```sh
88+
ut-run-tests . --color always
89+
```
90+
91+
## Run a single test file
92+
1193
```sh
12-
# cd to UnitTesting/docker
13-
docker build -t unittesting .
14-
# cd to package
15-
docker run --rm -it -e PACKAGE=$PACKAGE -v $PWD:/project unittesting
94+
docker run --rm -it \
95+
-e PACKAGE=$PACKAGE \
96+
-v $PWD:/project \
97+
-v unittesting-home:/root \
98+
unittesting-local run_tests --tests-dir tests --pattern test_example.py
1699
```

docker/docker.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ set -e
44

55
BASEDIR=`dirname $0`
66

7+
UNITTESTING_SOURCE=${UNITTESTING_SOURCE:-/unittesting}
8+
SOURCE_CISH="$UNITTESTING_SOURCE/sbin/ci.sh"
79
CISH="/tmp/ci.sh"
8-
if [ ! -f "$CISH" ]; then
10+
if [ -f "$SOURCE_CISH" ]; then
11+
# Normalize CRLF from mounted Windows checkouts.
12+
sed 's/\r$//' "$SOURCE_CISH" > "$CISH"
13+
elif [ ! -f "$CISH" ]; then
914
curl -s -L https://raw.githubusercontent.com/SublimeText/UnitTesting/master/sbin/ci.sh -o "$CISH"
1015
fi
1116

docker/entrypoint.sh

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,87 @@ echo PACKAGE = $PACKAGE
55
set -e
66

77
PATH="$HOME/.local/bin:$PATH"
8+
BOOTSTRAP_MARKER="$HOME/.cache/unittesting/bootstrap.done"
9+
10+
ensure_git_identity() {
11+
# Align local container behavior with typical CI runners where git
12+
# identity is configured for tests that create commits.
13+
if ! git config --global user.name >/dev/null 2>&1; then
14+
git config --global user.name "${UNITTESTING_GIT_USER_NAME:-UnitTesting CI}"
15+
fi
16+
17+
if ! git config --global user.email >/dev/null 2>&1; then
18+
git config --global user.email "${UNITTESTING_GIT_USER_EMAIL:-unittesting@example.invalid}"
19+
fi
20+
}
21+
22+
ensure_ci_platform_compat() {
23+
# Some test suites key off Travis-style OS markers while others expect
24+
# GitHub Actions' RUNNER_OS naming.
25+
local travis_name=""
26+
local runner_name=""
27+
28+
case "$(uname -s)" in
29+
Linux*)
30+
travis_name="linux"
31+
runner_name="Linux"
32+
;;
33+
Darwin*)
34+
travis_name="osx"
35+
runner_name="macOS"
36+
;;
37+
CYGWIN*|MINGW*|MSYS*)
38+
travis_name="windows"
39+
runner_name="Windows"
40+
;;
41+
esac
42+
43+
if [ -n "$travis_name" ] && [ -z "$TRAVIS_OS_NAME" ]; then
44+
export TRAVIS_OS_NAME="$travis_name"
45+
fi
46+
47+
if [ -n "$runner_name" ] && [ -z "$RUNNER_OS" ]; then
48+
export RUNNER_OS="$runner_name"
49+
fi
50+
}
51+
852
sudo sh -e /etc/init.d/xvfb start
9-
/docker.sh bootstrap
10-
/docker.sh install_package_control
11-
/docker.sh run_tests --coverage
53+
ensure_git_identity
54+
ensure_ci_platform_compat
55+
56+
UNITTESTING_SOURCE=${UNITTESTING_SOURCE:-/unittesting}
57+
SUBLIME_TEXT_VERSION=${SUBLIME_TEXT_VERSION:-4}
58+
if [ "$SUBLIME_TEXT_VERSION" -ge 4 ]; then
59+
ST_PACKAGES_DIR="$HOME/.config/sublime-text/Packages"
60+
else
61+
ST_PACKAGES_DIR="$HOME/.config/sublime-text-$SUBLIME_TEXT_VERSION/Packages"
62+
fi
63+
64+
if [ -d "$UNITTESTING_SOURCE/sbin" ]; then
65+
# Ensure UnitTesting comes from the local checkout running this script,
66+
# so first runs do not depend on tagged upstream releases.
67+
(cd "$UNITTESTING_SOURCE" && PACKAGE=UnitTesting /docker.sh copy_tested_package overwrite)
68+
69+
# Normalize CRLF in shell scripts copied from Windows workspaces.
70+
if [ -d "$ST_PACKAGES_DIR/UnitTesting/sbin" ]; then
71+
find "$ST_PACKAGES_DIR/UnitTesting/sbin" -type f -name "*.sh" -exec sed -i 's/\r$//' {} +
72+
fi
73+
fi
74+
75+
if [ ! -f "$BOOTSTRAP_MARKER" ]; then
76+
# Bootstrap from a neutral cwd to avoid Windows worktree .git indirection
77+
# paths breaking git commands inside the Linux container.
78+
(cd /tmp && /docker.sh bootstrap skip_package_copy)
79+
/docker.sh install_package_control
80+
mkdir -p "$(dirname "$BOOTSTRAP_MARKER")"
81+
touch "$BOOTSTRAP_MARKER"
82+
fi
83+
84+
# Always refresh checked-out package into Packages/<PACKAGE>
85+
/docker.sh copy_tested_package overwrite
86+
87+
if [ "$#" -gt 0 ]; then
88+
/docker.sh "$@"
89+
else
90+
/docker.sh run_tests --coverage
91+
fi

0 commit comments

Comments
 (0)