#!/usr/bin/env python3 """Exercise actual button routing and message filters with a host display.""" from pathlib import Path import subprocess import tempfile import unittest from test_replay_reset_integration import extract_braced ROOT = Path(__file__).resolve().parents[1] PREAMBLE = r''' #include #include #include #include #include #include #include #include #include #define UI_SMALL_MESSAGE_FONT 0 #define UI_BUTTON_READER_HINT 1 #define UI_MESSAGE_CHANNEL_FOOTER 0 #define UI_COMPACT_MESSAGE_STATUS 0 #define UI_MSG_PREVIEW_SIZE 161 #define MAX_GROUP_CHANNELS 4 #define AUTO_OFF_MILLIS 15000 #define MESH_DEBUG_PRINTLN(...) ColorVal UIColor::window_bkg=0, UIColor::title_bkg=0, UIColor::title_txt=1; ColorVal UIColor::primary_txt=1, UIColor::secondary_txt=1, UIColor::warning_txt=1; ColorVal UIColor::popup_bkg=0, UIColor::popup_txt=1, UIColor::corp_blue=1; uint64_t companionMessageNowMillis() { return millis(); } uint64_t companionMessageElapsedMillis(uint64_t when) { return millis()-when; } struct StrHelper { static void strncpy(char* out,const char* in,size_t size) { snprintf(out,size,"%s",in); } }; struct ChannelDetails { char name[32]; }; struct Mesh { bool getChannel(int channel,ChannelDetails& details) const { const char* names[]={"Public","","Second","Empty"}; if (channel<0 || channel>=MAX_GROUP_CHANNELS) return false; snprintf(details.name,sizeof(details.name),"%s",names[channel]); return true; } } the_mesh; struct Display : DisplayDriver { struct Line { int x,y; std::string text; }; std::vector lines; int x=0,y=0; bool on=true; Display() : DisplayDriver(128,64) {} bool isOn() override { return on; } void turnOn() override { on=true; } void turnOff() override { on=false; } void clear() override { lines.clear(); } void startFrame(ColorVal=0) override { clear(); } void endFrame() override {} void setTextSize(int) override {} void setColor(ColorVal) override {} void setCursor(int a,int b) override { x=a;y=b; } uint16_t getTextWidth(const char* text) override { return strlen(text)*4; } void print(const char* text) override { assert(x>=0 && x+getTextWidth(text)<=width()); assert(y>=0 && y+8<=height()); lines.push_back({x,y,text}); } void fillRect(int a,int b,int w,int h) override { assert(a>=0 && b>=0 && a+w<=width() && b+h<=height()); } void drawRect(int a,int b,int w,int h) override { fillRect(a,b,w,h); } void drawXbm(int,int,const uint8_t*,int,int) override {} bool contains(const char* text) const { for (const auto& line:lines) if (line.text==text) return true; return false; } }; struct Screen : UIScreen { uint8_t key=0; int render(DisplayDriver&) override { return 0; } bool handleInput(char c) override { key=static_cast(c); return true; } }; MomentaryButton user_btn(7,1000,true,true,true); class UITask { public: DisplayDriver* _display; UIScreen* curr=nullptr; UIScreen* msg_preview=nullptr; UIScreen* home=nullptr; UIScreen* john_reader=nullptr; uint32_t _auto_off=0,_next_refresh=0; int buzzer_changes=0; explicit UITask(DisplayDriver& display) : _display(&display) {} bool isJohnReaderActive() const { return john_reader && curr==john_reader; } void gotoHomeScreen() { curr=home; } void toggleBuzzer() { ++buzzer_changes; } char handleLongPress(char c) { return c; } char handleMultiClick(char,bool); char handleDoubleClick(char); char checkDisplayOn(char); void pollButton(); }; ''' SCENARIOS = r''' void gesture(UITask& task,int count) { for (int tap=0;taphandleInput(c);\n}\n" for enabled in (0, 1): for signedness in ("-fsigned-char", "-funsigned-char"): with self.subTest(feature=enabled, signedness=signedness), tempfile.TemporaryDirectory() as temp: binary = Path(temp) / "navigation" result = subprocess.run([ "c++", "-std=c++17", signedness, f"-DCOMPANION_FEATURE_JOHN={enabled}", "-I" + str(ROOT / "src"), "-I" + str(ROOT / "test/mocks"), "-fsanitize=address,undefined", "-fno-pie", "-no-pie", "-x", "c++", "-", str(ROOT / "src/helpers/ui/MomentaryButton.cpp"), "-o", str(binary), ], input=PREAMBLE + implementation + SCENARIOS, 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.stdout + result.stderr) if __name__ == "__main__": unittest.main()