Skip to content

Commit 5303044

Browse files
committed
wsen-pads: Add test scenario for pressure sensor driver.
1 parent 804646e commit 5303044

4 files changed

Lines changed: 119 additions & 8 deletions

File tree

tests/runner/executor.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@
55
from pathlib import Path
66

77

8-
def load_driver_mock(driver_name, fake_i2c):
8+
def load_driver_mock(driver_name, fake_i2c, module_name=None):
99
"""Load a driver using FakeI2C on CPython.
1010
1111
Patches machine and micropython modules, imports the driver,
1212
and returns an instance configured with the fake I2C bus.
13+
14+
Args:
15+
driver_name: directory name under lib/ (e.g. 'wsen-pads')
16+
fake_i2c: FakeI2C instance with pre-loaded registers
17+
module_name: Python module name if different from driver_name
18+
(e.g. 'wsen_pads' when dir is 'wsen-pads')
1319
"""
20+
if module_name is None:
21+
module_name = driver_name
1422
from tests.fake_machine import FakeI2C, FakePin
1523
from tests.fake_machine import micropython_stub
1624

@@ -32,6 +40,17 @@ def load_driver_mock(driver_name, fake_i2c):
3240
if not hasattr(time, "sleep_us"):
3341
time.sleep_us = lambda us: time.sleep(us / 1000000)
3442

43+
# Create utime module as alias for time (MicroPython compatibility)
44+
if "utime" not in sys.modules:
45+
utime = types.ModuleType("utime")
46+
utime.sleep_ms = time.sleep_ms
47+
utime.sleep_us = time.sleep_us
48+
utime.sleep = time.sleep
49+
utime.ticks_ms = lambda: int(time.time() * 1000)
50+
utime.ticks_us = lambda: int(time.time() * 1000000)
51+
utime.ticks_diff = lambda a, b: a - b
52+
sys.modules["utime"] = utime
53+
3554
# Add driver lib path to sys.path
3655
root = Path(__file__).parent.parent.parent
3756
driver_lib = root / "lib" / driver_name
@@ -40,17 +59,18 @@ def load_driver_mock(driver_name, fake_i2c):
4059

4160
# Force reimport of the driver module
4261
for mod_name in list(sys.modules):
43-
if mod_name.startswith(driver_name):
62+
if mod_name.startswith(module_name):
4463
del sys.modules[mod_name]
4564

46-
driver_module = importlib.import_module(f"{driver_name}.device")
65+
driver_module = importlib.import_module(f"{module_name}.device")
4766
return driver_module, fake_i2c
4867

4968

50-
def cleanup_driver(driver_name):
69+
def cleanup_driver(driver_name, module_name=None):
5170
"""Remove patched modules after test."""
71+
mod_prefix = module_name or driver_name
5272
for mod_name in list(sys.modules):
53-
if mod_name.startswith(driver_name):
73+
if mod_name.startswith(mod_prefix):
5474
del sys.modules[mod_name]
5575
sys.modules.pop("machine", None)
5676
sys.modules.pop("micropython", None)

tests/runner/mpremote_bridge.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ def _driver_dir(self, driver_name):
6464
"""Return the local lib path for a driver."""
6565
return PROJECT_ROOT / "lib" / driver_name
6666

67-
def call_method(self, driver_name, driver_class, i2c_config, method, args=None, i2c_address=None):
67+
def call_method(
68+
self, driver_name, driver_class, i2c_config, method,
69+
args=None, i2c_address=None, module_name=None,
70+
):
6871
"""Call a method on a driver instance and return the result."""
72+
mod = module_name or driver_name
6973
args_str = ", ".join(repr(a) for a in (args or []))
7074
i2c_init = _i2c_init_code(i2c_config)
7175
if i2c_address is not None:
@@ -75,7 +79,7 @@ def call_method(self, driver_name, driver_class, i2c_config, method, args=None,
7579
code = (
7680
f"import json\n"
7781
f"{i2c_init}\n"
78-
f"from {driver_name}.device import {driver_class}\n"
82+
f"from {mod}.device import {driver_class}\n"
7983
f"{dev_init}"
8084
f"result = dev.{method}({args_str})\n"
8185
f"print(json.dumps(result))"

tests/scenarios/wsen_pads.yaml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
driver: wsen-pads
2+
module_name: wsen_pads
3+
driver_class: WSEN_PADS
4+
i2c_address: 0x5D
5+
6+
# I2C config for hardware tests (STeaMi board - STM32WB55)
7+
i2c:
8+
id: 1
9+
10+
# Register values for mock tests
11+
# These simulate a real WSEN-PADS sensor
12+
mock_registers:
13+
# DEVICE_ID (expected 0xB3)
14+
0x0F: 0xB3
15+
# INT_SOURCE (BOOT_ON=0, no interrupts)
16+
0x24: 0x00
17+
# CTRL_1 (power down, BDU enabled)
18+
0x10: 0x02
19+
# CTRL_2 (auto-increment enabled)
20+
0x11: 0x10
21+
# STATUS (pressure and temperature data available)
22+
0x27: 0x03
23+
# INT_CFG (default)
24+
0x0B: 0x00
25+
# DATA_P_XL..DATA_P_H (simulated ~1013.25 hPa: 1013.25 * 4096 = 4150272 = 0x3F5400)
26+
0x28: [0x00, 0x54, 0x3F]
27+
# DATA_T_L..DATA_T_H (simulated ~25.00°C: 25.00 / 0.01 = 2500 = 0x09C4)
28+
0x2B: [0xC4, 0x09]
29+
30+
tests:
31+
- name: "Verify device ID register"
32+
action: read_register
33+
register: 0x0F
34+
expect: 0xB3
35+
mode: [mock, hardware]
36+
37+
- name: "Read device ID via method"
38+
action: call
39+
method: device_id
40+
expect: 0xB3
41+
mode: [mock, hardware]
42+
43+
- name: "Read status register"
44+
action: call
45+
method: status
46+
expect_not_none: true
47+
mode: [mock, hardware]
48+
49+
- name: "Read pressure returns float"
50+
action: call
51+
method: pressure
52+
expect_not_none: true
53+
mode: [mock]
54+
55+
- name: "Read temperature returns float"
56+
action: call
57+
method: temperature
58+
expect_not_none: true
59+
mode: [mock]
60+
61+
- name: "Pressure in plausible range"
62+
action: call
63+
method: pressure
64+
expect_range: [900.0, 1100.0]
65+
mode: [hardware]
66+
67+
- name: "Temperature in plausible range"
68+
action: call
69+
method: temperature
70+
expect_range: [-10.0, 60.0]
71+
mode: [hardware]
72+
73+
- name: "Pressure and temperature feel correct"
74+
action: manual
75+
display:
76+
- method: pressure
77+
label: "Pressure"
78+
unit: "hPa"
79+
- method: temperature
80+
label: "Temperature"
81+
unit: "°C"
82+
prompt: "Ces valeurs sont-elles cohérentes (pression ~1013 hPa, température ambiante) ?"
83+
expect_true: true
84+
mode: [hardware]

tests/test_scenarios.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def iter_scenario_tests():
4545
def make_mock_instance(scenario):
4646
"""Create a driver instance using FakeI2C from scenario data."""
4747
driver_name = scenario["driver"]
48+
module_name = scenario.get("module_name", driver_name)
4849
driver_class = scenario["driver_class"]
4950
address = scenario.get("i2c_address", 0x00)
5051
mock_registers = scenario.get("mock_registers", {})
@@ -56,7 +57,7 @@ def make_mock_instance(scenario):
5657
registers[key] = v
5758

5859
fake_i2c = FakeI2C(registers=registers, address=address)
59-
driver_module, _ = load_driver_mock(driver_name, fake_i2c)
60+
driver_module, _ = load_driver_mock(driver_name, fake_i2c, module_name=module_name)
6061

6162
cls = getattr(driver_module, driver_class)
6263
instance = cls(fake_i2c, address=address)
@@ -90,6 +91,7 @@ def test_scenario(scenario, test, mode, port):
9091
scenario["i2c"],
9192
display["method"],
9293
display.get("args"),
94+
module_name=scenario.get("module_name"),
9395
)
9496
label = display.get("label", display["method"])
9597
unit = display.get("unit", "")
@@ -107,6 +109,7 @@ def test_scenario(scenario, test, mode, port):
107109
scenario["i2c"],
108110
test["method"],
109111
test.get("args"),
112+
module_name=scenario.get("module_name"),
110113
)
111114
elif action == "read_register":
112115
result = bridge.read_register(

0 commit comments

Comments
 (0)