Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable nullable for TestAdapter project #1370

Merged
merged 5 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public AssemblyEnumerator(MSTestSettings settings)
/// <summary>
/// Gets or sets the run settings to use for current discovery session.
/// </summary>
public string RunSettingsXml { get; set; }
public string? RunSettingsXml { get; set; }

/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
Expand All @@ -73,7 +73,7 @@ public AssemblyEnumerator(MSTestSettings settings)
#endif
public override object InitializeLifetimeService()
{
return null;
return null!;
}

/// <summary>
Expand All @@ -84,9 +84,8 @@ public override object InitializeLifetimeService()
/// <returns>A collection of Test Elements.</returns>
internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName, out ICollection<string> warnings)
{
Debug.Assert(!string.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name.");
DebugEx.Assert(!StringEx.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name.");

var runSettingsXml = RunSettingsXml;
var warningMessages = new List<string>();
var tests = new List<UnitTestElement>();

Expand All @@ -113,7 +112,7 @@ internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName,
continue;
}

var testsInType = DiscoverTestsInType(assemblyFileName, runSettingsXml, assembly, type, warningMessages,
var testsInType = DiscoverTestsInType(assemblyFileName, RunSettingsXml, assembly, type, warningMessages,
discoverInternals, testDataSourceDiscovery, testIdGenerationStrategy);
tests.AddRange(testsInType);
}
Expand All @@ -129,7 +128,7 @@ internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName,
/// <param name="assemblyFileName">The file name of the assembly.</param>
/// <param name="warningMessages">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>Gets the types defined in the provided assembly.</returns>
internal static Type[] GetTypes(Assembly assembly, string assemblyFileName, ICollection<string> warningMessages)
internal static Type?[] GetTypes(Assembly assembly, string assemblyFileName, ICollection<string>? warningMessages)
{
var types = new List<Type>();
try
Expand All @@ -154,7 +153,7 @@ internal static Type[] GetTypes(Assembly assembly, string assemblyFileName, ICol
}
}

return ex.Types;
return ex.Types!;
}

return types.ToArray();
Expand All @@ -167,17 +166,17 @@ internal static Type[] GetTypes(Assembly assembly, string assemblyFileName, ICol
/// <returns>Returns loader exceptions as a multi-line string.</returns>
internal static string GetLoadExceptionDetails(ReflectionTypeLoadException ex)
{
Debug.Assert(ex != null, "exception should not be null.");
DebugEx.Assert(ex != null, "exception should not be null.");

var map = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); // Exception -> null.
var map = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase); // Exception -> null.
var errorDetails = new StringBuilder();

if (ex.LoaderExceptions?.Length > 0)
{
// Loader exceptions can contain duplicates, leave only unique exceptions.
foreach (var loaderException in ex.LoaderExceptions)
{
Debug.Assert(loaderException != null, "loader exception should not be null.");
DebugEx.Assert(loaderException != null, "loader exception should not be null.");
var line = string.Format(CultureInfo.CurrentCulture, Resource.EnumeratorLoadTypeErrorFormat, loaderException.GetType(), loaderException.Message);
if (!map.ContainsKey(line))
{
Expand Down Expand Up @@ -211,16 +210,16 @@ internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFile
return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator, testIdGenerationStrategy);
}

private IEnumerable<UnitTestElement> DiscoverTestsInType(string assemblyFileName, string runSettingsXml, Assembly assembly,
private IEnumerable<UnitTestElement> DiscoverTestsInType(string assemblyFileName, string? runSettingsXml, Assembly assembly,
Type type, List<string> warningMessages, bool discoverInternals, TestDataSourceDiscoveryOption discoveryOption,
TestIdGenerationStrategy testIdGenerationStrategy)
{
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(assemblyFileName);
sourceLevelParameters = RunSettingsUtilities.GetTestRunParameters(runSettingsXml)?.ConcatWithOverwrites(sourceLevelParameters)
?? sourceLevelParameters
?? new Dictionary<string, object>();
?? new Dictionary<string, object?>();

string typeFullName = null;
string? typeFullName = null;
var tests = new List<UnitTestElement>();

try
Expand All @@ -241,7 +240,7 @@ internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFile
{
if (discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery)
{
if (DynamicDataAttached(sourceLevelParameters, assembly, test, tests))
if (DynamicDataAttached(sourceLevelParameters, test, tests))
{
continue;
}
Expand All @@ -263,7 +262,7 @@ internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFile
return tests;
}

private bool DynamicDataAttached(IDictionary<string, object> sourceLevelParameters, Assembly assembly, UnitTestElement test, List<UnitTestElement> tests)
private bool DynamicDataAttached(IDictionary<string, object?> sourceLevelParameters, UnitTestElement test, List<UnitTestElement> tests)
{
// It should always be `true`, but if any part of the chain is obsolete; it might not contain those.
// Since we depend on those properties, if they don't exist, we bail out early.
Expand Down Expand Up @@ -387,7 +386,7 @@ private static bool TryProcessTestDataSourceTests(UnitTestElement test, TestMeth
// If strategy is DisplayName and we have a duplicate test name don't expand the test, bail out.
#pragma warning disable CS0618 // Type or member is obsolete
if (test.TestMethod.TestIdGenerationStrategy == TestIdGenerationStrategy.DisplayName
&& testDisplayNameFirstSeen.TryGetValue(discoveredTest.DisplayName, out var firstIndexSeen))
&& testDisplayNameFirstSeen.TryGetValue(discoveredTest.DisplayName!, out var firstIndexSeen))
{
var warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_DuplicateDisplayName, firstIndexSeen, index, discoveredTest.DisplayName);
warning = string.Format(CultureInfo.CurrentUICulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning);
Expand Down Expand Up @@ -416,7 +415,7 @@ private static bool TryProcessTestDataSourceTests(UnitTestElement test, TestMeth
}

discoveredTests.Add(discoveredTest);
testDisplayNameFirstSeen[discoveredTest.DisplayName] = index++;
testDisplayNameFirstSeen[discoveredTest.DisplayName!] = index++;
}

tests.AddRange(discoveredTests);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ internal class AssemblyEnumeratorWrapper
/// <param name="runSettings"> The run Settings. </param>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> A collection of test elements. </returns>
internal ICollection<UnitTestElement> GetTests(string assemblyFileName, IRunSettings runSettings, out ICollection<string> warnings)
internal ICollection<UnitTestElement>? GetTests(string? assemblyFileName, IRunSettings? runSettings, out ICollection<string> warnings)
{
warnings = new List<string>();

if (string.IsNullOrEmpty(assemblyFileName))
if (StringEx.IsNullOrEmpty(assemblyFileName))
{
return null;
}
Expand Down Expand Up @@ -101,14 +101,15 @@ internal ICollection<UnitTestElement> GetTests(string assemblyFileName, IRunSett
}
}

private static ICollection<UnitTestElement> GetTestsInIsolation(string fullFilePath, IRunSettings runSettings, out ICollection<string> warnings)
private static ICollection<UnitTestElement> GetTestsInIsolation(string fullFilePath, IRunSettings? runSettings, out ICollection<string> warnings)
{
using var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null);

// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var assemblyEnumerator = isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings }) as AssemblyEnumerator;
var assemblyEnumerator = (AssemblyEnumerator)isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings })!;

// This might not be supported if an older version of "PlatformServices" (Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices) assembly is already loaded into the App Domain.
// This might not be supported if an older version of Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
// assembly is already loaded into the App Domain.
try
{
assemblyEnumerator.RunSettingsXml = runSettings?.SettingsXml;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, IC
// Generic method Definitions are not valid.
if (testMethodInfo.IsGenericMethodDefinition)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType.FullName, testMethodInfo.Name);
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType!.FullName, testMethodInfo.Name);
warnings.Add(message);
return false;
}
Expand Down
11 changes: 5 additions & 6 deletions src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
Expand Down Expand Up @@ -52,7 +51,7 @@ internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflec
/// </summary>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> list of test cases.</returns>
internal virtual ICollection<UnitTestElement> Enumerate(out ICollection<string> warnings)
internal virtual ICollection<UnitTestElement>? Enumerate(out ICollection<string> warnings)
{
warnings = new Collection<string>();

Expand Down Expand Up @@ -109,7 +108,7 @@ internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)

while (currentType != null)
{
inheritanceDepths[currentType.FullName] = currentDepth;
inheritanceDepths[currentType.FullName!] = currentDepth;
++currentDepth;
currentType = currentType.GetTypeInfo().BaseType;
}
Expand All @@ -132,14 +131,14 @@ internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInTestTypeAssembly, ICollection<string> warnings)
{
// null if the current instance represents a generic type parameter.
Debug.Assert(_type.AssemblyQualifiedName != null, "AssemblyQualifiedName for method is null.");
DebugEx.Assert(_type.AssemblyQualifiedName != null, "AssemblyQualifiedName for method is null.");

// This allows void returning async test method to be valid test method. Though they will be executed similar to non-async test method.
var isAsync = ReflectHelper.MatchReturnType(method, typeof(Task));

var testMethod = new TestMethod(method, method.Name, _type.FullName, _assemblyFilePath, isAsync, _testIdGenerationStrategy);
var testMethod = new TestMethod(method, method.Name, _type.FullName!, _assemblyFilePath, isAsync, _testIdGenerationStrategy);

if (!method.DeclaringType.FullName.Equals(_type.FullName))
if (!string.Equals(method.DeclaringType!.FullName, _type.FullName))
{
testMethod.DeclaringClassFullName = method.DeclaringType.FullName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ internal virtual bool IsValidTestClass(Type type, ICollection<string> warnings)
/// <returns>Returns true if type has a valid TestContext property definition.</returns>
internal static bool HasCorrectTestContextSignature(Type type)
{
Debug.Assert(type != null, "HasCorrectTestContextSignature type is null");
DebugEx.Assert(type != null, "HasCorrectTestContextSignature type is null");

var propertyInfoEnumerable = type.GetTypeInfo().DeclaredProperties;
var propertyInfo = new List<PropertyInfo>();
Expand Down
15 changes: 8 additions & 7 deletions src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
internal class UnitTestDiscoverer
Expand Down Expand Up @@ -55,7 +56,7 @@ internal UnitTestDiscoverer()
string source,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink,
IDiscoveryContext discoveryContext)
IDiscoveryContext? discoveryContext)
{
var testElements = _assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out var warnings);

Expand Down Expand Up @@ -84,11 +85,11 @@ internal UnitTestDiscoverer()
SendTestCases(source, testElements, discoverySink, discoveryContext, logger);
}

internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext discoveryContext, IMessageLogger logger)
internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext? discoveryContext, IMessageLogger logger)
{
var shouldCollectSourceInformation = MSTestSettings.RunConfigurationSettings.CollectSourceInformation;

var navigationSessions = new Dictionary<string, object>();
var navigationSessions = new Dictionary<string, object?>();
try
{
if (shouldCollectSourceInformation)
Expand All @@ -97,7 +98,7 @@ internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElem
}

// Get filter expression and skip discovery in case filter expression has parsing error.
ITestCaseFilterExpression filterExpression = TestMethodFilter.GetFilterExpression(discoveryContext, logger, out var filterHasError);
ITestCaseFilterExpression? filterExpression = TestMethodFilter.GetFilterExpression(discoveryContext, logger, out var filterHasError);
if (filterHasError)
{
return;
Expand Down Expand Up @@ -139,7 +140,7 @@ internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElem
var methodName = testElement.TestMethod.Name;

// If it is async test method use compiler generated type and method name for navigation data.
if (!string.IsNullOrEmpty(testElement.AsyncTypeName))
if (!StringEx.IsNullOrEmpty(testElement.AsyncTypeName))
{
className = testElement.AsyncTypeName;

Expand All @@ -154,7 +155,7 @@ internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElem
out var minLineNumber,
out var fileName);

if (!string.IsNullOrEmpty(fileName))
if (!StringEx.IsNullOrEmpty(fileName))
{
testCase.LineNumber = minLineNumber;
testCase.CodeFilePath = fileName;
Expand All @@ -165,7 +166,7 @@ internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElem
}
finally
{
foreach (object navigationSession in navigationSessions.Values)
foreach (object? navigationSession in navigationSessions.Values)
{
PlatformServiceProvider.Instance.FileOperations.DisposeNavigationSession(navigationSession);
}
Expand Down