ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_image_lock_receipts.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Executable-order policy for the devcontainer image-lock selftest receipts."""
4
5from __future__ import annotations
6
7import re
8
9PROCESS_SOURCE_SPECS = (
10 (
11 "suite-root nested allocation selection removed",
12 "devcontainer_image_selftest",
13 "begin_selftest_tmp",
14 ' if [[ -n "$SELFTEST_SUITE_ROOT" ]]; then\n selftest_suite_root_is_safe || return 1',
15 ),
16 (
17 "shared worker process-group binding removed",
18 "devcontainer_image_lock_selftest",
19 "record_worker_group",
20 ' [[ "$pgid" == "$PPID" && "$pgid" != "$pid" ]] ||',
21 ),
22 (
23 "isolated worker process-group binding removed",
24 "devcontainer_image_lock_selftest",
25 "record_worker_group",
26 ' [[ "$pgid" == "$pid" ]] || die "selftest isolated worker is not its group leader"',
27 ),
28 (
29 "suite anchor canonical path binding removed",
30 "devcontainer_image_selftest",
31 "selftest_suite_anchor_is_safe",
32 ' "$anchor" == "$canonical/ra8-devcontainer-image-selftest.$suffix" &&',
33 ),
34 (
35 "suite anchor identity binding removed",
36 "devcontainer_image_selftest",
37 "selftest_suite_anchor_is_safe",
38 ' "$(file_identity "$anchor")" == "$SELFTEST_SUITE_ANCHOR_IDENTITY" &&',
39 ),
40 (
41 "suite anchor owner binding removed",
42 "devcontainer_image_selftest",
43 "selftest_suite_anchor_is_safe",
44 ' "$(file_owner_id "$anchor")" == "$SELFTEST_SUITE_ANCHOR_OWNER_UID" &&',
45 ),
46 (
47 "suite anchor mode binding removed",
48 "devcontainer_image_selftest",
49 "selftest_suite_anchor_is_safe",
50 ' "$(file_mode "$anchor")" == "700" ]]',
51 ),
52 (
53 "suite anchor child depth binding removed",
54 "devcontainer_image_selftest",
55 "selftest_suite_path_is_safe",
56 ' [[ "$suite" == "$SELFTEST_SUITE_ANCHOR/ra8-devcontainer-image-selftest.$suffix" &&',
57 ),
58 (
59 "suite anchor producer path removed",
60 "devcontainer_image_selftest",
61 "establish_selftest_suite_root",
62 ' SELFTEST_SUITE_ANCHOR="$SELFTEST_TMP_DIR"',
63 ),
64 (
65 "suite anchor producer identity removed",
66 "devcontainer_image_selftest",
67 "establish_selftest_suite_root",
68 ' SELFTEST_SUITE_ANCHOR_IDENTITY="$SELFTEST_TMP_IDENTITY"',
69 ),
70 (
71 "suite anchor producer owner removed",
72 "devcontainer_image_selftest",
73 "establish_selftest_suite_root",
74 ' SELFTEST_SUITE_ANCHOR_OWNER_UID="$SELFTEST_TMP_OWNER_UID"',
75 ),
76 (
77 "suite anchor receiver path removed",
78 "devcontainer_image_selftest",
79 "configure_selftest_suite_authority",
80 ' SELFTEST_SUITE_ANCHOR="$3"',
81 ),
82 (
83 "suite anchor receiver identity removed",
84 "devcontainer_image_selftest",
85 "configure_selftest_suite_authority",
86 ' SELFTEST_SUITE_ANCHOR_IDENTITY="$4"',
87 ),
88 (
89 "suite anchor receiver owner removed",
90 "devcontainer_image_selftest",
91 "configure_selftest_suite_authority",
92 ' SELFTEST_SUITE_ANCHOR_OWNER_UID="$5"',
93 ),
94 (
95 "suite anchor dispatcher propagation removed",
96 "devcontainer_image_selftest_cases",
97 "selftest_one_allocation_signal",
98 ' "$tmp" "$SELFTEST_TMP_IDENTITY" "$SELFTEST_SUITE_ANCHOR" \\\n'
99 ' "$SELFTEST_SUITE_ANCHOR_IDENTITY" "$SELFTEST_SUITE_ANCHOR_OWNER_UID"; then',
100 ),
101)
102LEGACY_PROCESS_FINDINGS = {
103 "image lock jobs-table lookup bypassed": (
104 "devcontainer image lifecycle selftest: required process-authority token is not unique: "
105 "done < <(jobs -r -l)",
106 ),
107 "image lock verified PID signal reverted to stale jobspec": (
108 "devcontainer image lifecycle selftest: required process-authority token is not unique: "
109 'builtin kill -"$signal" "$child"',
110 ),
111 "allocation KILL direct-child guard removed": (
112 "devcontainer image cases selftest: required process-authority token is not unique: "
113 'signal_owned_live_child KILL "$child" ||',
114 ),
115 "suite-root parent grammar reverted to ten characters": (
116 "devcontainer image lifecycle selftest: required process-authority token is not unique: "
117 '"$suffix" =~ ^[0-9a-f]{32}$ && ! -L "$canonical"',
118 ),
119 "portable Bash 3 shell identity replaced by BASHPID": (
120 "devcontainer image lifecycle selftest: required process-authority token is not unique: "
121 'local destination="$1" value="$$:${BASH_SUBSHELL:-0}"',
122 "devcontainer image selftest: macOS Bash 3.2 BASHPID dependency returned",
123 ),
124 "fresh allocation signal process bypassed": (
125 "devcontainer image cases selftest: required process-authority token is not unique: "
126 '--selftest-allocation-checkpoint-child "$phase" "$receipt"',
127 ),
128 "selftest atomic directory allocation replaced by mktemp create": (
129 "devcontainer image lifecycle selftest: required process-authority token is not unique: "
130 'if (umask 077 && mkdir -m 0700 -- "$candidate"); then',
131 "devcontainer image lifecycle selftest: allocation-before-binding returned",
132 ),
133 "selftest suite-root binding removed": (
134 "devcontainer image cases selftest: required process-authority token is not unique: "
135 'establish_selftest_suite_root || die "selftest: could not bind its suite root"',
136 ),
137 "selftest suite-root completion proof removed": (
138 "devcontainer image cases selftest: required process-authority token is not unique: "
139 'clear_selftest_suite_root || die "selftest: suite-root cleanup did not complete"',
140 ),
141 "worker group direct-child authority removed": (
142 "dev box image lock: bounded selftest harness is not load-bearing",
143 ),
144 "worker leader TERM resistance removed": (
145 "devcontainer image-lock selftest: required process-authority token is not unique: "
146 "pre-ready-hang | signal-controller | post-ready-build-hang) trap '' HUP INT TERM ;;",
147 ),
148 "process-enumeration failure scenario removed": (
149 "devcontainer image cases selftest: required process-authority token is not unique: "
150 'selftest_ps_failure_cleanup "$tmp"',
151 ),
152 "rebound process-group refusal removed": (
153 "devcontainer image-lock selftest: required process-authority token is not unique: "
154 "rebound numeric process group gained signal authority",
155 ),
156 "process-enumeration descendant proof removed": (
157 "devcontainer image-lock selftest: required process-authority token is not unique: "
158 "repeated ps failure left a signal-ignoring descendant",
159 ),
160}
161
162
163def _process_source_finding(key: str, owner: str, token: str) -> str:
164 """Return one exact Bash source-policy diagnostic."""
165 return f"devcontainer image source policy: {key}:{owner} token is not unique: {token}"
166
167
168def semantic_process_findings(label: str) -> tuple[str, ...] | None:
169 """Return one exact legacy Bash process-authority finding set."""
170 if (findings := LEGACY_PROCESS_FINDINGS.get(label)) is not None:
171 return findings
172 for candidate, key, owner, token in PROCESS_SOURCE_SPECS:
173 if label == candidate:
174 return (_process_source_finding(key, owner, token),)
175 return None
176
177
178def process_source_errors(inputs: dict[str, str]) -> list[str]:
179 """Bind nested-root selection and shared/isolated group identity by owner."""
180 errors = []
181 for _label, key, owner, token in PROCESS_SOURCE_SPECS:
182 body = _body(inputs[key], owner, 0)
183 if body is None or body.count(token) != 1:
184 errors.append(_process_source_finding(key, owner, token))
185 return errors
186
187
188def _body(source: str, name: str, indent: int) -> str | None:
189 """Return one Bash function body whose closing brace shares its indentation."""
190 prefix = " " * indent
191 opening = f"{prefix}{name}() {{\n"
192 if source.count(opening) != 1:
193 return None
194 start = source.find(opening)
195 if start < 0:
196 return None
197 body_start = start + len(opening)
198 match = re.search(rf"(?m)^{re.escape(prefix)}}}\s*$", source[body_start:])
199 if match is None:
200 return None
201 return source[body_start : body_start + match.start()]
202
203
204def _ordered(body: str, anchors: tuple[str, ...]) -> bool:
205 """Require active anchors exactly once and in strictly increasing order."""
206 active = "\n".join(line for line in body.splitlines() if not line.lstrip().startswith("#"))
207 position = -1
208 for anchor in anchors:
209 if active.count(anchor) != 1:
210 return False
211 position = active.find(anchor, position + 1)
212 if position < 0:
213 return False
214 return True
215
216
217def _early_success(body: str, indent: int) -> bool:
218 """Detect a top-level success return injected before completion accounting."""
219 prefix = " " * (indent + 2)
220 return any(
221 line in (f"{prefix}return", f"{prefix}return 0", f"{prefix}exit 0")
222 for line in body.splitlines()
223 )
224
225
226def _function_errors(source: str, names: tuple[str, ...], indent: int) -> list[str]:
227 """Require functions to exist and refuse top-level early-success exits."""
228 errors = []
229 for name in names:
230 body = _body(source, name, indent)
231 if body is None:
232 errors.append(f"image lock receipt: missing {name}")
233 elif _early_success(body, indent):
234 errors.append(f"image lock receipt: {name} can return success before its proof")
235 return errors
236
237
238def _order_error(source: str, name: str, indent: int, anchors: tuple[str, ...]) -> str | None:
239 """Return one finding when a function's executable proof order drifts."""
240 body = _body(source, name, indent)
241 if body is None or not _ordered(body, anchors):
242 return f"image lock receipt: {name} proof order drifted"
243 return None
244
245
246def _main_order_errors(image_source: str, cases_source: str) -> list[str]:
247 """Bind the lock, command, and main completion paths in executable order."""
248 build_anchors = (
249 'exec 9<"$IMAGE_LOCK_FILE"',
250 "validate_opened_image_lock 9\n if ! flock -n 9; then",
251 "if ! flock -n 9; then",
252 "fi\n validate_opened_image_lock 9",
253 'build_image "$want"\n )',
254 )
255 command_anchors = (
256 'SELFTEST_RECEIPT_DIR="$tmp/image-lock-selftest-receipts"',
257 'mkdir -m 0700 "$SELFTEST_RECEIPT_DIR"',
258 'dispatch_image_lock_selftest suite "$tmp"',
259 '[[ "$SELFTEST_DISPATCH_COMPLETE" == "1" ]]',
260 "SELFTEST_FILE_RECEIPTS_VERIFIED=0",
261 "verify_scenario_receipt_files early-exit pre-ready-hang forced-build-contention",
262 '[[ "$SELFTEST_FILE_RECEIPTS_VERIFIED" == "1" ]]',
263 'selftest_runtime_labels "$base"',
264 "SELFTEST_COMMAND_COMPLETE=1",
265 )
266 errors = []
267 for source, name, indent, anchors in (
268 (image_source, "build_locked", 2, build_anchors),
269 (cases_source, "cmd_selftest", 0, command_anchors),
270 ):
271 error = _order_error(source, name, indent, anchors)
272 if error:
273 errors.append(error)
274 main_order = (
275 "SELFTEST_COMMAND_COMPLETE=0",
276 "cmd_selftest",
277 '[[ "$SELFTEST_COMMAND_COMPLETE" == "1" ]]',
278 "SELFTEST_MAIN_COMPLETE=1",
279 )
280 main_body = _body(image_source, "main", 2)
281 if main_body is None or not _ordered(main_body, main_order):
282 errors.append("image lock receipt: main selftest completion check drifted")
283 top_order = (
284 "SELFTEST_MAIN_COMPLETE=0",
285 'main "$@"',
286 '[[ "$SELFTEST_MAIN_COMPLETE" == "1" ]] || die "selftest main returned before completion"',
287 )
288 if not _ordered(image_source, top_order):
289 errors.append("image lock receipt: top-level main completion check drifted")
290 return errors
291
292
293def _worker_cleanup_checks() -> tuple[tuple[str, tuple[str, ...]], ...]:
294 """Return lock release and direct worker cleanup proof order."""
295 return (
296 (
297 "release_parent_lock",
298 (
299 "flock -u 8 || release_failed=1",
300 "if exec 8>&-; then",
301 "SELFTEST_PARENT_LOCK_OPEN=0",
302 'return "$release_failed"',
303 ),
304 ),
305 (
306 "cleanup_image_lock_case",
307 (
308 'SELFTEST_CLEANUP_RECEIPT=""',
309 'kill -TERM -- "-$SELFTEST_WORKER_PGID"',
310 "release_parent_lock",
311 "kill -KILL",
312 "reap_worker",
313 'bounded_process_absent "$SELFTEST_WORKER_PID"',
314 'bounded_group_gone "$SELFTEST_WORKER_PGID"',
315 "assert_no_surviving_descendants",
316 "parent_lock_fd_is_closed",
317 "fresh_lock_probe",
318 'SELFTEST_CLEANUP_RECEIPT="$(expected_cleanup_receipt)"',
319 'return "$cleanup_failed"',
320 ),
321 ),
322 )
323
324
325def _controller_cleanup_checks() -> tuple[tuple[str, tuple[str, ...]], ...]:
326 """Return forced and signal-controller cleanup proof order."""
327 return (
328 (
329 "force_signal_controller_cleanup",
330 (
331 'SELFTEST_FORCE_CLEANUP_RECEIPT=""',
332 'signal_owned_controller_group TERM "$controller"',
333 'bounded_process_terminal "$controller" "$SELFTEST_CONTROLLER_CLEANUP_STEPS"',
334 "verify_signal_controller_cleanup",
335 'SELFTEST_FORCE_CLEANUP_RECEIPT="$(expected_force_cleanup_receipt "$case_dir")"',
336 ),
337 ),
338 (
339 "verify_signal_controller_cleanup",
340 (
341 "reap_controller",
342 'bounded_process_absent "$controller"',
343 "read_worker_group_from_case",
344 'bounded_process_absent "$SELFTEST_WORKER_PID"',
345 'bounded_group_gone "$SELFTEST_WORKER_PGID"',
346 "assert_no_surviving_descendants",
347 "parent_lock_fd_is_closed",
348 "fresh_lock_probe",
349 "write_controller_cleanup_receipt_file",
350 "require_controller_cleanup_receipt_file",
351 ),
352 ),
353 (
354 "image_lock_case_signal",
355 (
356 "cleanup_image_lock_case",
357 "require_cleanup_receipt",
358 "write_worker_cleanup_proof_file",
359 'exit "$status"',
360 ),
361 ),
362 )
363
364
365def _cleanup_order_errors(source: str) -> list[str]:
366 """Bind cleanup receipts after every termination and lock-release fact."""
367 errors = []
368 checks = (*_worker_cleanup_checks(), *_controller_cleanup_checks())
369 for name, anchors in checks:
370 error = _order_error(source, name, 0, anchors)
371 if error:
372 errors.append(error)
373 return errors
374
375
376def _simple_worker_scenario_checks() -> tuple[tuple[str, tuple[str, ...], str], ...]:
377 """Return complete semantic tails for exit and pre-ready scenarios."""
378 pre_ready_negative = (
379 'if wait_for_status_file "$SELFTEST_CASE_DIR/ready.status" '
380 '"$SELFTEST_WORKER_PID"; then\n'
381 ' die "selftest: pre-ready hang unexpectedly became ready"\n'
382 " fi"
383 )
384 return (
385 (
386 "selftest_early_exit",
387 (
388 "start_image_lock_worker early-exit",
389 "reap_worker",
390 '[[ "$SELFTEST_REAP_STATUS" == "23" ]]',
391 "cleanup_image_lock_case",
392 "require_cleanup_receipt",
393 "clear_image_lock_case_traps",
394 ),
395 "early-exit",
396 ),
397 (
398 "selftest_pre_ready_hang",
399 (
400 "start_image_lock_worker pre-ready-hang",
401 pre_ready_negative,
402 "cleanup_image_lock_case",
403 "require_cleanup_receipt",
404 "clear_image_lock_case_traps",
405 ),
406 "pre-ready-hang",
407 ),
408 )
409
410
411def _contention_scenario_checks() -> tuple[tuple[str, tuple[str, ...], str], ...]:
412 """Return complete semantic tails for lock-contention scenarios."""
413 post_ready_negative = (
414 'if wait_for_status_file "$SELFTEST_CASE_DIR/done.status" '
415 '"$SELFTEST_WORKER_PID"; then\n'
416 ' die "selftest: build-hang worker unexpectedly completed"\n'
417 " fi"
418 )
419 contention_loop = (
420 "for ((attempt = 0; attempt < 20; ++attempt)); do\n"
421 ' [[ ! -e "$SELFTEST_CASE_DIR/build-entered.status" ]] ||\n'
422 ' die "selftest: forced rebuild bypassed the held lock"\n'
423 ' process_is_terminal "$SELFTEST_WORKER_PID" &&\n'
424 ' die "selftest: forced rebuild exited while the lock was held"\n'
425 " sleep 0.01\n"
426 " done"
427 )
428 return (
429 (
430 "selftest_forced_build_contention",
431 (
432 "hold_parent_lock",
433 "if fresh_lock_probe; then",
434 "start_image_lock_worker normal",
435 'wait_for_status_file "$SELFTEST_CASE_DIR/ready.status"',
436 contention_loop,
437 "release_parent_lock",
438 'wait_for_status_file "$SELFTEST_CASE_DIR/done.status"',
439 "reap_worker",
440 '[[ "$SELFTEST_REAP_STATUS" == "0" ]]',
441 "fresh_lock_probe || die "
442 '"selftest: fresh lock probe failed after worker completion"',
443 "cleanup_image_lock_case",
444 "require_cleanup_receipt",
445 "clear_image_lock_case_traps",
446 ),
447 "forced-build-contention",
448 ),
449 (
450 "selftest_post_ready_hang",
451 (
452 "start_image_lock_worker post-ready-build-hang",
453 'wait_for_status_file "$SELFTEST_CASE_DIR/ready.status"',
454 "release_parent_lock",
455 'wait_for_status_file "$SELFTEST_CASE_DIR/build-entered.status"',
456 post_ready_negative,
457 "cleanup_image_lock_case",
458 "require_cleanup_receipt",
459 "clear_image_lock_case_traps",
460 ),
461 "post-ready-hang",
462 ),
463 )
464
465
466def _signal_scenario_checks() -> tuple[tuple[str, tuple[str, ...], str], ...]:
467 """Return complete semantic tails for the two signal scenarios."""
468 ready_negative = (
469 'if wait_for_status_file "$case_dir/controller-ready.status" "$controller"; then\n'
470 ' die "selftest: delayed signal controller unexpectedly became ready"\n'
471 " fi"
472 )
473 return (
474 (
475 "selftest_signal_ready_timeout",
476 (
477 'start_signal_controller controller "$managed" "$case_dir" delay-controller-ready',
478 ready_negative,
479 "force_signal_controller_cleanup",
480 "require_force_cleanup_receipt",
481 ),
482 "signal-ready-timeout",
483 ),
484 (
485 "selftest_signal_cleanup",
486 (
487 "for signal in HUP INT TERM; do",
488 'case "$signal" in HUP) expected=129 ;; INT) expected=130 ;; '
489 "TERM) expected=143 ;; esac",
490 'wait_for_status_file "$case_dir/controller-ready.status" "$controller"',
491 'signal_owned_controller_group "$signal" "$controller"',
492 'bounded_process_terminal "$controller" "$SELFTEST_CONTROLLER_CLEANUP_STEPS"',
493 "verify_signal_controller_cleanup",
494 '[[ "$SELFTEST_REAP_STATUS" == "$expected" ]]',
495 "require_worker_cleanup_proof_file",
496 "require_controller_cleanup_receipt_file",
497 " done",
498 ),
499 "signal-cleanup",
500 ),
501 )
502
503
504def _scenario_order_errors(lock_source: str, signal_source: str) -> list[str]:
505 """Require each complete scenario proof before its final unique receipt."""
506 errors = []
507 checks = (
508 *_simple_worker_scenario_checks(),
509 *_contention_scenario_checks(),
510 *_signal_scenario_checks(),
511 )
512 for function, semantics, receipt in checks:
513 source = signal_source if function.startswith("selftest_signal_") else lock_source
514 body = _body(source, function, 0)
515 final = f"write_scenario_receipt {receipt}"
516 if body is None or not _ordered(body, (*semantics, final)):
517 errors.append(f"image lock receipt: {function} completion order drifted")
518 continue
519 executable = [
520 line.strip()
521 for line in body.splitlines()
522 if line.strip() and not line.lstrip().startswith("#")
523 ]
524 if not executable or executable[-1] != final:
525 errors.append(f"image lock receipt: {function} receipt is not its final action")
526 return errors
527
528
529def _dispatch_order_errors(lock_source: str, receipt_source: str, cases_source: str) -> list[str]:
530 """Bind exact scenario identities, count, file proof, and dispatcher latch."""
531 calls = (
532 'run_image_lock_scenario early-exit selftest_early_exit "$tmp"',
533 'run_image_lock_scenario pre-ready-hang selftest_pre_ready_hang "$tmp"',
534 'run_image_lock_scenario forced-build-contention selftest_forced_build_contention "$tmp"',
535 'run_image_lock_scenario post-ready-hang selftest_post_ready_hang "$tmp"',
536 'run_image_lock_scenario signal-ready-timeout selftest_signal_ready_timeout "$tmp"',
537 'run_image_lock_scenario signal-cleanup selftest_signal_cleanup "$tmp"',
538 )
539 dispatcher = _body(cases_source, "run_managed_image_lock_suite", 0)
540 runner = _body(lock_source, "run_image_lock_scenario", 0)
541 verifier = _body(receipt_source, "verify_scenario_receipt_files", 0)
542 errors = []
543 if dispatcher is None or not _ordered(
544 dispatcher, (*calls, "verify_image_lock_suite_receipts", "SELFTEST_DISPATCH_COMPLETE=1")
545 ):
546 errors.append("image lock receipt: dispatcher scenario set or latch drifted")
547 runner_order = (
548 '"$scenario" "$tmp"',
549 "require_scenario_receipt",
550 "SELFTEST_SUITE_RECEIPTS+=",
551 "SELFTEST_SUITE_COUNT",
552 )
553 if runner is None or not _ordered(runner, runner_order):
554 errors.append("image lock receipt: scenario runner accounting drifted")
555 verifier_order = (
556 "SELFTEST_FILE_RECEIPTS_VERIFIED=0",
557 '[[ "$#" == "6" ]]',
558 "require_scenario_receipt",
559 "find ",
560 '[[ "$count" == "6" ]]',
561 "SELFTEST_FILE_RECEIPTS_VERIFIED=1",
562 )
563 if verifier is None or not _ordered(verifier, verifier_order):
564 errors.append("image lock receipt: independent receipt verifier drifted")
565 return errors
566
567
568def errors(
569 image_source: str,
570 receipt_source: str,
571 selftest_source: str,
572 cases_source: str,
573 signal_source: str,
574) -> list[str]:
575 """Return completion-accounting findings for the image-lock implementation."""
576 main_names = ("build_locked", "main")
577 receipt_helper_names = (
578 "expected_image_lock_suite_receipts",
579 "scenario_receipt_value",
580 "validate_scenario_receipt_directory",
581 "write_scenario_receipt",
582 "require_scenario_receipt",
583 "verify_scenario_receipt_files",
584 "expected_cleanup_receipt",
585 "expected_force_cleanup_receipt",
586 "parent_lock_fd_is_closed",
587 "require_cleanup_receipt",
588 "require_force_cleanup_receipt",
589 "write_worker_cleanup_proof_file",
590 "require_worker_cleanup_proof_file",
591 "write_controller_cleanup_receipt_file",
592 "require_controller_cleanup_receipt_file",
593 )
594 lock_helper_names = (
595 "reap_worker",
596 "reap_controller",
597 "release_parent_lock",
598 "fresh_lock_probe",
599 "assert_no_surviving_descendants",
600 "cleanup_image_lock_case",
601 "force_signal_controller_cleanup",
602 "verify_signal_controller_cleanup",
603 "verify_image_lock_suite_receipts",
604 "run_image_lock_scenario",
605 "dispatch_image_lock_selftest",
606 "selftest_early_exit",
607 "selftest_pre_ready_hang",
608 "selftest_forced_build_contention",
609 "selftest_post_ready_hang",
610 )
611 signal_helper_names = (
612 "selftest_signal_ready_timeout",
613 "selftest_signal_cleanup",
614 )
615 return (
616 _function_errors(image_source, main_names, 2)
617 + _function_errors(cases_source, ("cmd_selftest",), 0)
618 + _function_errors(receipt_source, receipt_helper_names, 0)
619 + _function_errors(selftest_source, lock_helper_names, 0)
620 + _function_errors(cases_source, ("run_managed_image_lock_suite",), 0)
621 + _function_errors(signal_source, signal_helper_names, 0)
622 + _main_order_errors(image_source, cases_source)
623 + _cleanup_order_errors(selftest_source)
624 + _scenario_order_errors(selftest_source, signal_source)
625 + _dispatch_order_errors(selftest_source, receipt_source, cases_source)
626 )