summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gen_products.py23
-rw-r--r--tools/tests/test_gen_products.py23
2 files changed, 43 insertions, 3 deletions
diff --git a/tools/gen_products.py b/tools/gen_products.py
index 7504287..ff9cf67 100644
--- a/tools/gen_products.py
+++ b/tools/gen_products.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# Author: cirakg & claude ai
-# Summary: Check that every product in manifest.xml is a watch with API 5.0+ and at least 512 KB for watch apps.
+# Summary: Check each manifest product: a watch with API 5.0+, 512 KB or more, and the watchdog the job runner assumes.
"""Check the manifest's product list against the downloaded device files.
The target rule (README section 14, decision D4): a product belongs in
@@ -9,6 +9,12 @@ variant supports Connect IQ API 5.0.0 or newer, and watch apps get at least
512 KB of memory. The data comes from each device's compiler.json, which
`make sdk` downloads for exactly the products in the manifest.
+The app's job runner (source/util/Jobs.mc) also assumes a watchdog limit
+from the memory size: watches with more than 600,000 bytes for watch apps
+run two job steps per event, which needs 240,000 bytecodes per event
+(simulator.json's watchdogCount); the others run one step and need at
+least 120,000.
+
Usage: gen_products.py --check [--manifest manifest.xml] [--devices DIR]
Exit: 0 every product qualifies, 1 some don't, 2 device files missing.
@@ -29,6 +35,9 @@ EXIT_OK, EXIT_UNQUALIFIED, EXIT_MISSING = 0, 1, 2
IQ_NS = "{http://www.garmin.com/xml/connectiq}"
MIN_API = (5, 0, 0)
MIN_WATCH_APP_MEMORY = 512 * 1024
+# Jobs.LARGE_WATCH_MEMORY and the watchdog each memory class needs.
+LARGE_WATCH_MEMORY = 600_000
+WATCHDOG_SMALL, WATCHDOG_LARGE = 120_000, 240_000
WATCH_GROUP = "Watches/Wearables"
DEFAULT_DEVICES = Path.home() / ".Garmin" / "ConnectIQ" / "Devices"
@@ -42,6 +51,7 @@ class Device:
group: str
min_api: tuple[int, ...]
watch_app_memory: int
+ watchdog: int
def problems(self) -> list[str]:
reasons = []
@@ -51,6 +61,12 @@ class Device:
reasons.append(f"API {'.'.join(map(str, self.min_api))} is below 5.0.0")
if self.watch_app_memory < MIN_WATCH_APP_MEMORY:
reasons.append(f"watch apps get {self.watch_app_memory // 1024} KB, below 512 KB")
+ needed = WATCHDOG_LARGE if self.watch_app_memory > LARGE_WATCH_MEMORY else WATCHDOG_SMALL
+ if self.watchdog < needed:
+ reasons.append(
+ f"watchdog allows {self.watchdog} bytecodes per event; the job runner needs {needed} at"
+ f" {self.watch_app_memory // 1024} KB (change Jobs.stepsPerTick)"
+ )
return reasons
@@ -61,12 +77,17 @@ def parse_version(text: str) -> tuple[int, ...]:
def load_device(devices: Path, product: str) -> Device:
compiler = json.loads((devices / product / "compiler.json").read_text(encoding="utf-8"))
memory = {app["type"]: int(app["memoryLimit"]) for app in compiler["appTypes"]}
+ simulator = devices / product / "simulator.json"
+ watchdog = 0
+ if simulator.exists():
+ watchdog = int(json.loads(simulator.read_text(encoding="utf-8")).get("watchdogCount", 0))
return Device(
product=product,
name=str(compiler.get("displayName", product)),
group=str(compiler.get("webDocDeviceGroup", "")),
min_api=min(parse_version(p["connectIQVersion"]) for p in compiler["partNumbers"]),
watch_app_memory=memory.get("watchApp", 0),
+ watchdog=watchdog,
)
diff --git a/tools/tests/test_gen_products.py b/tools/tests/test_gen_products.py
index 47424d2..e57973f 100644
--- a/tools/tests/test_gen_products.py
+++ b/tools/tests/test_gen_products.py
@@ -1,5 +1,5 @@
# Author: cirakg & claude ai
-# Summary: Tests for tools/gen_products.py (the manifest product rule).
+# Summary: Tests for tools/gen_products.py (the manifest product rule and the watchdog the job runner assumes).
"""Tests for tools/gen_products.py, with synthetic device definitions."""
from __future__ import annotations
@@ -17,6 +17,7 @@ MANIFEST = """<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/conne
def add_device(devices: Path, product: str, api: list[str], memory: int, group: str = gp.WATCH_GROUP) -> None:
folder = devices / product
folder.mkdir(parents=True)
+ set_watchdog(devices, product, gp.WATCHDOG_LARGE if memory > gp.LARGE_WATCH_MEMORY else gp.WATCHDOG_SMALL)
compiler = {
"displayName": product.upper(),
"webDocDeviceGroup": group,
@@ -26,6 +27,10 @@ def add_device(devices: Path, product: str, api: list[str], memory: int, group:
(folder / "compiler.json").write_text(json.dumps(compiler))
+def set_watchdog(devices: Path, product: str, count: int) -> None:
+ (devices / product / "simulator.json").write_text(json.dumps({"watchdogCount": count}))
+
+
def write_manifest(tmp_path: Path, products: list[str]) -> Path:
path = tmp_path / "manifest.xml"
path.write_text(MANIFEST.format(products="".join(f'<iq:product id="{p}"/>' for p in products)))
@@ -55,9 +60,23 @@ def test_missing_device_files(tmp_path: Path) -> None:
def test_problem_messages() -> None:
- device = gp.Device("x", "X", "Edge", (4, 2, 0), 131072)
+ device = gp.Device("x", "X", "Edge", (4, 2, 0), 131072, 120000)
assert device.problems() == [
"group is 'Edge', not 'Watches/Wearables'",
"API 4.2.0 is below 5.0.0",
"watch apps get 128 KB, below 512 KB",
]
+
+
+def test_watchdog_must_match_the_job_runner(tmp_path: Path) -> None:
+ devices = tmp_path / "Devices"
+ add_device(devices, "big_slow", ["6.0.0"], 786432)
+ set_watchdog(devices, "big_slow", 120000) # would run 2 steps on 120,000
+ add_device(devices, "small_fast", ["6.0.0"], 524288)
+ set_watchdog(devices, "small_fast", 240000) # fine: 1 step, more room
+ assert gp.check(write_manifest(tmp_path, ["small_fast"]), devices) == gp.EXIT_OK
+ assert gp.check(write_manifest(tmp_path, ["big_slow"]), devices) == gp.EXIT_UNQUALIFIED
+ problems = gp.load_device(devices, "big_slow").problems()
+ assert problems == [
+ "watchdog allows 120000 bytecodes per event; the job runner needs 240000 at 768 KB (change Jobs.stepsPerTick)"
+ ]