from pathlib import Path import re,subprocess,tempfile src=(Path(__file__).resolve().parents[1]/'components/platform/src/lcd_direct.c').read_text() def extract(name): m=re.search(r'static [^;{}]+\b'+name+r'\([^;{}]*\)\s*\{',src) a=m.start(); b=src.index('{',a)+1; depth=1 while depth: if src[b]=='{': depth+=1 if src[b]=='}': depth-=1 b+=1 return src[a:b] prefix='''#include #include #include #include #include #include #define LCD_DIRECT_WIDTH 320 #define LCD_DIRECT_EMOTE_DMA_ROWS 16 #define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100 #define portENTER_CRITICAL(x) ((void)0) #define portEXIT_CRITICAL(x) ((void)0) #define pdMS_TO_TICKS(x) (x) #define ESP_LOGE(...) ((void)0) #define ESP_OK 0 #define ESP_ERR_INVALID_SIZE 1 #define ESP_ERR_TIMEOUT 2 #define pdTRUE 1 typedef int esp_err_t; typedef int gfx_disp_t; static bool s_emote_active, s_emote_transfer_fault; static int s_emote_flush_error, s_panel, s_transfer_done_sem; static uint16_t dma[5120]; static uint16_t *s_emote_dma_pixels=dma; static uint16_t capture[320*240]; static int calls,ready,fail_call,timeout_call,origin_y,width; static bool gfx_disp_flush_ready(gfx_disp_t *d,bool swap) {++ready;return true;} static int xSemaphoreTake(int sem,int ticks) {return ticks && calls!=timeout_call;} static int esp_lcd_panel_draw_bitmap(int panel,int x1,int y1,int x2,int y2,const void *data) { ++calls; assert(ready==0); assert((x2-x1)*(y2-y1)<=5120); assert(data==dma); if(calls==fail_call) return 3; memcpy(capture+(y1-origin_y)*width,data,(x2-x1)*(y2-y1)*2); return 0; } ''' test='''int main(void) { uint16_t source[320*240]; for(int i=0;i<320*240;++i) source[i]=(uint16_t)i; for(width=1;width<=320;++width) { calls=ready=fail_call=timeout_call=0;origin_y=65;s_emote_active=true; s_emote_transfer_fault=false;s_emote_flush_error=0; lcd_direct_emote_flush(NULL,0,65,width,158,source); assert(ready==1 && s_emote_flush_error==0); assert(memcmp(source,capture,width*93*2)==0); assert(calls==(93+5120/width-1)/(5120/width)); } width=238;calls=ready=0;timeout_call=2;s_emote_flush_error=0; lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(ready==1 && s_emote_transfer_fault && s_emote_flush_error==2 && calls==2); lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(calls==2 && ready==2); s_emote_transfer_fault=false;timeout_call=0;fail_call=1;calls=ready=0; lcd_direct_emote_flush(NULL,41,65,279,158,source); assert(calls==1 && ready==1 && s_emote_flush_error==3); calls=ready=0;s_emote_active=false; lcd_direct_emote_flush(NULL,41,65,279,158,source);assert(calls==0 && ready==1); uint8_t eyes[238*14*3+2];memset(eyes,123,sizeof(eyes)); lcd_direct_emote_draw_closed_eyes(eyes+1,238,14,0xffe0); assert(eyes[0]==123 && eyes[sizeof(eyes)-1]==123); const uint8_t *alpha=eyes+1+238*14*2; for(int y=0;y<14;++y) for(int x=3;x<=95;++x) assert(alpha[y*238+x]==alpha[y*238+x+140]); assert(alpha[3*238+49]==255 && alpha[11*238+3]==255); puts("PASS: 320 transfer widths, DMA bounds/order, timeout latch, inactive/error completion and symmetric closed-eye raster"); }''' with tempfile.TemporaryDirectory(prefix='emote-transport-') as tmp: c=Path(tmp)/'test.c';exe=Path(tmp)/'test' c.write_text(prefix+extract('lcd_direct_emote_flush')+'\n'+extract('lcd_direct_emote_draw_closed_eyes')+'\n'+test) subprocess.run(['clang','-std=c11','-fsanitize=address,undefined',str(c),'-o',str(exe)],check=True) subprocess.run([str(exe)],check=True)