aboutsummaryrefslogtreecommitdiff
path: root/tools/tests/test_gen_products.py
blob: e57973f8966b28ebce9e90b5d60436c891257beb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# Author:  cirakg & claude ai
# 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

import json
from pathlib import Path

import gen_products as gp

MANIFEST = """<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq">
  <iq:application id="x" type="watch-app"><iq:products>{products}</iq:products></iq:application>
</iq:manifest>"""


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,
        "partNumbers": [{"connectIQVersion": v} for v in api],
        "appTypes": [{"type": "watchApp", "memoryLimit": memory}, {"type": "glance", "memoryLimit": 65536}],
    }
    (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)))
    return path


def test_qualifying_products_pass(tmp_path: Path) -> None:
    devices = tmp_path / "Devices"
    add_device(devices, "fr70", ["6.0.3"], 786432)
    add_device(devices, "fr255s", ["5.2.0", "5.2.1"], 524288)
    assert gp.check(write_manifest(tmp_path, ["fr70", "fr255s"]), devices) == gp.EXIT_OK


def test_each_rule_is_enforced(tmp_path: Path) -> None:
    devices = tmp_path / "Devices"
    add_device(devices, "old", ["4.2.0", "5.0.0"], 786432)  # one firmware variant below 5.0
    add_device(devices, "small", ["6.0.0"], 131072)
    add_device(devices, "bike", ["5.1.0"], 786432, group="Edge")
    manifest = write_manifest(tmp_path, ["old", "small", "bike"])
    assert gp.check(manifest, devices) == gp.EXIT_UNQUALIFIED


def test_missing_device_files(tmp_path: Path) -> None:
    devices = tmp_path / "Devices"
    devices.mkdir()
    assert gp.check(write_manifest(tmp_path, ["ghost"]), devices) == gp.EXIT_MISSING


def test_problem_messages() -> None:
    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)"
    ]