mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-13 02:35:37 +00:00
118 lines
6.0 KiB
Python
118 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Exercise actual non-Indicator driver geometry/drawing with recording panels."""
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
UI = ROOT / "src/helpers/ui"
|
|
|
|
|
|
def method(source, cls, name):
|
|
start = source.index(f"{cls}::{name}(")
|
|
start = source.rfind("\n", 0, start) + 1
|
|
end = source.index("\n}", start) + 2
|
|
return source[start:end] + "\n"
|
|
|
|
|
|
class NativeDisplaysTest(unittest.TestCase):
|
|
def test_real_headers_and_rendering(self):
|
|
cases = [
|
|
("ST7789Display", 240, 135, []),
|
|
("ST7789Display", 320, 170, ["HELTEC_VISION_MASTER_T190"]),
|
|
("ST7789Display", 320, 240, ["THINKNODE_M9"]),
|
|
("ST7789LCDDisplay", 320, 240, []),
|
|
("ST7789LCDDisplay", 320, 240, ["USE_PIN_TFT"]),
|
|
("ST7789LCDDisplay", 320, 240, ["LILYGO_TDECK"]),
|
|
("ST7789LCDDisplay", 240, 320, ["HELTEC_V4_R8_TFT", "ST7789_PORTRAIT_PROFILE", "DISPLAY_ROTATION=0"]),
|
|
("NV3001BDisplay", 220, 128, []),
|
|
("GxEPDDisplay", 200, 200, []),
|
|
("GxEPDDisplay", 250, 122, ["EINK_DISPLAY_MODEL=WidePanel"]),
|
|
("GxEPDDisplay", 250, 122, ["EINK_DISPLAY_MODEL=WidePanel", "DISPLAY_ROTATION=1"]),
|
|
("GxEPDDisplay", 192, 256, ["EINK_DISPLAY_MODEL=TallPanel", "DISPLAY_ROTATION=4"]),
|
|
]
|
|
with tempfile.TemporaryDirectory(prefix="meshcore-native-panels-") as temp:
|
|
for cls, width, height, defines in cases:
|
|
with self.subTest(driver=cls, defines=defines):
|
|
source = (ROOT / "test/fixtures/native_displays/recording.cpp").read_text()
|
|
header = (UI / (cls + ".h")).read_text()
|
|
source += re.sub(r'^\s*#(?:include|pragma).*$', '', header, flags=re.M)
|
|
driver = (UI / (cls + ".cpp")).read_text()
|
|
if cls == "NV3001BDisplay":
|
|
source += "Canvas nv_canvas;\n"
|
|
source += f"bool {cls}::begin() {{ is_on=true; return true; }}\n"
|
|
source += f"void {cls}::fillPhysicalRect(int x,int y,int w,int h) {{ Canvas::active->fillRect(x,y,w,h); }}\n"
|
|
a = driver.index("static const uint8_t font5x7")
|
|
b = driver.index("static void setupOptionalOutput", a)
|
|
source += driver[a:b]
|
|
source += method(driver, cls, "drawChar")
|
|
else:
|
|
source += f"bool {cls}::begin() {{ return true; }}\n"
|
|
for name in ("turnOn", "turnOff", "clear", "endFrame"):
|
|
source += f"void {cls}::{name}() {{}}\n"
|
|
source += f"void {cls}::startFrame(ColorVal) {{}}\n"
|
|
if cls == "ST7789LCDDisplay":
|
|
source += f"void {cls}::setFlipped(bool) {{}}\n"
|
|
names = ["setTextSize", "setColor", "setCursor", "print", "fillRect", "drawRect", "drawXbm", "getTextWidth"]
|
|
if cls == "ST7789Display":
|
|
names += ["printWordWrap"]
|
|
source += "\n".join(method(driver, cls, name) for name in names)
|
|
source += f"\nusing Driver = {cls};\n"
|
|
source += r'''
|
|
int main() {
|
|
Driver d; d.begin(); d.setColor(1); d.setTextSize(1);
|
|
auto& c=*Canvas::active;
|
|
assert(d.width()==EXPECTED_WIDTH && d.height()==EXPECTED_HEIGHT);
|
|
assert(!d.useSmallMessageFont());
|
|
c.reset();
|
|
for(int y=0;y<d.height();++y) for(int x=0;x<d.width();++x) d.fillRect(x,y,1,1);
|
|
for(int pixel:c.pixels) assert(pixel==1);
|
|
c.reset(); d.drawRect(0,0,d.width(),d.height());
|
|
for(int y=0;y<d.height();++y) for(int x=0;x<d.width();++x)
|
|
assert((c.pixels[y*d.width()+x]>0)==(x==0||y==0||x==d.width()-1||y==d.height()-1));
|
|
// Non-byte-aligned bitmap and the bottom/right panel boundary.
|
|
const uint8_t bitmap[]={0x81,0x80,0x42,0x00};
|
|
c.reset(); d.drawXbm(d.width()-9,d.height()-2,bitmap,9,2);
|
|
assert(std::count(c.pixels.begin(),c.pixels.end(),1)==5);
|
|
assert(c.pixels.back()==0 && c.pixels[(d.height()-2)*d.width()+d.width()-1]==1);
|
|
assert(d.getTextWidth("MMMM") >= 24);
|
|
assert(d.getTextWidth("MMMMMMMM") > d.getTextWidth("MMMM"));
|
|
assert(d.getTextWidth("")==0);
|
|
d.setCursor(7,9); d.print("M");
|
|
#ifdef NV_NATIVE_TEST
|
|
// Native 5x7 glyph: no historical vertical doubling.
|
|
c.reset(); d.setCursor(0,0); d.print("M");
|
|
for(int y=7;y<d.height();++y) for(int x=0;x<d.width();++x) assert(c.pixels[y*d.width()+x]==0);
|
|
#else
|
|
assert(c.x==7 && c.y==9+c.ascent);
|
|
#endif
|
|
}
|
|
'''
|
|
flags = [f"-D{x}" for x in defines]
|
|
if cls == "NV3001BDisplay":
|
|
flags += ["-DNV_NATIVE_TEST"]
|
|
if os.name != "nt":
|
|
flags += ["-fsanitize=address,undefined", "-fno-omit-frame-pointer"]
|
|
binary = Path(temp) / "display.exe"
|
|
result = subprocess.run(["c++", "-std=c++11", "-Wall", "-Wextra", "-Werror",
|
|
"-Wno-unused-parameter", "-Wno-unused-variable", "-Wno-type-limits", *flags,
|
|
f"-DEXPECTED_WIDTH={width}", f"-DEXPECTED_HEIGHT={height}",
|
|
"-I", str(ROOT / "src"), "-x", "c++", "-", "-o", str(binary)],
|
|
input=source, text=True, capture_output=True)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
result = subprocess.run([str(binary)], text=True, capture_output=True)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
|
|
def test_no_obsolete_panel_scaling_flags(self):
|
|
for driver in ("ST7735Display", "ST7789Display", "ST7789LCDDisplay", "NV3001BDisplay", "GxEPDDisplay"):
|
|
for suffix in (".h", ".cpp"):
|
|
source = (UI / (driver + suffix)).read_text()
|
|
self.assertNotRegex(source, r"DISPLAY_SCALE|EINK_SCALE|SCALE_[XY]|portraitViewport")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|