Skip to content

Commit 39dac5c

Browse files
committed
Add comprehensive Smart mode tests
Add SmartModeTests.cs with 8 test cases: - Default behavior verification - Small changes → CELL mode - High coverage (75%) → LINE mode - High fragmentation (6 regions) → LINE mode - Medium fragmentation (3 regions) → CELL mode - Threshold boundary testing (60%) - Full line changes → LINE mode - Mixed lines in same frame (most important) All 196 tests passing (188 original + 8 new)
1 parent 464bb1e commit 39dac5c

1 file changed

Lines changed: 355 additions & 0 deletions

File tree

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
using SharpConsoleUI;
2+
using SharpConsoleUI.Configuration;
3+
using SharpConsoleUI.Controls;
4+
using SharpConsoleUI.Tests.Infrastructure;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace SharpConsoleUI.Tests.Rendering.Unit.BottomLayer;
9+
10+
/// <summary>
11+
/// Tests for Smart adaptive dirty tracking mode.
12+
/// Validates that Smart mode chooses optimal rendering strategy per line.
13+
/// </summary>
14+
public class SmartModeTests
15+
{
16+
private readonly ITestOutputHelper _output;
17+
18+
public SmartModeTests(ITestOutputHelper output)
19+
{
20+
_output = output;
21+
}
22+
23+
[Fact]
24+
public void SmartMode_DefaultBehavior_AdaptsToContent()
25+
{
26+
// Verify that default test system (which uses Smart mode) adapts behavior
27+
// This test verifies Smart mode is working by checking adaptive behavior
28+
var system = TestWindowSystemBuilder.CreateTestSystem();
29+
30+
// Small change should result in minimal output (CELL mode behavior)
31+
var control = new MarkupControl(new List<string> { "AAAA" + new string(' ', 196) });
32+
var window = new Window(system) { Left = 10, Top = 5, Width = 200, Height = 10 };
33+
window.AddControl(control);
34+
system.WindowStateService.AddWindow(window);
35+
system.Render.UpdateDisplay();
36+
37+
control.SetContent(new List<string> { "BBBB" + new string(' ', 196) });
38+
window.Invalidate(true);
39+
system.Render.UpdateDisplay();
40+
41+
var metrics = system.RenderingDiagnostics?.LastMetrics;
42+
Assert.NotNull(metrics);
43+
44+
// Smart mode should use CELL mode for small changes (< 50 cells rendered)
45+
Assert.True(metrics.CellsActuallyRendered < 50,
46+
$"Smart mode should use CELL mode for small changes, got {metrics.CellsActuallyRendered}");
47+
}
48+
49+
[Fact]
50+
public void SmartMode_SmallChange_UsesCellMode()
51+
{
52+
// Arrange - Small change (8 cells = 4% coverage)
53+
var system = TestWindowSystemBuilder.CreateTestSystem();
54+
var control = new MarkupControl(new List<string> { "AAAAAAAA" + new string(' ', 192) });
55+
var window = new Window(system)
56+
{
57+
Left = 10,
58+
Top = 5,
59+
Width = 200,
60+
Height = 10,
61+
Title = "Smart Test"
62+
};
63+
64+
window.AddControl(control);
65+
system.WindowStateService.AddWindow(window);
66+
system.Render.UpdateDisplay(); // Frame 1
67+
68+
// Act - Change 8 characters (should use CELL mode)
69+
control.SetContent(new List<string> { "BBBBBBBB" + new string(' ', 192) });
70+
window.Invalidate(true);
71+
system.Render.UpdateDisplay(); // Frame 2
72+
73+
// Assert - Should render minimal cells (CELL mode behavior)
74+
var metrics = system.RenderingDiagnostics?.LastMetrics;
75+
Assert.NotNull(metrics);
76+
77+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
78+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
79+
80+
// 8 cells dirty, should render ~10-20 cells (CELL mode with small overhead)
81+
Assert.Equal(8, metrics.DirtyCellsMarked);
82+
Assert.True(metrics.CellsActuallyRendered < 50,
83+
$"Expected CELL mode (~10-20 cells), got {metrics.CellsActuallyRendered}");
84+
}
85+
86+
[Fact]
87+
public void SmartMode_HighCoverage_UsesLineMode()
88+
{
89+
// Arrange - High coverage change (150 cells = 75% coverage)
90+
var system = TestWindowSystemBuilder.CreateTestSystem();
91+
var content = new string('A', 150) + new string(' ', 50);
92+
var control = new MarkupControl(new List<string> { content });
93+
var window = new Window(system)
94+
{
95+
Left = 10,
96+
Top = 5,
97+
Width = 200,
98+
Height = 10,
99+
Title = "Smart Test"
100+
};
101+
102+
window.AddControl(control);
103+
system.WindowStateService.AddWindow(window);
104+
system.Render.UpdateDisplay(); // Frame 1
105+
106+
// Act - Change 150 characters (75% coverage → should use LINE mode)
107+
var newContent = new string('B', 150) + new string(' ', 50);
108+
control.SetContent(new List<string> { newContent });
109+
window.Invalidate(true);
110+
system.Render.UpdateDisplay(); // Frame 2
111+
112+
// Assert - Should render entire line (LINE mode behavior)
113+
var metrics = system.RenderingDiagnostics?.LastMetrics;
114+
Assert.NotNull(metrics);
115+
116+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
117+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
118+
_output.WriteLine($"Coverage: {150.0/200:P}");
119+
120+
// 150 cells dirty (75% coverage > 60% threshold), should use LINE mode (200 cells)
121+
Assert.Equal(150, metrics.DirtyCellsMarked);
122+
Assert.Equal(200, metrics.CellsActuallyRendered);
123+
}
124+
125+
[Fact]
126+
public void SmartMode_HighlyFragmented_UsesLineMode()
127+
{
128+
// Arrange - 6 separate regions (exceeds fragmentation threshold of 5)
129+
var system = TestWindowSystemBuilder.CreateTestSystem();
130+
// Pattern: "A____B____C____D____E____F____" (6 single chars separated by spaces)
131+
var content = "A" + new string(' ', 20) +
132+
"B" + new string(' ', 20) +
133+
"C" + new string(' ', 20) +
134+
"D" + new string(' ', 20) +
135+
"E" + new string(' ', 20) +
136+
"F" + new string(' ', 73); // Fill to 200
137+
var control = new MarkupControl(new List<string> { content });
138+
var window = new Window(system)
139+
{
140+
Left = 10,
141+
Top = 5,
142+
Width = 200,
143+
Height = 10,
144+
Title = "Smart Test"
145+
};
146+
147+
window.AddControl(control);
148+
system.WindowStateService.AddWindow(window);
149+
system.Render.UpdateDisplay(); // Frame 1
150+
151+
// Act - Change all 6 characters (6 regions > threshold of 5)
152+
var newContent = "X" + new string(' ', 20) +
153+
"X" + new string(' ', 20) +
154+
"X" + new string(' ', 20) +
155+
"X" + new string(' ', 20) +
156+
"X" + new string(' ', 20) +
157+
"X" + new string(' ', 73);
158+
control.SetContent(new List<string> { newContent });
159+
window.Invalidate(true);
160+
system.Render.UpdateDisplay(); // Frame 2
161+
162+
// Assert - Should use LINE mode (too fragmented)
163+
var metrics = system.RenderingDiagnostics?.LastMetrics;
164+
Assert.NotNull(metrics);
165+
166+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
167+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
168+
169+
// 6 cells dirty (6 regions > 5 threshold), should use LINE mode
170+
Assert.Equal(6, metrics.DirtyCellsMarked);
171+
Assert.Equal(200, metrics.CellsActuallyRendered);
172+
}
173+
174+
[Fact]
175+
public void SmartMode_MediumFragmentation_UsesCellMode()
176+
{
177+
// Arrange - 3 separate regions (below fragmentation threshold of 5)
178+
var system = TestWindowSystemBuilder.CreateTestSystem();
179+
// Pattern: "AAAA____BBBB____CCCC" (3 regions, low coverage)
180+
var content = "AAAA" + new string(' ', 20) +
181+
"BBBB" + new string(' ', 20) +
182+
"CCCC" + new string(' ', 148); // Fill to 200
183+
var control = new MarkupControl(new List<string> { content });
184+
var window = new Window(system)
185+
{
186+
Left = 10,
187+
Top = 5,
188+
Width = 200,
189+
Height = 10,
190+
Title = "Smart Test"
191+
};
192+
193+
window.AddControl(control);
194+
system.WindowStateService.AddWindow(window);
195+
system.Render.UpdateDisplay(); // Frame 1
196+
197+
// Act - Change all 3 regions (12 cells total, 6% coverage, 3 regions)
198+
var newContent = "XXXX" + new string(' ', 20) +
199+
"YYYY" + new string(' ', 20) +
200+
"ZZZZ" + new string(' ', 148);
201+
control.SetContent(new List<string> { newContent });
202+
window.Invalidate(true);
203+
system.Render.UpdateDisplay(); // Frame 2
204+
205+
// Assert - Should use CELL mode (3 regions < 5, 6% coverage < 60%)
206+
var metrics = system.RenderingDiagnostics?.LastMetrics;
207+
Assert.NotNull(metrics);
208+
209+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
210+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
211+
212+
// 12 cells dirty, should render ~15-20 cells (CELL mode)
213+
Assert.Equal(12, metrics.DirtyCellsMarked);
214+
Assert.True(metrics.CellsActuallyRendered >= 12);
215+
Assert.True(metrics.CellsActuallyRendered < 50,
216+
$"Expected CELL mode (~12-20 cells), got {metrics.CellsActuallyRendered}");
217+
}
218+
219+
[Fact]
220+
public void SmartMode_ThresholdBoundary_60Percent()
221+
{
222+
// Test the exact 60% threshold boundary
223+
var system = TestWindowSystemBuilder.CreateTestSystem();
224+
225+
// 120 cells = exactly 60% of 200
226+
var content = new string('A', 120) + new string(' ', 80);
227+
var control = new MarkupControl(new List<string> { content });
228+
var window = new Window(system)
229+
{
230+
Left = 10,
231+
Top = 5,
232+
Width = 200,
233+
Height = 10,
234+
Title = "Smart Test"
235+
};
236+
237+
window.AddControl(control);
238+
system.WindowStateService.AddWindow(window);
239+
system.Render.UpdateDisplay(); // Frame 1
240+
241+
// Act - Change exactly 120 characters (60% coverage)
242+
var newContent = new string('B', 120) + new string(' ', 80);
243+
control.SetContent(new List<string> { newContent });
244+
window.Invalidate(true);
245+
system.Render.UpdateDisplay(); // Frame 2
246+
247+
// Assert - At exactly 60%, should still use CELL mode (threshold is ">60%", not ">=60%")
248+
var metrics = system.RenderingDiagnostics?.LastMetrics;
249+
Assert.NotNull(metrics);
250+
251+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
252+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
253+
_output.WriteLine($"Coverage: {120.0/200:P}");
254+
255+
Assert.Equal(120, metrics.DirtyCellsMarked);
256+
// At exactly 60%, uses CELL mode (condition is >0.6, not >=0.6)
257+
Assert.True(metrics.CellsActuallyRendered < 200);
258+
}
259+
260+
[Fact]
261+
public void SmartMode_FullLineChange_UsesLineMode()
262+
{
263+
// Arrange - Full line change (content area may be less than window width due to borders)
264+
var system = TestWindowSystemBuilder.CreateTestSystem();
265+
var content = new string('A', 200);
266+
var control = new MarkupControl(new List<string> { content });
267+
var window = new Window(system)
268+
{
269+
Left = 10,
270+
Top = 5,
271+
Width = 200,
272+
Height = 10,
273+
Title = "Smart Test"
274+
};
275+
276+
window.AddControl(control);
277+
system.WindowStateService.AddWindow(window);
278+
system.Render.UpdateDisplay(); // Frame 1
279+
280+
// Act - Change entire line
281+
var newContent = new string('B', 200);
282+
control.SetContent(new List<string> { newContent });
283+
window.Invalidate(true);
284+
system.Render.UpdateDisplay(); // Frame 2
285+
286+
// Assert - Full line dirty → should use LINE mode
287+
var metrics = system.RenderingDiagnostics?.LastMetrics;
288+
Assert.NotNull(metrics);
289+
290+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
291+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
292+
293+
// All cells in content area dirty, should use LINE mode
294+
// (actual count may be less than 200 due to borders, but should be high)
295+
Assert.True(metrics.DirtyCellsMarked >= 180,
296+
$"Expected nearly full line dirty, got {metrics.DirtyCellsMarked}");
297+
Assert.True(metrics.CellsActuallyRendered >= 180,
298+
$"Expected LINE mode (full line render), got {metrics.CellsActuallyRendered}");
299+
}
300+
301+
[Fact]
302+
public void SmartMode_MixedLinesInSameFrame()
303+
{
304+
// Most important test: different lines use different strategies in the same frame
305+
var system = TestWindowSystemBuilder.CreateTestSystem();
306+
var control = new MarkupControl(new List<string>
307+
{
308+
"AAAA" + new string(' ', 196), // Line 0: 4 cells (will change to sparse)
309+
new string('B', 150) + new string(' ', 50), // Line 1: 150 cells (will change to high coverage)
310+
"CCCC" + new string(' ', 196) // Line 2: 4 cells (will stay sparse)
311+
});
312+
var window = new Window(system)
313+
{
314+
Left = 10,
315+
Top = 5,
316+
Width = 200,
317+
Height = 10,
318+
Title = "Smart Test"
319+
};
320+
321+
window.AddControl(control);
322+
system.WindowStateService.AddWindow(window);
323+
system.Render.UpdateDisplay(); // Frame 1
324+
325+
// Act - Different change patterns
326+
control.SetContent(new List<string>
327+
{
328+
"XXXX" + new string(' ', 196), // Line 0: 4 cells changed → CELL mode
329+
new string('Y', 150) + new string(' ', 50), // Line 1: 150 cells changed (75%) → LINE mode
330+
"ZZZZ" + new string(' ', 196) // Line 2: 4 cells changed → CELL mode
331+
});
332+
window.Invalidate(true);
333+
system.Render.UpdateDisplay(); // Frame 2
334+
335+
// Assert
336+
var metrics = system.RenderingDiagnostics?.LastMetrics;
337+
Assert.NotNull(metrics);
338+
339+
_output.WriteLine($"Dirty cells: {metrics.DirtyCellsMarked}");
340+
_output.WriteLine($"Cells rendered: {metrics.CellsActuallyRendered}");
341+
342+
// Total dirty: 4 + 150 + 4 = 158 cells
343+
Assert.Equal(158, metrics.DirtyCellsMarked);
344+
345+
// Expected output:
346+
// Line 0: ~4-10 cells (CELL mode)
347+
// Line 1: 200 cells (LINE mode due to 75% coverage)
348+
// Line 2: ~4-10 cells (CELL mode)
349+
// Total: ~208-220 cells
350+
Assert.True(metrics.CellsActuallyRendered >= 200,
351+
$"Should include LINE render (200 cells), got {metrics.CellsActuallyRendered}");
352+
Assert.True(metrics.CellsActuallyRendered < 250,
353+
$"Should use CELL for sparse lines, got {metrics.CellsActuallyRendered}");
354+
}
355+
}

0 commit comments

Comments
 (0)