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
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: lint + unit tests (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Floor is 3.13 (the code uses types.CapsuleType + PEP 701 f-strings).
python-version: ["3.13", "3.14"]
steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install fusil + tooling
run: |
python -m pip install --upgrade pip
pip install -e . # pulls python-ptrace (a hard runtime dependency)
pip install ruff==0.15.18 # pin so CI matches local lint results

- name: Ruff (lint)
run: ruff check fusil/ tests/ fuzzers/fusil-python-threaded

- name: Ruff (format check)
run: ruff format --check fusil/ tests/ fuzzers/fusil-python-threaded

- name: Unit tests
# numpy/h5py are optional; without them the relevant tests skip gracefully.
run: python -m unittest discover -s tests
56 changes: 56 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Contributing to fusil

Active development targets **only the Python fuzzer** (`fusil-python-threaded`,
`fusil.python` / `fusil.python.jit`). The legacy fuzzers and non-Python subsystems under
`*/notworking/` are out of scope. Start with `doc/python-fuzzer.md` for an architecture
overview and `CLAUDE.md` for repository orientation.

## Requirements

- **Python 3.13+** (the code uses `types.CapsuleType` and PEP 701 f-strings).
- **`python-ptrace`** — a hard runtime dependency (`fusil.application` imports it at module
load), so the fuzzer can't even start without it.

## Dev setup

```bash
python3.13 -m venv .venv && . .venv/bin/activate
pip install -e . # installs fusil + python-ptrace; adds the
# `fusil-python-threaded` console script
pip install -e '.[numpy,h5py]' # optional: enable the numpy/h5py argument generators
pip install ruff # linter/formatter (CI pins ruff==0.15.18)
```

A real fuzzing run drops the fuzzed child to a dedicated unprivileged `fusil` user; for quick
local runs pass `--unsafe` (runs children as you). **Never** point `--filenames` at files you
care about — fuzzed calls may overwrite them.

## Tests, lint, format

```bash
python -m unittest discover -s tests # the suite (unittest, NOT pytest)
ruff check fusil/ tests/ fuzzers/fusil-python-threaded
ruff format fusil/ tests/ fuzzers/fusil-python-threaded
```

numpy/h5py-dependent tests skip gracefully when those packages aren't installed. CI
(`.github/workflows/ci.yml`) runs ruff (check + format) and the unittest suite on Python 3.13
and 3.14.

### Writing tests

Prefer **runtime-free** unit tests (no real fuzzing child, no ptrace). Good models:
`tests/test_oom_dedup.py`, `tests/test_process_limits.py`, `tests/test_mas.py`. For tests that
construct `WritePythonCode`, use `tests/python/_test_options.py:make_test_options()` — it
harvests the real option defaults so tests don't rot when new options are added. Seed `random`
in `setUp` for any generator that picks values at random.

## Workflow

- Branch off `main`; keep the test suite green at every commit.
- One concern per pull request. Run `ruff check`, `ruff format`, and the suite before pushing.
- `ruff format` runs as an isolated commit; bulk-reformat commits are listed in
`.git-blame-ignore-revs` (enable locally with
`git config blame.ignoreRevsFile .git-blame-ignore-revs`).
- Code generators (`write_python_code.py`, `jit/`, `h5py/`) build target source as strings and
carry scoped lint ignores in `pyproject.toml`; match the surrounding style.
5 changes: 3 additions & 2 deletions tests/python/test_blacklists.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ class TestKnownEntriesPresent(unittest.TestCase):
"""Pin a few high-value entries so accidental deletion is caught."""

def test_sys_trace_hooks_blacklisted(self):
self.assertEqual(bl.BLACKLIST["sys"] & {"settrace", "setprofile"},
{"settrace", "setprofile"})
self.assertEqual(
bl.BLACKLIST["sys"] & {"settrace", "setprofile"}, {"settrace", "setprofile"}
)

def test_resource_setrlimit_blacklisted(self):
self.assertIn("setrlimit", bl.BLACKLIST["resource"])
Expand Down
21 changes: 15 additions & 6 deletions tests/test_mas.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@


class _StubLogger:
def debug(self, *a, **k): pass
def info(self, *a, **k): pass
def warning(self, *a, **k): pass
def error(self, *a, **k): pass
def debug(self, *a, **k):
pass

def info(self, *a, **k):
pass

def warning(self, *a, **k):
pass

def error(self, *a, **k):
pass


class _StubApp:
Expand All @@ -27,9 +34,11 @@ class _StubApp:
def __init__(self):
self.logger = _StubLogger()

def registerAgent(self, agent): pass
def registerAgent(self, agent):
pass

def unregisterAgent(self, agent, destroy=True): pass
def unregisterAgent(self, agent, destroy=True):
pass


def _make_mta():
Expand Down
File renamed without changes.
File renamed without changes.
Loading