diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs index 9018ee387..0f8dc4287 100644 --- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs +++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs @@ -33,7 +33,8 @@ public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger UseSourceLink = settings.UseSourceLink, SkipAutoProps = settings.SkipAutoProps, DoesNotReturnAttributes = settings.DoesNotReturnAttributes, - DeterministicReport = settings.DeterministicReport + DeterministicReport = settings.DeterministicReport, + InstrumentModulesWithoutLocalSources = settings.InstrumentModulesWithoutLocalSources }; return new Coverage( diff --git a/src/coverlet.collector/DataCollection/CoverletSettings.cs b/src/coverlet.collector/DataCollection/CoverletSettings.cs index bf04e6326..7a23e0a5e 100644 --- a/src/coverlet.collector/DataCollection/CoverletSettings.cs +++ b/src/coverlet.collector/DataCollection/CoverletSettings.cs @@ -81,6 +81,11 @@ internal class CoverletSettings /// public bool DeterministicReport { get; set; } + /// + /// Instruments modules even if the sources from the PDBs can't be resolved. + /// + public bool InstrumentModulesWithoutLocalSources { get; set; } + public override string ToString() { var builder = new StringBuilder(); @@ -98,6 +103,7 @@ public override string ToString() builder.AppendFormat("SkipAutoProps: '{0}'", SkipAutoProps); builder.AppendFormat("DoesNotReturnAttributes: '{0}'", string.Join(",", DoesNotReturnAttributes ?? Enumerable.Empty())); builder.AppendFormat("DeterministicReport: '{0}'", DeterministicReport); + builder.AppendFormat("InstrumentModulesWithoutLocalSources: '{0}'", InstrumentModulesWithoutLocalSources); return builder.ToString(); } diff --git a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs index 4440b24d8..b1a77b1dc 100644 --- a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs +++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs @@ -48,6 +48,7 @@ public CoverletSettings Parse(XmlElement configurationElement, IEnumerable + /// Parse InstrumentModulesWithoutLocalSources flag + /// + /// Configuration element + /// InstrumentModulesWithoutLocalSources flag + private static bool ParseInstrumentModulesWithoutLocalSources(XmlElement configurationElement) + { + XmlElement instrumentModulesWithoutLocalSourcesElement = configurationElement[CoverletConstants.InstrumentModulesWithoutLocalSources]; + bool.TryParse(instrumentModulesWithoutLocalSourcesElement?.InnerText, out bool instrumentModulesWithoutLocalSources); + return instrumentModulesWithoutLocalSources; + } + /// /// Parse include test assembly flag /// diff --git a/src/coverlet.collector/Utilities/CoverletConstants.cs b/src/coverlet.collector/Utilities/CoverletConstants.cs index 5aff53cfb..a8bd770e7 100644 --- a/src/coverlet.collector/Utilities/CoverletConstants.cs +++ b/src/coverlet.collector/Utilities/CoverletConstants.cs @@ -26,5 +26,6 @@ internal static class CoverletConstants public const string SkipAutoProps = "SkipAutoProps"; public const string DoesNotReturnAttributesElementName = "DoesNotReturnAttribute"; public const string DeterministicReport = "DeterministicReport"; + public const string InstrumentModulesWithoutLocalSources = "InstrumentModulesWithoutLocalSources"; } } diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 25f5f7350..a80f6ff31 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -70,6 +70,7 @@ static int Main(string[] args) CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue); CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue); CommandOption doesNotReturnAttributes = app.Option("--does-not-return-attribute", "Attributes that mark methods that do not return.", CommandOptionType.MultipleValue); + CommandOption instrumentModulesWithoutLocalSources = app.Option("--instrument-modules-without-local-sources", "Specifies whether modules should be instrumented even if the sources from the PDBs can't be found locally.", CommandOptionType.NoValue); app.OnExecute(() => { @@ -97,7 +98,8 @@ static int Main(string[] args) MergeWith = mergeWith.Value(), UseSourceLink = useSourceLink.HasValue(), SkipAutoProps = skipAutoProp.HasValue(), - DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray() + DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray(), + InstrumentModulesWithoutLocalSources = instrumentModulesWithoutLocalSources.HasValue(), }; ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService(); diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 25a415b5b..d0357997e 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -43,6 +43,8 @@ internal class CoverageParameters public bool SkipAutoProps { get; set; } [DataMember] public bool DeterministicReport { get; set; } + [DataMember] + public bool InstrumentModulesWithoutLocalSources { get; set; } } internal class Coverage diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 42a1a1835..36d195c50 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -94,6 +94,11 @@ public bool CanInstrument() { if (_instrumentationHelper.HasPdb(_module, out bool embeddedPdb)) { + if (this._parameters.InstrumentModulesWithoutLocalSources) + { + return true; + } + if (embeddedPdb) { if (_instrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module, out string firstNotFoundDocument)) diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 57571df9a..e3d299fda 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -227,9 +227,6 @@ private static string GetRelativePathFromBase(IEnumerable basePaths, str return path.Substring(basePath.Length); } } - - Debug.Assert(false, "Unexpected, we should find at least one path starts with one pre-build roots list"); - return path; } } diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 2ec4910da..b43f152d4 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -47,6 +47,8 @@ public class InstrumentationTask : BaseTask public bool DeterministicReport { get; set; } + public bool InstrumentModulesWithoutLocalSources { get; set; } + [Output] public ITaskItem InstrumenterState { get; set; } @@ -99,6 +101,7 @@ public override bool Execute() UseSourceLink = UseSourceLink, SkipAutoProps = SkipAutoProps, DeterministicReport = DeterministicReport, + InstrumentModulesWithoutLocalSources = InstrumentModulesWithoutLocalSources, DoesNotReturnAttributes = DoesNotReturnAttribute?.Split(',') };