Files
MeshCore/test/test_indicator_render_profile.py
T

194 lines
7.8 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
BUILD = ROOT / "build.sh"
MAIN = ROOT / "examples" / "companion_radio" / "main.cpp"
MESH = ROOT / "examples" / "companion_radio" / "MyMesh.cpp"
DATA_STORE = ROOT / "examples" / "companion_radio" / "DataStore.cpp"
PROFILE = ROOT / "variants" / "sensecap_indicator-espnow" / "platformio.ini"
class IndicatorRenderProfileTest(unittest.TestCase):
def test_transport_matrix(self):
source = r'''
#include <helpers/ui/IndicatorRenderProfile.h>
int main() {
using mesh::ui::selectIndicatorRenderProfile;
const auto lora_wifi = selectIndicatorRenderProfile(false, true);
const auto lora_ble = selectIndicatorRenderProfile(false, false);
const auto espnow_wifi = selectIndicatorRenderProfile(true, true);
const auto espnow_ble = selectIndicatorRenderProfile(true, false);
if (lora_wifi.canvas_size != 480 || lora_wifi.coordinate_scale != 3
|| lora_wifi.output_zoom != 1.0f) return 1;
if (lora_ble.canvas_size != 480 || lora_ble.coordinate_scale != 3
|| lora_ble.output_zoom != 1.0f) return 2;
if (espnow_wifi.canvas_size != 480 || espnow_wifi.coordinate_scale != 3
|| espnow_wifi.output_zoom != 1.0f) return 3;
if (espnow_ble.canvas_size != 320 || espnow_ble.coordinate_scale != 2
|| espnow_ble.output_zoom != 1.5f) return 4;
if (mesh::ui::selectIndicatorTextScale(480, false) != 1.2f) return 5;
if (mesh::ui::selectIndicatorTextScale(480, true) != 1.0f) return 6;
if (mesh::ui::selectIndicatorTextScale(320, false) != 1.0f) return 7;
return 0;
}
'''
with tempfile.TemporaryDirectory() as temp_dir:
executable = Path(temp_dir) / "indicator_render_profile"
compiled = subprocess.run(
[
"c++",
"-std=c++11",
f"-I{ROOT / 'src'}",
"-x",
"c++",
"-",
"-o",
str(executable),
],
input=source,
text=True,
capture_output=True,
check=False,
)
self.assertEqual(compiled.returncode, 0, compiled.stderr)
ran = subprocess.run(
[str(executable)], capture_output=True, text=True, check=False
)
self.assertEqual(ran.returncode, 0, ran.stderr)
def test_full_profiles_allocate_native_before_runtime_selection(self):
build = BUILD.read_text(encoding="utf-8")
self.assertIn(
"sensecapindicator-espnow_companion_radio_full|\\\n"
" sensecapindicator-lora_companion_radio_full)",
build,
)
self.assertIn("-UUI_ZOOM -DUI_ZOOM=1.0f", build)
self.assertIn("-UUI_COORD_SCALE -DUI_COORD_SCALE=3", build)
self.assertIn("-DINDICATOR_TRANSPORT_RENDER_PROFILE=1", build)
def test_runtime_selection_precedes_bluetooth_start(self):
main = MAIN.read_text(encoding="utf-8")
setup = main[main.index("void setup()") : main.index("\nvoid loop()")]
select = setup.index("selectIndicatorRenderProfile(")
resize = setup.index("disp->setRenderScale(", select)
bluetooth = setup.index("startCompanionBluetooth();", resize)
self.assertLess(select, resize)
self.assertLess(resize, bluetooth)
self.assertIn("true,", setup[select:resize])
self.assertIn("companionTransportWiFiActiveAtBoot()", setup[select:resize])
def test_canvas_stays_internal_and_can_rollback(self):
display = (ROOT / "src/helpers/ui/LGFXDisplay.cpp").read_text(
encoding="utf-8"
)
self.assertIn("buffer.setPsram(false);", display)
self.assertIn("if (!configurePalette()) {\n buffer.deleteSprite();", display)
self.assertIn("buffer.deleteSprite();", display)
self.assertIn("_coordinateScale = previous_scale;", display)
self.assertIn("_outputZoom = previous_zoom;", display)
self.assertIn("createRenderBuffer();\n return false;", display)
def test_failed_native_startup_retries_safe_canvas(self):
display = (ROOT / "src/helpers/ui/LGFXDisplay.cpp").read_text(
encoding="utf-8"
)
begin = display[display.index("bool LGFXDisplay::begin()") :]
begin = begin[: begin.index("\nbool LGFXDisplay::createRenderBuffer()")]
first_attempt = begin.index("if (createRenderBuffer()) return true;")
guard = begin.index("INDICATOR_TRANSPORT_RENDER_PROFILE", first_attempt)
fallback_scale = begin.index("_coordinateScale = 2;", guard)
fallback_zoom = begin.index("_outputZoom = 1.5f;", fallback_scale)
retry = begin.index("if (createRenderBuffer()) return true;", fallback_zoom)
self.assertLess(first_attempt, guard)
self.assertLess(guard, fallback_scale)
self.assertLess(fallback_scale, fallback_zoom)
self.assertLess(fallback_zoom, retry)
def test_native_font_increase_preserves_compact_chrome(self):
display = (ROOT / "src/helpers/ui/LGFXDisplay.cpp").read_text(
encoding="utf-8"
)
self.assertIn("selectIndicatorTextScale(", display)
self.assertIn("static_cast<uint16_t>(renderWidth()), _compactText", display)
self.assertIn("* profile_scale;", display)
def test_lora_fresh_install_uses_compiled_cascade_defaults(self):
profile = PROFILE.read_text(encoding="utf-8")
lora = profile[profile.index("[SenseCapIndicator-LoRa]") :]
lora = lora[: lora.index("[env:")]
self.assertIn("-UMESH_PRIMARY_ESPNOW", lora)
self.assertIn("-D SENSECAP_INDICATOR_LORA", lora)
self.assertIn("-D USE_SX1262", lora)
self.assertIn("-<helpers/esp32/ESPNOWRadio.cpp>", lora)
mesh = MESH.read_text(encoding="utf-8")
self.assertIn("_prefs.freq = LORA_FREQ;", mesh)
self.assertIn("_prefs.sf = LORA_SF;", mesh)
self.assertIn("_prefs.bw = LORA_BW;", mesh)
self.assertIn("_prefs.cr = LORA_CR;", mesh)
guarded_rx_delay = (
"#ifdef DEFAULT_RX_DELAY_BASE\n"
" _prefs.rx_delay_base = DEFAULT_RX_DELAY_BASE;\n"
"#endif"
)
self.assertIn(guarded_rx_delay, mesh)
# Saved preferences load after constructor defaults, so an app-only
# update remains non-destructive while an erased install uses them.
begin = mesh[mesh.index("void MyMesh::begin(") :]
self.assertIn("_store->loadPrefs(_prefs", begin)
store = DATA_STORE.read_text(encoding="utf-8")
self.assertIn('if (_fs->exists("/new_prefs"))', store)
self.assertIn('loadPrefsInt("/new_prefs", prefs', store)
def test_explicit_usa_cascadia_and_cascade_build_flags(self):
command = r'''
set -e
source "$1"
PLATFORMIO_BUILD_FLAGS=""
RADIO_FREQ_OVERRIDE=910.525
RADIO_BW_OVERRIDE=62.5
RADIO_SF_OVERRIDE=7
RADIO_CR_OVERRIDE=5
FIRMWARE_PROFILE_OVERRIDE=cascade
apply_radio_overrides
apply_firmware_profile_overrides
printf '%s\n' "$PLATFORMIO_BUILD_FLAGS"
'''
result = subprocess.run(
["bash", "-c", command, "test", str(BUILD)],
text=True,
capture_output=True,
check=False,
)
self.assertEqual(result.returncode, 0, result.stderr)
flags = result.stdout.split()
for expected in (
"-DLORA_FREQ=910.525",
"-DLORA_BW=62.5",
"-DLORA_SF=7",
"-DLORA_CR=5",
"-DCASCADE_PROFILE=1",
"-DDEFAULT_RX_DELAY_BASE=2.0f",
):
self.assertIn(expected, flags)
build = BUILD.read_text(encoding="utf-8")
self.assertIn(
"'SenseCapIndicator-LoRa_comp_radio_usb_wifi|"
"SenseCapIndicator-LoRa_companion_radio_full'",
build,
)
if __name__ == "__main__":
unittest.main()