Skip to content

Commit e907302

Browse files
ci(mutation): cover all subsystems with incremental builds
Generalize the harness beyond containers: resolve any component to its source anywhere under Source/ (with overrides for Json/Dns/AllocDebug/Iter), discover single-token (Elf), dotted (Io.Write) and multi-dot (Json.Read.Simple) suite names, and skip suites with no buildable Tests/<name> binary. Build the library ONCE per invocation and scope each component incrementally: delete just that source's library object so ninja recompiles one file under the component's MULL_CONFIG, instead of a full rebuild per component (minutes -> seconds after the first build). Clean mull-runner's stray CWD artifacts.
1 parent e615918 commit e907302

1 file changed

Lines changed: 81 additions & 17 deletions

File tree

Scripts/mutation.sh

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,16 @@ SAN=${MUTATION_SANITIZE:-none}
4040

4141
COMPONENTS=("$@")
4242
if [ ${#COMPONENTS[@]} -eq 0 ]; then
43-
COMPONENTS=(Map Vec Str List BitVec Graph Int Float)
43+
COMPONENTS=(
44+
# containers
45+
Map Vec Str List BitVec Graph Int Float
46+
# std subsystems
47+
Io File ArgParse Iter Allocator AllocDebug
48+
# parsers
49+
Elf MachO Pe Pdb Dwarf Json KvConfig Http Dns
50+
# sys
51+
Backtrace Socket ProcMaps SymbolResolver MachoCache PdbCache SysDns
52+
)
4453
fi
4554

4655
# Locate the pass plugin if the environment did not hand it to us.
@@ -61,35 +70,79 @@ echo "==> runner : $MULL_RUNNER"
6170
echo "==> components : ${COMPONENTS[*]}"
6271
echo
6372

64-
# Resolve a component name to its implementation source file.
73+
# Explicit component -> source overrides, for components whose test prefix does
74+
# not match a unique <Component>.c basename under Source/ (case, ambiguity, or
75+
# a differently-named file).
76+
declare -A SRC_OVERRIDE=(
77+
[Json]="Source/Misra/Parsers/JSON.c"
78+
[Dns]="Source/Misra/Parsers/Dns.c"
79+
[SysDns]="Source/Misra/Sys/Dns.c"
80+
[AllocDebug]="Source/Misra/Std/Allocator/Debug.c"
81+
[Iter]="Source/Misra/Std/Utility/Iter.c"
82+
)
83+
84+
# Resolve a component name to its implementation source file: an explicit
85+
# override, else a unique <Component>.c anywhere under Source/. Returns nonzero
86+
# (caller skips) when there is no match or an ambiguous one.
6587
component_source() {
66-
local c=$1 p
67-
for p in "Source/Misra/Std/Container/$c.c" "Source/Misra/Std/$c.c"; do
68-
[ -f "$p" ] && { echo "$p"; return 0; }
69-
done
88+
local c=$1
89+
if [ -n "${SRC_OVERRIDE[$c]:-}" ]; then
90+
[ -f "${SRC_OVERRIDE[$c]}" ] && { echo "${SRC_OVERRIDE[$c]}"; return 0; }
91+
return 1
92+
fi
93+
local hits n
94+
hits=$(find Source -type f -name "$c.c" 2>/dev/null)
95+
n=$(printf '%s\n' "$hits" | grep -c .)
96+
if [ "$n" = "1" ]; then
97+
echo "$hits"
98+
return 0
99+
fi
70100
return 1
71101
}
72102

103+
# One build dir per invocation, named by the first component so parallel runs
104+
# over disjoint component sets don't collide. The library is built ONCE; each
105+
# component is then scoped incrementally -- we delete just that source's library
106+
# object so ninja recompiles that single file (seconds) under the component's
107+
# MULL_CONFIG, instead of rebuilding the whole library per component (minutes).
108+
BUILD_DIR="$ROOT/${COMPONENTS[0]}"
109+
mkdir -p "$BUILD_DIR"
110+
111+
# Source/Misra/Parsers/Elf.c -> Source_Misra_Parsers_Elf.c.o (meson object name)
112+
obj_name() { printf '%s.o' "$(printf '%s' "$1" | tr '/' '_')"; }
113+
73114
overall_fail=0
115+
prev_src=""
74116
for comp in "${COMPONENTS[@]}"; do
75117
src=$(component_source "$comp") || { echo "::warning::no source for component '$comp', skipping"; continue; }
76-
bdir="$ROOT/$comp"
77-
cfg="$bdir/mull.yml"
78-
mkdir -p "$bdir"
79118

80-
# Scope mutations to this component's source for both compile and run.
119+
cfg="$BUILD_DIR/mull-$comp.yml"
81120
printf 'includePaths:\n - %s\nmutators:\n - cxx_all\n' "$src" > "$cfg"
82121
export MULL_CONFIG="$PWD/$cfg"
83122

84-
if [ ! -f "$bdir/build.ninja" ]; then
85-
CC="$CC" meson setup "$bdir" \
123+
# Force recompile of the current target (to embed its mutants) and the
124+
# previously-mutated one (to drop its mutants, now out of includePaths).
125+
# Deleting the library object -- not touching the shared source -- avoids
126+
# churning a parallel run's build of the same file.
127+
for s in "$src" "$prev_src"; do
128+
[ -n "$s" ] && find "$BUILD_DIR" -path "*.a.p/$(obj_name "$s")" -delete 2>/dev/null
129+
true
130+
done
131+
prev_src="$src"
132+
133+
# Configure once; MULL_CONFIG is already set so the initial full build embeds
134+
# the first component's mutants.
135+
if [ ! -f "$BUILD_DIR/build.ninja" ]; then
136+
CC="$CC" meson setup "$BUILD_DIR" \
86137
-Db_sanitize="$SAN" \
87138
-Db_lundef=false \
88139
-Dc_args="-g -O0 -grecord-command-line -fno-discard-value-names -fpass-plugin=$MULL_IR_FRONTEND"
89140
fi
90141

91-
# Suites for this component: meson tests named "<comp>.*".
92-
mapfile -t suites < <(meson test -C "$bdir" --list 2>/dev/null | grep -oE "(^|[^A-Za-z0-9_])$comp\.[A-Za-z0-9_]+" | grep -oE "$comp\.[A-Za-z0-9_]+" | sort -u)
142+
# Suites for this component: meson tests named exactly "<comp>" or "<comp>.*"
143+
# (single-token like Elf, dotted like Io.Write, multi-dot like
144+
# Json.Read.Simple). meson prints "<project>:<testname>"; strip the prefix.
145+
mapfile -t suites < <(meson test -C "$BUILD_DIR" --list 2>/dev/null | sed 's/^[^:]*://' | grep -E "^$comp(\.|\$)" | sort -u)
93146
if [ ${#suites[@]} -eq 0 ]; then
94147
echo "::warning::no suites found for component '$comp', skipping"
95148
continue
@@ -98,9 +151,16 @@ for comp in "${COMPONENTS[@]}"; do
98151
echo "==================== $comp ($src) [${#suites[@]} suites] ===================="
99152
for suite in "${suites[@]}"; do
100153
echo "-------------------- $suite --------------------"
101-
ninja -C "$bdir" "Tests/$suite"
102-
bin="$bdir/Tests/$suite"
103-
report="$bdir/mutation-$suite.txt"
154+
bin="$BUILD_DIR/Tests/$suite"
155+
report="$BUILD_DIR/mutation-$suite.txt"
156+
157+
# Most suites build as a "Tests/<name>" target; a few (custom test exes)
158+
# don't -- skip those rather than aborting the whole sweep.
159+
if ! ninja -C "$BUILD_DIR" "Tests/$suite" 2>/dev/null || [ ! -x "$bin" ]; then
160+
echo "::warning::$suite: no buildable 'Tests/$suite' binary -- skipping"
161+
echo "$suite: no binary" > "$report"
162+
continue
163+
fi
104164

105165
if ! "$bin" >/dev/null 2>&1; then
106166
echo "::warning::$suite baseline FAILS unmutated -- skipping mutation run"
@@ -124,4 +184,8 @@ for comp in "${COMPONENTS[@]}"; do
124184
done
125185
done
126186

187+
# mull-runner drops per-mutant artifact files (16-hex-char names) in the CWD;
188+
# clean them so they don't litter the repo root.
189+
find . -maxdepth 1 -type f -regextype posix-extended -regex '\./[0-9a-f]{16}' -delete 2>/dev/null || true
190+
127191
exit $overall_fail

0 commit comments

Comments
 (0)