diff --git a/.editorconfig b/.editorconfig index 26d6da3ab..1227fcc9b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -87,20 +87,34 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_auto_properties = true:warning dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = warning +# IDE0063: Use simple 'using' statement +dotnet_diagnostic.IDE0063.severity = warning +# IDE0057: Use range operator +dotnet_diagnostic.IDE0057.severity = warning +# IDE0075: Simplify conditional expression +dotnet_diagnostic.IDE0075.severity = warning +# IDE0071: Simplify interpolation +dotnet_diagnostic.IDE0071.severity = warning +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = warning +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = warning ############################### # Naming Conventions # ############################### # Name all constant fields using PascalCase dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_naming_style.pascal_case_style.capitalization = pascal_case # Static fields should have s_ prefix dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields -dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected @@ -109,11 +123,15 @@ dotnet_naming_style.static_prefix_style.capitalization = camel_case # Internal and private fields should be _camelCase dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields -dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style dotnet_naming_symbols.private_internal_fields.applicable_kinds = field dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal dotnet_naming_style.camel_case_underscore_style.required_prefix = _ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case +# IDE1006: Naming Styles +dotnet_diagnostic.IDE1006.severity = warning +# IDE0090: Use 'new(...)' +dotnet_diagnostic.IDE0090.severity = warning ############################### # C# Coding Conventions # ############################### diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs index cccbd3d10..e1b27a219 100644 --- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs +++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs @@ -163,15 +163,13 @@ private void OnSessionEnd(object sender, SessionEndEventArgs e) // Get coverage reports IEnumerable<(string report, string fileName)> coverageReports = _coverageManager?.GetCoverageReports(); - if (coverageReports != null && coverageReports.Count() > 0) + if (coverageReports != null && coverageReports.Any()) { // Send result attachments to test platform. - using (var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, _countDownEventFactory.Create(coverageReports.Count(), TimeSpan.FromSeconds(30)))) + using var attachmentManager = new AttachmentManager(_dataSink, _dataCollectionContext, _logger, _eqtTrace, _countDownEventFactory.Create(coverageReports.Count(), TimeSpan.FromSeconds(30))); + foreach ((string report, string fileName) in coverageReports) { - foreach ((string report, string fileName) in coverageReports) - { - attachmentManager.SendCoverageReport(report, fileName); - } + attachmentManager.SendCoverageReport(report, fileName); } } else diff --git a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs index d18d1553d..4440b24d8 100644 --- a/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs +++ b/src/coverlet.collector/DataCollection/CoverletSettingsParser.cs @@ -66,7 +66,7 @@ public CoverletSettings Parse(XmlElement configurationElement, IEnumerable /// Test modules /// Test module - private string ParseTestModule(IEnumerable testModules) + private static string ParseTestModule(IEnumerable testModules) { // Validate if at least one source present. if (testModules == null || !testModules.Any()) @@ -86,7 +86,7 @@ private string ParseTestModule(IEnumerable testModules) /// /// Configuration element /// Report formats - private string[] ParseReportFormats(XmlElement configurationElement) + private static string[] ParseReportFormats(XmlElement configurationElement) { string[] formats = Array.Empty(); if (configurationElement != null) @@ -103,7 +103,7 @@ private string[] ParseReportFormats(XmlElement configurationElement) /// /// Configuration element /// Filters to include - private string[] ParseIncludeFilters(XmlElement configurationElement) + private static string[] ParseIncludeFilters(XmlElement configurationElement) { XmlElement includeFiltersElement = configurationElement[CoverletConstants.IncludeFiltersElementName]; return SplitElement(includeFiltersElement); @@ -114,7 +114,7 @@ private string[] ParseIncludeFilters(XmlElement configurationElement) /// /// Configuration element /// Directories to include - private string[] ParseIncludeDirectories(XmlElement configurationElement) + private static string[] ParseIncludeDirectories(XmlElement configurationElement) { XmlElement includeDirectoriesElement = configurationElement[CoverletConstants.IncludeDirectoriesElementName]; return SplitElement(includeDirectoriesElement); @@ -125,7 +125,7 @@ private string[] ParseIncludeDirectories(XmlElement configurationElement) /// /// Configuration element /// Filters to exclude - private string[] ParseExcludeFilters(XmlElement configurationElement) + private static string[] ParseExcludeFilters(XmlElement configurationElement) { var excludeFilters = new List { CoverletConstants.DefaultExcludeFilter }; @@ -147,7 +147,7 @@ private string[] ParseExcludeFilters(XmlElement configurationElement) /// /// Configuration element /// Source files to exclude - private string[] ParseExcludeSourceFiles(XmlElement configurationElement) + private static string[] ParseExcludeSourceFiles(XmlElement configurationElement) { XmlElement excludeSourceFilesElement = configurationElement[CoverletConstants.ExcludeSourceFilesElementName]; return SplitElement(excludeSourceFilesElement); @@ -158,7 +158,7 @@ private string[] ParseExcludeSourceFiles(XmlElement configurationElement) /// /// Configuration element /// Attributes to exclude - private string[] ParseExcludeAttributes(XmlElement configurationElement) + private static string[] ParseExcludeAttributes(XmlElement configurationElement) { XmlElement excludeAttributesElement = configurationElement[CoverletConstants.ExcludeAttributesElementName]; return SplitElement(excludeAttributesElement); @@ -169,7 +169,7 @@ private string[] ParseExcludeAttributes(XmlElement configurationElement) /// /// Configuration element /// Merge with attribute - private string ParseMergeWith(XmlElement configurationElement) + private static string ParseMergeWith(XmlElement configurationElement) { XmlElement mergeWithElement = configurationElement[CoverletConstants.MergeWithElementName]; return mergeWithElement?.InnerText; @@ -180,7 +180,7 @@ private string ParseMergeWith(XmlElement configurationElement) /// /// Configuration element /// Use source link flag - private bool ParseUseSourceLink(XmlElement configurationElement) + private static bool ParseUseSourceLink(XmlElement configurationElement) { XmlElement useSourceLinkElement = configurationElement[CoverletConstants.UseSourceLinkElementName]; bool.TryParse(useSourceLinkElement?.InnerText, out bool useSourceLink); @@ -192,7 +192,7 @@ private bool ParseUseSourceLink(XmlElement configurationElement) /// /// Configuration element /// Single hit flag - private bool ParseSingleHit(XmlElement configurationElement) + private static bool ParseSingleHit(XmlElement configurationElement) { XmlElement singleHitElement = configurationElement[CoverletConstants.SingleHitElementName]; bool.TryParse(singleHitElement?.InnerText, out bool singleHit); @@ -204,7 +204,7 @@ private bool ParseSingleHit(XmlElement configurationElement) /// /// Configuration element /// ParseDeterministicReport flag - private bool ParseDeterministicReport(XmlElement configurationElement) + private static bool ParseDeterministicReport(XmlElement configurationElement) { XmlElement deterministicReportElement = configurationElement[CoverletConstants.DeterministicReport]; bool.TryParse(deterministicReportElement?.InnerText, out bool deterministicReport); @@ -216,7 +216,7 @@ private bool ParseDeterministicReport(XmlElement configurationElement) /// /// Configuration element /// Include Test Assembly Flag - private bool ParseIncludeTestAssembly(XmlElement configurationElement) + private static bool ParseIncludeTestAssembly(XmlElement configurationElement) { XmlElement includeTestAssemblyElement = configurationElement[CoverletConstants.IncludeTestAssemblyElementName]; bool.TryParse(includeTestAssemblyElement?.InnerText, out bool includeTestAssembly); @@ -228,7 +228,7 @@ private bool ParseIncludeTestAssembly(XmlElement configurationElement) /// /// Configuration element /// Include Test Assembly Flag - private bool ParseSkipAutoProps(XmlElement configurationElement) + private static bool ParseSkipAutoProps(XmlElement configurationElement) { XmlElement skipAutoPropsElement = configurationElement[CoverletConstants.SkipAutoProps]; bool.TryParse(skipAutoPropsElement?.InnerText, out bool skipAutoProps); @@ -240,7 +240,7 @@ private bool ParseSkipAutoProps(XmlElement configurationElement) /// /// Configuration element /// DoesNotReturn attributes - private string[] ParseDoesNotReturnAttributes(XmlElement configurationElement) + private static string[] ParseDoesNotReturnAttributes(XmlElement configurationElement) { XmlElement doesNotReturnAttributesElement = configurationElement[CoverletConstants.DoesNotReturnAttributesElementName]; return SplitElement(doesNotReturnAttributesElement); @@ -251,7 +251,7 @@ private string[] ParseDoesNotReturnAttributes(XmlElement configurationElement) /// /// The element to split /// An array of the values in the element - private string[] SplitElement(XmlElement element) + private static string[] SplitElement(XmlElement element) { return element?.InnerText?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(value => !string.IsNullOrWhiteSpace(value)).Select(value => value.Trim()).ToArray(); } diff --git a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs index 0ab4066a0..f682b2687 100644 --- a/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs +++ b/src/coverlet.collector/InProcDataCollection/CoverletInProcDataCollector.cs @@ -17,7 +17,7 @@ namespace Coverlet.Collector.DataCollection public class CoverletInProcDataCollector : InProcDataCollection { private TestPlatformEqtTrace _eqtTrace; - private bool _enableExceptionLog = false; + private bool _enableExceptionLog; private void AttachDebugger() { diff --git a/src/coverlet.console/Logging/ConsoleLogger.cs b/src/coverlet.console/Logging/ConsoleLogger.cs index 1752ddc43..1e41af504 100644 --- a/src/coverlet.console/Logging/ConsoleLogger.cs +++ b/src/coverlet.console/Logging/ConsoleLogger.cs @@ -9,7 +9,8 @@ namespace Coverlet.Console.Logging { class ConsoleLogger : ILogger { - private static readonly object _sync = new object(); + private static readonly object s_sync = new(); + public LogLevel Level { get; set; } = LogLevel.Normal; public void LogError(string message) => Log(LogLevel.Quiet, message, ConsoleColor.Red); @@ -26,7 +27,7 @@ private void Log(LogLevel level, string message, ConsoleColor color) { if (level < Level) return; - lock (_sync) + lock (s_sync) { ConsoleColor currentForegroundColor; if (color != (currentForegroundColor = ForegroundColor)) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index ecd9a834d..b53cdf5d8 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -330,7 +330,7 @@ private bool BranchInCompilerGeneratedClass(string methodName) return false; } - private Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine) + private static Method GetMethodWithSameLineInSameDocument(Classes documentClasses, string compilerGeneratedClassName, int branchLine) { foreach (KeyValuePair @class in documentClasses) { diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index d546ecc27..70dd7b677 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -18,7 +18,7 @@ namespace Coverlet.Core.Helpers internal class InstrumentationHelper : IInstrumentationHelper { private const int RetryAttempts = 12; - private readonly ConcurrentDictionary _backupList = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _backupList = new(); private readonly IRetryHelper _retryHelper; private readonly IFileSystem _fileSystem; private readonly ISourceRootTranslator _sourceRootTranslator; @@ -83,27 +83,25 @@ public string[] GetCoverableModules(string moduleOrAppDirectory, string[] direct public bool HasPdb(string module, out bool embedded) { embedded = false; - using (Stream moduleStream = _fileSystem.OpenRead(module)) - using (var peReader = new PEReader(moduleStream)) + using Stream moduleStream = _fileSystem.OpenRead(module); + using var peReader = new PEReader(moduleStream); + foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) { - foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) + if (entry.Type == DebugDirectoryEntryType.CodeView) { - if (entry.Type == DebugDirectoryEntryType.CodeView) + CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); + if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == $"{Path.GetFileNameWithoutExtension(module)}.pdb") { - CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); - if (_sourceRootTranslator.ResolveFilePath(codeViewData.Path) == $"{Path.GetFileNameWithoutExtension(module)}.pdb") - { - // PDB is embedded - embedded = true; - return true; - } - - return _fileSystem.Exists(_sourceRootTranslator.ResolveFilePath(codeViewData.Path)); + // PDB is embedded + embedded = true; + return true; } - } - return false; + return _fileSystem.Exists(_sourceRootTranslator.ResolveFilePath(codeViewData.Path)); + } } + + return false; } public bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNotFoundDocument) @@ -116,17 +114,15 @@ public bool EmbeddedPortablePdbHasLocalSource(string module, out string firstNot { if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb) { - using (MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry)) - { - MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader(); + using MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry); + MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader(); - (bool allDocumentsMatch, string notFoundDocument) = MatchDocumentsWithSources(metadataReader); + (bool allDocumentsMatch, string notFoundDocument) = MatchDocumentsWithSources(metadataReader); - if (!allDocumentsMatch) - { - firstNotFoundDocument = notFoundDocument; - return false; - } + if (!allDocumentsMatch) + { + firstNotFoundDocument = notFoundDocument; + return false; } } } @@ -388,7 +384,7 @@ public void SetLogger(ILogger logger) _logger = logger; } - private bool IsTypeFilterMatch(string module, string type, string[] filters) + private static bool IsTypeFilterMatch(string module, string type, string[] filters) { Debug.Assert(module != null); Debug.Assert(filters != null); @@ -408,7 +404,7 @@ private bool IsTypeFilterMatch(string module, string type, string[] filters) return false; } - private string GetBackupPath(string module, string identifier) + private static string GetBackupPath(string module, string identifier) { return Path.Combine( Path.GetTempPath(), @@ -428,14 +424,14 @@ TimeSpan retryStrategy() return retryStrategy; } - private string WildcardToRegex(string pattern) + private static string WildcardToRegex(string pattern) { return "^" + Regex.Escape(pattern). Replace("\\*", ".*"). Replace("\\?", "?") + "$"; } - private bool IsAssembly(string filePath) + private static bool IsAssembly(string filePath) { Debug.Assert(filePath != null); @@ -453,7 +449,7 @@ private bool IsAssembly(string filePath) } } - private bool IsUnknownModuleInFSharpAssembly(Guid languageGuid, string docName) + private static bool IsUnknownModuleInFSharpAssembly(Guid languageGuid, string docName) { // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PortablePdb-Metadata.md#document-table-0x30 return languageGuid.Equals(new Guid("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3")) diff --git a/src/coverlet.core/Helpers/SourceRootTranslator.cs b/src/coverlet.core/Helpers/SourceRootTranslator.cs index f03c83ff5..4d50fbf81 100644 --- a/src/coverlet.core/Helpers/SourceRootTranslator.cs +++ b/src/coverlet.core/Helpers/SourceRootTranslator.cs @@ -48,7 +48,7 @@ public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem f _sourceToDeterministicPathMapping = LoadSourceToDeterministicPathMapping(_sourceRootMapping); } - private Dictionary> LoadSourceToDeterministicPathMapping(Dictionary> sourceRootMapping) + private static Dictionary> LoadSourceToDeterministicPathMapping(Dictionary> sourceRootMapping) { if (sourceRootMapping is null) { diff --git a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs index 0f89edf5f..6a781485d 100644 --- a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs +++ b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs @@ -17,21 +17,21 @@ namespace Coverlet.Core.Instrumentation /// In case of testing different runtime i.e. netfx we could find netstandard.dll in folder. /// netstandard.dll is a forward only lib, there is no IL but only forwards to "runtime" implementation. /// For some classes implementation are in different assembly for different runtime for instance: - /// + /// /// For NetFx 4.7 /// // Token: 0x2700072C RID: 1836 /// .class extern forwarder System.Security.Cryptography.X509Certificates.StoreName /// { /// .assembly extern System - /// } - /// + /// } + /// /// For netcoreapp2.2 /// Token: 0x2700072C RID: 1836 /// .class extern forwarder System.Security.Cryptography.X509Certificates.StoreName /// { /// .assembly extern System.Security.Cryptography.X509Certificates /// } - /// + /// /// There is a concrete possibility that Cecil cannot find implementation and throws StackOverflow exception https://github.com/jbevain/cecil/issues/575 /// This custom resolver check if requested lib is a "official" netstandard.dll and load once of "current runtime" with /// correct forwards. @@ -39,10 +39,10 @@ namespace Coverlet.Core.Instrumentation /// internal class NetstandardAwareAssemblyResolver : DefaultAssemblyResolver { - private static readonly System.Reflection.Assembly _netStandardAssembly; - private static readonly string _name; - private static readonly byte[] _publicKeyToken; - private static readonly AssemblyDefinition _assemblyDefinition; + private static readonly System.Reflection.Assembly s_netStandardAssembly; + private static readonly string s_name; + private static readonly byte[] s_publicKeyToken; + private static readonly AssemblyDefinition s_assemblyDefinition; private readonly string _modulePath; private readonly Lazy _compositeResolver; @@ -53,11 +53,11 @@ static NetstandardAwareAssemblyResolver() try { // To be sure to load information of "real" runtime netstandard implementation - _netStandardAssembly = System.Reflection.Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll")); - System.Reflection.AssemblyName name = _netStandardAssembly.GetName(); - _name = name.Name; - _publicKeyToken = name.GetPublicKeyToken(); - _assemblyDefinition = AssemblyDefinition.ReadAssembly(_netStandardAssembly.Location); + s_netStandardAssembly = System.Reflection.Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll")); + System.Reflection.AssemblyName name = s_netStandardAssembly.GetName(); + s_name = name.Name; + s_publicKeyToken = name.GetPublicKeyToken(); + s_assemblyDefinition = AssemblyDefinition.ReadAssembly(s_netStandardAssembly.Location); } catch (FileNotFoundException) { @@ -70,7 +70,7 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger) _modulePath = modulePath; _logger = logger; - // this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime, + // this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime, // runtime folders are different _compositeResolver = new Lazy(() => new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[] { @@ -82,26 +82,26 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger) } // Check name and public key but not version that could be different - private bool CheckIfSearchingNetstandard(AssemblyNameReference name) + private static bool CheckIfSearchingNetstandard(AssemblyNameReference name) { - if (_netStandardAssembly is null) + if (s_netStandardAssembly is null) { return false; } - if (_name != name.Name) + if (s_name != name.Name) { return false; } - if (name.PublicKeyToken.Length != _publicKeyToken.Length) + if (name.PublicKeyToken.Length != s_publicKeyToken.Length) { return false; } for (int i = 0; i < name.PublicKeyToken.Length; i++) { - if (_publicKeyToken[i] != name.PublicKeyToken[i]) + if (s_publicKeyToken[i] != name.PublicKeyToken[i]) { return false; } @@ -114,7 +114,7 @@ public override AssemblyDefinition Resolve(AssemblyNameReference name) { if (CheckIfSearchingNetstandard(name)) { - return _assemblyDefinition; + return s_assemblyDefinition; } else { @@ -136,21 +136,21 @@ public override AssemblyDefinition Resolve(AssemblyNameReference name) } } - private bool IsDotNetCore() + private static bool IsDotNetCore() { // object for .NET Framework is inside mscorlib.dll return Path.GetFileName(typeof(object).Assembly.Location) == "System.Private.CoreLib.dll"; } /// - /// + /// /// We try to manually load assembly. /// To work test project needs to use /// /// /// true /// - /// + /// /// Runtime configuration file doc https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md /// /// @@ -202,7 +202,7 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere catch (Exception ex) { // if we don't find a lib go on - _logger.LogVerbose($"TryWithCustomResolverOnDotNetCore exception: {ex.ToString()}"); + _logger.LogVerbose($"TryWithCustomResolverOnDotNetCore exception: {ex}"); } } } @@ -218,8 +218,8 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere internal class AspNetCoreSharedFrameworkResolver : ICompilationAssemblyResolver { - private readonly string[] _aspNetSharedFrameworkDirs = null; - private readonly ILogger _logger = null; + private readonly string[] _aspNetSharedFrameworkDirs; + private readonly ILogger _logger; public AspNetCoreSharedFrameworkResolver(ILogger logger) { diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 9b2ca6fc7..51b18c07c 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -49,7 +49,7 @@ internal class Instrumenter private readonly string[] _doesNotReturnAttributes; private ReachabilityHelper _reachabilityHelper; - public bool SkipModule { get; set; } = false; + public bool SkipModule { get; set; } public Instrumenter( string module, @@ -185,158 +185,150 @@ private bool Is_System_Threading_Interlocked_CoreLib_Type(TypeDefinition type) // locking issues if we do it while writing. private void CreateReachabilityHelper() { - using (Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.Read)) - using (var resolver = new NetstandardAwareAssemblyResolver(_module, _logger)) + using Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.Read); + using var resolver = new NetstandardAwareAssemblyResolver(_module, _logger); + resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); + var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; + if (_isCoreLibrary) { - resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); - var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; - if (_isCoreLibrary) - { - parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider(); - } - - using (var module = ModuleDefinition.ReadModule(stream, parameters)) - { - _reachabilityHelper = ReachabilityHelper.CreateForModule(module, _doesNotReturnAttributes, _logger); - } + parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider(); } + + using var module = ModuleDefinition.ReadModule(stream, parameters); + _reachabilityHelper = ReachabilityHelper.CreateForModule(module, _doesNotReturnAttributes, _logger); } private void InstrumentModule() { CreateReachabilityHelper(); - using (Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.ReadWrite)) - using (var resolver = new NetstandardAwareAssemblyResolver(_module, _logger)) + using Stream stream = _fileSystem.NewFileStream(_module, FileMode.Open, FileAccess.ReadWrite); + using var resolver = new NetstandardAwareAssemblyResolver(_module, _logger); + resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); + var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; + if (_isCoreLibrary) { - resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); - var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; - if (_isCoreLibrary) + parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider(); + } + + using var module = ModuleDefinition.ReadModule(stream, parameters); + foreach (CustomAttribute customAttribute in module.Assembly.CustomAttributes) + { + if (IsExcludeAttribute(customAttribute)) { - parameters.MetadataImporterProvider = new CoreLibMetadataImporterProvider(); + _logger.LogVerbose($"Excluded module: '{module}' for assembly level attribute {customAttribute.AttributeType.FullName}"); + SkipModule = true; + return; } + } - using (var module = ModuleDefinition.ReadModule(stream, parameters)) - { - foreach (CustomAttribute customAttribute in module.Assembly.CustomAttributes) - { - if (IsExcludeAttribute(customAttribute)) - { - _logger.LogVerbose($"Excluded module: '{module}' for assembly level attribute {customAttribute.AttributeType.FullName}"); - SkipModule = true; - return; - } - } + bool containsAppContext = module.GetType(nameof(System), nameof(AppContext)) != null; + IEnumerable types = module.GetTypes(); + AddCustomModuleTrackerToModule(module); - bool containsAppContext = module.GetType(nameof(System), nameof(AppContext)) != null; - IEnumerable types = module.GetTypes(); - AddCustomModuleTrackerToModule(module); + CustomDebugInformation sourceLinkDebugInfo = module.CustomDebugInformations.FirstOrDefault(c => c.Kind == CustomDebugInformationKind.SourceLink); + if (sourceLinkDebugInfo != null) + { + _result.SourceLink = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content; + } - CustomDebugInformation sourceLinkDebugInfo = module.CustomDebugInformations.FirstOrDefault(c => c.Kind == CustomDebugInformationKind.SourceLink); - if (sourceLinkDebugInfo != null) + foreach (TypeDefinition type in types) + { + if ( + !Is_System_Threading_Interlocked_CoreLib_Type(type) && + !IsTypeExcluded(type) && + _instrumentationHelper.IsTypeIncluded(_module, type.FullName, _parameters.IncludeFilters) + ) + { + if (IsSynthesizedMemberToBeExcluded(type)) { - _result.SourceLink = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content; + (_excludedCompilerGeneratedTypes ??= new List()).Add(type.FullName); } - - foreach (TypeDefinition type in types) + else { - if ( - !Is_System_Threading_Interlocked_CoreLib_Type(type) && - !IsTypeExcluded(type) && - _instrumentationHelper.IsTypeIncluded(_module, type.FullName, _parameters.IncludeFilters) - ) - { - if (IsSynthesizedMemberToBeExcluded(type)) - { - (_excludedCompilerGeneratedTypes ??= new List()).Add(type.FullName); - } - else - { - InstrumentType(type); - } - } + InstrumentType(type); } + } + } - // Fixup the custom tracker class constructor, according to all instrumented types - if (_customTrackerRegisterUnloadEventsMethod == null) - { - _customTrackerRegisterUnloadEventsMethod = new MethodReference( - nameof(ModuleTrackerTemplate.RegisterUnloadEvents), module.TypeSystem.Void, _customTrackerTypeDef); - } + // Fixup the custom tracker class constructor, according to all instrumented types + if (_customTrackerRegisterUnloadEventsMethod == null) + { + _customTrackerRegisterUnloadEventsMethod = new MethodReference( + nameof(ModuleTrackerTemplate.RegisterUnloadEvents), module.TypeSystem.Void, _customTrackerTypeDef); + } - Instruction lastInstr = _customTrackerClassConstructorIl.Body.Instructions.Last(); + Instruction lastInstr = _customTrackerClassConstructorIl.Body.Instructions.Last(); - if (!containsAppContext) - { - // For "normal" cases, where the instrumented assembly is not the core library, we add a call to - // RegisterUnloadEvents to the static constructor of the generated custom tracker. Due to static - // initialization constraints, the core library is handled separately below. - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, _customTrackerRegisterUnloadEventsMethod)); - } + if (!containsAppContext) + { + // For "normal" cases, where the instrumented assembly is not the core library, we add a call to + // RegisterUnloadEvents to the static constructor of the generated custom tracker. Due to static + // initialization constraints, the core library is handled separately below. + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Call, _customTrackerRegisterUnloadEventsMethod)); + } - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4, _result.HitCandidates.Count)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Int32)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(_parameters.SingleHit ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerSingleHit)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4_1)); - _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerFlushHitFile)); - - if (containsAppContext) + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4, _result.HitCandidates.Count)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Int32)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsArray)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerHitsFilePath)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(_parameters.SingleHit ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerSingleHit)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Ldc_I4_1)); + _customTrackerClassConstructorIl.InsertBefore(lastInstr, Instruction.Create(OpCodes.Stsfld, _customTrackerFlushHitFile)); + + if (containsAppContext) + { + // Handle the core library by instrumenting System.AppContext.OnProcessExit to directly call + // the UnloadModule method of the custom tracker type. This avoids loops between the static + // initialization of the custom tracker and the static initialization of the hosting AppDomain + // (which for the core library case will be instrumented code). + var eventArgsType = new TypeReference(nameof(System), nameof(EventArgs), module, module.TypeSystem.CoreLibrary); + var customTrackerUnloadModule = new MethodReference(nameof(ModuleTrackerTemplate.UnloadModule), module.TypeSystem.Void, _customTrackerTypeDef); + customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(module.TypeSystem.Object)); + customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(eventArgsType)); + + var appContextType = new TypeReference(nameof(System), nameof(AppContext), module, module.TypeSystem.CoreLibrary); + MethodDefinition onProcessExitMethod = new MethodReference("OnProcessExit", module.TypeSystem.Void, appContextType).Resolve(); + ILProcessor onProcessExitIl = onProcessExitMethod.Body.GetILProcessor(); + + // Put the OnProcessExit body inside try/finally to ensure the call to the UnloadModule. + Instruction lastInst = onProcessExitMethod.Body.Instructions.Last(); + var firstNullParam = Instruction.Create(OpCodes.Ldnull); + var secondNullParam = Instruction.Create(OpCodes.Ldnull); + var callUnload = Instruction.Create(OpCodes.Call, customTrackerUnloadModule); + onProcessExitIl.InsertAfter(lastInst, firstNullParam); + onProcessExitIl.InsertAfter(firstNullParam, secondNullParam); + onProcessExitIl.InsertAfter(secondNullParam, callUnload); + var endFinally = Instruction.Create(OpCodes.Endfinally); + onProcessExitIl.InsertAfter(callUnload, endFinally); + Instruction ret = onProcessExitIl.Create(OpCodes.Ret); + Instruction leaveAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret); + onProcessExitIl.InsertAfter(endFinally, ret); + foreach (Instruction inst in onProcessExitMethod.Body.Instructions.ToArray()) + { + // Patch ret to leave after the finally + if (inst.OpCode == OpCodes.Ret && inst != ret) { - // Handle the core library by instrumenting System.AppContext.OnProcessExit to directly call - // the UnloadModule method of the custom tracker type. This avoids loops between the static - // initialization of the custom tracker and the static initialization of the hosting AppDomain - // (which for the core library case will be instrumented code). - var eventArgsType = new TypeReference(nameof(System), nameof(EventArgs), module, module.TypeSystem.CoreLibrary); - var customTrackerUnloadModule = new MethodReference(nameof(ModuleTrackerTemplate.UnloadModule), module.TypeSystem.Void, _customTrackerTypeDef); - customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(module.TypeSystem.Object)); - customTrackerUnloadModule.Parameters.Add(new ParameterDefinition(eventArgsType)); - - var appContextType = new TypeReference(nameof(System), nameof(AppContext), module, module.TypeSystem.CoreLibrary); - MethodDefinition onProcessExitMethod = new MethodReference("OnProcessExit", module.TypeSystem.Void, appContextType).Resolve(); - ILProcessor onProcessExitIl = onProcessExitMethod.Body.GetILProcessor(); - - // Put the OnProcessExit body inside try/finally to ensure the call to the UnloadModule. - Instruction lastInst = onProcessExitMethod.Body.Instructions.Last(); - var firstNullParam = Instruction.Create(OpCodes.Ldnull); - var secondNullParam = Instruction.Create(OpCodes.Ldnull); - var callUnload = Instruction.Create(OpCodes.Call, customTrackerUnloadModule); - onProcessExitIl.InsertAfter(lastInst, firstNullParam); - onProcessExitIl.InsertAfter(firstNullParam, secondNullParam); - onProcessExitIl.InsertAfter(secondNullParam, callUnload); - var endFinally = Instruction.Create(OpCodes.Endfinally); - onProcessExitIl.InsertAfter(callUnload, endFinally); - Instruction ret = onProcessExitIl.Create(OpCodes.Ret); - Instruction leaveAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret); - onProcessExitIl.InsertAfter(endFinally, ret); - foreach (Instruction inst in onProcessExitMethod.Body.Instructions.ToArray()) - { - // Patch ret to leave after the finally - if (inst.OpCode == OpCodes.Ret && inst != ret) - { - Instruction leaveBodyInstAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret); - Instruction prevInst = inst.Previous; - onProcessExitMethod.Body.Instructions.Remove(inst); - onProcessExitIl.InsertAfter(prevInst, leaveBodyInstAfterFinally); - } - } - var handler = new ExceptionHandler(ExceptionHandlerType.Finally) - { - TryStart = onProcessExitIl.Body.Instructions.First(), - TryEnd = firstNullParam, - HandlerStart = firstNullParam, - HandlerEnd = ret - }; - - onProcessExitMethod.Body.ExceptionHandlers.Add(handler); + Instruction leaveBodyInstAfterFinally = onProcessExitIl.Create(OpCodes.Leave, ret); + Instruction prevInst = inst.Previous; + onProcessExitMethod.Body.Instructions.Remove(inst); + onProcessExitIl.InsertAfter(prevInst, leaveBodyInstAfterFinally); } - - module.Write(stream, new WriterParameters { WriteSymbols = true }); } + var handler = new ExceptionHandler(ExceptionHandlerType.Finally) + { + TryStart = onProcessExitIl.Body.Instructions.First(), + TryEnd = firstNullParam, + HandlerStart = firstNullParam, + HandlerEnd = ret + }; + + onProcessExitMethod.Body.ExceptionHandlers.Add(handler); } + + module.Write(stream, new WriterParameters { WriteSymbols = true }); } private void AddCustomModuleTrackerToModule(ModuleDefinition module) @@ -864,52 +856,52 @@ public IMetadataImporter GetMetadataImporter(ModuleDefinition module) private class CoreLibMetadataImporter : IMetadataImporter { - private readonly ModuleDefinition module; - private readonly DefaultMetadataImporter defaultMetadataImporter; + private readonly ModuleDefinition _module; + private readonly DefaultMetadataImporter _defaultMetadataImporter; public CoreLibMetadataImporter(ModuleDefinition module) { - this.module = module; - defaultMetadataImporter = new DefaultMetadataImporter(module); + _module = module; + _defaultMetadataImporter = new DefaultMetadataImporter(module); } public AssemblyNameReference ImportReference(AssemblyNameReference reference) { - return defaultMetadataImporter.ImportReference(reference); + return _defaultMetadataImporter.ImportReference(reference); } public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) { - TypeReference importedRef = defaultMetadataImporter.ImportReference(type, context); - importedRef.GetElementType().Scope = module.TypeSystem.CoreLibrary; + TypeReference importedRef = _defaultMetadataImporter.ImportReference(type, context); + importedRef.GetElementType().Scope = _module.TypeSystem.CoreLibrary; return importedRef; } public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) { - FieldReference importedRef = defaultMetadataImporter.ImportReference(field, context); - importedRef.FieldType.GetElementType().Scope = module.TypeSystem.CoreLibrary; + FieldReference importedRef = _defaultMetadataImporter.ImportReference(field, context); + importedRef.FieldType.GetElementType().Scope = _module.TypeSystem.CoreLibrary; return importedRef; } public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) { - MethodReference importedRef = defaultMetadataImporter.ImportReference(method, context); - importedRef.DeclaringType.GetElementType().Scope = module.TypeSystem.CoreLibrary; + MethodReference importedRef = _defaultMetadataImporter.ImportReference(method, context); + importedRef.DeclaringType.GetElementType().Scope = _module.TypeSystem.CoreLibrary; foreach (ParameterDefinition parameter in importedRef.Parameters) { - if (parameter.ParameterType.Scope == module.TypeSystem.CoreLibrary) + if (parameter.ParameterType.Scope == _module.TypeSystem.CoreLibrary) { continue; } - parameter.ParameterType.GetElementType().Scope = module.TypeSystem.CoreLibrary; + parameter.ParameterType.GetElementType().Scope = _module.TypeSystem.CoreLibrary; } - if (importedRef.ReturnType.Scope != module.TypeSystem.CoreLibrary) + if (importedRef.ReturnType.Scope != _module.TypeSystem.CoreLibrary) { - importedRef.ReturnType.GetElementType().Scope = module.TypeSystem.CoreLibrary; + importedRef.ReturnType.GetElementType().Scope = _module.TypeSystem.CoreLibrary; } return importedRef; diff --git a/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs b/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs index b538143be..600fe91b1 100644 --- a/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs +++ b/src/coverlet.core/Instrumentation/ModuleTrackerTemplate.cs @@ -26,8 +26,8 @@ internal static class ModuleTrackerTemplate public static int[] HitsArray; public static bool SingleHit; public static bool FlushHitFile; - private static readonly bool _enableLog = int.TryParse(Environment.GetEnvironmentVariable("COVERLET_ENABLETRACKERLOG"), out int result) ? result == 1 : false; - private static readonly string _sessionId = Guid.NewGuid().ToString(); + private static readonly bool s_enableLog = int.TryParse(Environment.GetEnvironmentVariable("COVERLET_ENABLETRACKERLOG"), out int result) && result == 1; + private static readonly string s_sessionId = Guid.NewGuid().ToString(); static ModuleTrackerTemplate() { @@ -83,100 +83,94 @@ public static void UnloadModule(object sender, EventArgs e) { // The same module can be unloaded multiple times in the same process via different app domains. // Use a global mutex to ensure no concurrent access. - using (var mutex = new Mutex(true, Path.GetFileNameWithoutExtension(HitsFilePath) + "_Mutex", out bool createdNew)) + using var mutex = new Mutex(true, Path.GetFileNameWithoutExtension(HitsFilePath) + "_Mutex", out bool createdNew); + if (!createdNew) { - if (!createdNew) - { - mutex.WaitOne(); - } + mutex.WaitOne(); + } - if (FlushHitFile) + if (FlushHitFile) + { + try { - try - { - // Claim the current hits array and reset it to prevent double-counting scenarios. - int[] hitsArray = Interlocked.Exchange(ref HitsArray, new int[HitsArray.Length]); + // Claim the current hits array and reset it to prevent double-counting scenarios. + int[] hitsArray = Interlocked.Exchange(ref HitsArray, new int[HitsArray.Length]); - WriteLog($"Unload called for '{Assembly.GetExecutingAssembly().Location}' by '{sender ?? "null"}'"); - WriteLog($"Flushing hit file '{HitsFilePath}'"); + WriteLog($"Unload called for '{Assembly.GetExecutingAssembly().Location}' by '{sender ?? "null"}'"); + WriteLog($"Flushing hit file '{HitsFilePath}'"); - bool failedToCreateNewHitsFile = false; - try + bool failedToCreateNewHitsFile = false; + try + { + using var fs = new FileStream(HitsFilePath, FileMode.CreateNew); + using var bw = new BinaryWriter(fs); + bw.Write(hitsArray.Length); + foreach (int hitCount in hitsArray) { - using (var fs = new FileStream(HitsFilePath, FileMode.CreateNew)) - using (var bw = new BinaryWriter(fs)) - { - bw.Write(hitsArray.Length); - foreach (int hitCount in hitsArray) - { - bw.Write(hitCount); - } - } + bw.Write(hitCount); } - catch (Exception ex) + } + catch (Exception ex) + { + WriteLog($"Failed to create new hits file '{HitsFilePath}' -> '{ex.Message}'"); + failedToCreateNewHitsFile = true; + } + + if (failedToCreateNewHitsFile) + { + // Update the number of hits by adding value on disk with the ones on memory. + // This path should be triggered only in the case of multiple AppDomain unloads. + using var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + using var br = new BinaryReader(fs); + using var bw = new BinaryWriter(fs); + int hitsLength = br.ReadInt32(); + WriteLog($"Current hits found '{hitsLength}'"); + + if (hitsLength != hitsArray.Length) { - WriteLog($"Failed to create new hits file '{HitsFilePath}' -> '{ex.Message}'"); - failedToCreateNewHitsFile = true; + throw new InvalidOperationException($"{HitsFilePath} has {hitsLength} entries but on memory {nameof(HitsArray)} has {hitsArray.Length}"); } - if (failedToCreateNewHitsFile) + for (int i = 0; i < hitsLength; ++i) { - // Update the number of hits by adding value on disk with the ones on memory. - // This path should be triggered only in the case of multiple AppDomain unloads. - using (var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) - using (var br = new BinaryReader(fs)) - using (var bw = new BinaryWriter(fs)) + int oldHitCount = br.ReadInt32(); + bw.Seek(-sizeof(int), SeekOrigin.Current); + if (SingleHit) + { + bw.Write(hitsArray[i] + oldHitCount > 0 ? 1 : 0); + } + else { - int hitsLength = br.ReadInt32(); - WriteLog($"Current hits found '{hitsLength}'"); - - if (hitsLength != hitsArray.Length) - { - throw new InvalidOperationException($"{HitsFilePath} has {hitsLength} entries but on memory {nameof(HitsArray)} has {hitsArray.Length}"); - } - - for (int i = 0; i < hitsLength; ++i) - { - int oldHitCount = br.ReadInt32(); - bw.Seek(-sizeof(int), SeekOrigin.Current); - if (SingleHit) - { - bw.Write(hitsArray[i] + oldHitCount > 0 ? 1 : 0); - } - else - { - bw.Write(hitsArray[i] + oldHitCount); - } - } + bw.Write(hitsArray[i] + oldHitCount); } } + } - WriteHits(sender); + WriteHits(sender); - WriteLog($"Hit file '{HitsFilePath}' flushed, size {new FileInfo(HitsFilePath).Length}"); - WriteLog("--------------------------------"); - } - catch (Exception ex) - { - WriteLog(ex.ToString()); - throw; - } + WriteLog($"Hit file '{HitsFilePath}' flushed, size {new FileInfo(HitsFilePath).Length}"); + WriteLog("--------------------------------"); + } + catch (Exception ex) + { + WriteLog(ex.ToString()); + throw; } - - // On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file - // this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll. - mutex.ReleaseMutex(); } + + // On purpose this is not under a try-finally: it is better to have an exception if there was any error writing the hits file + // this case is relevant when instrumenting corelib since multiple processes can be running against the same instrumented dll. + mutex.ReleaseMutex(); } private static void WriteHits(object sender) { - if (_enableLog) + if (s_enableLog) { var currentAssembly = Assembly.GetExecutingAssembly(); var location = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(currentAssembly.Location), "TrackersHitsLog")); location.Create(); - string logFile = Path.Combine(location.FullName, $"{Path.GetFileName(currentAssembly.Location)}_{DateTime.UtcNow.Ticks}_{_sessionId}.txt"); + string logFile = Path.Combine(location.FullName, $"{Path.GetFileName(currentAssembly.Location)}_{DateTime.UtcNow.Ticks}_{s_sessionId}.txt"); using (var fs = new FileStream(HitsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) using (var log = new FileStream(logFile, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None)) using (var logWriter = new StreamWriter(log)) @@ -195,12 +189,12 @@ private static void WriteHits(object sender) private static void WriteLog(string logText) { - if (_enableLog) + if (s_enableLog) { // We don't set path as global var to keep benign possible errors inside try/catch // I'm not sure that location will be ok in every scenario string location = Assembly.GetExecutingAssembly().Location; - File.AppendAllText(Path.Combine(Path.GetDirectoryName(location), Path.GetFileName(location) + "_tracker.txt"), $"[{DateTime.UtcNow} S:{_sessionId} T:{Thread.CurrentThread.ManagedThreadId}]{logText}{Environment.NewLine}"); + File.AppendAllText(Path.Combine(Path.GetDirectoryName(location), Path.GetFileName(location) + "_tracker.txt"), $"[{DateTime.UtcNow} S:{s_sessionId} T:{Thread.CurrentThread.ManagedThreadId}]{logText}{Environment.NewLine}"); } } } diff --git a/src/coverlet.core/Instrumentation/ReachabilityHelper.cs b/src/coverlet.core/Instrumentation/ReachabilityHelper.cs index dee555a21..1b4840d85 100644 --- a/src/coverlet.core/Instrumentation/ReachabilityHelper.cs +++ b/src/coverlet.core/Instrumentation/ReachabilityHelper.cs @@ -100,9 +100,9 @@ private readonly struct BranchInstruction /// /// Returns true if this branch has multiple targets. /// - public bool HasMultiTargets => _TargetOffset == -1; + public bool HasMultiTargets => _targetOffset == -1; - private readonly int _TargetOffset; + private readonly int _targetOffset; /// /// Target of the branch, assuming it has a single target. @@ -118,11 +118,11 @@ public int TargetOffset throw new InvalidOperationException($"{HasMultiTargets} is true"); } - return _TargetOffset; + return _targetOffset; } } - private readonly ImmutableArray _TargetOffsets; + private readonly ImmutableArray _targetOffsets; /// /// Targets of the branch, assuming it has multiple targets. @@ -138,15 +138,15 @@ public ImmutableArray TargetOffsets throw new InvalidOperationException($"{HasMultiTargets} is false"); } - return _TargetOffsets; + return _targetOffsets; } } public BranchInstruction(int offset, int targetOffset) { Offset = offset; - _TargetOffset = targetOffset; - _TargetOffsets = ImmutableArray.Empty; + _targetOffset = targetOffset; + _targetOffsets = ImmutableArray.Empty; } public BranchInstruction(int offset, ImmutableArray targetOffset) @@ -157,8 +157,8 @@ public BranchInstruction(int offset, ImmutableArray targetOffset) } Offset = offset; - _TargetOffset = -1; - _TargetOffsets = targetOffset; + _targetOffset = -1; + _targetOffsets = targetOffset; } public override string ToString() @@ -169,7 +169,7 @@ public override string ToString() /// OpCodes that transfer control code, even if they do not /// introduce branch points. /// - private static readonly ImmutableHashSet BRANCH_OPCODES = + private static readonly ImmutableHashSet s_branchOpCodes = ImmutableHashSet.CreateRange( new[] { @@ -224,7 +224,7 @@ public override string ToString() /// OpCodes that unconditionally transfer control, so there /// is not "fall through" branch target. /// - private static readonly ImmutableHashSet UNCONDITIONAL_BRANCH_OPCODES = + private static readonly ImmutableHashSet s_unconditionalBranchOpCodes = ImmutableHashSet.CreateRange( new[] { @@ -235,11 +235,11 @@ public override string ToString() } ); - private readonly ImmutableHashSet DoesNotReturnMethods; + private readonly ImmutableHashSet _doesNotReturnMethods; private ReachabilityHelper(ImmutableHashSet doesNotReturnMethods) { - DoesNotReturnMethods = doesNotReturnMethods; + _doesNotReturnMethods = doesNotReturnMethods; } /// @@ -372,7 +372,7 @@ public ImmutableArray FindUnreachableIL(Collection.Empty; } @@ -406,7 +406,7 @@ public ImmutableArray FindUnreachableIL(Collection multiTargetOffsets) = GetInstructionTargets(i, exceptionHandlers); @@ -453,7 +453,7 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets) { // it's any of the B.*(_S)? or Leave(_S)? instructions - if (UNCONDITIONAL_BRANCH_OPCODES.Contains(i.OpCode)) + if (s_unconditionalBranchOpCodes.Contains(i.OpCode)) { multiTargetOffsets = ImmutableArray.Empty; singleTargetOffset = targetInstr.Offset; @@ -527,7 +527,7 @@ private static (int? SingleTargetOffset, ImmutableArray MultiTargetOffsets) /// /// Calculates which ranges of IL are unreachable, given blocks which have head and tail reachability calculated. /// - private ImmutableArray DetermineUnreachableRanges(ImmutableArray blocks, int lastInstructionOffset) + private static ImmutableArray DetermineUnreachableRanges(ImmutableArray blocks, int lastInstructionOffset) { ImmutableArray.Builder ret = ImmutableArray.CreateBuilder(); @@ -580,7 +580,7 @@ private ImmutableArray DetermineUnreachableRanges(ImmutableArr /// /// "Tail reachability" will have already been determined in CreateBlocks. /// - private void DetermineHeadReachability(ImmutableArray blocks) + private static void DetermineHeadReachability(ImmutableArray blocks) { var blockLookup = blocks.ToImmutableDictionary(b => b.StartOffset); @@ -791,7 +791,7 @@ private bool DoesNotReturn(Instruction instr) return false; } - return DoesNotReturnMethods.Contains(mtd.MetadataToken); + return _doesNotReturnMethods.Contains(mtd.MetadataToken); } /// diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 0daff6a69..57571df9a 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Toni Solarin-Sodara +// Copyright (c) Toni Solarin-Sodara // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; @@ -111,7 +111,7 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran { var condition = new XElement("condition"); condition.Add(new XAttribute("number", entry.Key)); - condition.Add(new XAttribute("type", entry.Value.Count() > 2 ? "switch" : "jump")); // Just guessing here + condition.Add(new XAttribute("type", entry.Value.Count > 2 ? "switch" : "jump")); // Just guessing here condition.Add(new XAttribute("coverage", $"{summary.CalculateBranchCoverage(entry.Value).Percent.ToString(CultureInfo.InvariantCulture)}%")); conditions.Add(condition); } @@ -119,7 +119,6 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran line.Add(conditions); } - lines.Add(line); classLines.Add(line); } @@ -234,4 +233,4 @@ private static string GetRelativePathFromBase(IEnumerable basePaths, str return path; } } -} \ No newline at end of file +} diff --git a/src/coverlet.core/Reporters/TeamCityReporter.cs b/src/coverlet.core/Reporters/TeamCityReporter.cs index 365f20e46..8ccb0d997 100644 --- a/src/coverlet.core/Reporters/TeamCityReporter.cs +++ b/src/coverlet.core/Reporters/TeamCityReporter.cs @@ -39,7 +39,7 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran return stringBuilder.ToString(); } - private void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder builder) + private static void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder builder) { // The number of covered lines OutputTeamCityServiceMessage("CodeCoverageAbsLCovered", coverageDetails.Covered, builder); @@ -48,7 +48,7 @@ private void OutputLineCoverage(CoverageDetails coverageDetails, StringBuilder b OutputTeamCityServiceMessage("CodeCoverageAbsLTotal", coverageDetails.Total, builder); } - private void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder builder) + private static void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder builder) { // The number of covered branches OutputTeamCityServiceMessage("CodeCoverageAbsBCovered", coverageDetails.Covered, builder); @@ -57,7 +57,7 @@ private void OutputBranchCoverage(CoverageDetails coverageDetails, StringBuilder OutputTeamCityServiceMessage("CodeCoverageAbsBTotal", coverageDetails.Total, builder); } - private void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder builder) + private static void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder builder) { // The number of covered methods OutputTeamCityServiceMessage("CodeCoverageAbsMCovered", coverageDetails.Covered, builder); @@ -66,7 +66,7 @@ private void OutputMethodCoverage(CoverageDetails coverageDetails, StringBuilder OutputTeamCityServiceMessage("CodeCoverageAbsMTotal", coverageDetails.Total, builder); } - private void OutputTeamCityServiceMessage(string key, double value, StringBuilder builder) + private static void OutputTeamCityServiceMessage(string key, double value, StringBuilder builder) { builder.AppendLine($"##teamcity[buildStatisticValue key='{key}' value='{value.ToString("0.##", new CultureInfo("en-US"))}']"); } diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs index 23789124a..a718c82bb 100644 --- a/src/coverlet.core/Symbols/CecilSymbolHelper.cs +++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs @@ -20,8 +20,8 @@ internal class CecilSymbolHelper : ICecilSymbolHelper { private const int StepOverLineCode = 0xFEEFEE; // Create single instance, we cannot collide because we use full method name as key - private readonly ConcurrentDictionary _compilerGeneratedBranchesToExclude = new ConcurrentDictionary(); - private readonly ConcurrentDictionary> _sequencePointOffsetToSkip = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _compilerGeneratedBranchesToExclude = new(); + private readonly ConcurrentDictionary> _sequencePointOffsetToSkip = new(); // In case of nested compiler generated classes, only the root one presents the CompilerGenerated attribute. // So let's search up to the outermost declaring type to find the attribute @@ -83,7 +83,8 @@ private static bool IsMoveNextInsideEnumerator(MethodDefinition methodDefinition { return false; } - if (methodDefinition.DeclaringType.CustomAttributes.Count(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName) > 0) + + if (methodDefinition.DeclaringType.CustomAttributes.Any(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName)) { foreach (InterfaceImplementation implementedInterface in methodDefinition.DeclaringType.Interfaces) { @@ -465,7 +466,6 @@ private static bool SkipGeneratedBranchesForAwaitForeach(List instr CheckIfExceptionThrown(instructions, instruction, currentIndex) || CheckThrownExceptionType(instructions, instruction, currentIndex); - // The pattern for the "should we stay in the loop or not?", which we don't // want to skip (so we have no method to try to find it), looks like this: // @@ -477,7 +477,6 @@ private static bool SkipGeneratedBranchesForAwaitForeach(List instr // the "call" and branch, but it's the same idea either way: branch // if GetResult() returned true. - static bool CheckForAsyncEnumerator(List instructions, Instruction instruction, int currentIndex) { // We're looking for the following pattern, which checks whether a @@ -506,7 +505,6 @@ static bool CheckForAsyncEnumerator(List instructions, Instruction return false; } - static bool CheckIfExceptionThrown(List instructions, Instruction instruction, int currentIndex) { // Here, we want to find a pattern where we're checking whether a @@ -564,7 +562,6 @@ instructions[j].Operand is MethodReference callRef && return false; } - static bool CheckThrownExceptionType(List instructions, Instruction instruction, int currentIndex) { // In this case, we're looking for a branch generated by the compiler to @@ -617,7 +614,6 @@ private static bool SkipGeneratedBranchesForAwaitUsing(List instruc return CheckForSkipDisposal(instructions, instruction, currentIndex) || CheckForCleanup(instructions, instruction, currentIndex); - static bool CheckForSkipDisposal(List instructions, Instruction instruction, int currentIndex) { // The async state machine generated for an "await using" contains a branch @@ -727,7 +723,6 @@ instructions[i].Operand is FieldDefinition reloadedField && return false; } - static bool CheckForCleanup(List instructions, Instruction instruction, int currentIndex) { // The pattern we're looking for here is this: @@ -812,7 +807,6 @@ private static bool SkipGeneratedBranchesForAsyncIterator(List inst return CheckForStateSwitch(instructions, instruction, currentIndex) || DisposeCheck(instructions, instruction, currentIndex); - static bool CheckForStateSwitch(List instructions, Instruction instruction, int currentIndex) { // The pattern we're looking for here is this one: @@ -890,7 +884,7 @@ static bool DisposeCheck(List instructions, Instruction instruction } } - private bool SkipGeneratedBranchesForEnumeratorCancellationAttribute(List instructions, Instruction instruction) + private static bool SkipGeneratedBranchesForEnumeratorCancellationAttribute(List instructions, Instruction instruction) { // For async-enumerable methods an additional cancellation token despite the default one can be passed. // The EnumeratorCancellation attribute marks the parameter whose value is received by GetAsyncEnumerator(CancellationToken). diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 642d2a92b..23a29f7a5 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -152,9 +152,9 @@ public override bool Execute() if (Threshold.Contains(',')) { IEnumerable thresholdValues = Threshold.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()); - if (thresholdValues.Count() != thresholdTypeFlagQueue.Count()) + if (thresholdValues.Count() != thresholdTypeFlagQueue.Count) { - throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count()}) and values count ({thresholdValues.Count()}) doesn't match"); + throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match"); } foreach (string threshold in thresholdValues) diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index e84327dd0..2ec4910da 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -112,13 +112,9 @@ public override bool Execute() CoveragePrepareResult prepareResult = coverage.PrepareModules(); InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName()); - using (Stream instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write)) - { - using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult)) - { - serializedState.CopyTo(instrumentedStateFile); - } - } + using Stream instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write); + using Stream serializedState = CoveragePrepareResult.Serialize(prepareResult); + serializedState.CopyTo(instrumentedStateFile); } catch (Exception ex) { diff --git a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs index 0e3286e06..191bdb232 100644 --- a/test/coverlet.collector.tests/CoverletSettingsParserTests.cs +++ b/test/coverlet.collector.tests/CoverletSettingsParserTests.cs @@ -188,14 +188,14 @@ public void ParseShouldUseDefaultFormatWhenNoFormatSpecified(string formats) Assert.Equal(defaultFormat, coverletSettings.ReportFormats[0]); } - private void CreateCoverletNodes(XmlDocument doc, XmlElement configElement, string nodeSetting, string nodeValue) + private static void CreateCoverletNodes(XmlDocument doc, XmlElement configElement, string nodeSetting, string nodeValue) { XmlNode node = doc.CreateNode("element", nodeSetting, string.Empty); node.InnerText = nodeValue; configElement.AppendChild(node); } - private void CreateCoverletNullInnerTextNodes(XmlDocument doc, XmlElement configElement, string nodeSetting) + private static void CreateCoverletNullInnerTextNodes(XmlDocument doc, XmlElement configElement, string nodeSetting) { XmlNode node = doc.CreateNode("element", nodeSetting, string.Empty); node.InnerText = null; diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.cs b/test/coverlet.core.tests/Coverage/CoverageTests.cs index e6bf461eb..06be97c72 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.cs @@ -13,7 +13,7 @@ namespace Coverlet.Core.Tests { public partial class CoverageTests { - private readonly Mock _mockLogger = new Mock(); + private readonly Mock _mockLogger = new(); [Fact] public void TestCoverage() diff --git a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs index efc0138a8..8cb5eca7f 100644 --- a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs +++ b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs @@ -23,7 +23,7 @@ namespace Coverlet.Core.Tests { static class TestInstrumentationHelper { - private static IServiceProvider _processWideContainer; + private static IServiceProvider s_processWideContainer; /// /// caller sample: TestInstrumentationHelper.GenerateHtmlReport(result, sourceFileFilter: @"+**\Samples\Instrumentation.cs"); @@ -64,9 +64,9 @@ public static CoverageResult GetCoverageResult(string filePath) { Assert.DoesNotContain("not found for module: ", message); }); - _processWideContainer.GetRequiredService().SetLogger(logger.Object); + s_processWideContainer.GetRequiredService().SetLogger(logger.Object); var coveragePrepareResultLoaded = CoveragePrepareResult.Deserialize(result); - var coverage = new Coverage(coveragePrepareResultLoaded, logger.Object, _processWideContainer.GetService(), new FileSystem(), new SourceRootTranslator(new Mock().Object, new FileSystem())); + var coverage = new Coverage(coveragePrepareResultLoaded, logger.Object, s_processWideContainer.GetService(), new FileSystem(), new SourceRootTranslator(new Mock().Object, new FileSystem())); return coverage.GetCoverageResult(); } @@ -121,7 +121,7 @@ public static async Task Run(Func callM // Instrument module var coverage = new Coverage(newPath, parameters, new Logger(logFile), - _processWideContainer.GetService(), _processWideContainer.GetService(), _processWideContainer.GetService(), _processWideContainer.GetService()); + s_processWideContainer.GetService(), s_processWideContainer.GetService(), s_processWideContainer.GetService(), s_processWideContainer.GetService()); CoveragePrepareResult prepareResult = coverage.PrepareModules(); Assert.Single(prepareResult.Results); @@ -153,7 +153,7 @@ public static async Task Run(Func callM private static void SetTestContainer(string testModule = null, bool disableRestoreModules = false) { - LazyInitializer.EnsureInitialized(ref _processWideContainer, () => + LazyInitializer.EnsureInitialized(ref s_processWideContainer, () => { var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); @@ -291,7 +291,7 @@ public override void RestoreOriginalModules() public abstract class ExternalProcessExecutionTest { - protected FunctionExecutor FunctionExecutor = new FunctionExecutor( + protected FunctionExecutor FunctionExecutor = new( o => { o.StartInfo.RedirectStandardError = true; diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 031b8a5d0..d0883ae61 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -15,7 +15,7 @@ namespace Coverlet.Core.Helpers.Tests public class InstrumentationHelperTests { private readonly InstrumentationHelper _instrumentationHelper = - new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem())); + new(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock().Object, new SourceRootTranslator(typeof(InstrumentationHelperTests).Assembly.Location, new Mock().Object, new FileSystem())); [Fact] public void TestGetDependencies() diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 907f5cc66..189648834 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -26,12 +26,12 @@ namespace Coverlet.Core.Instrumentation.Tests { public class InstrumenterTests : IDisposable { - private readonly Mock _mockLogger = new Mock(); - private Action disposeAction; + private readonly Mock _mockLogger = new(); + private Action _disposeAction; public void Dispose() { - disposeAction?.Invoke(); + _disposeAction?.Invoke(); } [ConditionalFact] @@ -192,9 +192,7 @@ public void TestInstrument_ClassesWithMethodWithCustomExcludeAttributeAreExclude Document doc = result.Documents.Values.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); Assert.NotNull(doc); -#pragma warning disable CS0612 // Type or member is obsolete bool found = doc.Lines.Values.Any(l => l.Method.Equals($"System.String Coverlet.Core.Samples.Tests.{testClassName}::Method(System.String)")); -#pragma warning restore CS0612 // Type or member is obsolete Assert.False(found, "Method decorated with with exclude attribute should be excluded"); instrumenterTest.Directory.Delete(true); @@ -211,14 +209,10 @@ public void TestInstrument_ClassesWithPropertyWithCustomExcludeAttributeAreExclu Document doc = result.Documents.Values.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); Assert.NotNull(doc); -#pragma warning disable CS0612 // Type or member is obsolete bool getFound = doc.Lines.Values.Any(l => l.Method.Equals($"System.String Coverlet.Core.Samples.Tests.{testClassName}::get_Property()")); -#pragma warning restore CS0612 // Type or member is obsolete Assert.False(getFound, "Property getter decorated with with exclude attribute should be excluded"); -#pragma warning disable CS0612 // Type or member is obsolete bool setFound = doc.Lines.Values.Any(l => l.Method.Equals($"System.String Coverlet.Core.Samples.Tests.{testClassName}::set_Property()")); -#pragma warning restore CS0612 // Type or member is obsolete Assert.False(setFound, "Property setter decorated with with exclude attribute should be excluded"); instrumenterTest.Directory.Delete(true); @@ -587,7 +581,7 @@ public int SampleMethod() string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); - disposeAction = () => Directory.Delete(tempDirectory, true); + _disposeAction = () => Directory.Delete(tempDirectory, true); var partialMockFileSystem = new Mock(); partialMockFileSystem.CallBase = true; diff --git a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs index c0b8dd282..7cd5f2943 100644 --- a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs +++ b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs @@ -30,7 +30,7 @@ public void Dispose() public class ModuleTrackerTemplateTests : ExternalProcessExecutionTest { - private static readonly Task _success = Task.FromResult(0); + private static readonly Task s_success = Task.FromResult(0); [Fact] public void HitsFileCorrectlyWritten() @@ -44,7 +44,7 @@ public void HitsFileCorrectlyWritten() int[] expectedHitsArray = new[] { 1, 2, 0, 3 }; Assert.Equal(expectedHitsArray, ReadHitsFile()); - return _success; + return s_success; }); } @@ -57,7 +57,7 @@ public void HitsFileWithDifferentNumberOfEntriesCausesExceptionOnUnload() WriteHitsFile(new[] { 1, 2, 3 }); ModuleTrackerTemplate.HitsArray = new[] { 1 }; Assert.Throws(() => ModuleTrackerTemplate.UnloadModule(null, null)); - return _success; + return s_success; }); } @@ -94,7 +94,7 @@ static void HitIndex(object index) } } - return _success; + return s_success; }); } @@ -113,7 +113,7 @@ public void MultipleSequentialUnloadsHaveCorrectTotalData() int[] expectedHitsArray = new[] { 0, 4, 4, 4 }; Assert.Equal(expectedHitsArray, ReadHitsFile()); - return _success; + return s_success; }); } @@ -149,32 +149,28 @@ public void MutexBlocksMultipleWriters() } - private void WriteHitsFile(int[] hitsArray) + private static void WriteHitsFile(int[] hitsArray) { - using (var fs = new FileStream(ModuleTrackerTemplate.HitsFilePath, FileMode.Create)) - using (var bw = new BinaryWriter(fs)) + using var fs = new FileStream(ModuleTrackerTemplate.HitsFilePath, FileMode.Create); + using var bw = new BinaryWriter(fs); + bw.Write(hitsArray.Length); + foreach (int hitCount in hitsArray) { - bw.Write(hitsArray.Length); - foreach (int hitCount in hitsArray) - { - bw.Write(hitCount); - } + bw.Write(hitCount); } } - private int[] ReadHitsFile() + private static int[] ReadHitsFile() { - using (var fs = new FileStream(ModuleTrackerTemplate.HitsFilePath, FileMode.Open)) - using (var br = new BinaryReader(fs)) + using var fs = new FileStream(ModuleTrackerTemplate.HitsFilePath, FileMode.Open); + using var br = new BinaryReader(fs); + int[] hitsArray = new int[br.ReadInt32()]; + for (int i = 0; i < hitsArray.Length; ++i) { - int[] hitsArray = new int[br.ReadInt32()]; - for (int i = 0; i < hitsArray.Length; ++i) - { - hitsArray[i] = br.ReadInt32(); - } - - return hitsArray; + hitsArray[i] = br.ReadInt32(); } + + return hitsArray; } } } diff --git a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs index 7ea79155b..8b6e46c6d 100644 --- a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs +++ b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs @@ -41,7 +41,7 @@ public void GetBranchPoints_OneBranch() // assert Assert.NotNull(points); - Assert.Equal(2, points.Count()); + Assert.Equal(2, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(0, points[0].Path); Assert.Equal(1, points[1].Path); @@ -61,7 +61,7 @@ public void GetBranchPoints_Using_Where_GeneratedBranchesIgnored() // act System.Collections.Generic.IReadOnlyList points = _cecilSymbolHelper.GetBranchPoints(method); - Assert.Equal(2, points.Count()); + Assert.Equal(2, points.Count); } [Fact] @@ -89,7 +89,7 @@ public void GetBranchPoints_TwoBranch() // assert Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[2].Offset, points[3].Offset); Assert.Equal(28, points[0].StartLine); @@ -108,7 +108,7 @@ public void GetBranchPoints_CompleteIf() // assert Assert.NotNull(points); - Assert.Equal(2, points.Count()); + Assert.Equal(2, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(35, points[0].StartLine); Assert.Equal(35, points[1].StartLine); @@ -127,7 +127,7 @@ public void GetBranchPoints_Switch() // assert Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); @@ -150,7 +150,7 @@ public void GetBranchPoints_SwitchWithDefault() // assert Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); @@ -173,7 +173,7 @@ public void GetBranchPoints_SwitchWithBreaks() // assert Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); @@ -196,7 +196,7 @@ public void GetBranchPoints_SwitchWithMultipleCases() // assert Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(points[0].Offset, points[3].Offset); @@ -351,7 +351,7 @@ public void GetBranchPoints_IgnoresMostBranchesIn_AwaitForeachStateMachine() // We do expect there to be a two-way branch (stay in the loop or not?) on // the line containing "await foreach". Assert.NotNull(points); - Assert.Equal(2, points.Count()); + Assert.Equal(2, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(204, points[0].StartLine); Assert.Equal(204, points[1].StartLine); @@ -375,7 +375,7 @@ public void GetBranchPoints_IgnoresMostBranchesIn_AwaitForeachStateMachine_WithB // containing "await foreach" and the other being the "if" statement inside // the loop. Assert.NotNull(points); - Assert.Equal(4, points.Count()); + Assert.Equal(4, points.Count); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[2].Offset, points[3].Offset); Assert.Equal(219, points[0].StartLine); @@ -399,7 +399,7 @@ public void GetBranchPoints_IgnoresExtraBranchesIn_AsyncIteratorStateMachine() // assert // We do expect the "for" loop to be a branch with two branch points, but that's it. Assert.NotNull(points); - Assert.Equal(2, points.Count()); + Assert.Equal(2, points.Count); Assert.Equal(237, points[0].StartLine); Assert.Equal(237, points[1].StartLine); } diff --git a/test/coverlet.integration.tests/BaseTest.cs b/test/coverlet.integration.tests/BaseTest.cs index 6072740c7..284373df3 100644 --- a/test/coverlet.integration.tests/BaseTest.cs +++ b/test/coverlet.integration.tests/BaseTest.cs @@ -24,7 +24,7 @@ public enum BuildConfiguration public abstract class BaseTest { - private static int _folderSuffix = 0; + private static int s_folderSuffix; protected BuildConfiguration GetAssemblyBuildConfiguration() { @@ -53,7 +53,7 @@ private protected string GetPackageVersion(string filter) private protected ClonedTemplateProject CloneTemplateProject(bool cleanupOnDispose = true, string testSDKVersion = "16.5.0") { - DirectoryInfo finalRoot = Directory.CreateDirectory($"{Guid.NewGuid().ToString("N").Substring(0, 6)}{Interlocked.Increment(ref _folderSuffix)}"); + DirectoryInfo finalRoot = Directory.CreateDirectory($"{Guid.NewGuid().ToString("N")[..6]}{Interlocked.Increment(ref s_folderSuffix)}"); foreach (string file in (Directory.GetFiles($"../../../../coverlet.integration.template", "*.cs") .Union(Directory.GetFiles($"../../../../coverlet.integration.template", "*.csproj") .Union(Directory.GetFiles($"../../../../coverlet.integration.template", "nuget.config")))))