From 854792080f6b4b7bd945f5b9d52e7a4997df1a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 24 May 2022 16:50:35 +0200 Subject: [PATCH 1/3] Enable nullable for EventLogCollector --- .../CollectorNameValueConfigurationManager.cs | 8 +- .../EventLogCollectorException.cs | 4 +- .../EventLogConstants.cs | 2 - .../EventLogContainer.cs | 10 +- .../EventLogDataCollector.cs | 70 ++++---- .../EventLogSessionContext.cs | 7 +- .../EventLogXmlWriter.cs | 2 - .../IEventLogContainer.cs | 4 +- .../NullableAttributes.cs | 153 ++++++++++++++++++ .../PublicAPI/PublicAPI.Shipped.txt | 3 +- .../TPDebug.cs | 22 +++ 11 files changed, 228 insertions(+), 57 deletions(-) create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/NullableAttributes.cs create mode 100644 src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/TPDebug.cs diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs index efb9f4aa11..ee48cbe243 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs @@ -6,8 +6,6 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// @@ -32,7 +30,7 @@ internal class CollectorNameValueConfigurationManager /// /// XML element containing the configuration /// - public CollectorNameValueConfigurationManager(XmlElement configurationElement) + public CollectorNameValueConfigurationManager(XmlElement? configurationElement) { if (configurationElement == null) { @@ -79,14 +77,14 @@ public CollectorNameValueConfigurationManager(XmlElement configurationElement) } } - internal IDictionary NameValuePairs { get; } = new Dictionary(); + internal IDictionary NameValuePairs { get; } = new Dictionary(); /// /// Gets the value of the setting specified by name, or null if it was not found /// /// The setting name /// The setting value, or null if the setting was not found - public string this[string name] + public string? this[string name] { get { diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs index 772145a95f..f6f706f979 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs @@ -3,8 +3,6 @@ using System; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// @@ -17,7 +15,7 @@ internal class EventLogCollectorException : Exception /// /// the localized exception message /// the inner exception - public EventLogCollectorException(string localizedMessage, Exception innerException) + public EventLogCollectorException(string? localizedMessage, Exception? innerException) : base(localizedMessage, innerException) { } diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs index 0618af0e2b..02fba1296f 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs index b50d790856..33049428e5 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs @@ -11,8 +11,6 @@ using Resource = Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// @@ -20,14 +18,14 @@ namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// internal class EventLogContainer : IEventLogContainer { - private readonly ISet _eventSources; + private readonly ISet? _eventSources; private readonly ISet _entryTypes; private readonly int _maxLogEntries; private readonly DataCollectionLogger _dataCollectionLogger; - private readonly DataCollectionContext _dataCollectionContext; + private readonly DataCollectionContext? _dataCollectionContext; /// /// Keeps track of if we are disposed. @@ -55,7 +53,7 @@ internal class EventLogContainer : IEventLogContainer /// /// Data Collection Context /// - public EventLogContainer(string eventLogName, ISet eventSources, ISet entryTypes, int maxLogEntries, DataCollectionLogger dataCollectionLogger, DataCollectionContext dataCollectionContext) + public EventLogContainer(string eventLogName, ISet? eventSources, ISet entryTypes, int maxLogEntries, DataCollectionLogger dataCollectionLogger, DataCollectionContext? dataCollectionContext) { EventLog = new EventLog(eventLogName); EventLog.EnableRaisingEvents = true; @@ -119,7 +117,7 @@ public void Dispose() /// /// Source /// The System.Diagnostics.EntryWrittenEventArgs object describing the entry that was written. - public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e) + public void OnEventLogEntryWritten(object? source, EntryWrittenEventArgs? e) { while (!LimitReached) { diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs index 497059efa0..07ab76c4ac 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -16,8 +17,6 @@ using Resource = Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// @@ -65,22 +64,22 @@ public class EventLogDataCollector : DataCollector /// /// Object containing the execution events the data collector registers for /// - private DataCollectionEvents _events; + private DataCollectionEvents? _events; /// /// The sink used by the data collector to send its data /// - private DataCollectionSink _dataSink; + private DataCollectionSink? _dataSink; /// /// The data collector context. /// - private DataCollectionContext _dataCollectorContext; + private DataCollectionContext? _dataCollectorContext; /// /// Used by the data collector to send warnings, errors, or other messages /// - private DataCollectionLogger _logger; + private DataCollectionLogger? _logger; /// /// The file helper. @@ -120,18 +119,17 @@ internal EventLogDataCollector(IFileHelper fileHelper) internal int MaxEntries { get; private set; } - internal ISet EventSources { get; private set; } + internal ISet? EventSources { get; private set; } - internal ISet EntryTypes { get; private set; } + internal ISet? EntryTypes { get; private set; } - internal ISet EventLogNames { get; private set; } + internal ISet? EventLogNames { get; private set; } /// /// Gets the context data. /// internal Dictionary ContextMap { get; } - #region DataCollector Members /// @@ -149,12 +147,13 @@ internal EventLogDataCollector(IFileHelper fileHelper) /// Used by the data collector to send warnings, errors, or other messages /// /// Provides contextual information about the agent environment + [MemberNotNull(nameof(_events), nameof(_dataSink), nameof(_logger))] public override void Initialize( - XmlElement configurationElement, + XmlElement? configurationElement, DataCollectionEvents events!!, DataCollectionSink dataSink!!, DataCollectionLogger logger!!, - DataCollectionEnvironmentContext dataCollectionEnvironmentContext) + DataCollectionEnvironmentContext dataCollectionEnvironmentContext!!) { _events = events; _dataSink = dataSink; @@ -224,7 +223,7 @@ internal string WriteEventLogs(List eventLogEntries, int maxLogEn { for (int i = 1; !unusedFilenameFound; i++) { - eventLogPath = eventLogBasePath + "-" + i.ToString(CultureInfo.InvariantCulture) + ".xml"; + eventLogPath = $"{eventLogBasePath}-{i.ToString(CultureInfo.InvariantCulture)}.xml"; if (!_fileHelper.Exists(eventLogPath)) { @@ -267,6 +266,7 @@ internal string WriteEventLogs(List eventLogEntries, int maxLogEn // Write the event log file FileTransferInformation fileTransferInformation = new(dataCollectionContext, eventLogPath, true, _fileHelper); + TPDebug.Assert(_dataSink != null, "Initialize should have been called."); _dataSink.SendFileAsync(fileTransferInformation); EqtTrace.Verbose( @@ -287,10 +287,13 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); // Unregister events - _events.SessionStart -= _sessionStartEventHandler; - _events.SessionEnd -= _sessionEndEventHandler; - _events.TestCaseStart -= _testCaseStartEventHandler; - _events.TestCaseEnd -= _testCaseEndEventHandler; + if (_events != null) + { + _events.SessionStart -= _sessionStartEventHandler; + _events.SessionEnd -= _sessionEndEventHandler; + _events.TestCaseStart -= _testCaseStartEventHandler; + _events.TestCaseEnd -= _testCaseEndEventHandler; + } // Unregister EventLogEntry Written. foreach (var eventLogContainer in _eventLogContainerMap.Values) @@ -352,7 +355,7 @@ private void OnTestCaseStart(object sender, TestCaseStartEventArgs e!!) private void OnTestCaseEnd(object sender, TestCaseEndEventArgs e!!) { Debug.Assert(e.Context != null, "Context is null"); - Debug.Assert(e.Context.HasTestCase, "Context is not for a test case"); + Debug.Assert(e.Context!.HasTestCase, "Context is not for a test case"); EqtTrace.Verbose( "EventLogDataCollector: TestCaseEnd received for test '{0}' with Test Outcome: {1}.", @@ -402,6 +405,7 @@ private void WriteCollectedEventLogEntries( kvp.Value.EventLog.EnableRaisingEvents = false; } + // REVIEW ME: We are initializing for (int i = context.EventLogContainerStartIndexMap[kvp.Key]; i <= context.EventLogContainerEndIndexMap[kvp.Key]; i++) { eventLogEntries.Add(kvp.Value.EventLogEntries[i]); @@ -409,6 +413,7 @@ private void WriteCollectedEventLogEntries( } catch (Exception e) { + TPDebug.Assert(_logger != null, "Initialize should have been called."); _logger.LogWarning( dataCollectionContext, string.Format( @@ -430,15 +435,16 @@ private void WriteCollectedEventLogEntries( } } + [MemberNotNull(nameof(EventLogNames))] private void ConfigureEventLogNames(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) { EventLogNames = new HashSet(); - string eventLogs = collectorNameValueConfigurationManager[EventLogConstants.SettingEventLogs]; + string? eventLogs = collectorNameValueConfigurationManager[EventLogConstants.SettingEventLogs]; if (eventLogs != null) { EventLogNames = ParseCommaSeparatedList(eventLogs); EqtTrace.Verbose( - "EventLogDataCollector configuration: " + EventLogConstants.SettingEventLogs + "=" + eventLogs); + $"EventLogDataCollector configuration: {EventLogConstants.SettingEventLogs}={eventLogs}"); } else { @@ -447,6 +453,8 @@ private void ConfigureEventLogNames(CollectorNameValueConfigurationManager colle EventLogNames.Add("Application"); } + TPDebug.Assert(_logger != null && EntryTypes != null, "Initialize should have been called."); + foreach (string eventLogName in EventLogNames) { try @@ -470,27 +478,27 @@ private void ConfigureEventLogNames(CollectorNameValueConfigurationManager colle { _logger.LogError( _dataCollectorContext, - new EventLogCollectorException(string.Format(CultureInfo.InvariantCulture, Resource.ReadError, eventLogName, Environment.MachineName), ex)); + new EventLogCollectorException(string.Format(CultureInfo.CurrentCulture, Resource.ReadError, eventLogName, Environment.MachineName), ex)); } } } private void ConfigureEventSources(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) { - string eventSourcesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEventSources]; + string? eventSourcesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEventSources]; if (!string.IsNullOrEmpty(eventSourcesStr)) { - EventSources = ParseCommaSeparatedList(eventSourcesStr); + EventSources = ParseCommaSeparatedList(eventSourcesStr!); EqtTrace.Verbose( - "EventLogDataCollector configuration: " + EventLogConstants.SettingEventSources + "=" - + EventSources); + $"EventLogDataCollector configuration: {EventLogConstants.SettingEventSources}={EventSources}"); } } + [MemberNotNull(nameof(EntryTypes))] private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) { EntryTypes = new HashSet(); - string entryTypesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEntryTypes]; + string? entryTypesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEntryTypes]; if (entryTypesStr != null) { foreach (string entryTypestring in ParseCommaSeparatedList(entryTypesStr)) @@ -500,8 +508,7 @@ private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collecto } EqtTrace.Verbose( - "EventLogDataCollector configuration: " + EventLogConstants.SettingEntryTypes + "=" - + EntryTypes); + $"EventLogDataCollector configuration: {EventLogConstants.SettingEntryTypes}={EntryTypes}"); } else { @@ -513,7 +520,7 @@ private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collecto private void ConfigureMaxEntries(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager) { - string maxEntriesstring = collectorNameValueConfigurationManager[EventLogConstants.SettingMaxEntries]; + string? maxEntriesstring = collectorNameValueConfigurationManager[EventLogConstants.SettingMaxEntries]; if (maxEntriesstring != null) { try @@ -532,8 +539,7 @@ private void ConfigureMaxEntries(CollectorNameValueConfigurationManager collecto } EqtTrace.Verbose( - "EventLogDataCollector configuration: " + EventLogConstants.SettingMaxEntries + "=" - + MaxEntries); + $"EventLogDataCollector configuration: {EventLogConstants.SettingMaxEntries}={MaxEntries}"); } else { @@ -553,7 +559,7 @@ private EventLogSessionContext GetEventLogSessionContext(DataCollectionContext d if (!eventLogContainerFound) { string msg = string.Format( - CultureInfo.InvariantCulture, + CultureInfo.CurrentCulture, Resource.ContextNotFoundException, dataCollectionContext.ToString()); throw new EventLogCollectorException(msg, null); diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs index a79de930ec..9b396ea3f7 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs @@ -2,8 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; - -#nullable disable +using System.Diagnostics.CodeAnalysis; namespace Microsoft.TestPlatform.Extensions.EventLogCollector; @@ -34,11 +33,12 @@ public EventLogSessionContext(IDictionary eventLogCo /// /// Gets the end index for EventLogs Entries /// - internal Dictionary EventLogContainerEndIndexMap { get; private set; } + internal Dictionary? EventLogContainerEndIndexMap { get; private set; } /// /// Creates the end index map for EventLogs Entries /// + [MemberNotNull(nameof(EventLogContainerEndIndexMap))] public void CreateEventLogContainerEndIndexMap() { EventLogContainerEndIndexMap = new Dictionary(_eventLogContainerMap.Count); @@ -54,6 +54,7 @@ public void CreateEventLogContainerEndIndexMap() /// /// Creates the start index map for EventLogs Entries /// + [MemberNotNull(nameof(EventLogContainerStartIndexMap))] public void CreateEventLogContainerStartIndexMap() { EventLogContainerStartIndexMap = new Dictionary(_eventLogContainerMap.Count); diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs index 8d38424003..29e8279d44 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs @@ -11,8 +11,6 @@ using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs index 849ca4e2d6..20ad2a6647 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs @@ -5,8 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; -#nullable disable - namespace Microsoft.TestPlatform.Extensions.EventLogCollector; /// @@ -33,5 +31,5 @@ internal interface IEventLogContainer : IDisposable /// /// Contains data related to EventLog entry. /// - void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e); + void OnEventLogEntryWritten(object? source, EntryWrittenEventArgs? e); } diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/NullableAttributes.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/NullableAttributes.cs new file mode 100644 index 0000000000..91a8acbef5 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/NullableAttributes.cs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This was copied from https://github.com/dotnet/coreclr/blob/60f1e6265bd1039f023a82e0643b524d6aaf7845/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// and updated to have the scope of the attributes be internal. + +namespace System.Diagnostics.CodeAnalysis; + +#if NETFRAMEWORK || WINDOWS_UWP || NETSTANDARD && !NETSTANDARD2_1 || NETCOREAPP && !NETCOREAPP3_0_OR_GREATER + +/// Specifies that null is allowed as an input even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class AllowNullAttribute : Attribute { } + +/// Specifies that null is disallowed as an input even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class DisallowNullAttribute : Attribute { } + +/// Specifies that an output may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class MaybeNullAttribute : Attribute { } + +/// Specifies that an output will not be null even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class NotNullAttribute : Attribute { } + +/// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class MaybeNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} + +/// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} + +/// Specifies that the output will be non-null if the named parameter is non-null. +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +internal sealed class NotNullIfNotNullAttribute : Attribute +{ + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } +} + +/// Applied to a method that will never return under any circumstance. +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class DoesNotReturnAttribute : Attribute { } + +/// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class DoesNotReturnIfAttribute : Attribute +{ + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } +} + +/// Specifies that the method or property will ensure that the listed field and property members have not-null values. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullAttribute : Attribute +{ + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } +} + +/// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } +} + +/// +/// Secret attribute that tells the CA1062 validate arguments rule that this method validates the argument is not null. +/// +[AttributeUsage(AttributeTargets.Parameter)] +internal sealed class ValidatedNotNullAttribute : Attribute +{ +} + +#endif diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/PublicAPI/PublicAPI.Shipped.txt b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/PublicAPI/PublicAPI.Shipped.txt index b4355ee4a3..31068b5e95 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/PublicAPI/PublicAPI.Shipped.txt +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,5 @@ +#nullable enable Microsoft.TestPlatform.Extensions.EventLogCollector.EventLogDataCollector Microsoft.TestPlatform.Extensions.EventLogCollector.EventLogDataCollector.EventLogDataCollector() -> void override Microsoft.TestPlatform.Extensions.EventLogCollector.EventLogDataCollector.Dispose(bool disposing) -> void -override Microsoft.TestPlatform.Extensions.EventLogCollector.EventLogDataCollector.Initialize(System.Xml.XmlElement configurationElement, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionEvents events, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionSink dataSink, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionLogger logger, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionEnvironmentContext dataCollectionEnvironmentContext) -> void \ No newline at end of file +override Microsoft.TestPlatform.Extensions.EventLogCollector.EventLogDataCollector.Initialize(System.Xml.XmlElement? configurationElement, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionEvents! events, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionSink! dataSink, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionLogger! logger, Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection.DataCollectionEnvironmentContext! dataCollectionEnvironmentContext) -> void diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/TPDebug.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/TPDebug.cs new file mode 100644 index 0000000000..dd1ecabd33 --- /dev/null +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/TPDebug.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; + +using SystemDebug = System.Diagnostics.Debug; + +namespace Microsoft.TestPlatform; + +internal static class TPDebug +{ + /// + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool b) + => SystemDebug.Assert(b); + + /// + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool b, string message) + => SystemDebug.Assert(b, message); +} From 1d7c4c68266f253bb3f41b0f91584bb586b3e432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 24 May 2022 16:55:15 +0200 Subject: [PATCH 2/3] Update code --- .../EventLogDataCollector.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs index 07ab76c4ac..c065b4e6be 100644 --- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs +++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs @@ -354,8 +354,8 @@ private void OnTestCaseStart(object sender, TestCaseStartEventArgs e!!) private void OnTestCaseEnd(object sender, TestCaseEndEventArgs e!!) { - Debug.Assert(e.Context != null, "Context is null"); - Debug.Assert(e.Context!.HasTestCase, "Context is not for a test case"); + TPDebug.Assert(e.Context != null, "Context is null"); + TPDebug.Assert(e.Context.HasTestCase, "Context is not for a test case"); EqtTrace.Verbose( "EventLogDataCollector: TestCaseEnd received for test '{0}' with Test Outcome: {1}.", @@ -405,7 +405,6 @@ private void WriteCollectedEventLogEntries( kvp.Value.EventLog.EnableRaisingEvents = false; } - // REVIEW ME: We are initializing for (int i = context.EventLogContainerStartIndexMap[kvp.Key]; i <= context.EventLogContainerEndIndexMap[kvp.Key]; i++) { eventLogEntries.Add(kvp.Value.EventLogEntries[i]); From c49681c6be50b3889096ba76145cf1de28eb7cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 26 May 2022 11:47:21 +0200 Subject: [PATCH 3/3] Fix warnings --- .../EventLogDataCollectorTests.cs | 6 +++--- .../EventLogSessionContextTests.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs index bd6172a6df..8195658cf7 100644 --- a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogDataCollectorTests.cs @@ -61,7 +61,7 @@ public void InitializeShouldThrowExceptionIfEventsIsNull() Assert.ThrowsException( () => _eventLogDataCollector.Initialize( null, - null, + null!, _mockDataCollectionSink, _mockDataCollectionLogger.Object, _dataCollectionEnvironmentContext)); @@ -74,7 +74,7 @@ public void InitializeShouldThrowExceptionIfCollectionSinkIsNull() () => _eventLogDataCollector.Initialize( null, _mockDataCollectionEvents.Object, - null, + null!, _mockDataCollectionLogger.Object, _dataCollectionEnvironmentContext)); } @@ -87,7 +87,7 @@ public void InitializeShouldThrowExceptionIfLoggerIsNull() null, _mockDataCollectionEvents.Object, _mockDataCollectionSink, - null, + null!, _dataCollectionEnvironmentContext)); } diff --git a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs index 3d0100dc13..cb5e7dd4dd 100644 --- a/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs +++ b/test/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests/EventLogSessionContextTests.cs @@ -90,7 +90,7 @@ public void Dispose() public List EventLogEntries { get; set; } - public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e) + public void OnEventLogEntryWritten(object? source, EntryWrittenEventArgs? e) { } }