Version at 0.2.0: complete the wishlist, document it, gate the docs
Closes what was left of TODO.md sections 1, 2 and 3, and rewrites that
file to hold outstanding items only.
The API break gets a minor bump, because pre-1.0 the soname carries
MAJOR.MINOR and 0.1 and 0.2 are therefore different ABIs. Five
signatures changed and the ato* contract with them; UPGRADING.md is new
and lists every one, with the before/after for the cases the compiler
cannot warn about.
Section 3.1 is finished: reallocarray with the multiplication checked,
aligned_alloc and posix_memalign, asprintf/vasprintf, scanf/vscanf.
Four functions on that list are deliberately absent rather than missing
-- sprintf, strtok, setbuf and perror -- and TODO.md now says which and
why, so nobody adds them thinking they were forgotten.
Section 1.9, the cross-cutting tests:
tests/test_pool.c drives every failure path AKERR_MAX_ARRAY_ERROR
+ 10 times and checks the pool after each round,
because a wrapper that leaks a slot fails a
hundred calls later in unrelated code. It also
asserts that each error names the function and
file it was raised from, which is what catches a
FAIL that migrates into a helper during a
refactor: status right, message right, origin
quietly lying.
tests/negative/ two sources that must FAIL to compile, built with
-Werror and registered WILL_FAIL. AKERR_NOIGNORE
and the format attributes are enforced by the
compiler and by nothing else; drop either and
every ordinary test still passes.
Thread safety is answered rather than tested: the library is not
thread-safe and cannot be made so from here, because libakerror's error
pool is an unlocked process-global array. README.md says so plainly and
TODO.md carries it as the item blocking any future pthread wrappers.
Doxygen is configured and gated. All 147 public functions have @brief,
a @param each, @throws per status and @return; EXTRACT_ALL is off and
WARN_NO_PARAMDOC on, so `cmake --build build --target docs` fails on an
undocumented entity. It ran to 0 warnings. The Doxyfile carries no
version -- cmake/RunDoxygen.cmake feeds PROJECT_NUMBER in from
project(), so that stays the one place a version is written.
CI now builds against the submodule it pins instead of also installing
libakerror@main and never linking it, adds -Werror, and gains a
sanitizer job. The pre-push hook matches, and runs the docs check too.
Coverage: 99.5% of lines (1643/1651), 100% of functions (147/147). The
eight uncovered lines are each uncovered on purpose and TODO.md says
which and why.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,20 +17,48 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake gcc moreutils
|
||||
# Depends on libakerror@main
|
||||
git clone https://source.starfort.tech/andrew/libakerror.git
|
||||
cd libakerror
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
cmake --install build
|
||||
# This step used to clone and install libakerror@main as well. That was two
|
||||
# different libakerrors depending on where you built: the install went to
|
||||
# /usr/local, while the build below is top-level and so compiles
|
||||
# deps/libakerror at the pinned commit via add_subdirectory -- the
|
||||
# installed one was never actually linked against. TODO.md 2.3.
|
||||
#
|
||||
# The submodule wins. It is the version this repository pins, tests and
|
||||
# ships against, and a CI that tests a different one is testing something
|
||||
# nobody runs. `cmake --install` below still installs both, so the
|
||||
# find_package and pkg-config paths are exercised.
|
||||
- name: build and test
|
||||
run: |
|
||||
cmake -S . -B build
|
||||
# -DAKSL_WERROR=ON here and not locally: a warning that stops the build
|
||||
# mid-thought teaches people to turn warnings off, but a warning that
|
||||
# reaches main is one nobody will ever look at again.
|
||||
cmake -S . -B build -DAKSL_WERROR=ON
|
||||
cmake --build build
|
||||
sudo cmake --install build
|
||||
ctest --test-dir build --output-on-failure
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
|
||||
# The sanitizer build is its own job rather than a step on the one above,
|
||||
# because three of the defects fixed in 0.2.0 only ever misbehaved under
|
||||
# instrumentation and the tests that pin them are worth failing on their own.
|
||||
sanitizers:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: dependencies
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake gcc
|
||||
- name: build and test under ASan + UBSan
|
||||
run: |
|
||||
cmake -S . -B build-asan -DAKSL_SANITIZE=ON -DAKSL_WERROR=ON
|
||||
cmake --build build-asan
|
||||
ctest --test-dir build-asan --output-on-failure
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -45,28 +73,27 @@ jobs:
|
||||
# scripts/coverage.py needs nothing but python3 and gcc's own gcov, so
|
||||
# there is no lcov/gcovr to install here.
|
||||
#
|
||||
# The gate is a ratchet, not a target: src/stdlib.c is at 99.1% of lines
|
||||
# and 45.1% of branches, so 90/40 fails on a real regression (a test
|
||||
# deleted, or new untested code added) without tripping over rounding.
|
||||
# The report is printed either way -- the uncovered lines it lists are the
|
||||
# The gate is a ratchet, not a target. Across all four sources the suite
|
||||
# covers 99.5% of lines (1643/1651), 46.0% of branches and 100% of
|
||||
# functions (147/147), so 90/40 fails on a real regression -- a test
|
||||
# deleted, or new untested code added -- without tripping over rounding.
|
||||
# The report is printed either way; the uncovered lines it lists are the
|
||||
# missing tests.
|
||||
#
|
||||
# Branch coverage is back over 45 (the aksl_version_* functions are fully
|
||||
# covered, which lifted it from 44.3%), but the gate stays at 40: 0.1
|
||||
# points of headroom is not a ratchet, it is a coin flip on the next
|
||||
# rounding change.
|
||||
# Eight lines are uncovered and every one is deliberate: four
|
||||
# `HANDLE(e, AKERR_ITERATOR_BREAK)` macro artifacts, the size_t overflow
|
||||
# guard in strbuf_reserve (reaching it needs a buffer near SIZE_MAX), and
|
||||
# the short transfer with neither feof nor ferror set, which the standard
|
||||
# permits and Linux never produces. README.md has the detail.
|
||||
#
|
||||
# The branch gate was 45 against 51.0% until the libakerror 1.0.0 bump.
|
||||
# Nothing about this library's tests changed: line coverage held at
|
||||
# 99.0% (200/202) and function coverage at 100% (21/21), but the branch
|
||||
# denominator went from 661 to 1087 because the 1.0.0 PREPARE_ERROR /
|
||||
# FAIL_* macros expand to more branches at every call site in
|
||||
# src/stdlib.c, and most of the added branches are not reachable from the
|
||||
# way this library calls them. 337/661 became 481/1087 -- 144 more
|
||||
# branches covered, 426 more branches counted. Chasing them here would be
|
||||
# testing libakerror's macros, which is libakerror's mutation suite's job
|
||||
# (macros expand at the call site, so coverage cannot see them properly
|
||||
# from either side). Re-ratcheted rather than papered over.
|
||||
# Branch coverage sits far below line coverage because most branches are
|
||||
# inside libakerror's FAIL_*/ATTEMPT/FINISH expansions -- pool exhaustion,
|
||||
# stack-trace limits, akerr_valid_error_address failures -- which this
|
||||
# library has no way to reach. Chasing them here would be testing
|
||||
# libakerror's macros, which is libakerror's mutation suite's job: macros
|
||||
# expand at the call site, so coverage cannot see them properly from
|
||||
# either side. The gate stays at 40 for that reason and not for want of
|
||||
# tests.
|
||||
- name: coverage
|
||||
run: |
|
||||
cmake -S . -B build-coverage -DAKSL_COVERAGE=ON \
|
||||
|
||||
Reference in New Issue
Block a user