Skip to content

Integration Tests

Integration Tests #83

Workflow file for this run

name: Integration Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_run:
workflows: ["Build and Push Images", "Build and Push TSan Image"]
types: [completed]
workflow_dispatch:
# Container jobs run as root (required by actions/checkout), so we must
# point opam at the vscode user's opam root and add its bin to PATH.
env:
OPAMROOT: /home/vscode/.opam
jobs:
# ============================================================================
# Test OCaml compiler and tools (single "ocaml" switch)
# ============================================================================
test-ocaml:
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
runner: [ubuntu-latest]
# Uncomment to add ARM runners:
# runner: [ubuntu-latest, ubuntu-24.04-arm]
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run OCaml tests
run: |
./test/test-ocaml.sh ocaml
# ============================================================================
# Test LSP server
# ============================================================================
test-lsp:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run LSP tests
run: |
./test/test-lsp.sh ocaml
# ============================================================================
# Test profiling tools
# ============================================================================
test-profiling:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run profiling tests
run: |
./test/test-profiling.sh ocaml
# ============================================================================
# Test OCaml TSan image (matrix: ocaml + ocaml+tsan switches)
# ============================================================================
test-ocaml-tsan:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
switch: ['ocaml', 'ocaml+tsan']
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer-tsan:latest
options: >-
--user root
${{ contains(matrix.switch, 'tsan') && '--privileged' || '' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Configure ASLR for TSan
if: contains(matrix.switch, 'tsan')
run: sysctl -w vm.mmap_rnd_bits=28
- name: Run OCaml tests
run: |
./test/test-ocaml.sh ${{ matrix.switch }}
test-lsp-tsan:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
switch: ['ocaml', 'ocaml+tsan']
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer-tsan:latest
options: >-
--user root
${{ contains(matrix.switch, 'tsan') && '--privileged' || '' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Configure ASLR for TSan
if: contains(matrix.switch, 'tsan')
run: sysctl -w vm.mmap_rnd_bits=28
- name: Run LSP tests
run: |
./test/test-lsp.sh ${{ matrix.switch }}
test-profiling-tsan:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
switch: ['ocaml', 'ocaml+tsan']
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer-tsan:latest
options: >-
--user root
${{ contains(matrix.switch, 'tsan') && '--privileged' || '' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Configure ASLR for TSan
if: contains(matrix.switch, 'tsan')
run: sysctl -w vm.mmap_rnd_bits=28
- name: Run profiling tests
run: |
./test/test-profiling.sh ${{ matrix.switch }}
# ============================================================================
# Test dune package management
# ============================================================================
test-dune-pkg:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run dune pkg tests
run: |
./test/test-dune-pkg.sh
# ============================================================================
# Test editor integrations
# ============================================================================
test-vscode:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install devcontainer CLI
run: npm install -g @devcontainers/cli
- name: Validate .devcontainer/devcontainer.json
run: devcontainer read-configuration --workspace-folder .
- name: Validate .devcontainer-from-scratch/devcontainer.json
run: devcontainer read-configuration --workspace-folder . --config .devcontainer-from-scratch/devcontainer.json
- name: Validate .devcontainer-tsan/devcontainer.json
run: devcontainer read-configuration --workspace-folder . --config .devcontainer-tsan/devcontainer.json
- name: Validate .devcontainer-tsan-from-scratch/devcontainer.json
run: devcontainer read-configuration --workspace-folder . --config .devcontainer-tsan-from-scratch/devcontainer.json
- name: Run VS Code tests
run: |
./test/test-vscode.sh
test-emacs:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run Emacs tests
run: |
./test/test-emacs.sh
test-neovim:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run Neovim tests
run: |
./test/test-neovim.sh
test-claude:
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository_owner }}/ocaml-devcontainer:latest
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Run Claude Code tests
run: |
./test/test-claude.sh
# ============================================================================
# Functional test: spin up devcontainer and run smoke tests
# ============================================================================
test-devcontainer:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build and run devcontainer
uses: devcontainers/ci@v0.3
with:
runCmd: |
ocaml -version
dune --version
opam switch list
cd examples/hello && dune build && dune exec ./hello.exe
push: never
# ============================================================================
# Test devcontainer workflow with Podman (smoke test)
# ============================================================================
test-podman:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Replace Docker with Podman
run: |
# Stop Docker (mirrors a system without Docker installed)
sudo systemctl disable --now docker.service docker.socket containerd.service
sudo rm /usr/bin/docker
# Alias podman as docker (same as macOS Homebrew setup)
sudo ln -s /usr/bin/podman /usr/bin/docker
# Start Podman API socket for devcontainer CLI
sudo systemctl start podman.socket
echo "DOCKER_HOST=unix:///run/podman/podman.sock" >> "$GITHUB_ENV"
- name: Verify Podman as Docker
run: |
docker --version
docker info
- name: Install devcontainer CLI
run: npm install -g @devcontainers/cli
- name: Fix workspace permissions for Podman
run: |
# Rootful Podman maps UIDs 1:1; the container's vscode user (UID 1000)
# needs write access to the workspace owned by runner (UID 1001).
# Docker's updateRemoteUserUID handles this automatically but Podman
# does not, so we open permissions on the workspace.
chmod -R a+rwX .
- name: Start devcontainer with Podman
run: devcontainer up --workspace-folder .
- name: Run smoke tests
run: |
devcontainer exec --workspace-folder . ocaml -version
devcontainer exec --workspace-folder . dune --version
devcontainer exec --workspace-folder . opam switch list
devcontainer exec --workspace-folder . bash -c \
'cd examples/hello && dune build && dune exec ./hello.exe'
- name: Clean up
if: always()
run: |
docker stop $(docker ps -q) 2>/dev/null || true
# ============================================================================
# Summary job (for required status checks)
# ============================================================================
test-summary:
runs-on: ubuntu-latest
needs:
- test-ocaml
- test-lsp
- test-profiling
- test-ocaml-tsan
- test-lsp-tsan
- test-profiling-tsan
- test-dune-pkg
- test-vscode
- test-emacs
- test-neovim
- test-claude
- test-devcontainer
- test-podman
if: always()
steps:
- name: Check test results
run: |
if [ "${{ needs.test-ocaml.result }}" != "success" ]; then
echo "test-ocaml failed"
exit 1
fi
if [ "${{ needs.test-lsp.result }}" != "success" ]; then
echo "test-lsp failed"
exit 1
fi
if [ "${{ needs.test-devcontainer.result }}" != "success" ]; then
echo "test-devcontainer failed"
exit 1
fi
if [ "${{ needs.test-podman.result }}" != "success" ]; then
echo "::warning::test-podman failed (Podman support is best-effort)"
fi
# TSan image tests are best-effort (image may not exist yet)
if [ "${{ needs.test-ocaml-tsan.result }}" != "success" ]; then
echo "::warning::test-ocaml-tsan failed (TSan image may not be available)"
fi
echo "All critical tests passed"