3737 Q10MapPacketKind ,
3838 Q10Room ,
3939 parse_map_packet ,
40+ parse_trace_packet ,
4041)
42+ from roborock .map .b01_q10_render import Q10MapOverlays , render_q10_map
4143from roborock .map .map_parser import MapParser , MapParserConfig , ParsedMapData
4244
4345if TYPE_CHECKING :
@@ -63,6 +65,7 @@ def _runner(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
6365_V1_S5_MAP = _TESTDATA_DIR / "v1" / "s5_fw2008_with_segments.bin"
6466_V1_S6_MAP = _TESTDATA_DIR / "v1" / "s6_fw2652_with_active_segment_and_no_mop_zone.bin"
6567_Q10_MAP = _TESTDATA_DIR / "b01_q10_map.bin"
68+ _Q10_TRACE = _TESTDATA_DIR / "b01_q10_trace_session.bin"
6669
6770
6871def _build_synthetic_q10_grid (width : int = 200 , height : int = 200 ) -> Q10MapPacket :
@@ -98,14 +101,15 @@ def _build_synthetic_q10_grid(width: int = 200, height: int = 200) -> Q10MapPack
98101
99102
100103def _validate_image (
101- result : ParsedMapData | None ,
104+ result : ParsedMapData | bytes | None ,
102105 expected_size : tuple [int , int ] | None = None ,
103106) -> None :
104107 """Validate rendered PNG image integrity and pixel bounds outside the timed benchmark."""
105108 assert result is not None
106- assert result .image_content is not None
107- assert len (result .image_content ) > 0
108- image = Image .open (io .BytesIO (result .image_content ))
109+ image_bytes = result if isinstance (result , bytes ) else result .image_content
110+ assert image_bytes is not None
111+ assert len (image_bytes ) > 0
112+ image = Image .open (io .BytesIO (image_bytes ))
109113 assert image .format == "PNG"
110114 assert image .mode == "RGBA"
111115 assert image .getbbox () is not None # Image has non-empty drawn content
@@ -203,6 +207,17 @@ def test_benchmark_q10_map_full_scale_render_scale_4(benchmark: BenchmarkFixture
203207 _validate_image (result , (800 , 800 ))
204208
205209
210+ def test_benchmark_q10_map_render_with_trace (benchmark : BenchmarkFixture ) -> None :
211+ """Benchmark Q10 render_q10_map end-to-end composite pipeline with trace overlay."""
212+ map_bytes = _Q10_MAP .read_bytes ()
213+ trace_bytes = _Q10_TRACE .read_bytes ()
214+ packet = parse_map_packet (map_bytes )
215+ trace = parse_trace_packet (trace_bytes )
216+ config = B01Q10MapParserConfig (map_scale = 4 )
217+ result = benchmark (render_q10_map , packet , trace = trace , overlays = Q10MapOverlays (), config = config )
218+ _validate_image (result , (32 , 24 ))
219+
220+
206221# ---------------------------------------------------------------------------
207222# Standalone CLI Benchmark & Profiler
208223# ---------------------------------------------------------------------------
@@ -225,6 +240,11 @@ def _run_benchmarks(iterations: int, warmup: int, profile: bool) -> None:
225240 p_q10_scale1 = B01Q10MapParser (B01Q10MapParserConfig (map_scale = 1 ))
226241 p_q10_scale4 = B01Q10MapParser (B01Q10MapParserConfig (map_scale = 4 ))
227242
243+ q10_trace_data = _Q10_TRACE .read_bytes ()
244+ q10_map_packet = parse_map_packet (q10_data )
245+ q10_trace_packet = parse_trace_packet (q10_trace_data )
246+ q10_render_config = B01Q10MapParserConfig (map_scale = 4 )
247+
228248 benchmarks : list [tuple [str , Callable [[], Any ]]] = [
229249 ("V1 S5 Map (Scale 4, Default)" , lambda : p_v1_s5 .parse (v1_s5_data )),
230250 ("V1 S6 Map (Scale 4, Default)" , lambda : p_v1_s6_scale4 .parse (v1_s6_data )),
@@ -236,6 +256,15 @@ def _run_benchmarks(iterations: int, warmup: int, profile: bool) -> None:
236256 ("Q10 Small Map (Scale 1)" , lambda : p_q10_small .parse (q10_data )),
237257 ("Q10 200x200 Map (Scale 1)" , lambda : p_q10_scale1 .parse_packet (q10_packet_200 )),
238258 ("Q10 200x200 Map (Scale 4)" , lambda : p_q10_scale4 .parse_packet (q10_packet_200 )),
259+ (
260+ "Q10 Render with Trace" ,
261+ lambda : render_q10_map (
262+ q10_map_packet ,
263+ trace = q10_trace_packet ,
264+ overlays = Q10MapOverlays (),
265+ config = q10_render_config ,
266+ ),
267+ ),
239268 ]
240269
241270 print (f"\n Running { len (benchmarks )} benchmarks ({ warmup } warmup, { iterations } timed iterations)...\n " )
@@ -255,7 +284,7 @@ def _run_benchmarks(iterations: int, warmup: int, profile: bool) -> None:
255284 min_ms = timings [0 ]
256285 mean_ms = sum (timings ) / len (timings )
257286 med_ms = timings [len (timings ) // 2 ]
258- p95_ms = timings [int (len (timings ) * 0.95 )]
287+ p95_ms = timings [max ( 0 , int (( len (timings ) - 1 ) * 0.95 ) )]
259288 ops_per_sec = 1000.0 / mean_ms if mean_ms > 0 else float ("inf" )
260289
261290 results .append ((name , min_ms , med_ms , mean_ms , p95_ms , ops_per_sec ))
0 commit comments