Skip to content

Commit 96da821

Browse files
authored
Merge pull request #250 from tonyhallett/fix-memory-leak
fix #248
2 parents ca33352 + bb62727 commit 96da821

File tree

5 files changed

+67
-73
lines changed

5 files changed

+67
-73
lines changed

FineCodeCoverageTests/FCCEngine_Tests.cs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ public void SetUp()
3030
updatedMarginTags = false;
3131
mocker = new AutoMoqer();
3232
fccEngine = mocker.Create<FCCEngine>();
33-
fccEngine.UpdateMarginTags += (UpdateMarginTagsEventArgs e) =>
34-
{
35-
updatedMarginTags = true;
36-
};
37-
3833
}
3934

4035
[Test]
@@ -70,29 +65,20 @@ public void Should_Set_AppDataFolderPath_From_Initialized_AppDataFolder_Director
7065
fccEngine.Initialize(null, CancellationToken.None);
7166
Assert.AreEqual("some path", fccEngine.AppDataFolderPath);
7267
}
73-
68+
7469
[Test]
75-
public void Should_UpdateMarginTags_And_Set_Null_CoverageLines_When_ClearUI()
70+
public void Should_Send_NewCoverageLinesMessage_With_Null_CoverageLines_When_ClearUI()
7671
{
77-
fccEngine.CoverageLines = new List<CoverageLine>();
7872
fccEngine.ClearUI();
79-
Assert.IsTrue(updatedMarginTags);
80-
Assert.IsNull(fccEngine.CoverageLines);
73+
mocker.Verify<IEventAggregator>(ea => ea.SendMessage(It.Is<NewCoverageLinesMessage>(msg => msg.CoverageLines == null), null));
8174
}
8275

83-
[Test]
84-
public void Should_Begin_With_Null_CoverageLines()
85-
{
86-
Assert.IsNull(fccEngine.CoverageLines);
87-
}
8876
}
8977

9078
public class FCCEngine_ReloadCoverage_Tests
9179
{
9280
private AutoMoqer mocker;
9381
private FCCEngine fccEngine;
94-
private List<UpdateMarginTagsEventArgs> updateMarginTagsEvents;
95-
private List<List<CoverageLine>> updateMarginTagsCoverageLines;
9682

9783
[SetUp]
9884
public void SetUp()
@@ -102,15 +88,6 @@ public void SetUp()
10288
mockDisposeAwareTaskRunner.Setup(runner => runner.RunAsync(It.IsAny<Func<Task>>())).Callback<Func<Task>>(async taskProvider => await taskProvider());
10389
fccEngine = mocker.Create<FCCEngine>();
10490

105-
updateMarginTagsEvents = new List<UpdateMarginTagsEventArgs>();
106-
updateMarginTagsCoverageLines = new List<List<CoverageLine>>();
107-
108-
fccEngine.UpdateMarginTags += (UpdateMarginTagsEventArgs e) =>
109-
{
110-
updateMarginTagsEvents.Add(e);
111-
updateMarginTagsCoverageLines.Add(fccEngine.CoverageLines);
112-
};
113-
11491
var mockedAppOptions = mocker.GetMock<IAppOptions>();
11592
mockedAppOptions.Setup(x => x.RunMsCodeCoverage).Returns(RunMsCodeCoverage.No);
11693
var mockAppOptionsProvider = mocker.GetMock<IAppOptionsProvider>();
@@ -356,11 +333,6 @@ private void VerifyLogsReloadCoverageStatus(ReloadCoverageStatus reloadCoverageS
356333
mocker.Verify<ILogger>(l => l.Log(fccEngine.GetLogReloadCoverageStatusMessage(reloadCoverageStatus)));
357334
}
358335

359-
private void VerifyClearUIEvents(int eventNumber)
360-
{
361-
Assert.Null(updateMarginTagsCoverageLines[eventNumber]);
362-
}
363-
364336
private async Task<(string reportGeneratedHtmlContent, List<CoverageLine> updatedCoverageLines)> RunToCompletion(bool noCoverageProjects)
365337
{
366338
var coverageProject = CreateSuitableProject().Object;

SharedProject/Core/FCCEngine.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ namespace FineCodeCoverage.Engine
1717
{
1818
internal enum ReloadCoverageStatus { Start, Done, Cancelled, Error, Initializing };
1919

20+
internal class NewCoverageLinesMessage
21+
{
22+
public List<CoverageLine> CoverageLines { get; set; }
23+
}
24+
2025
internal class DisplayCoverageResultState
2126
{
2227
public CancellationTokenSource CancellationTokenSource { get; set; }
@@ -30,10 +35,7 @@ internal class FCCEngine : IFCCEngine,IDisposable
3035
internal const string initializationFailedMessagePrefix = "Initialization failed. Please check the following error which may be resolved by reopening visual studio which will start the initialization process again.";
3136
private CancellationTokenSource cancellationTokenSource;
3237

33-
public event UpdateMarginTagsDelegate UpdateMarginTags;
34-
3538
public string AppDataFolderPath { get; private set; }
36-
public List<CoverageLine> CoverageLines { get; internal set; }
3739
private bool IsVsShutdown => disposeAwareTaskRunner.DisposalToken.IsCancellationRequested;
3840

3941
private readonly ICoverageUtilManager coverageUtilManager;
@@ -115,8 +117,7 @@ public void Initialize(IInitializeStatusProvider initializeStatusProvider, Cance
115117

116118
public void ClearUI()
117119
{
118-
CoverageLines = null;
119-
UpdateMarginTags?.Invoke(new UpdateMarginTagsEventArgs());
120+
ClearCoverageLines();
120121
ClearOutputWindow(true);
121122
}
122123

@@ -139,8 +140,7 @@ public void StopCoverage()
139140

140141
private CancellationTokenSource Reset()
141142
{
142-
CoverageLines = null;
143-
UpdateMarginTags?.Invoke(new UpdateMarginTagsEventArgs());
143+
ClearCoverageLines();
144144

145145
StopCoverage();
146146

@@ -197,10 +197,19 @@ private void RaiseUpdateOutputWindow(string reportHtml)
197197
eventAggregator.SendMessage(new NewReportMessage { Report = reportHtml });
198198
}
199199

200+
private void ClearCoverageLines()
201+
{
202+
RaiseCoverageLines(null);
203+
}
204+
205+
private void RaiseCoverageLines(List<CoverageLine> coverageLines)
206+
{
207+
eventAggregator.SendMessage(new NewCoverageLinesMessage { CoverageLines = coverageLines});
208+
}
209+
200210
private void UpdateUI(List<CoverageLine> coverageLines, string reportHtml)
201211
{
202-
CoverageLines = coverageLines;
203-
UpdateMarginTags?.Invoke(new UpdateMarginTagsEventArgs());
212+
RaiseCoverageLines(coverageLines);
204213
if (reportHtml == null)
205214
{
206215
reportHtml = reportGeneratorUtil.BlankReport(true);

SharedProject/Core/IFCCEngine.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Threading.Tasks;
43
using FineCodeCoverage.Engine.Model;
54
using FineCodeCoverage.Impl;
65

76
namespace FineCodeCoverage.Engine
87
{
98
internal interface IFCCEngine
109
{
11-
event UpdateMarginTagsDelegate UpdateMarginTags;
1210
string AppDataFolderPath { get; }
1311
void Initialize(IInitializeStatusProvider initializeStatusProvider, System.Threading.CancellationToken cancellationToken);
1412
void StopCoverage();
1513
void ReloadCoverage(Func<System.Threading.Tasks.Task<List<ICoverageProject>>> coverageRequestCallback);
1614
void RunAndProcessReport(string[] coberturaFiles,Action cleanUp = null);
1715
void ClearUI();
18-
List<CoverageLine> CoverageLines { get; }
1916
}
2017

2118
}

SharedProject/Impl/CoverageLineGlyphTagger.cs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,28 @@
55
using Microsoft.VisualStudio.Text.Tagging;
66
using FineCodeCoverage.Engine.Model;
77
using System.Linq;
8+
using FineCodeCoverage.Core.Utilities;
89

910
namespace FineCodeCoverage.Impl
1011
{
11-
internal class CoverageLineGlyphTagger : ITagger<CoverageLineGlyphTag>
12+
internal class CoverageLineGlyphTagger : ITagger<CoverageLineGlyphTag>, IListener<NewCoverageLinesMessage>
1213
{
1314
private readonly ITextBuffer _textBuffer;
14-
private readonly IFCCEngine fccEngine;
15+
private List<CoverageLine> coverageLines;
1516

1617
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
1718

18-
public CoverageLineGlyphTagger(ITextBuffer textBuffer, IFCCEngine fccEngine)
19+
public CoverageLineGlyphTagger(ITextBuffer textBuffer, List<CoverageLine> lastCoverageLines)
1920
{
2021
_textBuffer = textBuffer;
21-
this.fccEngine = fccEngine;
22-
fccEngine.UpdateMarginTags += FCCEngine_UpdateMarginTags;
22+
coverageLines = lastCoverageLines;
23+
if (lastCoverageLines != null)
24+
{
25+
RaiseTagsChanged();
26+
}
2327
}
2428

25-
private void FCCEngine_UpdateMarginTags(UpdateMarginTagsEventArgs e)
29+
private void RaiseTagsChanged()
2630
{
2731
var span = new SnapshotSpan(_textBuffer.CurrentSnapshot, 0, _textBuffer.CurrentSnapshot.Length);
2832
var spanEventArgs = new SnapshotSpanEventArgs(span);
@@ -33,7 +37,7 @@ IEnumerable<ITagSpan<CoverageLineGlyphTag>> ITagger<CoverageLineGlyphTag>.GetTag
3337
{
3438
var result = new List<ITagSpan<CoverageLineGlyphTag>>();
3539

36-
if (spans == null || fccEngine.CoverageLines == null)
40+
if (spans == null || coverageLines == null)
3741
{
3842
return result;
3943
}
@@ -47,11 +51,11 @@ IEnumerable<ITagSpan<CoverageLineGlyphTag>> ITagger<CoverageLineGlyphTag>.GetTag
4751

4852
var startLineNumber = span.Start.GetContainingLine().LineNumber + 1;
4953
var endLineNumber = span.End.GetContainingLine().LineNumber + 1;
50-
var coverageLines = GetLines(document.FilePath, startLineNumber, endLineNumber);
54+
var applicableCoverageLines = GetApplicableLines(document.FilePath, startLineNumber, endLineNumber);
5155

52-
foreach (var coverageLine in coverageLines)
56+
foreach (var applicableCoverageLine in applicableCoverageLines)
5357
{
54-
var tag = new CoverageLineGlyphTag(coverageLine);
58+
var tag = new CoverageLineGlyphTag(applicableCoverageLine);
5559
var tagSpan = new TagSpan<CoverageLineGlyphTag>(span, tag);
5660
result.Add(tagSpan);
5761
}
@@ -60,13 +64,19 @@ IEnumerable<ITagSpan<CoverageLineGlyphTag>> ITagger<CoverageLineGlyphTag>.GetTag
6064
return result;
6165
}
6266

63-
private IEnumerable<CoverageLine> GetLines(string filePath, int startLineNumber, int endLineNumber)
67+
private IEnumerable<CoverageLine> GetApplicableLines(string filePath, int startLineNumber, int endLineNumber)
6468
{
65-
return fccEngine.CoverageLines
69+
return coverageLines
6670
.AsParallel()
6771
.Where(x => x.Class.Filename.Equals(filePath, StringComparison.OrdinalIgnoreCase))
6872
.Where(x => x.Line.Number >= startLineNumber && x.Line.Number <= endLineNumber)
6973
.ToArray();
7074
}
71-
}
75+
76+
public void Handle(NewCoverageLinesMessage message)
77+
{
78+
coverageLines = message.CoverageLines;
79+
RaiseTagsChanged();
80+
}
81+
}
7282
}

SharedProject/Impl/CoverageLineGlyphTaggerProvider.cs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,48 @@
44
using Microsoft.VisualStudio.Text.Tagging;
55
using FineCodeCoverage.Engine;
66
using Microsoft.VisualStudio.Shell;
7+
using FineCodeCoverage.Core.Utilities;
8+
using System.Collections.Generic;
9+
using FineCodeCoverage.Engine.Model;
710

811
namespace FineCodeCoverage.Impl
912
{
1013
[ContentType("code")]
1114
[TagType(typeof(CoverageLineGlyphTag))]
1215
[Name(Vsix.TaggerProviderName)]
1316
[Export(typeof(ITaggerProvider))]
14-
internal class CoverageLineGlyphTaggerProvider : ITaggerProvider
17+
internal class CoverageLineGlyphTaggerProvider : ITaggerProvider, IListener<NewCoverageLinesMessage>
1518
{
16-
private readonly IFCCEngine fccEngine;
19+
private readonly IEventAggregator eventAggregator;
1720
private readonly ICoverageColoursProvider coverageColoursProvider;
21+
private List<CoverageLine> lastCoverageLines;
1822

1923
[ImportingConstructor]
2024
public CoverageLineGlyphTaggerProvider(
21-
IFCCEngine fccEngine,
25+
IEventAggregator eventAggregator,
2226
ICoverageColoursProvider coverageColoursProvider)
2327
{
24-
this.fccEngine = fccEngine;
25-
fccEngine.UpdateMarginTags += FccEngine_UpdateMarginTags;
28+
eventAggregator.AddListener(this);
29+
this.eventAggregator = eventAggregator;
2630
this.coverageColoursProvider = coverageColoursProvider;
2731
}
2832

29-
private void FccEngine_UpdateMarginTags(UpdateMarginTagsEventArgs e)
33+
public ITagger<T> CreateTagger<T>(ITextBuffer textBuffer) where T : ITag
34+
{
35+
var coverageLineGlyphTagger = new CoverageLineGlyphTagger(textBuffer, lastCoverageLines);
36+
eventAggregator.AddListener(coverageLineGlyphTagger, false);
37+
return coverageLineGlyphTagger as ITagger<T>;
38+
}
39+
40+
public void Handle(NewCoverageLinesMessage message)
3041
{
42+
lastCoverageLines = message.CoverageLines;
3143
#pragma warning disable VSTHRD102 // Implement internal logic asynchronously
32-
ThreadHelper.JoinableTaskFactory.Run(async () =>
44+
ThreadHelper.JoinableTaskFactory.Run(async () =>
3345
#pragma warning restore VSTHRD102 // Implement internal logic asynchronously
34-
{
35-
await coverageColoursProvider.PrepareAsync();
36-
});
37-
46+
{
47+
await coverageColoursProvider.PrepareAsync();
48+
});
3849
}
39-
40-
public ITagger<T> CreateTagger<T>(ITextBuffer textBuffer) where T : ITag
41-
{
42-
return new CoverageLineGlyphTagger(textBuffer, fccEngine) as ITagger<T>;
43-
}
44-
}
50+
}
4551
}

0 commit comments

Comments
 (0)