[Merge] (MGPipe, P5): land j0 rounds 2-3 - the split-lane private logs wired into CI and the skipped-selection control failure (ID-62)

This commit is contained in:
2026-09-16 10:15:58 -04:00
9 changed files with 156 additions and 36 deletions
+6 -4
View File
@@ -1043,9 +1043,9 @@ jobs:
# THE RUNTIME HALF OF "THIS IS REALLY A SPLIT BUILD". The build-level nm check in # THE RUNTIME HALF OF "THIS IS REALLY A SPLIT BUILD". The build-level nm check in
# build-linux-split proves the library CARRIES MG_Remote; this proves the transport # build-linux-split proves the library CARRIES MG_Remote; this proves the transport
# RESOLVED in a process of this lane. ConfigLoader::InitTransport logs one INFO line when it # RESOLVED in a process of this lane. ConfigLoader::InitTransport logs one INFO line when it
# selects InProcess, and the DirectGLES.Split.PersistentMapArm. entry is the one Split entry # selects InProcess, and the DirectGLES.Split.PersistentMapArm. entry retains its private
# with a MOBILEGL_LOG_FILE_PATH of its own (nothing else writes it, so the grep means what it # counting log (all Split entries now have private paths; nothing else writes this file).
# says). The line is written during bring-up, before any scenario decides to skip, so this # The line is written during bring-up, before any scenario decides to skip, so this
# check is live from the day the lanes land rather than from the day they stop skipping. # check is live from the day the lanes land rather than from the day they stop skipping.
- name: The split lane really resolved the transport - name: The split lane really resolved the transport
working-directory: build-split working-directory: build-split
@@ -1106,7 +1106,9 @@ jobs:
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: integration-split-logs name: integration-split-logs
path: build-split/MobileGL/MG_IntegrationTest/*.log* path: |
build-split/MobileGL/MG_IntegrationTest/*.log*
build-split/MobileGL/MG_IntegrationTest/split-logs/*.log
if-no-files-found: warn if-no-files-found: warn
- name: Upload core dumps - name: Upload core dumps
+5 -2
View File
@@ -1950,7 +1950,6 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
gtest_discover_tests(MobileGLIntegrationTest gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "DirectGLES.Split.PersistentMapArm." TEST_PREFIX "DirectGLES.Split.PersistentMapArm."
TEST_LIST MGL_SPLIT_ARM_TESTS
TEST_FILTER "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares" TEST_FILTER "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares"
DISCOVERY_TIMEOUT 30 DISCOVERY_TIMEOUT 30
PROPERTIES PROPERTIES
@@ -1984,8 +1983,9 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
"MGITEST_SMALL_RING_LANE=1" "MOBILEGL_IPC_RING_MB=1" "MOBILEGL_IPC_STAGE_MB=1" "MGITEST_SMALL_RING_LANE=1" "MOBILEGL_IPC_RING_MB=1" "MOBILEGL_IPC_STAGE_MB=1"
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV})
foreach(mglItestSmallRingScenario ClearThenReadPixelsScenario TriangleScenario set(MGL_SPLIT_SMALL_RING_SCENARIOS ClearThenReadPixelsScenario TriangleScenario
PersistentCoherentMapScenario) PersistentCoherentMapScenario)
foreach(mglItestSmallRingScenario IN LISTS MGL_SPLIT_SMALL_RING_SCENARIOS)
gtest_discover_tests(MobileGLIntegrationTest gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "DirectGLES.Split.SmallRing." TEST_PREFIX "DirectGLES.Split.SmallRing."
TEST_LIST "MGL_SPLIT_SMALL_${mglItestSmallRingScenario}_TESTS" TEST_LIST "MGL_SPLIT_SMALL_${mglItestSmallRingScenario}_TESTS"
@@ -2001,9 +2001,12 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
configure_file(Harness/SplitLogPaths.cmake.in SplitLogPaths.cmake @ONLY) configure_file(Harness/SplitLogPaths.cmake.in SplitLogPaths.cmake @ONLY)
set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES
"${CMAKE_CURRENT_BINARY_DIR}/SplitLogPaths.cmake") "${CMAKE_CURRENT_BINARY_DIR}/SplitLogPaths.cmake")
# Python is mandatory for this split integration gate (also used by the CI controls).
# Do not silently omit the ownership check when the interpreter is unavailable.
find_package(Python3 REQUIRED COMPONENTS Interpreter) find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_test(NAME SplitLogPaths.PrivateAndDistinct add_test(NAME SplitLogPaths.PrivateAndDistinct
COMMAND "${Python3_EXECUTABLE}" COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/Harness/split_log_paths.py" "${CMAKE_CURRENT_SOURCE_DIR}/Harness/split_log_paths.py"
check "${CMAKE_CTEST_COMMAND}" "${CMAKE_BINARY_DIR}") check "${CMAKE_CTEST_COMMAND}" "${CMAKE_BINARY_DIR}")
set_tests_properties(SplitLogPaths.PrivateAndDistinct PROPERTIES LABELS "integration-split")
endif() endif()
@@ -5,7 +5,7 @@ foreach(entry IN LISTS MGL_SPLIT_CLEAR_TESTS MGL_SPLIT_TRIANGLE_TESTS MGL_SPLIT_
set_tests_properties("${entry}" PROPERTIES ENVIRONMENT set_tests_properties("${entry}" PROPERTIES ENVIRONMENT
"MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log") "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log")
endforeach() endforeach()
foreach(scenario ClearThenReadPixelsScenario TriangleScenario PersistentCoherentMapScenario) foreach(scenario @MGL_SPLIT_SMALL_RING_SCENARIOS@)
foreach(entry IN LISTS MGL_SPLIT_SMALL_${scenario}_TESTS) foreach(entry IN LISTS MGL_SPLIT_SMALL_${scenario}_TESTS)
set_tests_properties("${entry}" PROPERTIES ENVIRONMENT set_tests_properties("${entry}" PROPERTIES ENVIRONMENT
"MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log") "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log")
@@ -5,6 +5,7 @@ from pathlib import Path
import re import re
import subprocess import subprocess
import sys import sys
import xml.etree.ElementTree as ET
def paths(document): def paths(document):
@@ -14,7 +15,7 @@ def paths(document):
props = {p["name"]: p["value"] for p in test.get("properties", [])} props = {p["name"]: p["value"] for p in test.get("properties", [])}
values = [v.split("=", 1)[1] for v in props.get("ENVIRONMENT", []) values = [v.split("=", 1)[1] for v in props.get("ENVIRONMENT", [])
if v.startswith("MOBILEGL_LOG_FILE_PATH=")] if v.startswith("MOBILEGL_LOG_FILE_PATH=")]
is_split = "integration-split" in props.get("LABELS", []) is_split = test["name"].startswith("DirectGLES.Split.")
if is_split and (len(values) != 1 or not values[0]): if is_split and (len(values) != 1 or not values[0]):
raise ValueError(f"{test['name']}: requires exactly one nonempty MOBILEGL_LOG_FILE_PATH") raise ValueError(f"{test['name']}: requires exactly one nonempty MOBILEGL_LOG_FILE_PATH")
for value in values: for value in values:
@@ -47,13 +48,36 @@ def main():
if mode == "reset": if mode == "reset":
for path in selected.values(): for path in selected.values():
Path(path).unlink(missing_ok=True) Path(path).unlink(missing_ok=True)
elif mode == "results":
cases = ET.parse(sys.argv[4]).getroot().findall(".//testcase")
label = sys.argv[5]
by_name = {}
for case in cases:
by_name.setdefault(case.get("name"), []).append(case)
skipped = sum(any(c.find("skipped") is not None for c in by_name.get(n, []))
for n in selected)
# ID-62: pre-flight Fatal is not evidence that a selected entry ran.
if skipped:
raise ValueError(f"{label} control: the knob killed the pre-flight, not the entry - "
f"{skipped} selected entries skipped")
missing = sum(len(by_name.get(n, [])) != 1 or
by_name[n][0].get("status") in ("notrun", "disabled") for n in selected)
if missing:
raise ValueError(f"{label} control: {missing} selected entries did not run")
not_failed = sum(c.find("failure") is None or c.get("status") != "fail"
for n in selected for c in by_name[n])
if not_failed:
raise ValueError(f"{label} control: {not_failed} selected entries did not fail")
elif mode == "evidence": elif mode == "evidence":
missing = []
for name, path in selected.items(): for name, path in selected.items():
if Path(path).is_file() and re.search(sys.argv[4], Path(path).read_text(errors="replace")): if Path(path).is_file() and re.search(sys.argv[4], Path(path).read_text(errors="replace")):
print(f"private-log evidence: {name}: {path}") print(f"private-log evidence: {name}: {path}")
return else:
raise ValueError("E1 FAILED: selected private logs lack expected Fatal{BarrierViolation, \"<slot>\"} line: " missing.append(f"{name} ({path})")
+ ", ".join(f"{n} ({p})" for n, p in selected.items())) if missing:
raise ValueError("E1 FAILED: selected private logs lack expected Fatal{BarrierViolation, \"<slot>\"} line: "
+ ", ".join(missing))
else: else:
raise ValueError(f"unknown mode: {mode}") raise ValueError(f"unknown mode: {mode}")
@@ -61,5 +85,5 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: try:
main() main()
except (ValueError, OSError, subprocess.CalledProcessError) as error: except (ValueError, OSError, ET.ParseError, subprocess.CalledProcessError) as error:
sys.exit(f"SplitLogPaths FAILED: {error}") sys.exit(f"SplitLogPaths FAILED: {error}")
+5 -1
View File
@@ -19,7 +19,7 @@
set -u set -u
HERE="$(cd "$(dirname "$0")" && pwd)" HERE="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)" WORK="$(mktemp -d)" || exit 1
trap 'rm -rf "${WORK}"' EXIT trap 'rm -rf "${WORK}"' EXIT
STUB_DIR="${WORK}/stub" STUB_DIR="${WORK}/stub"
@@ -99,6 +99,10 @@ expect FAILED "a pull library passed the split retrace" -- run_retrace retr
echo echo
echo "smoke test: ${passes} passed, ${failures} failed" echo "smoke test: ${passes} passed, ${failures} failed"
# Keep the private-file cases on the entry point used by CI and the local gate.
if ! bash "${HERE}/testdata/split_private_log_smoke.sh"; then
failures=$((failures + 1))
fi
if [ "${failures}" -gt 0 ]; then if [ "${failures}" -gt 0 ]; then
echo "CONTROL_SMOKE_TEST_FAILED" echo "CONTROL_SMOKE_TEST_FAILED"
exit 1 exit 1
+44 -12
View File
@@ -5,17 +5,19 @@
# X = revert the message check in each control, i.e. put the controls back in the state ID-46 # X = revert the message check in each control, i.e. put the controls back in the state ID-46
# finding 8 found them in: a non-zero ctest exit is accepted whatever the failure was. # finding 8 found them in: a non-zero ctest exit is accepted whatever the failure was.
# #
# The smoke test must then FAIL, and it must fail on the two cases that exist for this defect - # The smoke test must then FAIL on the cases that exist for this defect -
# "unrelated failure with a non-empty selection" and "red without the transport-resolution # "unrelated failure with a non-empty selection" and "red without the transport-resolution
# message" - and not merely somewhere. A smoke test that goes red for any other reason when the # message", plus the missing private-file Fatal - and not merely somewhere. A smoke test that goes
# evidence check is removed would not be pinning the evidence check. # red for any other reason when the evidence check is removed would not be pinning the evidence check.
set -u set -u
HERE="$(cd "$(dirname "$0")" && pwd)" HERE="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)" WORK="$(mktemp -d)" || exit 1
trap 'cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"; cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"; rm -rf "${WORK}"' EXIT HELPER="${HERE}/../../MobileGL/MG_IntegrationTest/Harness/split_log_paths.py"
trap 'cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"; cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"; cp "${WORK}/helper.orig" "${HELPER}"; rm -rf "${WORK}"' EXIT
cp "${HERE}/split_negative_controls.sh" "${WORK}/split.orig" cp "${HERE}/split_negative_controls.sh" "${WORK}/split.orig"
cp "${HERE}/retrace_pull_library_control.sh" "${WORK}/retrace.orig" cp "${HERE}/retrace_pull_library_control.sh" "${WORK}/retrace.orig"
cp "${HELPER}" "${WORK}/helper.orig"
echo "=== baseline: the smoke test must be GREEN before anything is perturbed" echo "=== baseline: the smoke test must be GREEN before anything is perturbed"
if ! bash "${HERE}/control_smoke_test.sh" > "${WORK}/before.log" 2>&1; then if ! bash "${HERE}/control_smoke_test.sh" > "${WORK}/before.log" 2>&1; then
@@ -27,27 +29,35 @@ tail -1 "${WORK}/before.log"
echo echo
echo "=== perturbation: remove the evidence check from both controls" echo "=== perturbation: remove the evidence check from both controls"
python3 - "${HERE}/split_negative_controls.sh" "${HERE}/retrace_pull_library_control.sh" <<'PY' python3 - "${HERE}/split_negative_controls.sh" "${HERE}/retrace_pull_library_control.sh" <<'PY' || exit 1
import sys import sys
import re
split, retrace = sys.argv[1], sys.argv[2] split, retrace = sys.argv[1], sys.argv[2]
for path, needle in ((split, 'grep -qE "${evidence}"'), (retrace, 'grep -qF "${EVIDENCE}"')): for path, needle in ((split, 'grep -qE "${evidence}"'), (retrace, 'grep -qF "${EVIDENCE}"')):
text = open(path).read() text = open(path).read()
if path == split:
text, count = re.subn(
r'(?m)^ python3 "\$\{log_helper\}" evidence[^\n]*\n[^\n]*\|\| exit 1\n',
' : "private-file evidence check removed by red-check"\n', text)
if count != 1:
raise SystemExit(f"expected exactly one private-file evidence check in {path}, found {count}")
out, hit = [], 0 out, hit = [], 0
for line in text.splitlines(keepends=True): for line in text.splitlines(keepends=True):
if needle in line and line.lstrip().startswith('if ! '): if needle in line and line.lstrip().startswith(('if ! ', 'elif ! ')):
indent = line[:len(line) - len(line.lstrip())] indent = line[:len(line) - len(line.lstrip())]
out.append(f"{indent}if false; then\n") keyword = 'elif' if line.lstrip().startswith('elif ') else 'if'
out.append(f"{indent}{keyword} false; then\n")
hit += 1 hit += 1
else: else:
out.append(line) out.append(line)
if hit != 1: if hit != 1:
raise SystemExit(f"expected exactly one evidence check in {path}, found {hit}") raise SystemExit(f"expected exactly one evidence check in {path}, found {hit}")
open(path, 'w').write(''.join(out)) open(path, 'w').write(''.join(out))
print("both evidence checks reverted to 'any non-zero ctest exit is accepted'") print("E1 private-file, E3(a) and retrace evidence checks reverted to 'any non-zero ctest exit is accepted'")
PY PY
echo echo
echo "=== the smoke test on the reverted controls (it MUST be red, on those two cases)" echo "=== the smoke test on the reverted controls (it MUST be red on the pinned cases)"
bash "${HERE}/control_smoke_test.sh" > "${WORK}/after.log" 2>&1 bash "${HERE}/control_smoke_test.sh" > "${WORK}/after.log" 2>&1
rc=$? rc=$?
cat "${WORK}/after.log" cat "${WORK}/after.log"
@@ -61,11 +71,33 @@ fi
missed=0 missed=0
grep -q "NOT OK unrelated failure with a non-empty selection" "${WORK}/after.log" || missed=1 grep -q "NOT OK unrelated failure with a non-empty selection" "${WORK}/after.log" || missed=1
grep -q "NOT OK red without the transport-resolution message" "${WORK}/after.log" || missed=1 grep -q "NOT OK red without the transport-resolution message" "${WORK}/after.log" || missed=1
grep -qF "NOT OK missing-fatal: control must report FAILED for its own reason" "${WORK}/after.log" || missed=1
if [ "${missed}" -ne 0 ]; then if [ "${missed}" -ne 0 ]; then
echo echo
echo "RED-CHECK FAILED: the smoke test went red, but not on the two cases the evidence check exists for." echo "RED-CHECK FAILED: the smoke test went red, but not on the unrelated split, unrelated retrace and missing private-Fatal cases."
exit 1 exit 1
fi fi
echo echo
echo "P5_T1_CONTROL_SMOKE_REDCHECK_OK - removing the evidence check reds exactly the two cases that pin it" cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"
cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"
echo "=== ID-62 perturbation: remove only the skip check"
python3 - "${HELPER}" <<'PY' || exit 1
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text()
needle = ' if skipped:\n'
if text.count(needle) != 1:
raise SystemExit('expected exactly one skip check')
path.write_text(text.replace(needle, ' if False: # skip check removed by red-check\n'))
PY
bash "${HERE}/control_smoke_test.sh" > "${WORK}/skips.log" 2>&1
rc=$?
cat "${WORK}/skips.log"
if [ "${rc}" -eq 0 ] || ! grep -qFx 'NOT OK skipped-selection: control must report FAILED for its own reason' "${WORK}/skips.log"; then
echo 'RED-CHECK FAILED: removing the skip check must red the skipped-selection message assertion'
exit 1
fi
echo 'ID-62 red-once: removing only the skip check made skipped-selection red (smoke rc=1)'
echo "P5_T1_CONTROL_SMOKE_REDCHECK_OK - evidence and skipped-selection checks each made their smoke cases red"
+14 -5
View File
@@ -42,8 +42,7 @@
set -u set -u
if [ "${1:-}" = "--self-test" ]; then if [ "${1:-}" = "--self-test" ]; then
bash "$(dirname "$0")/control_smoke_test.sh" && bash "$(dirname "$0")/control_smoke_test.sh"
bash "$(dirname "$0")/testdata/split_private_log_smoke.sh"
exit $? exit $?
fi fi
@@ -65,7 +64,8 @@ python3 "${log_helper}" check "${CTEST}" "$PWD" || exit 1
# runtime fact inside each test process (MG_Config::Transport, ClientSession::Active(), # runtime fact inside each test process (MG_Config::Transport, ClientSession::Active(),
# ImplementedVerbCount(), read by Harness/SplitRuntimePeek), so the only honest way to ask it from a # ImplementedVerbCount(), read by Harness/SplitRuntimePeek), so the only honest way to ask it from a
# shell is to look at what the entries DID. # shell is to look at what the entries DID.
"${CTEST}" -L integration-split -j 4 --no-tests=error --output-junit "${junit}" # The metadata ownership test shares the label but cannot arm runtime controls.
"${CTEST}" -L integration-split -R '^DirectGLES\.Split\.' -j 4 --no-tests=error --output-junit "${junit}"
baseline_rc=$? baseline_rc=$?
if [ ! -f "${junit}" ]; then if [ ! -f "${junit}" ]; then
@@ -115,10 +115,17 @@ run_control() {
python3 "${log_helper}" reset "${manifest}" "${filter}" || exit 1 python3 "${log_helper}" reset "${manifest}" "${filter}" || exit 1
out="${CONTROL_TMPDIR}/control-output.txt" out="${CONTROL_TMPDIR}/control-output.txt"
env "$@" "${CTEST}" --output-on-failure -L integration-split -R "${filter}" --no-tests=error > "${out}" 2>&1 result="${CONTROL_TMPDIR}/control.xml"
rm -f "${result}"
env "$@" "${CTEST}" --output-on-failure -L integration-split -R "${filter}" --no-tests=error --output-junit "${result}" > "${out}" 2>&1
control_rc=$? control_rc=$?
cat "${out}" cat "${out}"
# Inspect JUnit before exit status or private Fatal: a skipped pre-flight can carry both.
label='E3(a)'
[ "${evidence}" != private-barrier-fatal ] || label=E1
python3 "${log_helper}" results "${manifest}" "${filter}" "${result}" "${label}" || exit 1
if [ "${control_rc}" -eq 0 ]; then if [ "${control_rc}" -eq 0 ]; then
echo "::error::${name} left ${matched} split entries GREEN, so the knob it turns is not load-bearing and the gate it controls proves nothing." echo "::error::${name} left ${matched} split entries GREEN, so the knob it turns is not load-bearing and the gate it controls proves nothing."
exit 1 exit 1
@@ -136,7 +143,9 @@ run_control() {
echo "${name} turned ${matched} selected entries red, and the red carries the scenario's own diagnostic, as it must" echo "${name} turned ${matched} selected entries red, and the red carries the scenario's own diagnostic, as it must"
} }
# E1: c1 ClientSession::Post emits MGLOG_F Fatal{BarrierViolation, "<slot>"}. # E1: c1 ClientSession::EmitAndWait emits MGLOG_F Fatal{BarrierViolation, "<slot>"}.
# This asserts an observed overlap with the applier, which is timing-dependent.
# Without that Fatal in a fresh selected file E1 fails; pixel/status fallbacks do not count.
# Keep the ID-53-approved SmallRing selection as well as the default lane. # Keep the ID-53-approved SmallRing selection as well as the default lane.
run_control "negative control E1 (MOBILEGL_IPC_VERB_BARRIER=0)" \ run_control "negative control E1 (MOBILEGL_IPC_VERB_BARRIER=0)" \
'DirectGLES\.Split\.(SmallRing\.)?(Triangle|ClearThenReadPixels)' \ 'DirectGLES\.Split\.(SmallRing\.)?(Triangle|ClearThenReadPixels)' \
+14 -2
View File
@@ -7,7 +7,7 @@ trap 'rm -rf "${WORK}"' EXIT
cp "${HERE}/testdata/stub_ctest.sh" "${WORK}/ctest" cp "${HERE}/testdata/stub_ctest.sh" "${WORK}/ctest"
chmod +x "${WORK}/ctest" chmod +x "${WORK}/ctest"
passes=0 passes=0
for mode in missing-fatal stdout-fatal stale-fatal evidence e3-unrelated; do for mode in missing-fatal stdout-fatal stale-fatal evidence e3-unrelated skipped-selection notrun-selection missing-selection partial-fatal wrong-fatal; do
mkdir -p "${WORK}/${mode}" mkdir -p "${WORK}/${mode}"
rc=0 rc=0
STUB_MODE="${mode}" CTEST="${WORK}/ctest" CONTROL_TMPDIR="${WORK}/${mode}" \ STUB_MODE="${mode}" CTEST="${WORK}/ctest" CONTROL_TMPDIR="${WORK}/${mode}" \
@@ -16,10 +16,22 @@ for mode in missing-fatal stdout-fatal stale-fatal evidence e3-unrelated; do
[ "${rc}" = 0 ] && grep -q "negative control E3(a).*scenario's own diagnostic" "${WORK}/${mode}.out" || { [ "${rc}" = 0 ] && grep -q "negative control E3(a).*scenario's own diagnostic" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK private-file Fatal must PASS"; exit 1; cat "${WORK}/${mode}.out"; echo "NOT OK private-file Fatal must PASS"; exit 1;
} }
grep -qFx "private-log evidence: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels: ${WORK}/${mode}/entry.log" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK private-file evidence line must name the selected entry and path"; exit 1;
}
else else
message='E1 FAILED: selected private logs lack expected Fatal' message='E1 FAILED: selected private logs lack expected Fatal'
[ "${mode}" != e3-unrelated ] || message='FAILED: red lacks its persistent-map push diagnostic' [ "${mode}" != e3-unrelated ] || message='FAILED: red lacks its persistent-map push diagnostic'
[ "${rc}" != 0 ] && grep -q "${message}" "${WORK}/${mode}.out" || { case "${mode}" in
skipped-selection) message='SplitLogPaths FAILED: E1 control: the knob killed the pre-flight, not the entry - 1 selected entries skipped' ;;
notrun-selection|missing-selection) message='SplitLogPaths FAILED: E1 control: 1 selected entries did not run' ;;
esac
if [[ "${mode}" = *-selection ]]; then
match=(-qFx)
else
match=(-qF)
fi
[ "${rc}" != 0 ] && grep "${match[@]}" "${message}" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK ${mode}: control must report FAILED for its own reason"; exit 1; cat "${WORK}/${mode}.out"; echo "NOT OK ${mode}: control must report FAILED for its own reason"; exit 1;
} }
fi fi
+38 -4
View File
@@ -15,6 +15,11 @@
# stdout-fatal Fatal exists only on stdout, never in the private file # stdout-fatal Fatal exists only on stdout, never in the private file
# stale-fatal Fatal exists before reset, never from this control run # stale-fatal Fatal exists before reset, never from this control run
# e3-unrelated E1 has its private Fatal; E3(a) fails for an unrelated reason # e3-unrelated E1 has its private Fatal; E3(a) fails for an unrelated reason
# skipped-selection knob kills pre-flight; private Fatal exists, entry skips, ctest exits 0
# notrun-selection selected entry was not run, despite a private Fatal
# missing-selection selected entry is absent from the result XML
# partial-fatal two entries fail, but only the first has its expected private Fatal
# wrong-fatal entry fails with a different Fatal in its private file
# green baseline green; the control's own run PASSES (the knob is not load-bearing) # green baseline green; the control's own run PASSES (the knob is not load-bearing)
# red-baseline the baseline itself has a failed entry # red-baseline the baseline itself has a failed entry
# all-skipped the baseline is entirely skipped (the disarmed lane, a legitimate exit 0) # all-skipped the baseline is entirely skipped (the disarmed lane, a legitimate exit 0)
@@ -44,10 +49,31 @@ emit_listing() {
return return
fi fi
echo " Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels" echo " Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels"
echo "Total Tests: 1" if [ "${mode}" = partial-fatal ]; then
echo " Test #2: DirectGLES.Split.TriangleScenario.SecondEntry"
echo "Total Tests: 2"
else
echo "Total Tests: 1"
fi
} }
write_junit() { write_junit() {
if [ "${MOBILEGL_IPC_VERB_BARRIER:-1}" = 0 ] || [ "${MOBILEGL_IPC_PERSISTENT_BLOCK_KB:-64}" = 0 ]; then
entry=DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels
[ "${MOBILEGL_IPC_PERSISTENT_BLOCK_KB:-64}" != 0 ] || entry=DirectGLES.Split.PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw
body="<testcase name=\"${entry}\" status=\"fail\"><failure message=\"control red\"/></testcase>"
if [ "${mode}" = partial-fatal ] && [ "${MOBILEGL_IPC_VERB_BARRIER:-1}" = 0 ]; then
body="${body}<testcase name=\"DirectGLES.Split.TriangleScenario.SecondEntry\" status=\"fail\"><failure/></testcase>"
fi
case "${mode}" in
skipped-selection) body="<testcase name=\"${entry}\" status=\"notrun\"><skipped/></testcase>" ;;
notrun-selection) body="<testcase name=\"${entry}\" status=\"notrun\"/>" ;;
missing-selection) body='' ;;
green) body="<testcase name=\"${entry}\" status=\"run\"/>" ;;
esac
printf '%s\n' "<testsuite>${body}</testsuite>" > "$1"
return
fi
case "${mode}" in case "${mode}" in
red-baseline) red-baseline)
body='<testcase name="DirectGLES.Split.TriangleScenario.AVboBackedTriangleReachesReadPixels" status="failed"><failure message="already red"/></testcase>' body='<testcase name="DirectGLES.Split.TriangleScenario.AVboBackedTriangleReachesReadPixels" status="failed"><failure message="already red"/></testcase>'
@@ -65,7 +91,7 @@ write_junit() {
# Model the library file sink separately from ctest stdout (ID-53). # Model the library file sink separately from ctest stdout (ID-53).
log="${CONTROL_TMPDIR}/entry.log" log="${CONTROL_TMPDIR}/entry.log"
if [ "${json_requested:-0}" = 1 ]; then if [ "${json_requested:-0}" = 1 ]; then
python3 -c 'import json, os; p=os.environ["CONTROL_TMPDIR"]; print(json.dumps({"tests": [{"name": "DirectGLES.Split."+n, "properties": [{"name": "LABELS", "value": ["integration-split"]}, {"name": "ENVIRONMENT", "value": ["MOBILEGL_LOG_FILE_PATH="+p+"/"+f]}]} for n,f in [("ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels", "entry.log"), ("PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw", "pmap.log")]]}))' python3 -c 'import json, os; p=os.environ["CONTROL_TMPDIR"]; entries=[("ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels", "entry.log"), ("PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw", "pmap.log")]; entries += [("TriangleScenario.SecondEntry", "second.log")] if os.environ["STUB_MODE"] == "partial-fatal" else []; print(json.dumps({"tests": [{"name": "DirectGLES.Split."+n, "properties": [{"name": "LABELS", "value": ["integration-split"]}, {"name": "ENVIRONMENT", "value": ["MOBILEGL_LOG_FILE_PATH="+p+"/"+f]}]} for n,f in entries]}))'
if [ "${mode}" = stale-fatal ]; then if [ "${mode}" = stale-fatal ]; then
echo 'Fatal{BarrierViolation, "DrawVbo"}' > "${log}" echo 'Fatal{BarrierViolation, "DrawVbo"}' > "${log}"
fi fi
@@ -79,22 +105,30 @@ fi
if [ -n "${junit}" ]; then if [ -n "${junit}" ]; then
write_junit "${junit}" write_junit "${junit}"
if [ "${MOBILEGL_IPC_VERB_BARRIER:-1}" != 0 ] && [ "${MOBILEGL_IPC_PERSISTENT_BLOCK_KB:-64}" != 0 ]; then
case "${mode}" in case "${mode}" in
red-baseline) echo "1/1 Test #1: ... ***Failed"; exit 8 ;; red-baseline) echo "1/1 Test #1: ... ***Failed"; exit 8 ;;
*) echo "100% tests passed, 0 tests failed out of 1"; exit 0 ;; *) echo "100% tests passed, 0 tests failed out of 1"; exit 0 ;;
esac esac
fi
fi fi
# The control's own run. # The control's own run.
if [ "${MOBILEGL_IPC_VERB_BARRIER:-1}" = 0 ]; then if [ "${MOBILEGL_IPC_VERB_BARRIER:-1}" = 0 ]; then
case "${mode}" in case "${mode}" in
evidence|e3-unrelated) echo 'Fatal{BarrierViolation, "DrawVbo"}' > "${log}" ;; evidence|e3-unrelated|skipped-selection|notrun-selection|missing-selection|partial-fatal) echo 'Fatal{BarrierViolation, "DrawVbo"}' > "${log}" ;;
wrong-fatal) echo 'Fatal{ReplyMissing, "DrawVbo"}' > "${log}" ;;
missing-fatal) echo "library setup only; no fatal" > "${log}" ;; missing-fatal) echo "library setup only; no fatal" > "${log}" ;;
stdout-fatal) echo 'Fatal{BarrierViolation, "DrawVbo"}' ;; stdout-fatal) echo 'Fatal{BarrierViolation, "DrawVbo"}' ;;
esac esac
fi fi
case "${mode}" in case "${mode}" in
unrelated|missing-fatal|stdout-fatal|stale-fatal|e3-unrelated) skipped-selection|notrun-selection|missing-selection)
echo '1/1 Test #1: selected entry ... ***Skipped'
echo '100% tests passed, 0 tests failed out of 1'
exit 0
;;
unrelated|missing-fatal|stdout-fatal|stale-fatal|e3-unrelated|partial-fatal|wrong-fatal)
echo "1/1 Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels ...***Failed" echo "1/1 Test #1: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels ...***Failed"
echo "UNRELATED_CONTROL_FAILURE: the harness aborted in setup before the knob was read" echo "UNRELATED_CONTROL_FAILURE: the harness aborted in setup before the knob was read"
if [ "${MOBILEGL_IPC_PERSISTENT_BLOCK_KB:-64}" = 0 ]; then if [ "${MOBILEGL_IPC_PERSISTENT_BLOCK_KB:-64}" = 0 ]; then