139 lines
6.7 KiB
Python
139 lines
6.7 KiB
Python
"""Resolve the real Indicator repeater recipes without PlatformIO or hardware."""
|
|
import configparser
|
|
import fnmatch
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
N8 = "SenseCapIndicator-LoRa_repeater"
|
|
N16 = "SenseCapIndicator-LoRa-N16R2_repeater"
|
|
|
|
|
|
class IndicatorRepeaterProfileTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.config = configparser.ConfigParser(interpolation=None, strict=False)
|
|
cls.config.read([ROOT / "platformio.ini",
|
|
ROOT / "variants/sensecap_indicator-espnow/platformio.ini"], encoding="ascii")
|
|
|
|
def option(self, section, option, depth=0):
|
|
self.assertLess(depth, 30)
|
|
if self.config.has_option(section, option):
|
|
value = self.config.get(section, option)
|
|
return re.sub(r"\$\{([^}]+)\}", lambda match:
|
|
self.option(*match[1].rsplit(".", 1), depth=depth + 1), value)
|
|
for parent in self.config.get(section, "extends", fallback="").split(","):
|
|
if parent.strip():
|
|
try:
|
|
return self.option(parent.strip(), option, depth + 1)
|
|
except KeyError:
|
|
pass
|
|
raise KeyError((section, option))
|
|
|
|
def flags(self, target):
|
|
return [line.strip() for line in self.option("env:" + target, "build_flags").splitlines()
|
|
if line.strip() and not line.lstrip().startswith((";", "#"))]
|
|
|
|
def definitions(self, target):
|
|
definitions = {}
|
|
for flag in self.flags(target):
|
|
match = re.fullmatch(r"-([DU])\s*(\w+)(?:=(.*))?", flag)
|
|
if not match:
|
|
continue
|
|
kind, name, value = match.groups()
|
|
if kind == "U":
|
|
definitions.pop(name, None)
|
|
else:
|
|
definitions[name] = (value or "1").strip("'")
|
|
return definitions
|
|
|
|
def selected(self, target, source):
|
|
result = False
|
|
rules = self.option("env:" + target, "build_src_filter")
|
|
for action, pattern in re.findall(r"([+-])<([^>]+)>", rules):
|
|
if "/" not in pattern and "/" in source:
|
|
continue
|
|
match = fnmatch.fnmatchcase(source, pattern)
|
|
if not any(character in pattern for character in "*?["):
|
|
match = match or source.startswith(pattern.rstrip("/") + "/")
|
|
if match:
|
|
result = action == "+"
|
|
return result
|
|
|
|
def test_real_radio_and_display_are_retained_in_both_memory_variants(self):
|
|
for target in (N8, N16):
|
|
with self.subTest(target=target):
|
|
definitions = self.definitions(target)
|
|
self.assertIn("SENSECAP_INDICATOR_LORA", definitions)
|
|
self.assertNotIn("MESH_PRIMARY_ESPNOW", definitions)
|
|
self.assertNotIn("DISABLE_WIFI_OTA", definitions)
|
|
self.assertEqual(definitions["WRAPPER_CLASS"], "IndicatorSX1262Wrapper")
|
|
self.assertEqual(definitions["DISPLAY_CLASS"], "SCIndicatorDisplay")
|
|
self.assertEqual(definitions["INDICATOR_WIFI_FONT_RECOVERY"], "1")
|
|
self.assertEqual(definitions["MOTA_HW_ID"], '"' + target[:-9] + '"')
|
|
self.assertEqual([definitions[key] for key in ("P_LORA_NSS", "P_LORA_RESET",
|
|
"P_LORA_BUSY", "P_LORA_DIO_1")], ["64", "65", "66", "67"])
|
|
for source in ("../examples/simple_repeater/main.cpp", "../examples/simple_repeater/MyMesh.cpp",
|
|
"../variants/sensecap_indicator-espnow/target.cpp",
|
|
"../variants/sensecap_indicator-espnow/IndicatorRadioHal.cpp",
|
|
"../variants/sensecap_indicator-espnow/IndicatorFontClient.cpp",
|
|
"helpers/ui/LGFXDisplay.cpp", "helpers/ota/OtaContext.cpp",
|
|
"helpers/esp32/WebConfigServer.cpp"):
|
|
self.assertTrue(self.selected(target, source), source)
|
|
for source in ("helpers/esp32/ESPNOWRadio.cpp", "../examples/companion_radio/main.cpp"):
|
|
self.assertFalse(self.selected(target, source), source)
|
|
self.assertIn("AsyncElegantOTA", self.option("env:" + target, "lib_deps"))
|
|
|
|
def test_flash_partition_and_psram_modes_are_explicit(self):
|
|
for target, flash, mode, psram, table, size in (
|
|
(N8, "8MB", "qio_opi", "opi", "dual_ota_2560k_preserve_spiffs.csv", "2621440"),
|
|
(N16, "16MB", "qio_qspi", "qspi", "dual_ota_6400k_preserve_spiffs.csv", "6553600")):
|
|
with self.subTest(target=target):
|
|
for key, expected in (("board_upload.flash_size", flash),
|
|
("board_build.arduino.memory_type", mode), ("board_build.psram_type", psram),
|
|
("board_upload.maximum_size", size)):
|
|
self.assertEqual(self.option("env:" + target, key), expected)
|
|
self.assertEqual(self.option("env:" + target, "board_build.partitions"),
|
|
"variants/sensecap_indicator-espnow/" + table)
|
|
|
|
def test_actual_full_overlay_uses_lora_not_overridden_primary_espnow_flag(self):
|
|
rows = [["env:" + target, [["build_flags", self.flags(target)]]] for target in (N8, N16)]
|
|
environment = dict(os.environ, INDICATOR_TEST_CONFIG=json.dumps(rows))
|
|
script = r'''
|
|
set -euo pipefail
|
|
source build.sh
|
|
PIO_CONFIG_JSON=$INDICATOR_TEST_CONFIG
|
|
for target in SenseCapIndicator-LoRa_repeater SenseCapIndicator-LoRa-N16R2_repeater; do
|
|
PIO_ENV_PLATFORM_BY_NAME[$target]=ESP32_PLATFORM
|
|
PIO_ENV_BOARD_BY_NAME[$target]=esp32-s3-devkitc-1
|
|
PIO_ENV_FULL_BUILD_BY_NAME[$target]=1
|
|
PIO_ENV_MQTT_BY_NAME[$target]=0
|
|
supports_esp32_full_shared_espnow "$target"
|
|
is_esp32_full_only_bulk_target "$target"
|
|
[[ $(get_exact_identity_full_pio_env "$target") == "$target" ]]
|
|
ESP32_FULL_BUILD=1
|
|
PLATFORMIO_BUILD_FLAGS=''
|
|
PLATFORMIO_BUILD_SRC_FILTER=''
|
|
apply_esp32_full_shared_bridge_profile "$target"
|
|
[[ $PLATFORMIO_BUILD_FLAGS == *-DWITH_UDP_BRIDGE=1* ]]
|
|
[[ $PLATFORMIO_BUILD_FLAGS == *-DWITH_ESPNOW_BRIDGE=1* ]]
|
|
[[ $PLATFORMIO_BUILD_SRC_FILTER == *UDPBridgeSettings.cpp* ]]
|
|
done
|
|
PIO_CONFIG_JSON='[["env:bad", [["build_flags", ["-DMESH_PRIMARY_ESPNOW=1", "-UMESH_PRIMARY_ESPNOW", "-DMESH_PRIMARY_ESPNOW=1"]]]]]'
|
|
pio_env_has_effective_define bad MESH_PRIMARY_ESPNOW
|
|
PIO_CONFIG_JSON='[["env:bad", [["build_flags", ["-DMESH_ESPNOW_RADIO=1"]]]]]'
|
|
pio_env_has_effective_define bad MESH_ESPNOW_RADIO
|
|
'''
|
|
result = subprocess.run(["bash", "-c", script], cwd=ROOT, env=environment,
|
|
capture_output=True, text=True, timeout=30)
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|