199 lines
7.5 KiB
Bash
Executable File
199 lines
7.5 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
: "${PRE15_DUT:?PRE15_DUT is required}"
|
|
: "${PRE15_ROOT:?PRE15_ROOT is required}"
|
|
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
|
|
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
|
|
. "$PRE15_LIB_DIR/runner.sh"
|
|
|
|
gate=$PRE15_DUT/tests/pre15/gates/P15-006.sh
|
|
input=$PRE15_DUT/tests/pre15/gates/P15-006-input.json
|
|
oracle=$PRE15_DUT/tests/pre15/fixtures/B07-map-oracle.json
|
|
artifacts=$PRE15_RUN_DIR/artifacts
|
|
|
|
for tool in cc python3 sha256sum; do
|
|
command -v "$tool" >/dev/null 2>&1 || \
|
|
pre15_infra_blocked "missing B07a host tool: $tool"
|
|
done
|
|
|
|
pre15_record_fixture b07a-gate "$gate"
|
|
pre15_record_fixture b07a-input "$input"
|
|
pre15_record_fixture b07a-oracle "$oracle"
|
|
for source in internal.h data.c zmap.c zdata.c super.c; do
|
|
pre15_record_fixture "b07a-$source" "$PRE15_DUT/src/$source"
|
|
done
|
|
|
|
mkdir -p "$artifacts"
|
|
pre15_target_reached
|
|
|
|
if "$gate" --worktree --oracle "$oracle" --output "$artifacts/gate" \
|
|
>"$artifacts/gate.stdout" 2>"$artifacts/gate.stderr"; then
|
|
:
|
|
else
|
|
pre15_dut_fail 'B07a P15-006 candidate replay failed'
|
|
fi
|
|
|
|
if python3 - "$PRE15_DUT" "$oracle" "$artifacts/gate" \
|
|
"$artifacts/adapter-check.json" <<'PY'
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
|
|
dut = Path(sys.argv[1])
|
|
oracle = json.loads(Path(sys.argv[2]).read_text(encoding="ascii"))
|
|
gate = Path(sys.argv[3])
|
|
output = Path(sys.argv[4])
|
|
result = json.loads((gate / "result.json").read_text(encoding="ascii"))
|
|
tuples = json.loads((gate / "tuples.json").read_text(encoding="ascii"))
|
|
devices = json.loads((gate / "devices.json").read_text(encoding="ascii"))
|
|
|
|
if result["status"] != "GO" or result["failures"]:
|
|
raise SystemExit(f"P15-006 candidate did not GO: {result!r}")
|
|
if not result["adapter_mode"] or result["oracle_equal"] is not True:
|
|
raise SystemExit("candidate did not use the common map adapter and frozen oracle")
|
|
if result["map_case_count"] != 80 or result["device_case_count"] != 13:
|
|
raise SystemExit("P15-006 corpus denominator changed")
|
|
if result["model_sha256"] != oracle["model_sha256"]:
|
|
raise SystemExit("independent model digest changed")
|
|
if result["tuple_bytes_sha256"] != oracle["tuple_bytes_sha256"]:
|
|
raise SystemExit("tuple byte stream changed")
|
|
if result["adapter_probe"]["status"] != "PASS":
|
|
raise SystemExit("FULL/COMPACT adapter pass-through probe failed")
|
|
if not result["adapter_probe"]["full_and_compact_dispatch"]:
|
|
raise SystemExit("compressed layout dispatch is incomplete")
|
|
if result["adapter_probe"]["flag_mask"] != 0x1F:
|
|
raise SystemExit("not all map flag bits passed through the adapter")
|
|
if tuples["tuple_bytes_sha256"] != oracle["tuple_bytes_sha256"]:
|
|
raise SystemExit("tuple artifact does not match frozen bytes")
|
|
if devices["status"] != "PASS" or len(devices["records"]) != 13:
|
|
raise SystemExit("device/GEOM resolution oracle failed")
|
|
|
|
|
|
def extract_function(source: str, name: str) -> str:
|
|
match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
|
|
if not match:
|
|
raise SystemExit(f"missing function: {name}")
|
|
start = source.rfind("\n\n", 0, match.start()) + 2
|
|
brace = source.find("{", match.end())
|
|
depth = 0
|
|
state = "code"
|
|
index = brace
|
|
while index < len(source):
|
|
char = source[index]
|
|
following = source[index + 1] if index + 1 < len(source) else ""
|
|
if state == "code":
|
|
if char == "/" and following == "*":
|
|
state = "block"
|
|
index += 2
|
|
continue
|
|
if char == "/" and following == "/":
|
|
state = "line"
|
|
index += 2
|
|
continue
|
|
if char == '"':
|
|
state = "string"
|
|
elif char == "'":
|
|
state = "character"
|
|
elif char == "{":
|
|
depth += 1
|
|
elif char == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[start : index + 1]
|
|
elif state == "block" and char == "*" and following == "/":
|
|
state = "code"
|
|
index += 2
|
|
continue
|
|
elif state == "line" and char == "\n":
|
|
state = "code"
|
|
elif state in {"string", "character"}:
|
|
if char == "\\":
|
|
index += 2
|
|
continue
|
|
if (state == "string" and char == '"') or (
|
|
state == "character" and char == "'"
|
|
):
|
|
state = "code"
|
|
index += 1
|
|
raise SystemExit(f"unterminated function: {name}")
|
|
|
|
|
|
internal = (dut / "src/internal.h").read_text(encoding="utf-8")
|
|
data = (dut / "src/data.c").read_text(encoding="utf-8")
|
|
zmap = (dut / "src/zmap.c").read_text(encoding="utf-8")
|
|
zdata = (dut / "src/zdata.c").read_text(encoding="utf-8")
|
|
super_source = (dut / "src/super.c").read_text(encoding="utf-8")
|
|
|
|
object_prototype = re.search(
|
|
r"int erofs_map_blocks\s*\(struct erofs_sb_info \*sbi,\s*"
|
|
r"struct erofs_inode \*vi,\s*struct erofs_map_blocks \*map\s*\);",
|
|
internal,
|
|
re.MULTILINE,
|
|
)
|
|
if object_prototype is None:
|
|
raise SystemExit("common map object prototype is absent")
|
|
if re.search(r"erofs_map_blocks\s*\([^;]*phys_off", internal, re.DOTALL):
|
|
raise SystemExit("legacy scatter prototype remains exported")
|
|
if "static int\nerofs_map_blocks_legacy(" not in data:
|
|
raise SystemExit("legacy producer target is not file-local")
|
|
if len(re.findall(r"\berofs_map_blocks_legacy\s*\(", data)) != 4:
|
|
raise SystemExit("B07a must retain exactly two legacy data consumers")
|
|
if len(re.findall(r"\berofs_map_blocks\s*\(", data)) != 1:
|
|
raise SystemExit("B07a common map entry has an unexpected data.c caller")
|
|
|
|
adapter = extract_function(data, "erofs_map_blocks")
|
|
required_adapter = [
|
|
"struct erofs_map_blocks next = { .m_la = map->m_la };",
|
|
"z_erofs_map_blocks_iter(sbi, vi, &next, 0);",
|
|
"next.m_deviceid = device_id;",
|
|
"next.m_flags |= EROFS_MAP_MAPPED;",
|
|
"next.m_flags |= EROFS_MAP_META;",
|
|
"*map = next;",
|
|
]
|
|
for marker in required_adapter:
|
|
if marker not in adapter:
|
|
raise SystemExit(f"adapter contract marker is absent: {marker}")
|
|
|
|
for function in ("erofs_read_data", "erofs_read_uio"):
|
|
body = extract_function(data, function)
|
|
if "erofs_map_blocks_legacy(" not in body or "erofs_map_blocks(" in body:
|
|
raise SystemExit(f"B07a migrated consumer early: {function}")
|
|
for source_name, source in (("zdata.c", zdata), ("super.c", super_source)):
|
|
if "z_erofs_map_blocks_iter(" not in source or "erofs_map_blocks(" in source:
|
|
raise SystemExit(f"B07a migrated compressed consumer early: {source_name}")
|
|
if "map adapter must preserve every map flag" not in zmap:
|
|
raise SystemExit("compressed map flag contract assertion is absent")
|
|
|
|
summary = {
|
|
"status": "PASS",
|
|
"map_cases": result["map_case_count"],
|
|
"device_cases": result["device_case_count"],
|
|
"tuple_bytes_sha256": result["tuple_bytes_sha256"],
|
|
"model_sha256": result["model_sha256"],
|
|
"adapter_probe": result["adapter_probe"],
|
|
"legacy_data_consumers": 2,
|
|
"compressed_consumers_migrated": False,
|
|
"qemu": result["qemu"],
|
|
"full_feature_suite": result["full_feature_suite"],
|
|
}
|
|
with output.open("x", encoding="ascii") as stream:
|
|
json.dump(summary, stream, ensure_ascii=True, indent=2, sort_keys=True)
|
|
stream.write("\n")
|
|
print("B07a PASS map=80 device=13 tuple-bytes exact flags=0x1f")
|
|
PY
|
|
then
|
|
:
|
|
else
|
|
pre15_dut_fail 'B07a adapter boundary check failed'
|
|
fi
|
|
|
|
sha256sum "$artifacts/gate/result.json" "$artifacts/gate/tuples.json" \
|
|
"$artifacts/gate/devices.json" "$artifacts/adapter-check.json" \
|
|
>"$artifacts/SHA256SUMS"
|
|
printf 'B07a PASS exact map adapter; producers and consumers remain staged\n'
|