You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Add testing mocks
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
0.9.0
|
||||
=====
|
||||
|
||||
Builtin Apps:
|
||||
- Hotspot Settings
|
||||
- WebServer Settings
|
||||
|
||||
Frameworks:
|
||||
- AudioManager: add support for multiple speakers and microphones
|
||||
- AudioManager: add support for ADC-based microphone (adc_mic)
|
||||
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
class MockSharedPreferences:
|
||||
"""Mock SharedPreferences for testing."""
|
||||
|
||||
_all_data = {} # Class-level storage
|
||||
|
||||
def __init__(self, app_id, filename=None):
|
||||
self.app_id = app_id
|
||||
self.filename = filename
|
||||
if app_id not in MockSharedPreferences._all_data:
|
||||
MockSharedPreferences._all_data[app_id] = {}
|
||||
|
||||
def _get_value(self, key, default):
|
||||
return MockSharedPreferences._all_data.get(self.app_id, {}).get(key, default)
|
||||
|
||||
def get_dict(self, key):
|
||||
return self._get_value(key, {})
|
||||
|
||||
def get_list(self, key, default=None):
|
||||
return self._get_value(key, default)
|
||||
|
||||
def get_bool(self, key, default=False):
|
||||
value = self._get_value(key, default)
|
||||
return bool(value)
|
||||
|
||||
def get_string(self, key, default=""):
|
||||
value = self._get_value(key, default)
|
||||
return value if value is not None else default
|
||||
|
||||
def get_int(self, key, default=0):
|
||||
value = self._get_value(key, default)
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
def edit(self):
|
||||
return MockEditor(self)
|
||||
|
||||
@classmethod
|
||||
def reset_all(cls):
|
||||
cls._all_data = {}
|
||||
|
||||
|
||||
class MockEditor:
|
||||
"""Mock editor for SharedPreferences."""
|
||||
|
||||
def __init__(self, prefs):
|
||||
self.prefs = prefs
|
||||
self.pending = {}
|
||||
|
||||
def put_dict(self, key, value):
|
||||
self.pending[key] = value
|
||||
|
||||
def put_list(self, key, value):
|
||||
self.pending[key] = value
|
||||
|
||||
def put_bool(self, key, value):
|
||||
self.pending[key] = bool(value)
|
||||
|
||||
def put_string(self, key, value):
|
||||
self.pending[key] = value
|
||||
|
||||
def put_int(self, key, value):
|
||||
self.pending[key] = int(value)
|
||||
|
||||
def commit(self):
|
||||
if self.prefs.app_id not in MockSharedPreferences._all_data:
|
||||
MockSharedPreferences._all_data[self.prefs.app_id] = {}
|
||||
MockSharedPreferences._all_data[self.prefs.app_id].update(self.pending)
|
||||
|
||||
|
||||
class MockMpos:
|
||||
"""Mock mpos module with config and time."""
|
||||
|
||||
class config:
|
||||
@staticmethod
|
||||
def SharedPreferences(app_id):
|
||||
return MockSharedPreferences(app_id)
|
||||
|
||||
class time:
|
||||
@staticmethod
|
||||
def sync_time():
|
||||
pass # No-op for testing
|
||||
|
||||
|
||||
class HotspotMockNetwork:
|
||||
"""Mock network module with AP/STA support for hotspot tests."""
|
||||
|
||||
STA_IF = 0
|
||||
AP_IF = 1
|
||||
|
||||
AUTH_OPEN = 0
|
||||
AUTH_WPA_PSK = 1
|
||||
AUTH_WPA2_PSK = 2
|
||||
AUTH_WPA_WPA2_PSK = 3
|
||||
|
||||
class MockWLAN:
|
||||
def __init__(self, interface):
|
||||
self.interface = interface
|
||||
self._active = False
|
||||
self._connected = False
|
||||
self._config = {}
|
||||
self._scan_results = []
|
||||
self._ifconfig = ("0.0.0.0", "0.0.0.0", "0.0.0.0", "0.0.0.0")
|
||||
|
||||
def active(self, is_active=None):
|
||||
if is_active is None:
|
||||
return self._active
|
||||
self._active = is_active
|
||||
return None
|
||||
|
||||
def isconnected(self):
|
||||
return self._connected
|
||||
|
||||
def connect(self, ssid, password):
|
||||
self._connected = True
|
||||
self._config["essid"] = ssid
|
||||
|
||||
def disconnect(self):
|
||||
self._connected = False
|
||||
|
||||
def config(self, *args, **kwargs):
|
||||
if kwargs:
|
||||
self._config.update(kwargs)
|
||||
return None
|
||||
if args:
|
||||
return self._config.get(args[0])
|
||||
return self._config
|
||||
|
||||
def ifconfig(self, cfg=None):
|
||||
if cfg is None:
|
||||
return self._ifconfig
|
||||
self._ifconfig = cfg
|
||||
return None
|
||||
|
||||
def ipconfig(self, key=None):
|
||||
config = self.ifconfig()
|
||||
mapping = {
|
||||
"addr4": config[0],
|
||||
"netmask4": config[1],
|
||||
"gw4": config[2],
|
||||
"dns4": config[3],
|
||||
}
|
||||
if key is None:
|
||||
return mapping
|
||||
return mapping.get(key)
|
||||
|
||||
def scan(self):
|
||||
return self._scan_results
|
||||
|
||||
def __init__(self):
|
||||
self._wlan_instances = {}
|
||||
|
||||
def WLAN(self, interface):
|
||||
if interface not in self._wlan_instances:
|
||||
self._wlan_instances[interface] = self.MockWLAN(interface)
|
||||
return self._wlan_instances[interface]
|
||||
|
||||
|
||||
class MockADC:
|
||||
"""Mock ADC for testing."""
|
||||
|
||||
ATTN_11DB = 3
|
||||
|
||||
def __init__(self, pin):
|
||||
self.pin = pin
|
||||
self._atten = None
|
||||
self._read_value = 2048
|
||||
|
||||
def atten(self, value):
|
||||
self._atten = value
|
||||
|
||||
def read(self):
|
||||
return self._read_value
|
||||
|
||||
def set_read_value(self, value):
|
||||
"""Test helper to set ADC reading."""
|
||||
self._read_value = value
|
||||
|
||||
|
||||
class MockPin:
|
||||
"""Mock Pin for testing."""
|
||||
|
||||
def __init__(self, pin_num):
|
||||
self.pin_num = pin_num
|
||||
|
||||
|
||||
class MockMachineADC:
|
||||
"""Mock machine module with ADC/Pin."""
|
||||
|
||||
ADC = MockADC
|
||||
Pin = MockPin
|
||||
|
||||
|
||||
class MockWifiService:
|
||||
"""Mock WifiService for testing."""
|
||||
|
||||
wifi_busy = False
|
||||
_connected = False
|
||||
_temporarily_disabled = False
|
||||
|
||||
@classmethod
|
||||
def is_connected(cls):
|
||||
return cls._connected
|
||||
|
||||
@classmethod
|
||||
def disconnect(cls):
|
||||
cls._connected = False
|
||||
|
||||
@classmethod
|
||||
def temporarily_disable(cls):
|
||||
"""Temporarily disable WiFi and return whether it was connected."""
|
||||
if cls.wifi_busy:
|
||||
raise RuntimeError("Cannot disable WiFi: WifiService is already busy")
|
||||
was_connected = cls._connected
|
||||
cls.wifi_busy = True
|
||||
cls._connected = False
|
||||
cls._temporarily_disabled = True
|
||||
return was_connected
|
||||
|
||||
@classmethod
|
||||
def temporarily_enable(cls, was_connected):
|
||||
"""Re-enable WiFi and reconnect if it was connected before."""
|
||||
cls.wifi_busy = False
|
||||
cls._temporarily_disabled = False
|
||||
if was_connected:
|
||||
cls._connected = True
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
"""Test helper to reset state."""
|
||||
cls.wifi_busy = False
|
||||
cls._connected = False
|
||||
cls._temporarily_disabled = False
|
||||
|
||||
|
||||
class MockI2C:
|
||||
"""Mock I2C bus for testing."""
|
||||
|
||||
def __init__(self, bus_id, sda=None, scl=None):
|
||||
self.bus_id = bus_id
|
||||
self.sda = sda
|
||||
self.scl = scl
|
||||
self.memory = {}
|
||||
|
||||
def readfrom_mem(self, addr, reg, nbytes):
|
||||
"""Read from memory (simulates I2C read)."""
|
||||
if addr not in self.memory:
|
||||
raise OSError("I2C device not found")
|
||||
if reg not in self.memory[addr]:
|
||||
return bytes([0] * nbytes)
|
||||
return bytes(self.memory[addr][reg])
|
||||
|
||||
def writeto_mem(self, addr, reg, data):
|
||||
"""Write to memory (simulates I2C write)."""
|
||||
if addr not in self.memory:
|
||||
self.memory[addr] = {}
|
||||
self.memory[addr][reg] = list(data)
|
||||
|
||||
|
||||
class MockQMI8658:
|
||||
"""Mock QMI8658 IMU sensor."""
|
||||
|
||||
def __init__(self, i2c_bus, address=0x6B, accel_scale=0b10, gyro_scale=0b100):
|
||||
self.i2c = i2c_bus
|
||||
self.address = address
|
||||
self.accel_scale = accel_scale
|
||||
self.gyro_scale = gyro_scale
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""Return mock temperature."""
|
||||
return 25.5
|
||||
|
||||
@property
|
||||
def acceleration(self):
|
||||
"""Return mock acceleration (in G)."""
|
||||
return (0.0, 0.0, 1.0)
|
||||
|
||||
@property
|
||||
def gyro(self):
|
||||
"""Return mock gyroscope (in deg/s)."""
|
||||
return (0.0, 0.0, 0.0)
|
||||
|
||||
|
||||
class MockWsenIsds:
|
||||
"""Mock WSEN_ISDS IMU sensor."""
|
||||
|
||||
def __init__(self, i2c, address=0x6B, acc_range="8g", acc_data_rate="104Hz",
|
||||
gyro_range="500dps", gyro_data_rate="104Hz"):
|
||||
self.i2c = i2c
|
||||
self.address = address
|
||||
self.acc_range = acc_range
|
||||
self.gyro_range = gyro_range
|
||||
self.acc_sensitivity = 0.244
|
||||
self.gyro_sensitivity = 17.5
|
||||
self.acc_offset_x = 0
|
||||
self.acc_offset_y = 0
|
||||
self.acc_offset_z = 0
|
||||
self.gyro_offset_x = 0
|
||||
self.gyro_offset_y = 0
|
||||
self.gyro_offset_z = 0
|
||||
|
||||
def get_chip_id(self):
|
||||
"""Return WHO_AM_I value."""
|
||||
return 0x6A
|
||||
|
||||
def _read_raw_accelerations(self):
|
||||
"""Return mock acceleration (in mg)."""
|
||||
return (0.0, 0.0, 1000.0)
|
||||
|
||||
def read_angular_velocities(self):
|
||||
"""Return mock gyroscope (in mdps)."""
|
||||
return (0.0, 0.0, 0.0)
|
||||
|
||||
def acc_calibrate(self, samples=None):
|
||||
"""Mock calibration."""
|
||||
pass
|
||||
|
||||
def gyro_calibrate(self, samples=None):
|
||||
"""Mock calibration."""
|
||||
pass
|
||||
|
||||
|
||||
def make_machine_i2c_module(i2c_cls, pin_cls=None):
|
||||
if pin_cls is None:
|
||||
pin_cls = type("Pin", (), {})
|
||||
return type("module", (), {"I2C": i2c_cls, "Pin": pin_cls})()
|
||||
|
||||
|
||||
def make_machine_timer_module(timer_cls):
|
||||
return type("module", (), {"Timer": timer_cls})()
|
||||
|
||||
|
||||
def make_usocket_module(socket_cls):
|
||||
class MockUsocket:
|
||||
"""Mock usocket module."""
|
||||
|
||||
AF_INET = socket_cls.AF_INET
|
||||
SOCK_STREAM = socket_cls.SOCK_STREAM
|
||||
|
||||
@staticmethod
|
||||
def socket(af, sock_type):
|
||||
return socket_cls(af, sock_type)
|
||||
|
||||
return MockUsocket
|
||||
|
||||
|
||||
def make_config_module(shared_prefs_cls):
|
||||
return type("module", (), {"SharedPreferences": shared_prefs_cls})()
|
||||
Reference in New Issue
Block a user