Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
# files detected as binary untouched.
* text=auto

# Shell scripts used in Linux containers must stay LF in the working tree.
/docker/*.sh text eol=lf
/docker/ut-run-tests text eol=lf
/docker/xvfb text eol=lf
/sbin/*.sh text eol=lf

# Files and directories with the attribute export-ignore won’t be added to
# archive files. See http://git-scm.com/docs/gitattributes for details.

# Git
/.gitattributes export-ignore
/.github/ export-ignore
/.gitignore export-ignore

Expand Down
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,49 @@ so that <kbd>ctrl</kbd>+<kbd>b</kbd> would invoke the testing action.
```


### Headless container runner

To run tests without affecting your interactive Sublime Text session,
use the bundled `docker/ut-run-tests` launcher.

```sh
# run all tests from current package root
/path/to/UnitTesting/docker/ut-run-tests .

# run just one test file (faster)
/path/to/UnitTesting/docker/ut-run-tests . --file tests/test_example.py
```

If `UnitTesting/docker` is on your `PATH`, you can simply run:

```sh
ut-run-tests .
```

This launcher calls `docker/run_tests.py`, which runs tests in a Docker
container (headless), streams output to stdout/stderr and keeps a cache
volume so repeated runs are fast.

Useful options:

- `--file tests/test_foo.py`
- `--pattern test_foo.py --tests-dir tests/subdir`
- `--coverage`
- `--failfast`
- `--reload-package-on-testing` (default: off)
- `--scheduler-delay-ms 0` (default)
- `--dry-run` (only print runtime metadata and schedule)
- `--refresh-cache` (re-bootstrap cached `/root` state)
- `--refresh-image` (rebuild local Docker image)
- `--refresh` (both cache and image refresh)
- `--no-cache-volume` (run without persistent cache)

> [!TIP]
>
> This is useful for editor build systems and for AI agents,
> because test runs no longer commandeer your active editor window.


## GitHub Actions

Unittesting provides the following GitHub Actions, which can be combined
Expand Down
14 changes: 6 additions & 8 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ FROM ubuntu:latest
USER root
RUN apt-get update
RUN apt-get install --no-install-recommends sudo apt-utils -y
RUN apt-get install --no-install-recommends python software-properties-common -y
RUN apt-get install --no-install-recommends git curl xvfb -y
RUN apt-get install --no-install-recommends python3 python-is-python3 software-properties-common -y
RUN apt-get install --no-install-recommends git curl xvfb rsync -y
RUN apt-get install --no-install-recommends libglib2.0-0 libgtk-3-0 -y
RUN apt-get install --no-install-recommends psmisc -y
RUN apt-get install --no-install-recommends locales locales-all -y
Expand All @@ -15,17 +15,15 @@ RUN if [ "$arch" = "i386" ]; then apt-get update; fi
RUN if [ "$arch" = "i386" ]; then apt-get install --no-install-recommends libc6:i386 libncurses5:i386 libstdc++6:i386 -y; fi
RUN if [ "$arch" = "i386" ]; then apt-get install --no-install-recommends libglib2.0-0:i386 libgtk-3-0:i386 libx11-6:i386 -y; fi

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8

ENV DISPLAY=:1
COPY xvfb /etc/init.d/xvfb
RUN chmod +x /etc/init.d/xvfb
COPY docker.sh /docker.sh
RUN chmod +x /docker.sh
COPY entrypoint.sh /entrypoint.sh
RUN chmod 666 /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN sed -i 's/\r$//' /etc/init.d/xvfb /docker.sh /entrypoint.sh
RUN chmod +x /etc/init.d/xvfb /docker.sh /entrypoint.sh

WORKDIR /project
ENTRYPOINT ["/entrypoint.sh"]
99 changes: 91 additions & 8 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,99 @@
# Docker image for Sublime Text UnitTesting

## Recommended usage

Use the launcher script:

```sh
# from UnitTesting repo root
./docker/ut-run-tests /path/to/package
./docker/ut-run-tests /path/to/package --file tests/test_example.py
```

Or call it via absolute path from any package directory:

```sh
/path/to/UnitTesting/docker/ut-run-tests .
```

If this directory is on your `PATH`, you can run `ut-run-tests` directly.

The launcher calls `docker/run_tests.py`, builds/uses a local image,
mounts the package at `/project`, runs tests headlessly, and keeps a cache
volume for fast reruns.

By default it:

- builds `unittesting-local` image from `./docker` if missing
- mounts your repo as `/project`
- runs UnitTesting through the same CI shell entrypoints
- stores Sublime install/cache in docker volume `unittesting-home`
- synchronizes only changed files into `Packages/<Package>` using `rsync`

## Manual docker usage

## From Docker Hub
```sh
# cd to package
docker run --rm -it -e PACKAGE=$PACKAGE -v $PWD:/project sublimetext/unittesting
# build from UnitTesting/docker
docker build -t unittesting-local .

# run from package root
docker run --rm -it \
-e PACKAGE=$PACKAGE \
-v $PWD:/project \
-v unittesting-home:/root \
unittesting-local run_tests
```

## Build image from scratch
## Fast reruns

The container entrypoint writes a marker in `/root/.cache/unittesting`.
With `-v unittesting-home:/root`, bootstrap/install runs once and later runs
only refresh your package files and execute tests.

## Refresh/update controls (without direct docker commands)

Use launcher flags instead of calling `docker` manually:

- `--refresh-cache`: recreate `unittesting-home` cache volume (forces fresh
bootstrap, including Sublime Text/Package Control install path)
- `--refresh-image`: rebuild local image (for Dockerfile/entrypoint changes)
- `--refresh`: both `--refresh-cache` and `--refresh-image`

Examples:

```sh
ut-run-tests . --refresh-image
ut-run-tests . --refresh-cache
ut-run-tests . --refresh
```

## Dry run (metadata and schedule only)

Use `--dry-run` to print runner metadata (including detected Sublime
Text and Package Control versions) plus the generated schedule.

```sh
ut-run-tests . --dry-run
```

## Colored output

Use `--color` to control ANSI colors in test output:

- `--color auto` (default): color only when stdout is a TTY
- `--color always`: force color
- `--color never`: disable color

```sh
ut-run-tests . --color always
```

## Run a single test file

```sh
# cd to UnitTesting/docker
docker build -t unittesting .
# cd to package
docker run --rm -it -e PACKAGE=$PACKAGE -v $PWD:/project unittesting
docker run --rm -it \
-e PACKAGE=$PACKAGE \
-v $PWD:/project \
-v unittesting-home:/root \
unittesting-local run_tests --tests-dir tests --pattern test_example.py
```
7 changes: 6 additions & 1 deletion docker/docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ set -e

BASEDIR=`dirname $0`

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

Expand Down
86 changes: 83 additions & 3 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,87 @@ echo PACKAGE = $PACKAGE
set -e

PATH="$HOME/.local/bin:$PATH"
BOOTSTRAP_MARKER="$HOME/.cache/unittesting/bootstrap.done"

ensure_git_identity() {
# Align local container behavior with typical CI runners where git
# identity is configured for tests that create commits.
if ! git config --global user.name >/dev/null 2>&1; then
git config --global user.name "${UNITTESTING_GIT_USER_NAME:-UnitTesting CI}"
fi

if ! git config --global user.email >/dev/null 2>&1; then
git config --global user.email "${UNITTESTING_GIT_USER_EMAIL:-unittesting@example.invalid}"
fi
}

ensure_ci_platform_compat() {
# Some test suites key off Travis-style OS markers while others expect
# GitHub Actions' RUNNER_OS naming.
local travis_name=""
local runner_name=""

case "$(uname -s)" in
Linux*)
travis_name="linux"
runner_name="Linux"
;;
Darwin*)
travis_name="osx"
runner_name="macOS"
;;
CYGWIN*|MINGW*|MSYS*)
travis_name="windows"
runner_name="Windows"
;;
esac

if [ -n "$travis_name" ] && [ -z "$TRAVIS_OS_NAME" ]; then
export TRAVIS_OS_NAME="$travis_name"
fi

if [ -n "$runner_name" ] && [ -z "$RUNNER_OS" ]; then
export RUNNER_OS="$runner_name"
fi
}

sudo sh -e /etc/init.d/xvfb start
/docker.sh bootstrap
/docker.sh install_package_control
/docker.sh run_tests --coverage
ensure_git_identity
ensure_ci_platform_compat

UNITTESTING_SOURCE=${UNITTESTING_SOURCE:-/unittesting}
SUBLIME_TEXT_VERSION=${SUBLIME_TEXT_VERSION:-4}
if [ "$SUBLIME_TEXT_VERSION" -ge 4 ]; then
ST_PACKAGES_DIR="$HOME/.config/sublime-text/Packages"
else
ST_PACKAGES_DIR="$HOME/.config/sublime-text-$SUBLIME_TEXT_VERSION/Packages"
fi

if [ -d "$UNITTESTING_SOURCE/sbin" ]; then
# Ensure UnitTesting comes from the local checkout running this script,
# so first runs do not depend on tagged upstream releases.
(cd "$UNITTESTING_SOURCE" && PACKAGE=UnitTesting /docker.sh copy_tested_package overwrite)

# Normalize CRLF in shell scripts copied from Windows workspaces.
if [ -d "$ST_PACKAGES_DIR/UnitTesting/sbin" ]; then
find "$ST_PACKAGES_DIR/UnitTesting/sbin" -type f -name "*.sh" -exec sed -i 's/\r$//' {} +
fi
fi

if [ ! -f "$BOOTSTRAP_MARKER" ]; then
# Bootstrap from a neutral cwd to avoid Windows worktree .git indirection
# paths breaking git commands inside the Linux container.
(cd /tmp && /docker.sh bootstrap skip_package_copy)
/docker.sh install_package_control
mkdir -p "$(dirname "$BOOTSTRAP_MARKER")"
touch "$BOOTSTRAP_MARKER"
fi

# Always refresh checked-out package into Packages/<PACKAGE>
/docker.sh copy_tested_package overwrite

if [ "$#" -gt 0 ]; then
/docker.sh "$@"
else
/docker.sh run_tests --coverage
fi
Loading
Loading