Skip to content

Commit

Permalink
Merge pull request #297 from kmaddock/fast-load
Browse files Browse the repository at this point in the history
Improve performance with large solutions
  • Loading branch information
tonyhallett authored Oct 26, 2022
2 parents 8e42405 + 127ed8c commit 86dd4db
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 108 deletions.
11 changes: 7 additions & 4 deletions FineCodeCoverageTests/FCCEngine_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ private void VerifyLogsReloadCoverageStatus(ReloadCoverageStatus reloadCoverageS
mocker.Verify<ILogger>(l => l.Log(fccEngine.GetLogReloadCoverageStatusMessage(reloadCoverageStatus)));
}

private async Task<(string reportGeneratedHtmlContent, List<CoverageLine> updatedCoverageLines)> RunToCompletion(bool noCoverageProjects)
private async Task<(string reportGeneratedHtmlContent, FileLineCoverage updatedCoverageLines)> RunToCompletion(bool noCoverageProjects)
{
var coverageProject = CreateSuitableProject().Object;
var mockReportGenerator = mocker.GetMock<IReportGeneratorUtil>();
Expand All @@ -352,7 +352,9 @@ private void VerifyLogsReloadCoverageStatus(ReloadCoverageStatus reloadCoverageS

var reportGeneratedHtmlContent = "<somehtml/>";
mockReportGenerator.Setup(rg => rg.ProcessUnifiedHtml("Unified", It.IsAny<string>())).Returns(reportGeneratedHtmlContent);
List<CoverageLine> coverageLines = new List<CoverageLine>() { new CoverageLine() };
var coverageLines = new FileLineCoverage();
coverageLines.Add("test", new[] { new Line() });
coverageLines.Completed();
mocker.GetMock<ICoberturaUtil>().Setup(coberturaUtil => coberturaUtil.ProcessCoberturaXml(It.IsAny<string>())).Returns(coverageLines);
if (noCoverageProjects)
{
Expand Down Expand Up @@ -384,8 +386,9 @@ private async Task ThrowReadingReportHtml()
}
);


List<CoverageLine> coverageLines = new List<CoverageLine>() { new CoverageLine() };
var coverageLines = new FileLineCoverage();
coverageLines.Add("test", new[] { new Line() });
coverageLines.Completed();
mocker.GetMock<ICoberturaUtil>().Setup(coberturaUtil => coberturaUtil.ProcessCoberturaXml(It.IsAny<string>())).Returns(coverageLines);

await ReloadInitializedCoverage(passedProject.Object);
Expand Down
35 changes: 35 additions & 0 deletions FineCodeCoverageTests/FileLineCoverage_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FineCodeCoverage.Engine.Model;
using NUnit.Framework;

namespace FineCodeCoverageTests
{
internal class FileLineCoverage_Tests
{
[TestCaseSource(nameof(Cases))]
public void GetLines_Test(IEnumerable<int> lineNumbers, int startLineNumber, int endLineNumber, IEnumerable<int> expectedLineNumbers)
{
var fileLineCoverage = new FileLineCoverage();
fileLineCoverage.Add("fp", lineNumbers.Select(n => new FineCodeCoverage.Engine.Cobertura.Line { Number = n }));
fileLineCoverage.Completed();

var lines = fileLineCoverage.GetLines("fp", startLineNumber, endLineNumber);
Assert.That(lines.Select(l => l.Number), Is.EqualTo(expectedLineNumbers));
}

static object[] Cases =
{
new object[] { new int[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}, 19, 20, new int[]{ 19,20} },
new object[] { new int[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}, 12, 13, new int[]{ 12,13} },
new object[] { new int[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}, 6, 7, new int[]{ 6,7} },
new object[] {Enumerable.Empty<int>(), 0, 4,Enumerable.Empty<int>() },
new object[] { new int[] { 3,2,1}, 0, 4, new int[]{ 1,2,3} },
new object[] { new int[] { 3,2,1}, 0, 3, new int[]{ 1,2,3} },
new object[] { new int[] { 3,2,1}, 1, 2, new int[]{ 1,2} },
new object[] { new int[] { 3,2,1}, 2, 2, new int[]{ 2} },
new object[] { new int[] { 3,2,1}, 4, 5, Enumerable.Empty<int>() }
};
}
}
1 change: 1 addition & 0 deletions FineCodeCoverageTests/FineCodeCoverageTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<Compile Include="CoverageToolOutput_Tests\SolutionFolderProvider_Tests.cs" />
<Compile Include="AppOptionsProvider_Tests.cs" />
<Compile Include="CoverageUtilManager_Tests.cs" />
<Compile Include="FileLineCoverage_Tests.cs" />
<Compile Include="MsCodeCoverage\ShimCopier_Tests.cs" />
<Compile Include="MsCodeCoverage\TemplatedRunSettingsService_Tests.cs" />
<Compile Include="MsCodeCoverage\CustomRunSettingsTemplateProvider_Tests.cs" />
Expand Down
60 changes: 5 additions & 55 deletions SharedProject/Core/Cobertura/CoberturaUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using FineCodeCoverage.Engine.Model;
using System.ComponentModel.Composition;
using System.IO;

namespace FineCodeCoverage.Engine.Cobertura
{
Expand All @@ -25,71 +24,22 @@ private CoverageReport LoadReport(string xmlFile)
}
}

//private void CoverageXmlFileToJsonFile(string xmlFile, string jsonFile, bool formattedJson = false)
//{
// var xmlText = File.ReadAllText(xmlFile);
// var jsonText = CoverageXmlTextToJsonText(xmlText, formattedJson);
// File.WriteAllText(jsonFile, jsonText);
//}

//private string CoverageXmlTextToJsonText(string xmlText, bool formattedJson = false)
//{
// long count = 0;

// var xmlLines = xmlText
// .Split('\r', '\n')
// .Select(x => x.Trim())
// .Where(x => !x.StartsWith("<?xml"))
// .Where(x => !x.StartsWith("<!DOCTYPE"))
// .Where(x => !x.StartsWith("<sources>") && !x.StartsWith("</sources>") && !x.StartsWith("<source>"))
// .Where(x => !x.StartsWith("<packages>") && !x.StartsWith("</packages>"))
// .Where(x => !x.StartsWith("<classes>") && !x.StartsWith("</classes>"))
// .Where(x => !x.StartsWith("<methods>") && !x.StartsWith("</methods>"))
// .Where(x => !x.StartsWith("<lines>") && !x.StartsWith("</lines>"))
// .Select(x => x
// .Replace("<package ", $"<packages id=\"p{count++}\" json:Array='true' ").Replace("</package>", "</packages>")
// .Replace("<class ", $"<classes id=\"c{count++}\" json:Array='true' ").Replace("</class>", "</classes>")
// .Replace("<method ", $"<methods id=\"m{count++}\" json:Array='true' ").Replace("</method>", "</methods>")
// .Replace("<line ", $"<lines id=\"l{count++}\" json:Array='true' ").Replace("</line>", "</lines>")
// );

// var processedXmlText = string
// .Join(Environment.NewLine, xmlLines)
// .Replace("<coverage ", "<coverage xmlns:json='http://james.newtonking.com/projects/json' ");

// var xmlDocument = new XmlDocument();
// xmlDocument.LoadXml(processedXmlText);

// string jsonText = JsonConvert
// .SerializeXmlNode(xmlDocument, formattedJson ? Newtonsoft.Json.Formatting.Indented : Newtonsoft.Json.Formatting.None, true)
// .Replace("\"@", "\"");

// return jsonText;
//}

public List<CoverageLine> ProcessCoberturaXml(string xmlFile)
public FileLineCoverage ProcessCoberturaXml(string xmlFile)
{
var coverageLines = new List<CoverageLine>();
var fileLineCoverage = new FileLineCoverage();

coverageReport = LoadReport(xmlFile);

foreach (var package in coverageReport.Packages.Package)
{
foreach (var classs in package.Classes.Class)
{
foreach (var line in classs.Lines.Line)
{
coverageLines.Add(new CoverageLine
{
Package = package,
Class = classs,
Line = line
});
}
fileLineCoverage.Add(classs.Filename, classs.Lines.Line);
}
}

return coverageLines;
fileLineCoverage.Completed();
return fileLineCoverage;
}

public string[] GetSourceFiles(string assemblyName, string qualifiedClassName, int file)
Expand Down
2 changes: 1 addition & 1 deletion SharedProject/Core/Cobertura/ICoberturaUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace FineCodeCoverage.Engine.Cobertura
{
interface ICoberturaUtil
{
List<CoverageLine> ProcessCoberturaXml(string xmlFile);
FileLineCoverage ProcessCoberturaXml(string xmlFile);
string[] GetSourceFiles(string assemblyName, string qualifiedClassName, int file);
}
}
16 changes: 8 additions & 8 deletions SharedProject/Core/FCCEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal enum ReloadCoverageStatus { Start, Done, Cancelled, Error, Initializing

internal class NewCoverageLinesMessage
{
public List<CoverageLine> CoverageLines { get; set; }
public FileLineCoverage CoverageLines { get; set; }
}

internal class DisplayCoverageResultState
Expand Down Expand Up @@ -202,12 +202,12 @@ private void ClearCoverageLines()
RaiseCoverageLines(null);
}

private void RaiseCoverageLines(List<CoverageLine> coverageLines)
private void RaiseCoverageLines(FileLineCoverage coverageLines)
{
eventAggregator.SendMessage(new NewCoverageLinesMessage { CoverageLines = coverageLines});
}

private void UpdateUI(List<CoverageLine> coverageLines, string reportHtml)
private void UpdateUI(FileLineCoverage coverageLines, string reportHtml)
{
RaiseCoverageLines(coverageLines);
if (reportHtml == null)
Expand All @@ -217,7 +217,7 @@ private void UpdateUI(List<CoverageLine> coverageLines, string reportHtml)
RaiseUpdateOutputWindow(reportHtml);
}

private async System.Threading.Tasks.Task<(List<CoverageLine> coverageLines,string reportFilePath)> RunAndProcessReportAsync(string[] coverOutputFiles, CancellationToken vsShutdownLinkedCancellationToken)
private async System.Threading.Tasks.Task<(FileLineCoverage coverageLines,string reportFilePath)> RunAndProcessReportAsync(string[] coverOutputFiles, CancellationToken vsShutdownLinkedCancellationToken)
{
var reportOutputFolder = coverageOutputManager.GetReportOutputFolder();
vsShutdownLinkedCancellationToken.ThrowIfCancellationRequested();
Expand Down Expand Up @@ -262,7 +262,7 @@ private async System.Threading.Tasks.Task PrepareCoverageProjectsAsync(List<ICov
}
}

private void DisplayCoverageResult(System.Threading.Tasks.Task<(List<CoverageLine> coverageLines, string reportHtml)> t, object state)
private void DisplayCoverageResult(System.Threading.Tasks.Task<(FileLineCoverage coverageLines, string reportHtml)> t, object state)
{
var displayCoverageResultState = (DisplayCoverageResultState)state;
if (!IsVsShutdown)
Expand Down Expand Up @@ -321,7 +321,7 @@ public void RunAndProcessReport(string[] coberturaFiles, Action cleanUp = null)
{
RunCancellableCoverageTask(async (vsShutdownLinkedCancellationToken) =>
{
List<CoverageLine> coverageLines = null;
FileLineCoverage coverageLines = null;
string reportHtml = null;

if (coberturaFiles.Any())
Expand All @@ -333,7 +333,7 @@ public void RunAndProcessReport(string[] coberturaFiles, Action cleanUp = null)
}

private void RunCancellableCoverageTask(
Func<CancellationToken,System.Threading.Tasks.Task<(List<CoverageLine>, string)>> taskCreator, Action cleanUp)
Func<CancellationToken,System.Threading.Tasks.Task<(FileLineCoverage, string)>> taskCreator, Action cleanUp)
{
var vsLinkedCancellationTokenSource = Reset();
var vsShutdownLinkedCancellationToken = vsLinkedCancellationTokenSource.Token;
Expand All @@ -355,7 +355,7 @@ public void ReloadCoverage(Func<System.Threading.Tasks.Task<List<ICoverageProjec
{
RunCancellableCoverageTask(async (vsShutdownLinkedCancellationToken) =>
{
List<CoverageLine> coverageLines = null;
FileLineCoverage coverageLines = null;
string reportHtml = null;

await PollInitializedStatusAsync(vsShutdownLinkedCancellationToken);
Expand Down
11 changes: 0 additions & 11 deletions SharedProject/Core/Model/CoverageLine.cs

This file was deleted.

46 changes: 46 additions & 0 deletions SharedProject/Core/Model/FileLineCoverage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using FineCodeCoverage.Core.Utilities;
using FineCodeCoverage.Engine.Cobertura;
using System;
using System.Collections.Generic;

namespace FineCodeCoverage.Engine.Model
{
// FileLineCoverage maps from a filename to the list of lines in the file
internal class FileLineCoverage
{
private Dictionary<string, List<Line>> m_coverageLines = new Dictionary<string, List<Line>>(StringComparer.OrdinalIgnoreCase);

public void Add(string filename, IEnumerable<Line> lines)
{
if (!m_coverageLines.TryGetValue(filename, out var fileCoverageLines))
{
fileCoverageLines = new List<Line>();
m_coverageLines.Add(filename, fileCoverageLines);
}

fileCoverageLines.AddRange(lines);
}

public void Completed()
{
foreach (var lines in m_coverageLines.Values)
lines.Sort((a, b) => a.Number - b.Number);
}

public IEnumerable<Line> GetLines(string filePath, int startLineNumber, int endLineNumber)
{
if (!m_coverageLines.TryGetValue(filePath, out var lines))
yield break;

int first = lines.LowerBound(line => startLineNumber - line.Number);
if (first != -1)
{
for (int it = first; it < lines.Count && lines[it].Number <= endLineNumber; ++it)
yield return lines[it];
}

}
}


}
42 changes: 42 additions & 0 deletions SharedProject/Core/Utilities/ListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;

namespace FineCodeCoverage.Core.Utilities
{
public static class ListExtensions
{
// To be performed on a sorted list
// Returns -1 for empty list or when all elements are outside the lower bounds
// Compare fn to return 0 for element considered the lower bound
// > 0 for lower bound greater than element

public static int LowerBound<T>(this IList<T> list, Func<T, int> compare)
{
int first = 0;
int count = list.Count;
if (count == 0) return -1;

while (count > 0)
{
int step = count / 2;
int index = first + step;
var result = compare(list[index]);
if (result == 0)
{
return index;
}
else if (result > 0)
{
first = ++index;
count -= step + 1;
}
else
{
count = step;
}
}

return first != list.Count ? first : -1;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using FineCodeCoverage.Engine.Model;
using FineCodeCoverage.Engine.Cobertura;
using FineCodeCoverage.Engine.Model;
using Microsoft.VisualStudio.Text.Editor;

namespace FineCodeCoverage.Impl
{
internal class CoverageLineGlyphTag : IGlyphTag
{
public CoverageLine CoverageLine { get; }
public Line CoverageLine { get; }

public CoverageLineGlyphTag(CoverageLine coverageLine)
public CoverageLineGlyphTag(Line coverageLine)
{
CoverageLine = coverageLine;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace FineCodeCoverage.Impl
{
internal class CoverageLineGlyphTagger : CoverageLineTaggerBase<CoverageLineGlyphTag>, IListener<RefreshCoverageGlyphsMessage>
{
public CoverageLineGlyphTagger(ITextBuffer textBuffer, List<CoverageLine> lastCoverageLines) : base(textBuffer, lastCoverageLines)
public CoverageLineGlyphTagger(ITextBuffer textBuffer, FileLineCoverage lastCoverageLines) : base(textBuffer, lastCoverageLines)
{
}

Expand All @@ -17,7 +17,7 @@ public void Handle(RefreshCoverageGlyphsMessage message)
RaiseTagsChanged();
}

protected override TagSpan<CoverageLineGlyphTag> GetTagSpan(CoverageLine coverageLine, SnapshotSpan span)
protected override TagSpan<CoverageLineGlyphTag> GetTagSpan(Engine.Cobertura.Line coverageLine, SnapshotSpan span)
{
return new TagSpan<CoverageLineGlyphTag>(span, new CoverageLineGlyphTag(coverageLine));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected override void NewCoverageLinesMessageReceived()
});
}

protected override CoverageLineGlyphTagger CreateTagger(ITextBuffer textBuffer, List<CoverageLine> lastCoverageLines)
protected override CoverageLineGlyphTagger CreateTagger(ITextBuffer textBuffer, FileLineCoverage lastCoverageLines)
{
return new CoverageLineGlyphTagger(textBuffer, lastCoverageLines);
}
Expand Down
Loading

0 comments on commit 86dd4db

Please sign in to comment.