mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-17 08:38:30 +09:00
[Fix] (scripts): make gen_pipe.py's expect_trip require the tripped guard's own message and a non-zero exit code, with an R-16 meta-control that an unrelated SystemExit or sys.exit(0) cannot pass as a trip - the any-SystemExit defect fb70704e fixed in the field-ownership generator, confirmed still present here by codex review finding 9
This commit is contained in:
+70
-12
@@ -1376,27 +1376,54 @@ def write(path, text, check, changed):
|
||||
handle.write(text)
|
||||
|
||||
|
||||
def expect_trip(name, fn):
|
||||
"""Runs one negative control; a gate that lets it through is the failure."""
|
||||
def expect_trip(name, because, fn, quiet=False):
|
||||
"""Runs one negative control; a gate that lets it through, or trips for a reason that is
|
||||
not ITS OWN, is the failure.
|
||||
|
||||
The first version of this function caught any SystemExit - including an unrelated
|
||||
diagnostic, and even a CLEAN sys.exit(0) - and asked nothing about which one, so the
|
||||
codex cross-family review's finding 9 could replace a control's callback with either and
|
||||
the "nine negative controls all trip" line stayed true while the guard it named never
|
||||
fired. gen_pipe_field_ownership.py's M-1 was the same defect in the sibling generator;
|
||||
this mirrors that fix.
|
||||
|
||||
`because` is a substring the control's OWN message must contain, and an exit code of 0
|
||||
can never count as a trip."""
|
||||
try:
|
||||
fn()
|
||||
except SystemExit as trip:
|
||||
print("gen_pipe: self-test %s: tripped as expected (%s)" % (name, str(trip).splitlines()[0][:100]))
|
||||
return 1
|
||||
print("gen_pipe: self-test %s: DID NOT TRIP" % name, file=sys.stderr)
|
||||
code = trip.code
|
||||
message = str(code) if code is not None else ""
|
||||
if code != 0 and because in message:
|
||||
if not quiet:
|
||||
print("gen_pipe: self-test %s: tripped as expected (%s)"
|
||||
% (name, message.splitlines()[0][:100]))
|
||||
return 1
|
||||
if not quiet:
|
||||
print("gen_pipe: self-test %s: tripped for SOMEONE ELSE'S reason:\n"
|
||||
" expected to contain: %s\n"
|
||||
" actually said (code=%r): %s"
|
||||
% (name, because, code, message.splitlines()[0][:200] if message else "<empty>"),
|
||||
file=sys.stderr)
|
||||
return 0
|
||||
if not quiet:
|
||||
print("gen_pipe: self-test %s: DID NOT TRIP" % name, file=sys.stderr)
|
||||
return 0
|
||||
|
||||
|
||||
def self_test(accessors):
|
||||
"""The negative controls (check_include_closure.py's shape): each gate must go red for
|
||||
its reason, and zero trips is itself an error."""
|
||||
its OWN reason, and zero trips is itself an error."""
|
||||
canned_struct = "struct Canned {\n Uint32 A;\n Uint32 B, C;\n Uint8 Pad0[3];\n void F() { return; }\n};\n"
|
||||
controls = [
|
||||
("struct member without F(...)",
|
||||
"member(s) with no F(...) in PipeFields.def",
|
||||
lambda: check_field_lists_cover_struct_members({"Canned": ["A", "B"]}, ["Canned"], [canned_struct])),
|
||||
("F(...) that is not a member",
|
||||
"F(...) name(s) that are not members",
|
||||
lambda: check_field_lists_cover_struct_members({"Canned": ["A", "B", "C", "D"]}, ["Canned"], [canned_struct])),
|
||||
("payload with no struct",
|
||||
"struct not found",
|
||||
lambda: check_field_lists_cover_struct_members({"Nowhere": ["A"]}, ["Nowhere"], [canned_struct])),
|
||||
]
|
||||
fill_text = read(os.path.join(PIPE_DIR, "FillPoints.def"))
|
||||
@@ -1404,16 +1431,24 @@ def self_test(accessors):
|
||||
field_row = re.compile(r"X\(\s*kDraw\s*,\s*GetBoundVertexArray\s*\)")
|
||||
if not verb_row.search(fill_text) or not field_row.search(fill_text):
|
||||
sys.exit("gen_pipe: self-test: FillPoints.def lost the rows the controls edit")
|
||||
controls.append(("verb missing from FillPoints.def", lambda: parse_fill_points(
|
||||
controls.append(("verb missing from FillPoints.def",
|
||||
"GLFunctionsTable member(s) without a verb row",
|
||||
lambda: parse_fill_points(
|
||||
accessors, text=verb_row.sub("", fill_text, count=1))))
|
||||
controls.append(("verb that is not a GLFunctionsTable member", lambda: parse_fill_points(
|
||||
controls.append(("verb that is not a GLFunctionsTable member",
|
||||
"verb(s) that are not GLFunctionsTable members",
|
||||
lambda: parse_fill_points(
|
||||
accessors, text=verb_row.sub("X(DrawArrays, kDraw) X(NotAVerb, kDraw)", fill_text, count=1))))
|
||||
controls.append(("field row naming a non-accessor", lambda: parse_fill_points(
|
||||
controls.append(("field row naming a non-accessor",
|
||||
"is not an accessor in Coverage.def",
|
||||
lambda: parse_fill_points(
|
||||
accessors, text=field_row.sub("X(kDraw, NotAnAccessor)", fill_text, count=1))))
|
||||
# The EMITTED list's own gate: a row naming a call that is not in PipeCalls.def would
|
||||
# generate an enumerator nothing can dispatch on.
|
||||
calls_for_control = parse_calls()
|
||||
controls.append(("emitted row naming a call that does not exist", lambda: gen_emitted_by(
|
||||
controls.append(("emitted row naming a call that does not exist",
|
||||
"which is not a call in PipeCalls.def",
|
||||
lambda: gen_emitted_by(
|
||||
[("GetViewport", "SetDynamicState")], calls_for_control, [("GetViewport", "NotACall")])))
|
||||
# P5 R-13.4's gate. The flags are now a GENERATED TABLE six packages read instead of six
|
||||
# hard-coded copies, so a token that is not an MGPipeCallFlags enumerator has to stop the
|
||||
@@ -1421,13 +1456,36 @@ def self_test(accessors):
|
||||
# kNone, the empty set, may not be OR'd with a real flag and quietly read as one.
|
||||
flag_typo = Call(1, "Canned", "MGPHandleOnly", "kScreen", ["kHasBlobb"])
|
||||
flag_kNone = Call(1, "Canned", "MGPHandleOnly", "kScreen", ["kNone", "kHasBlob"])
|
||||
flag_typo_because = "which is not an MGPipeCallFlags enumerator"
|
||||
controls.append(("call flag that is not an MGPipeCallFlags enumerator",
|
||||
flag_typo_because,
|
||||
lambda: check_call_flags_are_known([flag_typo])))
|
||||
controls.append(("kNone combined with a real flag",
|
||||
"combines kNone with",
|
||||
lambda: check_call_flags_are_known([flag_kNone])))
|
||||
|
||||
# R-16 meta-control (codex review finding 9; verified by execution in
|
||||
# p5-results/wave1-codex-verify.md §9). The exact perturbation there replaced the
|
||||
# flag-typo control's callback with `sys.exit("unrelated parser failure")` and,
|
||||
# separately, with `sys.exit(0)`; the OLD expect_trip counted both as "tripped as
|
||||
# expected" because it never looked at the message or the code. Both must be REJECTED
|
||||
# here, under the real control's own `because`. This is a quiet, unlisted assertion (not
|
||||
# one of the nine controls below) so it cannot itself inflate the trip count.
|
||||
# "Red once by doing X" = reverting expect_trip to `except SystemExit as trip: return 1`
|
||||
# (accept any SystemExit) - that alone turns each of these two checks from a silent pass
|
||||
# into the sys.exit below.
|
||||
if expect_trip("(R-16 meta-control) unrelated diagnostic standing in for the flag-typo guard",
|
||||
flag_typo_because, lambda: sys.exit("unrelated parser failure"), quiet=True) != 0:
|
||||
sys.exit("gen_pipe: self-test: expect_trip counted an UNRELATED SystemExit as the "
|
||||
"flag-typo guard's own trip - finding 9 / R-16 is back")
|
||||
if expect_trip("(R-16 meta-control) sys.exit(0) standing in for the flag-typo guard",
|
||||
flag_typo_because, lambda: sys.exit(0), quiet=True) != 0:
|
||||
sys.exit("gen_pipe: self-test: expect_trip counted sys.exit(0) as a trip - "
|
||||
"finding 9 / R-16 is back")
|
||||
|
||||
trips = 0
|
||||
for name, fn in controls:
|
||||
trips += expect_trip(name, fn)
|
||||
for name, because, fn in controls:
|
||||
trips += expect_trip(name, because, fn)
|
||||
# The positive control: the canned struct's exact list passes, and the parser sees the
|
||||
# padding member as padding and the function as not a member.
|
||||
check_field_lists_cover_struct_members({"Canned": ["A", "B", "C"]}, ["Canned"], [canned_struct])
|
||||
|
||||
Reference in New Issue
Block a user