Skip to content

Commit 01bfa96

Browse files
committed
Add unit tests for FastMouseProcessor
1 parent 7e3e66e commit 01bfa96

File tree

7 files changed

+153
-21
lines changed

7 files changed

+153
-21
lines changed

Tvl.VisualStudio.MouseFastScroll.UnitTests/CompositionHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ public static IMouseProcessorProvider GetProvider(out ExportProvider exportProvi
2525
return provider;
2626
}
2727

28-
public static IMouseProcessor GetProcessor(out ExportProvider exportProvider, out IWpfTextView wpfTextView)
28+
public static IMouseProcessor GetProcessor(string content, out ExportProvider exportProvider, out FakeWpfTextView wpfTextView)
2929
{
3030
var provider = GetProvider(out exportProvider);
31-
wpfTextView = new FakeWpfTextView(exportProvider, new FakeTextSnapshot(string.Empty));
31+
wpfTextView = new FakeWpfTextView(exportProvider, new FakeTextSnapshot(content));
3232
var processor = provider.GetAssociatedProcessor(wpfTextView);
3333
Assert.NotNull(processor);
3434

Tvl.VisualStudio.MouseFastScroll.UnitTests/Fakes/FakeTextSnapshot.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ public FakeTextSnapshot(string content)
2525
{
2626
int start = lines.Count == 0 ? 0 : lines.Last().EndIncludingLineBreak;
2727
int endIncludingLineBreak = i + 1;
28-
lines.Add(new FakeTextSnapshotLine(lines.Count, new SnapshotSpan(this, start, endIncludingLineBreak)));
28+
lines.Add(new FakeTextSnapshotLine(lines.Count, new SnapshotSpan(this, Span.FromBounds(start, endIncludingLineBreak))));
2929
}
3030

3131
int lastLineStart = lines.Count == 0 ? 0 : lines.Last().EndIncludingLineBreak;
32-
lines.Add(new FakeTextSnapshotLine(lines.Count, new SnapshotSpan(this, lastLineStart, content.Length)));
32+
lines.Add(new FakeTextSnapshotLine(lines.Count, new SnapshotSpan(this, Span.FromBounds(lastLineStart, content.Length))));
3333

3434
_lines = lines.AsReadOnly();
3535
}

Tvl.VisualStudio.MouseFastScroll.UnitTests/Fakes/FakeViewScroller.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ namespace Tvl.VisualStudio.MouseFastScroll.UnitTests.Fakes
99

1010
internal class FakeViewScroller : IViewScroller
1111
{
12+
private readonly FakeWpfTextView _wpfTextView;
13+
14+
public FakeViewScroller(FakeWpfTextView wpfTextView)
15+
{
16+
_wpfTextView = wpfTextView;
17+
}
18+
1219
public void EnsureSpanVisible(SnapshotSpan span)
1320
{
1421
throw new NotImplementedException();
@@ -31,17 +38,22 @@ public void ScrollViewportHorizontallyByPixels(double distanceToScroll)
3138

3239
public void ScrollViewportVerticallyByLine(ScrollDirection direction)
3340
{
34-
throw new NotImplementedException();
41+
ScrollViewportVerticallyByLines(direction, 1);
3542
}
3643

3744
public void ScrollViewportVerticallyByLines(ScrollDirection direction, int count)
3845
{
39-
throw new NotImplementedException();
46+
_wpfTextView.TextViewLines.Scroll(direction, count);
4047
}
4148

4249
public bool ScrollViewportVerticallyByPage(ScrollDirection direction)
4350
{
44-
throw new NotImplementedException();
51+
int lastVisibleLine = _wpfTextView.TextViewLines.LastVisibleLine.Start.GetContainingLine().LineNumber;
52+
int firstVisibleLine = _wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber;
53+
int pageSize = lastVisibleLine - firstVisibleLine;
54+
55+
_wpfTextView.TextViewLines.Scroll(direction, pageSize);
56+
return true;
4557
}
4658

4759
public void ScrollViewportVerticallyByPixels(double distanceToScroll)

Tvl.VisualStudio.MouseFastScroll.UnitTests/Fakes/FakeWpfTextView.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ namespace Tvl.VisualStudio.MouseFastScroll.UnitTests.Fakes
1515

1616
internal class FakeWpfTextView : IWpfTextView
1717
{
18-
private readonly IEditorOptions _editorOptions;
19-
private readonly IWpfTextViewLineCollection _textViewLines;
20-
private readonly IViewScroller _viewScroller = new FakeViewScroller();
18+
private readonly FakeEditorOptions _editorOptions;
19+
private readonly FakeWpfTextViewLineCollection _textViewLines;
20+
private readonly FakeViewScroller _viewScroller;
2121

2222
public FakeWpfTextView(ExportProvider exportProvider, ITextSnapshot snapshot)
2323
{
2424
_editorOptions = new FakeEditorOptions(exportProvider, this);
2525
_textViewLines = new FakeWpfTextViewLineCollection(snapshot);
26+
_viewScroller = new FakeViewScroller(this);
2627
}
2728

2829
public event EventHandler<BackgroundBrushChangedEventArgs> BackgroundBrushChanged
@@ -93,7 +94,9 @@ public Brush Background
9394
set => throw new NotImplementedException();
9495
}
9596

96-
public IWpfTextViewLineCollection TextViewLines => _textViewLines;
97+
public FakeWpfTextViewLineCollection TextViewLines => _textViewLines;
98+
99+
IWpfTextViewLineCollection IWpfTextView.TextViewLines => TextViewLines;
97100

98101
public IFormattedLineSource FormattedLineSource => throw new NotImplementedException();
99102

Tvl.VisualStudio.MouseFastScroll.UnitTests/Fakes/FakeWpfTextViewLineCollection.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,34 @@ namespace Tvl.VisualStudio.MouseFastScroll.UnitTests.Fakes
1313
using Microsoft.VisualStudio.Text;
1414
using Microsoft.VisualStudio.Text.Editor;
1515
using Microsoft.VisualStudio.Text.Formatting;
16+
using Xunit;
1617

1718
internal class FakeWpfTextViewLineCollection : IWpfTextViewLineCollection
1819
{
1920
private readonly ITextSnapshot _snapshot;
20-
private readonly IWpfTextViewLine[] _lines;
21+
private readonly ReadOnlyCollection<IWpfTextViewLine> _lines;
2122

23+
private int _viewableLines = 20;
2224
private int _firstVisibleLine = 0;
2325

2426
public FakeWpfTextViewLineCollection(ITextSnapshot snapshot)
2527
{
2628
_snapshot = snapshot;
27-
_lines = _snapshot.Lines.Select(line => new FakeWpfTextViewLine(line)).ToArray();
29+
_lines = _snapshot.Lines.Select<ITextSnapshotLine, IWpfTextViewLine>(line => new FakeWpfTextViewLine(line)).ToList().AsReadOnly();
2830
}
2931

30-
public ReadOnlyCollection<IWpfTextViewLine> WpfTextViewLines => throw new NotImplementedException();
32+
public ReadOnlyCollection<IWpfTextViewLine> WpfTextViewLines => _lines;
3133

3234
public IWpfTextViewLine FirstVisibleLine => this[_firstVisibleLine];
3335

34-
public IWpfTextViewLine LastVisibleLine => throw new NotImplementedException();
36+
public IWpfTextViewLine LastVisibleLine
37+
{
38+
get
39+
{
40+
int lastVisibleLine = Math.Min(_lines.Count - 1, _firstVisibleLine + _viewableLines - 1);
41+
return this[lastVisibleLine];
42+
}
43+
}
3544

3645
public SnapshotSpan FormattedSpan => throw new NotImplementedException();
3746

@@ -49,8 +58,28 @@ public FakeWpfTextViewLineCollection(ITextSnapshot snapshot)
4958

5059
ITextViewLine IList<ITextViewLine>.this[int index]
5160
{
52-
get => throw new NotImplementedException();
53-
set => throw new NotImplementedException();
61+
get => this[index];
62+
set => throw new NotSupportedException();
63+
}
64+
65+
internal void Scroll(ScrollDirection direction, int lines)
66+
{
67+
Assert.True(lines >= 0, "Assertion failed: lines >= 0");
68+
69+
if (direction == ScrollDirection.Down)
70+
{
71+
lines = Math.Min(lines, _lines.Count - _firstVisibleLine - 1);
72+
if (lines > 0)
73+
{
74+
_firstVisibleLine += lines;
75+
}
76+
}
77+
else
78+
{
79+
Assert.Equal(ScrollDirection.Up, direction);
80+
int newTopLine = Math.Max(0, _firstVisibleLine - lines);
81+
_firstVisibleLine = newTopLine;
82+
}
5483
}
5584

5685
public void Add(ITextViewLine item)
@@ -85,7 +114,7 @@ public TextBounds GetCharacterBounds(SnapshotPoint bufferPosition)
85114

86115
public IEnumerator<ITextViewLine> GetEnumerator()
87116
{
88-
throw new NotImplementedException();
117+
return WpfTextViewLines.GetEnumerator();
89118
}
90119

91120
public int GetIndexOfTextLine(ITextViewLine textLine)
@@ -175,12 +204,12 @@ public void RemoveAt(int index)
175204

176205
IEnumerator IEnumerable.GetEnumerator()
177206
{
178-
throw new NotImplementedException();
207+
return GetEnumerator();
179208
}
180209

181210
ITextViewLine ITextViewLineCollection.GetTextViewLineContainingBufferPosition(SnapshotPoint bufferPosition)
182211
{
183-
throw new NotImplementedException();
212+
return GetTextViewLineContainingBufferPosition(bufferPosition);
184213
}
185214
}
186215
}

Tvl.VisualStudio.MouseFastScroll.UnitTests/FastScrollProcessorTests.cs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,110 @@
44
namespace Tvl.VisualStudio.MouseFastScroll.UnitTests
55
{
66
using System;
7+
using System.Linq;
8+
using System.Windows;
79
using System.Windows.Input;
10+
using Microsoft.VisualStudio.Text.Editor;
11+
using WindowsInput;
12+
using WindowsInput.Native;
813
using Xunit;
914

1015
public class FastScrollProcessorTests
1116
{
1217
[StaFact]
1318
public void ScrollWithoutControlPressed()
1419
{
15-
var processor = CompositionHelper.GetProcessor(out var exportProvider, out var wpfTextView);
20+
string content = string.Join("\r\n", Enumerable.Range(0, 200).Select(i => Guid.NewGuid().ToString()));
21+
var processor = CompositionHelper.GetProcessor(content, out var exportProvider, out var wpfTextView);
1622
var args = new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, 120);
1723

1824
Assert.Equal(ModifierKeys.None, Keyboard.Modifiers & ModifierKeys.Control);
1925
Assert.False(args.Handled);
2026
var firstVisibleLine = wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber;
2127
processor.PreprocessMouseWheel(args);
2228
Assert.False(args.Handled);
29+
30+
// The line number stays the same because the editor's default mouse handler is not part of these tests
2331
Assert.Equal(firstVisibleLine, wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber);
2432
}
33+
34+
[StaFact]
35+
public void ScrollDownWithControlPressed()
36+
{
37+
string content = string.Join("\r\n", Enumerable.Range(0, 200).Select(i => Guid.NewGuid().ToString()));
38+
var processor = CompositionHelper.GetProcessor(content, out var exportProvider, out var wpfTextView);
39+
var args = new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, -120) { RoutedEvent = UIElement.MouseWheelEvent };
40+
41+
var inputSimulator = new InputSimulator();
42+
inputSimulator.Keyboard.KeyDown(VirtualKeyCode.CONTROL);
43+
try
44+
{
45+
Assert.Equal(ModifierKeys.Control, Keyboard.Modifiers & ModifierKeys.Control);
46+
Assert.False(args.Handled);
47+
var lastVisibleLine = wpfTextView.TextViewLines.LastVisibleLine.Start.GetContainingLine().LineNumber;
48+
processor.PreprocessMouseWheel(args);
49+
Assert.True(args.Handled);
50+
51+
// For a Scroll Page Down operation, the previous LastVisibleLine becomes the new FirstVisibleLine
52+
Assert.Equal(lastVisibleLine, wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber);
53+
}
54+
finally
55+
{
56+
inputSimulator.Keyboard.KeyUp(VirtualKeyCode.CONTROL);
57+
}
58+
}
59+
60+
[StaFact]
61+
public void ScrollUpPartialPageWithControlPressed()
62+
{
63+
string content = string.Join("\r\n", Enumerable.Range(0, 200).Select(i => Guid.NewGuid().ToString()));
64+
var processor = CompositionHelper.GetProcessor(content, out var exportProvider, out var wpfTextView);
65+
var args = new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, 120) { RoutedEvent = UIElement.MouseWheelEvent };
66+
67+
var inputSimulator = new InputSimulator();
68+
inputSimulator.Keyboard.KeyDown(VirtualKeyCode.CONTROL);
69+
try
70+
{
71+
Assert.Equal(ModifierKeys.Control, Keyboard.Modifiers & ModifierKeys.Control);
72+
Assert.False(args.Handled);
73+
var lastVisibleLine = wpfTextView.TextViewLines.LastVisibleLine.Start.GetContainingLine().LineNumber;
74+
wpfTextView.TextViewLines.Scroll(ScrollDirection.Down, lastVisibleLine / 2);
75+
processor.PreprocessMouseWheel(args);
76+
Assert.True(args.Handled);
77+
78+
// For a Scroll Page Up operation, the movement stops if the top line is reached
79+
Assert.Equal(0, wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber);
80+
}
81+
finally
82+
{
83+
inputSimulator.Keyboard.KeyUp(VirtualKeyCode.CONTROL);
84+
}
85+
}
86+
87+
[StaFact]
88+
public void ScrollUpFullPageWithControlPressed()
89+
{
90+
string content = string.Join("\r\n", Enumerable.Range(0, 200).Select(i => Guid.NewGuid().ToString()));
91+
var processor = CompositionHelper.GetProcessor(content, out var exportProvider, out var wpfTextView);
92+
var args = new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, 120) { RoutedEvent = UIElement.MouseWheelEvent };
93+
94+
var inputSimulator = new InputSimulator();
95+
inputSimulator.Keyboard.KeyDown(VirtualKeyCode.CONTROL);
96+
try
97+
{
98+
Assert.Equal(ModifierKeys.Control, Keyboard.Modifiers & ModifierKeys.Control);
99+
Assert.False(args.Handled);
100+
var lastVisibleLine = wpfTextView.TextViewLines.LastVisibleLine.Start.GetContainingLine().LineNumber;
101+
wpfTextView.TextViewLines.Scroll(ScrollDirection.Down, lastVisibleLine + 4);
102+
processor.PreprocessMouseWheel(args);
103+
Assert.True(args.Handled);
104+
105+
Assert.Equal(4, wpfTextView.TextViewLines.FirstVisibleLine.Start.GetContainingLine().LineNumber);
106+
}
107+
finally
108+
{
109+
inputSimulator.Keyboard.KeyUp(VirtualKeyCode.CONTROL);
110+
}
111+
}
25112
}
26113
}

Tvl.VisualStudio.MouseFastScroll.UnitTests/Tvl.VisualStudio.MouseFastScroll.UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17+
<PackageReference Include="InputSimulatorPlus" Version="1.0.6" />
1718
<PackageReference Include="Xunit.StaFact" Version="0.2.9" />
1819
<PackageReference Include="xunit" Version="2.3.1" />
1920
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" PrivateAssets="all" />

0 commit comments

Comments
 (0)