test(readme): drive the succeeding-example arc rather than inherit it #114
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 and YAML 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 document this project reads 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" | |
| - name: Lint the shell scripts | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y shellcheck | |
| shellcheck --severity=style --shell=bash scripts/*.sh | |
| types: | |
| name: Types | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - 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. A processor | |
| # core is masked integers, flag bits and addressing modes end to end, and a | |
| # width crossing a boundary it should not is exactly what this catches. The | |
| # settings live in pyproject.toml so a local run and this one agree. | |
| - 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: | |
| - uses: actions/checkout@v7 | |
| - 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 mos65xx conformance -name '*.test.py' | sort) | |
| exit "${status}" | |
| shell: bash | |
| - name: Coverage must be total | |
| run: python -m coverage report | |
| # 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 documents on disk gets a real answer, and | |
| # that a run without them cannot be mistaken for one. | |
| - name: Every quoted sentence, against the document it came from | |
| run: python -m conformance.quotes | |
| # Uninstrumented, and deliberately after the coverage run rather than | |
| # inside it. A coverage tracer costs about ten times what the model does, so | |
| # a throughput check under one measures the tracer. | |
| - name: The model has not become several times slower | |
| run: python -m conformance.speed | |
| # Opt-in, and silent when nothing is there. A transistor level simulation | |
| # of the die answers what no data sheet states and no recording carries, | |
| # but it is somebody else's work under their own licence, so a copy belongs | |
| # on the machine that runs it rather than in this repository or this job. | |
| - name: The model against a simulation of the die, when one is present | |
| run: python -m conformance.netlist | |
| conformance: | |
| name: Conformance against SingleStepTests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| # One key for every suite, taken from the file that pins them all, so the | |
| # two jobs that need them share one entry rather than filling the cache | |
| # with the same gigabytes twice. | |
| - name: Cache the suites | |
| id: cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ~/.cache/conformance-suites | |
| key: suites-${{ hashFiles('conformance/suites.json') }} | |
| - name: Fetch the suite | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: python -m conformance.fetch ~/.cache/conformance-suites | |
| # A pull request runs a tenth of each suite for a quick answer. A push to | |
| # main runs every case, because the number of them is what the claim of | |
| # conformance rests on, and the claim covers every part rather than one. | |
| - name: Run every suite | |
| run: | | |
| if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then | |
| limit=1000 | |
| else | |
| limit=0 | |
| fi | |
| status=0 | |
| while read -r suite model; do | |
| echo "::group::${suite} as a ${model}" | |
| python -m conformance.singlestep \ | |
| "${HOME}/.cache/conformance-suites/${suite}" "${limit}" --model "${model}" || status=1 | |
| echo "::endgroup::" | |
| done <<'SUITES' | |
| 65816/65816/v1 65816 | |
| 6502/6502/v1 6502 | |
| nes6502/nes6502/v1 2a03 | |
| synertek65c02/synertek65c02/v1 65c02 | |
| rockwell65c02/rockwell65c02/v1 r65c02 | |
| wdc65c02/wdc65c02/v1 w65c02 | |
| SUITES | |
| exit "${status}" | |
| shell: bash | |
| cycles: | |
| name: Cycle for cycle against SingleStepTests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| # One key for every suite, taken from the file that pins them all, so the | |
| # two jobs that need them share one entry rather than filling the cache | |
| # with the same gigabytes twice. | |
| - name: Cache the suites | |
| id: cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ~/.cache/conformance-suites | |
| key: suites-${{ hashFiles('conformance/suites.json') }} | |
| - name: Fetch the suite | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: python -m conformance.fetch ~/.cache/conformance-suites | |
| # Every cycle of an eight-bit part is a bus cycle, so this compares timing | |
| # and side effects at once: address, value, and read against write, in | |
| # order. On the 65816 it also compares eight output pins per cycle, and the | |
| # cycles where the part drives no valid address at all. | |
| - name: Compare the bus, cycle by cycle | |
| run: | | |
| if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then | |
| limit=1000 | |
| else | |
| limit=0 | |
| fi | |
| status=0 | |
| while read -r suite model; do | |
| echo "::group::${suite} as a ${model}" | |
| python -m conformance.cycles \ | |
| "${HOME}/.cache/conformance-suites/${suite}" "${limit}" --model "${model}" || status=1 | |
| echo "::endgroup::" | |
| done <<'SUITES' | |
| 65816/65816/v1 65816 | |
| 6502/6502/v1 6502 | |
| nes6502/nes6502/v1 2a03 | |
| synertek65c02/synertek65c02/v1 65c02 | |
| rockwell65c02/rockwell65c02/v1 r65c02 | |
| wdc65c02/wdc65c02/v1 w65c02 | |
| SUITES | |
| exit "${status}" | |
| shell: bash |