Skip to content
Merged
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
50 changes: 50 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Lint

# Lint runs on a cheap GitHub-hosted runner (ubuntu-latest). It is one of the
# REQUIRED status checks that gate merges into develop/master; the self-hosted
# Windows unit tier (run-unittests-windows.yml) needs lab hardware + USB drivers
# and is unavailable on forks, so it must NOT be a required check. black and
# flake8 read their config from pyproject.toml ([tool.black] / [tool.flake8] via
# Flake8-pyproject); the Black version is pinned to match .pre-commit-config.yaml
# so CI and the local pre-commit hook agree.

on:
push:
branches:
- master
- develop
pull_request:
branches:
- master
- develop

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: lint (black + flake8)
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install lint tools
# Pin Black to the version the pre-commit hook / local devs run so CI
# and pre-commit agree. flake8 reads [tool.flake8] via Flake8-pyproject.
run: |
python -m pip install --upgrade pip
pip install "black==25.9.0" flake8 Flake8-pyproject

- name: black --check
run: black --check .

- name: flake8
run: flake8 .
66 changes: 66 additions & 0 deletions .github/workflows/run-unittests-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Python Unit Tests (Windows)

# NOTE: auto-run DEACTIVATED (2026-08-04). This Windows unit-test job mirrors the
# lab target platform (PycroFlow targets Windows 10), but the required merge gate
# is now the GitHub-hosted "Unit Tests (hosted)" job (unit-tests-hosted.yml,
# ubuntu-latest) + "Lint" (lint.yml). Windows runners are slower/scarcer and this
# tier is unavailable on forks, so it must NOT be a required check that can block
# merges. Manual-trigger only for now (Actions tab -> this workflow -> "Run
# workflow"). To reactivate the automatic run, uncomment the push / pull_request
# triggers below (and keep it OUT of the required-checks list in branch
# protection).
on:
workflow_dispatch:
# push:
# branches:
# - master
# - develop
# pull_request:
# branches:
# - master
# - develop

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
unittest:
name: unittest on Windows (Python ${{ matrix.python-version }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
env:
QT_QPA_PLATFORM: offscreen

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install package with dev + gui extras
# Intentionally NOT installing [hardware] — the conftest.py mocks
# pycromanager / monet / pycobolt / nidaqmx so CI can run without
# any vendor SDKs or USB drivers. [gui] adds PyQt6 so `unittest
# discover` actually exercises the Stage 5 GUI tests (headless via
# QT_QPA_PLATFORM=offscreen) instead of skipping them — matching the
# hosted "Unit Tests (hosted)" job.
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,gui]"

- name: Run unit tests
run: python -m unittest discover -v

- name: Run pyHamilton unit tests
run: python -m unittest discover -s PycroFlow/pyHamilton -v
# Soft failure: pyHamilton tests may not exist yet.
continue-on-error: true
71 changes: 0 additions & 71 deletions .github/workflows/tests.yml

This file was deleted.

69 changes: 69 additions & 0 deletions .github/workflows/unit-tests-hosted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Unit Tests (hosted)

# Hardware-independent unit tests on a cheap GitHub-hosted runner
# (ubuntu-latest), meant to be the REQUIRED status check that gates merges into
# develop/master. The deep Windows tier ("Python Unit Tests (Windows)",
# run-unittests-windows.yml) needs the lab box with real instruments + USB
# drivers and is unavailable on forks — so it must NOT be a required check.
#
# The suite runs with no vendor SDKs: tests/__init__.py + the top-level
# conftest.py install sys.modules mocks for pycromanager / monet / pycobolt /
# nidaqmx, so no hardware, data, or network is needed.
#
# Headless setup:
# * base `pip install -e .` is wheel-only (pyserial / loguru / pyyaml /
# pydantic); the heavy instrument stack lives in the [hardware] extra and is
# intentionally NOT installed. The [dev] extra adds pytest; [gui] adds PyQt6
# (also a wheel) so the Stage 5 GUI unit tests actually run.
# * the Qt runtime libs + QT_QPA_PLATFORM=offscreen let the GUI unit tests
# import PyQt6 without a display (the package is import-safe without PyQt6;
# Qt is imported lazily).

on:
push:
branches:
- master
- develop
pull_request:
branches:
- master
- develop

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
unit-tests:
name: Unit Tests (hosted)
runs-on: ubuntu-latest
env:
QT_QPA_PLATFORM: offscreen

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install Qt runtime libraries (headless PyQt6 import)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libegl1 libgl1 libxkbcommon0 libdbus-1-3

- name: Install package with dev + gui extras
# Intentionally NOT installing [hardware] — the conftest.py mocks
# pycromanager / monet / pycobolt / nidaqmx so CI runs without any
# vendor SDKs or USB drivers. [dev] adds pytest; [gui] adds PyQt6.
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,gui]"

- name: Run unit tests
run: python -m unittest discover -v
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(line-length 79, `target-version = ["py310"]`) and `[tool.flake8]`
(`extend-ignore = E203,E501,W503` — Black owns line length), replacing the
standalone `.flake8`.
- CI runner strategy (S0A-3): required checks now run on GitHub-hosted runners.
Split the old combined `tests.yml` into hosted `lint.yml` + hosted
`unit-tests-hosted.yml` (both trigger on push/PR to `master`/`develop`), and
demoted the Windows unit tier to `run-unittests-windows.yml` triggered by
`workflow_dispatch` only so a runner-less self-hosted/Windows check can't
block merges. Branch protection should list only the hosted checks as
required.

### Added

Expand All @@ -28,6 +35,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Shared `.pre-commit-config.yaml` (pre-commit-hooks + Black + flake8 via
Flake8-pyproject), matching the rest of the DNA-PAINT stack.
- `black --check` and `flake8` lint job in CI.
- Hosted (`ubuntu-latest`) `Unit Tests (hosted)` CI job
(`unit-tests-hosted.yml`) intended as the required merge gate alongside the
hosted `Lint` job: installs Qt runtime libs, `pip install -e ".[dev,gui]"`
(base install stays wheel-only; the hardware stack is mocked), and runs the
unit suite with `QT_QPA_PLATFORM=offscreen` so the GUI tests run headlessly.
- This changelog.

### Removed
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ PYCROFLOW_UPDATE_SNAPSHOTS=1 python -m unittest PycroFlow.tests.test_regression_
```
Commit the updated JSON in `PycroFlow/tests/fixtures/snapshots/`.

CI runs `python -m unittest discover -v` on Windows / Python 3.10 (`.github/workflows/tests.yml`). There are no configured linters/formatters yet (`ruff`/`mypy` are in the `[dev]` extra).
CI runs on GitHub-hosted `ubuntu-latest` runners as the required merge gate: a `Lint` job (`black --check` + `flake8`, `.github/workflows/lint.yml`) and a `Unit Tests (hosted)` job (`python -m unittest discover -v` with the Qt runtime libs + `QT_QPA_PLATFORM=offscreen` so the GUI tests run headlessly, `.github/workflows/unit-tests-hosted.yml`), both on push/PR to `master`/`develop`. The Windows unit tier (`.github/workflows/run-unittests-windows.yml`, mirroring the lab target platform) is demoted to `workflow_dispatch` only so a scarce/forks-unavailable runner can't block merges — run it manually from the Actions tab. `ruff`/`mypy` are in the `[dev]` extra but not yet wired into CI.

### Hardware emulators (`tests/emulators/`)
Behavioral hardware fakes for tests, in three fidelity layers (vs. the import-only `MagicMock` shims in `tests/_mock_hardware.py`):
Expand Down
13 changes: 9 additions & 4 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ Commit the new JSON files in `PycroFlow/tests/fixtures/snapshots/`.

## 7. Linting / CI

GitHub Actions runs `python -m unittest discover -v` on Windows / Python
3.10 for every push and PR. See `.github/workflows/tests.yml`. The
workflow installs only `.[dev]`; vendor SDKs are mocked at test-discovery
time by `PycroFlow/tests/_mock_hardware.py`.
GitHub Actions gates every push and PR to `master`/`develop` with two
GitHub-hosted (`ubuntu-latest`) jobs: `Lint` (`black --check` + `flake8`,
`.github/workflows/lint.yml`) and `Unit Tests (hosted)`
(`python -m unittest discover -v`, `.github/workflows/unit-tests-hosted.yml`).
The hosted test job installs `.[dev,gui]` plus the Qt runtime libs and runs
with `QT_QPA_PLATFORM=offscreen`; vendor SDKs are mocked at test-discovery
time by `PycroFlow/tests/_mock_hardware.py`. The Windows tier
(`.github/workflows/run-unittests-windows.yml`) is `workflow_dispatch` only —
trigger it manually from the Actions tab.
Loading