diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca022ed7..4c3e839f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1043,9 +1043,9 @@ jobs: # 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 # 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 - # with a MOBILEGL_LOG_FILE_PATH of its own (nothing else writes it, so the grep means what it - # says). The line is written during bring-up, before any scenario decides to skip, so this + # selects InProcess, and the DirectGLES.Split.PersistentMapArm. entry retains its private + # counting log (all Split entries now have private paths; nothing else writes this file). + # 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. - name: The split lane really resolved the transport working-directory: build-split @@ -1106,7 +1106,9 @@ jobs: uses: actions/upload-artifact@v7 with: 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 - name: Upload core dumps diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 5c8e6384..af59f21a 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -1950,7 +1950,6 @@ if (MOBILEGL_BUILD_DISAGGREGATED) gtest_discover_tests(MobileGLIntegrationTest TEST_PREFIX "DirectGLES.Split.PersistentMapArm." - TEST_LIST MGL_SPLIT_ARM_TESTS TEST_FILTER "PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares" DISCOVERY_TIMEOUT 30 PROPERTIES @@ -1984,8 +1983,9 @@ if (MOBILEGL_BUILD_DISAGGREGATED) "MGITEST_SMALL_RING_LANE=1" "MOBILEGL_IPC_RING_MB=1" "MOBILEGL_IPC_STAGE_MB=1" ${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_COMMON_ENV}) - foreach(mglItestSmallRingScenario ClearThenReadPixelsScenario TriangleScenario + set(MGL_SPLIT_SMALL_RING_SCENARIOS ClearThenReadPixelsScenario TriangleScenario PersistentCoherentMapScenario) + foreach(mglItestSmallRingScenario IN LISTS MGL_SPLIT_SMALL_RING_SCENARIOS) gtest_discover_tests(MobileGLIntegrationTest TEST_PREFIX "DirectGLES.Split.SmallRing." TEST_LIST "MGL_SPLIT_SMALL_${mglItestSmallRingScenario}_TESTS" @@ -2001,9 +2001,12 @@ if (MOBILEGL_BUILD_DISAGGREGATED) configure_file(Harness/SplitLogPaths.cmake.in SplitLogPaths.cmake @ONLY) set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${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) add_test(NAME SplitLogPaths.PrivateAndDistinct COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/Harness/split_log_paths.py" check "${CMAKE_CTEST_COMMAND}" "${CMAKE_BINARY_DIR}") + set_tests_properties(SplitLogPaths.PrivateAndDistinct PROPERTIES LABELS "integration-split") endif() diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in index d8cdd949..532ba453 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in +++ b/MobileGL/MG_IntegrationTest/Harness/SplitLogPaths.cmake.in @@ -5,7 +5,7 @@ foreach(entry IN LISTS MGL_SPLIT_CLEAR_TESTS MGL_SPLIT_TRIANGLE_TESTS MGL_SPLIT_ set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log") endforeach() -foreach(scenario ClearThenReadPixelsScenario TriangleScenario PersistentCoherentMapScenario) +foreach(scenario @MGL_SPLIT_SMALL_RING_SCENARIOS@) foreach(entry IN LISTS MGL_SPLIT_SMALL_${scenario}_TESTS) set_tests_properties("${entry}" PROPERTIES ENVIRONMENT "MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log") diff --git a/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py b/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py index 41a82ecc..b605ecc4 100644 --- a/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py +++ b/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py @@ -14,7 +14,7 @@ def paths(document): props = {p["name"]: p["value"] for p in test.get("properties", [])} values = [v.split("=", 1)[1] for v in props.get("ENVIRONMENT", []) 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]): raise ValueError(f"{test['name']}: requires exactly one nonempty MOBILEGL_LOG_FILE_PATH") for value in values: diff --git a/scripts/ci/control_smoke_test.sh b/scripts/ci/control_smoke_test.sh index d26fc2fc..2fcb5290 100755 --- a/scripts/ci/control_smoke_test.sh +++ b/scripts/ci/control_smoke_test.sh @@ -19,7 +19,7 @@ set -u HERE="$(cd "$(dirname "$0")" && pwd)" -WORK="$(mktemp -d)" +WORK="$(mktemp -d)" || exit 1 trap 'rm -rf "${WORK}"' EXIT STUB_DIR="${WORK}/stub" @@ -99,6 +99,10 @@ expect FAILED "a pull library passed the split retrace" -- run_retrace retr echo 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 echo "CONTROL_SMOKE_TEST_FAILED" exit 1 diff --git a/scripts/ci/redcheck_control_smoke_test.sh b/scripts/ci/redcheck_control_smoke_test.sh index 6f540e1c..b8ee301f 100755 --- a/scripts/ci/redcheck_control_smoke_test.sh +++ b/scripts/ci/redcheck_control_smoke_test.sh @@ -5,13 +5,13 @@ # 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. # -# 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 -# message" - and not merely somewhere. A smoke test that goes red for any other reason when the -# evidence check is removed would not be pinning the evidence check. +# message", plus the missing private-file Fatal - and not merely somewhere. A smoke test that goes +# red for any other reason when the evidence check is removed would not be pinning the evidence check. set -u 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 cp "${HERE}/split_negative_controls.sh" "${WORK}/split.orig" @@ -27,27 +27,35 @@ tail -1 "${WORK}/before.log" echo 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 re split, retrace = sys.argv[1], sys.argv[2] for path, needle in ((split, 'grep -qE "${evidence}"'), (retrace, 'grep -qF "${EVIDENCE}"')): 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 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())] - 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 else: out.append(line) if hit != 1: raise SystemExit(f"expected exactly one evidence check in {path}, found {hit}") 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 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 rc=$? cat "${WORK}/after.log" @@ -61,11 +69,12 @@ fi missed=0 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 -qF "NOT OK missing-fatal: control must report FAILED for its own reason" "${WORK}/after.log" || missed=1 if [ "${missed}" -ne 0 ]; then 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 fi echo -echo "P5_T1_CONTROL_SMOKE_REDCHECK_OK - removing the evidence check reds exactly the two cases that pin it" +echo "P5_T1_CONTROL_SMOKE_REDCHECK_OK - removing the evidence checks reds the unrelated split, unrelated retrace and missing private-Fatal cases" diff --git a/scripts/ci/split_negative_controls.sh b/scripts/ci/split_negative_controls.sh index 6baf5d5e..9c4644dd 100755 --- a/scripts/ci/split_negative_controls.sh +++ b/scripts/ci/split_negative_controls.sh @@ -42,8 +42,7 @@ set -u if [ "${1:-}" = "--self-test" ]; then - bash "$(dirname "$0")/control_smoke_test.sh" && - bash "$(dirname "$0")/testdata/split_private_log_smoke.sh" + bash "$(dirname "$0")/control_smoke_test.sh" exit $? fi @@ -65,7 +64,8 @@ python3 "${log_helper}" check "${CTEST}" "$PWD" || exit 1 # 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 # 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=$? if [ ! -f "${junit}" ]; then @@ -136,7 +136,9 @@ run_control() { 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, ""}. +# E1: c1 ClientSession::EmitAndWait emits MGLOG_F Fatal{BarrierViolation, ""}. +# 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. run_control "negative control E1 (MOBILEGL_IPC_VERB_BARRIER=0)" \ 'DirectGLES\.Split\.(SmallRing\.)?(Triangle|ClearThenReadPixels)' \ diff --git a/scripts/ci/testdata/split_private_log_smoke.sh b/scripts/ci/testdata/split_private_log_smoke.sh index cf226eea..725cb5de 100644 --- a/scripts/ci/testdata/split_private_log_smoke.sh +++ b/scripts/ci/testdata/split_private_log_smoke.sh @@ -16,6 +16,9 @@ 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" || { 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 message='E1 FAILED: selected private logs lack expected Fatal' [ "${mode}" != e3-unrelated ] || message='FAILED: red lacks its persistent-map push diagnostic'