@@ -498,6 +498,135 @@ class AttrRowTests
498
498
}
499
499
}
500
500
501
+ TEST_METHOD (TestReverseIteratorWalkFromMiddle)
502
+ {
503
+ // GH #3409, walking backwards through color range runs out of bounds
504
+ // We're going to create an attribute row with assorted colors and varying lengths
505
+ // just like the row of text on the Ubuntu prompt line that triggered this bug being found.
506
+ // Then we're going to walk backwards through the iterator like a selection-expand-to-left
507
+ // operation and ensure we don't run off the bounds.
508
+
509
+ // walk the chain, from index, stepSize at a time
510
+ // ensure we don't crash
511
+ auto testWalk = [](ATTR_ROW* chain, size_t index , int stepSize) {
512
+ // move to starting index
513
+ auto iter = chain->cbegin ();
514
+ iter += index ;
515
+
516
+ // Now walk backwards in a loop until 0.
517
+ while (iter)
518
+ {
519
+ iter -= stepSize;
520
+ }
521
+
522
+ Log::Comment (L" We made it through without crashing!" );
523
+ };
524
+
525
+ // take one step of size stepSize on the chain
526
+ // index is where we start from
527
+ // expectedAttribute is what we expect to read here
528
+ auto verifyStep = [](ATTR_ROW* chain, size_t index , int stepSize, TextAttribute expectedAttribute) {
529
+ // move to starting index
530
+ auto iter = chain->cbegin ();
531
+ iter += index ;
532
+
533
+ // Now step backwards
534
+ iter -= stepSize;
535
+
536
+ VERIFY_ARE_EQUAL (expectedAttribute, *iter);
537
+ };
538
+
539
+ Log::Comment (L" Reverse iterate through ubuntu prompt" );
540
+ {
541
+ // Create attr row representing a buffer that's 121 wide.
542
+ auto chain = std::make_unique<ATTR_ROW>(121 , _DefaultAttr);
543
+
544
+ // The repro case had 4 chain segments.
545
+ chain->_list .resize (4 );
546
+
547
+ // The color 10 went for the first 18.
548
+ chain->_list [0 ].SetAttributes (TextAttribute (0xA ));
549
+ chain->_list [0 ].SetLength (18 );
550
+
551
+ // Default color for the next 1
552
+ chain->_list [1 ].SetAttributes (TextAttribute ());
553
+ chain->_list [1 ].SetLength (1 );
554
+
555
+ // Color 12 for the next 29
556
+ chain->_list [2 ].SetAttributes (TextAttribute (0xC ));
557
+ chain->_list [2 ].SetLength (29 );
558
+
559
+ // Then default color to end the run
560
+ chain->_list [3 ].SetAttributes (TextAttribute ());
561
+ chain->_list [3 ].SetLength (73 );
562
+
563
+ // The sum of the lengths should be 121.
564
+ VERIFY_ARE_EQUAL (chain->_cchRowWidth , chain->_list [0 ]._cchLength + chain->_list [1 ]._cchLength + chain->_list [2 ]._cchLength + chain->_list [3 ]._cchLength );
565
+
566
+ auto index = chain->_list [0 ].GetLength ();
567
+ auto stepSize = 1 ;
568
+ testWalk (chain.get (), index , stepSize);
569
+ }
570
+
571
+ Log::Comment (L" Reverse iterate across a text run in the chain" );
572
+ {
573
+ // Create attr row representing a buffer that's 3 wide.
574
+ auto chain = std::make_unique<ATTR_ROW>(3 , _DefaultAttr);
575
+
576
+ // The repro case had 3 chain segments.
577
+ chain->_list .resize (3 );
578
+
579
+ // The color 10 went for the first 1.
580
+ chain->_list [0 ].SetAttributes (TextAttribute (0xA ));
581
+ chain->_list [0 ].SetLength (1 );
582
+
583
+ // The color 11 for the next 1
584
+ chain->_list [1 ].SetAttributes (TextAttribute (0xB ));
585
+ chain->_list [1 ].SetLength (1 );
586
+
587
+ // Color 12 for the next 1
588
+ chain->_list [2 ].SetAttributes (TextAttribute (0xC ));
589
+ chain->_list [2 ].SetLength (1 );
590
+
591
+ // The sum of the lengths should be 3.
592
+ VERIFY_ARE_EQUAL (chain->_cchRowWidth , chain->_list [0 ]._cchLength + chain->_list [1 ]._cchLength + chain->_list [2 ]._cchLength );
593
+
594
+ // on 'ABC', step from B to A
595
+ auto index = 1 ;
596
+ auto stepSize = 1 ;
597
+ verifyStep (chain.get (), index , stepSize, TextAttribute (0xA ));
598
+ }
599
+
600
+ Log::Comment (L" Reverse iterate across two text runs in the chain" );
601
+ {
602
+ // Create attr row representing a buffer that's 3 wide.
603
+ auto chain = std::make_unique<ATTR_ROW>(3 , _DefaultAttr);
604
+
605
+ // The repro case had 3 chain segments.
606
+ chain->_list .resize (3 );
607
+
608
+ // The color 10 went for the first 1.
609
+ chain->_list [0 ].SetAttributes (TextAttribute (0xA ));
610
+ chain->_list [0 ].SetLength (1 );
611
+
612
+ // The color 11 for the next 1
613
+ chain->_list [1 ].SetAttributes (TextAttribute (0xB ));
614
+ chain->_list [1 ].SetLength (1 );
615
+
616
+ // Color 12 for the next 1
617
+ chain->_list [2 ].SetAttributes (TextAttribute (0xC ));
618
+ chain->_list [2 ].SetLength (1 );
619
+
620
+ // The sum of the lengths should be 3.
621
+ VERIFY_ARE_EQUAL (chain->_cchRowWidth , chain->_list [0 ]._cchLength + chain->_list [1 ]._cchLength + chain->_list [2 ]._cchLength );
622
+
623
+ // on 'ABC', step from C to A
624
+ auto index = 2 ;
625
+ auto stepSize = 2 ;
626
+ verifyStep (chain.get (), index , stepSize, TextAttribute (0xA ));
627
+ }
628
+ }
629
+
501
630
TEST_METHOD (TestSetAttrToEnd)
502
631
{
503
632
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
0 commit comments