Fix phone-only Mesh Lab scenario selection

This commit is contained in:
callebtc 2026-08-02 13:47:23 +02:00
parent 67e942b04b
commit 2c59c49f72
3 changed files with 48 additions and 5 deletions

View File

@ -298,7 +298,7 @@ python3 tools/release_gate/mesh_lab.py scenario all \
| `dm` | Noise handshake both ways, encrypted DM round trips with content match |
| `favorite_verification` | favorite signal, orange-outline/filled mutual state, and peer fingerprint verification |
| `broadcast` | public mesh message A→B |
| `watch_power` | phone→Watch screen-off delivery, two-link ceiling, scan quiescence, and zero background RSSI polling |
| `watch_power` | phone→Watch screen-off delivery, two-link ceiling, scan quiescence, and zero background RSSI polling (Watch topology only) |
| `ptt_dm` | Noise-encrypted 440 Hz PTT in both directions; asserts real-time capture, zero sequence gaps, decoded PCM duration/energy/continuity, and finalized-note absorption |
| `ptt_broadcast` | signed public 440 Hz PTT with the same bidirectional packet and decoded-audio quality assertions |
| `file` | 1 KB broadcast file, receiver SHA-256 matches fixture |
@ -308,7 +308,7 @@ python3 tools/release_gate/mesh_lab.py scenario all \
| `raw` | raw packet injection is accepted by the mesh |
| `session_recovery` | force-stop B mid-session: identity persists, re-handshake, DMs flow again |
| `identity_reset` | pm clear B mid-session: new identity, rediscovery, handshake, DMs |
| `all` | every scenario above in sequence |
| `all` | every scenario supported by the selected topology in sequence; phone→phone excludes Watch-only checks |
Each run writes `<scenario>-evidence.json` to `--out` (digests, timings,
session states, logcat excerpts on failure) and exits non-zero on failure.

View File

@ -964,9 +964,12 @@ SCENARIOS = {
"identity_reset": scenario_identity_reset,
}
WATCH_ONLY_SCENARIOS = frozenset({"watch_power"})
PHONE_SCENARIOS = tuple(name for name in SCENARIOS if name not in WATCH_ONLY_SCENARIOS)
# Scenarios supported when device B is a watch (file scenarios are receive-only: phone sends,
# the watch must receive with matching digests).
WATCH_SCENARIOS = [
WATCH_SCENARIOS = (
"dm",
"favorite_verification",
"broadcast",
@ -978,14 +981,14 @@ WATCH_SCENARIOS = [
"file_private",
"session_recovery",
"identity_reset",
]
)
def run_scenario(name: str, a: Device, b: Device, out: Path | None) -> dict:
started = time.time()
evidence: dict[str, object] = {"scenario": name, "devices": [a.alias, b.alias]}
try:
supported = WATCH_SCENARIOS if isinstance(b, WatchDevice) else list(SCENARIOS)
supported = WATCH_SCENARIOS if isinstance(b, WatchDevice) else PHONE_SCENARIOS
if name == "all":
results = {}
failures = []

View File

@ -0,0 +1,40 @@
import unittest
from unittest import mock
from tools.release_gate import mesh_lab
class MeshLabScenarioDispatchTest(unittest.TestCase):
def scenario_handlers(self):
return {
name: mock.Mock(return_value={"scenario": name})
for name in mesh_lab.SCENARIOS
}
def test_phone_all_excludes_watch_only_scenarios(self):
handlers = self.scenario_handlers()
phone_a = mesh_lab.Device("phone-a", "alpha")
phone_b = mesh_lab.Device("phone-b", "beta")
with mock.patch.object(mesh_lab, "SCENARIOS", handlers):
result = mesh_lab.run_scenario("all", phone_a, phone_b, out=None)
self.assertEqual("pass", result["status"])
handlers["watch_power"].assert_not_called()
for name in mesh_lab.PHONE_SCENARIOS:
handlers[name].assert_called_once_with(phone_a, phone_b)
def test_watch_all_includes_watch_power(self):
handlers = self.scenario_handlers()
phone = mesh_lab.Device("phone", "alpha")
watch = mesh_lab.WatchDevice("watch")
with mock.patch.object(mesh_lab, "SCENARIOS", handlers):
result = mesh_lab.run_scenario("all", phone, watch, out=None)
self.assertEqual("pass", result["status"])
handlers["watch_power"].assert_called_once_with(phone, watch)
if __name__ == "__main__":
unittest.main()