This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Synthetic tests for Phase-1.0AB nonblocking lifecycle traces."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def load(path: Path):
|
||||
spec = importlib.util.spec_from_file_location("phase10ab_trace", path)
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec); sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module); return module
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(); parser.add_argument("--root", type=Path, required=True)
|
||||
args = parser.parse_args(); module = load(args.root.resolve() / "tools/phase10ab_nonblocking_trace_model.py")
|
||||
|
||||
def event(op, at, value=0): return module.TraceEvent(op, at, value)
|
||||
def valid(pending=True, sends=(2, 3), waits=()):
|
||||
values = [event(module.RECEIPT_CREATED, 0), event(module.SOCKET_CREATED, 0), event(module.SET_NONBLOCKING, 0)]
|
||||
if pending:
|
||||
values += [event(module.CONNECT_PENDING, .1), event(module.READY_WRITE, .2), event(module.SO_ERROR_ZERO, .2)]
|
||||
else: values += [event(module.CONNECT_IMMEDIATE, .1)]
|
||||
at = .3
|
||||
for count in sends:
|
||||
values += [event(module.READY_WRITE, at), event(module.SEND_BYTES, at, count)]; at += .1
|
||||
for op in waits: values.append(event(op, at)); at += .1
|
||||
values += [event(module.READY_READ, at), event(module.RECV_BYTES, at, 100), event(module.DEADLINE_REACHED, 10), event(module.SANITIZER_ACCEPTED, 10), event(module.LOCAL_CLOSE, 10), event(module.OUTPUT_CREATED, 10)]
|
||||
return tuple(values)
|
||||
def expect_failure(function):
|
||||
try: function()
|
||||
except module.TraceModelError: return
|
||||
raise RuntimeError("invalid trace accepted")
|
||||
cases = []
|
||||
def case(name):
|
||||
def register(function): cases.append((name, function)); return function
|
||||
return register
|
||||
|
||||
@case("01 pending connect complete trace succeeds")
|
||||
def _():
|
||||
result = module.assess_nonblocking_trace(5, 10, valid()); assert result.batch_bytes_sent == 5
|
||||
@case("02 immediate connect complete trace succeeds")
|
||||
def _(): assert module.assess_nonblocking_trace(5, 10, valid(False)).classification == "OFFLINE_NONBLOCKING_SEQUENCE_FEASIBLE"
|
||||
@case("03 partial sends require repeated readiness")
|
||||
def _(): assert module.assess_nonblocking_trace(5, 10, valid(sends=(1, 1, 3))).complete_send_loop
|
||||
@case("04 interrupted waits are recomputed")
|
||||
def _(): assert module.assess_nonblocking_trace(5, 10, valid(waits=(module.WAIT_INTERRUPTED,))).deadline_only_completion
|
||||
@case("05 timeout before deadline is not completion")
|
||||
def _(): assert module.assess_nonblocking_trace(5, 10, valid(waits=(module.WAIT_TIMEOUT,))).local_close_observed
|
||||
@case("06 receipt must precede creation")
|
||||
def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid()[1:]))
|
||||
@case("07 nonblocking must precede connect")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.SET_NONBLOCKING); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("08 pending connect requires readiness")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.READY_WRITE or e.at_seconds != .2); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("09 pending connect requires SO_ERROR")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.SO_ERROR_ZERO); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("10 send requires write readiness")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if not (e.operation == module.READY_WRITE and e.at_seconds == .3)); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("11 send cannot exceed exact batch")
|
||||
def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid(sends=(6,))))
|
||||
@case("12 incomplete send fails")
|
||||
def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid(sends=(2, 2))))
|
||||
@case("13 receive requires read readiness")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.READY_READ); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("14 receive bound is enforced")
|
||||
def _():
|
||||
trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_BYTES, trace[index].at_seconds, 65537); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace)))
|
||||
@case("15 EOF never completes")
|
||||
def _():
|
||||
trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_EOF, trace[index].at_seconds); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace)))
|
||||
@case("16 early deadline fails")
|
||||
def _():
|
||||
trace = tuple(event(e.operation, 9 if e.operation in {module.DEADLINE_REACHED,module.SANITIZER_ACCEPTED,module.LOCAL_CLOSE,module.OUTPUT_CREATED} else e.at_seconds, e.value) for e in valid()); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("17 data at deadline fails")
|
||||
def _():
|
||||
trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_BYTES, 10, 100); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace)))
|
||||
@case("18 deadline requires received data")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation not in {module.READY_READ,module.RECV_BYTES}); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("19 sanitizer must follow deadline")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.DEADLINE_REACHED); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("20 close must follow sanitizer")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.SANITIZER_ACCEPTED); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("21 output must follow close")
|
||||
def _():
|
||||
trace = tuple(e for e in valid() if e.operation != module.LOCAL_CLOSE); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace))
|
||||
@case("22 backward time fails")
|
||||
def _():
|
||||
trace = list(valid()); trace[2] = event(module.SET_NONBLOCKING, -.0); trace[1] = event(module.SOCKET_CREATED, .1); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace)))
|
||||
@case("23 trace event bound is enforced")
|
||||
def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(event(module.RECEIPT_CREATED, 0) for _ in range(513))))
|
||||
@case("24 result never claims device proof")
|
||||
def _():
|
||||
result = module.assess_nonblocking_trace(5, 10, valid()); assert not result.device_behavior_proven and not result.live_transport_present
|
||||
@case("25 no live API exists")
|
||||
def _(): assert not ({"connect", "send", "recv", "main"} & set(dir(module)))
|
||||
|
||||
failures=[]
|
||||
for name,function in cases:
|
||||
try: function(); print(f"PASS {name}")
|
||||
except Exception as error: failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}")
|
||||
if failures: return 1
|
||||
print(f"Phase-1.0AB nonblocking-trace tests passed: {len(cases)}"); return 0
|
||||
|
||||
|
||||
if __name__ == "__main__": raise SystemExit(main())
|
||||
Reference in New Issue
Block a user