/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="methods">The methods.</param> /// <param name="class">The class.</param> private static void SetMethodMetrics(IEnumerable<XElement> methods, Class @class) { foreach (var method in methods) { string methodName = method.Element("MethodName").Value; // Exclude properties and lambda expressions if (methodName.StartsWith("get_", StringComparison.Ordinal) || methodName.StartsWith("set_", StringComparison.Ordinal) || Regex.IsMatch(methodName, "<.+>.+__")) { continue; } var metrics = new[] { new Metric( "Blocks covered", int.Parse(method.Element("BlocksCovered").Value, CultureInfo.InvariantCulture)), new Metric( "Blocks not covered", int.Parse(method.Element("BlocksNotCovered").Value, CultureInfo.InvariantCulture)) }; @class.AddMethodMetric(new MethodMetric(methodName, metrics)); } }
/// <summary> /// Creates a class report. /// </summary> /// <param name="class">The class.</param> /// <param name="fileAnalyses">The file analyses that correspond to the class.</param> public override void CreateClassReport(Class @class, IEnumerable<FileAnalysis> fileAnalyses) { using (var renderer = new HtmlRenderer(false, this.javaScriptContent)) { this.CreateClassReport(renderer, @class, fileAnalyses); } }
/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="methods">The methods.</param> /// <param name="class">The class.</param> private static void SetMethodMetrics(IEnumerable<XElement> methods, Class @class) { foreach (var methodGroup in methods.GroupBy(m => m.Element("Name").Value)) { var method = methodGroup.First(); // Exclude properties and lambda expressions if (method.Attribute("skippedDueTo") != null || method.HasAttributeWithValue("isGetter", "true") || method.HasAttributeWithValue("isSetter", "true") || Regex.IsMatch(methodGroup.Key, "::<.+>.+__")) { continue; } var metrics = new[] { new Metric( "Cyclomatic Complexity", methodGroup.Max(m => int.Parse(m.Attribute("cyclomaticComplexity").Value, CultureInfo.InvariantCulture))), new Metric( "Sequence Coverage", methodGroup.Max(m => decimal.Parse(m.Attribute("sequenceCoverage").Value, CultureInfo.InvariantCulture))), new Metric( "Branch Coverage", methodGroup.Max(m => decimal.Parse(m.Attribute("branchCoverage").Value, CultureInfo.InvariantCulture))) }; @class.AddMethodMetric(new MethodMetric(methodGroup.Key, metrics)); } }
/// <summary> /// Initializes a new instance of the <see cref="HistoricCoverage" /> class. /// </summary> /// <param name="class">The class.</param> /// <param name="executionTime">The execution time.</param> public HistoricCoverage(Class @class, DateTime executionTime) : this(executionTime) { this.CoveredLines = @class.CoveredLines; this.CoverableLines = @class.CoverableLines; this.TotalLines = @class.TotalLines.GetValueOrDefault(); this.CoveredBranches = @class.CoveredBranches.GetValueOrDefault(); this.TotalBranches = @class.TotalBranches.GetValueOrDefault(); }
public void AddFile_AddSingleFile_FileIsStored() { var assembly = new Assembly("C:\\test\\TestAssembly.dll"); var sut = new Class("Test", assembly); var file = new CodeFile("C:\\temp\\Program.cs", new int[0]); sut.AddFile(file); Assert.AreEqual(file, sut.Files.First(), "Not equal"); Assert.AreEqual(1, sut.Files.Count(), "Wrong number of classes"); }
public void Constructor() { Assembly assembly = new Assembly("C:\\test\\TestAssembly.dll"); string classname = "TestClass"; var sut = new Class(classname, assembly); Assert.AreEqual(assembly, sut.Assembly, "Not equal"); Assert.AreEqual(classname, sut.Name, "Not equal"); }
public void AddClass_AddSingleClass_ClassIsStored() { var sut = new Assembly("C:\\test\\TestAssembly.dll"); var @class = new Class("Test", sut); sut.AddClass(@class); Assert.AreEqual(@class, sut.Classes.First(), "Not equal"); Assert.AreEqual(1, sut.Classes.Count(), "Wrong number of classes"); }
public void Merge_MergeAssemblyWithOneClass_ClassIsStored() { var sut = new Assembly("C:\\test\\TestAssembly.dll"); var assemblyToMerge = new Assembly("C:\\test\\TestAssembly.dll"); var @class = new Class("Test", sut); assemblyToMerge.AddClass(@class); sut.Merge(assemblyToMerge); Assert.AreEqual(@class, sut.Classes.First(), "Not equal"); Assert.AreEqual(1, sut.Classes.Count(), "Wrong number of classes"); }
public void Equals() { Assembly assembly = new Assembly("C:\\test\\TestAssembly.dll"); string classname = "TestClass"; var target1 = new Class(classname, assembly); var target2 = new Class(classname, assembly); var target3 = new Class(classname + "123", assembly); Assert.IsTrue(target1.Equals(target2), "Objects are not equal"); Assert.IsFalse(target1.Equals(target3), "Objects are equal"); Assert.IsFalse(target1.Equals(null), "Objects are equal"); Assert.IsFalse(target1.Equals(new object()), "Objects are equal"); }
public void Merge_MergeClassWithOneFileAndOneMethodMetric_FileIsStored() { var assembly = new Assembly("C:\\test\\TestAssembly.dll"); var sut = new Class("Test", assembly); var classToMerge = new Class("Test", assembly); var file = new CodeFile("C:\\temp\\Program.cs", new int[0]); var methodMetric = new MethodMetric("Test"); classToMerge.AddFile(file); classToMerge.AddMethodMetric(methodMetric); sut.Merge(classToMerge); Assert.AreEqual(file, sut.Files.First(), "Not equal"); Assert.AreEqual(1, sut.Files.Count(), "Wrong number of classes"); Assert.AreEqual(methodMetric, sut.MethodMetrics.First(), "Not equal"); Assert.AreEqual(1, sut.MethodMetrics.Count(), "Wrong number of method metrics"); }
public void Merge_MergeClassWithCoverageQuota_FileIsStored() { var assembly = new Assembly("C:\\test\\TestAssembly.dll"); var sut = new Class("Test", assembly); var classToMerge = new Class("Test", assembly) { CoverageQuota = 15 }; sut.Merge(classToMerge); Assert.AreEqual(15, sut.CoverageQuota); classToMerge = new Class("Test", assembly) { CoverageQuota = 20 }; sut.Merge(classToMerge); Assert.AreEqual(20, sut.CoverageQuota); }
/// <summary> /// Extracts the metrics from the given <see cref="XElement">XElements</see>. /// </summary> /// <param name="methods">The methods.</param> /// <param name="class">The class.</param> private static void SetMethodMetrics(IEnumerable<XElement> methods, Class @class) { foreach (var methodGroup in methods.GroupBy(m => m.Element("Name").Value)) { var method = methodGroup.First(); // Exclude properties and lambda expressions if (method.Attribute("skippedDueTo") != null || method.HasAttributeWithValue("isGetter", "true") || method.HasAttributeWithValue("isSetter", "true") || Regex.IsMatch(methodGroup.Key, "::<.+>.+__")) { continue; } string methodName = Regex.Replace( methodGroup.Key, MethodRegex, m => string.Format(CultureInfo.InvariantCulture, "{0}({1})", m.Groups["MethodName"].Value, m.Groups["Arguments"].Value.Length > 0 ? "..." : string.Empty)); var metrics = new[] { new Metric( "Cyclomatic Complexity", methodGroup.Max(m => int.Parse(m.Attribute("cyclomaticComplexity").Value, CultureInfo.InvariantCulture))), new Metric( "Sequence Coverage", methodGroup.Max(m => int.Parse(m.Attribute("sequenceCoverage").Value, CultureInfo.InvariantCulture))), new Metric( "Branch Coverage", methodGroup.Max(m => int.Parse(m.Attribute("branchCoverage").Value, CultureInfo.InvariantCulture))) }; @class.AddMethodMetric(new MethodMetric(methodName, metrics)); } }
/// <summary> /// Creates a class report. /// </summary> /// <param name="class">The class.</param> /// <param name="fileAnalyses">The file analyses that correspond to the class.</param> public override void CreateClassReport(Class @class, IEnumerable<FileAnalysis> fileAnalyses) { this.CreateClassReport(new XmlRenderer(), @class, fileAnalyses); }
/// <summary> /// Processes the given class. /// </summary> /// <param name="assembly">The assembly.</param> /// <param name="className">Name of the class.</param> /// <returns>The <see cref="Class"/>.</returns> private Class ProcessClass(Assembly assembly, string className) { var fileIdsOfClass = this.modules .Where(m => m.Element("ModuleName").Value.Equals(assembly.Name)) .Elements("NamespaceTable") .Elements("Class") .Where(c => (c.Parent.Element("NamespaceName").Value + "." + c.Element("ClassName").Value).Equals(className, StringComparison.Ordinal) || (c.Parent.Element("NamespaceName").Value + "." + c.Element("ClassName").Value).StartsWith(className + ".", StringComparison.Ordinal)) .Elements("Method") .Elements("Lines") .Elements("SourceFileID") .Select(m => m.Value) .Distinct(); var @class = new Class(className, assembly); foreach (var fileId in fileIdsOfClass) { string file = this.files.First(f => f.Element("SourceFileID").Value == fileId).Element("SourceFileName").Value; @class.AddFile(this.ProcessFile(fileId, @class, file)); } return @class; }
/// <summary> /// Processes the given class. /// </summary> /// <param name="assembly">The assembly.</param> /// <param name="className">Name of the class.</param> /// <returns>The <see cref="Class"/>.</returns> private Class ProcessClass(Assembly assembly, string className) { var filesOfClass = this.modules .Where(module => module.Attribute("assembly").Value.Equals(assembly.Name)).Elements("method") .Where(method => method.Attribute("class").Value.Equals(className)) .Where(m => m.Attribute("excluded").Value == "false") .Elements("seqpnt").Select(seqpnt => seqpnt.Attribute("document").Value) .Distinct() .ToArray(); var @class = new Class(className, assembly); foreach (var file in filesOfClass) { @class.AddFile(this.ProcessFile(@class, file)); } return @class; }
/// <summary> /// Processes the file. /// </summary> /// <param name="class">The class.</param> /// <param name="filePath">The file path.</param> /// <returns>The <see cref="CodeFile"/>.</returns> private CodeFile ProcessFile(Class @class, string filePath) { var seqpntsOfFile = this.modules .Where(type => type.Attribute("assembly").Value.Equals(@class.Assembly.Name)) .Elements("method") .Where(m => m.Attribute("excluded").Value == "false") .Where(method => method.Attribute("class").Value.StartsWith(@class.Name, StringComparison.Ordinal)) .Elements("seqpnt") .Where(seqpnt => seqpnt.Attribute("document").Value.Equals(filePath) && seqpnt.Attribute("line").Value != "16707566") .Select(seqpnt => new { LineNumber = int.Parse(seqpnt.Attribute("line").Value, CultureInfo.InvariantCulture), Visits = int.Parse(seqpnt.Attribute("visitcount").Value, CultureInfo.InvariantCulture) }) .OrderBy(seqpnt => seqpnt.LineNumber) .ToArray(); int[] coverage = new int[] { }; if (seqpntsOfFile.Length > 0) { coverage = new int[seqpntsOfFile[seqpntsOfFile.LongLength - 1].LineNumber + 1]; for (int i = 0; i < coverage.Length; i++) { coverage[i] = -1; } foreach (var seqpnt in seqpntsOfFile) { coverage[seqpnt.LineNumber] = coverage[seqpnt.LineNumber] == -1 ? seqpnt.Visits : coverage[seqpnt.LineNumber] + seqpnt.Visits; } } return new CodeFile(filePath, coverage); }
/// <summary> /// Processes the file. /// </summary> /// <param name="fileId">The file id.</param> /// <param name="class">The class.</param> /// <param name="filePath">The file path.</param> /// <returns>The <see cref="CodeFile"/>.</returns> private CodeFile ProcessFile(string fileId, Class @class, string filePath) { var methods = this.modules .Where(m => m.Element("ModuleName").Value.Equals(@class.Assembly.Name)) .Elements("NamespaceTable") .Elements("Class") .Where(c => (c.Parent.Element("NamespaceName").Value + "." + c.Element("ClassName").Value).Equals(@class.Name, StringComparison.Ordinal) || (c.Parent.Element("NamespaceName").Value + "." + c.Element("ClassName").Value).StartsWith(@class.Name + ".", StringComparison.Ordinal)) .Elements("Method") .Where(m => m.Elements("Lines").Elements("SourceFileID").Any(s => s.Value == fileId)) .ToArray(); SetMethodMetrics(methods, @class); var linesOfFile = methods .Elements("Lines") .Select(l => new { LineNumberStart = int.Parse(l.Element("LnStart").Value, CultureInfo.InvariantCulture), LineNumberEnd = int.Parse(l.Element("LnEnd").Value, CultureInfo.InvariantCulture), Coverage = int.Parse(l.Element("Coverage").Value, CultureInfo.InvariantCulture) }) .OrderBy(seqpnt => seqpnt.LineNumberEnd) .ToArray(); int[] coverage = new int[] { }; if (linesOfFile.Length > 0) { coverage = new int[linesOfFile[linesOfFile.LongLength - 1].LineNumberEnd + 1]; for (int i = 0; i < coverage.Length; i++) { coverage[i] = -1; } foreach (var seqpnt in linesOfFile) { for (int lineNumber = seqpnt.LineNumberStart; lineNumber <= seqpnt.LineNumberEnd; lineNumber++) { int visits = seqpnt.Coverage < 2 ? 1 : 0; coverage[lineNumber] = coverage[lineNumber] == -1 ? visits : Math.Min(coverage[lineNumber] + visits, 1); } } } return new CodeFile(filePath, coverage); }
/// <summary> /// Adds the coverage information of a class to the report. /// </summary> /// <param name="class">The class.</param> public void SummaryClass(Class @class) { if (@class == null) { throw new ArgumentNullException(nameof(@class)); } string filenameColumn = @class.Name; if (!this.onlySummary) { filenameColumn = string.Format( CultureInfo.InvariantCulture, "<a href=\"{0}\">{1}</a>", WebUtility.HtmlEncode(GetClassReportFilename(@class.Assembly.ShortName, @class.Name)), WebUtility.HtmlEncode(@class.Name)); } this.reportTextWriter.Write("<tr>"); this.reportTextWriter.Write("<td>{0}</td>", filenameColumn); this.reportTextWriter.Write("<td class=\"right\">{0}</td>", @class.CoveredLines); this.reportTextWriter.Write("<td class=\"right\">{0}</td>", @class.CoverableLines - @class.CoveredLines); this.reportTextWriter.Write("<td class=\"right\">{0}</td>", @class.CoverableLines); this.reportTextWriter.Write("<td class=\"right\">{0}</td>", @class.TotalLines.GetValueOrDefault()); this.reportTextWriter.Write( "<td title=\"{0}\" class=\"right\">{1}</td>", @class.CoverageType, @class.CoverageQuota.HasValue ? @class.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty); this.reportTextWriter.Write("<td>{0}</td>", CreateCoverageTable(@class.CoverageQuota)); this.reportTextWriter.Write( "<td class=\"right\">{0}</td>", @class.BranchCoverageQuota.HasValue ? @class.BranchCoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty); this.reportTextWriter.Write("<td>{0}</td>", CreateCoverageTable(@class.BranchCoverageQuota)); this.reportTextWriter.WriteLine("</tr>"); }
/// <summary> /// Adds the coverage information of a class to the report. /// </summary> /// <param name="class">The class.</param> public void SummaryClass(Class @class) { if (@class == null) { throw new ArgumentNullException("class"); } string row = string.Format( CultureInfo.InvariantCulture, @"{0} & {1}\%\\", EscapeLatexChars(@class.Name), @class.CoverageQuota); this.reportBuilder.AppendLine(row); }
/// <summary> /// Adds the coverage information of a class to the report. /// </summary> /// <param name="class">The class.</param> public void SummaryClass(Class @class) { if (@class == null) { throw new ArgumentNullException("class"); } var coverage = new XElement( "Class", new XAttribute("name", @class.Name), new XAttribute("coverage", @class.CoverageQuota), new XAttribute("coveredlines", @class.CoveredLines), new XAttribute("coverablelines", @class.CoverableLines), new XAttribute("totallines", @class.TotalLines)); this.currentAssembly.Add(coverage); }
/// <summary> /// Adds the coverage information of a class to the report. /// </summary> /// <param name="class">The class.</param> public void SummaryClass(Class @class) { if (@class == null) { throw new ArgumentNullException("class"); } this.reportTextWriter.WriteStartElement("Class"); this.reportTextWriter.WriteAttributeString("name", @class.Name); this.reportTextWriter.WriteAttributeString("coverage", @class.CoverageQuota.HasValue ? @class.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) : string.Empty); this.reportTextWriter.WriteAttributeString("coveredlines", @class.CoveredLines.ToString(CultureInfo.InvariantCulture)); this.reportTextWriter.WriteAttributeString("coverablelines", @class.CoverableLines.ToString(CultureInfo.InvariantCulture)); this.reportTextWriter.WriteAttributeString("totallines", @class.TotalLines.HasValue ? @class.TotalLines.Value.ToString(CultureInfo.InvariantCulture) : string.Empty); this.reportTextWriter.WriteEndElement(); }
/// <summary> /// Processes the file. /// </summary> /// <param name="module">The module.</param> /// <param name="fileId">The file id.</param> /// <param name="class">The class.</param> /// <param name="filePath">The file path.</param> /// <returns>The <see cref="CodeFile"/>.</returns> private static CodeFile ProcessFile(XElement module, string fileId, Class @class, string filePath) { var methods = module .Elements("functions") .Elements("function") .Where(c => c.Attribute("type_name").Value.Equals(@class.Name, StringComparison.Ordinal) || c.Attribute("type_name").Value.StartsWith(@class.Name + ".", StringComparison.Ordinal)) .Where(m => m.Elements("ranges").Elements("range").Any(r => r.Attribute("source_id").Value == fileId)) .ToArray(); SetMethodMetrics(methods, @class); var linesOfFile = methods .Elements("ranges") .Elements("range") .Select(l => new { LineNumberStart = int.Parse(l.Attribute("start_line").Value, CultureInfo.InvariantCulture), LineNumberEnd = int.Parse(l.Attribute("end_line").Value, CultureInfo.InvariantCulture), Coverage = l.Attribute("covered").Value.Equals("no") ? 0 : 1 }) .OrderBy(seqpnt => seqpnt.LineNumberEnd) .ToArray(); int[] coverage = new int[] { }; if (linesOfFile.Length > 0) { coverage = new int[linesOfFile[linesOfFile.LongLength - 1].LineNumberEnd + 1]; for (int i = 0; i < coverage.Length; i++) { coverage[i] = -1; } foreach (var seqpnt in linesOfFile) { for (int lineNumber = seqpnt.LineNumberStart; lineNumber <= seqpnt.LineNumberEnd; lineNumber++) { coverage[lineNumber] = coverage[lineNumber] == -1 ? seqpnt.Coverage : Math.Min(coverage[lineNumber] + seqpnt.Coverage, 1); } } } return new CodeFile(filePath, coverage); }
/// <summary> /// Processes the given class. /// </summary> /// <param name="module">The module.</param> /// <param name="assembly">The assembly.</param> /// <param name="className">Name of the class.</param> /// <returns>The <see cref="Class"/>.</returns> private static Class ProcessClass(XElement module, Assembly assembly, string className) { var fileIdsOfClass = module .Elements("functions") .Elements("function") .Where(c => c.Attribute("type_name").Value.Equals(className, StringComparison.Ordinal) || c.Attribute("type_name").Value.StartsWith(className + ".", StringComparison.Ordinal)) .Elements("ranges") .Elements("range") .Select(r => r.Attribute("source_id").Value) .Distinct(); var @class = new Class(className, assembly); var files = module.Elements("source_files").Elements("source_file"); foreach (var fileId in fileIdsOfClass) { string file = files.First(f => f.Attribute("id").Value == fileId).Attribute("path").Value; @class.AddFile(ProcessFile(module, fileId, @class, file)); } return @class; }
/// <summary> /// Adds the coverage information of a class to the report. /// </summary> /// <param name="class">The class.</param> public void SummaryClass(Class @class) { if (@class == null) { throw new ArgumentNullException("class"); } string row = string.Format( CultureInfo.InvariantCulture, @"{0} & {1}\\", EscapeLatexChars(@class.Name), @class.CoverageQuota.HasValue ? @class.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + @"\%" : string.Empty); this.reportTextWriter.WriteLine(row); }
/// <summary> /// Processes the given class. /// </summary> /// <param name="fileIdsByFilename">Dictionary containing the file ids by filename.</param> /// <param name="assembly">The assembly.</param> /// <param name="className">Name of the class.</param> /// <returns>The <see cref="Class"/>.</returns> private Class ProcessClass(Dictionary<string, HashSet<string>> fileIdsByFilename, Assembly assembly, string className) { var methods = this.modules .Where(m => m.Element("ModuleName").Value.Equals(assembly.Name)) .Elements("Classes") .Elements("Class") .Where(c => c.Element("FullName").Value.Equals(className) || c.Element("FullName").Value.StartsWith(className + "/", StringComparison.Ordinal)) .Elements("Methods") .Elements("Method"); var fileIdsOfClassInSequencePoints = methods .Elements("SequencePoints") .Elements("SequencePoint") .Where(seqpnt => seqpnt.Attribute("fileid") != null && seqpnt.Attribute("fileid").Value != "0") .Select(seqpnt => seqpnt.Attribute("fileid").Value) .ToArray(); // Only required for backwards compatibility, older versions of OpenCover did not apply fileid for partial classes var fileIdsOfClassInFileRef = methods .Where(m => m.Element("FileRef") != null) .Select(m => m.Element("FileRef").Attribute("uid").Value) .ToArray(); var fileIdsOfClass = fileIdsOfClassInSequencePoints .Concat(fileIdsOfClassInFileRef) .Distinct() .ToHashSet(); var filesOfClass = this.files .Where(file => fileIdsOfClass.Contains(file.Attribute("uid").Value)) .Select(file => file.Attribute("fullPath").Value) .Distinct() .ToArray(); var @class = new Class(className, assembly); foreach (var file in filesOfClass) { @class.AddFile(this.ProcessFile(fileIdsByFilename[file], @class, file)); } @class.CoverageQuota = this.GetCoverageQuotaOfClass(assembly, className); return @class; }
/// <summary> /// Merges the given class with the current instance. /// </summary> /// <param name="class">The class to merge.</param> internal void Merge(Class @class) { if (@class == null) { throw new ArgumentNullException(nameof(@class)); } if (this.coverageQuota.HasValue && @class.coverageQuota.HasValue) { this.CoverageQuota = Math.Max(this.coverageQuota.Value, @class.coverageQuota.Value); } else if (@class.coverageQuota.HasValue) { this.CoverageQuota = @class.coverageQuota.Value; } foreach (var methodMetric in @class.methodMetrics) { var existingMethodMetric = this.methodMetrics.FirstOrDefault(m => m.Name == methodMetric.Name); if (existingMethodMetric != null) { existingMethodMetric.Merge(methodMetric); } else { this.AddMethodMetric(methodMetric); } } foreach (var file in @class.files) { var existingFile = this.files.FirstOrDefault(f => f.Path == file.Path); if (existingFile != null) { existingFile.Merge(file); } else { this.AddFile(file); } } }
/// <summary> /// Processes the file. /// </summary> /// <param name="fileIds">The file ids of the class.</param> /// <param name="class">The class.</param> /// <param name="filePath">The file path.</param> /// <returns>The <see cref="CodeFile"/>.</returns> private CodeFile ProcessFile(HashSet<string> fileIds, Class @class, string filePath) { var methods = this.modules .Where(m => m.Element("ModuleName").Value.Equals(@class.Assembly.Name)) .Elements("Classes") .Elements("Class") .Where(c => c.Element("FullName").Value.Equals(@class.Name) || c.Element("FullName").Value.StartsWith(@class.Name + "/", StringComparison.Ordinal)) .Elements("Methods") .Elements("Method") .ToArray(); var methodsOfFile = methods .Where(m => m.Element("FileRef") != null && fileIds.Contains(m.Element("FileRef").Attribute("uid").Value)) .ToArray(); SetMethodMetrics(methodsOfFile, @class); var seqpntsOfFile = methods .Elements("SequencePoints") .Elements("SequencePoint") .Where(seqpnt => (seqpnt.Attribute("fileid") != null && fileIds.Contains(seqpnt.Attribute("fileid").Value)) || (seqpnt.Attribute("fileid") == null && seqpnt.Parent.Parent.Element("FileRef") != null && fileIds.Contains(seqpnt.Parent.Parent.Element("FileRef").Attribute("uid").Value))) .Select(seqpnt => new { LineNumberStart = int.Parse(seqpnt.Attribute("sl").Value, CultureInfo.InvariantCulture), LineNumberEnd = seqpnt.Attribute("el") != null ? int.Parse(seqpnt.Attribute("el").Value, CultureInfo.InvariantCulture) : int.Parse(seqpnt.Attribute("sl").Value, CultureInfo.InvariantCulture), Visits = int.Parse(seqpnt.Attribute("vc").Value, CultureInfo.InvariantCulture), TrackedMethodRefs = seqpnt.Elements("TrackedMethodRefs") .Elements("TrackedMethodRef") .Select(t => new { Visits = int.Parse(t.Attribute("vc").Value, CultureInfo.InvariantCulture), TrackedMethodId = t.Attribute("uid").Value }) }) .OrderBy(seqpnt => seqpnt.LineNumberEnd) .ToArray(); int[] coverage = new int[] { }; var branches = GetBranches(methods, fileIds); var trackedMethodsCoverage = seqpntsOfFile .SelectMany(s => s.TrackedMethodRefs) .Select(t => t.TrackedMethodId) .Distinct() .ToDictionary(id => id, id => new int[] { }); if (seqpntsOfFile.Length > 0) { coverage = new int[seqpntsOfFile[seqpntsOfFile.LongLength - 1].LineNumberEnd + 1]; for (int i = 0; i < coverage.Length; i++) { coverage[i] = -1; } foreach (var name in trackedMethodsCoverage.Keys.ToArray()) { trackedMethodsCoverage[name] = (int[])coverage.Clone(); } foreach (var seqpnt in seqpntsOfFile) { for (int lineNumber = seqpnt.LineNumberStart; lineNumber <= seqpnt.LineNumberEnd; lineNumber++) { int visits = coverage[lineNumber] == -1 ? seqpnt.Visits : coverage[lineNumber] + seqpnt.Visits; coverage[lineNumber] = visits; if (visits > -1) { foreach (var trackedMethodCoverage in trackedMethodsCoverage) { if (trackedMethodCoverage.Value[lineNumber] == -1) { trackedMethodCoverage.Value[lineNumber] = 0; } } } foreach (var trackedMethod in seqpnt.TrackedMethodRefs) { var trackedMethodCoverage = trackedMethodsCoverage[trackedMethod.TrackedMethodId]; trackedMethodCoverage[lineNumber] = trackedMethodCoverage[lineNumber] == -1 ? trackedMethod.Visits : trackedMethodCoverage[lineNumber] + trackedMethod.Visits; } } } } var codeFile = new CodeFile(filePath, coverage, branches); foreach (var trackedMethodCoverage in trackedMethodsCoverage) { string name = null; // Sometimes no corresponding MethodRef element exists if (this.trackedMethods.TryGetValue(trackedMethodCoverage.Key, out name)) { string shortName = name.Substring(name.Substring(0, name.IndexOf(':') + 1).LastIndexOf('.') + 1); TestMethod testMethod = new TestMethod(name, shortName); codeFile.AddCoverageByTestMethod(testMethod, trackedMethodCoverage.Value); } } return codeFile; }
/// <summary> /// Processes the file. /// </summary> /// <param name="class">The class.</param> /// <param name="filePath">The file path.</param> /// <returns>The <see cref="CodeFile"/>.</returns> private CodeFile ProcessFile(Class @class, string filePath) { string fileId = this.fileIdByFilenameDictionary[filePath]; var seqpntsOfFile = this.types .Where(type => type.Attribute("asm").Value.Equals(@class.Assembly.Name) && (type.Attribute("name").Value.Equals(@class.Name, StringComparison.Ordinal) || type.Attribute("name").Value.StartsWith(@class.Name + "<", StringComparison.Ordinal))) .Elements("method") .Elements("code") .Elements("pt") .Where(seqpnt => seqpnt.HasAttributeWithValue("fid", fileId)) .Select(seqpnt => new { LineNumberStart = int.Parse(seqpnt.Attribute("sl").Value, CultureInfo.InvariantCulture), LineNumberEnd = seqpnt.Attribute("el") != null ? int.Parse(seqpnt.Attribute("el").Value, CultureInfo.InvariantCulture) : int.Parse(seqpnt.Attribute("sl").Value, CultureInfo.InvariantCulture), Visits = int.Parse(seqpnt.Attribute("visit").Value, CultureInfo.InvariantCulture) }) .OrderBy(seqpnt => seqpnt.LineNumberEnd) .ToArray(); int[] coverage = new int[] { }; if (seqpntsOfFile.Length > 0) { coverage = new int[seqpntsOfFile[seqpntsOfFile.LongLength - 1].LineNumberEnd + 1]; for (int i = 0; i < coverage.Length; i++) { coverage[i] = -1; } foreach (var seqpnt in seqpntsOfFile) { for (int lineNumber = seqpnt.LineNumberStart; lineNumber <= seqpnt.LineNumberEnd; lineNumber++) { coverage[lineNumber] = coverage[lineNumber] == -1 ? seqpnt.Visits : coverage[lineNumber] + seqpnt.Visits; } } } return new CodeFile(filePath, coverage); }
/// <summary> /// Creates a class report. /// </summary> /// <param name="class">The class.</param> /// <param name="fileAnalyses">The file analyses that correspond to the class.</param> public void CreateClassReport(Palmmedia.ReportGenerator.Parser.Analysis.Class @class, IEnumerable <Palmmedia.ReportGenerator.Parser.Analysis.FileAnalysis> fileAnalyses) { }
/// <summary> /// Creates a class report. /// </summary> /// <param name="class">The class.</param> /// <param name="fileAnalyses">The file analyses that correspond to the class.</param> public void CreateClassReport(Class @class, IEnumerable<FileAnalysis> fileAnalyses) { }
/// <summary> /// Processes the given class. /// </summary> /// <param name="assembly">The assembly.</param> /// <param name="className">Name of the class.</param> /// <returns>The <see cref="Class"/>.</returns> private Class ProcessClass(Assembly assembly, string className) { var fileIdsOfClass = this.types .Where(type => type.Attribute("asm").Value.Equals(assembly.Name) && (type.Attribute("name").Value.Equals(className, StringComparison.Ordinal) || type.Attribute("name").Value.StartsWith(className + "<", StringComparison.Ordinal))) .Elements("method") .Elements("code") .Elements("pt") .Where(pt => pt.Attribute("fid") != null) .Select(pt => pt.Attribute("fid").Value) .Distinct() .ToHashSet(); var filesOfClass = this.files .Where(file => fileIdsOfClass.Contains(file.Attribute("id").Value)) .Select(file => file.Attribute("url").Value) .ToArray(); var @class = new Class(className, assembly); foreach (var file in filesOfClass) { @class.AddFile(this.ProcessFile(@class, file)); } return @class; }