From 59c5af61e8d28c4cb6faddc0f58b57c5ea049e97 Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Wed, 15 May 2019 14:06:21 +0530 Subject: [PATCH 1/8] Coverlet DataCollectors Implementation --- coverlet.sln | 21 ++ .../DataCollector/AttachmentManager.cs | 156 +++++++++++++ .../DataCollector/CoverageManager.cs | 106 +++++++++ .../DataCollector/CoverageWrapper.cs | 55 +++++ .../CoverletCoverageDataCollector.cs | 179 +++++++++++++++ .../DataCollector/CoverletLogger.cs | 67 ++++++ .../DataCollector/CoverletSettings.cs | 84 +++++++ .../DataCollector/CoverletSettingsParser.cs | 207 ++++++++++++++++++ src/coverlet.collector/Friends.cs | 8 + .../Resources/Resources.Designer.cs | 117 ++++++++++ .../Resources/Resources.resx | 138 ++++++++++++ .../Utilities/CoverletConstants.cs | 23 ++ .../CoverletDataCollectorException.cs | 15 ++ .../Utilities/DirectoryHelper.cs | 27 +++ .../Utilities/FileHelper.cs | 21 ++ .../Utilities/Interfaces/ICoverageWrapper.cs | 36 +++ .../Utilities/Interfaces/IDirectoryHelper.cs | 28 +++ .../Utilities/Interfaces/IFileHelper.cs | 23 ++ .../Utilities/TestPlatformEqtTrace.cs | 43 ++++ .../Utilities/TestPlatformLogger.cs | 28 +++ .../coverlet.collector.csproj | 69 ++++++ .../coverlet.collector.nuspec | 21 ++ .../coverlet.collector.targets | 24 ++ .../CoverletInProcDataCollector.cs | 73 ++++++ .../coverlet.inproccollector.csproj | 15 ++ .../AttachmentManagerTests.cs | 110 ++++++++++ .../CoverletCoverageDataCollectorTests.cs | 135 ++++++++++++ .../CoverletSettingsParserTests.cs | 82 +++++++ .../coverlet.collector.tests.csproj | 25 +++ 29 files changed, 1936 insertions(+) create mode 100644 src/coverlet.collector/DataCollector/AttachmentManager.cs create mode 100644 src/coverlet.collector/DataCollector/CoverageManager.cs create mode 100644 src/coverlet.collector/DataCollector/CoverageWrapper.cs create mode 100644 src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs create mode 100644 src/coverlet.collector/DataCollector/CoverletLogger.cs create mode 100644 src/coverlet.collector/DataCollector/CoverletSettings.cs create mode 100644 src/coverlet.collector/DataCollector/CoverletSettingsParser.cs create mode 100644 src/coverlet.collector/Friends.cs create mode 100644 src/coverlet.collector/Resources/Resources.Designer.cs create mode 100644 src/coverlet.collector/Resources/Resources.resx create mode 100644 src/coverlet.collector/Utilities/CoverletConstants.cs create mode 100644 src/coverlet.collector/Utilities/CoverletDataCollectorException.cs create mode 100644 src/coverlet.collector/Utilities/DirectoryHelper.cs create mode 100644 src/coverlet.collector/Utilities/FileHelper.cs create mode 100644 src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs create mode 100644 src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs create mode 100644 src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs create mode 100644 src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs create mode 100644 src/coverlet.collector/Utilities/TestPlatformLogger.cs create mode 100644 src/coverlet.collector/coverlet.collector.csproj create mode 100644 src/coverlet.collector/coverlet.collector.nuspec create mode 100644 src/coverlet.collector/coverlet.collector.targets create mode 100644 src/coverlet.inproccollector/CoverletInProcDataCollector.cs create mode 100644 src/coverlet.inproccollector/coverlet.inproccollector.csproj create mode 100644 test/coverlet.collector.tests/AttachmentManagerTests.cs create mode 100644 test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs create mode 100644 test/coverlet.collector.tests/CoverletSettingsParserTests.cs create mode 100644 test/coverlet.collector.tests/coverlet.collector.tests.csproj diff --git a/coverlet.sln b/coverlet.sln index 7853fb0..91c97bc 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -19,6 +19,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.inproccollector", "src\coverlet.inproccollector\coverlet.inproccollector.csproj", "{C53F1496-65AE-415D-A46B-4BA1BC45709B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -49,6 +55,18 @@ Global {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU + {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Release|Any CPU.Build.0 = Release|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -60,6 +78,9 @@ Global {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {C53F1496-65AE-415D-A46B-4BA1BC45709B} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} diff --git a/src/coverlet.collector/DataCollector/AttachmentManager.cs b/src/coverlet.collector/DataCollector/AttachmentManager.cs new file mode 100644 index 0000000..bf6f44e --- /dev/null +++ b/src/coverlet.collector/DataCollector/AttachmentManager.cs @@ -0,0 +1,156 @@ +using System; +using System.ComponentModel; +using System.IO; +using coverlet.collector.Resources; +using Coverlet.Collector.Utilities; +using Coverlet.Collector.Utilities.Interfaces; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + +namespace Coverlet.Collector.DataCollector +{ + /// + /// Manages coverage report attachments + /// + internal class AttachmentManager : IDisposable + { + private readonly DataCollectionSink dataSink; + private readonly TestPlatformEqtTrace eqtTrace; + private readonly TestPlatformLogger logger; + private readonly DataCollectionContext dataCollectionContext; + private readonly IFileHelper fileHelper; + private readonly IDirectoryHelper directoryHelper; + private readonly string reportFileName; + private readonly string reportDirectory; + + public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName) + : this(dataSink, + dataCollectionContext, + logger, + eqtTrace, + reportFileName, + Guid.NewGuid().ToString(), + new FileHelper(), + new DirectoryHelper()) + { + } + + public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName, string reportDirectoryName, IFileHelper fileHelper, IDirectoryHelper directoryHelper) + { + // Store input vars + this.dataSink = dataSink; + this.dataCollectionContext = dataCollectionContext; + this.logger = logger; + this.eqtTrace = eqtTrace; + this.reportFileName = reportFileName; + this.fileHelper = fileHelper; + this.directoryHelper = directoryHelper; + + // Report directory to store the coverage reports. + this.reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName); + + // Register events + this.dataSink.SendFileCompleted += this.OnSendFileCompleted; + } + + /// + /// Sends coverage report to test platform + /// + /// Coverage report + public void SendCoverageReport(string coverageReport) + { + // Save coverage report to file + var coverageReportPath = this.SaveCoverageReport(coverageReport); + + // Send coverage attachment to test platform. + this.SendAttachment(coverageReportPath); + } + + /// + /// Disposes attachment manager + /// + public void Dispose() + { + // Unregister events + if (this.dataSink != null) + { + this.dataSink.SendFileCompleted -= this.OnSendFileCompleted; + } + } + + /// + /// Saves coverage report to file system + /// + /// Coverage report + /// Coverage report file path + private string SaveCoverageReport(string report) + { + try + { + this.directoryHelper.CreateDirectory(this.reportDirectory); + var filePath = Path.Combine(this.reportDirectory, this.reportFileName); + this.fileHelper.WriteAllText(filePath, report); + this.eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath); + + return filePath; + } + catch (Exception ex) + { + var errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, this.reportFileName, this.reportDirectory); + throw new CoverletDataCollectorException(errorMessage, ex); + } + } + + /// + /// SendFileCompleted event handler + /// + /// Sender + /// Event args + public void OnSendFileCompleted(object sender, AsyncCompletedEventArgs e) + { + try + { + this.eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName); + this.CleanupReportDirectory(); + } + catch (Exception ex) + { + this.logger.LogWarning(ex.ToString()); + this.Dispose(); + } + } + + /// + /// Sends attachment file to test platform + /// + /// Attachment file path + private void SendAttachment(string attachmentPath) + { + if (this.fileHelper.Exists(attachmentPath)) + { + // Send coverage attachment to test platform. + this.eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName); + this.dataSink.SendFileAsync(this.dataCollectionContext, attachmentPath, false); + } + } + + /// + /// Cleans up coverage report directory + /// + private void CleanupReportDirectory() + { + try + { + if (this.directoryHelper.Exists(this.reportDirectory)) + { + this.directoryHelper.Delete(this.reportDirectory, true); + this.eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, this.reportDirectory); + } + } + catch (Exception ex) + { + var errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, this.reportDirectory); + throw new CoverletDataCollectorException(errorMessage, ex); + } + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverageManager.cs b/src/coverlet.collector/DataCollector/CoverageManager.cs new file mode 100644 index 0000000..9be4abb --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverageManager.cs @@ -0,0 +1,106 @@ +using System; +using coverlet.collector.Resources; +using Coverlet.Collector.Utilities; +using Coverlet.Collector.Utilities.Interfaces; +using Coverlet.Core; +using Coverlet.Core.Logging; +using Coverlet.Core.Reporters; + +namespace Coverlet.Collector.DataCollector +{ + /// + /// Manages coverlet coverage + /// + internal class CoverageManager + { + private readonly Coverage coverage; + + private ICoverageWrapper coverageWrapper; + + public IReporter Reporter { get; } + + public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper) + : this(settings, + new ReporterFactory(settings.ReportFormat).CreateReporter(), + new CoverletLogger(eqtTrace, logger), + coverageWrapper) + { + } + + public CoverageManager(CoverletSettings settings, IReporter reporter, ILogger logger, ICoverageWrapper coverageWrapper) + { + // Store input vars + this.Reporter = reporter; + this.coverageWrapper = coverageWrapper; + + // Coverage object + this.coverage = this.coverageWrapper.CreateCoverage(settings, logger); + } + + /// + /// Instrument modules + /// + public void InstrumentModules() + { + try + { + // Instrument modules + this.coverageWrapper.PrepareModules(this.coverage); + } + catch (Exception ex) + { + var errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName); + throw new CoverletDataCollectorException(errorMessage, ex); + } + } + + /// + /// Gets coverlet coverage report + /// + /// Coverage report + public string GetCoverageReport() + { + // Get coverage result + var coverageResult = this.GetCoverageResult(); + + // Get coverage report in default format + var coverageReport = this.GetCoverageReport(coverageResult); + return coverageReport; + } + + /// + /// Gets coverlet coverage result + /// + /// Coverage result + private CoverageResult GetCoverageResult() + { + try + { + return this.coverageWrapper.GetCoverageResult(this.coverage); + } + catch (Exception ex) + { + var errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName); + throw new CoverletDataCollectorException(errorMessage, ex); + } + } + + /// + /// Gets coverage report from coverage result + /// + /// Coverage result + /// Coverage report + private string GetCoverageReport(CoverageResult coverageResult) + { + try + { + return this.Reporter.Report(coverageResult); + } + catch (Exception ex) + { + var errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName); + throw new CoverletDataCollectorException(errorMessage, ex); + } + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverageWrapper.cs b/src/coverlet.collector/DataCollector/CoverageWrapper.cs new file mode 100644 index 0000000..b8f2b32 --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverageWrapper.cs @@ -0,0 +1,55 @@ +using Coverlet.Collector.DataCollector; +using Coverlet.Collector.Utilities.Interfaces; +using Coverlet.Core; +using Coverlet.Core.Logging; + +namespace coverlet.collector.DataCollector +{ + /// + /// Implementation for wrapping over Coverage class in coverlet.core + /// + internal class CoverageWrapper : ICoverageWrapper + { + /// + /// Creates a coverage object from given coverlet settings + /// + /// Coverlet settings + /// Coverlet logger + /// Coverage object + public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger) + { + return new Coverage( + settings.TestModule, + settings.IncludeFilters, + settings.IncludeDirectories, + settings.ExcludeFilters, + settings.ExcludeSourceFiles, + settings.ExcludeAttributes, + settings.IncludeTestAssembly, + settings.SingleHit, + settings.MergeWith, + settings.UseSourceLink, + coverletLogger); + } + + /// + /// Gets the coverage result from provided coverage object + /// + /// Coverage + /// The coverage result + public CoverageResult GetCoverageResult(Coverage coverage) + { + return coverage.GetCoverageResult(); + } + + /// + /// Prepares modules for getting coverage. + /// Wrapper over coverage.PrepareModules + /// + /// + public void PrepareModules(Coverage coverage) + { + coverage.PrepareModules(); + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs b/src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs new file mode 100644 index 0000000..705c9a3 --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs @@ -0,0 +1,179 @@ +namespace Coverlet.Collector.DataCollector +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Xml; + using coverlet.collector.DataCollector; + using Coverlet.Collector.Utilities; + using Coverlet.Collector.Utilities.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + + /// + /// Coverlet coverage out-proc data collector. + /// + [DataCollectorTypeUri(CoverletConstants.DefaultUri)] + [DataCollectorFriendlyName(CoverletConstants.FriendlyName)] + public class CoverletCoverageCollector : DataCollector + { + private readonly TestPlatformEqtTrace eqtTrace; + private DataCollectionEvents events; + private TestPlatformLogger logger; + private XmlElement configurationElement; + private DataCollectionSink dataSink; + private DataCollectionContext dataCollectionContext; + private CoverageManager coverageManager; + private ICoverageWrapper coverageWrapper; + + public CoverletCoverageCollector() : this(new TestPlatformEqtTrace(), new CoverageWrapper()) + { + } + + internal CoverletCoverageCollector(TestPlatformEqtTrace eqtTrace, ICoverageWrapper coverageWrapper) : base() + { + this.eqtTrace = eqtTrace; + this.coverageWrapper = coverageWrapper; + } + + /// + /// Initializes data collector + /// + /// Configuration element + /// Events to register on + /// Data sink to send attachments to test platform + /// Test platform logger + /// Environment context + public override void Initialize( + XmlElement configurationElement, + DataCollectionEvents events, + DataCollectionSink dataSink, + DataCollectionLogger logger, + DataCollectionEnvironmentContext environmentContext) + { + if (this.eqtTrace.IsInfoEnabled) + { + this.eqtTrace.Info("Initializing {0} with configuration: '{1}'", CoverletConstants.DataCollectorName, configurationElement?.OuterXml); + } + + // Store input variables + this.events = events; + this.configurationElement = configurationElement; + this.dataSink = dataSink; + this.dataCollectionContext = environmentContext.SessionDataCollectionContext; + this.logger = new TestPlatformLogger(logger, this.dataCollectionContext); + + // Register events + this.events.SessionStart += this.OnSessionStart; + this.events.SessionEnd += this.OnSessionEnd; + } + + /// + /// Disposes the data collector + /// + /// Disposing flag + protected override void Dispose(bool disposing) + { + this.eqtTrace.Verbose("{0}: Disposing", CoverletConstants.DataCollectorName); + + // Unregister events + if (this.events != null) + { + this.events.SessionStart -= this.OnSessionStart; + this.events.SessionEnd -= this.OnSessionEnd; + } + + // Remove vars + this.events = null; + this.dataSink = null; + this.coverageManager = null; + + base.Dispose(disposing); + } + + /// + /// SessionStart event handler + /// + /// Sender + /// Event args + private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEventArgs) + { + this.eqtTrace.Verbose("{0}: SessionStart received", CoverletConstants.DataCollectorName); + + try + { + // Get coverlet settings + IEnumerable testModules = this.GetTestModules(sessionStartEventArgs); + var coverletSettingsParser = new CoverletSettingsParser(this.eqtTrace); + var coverletSettings = coverletSettingsParser.Parse(this.configurationElement, testModules); + + // Get coverage and attachment managers + this.coverageManager = new CoverageManager(coverletSettings, this.eqtTrace, this.logger, this.coverageWrapper); + + // Instrument modules + this.coverageManager.InstrumentModules(); + } + catch (Exception ex) + { + this.logger.LogWarning(ex.ToString()); + this.Dispose(true); + } + } + + /// + /// SessionEnd event handler + /// + /// Sender + /// Event args + private void OnSessionEnd(object sender, SessionEndEventArgs e) + { + try + { + this.eqtTrace.Verbose("{0}: SessionEnd received", CoverletConstants.DataCollectorName); + + // Get coverage reports + var coverageReport = this.coverageManager?.GetCoverageReport(); + + // Send result attachments to test platform. + using (var attachmentManager = new AttachmentManager(dataSink, this.dataCollectionContext, this.logger, this.eqtTrace, this.GetReportFileName())) + { + attachmentManager?.SendCoverageReport(coverageReport); + } + } + catch (Exception ex) + { + this.logger.LogWarning(ex.ToString()); + this.Dispose(true); + } + } + + /// + /// Gets coverage report file name + /// + /// Coverage report file name + private string GetReportFileName() + { + var fileName = CoverletConstants.DefaultFileName; + var extension = this.coverageManager?.Reporter.Extension; + + return extension == null ? fileName : $"{fileName}.{extension}"; + } + + /// + /// Gets test modules + /// + /// Event args + /// Test modules list + private IEnumerable GetTestModules(SessionStartEventArgs sessionStartEventArgs) + { + var testModules = sessionStartEventArgs.GetPropertyValue>(CoverletConstants.TestSourcesPropertyName); + if (this.eqtTrace.IsInfoEnabled) + { + this.eqtTrace.Info("{0}: TestModules: '{1}'", + CoverletConstants.DataCollectorName, + string.Join(",", testModules ?? Enumerable.Empty())); + } + + return testModules; + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverletLogger.cs b/src/coverlet.collector/DataCollector/CoverletLogger.cs new file mode 100644 index 0000000..9deb6bf --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverletLogger.cs @@ -0,0 +1,67 @@ +using System; +using Coverlet.Collector.Utilities; +using Coverlet.Core.Logging; + +namespace Coverlet.Collector.DataCollector +{ + /// + /// Coverlet logger + /// + internal class CoverletLogger : ILogger + { + private readonly TestPlatformEqtTrace eqtTrace; + private readonly TestPlatformLogger logger; + + public CoverletLogger(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger) + { + this.eqtTrace = eqtTrace; + this.logger = logger; + } + + /// + /// Logs error + /// + /// Error message + public void LogError(string message) + { + this.logger.LogWarning(message); + } + + /// + /// Logs error + /// + /// Exception to log + public void LogError(Exception exception) + { + this.logger.LogWarning(exception.ToString()); + } + + /// + /// Logs information + /// + /// Information message + /// importance + public void LogInformation(string message, bool important = false) + { + this.eqtTrace.Info(message); + } + + /// + /// Logs verbose + /// + /// Verbose message + public void LogVerbose(string message) + { + this.eqtTrace.Verbose(message); + } + + /// + /// Logs warning + /// + /// Warning message + public void LogWarning(string message) + { + this.eqtTrace.Warning(message); + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverletSettings.cs b/src/coverlet.collector/DataCollector/CoverletSettings.cs new file mode 100644 index 0000000..a31374b --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverletSettings.cs @@ -0,0 +1,84 @@ +using System.Linq; +using System.Text; + +namespace Coverlet.Collector.DataCollector +{ + /// + /// Coverlet settings + /// + internal class CoverletSettings + { + /// + /// Test module + /// + public string TestModule { get; set; } + + /// + /// Report format + /// + public string ReportFormat { get; set; } + + /// + /// Filters to include + /// + public string[] IncludeFilters { get; set; } + + /// + /// Directories to include + /// + public string[] IncludeDirectories { get; set; } + + /// + /// Filters to exclude + /// + public string[] ExcludeFilters { get; set; } + + /// + /// Source files to exclude + /// + public string[] ExcludeSourceFiles { get; set; } + + /// + /// Attributes to exclude + /// + public string[] ExcludeAttributes { get; set; } + + /// + /// Coverate report path to merge with + /// + public string MergeWith { get; set; } + + /// + /// Use source link flag + /// + public bool UseSourceLink { get; set; } + + /// + /// Single hit flag + /// + public bool SingleHit { get; set; } + + /// + /// Includes test assembly + /// + public bool IncludeTestAssembly { get; set; } + + public override string ToString() + { + var builder = new StringBuilder(); + + builder.AppendFormat("TestModule: '{0}', ", this.TestModule); + builder.AppendFormat("IncludeFilters: '{0}', ", string.Join(",", this.IncludeFilters ?? Enumerable.Empty())); + builder.AppendFormat("IncludeDirectories: '{0}', ", string.Join(",", this.IncludeDirectories ?? Enumerable.Empty())); + builder.AppendFormat("ExcludeFilters: '{0}', ", string.Join(",", this.ExcludeFilters ?? Enumerable.Empty())); + builder.AppendFormat("ExcludeSourceFiles: '{0}', ", string.Join(",", this.ExcludeSourceFiles ?? Enumerable.Empty())); + builder.AppendFormat("ExcludeAttributes: '{0}', ", string.Join(",", this.ExcludeAttributes ?? Enumerable.Empty())); + builder.AppendFormat("MergeWith: '{0}', ", this.MergeWith); + builder.AppendFormat("UseSourceLink: '{0}'", this.UseSourceLink); + builder.AppendFormat("SingleHit: '{0}'", this.SingleHit); + builder.AppendFormat("IncludeTestAssembly: '{0}'", this.IncludeTestAssembly); + + return builder.ToString(); + } + } +} diff --git a/src/coverlet.collector/DataCollector/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollector/CoverletSettingsParser.cs new file mode 100644 index 0000000..937ecaf --- /dev/null +++ b/src/coverlet.collector/DataCollector/CoverletSettingsParser.cs @@ -0,0 +1,207 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using coverlet.collector.Resources; +using Coverlet.Collector.Utilities; + +namespace Coverlet.Collector.DataCollector +{ + /// + /// Coverlet settings parser + /// + internal class CoverletSettingsParser + { + private readonly TestPlatformEqtTrace eqtTrace; + + public CoverletSettingsParser(TestPlatformEqtTrace eqtTrace) + { + this.eqtTrace = eqtTrace; + } + + /// + /// Parser coverlet settings + /// + /// Configuration element + /// Test modules + /// Coverlet settings + public CoverletSettings Parse(XmlElement configurationElement, IEnumerable testModules) + { + var coverletSettings = new CoverletSettings + { + TestModule = this.ParseTestModule(testModules) + }; + + if (configurationElement != null) + { + + coverletSettings.IncludeFilters = this.ParseIncludeFilters(configurationElement); + coverletSettings.IncludeDirectories = this.ParseIncludeDirectories(configurationElement); + coverletSettings.ExcludeAttributes = this.ParseExcludeAttributes(configurationElement); + coverletSettings.ExcludeSourceFiles = this.ParseExcludeSourceFiles(configurationElement); + coverletSettings.MergeWith = this.ParseMergeWith(configurationElement); + coverletSettings.UseSourceLink = this.ParseUseSourceLink(configurationElement); + coverletSettings.SingleHit = this.ParseSingleHit(configurationElement); + coverletSettings.IncludeTestAssembly = this.ParseIncludeTestAssembly(configurationElement); + } + + coverletSettings.ReportFormat = this.ParseReportFormat(configurationElement); + coverletSettings.ExcludeFilters = this.ParseExcludeFilters(configurationElement); + + if (this.eqtTrace.IsVerboseEnabled) + { + this.eqtTrace.Verbose("{0}: Initializing coverlet process with settings: \"{1}\"", CoverletConstants.DataCollectorName, coverletSettings.ToString()); + } + + return coverletSettings; + } + + /// + /// Parses test module + /// + /// Test modules + /// Test module + private string ParseTestModule(IEnumerable testModules) + { + // Validate if atleast one source present. + if (testModules == null || !testModules.Any()) + { + var errorMessage = string.Format(Resources.NoTestModulesFound, CoverletConstants.DataCollectorName); + throw new CoverletDataCollectorException(errorMessage); + } + + // Note: + // 1) .NET core test run supports one testModule per run. Coverlet also supports one testModule per run. So, we are using first testSource only and ignoring others. + // 2) If and when .NET full is supported with coverlet OR .NET core starts supporting multiple testModules, revisit this code to use other testModules as well. + return testModules.FirstOrDefault(); + } + + /// + /// Parse report format + /// + /// Configuration element + /// Report format + private string ParseReportFormat(XmlElement configurationElement) + { + string format = string.Empty; + if (configurationElement != null) + { + var reportFormat = configurationElement[CoverletConstants.ReportFormatElementName]; + format = reportFormat?.InnerText?.Split(',').FirstOrDefault(); + } + return string.IsNullOrEmpty(format) ? CoverletConstants.DefaultReportFormat : format; + } + + /// + /// Parse filters to include + /// + /// Configuration element + /// Filters to include + private string[] ParseIncludeFilters(XmlElement configurationElement) + { + var includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName]; + return includeFiltersElement?.InnerText?.Split(','); + } + + /// + /// Parse directories to include + /// + /// Configuration element + /// Directories to include + private string[] ParseIncludeDirectories(XmlElement configurationElement) + { + var includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName]; + return includeDirectoriesElement?.InnerText?.Split(','); + } + + /// + /// Parse filters to exclude + /// + /// Configuration element + /// Filters to exclude + private string[] ParseExcludeFilters(XmlElement configurationElement) + { + var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter }; + + if (configurationElement != null) + { + var excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName]; + var filters = excludeFiltersElement?.InnerText?.Split(','); + if (filters != null) + { + excludeFilters.AddRange(filters); + } + } + + return excludeFilters.ToArray(); + } + + /// + /// Parse source files to exclude + /// + /// Configuration element + /// Source files to exclude + private string[] ParseExcludeSourceFiles(XmlElement configurationElement) + { + var excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName]; + return excludeSourceFilesElement?.InnerText?.Split(','); + } + + /// + /// Parse attributes to exclude + /// + /// Configuration element + /// Attributes to exclude + private string[] ParseExcludeAttributes(XmlElement configurationElement) + { + var excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName]; + return excludeAttributesElement?.InnerText?.Split(','); + } + + /// + /// Parse merge with attribute + /// + /// Configuration element + /// Merge with attribute + private string ParseMergeWith(XmlElement configurationElement) + { + var mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName]; + return mergeWithElement?.InnerText; + } + + /// + /// Parse use source link flag + /// + /// Configuration element + /// Use source link flag + private bool ParseUseSourceLink(XmlElement configurationElement) + { + var useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName]; + bool.TryParse(useSourceLinkElement?.InnerText, out var useSourceLink); + return useSourceLink; + } + + /// + /// Parse single hit flag + /// + /// Configuration element + /// Single hit flag + private bool ParseSingleHit(XmlElement configurationElement) + { + var singleHitElement = configurationElement[CoverletConstants.SingleHitElementName]; + bool.TryParse(singleHitElement?.InnerText, out var singleHit); + return singleHit; + } + + /// + /// Parse include test assembly flag + /// + /// Configuration element + /// Include Test Assembly Flag + private bool ParseIncludeTestAssembly(XmlElement configurationElement) + { + var includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName]; + bool.TryParse(includeTestAssemblyElement?.InnerText, out var includeTestAssembly); + return includeTestAssembly; + } + } +} diff --git a/src/coverlet.collector/Friends.cs b/src/coverlet.collector/Friends.cs new file mode 100644 index 0000000..5de27ab --- /dev/null +++ b/src/coverlet.collector/Friends.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; + +#region Test Assemblies + +[assembly: InternalsVisibleTo("coverlet.collector.tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] + +#endregion \ No newline at end of file diff --git a/src/coverlet.collector/Resources/Resources.Designer.cs b/src/coverlet.collector/Resources/Resources.Designer.cs new file mode 100644 index 0000000..7988746 --- /dev/null +++ b/src/coverlet.collector/Resources/Resources.Designer.cs @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace coverlet.collector.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("coverlet.collector.Resources.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to {0}: Failed to get coverage report. + /// + internal static string CoverageReportException { + get { + return ResourceManager.GetString("CoverageReportException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: Failed to get coverage result. + /// + internal static string CoverageResultException { + get { + return ResourceManager.GetString("CoverageResultException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: Failed to cleanup report directory: '{1}'. + /// + internal static string FailedToCleanupReportDirectory { + get { + return ResourceManager.GetString("FailedToCleanupReportDirectory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: Failed to save coverage report '{1}' in directory '{2}'. + /// + internal static string FailedToSaveCoverageReport { + get { + return ResourceManager.GetString("FailedToSaveCoverageReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: Failed to instrument modules. + /// + internal static string InstrumentationException { + get { + return ResourceManager.GetString("InstrumentationException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: No test modules found. + /// + internal static string NoTestModulesFound { + get { + return ResourceManager.GetString("NoTestModulesFound", resourceCulture); + } + } + } +} diff --git a/src/coverlet.collector/Resources/Resources.resx b/src/coverlet.collector/Resources/Resources.resx new file mode 100644 index 0000000..5fc47d0 --- /dev/null +++ b/src/coverlet.collector/Resources/Resources.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0}: Failed to get coverage report + + + {0}: Failed to get coverage result + + + {0}: Failed to cleanup report directory: '{1}' + + + {0}: Failed to save coverage report '{1}' in directory '{2}' + + + {0}: Failed to instrument modules + + + {0}: No test modules found + + \ No newline at end of file diff --git a/src/coverlet.collector/Utilities/CoverletConstants.cs b/src/coverlet.collector/Utilities/CoverletConstants.cs new file mode 100644 index 0000000..837d12a --- /dev/null +++ b/src/coverlet.collector/Utilities/CoverletConstants.cs @@ -0,0 +1,23 @@ +namespace Coverlet.Collector.Utilities +{ + internal static class CoverletConstants + { + public const string FriendlyName = "XPlat code coverage"; + public const string DefaultUri = @"datacollector://Microsoft/CoverletCodeCoverage/1.0"; + public const string DataCollectorName = "CoverletCoverageDataCollector"; + public const string DefaultReportFormat = "cobertura"; + public const string DefaultFileName = "coverage"; + public const string IncludeFiltersElementName = "Include"; + public const string IncludeDirectoriesElementName = "IncludeDirectory"; + public const string ExcludeFiltersElementName = "Exclude"; + public const string ExcludeSourceFilesElementName = "ExcludeByFile"; + public const string ExcludeAttributesElementName = "ExcludeByAttribute"; + public const string MergeWithElementName = "MergeWith"; + public const string UseSourceLinkElementName = "UseSourceLink"; + public const string SingleHitElementName = "SingleHit"; + public const string IncludeTestAssemblyElementName = "IncludeTestAssembly"; + public const string TestSourcesPropertyName = "TestSources"; + public const string ReportFormatElementName = "Format"; + public const string DefaultExcludeFilter = "[coverlet.*]*"; + } +} diff --git a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs new file mode 100644 index 0000000..9168794 --- /dev/null +++ b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs @@ -0,0 +1,15 @@ +namespace Coverlet.Collector.Utilities +{ + using System; + + internal class CoverletDataCollectorException : Exception + { + public CoverletDataCollectorException(string message) : base(message) + { + } + + public CoverletDataCollectorException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/coverlet.collector/Utilities/DirectoryHelper.cs b/src/coverlet.collector/Utilities/DirectoryHelper.cs new file mode 100644 index 0000000..d03d212 --- /dev/null +++ b/src/coverlet.collector/Utilities/DirectoryHelper.cs @@ -0,0 +1,27 @@ +using System.IO; +using Coverlet.Collector.Utilities.Interfaces; + +namespace Coverlet.Collector.Utilities +{ + /// + internal class DirectoryHelper : IDirectoryHelper + { + /// + public bool Exists(string path) + { + return Directory.Exists(path); + } + + /// + public void CreateDirectory(string path) + { + Directory.CreateDirectory(path); + } + + /// + public void Delete(string path, bool recursive) + { + Directory.Delete(path, recursive); + } + } +} diff --git a/src/coverlet.collector/Utilities/FileHelper.cs b/src/coverlet.collector/Utilities/FileHelper.cs new file mode 100644 index 0000000..f6e5783 --- /dev/null +++ b/src/coverlet.collector/Utilities/FileHelper.cs @@ -0,0 +1,21 @@ +using System.IO; +using Coverlet.Collector.Utilities.Interfaces; + +namespace Coverlet.Collector.Utilities +{ + /// + internal class FileHelper : IFileHelper + { + /// + public bool Exists(string path) + { + return File.Exists(path); + } + + /// + public void WriteAllText(string path, string contents) + { + File.WriteAllText(path, contents); + } + } +} diff --git a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs new file mode 100644 index 0000000..dd3be35 --- /dev/null +++ b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs @@ -0,0 +1,36 @@ +using Coverlet.Collector.DataCollector; +using Coverlet.Core; +using Coverlet.Core.Logging; + +namespace Coverlet.Collector.Utilities.Interfaces +{ + /// + /// Wrapper interface for Coverage class in coverlet.core + /// Since the class is not testable, this interface is used to abstract methods for mocking in unit tests. + /// + internal interface ICoverageWrapper + { + /// + /// Creates a coverage object from given coverlet settings + /// + /// Coverlet settings + /// Coverlet logger + /// Coverage object + Coverage CreateCoverage(CoverletSettings settings, ILogger logger); + + /// + /// Gets the coverage result from provided coverage object + /// + /// Coverage + /// The coverage result + CoverageResult GetCoverageResult(Coverage coverage); + + /// + /// Prepares modules for getting coverage. + /// Wrapper over coverage.PrepareModules + /// + /// + void PrepareModules(Coverage coverage); + + } +} diff --git a/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs new file mode 100644 index 0000000..8e26c0d --- /dev/null +++ b/src/coverlet.collector/Utilities/Interfaces/IDirectoryHelper.cs @@ -0,0 +1,28 @@ +namespace Coverlet.Collector.Utilities.Interfaces +{ + interface IDirectoryHelper + { + /// + /// Determines whether the specified directory exists. + /// + /// The directory to check. + /// true if the caller has the required permissions and path contains the name of an existing directory; otherwise, false. + /// This method also returns false if path is null, an invalid path, or a zero-length string. + /// If the caller does not have sufficient permissions to read the specified file, + /// no exception is thrown and the method returns false regardless of the existence of path. + bool Exists(string path); + + /// + /// Creates all directories and subdirectories in the specified path unless they already exist. + /// + /// The directory to create. + void CreateDirectory(string directory); + + /// + /// Deletes the specified directory and, if indicated, any subdirectories and files in the directory. + /// + /// The name of the directory to remove. + /// true to remove directories, subdirectories, and files in path; otherwise, false. + void Delete(string path, bool recursive); + } +} diff --git a/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs new file mode 100644 index 0000000..8fd0ab9 --- /dev/null +++ b/src/coverlet.collector/Utilities/Interfaces/IFileHelper.cs @@ -0,0 +1,23 @@ +namespace Coverlet.Collector.Utilities.Interfaces +{ + internal interface IFileHelper + { + /// + /// Determines whether the specified file exists. + /// + /// The file to check. + /// true if the caller has the required permissions and path contains the name of an existing file; otherwise, false. + /// This method also returns false if path is null, an invalid path, or a zero-length string. + /// If the caller does not have sufficient permissions to read the specified file, + /// no exception is thrown and the method returns false regardless of the existence of path. + bool Exists(string path); + + /// + /// Creates a new file, writes the specified string to the file, and then closes the file. + /// If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The string to write to the file. + void WriteAllText(string path, string contents); + } +} diff --git a/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs new file mode 100644 index 0000000..9fcf62c --- /dev/null +++ b/src/coverlet.collector/Utilities/TestPlatformEqtTrace.cs @@ -0,0 +1,43 @@ +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Coverlet.Collector.Utilities +{ + /// + /// Test platform eqttrace + /// + internal class TestPlatformEqtTrace + { + public bool IsInfoEnabled => EqtTrace.IsInfoEnabled; + public bool IsVerboseEnabled => EqtTrace.IsVerboseEnabled; + + /// + /// Verbose logger + /// + /// Format + /// Args + public void Verbose(string format, params object[] args) + { + EqtTrace.Verbose(format, args); + } + + /// + /// Warning logger + /// + /// Format + /// Args + public void Warning(string format, params object[] args) + { + EqtTrace.Warning(format, args); + } + + /// + /// Info logger + /// + /// Format + /// Args + public void Info(string format, params object[] args) + { + EqtTrace.Info(format, args); + } + } +} diff --git a/src/coverlet.collector/Utilities/TestPlatformLogger.cs b/src/coverlet.collector/Utilities/TestPlatformLogger.cs new file mode 100644 index 0000000..60b5271 --- /dev/null +++ b/src/coverlet.collector/Utilities/TestPlatformLogger.cs @@ -0,0 +1,28 @@ +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + +namespace Coverlet.Collector.Utilities +{ + /// + /// Test platform logger + /// + internal class TestPlatformLogger + { + private readonly DataCollectionLogger logger; + private readonly DataCollectionContext dataCollectionContext; + + public TestPlatformLogger(DataCollectionLogger logger, DataCollectionContext dataCollectionContext) + { + this.logger = logger; + this.dataCollectionContext = dataCollectionContext; + } + + /// + /// Log warning + /// + /// Warning message + public void LogWarning(string warning) + { + this.logger.LogWarning(this.dataCollectionContext, warning); + } + } +} diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj new file mode 100644 index 0000000..c9c1e36 --- /dev/null +++ b/src/coverlet.collector/coverlet.collector.csproj @@ -0,0 +1,69 @@ + + + + netcoreapp2.0 + coverlet.collector + 1.0.0 + + coverlet.collector + $(AssemblyVersion) + coverlet.collector + tonerdo + MIT + http://github.com/tonerdo/coverlet + https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true + false + true + Coverlet is a cross platform code coverage library for .NET, with support for line, branch and method coverage. + coverage testing unit-test lcov opencover quality + git + https://github.com/tonerdo/coverlet + true + + + + + + + + + + + + + True + True + Resources.resx + + + True + True + Resources.resx + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + ResXFileCodeGenerator + Resource.Designer.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + diff --git a/src/coverlet.collector/coverlet.collector.nuspec b/src/coverlet.collector/coverlet.collector.nuspec new file mode 100644 index 0000000..cfde3ff --- /dev/null +++ b/src/coverlet.collector/coverlet.collector.nuspec @@ -0,0 +1,21 @@ + + + + coverlet.collector + 1.0.0 + coverlet.collector + tonerdo + tonerdo + false + https://github.com/tonerdo/coverlet/blob/master/LICENSE + https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true + Coverlet is a cross platform code coverage library for .NET, with support for line, branch and method coverage. + http://github.com/tonerdo/coverlet + coverage testing unit-test lcov opencover quality + + + + + + + \ No newline at end of file diff --git a/src/coverlet.collector/coverlet.collector.targets b/src/coverlet.collector/coverlet.collector.targets new file mode 100644 index 0000000..cc4376d --- /dev/null +++ b/src/coverlet.collector/coverlet.collector.targets @@ -0,0 +1,24 @@ + + + + + + + + + + + + + $(VSTestTestAdapterPath);$(MSBuildThisFileDirectory) + + + diff --git a/src/coverlet.inproccollector/CoverletInProcDataCollector.cs b/src/coverlet.inproccollector/CoverletInProcDataCollector.cs new file mode 100644 index 0000000..1c053bd --- /dev/null +++ b/src/coverlet.inproccollector/CoverletInProcDataCollector.cs @@ -0,0 +1,73 @@ +using System; +using System.Reflection; +using Coverlet.Core.Instrumentation; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollector.InProcDataCollector; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.InProcDataCollector; + +namespace Coverlet.InProcDataCollector +{ + public class CoverletInProcDataCollector : InProcDataCollection + { + public void Initialize(IDataCollectionSink dataCollectionSink) + { + } + + public void TestCaseEnd(TestCaseEndArgs testCaseEndArgs) + { + } + + public void TestCaseStart(TestCaseStartArgs testCaseStartArgs) + { + } + + public void TestSessionEnd(TestSessionEndArgs testSessionEndArgs) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Type injectedInstrumentationClass = GetInstrumentationClass(assembly); + if (injectedInstrumentationClass is null) + continue; + + var unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) }); + if (unloadModule is null) + continue; + + try + { + unloadModule.Invoke(null, new[] { null, EventArgs.Empty }); + } + catch + { + // Ignore exceptions and continue with the unload + } + } + } + + public void TestSessionStart(TestSessionStartArgs testSessionStartArgs) + { + } + + private static Type GetInstrumentationClass(Assembly assembly) + { + try + { + foreach (var type in assembly.GetTypes()) + { + if (type.Namespace == "Coverlet.Core.Instrumentation.Tracker" + && type.Name.StartsWith(assembly.GetName().Name + "_")) + { + return type; + } + } + + return null; + } + catch + { + // Avoid crashing if reflection fails + return null; + } + } + } +} diff --git a/src/coverlet.inproccollector/coverlet.inproccollector.csproj b/src/coverlet.inproccollector/coverlet.inproccollector.csproj new file mode 100644 index 0000000..d2fb005 --- /dev/null +++ b/src/coverlet.inproccollector/coverlet.inproccollector.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.0 + + + + + + + + + + + diff --git a/test/coverlet.collector.tests/AttachmentManagerTests.cs b/test/coverlet.collector.tests/AttachmentManagerTests.cs new file mode 100644 index 0000000..9641dc6 --- /dev/null +++ b/test/coverlet.collector.tests/AttachmentManagerTests.cs @@ -0,0 +1,110 @@ +using System; +using System.ComponentModel; +using System.IO; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; +using Xunit; +using Moq; +using Coverlet.Collector.DataCollector; +using Coverlet.Collector.Utilities; +using Coverlet.Collector.Utilities.Interfaces; + +namespace Coverlet.Collector.Tests +{ + public class AttachmentManagerTests + { + private AttachmentManager attachmentManager; + private Mock mockDataCollectionSink; + private DataCollectionContext dataCollectionContext; + private TestPlatformLogger testPlatformLogger; + private TestPlatformEqtTrace eqtTrace; + private Mock mockFileHelper; + private Mock mockDirectoryHelper; + private Mock mockDataCollectionLogger; + + public AttachmentManagerTests() + { + this.mockDataCollectionSink = new Mock(); + this.mockDataCollectionLogger = new Mock(); + TestCase testcase = new TestCase { Id = Guid.NewGuid() }; + this.dataCollectionContext = new DataCollectionContext(testcase); + this.testPlatformLogger = new TestPlatformLogger(this.mockDataCollectionLogger.Object, this.dataCollectionContext); + this.eqtTrace = new TestPlatformEqtTrace(); + this.mockFileHelper = new Mock(); + this.mockDirectoryHelper = new Mock(); + + this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, + this.eqtTrace, "report.cobertura.xml", @"E:\temp", this.mockFileHelper.Object, this.mockDirectoryHelper.Object); + } + + [Fact] + public void SendCoverageReportShouldSaveReportToFile() + { + string coverageReport = "" + + "" + + "" + + "" + + ""; + + this.attachmentManager.SendCoverageReport(coverageReport); + this.mockFileHelper.Verify(x => x.WriteAllText(@"E:\temp\report.cobertura.xml", coverageReport), Times.Once); + } + + [Fact] + public void SendCoverageReportShouldThrowExceptionWhenFailedToSaveReportToFile() + { + this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, + this.eqtTrace, null, @"E:\temp", this.mockFileHelper.Object, this.mockDirectoryHelper.Object); + + string coverageReport = "" + + "" + + "" + + "" + + ""; + + var message = Assert.Throws(() => this.attachmentManager.SendCoverageReport(coverageReport)).Message; + Assert.Equal("CoverletCoverageDataCollector: Failed to save coverage report '' in directory 'E:\\temp'", message); + } + + [Fact] + public void SendCoverageReportShouldSendAttachmentToTestPlatform() + { + var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); + this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, + this.eqtTrace, "report.cobertura.xml", directory.ToString(), new FileHelper(), this.mockDirectoryHelper.Object); + + string coverageReport = "" + + "" + + "" + + "" + + ""; + + this.attachmentManager.SendCoverageReport(coverageReport); + + this.mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny())); + + directory.Delete(true); + } + + [Fact] + public void OnSendFileCompletedShouldCleanUpReportDirectory() + { + this.mockDirectoryHelper.Setup(x => x.Exists(@"E:\temp")).Returns(true); + + this.mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); + + this.mockDirectoryHelper.Verify(x => x.Delete(@"E:\temp", true), Times.Once); + } + + [Fact] + public void OnSendFileCompletedShouldThrowCoverletDataCollectorExceptionIfUnableToCleanUpReportDirectory() + { + this.mockDirectoryHelper.Setup(x => x.Exists(@"E:\temp")).Returns(true); + this.mockDirectoryHelper.Setup(x => x.Delete(@"E:\temp", true)).Throws(new FileNotFoundException()); + + this.mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); + this.mockDataCollectionLogger.Verify(x => x.LogWarning(this.dataCollectionContext, + It.Is(y => y.Contains("CoverletDataCollectorException: CoverletCoverageDataCollector: Failed to cleanup report directory"))), Times.Once); + } + } +} diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs new file mode 100644 index 0000000..d6ee7d2 --- /dev/null +++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; +using Moq; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Coverlet.Core; +using Coverlet.Core.Logging; +using Coverlet.Collector.DataCollector; +using Coverlet.Collector.Utilities.Interfaces; +using Coverlet.Collector.Utilities; +using coverlet.collector.DataCollector; +using Xunit; + +namespace Coverlet.Collector.Tests +{ + public class CoverletCoverageDataCollectorTests + { + private DataCollectionEnvironmentContext context; + private CoverletCoverageCollector coverletCoverageDataCollector; + private DataCollectionContext dataCollectionContext; + private Mock mockDataColectionEvents; + private Mock mockDataCollectionSink; + private Mock mockCoverageWrapper; + private XmlElement configurationElement; + private Mock mockLogger; + + public CoverletCoverageDataCollectorTests() + { + this.mockDataColectionEvents = new Mock(); + this.mockDataCollectionSink = new Mock(); + this.mockLogger = new Mock(); + this.configurationElement = null; + + TestCase testcase = new TestCase { Id = Guid.NewGuid() }; + this.dataCollectionContext = new DataCollectionContext(testcase); + this.context = new DataCollectionEnvironmentContext(this.dataCollectionContext); + this.mockCoverageWrapper = new Mock(); + } + + [Fact] + public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings() + { + coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); + coverletCoverageDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + this.mockLogger.Object, + this.context); + IDictionary sessionStartProperties = new Dictionary(); + + sessionStartProperties.Add("TestSources", new List { "abc.dll" }); + + this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + + this.mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => string.Equals(y.TestModule, "abc.dll")), It.IsAny()), Times.Once); + } + + [Fact] + public void OnSessionStartShouldPrepareModulesForCoverage() + { + coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); + coverletCoverageDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + null, + this.context); + IDictionary sessionStartProperties = new Dictionary(); + Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny()); + + sessionStartProperties.Add("TestSources", new List { "abc.dll" }); + this.mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny())).Returns(coverage); + + this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + + this.mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => y.TestModule.Contains("abc.dll")), It.IsAny()), Times.Once); + this.mockCoverageWrapper.Verify(x => x.PrepareModules(It.IsAny()), Times.Once); + } + + [Fact] + public void OnSessionEndShouldSendGetCoverageReportToTestPlatform() + { + coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper()); + coverletCoverageDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + this.mockLogger.Object, + this.context); + + string module = GetType().Assembly.Location; + string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); + + var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); + + File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); + File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); + + IDictionary sessionStartProperties = new Dictionary(); + sessionStartProperties.Add("TestSources", new List { Path.Combine(directory.FullName, Path.GetFileName(module)) }); + + this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + this.mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); + + this.mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Once); + + directory.Delete(true); + } + + [Fact] + public void OnSessionStartShouldLogWarningIfInstrumentationFailed() + { + coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); + coverletCoverageDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + this.mockLogger.Object, + this.context); + IDictionary sessionStartProperties = new Dictionary(); + + sessionStartProperties.Add("TestSources", new List { "abc.dll" }); + + this.mockCoverageWrapper.Setup(x => x.PrepareModules(It.IsAny())).Throws(new FileNotFoundException()); + + this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + + this.mockLogger.Verify(x => x.LogWarning(this.dataCollectionContext, + It.Is(y => y.Contains("CoverletDataCollectorException")))); + } + } +} diff --git a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs new file mode 100644 index 0000000..1d0b5d1 --- /dev/null +++ b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using Coverlet.Collector.DataCollector; +using Coverlet.Collector.Utilities; +using Xunit; + +namespace Coverlet.Collector.Tests +{ + public class CoverletSettingsParserTests + { + private CoverletSettingsParser coverletSettingsParser; + + public CoverletSettingsParserTests() + { + this.coverletSettingsParser = new CoverletSettingsParser(new TestPlatformEqtTrace()); + } + + [Fact] + public void ParseShouldThrowCoverletDataCollectorExceptionIfTestModulesIsNull() + { + var message = Assert.Throws(() => this.coverletSettingsParser.Parse(null, null)).Message; + + Assert.Equal("CoverletCoverageDataCollector: No test modules found", message); + } + + [Fact] + public void ParseShouldThrowCoverletDataCollectorExceptionIfTestModulesIsEmpty() + { + var message = Assert.Throws(() => this.coverletSettingsParser.Parse(null, Enumerable.Empty())).Message; + + Assert.Equal("CoverletCoverageDataCollector: No test modules found", message); + } + + [Fact] + public void ParseShouldSelectFirstTestModuleFromTestModulesList() + { + var testModules = new List { "module1.dll", "module2.dll", "module3.dll" }; + + var coverletSettings = this.coverletSettingsParser.Parse(null, testModules); + + Assert.Equal("module1.dll", coverletSettings.TestModule); + } + + [Fact] + public void ParseShouldCorrectlyParseConfigurationElement() + { + var testModules = new List { "abc.dll" }; + var doc = new XmlDocument(); + var configElement = doc.CreateElement("Configuration"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeFiltersElementName, "[*]*"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeFiltersElementName, "[coverlet.*.tests?]*"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.IncludeDirectoriesElementName, @"E:\temp"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeSourceFilesElementName, "module1.cs,module2.cs"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.ExcludeAttributesElementName, "Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.MergeWithElementName, "/path/to/result.json"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.UseSourceLinkElementName, "false"); + this.CreateCoverletNodes(doc, configElement, CoverletConstants.SingleHitElementName, "true"); + + var coverletSettings = this.coverletSettingsParser.Parse(configElement, testModules); + + Assert.Equal("abc.dll", coverletSettings.TestModule); + Assert.Equal("[*]*", coverletSettings.IncludeFilters[0]); + Assert.Equal(@"E:\temp", coverletSettings.IncludeDirectories[0]); + Assert.Equal("module1.cs", coverletSettings.ExcludeSourceFiles[0]); + Assert.Equal("module2.cs", coverletSettings.ExcludeSourceFiles[1]); + Assert.Equal("Obsolete", coverletSettings.ExcludeAttributes[0]); + Assert.Equal("GeneratedCodeAttribute", coverletSettings.ExcludeAttributes[1]); + Assert.Equal("/path/to/result.json", coverletSettings.MergeWith); + Assert.Equal("[coverlet.*]*", coverletSettings.ExcludeFilters[0]); + Assert.False(coverletSettings.UseSourceLink); + Assert.True(coverletSettings.SingleHit); + } + + private void CreateCoverletNodes(XmlDocument doc, XmlElement configElement, string nodeSetting, string nodeValue) + { + var node = doc.CreateNode("element", nodeSetting, string.Empty); + node.InnerText = nodeValue; + configElement.AppendChild(node); + } + } +} diff --git a/test/coverlet.collector.tests/coverlet.collector.tests.csproj b/test/coverlet.collector.tests/coverlet.collector.tests.csproj new file mode 100644 index 0000000..dce1752 --- /dev/null +++ b/test/coverlet.collector.tests/coverlet.collector.tests.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.2 + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + From f5ffe46be906482edf561c9de44ad9f29f9245c0 Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Wed, 15 May 2019 14:29:30 +0530 Subject: [PATCH 2/8] Modifying build.proj for including collectors build and test --- build.proj | 3 +++ src/coverlet.collector/coverlet.collector.nuspec | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build.proj b/build.proj index 9eb9361..b73737b 100644 --- a/build.proj +++ b/build.proj @@ -9,6 +9,7 @@ + @@ -25,11 +26,13 @@ + + diff --git a/src/coverlet.collector/coverlet.collector.nuspec b/src/coverlet.collector/coverlet.collector.nuspec index cfde3ff..7da3d08 100644 --- a/src/coverlet.collector/coverlet.collector.nuspec +++ b/src/coverlet.collector/coverlet.collector.nuspec @@ -7,15 +7,15 @@ tonerdo tonerdo false - https://github.com/tonerdo/coverlet/blob/master/LICENSE + MIT https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true Coverlet is a cross platform code coverage library for .NET, with support for line, branch and method coverage. http://github.com/tonerdo/coverlet coverage testing unit-test lcov opencover quality - - + + \ No newline at end of file From c2d30e084854d662b4a193def2ce3f80f3e0509b Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Thu, 16 May 2019 15:02:32 +0530 Subject: [PATCH 3/8] Review comments addressed --- coverlet.sln | 7 -- .../AttachmentManager.cs | 84 +++++++------ .../CoverageManager.cs | 30 ++--- .../CoverageWrapper.cs | 5 +- .../CoverletCoverageCollector.cs} | 104 ++++++++-------- .../CoverletLogger.cs | 20 ++-- .../CoverletSettings.cs | 2 +- .../CoverletSettingsParser.cs | 53 ++++----- .../CoverletInProcDataCollector.cs | 2 +- .../CoverletDataCollectorException.cs | 6 +- .../Utilities/Interfaces/ICoverageWrapper.cs | 2 +- .../Utilities/TestPlatformLogger.cs | 10 +- .../coverlet.collector.csproj | 1 - .../coverlet.inproccollector.csproj | 15 --- .../AttachmentManagerTests.cs | 74 ++++++------ .../CoverletCoverageDataCollectorTests.cs | 111 +++++++++--------- .../CoverletSettingsParserTests.cs | 14 +-- 17 files changed, 262 insertions(+), 278 deletions(-) rename src/coverlet.collector/{DataCollector => DataCollection}/AttachmentManager.cs (55%) rename src/coverlet.collector/{DataCollector => DataCollection}/CoverageManager.cs (70%) rename src/coverlet.collector/{DataCollector => DataCollection}/CoverageWrapper.cs (92%) rename src/coverlet.collector/{DataCollector/CoverletCoverageDataCollector.cs => DataCollection/CoverletCoverageCollector.cs} (56%) rename src/coverlet.collector/{DataCollector => DataCollection}/CoverletLogger.cs (75%) rename src/coverlet.collector/{DataCollector => DataCollection}/CoverletSettings.cs (98%) rename src/coverlet.collector/{DataCollector => DataCollection}/CoverletSettingsParser.cs (75%) rename src/{coverlet.inproccollector => coverlet.collector/InProcDataCollection}/CoverletInProcDataCollector.cs (97%) delete mode 100644 src/coverlet.inproccollector/coverlet.inproccollector.csproj diff --git a/coverlet.sln b/coverlet.sln index 91c97bc..8c6728c 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -21,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancete EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.inproccollector", "src\coverlet.inproccollector\coverlet.inproccollector.csproj", "{C53F1496-65AE-415D-A46B-4BA1BC45709B}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}" EndProject Global @@ -59,10 +57,6 @@ Global {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU - {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C53F1496-65AE-415D-A46B-4BA1BC45709B}.Release|Any CPU.Build.0 = Release|Any CPU {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -79,7 +73,6 @@ Global {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} - {C53F1496-65AE-415D-A46B-4BA1BC45709B} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/coverlet.collector/DataCollector/AttachmentManager.cs b/src/coverlet.collector/DataCollection/AttachmentManager.cs similarity index 55% rename from src/coverlet.collector/DataCollector/AttachmentManager.cs rename to src/coverlet.collector/DataCollection/AttachmentManager.cs index bf6f44e..b8b4b20 100644 --- a/src/coverlet.collector/DataCollector/AttachmentManager.cs +++ b/src/coverlet.collector/DataCollection/AttachmentManager.cs @@ -6,21 +6,21 @@ using Coverlet.Collector.Utilities; using Coverlet.Collector.Utilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; -namespace Coverlet.Collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Manages coverage report attachments /// internal class AttachmentManager : IDisposable { - private readonly DataCollectionSink dataSink; - private readonly TestPlatformEqtTrace eqtTrace; - private readonly TestPlatformLogger logger; - private readonly DataCollectionContext dataCollectionContext; - private readonly IFileHelper fileHelper; - private readonly IDirectoryHelper directoryHelper; - private readonly string reportFileName; - private readonly string reportDirectory; + private readonly DataCollectionSink _dataSink; + private readonly TestPlatformEqtTrace _eqtTrace; + private readonly TestPlatformLogger _logger; + private readonly DataCollectionContext _dataCollectionContext; + private readonly IFileHelper _fileHelper; + private readonly IDirectoryHelper _directoryHelper; + private readonly string _reportFileName; + private readonly string _reportDirectory; public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName) : this(dataSink, @@ -36,20 +36,20 @@ namespace Coverlet.Collector.DataCollector public AttachmentManager(DataCollectionSink dataSink, DataCollectionContext dataCollectionContext, TestPlatformLogger logger, TestPlatformEqtTrace eqtTrace, string reportFileName, string reportDirectoryName, IFileHelper fileHelper, IDirectoryHelper directoryHelper) { - // Store input vars - this.dataSink = dataSink; - this.dataCollectionContext = dataCollectionContext; - this.logger = logger; - this.eqtTrace = eqtTrace; - this.reportFileName = reportFileName; - this.fileHelper = fileHelper; - this.directoryHelper = directoryHelper; + // Store input variabless + _dataSink = dataSink; + _dataCollectionContext = dataCollectionContext; + _logger = logger; + _eqtTrace = eqtTrace; + _reportFileName = reportFileName; + _fileHelper = fileHelper; + _directoryHelper = directoryHelper; // Report directory to store the coverage reports. - this.reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName); + _reportDirectory = Path.Combine(Path.GetTempPath(), reportDirectoryName); // Register events - this.dataSink.SendFileCompleted += this.OnSendFileCompleted; + _dataSink.SendFileCompleted += this.OnSendFileCompleted; } /// @@ -59,7 +59,7 @@ namespace Coverlet.Collector.DataCollector public void SendCoverageReport(string coverageReport) { // Save coverage report to file - var coverageReportPath = this.SaveCoverageReport(coverageReport); + string coverageReportPath = this.SaveCoverageReport(coverageReport); // Send coverage attachment to test platform. this.SendAttachment(coverageReportPath); @@ -71,9 +71,17 @@ namespace Coverlet.Collector.DataCollector public void Dispose() { // Unregister events - if (this.dataSink != null) + try { - this.dataSink.SendFileCompleted -= this.OnSendFileCompleted; + if (_dataSink != null) + { + _dataSink.SendFileCompleted -= this.OnSendFileCompleted; + } + this.CleanupReportDirectory(); + } + catch (Exception ex) + { + _logger.LogWarning(ex.ToString()); } } @@ -86,16 +94,16 @@ namespace Coverlet.Collector.DataCollector { try { - this.directoryHelper.CreateDirectory(this.reportDirectory); - var filePath = Path.Combine(this.reportDirectory, this.reportFileName); - this.fileHelper.WriteAllText(filePath, report); - this.eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath); + _directoryHelper.CreateDirectory(_reportDirectory); + string filePath = Path.Combine(_reportDirectory, _reportFileName); + _fileHelper.WriteAllText(filePath, report); + _eqtTrace.Info("{0}: Saved coverage report to path: '{1}'", CoverletConstants.DataCollectorName, filePath); return filePath; } catch (Exception ex) { - var errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, this.reportFileName, this.reportDirectory); + string errorMessage = string.Format(Resources.FailedToSaveCoverageReport, CoverletConstants.DataCollectorName, _reportFileName, _reportDirectory); throw new CoverletDataCollectorException(errorMessage, ex); } } @@ -109,12 +117,12 @@ namespace Coverlet.Collector.DataCollector { try { - this.eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName); + _eqtTrace.Verbose("{0}: SendFileCompleted received", CoverletConstants.DataCollectorName); this.CleanupReportDirectory(); } catch (Exception ex) { - this.logger.LogWarning(ex.ToString()); + _logger.LogWarning(ex.ToString()); this.Dispose(); } } @@ -125,11 +133,15 @@ namespace Coverlet.Collector.DataCollector /// Attachment file path private void SendAttachment(string attachmentPath) { - if (this.fileHelper.Exists(attachmentPath)) + if (_fileHelper.Exists(attachmentPath)) { // Send coverage attachment to test platform. - this.eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName); - this.dataSink.SendFileAsync(this.dataCollectionContext, attachmentPath, false); + _eqtTrace.Verbose("{0}: Sending attachment to test platform", CoverletConstants.DataCollectorName); + _dataSink.SendFileAsync(_dataCollectionContext, attachmentPath, false); + } + else + { + _eqtTrace.Warning("{0}: Attachment file does not exist", CoverletConstants.DataCollectorName); } } @@ -140,15 +152,15 @@ namespace Coverlet.Collector.DataCollector { try { - if (this.directoryHelper.Exists(this.reportDirectory)) + if (_directoryHelper.Exists(_reportDirectory)) { - this.directoryHelper.Delete(this.reportDirectory, true); - this.eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, this.reportDirectory); + _directoryHelper.Delete(_reportDirectory, true); + _eqtTrace.Verbose("{0}: Deleted report directory: '{1}'", CoverletConstants.DataCollectorName, _reportDirectory); } } catch (Exception ex) { - var errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, this.reportDirectory); + string errorMessage = string.Format(Resources.FailedToCleanupReportDirectory, CoverletConstants.DataCollectorName, _reportDirectory); throw new CoverletDataCollectorException(errorMessage, ex); } } diff --git a/src/coverlet.collector/DataCollector/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs similarity index 70% rename from src/coverlet.collector/DataCollector/CoverageManager.cs rename to src/coverlet.collector/DataCollection/CoverageManager.cs index 9be4abb..d02aa21 100644 --- a/src/coverlet.collector/DataCollector/CoverageManager.cs +++ b/src/coverlet.collector/DataCollection/CoverageManager.cs @@ -6,18 +6,18 @@ using Coverlet.Core; using Coverlet.Core.Logging; using Coverlet.Core.Reporters; -namespace Coverlet.Collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Manages coverlet coverage /// internal class CoverageManager { - private readonly Coverage coverage; + private readonly Coverage _coverage; - private ICoverageWrapper coverageWrapper; + private ICoverageWrapper _coverageWrapper; - public IReporter Reporter { get; } + public IReporter _reporter { get; } public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper) : this(settings, @@ -30,11 +30,11 @@ namespace Coverlet.Collector.DataCollector public CoverageManager(CoverletSettings settings, IReporter reporter, ILogger logger, ICoverageWrapper coverageWrapper) { // Store input vars - this.Reporter = reporter; - this.coverageWrapper = coverageWrapper; + _reporter = reporter; + _coverageWrapper = coverageWrapper; // Coverage object - this.coverage = this.coverageWrapper.CreateCoverage(settings, logger); + _coverage = _coverageWrapper.CreateCoverage(settings, logger); } /// @@ -45,11 +45,11 @@ namespace Coverlet.Collector.DataCollector try { // Instrument modules - this.coverageWrapper.PrepareModules(this.coverage); + _coverageWrapper.PrepareModules(_coverage); } catch (Exception ex) { - var errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName); + string errorMessage = string.Format(Resources.InstrumentationException, CoverletConstants.DataCollectorName); throw new CoverletDataCollectorException(errorMessage, ex); } } @@ -61,10 +61,10 @@ namespace Coverlet.Collector.DataCollector public string GetCoverageReport() { // Get coverage result - var coverageResult = this.GetCoverageResult(); + CoverageResult coverageResult = this.GetCoverageResult(); // Get coverage report in default format - var coverageReport = this.GetCoverageReport(coverageResult); + string coverageReport = this.GetCoverageReport(coverageResult); return coverageReport; } @@ -76,11 +76,11 @@ namespace Coverlet.Collector.DataCollector { try { - return this.coverageWrapper.GetCoverageResult(this.coverage); + return _coverageWrapper.GetCoverageResult(_coverage); } catch (Exception ex) { - var errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName); + string errorMessage = string.Format(Resources.CoverageResultException, CoverletConstants.DataCollectorName); throw new CoverletDataCollectorException(errorMessage, ex); } } @@ -94,11 +94,11 @@ namespace Coverlet.Collector.DataCollector { try { - return this.Reporter.Report(coverageResult); + return _reporter.Report(coverageResult); } catch (Exception ex) { - var errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName); + string errorMessage = string.Format(Resources.CoverageReportException, CoverletConstants.DataCollectorName); throw new CoverletDataCollectorException(errorMessage, ex); } } diff --git a/src/coverlet.collector/DataCollector/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs similarity index 92% rename from src/coverlet.collector/DataCollector/CoverageWrapper.cs rename to src/coverlet.collector/DataCollection/CoverageWrapper.cs index b8f2b32..76eece4 100644 --- a/src/coverlet.collector/DataCollector/CoverageWrapper.cs +++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs @@ -1,9 +1,8 @@ -using Coverlet.Collector.DataCollector; -using Coverlet.Collector.Utilities.Interfaces; +using Coverlet.Collector.Utilities.Interfaces; using Coverlet.Core; using Coverlet.Core.Logging; -namespace coverlet.collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Implementation for wrapping over Coverage class in coverlet.core diff --git a/src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs similarity index 56% rename from src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs rename to src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs index 705c9a3..9f1f953 100644 --- a/src/coverlet.collector/DataCollector/CoverletCoverageDataCollector.cs +++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs @@ -1,14 +1,13 @@ -namespace Coverlet.Collector.DataCollector -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Xml; - using coverlet.collector.DataCollector; - using Coverlet.Collector.Utilities; - using Coverlet.Collector.Utilities.Interfaces; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using Coverlet.Collector.Utilities; +using Coverlet.Collector.Utilities.Interfaces; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; +namespace Coverlet.Collector.DataCollection +{ /// /// Coverlet coverage out-proc data collector. /// @@ -16,14 +15,14 @@ [DataCollectorFriendlyName(CoverletConstants.FriendlyName)] public class CoverletCoverageCollector : DataCollector { - private readonly TestPlatformEqtTrace eqtTrace; - private DataCollectionEvents events; - private TestPlatformLogger logger; - private XmlElement configurationElement; - private DataCollectionSink dataSink; - private DataCollectionContext dataCollectionContext; - private CoverageManager coverageManager; - private ICoverageWrapper coverageWrapper; + private readonly TestPlatformEqtTrace _eqtTrace; + private DataCollectionEvents _events; + private TestPlatformLogger _logger; + private XmlElement _configurationElement; + private DataCollectionSink _dataSink; + private DataCollectionContext _dataCollectionContext; + private CoverageManager _coverageManager; + private ICoverageWrapper _coverageWrapper; public CoverletCoverageCollector() : this(new TestPlatformEqtTrace(), new CoverageWrapper()) { @@ -31,8 +30,8 @@ internal CoverletCoverageCollector(TestPlatformEqtTrace eqtTrace, ICoverageWrapper coverageWrapper) : base() { - this.eqtTrace = eqtTrace; - this.coverageWrapper = coverageWrapper; + _eqtTrace = eqtTrace; + _coverageWrapper = coverageWrapper; } /// @@ -50,21 +49,21 @@ DataCollectionLogger logger, DataCollectionEnvironmentContext environmentContext) { - if (this.eqtTrace.IsInfoEnabled) + if (_eqtTrace.IsInfoEnabled) { - this.eqtTrace.Info("Initializing {0} with configuration: '{1}'", CoverletConstants.DataCollectorName, configurationElement?.OuterXml); + _eqtTrace.Info("Initializing {0} with configuration: '{1}'", CoverletConstants.DataCollectorName, configurationElement?.OuterXml); } // Store input variables - this.events = events; - this.configurationElement = configurationElement; - this.dataSink = dataSink; - this.dataCollectionContext = environmentContext.SessionDataCollectionContext; - this.logger = new TestPlatformLogger(logger, this.dataCollectionContext); + _events = events; + _configurationElement = configurationElement; + _dataSink = dataSink; + _dataCollectionContext = environmentContext.SessionDataCollectionContext; + _logger = new TestPlatformLogger(logger, _dataCollectionContext); // Register events - this.events.SessionStart += this.OnSessionStart; - this.events.SessionEnd += this.OnSessionEnd; + _events.SessionStart += OnSessionStart; + _events.SessionEnd += OnSessionEnd; } /// @@ -73,19 +72,19 @@ /// Disposing flag protected override void Dispose(bool disposing) { - this.eqtTrace.Verbose("{0}: Disposing", CoverletConstants.DataCollectorName); + _eqtTrace.Verbose("{0}: Disposing", CoverletConstants.DataCollectorName); // Unregister events - if (this.events != null) + if (_events != null) { - this.events.SessionStart -= this.OnSessionStart; - this.events.SessionEnd -= this.OnSessionEnd; + _events.SessionStart -= OnSessionStart; + _events.SessionEnd -= OnSessionEnd; } // Remove vars - this.events = null; - this.dataSink = null; - this.coverageManager = null; + _events = null; + _dataSink = null; + _coverageManager = null; base.Dispose(disposing); } @@ -97,24 +96,24 @@ /// Event args private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEventArgs) { - this.eqtTrace.Verbose("{0}: SessionStart received", CoverletConstants.DataCollectorName); + _eqtTrace.Verbose("{0}: SessionStart received", CoverletConstants.DataCollectorName); try { // Get coverlet settings IEnumerable testModules = this.GetTestModules(sessionStartEventArgs); - var coverletSettingsParser = new CoverletSettingsParser(this.eqtTrace); - var coverletSettings = coverletSettingsParser.Parse(this.configurationElement, testModules); + var coverletSettingsParser = new CoverletSettingsParser(_eqtTrace); + CoverletSettings coverletSettings = coverletSettingsParser.Parse(_configurationElement, testModules); // Get coverage and attachment managers - this.coverageManager = new CoverageManager(coverletSettings, this.eqtTrace, this.logger, this.coverageWrapper); + _coverageManager = new CoverageManager(coverletSettings, _eqtTrace, _logger, _coverageWrapper); // Instrument modules - this.coverageManager.InstrumentModules(); + _coverageManager.InstrumentModules(); } catch (Exception ex) { - this.logger.LogWarning(ex.ToString()); + _logger.LogWarning(ex.ToString()); this.Dispose(true); } } @@ -128,20 +127,19 @@ { try { - this.eqtTrace.Verbose("{0}: SessionEnd received", CoverletConstants.DataCollectorName); + _eqtTrace.Verbose("{0}: SessionEnd received", CoverletConstants.DataCollectorName); // Get coverage reports - var coverageReport = this.coverageManager?.GetCoverageReport(); + string coverageReport = _coverageManager?.GetCoverageReport(); // Send result attachments to test platform. - using (var attachmentManager = new AttachmentManager(dataSink, this.dataCollectionContext, this.logger, this.eqtTrace, this.GetReportFileName())) - { - attachmentManager?.SendCoverageReport(coverageReport); - } + var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, this.GetReportFileName()); + attachmentManager?.SendCoverageReport(coverageReport); + } catch (Exception ex) { - this.logger.LogWarning(ex.ToString()); + _logger.LogWarning(ex.ToString()); this.Dispose(true); } } @@ -152,8 +150,8 @@ /// Coverage report file name private string GetReportFileName() { - var fileName = CoverletConstants.DefaultFileName; - var extension = this.coverageManager?.Reporter.Extension; + string fileName = CoverletConstants.DefaultFileName; + string extension = _coverageManager?._reporter.Extension; return extension == null ? fileName : $"{fileName}.{extension}"; } @@ -166,9 +164,9 @@ private IEnumerable GetTestModules(SessionStartEventArgs sessionStartEventArgs) { var testModules = sessionStartEventArgs.GetPropertyValue>(CoverletConstants.TestSourcesPropertyName); - if (this.eqtTrace.IsInfoEnabled) + if (_eqtTrace.IsInfoEnabled) { - this.eqtTrace.Info("{0}: TestModules: '{1}'", + _eqtTrace.Info("{0}: TestModules: '{1}'", CoverletConstants.DataCollectorName, string.Join(",", testModules ?? Enumerable.Empty())); } diff --git a/src/coverlet.collector/DataCollector/CoverletLogger.cs b/src/coverlet.collector/DataCollection/CoverletLogger.cs similarity index 75% rename from src/coverlet.collector/DataCollector/CoverletLogger.cs rename to src/coverlet.collector/DataCollection/CoverletLogger.cs index 9deb6bf..8304e2c 100644 --- a/src/coverlet.collector/DataCollector/CoverletLogger.cs +++ b/src/coverlet.collector/DataCollection/CoverletLogger.cs @@ -2,20 +2,20 @@ using Coverlet.Collector.Utilities; using Coverlet.Core.Logging; -namespace Coverlet.Collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Coverlet logger /// internal class CoverletLogger : ILogger { - private readonly TestPlatformEqtTrace eqtTrace; - private readonly TestPlatformLogger logger; + private readonly TestPlatformEqtTrace _eqtTrace; + private readonly TestPlatformLogger _logger; public CoverletLogger(TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger) { - this.eqtTrace = eqtTrace; - this.logger = logger; + _eqtTrace = eqtTrace; + _logger = logger; } /// @@ -24,7 +24,7 @@ namespace Coverlet.Collector.DataCollector /// Error message public void LogError(string message) { - this.logger.LogWarning(message); + _logger.LogWarning(message); } /// @@ -33,7 +33,7 @@ namespace Coverlet.Collector.DataCollector /// Exception to log public void LogError(Exception exception) { - this.logger.LogWarning(exception.ToString()); + _logger.LogWarning(exception.ToString()); } /// @@ -43,7 +43,7 @@ namespace Coverlet.Collector.DataCollector /// importance public void LogInformation(string message, bool important = false) { - this.eqtTrace.Info(message); + _eqtTrace.Info(message); } /// @@ -52,7 +52,7 @@ namespace Coverlet.Collector.DataCollector /// Verbose message public void LogVerbose(string message) { - this.eqtTrace.Verbose(message); + _eqtTrace.Verbose(message); } /// @@ -61,7 +61,7 @@ namespace Coverlet.Collector.DataCollector /// Warning message public void LogWarning(string message) { - this.eqtTrace.Warning(message); + _eqtTrace.Warning(message); } } } diff --git a/src/coverlet.collector/DataCollector/CoverletSettings.cs b/src/coverlet.collector/DataCollection/CoverletSettings.cs similarity index 98% rename from src/coverlet.collector/DataCollector/CoverletSettings.cs rename to src/coverlet.collector/DataCollection/CoverletSettings.cs index a31374b..f0a519e 100644 --- a/src/coverlet.collector/DataCollector/CoverletSettings.cs +++ b/src/coverlet.collector/DataCollection/CoverletSettings.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Text; -namespace Coverlet.Collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Coverlet settings diff --git a/src/coverlet.collector/DataCollector/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs similarity index 75% rename from src/coverlet.collector/DataCollector/CoverletSettingsParser.cs rename to src/coverlet.collector/DataCollection/CoverletSettingsParser.cs index 937ecaf..c882755 100644 --- a/src/coverlet.collector/DataCollector/CoverletSettingsParser.cs +++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs @@ -4,18 +4,18 @@ using System.Xml; using coverlet.collector.Resources; using Coverlet.Collector.Utilities; -namespace Coverlet.Collector.DataCollector +namespace Coverlet.Collector.DataCollection { /// /// Coverlet settings parser /// internal class CoverletSettingsParser { - private readonly TestPlatformEqtTrace eqtTrace; + private readonly TestPlatformEqtTrace _eqtTrace; public CoverletSettingsParser(TestPlatformEqtTrace eqtTrace) { - this.eqtTrace = eqtTrace; + _eqtTrace = eqtTrace; } /// @@ -32,10 +32,9 @@ namespace Coverlet.Collector.DataCollector }; if (configurationElement != null) - { - + { coverletSettings.IncludeFilters = this.ParseIncludeFilters(configurationElement); - coverletSettings.IncludeDirectories = this.ParseIncludeDirectories(configurationElement); + coverletSettings.IncludeDirectories = this.ParseIncludeDirectories(configurationElement); coverletSettings.ExcludeAttributes = this.ParseExcludeAttributes(configurationElement); coverletSettings.ExcludeSourceFiles = this.ParseExcludeSourceFiles(configurationElement); coverletSettings.MergeWith = this.ParseMergeWith(configurationElement); @@ -47,9 +46,9 @@ namespace Coverlet.Collector.DataCollector coverletSettings.ReportFormat = this.ParseReportFormat(configurationElement); coverletSettings.ExcludeFilters = this.ParseExcludeFilters(configurationElement); - if (this.eqtTrace.IsVerboseEnabled) + if (_eqtTrace.IsVerboseEnabled) { - this.eqtTrace.Verbose("{0}: Initializing coverlet process with settings: \"{1}\"", CoverletConstants.DataCollectorName, coverletSettings.ToString()); + _eqtTrace.Verbose("{0}: Initializing coverlet process with settings: \"{1}\"", CoverletConstants.DataCollectorName, coverletSettings.ToString()); } return coverletSettings; @@ -62,10 +61,10 @@ namespace Coverlet.Collector.DataCollector /// Test module private string ParseTestModule(IEnumerable testModules) { - // Validate if atleast one source present. + // Validate if at least one source present. if (testModules == null || !testModules.Any()) { - var errorMessage = string.Format(Resources.NoTestModulesFound, CoverletConstants.DataCollectorName); + string errorMessage = string.Format(Resources.NoTestModulesFound, CoverletConstants.DataCollectorName); throw new CoverletDataCollectorException(errorMessage); } @@ -85,8 +84,8 @@ namespace Coverlet.Collector.DataCollector string format = string.Empty; if (configurationElement != null) { - var reportFormat = configurationElement[CoverletConstants.ReportFormatElementName]; - format = reportFormat?.InnerText?.Split(',').FirstOrDefault(); + XmlElement reportFormatElement = configurationElement[CoverletConstants.ReportFormatElementName]; + format = reportFormatElement?.InnerText?.Split(',').FirstOrDefault(); } return string.IsNullOrEmpty(format) ? CoverletConstants.DefaultReportFormat : format; } @@ -98,7 +97,7 @@ namespace Coverlet.Collector.DataCollector /// Filters to include private string[] ParseIncludeFilters(XmlElement configurationElement) { - var includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName]; + XmlElement includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName]; return includeFiltersElement?.InnerText?.Split(','); } @@ -109,7 +108,7 @@ namespace Coverlet.Collector.DataCollector /// Directories to include private string[] ParseIncludeDirectories(XmlElement configurationElement) { - var includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName]; + XmlElement includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName]; return includeDirectoriesElement?.InnerText?.Split(','); } @@ -120,12 +119,12 @@ namespace Coverlet.Collector.DataCollector /// Filters to exclude private string[] ParseExcludeFilters(XmlElement configurationElement) { - var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter }; + List excludeFilters = new List { CoverletConstants.DefaultExcludeFilter }; if (configurationElement != null) { - var excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName]; - var filters = excludeFiltersElement?.InnerText?.Split(','); + XmlElement excludeFiltersElement = configurationElement[CoverletConstants.ExcludeFiltersElementName]; + string[] filters = excludeFiltersElement?.InnerText?.Split(','); if (filters != null) { excludeFilters.AddRange(filters); @@ -142,8 +141,8 @@ namespace Coverlet.Collector.DataCollector /// Source files to exclude private string[] ParseExcludeSourceFiles(XmlElement configurationElement) { - var excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName]; - return excludeSourceFilesElement?.InnerText?.Split(','); + XmlElement excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName]; + return excludeSourceFilesElement?.InnerText?.Split(','); } /// @@ -153,7 +152,7 @@ namespace Coverlet.Collector.DataCollector /// Attributes to exclude private string[] ParseExcludeAttributes(XmlElement configurationElement) { - var excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName]; + XmlElement excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName]; return excludeAttributesElement?.InnerText?.Split(','); } @@ -164,7 +163,7 @@ namespace Coverlet.Collector.DataCollector /// Merge with attribute private string ParseMergeWith(XmlElement configurationElement) { - var mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName]; + XmlElement mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName]; return mergeWithElement?.InnerText; } @@ -175,8 +174,8 @@ namespace Coverlet.Collector.DataCollector /// Use source link flag private bool ParseUseSourceLink(XmlElement configurationElement) { - var useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName]; - bool.TryParse(useSourceLinkElement?.InnerText, out var useSourceLink); + XmlElement useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName]; + bool.TryParse(useSourceLinkElement?.InnerText, out bool useSourceLink); return useSourceLink; } @@ -187,8 +186,8 @@ namespace Coverlet.Collector.DataCollector /// Single hit flag private bool ParseSingleHit(XmlElement configurationElement) { - var singleHitElement = configurationElement[CoverletConstants.SingleHitElementName]; - bool.TryParse(singleHitElement?.InnerText, out var singleHit); + XmlElement singleHitElement = configurationElement[CoverletConstants.SingleHitElementName]; + bool.TryParse(singleHitElement?.InnerText, out bool singleHit); return singleHit; } @@ -199,8 +198,8 @@ namespace Coverlet.Collector.DataCollector /// Include Test Assembly Flag private bool ParseIncludeTestAssembly(XmlElement configurationElement) { - var includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName]; - bool.TryParse(includeTestAssemblyElement?.InnerText, out var includeTestAssembly); + XmlElement includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName]; + bool.TryParse(includeTestAssemblyElement?.InnerText, out bool includeTestAssembly); return includeTestAssembly; } } diff --git a/src/coverlet.inproccollector/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs similarity index 97% rename from src/coverlet.inproccollector/CoverletInProcDataCollector.cs rename to src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs index 1c053bd..3ca1de7 100644 --- a/src/coverlet.inproccollector/CoverletInProcDataCollector.cs +++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollector.InProcDataCollector; using Microsoft.VisualStudio.TestPlatform.ObjectModel.InProcDataCollector; -namespace Coverlet.InProcDataCollector +namespace Coverlet.Collector.InProcDataCollector { public class CoverletInProcDataCollector : InProcDataCollection { diff --git a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs index 9168794..e066567 100644 --- a/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs +++ b/src/coverlet.collector/Utilities/CoverletDataCollectorException.cs @@ -1,7 +1,7 @@ -namespace Coverlet.Collector.Utilities -{ - using System; +using System; +namespace Coverlet.Collector.Utilities +{ internal class CoverletDataCollectorException : Exception { public CoverletDataCollectorException(string message) : base(message) diff --git a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs index dd3be35..d8b2f51 100644 --- a/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs +++ b/src/coverlet.collector/Utilities/Interfaces/ICoverageWrapper.cs @@ -1,4 +1,4 @@ -using Coverlet.Collector.DataCollector; +using Coverlet.Collector.DataCollection; using Coverlet.Core; using Coverlet.Core.Logging; diff --git a/src/coverlet.collector/Utilities/TestPlatformLogger.cs b/src/coverlet.collector/Utilities/TestPlatformLogger.cs index 60b5271..8175980 100644 --- a/src/coverlet.collector/Utilities/TestPlatformLogger.cs +++ b/src/coverlet.collector/Utilities/TestPlatformLogger.cs @@ -7,13 +7,13 @@ namespace Coverlet.Collector.Utilities /// internal class TestPlatformLogger { - private readonly DataCollectionLogger logger; - private readonly DataCollectionContext dataCollectionContext; + private readonly DataCollectionLogger _logger; + private readonly DataCollectionContext _dataCollectionContext; public TestPlatformLogger(DataCollectionLogger logger, DataCollectionContext dataCollectionContext) { - this.logger = logger; - this.dataCollectionContext = dataCollectionContext; + _logger = logger; + _dataCollectionContext = dataCollectionContext; } /// @@ -22,7 +22,7 @@ namespace Coverlet.Collector.Utilities /// Warning message public void LogWarning(string warning) { - this.logger.LogWarning(this.dataCollectionContext, warning); + _logger.LogWarning(_dataCollectionContext, warning); } } } diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj index c9c1e36..dae8389 100644 --- a/src/coverlet.collector/coverlet.collector.csproj +++ b/src/coverlet.collector/coverlet.collector.csproj @@ -63,7 +63,6 @@ - diff --git a/src/coverlet.inproccollector/coverlet.inproccollector.csproj b/src/coverlet.inproccollector/coverlet.inproccollector.csproj deleted file mode 100644 index d2fb005..0000000 --- a/src/coverlet.inproccollector/coverlet.inproccollector.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - netcoreapp2.0 - - - - - - - - - - - diff --git a/test/coverlet.collector.tests/AttachmentManagerTests.cs b/test/coverlet.collector.tests/AttachmentManagerTests.cs index 9641dc6..9541c4c 100644 --- a/test/coverlet.collector.tests/AttachmentManagerTests.cs +++ b/test/coverlet.collector.tests/AttachmentManagerTests.cs @@ -5,36 +5,36 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Xunit; using Moq; -using Coverlet.Collector.DataCollector; using Coverlet.Collector.Utilities; using Coverlet.Collector.Utilities.Interfaces; +using Coverlet.Collector.DataCollection; namespace Coverlet.Collector.Tests { public class AttachmentManagerTests { - private AttachmentManager attachmentManager; - private Mock mockDataCollectionSink; - private DataCollectionContext dataCollectionContext; - private TestPlatformLogger testPlatformLogger; - private TestPlatformEqtTrace eqtTrace; - private Mock mockFileHelper; - private Mock mockDirectoryHelper; - private Mock mockDataCollectionLogger; + private AttachmentManager _attachmentManager; + private Mock _mockDataCollectionSink; + private DataCollectionContext _dataCollectionContext; + private TestPlatformLogger _testPlatformLogger; + private TestPlatformEqtTrace _eqtTrace; + private Mock _mockFileHelper; + private Mock _mockDirectoryHelper; + private Mock _mockDataCollectionLogger; public AttachmentManagerTests() { - this.mockDataCollectionSink = new Mock(); - this.mockDataCollectionLogger = new Mock(); - TestCase testcase = new TestCase { Id = Guid.NewGuid() }; - this.dataCollectionContext = new DataCollectionContext(testcase); - this.testPlatformLogger = new TestPlatformLogger(this.mockDataCollectionLogger.Object, this.dataCollectionContext); - this.eqtTrace = new TestPlatformEqtTrace(); - this.mockFileHelper = new Mock(); - this.mockDirectoryHelper = new Mock(); + _mockDataCollectionSink = new Mock(); + _mockDataCollectionLogger = new Mock(); + var testcase = new TestCase { Id = Guid.NewGuid() }; + _dataCollectionContext = new DataCollectionContext(testcase); + _testPlatformLogger = new TestPlatformLogger(_mockDataCollectionLogger.Object, _dataCollectionContext); + _eqtTrace = new TestPlatformEqtTrace(); + _mockFileHelper = new Mock(); + _mockDirectoryHelper = new Mock(); - this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, - this.eqtTrace, "report.cobertura.xml", @"E:\temp", this.mockFileHelper.Object, this.mockDirectoryHelper.Object); + _attachmentManager = new AttachmentManager(_mockDataCollectionSink.Object, _dataCollectionContext, _testPlatformLogger, + _eqtTrace, "report.cobertura.xml", @"E:\temp", _mockFileHelper.Object, _mockDirectoryHelper.Object); } [Fact] @@ -46,15 +46,15 @@ namespace Coverlet.Collector.Tests + "" + ""; - this.attachmentManager.SendCoverageReport(coverageReport); - this.mockFileHelper.Verify(x => x.WriteAllText(@"E:\temp\report.cobertura.xml", coverageReport), Times.Once); + _attachmentManager.SendCoverageReport(coverageReport); + _mockFileHelper.Verify(x => x.WriteAllText(It.Is(y => y.Contains(@"E:\temp\report.cobertura.xml")), coverageReport), Times.Once); } [Fact] public void SendCoverageReportShouldThrowExceptionWhenFailedToSaveReportToFile() { - this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, - this.eqtTrace, null, @"E:\temp", this.mockFileHelper.Object, this.mockDirectoryHelper.Object); + _attachmentManager = new AttachmentManager(_mockDataCollectionSink.Object, _dataCollectionContext, _testPlatformLogger, + _eqtTrace, null, @"E:\temp", _mockFileHelper.Object, _mockDirectoryHelper.Object); string coverageReport = "" + "" @@ -62,16 +62,16 @@ namespace Coverlet.Collector.Tests + "" + ""; - var message = Assert.Throws(() => this.attachmentManager.SendCoverageReport(coverageReport)).Message; - Assert.Equal("CoverletCoverageDataCollector: Failed to save coverage report '' in directory 'E:\\temp'", message); + string message = Assert.Throws(() => _attachmentManager.SendCoverageReport(coverageReport)).Message; + Assert.Contains("CoverletCoverageDataCollector: Failed to save coverage report", message); } [Fact] public void SendCoverageReportShouldSendAttachmentToTestPlatform() { var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); - this.attachmentManager = new AttachmentManager(this.mockDataCollectionSink.Object, this.dataCollectionContext, this.testPlatformLogger, - this.eqtTrace, "report.cobertura.xml", directory.ToString(), new FileHelper(), this.mockDirectoryHelper.Object); + _attachmentManager = new AttachmentManager(_mockDataCollectionSink.Object, _dataCollectionContext, _testPlatformLogger, + _eqtTrace, "report.cobertura.xml", directory.ToString(), new FileHelper(), _mockDirectoryHelper.Object); string coverageReport = "" + "" @@ -79,9 +79,9 @@ namespace Coverlet.Collector.Tests + "" + ""; - this.attachmentManager.SendCoverageReport(coverageReport); + _attachmentManager.SendCoverageReport(coverageReport); - this.mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny())); + _mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny())); directory.Delete(true); } @@ -89,22 +89,22 @@ namespace Coverlet.Collector.Tests [Fact] public void OnSendFileCompletedShouldCleanUpReportDirectory() { - this.mockDirectoryHelper.Setup(x => x.Exists(@"E:\temp")).Returns(true); + _mockDirectoryHelper.Setup(x => x.Exists(It.Is(y => y.Contains(@"E:\temp")))).Returns(true); - this.mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); + _mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); - this.mockDirectoryHelper.Verify(x => x.Delete(@"E:\temp", true), Times.Once); + _mockDirectoryHelper.Verify(x => x.Delete(It.Is(y => y.Contains(@"E:\temp")), true), Times.Once); } [Fact] public void OnSendFileCompletedShouldThrowCoverletDataCollectorExceptionIfUnableToCleanUpReportDirectory() { - this.mockDirectoryHelper.Setup(x => x.Exists(@"E:\temp")).Returns(true); - this.mockDirectoryHelper.Setup(x => x.Delete(@"E:\temp", true)).Throws(new FileNotFoundException()); + _mockDirectoryHelper.Setup(x => x.Exists(It.Is(y => y.Contains(@"E:\temp")))).Returns(true); + _mockDirectoryHelper.Setup(x => x.Delete(It.Is(y => y.Contains(@"E:\temp")), true)).Throws(new FileNotFoundException()); - this.mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); - this.mockDataCollectionLogger.Verify(x => x.LogWarning(this.dataCollectionContext, - It.Is(y => y.Contains("CoverletDataCollectorException: CoverletCoverageDataCollector: Failed to cleanup report directory"))), Times.Once); + _mockDataCollectionSink.Raise(x => x.SendFileCompleted += null, new AsyncCompletedEventArgs(null, false, null)); + _mockDataCollectionLogger.Verify(x => x.LogWarning(_dataCollectionContext, + It.Is(y => y.Contains("CoverletDataCollectorException: CoverletCoverageDataCollector: Failed to cleanup report directory"))), Times.AtLeastOnce); } } } diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs index d6ee7d2..6e2dbbc 100644 --- a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs +++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs @@ -7,89 +7,88 @@ using Moq; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Coverlet.Core; using Coverlet.Core.Logging; -using Coverlet.Collector.DataCollector; using Coverlet.Collector.Utilities.Interfaces; using Coverlet.Collector.Utilities; -using coverlet.collector.DataCollector; using Xunit; +using Coverlet.Collector.DataCollection; namespace Coverlet.Collector.Tests { public class CoverletCoverageDataCollectorTests { - private DataCollectionEnvironmentContext context; - private CoverletCoverageCollector coverletCoverageDataCollector; - private DataCollectionContext dataCollectionContext; - private Mock mockDataColectionEvents; - private Mock mockDataCollectionSink; - private Mock mockCoverageWrapper; - private XmlElement configurationElement; - private Mock mockLogger; + private DataCollectionEnvironmentContext _context; + private CoverletCoverageCollector _coverletCoverageDataCollector; + private DataCollectionContext _dataCollectionContext; + private Mock _mockDataColectionEvents; + private Mock _mockDataCollectionSink; + private Mock _mockCoverageWrapper; + private XmlElement _configurationElement; + private Mock _mockLogger; public CoverletCoverageDataCollectorTests() { - this.mockDataColectionEvents = new Mock(); - this.mockDataCollectionSink = new Mock(); - this.mockLogger = new Mock(); - this.configurationElement = null; + _mockDataColectionEvents = new Mock(); + _mockDataCollectionSink = new Mock(); + _mockLogger = new Mock(); + _configurationElement = null; TestCase testcase = new TestCase { Id = Guid.NewGuid() }; - this.dataCollectionContext = new DataCollectionContext(testcase); - this.context = new DataCollectionEnvironmentContext(this.dataCollectionContext); - this.mockCoverageWrapper = new Mock(); + _dataCollectionContext = new DataCollectionContext(testcase); + _context = new DataCollectionEnvironmentContext(_dataCollectionContext); + _mockCoverageWrapper = new Mock(); } [Fact] public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings() { - coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); - coverletCoverageDataCollector.Initialize( - this.configurationElement, - this.mockDataColectionEvents.Object, - this.mockDataCollectionSink.Object, - this.mockLogger.Object, - this.context); + _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object); + _coverletCoverageDataCollector.Initialize( + _configurationElement, + _mockDataColectionEvents.Object, + _mockDataCollectionSink.Object, + _mockLogger.Object, + _context); IDictionary sessionStartProperties = new Dictionary(); sessionStartProperties.Add("TestSources", new List { "abc.dll" }); - this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - this.mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => string.Equals(y.TestModule, "abc.dll")), It.IsAny()), Times.Once); + _mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => string.Equals(y.TestModule, "abc.dll")), It.IsAny()), Times.Once); } [Fact] public void OnSessionStartShouldPrepareModulesForCoverage() { - coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); - coverletCoverageDataCollector.Initialize( - this.configurationElement, - this.mockDataColectionEvents.Object, - this.mockDataCollectionSink.Object, + _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object); + _coverletCoverageDataCollector.Initialize( + _configurationElement, + _mockDataColectionEvents.Object, + _mockDataCollectionSink.Object, null, - this.context); + _context); IDictionary sessionStartProperties = new Dictionary(); Coverage coverage = new Coverage("abc.dll", null, null, null, null, null, true, true, "abc.json", true, It.IsAny()); sessionStartProperties.Add("TestSources", new List { "abc.dll" }); - this.mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny())).Returns(coverage); + _mockCoverageWrapper.Setup(x => x.CreateCoverage(It.IsAny(), It.IsAny())).Returns(coverage); - this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - this.mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => y.TestModule.Contains("abc.dll")), It.IsAny()), Times.Once); - this.mockCoverageWrapper.Verify(x => x.PrepareModules(It.IsAny()), Times.Once); + _mockCoverageWrapper.Verify(x => x.CreateCoverage(It.Is(y => y.TestModule.Contains("abc.dll")), It.IsAny()), Times.Once); + _mockCoverageWrapper.Verify(x => x.PrepareModules(It.IsAny()), Times.Once); } [Fact] public void OnSessionEndShouldSendGetCoverageReportToTestPlatform() { - coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper()); - coverletCoverageDataCollector.Initialize( - this.configurationElement, - this.mockDataColectionEvents.Object, - this.mockDataCollectionSink.Object, - this.mockLogger.Object, - this.context); + _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper()); + _coverletCoverageDataCollector.Initialize( + _configurationElement, + _mockDataColectionEvents.Object, + _mockDataCollectionSink.Object, + _mockLogger.Object, + _context); string module = GetType().Assembly.Location; string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); @@ -102,10 +101,10 @@ namespace Coverlet.Collector.Tests IDictionary sessionStartProperties = new Dictionary(); sessionStartProperties.Add("TestSources", new List { Path.Combine(directory.FullName, Path.GetFileName(module)) }); - this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - this.mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); + _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs()); - this.mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Once); + _mockDataCollectionSink.Verify(x => x.SendFileAsync(It.IsAny()), Times.Once); directory.Delete(true); } @@ -113,22 +112,22 @@ namespace Coverlet.Collector.Tests [Fact] public void OnSessionStartShouldLogWarningIfInstrumentationFailed() { - coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), this.mockCoverageWrapper.Object); - coverletCoverageDataCollector.Initialize( - this.configurationElement, - this.mockDataColectionEvents.Object, - this.mockDataCollectionSink.Object, - this.mockLogger.Object, - this.context); + _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), _mockCoverageWrapper.Object); + _coverletCoverageDataCollector.Initialize( + _configurationElement, + _mockDataColectionEvents.Object, + _mockDataCollectionSink.Object, + _mockLogger.Object, + _context); IDictionary sessionStartProperties = new Dictionary(); sessionStartProperties.Add("TestSources", new List { "abc.dll" }); - this.mockCoverageWrapper.Setup(x => x.PrepareModules(It.IsAny())).Throws(new FileNotFoundException()); + _mockCoverageWrapper.Setup(x => x.PrepareModules(It.IsAny())).Throws(new FileNotFoundException()); - this.mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); + _mockDataColectionEvents.Raise(x => x.SessionStart += null, new SessionStartEventArgs(sessionStartProperties)); - this.mockLogger.Verify(x => x.LogWarning(this.dataCollectionContext, + _mockLogger.Verify(x => x.LogWarning(_dataCollectionContext, It.Is(y => y.Contains("CoverletDataCollectorException")))); } } diff --git a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs index 1d0b5d1..f392dd4 100644 --- a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs +++ b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml; -using Coverlet.Collector.DataCollector; +using Coverlet.Collector.DataCollection; using Coverlet.Collector.Utilities; using Xunit; @@ -9,17 +9,17 @@ namespace Coverlet.Collector.Tests { public class CoverletSettingsParserTests { - private CoverletSettingsParser coverletSettingsParser; + private CoverletSettingsParser _coverletSettingsParser; public CoverletSettingsParserTests() { - this.coverletSettingsParser = new CoverletSettingsParser(new TestPlatformEqtTrace()); + _coverletSettingsParser = new CoverletSettingsParser(new TestPlatformEqtTrace()); } [Fact] public void ParseShouldThrowCoverletDataCollectorExceptionIfTestModulesIsNull() { - var message = Assert.Throws(() => this.coverletSettingsParser.Parse(null, null)).Message; + string message = Assert.Throws(() => _coverletSettingsParser.Parse(null, null)).Message; Assert.Equal("CoverletCoverageDataCollector: No test modules found", message); } @@ -27,7 +27,7 @@ namespace Coverlet.Collector.Tests [Fact] public void ParseShouldThrowCoverletDataCollectorExceptionIfTestModulesIsEmpty() { - var message = Assert.Throws(() => this.coverletSettingsParser.Parse(null, Enumerable.Empty())).Message; + string message = Assert.Throws(() => _coverletSettingsParser.Parse(null, Enumerable.Empty())).Message; Assert.Equal("CoverletCoverageDataCollector: No test modules found", message); } @@ -37,7 +37,7 @@ namespace Coverlet.Collector.Tests { var testModules = new List { "module1.dll", "module2.dll", "module3.dll" }; - var coverletSettings = this.coverletSettingsParser.Parse(null, testModules); + CoverletSettings coverletSettings = _coverletSettingsParser.Parse(null, testModules); Assert.Equal("module1.dll", coverletSettings.TestModule); } @@ -57,7 +57,7 @@ namespace Coverlet.Collector.Tests this.CreateCoverletNodes(doc, configElement, CoverletConstants.UseSourceLinkElementName, "false"); this.CreateCoverletNodes(doc, configElement, CoverletConstants.SingleHitElementName, "true"); - var coverletSettings = this.coverletSettingsParser.Parse(configElement, testModules); + CoverletSettings coverletSettings = _coverletSettingsParser.Parse(configElement, testModules); Assert.Equal("abc.dll", coverletSettings.TestModule); Assert.Equal("[*]*", coverletSettings.IncludeFilters[0]); From 87a6f3a1fb2db42ea496be49177324fe4dc2524d Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Thu, 16 May 2019 15:09:46 +0530 Subject: [PATCH 4/8] Fixed failing UT --- test/coverlet.collector.tests/AttachmentManagerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/coverlet.collector.tests/AttachmentManagerTests.cs b/test/coverlet.collector.tests/AttachmentManagerTests.cs index 9541c4c..39b1776 100644 --- a/test/coverlet.collector.tests/AttachmentManagerTests.cs +++ b/test/coverlet.collector.tests/AttachmentManagerTests.cs @@ -47,7 +47,7 @@ namespace Coverlet.Collector.Tests + ""; _attachmentManager.SendCoverageReport(coverageReport); - _mockFileHelper.Verify(x => x.WriteAllText(It.Is(y => y.Contains(@"E:\temp\report.cobertura.xml")), coverageReport), Times.Once); + _mockFileHelper.Verify(x => x.WriteAllText(It.Is(y => y.Contains(@"report.cobertura.xml")), coverageReport), Times.Once); } [Fact] From 73de95d6533f432aebcdc8b31abe3063d2677dab Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Thu, 16 May 2019 15:53:09 +0530 Subject: [PATCH 5/8] Variable change suggestion --- src/coverlet.collector/DataCollection/CoverageManager.cs | 6 +++--- .../DataCollection/CoverletCoverageCollector.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coverlet.collector/DataCollection/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs index d02aa21..1a88a0f 100644 --- a/src/coverlet.collector/DataCollection/CoverageManager.cs +++ b/src/coverlet.collector/DataCollection/CoverageManager.cs @@ -17,7 +17,7 @@ namespace Coverlet.Collector.DataCollection private ICoverageWrapper _coverageWrapper; - public IReporter _reporter { get; } + public IReporter Reporter { get; } public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper) : this(settings, @@ -30,7 +30,7 @@ namespace Coverlet.Collector.DataCollection public CoverageManager(CoverletSettings settings, IReporter reporter, ILogger logger, ICoverageWrapper coverageWrapper) { // Store input vars - _reporter = reporter; + Reporter = reporter; _coverageWrapper = coverageWrapper; // Coverage object @@ -94,7 +94,7 @@ namespace Coverlet.Collector.DataCollection { try { - return _reporter.Report(coverageResult); + return Reporter.Report(coverageResult); } catch (Exception ex) { diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs index 9f1f953..7918253 100644 --- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs +++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs @@ -151,7 +151,7 @@ namespace Coverlet.Collector.DataCollection private string GetReportFileName() { string fileName = CoverletConstants.DefaultFileName; - string extension = _coverageManager?._reporter.Extension; + string extension = _coverageManager?.Reporter.Extension; return extension == null ? fileName : $"{fileName}.{extension}"; } From faa5b92a5496d538410cae6c50065c4ed29556da Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Thu, 16 May 2019 15:59:19 +0530 Subject: [PATCH 6/8] Nit change --- .../InProcDataCollection/CoverletInProcDataCollector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs index 3ca1de7..0e6379a 100644 --- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs +++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs @@ -65,7 +65,7 @@ namespace Coverlet.Collector.InProcDataCollector } catch { - // Avoid crashing if reflection fails + // Avoid crashing if reflection fails. return null; } } From 28aa07fcc46041bce5e3f53e41879da93398fcc1 Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Mon, 20 May 2019 14:12:10 +0530 Subject: [PATCH 7/8] Adding diagnostic logs --- .../CoverletInProcDataCollector.cs | 16 +++++++++++++--- .../Utilities/CoverletConstants.cs | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs index 0e6379a..0016f35 100644 --- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs +++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs @@ -1,11 +1,13 @@ using System; using System.Reflection; +using Coverlet.Collector.Utilities; using Coverlet.Core.Instrumentation; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollector.InProcDataCollector; using Microsoft.VisualStudio.TestPlatform.ObjectModel.InProcDataCollector; -namespace Coverlet.Collector.InProcDataCollector +namespace Coverlet.Collector.DataCollection { public class CoverletInProcDataCollector : InProcDataCollection { @@ -37,9 +39,13 @@ namespace Coverlet.Collector.InProcDataCollector { unloadModule.Invoke(null, new[] { null, EventArgs.Empty }); } - catch + catch(Exception ex) { // Ignore exceptions and continue with the unload + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning("{0}: Failed to unload module with error: {1}", CoverletConstants.InProcDataCollectorName, ex); + } } } } @@ -63,9 +69,13 @@ namespace Coverlet.Collector.InProcDataCollector return null; } - catch + catch(Exception ex) { // Avoid crashing if reflection fails. + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning("{0}: Failed to get Instrumentation class with error: {1}", CoverletConstants.InProcDataCollectorName, ex); + } return null; } } diff --git a/src/coverlet.collector/Utilities/CoverletConstants.cs b/src/coverlet.collector/Utilities/CoverletConstants.cs index 837d12a..d3e6469 100644 --- a/src/coverlet.collector/Utilities/CoverletConstants.cs +++ b/src/coverlet.collector/Utilities/CoverletConstants.cs @@ -19,5 +19,6 @@ public const string TestSourcesPropertyName = "TestSources"; public const string ReportFormatElementName = "Format"; public const string DefaultExcludeFilter = "[coverlet.*]*"; + public const string InProcDataCollectorName = "CoverletInProcDataCollector"; } } From 56855bc865d015759c501c8c2b3f74c2e4b8bb12 Mon Sep 17 00:00:00 2001 From: Vagisha Nidhi Date: Mon, 20 May 2019 15:52:50 +0530 Subject: [PATCH 8/8] Unload module raise exception --- .../CoverletInProcDataCollector.cs | 17 ++++++++++------- .../Resources/Resources.Designer.cs | 9 +++++++++ src/coverlet.collector/Resources/Resources.resx | 3 +++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs index 0016f35..1de4167 100644 --- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs +++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using coverlet.collector.Resources; using Coverlet.Collector.Utilities; using Coverlet.Core.Instrumentation; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -29,23 +30,25 @@ namespace Coverlet.Collector.DataCollection { Type injectedInstrumentationClass = GetInstrumentationClass(assembly); if (injectedInstrumentationClass is null) + { continue; - - var unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) }); - if (unloadModule is null) - continue; + } try { + var unloadModule = injectedInstrumentationClass.GetMethod(nameof(ModuleTrackerTemplate.UnloadModule), new[] { typeof(object), typeof(EventArgs) }); unloadModule.Invoke(null, new[] { null, EventArgs.Empty }); } catch(Exception ex) { - // Ignore exceptions and continue with the unload - if (EqtTrace.IsWarningEnabled) + // Throw any exception if unload fails + if (EqtTrace.IsErrorEnabled) { - EqtTrace.Warning("{0}: Failed to unload module with error: {1}", CoverletConstants.InProcDataCollectorName, ex); + EqtTrace.Error("{0}: Failed to unload module with error: {1}", CoverletConstants.InProcDataCollectorName, ex); } + + string errorMessage = string.Format(Resources.FailedToUnloadModule, CoverletConstants.InProcDataCollectorName); + throw new CoverletDataCollectorException(errorMessage, ex); } } } diff --git a/src/coverlet.collector/Resources/Resources.Designer.cs b/src/coverlet.collector/Resources/Resources.Designer.cs index 7988746..a1af3af 100644 --- a/src/coverlet.collector/Resources/Resources.Designer.cs +++ b/src/coverlet.collector/Resources/Resources.Designer.cs @@ -96,6 +96,15 @@ namespace coverlet.collector.Resources { } } + /// + /// Looks up a localized string similar to {0}: Failed to unload module. + /// + internal static string FailedToUnloadModule { + get { + return ResourceManager.GetString("FailedToUnloadModule", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0}: Failed to instrument modules. /// diff --git a/src/coverlet.collector/Resources/Resources.resx b/src/coverlet.collector/Resources/Resources.resx index 5fc47d0..af11154 100644 --- a/src/coverlet.collector/Resources/Resources.resx +++ b/src/coverlet.collector/Resources/Resources.resx @@ -129,6 +129,9 @@ {0}: Failed to save coverage report '{1}' in directory '{2}' + + {0}: Failed to unload module + {0}: Failed to instrument modules