@@ -614,6 +614,68 @@ public void Measure_And_Arrange_Should_Use_WidthIncludingTrailingWhitespace_For_
614614 Assert . Equal ( new Rect ( default , expectedSize ) , target . Bounds ) ;
615615 }
616616
617+ [ Fact ]
618+ public void Reading_TextLayout_Before_Measure_Should_Not_Poison_Inlines_Shaping ( )
619+ {
620+ using var app = UnitTestApplication . Start ( TestServices . MockPlatformRenderInterface ) ;
621+
622+ var target = new TextBlock
623+ {
624+ Inlines = new InlineCollection { new Run ( "Hello World" ) }
625+ } ;
626+
627+ // Something outside of the layout pass (e.g. a render pass that runs before the
628+ // queued measure) reads TextLayout while _textRuns has not been built yet.
629+ _ = target . TextLayout ;
630+
631+ target . Measure ( new Size ( 1000 , 1000 ) ) ;
632+
633+ Assert . True ( target . DesiredSize . Width > 0 , $ "DesiredSize was { target . DesiredSize } ") ;
634+ }
635+
636+ [ Fact ]
637+ public void Reading_TextLayout_Between_Invalidation_And_Measure_Should_Not_Poison_Inlines_Shaping ( )
638+ {
639+ using var app = UnitTestApplication . Start ( TestServices . MockPlatformRenderInterface ) ;
640+
641+ // Starts out empty, exactly like a title bound through a converter that yields an
642+ // empty InlineCollection until the view model delivers the real value.
643+ var target = new TextBlock { Inlines = new InlineCollection ( ) } ;
644+
645+ target . Measure ( new Size ( 1000 , 1000 ) ) ;
646+ target . Arrange ( new Rect ( 0 , 0 , 1000 , 1000 ) ) ;
647+
648+ target . Inlines = new InlineCollection { new Run ( "Hello World" ) } ;
649+
650+ // Render runs before the queued measure is processed.
651+ _ = target . TextLayout ;
652+
653+ target . Measure ( new Size ( 1000 , 1000 ) ) ;
654+
655+ Assert . True ( target . DesiredSize . Width > 0 , $ "DesiredSize was { target . DesiredSize } ") ;
656+ }
657+
658+ [ Fact ]
659+ public void Reading_TextLayout_Before_Measure_Should_Not_Poison_TextRunCache_Across_Constraints ( )
660+ {
661+ using var app = UnitTestApplication . Start ( TestServices . MockPlatformRenderInterface ) ;
662+
663+ var target = new TextBlock { Inlines = new InlineCollection ( ) } ;
664+
665+ target . Measure ( new Size ( 1000 , 1000 ) ) ;
666+ target . Arrange ( new Rect ( 0 , 0 , 1000 , 1000 ) ) ;
667+
668+ target . Inlines = new InlineCollection { new Run ( "Hello World" ) } ;
669+
670+ _ = target . TextLayout ;
671+
672+ // A *different* constraint forces MeasureOverride to drop _textLayout, so anything
673+ // still wrong here comes from the TextRunCache rather than the stale layout.
674+ target . Measure ( new Size ( 900 , 1000 ) ) ;
675+
676+ Assert . True ( target . DesiredSize . Width > 0 , $ "DesiredSize was { target . DesiredSize } ") ;
677+ }
678+
617679 private class TestTextBlock : TextBlock
618680 {
619681 public Size Constraint => _constraint ;
0 commit comments