test(family): check that a clocked part's reset chains #85
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| jobs: | |
| lint: | |
| name: Format and lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install ruff | |
| run: pipx install ruff==0.16.3 | |
| - name: Check formatting | |
| run: ruff format --check . | |
| - name: Lint | |
| run: ruff check --output-format=github . | |
| - uses: pnpm/action-setup@v6 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - name: Install the formatter | |
| run: pnpm install --frozen-lockfile --ignore-scripts | |
| - name: Check that every JSON file is formatted | |
| run: pnpm run format:check | |
| # The workflows are the one thing here nothing else checks, and a mistake | |
| # in them is only visible once a run has already gone wrong. The archive | |
| # is verified by digest before anything in it runs, for the same reason | |
| # every image this project loads is. | |
| - name: Check the workflows themselves | |
| run: | | |
| curl -fsSL -o actionlint.tar.gz \ | |
| "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | |
| echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c - | |
| tar -xzf actionlint.tar.gz actionlint | |
| ./actionlint -color | |
| rm -f actionlint actionlint.tar.gz | |
| shell: bash | |
| env: | |
| ACTIONLINT_VERSION: "1.7.12" | |
| ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" | |
| types: | |
| name: Types | |
| runs-on: ubuntu-latest | |
| steps: | |
| # The processor is reached through a submodule, and the checker follows the | |
| # import to type it. Without the checkout it would report the whole backend | |
| # as missing rather than as wrong, which is a different and useless answer. | |
| - uses: actions/checkout@v7 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install the type checker | |
| run: pipx install mypy==1.14.1 | |
| # Strict, with every optional error class the version offers turned on. The | |
| # settings live in pyproject.toml so a local run and this one agree; a gate | |
| # that only holds in CI teaches people to push and find out. | |
| - name: Every annotation has to hold | |
| run: mypy | |
| test: | |
| name: Tests on Python ${{ matrix.python }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.12", "3.13", "3.14"] | |
| steps: | |
| # The package reaches the processor through this submodule, and without it | |
| # the microcode backend cannot be built at all. Checking it out here does | |
| # not supply any firmware, so the runner still exercises the path a user | |
| # without an image takes. | |
| - uses: actions/checkout@v7 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Install coverage | |
| run: python -m pip install --disable-pip-version-check coverage==7.6.10 | |
| - name: Run every test file, under coverage | |
| run: | | |
| python -m coverage erase | |
| status=0 | |
| while IFS= read -r file; do | |
| echo "::group::${file}" | |
| python -m coverage run -a "${file}" || status=1 | |
| echo "::endgroup::" | |
| done < <(find snesdsp conformance -name '*.test.py' | sort) | |
| exit "${status}" | |
| shell: bash | |
| - name: Coverage must be total | |
| run: python -m coverage report | |
| speed: | |
| name: Throughput | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: The part must still run faster than the floor | |
| run: python -m conformance.speed | |
| quotes: | |
| name: Every quoted sentence, against the document it came from | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| # No document is redistributable, so none is here and this checks nothing | |
| # on a runner. It says so rather than reporting a pass: the value is that | |
| # somebody running it with the data sheet on disk gets a real answer, and | |
| # that a run without it cannot be mistaken for one. | |
| - name: Every quoted sentence, against the document it came from | |
| run: python -m conformance.quotes | |
| firmware: | |
| name: Against the parts, when any microcode is present | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| # This runs the program a cartridge carries, which belongs to whoever | |
| # wrote it, so on a machine without one it says so and stops rather than | |
| # reporting a pass. Everything a machine without one can check runs in the | |
| # test job above: this package has no behaviour of its own to get wrong. | |
| # Exit 2 is the runner saying it had nothing to run, which is the honest | |
| # answer to a person who asked it to drive a part and holds no image. It is | |
| # not a failure of this build, so it is caught here rather than by making | |
| # the tool lie about it. | |
| - name: Drive the parts with the exchanges real cartridges make | |
| run: | | |
| set +e | |
| status=0 | |
| for part in dsp1 dsp2 dsp3 dsp4; do | |
| echo "::group::${part}" | |
| python -m conformance.against_cartridges "${part}" | tee -a report.txt | |
| here=${PIPESTATUS[0]} | |
| echo "::endgroup::" | |
| [ "${here}" -eq 0 ] || status="${here}" | |
| done | |
| set -e | |
| if [ "${status}" -eq 2 ]; then | |
| echo "::notice title=No microcode present::The checks that run a part's own program did not run. They are not failures; they had nothing to run." | |
| exit 0 | |
| fi | |
| exit "${status}" | |
| shell: bash | |
| # What each part answers, re-derived and compared against what it answered | |
| # when the corpus was taken. The microcode makes the values right; this | |
| # catches the layers around it changing them, which nothing else would. | |
| - name: Every part still answers what it answered | |
| run: | | |
| set +e | |
| python -m conformance.answers | |
| status=$? | |
| set -e | |
| if [ "${status}" -eq 2 ]; then | |
| echo "::notice title=No microcode present::The recorded answers were not re-derived. They are not failures; there was nothing to run." | |
| exit 0 | |
| fi | |
| exit "${status}" | |
| shell: bash | |
| # The DSP-1B corrected an arithmetic fault in the DSP-1. That is a claim | |
| # with a consequence, and this re-derives the inputs where the two masks | |
| # actually part company rather than taking the claim on trust. | |
| - name: The DSP-1B correction is still where it was | |
| run: | | |
| set +e | |
| python -m conformance.masks | |
| status=$? | |
| set -e | |
| if [ "${status}" -eq 2 ]; then | |
| echo "::notice title=Both DSP-1 masks not present::The mask comparison needs an image for the DSP-1 and the DSP-1B. It did not run." | |
| exit 0 | |
| fi | |
| exit "${status}" | |
| shell: bash | |
| # Every snippet the README prints, run against the parts it names. A | |
| # number in a document is a claim about the code, and this is what keeps | |
| # the two from drifting apart. Same exit 2 rule as above. | |
| - name: Every example in the README still gives what it says | |
| run: | | |
| set +e | |
| python -m conformance.documented | |
| status=$? | |
| set -e | |
| if [ "${status}" -eq 2 ]; then | |
| echo "::notice title=No microcode present::The README examples were not run. They are not failures; they had nothing to run." | |
| exit 0 | |
| fi | |
| exit "${status}" | |
| shell: bash |