Last active
January 2, 2016 16:59
-
-
Save jonorossi/8333504 to your computer and use it in GitHub Desktop.
Bullseye Coverage - XML coverage file parsing (extracted from an in-house tool written several years ago)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Files: | |
// * CoverageDetails.cs - CoverageDetails has a bunch of simple attributes read from the coverage file, and some simple structs and enums | |
// * CoveredObject - base class of the next three classes | |
// * CoveredProject - a collection of modules | |
// * CoveredModule - a collection of functions | |
// * CoveredFunction - holds coverage information about the function | |
// * ReportHelper - some utility methods | |
// After running the binary coverage file through covxml with the --file argument | |
// construct the model using an XmlDocument object | |
// Create one set of these models | |
CoverageDetails details = new CoverageDetails(xmlDocument); | |
CoveredProject project = new CoveredProject("name here", details); | |
// Add your modules to the project model, you'll need to load each module up from separate coverage files | |
project.AddModule(xmlDocument, "moduleName"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Text.RegularExpressions; | |
using System.Xml; | |
namespace Tsd.Tools.Build.TsdCover.Models | |
{ | |
public class CoverageDetails | |
{ | |
public Version CoverageVersion { get; private set; } | |
public Version ReportVersion { get; private set; } | |
public CoverageDetails(XmlDocument document) | |
{ | |
// <!-- BullseyeCoverage 7.13.25 Windows License xxxx --> | |
Regex version = new Regex(@"<!-- BullseyeCoverage (?<match>\d+\.\d+\.\d+) Windows License .*? -->"); | |
Match versionMatch = version.Match(document.InnerXml); | |
if (versionMatch.Groups["match"] != null) | |
{ | |
CoverageVersion = new Version(versionMatch.Groups["match"].Value); | |
} | |
//ReportVersion = new Version(Application.ProductVersion); | |
} | |
} | |
public enum CoveredCoditionalSequence | |
{ | |
None, | |
True, | |
False, | |
Full | |
} | |
public enum CoveredKind | |
{ | |
Function, | |
Decision, | |
Condition, | |
Try, | |
Catch, | |
Switch, | |
Constant | |
} | |
public class Probe | |
{ | |
public int Line; | |
public int Column; | |
public CoveredKind Kind; | |
public CoveredCoditionalSequence Covered; | |
} | |
public struct Metrics | |
{ | |
public int FunctionsCovered; | |
public int FunctionsTotal; | |
public int ConditionalsCovered; | |
public int ConditionalsTotal; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Xml; | |
namespace Tsd.Tools.Build.TsdCover.Models | |
{ | |
public class CoveredFunction : CoveredObject | |
{ | |
public string SourceFile { get; set; } | |
public override int ConditionalsCovered | |
{ | |
get | |
{ | |
int coveredConditionals = 0; | |
foreach (Probe probe in Probes) | |
{ | |
if ((probe.Kind == CoveredKind.Decision) || (probe.Kind == CoveredKind.Condition)) | |
{ | |
if ((probe.Covered == CoveredCoditionalSequence.False) || (probe.Covered == CoveredCoditionalSequence.True)) | |
coveredConditionals++; | |
else if (probe.Covered == CoveredCoditionalSequence.Full) | |
coveredConditionals += 2; | |
} | |
else if ((probe.Kind == CoveredKind.Try) || (probe.Kind == CoveredKind.Catch) || (probe.Kind == CoveredKind.Switch)) | |
{ | |
if (!(probe.Covered == CoveredCoditionalSequence.None)) | |
coveredConditionals++; | |
} | |
} | |
return coveredConditionals; | |
} | |
} | |
public override int ConditionalsTotal | |
{ | |
get | |
{ | |
int countConditionals = 0; | |
foreach (Probe probe in Probes) | |
{ | |
if ((probe.Kind == CoveredKind.Decision) || (probe.Kind == CoveredKind.Condition)) | |
countConditionals += 2; | |
else if ((probe.Kind == CoveredKind.Try) || (probe.Kind == CoveredKind.Catch) || (probe.Kind == CoveredKind.Switch)) | |
countConditionals++; | |
} | |
return countConditionals; | |
} | |
} | |
public override int FunctionsCovered | |
{ | |
get | |
{ | |
int coveredFunctions = 0; | |
foreach (Probe probe in Probes) | |
if (probe.Kind == CoveredKind.Function) | |
if (probe.Covered == CoveredCoditionalSequence.Full) | |
coveredFunctions++; | |
return coveredFunctions; | |
} | |
} | |
public override int FunctionsTotal | |
{ | |
get | |
{ | |
int countFunctions = 0; | |
foreach (Probe probe in Probes) | |
if (probe.Kind == CoveredKind.Function) | |
countFunctions++; | |
return countFunctions; | |
} | |
} | |
public List<Probe> Probes { get; set; } | |
public CoveredFunction(XmlNode node, string path, string sourcefile) | |
{ | |
SourceFile = sourcefile; | |
Path = path; | |
Probes = new List<Probe>(); | |
// Parse node | |
if (node.Name.ToLower() == "fn") | |
{ | |
if (node.Attributes["name"] != null) | |
{ | |
Name = node.Attributes["name"].Value; | |
} | |
if (node.Attributes != null) | |
{ | |
Metrics m; | |
m.FunctionsTotal = 0; | |
if (node.Attributes["fn_total"] != null) | |
int.TryParse(node.Attributes["fn_total"].Value, out m.FunctionsTotal); | |
m.FunctionsCovered = 0; | |
if (node.Attributes["fn_cov"] != null) | |
int.TryParse(node.Attributes["fn_cov"].Value, out m.FunctionsCovered); | |
m.ConditionalsTotal = 0; | |
if (node.Attributes["cd_total"] != null) | |
int.TryParse(node.Attributes["cd_total"].Value, out m.ConditionalsTotal); | |
m.ConditionalsCovered = 0; | |
if (node.Attributes["cd_cov"] != null) | |
int.TryParse(node.Attributes["cd_cov"].Value, out m.ConditionalsCovered); | |
Metrics = m; | |
} | |
foreach (XmlNode child in node.ChildNodes) | |
{ | |
if (child.Name.ToLower() == "probe") | |
{ | |
Probe newProbe = new Probe(); | |
if (child.Attributes["line"] != null) | |
int.TryParse(child.Attributes["line"].Value, out newProbe.Line); | |
if (child.Attributes["column"] != null) | |
int.TryParse(child.Attributes["column"].Value, out newProbe.Column); | |
if (child.Attributes["event"] != null) | |
{ | |
if (child.Attributes["event"].Value.ToLower() == "full") | |
newProbe.Covered = CoveredCoditionalSequence.Full; | |
else if (child.Attributes["event"].Value.ToLower() == "false") | |
newProbe.Covered = CoveredCoditionalSequence.False; | |
else if (child.Attributes["event"].Value.ToLower() == "true") | |
newProbe.Covered = CoveredCoditionalSequence.True; | |
else | |
newProbe.Covered = CoveredCoditionalSequence.None; | |
} | |
if (child.Attributes["kind"] != null) | |
{ | |
if (child.Attributes["kind"].Value.ToLower() == "decision") | |
newProbe.Kind = CoveredKind.Decision; | |
else if (child.Attributes["kind"].Value.ToLower() == "condition") | |
newProbe.Kind = CoveredKind.Condition; | |
else if (child.Attributes["kind"].Value.ToLower() == "function") | |
newProbe.Kind = CoveredKind.Function; | |
else if (child.Attributes["kind"].Value.ToLower() == "try") | |
newProbe.Kind = CoveredKind.Try; | |
else if (child.Attributes["kind"].Value.ToLower() == "catch") | |
newProbe.Kind = CoveredKind.Catch; | |
else if (child.Attributes["kind"].Value.ToLower() == "switch-label") | |
newProbe.Kind = CoveredKind.Switch; | |
else if (child.Attributes["kind"].Value.ToLower() == "constant") | |
newProbe.Kind = CoveredKind.Constant; | |
else | |
Console.WriteLine("[WARNING] Unsupported Probe Kind for Function: {0} = ({2}) [{1}]", Name, sourcefile, child.Attributes["kind"].Value.ToLower()); | |
} | |
Probes.Add(newProbe); | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Append Coverage information to the Function. (Adds the metrics if the functions match) | |
/// </summary> | |
/// <param name="function"></param> | |
public void AppendCoverage(CoveredFunction function) | |
{ | |
if ((function.Name.ToLower() == Name.ToLower()) && (function.Path.ToLower() == Path.ToLower())) | |
{ | |
// Check all Probes | |
foreach (Probe probe in function.Probes) | |
{ | |
bool found = false; | |
foreach (Probe probeExists in Probes) | |
{ | |
if ((probeExists.Line == probe.Line) && | |
(probeExists.Kind == probe.Kind) && | |
(probeExists.Column == probe.Column)) | |
{ | |
found = true; | |
if (probeExists.Covered < probe.Covered) | |
probeExists.Covered = probe.Covered; | |
break; | |
} | |
} | |
if (!found) | |
Probes.Add(probe); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Xml; | |
namespace Tsd.Tools.Build.TsdCover.Models | |
{ | |
public class CoveredModule : CoveredObject | |
{ | |
public List<CoveredFunction> Functions { get; set; } | |
public override int ConditionalsCovered | |
{ | |
get | |
{ | |
int countCovered = 0; | |
foreach (CoveredFunction function in Functions) | |
countCovered += function.ConditionalsCovered; | |
return countCovered; | |
} | |
} | |
public override int ConditionalsTotal | |
{ | |
get | |
{ | |
int countTotal = 0; | |
foreach (CoveredFunction function in Functions) | |
countTotal += function.ConditionalsTotal; | |
return countTotal; | |
} | |
} | |
public override int FunctionsCovered | |
{ | |
get | |
{ | |
int countCovered = 0; | |
foreach (CoveredFunction function in Functions) | |
countCovered += function.FunctionsCovered; | |
return countCovered; | |
} | |
} | |
public override int FunctionsTotal | |
{ | |
get | |
{ | |
int countTotal = 0; | |
foreach (CoveredFunction function in Functions) | |
countTotal += function.FunctionsTotal; | |
return countTotal; | |
} | |
} | |
public CoveredModule(XmlNode node, string name, string path) | |
{ | |
Name = name; | |
Path = path; | |
Functions = new List<CoveredFunction>(); | |
foreach (XmlNode srcchild in node.ChildNodes) | |
{ | |
if (srcchild.Name.ToLower() == "src") | |
{ | |
string sourceName = ""; | |
if (srcchild.Attributes["name"] != null) | |
sourceName = srcchild.Attributes["name"].Value; | |
foreach (XmlNode child in srcchild.ChildNodes) | |
{ | |
if (child.Name.ToLower() == "fn") | |
{ | |
CoveredFunction function = new CoveredFunction(child, Path + "\\" + Name, sourceName); | |
AppendFunction(function); | |
} | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Append Coverage information to a Function in the module that matches. | |
/// </summary> | |
/// <param name="function"></param> | |
public void AppendFunction(CoveredFunction function) | |
{ | |
foreach (CoveredFunction coveredFunction in Functions) | |
{ | |
if ((function.Name.ToLower() == coveredFunction.Name.ToLower()) && (function.Path.ToLower() == coveredFunction.Path.ToLower())) | |
{ | |
coveredFunction.AppendCoverage(function); | |
return; | |
} | |
} | |
Functions.Add(function); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Tsd.Tools.Build.TsdCover.Models | |
{ | |
public abstract class CoveredObject | |
{ | |
public virtual int FunctionsCovered { get; set; } | |
public virtual int FunctionsTotal { get; set; } | |
public virtual int ConditionalsCovered { get; set; } | |
public virtual int ConditionalsTotal { get; set; } | |
public virtual Metrics Metrics | |
{ | |
get | |
{ | |
Metrics metrics; | |
metrics.FunctionsCovered = FunctionsCovered; | |
metrics.FunctionsTotal = FunctionsTotal; | |
metrics.ConditionalsCovered = ConditionalsCovered; | |
metrics.ConditionalsTotal = ConditionalsTotal; | |
return metrics; | |
} | |
set | |
{ | |
FunctionsCovered = value.FunctionsCovered; | |
FunctionsTotal = value.FunctionsTotal; | |
ConditionalsCovered = value.ConditionalsCovered; | |
ConditionalsTotal = value.ConditionalsTotal; | |
} | |
} | |
public string Name { get; internal set; } | |
public virtual string Path { get; internal set; } | |
public virtual float FunctionsPercentCovered | |
{ | |
get | |
{ | |
if (FunctionsTotal == 0) | |
return 100; | |
return ((float)FunctionsCovered / (float)FunctionsTotal) * 100; | |
} | |
} | |
public virtual float ConditionalsPercentCovered | |
{ | |
get | |
{ | |
if (ConditionalsTotal == 0) | |
return 100; | |
return ((float)ConditionalsCovered / (float)ConditionalsTotal) * 100; | |
} | |
} | |
/// <summary> | |
/// Add Metrics to the Existing Metrics. | |
/// </summary> | |
/// <param name="metrics"></param> | |
public void AddMetrics(Metrics metrics) | |
{ | |
FunctionsCovered += metrics.FunctionsCovered; | |
FunctionsTotal += metrics.FunctionsTotal; | |
ConditionalsCovered += metrics.ConditionalsCovered; | |
ConditionalsTotal += metrics.ConditionalsTotal; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Xml; | |
namespace Tsd.Tools.Build.TsdCover.Models | |
{ | |
public class CoveredProject : CoveredObject | |
{ | |
public List<CoveredModule> Modules { get; set; } | |
public CoverageDetails Details { get; set; } | |
public override int FunctionsCovered | |
{ | |
get | |
{ | |
int count = 0; | |
foreach (CoveredModule module in Modules) | |
{ | |
count += module.FunctionsCovered; | |
} | |
return count; | |
} | |
} | |
public override int FunctionsTotal | |
{ | |
get | |
{ | |
int count = 0; | |
foreach (CoveredModule o in Modules) | |
{ | |
count += o.FunctionsTotal; | |
} | |
return count; | |
} | |
} | |
public override int ConditionalsCovered | |
{ | |
get | |
{ | |
int count = 0; | |
foreach (CoveredModule o in Modules) | |
{ | |
count += o.ConditionalsCovered; | |
} | |
return count; | |
} | |
} | |
public override int ConditionalsTotal | |
{ | |
get | |
{ | |
int count = 0; | |
foreach (CoveredModule o in Modules) | |
{ | |
count += o.ConditionalsTotal; | |
} | |
return count; | |
} | |
} | |
public CoveredProject(string name, CoverageDetails details) | |
{ | |
Name = name; | |
Modules = new List<CoveredModule>(); | |
Details = details; | |
} | |
/// <summary> | |
/// Add a module to the Project. | |
/// </summary> | |
/// <param name="file">Xml Data</param> | |
/// <param name="moduleName"></param> | |
public void AddModule(string file, string moduleName) | |
{ | |
XmlDocument document = new XmlDocument(); | |
document.Load(file); | |
AddModule(document, moduleName); | |
} | |
/// <summary> | |
/// Add a module to the Project. | |
/// </summary> | |
/// <param name="document"></param> | |
/// <param name="moduleName"></param> | |
public void AddModule(XmlDocument document, string moduleName) | |
{ | |
XmlNode sourceNode = ReportHelper.FindSourceNode(document); | |
if (sourceNode != null) | |
{ | |
AddModules(sourceNode, "..\\..", moduleName); | |
} | |
} | |
/// <summary> | |
/// Add multiple modules to the Project. | |
/// </summary> | |
/// <param name="node"></param> | |
/// <param name="parentPath"></param> | |
/// <param name="moduleName"></param> | |
private void AddModules(XmlNode node, string parentPath, string moduleName) | |
{ | |
if (node == null) | |
return; | |
CoveredModule temp; | |
string path = parentPath; | |
if (path != "") | |
path += "\\"; | |
if (((node.Name.ToLower() == "folder")) && (node.Attributes["name"] != null)) | |
{ | |
path += node.Attributes["name"].Value; | |
if (moduleName == node.Attributes["name"].Value) | |
{ | |
temp = new CoveredModule(node, node.Attributes["name"].Value, path); | |
if (node.Attributes != null) | |
{ | |
Metrics m; | |
m.FunctionsTotal = 0; | |
if (node.Attributes["fn_total"] != null) | |
int.TryParse(node.Attributes["fn_total"].Value, out m.FunctionsTotal); | |
m.FunctionsCovered = 0; | |
if (node.Attributes["fn_cov"] != null) | |
int.TryParse(node.Attributes["fn_cov"].Value, out m.FunctionsCovered); | |
m.ConditionalsTotal = 0; | |
if (node.Attributes["cd_total"] != null) | |
int.TryParse(node.Attributes["cd_total"].Value, out m.ConditionalsTotal); | |
m.ConditionalsCovered = 0; | |
if (node.Attributes["cd_cov"] != null) | |
int.TryParse(node.Attributes["cd_cov"].Value, out m.ConditionalsCovered); | |
temp.FunctionsCovered = m.FunctionsCovered; | |
temp.FunctionsTotal = m.FunctionsTotal; | |
temp.ConditionalsCovered = m.ConditionalsCovered; | |
temp.ConditionalsTotal = m.ConditionalsTotal; | |
} | |
if (ReportHelper.HasSourceNodes(node)) | |
{ | |
AppendObject(temp); | |
} | |
} | |
// No deeper then project | |
foreach (XmlNode child in node.ChildNodes) | |
{ | |
if (child.Name.ToLower() == "folder") | |
{ | |
AddModules(child, path, moduleName); | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Append Coverage information to an object in the project that matches. | |
/// </summary> | |
/// <param name="module"></param> | |
private void AppendObject(CoveredModule module) | |
{ | |
// Check if the object exists (Full Path Check) | |
CoveredObject found = null; | |
foreach (CoveredModule o in Modules) | |
{ | |
if (o.Path.ToLower() == module.Path.ToLower()) | |
{ | |
found = o; | |
break; | |
} | |
} | |
if (found == null) | |
{ | |
Modules.Add(module); | |
} | |
else | |
{ | |
found.AddMetrics(module.Metrics); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static void OutputTeamCityServiceMessages(IEnumerable<ModuleCoverage> modules) | |
{ | |
int methodTotal = modules.Sum(x => x.MethodTotal); | |
int methodCovered = modules.Sum(x => x.MethodCovered); | |
int branchTotal = modules.Sum(x => x.BranchTotal); | |
int branchCovered = modules.Sum(x => x.BranchCovered); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageM' value='{0}']", methodTotal > 0 ? methodCovered * 100.0 / methodTotal : 0); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsMCovered' value='{0}']", methodCovered); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsMTotal' value='{0}']", methodTotal); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageB' value='{0}']", branchTotal > 0 ? branchCovered * 100.0 / branchTotal : 0); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsBCovered' value='{0}']", branchCovered); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsBTotal' value='{0}']", branchTotal); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageL' value='100']"); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsLCovered' value='1']"); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsLTotal' value='1']"); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageC' value='100']"); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsCCovered' value='1']"); | |
Console.WriteLine("##teamcity[buildStatisticValue key='CodeCoverageAbsCTotal' value='1']"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Xml; | |
using System.ComponentModel; | |
namespace Tsd.Tools.Build.TsdCover | |
{ | |
public static class ReportHelper | |
{ | |
public static XmlNode FindSourceNode(XmlNode root) | |
{ | |
// Is this a Folder? | |
if (root.Name.ToLower() == "folder") | |
{ | |
// Is this in a .. Folder | |
if ((root.Attributes["name"] != null) && (root.Attributes["name"].Value.ToLower() == "..")) | |
{ | |
// Last Folder thats .. | |
foreach (XmlNode node in root.ChildNodes) | |
{ | |
// Include Directory is considered a seperate Node. | |
if ((node.Attributes["name"] != null) && (node.Attributes["name"].Value.ToLower() != "..") && (node.Attributes["name"].Value.ToLower() != "include")) | |
{ | |
return root; | |
} | |
} | |
} | |
} | |
XmlNode check; | |
foreach (XmlNode node in root.ChildNodes) | |
{ | |
check = FindSourceNode(node); | |
if (check != null) | |
return check; | |
} | |
return null; | |
} | |
public static XmlNode FindIncludeNode(XmlNode root) | |
{ | |
// Is this a Folder? | |
if (root.Name.ToLower() == "folder") | |
{ | |
// Is this in a .. Folder | |
if ((root.Attributes["name"] != null) && (root.Attributes["name"].Value.ToLower() == "include")) | |
{ | |
// First "include" folder | |
return root; | |
} | |
} | |
XmlNode check; | |
foreach (XmlNode node in root.ChildNodes) | |
{ | |
check = FindIncludeNode(node); | |
if (check != null) | |
return check; | |
} | |
return null; | |
} | |
public static bool IsTestNamed(string name) | |
{ | |
if (Regex.IsMatch(name, @"(UnitTests|.*?\.UnitTests|.*?_test)", RegexOptions.Singleline | RegexOptions.IgnoreCase)) | |
{ | |
return true; | |
} | |
return false; | |
} | |
public static bool HasSourceNodes(XmlNode node) | |
{ | |
foreach (XmlNode child in node.ChildNodes) | |
{ | |
if (child.Name.ToLower() == "src") | |
return true; | |
} | |
return false; | |
} | |
public static string CovToXml(string file) | |
{ | |
Process proc = new Process(); | |
proc.StartInfo.CreateNoWindow = true; | |
proc.StartInfo.FileName = "covxml"; | |
proc.StartInfo.Arguments = "--file \"" + file + "\""; | |
proc.StartInfo.UseShellExecute = false; | |
proc.StartInfo.RedirectStandardOutput = true; | |
try | |
{ | |
proc.Start(); | |
StringBuilder data = new StringBuilder(); | |
while (!proc.HasExited) | |
{ | |
data.Append(proc.StandardOutput.ReadToEnd()); | |
} | |
data.Append(proc.StandardOutput.ReadToEnd()); | |
return data.ToString(); | |
} | |
catch (Win32Exception ex) | |
{ | |
throw new Exception("Error converting Coverage file, ensure covxml is on the PATH."); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment