Skip to content

Commit 5dc1c3d

Browse files
author
BRUNER Patrick
committed
regression tests
1 parent 2e454d8 commit 5dc1c3d

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using ColumnizerLib;
2+
3+
using LogExpert.Core.Callback;
4+
using LogExpert.Core.Interfaces;
5+
using LogExpert.UI.Controls.LogWindow;
6+
7+
using Moq;
8+
9+
using NUnit.Framework;
10+
11+
namespace LogExpert.Tests.Controls;
12+
13+
[TestFixture]
14+
public class ColumnCacheTests
15+
{
16+
/// <summary>
17+
/// Regression test for a cache-poisoning bug in <see cref="ColumnCache.GetColumnsForLine"/>:
18+
/// when the underlying <see cref="ILogfileReader.GetLogLineMemoryWithWait"/> returned null
19+
/// (e.g. fast-fail timeout) for a given line, the cache stored that null and
20+
/// permanently returned null for every subsequent request of the same line number.
21+
/// This manifested in the GUI as a blank data row in a CSV file containing a header
22+
/// plus exactly one data line (header was dropped by CsvColumnizer's PreProcessLine,
23+
/// leaving a single grid row that was repeatedly requested for paint).
24+
/// The fix adds <c>_cachedColumns == null</c> to the re-fetch condition so a null
25+
/// result is never cached.
26+
/// </summary>
27+
[Test]
28+
public void GetColumnsForLine_NullThenValidLine_ReturnsColumnsOnSecondCall ()
29+
{
30+
const int lineNumber = 0;
31+
32+
var validLine = new Mock<ILogLineMemory>().Object;
33+
var splitResult = new Mock<IColumnizedLogLineMemory>().Object;
34+
35+
var readerMock = new Mock<ILogfileReader>();
36+
readerMock
37+
.SetupSequence(r => r.GetLogLineMemoryWithWait(lineNumber))
38+
.Returns(Task.FromResult<ILogLineMemory>(null))
39+
.Returns(Task.FromResult(validLine));
40+
41+
var columnizerMock = new Mock<ILogLineMemoryColumnizer>();
42+
columnizerMock
43+
.Setup(c => c.SplitLine(It.IsAny<ILogLineMemoryColumnizerCallback>(), validLine))
44+
.Returns(splitResult);
45+
46+
var logWindowMock = new Mock<ILogWindow>();
47+
var callback = new ColumnizerCallback(logWindowMock.Object);
48+
49+
var cache = new ColumnCache();
50+
51+
// First call: reader returns null -> cache must NOT store this null.
52+
var firstResult = cache.GetColumnsForLine(readerMock.Object, lineNumber, columnizerMock.Object, callback);
53+
Assert.That(firstResult, Is.Null, "First call should return null because the reader returned null.");
54+
55+
// Second call for the SAME line: reader now returns a valid line.
56+
// Before the fix this would return the cached null and never call SplitLine.
57+
var secondResult = cache.GetColumnsForLine(readerMock.Object, lineNumber, columnizerMock.Object, callback);
58+
59+
Assert.That(secondResult, Is.SameAs(splitResult), "Second call must re-fetch and return the freshly split columns instead of a cached null.");
60+
readerMock.Verify(r => r.GetLogLineMemoryWithWait(lineNumber), Times.Exactly(2));
61+
columnizerMock.Verify(c => c.SplitLine(It.IsAny<ILogLineMemoryColumnizerCallback>(), validLine), Times.Once);
62+
}
63+
64+
/// <summary>
65+
/// Sanity check that a valid result IS cached: requesting the same line twice
66+
/// with a successful first fetch must not call the reader/columnizer a second time.
67+
/// </summary>
68+
[Test]
69+
public void GetColumnsForLine_SameLineTwice_UsesCachedValue ()
70+
{
71+
const int lineNumber = 0;
72+
73+
var validLine = new Mock<ILogLineMemory>().Object;
74+
var splitResult = new Mock<IColumnizedLogLineMemory>().Object;
75+
76+
var readerMock = new Mock<ILogfileReader>();
77+
readerMock
78+
.Setup(r => r.GetLogLineMemoryWithWait(lineNumber))
79+
.Returns(Task.FromResult(validLine));
80+
81+
var columnizerMock = new Mock<ILogLineMemoryColumnizer>();
82+
columnizerMock
83+
.Setup(c => c.SplitLine(It.IsAny<ILogLineMemoryColumnizerCallback>(), validLine))
84+
.Returns(splitResult);
85+
86+
var logWindowMock = new Mock<ILogWindow>();
87+
var callback = new ColumnizerCallback(logWindowMock.Object);
88+
89+
var cache = new ColumnCache();
90+
91+
var firstResult = cache.GetColumnsForLine(readerMock.Object, lineNumber, columnizerMock.Object, callback);
92+
// callback.LineNum is now equal to lineNumber (set by GetColumnsForLine), so the
93+
// second call should hit the cache.
94+
var secondResult = cache.GetColumnsForLine(readerMock.Object, lineNumber, columnizerMock.Object, callback);
95+
96+
Assert.That(firstResult, Is.SameAs(splitResult));
97+
Assert.That(secondResult, Is.SameAs(splitResult));
98+
readerMock.Verify(r => r.GetLogLineMemoryWithWait(lineNumber), Times.Once);
99+
columnizerMock.Verify(c => c.SplitLine(It.IsAny<ILogLineMemoryColumnizerCallback>(), validLine), Times.Once);
100+
}
101+
}

0 commit comments

Comments
 (0)