Skip to content

Commit 9458981

Browse files
authored
Check the code map against the tree (#83)
A script, run by make and CI, that fails when a source file is missing from docs/code-map.md, an Erlang module has no moduledoc, or a module has no row in the new Modules table of the coverage audit. Running it once found two modules, seven headers and two Python files the map had missed.
1 parent e44a0aa commit 9458981

7 files changed

Lines changed: 85 additions & 5 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ jobs:
326326
- name: Run dialyzer
327327
run: rebar3 dialyzer
328328

329+
- name: Check code map
330+
run: make check-code-map
331+
329332
docs:
330333
name: Documentation
331334
runs-on: ubuntu-24.04

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
virtual environments and shared dicts to `py_stream`, `py_venv` and
6666
`py_shared_dict`. The public API is unchanged.
6767

68+
### Documentation
69+
70+
- `make check-code-map` (also run by CI) verifies that every source file is
71+
in `docs/code-map.md`, every Erlang module has a moduledoc and a row in
72+
the Modules table of `test/coverage_audit.md`.
73+
6874
### Removed
6975

7076
- The legacy worker API (`py_nif:worker_new/0,1`, `worker_call`, `worker_eval`,

‎Makefile‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all compile test lint-docs clean
1+
.PHONY: all compile test lint-docs check-code-map clean
22

33
all: compile
44

@@ -16,5 +16,10 @@ test:
1616
lint-docs: compile
1717
escript scripts/lint_doc_snippets.escript
1818

19+
# Every source file in docs/code-map.md, every module with a moduledoc and
20+
# a row in the Modules table of test/coverage_audit.md.
21+
check-code-map:
22+
sh scripts/check_code_map.sh
23+
1924
clean:
2025
rebar3 clean

‎docs/code-map.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ exercised by suites). Guides are in `docs/`, suites in `test/`. Start with
2323
| `py_thread_handler` | Coordinator that gives each Python thread calling Erlang a handler process and a pipe | live | threading | `py_thread_callback_SUITE`, `py_reentrant_SUITE` |
2424
| `py_event_loop` | Main-interpreter asyncio loop: `run`, `create_task`, `await`, and the loop callbacks Python needs | live | asyncio | `py_event_loop_SUITE`, `py_async_task_SUITE` |
2525
| `py_event_loop_pool` | Several main-interpreter loops with process affinity | live | asyncio | `py_event_loop_pool_SUITE` |
26-
| `py_event_worker`, `_sup`, `_registry` | One process per running loop receiving `enif_select` readiness and timers | live | event_loop_architecture | `py_event_loop_SUITE`, `py_fd_ops_SUITE` |
26+
| `py_event_worker` | One process per running loop receiving `enif_select` readiness and timers | live | event_loop_architecture | `py_event_loop_SUITE`, `py_fd_ops_SUITE` |
27+
| `py_event_worker_sup`, `py_event_worker_registry` | Supervisor and name registry of the event workers | live | event_loop_architecture | `py_event_loop_SUITE` |
2728
| `py_reactor_context` | FD-owning context for the protocol-based reactor | live | reactor | `py_reactor_SUITE` |
2829
| `py_channel`, `py_byte_channel` | Term and byte queues between Erlang and Python coroutines (NIF resources) | live | channel | `py_channel_SUITE`, `py_byte_channel_SUITE` |
2930
| `py_buffer` | Native streaming input buffer; shared variant delegates to `py_shm` | live | buffer, isolated | `py_buffer_SUITE`, `py_isolated_buffer_SUITE` |
@@ -56,6 +57,7 @@ files. Editing `py_convert.c` alone does not compile it alone; build with
5657
| `py_logging.c` | Logging and tracing NIFs | live |
5758
| `py_mem_limit.c` | Per-interpreter memory caps (owngil) | live |
5859
| `py_util.c/.h` | Macros and helpers | live |
60+
| `py_nif.h`, `py_event_loop.h`, `py_channel.h`, `py_buffer.h`, `py_reactor_buffer.h`, `py_subinterp_thread.h`, `py_util.h` | Declarations shared between the included files; the struct comments in `py_nif.h` and `py_event_loop.h` carry the lock contracts | live |
5961

6062
The only code not on a live path is the "Test Helper Functions" section of
6163
`py_event_loop.c` (fd, pipe, TCP and UDP helpers the suites use).
@@ -79,15 +81,16 @@ loop, channels and servers.
7981
| `_erlang_impl/_isolated.py` | Child runtime: socket frames, reader thread, re-entrant main loop, interrupt signal, asyncio loop, the `erlang` shim | isolated child |
8082
| `_erlang_impl/_shm.py` | `SharedMemory` and `SharedBuffer` wrappers over mmap | all |
8183
| `py_isolated_child.py` | Child launcher: rlimits, parent-death signal, cgroup join, connect | isolated child |
82-
| `test_erlang_loop.py`, `tests/` | Python-side tests of the loop | test |
84+
| `test_erlang_loop.py`, `test_async_task.py`, `test_channel_ref.py`, `tests/` | Python-side tests of the loop, tasks and channels | test |
8385

8486
## Tests (`test/`)
8587

8688
Suites named `py_<area>_SUITE`. Cross-mode suites run the same cases in
8789
`worker` and `isolated` groups (`py_isolated_SUITE`, `py_isolated_vm_SUITE`,
8890
`py_isolated_shm_SUITE`, `py_isolated_buffer_SUITE`). Python helpers used by
8991
suites are `test/py_test_*.py`. `test/coverage_audit.md` maps public APIs to
90-
cases. `test/test.config` holds node-wide settings (memory limits flag).
92+
cases and every module to its suites; `make check-code-map` verifies this
93+
page and that table against the tree. `test/test.config` holds node-wide settings (memory limits flag).
9194

9295
## Build and docs
9396

‎docs/contributing.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ rebar3 ct --suite test/py_isolated_SUITE # one suite
3333
rebar3 ct --suite test/py_context_SUITE --case test_call # one case
3434
rebar3 dialyzer && rebar3 xref # required before a PR
3535
make lint-docs # snippets in README and docs/
36+
make check-code-map # every file in the code map
3637
```
3738

3839
Notes:
@@ -182,7 +183,8 @@ supervisor to carry it.
182183
4. Skip, do not fail, when a platform or interpreter cannot run a case:
183184
`{skip, Reason}` with the reason a human can act on.
184185
5. Add the suite to the table in `docs/code-map.md` and the cases that
185-
cover a documented API to `test/coverage_audit.md`.
186+
cover a documented API to `test/coverage_audit.md`; a new module also
187+
needs a row in its Modules table. `make check-code-map` verifies both.
186188
6. Cases that measure time or memory print their numbers with `ct:pal`
187189
and assert only on invariants, never on absolute timings.
188190

‎scripts/check_code_map.sh‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
# Every source file must be listed in docs/code-map.md, every Erlang module
3+
# must have a moduledoc, and every Erlang module must have a row in the
4+
# "Modules" table of test/coverage_audit.md. Exit code is the number of
5+
# failures. Run: sh scripts/check_code_map.sh
6+
cd "$(dirname "$0")/.." || exit 2
7+
fail=0
8+
note() { echo "check_code_map: $1"; fail=$((fail + 1)); }
9+
10+
for f in src/*.erl; do
11+
m=$(basename "$f" .erl)
12+
grep -q "\`$m\`" docs/code-map.md || note "$m is not in docs/code-map.md"
13+
grep -q '^%%%\{0,1\} @doc' "$f" || note "$f has no @doc moduledoc"
14+
grep -q "^| \`$m\` |" test/coverage_audit.md || note "$m has no row in the Modules table of test/coverage_audit.md"
15+
done
16+
for f in c_src/*.c c_src/*.h priv/_erlang_impl/*.py priv/*.py; do
17+
b=$(basename "$f")
18+
grep -q "$b" docs/code-map.md || note "$b is not in docs/code-map.md"
19+
done
20+
[ "$fail" -eq 0 ] && echo "check_code_map: clean"
21+
exit $fail

‎test/coverage_audit.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,46 @@ README and `docs/*.md` to at least one `*_SUITE.erl` test that
55
exercises it. Update this table whenever a documented API is added,
66
renamed, or removed.
77

8+
## Modules
9+
10+
Every Erlang module and the suites that exercise it, so a module without a
11+
suite is visible. `scripts/check_code_map.sh` requires a row per module.
12+
13+
| Module | Suites |
14+
|---|---|
15+
| `py` | `py_SUITE`, `py_api_SUITE`, `py_stream_SUITE`, `py_venv_SUITE` |
16+
| `py_context` | `py_context_SUITE`, `py_context_process_SUITE`, `py_interrupt_SUITE`, `py_worker_loop_SUITE` |
17+
| `py_context_embedded` | `py_context_SUITE`, `py_context_process_SUITE`, `py_interrupt_SUITE`, `py_worker_loop_SUITE` |
18+
| `py_stream` | `py_stream_SUITE` |
19+
| `py_venv` | `py_venv_SUITE` |
20+
| `py_shared_dict` | `py_SUITE` |
21+
| `py_isolated` | `py_isolated_*_SUITE` |
22+
| `py_context_router` | `py_context_router_SUITE`, `py_pool_SUITE` |
23+
| `py_context_sup` | (through the above) |
24+
| `py_context_init` | (through the above) |
25+
| `py_nif` | all |
26+
| `py_callback` | `py_callback_encoding_SUITE`, `py_thread_callback_SUITE` |
27+
| `py_thread_handler` | `py_thread_callback_SUITE`, `py_reentrant_SUITE` |
28+
| `py_event_loop` | `py_event_loop_SUITE`, `py_async_task_SUITE` |
29+
| `py_event_loop_pool` | `py_event_loop_pool_SUITE` |
30+
| `py_event_worker` | `py_event_loop_SUITE`, `py_fd_ops_SUITE` |
31+
| `py_event_worker_sup` | `py_event_loop_SUITE` |
32+
| `py_event_worker_registry` | `py_event_loop_SUITE` |
33+
| `py_reactor_context` | `py_reactor_SUITE` |
34+
| `py_channel` | `py_channel_SUITE`, `py_byte_channel_SUITE` |
35+
| `py_byte_channel` | `py_channel_SUITE`, `py_byte_channel_SUITE` |
36+
| `py_buffer` | `py_buffer_SUITE`, `py_isolated_buffer_SUITE` |
37+
| `py_shm` | `py_isolated_shm_SUITE` |
38+
| `py_import` | `py_import_SUITE` |
39+
| `py_preload` | `py_preload_SUITE` |
40+
| `py_state` | `py_state_SUITE` |
41+
| `py_semaphore` | (through `py_SUITE`) |
42+
| `py_logger` | `py_logging_SUITE` |
43+
| `py_util` | (through every suite) |
44+
| `py_tracer` | `py_logging_SUITE` |
45+
| `erlang_python_app` | all |
46+
| `erlang_python_sup` | all |
47+
848
## Erlang public API (`src/py.erl` exports)
949

1050
| API | Documented in | Test suite | Test case |

0 commit comments

Comments
 (0)