diff --git a/NUnitConsole.sln b/NUnitConsole.sln index 24c9bd494..61aecb8b9 100644 --- a/NUnitConsole.sln +++ b/NUnitConsole.sln @@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution NetCoreTests.nunit = NetCoreTests.nunit NOTICES.txt = NOTICES.txt NuGet.config = NuGet.config + src\Nullable.props = src\Nullable.props nunit.ico = nunit.ico package-tests.cake = package-tests.cake README.md = README.md diff --git a/build.cake b/build.cake index 5dc053fe0..b7c5ccfcc 100644 --- a/build.cake +++ b/build.cake @@ -12,7 +12,7 @@ BuildSettings.Initialize( githubRepository: "nunit-console", solutionFile: "NUnitConsole.sln", buildWithMSBuild: true, - exemptFiles: new[] { "Options.cs", "ProcessUtils.cs", "ProcessUtilsTests.cs" }); + exemptFiles: new[] { "Options.cs", "ProcessUtils.cs", "ProcessUtilsTests.cs", "CallerArgumentExpressionAttribute.cs" }); ////////////////////////////////////////////////////////////////////// // INDIVIDUAL PACKAGE DEFINITIONS diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 6364dbe24..0561b6fae 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,6 +7,7 @@ true NUnit Software Copyright (c) 2022 Charlie Poole, Rob Prouse + false @@ -15,4 +16,4 @@ 3.99.0.0-VSIDE - \ No newline at end of file + diff --git a/src/NUnitConsole/nunit4-console.tests/CommandLineTests.cs b/src/NUnitConsole/nunit4-console.tests/CommandLineTests.cs index 691e6ec86..2a5c3a615 100644 --- a/src/NUnitConsole/nunit4-console.tests/CommandLineTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/CommandLineTests.cs @@ -160,7 +160,7 @@ public void CanRecognizeBooleanOptions(string propertyName, string pattern) string[] prototypes = pattern.Split('|'); PropertyInfo property = GetPropertyInfo(propertyName); - Assert.That(property.PropertyType, Is.EqualTo(typeof(bool)), "Property '{0}' is wrong type", propertyName); + Assert.That(property.PropertyType, Is.EqualTo(typeof(bool)), $"Property '{propertyName}' is wrong type"); foreach (string option in prototypes) { @@ -169,22 +169,22 @@ public void CanRecognizeBooleanOptions(string propertyName, string pattern) if (option.Length == 1) { options = ConsoleMocks.Options("-" + option); - Assert.That((bool)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize -" + option); + Assert.That((bool?)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize -" + option); options = ConsoleMocks.Options("-" + option + "+"); - Assert.That((bool)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize -" + option + "+"); + Assert.That((bool?)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize -" + option + "+"); options = ConsoleMocks.Options("-" + option + "-"); - Assert.That((bool)property.GetValue(options, null), Is.EqualTo(false), "Didn't recognize -" + option + "-"); + Assert.That((bool?)property.GetValue(options, null), Is.EqualTo(false), "Didn't recognize -" + option + "-"); } else { options = ConsoleMocks.Options("--" + option); - Assert.That((bool)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize --" + option); + Assert.That((bool?)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize --" + option); } options = ConsoleMocks.Options("/" + option); - Assert.That((bool)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize /" + option); + Assert.That((bool?)property.GetValue(options, null), Is.EqualTo(true), "Didn't recognize /" + option); } } @@ -217,7 +217,7 @@ public void CanRecognizeStringOptions(string propertyName, string pattern, strin string optionPlusValue = string.Format("--{0}:{1}", option, value); ConsoleOptions options = ConsoleMocks.Options(optionPlusValue); Assert.That(options.ErrorMessages.Count, Is.EqualTo(0), "Should be valid: " + optionPlusValue); - Assert.That((string)property.GetValue(options, null), Is.EqualTo(value), "Didn't recognize " + optionPlusValue); + Assert.That((string?)property.GetValue(options, null), Is.EqualTo(value), "Didn't recognize " + optionPlusValue); } foreach (string value in badValues) @@ -242,7 +242,7 @@ public void CanRecognizeLowerCaseOptionValues(string propertyName, string option string optionPlusValue = string.Format("--{0}:{1}", optionName, lowercaseValue); ConsoleOptions options = ConsoleMocks.Options(optionPlusValue); Assert.That(options.ErrorMessages.Count, Is.EqualTo(0), "Should be valid: " + optionPlusValue); - Assert.That((string)property.GetValue(options, null), Is.EqualTo(canonicalValue), "Didn't recognize " + optionPlusValue); + Assert.That((string?)property.GetValue(options, null), Is.EqualTo(canonicalValue), "Didn't recognize " + optionPlusValue); } } @@ -262,7 +262,7 @@ public void CanRecognizeIntOptions(string propertyName, string pattern) foreach (string option in prototypes) { ConsoleOptions options = ConsoleMocks.Options("--" + option + ":42"); - Assert.That((int)property.GetValue(options, null), Is.EqualTo(42), "Didn't recognize --" + option + ":42"); + Assert.That((int?)property.GetValue(options, null), Is.EqualTo(42), "Didn't recognize --" + option + ":42"); } } @@ -750,14 +750,14 @@ private static IFileSystem GetFileSystemContainingFile(string fileName) private static FieldInfo GetFieldInfo(string fieldName) { - FieldInfo field = typeof(ConsoleOptions).GetField(fieldName); + FieldInfo? field = typeof(ConsoleOptions).GetField(fieldName); Assert.That(field, Is.Not.Null, $"The field '{fieldName}' is not defined"); return field; } private static PropertyInfo GetPropertyInfo(string propertyName) { - PropertyInfo property = typeof(ConsoleOptions).GetProperty(propertyName); + PropertyInfo? property = typeof(ConsoleOptions).GetProperty(propertyName); Assert.That(property, Is.Not.Null, $"The property '{propertyName}' is not defined"); return property; } diff --git a/src/NUnitConsole/nunit4-console.tests/ConsoleOutputTests.cs b/src/NUnitConsole/nunit4-console.tests/ConsoleOutputTests.cs index 2feb6fca0..9dcc2a330 100644 --- a/src/NUnitConsole/nunit4-console.tests/ConsoleOutputTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/ConsoleOutputTests.cs @@ -13,7 +13,7 @@ public void OneTimeSetUp() { Console.WriteLine("OneTimeSetUp: Console.WriteLine()"); Console.Error.WriteLine("OneTimeSetUp: Console.Error.WriteLine()"); - TestContext.WriteLine("OneTimeSetUp: TestContext.WriteLine()"); + TestContext.Out.WriteLine("OneTimeSetUp: TestContext.WriteLine()"); } @@ -22,7 +22,7 @@ public void SetUp() { Console.WriteLine("SetUp: Console.WriteLine()"); Console.Error.WriteLine("SetUp: Console.Error.WriteLine()"); - TestContext.WriteLine("SetUp: TestContext.WriteLine()"); + TestContext.Out.WriteLine("SetUp: TestContext.WriteLine()"); } [TearDown] @@ -30,7 +30,7 @@ public void TearDown() { Console.WriteLine("TearDown: Console.WriteLine()"); Console.Error.WriteLine("TearDown: Console.Error.WriteLine()"); - TestContext.WriteLine("TearDown: TestContext.WriteLine()"); + TestContext.Out.WriteLine("TearDown: TestContext.WriteLine()"); } [OneTimeTearDown] @@ -38,7 +38,7 @@ public void OneTimeTearDown() { Console.WriteLine("OneTimeTearDown: Console.WriteLine()"); Console.Error.WriteLine("OneTimeTearDown: Console.Error.WriteLine()"); - TestContext.WriteLine("OneTimeTearDown: TestContext.WriteLine()"); + TestContext.Out.WriteLine("OneTimeTearDown: TestContext.WriteLine()"); } [Test] @@ -46,13 +46,13 @@ public void Test() { Console.WriteLine("Test: Console.WriteLine()"); Console.Error.WriteLine("Test: Console.Error.WriteLine()"); - TestContext.WriteLine("Test: TestContext.WriteLine()"); + TestContext.Out.WriteLine("Test: TestContext.WriteLine()"); } [Test] public void ConsoleEncoding() { - TestContext.WriteLine("•ÑÜńĭŧ·"); + TestContext.Out.WriteLine("•ÑÜńĭŧ·"); } } } diff --git a/src/NUnitConsole/nunit4-console.tests/ConsoleRunnerTests.cs b/src/NUnitConsole/nunit4-console.tests/ConsoleRunnerTests.cs index 01c080344..e49dd7d8d 100644 --- a/src/NUnitConsole/nunit4-console.tests/ConsoleRunnerTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/ConsoleRunnerTests.cs @@ -28,6 +28,13 @@ public void SetUp() _testEngine.Services.GetService().Returns(_resultService); } + [TearDown] + public void TearDown() + { + (_resultService as IDisposable)?.Dispose(); + _testEngine.Dispose(); + } + [Test] public void ThrowsNUnitEngineExceptionWhenTestResultsAreNotWriteable() { @@ -62,7 +69,7 @@ public string[] Formats } } - public IResultWriter GetResultWriter(string format, object[] args) + public IResultWriter GetResultWriter(string format, params object?[]? args) { return new FakeResultWriter(this); } diff --git a/src/NUnitConsole/nunit4-console.tests/ExtendedTextWrapperTests.cs b/src/NUnitConsole/nunit4-console.tests/ExtendedTextWrapperTests.cs index 67b3e1b3f..7d4cec81c 100644 --- a/src/NUnitConsole/nunit4-console.tests/ExtendedTextWrapperTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/ExtendedTextWrapperTests.cs @@ -25,6 +25,12 @@ public void SetUp() writer = new ExtendedTextWrapper(new StringWriter(sb)); } + [TearDown] + public void TearDown() + { + writer.Dispose(); + } + [Test] public void Write() { diff --git a/src/NUnitConsole/nunit4-console.tests/MakeTestPackageTests.cs b/src/NUnitConsole/nunit4-console.tests/MakeTestPackageTests.cs index f0872a4e3..b8855d4fe 100644 --- a/src/NUnitConsole/nunit4-console.tests/MakeTestPackageTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/MakeTestPackageTests.cs @@ -59,8 +59,8 @@ public void WhenOptionIsSpecified_PackageIncludesSetting(string option, string k var options = ConsoleMocks.Options("test.dll", option); var package = ConsoleRunner.MakeTestPackage(options); - Assert.That(package.Settings.ContainsKey(key), "Setting not included for {0}", option); - Assert.That(package.Settings[key], Is.EqualTo(val), "NumberOfTestWorkers not set correctly for {0}", option); + Assert.That(package.Settings.ContainsKey(key), $"Setting not included for {options}", option); + Assert.That(package.Settings[key], Is.EqualTo(val), $"NumberOfTestWorkers not set correctly for {option}"); } [Test] @@ -71,6 +71,7 @@ public void TestRunParametersAreIncludedInSettings() Assert.That(settings.ContainsKey("TestParametersDictionary"), "TestParametersDictionary setting not included."); var paramDictionary = settings["TestParametersDictionary"] as IDictionary; + Assert.That(paramDictionary, Is.Not.Null); Assert.That(paramDictionary.Keys, Is.EqualTo(new[] { "X", "Y" })); Assert.That(paramDictionary["X"], Is.EqualTo("5")); Assert.That(paramDictionary["Y"], Is.EqualTo("7")); diff --git a/src/NUnitConsole/nunit4-console.tests/Options/OutputSpecificationTests.cs b/src/NUnitConsole/nunit4-console.tests/Options/OutputSpecificationTests.cs index 449e7f2c1..7bb462650 100644 --- a/src/NUnitConsole/nunit4-console.tests/Options/OutputSpecificationTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/Options/OutputSpecificationTests.cs @@ -11,7 +11,7 @@ public class OutputSpecificationTests public void SpecMayNotBeNull() { Assert.That( - () => new OutputSpecification(null, null), + () => new OutputSpecification(null!, null), Throws.TypeOf()); } @@ -109,7 +109,7 @@ public void TransformWithNonUserFormatNotAllowed() [TestCase(@"C:\")] [TestCase(@"C:\Temp")] - public void TransformFolderIsUsedToSpecifyTransform(string transformFolder) + public void TransformFolderIsUsedToSpecifyTransform(string? transformFolder) { const string fileName = "transform.xslt"; var spec = new OutputSpecification($"MyFile.xml;transform=transform.xslt", transformFolder); diff --git a/src/NUnitConsole/nunit4-console.tests/ResultReporterTests.cs b/src/NUnitConsole/nunit4-console.tests/ResultReporterTests.cs index f21bc5693..e347e57cb 100644 --- a/src/NUnitConsole/nunit4-console.tests/ResultReporterTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/ResultReporterTests.cs @@ -191,7 +191,7 @@ private IList GetReportLines(TestDelegate del) { var rdr = new StringReader(GetReport(del)); - string line; + string? line; var lines = new List(); while ((line = rdr.ReadLine()) != null) lines.Add(line); diff --git a/src/NUnitConsole/nunit4-console.tests/TestEventHandlerTests.cs b/src/NUnitConsole/nunit4-console.tests/TestEventHandlerTests.cs index 7bae81eb5..54c8f09a3 100644 --- a/src/NUnitConsole/nunit4-console.tests/TestEventHandlerTests.cs +++ b/src/NUnitConsole/nunit4-console.tests/TestEventHandlerTests.cs @@ -21,13 +21,19 @@ public void CreateWriter() _writer = new ExtendedTextWrapper(new StringWriter(_output)); } + [TearDown] + public void DisposeWriter() + { + _writer.Dispose(); + } + [TestCase(char.MaxValue)] public void TestNameContainsInvalidChar(char c) { Console.WriteLine($"Test for char {c}"); } - [TestCaseSource("SingleEventData")] + [TestCaseSource(nameof(SingleEventData))] public void SingleEventsWriteExpectedOutput(string report, string labels, string expected) { var handler = new TestEventHandler(_writer, labels); @@ -39,7 +45,7 @@ public void SingleEventsWriteExpectedOutput(string report, string labels, string Assert.That(Output, Is.EqualTo(expected)); } - [TestCaseSource("MultipleEventData")] + [TestCaseSource(nameof(MultipleEventData))] public void MultipleEvents(string[] reports, string labels, string expected) { var handler = new TestEventHandler(_writer, labels); @@ -56,8 +62,7 @@ public void MultipleEvents(string[] reports, string labels, string expected) Assert.That(Output, Is.EqualTo(expected)); } -#pragma warning disable 414 - static TestCaseData[] SingleEventData = new TestCaseData[] + static readonly TestCaseData[] SingleEventData = new TestCaseData[] { // Start Events new TestCaseData("", "Off", ""), @@ -269,7 +274,7 @@ public void MultipleEvents(string[] reports, string labels, string expected) "Output from first test" }; - static TestCaseData[] MultipleEventData = new TestCaseData[] + static readonly TestCaseData[] MultipleEventData = new TestCaseData[] { new TestCaseData( SingleTest_StartAndFinish, diff --git a/src/NUnitConsole/nunit4-console.tests/VirtualFileSystem.cs b/src/NUnitConsole/nunit4-console.tests/VirtualFileSystem.cs index 3670abb61..781be9f0d 100644 --- a/src/NUnitConsole/nunit4-console.tests/VirtualFileSystem.cs +++ b/src/NUnitConsole/nunit4-console.tests/VirtualFileSystem.cs @@ -3,11 +3,10 @@ using System; using System.Collections.Generic; using NUnit.Common; +using System.IO; namespace NUnit.ConsoleRunner { - using System.IO; - internal class VirtualFileSystem: IFileSystem { private readonly Dictionary> files = new Dictionary>(); @@ -19,7 +18,7 @@ public bool FileExists(string fileName) public IEnumerable ReadLines(string fileName) { - IEnumerable lines; + IEnumerable? lines; if (!files.TryGetValue(fileName, out lines)) { throw new FileNotFoundException("File not found", fileName); diff --git a/src/NUnitConsole/nunit4-console.tests/nunit4-console.tests.csproj b/src/NUnitConsole/nunit4-console.tests/nunit4-console.tests.csproj index c6ad0c8ad..def87260f 100644 --- a/src/NUnitConsole/nunit4-console.tests/nunit4-console.tests.csproj +++ b/src/NUnitConsole/nunit4-console.tests/nunit4-console.tests.csproj @@ -16,6 +16,8 @@ Tests of the NUnit Console Runner + + @@ -24,7 +26,11 @@ - + + + + + PreserveNewest diff --git a/src/NUnitConsole/nunit4-console/ColorConsoleWriter.cs b/src/NUnitConsole/nunit4-console/ColorConsoleWriter.cs index e01ef7986..69d77653e 100644 --- a/src/NUnitConsole/nunit4-console/ColorConsoleWriter.cs +++ b/src/NUnitConsole/nunit4-console/ColorConsoleWriter.cs @@ -86,11 +86,10 @@ public override void WriteLabelLine(string label, object option) /// The color to display the value with public override void WriteLabel(string label, object option, ColorStyle valueStyle) { - if (option == null) - throw new NullReferenceException(nameof(option)); + Guard.ArgumentNotNull(option, nameof(option)); Write(ColorStyle.Label, label); - Write(valueStyle, option.ToString()); + Write(valueStyle, option.ToString() ?? string.Empty); } /// diff --git a/src/NUnitConsole/nunit4-console/ConsoleOptions.cs b/src/NUnitConsole/nunit4-console/ConsoleOptions.cs index ed746ed43..66d60717d 100644 --- a/src/NUnitConsole/nunit4-console/ConsoleOptions.cs +++ b/src/NUnitConsole/nunit4-console/ConsoleOptions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -52,7 +53,9 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public IDictionary TestParameters { get; } = new Dictionary(); - public string WhereClause { get; private set; } + public string? WhereClause { get; private set; } + + [MemberNotNullWhen(true, nameof(WhereClause))] public bool WhereClauseSpecified { get { return WhereClause != null; } } public int DefaultTestCaseTimeout { get; private set; } = -1; @@ -61,7 +64,7 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public int RandomSeed { get; private set; } = -1; public bool RandomSeedSpecified { get { return RandomSeed >= 0; } } - public string DefaultTestNamePattern { get; private set; } + public string? DefaultTestNamePattern { get; private set; } public int NumberOfTestWorkers { get; private set; } = -1; public bool NumberOfTestWorkersSpecified { get { return NumberOfTestWorkers >= 0; } } @@ -71,7 +74,7 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys // Output Control - public string ConsoleEncoding { get; private set; } + public string? ConsoleEncoding { get; private set; } public bool NoHeader { get; private set; } @@ -79,19 +82,23 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public bool TeamCity { get; private set; } - public string OutFile { get; private set; } + public string? OutFile { get; private set; } + + [MemberNotNullWhen(true, nameof(OutFile))] public bool OutFileSpecified { get { return OutFile != null; } } - public string DisplayTestLabels { get; private set; } + public string? DisplayTestLabels { get; private set; } - private string workDirectory = null; + private string? workDirectory = null; public string WorkDirectory { get { return workDirectory ?? CURRENT_DIRECTORY_ON_ENTRY; } } public bool WorkDirectorySpecified { get { return workDirectory != null; } } - public string InternalTraceLevel { get; private set; } + public string? InternalTraceLevel { get; private set; } + + [MemberNotNullWhen(true, nameof(InternalTraceLevel))] public bool InternalTraceLevelSpecified { get { return InternalTraceLevel != null; } } private readonly List resultOutputSpecifications = new List(); @@ -114,15 +121,19 @@ public IList ResultOutputSpecifications public IList ExploreOutputSpecifications { get; } = new List(); - public string ActiveConfig { get; private set; } + public string? ActiveConfig { get; private set; } + + [MemberNotNullWhen(true, nameof(ActiveConfig))] public bool ActiveConfigSpecified { get { return ActiveConfig != null; } } // How to Run Tests - public string RuntimeFramework { get; private set; } + public string? RuntimeFramework { get; private set; } + + [MemberNotNullWhen(true, nameof(RuntimeFramework))] public bool RuntimeFrameworkSpecified { get { return RuntimeFramework != null; } } - public string ConfigurationFile { get; private set; } + public string? ConfigurationFile { get; private set; } public bool RunAsX86 { get; private set; } @@ -146,7 +157,7 @@ public IList ResultOutputSpecifications public bool PauseBeforeRun { get; private set; } - public string PrincipalPolicy { get; private set; } + public string? PrincipalPolicy { get; private set; } public IList WarningMessages { get; } = new List(); @@ -179,7 +190,7 @@ private void ConfigureOptions() { while (!rdr.EndOfStream) { - var line = rdr.ReadLine().Trim(); + var line = rdr.ReadLine()?.Trim(); if (!string.IsNullOrEmpty(line) && line[0] != '#') ((List)TestList).Add(line); @@ -216,10 +227,10 @@ private void ConfigureOptions() v => NumberOfTestWorkers = parser.RequiredInt(v, "--workers")); this.Add("stoponerror", "Stop run immediately upon any test failure or error.", - v => StopOnError = v != null); + v => StopOnError = !string.IsNullOrEmpty(v)); this.Add("wait", "Wait for input before closing console window.", - v => WaitBeforeExit = v != null); + v => WaitBeforeExit = !string.IsNullOrEmpty(v)); // Output Control this.Add("work=", "{PATH} of the directory to use for output files. If not specified, defaults to the current directory.", @@ -243,7 +254,7 @@ private void ConfigureOptions() }); this.Add("noresult", "Don't save any test results.", - v => NoResultSpecified = v != null); + v => NoResultSpecified = !string.IsNullOrEmpty(v)); this.Add("labels=", "Specify whether to write test case names to the output. Values: Off (Default), On, OnOutput, Before, After, BeforeAndAfter. On is currently an alias for OnOutput, but is subject to change.", v => { @@ -257,19 +268,19 @@ private void ConfigureOptions() v => InternalTraceLevel = parser.RequiredValue(v, "--trace", "Off", "Error", "Warning", "Info", "Verbose", "Debug")); this.Add("teamcity", "Turns on use of TeamCity service messages. TeamCity engine extension is required.", - v => TeamCity = v != null); + v => TeamCity = !string.IsNullOrEmpty(v)); this.Add("noheader|noh", "Suppress display of program information at start of run.", - v => NoHeader = v != null); + v => NoHeader = !string.IsNullOrEmpty(v)); this.Add("nocolor|noc", "Displays console output without color.", - v => NoColor = v != null); + v => NoColor = !string.IsNullOrEmpty(v)); this.Add("help|h", "Display this message and exit.", - v => ShowHelp = v != null); + v => ShowHelp = !string.IsNullOrEmpty(v)); this.Add("version|V", "Display the header and exit.", - v => ShowVersion = v != null); + v => ShowVersion = !string.IsNullOrEmpty(v)); this.Add("encoding=", "Specifies the encoding to use for Console standard output, for example utf-8, ascii, unicode.", v => ConsoleEncoding = parser.RequiredValue(v, "--encoding")); @@ -294,38 +305,38 @@ private void ConfigureOptions() NetFxOnlyOption("framework=", v => RuntimeFramework = parser.RequiredValue(v, "--framework"))); this.AddNetFxOnlyOption("x86", "Run tests in an x86 process on 64 bit systems", - NetFxOnlyOption("x86", v => RunAsX86 = v != null)); + NetFxOnlyOption("x86", v => RunAsX86 = !string.IsNullOrEmpty(v))); this.Add("dispose-runners", "Dispose each test runner after it has finished running its tests.", - v => DisposeRunners = v != null); + v => DisposeRunners = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("shadowcopy", "Shadow copy test files", - NetFxOnlyOption("shadowcopy", v => ShadowCopyFiles = v != null)); + NetFxOnlyOption("shadowcopy", v => ShadowCopyFiles = !string.IsNullOrEmpty(v))); this.AddNetFxOnlyOption("loaduserprofile", "Load user profile in test runner processes", - NetFxOnlyOption("loaduserprofile", v => LoadUserProfile = v != null)); + NetFxOnlyOption("loaduserprofile", v => LoadUserProfile = !string.IsNullOrEmpty(v))); this.Add("skipnontestassemblies", "Skip any non-test assemblies specified, without error.", - v => SkipNonTestAssemblies = v != null); + v => SkipNonTestAssemblies = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("agents=", "Specify the maximum {NUMBER} of test assembly agents to run at one time. If not specified, there is no limit.", NetFxOnlyOption("agents=", v => _maxAgents = parser.RequiredInt(v, "--agents"))); this.AddNetFxOnlyOption("debug", "Launch debugger to debug tests.", - NetFxOnlyOption("debug", v => DebugTests = v != null)); + NetFxOnlyOption("debug", v => DebugTests = !string.IsNullOrEmpty(v))); this.AddNetFxOnlyOption("pause", "Pause before running to allow attaching a debugger.", - NetFxOnlyOption("pause", v => PauseBeforeRun = v != null)); + NetFxOnlyOption("pause", v => PauseBeforeRun = !string.IsNullOrEmpty(v))); this.Add("list-extensions", "List all extension points and the extensions for each.", - v => ListExtensions = v != null); + v => ListExtensions = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("set-principal-policy=", "Set PrincipalPolicy for the test domain.", NetFxOnlyOption("set-principal-policy=", v => PrincipalPolicy = parser.RequiredValue(v, "--set-principal-policy", "UnauthenticatedPrincipal", "NoPrincipal", "WindowsPrincipal"))); #if DEBUG this.AddNetFxOnlyOption("debug-agent", "Launch debugger in nunit-agent when it starts.", - NetFxOnlyOption("debug-agent", v => DebugAgent = v != null)); + NetFxOnlyOption("debug-agent", v => DebugAgent = !string.IsNullOrEmpty(v))); #endif } @@ -431,7 +442,7 @@ private IEnumerable GetArgsFromFile(string filename) return GetArgs(sb.ToString()); } - private string ExpandToFullPath(string path) + private string? ExpandToFullPath(string path) { if (path == null) return null; diff --git a/src/NUnitConsole/nunit4-console/ConsoleRunner.cs b/src/NUnitConsole/nunit4-console/ConsoleRunner.cs index 7bdc21d99..e01e99895 100644 --- a/src/NUnitConsole/nunit4-console/ConsoleRunner.cs +++ b/src/NUnitConsole/nunit4-console/ConsoleRunner.cs @@ -142,7 +142,7 @@ private int ExploreTests(TestPackage package, TestFilter filter) { foreach (OutputSpecification spec in _options.ExploreOutputSpecifications) { - _resultService.GetResultWriter(spec.Format, new object[] {spec.Transform}).WriteResultFile(result, spec.OutputPath); + _resultService.GetResultWriter(spec.Format, spec.Transform).WriteResultFile(result, spec.OutputPath); _outWriter.WriteLine("Results ({0}) saved as {1}", spec.Format, spec.OutputPath); } } @@ -172,7 +172,7 @@ private int RunTests(TestPackage package, TestFilter filter) try { - var outputDirectory = Path.GetDirectoryName(outputPath); + var outputDirectory = Path.GetDirectoryName(outputPath)!; Directory.CreateDirectory(outputDirectory); } catch (Exception ex) @@ -202,9 +202,9 @@ private int RunTests(TestPackage package, TestFilter filter) ? _options.DisplayTestLabels.ToUpperInvariant() : "ON"; - XmlNode result = null; - NUnitEngineUnloadException unloadException = null; - NUnitEngineException engineException = null; + XmlNode? result = null; + NUnitEngineUnloadException? unloadException = null; + NUnitEngineException? engineException = null; try { @@ -298,7 +298,7 @@ private static string GetOSVersion() if (uname(buf) == 0) { var unixVariant = Marshal.PtrToStringAnsi(buf); - if (unixVariant.Equals("Darwin")) + if (string.Equals(unixVariant, "Darwin")) unixVariant = "MacOSX"; osString = string.Format("{0} {1} {2}", unixVariant, os.Version, os.ServicePack); @@ -380,7 +380,7 @@ private ExtendedTextWriter CreateOutputWriter() private IResultWriter GetResultWriter(OutputSpecification spec) { - return _resultService.GetResultWriter(spec.Format, new object[] {spec.Transform}); + return _resultService.GetResultWriter(spec.Format, spec.Transform); } // This is public static for ease of testing diff --git a/src/NUnitConsole/nunit4-console/ConsoleTestResult.cs b/src/NUnitConsole/nunit4-console/ConsoleTestResult.cs index 971bc3fa0..19265bd28 100644 --- a/src/NUnitConsole/nunit4-console/ConsoleTestResult.cs +++ b/src/NUnitConsole/nunit4-console/ConsoleTestResult.cs @@ -31,23 +31,23 @@ public ConsoleTestResult(XmlNode resultNode, int reportIndex) Label = resultNode.GetAttribute("label"); Site = resultNode.GetAttribute("site"); - Status = Label ?? Result; + Status = Label ?? Result ?? "Unkown"; if (Status == "Failed" || Status == "Error") if (Site == "SetUp" || Site == "TearDown") Status = Site + " " + Status; - FullName = resultNode.GetAttribute("fullname"); + FullName = resultNode.GetAttribute("fullname") ?? "Unknown"; } - public string Result { get; private set; } - public string Label { get; private set; } - public string Site { get; private set; } + public string? Result { get; private set; } + public string? Label { get; private set; } + public string? Site { get; private set; } public string FullName { get; private set; } public string ReportID { get; private set; } public string Status { get; private set; } - public string Message + public string? Message { get { @@ -56,12 +56,12 @@ public string Message } } - public string StackTrace + public string? StackTrace { get { return GetTrimmedInnerText(_resultNode.SelectSingleNode("failure/stack-trace")); } } - private List _assertions; + private List? _assertions; public List Assertions { get @@ -69,8 +69,10 @@ public List Assertions if (_assertions == null) { _assertions = new List(); - foreach (XmlNode assertion in _resultNode.SelectNodes("assertions/assertion")) - Assertions.Add(new ConsoleTestResult.AssertionResult(assertion)); + XmlNodeList? assertions = _resultNode.SelectNodes("assertions/assertion"); + if (assertions is not null) + foreach (XmlNode assertion in assertions) + Assertions.Add(new ConsoleTestResult.AssertionResult(assertion)); } return _assertions; @@ -98,7 +100,7 @@ public void WriteResult(ExtendedTextWriter writer) WriteResult(writer, ReportID, Status, FullName, Message, StackTrace); } - private void WriteResult(ExtendedTextWriter writer, string reportID, string status, string fullName, string message, string stackTrace) + private void WriteResult(ExtendedTextWriter writer, string reportID, string status, string fullName, string? message, string? stackTrace) { ColorStyle style = GetColorStyle(); @@ -123,7 +125,7 @@ private ColorStyle GetColorStyle() : ColorStyle.Output; } - private static string GetTrimmedInnerText(XmlNode node) + private static string? GetTrimmedInnerText(XmlNode? node) { // In order to control the format, we trim any line-end chars // from end of the strings we write and supply them via calls @@ -141,8 +143,8 @@ public AssertionResult(XmlNode assertion) StackTrace = GetTrimmedInnerText(assertion.SelectSingleNode("stack-trace")); } - public string Message { get; private set; } - public string StackTrace { get; private set; } + public string? Message { get; private set; } + public string? StackTrace { get; private set; } } } } diff --git a/src/NUnitConsole/nunit4-console/ExtendedTextWrapper.cs b/src/NUnitConsole/nunit4-console/ExtendedTextWrapper.cs index e0ce8af53..08892960a 100644 --- a/src/NUnitConsole/nunit4-console/ExtendedTextWrapper.cs +++ b/src/NUnitConsole/nunit4-console/ExtendedTextWrapper.cs @@ -31,7 +31,7 @@ public override void Write(char value) /// /// Write a string value /// - public override void Write(string value) + public override void Write(string? value) { _writer.Write(value); } @@ -39,7 +39,7 @@ public override void Write(string value) /// /// Write a string value followed by a NewLine /// - public override void WriteLine(string value) + public override void WriteLine(string? value) { _writer.WriteLine(value); } diff --git a/src/NUnitConsole/nunit4-console/FileSystem.cs b/src/NUnitConsole/nunit4-console/FileSystem.cs index 4636d8701..c1f78f008 100644 --- a/src/NUnitConsole/nunit4-console/FileSystem.cs +++ b/src/NUnitConsole/nunit4-console/FileSystem.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.IO; +using System; namespace NUnit.ConsoleRunner { - using System; - internal class FileSystem : IFileSystem { public bool FileExists(string fileName) @@ -22,7 +21,7 @@ public IEnumerable ReadLines(string fileName) using (var file = File.OpenText(fileName)) { - string line; + string? line; while ((line = file.ReadLine()) != null) { yield return line; diff --git a/src/NUnitConsole/nunit4-console/Options/DefaultOptionsProvider.cs b/src/NUnitConsole/nunit4-console/Options/DefaultOptionsProvider.cs index 33bdd1e16..72fd0c520 100644 --- a/src/NUnitConsole/nunit4-console/Options/DefaultOptionsProvider.cs +++ b/src/NUnitConsole/nunit4-console/Options/DefaultOptionsProvider.cs @@ -1,9 +1,9 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt +using System; + namespace NUnit.ConsoleRunner.Options { - using System; - internal sealed class DefaultOptionsProvider : IDefaultOptionsProvider { private const string EnvironmentVariableTeamcityProjectName = "TEAMCITY_PROJECT_NAME"; diff --git a/src/NUnitConsole/nunit4-console/Options/OptionParser.cs b/src/NUnitConsole/nunit4-console/Options/OptionParser.cs index 158c62d46..40f4fb6be 100644 --- a/src/NUnitConsole/nunit4-console/Options/OptionParser.cs +++ b/src/NUnitConsole/nunit4-console/Options/OptionParser.cs @@ -95,7 +95,7 @@ private bool IsQuotedString(string value) return false; } - public OutputSpecification ResolveOutputSpecification(string value, IList outputSpecifications, IFileSystem fileSystem, string currentDir) + public OutputSpecification? ResolveOutputSpecification(string value, IList outputSpecifications, IFileSystem fileSystem, string currentDir) { if (value == null) return null; diff --git a/src/NUnitConsole/nunit4-console/Options/Options.cs b/src/NUnitConsole/nunit4-console/Options/Options.cs index 1d830f1a5..0e8fe974b 100644 --- a/src/NUnitConsole/nunit4-console/Options/Options.cs +++ b/src/NUnitConsole/nunit4-console/Options/Options.cs @@ -160,6 +160,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; #if PCL @@ -170,6 +171,8 @@ #endif using System.Text; using System.Text.RegularExpressions; +using NUnit.Common; + #if LINQ using System.Linq; @@ -308,14 +311,14 @@ internal OptionValueCollection(OptionContext c) #endregion #region IList - int IList.Add(object value) { return (values as IList).Add(value); } - bool IList.Contains(object value) { return (values as IList).Contains(value); } - int IList.IndexOf(object value) { return (values as IList).IndexOf(value); } - void IList.Insert(int index, object value) { (values as IList).Insert(index, value); } - void IList.Remove(object value) { (values as IList).Remove(value); } + int IList.Add(object? value) { return (values as IList).Add(value); } + bool IList.Contains(object? value) { return (values as IList).Contains(value); } + int IList.IndexOf(object? value) { return (values as IList).IndexOf(value); } + void IList.Insert(int index, object? value) { (values as IList).Insert(index, value); } + void IList.Remove(object? value) { (values as IList).Remove(value); } void IList.RemoveAt(int index) { (values as IList).RemoveAt(index); } bool IList.IsFixedSize { get { return false; } } - object IList.this[int index] { get { return this[index]; } set { (values as IList)[index] = value; } } + object? IList.this[int index] { get { return this[index]; } set { (values as IList)[index] = value; } } #endregion #region IList @@ -336,12 +339,13 @@ private void AssertValid(int index) c.OptionName); } + [DisallowNull] public string this[int index] { get { AssertValid(index); - return index >= values.Count ? null : values[index]; + return index >= values.Count ? string.Empty : values[index]; } set { @@ -368,8 +372,8 @@ public override string ToString() public class OptionContext { - private Option option; - private string name; + private Option? option; + private string? name; private int index; private readonly OptionSet set; private readonly OptionValueCollection c; @@ -380,13 +384,13 @@ public OptionContext(OptionSet set) this.c = new OptionValueCollection(this); } - public Option Option + public Option? Option { get { return option; } set { option = value; } } - public string OptionName + public string? OptionName { get { return name; } set { name = value; } @@ -419,24 +423,24 @@ public enum OptionValueType public abstract class Option { readonly string prototype; - readonly string description; + readonly string? description; readonly string[] names; readonly OptionValueType type; readonly int count; - string[] separators; + string[]? separators; readonly bool hidden; - protected Option(string prototype, string description) + protected Option(string prototype, string? description) : this(prototype, description, 1, false) { } - protected Option(string prototype, string description, int maxValueCount) + protected Option(string prototype, string? description, int maxValueCount) : this(prototype, description, maxValueCount, false) { } - protected Option(string prototype, string description, int maxValueCount, bool hidden) + protected Option(string prototype, string? description, int maxValueCount, bool hidden) { if (prototype == null) throw new ArgumentNullException("prototype"); @@ -478,7 +482,7 @@ protected Option(string prototype, string description, int maxValueCount, bool h } public string Prototype { get { return prototype; } } - public string Description { get { return description; } } + public string? Description { get { return description; } } public OptionValueType OptionValueType { get { return type; } } public int MaxValueCount { get { return count; } } public bool Hidden { get { return hidden; } } @@ -491,7 +495,7 @@ public string[] GetNames() public string[] GetValueSeparators() { if (separators == null) - return new string[0]; + return Array.Empty(); return (string[])separators.Clone(); } @@ -513,19 +517,19 @@ protected static T Parse(string value, OptionContext c) #else Type targetType = nullable ? tt.GetGenericArguments()[0] : tt; #endif - T t = default(T); + T t = default(T)!; try { if (value != null) { #if PCL - if (targetType.GetTypeInfo ().IsEnum) - t = (T) Enum.Parse (targetType, value, true); - else - t = (T) Convert.ChangeType (value, targetType); + if (targetType.GetTypeInfo ().IsEnum) + t = (T) Enum.Parse (targetType, value, true); + else + t = (T) Convert.ChangeType (value, targetType); #else TypeConverter conv = TypeDescriptor.GetConverter(targetType); - t = (T)conv.ConvertFromString(value); + t = (T)conv.ConvertFromString(value)!; #endif } } @@ -537,11 +541,12 @@ protected static T Parse(string value, OptionContext c) value, targetType.Name, c.OptionName), c.OptionName, e); } + return t; } internal string[] Names { get { return names; } } - internal string[] ValueSeparators { get { return separators; } } + internal string[]? ValueSeparators { get { return separators; } } static readonly char[] NameTerminator = new char[] { '=', ':' }; @@ -551,7 +556,7 @@ private OptionValueType ParsePrototype() List seps = new List(); for (int i = 0; i < names.Length; ++i) { - string name = names[i]; + string? name = names[i]; if (name.Length == 0) throw new ArgumentException("Empty option names are not supported.", "prototype"); @@ -652,7 +657,7 @@ protected ArgumentSource() public abstract string[] GetNames(); public abstract string Description { get; } - public abstract bool GetArguments(string value, out IEnumerable replacement); + public abstract bool GetArguments(string value, [NotNullWhen(true)] out IEnumerable? replacement); #if !PCL || NETSTANDARD1_3 public static IEnumerable GetArgumentsFromFile(string file) @@ -673,7 +678,7 @@ static IEnumerable GetArguments(TextReader reader, bool close) { StringBuilder arg = new StringBuilder(); - string line; + string? line; while ((line = reader.ReadLine()) != null) { int t = line.Length; @@ -735,7 +740,7 @@ public override string Description get { return "Read response file for more options."; } } - public override bool GetArguments(string value, out IEnumerable replacement) + public override bool GetArguments(string value, [NotNullWhen(true)] out IEnumerable? replacement) { if (string.IsNullOrEmpty(value) || !value.StartsWith("@")) { @@ -753,19 +758,19 @@ public override bool GetArguments(string value, out IEnumerable replacem #endif public class OptionException : Exception { - private string option; + private string? option; public OptionException() { } - public OptionException(string message, string optionName) + public OptionException(string message, string? optionName) : base(message) { this.option = optionName; } - public OptionException(string message, string optionName, Exception innerException) + public OptionException(string message, string? optionName, Exception innerException) : base(message, innerException) { this.option = optionName; @@ -779,7 +784,7 @@ protected OptionException(SerializationInfo info, StreamingContext context) } #endif - public string OptionName + public string? OptionName { get { return this.option; } } @@ -802,20 +807,20 @@ public OptionSet() { } - public OptionSet(MessageLocalizerConverter localizer) + public OptionSet(MessageLocalizerConverter? localizer) + : base(StringComparer.Ordinal, dictionaryCreationThreshold: 0) { this.roSources = new ReadOnlyCollection(sources); - this.localizer = localizer; - if (this.localizer == null) - { - this.localizer = delegate (string f) { + this.localizer = localizer ?? + delegate (string f) { return f; }; - } } MessageLocalizerConverter localizer; + private new IDictionary Dictionary => base.Dictionary!; + public MessageLocalizerConverter MessageLocalizer { get { return localizer; } @@ -843,7 +848,7 @@ protected override string GetKeyForItem(Option item) } [Obsolete("Use KeyedCollection.this[string]")] - protected Option GetOptionForName(string option) + protected Option? GetOptionForName(string option) { if (option == null) throw new ArgumentNullException("option"); @@ -938,12 +943,12 @@ sealed class ActionOption : Option { readonly Action action; - public ActionOption(string prototype, string description, int count, Action action) + public ActionOption(string prototype, string? description, int count, Action action) : this(prototype, description, count, action, false) { } - public ActionOption(string prototype, string description, int count, Action action, bool hidden) + public ActionOption(string prototype, string? description, int count, Action action, bool hidden) : base(prototype, description, count, hidden) { if (action == null) @@ -962,12 +967,12 @@ public OptionSet Add(string prototype, Action action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, Action action) + public OptionSet Add(string prototype, string? description, Action action) { return Add(prototype, description, action, false); } - public OptionSet Add(string prototype, string description, Action action, bool hidden) + public OptionSet Add(string prototype, string? description, Action action, bool hidden) { if (action == null) throw new ArgumentNullException("action"); @@ -982,12 +987,12 @@ public OptionSet Add(string prototype, OptionAction action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, OptionAction action) + public OptionSet Add(string prototype, string? description, OptionAction action) { return Add(prototype, description, action, false); } - public OptionSet Add(string prototype, string description, OptionAction action, bool hidden) + public OptionSet Add(string prototype, string? description, OptionAction action, bool hidden) { if (action == null) throw new ArgumentNullException("action"); @@ -1001,7 +1006,7 @@ sealed class ActionOption : Option { readonly Action action; - public ActionOption(string prototype, string description, Action action) + public ActionOption(string prototype, string? description, Action action) : base(prototype, description, 1) { if (action == null) @@ -1019,7 +1024,7 @@ sealed class ActionOption : Option { readonly OptionAction action; - public ActionOption(string prototype, string description, OptionAction action) + public ActionOption(string prototype, string? description, OptionAction action) : base(prototype, description, 2) { if (action == null) @@ -1040,7 +1045,7 @@ public OptionSet Add(string prototype, Action action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, Action action) + public OptionSet Add(string prototype, string? description, Action action) { return Add(new ActionOption(prototype, description, action)); } @@ -1050,7 +1055,7 @@ public OptionSet Add(string prototype, OptionAction return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, OptionAction action) + public OptionSet Add(string prototype, string? description, OptionAction action) { return Add(new ActionOption(prototype, description, action)); } @@ -1076,7 +1081,7 @@ public List Parse(IEnumerable arguments) c.OptionIndex = -1; bool process = true; List unprocessed = new List(); - Option def = Contains("<>") ? this["<>"] : null; + Option? def = Contains("<>") ? this["<>"] : null; ArgumentEnumerator ae = new ArgumentEnumerator(arguments); foreach (string argument in ae) { @@ -1140,7 +1145,7 @@ bool AddSource(ArgumentEnumerator ae, string argument) { foreach (ArgumentSource source in sources) { - IEnumerable replacement; + IEnumerable? replacement; if (!source.GetArguments(argument, out replacement)) continue; ae.Add(replacement); @@ -1149,7 +1154,7 @@ bool AddSource(ArgumentEnumerator ae, string argument) return false; } - private static bool Unprocessed(ICollection extra, Option def, OptionContext c, string argument) + private static bool Unprocessed(ICollection extra, Option? def, OptionContext c, string argument) { if (def == null) { @@ -1165,7 +1170,7 @@ private static bool Unprocessed(ICollection extra, Option def, OptionCon private readonly Regex ValueOption = new Regex( @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); - protected bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value) + protected bool GetOptionParts(string argument, [NotNullWhen(true)] out string? flag, [NotNullWhen(true)] out string? name, out string? sep, out string? value) { if (argument == null) throw new ArgumentNullException("argument"); @@ -1194,7 +1199,7 @@ protected virtual bool Parse(string argument, OptionContext c) return true; } - string f, n, s, v; + string? f, n, s, v; if (!GetOptionParts(argument, out f, out n, out s, out v)) return false; @@ -1227,8 +1232,10 @@ protected virtual bool Parse(string argument, OptionContext c) return false; } - private void ParseValue(string option, OptionContext c) + private void ParseValue(string? option, OptionContext c) { + Guard.OperationValid(c.Option != null, "OptionContext.Option != null"); + if (option != null) foreach (string o in c.Option.ValueSeparators != null ? option.Split(c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) @@ -1256,7 +1263,7 @@ private bool ParseBool(string option, string n, OptionContext c) Contains((rn = n.Substring(0, n.Length - 1)))) { p = this[rn]; - string v = n[n.Length - 1] == '+' ? option : null; + string v = n[n.Length - 1] == '+' ? option : string.Empty; c.OptionName = option; c.Option = p; c.OptionValues.Add(v); @@ -1328,13 +1335,13 @@ public void WriteOptionDescriptions(TextWriter o) if (p.Hidden) continue; - Category c = p as Category; + Category? c = p as Category; if (c != null) { WriteDescription(o, p.Description, "", 80, 80); continue; } - CommandOption co = p as CommandOption; + CommandOption? co = p as CommandOption; if (co != null) { WriteCommandDescription(o, co.Command, co.CommandName); @@ -1385,7 +1392,7 @@ public void WriteOptionDescriptions(TextWriter o) } } - internal void WriteCommandDescription(TextWriter o, Command c, string commandName) + internal void WriteCommandDescription(TextWriter o, Command c, string? commandName) { var name = new string(' ', 8) + (commandName ?? c.Name); if (name.Length < OptionWidth - 1) @@ -1399,7 +1406,7 @@ internal void WriteCommandDescription(TextWriter o, Command c, string commandNam } } - void WriteDescription(TextWriter o, string value, string prefix, int firstWidth, int remWidth) + void WriteDescription(TextWriter o, string? value, string prefix, int firstWidth, int remWidth) { bool indent = false; foreach (string line in GetLines(localizer(GetDescription(value)), firstWidth, remWidth)) @@ -1476,7 +1483,7 @@ static void Write(TextWriter o, ref int n, string s) o.Write(s); } - static string GetArgumentName(int index, int maxIndex, string description) + static string GetArgumentName(int index, int maxIndex, string? description) { var matches = Regex.Matches(description ?? "", @"(?<=(? GetLines(string description, int firstWidth, public class Command { + private CommandSet? commandSet; + public string Name { get; } - public string Help { get; } + public string? Help { get; } - public OptionSet Options { get; set; } - public Action> Run { get; set; } + public OptionSet? Options { get; set; } + public Action>? Run { get; set; } - public CommandSet CommandSet { get; internal set; } + public CommandSet CommandSet { get => commandSet.ShouldNotBeNull(); internal set => commandSet = value; } - public Command(string name, string help = null) + public Command(string name, string? help = null) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); @@ -1611,7 +1620,7 @@ class CommandOption : Option // Prototype starts with '=' because this is an invalid prototype // (see Option.ParsePrototype(), and thus it'll prevent Category // instances from being accidentally used as normal options. - public CommandOption(Command command, string commandName = null, bool hidden = false) + public CommandOption(Command command, string? commandName = null, bool hidden = false) : base("=:Command:= " + (commandName ?? command?.Name), (commandName ?? command?.Name), maxValueCount: 0, hidden: hidden) { if (command == null) @@ -1650,7 +1659,7 @@ class CommandOptionSet : OptionSet { readonly CommandSet commands; - public CommandOptionSet(CommandSet commands, MessageLocalizerConverter localizer) + public CommandOptionSet(CommandSet commands, MessageLocalizerConverter? localizer) : base(localizer) { this.commands = commands; @@ -1700,22 +1709,22 @@ public class CommandSet : KeyedCollection TextWriter outWriter; TextWriter errorWriter; - internal List NestedCommandSets; + internal List? NestedCommandSets; - internal HelpCommand help; + internal HelpCommand? help; internal bool showHelp; internal OptionSet Options => options; #if !PCL || NETSTANDARD1_3 - public CommandSet(string suite, MessageLocalizerConverter localizer = null) + public CommandSet(string suite, MessageLocalizerConverter? localizer = null) : this(suite, Console.Out, Console.Error, localizer) { } #endif - public CommandSet(string suite, TextWriter output, TextWriter error, MessageLocalizerConverter localizer = null) + public CommandSet(string suite, TextWriter output, TextWriter error, MessageLocalizerConverter? localizer = null) { if (suite == null) throw new ArgumentNullException(nameof(suite)); @@ -1737,7 +1746,7 @@ public CommandSet(string suite, TextWriter output, TextWriter error, MessageLoca protected override string GetKeyForItem(Command item) { - return item?.Name; + return item.Name; } public new CommandSet Add(Command value) @@ -1891,7 +1900,7 @@ bool AlreadyAdded(CommandSet value) return false; } - public IEnumerable GetCompletions(string prefix = null) + public IEnumerable GetCompletions(string? prefix = null) { string rest; ExtractToken(ref prefix, out rest); @@ -1919,7 +1928,7 @@ public IEnumerable GetCompletions(string prefix = null) } } - static void ExtractToken(ref string input, out string rest) + static void ExtractToken([NotNull] ref string? input, out string rest) { rest = ""; input = input ?? ""; @@ -1995,12 +2004,12 @@ public int Run(IEnumerable arguments) return command.Invoke(extra); } - internal Command GetCommand(List extra) + internal Command? GetCommand(List extra) { return TryGetLocalCommand(extra) ?? TryGetNestedCommand(extra); } - Command TryGetLocalCommand(List extra) + Command? TryGetLocalCommand(List extra) { var name = extra[0]; if (Contains(name)) @@ -2019,7 +2028,7 @@ Command TryGetLocalCommand(List extra) return null; } - Command TryGetNestedCommand(List extra) + Command? TryGetNestedCommand(List extra) { if (NestedCommandSets == null) return null; @@ -2053,7 +2062,7 @@ public HelpCommand() public override int Invoke(IEnumerable arguments) { - var extra = new List(arguments ?? new string[0]); + var extra = new List(); var _ = CommandSet.Options.MessageLocalizer; if (extra.Count == 0) { @@ -2078,7 +2087,8 @@ public override int Invoke(IEnumerable arguments) } CommandSet.Options.WriteCommandDescription(CommandSet.Out, c.Value, c.Key); } - CommandSet.Options.WriteCommandDescription(CommandSet.Out, CommandSet.help, "help"); + if (CommandSet.help != null) + CommandSet.Options.WriteCommandDescription(CommandSet.Out, CommandSet.help, "help"); return 0; } if (command == null) @@ -2098,15 +2108,17 @@ List> GetCommands() { var commands = new List>(); - foreach (var c in CommandSet) + var commandSet = CommandSet; + + foreach (var c in commandSet) { commands.Add(new KeyValuePair(c.Name, c)); } - if (CommandSet.NestedCommandSets == null) + if (commandSet.NestedCommandSets == null) return commands; - foreach (var nc in CommandSet.NestedCommandSets) + foreach (var nc in commandSet.NestedCommandSets) { AddNestedCommands(commands, "", nc); } @@ -2130,8 +2142,10 @@ void AddNestedCommands(List> commands, string oute internal void WriteUnknownCommand(string unknownCommand) { - CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Unknown command: {unknownCommand}")); - CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage.")); + var commandSet = CommandSet; + + commandSet.Error.WriteLine(commandSet.Options.MessageLocalizer($"{commandSet.Suite}: Unknown command: {unknownCommand}")); + commandSet.Error.WriteLine(commandSet.Options.MessageLocalizer($"{commandSet.Suite}: Use `{commandSet.Suite} help` for usage.")); } } } diff --git a/src/NUnitConsole/nunit4-console/Options/OutputSpecification.cs b/src/NUnitConsole/nunit4-console/Options/OutputSpecification.cs index 2390025bc..24f03b8b5 100644 --- a/src/NUnitConsole/nunit4-console/Options/OutputSpecification.cs +++ b/src/NUnitConsole/nunit4-console/Options/OutputSpecification.cs @@ -17,7 +17,7 @@ public class OutputSpecification /// /// The option value string. /// The folder containing the transform. - public OutputSpecification(string spec, string transformFolder) + public OutputSpecification(string spec, string? transformFolder) { if (spec == null) throw new ArgumentNullException(nameof(spec), "Output spec may not be null"); @@ -78,7 +78,7 @@ public OutputSpecification(string spec, string transformFolder) /// /// Gets the file name of a transform to be applied /// - public string Transform { get; private set; } + public string? Transform { get; private set; } public override string ToString() { diff --git a/src/NUnitConsole/nunit4-console/Program.cs b/src/NUnitConsole/nunit4-console/Program.cs index 9119b898f..5b0ed50d4 100644 --- a/src/NUnitConsole/nunit4-console/Program.cs +++ b/src/NUnitConsole/nunit4-console/Program.cs @@ -21,7 +21,7 @@ public class Program { //static Logger log = InternalTrace.GetLogger(typeof(Runner)); static readonly ConsoleOptions Options = new ConsoleOptions(new DefaultOptionsProvider(), new FileSystem()); - private static ExtendedTextWriter _outWriter; + private static ExtendedTextWriter? _outWriter; // This has to be lazy otherwise NoColor command line option is not applied correctly private static ExtendedTextWriter OutWriter @@ -174,7 +174,7 @@ public static int Main(string[] args) private static void WriteHeader() { - Assembly entryAssembly = Assembly.GetEntryAssembly(); + Assembly entryAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var versionBlock = FileVersionInfo.GetVersionInfo(entryAssembly.ManifestModule.FullyQualifiedName); var header = $"{versionBlock.ProductName} {versionBlock.ProductVersion}"; @@ -188,7 +188,7 @@ private static void WriteHeader() } OutWriter.WriteLine(ColorStyle.Header, header); - OutWriter.WriteLine(ColorStyle.SubHeader, versionBlock.LegalCopyright); + OutWriter.WriteLine(ColorStyle.SubHeader, versionBlock.LegalCopyright ?? "No Copyright statement found"); OutWriter.WriteLine(ColorStyle.SubHeader, DateTime.Now.ToString(CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern)); OutWriter.WriteLine(); } @@ -271,7 +271,7 @@ private static void WriteErrorMessage(string msg) OutWriter.WriteLine(ColorStyle.Error, msg); } - private static void CancelHandler(object sender, ConsoleCancelEventArgs args) + private static void CancelHandler(object? sender, ConsoleCancelEventArgs args) { Console.ResetColor(); } diff --git a/src/NUnitConsole/nunit4-console/ResultReporter.cs b/src/NUnitConsole/nunit4-console/ResultReporter.cs index 2e7dfcf85..35893f5a2 100644 --- a/src/NUnitConsole/nunit4-console/ResultReporter.cs +++ b/src/NUnitConsole/nunit4-console/ResultReporter.cs @@ -5,11 +5,10 @@ using System.Globalization; using System.Xml; using NUnit.ConsoleRunner.Options; +using NUnit.ConsoleRunner.Utilities; namespace NUnit.ConsoleRunner { - using Utilities; - public class ResultReporter { public ResultReporter(XmlNode resultNode, ExtendedTextWriter writer, ConsoleOptions options) @@ -18,12 +17,13 @@ public ResultReporter(XmlNode resultNode, ExtendedTextWriter writer, ConsoleOpti Writer = writer; Options = options; - OverallResult = resultNode.GetAttribute("result"); - if (OverallResult == "Skipped") + string? overallResult = resultNode.GetAttribute("result"); + if (overallResult == "Skipped") OverallResult = "Warning"; - if (OverallResult == null) + if (overallResult == null) OverallResult = "Unknown"; - + else + OverallResult = overallResult; Summary = new ResultSummary(resultNode); } @@ -60,7 +60,7 @@ internal void WriteRunSettingsReport() { var settings = firstSuite.SelectNodes("settings/setting"); - if (settings.Count > 0) + if (settings is not null && settings.Count > 0) { Writer.WriteLine(ColorStyle.SectionHeader, "Run Settings"); @@ -78,13 +78,18 @@ private void WriteSettingsNode(XmlNode node) var name = node.GetAttribute("name"); var val = node.GetAttribute("value") ?? string.Empty; - Writer.WriteLabelLine($" {name}:", items.Count > 0 ? string.Empty : $" |{val}|"); - - foreach (XmlNode item in items) + if (items is null || items.Count == 0) + Writer.WriteLabelLine($" {name}:", $" |{val}|"); + else { - var key = item.GetAttribute("key"); - var value = item.GetAttribute("value"); - Writer.WriteLine(ColorStyle.Value, $" {key} -> |{value}|"); + Writer.WriteLabelLine($" {name}:", string.Empty); + + foreach (XmlNode item in items) + { + var key = item.GetAttribute("key"); + var value = item.GetAttribute("value"); + Writer.WriteLine(ColorStyle.Value, $" {key} -> |{value}|"); + } } } @@ -154,7 +159,7 @@ public void WriteErrorsFailuresAndWarningsReport() private void WriteErrorsFailuresAndWarnings(XmlNode resultNode) { - string resultState = resultNode.GetAttribute("result"); + string? resultState = resultNode.GetAttribute("result"); switch (resultNode.Name) { @@ -225,7 +230,7 @@ private void WriteNotRunResults(XmlNode resultNode) switch (resultNode.Name) { case "test-case": - string status = resultNode.GetAttribute("result"); + string? status = resultNode.GetAttribute("result"); if (status == "Skipped") new ConsoleTestResult(resultNode, ++ReportIndex).WriteResult(Writer); diff --git a/src/NUnitConsole/nunit4-console/ResultSummary.cs b/src/NUnitConsole/nunit4-console/ResultSummary.cs index b2f408ace..73c68ab09 100644 --- a/src/NUnitConsole/nunit4-console/ResultSummary.cs +++ b/src/NUnitConsole/nunit4-console/ResultSummary.cs @@ -3,11 +3,10 @@ using System; using System.Globalization; using System.Xml; +using NUnit.ConsoleRunner.Utilities; namespace NUnit.ConsoleRunner { - using Utilities; - /// /// Summary description for ResultSummary. /// @@ -138,10 +137,10 @@ private void InitializeCounters() private void Summarize(XmlNode node, bool failedInFixtureTearDown) { - string type = node.GetAttribute("type"); - string status = node.GetAttribute("result"); - string label = node.GetAttribute("label"); - string site = node.GetAttribute("site"); + string? type = node.GetAttribute("type"); + string? status = node.GetAttribute("result"); + string? label = node.GetAttribute("label"); + string? site = node.GetAttribute("site"); switch (node.Name) { diff --git a/src/NUnitConsole/nunit4-console/SafeAttributeAccess.cs b/src/NUnitConsole/nunit4-console/SafeAttributeAccess.cs index 35c0ef6b3..6566ef87d 100644 --- a/src/NUnitConsole/nunit4-console/SafeAttributeAccess.cs +++ b/src/NUnitConsole/nunit4-console/SafeAttributeAccess.cs @@ -23,9 +23,9 @@ public static class SafeAttributeAccess /// The result. /// The name. /// - public static string GetAttribute(this XmlNode result, string name) + public static string? GetAttribute(this XmlNode result, string name) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? null : attr.Value; } @@ -39,7 +39,7 @@ public static string GetAttribute(this XmlNode result, string name) /// public static double GetAttribute(this XmlNode result, string name, double defaultValue) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? defaultValue @@ -55,7 +55,7 @@ public static double GetAttribute(this XmlNode result, string name, double defau /// public static DateTime GetAttribute(this XmlNode result, string name, DateTime defaultValue) { - string dateStr = GetAttribute(result, name); + string? dateStr = GetAttribute(result, name); if (dateStr == null) return defaultValue; diff --git a/src/NUnitConsole/nunit4-console/TestEventHandler.cs b/src/NUnitConsole/nunit4-console/TestEventHandler.cs index 23013310f..871f5e260 100644 --- a/src/NUnitConsole/nunit4-console/TestEventHandler.cs +++ b/src/NUnitConsole/nunit4-console/TestEventHandler.cs @@ -22,7 +22,7 @@ public class TestEventHandler : ITestEventListener private readonly bool _displayAfterTest; private readonly bool _displayBeforeOutput; - private string _lastTestOutput; + private string? _lastTestOutput; private bool _wantNewLine = false; public TestEventHandler(ExtendedTextWriter outWriter, string labelsOption) @@ -41,6 +41,9 @@ public void OnTestEvent(string report) doc.LoadXml(report); var testEvent = doc.FirstChild; + if (testEvent == null) + return; + switch (testEvent.Name) { case "start-test": @@ -63,16 +66,20 @@ public void OnTestEvent(string report) private void TestStarted(XmlNode testResult) { - var testName = testResult.Attributes["fullname"].Value; + var testName = testResult.Attributes?["fullname"]?.Value; - if (_displayBeforeTest) + if (_displayBeforeTest && testName != null) WriteLabelLine(testName); } private void TestFinished(XmlNode testResult) { - var testName = testResult.Attributes["fullname"].Value; - var status = testResult.GetAttribute("label") ?? testResult.GetAttribute("result"); + var testName = testResult.Attributes?["fullname"]?.Value; + + if (testName == null) + return; + + var status = testResult.GetAttribute("label") ?? testResult.GetAttribute("result") ?? "Unknown"; var outputNode = testResult.SelectSingleNode("output"); if (outputNode != null) @@ -90,10 +97,10 @@ private void TestFinished(XmlNode testResult) private void SuiteFinished(XmlNode testResult) { - var suiteName = testResult.Attributes["fullname"].Value; + var suiteName = testResult.Attributes?["fullname"]?.Value; var outputNode = testResult.SelectSingleNode("output"); - if (outputNode != null) + if (suiteName != null && outputNode != null) { if (_displayBeforeOutput) WriteLabelLine(suiteName); @@ -106,15 +113,18 @@ private void SuiteFinished(XmlNode testResult) private void TestOutput(XmlNode outputNode) { var testName = outputNode.GetAttribute("testname"); - var stream = outputNode.GetAttribute("stream"); - if (_displayBeforeOutput && testName != null) - WriteLabelLine(testName); + if (testName != null) + { + if (_displayBeforeOutput) + WriteLabelLine(testName); - WriteOutputLine(testName, outputNode.InnerText, stream == "Error" ? ColorStyle.Error : ColorStyle.Output); + var stream = outputNode.GetAttribute("stream"); + WriteOutputLine(testName, outputNode.InnerText, stream == "Error" ? ColorStyle.Error : ColorStyle.Output); + } } - private string _currentLabel; + private string? _currentLabel; private void WriteLabelLine(string label) { @@ -198,7 +208,7 @@ private static ColorStyle GetColorForResultStatus(string status) #if NETFRAMEWORK public override object InitializeLifetimeService() { - return null; + return null!; } #endif } diff --git a/src/NUnitConsole/nunit4-console/nunit4-console.csproj b/src/NUnitConsole/nunit4-console/nunit4-console.csproj index e49338b0c..f965a98ec 100644 --- a/src/NUnitConsole/nunit4-console/nunit4-console.csproj +++ b/src/NUnitConsole/nunit4-console/nunit4-console.csproj @@ -16,6 +16,8 @@ The standard command-line runner for NUnit + + ..\..\..\nunit.ico app.manifest @@ -23,6 +25,8 @@ + + diff --git a/src/NUnitConsole/nunit4-netcore-console/ColorConsoleWriter.cs b/src/NUnitConsole/nunit4-netcore-console/ColorConsoleWriter.cs index 08853b4b9..69d77653e 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ColorConsoleWriter.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ColorConsoleWriter.cs @@ -89,7 +89,7 @@ public override void WriteLabel(string label, object option, ColorStyle valueSty Guard.ArgumentNotNull(option, nameof(option)); Write(ColorStyle.Label, label); - Write(valueStyle, option.ToString()); + Write(valueStyle, option.ToString() ?? string.Empty); } /// diff --git a/src/NUnitConsole/nunit4-netcore-console/ConsoleOptions.cs b/src/NUnitConsole/nunit4-netcore-console/ConsoleOptions.cs index ed746ed43..66d60717d 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ConsoleOptions.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ConsoleOptions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -52,7 +53,9 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public IDictionary TestParameters { get; } = new Dictionary(); - public string WhereClause { get; private set; } + public string? WhereClause { get; private set; } + + [MemberNotNullWhen(true, nameof(WhereClause))] public bool WhereClauseSpecified { get { return WhereClause != null; } } public int DefaultTestCaseTimeout { get; private set; } = -1; @@ -61,7 +64,7 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public int RandomSeed { get; private set; } = -1; public bool RandomSeedSpecified { get { return RandomSeed >= 0; } } - public string DefaultTestNamePattern { get; private set; } + public string? DefaultTestNamePattern { get; private set; } public int NumberOfTestWorkers { get; private set; } = -1; public bool NumberOfTestWorkersSpecified { get { return NumberOfTestWorkers >= 0; } } @@ -71,7 +74,7 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys // Output Control - public string ConsoleEncoding { get; private set; } + public string? ConsoleEncoding { get; private set; } public bool NoHeader { get; private set; } @@ -79,19 +82,23 @@ internal ConsoleOptions(IDefaultOptionsProvider defaultOptionsProvider, IFileSys public bool TeamCity { get; private set; } - public string OutFile { get; private set; } + public string? OutFile { get; private set; } + + [MemberNotNullWhen(true, nameof(OutFile))] public bool OutFileSpecified { get { return OutFile != null; } } - public string DisplayTestLabels { get; private set; } + public string? DisplayTestLabels { get; private set; } - private string workDirectory = null; + private string? workDirectory = null; public string WorkDirectory { get { return workDirectory ?? CURRENT_DIRECTORY_ON_ENTRY; } } public bool WorkDirectorySpecified { get { return workDirectory != null; } } - public string InternalTraceLevel { get; private set; } + public string? InternalTraceLevel { get; private set; } + + [MemberNotNullWhen(true, nameof(InternalTraceLevel))] public bool InternalTraceLevelSpecified { get { return InternalTraceLevel != null; } } private readonly List resultOutputSpecifications = new List(); @@ -114,15 +121,19 @@ public IList ResultOutputSpecifications public IList ExploreOutputSpecifications { get; } = new List(); - public string ActiveConfig { get; private set; } + public string? ActiveConfig { get; private set; } + + [MemberNotNullWhen(true, nameof(ActiveConfig))] public bool ActiveConfigSpecified { get { return ActiveConfig != null; } } // How to Run Tests - public string RuntimeFramework { get; private set; } + public string? RuntimeFramework { get; private set; } + + [MemberNotNullWhen(true, nameof(RuntimeFramework))] public bool RuntimeFrameworkSpecified { get { return RuntimeFramework != null; } } - public string ConfigurationFile { get; private set; } + public string? ConfigurationFile { get; private set; } public bool RunAsX86 { get; private set; } @@ -146,7 +157,7 @@ public IList ResultOutputSpecifications public bool PauseBeforeRun { get; private set; } - public string PrincipalPolicy { get; private set; } + public string? PrincipalPolicy { get; private set; } public IList WarningMessages { get; } = new List(); @@ -179,7 +190,7 @@ private void ConfigureOptions() { while (!rdr.EndOfStream) { - var line = rdr.ReadLine().Trim(); + var line = rdr.ReadLine()?.Trim(); if (!string.IsNullOrEmpty(line) && line[0] != '#') ((List)TestList).Add(line); @@ -216,10 +227,10 @@ private void ConfigureOptions() v => NumberOfTestWorkers = parser.RequiredInt(v, "--workers")); this.Add("stoponerror", "Stop run immediately upon any test failure or error.", - v => StopOnError = v != null); + v => StopOnError = !string.IsNullOrEmpty(v)); this.Add("wait", "Wait for input before closing console window.", - v => WaitBeforeExit = v != null); + v => WaitBeforeExit = !string.IsNullOrEmpty(v)); // Output Control this.Add("work=", "{PATH} of the directory to use for output files. If not specified, defaults to the current directory.", @@ -243,7 +254,7 @@ private void ConfigureOptions() }); this.Add("noresult", "Don't save any test results.", - v => NoResultSpecified = v != null); + v => NoResultSpecified = !string.IsNullOrEmpty(v)); this.Add("labels=", "Specify whether to write test case names to the output. Values: Off (Default), On, OnOutput, Before, After, BeforeAndAfter. On is currently an alias for OnOutput, but is subject to change.", v => { @@ -257,19 +268,19 @@ private void ConfigureOptions() v => InternalTraceLevel = parser.RequiredValue(v, "--trace", "Off", "Error", "Warning", "Info", "Verbose", "Debug")); this.Add("teamcity", "Turns on use of TeamCity service messages. TeamCity engine extension is required.", - v => TeamCity = v != null); + v => TeamCity = !string.IsNullOrEmpty(v)); this.Add("noheader|noh", "Suppress display of program information at start of run.", - v => NoHeader = v != null); + v => NoHeader = !string.IsNullOrEmpty(v)); this.Add("nocolor|noc", "Displays console output without color.", - v => NoColor = v != null); + v => NoColor = !string.IsNullOrEmpty(v)); this.Add("help|h", "Display this message and exit.", - v => ShowHelp = v != null); + v => ShowHelp = !string.IsNullOrEmpty(v)); this.Add("version|V", "Display the header and exit.", - v => ShowVersion = v != null); + v => ShowVersion = !string.IsNullOrEmpty(v)); this.Add("encoding=", "Specifies the encoding to use for Console standard output, for example utf-8, ascii, unicode.", v => ConsoleEncoding = parser.RequiredValue(v, "--encoding")); @@ -294,38 +305,38 @@ private void ConfigureOptions() NetFxOnlyOption("framework=", v => RuntimeFramework = parser.RequiredValue(v, "--framework"))); this.AddNetFxOnlyOption("x86", "Run tests in an x86 process on 64 bit systems", - NetFxOnlyOption("x86", v => RunAsX86 = v != null)); + NetFxOnlyOption("x86", v => RunAsX86 = !string.IsNullOrEmpty(v))); this.Add("dispose-runners", "Dispose each test runner after it has finished running its tests.", - v => DisposeRunners = v != null); + v => DisposeRunners = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("shadowcopy", "Shadow copy test files", - NetFxOnlyOption("shadowcopy", v => ShadowCopyFiles = v != null)); + NetFxOnlyOption("shadowcopy", v => ShadowCopyFiles = !string.IsNullOrEmpty(v))); this.AddNetFxOnlyOption("loaduserprofile", "Load user profile in test runner processes", - NetFxOnlyOption("loaduserprofile", v => LoadUserProfile = v != null)); + NetFxOnlyOption("loaduserprofile", v => LoadUserProfile = !string.IsNullOrEmpty(v))); this.Add("skipnontestassemblies", "Skip any non-test assemblies specified, without error.", - v => SkipNonTestAssemblies = v != null); + v => SkipNonTestAssemblies = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("agents=", "Specify the maximum {NUMBER} of test assembly agents to run at one time. If not specified, there is no limit.", NetFxOnlyOption("agents=", v => _maxAgents = parser.RequiredInt(v, "--agents"))); this.AddNetFxOnlyOption("debug", "Launch debugger to debug tests.", - NetFxOnlyOption("debug", v => DebugTests = v != null)); + NetFxOnlyOption("debug", v => DebugTests = !string.IsNullOrEmpty(v))); this.AddNetFxOnlyOption("pause", "Pause before running to allow attaching a debugger.", - NetFxOnlyOption("pause", v => PauseBeforeRun = v != null)); + NetFxOnlyOption("pause", v => PauseBeforeRun = !string.IsNullOrEmpty(v))); this.Add("list-extensions", "List all extension points and the extensions for each.", - v => ListExtensions = v != null); + v => ListExtensions = !string.IsNullOrEmpty(v)); this.AddNetFxOnlyOption("set-principal-policy=", "Set PrincipalPolicy for the test domain.", NetFxOnlyOption("set-principal-policy=", v => PrincipalPolicy = parser.RequiredValue(v, "--set-principal-policy", "UnauthenticatedPrincipal", "NoPrincipal", "WindowsPrincipal"))); #if DEBUG this.AddNetFxOnlyOption("debug-agent", "Launch debugger in nunit-agent when it starts.", - NetFxOnlyOption("debug-agent", v => DebugAgent = v != null)); + NetFxOnlyOption("debug-agent", v => DebugAgent = !string.IsNullOrEmpty(v))); #endif } @@ -431,7 +442,7 @@ private IEnumerable GetArgsFromFile(string filename) return GetArgs(sb.ToString()); } - private string ExpandToFullPath(string path) + private string? ExpandToFullPath(string path) { if (path == null) return null; diff --git a/src/NUnitConsole/nunit4-netcore-console/ConsoleRunner.cs b/src/NUnitConsole/nunit4-netcore-console/ConsoleRunner.cs index 7bdc21d99..581f87692 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ConsoleRunner.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ConsoleRunner.cs @@ -142,7 +142,7 @@ private int ExploreTests(TestPackage package, TestFilter filter) { foreach (OutputSpecification spec in _options.ExploreOutputSpecifications) { - _resultService.GetResultWriter(spec.Format, new object[] {spec.Transform}).WriteResultFile(result, spec.OutputPath); + _resultService.GetResultWriter(spec.Format, spec.Transform).WriteResultFile(result, spec.OutputPath); _outWriter.WriteLine("Results ({0}) saved as {1}", spec.Format, spec.OutputPath); } } @@ -172,7 +172,7 @@ private int RunTests(TestPackage package, TestFilter filter) try { - var outputDirectory = Path.GetDirectoryName(outputPath); + var outputDirectory = Path.GetDirectoryName(outputPath)!; Directory.CreateDirectory(outputDirectory); } catch (Exception ex) @@ -202,9 +202,9 @@ private int RunTests(TestPackage package, TestFilter filter) ? _options.DisplayTestLabels.ToUpperInvariant() : "ON"; - XmlNode result = null; - NUnitEngineUnloadException unloadException = null; - NUnitEngineException engineException = null; + XmlNode? result = null; + NUnitEngineUnloadException? unloadException = null; + NUnitEngineException? engineException = null; try { @@ -380,7 +380,7 @@ private ExtendedTextWriter CreateOutputWriter() private IResultWriter GetResultWriter(OutputSpecification spec) { - return _resultService.GetResultWriter(spec.Format, new object[] {spec.Transform}); + return _resultService.GetResultWriter(spec.Format, spec.Transform); } // This is public static for ease of testing diff --git a/src/NUnitConsole/nunit4-netcore-console/ConsoleTestResult.cs b/src/NUnitConsole/nunit4-netcore-console/ConsoleTestResult.cs index 971bc3fa0..19265bd28 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ConsoleTestResult.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ConsoleTestResult.cs @@ -31,23 +31,23 @@ public ConsoleTestResult(XmlNode resultNode, int reportIndex) Label = resultNode.GetAttribute("label"); Site = resultNode.GetAttribute("site"); - Status = Label ?? Result; + Status = Label ?? Result ?? "Unkown"; if (Status == "Failed" || Status == "Error") if (Site == "SetUp" || Site == "TearDown") Status = Site + " " + Status; - FullName = resultNode.GetAttribute("fullname"); + FullName = resultNode.GetAttribute("fullname") ?? "Unknown"; } - public string Result { get; private set; } - public string Label { get; private set; } - public string Site { get; private set; } + public string? Result { get; private set; } + public string? Label { get; private set; } + public string? Site { get; private set; } public string FullName { get; private set; } public string ReportID { get; private set; } public string Status { get; private set; } - public string Message + public string? Message { get { @@ -56,12 +56,12 @@ public string Message } } - public string StackTrace + public string? StackTrace { get { return GetTrimmedInnerText(_resultNode.SelectSingleNode("failure/stack-trace")); } } - private List _assertions; + private List? _assertions; public List Assertions { get @@ -69,8 +69,10 @@ public List Assertions if (_assertions == null) { _assertions = new List(); - foreach (XmlNode assertion in _resultNode.SelectNodes("assertions/assertion")) - Assertions.Add(new ConsoleTestResult.AssertionResult(assertion)); + XmlNodeList? assertions = _resultNode.SelectNodes("assertions/assertion"); + if (assertions is not null) + foreach (XmlNode assertion in assertions) + Assertions.Add(new ConsoleTestResult.AssertionResult(assertion)); } return _assertions; @@ -98,7 +100,7 @@ public void WriteResult(ExtendedTextWriter writer) WriteResult(writer, ReportID, Status, FullName, Message, StackTrace); } - private void WriteResult(ExtendedTextWriter writer, string reportID, string status, string fullName, string message, string stackTrace) + private void WriteResult(ExtendedTextWriter writer, string reportID, string status, string fullName, string? message, string? stackTrace) { ColorStyle style = GetColorStyle(); @@ -123,7 +125,7 @@ private ColorStyle GetColorStyle() : ColorStyle.Output; } - private static string GetTrimmedInnerText(XmlNode node) + private static string? GetTrimmedInnerText(XmlNode? node) { // In order to control the format, we trim any line-end chars // from end of the strings we write and supply them via calls @@ -141,8 +143,8 @@ public AssertionResult(XmlNode assertion) StackTrace = GetTrimmedInnerText(assertion.SelectSingleNode("stack-trace")); } - public string Message { get; private set; } - public string StackTrace { get; private set; } + public string? Message { get; private set; } + public string? StackTrace { get; private set; } } } } diff --git a/src/NUnitConsole/nunit4-netcore-console/EnginePackageSettings.cs b/src/NUnitConsole/nunit4-netcore-console/EnginePackageSettings.cs deleted file mode 100644 index 96db5c685..000000000 --- a/src/NUnitConsole/nunit4-netcore-console/EnginePackageSettings.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -#if false -using System; - -namespace NUnit -{ - /// - /// EngineSettings contains constant values that - /// are used as keys in setting up a TestPackage. - /// Values here are used in the engine, and set by the runner. - /// Setting values may be a string, int or bool. - /// - public static class EnginePackageSettings - { - /// - /// The name of the config to use in loading a project. - /// If not specified, the first config found is used. - /// - public const string ActiveConfig = "ActiveConfig"; - - /// - /// Bool indicating whether the engine should determine the private - /// bin path by examining the paths to all the tests. Defaults to - /// true unless PrivateBinPath is specified. - /// - public const string AutoBinPath = "AutoBinPath"; - - /// - /// The ApplicationBase to use in loading the tests. If not - /// specified, and each assembly has its own process, then the - /// location of the assembly is used. For multiple assemblies - /// in a single process, the closest common root directory is used. - /// - public const string BasePath = "BasePath"; - - /// - /// Path to the config file to use in running the tests. - /// - public const string ConfigurationFile = "ConfigurationFile"; - - /// - /// Flag (bool) indicating whether tests are being debugged. - /// - public const string DebugTests = "DebugTests"; - - /// - /// Bool flag indicating whether a debugger should be launched at agent - /// startup. Used only for debugging NUnit itself. - /// - public const string DebugAgent = "DebugAgent"; - - /// - /// The private binpath used to locate assemblies. Directory paths - /// is separated by a semicolon. It's an error to specify this and - /// also set AutoBinPath to true. - /// - public const string PrivateBinPath = "PrivateBinPath"; - - /// - /// The maximum number of test agents permitted to run simultaneously. - /// Ignored if the ProcessModel is not set or defaulted to Multiple. - /// - public const string MaxAgents = "MaxAgents"; - - /// - /// Indicates how to allocate assemblies to processes. Values are: - /// "Default", "Single", "Separate", "Multiple". Default is "Multiple" - /// for more than one assembly, "Separate" for a single assembly. - /// - public const string ProcessModel = "ProcessModel"; - - /// - /// Indicates the desired runtime to use for the tests. Values - /// are strings like "net-4.5", "mono-4.0", etc. Default is to - /// use the target framework for which an assembly was built. - /// - [Obsolete("Use 'RequestedRuntimeFramework' instead.")] - public const string RuntimeFramework = "RequestedRuntimeFramework"; - - /// - /// Indicates the desired runtime to use for the tests. Values - /// are strings like "net-4.5", "mono-4.0", etc. Default is to - /// use the target framework for which an assembly was built. - /// - public const string RequestedRuntimeFramework = "RequestedRuntimeFramework"; - - /// - /// Indicates the Target runtime selected for use by the engine, - /// based on the requested runtime and assembly metadata. - /// - public const string TargetRuntimeFramework = "TargetRuntimeFramework"; - - /// - /// Bool flag indicating that the test should be run in a 32-bit process - /// on a 64-bit system. By default, NUNit runs in a 64-bit process on - /// a 64-bit system. Ignored if set on a 32-bit system. - /// - public const string RunAsX86 = "RunAsX86"; - - /// - /// Indicates that test runners should be disposed after the tests are executed - /// - public const string DisposeRunners = "DisposeRunners"; - - /// - /// Bool flag indicating that the test assemblies should be shadow copied. - /// Defaults to false. - /// - public const string ShadowCopyFiles = "ShadowCopyFiles"; - - /// - /// Bool flag indicating that user profile should be loaded on test runner processes - /// - public const string LoadUserProfile = "LoadUserProfile"; - - /// - /// Bool flag indicating that non-test assemblies should be skipped without error. - /// - public const string SkipNonTestAssemblies = "SkipNonTestAssemblies"; - - /// - /// Flag (bool) indicating whether to pause execution of tests to allow - /// the user to attach a debugger. - /// - public const string PauseBeforeRun = "PauseBeforeRun"; - - /// - /// The InternalTraceLevel for this run. Values are: "Default", - /// "Off", "Error", "Warning", "Info", "Debug", "Verbose". - /// Default is "Off". "Debug" and "Verbose" are synonyms. - /// - public const string InternalTraceLevel = "InternalTraceLevel"; - - /// - /// The PrincipalPolicy to set on the test application domain. Values are: - /// "UnauthenticatedPrincipal", "NoPrincipal" and "WindowsPrincipal". - /// - public const string PrincipalPolicy = "PrincipalPolicy"; - } -} -#endif diff --git a/src/NUnitConsole/nunit4-netcore-console/ExtendedTextWrapper.cs b/src/NUnitConsole/nunit4-netcore-console/ExtendedTextWrapper.cs index e0ce8af53..08892960a 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ExtendedTextWrapper.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ExtendedTextWrapper.cs @@ -31,7 +31,7 @@ public override void Write(char value) /// /// Write a string value /// - public override void Write(string value) + public override void Write(string? value) { _writer.Write(value); } @@ -39,7 +39,7 @@ public override void Write(string value) /// /// Write a string value followed by a NewLine /// - public override void WriteLine(string value) + public override void WriteLine(string? value) { _writer.WriteLine(value); } diff --git a/src/NUnitConsole/nunit4-netcore-console/FileSystem.cs b/src/NUnitConsole/nunit4-netcore-console/FileSystem.cs index 4636d8701..c1f78f008 100644 --- a/src/NUnitConsole/nunit4-netcore-console/FileSystem.cs +++ b/src/NUnitConsole/nunit4-netcore-console/FileSystem.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.IO; +using System; namespace NUnit.ConsoleRunner { - using System; - internal class FileSystem : IFileSystem { public bool FileExists(string fileName) @@ -22,7 +21,7 @@ public IEnumerable ReadLines(string fileName) using (var file = File.OpenText(fileName)) { - string line; + string? line; while ((line = file.ReadLine()) != null) { yield return line; diff --git a/src/NUnitConsole/nunit4-netcore-console/Options/DefaultOptionsProvider.cs b/src/NUnitConsole/nunit4-netcore-console/Options/DefaultOptionsProvider.cs index 33bdd1e16..72fd0c520 100644 --- a/src/NUnitConsole/nunit4-netcore-console/Options/DefaultOptionsProvider.cs +++ b/src/NUnitConsole/nunit4-netcore-console/Options/DefaultOptionsProvider.cs @@ -1,9 +1,9 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt +using System; + namespace NUnit.ConsoleRunner.Options { - using System; - internal sealed class DefaultOptionsProvider : IDefaultOptionsProvider { private const string EnvironmentVariableTeamcityProjectName = "TEAMCITY_PROJECT_NAME"; diff --git a/src/NUnitConsole/nunit4-netcore-console/Options/OptionParser.cs b/src/NUnitConsole/nunit4-netcore-console/Options/OptionParser.cs index 158c62d46..40f4fb6be 100644 --- a/src/NUnitConsole/nunit4-netcore-console/Options/OptionParser.cs +++ b/src/NUnitConsole/nunit4-netcore-console/Options/OptionParser.cs @@ -95,7 +95,7 @@ private bool IsQuotedString(string value) return false; } - public OutputSpecification ResolveOutputSpecification(string value, IList outputSpecifications, IFileSystem fileSystem, string currentDir) + public OutputSpecification? ResolveOutputSpecification(string value, IList outputSpecifications, IFileSystem fileSystem, string currentDir) { if (value == null) return null; diff --git a/src/NUnitConsole/nunit4-netcore-console/Options/Options.cs b/src/NUnitConsole/nunit4-netcore-console/Options/Options.cs index 56d4b19f1..7cb932a4c 100644 --- a/src/NUnitConsole/nunit4-netcore-console/Options/Options.cs +++ b/src/NUnitConsole/nunit4-netcore-console/Options/Options.cs @@ -160,6 +160,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; #if PCL @@ -170,6 +171,8 @@ #endif using System.Text; using System.Text.RegularExpressions; +using NUnit.Common; + #if LINQ using System.Linq; @@ -308,14 +311,14 @@ internal OptionValueCollection(OptionContext c) #endregion #region IList - int IList.Add(object value) { return (values as IList).Add(value); } - bool IList.Contains(object value) { return (values as IList).Contains(value); } - int IList.IndexOf(object value) { return (values as IList).IndexOf(value); } - void IList.Insert(int index, object value) { (values as IList).Insert(index, value); } - void IList.Remove(object value) { (values as IList).Remove(value); } + int IList.Add(object? value) { return (values as IList).Add(value); } + bool IList.Contains(object? value) { return (values as IList).Contains(value); } + int IList.IndexOf(object? value) { return (values as IList).IndexOf(value); } + void IList.Insert(int index, object? value) { (values as IList).Insert(index, value); } + void IList.Remove(object? value) { (values as IList).Remove(value); } void IList.RemoveAt(int index) { (values as IList).RemoveAt(index); } bool IList.IsFixedSize { get { return false; } } - object IList.this[int index] { get { return this[index]; } set { (values as IList)[index] = value; } } + object? IList.this[int index] { get { return this[index]; } set { (values as IList)[index] = value; } } #endregion #region IList @@ -336,12 +339,13 @@ private void AssertValid(int index) c.OptionName); } + [DisallowNull] public string this[int index] { get { AssertValid(index); - return index >= values.Count ? null : values[index]; + return index >= values.Count ? string.Empty : values[index]; } set { @@ -368,8 +372,8 @@ public override string ToString() public class OptionContext { - private Option option; - private string name; + private Option? option; + private string? name; private int index; private readonly OptionSet set; private readonly OptionValueCollection c; @@ -380,13 +384,13 @@ public OptionContext(OptionSet set) this.c = new OptionValueCollection(this); } - public Option Option + public Option? Option { get { return option; } set { option = value; } } - public string OptionName + public string? OptionName { get { return name; } set { name = value; } @@ -419,24 +423,24 @@ public enum OptionValueType public abstract class Option { readonly string prototype; - readonly string description; + readonly string? description; readonly string[] names; readonly OptionValueType type; readonly int count; - string[] separators; + string[]? separators; readonly bool hidden; - protected Option(string prototype, string description) + protected Option(string prototype, string? description) : this(prototype, description, 1, false) { } - protected Option(string prototype, string description, int maxValueCount) + protected Option(string prototype, string? description, int maxValueCount) : this(prototype, description, maxValueCount, false) { } - protected Option(string prototype, string description, int maxValueCount, bool hidden) + protected Option(string prototype, string? description, int maxValueCount, bool hidden) { if (prototype == null) throw new ArgumentNullException("prototype"); @@ -478,7 +482,7 @@ protected Option(string prototype, string description, int maxValueCount, bool h } public string Prototype { get { return prototype; } } - public string Description { get { return description; } } + public string? Description { get { return description; } } public OptionValueType OptionValueType { get { return type; } } public int MaxValueCount { get { return count; } } public bool Hidden { get { return hidden; } } @@ -491,7 +495,7 @@ public string[] GetNames() public string[] GetValueSeparators() { if (separators == null) - return new string[0]; + return Array.Empty(); return (string[])separators.Clone(); } @@ -513,19 +517,19 @@ protected static T Parse(string value, OptionContext c) #else Type targetType = nullable ? tt.GetGenericArguments()[0] : tt; #endif - T t = default(T); + T t = default(T)!; try { if (value != null) { #if PCL - if (targetType.GetTypeInfo ().IsEnum) - t = (T) Enum.Parse (targetType, value, true); - else - t = (T) Convert.ChangeType (value, targetType); + if (targetType.GetTypeInfo ().IsEnum) + t = (T) Enum.Parse (targetType, value, true); + else + t = (T) Convert.ChangeType (value, targetType); #else TypeConverter conv = TypeDescriptor.GetConverter(targetType); - t = (T)conv.ConvertFromString(value); + t = (T)conv.ConvertFromString(value)!; #endif } } @@ -537,11 +541,12 @@ protected static T Parse(string value, OptionContext c) value, targetType.Name, c.OptionName), c.OptionName, e); } + return t; } internal string[] Names { get { return names; } } - internal string[] ValueSeparators { get { return separators; } } + internal string[]? ValueSeparators { get { return separators; } } static readonly char[] NameTerminator = new char[] { '=', ':' }; @@ -551,7 +556,7 @@ private OptionValueType ParsePrototype() List seps = new List(); for (int i = 0; i < names.Length; ++i) { - string name = names[i]; + string? name = names[i]; if (name.Length == 0) throw new ArgumentException("Empty option names are not supported.", "prototype"); @@ -652,7 +657,7 @@ protected ArgumentSource() public abstract string[] GetNames(); public abstract string Description { get; } - public abstract bool GetArguments(string value, out IEnumerable replacement); + public abstract bool GetArguments(string value, [NotNullWhen(true)] out IEnumerable? replacement); #if !PCL || NETSTANDARD1_3 public static IEnumerable GetArgumentsFromFile(string file) @@ -673,7 +678,7 @@ static IEnumerable GetArguments(TextReader reader, bool close) { StringBuilder arg = new StringBuilder(); - string line; + string? line; while ((line = reader.ReadLine()) != null) { int t = line.Length; @@ -735,7 +740,7 @@ public override string Description get { return "Read response file for more options."; } } - public override bool GetArguments(string value, out IEnumerable replacement) + public override bool GetArguments(string value, [NotNullWhen(true)] out IEnumerable? replacement) { if (string.IsNullOrEmpty(value) || !value.StartsWith("@")) { @@ -753,19 +758,19 @@ public override bool GetArguments(string value, out IEnumerable replacem #endif public class OptionException : Exception { - private string option; + private string? option; public OptionException() { } - public OptionException(string message, string optionName) + public OptionException(string message, string? optionName) : base(message) { this.option = optionName; } - public OptionException(string message, string optionName, Exception innerException) + public OptionException(string message, string? optionName, Exception innerException) : base(message, innerException) { this.option = optionName; @@ -780,7 +785,7 @@ protected OptionException(SerializationInfo info, StreamingContext context) } #endif - public string OptionName + public string? OptionName { get { return this.option; } } @@ -804,20 +809,20 @@ public OptionSet() { } - public OptionSet(MessageLocalizerConverter localizer) + public OptionSet(MessageLocalizerConverter? localizer) + : base(StringComparer.Ordinal, dictionaryCreationThreshold: 0) { this.roSources = new ReadOnlyCollection(sources); - this.localizer = localizer; - if (this.localizer == null) - { - this.localizer = delegate (string f) { + this.localizer = localizer ?? + delegate (string f) { return f; }; - } } MessageLocalizerConverter localizer; + private new IDictionary Dictionary => base.Dictionary!; + public MessageLocalizerConverter MessageLocalizer { get { return localizer; } @@ -845,7 +850,7 @@ protected override string GetKeyForItem(Option item) } [Obsolete("Use KeyedCollection.this[string]")] - protected Option GetOptionForName(string option) + protected Option? GetOptionForName(string option) { if (option == null) throw new ArgumentNullException("option"); @@ -940,12 +945,12 @@ sealed class ActionOption : Option { readonly Action action; - public ActionOption(string prototype, string description, int count, Action action) + public ActionOption(string prototype, string? description, int count, Action action) : this(prototype, description, count, action, false) { } - public ActionOption(string prototype, string description, int count, Action action, bool hidden) + public ActionOption(string prototype, string? description, int count, Action action, bool hidden) : base(prototype, description, count, hidden) { if (action == null) @@ -964,12 +969,12 @@ public OptionSet Add(string prototype, Action action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, Action action) + public OptionSet Add(string prototype, string? description, Action action) { return Add(prototype, description, action, false); } - public OptionSet Add(string prototype, string description, Action action, bool hidden) + public OptionSet Add(string prototype, string? description, Action action, bool hidden) { if (action == null) throw new ArgumentNullException("action"); @@ -984,12 +989,12 @@ public OptionSet Add(string prototype, OptionAction action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, OptionAction action) + public OptionSet Add(string prototype, string? description, OptionAction action) { return Add(prototype, description, action, false); } - public OptionSet Add(string prototype, string description, OptionAction action, bool hidden) + public OptionSet Add(string prototype, string? description, OptionAction action, bool hidden) { if (action == null) throw new ArgumentNullException("action"); @@ -1003,7 +1008,7 @@ sealed class ActionOption : Option { readonly Action action; - public ActionOption(string prototype, string description, Action action) + public ActionOption(string prototype, string? description, Action action) : base(prototype, description, 1) { if (action == null) @@ -1021,7 +1026,7 @@ sealed class ActionOption : Option { readonly OptionAction action; - public ActionOption(string prototype, string description, OptionAction action) + public ActionOption(string prototype, string? description, OptionAction action) : base(prototype, description, 2) { if (action == null) @@ -1042,7 +1047,7 @@ public OptionSet Add(string prototype, Action action) return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, Action action) + public OptionSet Add(string prototype, string? description, Action action) { return Add(new ActionOption(prototype, description, action)); } @@ -1052,7 +1057,7 @@ public OptionSet Add(string prototype, OptionAction return Add(prototype, null, action); } - public OptionSet Add(string prototype, string description, OptionAction action) + public OptionSet Add(string prototype, string? description, OptionAction action) { return Add(new ActionOption(prototype, description, action)); } @@ -1078,7 +1083,7 @@ public List Parse(IEnumerable arguments) c.OptionIndex = -1; bool process = true; List unprocessed = new List(); - Option def = Contains("<>") ? this["<>"] : null; + Option? def = Contains("<>") ? this["<>"] : null; ArgumentEnumerator ae = new ArgumentEnumerator(arguments); foreach (string argument in ae) { @@ -1142,7 +1147,7 @@ bool AddSource(ArgumentEnumerator ae, string argument) { foreach (ArgumentSource source in sources) { - IEnumerable replacement; + IEnumerable? replacement; if (!source.GetArguments(argument, out replacement)) continue; ae.Add(replacement); @@ -1151,7 +1156,7 @@ bool AddSource(ArgumentEnumerator ae, string argument) return false; } - private static bool Unprocessed(ICollection extra, Option def, OptionContext c, string argument) + private static bool Unprocessed(ICollection extra, Option? def, OptionContext c, string argument) { if (def == null) { @@ -1167,7 +1172,7 @@ private static bool Unprocessed(ICollection extra, Option def, OptionCon private readonly Regex ValueOption = new Regex( @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); - protected bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value) + protected bool GetOptionParts(string argument, [NotNullWhen(true)] out string? flag, [NotNullWhen(true)] out string? name, out string? sep, out string? value) { if (argument == null) throw new ArgumentNullException("argument"); @@ -1196,7 +1201,7 @@ protected virtual bool Parse(string argument, OptionContext c) return true; } - string f, n, s, v; + string? f, n, s, v; if (!GetOptionParts(argument, out f, out n, out s, out v)) return false; @@ -1229,8 +1234,10 @@ protected virtual bool Parse(string argument, OptionContext c) return false; } - private void ParseValue(string option, OptionContext c) + private void ParseValue(string? option, OptionContext c) { + Guard.OperationValid(c.Option != null, "OptionContext.Option != null"); + if (option != null) foreach (string o in c.Option.ValueSeparators != null ? option.Split(c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) @@ -1258,7 +1265,7 @@ private bool ParseBool(string option, string n, OptionContext c) Contains((rn = n.Substring(0, n.Length - 1)))) { p = this[rn]; - string v = n[n.Length - 1] == '+' ? option : null; + string v = n[n.Length - 1] == '+' ? option : string.Empty; c.OptionName = option; c.Option = p; c.OptionValues.Add(v); @@ -1330,13 +1337,13 @@ public void WriteOptionDescriptions(TextWriter o) if (p.Hidden) continue; - Category c = p as Category; + Category? c = p as Category; if (c != null) { WriteDescription(o, p.Description, "", 80, 80); continue; } - CommandOption co = p as CommandOption; + CommandOption? co = p as CommandOption; if (co != null) { WriteCommandDescription(o, co.Command, co.CommandName); @@ -1387,7 +1394,7 @@ public void WriteOptionDescriptions(TextWriter o) } } - internal void WriteCommandDescription(TextWriter o, Command c, string commandName) + internal void WriteCommandDescription(TextWriter o, Command c, string? commandName) { var name = new string(' ', 8) + (commandName ?? c.Name); if (name.Length < OptionWidth - 1) @@ -1401,7 +1408,7 @@ internal void WriteCommandDescription(TextWriter o, Command c, string commandNam } } - void WriteDescription(TextWriter o, string value, string prefix, int firstWidth, int remWidth) + void WriteDescription(TextWriter o, string? value, string prefix, int firstWidth, int remWidth) { bool indent = false; foreach (string line in GetLines(localizer(GetDescription(value)), firstWidth, remWidth)) @@ -1478,7 +1485,7 @@ static void Write(TextWriter o, ref int n, string s) o.Write(s); } - static string GetArgumentName(int index, int maxIndex, string description) + static string GetArgumentName(int index, int maxIndex, string? description) { var matches = Regex.Matches(description ?? "", @"(?<=(? GetLines(string description, int firstWidth, public class Command { + private CommandSet? commandSet; + public string Name { get; } - public string Help { get; } + public string? Help { get; } - public OptionSet Options { get; set; } - public Action> Run { get; set; } + public OptionSet? Options { get; set; } + public Action>? Run { get; set; } - public CommandSet CommandSet { get; internal set; } + public CommandSet CommandSet { get => commandSet.ShouldNotBeNull(); internal set => commandSet = value; } - public Command(string name, string help = null) + public Command(string name, string? help = null) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); @@ -1613,7 +1622,7 @@ class CommandOption : Option // Prototype starts with '=' because this is an invalid prototype // (see Option.ParsePrototype(), and thus it'll prevent Category // instances from being accidentally used as normal options. - public CommandOption(Command command, string commandName = null, bool hidden = false) + public CommandOption(Command command, string? commandName = null, bool hidden = false) : base("=:Command:= " + (commandName ?? command?.Name), (commandName ?? command?.Name), maxValueCount: 0, hidden: hidden) { if (command == null) @@ -1652,7 +1661,7 @@ class CommandOptionSet : OptionSet { readonly CommandSet commands; - public CommandOptionSet(CommandSet commands, MessageLocalizerConverter localizer) + public CommandOptionSet(CommandSet commands, MessageLocalizerConverter? localizer) : base(localizer) { this.commands = commands; @@ -1702,22 +1711,22 @@ public class CommandSet : KeyedCollection TextWriter outWriter; TextWriter errorWriter; - internal List NestedCommandSets; + internal List? NestedCommandSets; - internal HelpCommand help; + internal HelpCommand? help; internal bool showHelp; internal OptionSet Options => options; #if !PCL || NETSTANDARD1_3 - public CommandSet(string suite, MessageLocalizerConverter localizer = null) + public CommandSet(string suite, MessageLocalizerConverter? localizer = null) : this(suite, Console.Out, Console.Error, localizer) { } #endif - public CommandSet(string suite, TextWriter output, TextWriter error, MessageLocalizerConverter localizer = null) + public CommandSet(string suite, TextWriter output, TextWriter error, MessageLocalizerConverter? localizer = null) { if (suite == null) throw new ArgumentNullException(nameof(suite)); @@ -1739,7 +1748,7 @@ public CommandSet(string suite, TextWriter output, TextWriter error, MessageLoca protected override string GetKeyForItem(Command item) { - return item?.Name; + return item.Name; } public new CommandSet Add(Command value) @@ -1893,7 +1902,7 @@ bool AlreadyAdded(CommandSet value) return false; } - public IEnumerable GetCompletions(string prefix = null) + public IEnumerable GetCompletions(string? prefix = null) { string rest; ExtractToken(ref prefix, out rest); @@ -1921,7 +1930,7 @@ public IEnumerable GetCompletions(string prefix = null) } } - static void ExtractToken(ref string input, out string rest) + static void ExtractToken([NotNull] ref string? input, out string rest) { rest = ""; input = input ?? ""; @@ -1997,12 +2006,12 @@ public int Run(IEnumerable arguments) return command.Invoke(extra); } - internal Command GetCommand(List extra) + internal Command? GetCommand(List extra) { return TryGetLocalCommand(extra) ?? TryGetNestedCommand(extra); } - Command TryGetLocalCommand(List extra) + Command? TryGetLocalCommand(List extra) { var name = extra[0]; if (Contains(name)) @@ -2021,7 +2030,7 @@ Command TryGetLocalCommand(List extra) return null; } - Command TryGetNestedCommand(List extra) + Command? TryGetNestedCommand(List extra) { if (NestedCommandSets == null) return null; @@ -2055,7 +2064,7 @@ public HelpCommand() public override int Invoke(IEnumerable arguments) { - var extra = new List(arguments ?? new string[0]); + var extra = new List(); var _ = CommandSet.Options.MessageLocalizer; if (extra.Count == 0) { @@ -2080,7 +2089,8 @@ public override int Invoke(IEnumerable arguments) } CommandSet.Options.WriteCommandDescription(CommandSet.Out, c.Value, c.Key); } - CommandSet.Options.WriteCommandDescription(CommandSet.Out, CommandSet.help, "help"); + if (CommandSet.help != null) + CommandSet.Options.WriteCommandDescription(CommandSet.Out, CommandSet.help, "help"); return 0; } if (command == null) @@ -2100,15 +2110,17 @@ List> GetCommands() { var commands = new List>(); - foreach (var c in CommandSet) + var commandSet = CommandSet; + + foreach (var c in commandSet) { commands.Add(new KeyValuePair(c.Name, c)); } - if (CommandSet.NestedCommandSets == null) + if (commandSet.NestedCommandSets == null) return commands; - foreach (var nc in CommandSet.NestedCommandSets) + foreach (var nc in commandSet.NestedCommandSets) { AddNestedCommands(commands, "", nc); } @@ -2132,8 +2144,10 @@ void AddNestedCommands(List> commands, string oute internal void WriteUnknownCommand(string unknownCommand) { - CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Unknown command: {unknownCommand}")); - CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage.")); + var commandSet = CommandSet; + + commandSet.Error.WriteLine(commandSet.Options.MessageLocalizer($"{commandSet.Suite}: Unknown command: {unknownCommand}")); + commandSet.Error.WriteLine(commandSet.Options.MessageLocalizer($"{commandSet.Suite}: Use `{commandSet.Suite} help` for usage.")); } } } diff --git a/src/NUnitConsole/nunit4-netcore-console/Options/OutputSpecification.cs b/src/NUnitConsole/nunit4-netcore-console/Options/OutputSpecification.cs index 2390025bc..24f03b8b5 100644 --- a/src/NUnitConsole/nunit4-netcore-console/Options/OutputSpecification.cs +++ b/src/NUnitConsole/nunit4-netcore-console/Options/OutputSpecification.cs @@ -17,7 +17,7 @@ public class OutputSpecification /// /// The option value string. /// The folder containing the transform. - public OutputSpecification(string spec, string transformFolder) + public OutputSpecification(string spec, string? transformFolder) { if (spec == null) throw new ArgumentNullException(nameof(spec), "Output spec may not be null"); @@ -78,7 +78,7 @@ public OutputSpecification(string spec, string transformFolder) /// /// Gets the file name of a transform to be applied /// - public string Transform { get; private set; } + public string? Transform { get; private set; } public override string ToString() { diff --git a/src/NUnitConsole/nunit4-netcore-console/Program.cs b/src/NUnitConsole/nunit4-netcore-console/Program.cs index 59f545d6e..5103f372b 100644 --- a/src/NUnitConsole/nunit4-netcore-console/Program.cs +++ b/src/NUnitConsole/nunit4-netcore-console/Program.cs @@ -21,7 +21,7 @@ public class Program { //static Logger log = InternalTrace.GetLogger(typeof(Runner)); static readonly ConsoleOptions Options = new ConsoleOptions(new DefaultOptionsProvider(), new FileSystem()); - private static ExtendedTextWriter _outWriter; + private static ExtendedTextWriter? _outWriter; // This has to be lazy otherwise NoColor command line option is not applied correctly private static ExtendedTextWriter OutWriter @@ -173,7 +173,7 @@ public static int Main(string[] args) private static void WriteHeader() { - Assembly entryAssembly = Assembly.GetEntryAssembly(); + Assembly entryAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var versionBlock = FileVersionInfo.GetVersionInfo(entryAssembly.ManifestModule.FullyQualifiedName); var header = $"{versionBlock.ProductName} {versionBlock.ProductVersion}"; @@ -187,7 +187,7 @@ private static void WriteHeader() } OutWriter.WriteLine(ColorStyle.Header, header); - OutWriter.WriteLine(ColorStyle.SubHeader, versionBlock.LegalCopyright); + OutWriter.WriteLine(ColorStyle.SubHeader, versionBlock.LegalCopyright ?? "No Copyright statement found"); OutWriter.WriteLine(ColorStyle.SubHeader, DateTime.Now.ToString(CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern)); OutWriter.WriteLine(); } @@ -195,9 +195,9 @@ private static void WriteHeader() private static void WriteHelpText() { OutWriter.WriteLine(); - OutWriter.WriteLine(ColorStyle.Header, "NUNIT3-CONSOLE [inputfiles] [options]"); + OutWriter.WriteLine(ColorStyle.Header, "NUNIT4-CONSOLE [inputfiles] [options]"); OutWriter.WriteLine(); - OutWriter.WriteLine(ColorStyle.SectionHeader, "Operation"); + OutWriter.WriteLine(ColorStyle.SectionHeader, "Operation:"); using (new ColorConsole(ColorStyle.Default)) { OutWriter.WriteLine(" The NetCore Console Runner runs a set of NUnit tests from the console"); @@ -294,7 +294,7 @@ private static void WriteErrorMessage(string msg) OutWriter.WriteLine(ColorStyle.Error, msg); } - private static void CancelHandler(object sender, ConsoleCancelEventArgs args) + private static void CancelHandler(object? sender, ConsoleCancelEventArgs args) { Console.ResetColor(); } diff --git a/src/NUnitConsole/nunit4-netcore-console/ResultReporter.cs b/src/NUnitConsole/nunit4-netcore-console/ResultReporter.cs index 2e7dfcf85..35893f5a2 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ResultReporter.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ResultReporter.cs @@ -5,11 +5,10 @@ using System.Globalization; using System.Xml; using NUnit.ConsoleRunner.Options; +using NUnit.ConsoleRunner.Utilities; namespace NUnit.ConsoleRunner { - using Utilities; - public class ResultReporter { public ResultReporter(XmlNode resultNode, ExtendedTextWriter writer, ConsoleOptions options) @@ -18,12 +17,13 @@ public ResultReporter(XmlNode resultNode, ExtendedTextWriter writer, ConsoleOpti Writer = writer; Options = options; - OverallResult = resultNode.GetAttribute("result"); - if (OverallResult == "Skipped") + string? overallResult = resultNode.GetAttribute("result"); + if (overallResult == "Skipped") OverallResult = "Warning"; - if (OverallResult == null) + if (overallResult == null) OverallResult = "Unknown"; - + else + OverallResult = overallResult; Summary = new ResultSummary(resultNode); } @@ -60,7 +60,7 @@ internal void WriteRunSettingsReport() { var settings = firstSuite.SelectNodes("settings/setting"); - if (settings.Count > 0) + if (settings is not null && settings.Count > 0) { Writer.WriteLine(ColorStyle.SectionHeader, "Run Settings"); @@ -78,13 +78,18 @@ private void WriteSettingsNode(XmlNode node) var name = node.GetAttribute("name"); var val = node.GetAttribute("value") ?? string.Empty; - Writer.WriteLabelLine($" {name}:", items.Count > 0 ? string.Empty : $" |{val}|"); - - foreach (XmlNode item in items) + if (items is null || items.Count == 0) + Writer.WriteLabelLine($" {name}:", $" |{val}|"); + else { - var key = item.GetAttribute("key"); - var value = item.GetAttribute("value"); - Writer.WriteLine(ColorStyle.Value, $" {key} -> |{value}|"); + Writer.WriteLabelLine($" {name}:", string.Empty); + + foreach (XmlNode item in items) + { + var key = item.GetAttribute("key"); + var value = item.GetAttribute("value"); + Writer.WriteLine(ColorStyle.Value, $" {key} -> |{value}|"); + } } } @@ -154,7 +159,7 @@ public void WriteErrorsFailuresAndWarningsReport() private void WriteErrorsFailuresAndWarnings(XmlNode resultNode) { - string resultState = resultNode.GetAttribute("result"); + string? resultState = resultNode.GetAttribute("result"); switch (resultNode.Name) { @@ -225,7 +230,7 @@ private void WriteNotRunResults(XmlNode resultNode) switch (resultNode.Name) { case "test-case": - string status = resultNode.GetAttribute("result"); + string? status = resultNode.GetAttribute("result"); if (status == "Skipped") new ConsoleTestResult(resultNode, ++ReportIndex).WriteResult(Writer); diff --git a/src/NUnitConsole/nunit4-netcore-console/ResultSummary.cs b/src/NUnitConsole/nunit4-netcore-console/ResultSummary.cs index b2f408ace..73c68ab09 100644 --- a/src/NUnitConsole/nunit4-netcore-console/ResultSummary.cs +++ b/src/NUnitConsole/nunit4-netcore-console/ResultSummary.cs @@ -3,11 +3,10 @@ using System; using System.Globalization; using System.Xml; +using NUnit.ConsoleRunner.Utilities; namespace NUnit.ConsoleRunner { - using Utilities; - /// /// Summary description for ResultSummary. /// @@ -138,10 +137,10 @@ private void InitializeCounters() private void Summarize(XmlNode node, bool failedInFixtureTearDown) { - string type = node.GetAttribute("type"); - string status = node.GetAttribute("result"); - string label = node.GetAttribute("label"); - string site = node.GetAttribute("site"); + string? type = node.GetAttribute("type"); + string? status = node.GetAttribute("result"); + string? label = node.GetAttribute("label"); + string? site = node.GetAttribute("site"); switch (node.Name) { diff --git a/src/NUnitConsole/nunit4-netcore-console/SafeAttributeAccess.cs b/src/NUnitConsole/nunit4-netcore-console/SafeAttributeAccess.cs index 35c0ef6b3..6566ef87d 100644 --- a/src/NUnitConsole/nunit4-netcore-console/SafeAttributeAccess.cs +++ b/src/NUnitConsole/nunit4-netcore-console/SafeAttributeAccess.cs @@ -23,9 +23,9 @@ public static class SafeAttributeAccess /// The result. /// The name. /// - public static string GetAttribute(this XmlNode result, string name) + public static string? GetAttribute(this XmlNode result, string name) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? null : attr.Value; } @@ -39,7 +39,7 @@ public static string GetAttribute(this XmlNode result, string name) /// public static double GetAttribute(this XmlNode result, string name, double defaultValue) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? defaultValue @@ -55,7 +55,7 @@ public static double GetAttribute(this XmlNode result, string name, double defau /// public static DateTime GetAttribute(this XmlNode result, string name, DateTime defaultValue) { - string dateStr = GetAttribute(result, name); + string? dateStr = GetAttribute(result, name); if (dateStr == null) return defaultValue; diff --git a/src/NUnitConsole/nunit4-netcore-console/TestEventHandler.cs b/src/NUnitConsole/nunit4-netcore-console/TestEventHandler.cs index 23013310f..871f5e260 100644 --- a/src/NUnitConsole/nunit4-netcore-console/TestEventHandler.cs +++ b/src/NUnitConsole/nunit4-netcore-console/TestEventHandler.cs @@ -22,7 +22,7 @@ public class TestEventHandler : ITestEventListener private readonly bool _displayAfterTest; private readonly bool _displayBeforeOutput; - private string _lastTestOutput; + private string? _lastTestOutput; private bool _wantNewLine = false; public TestEventHandler(ExtendedTextWriter outWriter, string labelsOption) @@ -41,6 +41,9 @@ public void OnTestEvent(string report) doc.LoadXml(report); var testEvent = doc.FirstChild; + if (testEvent == null) + return; + switch (testEvent.Name) { case "start-test": @@ -63,16 +66,20 @@ public void OnTestEvent(string report) private void TestStarted(XmlNode testResult) { - var testName = testResult.Attributes["fullname"].Value; + var testName = testResult.Attributes?["fullname"]?.Value; - if (_displayBeforeTest) + if (_displayBeforeTest && testName != null) WriteLabelLine(testName); } private void TestFinished(XmlNode testResult) { - var testName = testResult.Attributes["fullname"].Value; - var status = testResult.GetAttribute("label") ?? testResult.GetAttribute("result"); + var testName = testResult.Attributes?["fullname"]?.Value; + + if (testName == null) + return; + + var status = testResult.GetAttribute("label") ?? testResult.GetAttribute("result") ?? "Unknown"; var outputNode = testResult.SelectSingleNode("output"); if (outputNode != null) @@ -90,10 +97,10 @@ private void TestFinished(XmlNode testResult) private void SuiteFinished(XmlNode testResult) { - var suiteName = testResult.Attributes["fullname"].Value; + var suiteName = testResult.Attributes?["fullname"]?.Value; var outputNode = testResult.SelectSingleNode("output"); - if (outputNode != null) + if (suiteName != null && outputNode != null) { if (_displayBeforeOutput) WriteLabelLine(suiteName); @@ -106,15 +113,18 @@ private void SuiteFinished(XmlNode testResult) private void TestOutput(XmlNode outputNode) { var testName = outputNode.GetAttribute("testname"); - var stream = outputNode.GetAttribute("stream"); - if (_displayBeforeOutput && testName != null) - WriteLabelLine(testName); + if (testName != null) + { + if (_displayBeforeOutput) + WriteLabelLine(testName); - WriteOutputLine(testName, outputNode.InnerText, stream == "Error" ? ColorStyle.Error : ColorStyle.Output); + var stream = outputNode.GetAttribute("stream"); + WriteOutputLine(testName, outputNode.InnerText, stream == "Error" ? ColorStyle.Error : ColorStyle.Output); + } } - private string _currentLabel; + private string? _currentLabel; private void WriteLabelLine(string label) { @@ -198,7 +208,7 @@ private static ColorStyle GetColorForResultStatus(string status) #if NETFRAMEWORK public override object InitializeLifetimeService() { - return null; + return null!; } #endif } diff --git a/src/NUnitConsole/nunit4-netcore-console/nunit4-netcore-console.csproj b/src/NUnitConsole/nunit4-netcore-console/nunit4-netcore-console.csproj index 83f6ee8fb..d0d29ef09 100644 --- a/src/NUnitConsole/nunit4-netcore-console/nunit4-netcore-console.csproj +++ b/src/NUnitConsole/nunit4-netcore-console/nunit4-netcore-console.csproj @@ -34,12 +34,17 @@ Copyright (c) 2022 Charlie Poole, Rob Prouse + + ..\..\..\nunit.ico app.manifest + + + @@ -53,7 +58,6 @@ - \ No newline at end of file diff --git a/src/NUnitEngine/agents/Program.cs b/src/NUnitEngine/agents/Program.cs index 5ccb3475d..3b5af3031 100644 --- a/src/NUnitEngine/agents/Program.cs +++ b/src/NUnitEngine/agents/Program.cs @@ -2,10 +2,8 @@ using System; using System.Diagnostics; -using System.IO; using System.Runtime.InteropServices; using System.Security; -using Microsoft.Win32; using NUnit.Common; using NUnit.Engine; using NUnit.Engine.Agents; @@ -38,10 +36,9 @@ public class NUnitTestAgent #endif static Guid AgentId; - static string AgencyUrl; - static Process AgencyProcess; - static RemoteTestAgent Agent; - private static Logger log; + static string? AgencyUrl; + static Process? AgencyProcess; + static RemoteTestAgent? Agent; /// /// The main entry point for the application. @@ -84,15 +81,15 @@ public static void Main(string[] args) var logName = $"nunit-agent_{pid}.log"; //InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); - log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); + Logger log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); log.Info($"Agent process {pid} starting"); log.Info($"Running {AGENT_RUNTIME} agent under {CURRENT_RUNTIME}"); if (debugArgPassed) - TryLaunchDebugger(); + TryLaunchDebugger(log); - LocateAgencyProcess(agencyPid); + AgencyProcess = LocateAgencyProcess(log, agencyPid); log.Info("Starting RemoteTestAgent"); Agent = new RemoteTestAgent(AgentId); @@ -106,7 +103,7 @@ public static void Main(string[] args) try { if (Agent.Start()) - WaitForStop(); + WaitForStop(log, Agent, AgencyProcess); else { log.Error("Failed to start RemoteTestAgent"); @@ -123,28 +120,29 @@ public static void Main(string[] args) Environment.Exit(AgentExitCodes.OK); } - private static void LocateAgencyProcess(string agencyPid) + private static Process LocateAgencyProcess(Logger log, string agencyPid) { var agencyProcessId = int.Parse(agencyPid); try { - AgencyProcess = Process.GetProcessById(agencyProcessId); + return Process.GetProcessById(agencyProcessId); } catch (Exception e) { log.Error($"Unable to connect to agency process with PID: {agencyProcessId}"); log.Error($"Failed with exception: {e.Message} {e.StackTrace}"); Environment.Exit(AgentExitCodes.UNABLE_TO_LOCATE_AGENCY); + return null; // Will never reach here } } - private static void WaitForStop() + private static void WaitForStop(Logger log, RemoteTestAgent agent, Process agencyProcess) { log.Debug("Waiting for stopSignal"); - while (!Agent.WaitForStop(500)) + while (!agent.WaitForStop(500)) { - if (AgencyProcess.HasExited) + if (agencyProcess.HasExited) { log.Error("Parent process has been terminated."); Environment.Exit(AgentExitCodes.PARENT_PROCESS_TERMINATED); @@ -154,7 +152,7 @@ private static void WaitForStop() log.Debug("Stop signal received"); } - private static void TryLaunchDebugger() + private static void TryLaunchDebugger(Logger log) { if (Debugger.IsAttached) return; diff --git a/src/NUnitEngine/agents/nunit-agent-net462-x86/nunit-agent-net462-x86.csproj b/src/NUnitEngine/agents/nunit-agent-net462-x86/nunit-agent-net462-x86.csproj index d1f40e79b..eebdb98cc 100644 --- a/src/NUnitEngine/agents/nunit-agent-net462-x86/nunit-agent-net462-x86.csproj +++ b/src/NUnitEngine/agents/nunit-agent-net462-x86/nunit-agent-net462-x86.csproj @@ -11,6 +11,8 @@ False + + NUnit Engine NUnit X86 Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/agents/nunit-agent-net462/nunit-agent-net462.csproj b/src/NUnitEngine/agents/nunit-agent-net462/nunit-agent-net462.csproj index 02dfc2346..f13bce4c4 100644 --- a/src/NUnitEngine/agents/nunit-agent-net462/nunit-agent-net462.csproj +++ b/src/NUnitEngine/agents/nunit-agent-net462/nunit-agent-net462.csproj @@ -10,6 +10,8 @@ false + + NUnit Engine NUnit Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/agents/nunit-agent-net60/nunit-agent-net60.csproj b/src/NUnitEngine/agents/nunit-agent-net60/nunit-agent-net60.csproj index 05cfc0e5e..e784b37e8 100644 --- a/src/NUnitEngine/agents/nunit-agent-net60/nunit-agent-net60.csproj +++ b/src/NUnitEngine/agents/nunit-agent-net60/nunit-agent-net60.csproj @@ -11,6 +11,8 @@ 1505 + + NUnit Engine NUnit Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/agents/nunit-agent-net70/nunit-agent-net70.csproj b/src/NUnitEngine/agents/nunit-agent-net70/nunit-agent-net70.csproj index 5e583844d..79fe076cb 100644 --- a/src/NUnitEngine/agents/nunit-agent-net70/nunit-agent-net70.csproj +++ b/src/NUnitEngine/agents/nunit-agent-net70/nunit-agent-net70.csproj @@ -11,6 +11,8 @@ 1505 + + NUnit Engine NUnit Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/agents/nunit-agent-net80/nunit-agent-net80.csproj b/src/NUnitEngine/agents/nunit-agent-net80/nunit-agent-net80.csproj index 4dc66611a..571deac30 100644 --- a/src/NUnitEngine/agents/nunit-agent-net80/nunit-agent-net80.csproj +++ b/src/NUnitEngine/agents/nunit-agent-net80/nunit-agent-net80.csproj @@ -12,6 +12,8 @@ true + + NUnit Engine NUnit Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/agents/nunit-agent-netcore31/nunit-agent-netcore31.csproj b/src/NUnitEngine/agents/nunit-agent-netcore31/nunit-agent-netcore31.csproj index d7d7b10af..62ca5ee1b 100644 --- a/src/NUnitEngine/agents/nunit-agent-netcore31/nunit-agent-netcore31.csproj +++ b/src/NUnitEngine/agents/nunit-agent-netcore31/nunit-agent-netcore31.csproj @@ -12,6 +12,8 @@ $(NoWarn);SYSLIB0011;SYSLIB0012;1505 + + NUnit Engine NUnit Agent ($(TargetFramework)) diff --git a/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineException.cs b/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineException.cs index 9fd086f39..4502ee5b6 100644 --- a/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineException.cs +++ b/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineException.cs @@ -23,7 +23,7 @@ public NUnitEngineException(string message) : base(message) /// /// Construct with a message and inner exception /// - public NUnitEngineException(string message, Exception innerException) : base(message, innerException) + public NUnitEngineException(string message, Exception? innerException) : base(message, innerException) { } diff --git a/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineUnloadException.cs b/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineUnloadException.cs index 44b8d2fe7..ce9a00121 100644 --- a/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineUnloadException.cs +++ b/src/NUnitEngine/nunit.engine.api/Exceptions/NUnitEngineUnloadException.cs @@ -49,6 +49,6 @@ public NUnitEngineUnloadException(SerializationInfo info, StreamingContext conte /// /// Gets the collection of exceptions . /// - public ICollection AggregatedExceptions { get; } + public ICollection? AggregatedExceptions { get; } } -} \ No newline at end of file +} diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionAttribute.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionAttribute.cs index 0d00818b1..44aeeee2f 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionAttribute.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionAttribute.cs @@ -24,12 +24,12 @@ public ExtensionAttribute() /// intended. This is an optional field provided NUnit is able to deduce the /// ExtensionPoint from the Type of the extension class. /// - public string Path { get; set; } + public string? Path { get; set; } /// /// An optional description of what the extension does. /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Flag indicating whether the extension is enabled. @@ -40,6 +40,6 @@ public ExtensionAttribute() /// /// The minimum Engine version for which this extension is designed /// - public string EngineVersion { get; set; } + public string? EngineVersion { get; set; } } } diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPointAttribute.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPointAttribute.cs index e6dc9c7ec..652241ff7 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPointAttribute.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPointAttribute.cs @@ -27,17 +27,17 @@ public ExtensionPointAttribute(string path, Type type) /// is typically formatted as a path using '/' and the set of extension /// points is sometimes viewed as forming a tree. /// - public string Path { get; private set; } + public string Path { get; } /// /// The required Type (usually an interface) of any extension that is /// installed at this ExtensionPoint. /// - public Type Type { get; private set; } + public Type Type { get; } /// /// An optional description of the purpose of the ExtensionPoint /// - public string Description { get; set; } + public string? Description { get; set; } } } diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPropertyAttribute.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPropertyAttribute.cs index 21cef130e..b09b652e1 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPropertyAttribute.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/ExtensionPropertyAttribute.cs @@ -24,11 +24,11 @@ public ExtensionPropertyAttribute(string name, string value) /// /// The name of the property. /// - public string Name { get; private set; } + public string Name { get; } /// /// The property value /// - public string Value { get; private set; } + public string Value { get; } } } diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionNode.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionNode.cs index dd7a09fe2..1edbfde00 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionNode.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionNode.cs @@ -32,12 +32,12 @@ public interface IExtensionNode /// /// Gets an optional description of what the extension does. /// - string Description { get; } + string? Description { get; } /// /// The TargetFramework of the extension assembly. /// - IRuntimeFramework TargetFramework { get; } + IRuntimeFramework? TargetFramework { get; } /// /// Gets a collection of the names of all this extension's properties diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionPoint.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionPoint.cs index c0365a5c6..9c436862c 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionPoint.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/IExtensionPoint.cs @@ -19,7 +19,7 @@ public interface IExtensionPoint /// /// Gets the description of this extension point. May be null. /// - string Description { get; } + string? Description { get; } /// /// Gets the FullName of the Type required for any extension to be installed at this extension point. diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs index 0c0599b2a..3f8f190bb 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs @@ -39,7 +39,7 @@ public interface IFrameworkDriver /// An ITestEventHandler that receives progress notices /// A XML string representing the filter that controls which tests are executed /// An Xml string representing the result - string Run(ITestEventListener listener, string filter); + string Run(ITestEventListener? listener, string filter); /// /// Returns information about the tests in an assembly. diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/TypeExtensionPointAttribute.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/TypeExtensionPointAttribute.cs index 170cfa6b0..a005119f1 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/TypeExtensionPointAttribute.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/TypeExtensionPointAttribute.cs @@ -35,11 +35,11 @@ public TypeExtensionPointAttribute() /// is typically formatted as a path using '/' and the set of extension /// points is sometimes viewed as forming a tree. /// - public string Path { get; private set; } + public string? Path { get; } /// /// An optional description of the purpose of the ExtensionPoint /// - public string Description { get; set; } + public string? Description { get; set; } } } diff --git a/src/NUnitEngine/nunit.engine.api/IExtensionService.cs b/src/NUnitEngine/nunit.engine.api/IExtensionService.cs index 6c8b31544..3574bfbb8 100644 --- a/src/NUnitEngine/nunit.engine.api/IExtensionService.cs +++ b/src/NUnitEngine/nunit.engine.api/IExtensionService.cs @@ -31,7 +31,7 @@ public interface IExtensionService /// /// Get an ExtensionPoint based on its unique identifying path. /// - IExtensionPoint GetExtensionPoint(string path); + IExtensionPoint? GetExtensionPoint(string path); /// /// Get an enumeration of ExtensionNodes based on their identifying path. diff --git a/src/NUnitEngine/nunit.engine.api/IResultService.cs b/src/NUnitEngine/nunit.engine.api/IResultService.cs index 2a04a53a3..f511b7bee 100644 --- a/src/NUnitEngine/nunit.engine.api/IResultService.cs +++ b/src/NUnitEngine/nunit.engine.api/IResultService.cs @@ -22,6 +22,6 @@ public interface IResultService /// The name of the format to be used /// A set of arguments to be used in constructing the writer or null if non arguments are needed /// An IResultWriter - IResultWriter GetResultWriter(string format, object[] args); + IResultWriter GetResultWriter(string format, params object?[]? args); } } diff --git a/src/NUnitEngine/nunit.engine.api/IRuntimeFramework.cs b/src/NUnitEngine/nunit.engine.api/IRuntimeFramework.cs index ce41f6b92..ddbd4839a 100644 --- a/src/NUnitEngine/nunit.engine.api/IRuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine.api/IRuntimeFramework.cs @@ -30,6 +30,6 @@ public interface IRuntimeFramework /// or null if there is no profile. Currently. the only defined /// values are Full and Client. /// - string Profile { get; } + string? Profile { get; } } } diff --git a/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs index 4f5ffb6de..788a4f5ee 100644 --- a/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs @@ -15,7 +15,7 @@ public interface IRuntimeFrameworkService /// Gets a RuntimeFramework instance representing the runtime under /// which the code is currently running. /// - IRuntimeFramework CurrentFramework { get; } + IRuntimeFramework? CurrentFramework { get; } /// /// Returns true if the runtime framework represented by diff --git a/src/NUnitEngine/nunit.engine.api/IService.cs b/src/NUnitEngine/nunit.engine.api/IService.cs index 979308c7a..f4dc069ad 100644 --- a/src/NUnitEngine/nunit.engine.api/IService.cs +++ b/src/NUnitEngine/nunit.engine.api/IService.cs @@ -1,6 +1,7 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System; +using System.Diagnostics.CodeAnalysis; using NUnit.Engine.Extensibility; namespace NUnit.Engine @@ -30,7 +31,8 @@ public interface IService /// /// The ServiceContext /// - IServiceLocator ServiceContext { get; set; } + [DisallowNull] + IServiceLocator? ServiceContext { get; set; } /// /// Gets the ServiceStatus of this service diff --git a/src/NUnitEngine/nunit.engine.api/TestEngineActivator.cs b/src/NUnitEngine/nunit.engine.api/TestEngineActivator.cs index ccfebd961..c4b0481ba 100644 --- a/src/NUnitEngine/nunit.engine.api/TestEngineActivator.cs +++ b/src/NUnitEngine/nunit.engine.api/TestEngineActivator.cs @@ -24,8 +24,8 @@ public static ITestEngine CreateInstance() var directoryName = Path.GetDirectoryName(apiLocation); var enginePath = directoryName == null ? DEFAULT_ENGINE_ASSEMBLY : Path.Combine(directoryName, DEFAULT_ENGINE_ASSEMBLY); var assembly = Assembly.LoadFrom(enginePath); - var engineType = assembly.GetType(DEFAULT_ENGINE_TYPE); - return Activator.CreateInstance(engineType) as ITestEngine; + var engineType = assembly.GetType(DEFAULT_ENGINE_TYPE, throwOnError: true)!; + return (ITestEngine)Activator.CreateInstance(engineType)!; } } } diff --git a/src/NUnitEngine/nunit.engine.api/TestFilter.cs b/src/NUnitEngine/nunit.engine.api/TestFilter.cs index 7550a310a..21126fdd3 100644 --- a/src/NUnitEngine/nunit.engine.api/TestFilter.cs +++ b/src/NUnitEngine/nunit.engine.api/TestFilter.cs @@ -31,6 +31,6 @@ public TestFilter(string xmlText) /// /// Gets the XML representation of this filter as a string. /// - public string Text { get; private set; } + public string Text { get; } } } diff --git a/src/NUnitEngine/nunit.engine.api/TestPackage.cs b/src/NUnitEngine/nunit.engine.api/TestPackage.cs index bba36c05e..0f85d1d60 100644 --- a/src/NUnitEngine/nunit.engine.api/TestPackage.cs +++ b/src/NUnitEngine/nunit.engine.api/TestPackage.cs @@ -76,7 +76,7 @@ private static string GetNextID() /// /// Gets the name of the package /// - public string Name + public string? Name { get { return FullName == null ? null : Path.GetFileName(FullName); } } @@ -85,7 +85,7 @@ public string Name /// Gets the path to the file containing tests. It may be /// an assembly or a recognized project type. /// - public string FullName { get; private set; } + public string? FullName { get; private init; } /// /// Gets the list of SubPackages contained in this package @@ -149,8 +149,8 @@ public void AddSetting(string name, object value) /// public T GetSetting(string name, T defaultSetting) { - return Settings.ContainsKey(name) - ? (T)Settings[name] + return (Settings.TryGetValue(name, out object? setting) && setting is not null) + ? (T)setting : defaultSetting; } } diff --git a/src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj b/src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj index e11144055..007606c92 100644 --- a/src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj +++ b/src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj @@ -3,14 +3,16 @@ NUnit.Engine net462;netstandard2.0 - ../../../bin/$(Configuration) + $(MSBuildThisFileDirectory)/../../../bin/$(Configuration) true - ..\..\nunit.snk + $(MSBuildThisFileDirectory)/../../nunit.snk true portable true + + NUnit Engine NUnit Engine API ($(TargetFramework)) @@ -19,8 +21,4 @@ 3.99.0.0 - - - - diff --git a/src/NUnitEngine/nunit.engine.core.tests/AppContextTest.cs b/src/NUnitEngine/nunit.engine.core.tests/AppContextTest.cs index 40fa60e63..4b00b250d 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/AppContextTest.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/AppContextTest.cs @@ -11,7 +11,7 @@ public class AppContextTest [Test] public void VerifyBasePath() { - var expectedPath = Path.GetDirectoryName(GetType().Assembly.Location); + var expectedPath = Path.GetDirectoryName(GetType().Assembly.Location)!; #if NETCORAPP Assert.That(AppContext.GetData("APP_CONTEXT_BASE_DIRECTORY"), Is.EqualTo(expectedPath)); #endif diff --git a/src/NUnitEngine/nunit.engine.core.tests/AsyncTestEngineResultTests.cs b/src/NUnitEngine/nunit.engine.core.tests/AsyncTestEngineResultTests.cs index 69b15d07e..a5bf60705 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/AsyncTestEngineResultTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/AsyncTestEngineResultTests.cs @@ -19,14 +19,13 @@ public void SetUp() [Test] public void Result_ThrowsIfNotSet() { - XmlNode result = null; - Assert.Throws(() => result = _asyncResult.EngineResult.Xml); + Assert.Throws(() => _ = _asyncResult.EngineResult.Xml); } [Test] public void SetResult_ThrowsIfNull() { - Assert.Throws(() => _asyncResult.SetResult(null)); + Assert.Throws(() => _asyncResult.SetResult(null!)); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Drivers/NUnit3FrameworkDriverTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Drivers/NUnit3FrameworkDriverTests.cs index 388bb0941..43d6b0b42 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Drivers/NUnit3FrameworkDriverTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Drivers/NUnit3FrameworkDriverTests.cs @@ -40,7 +40,7 @@ public void Load_GoodFile_ReturnsRunnableSuite() Assert.That(result.GetAttribute("type"), Is.EqualTo("Assembly")); Assert.That(result.GetAttribute("runstate"), Is.EqualTo("Runnable")); Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo(MockAssembly.Tests.ToString())); - Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.EqualTo(0), "Load result should not have child tests"); } [Test] @@ -53,7 +53,7 @@ public void Explore_AfterLoad_ReturnsRunnableSuite() Assert.That(result.GetAttribute("type"), Is.EqualTo("Assembly")); Assert.That(result.GetAttribute("runstate"), Is.EqualTo("Runnable")); Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo(MockAssembly.Tests.ToString())); - Assert.That(result.SelectNodes("test-suite").Count, Is.GreaterThan(0), "Explore result should have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.GreaterThan(0), "Explore result should have child tests"); } [Test] @@ -98,7 +98,7 @@ public void RunTestsAction_AfterLoad_ReturnsRunnableSuite() Assert.That(result.GetAttribute("failed"), Is.EqualTo(MockAssembly.Failed.ToString())); Assert.That(result.GetAttribute("skipped"), Is.EqualTo(MockAssembly.Skipped.ToString())); Assert.That(result.GetAttribute("inconclusive"), Is.EqualTo(MockAssembly.Inconclusive.ToString())); - Assert.That(result.SelectNodes("test-suite").Count, Is.GreaterThan(0), "Explore result should have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.GreaterThan(0), "Explore result should have child tests"); } [Test] @@ -119,17 +119,11 @@ public void RunTestsAction_WithInvalidFilterElement_ThrowsNUnitEngineException() Assert.That(ex, Is.TypeOf()); } - private static string GetSkipReason(XmlNode result) - { - var propNode = result.SelectSingleNode(string.Format("properties/property[@name='{0}']", PropertyNames.SkipReason)); - return propNode == null ? null : propNode.GetAttribute("value"); - } - private class CallbackEventHandler : System.Web.UI.ICallbackEventHandler { - private string _result; + private string? _result; - public string GetCallbackResult() + public string? GetCallbackResult() { return _result; } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Drivers/NotRunnableFrameworkDriverTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Drivers/NotRunnableFrameworkDriverTests.cs index f6173b558..0119e74ed 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Drivers/NotRunnableFrameworkDriverTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Drivers/NotRunnableFrameworkDriverTests.cs @@ -17,10 +17,10 @@ public abstract class NotRunnableFrameworkDriverTests private const string DRIVER_ID = "99"; private const string EXPECTED_ID = "99-1"; - protected string _expectedRunState; - protected string _expectedReason; - protected string _expectedResult; - protected string _expectedLabel; + protected string? _expectedRunState; + protected string? _expectedReason; + protected string? _expectedResult; + protected string? _expectedLabel; [TestCase("junk.dll", "Assembly")] [TestCase("junk.exe", "Assembly")] @@ -38,7 +38,7 @@ public void Load(string filePath, string expectedType) Assert.That(result.GetAttribute("runstate"), Is.EqualTo(_expectedRunState)); Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo("0")); Assert.That(GetSkipReason(result), Is.EqualTo(_expectedReason)); - Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.EqualTo(0), "Load result should not have child tests"); } [TestCase("junk.dll", "Assembly")] @@ -57,7 +57,7 @@ public void Explore(string filePath, string expectedType) Assert.That(result.GetAttribute("runstate"), Is.EqualTo(_expectedRunState)); Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo("0")); Assert.That(GetSkipReason(result), Is.EqualTo(_expectedReason)); - Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Result should not have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.EqualTo(0), "Result should not have child tests"); } [TestCase("junk.dll")] @@ -84,10 +84,10 @@ public void Run(string filePath, string expectedType) Assert.That(result.GetAttribute("runstate"), Is.EqualTo(_expectedRunState)); Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo("0")); Assert.That(GetSkipReason(result), Is.EqualTo(_expectedReason)); - Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); + Assert.That(result.SelectNodes("test-suite")?.Count, Is.EqualTo(0), "Load result should not have child tests"); Assert.That(result.GetAttribute("result"), Is.EqualTo(_expectedResult)); Assert.That(result.GetAttribute("label"), Is.EqualTo(_expectedLabel)); - Assert.That(result.SelectSingleNode("reason/message").InnerText, Is.EqualTo(_expectedReason)); + Assert.That(result.SelectSingleNode("reason/message")?.InnerText, Is.EqualTo(_expectedReason)); } protected abstract IFrameworkDriver CreateDriver(string filePath); @@ -99,7 +99,7 @@ private IFrameworkDriver GetDriver(string filePath) return driver; } - private static string GetSkipReason(XmlNode result) + private static string? GetSkipReason(XmlNode result) { var propNode = result.SelectSingleNode(string.Format("properties/property[@name='{0}']", PropertyNames.SkipReason)); return propNode == null ? null : propNode.GetAttribute("value"); @@ -126,7 +126,7 @@ public InvalidAssemblyFrameworkDriverTests() protected override IFrameworkDriver CreateDriver(string filePath) { - return new InvalidAssemblyFrameworkDriver(filePath, _expectedReason); + return new InvalidAssemblyFrameworkDriver(filePath, _expectedReason ?? "Not Specified"); } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/DummyExtensions.cs b/src/NUnitEngine/nunit.engine.core.tests/DummyExtensions.cs index 21fd479cb..5d549efad 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/DummyExtensions.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/DummyExtensions.cs @@ -135,7 +135,7 @@ public string Load(string testAssemblyPath, IDictionary settings throw new NotImplementedException(); } - public string Run(ITestEventListener listener, string filter) + public string Run(ITestEventListener? listener, string filter) { throw new NotImplementedException(); } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs index 39e38a7e5..0f3c03829 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs @@ -12,8 +12,8 @@ public class ExtensionAssemblyTests { private static readonly Assembly THIS_ASSEMBLY = Assembly.GetExecutingAssembly(); private static readonly string THIS_ASSEMBLY_PATH = THIS_ASSEMBLY.Location; - private static readonly string THIS_ASSEMBLY_NAME = THIS_ASSEMBLY.GetName().Name; - private static readonly Version THIS_ASSEMBLY_VERSION = THIS_ASSEMBLY.GetName().Version; + private static readonly string? THIS_ASSEMBLY_NAME = THIS_ASSEMBLY.GetName().Name; + private static readonly Version? THIS_ASSEMBLY_VERSION = THIS_ASSEMBLY.GetName().Version; private ExtensionAssembly _ea; @@ -23,6 +23,12 @@ public void CreateExtensionAssemblies() _ea = new ExtensionAssembly(THIS_ASSEMBLY_PATH, false); } + [OneTimeTearDown] + public void DisposeExtensionAssemblies() + { + _ea.Dispose(); + } + [Test] public void AssemblyName() { diff --git a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs index 97effeaea..a68154fc4 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs @@ -105,8 +105,8 @@ public void IsBetterVersionOfPrefersNoChangeIfFromWildcard() } private static IExtensionAssembly MockExtension(string assemblyName = "ExtensionSelectorTestsExtension", - Version assemblyVersion = null, - Version targetFramework = null, + Version? assemblyVersion = null, + Version? targetFramework = null, bool fromWildcard = false) { var sub = Substitute.For(); diff --git a/src/NUnitEngine/nunit.engine.core.tests/Helpers/On.cs b/src/NUnitEngine/nunit.engine.core.tests/Helpers/On.cs index 6e0af761f..212d93aad 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Helpers/On.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Helpers/On.cs @@ -11,7 +11,7 @@ public static class On private sealed class OnDisposeAction : IDisposable { - private Action action; + private Action? action; public OnDisposeAction(Action action) { diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs index 25d4363b1..05bbfa581 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs @@ -19,7 +19,7 @@ public class AddinsFileTests [Test] public void Read_IFile_Null() { - Assert.That(() => AddinsFile.Read((IFile)null), Throws.ArgumentNullException); + Assert.That(() => AddinsFile.Read((IFile)null!), Throws.ArgumentNullException); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/AssemblyHelperTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/AssemblyHelperTests.cs index e13a2cf4b..8afbf20bb 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/AssemblyHelperTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/AssemblyHelperTests.cs @@ -22,6 +22,7 @@ public void GetPathForAssembly() Assert.That(File.Exists(path)); } +#if NETFRAMEWORK // The following tests are only useful to the extent that the test cases // match what will actually be provided to the method in production. // As currently used, NUnit's codebase can only use the file: schema, @@ -54,5 +55,6 @@ public void GetAssemblyPathFromCodeBase(string uri, string expectedPath) string localPath = AssemblyHelper.GetAssemblyPathFromCodeBase(uri); Assert.That(localPath, Is.SamePath(expectedPath)); } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs index 1606e57f4..94facf7f4 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs @@ -54,7 +54,7 @@ public bool IsPathFullyQualified_NonWindows(string path) [Test] public void IsPathFullyQualified_PathIsNull() { - Assert.That(() => Path.IsPathFullyQualified(null), Throws.ArgumentNullException); + Assert.That(() => Path.IsPathFullyQualified(null!), Throws.ArgumentNullException); } } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/DirectoryFinderTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/DirectoryFinderTests.cs index 17c9d7f56..0e77b99c4 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/DirectoryFinderTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/DirectoryFinderTests.cs @@ -110,7 +110,7 @@ public void GetDirectories_Asterisk_Tools() var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("*", SIO.SearchOption.TopDirectoryOnly); this.GetFakeDirectory("tools", "frobuscator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); this.GetFakeDirectory("tools", "metamorphosator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); @@ -127,7 +127,7 @@ public void GetDirectories_Asterisk_Metamorphosator() var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories(Arg.Any(), Arg.Any()); this.GetFakeDirectory("tools", "frobuscator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); this.GetFakeDirectory("tools", "metamorphosator", "addins").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); @@ -145,7 +145,7 @@ public void GetDirectories_Greedy_Tools() var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("*", SIO.SearchOption.AllDirectories); this.GetFakeDirectory("tools", "frobuscator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); this.GetFakeDirectory("tools", "metamorphosator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); @@ -172,7 +172,7 @@ public void GetDirectories_Greedy_Metamorphosator() var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("*", SIO.SearchOption.AllDirectories); this.GetFakeDirectory("tools", "frobuscator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); } @@ -188,7 +188,7 @@ public void GetDirectories_WordWithWildcard_NoMatch(string pattern) var result = finder.GetDirectories(baseDir, pattern); Assert.That(result, Is.Empty); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories(pattern, SIO.SearchOption.TopDirectoryOnly); } @@ -208,7 +208,7 @@ public void GetDirectories_WordWithWildcard_OneMatch(string pattern) var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories(pattern, SIO.SearchOption.TopDirectoryOnly); } @@ -226,7 +226,7 @@ public void GetDirectories_WordWithWildcard_MultipleMatches(string pattern) var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories(pattern, SIO.SearchOption.TopDirectoryOnly); } @@ -308,7 +308,7 @@ public void GetDirectories_MultipleComponents_MultipleMatches_Asterisk(string pa var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); testsDir.Received().GetDirectories("v*", SIO.SearchOption.TopDirectoryOnly); } @@ -332,7 +332,7 @@ public void GetDirectories_MultipleComponents_MultipleMatches_QuestionMark(strin var actual = result.Select(x => x.FullName); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); testsDir.Received().GetDirectories("v?", SIO.SearchOption.TopDirectoryOnly); } @@ -347,7 +347,7 @@ public void GetDirectories_MultipleComponents_AllDirectories(string pattern) var actual = finder.GetDirectories(baseDir, pattern); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("*", SIO.SearchOption.AllDirectories); foreach (var dir in this.fakedDirectories.Values.Where(x => x != baseDir)) { @@ -376,7 +376,7 @@ public void GetDirectories_GreedyThenWordThenGreedy() var actual = finder.GetDirectories(baseDir, "**/tests/**"); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("*", SIO.SearchOption.AllDirectories); this.GetFakeDirectory("tools", "frobuscator").Received().GetDirectories("tests", SIO.SearchOption.TopDirectoryOnly); this.GetFakeDirectory("tools", "metamorphosator").Received().GetDirectories("tests", SIO.SearchOption.TopDirectoryOnly); @@ -397,7 +397,7 @@ public void GetDirectories_WordWithAsteriskThenGreedyThenWord() var actual = finder.GetDirectories(baseDir, "meta*/**/v1"); Assert.That(actual, Is.EquivalentTo(expected)); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); baseDir.Received().GetDirectories("meta*", SIO.SearchOption.TopDirectoryOnly); this.GetFakeDirectory("tools", "frobuscator").DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); this.GetFakeDirectory("tools", "metamorphosator").Received().GetDirectories("*", SIO.SearchOption.AllDirectories); @@ -415,7 +415,7 @@ public void GetDirectories_Parent() Assert.That(actual, Is.EquivalentTo(expected)); baseDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); } [Test] @@ -431,8 +431,8 @@ public void GetDirectories_ParentThenParentThenWordThenWord() Assert.That(actual, Is.EquivalentTo(expected)); baseDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); - baseDir.Parent.Parent.Received().GetDirectories("metamorphosator", SIO.SearchOption.TopDirectoryOnly); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.Parent!.Received().GetDirectories("metamorphosator", SIO.SearchOption.TopDirectoryOnly); this.GetFakeDirectory("tools", "metamorphosator").Received().GetDirectories("addins", SIO.SearchOption.TopDirectoryOnly); } @@ -441,7 +441,7 @@ public void GetDirectories_StartDirectoryIsNull() { var finder = new DirectoryFinder(Substitute.For()); - Assert.That(() => finder.GetDirectories((IDirectory)null, "notused"), Throws.ArgumentNullException.With.Message.Contains(" startDirectory ")); + Assert.That(() => finder.GetDirectories((IDirectory)null!, "notused"), Throws.ArgumentNullException.With.Message.Contains(" startDirectory ")); } [Test] @@ -449,7 +449,7 @@ public void GetDirectories_PatternIsNull() { var finder = new DirectoryFinder(Substitute.For()); - Assert.That(() => finder.GetDirectories(Substitute.For(), null), Throws.ArgumentNullException.With.Message.Contains(" pattern ")); + Assert.That(() => finder.GetDirectories(Substitute.For(), null!), Throws.ArgumentNullException.With.Message.Contains(" pattern ")); } [Test] @@ -460,7 +460,7 @@ public void GetDirectories_PatternIsEmpty() var directories = sut.GetDirectories(directory, string.Empty); - Assert.That(directories, Has.Count.EqualTo(1)); + Assert.That(directories.Count(), Is.EqualTo(1)); Assert.That(directories.First(), Is.EqualTo(directory)); } @@ -478,7 +478,7 @@ public void GetFiles_WordWithWildcard(string pattern) Assert.That(actual, Is.EquivalentTo(expected)); baseDir.Received().GetFiles(pattern); - baseDir.Parent.DidNotReceive().GetFiles(Arg.Any()); + baseDir.Parent!.DidNotReceive().GetFiles(Arg.Any()); } [TestCase("*/tests.*.dll")] @@ -500,7 +500,7 @@ public void GetFiles_AsteriskThenWordWithWildcard(string pattern) Assert.That(actual, Is.EquivalentTo(expected)); abcDir.Received().GetFiles(filePattern); defDir.Received().GetFiles(filePattern); - baseDir.Parent.DidNotReceive().GetFiles(Arg.Any()); + baseDir.Parent!.DidNotReceive().GetFiles(Arg.Any()); baseDir.DidNotReceive().GetFiles(Arg.Any()); abcDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); defDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); @@ -539,7 +539,7 @@ public void GetFiles_GreedyThenWordWithWildcard(string pattern) { dir.Received().GetFiles(filePattern); } - baseDir.Parent.DidNotReceive().GetFiles(Arg.Any()); + baseDir.Parent!.DidNotReceive().GetFiles(Arg.Any()); abcDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); defDir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); v1Dir.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); @@ -585,8 +585,8 @@ public void GetFiles_CurrentDirThenAsterisk() Assert.That(actual, Is.EquivalentTo(expected)); baseDir.Received().GetFiles("*"); - baseDir.Parent.DidNotReceive().GetFiles(Arg.Any()); - baseDir.Parent.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); + baseDir.Parent!.DidNotReceive().GetFiles(Arg.Any()); + baseDir.Parent!.DidNotReceive().GetDirectories(Arg.Any(), Arg.Any()); } [Test] @@ -594,7 +594,7 @@ public void GetFiles_StartDirectoryIsNull() { var finder = new DirectoryFinder(Substitute.For()); - Assert.That(() => finder.GetFiles((IDirectory)null, "notused"), Throws.ArgumentNullException.With.Message.Contains(" startDirectory ")); + Assert.That(() => finder.GetFiles((IDirectory)null!, "notused"), Throws.ArgumentNullException.With.Message.Contains(" startDirectory ")); } [Test] @@ -602,7 +602,7 @@ public void GetFiles_PatternIsNull() { var finder = new DirectoryFinder(Substitute.For()); - Assert.That(() => finder.GetDirectories(Substitute.For(), null), Throws.ArgumentNullException.With.Message.Contains(" pattern ")); + Assert.That(() => finder.GetDirectories(Substitute.For(), null!), Throws.ArgumentNullException.With.Message.Contains(" pattern ")); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/DirectoryTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/DirectoryTests.cs index 2fb12c939..200d86e15 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/DirectoryTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/DirectoryTests.cs @@ -14,18 +14,18 @@ public sealed class DirectoryTests public void Init() { var path = SIO.Directory.GetCurrentDirectory(); - var parent = new SIO.DirectoryInfo(path).Parent.FullName; + var parent = new SIO.DirectoryInfo(path).Parent!.FullName; var directory = new Directory(path); Assert.That(path, Is.EqualTo(directory.FullName)); - Assert.That(parent, Is.EqualTo(directory.Parent.FullName)); + Assert.That(parent, Is.EqualTo(directory.Parent!.FullName)); } [Test] public void Init_PathIsNull() { - Assert.That(() => new Directory(null), Throws.ArgumentNullException); + Assert.That(() => new Directory(null!), Throws.ArgumentNullException); } [Test] @@ -49,12 +49,12 @@ public void Init_EmptyPath() public void Init_TrailingDirectorySeparator() { var path = SIO.Directory.GetCurrentDirectory() + SIO.Path.DirectorySeparatorChar; - var parent = new SIO.DirectoryInfo(SIO.Directory.GetCurrentDirectory()).Parent.FullName; + var parent = new SIO.DirectoryInfo(SIO.Directory.GetCurrentDirectory()).Parent!.FullName; var directory = new Directory(path); Assert.That(path, Is.EqualTo(directory.FullName)); - Assert.That(parent, Is.EqualTo(directory.Parent.FullName)); + Assert.That(parent, Is.EqualTo(directory.Parent!.FullName)); } // Skip this test on non-Windows systems since System.IO.DirectoryInfo appends '\\server\share' to the current working-directory, making this test useless. @@ -136,7 +136,7 @@ public void GetFiles_SearchPatternIsNull() var path = SIO.Directory.GetCurrentDirectory(); var directory = new Directory(path); - Assert.That(() => directory.GetFiles(null), Throws.ArgumentNullException); + Assert.That(() => directory.GetFiles(null!), Throws.ArgumentNullException); } [Test] @@ -158,7 +158,7 @@ public void GetDirectories_SearchPatternIsNull() var path = SIO.Directory.GetCurrentDirectory(); var directory = new Directory(path); - Assert.That(() => directory.GetDirectories(null, SIO.SearchOption.TopDirectoryOnly), Throws.ArgumentNullException); + Assert.That(() => directory.GetDirectories(null!, SIO.SearchOption.TopDirectoryOnly), Throws.ArgumentNullException); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileSystemTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileSystemTests.cs index 4b48331ce..5ad1c2ee4 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileSystemTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileSystemTests.cs @@ -98,7 +98,7 @@ public void ExistsFileIsNull() { var fileSystem = new FileSystem(); - Assert.That(() => fileSystem.Exists((IFile)null), Throws.ArgumentNullException); + Assert.That(() => fileSystem.Exists((IFile)null!), Throws.ArgumentNullException); } [Test] @@ -135,12 +135,12 @@ public void Exists_DirectoryIsNull() { var fileSystem = new FileSystem(); - Assert.That(() => fileSystem.Exists((IDirectory)null), Throws.ArgumentNullException); + Assert.That(() => fileSystem.Exists((IDirectory)null!), Throws.ArgumentNullException); } private string GetTestFileLocation() { - return Assembly.GetAssembly(typeof(FileTests)).Location; + return Assembly.GetAssembly(typeof(FileTests))!.Location; } } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileTests.cs index 541aeb3a2..46e5d5606 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/FileSystemAccess/Default/FileTests.cs @@ -26,7 +26,7 @@ public void Init() [Test] public void Init_PathIsNull() { - Assert.That(() => new File(null), Throws.ArgumentNullException); + Assert.That(() => new File(null!), Throws.ArgumentNullException); } [Test] @@ -89,7 +89,7 @@ public void Init_PathIsDirectory() private string GetTestFileLocation() { - return Assembly.GetAssembly(typeof(FileTests)).Location; + return Assembly.GetAssembly(typeof(FileTests))!.Location; } } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs index 81389009f..127bcc152 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs @@ -55,13 +55,13 @@ public bool IsFullyQualifiedUnixPath(string path) [Test] public void IsFullyQualifiedUnixPath_PathIsNull() { - Assert.That(() => PathUtils.IsFullyQualifiedUnixPath(null), Throws.ArgumentNullException); + Assert.That(() => PathUtils.IsFullyQualifiedUnixPath(null!), Throws.ArgumentNullException); } [Test] public void IsFullyQualifiedWindowsPath_PathIsNull() { - Assert.That(() => PathUtils.IsFullyQualifiedWindowsPath(null), Throws.ArgumentNullException); + Assert.That(() => PathUtils.IsFullyQualifiedWindowsPath(null!), Throws.ArgumentNullException); } } @@ -81,14 +81,12 @@ internal class PathAssert : NUnit.Framework.Assert { public static void SamePathOrUnder( string path1, string path2 ) { - string msg = "\r\n\texpected: Same path or under <{0}>\r\n\t but was: <{1}>"; - Assert.That(PathUtils.SamePathOrUnder( path1, path2 ), Is.True, msg, path1, path2); + Assert.That(PathUtils.SamePathOrUnder( path1, path2 ), Is.True, $"\r\n\texpected: Same path or under <{path1}>\r\n\t but was: <{path2}>"); } public static void NotSamePathOrUnder( string path1, string path2 ) { - string msg = "\r\n\texpected: Not same path or under <{0}>\r\n\t but was: <{1}>"; - Assert.That(PathUtils.SamePathOrUnder( path1, path2 ), Is.False, msg, path1, path2); + Assert.That(PathUtils.SamePathOrUnder( path1, path2 ), Is.False, $"\r\n\texpected: Not same path or under <{path1}>\r\n\t but was: <{path2}>"); } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/ProcessUtilsTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/ProcessUtilsTests.cs index 44b508f96..fefb9c53c 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/ProcessUtilsTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/ProcessUtilsTests.cs @@ -29,7 +29,7 @@ namespace NUnit.Engine.Tests.Internal { public static class ProcessUtilsTests { - private static string EscapeProcessArgument(string value, bool alwaysQuote = false) + private static string EscapeProcessArgument(string? value, bool alwaysQuote = false) { var builder = new StringBuilder(); ProcessUtils.EscapeProcessArgument(builder, value, alwaysQuote); diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/TcpChannelUtilsTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/TcpChannelUtilsTests.cs index 79a3c2154..3468c6073 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/TcpChannelUtilsTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/TcpChannelUtilsTests.cs @@ -18,6 +18,7 @@ public class TcpChannelUtilsTests public void GetTcpChannelReturnsSameChannelForSameNameDifferentPort() { var first = TcpChannelUtils.GetTcpChannel("test", 1234); + Assert.That(first, Is.Not.Null); using (CleanUpOnDispose(first)) { var second = TcpChannelUtils.GetTcpChannel("test", 4321); @@ -29,6 +30,7 @@ public void GetTcpChannelReturnsSameChannelForSameNameDifferentPort() public void GetTcpChannelReturnsSameChannelForSameNameUnspecifiedPorts() { var first = TcpChannelUtils.GetTcpChannel("test", 0); + Assert.That(first, Is.Not.Null); using (CleanUpOnDispose(first)) { var second = TcpChannelUtils.GetTcpChannel("test", 0); @@ -40,6 +42,7 @@ public void GetTcpChannelReturnsSameChannelForSameNameUnspecifiedPorts() public void GetTcpChannelReturnsChannelWithCorrectName() { var channel = TcpChannelUtils.GetTcpChannel("test", 1234); + Assert.That(channel, Is.Not.Null); using (CleanUpOnDispose(channel)) { Assert.That(channel, HasChannelName().EqualTo("test")); @@ -50,6 +53,7 @@ public void GetTcpChannelReturnsChannelWithCorrectName() public void GetTcpChannelReturnsChannelWithCorrectNameForUnspecifiedPort() { var channel = TcpChannelUtils.GetTcpChannel("test", 0); + Assert.That(channel, Is.Not.Null); using (CleanUpOnDispose(channel)) { Assert.That(channel, HasChannelName().EqualTo("test")); @@ -60,6 +64,7 @@ public void GetTcpChannelReturnsChannelWithCorrectNameForUnspecifiedPort() public void GetTcpChannelReturnsChannelWithCorrectURI() { var channel = TcpChannelUtils.GetTcpChannel("test", 1234); + Assert.That(channel, Is.Not.Null); using (CleanUpOnDispose(channel)) { Assert.That(channel, HasChannelUris().EqualTo(new[] { "tcp://127.0.0.1:1234" })); diff --git a/src/NUnitEngine/nunit.engine.core.tests/ResultHelperTests.cs b/src/NUnitEngine/nunit.engine.core.tests/ResultHelperTests.cs index 4f18da030..d5636ee81 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/ResultHelperTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/ResultHelperTests.cs @@ -1,11 +1,10 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System.Xml; +using NUnit.Framework; namespace NUnit.Engine.Internal { - using Framework; - public class ResultHelperTests { private const string resultText1 = ""; @@ -46,17 +45,18 @@ public void AggregateTestResult() XmlNode combinedNode = combined.Xml; Assert.That(combinedNode.Name, Is.EqualTo("test-run")); - Assert.That(combinedNode.Attributes["id"].Value, Is.EqualTo("ID")); - Assert.That(combinedNode.Attributes["name"].Value, Is.EqualTo("NAME")); - Assert.That(combinedNode.Attributes["fullname"].Value, Is.EqualTo("FULLNAME")); - Assert.That(combinedNode.Attributes["result"].Value, Is.EqualTo("Failed")); - Assert.That(combinedNode.Attributes["total"].Value, Is.EqualTo("42")); - Assert.That(combinedNode.Attributes["passed"].Value, Is.EqualTo("31")); - Assert.That(combinedNode.Attributes["failed"].Value, Is.EqualTo("3")); - Assert.That(combinedNode.Attributes["warnings"].Value, Is.EqualTo("1")); - Assert.That(combinedNode.Attributes["inconclusive"].Value, Is.EqualTo("5")); - Assert.That(combinedNode.Attributes["skipped"].Value, Is.EqualTo("2")); - Assert.That(combinedNode.Attributes["asserts"].Value, Is.EqualTo("53")); + Assert.That(combinedNode.Attributes, Is.Not.Null); + Assert.That(combinedNode.Attributes["id"]?.Value, Is.EqualTo("ID")); + Assert.That(combinedNode.Attributes["name"]?.Value, Is.EqualTo("NAME")); + Assert.That(combinedNode.Attributes["fullname"]?.Value, Is.EqualTo("FULLNAME")); + Assert.That(combinedNode.Attributes["result"]?.Value, Is.EqualTo("Failed")); + Assert.That(combinedNode.Attributes["total"]?.Value, Is.EqualTo("42")); + Assert.That(combinedNode.Attributes["passed"]?.Value, Is.EqualTo("31")); + Assert.That(combinedNode.Attributes["failed"]?.Value, Is.EqualTo("3")); + Assert.That(combinedNode.Attributes["warnings"]?.Value, Is.EqualTo("1")); + Assert.That(combinedNode.Attributes["inconclusive"]?.Value, Is.EqualTo("5")); + Assert.That(combinedNode.Attributes["skipped"]?.Value, Is.EqualTo("2")); + Assert.That(combinedNode.Attributes["asserts"]?.Value, Is.EqualTo("53")); } [Test] @@ -68,18 +68,19 @@ public void MergeAndAggregateTestResults() XmlNode combinedNode = combined.Xml; Assert.That(combinedNode.Name, Is.EqualTo("test-suite")); - Assert.That(combinedNode.Attributes["type"].Value, Is.EqualTo("Project")); - Assert.That(combinedNode.Attributes["id"].Value, Is.EqualTo("ID")); - Assert.That(combinedNode.Attributes["name"].Value, Is.EqualTo("NAME")); - Assert.That(combinedNode.Attributes["fullname"].Value, Is.EqualTo("FULLNAME")); - Assert.That(combinedNode.Attributes["result"].Value, Is.EqualTo("Failed")); - Assert.That(combinedNode.Attributes["total"].Value, Is.EqualTo("65")); - Assert.That(combinedNode.Attributes["passed"].Value, Is.EqualTo("54")); - Assert.That(combinedNode.Attributes["failed"].Value, Is.EqualTo("3")); - Assert.That(combinedNode.Attributes["warnings"].Value, Is.EqualTo("1")); - Assert.That(combinedNode.Attributes["inconclusive"].Value, Is.EqualTo("5")); - Assert.That(combinedNode.Attributes["skipped"].Value, Is.EqualTo("2")); - Assert.That(combinedNode.Attributes["asserts"].Value, Is.EqualTo("93")); + Assert.That(combinedNode.Attributes, Is.Not.Null); + Assert.That(combinedNode.Attributes["type"]?.Value, Is.EqualTo("Project")); + Assert.That(combinedNode.Attributes["id"]?.Value, Is.EqualTo("ID")); + Assert.That(combinedNode.Attributes["name"]?.Value, Is.EqualTo("NAME")); + Assert.That(combinedNode.Attributes["fullname"]?.Value, Is.EqualTo("FULLNAME")); + Assert.That(combinedNode.Attributes["result"]?.Value, Is.EqualTo("Failed")); + Assert.That(combinedNode.Attributes["total"]?.Value, Is.EqualTo("65")); + Assert.That(combinedNode.Attributes["passed"]?.Value, Is.EqualTo("54")); + Assert.That(combinedNode.Attributes["failed"]?.Value, Is.EqualTo("3")); + Assert.That(combinedNode.Attributes["warnings"]?.Value, Is.EqualTo("1")); + Assert.That(combinedNode.Attributes["inconclusive"]?.Value, Is.EqualTo("5")); + Assert.That(combinedNode.Attributes["skipped"]?.Value, Is.EqualTo("2")); + Assert.That(combinedNode.Attributes["asserts"]?.Value, Is.EqualTo("93")); } [Test] @@ -88,17 +89,18 @@ public void AggregateXmlNodes() XmlNode combined = ResultHelper.Aggregate("test-run", "ID", "NAME", "FULLNAME", twoNodes); Assert.That(combined.Name, Is.EqualTo("test-run")); - Assert.That(combined.Attributes["id"].Value, Is.EqualTo("ID")); - Assert.That(combined.Attributes["name"].Value, Is.EqualTo("NAME")); - Assert.That(combined.Attributes["fullname"].Value, Is.EqualTo("FULLNAME")); - Assert.That(combined.Attributes["result"].Value, Is.EqualTo("Failed")); - Assert.That(combined.Attributes["total"].Value, Is.EqualTo("65")); - Assert.That(combined.Attributes["passed"].Value, Is.EqualTo("54")); - Assert.That(combined.Attributes["failed"].Value, Is.EqualTo("3")); - Assert.That(combined.Attributes["warnings"].Value, Is.EqualTo("1")); - Assert.That(combined.Attributes["inconclusive"].Value, Is.EqualTo("5")); - Assert.That(combined.Attributes["skipped"].Value, Is.EqualTo("2")); - Assert.That(combined.Attributes["asserts"].Value, Is.EqualTo("93")); + Assert.That(combined.Attributes, Is.Not.Null); + Assert.That(combined.Attributes["id"]?.Value, Is.EqualTo("ID")); + Assert.That(combined.Attributes["name"]?.Value, Is.EqualTo("NAME")); + Assert.That(combined.Attributes["fullname"]?.Value, Is.EqualTo("FULLNAME")); + Assert.That(combined.Attributes["result"]?.Value, Is.EqualTo("Failed")); + Assert.That(combined.Attributes["total"]?.Value, Is.EqualTo("65")); + Assert.That(combined.Attributes["passed"]?.Value, Is.EqualTo("54")); + Assert.That(combined.Attributes["failed"]?.Value, Is.EqualTo("3")); + Assert.That(combined.Attributes["warnings"]?.Value, Is.EqualTo("1")); + Assert.That(combined.Attributes["inconclusive"]?.Value, Is.EqualTo("5")); + Assert.That(combined.Attributes["skipped"]?.Value, Is.EqualTo("2")); + Assert.That(combined.Attributes["asserts"]?.Value, Is.EqualTo("93")); } [TestCase("Skipped", "Skipped", "Skipped")] @@ -126,7 +128,7 @@ public void Aggregate_CalculatesAggregateResultCorrectly(string firstResult, str var secondEngineResult = new TestEngineResult(secondResultText); var data = new XmlNode[]{ firstEngineResult.Xml, secondEngineResult.Xml }; XmlNode combined = ResultHelper.Aggregate("test-run", "ID", "NAME", "FULLNAME", data); - Assert.That(combined.Attributes["result"].Value, Is.EqualTo(aggregateResult)); + Assert.That(combined.Attributes?["result"]?.Value, Is.EqualTo(aggregateResult)); } } } \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerStaticTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerStaticTests.cs index 595174aa7..3e03e2659 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerStaticTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerStaticTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Diagnostics.CodeAnalysis; using System.IO; using NUnit.Framework; @@ -85,7 +86,7 @@ public static void CanReadConfigFile() [TestCase("/path/to/mytest.dll", null, "/path/to/")] [TestCase("/path/to/mytest.dll", "/path", "/path/")] - public static void ApplicationBaseTests(string filePath, string appBase, string expected) + public static void ApplicationBaseTests(string filePath, string? appBase, string expected) { filePath = TestPath(filePath); appBase = TestPath(appBase); @@ -100,7 +101,7 @@ public static void ApplicationBaseTests(string filePath, string appBase, string [TestCase("/path/to/mytest.dll", "/path/to", null)] [TestCase("/path/to/mytest.dll", "/path", "to")] - public static void PrivateBinPathTests(string filePath, string appBase, string expected) + public static void PrivateBinPathTests(string filePath, string appBase, string? expected) { filePath = TestPath(filePath); appBase = TestPath(appBase); @@ -115,7 +116,7 @@ public static void PrivateBinPathTests(string filePath, string appBase, string e [TestCase("/path/to/mytest.dll", "/path", null, "/path/to/mytest.dll.config")] [TestCase("/path/to/mytest.nunit", "/path/to", null, null)] [TestCase("/path/to/mytest.nunit", "/path/to", "/path/to/mytest.config", "/path/to/mytest.config")] - public static void ConfigFileTests(string filePath, string appBase, string configSetting, string expected) + public static void ConfigFileTests(string filePath, string appBase, string? configSetting, string? expected) { filePath = TestPath(filePath); appBase = TestPath(appBase); @@ -134,7 +135,8 @@ public static void ConfigFileTests(string filePath, string appBase, string confi /// if we are on Windows. Change slashes to backslashes and, if the /// filePath starts with a slash, add C: in front of it. /// - private static string TestPath(string path) + [return: NotNullIfNotNull(nameof(path))] + private static string? TestPath(string? path) { if (path != null && Path.DirectorySeparatorChar != '/') { diff --git a/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerTests.cs index 9026b37f9..aa6c7ee5a 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Runners/DomainManagerTests.cs @@ -47,8 +47,8 @@ public void CanCreateDomain() [Test, Platform("Linux,Net", Reason = "get_SetupInformation() fails on Windows+Mono")] public void CanCreateDomainWithApplicationBaseSpecified() { - string assemblyDir = Path.GetDirectoryName(_package.FullName); - string basePath = Path.GetDirectoryName(Path.GetDirectoryName(assemblyDir)); + string assemblyDir = Path.GetDirectoryName(_package.FullName)!; + string basePath = Path.GetDirectoryName(Path.GetDirectoryName(assemblyDir))!; string relPath = assemblyDir.Substring(basePath.Length + 1); _package.Settings["BasePath"] = basePath; diff --git a/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerExceptionTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerExceptionTests.cs index 881c58f83..098237a5b 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerExceptionTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerExceptionTests.cs @@ -32,6 +32,12 @@ public void Initialize() }; } + [TearDown] + public void Cleanup() + { + _runner.Dispose(); + } + [Test] public void Explore_Passes_Along_NUnitEngineException() { @@ -45,7 +51,8 @@ public void Explore_Throws_NUnitEngineException() { _driver.Explore(Arg.Any()).Throws(new ArgumentException("Message")); var ex = Assert.Throws(() => _runner.Explore(new TestFilter(string.Empty))); - Assert.That(ex.InnerException is ArgumentException); + Assert.That(ex.InnerException, Is.Not.Null); + Assert.That(ex.InnerException, Is.InstanceOf()); Assert.That(ex.InnerException.Message, Is.EqualTo("Message")); } @@ -62,7 +69,8 @@ public void Load_Throws_NUnitEngineException() { _driver.Load(Arg.Any(), Arg.Any>()).Throws(new ArgumentException("Message")); var ex = Assert.Throws(() => _runner.Load()); - Assert.That(ex.InnerException is ArgumentException); + Assert.That(ex.InnerException, Is.Not.Null); + Assert.That(ex.InnerException, Is.InstanceOf()); Assert.That(ex.InnerException.Message, Is.EqualTo("Message")); } @@ -79,7 +87,8 @@ public void CountTestCases_Throws_NUnitEngineException() { _driver.CountTestCases(Arg.Any()).Throws(new ArgumentException("Message")); var ex = Assert.Throws(() => _runner.CountTestCases(_testFilter)); - Assert.That(ex.InnerException is ArgumentException); + Assert.That(ex.InnerException, Is.Not.Null); + Assert.That(ex.InnerException, Is.InstanceOf()); Assert.That(ex.InnerException.Message, Is.EqualTo("Message")); } @@ -96,7 +105,8 @@ public void Run_Throws_NUnitEngineException() { _driver.Run(Arg.Any(), Arg.Any()).Throws(new ArgumentException("Message")); var ex = Assert.Throws(() => _runner.Run(Substitute.For(), _testFilter)); - Assert.That(ex.InnerException is ArgumentException); + Assert.That(ex.InnerException, Is.Not.Null); + Assert.That(ex.InnerException, Is.InstanceOf()); Assert.That(ex.InnerException.Message, Is.EqualTo("Message")); } @@ -117,7 +127,8 @@ public void StopRun_Throws_NUnitEngineException() .Do(x => { throw new ArgumentException("Message"); }); var ex = Assert.Throws(() => _runner.StopRun(true)); - Assert.That(ex.InnerException is ArgumentException); + Assert.That(ex.InnerException, Is.Not.Null); + Assert.That(ex.InnerException, Is.InstanceOf()); Assert.That(ex.InnerException.Message, Is.EqualTo("Message")); } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerTests.cs index a3ff805c7..b02f94a7a 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Runners/TestAgentRunnerTests.cs @@ -28,7 +28,7 @@ public void Initialize() _package = new TestPackage(mockAssemblyPath).SubPackages[0]; - _runner = (TRunner)Activator.CreateInstance(typeof(TRunner), _package); + _runner = (TRunner)Activator.CreateInstance(typeof(TRunner), _package)!; } [TearDown] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Services/DriverServiceTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Services/DriverServiceTests.cs index 48d13429d..61122628c 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Services/DriverServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Services/DriverServiceTests.cs @@ -72,7 +72,7 @@ public void EnsureWeHaveSomeValidTestCases() { // Third argument is the Type of the driver var driverType = testcase.Arguments[2] as Type; - if (!(driverType.BaseType == typeof(NotRunnableFrameworkDriver))) + if (driverType is null || !(driverType.BaseType == typeof(NotRunnableFrameworkDriver))) break; // All expected drivers derive from NotRunnableFrameworkDriver diff --git a/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs index 8d7eee359..f427ecc22 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs @@ -295,14 +295,14 @@ public static IEnumerable InvalidRunnerCombos() private static string GetSiblingDirectory(string dir) { var file = new FileInfo(typeof(ExtensionManagerTests).Assembly.Location); - return Path.Combine(file.Directory.Parent.FullName, dir); + return Path.Combine(file.Directory!.Parent!.FullName, dir); } private static readonly Assembly THIS_ASSEMBLY = typeof(ExtensionManagerTests).Assembly; - private static readonly string THIS_ASSEMBLY_DIRECTORY = Path.GetDirectoryName(THIS_ASSEMBLY.Location); + private static readonly string THIS_ASSEMBLY_DIRECTORY = Path.GetDirectoryName(THIS_ASSEMBLY.Location)!; private const string FAKE_EXTENSIONS_FILENAME = "FakeExtensions.dll"; private static readonly string FAKE_EXTENSIONS_PARENT_DIRECTORY = - Path.Combine(new DirectoryInfo(THIS_ASSEMBLY_DIRECTORY).Parent.FullName, "fakes"); + Path.Combine(new DirectoryInfo(THIS_ASSEMBLY_DIRECTORY).Parent!.FullName, "fakes"); /// /// Returns an ExtensionAssembly referring to a particular build of the fake test extensions diff --git a/src/NUnitEngine/nunit.engine.core.tests/TestEngineResultTests.cs b/src/NUnitEngine/nunit.engine.core.tests/TestEngineResultTests.cs index 80f03f121..f0a57a1fe 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/TestEngineResultTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/TestEngineResultTests.cs @@ -17,13 +17,14 @@ public void CanCreateFromXmlString() TestEngineResult result = new TestEngineResult(xmlText); Assert.That(result.IsSingle, Is.True); Assert.That(result.Xml.Name, Is.EqualTo("test-assembly")); - Assert.That(result.Xml.Attributes["result"].Value, Is.EqualTo("Passed")); - Assert.That(result.Xml.Attributes["total"].Value, Is.EqualTo("23")); - Assert.That(result.Xml.Attributes["passed"].Value, Is.EqualTo("23")); - Assert.That(result.Xml.Attributes["failed"].Value, Is.EqualTo("0")); - Assert.That(result.Xml.Attributes["inconclusive"].Value, Is.EqualTo("0")); - Assert.That(result.Xml.Attributes["skipped"].Value, Is.EqualTo("0")); - Assert.That(result.Xml.Attributes["asserts"].Value, Is.EqualTo("40")); + Assert.That(result.Xml.Attributes, Is.Not.Null); + Assert.That(result.Xml.Attributes["result"]?.Value, Is.EqualTo("Passed")); + Assert.That(result.Xml.Attributes["total"]?.Value, Is.EqualTo("23")); + Assert.That(result.Xml.Attributes["passed"]?.Value, Is.EqualTo("23")); + Assert.That(result.Xml.Attributes["failed"]?.Value, Is.EqualTo("0")); + Assert.That(result.Xml.Attributes["inconclusive"]?.Value, Is.EqualTo("0")); + Assert.That(result.Xml.Attributes["skipped"]?.Value, Is.EqualTo("0")); + Assert.That(result.Xml.Attributes["asserts"]?.Value, Is.EqualTo("40")); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/XmlHelperTests.cs b/src/NUnitEngine/nunit.engine.core.tests/XmlHelperTests.cs index fc20a739d..05d83d3c5 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/XmlHelperTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/XmlHelperTests.cs @@ -15,6 +15,7 @@ public void SingleElement() XmlNode node = XmlHelper.CreateTopLevelElement("myelement"); Assert.That(node.Name, Is.EqualTo("myelement")); + Assert.That(node.Attributes, Is.Not.Null); Assert.That(node.Attributes.Count, Is.EqualTo(0)); Assert.That(node.ChildNodes.Count, Is.EqualTo(0)); } @@ -28,11 +29,12 @@ public void SingleElementWithAttributes() XmlHelper.AddAttribute(node, "quotes", "'c' is a char but \"c\" is a string"); Assert.That(node.Name, Is.EqualTo("person")); + Assert.That(node.Attributes, Is.Not.Null); Assert.That(node.Attributes.Count, Is.EqualTo(3)); Assert.That(node.ChildNodes.Count, Is.EqualTo(0)); - Assert.That(node.Attributes["name"].Value, Is.EqualTo("Fred")); - Assert.That(node.Attributes["age"].Value, Is.EqualTo("42")); - Assert.That(node.Attributes["quotes"].Value, Is.EqualTo("'c' is a char but \"c\" is a string")); + Assert.That(node.Attributes["name"]?.Value, Is.EqualTo("Fred")); + Assert.That(node.Attributes["age"]?.Value, Is.EqualTo("42")); + Assert.That(node.Attributes["quotes"]?.Value, Is.EqualTo("'c' is a char but \"c\" is a string")); } [Test] @@ -42,7 +44,7 @@ public void ElementContainsElementWithInnerText() XmlNode message = top.AddElement("message"); message.InnerText = "This is my message"; - Assert.That(top.SelectSingleNode("message").InnerText, Is.EqualTo("This is my message")); + Assert.That(top.SelectSingleNode("message")?.InnerText, Is.EqualTo("This is my message")); } [Test] @@ -51,7 +53,7 @@ public void ElementContainsElementWithCData() XmlNode top = XmlHelper.CreateTopLevelElement("top"); top.AddElementWithCDataSection("message", "x > 5 && x < 7"); - Assert.That(top.SelectSingleNode("message").InnerText, Is.EqualTo("x > 5 && x < 7")); + Assert.That(top.SelectSingleNode("message")?.InnerText, Is.EqualTo("x > 5 && x < 7")); } [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj b/src/NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj index 76c531c66..ee7dd4d94 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj +++ b/src/NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj @@ -18,6 +18,12 @@ Tests of nunit.engine.core assembly + + true + + + + @@ -28,6 +34,10 @@ + + + + diff --git a/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs b/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs index f272e2fe3..bbeeeff0d 100644 --- a/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs +++ b/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs @@ -25,7 +25,7 @@ public class RemoteTestAgent : TestAgent /// public RemoteTestAgent(Guid agentId) : base(agentId) { } - public ITestAgentTransport Transport; + public ITestAgentTransport? Transport; public int ProcessId => System.Diagnostics.Process.GetCurrentProcess().Id; @@ -37,6 +37,7 @@ public override bool Start() public override void Stop() { + Guard.OperationValid(Transport != null, "Transport must be set before calling Stop()."); Transport.Stop(); } diff --git a/src/NUnitEngine/nunit.engine.core/AsyncTestEngineResult.cs b/src/NUnitEngine/nunit.engine.core/AsyncTestEngineResult.cs index 5b586fd0f..16b674298 100644 --- a/src/NUnitEngine/nunit.engine.core/AsyncTestEngineResult.cs +++ b/src/NUnitEngine/nunit.engine.core/AsyncTestEngineResult.cs @@ -13,7 +13,7 @@ namespace NUnit.Engine [Serializable] public class AsyncTestEngineResult : ITestRun { - private volatile TestEngineResult _result; + private volatile TestEngineResult? _result; private readonly ManualResetEvent _waitHandle = new ManualResetEvent(false); /// diff --git a/src/NUnitEngine/nunit.engine.core/CallbackHandler.cs b/src/NUnitEngine/nunit.engine.core/CallbackHandler.cs index d18f27ff4..cec574316 100644 --- a/src/NUnitEngine/nunit.engine.core/CallbackHandler.cs +++ b/src/NUnitEngine/nunit.engine.core/CallbackHandler.cs @@ -8,11 +8,11 @@ namespace NUnit.Engine { public class CallbackHandler : MarshalByRefObject, ICallbackEventHandler { - public string Result { get; private set; } + public string? Result { get; private set; } public override object InitializeLifetimeService() { - return null; + return null!; } public string GetCallbackResult() diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs index ac8486355..a772ee97c 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs @@ -22,10 +22,10 @@ public class TestAgentRemotingTransport : MarshalByRefObject, ITestAgentTranspor private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentRemotingTransport)); private readonly string _agencyUrl; - private ITestEngineRunner _runner; + private ITestEngineRunner? _runner; - private TcpChannel _channel; - private ITestAgency _agency; + private TcpChannel? _channel; + private ITestAgency? _agency; private readonly CurrentMessageCounter _currentMessageCounter = new CurrentMessageCounter(); public TestAgentRemotingTransport(RemoteTestAgent agent, string agencyUrl) @@ -54,6 +54,7 @@ public bool Start() catch (Exception ex) { log.Error("Unable to connect: {0}", ExceptionHelper.BuildMessageAndStackTrace(ex)); + return false; } try @@ -72,6 +73,8 @@ public bool Start() public void Stop() { + Guard.OperationValid(_channel != null, "Channel is not open"); + log.Info("Stopping"); // Do this on a different thread since we need to wait until all messages are through, @@ -97,6 +100,7 @@ public void Stop() public ITestEngineRunner CreateRunner(TestPackage package) { + _runner?.Dispose(); _runner = Agent.CreateRunner(package); return this; } @@ -104,7 +108,7 @@ public ITestEngineRunner CreateRunner(TestPackage package) public void Dispose() { Agent.Dispose(); - _runner.Dispose(); + _runner?.Dispose(); } #region ITestEngineRunner Implementation @@ -117,23 +121,22 @@ public void Dispose() /// A TestEngineResult. public TestEngineResult Explore(TestFilter filter) { - return _runner.Explore(filter); + return _runner.ShouldNotBeNull().Explore(filter); } public TestEngineResult Load() { - return _runner.Load(); + return _runner.ShouldNotBeNull().Load(); } public void Unload() { - if (_runner != null) - _runner.Unload(); + _runner?.Unload(); } public TestEngineResult Reload() { - return _runner.Reload(); + return _runner.ShouldNotBeNull().Reload(); } /// @@ -144,7 +147,7 @@ public TestEngineResult Reload() /// The count of test cases public int CountTestCases(TestFilter filter) { - return _runner.CountTestCases(filter); + return _runner.ShouldNotBeNull().CountTestCases(filter); } /// @@ -156,7 +159,7 @@ public int CountTestCases(TestFilter filter) /// A TestEngineResult giving the result of the test execution public TestEngineResult Run(ITestEventListener listener, TestFilter filter) { - return _runner.Run(listener, filter); + return _runner.ShouldNotBeNull().Run(listener, filter); } /// @@ -168,7 +171,7 @@ public TestEngineResult Run(ITestEventListener listener, TestFilter filter) /// A that will provide the result of the test execution public AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter filter) { - return _runner.RunAsync(listener, filter); + return _runner.ShouldNotBeNull().RunAsync(listener, filter); } /// @@ -177,8 +180,7 @@ public AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter fi /// If true, cancel any ongoing test threads, otherwise wait for them to complete. public void StopRun(bool force) { - if (_runner != null) - _runner.StopRun(force); + _runner?.StopRun(force); } #endregion @@ -188,7 +190,7 @@ public void StopRun(bool force) /// public override object InitializeLifetimeService() { - return null; + return null!; } } } diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs index 60223956e..2553a59d5 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs @@ -16,11 +16,11 @@ public class SocketReader { private const int BUFFER_SIZE = 1024; - private Socket _socket; - private ISerializationProtocol _wireProtocol; + private readonly Socket _socket; + private readonly ISerializationProtocol _wireProtocol; - private Queue _msgQueue; - private byte[] _buffer; + private readonly Queue _msgQueue; + private readonly byte[] _buffer; public SocketReader(Socket socket, ISerializationProtocol protocol) { diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs index bd8f913e3..f130c8621 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs @@ -20,9 +20,9 @@ public class TestAgentTcpTransport : ITestAgentTransport, ITestEventListener { private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentTcpTransport)); - private string _agencyUrl; - private Socket _clientSocket; - private ITestEngineRunner _runner; + private readonly string _agencyUrl; + private Socket? _clientSocket; + private ITestEngineRunner? _runner; public TestAgentTcpTransport(RemoteTestAgent agent, string serverUrl) { @@ -72,7 +72,7 @@ public ITestEngineRunner CreateRunner(TestPackage package) private void CommandLoop() { bool keepRunning = true; - var socketReader = new SocketReader(_clientSocket, new BinarySerializationProtocol()); + var socketReader = new SocketReader(_clientSocket.ShouldNotBeNull(), new BinarySerializationProtocol()); while (keepRunning) { @@ -82,33 +82,34 @@ private void CommandLoop() { case "CreateRunner": var package = (TestPackage)command.Arguments[0]; + _runner?.Unload(); _runner = CreateRunner(package); break; case "Load": - SendResult(_runner.Load()); + SendResult(_runner.ShouldNotBeNull().Load()); break; case "Reload": - SendResult(_runner.Reload()); + SendResult(_runner.ShouldNotBeNull().Reload()); break; case "Unload": - _runner.Unload(); + _runner.ShouldNotBeNull().Unload(); break; case "Explore": var filter = (TestFilter)command.Arguments[0]; - SendResult(_runner.Explore(filter)); + SendResult(_runner.ShouldNotBeNull().Explore(filter)); break; case "CountTestCases": filter = (TestFilter)command.Arguments[0]; - SendResult(_runner.CountTestCases(filter)); + SendResult(_runner.ShouldNotBeNull().CountTestCases(filter)); break; case "Run": filter = (TestFilter)command.Arguments[0]; - SendResult(_runner.Run(this, filter)); + SendResult(_runner.ShouldNotBeNull().Run(this, filter)); break; case "RunAsync": filter = (TestFilter)command.Arguments[0]; - _runner.RunAsync(this, filter); + _runner.ShouldNotBeNull().RunAsync(this, filter); break; case "Stop": @@ -124,14 +125,14 @@ private void SendResult(object result) { var resultMessage = new CommandReturnMessage(result); var bytes = new BinarySerializationProtocol().Encode(resultMessage); - _clientSocket.Send(bytes); + _clientSocket.ShouldNotBeNull().Send(bytes); } public void OnTestEvent(string report) { var progressMessage = new ProgressMessage(report); var bytes = new BinarySerializationProtocol().Encode(progressMessage); - _clientSocket.Send(bytes); + _clientSocket.ShouldNotBeNull().Send(bytes); } } } diff --git a/src/NUnitEngine/nunit.engine.core/DotNet.cs b/src/NUnitEngine/nunit.engine.core/DotNet.cs index bba07a275..5d4eece65 100644 --- a/src/NUnitEngine/nunit.engine.core/DotNet.cs +++ b/src/NUnitEngine/nunit.engine.core/DotNet.cs @@ -9,14 +9,14 @@ namespace NUnit.Engine { public static class DotNet { - public static string GetInstallDirectory() => Environment.Is64BitProcess + public static string? GetInstallDirectory() => Environment.Is64BitProcess ? GetX64InstallDirectory() : GetX86InstallDirectory(); - public static string GetInstallDirectory(bool x86) => x86 + public static string? GetInstallDirectory(bool x86) => x86 ? GetX86InstallDirectory() : GetX64InstallDirectory(); - private static string _x64InstallDirectory; - public static string GetX64InstallDirectory() + private static string? _x64InstallDirectory; + public static string? GetX64InstallDirectory() { if (_x64InstallDirectory == null) _x64InstallDirectory = Environment.GetEnvironmentVariable("DOTNET_ROOT"); @@ -29,8 +29,8 @@ public static string GetX64InstallDirectory() if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) #endif { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\"); - _x64InstallDirectory = (string)key?.GetValue("Path"); + using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\")) + _x64InstallDirectory = (string?)key?.GetValue("Path"); } else _x64InstallDirectory = "/usr/shared/dotnet/"; @@ -39,8 +39,8 @@ public static string GetX64InstallDirectory() return _x64InstallDirectory; } - private static string _x86InstallDirectory; - public static string GetX86InstallDirectory() + private static string? _x86InstallDirectory; + public static string? GetX86InstallDirectory() { if (_x86InstallDirectory == null) _x86InstallDirectory = Environment.GetEnvironmentVariable("DOTNET_ROOT_X86"); @@ -53,8 +53,8 @@ public static string GetX86InstallDirectory() if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) #endif { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\"); - _x86InstallDirectory = (string)key?.GetValue("InstallLocation"); + using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\")) + _x86InstallDirectory = (string?)key?.GetValue("InstallLocation"); } else _x86InstallDirectory = "/usr/shared/dotnet/"; diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/DriverService.cs b/src/NUnitEngine/nunit.engine.core/Drivers/DriverService.cs index 15e6bea3f..ccaec8db7 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/DriverService.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/DriverService.cs @@ -51,7 +51,7 @@ public DriverService() /// The value of any TargetFrameworkAttribute on the assembly, or null /// True if non-test assemblies should simply be skipped rather than reporting an error /// - public IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string targetFramework, bool skipNonTestAssemblies) + public IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string? targetFramework, bool skipNonTestAssemblies) { if (!File.Exists(assemblyPath)) return new InvalidAssemblyFrameworkDriver(assemblyPath, "File not found: " + assemblyPath); @@ -98,11 +98,13 @@ public IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string foreach (var reference in references) { if (factory.IsSupportedTestFramework(reference)) + { #if NETFRAMEWORK - return factory.GetDriver(domain, reference); + return factory.GetDriver(domain, reference); #else - return factory.GetDriver(reference); + return factory.GetDriver(reference); #endif + } } } } diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/IDriverService.cs b/src/NUnitEngine/nunit.engine.core/Drivers/IDriverService.cs index d077673f2..fb3c26d3f 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/IDriverService.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/IDriverService.cs @@ -19,6 +19,6 @@ public interface IDriverService /// The value of any TargetFrameworkAttribute on the assembly, or null /// True if non-test assemblies should simply be skipped rather than reporting an error /// - IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string targetFramework, bool skipNonTestAssemblies); + IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string? targetFramework, bool skipNonTestAssemblies); } } diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs index 6bc490cc8..df4fb3688 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs @@ -13,10 +13,10 @@ public class NUnit2DriverFactory : IDriverFactory { private const string NUNIT_FRAMEWORK = "nunit.framework"; private const string NUNITLITE_FRAMEWORK = "nunitlite"; - private ExtensionNode _driverNode; + private readonly ExtensionNode _driverNode; // TODO: This should be a central service but for now it's local - private ProvidedPathsAssemblyResolver _resolver; + private readonly ProvidedPathsAssemblyResolver _resolver; bool _resolverInstalled; public NUnit2DriverFactory(ExtensionNode driverNode) @@ -32,8 +32,8 @@ public NUnit2DriverFactory(ExtensionNode driverNode) /// An AssemblyName referring to the possible test framework. public bool IsSupportedTestFramework(AssemblyName reference) { - return NUNIT_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version.Major == 2 - || NUNITLITE_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version.Major == 1; + return NUNIT_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version?.Major == 2 + || NUNITLITE_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version?.Major == 1; } /// @@ -55,7 +55,7 @@ public IFrameworkDriver GetDriver(AppDomain domain, AssemblyName reference) _resolver.AddPathFromFile(_driverNode.AssemblyPath); } - return _driverNode.CreateExtensionObject(domain) as IFrameworkDriver; + return (IFrameworkDriver)_driverNode.CreateExtensionObject(domain); } } } diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3DriverFactory.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3DriverFactory.cs index 03b0bb5ae..5186570b0 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3DriverFactory.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3DriverFactory.cs @@ -11,7 +11,7 @@ namespace NUnit.Engine.Drivers public class NUnit3DriverFactory : IDriverFactory { internal const string NUNIT_FRAMEWORK = "nunit.framework"; - static ILogger log = InternalTrace.GetLogger(typeof(NUnit3DriverFactory)); + static readonly ILogger log = InternalTrace.GetLogger(typeof(NUnit3DriverFactory)); /// /// Gets a flag indicating whether a given assembly name and version @@ -20,7 +20,7 @@ public class NUnit3DriverFactory : IDriverFactory /// An AssemblyName referring to the possible test framework. public bool IsSupportedTestFramework(AssemblyName reference) { - return NUNIT_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version.Major >= 3; + return NUNIT_FRAMEWORK.Equals(reference.Name, StringComparison.OrdinalIgnoreCase) && reference.Version?.Major >= 3; } #if NETFRAMEWORK diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3FrameworkDriver.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3FrameworkDriver.cs index d89eadb8b..8084d79b0 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3FrameworkDriver.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit3FrameworkDriver.cs @@ -9,6 +9,7 @@ using NUnit.Common; using NUnit.Engine.Internal; using NUnit.Engine.Extensibility; +using System.Diagnostics.CodeAnalysis; namespace NUnit.Engine.Drivers { @@ -27,13 +28,13 @@ public class NUnit3FrameworkDriver : IFrameworkDriver private static readonly string RUN_ACTION = CONTROLLER_TYPE + "+RunTestsAction"; private static readonly string STOP_RUN_ACTION = CONTROLLER_TYPE + "+StopRunAction"; - static ILogger log = InternalTrace.GetLogger("NUnitFrameworkDriver"); + static readonly ILogger log = InternalTrace.GetLogger("NUnitFrameworkDriver"); - AppDomain _testDomain; - AssemblyName _reference; - string _testAssemblyPath; + readonly AppDomain _testDomain; + readonly AssemblyName _reference; + string? _testAssemblyPath; - object _frameworkController; + object? _frameworkController; /// /// Construct an NUnit3FrameworkDriver @@ -46,7 +47,7 @@ public NUnit3FrameworkDriver(AppDomain testDomain, AssemblyName reference) _reference = reference; } - public string ID { get; set; } + public string ID { get; set; } = string.Empty; /// /// Loads the tests in an assembly. @@ -87,7 +88,7 @@ public string Load(string testAssemblyPath, IDictionary settings log.Info("Loaded {0}", fileName); - return handler.Result; + return handler.Result.ShouldNotBeNull(); } public int CountTestCases(string filter) @@ -96,9 +97,9 @@ public int CountTestCases(string filter) CallbackHandler handler = new CallbackHandler(); - CreateObject(COUNT_ACTION, _frameworkController, filter, handler); + CreateObject(COUNT_ACTION, _frameworkController.ShouldNotBeNull(), filter, handler); - return int.Parse(handler.Result); + return int.Parse(handler.Result.ShouldNotBeNull()); } /// @@ -107,16 +108,16 @@ public int CountTestCases(string filter) /// An ITestEventHandler that receives progress notices /// A filter that controls which tests are executed /// An Xml string representing the result - public string Run(ITestEventListener listener, string filter) + public string Run(ITestEventListener? listener, string filter) { CheckLoadWasCalled(); var handler = new RunTestsCallbackHandler(listener); - log.Info("Running {0} - see separate log file", Path.GetFileName(_testAssemblyPath)); - CreateObject(RUN_ACTION, _frameworkController, filter, handler); + log.Info("Running {0} - see separate log file", Path.GetFileName(_testAssemblyPath.ShouldNotBeNull())); + CreateObject(RUN_ACTION, _frameworkController.ShouldNotBeNull(), filter, handler); - return handler.Result; + return handler.Result.ShouldNotBeNull(); } /// @@ -125,7 +126,7 @@ public string Run(ITestEventListener listener, string filter) /// If true, cancel any ongoing test threads, otherwise wait for them to complete. public void StopRun(bool force) { - CreateObject(STOP_RUN_ACTION, _frameworkController, force, new CallbackHandler()); + CreateObject(STOP_RUN_ACTION, _frameworkController.ShouldNotBeNull(), force, new CallbackHandler()); } /// @@ -139,10 +140,10 @@ public string Explore(string filter) CallbackHandler handler = new CallbackHandler(); - log.Info("Exploring {0} - see separate log file", Path.GetFileName(_testAssemblyPath)); - CreateObject(EXPLORE_ACTION, _frameworkController, filter, handler); + log.Info("Exploring {0} - see separate log file", Path.GetFileName(_testAssemblyPath.ShouldNotBeNull())); + CreateObject(EXPLORE_ACTION, _frameworkController.ShouldNotBeNull(), filter, handler); - return handler.Result; + return handler.Result.ShouldNotBeNull(); } private void CheckLoadWasCalled() @@ -151,12 +152,12 @@ private void CheckLoadWasCalled() throw new InvalidOperationException(LOAD_MESSAGE); } - private object CreateObject(string typeName, params object[] args) + private object CreateObject(string typeName, params object?[]? args) { try { return _testDomain.CreateInstanceAndUnwrap( - _reference.FullName, typeName, false, 0, null, args, null, null ); + _reference.FullName, typeName, false, 0, null, args, null, null )!; } catch (TargetInvocationException ex) { diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NUnitNetCore31Driver.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NUnitNetCore31Driver.cs index 60063357d..7199f9a8f 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NUnitNetCore31Driver.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NUnitNetCore31Driver.cs @@ -9,6 +9,7 @@ using System.Reflection; using NUnit.Engine.Extensibility; using System.Diagnostics; +using NUnit.Common; namespace NUnit.Engine.Drivers { @@ -35,17 +36,17 @@ public class NUnitNetCore31Driver : IFrameworkDriver static ILogger log = InternalTrace.GetLogger(nameof(NUnitNetCore31Driver)); - Assembly _testAssembly; - Assembly _frameworkAssembly; - object _frameworkController; - Type _frameworkControllerType; - TestAssemblyLoadContext _assemblyLoadContext; + Assembly? _testAssembly; + Assembly? _frameworkAssembly; + object? _frameworkController; + Type? _frameworkControllerType; + TestAssemblyLoadContext? _assemblyLoadContext; /// /// An id prefix that will be passed to the test framework and used as part of the /// test ids created. /// - public string ID { get; set; } + public string ID { get; set; } = string.Empty; /// /// Loads the tests in an assembly. @@ -73,7 +74,7 @@ public string Load(string assemblyPath, IDictionary settings) } log.Debug($"Loaded {assemblyPath}"); - var nunitRef = _testAssembly.GetReferencedAssemblies().FirstOrDefault(reference => reference.Name.Equals("nunit.framework", StringComparison.OrdinalIgnoreCase)); + var nunitRef = _testAssembly.GetReferencedAssemblies().FirstOrDefault(reference => string.Equals(reference.Name, "nunit.framework", StringComparison.OrdinalIgnoreCase)); if (nunitRef == null) { log.Error(FAILED_TO_LOAD_NUNIT); @@ -101,8 +102,8 @@ public string Load(string assemblyPath, IDictionary settings) _frameworkControllerType = _frameworkController.GetType(); log.Debug($"Created FrameworkControler {_frameworkControllerType.Name}"); - log.Info("Loading {0} - see separate log file", _testAssembly.FullName); - return ExecuteMethod(LOAD_METHOD) as string; + log.Info("Loading {0} - see separate log file", _testAssembly.FullName!); + return (string)ExecuteMethod(LOAD_METHOD); } /// @@ -113,7 +114,7 @@ public string Load(string assemblyPath, IDictionary settings) public int CountTestCases(string filter) { CheckLoadWasCalled(); - object count = ExecuteMethod(COUNT_METHOD, filter); + object? count = ExecuteMethod(COUNT_METHOD, filter); return count != null ? (int)count : 0; } @@ -123,12 +124,12 @@ public int CountTestCases(string filter) /// An ITestEventHandler that receives progress notices /// A filter that controls which tests are executed /// An Xml string representing the result - public string Run(ITestEventListener listener, string filter) + public string Run(ITestEventListener? listener, string filter) { CheckLoadWasCalled(); - log.Info("Running {0} - see separate log file", _testAssembly.FullName); - Action callback = listener != null ? listener.OnTestEvent : (Action)null; - return ExecuteMethod(RUN_METHOD, new[] { typeof(Action), typeof(string) }, callback, filter) as string; + log.Info("Running {0} - see separate log file", _testAssembly.ShouldNotBeNull().FullName!); + Action? callback = listener != null ? listener.OnTestEvent : (Action?)null; + return (string)ExecuteMethod(RUN_METHOD, new[] { typeof(Action), typeof(string) }, callback, filter); } /// @@ -139,7 +140,7 @@ public string Run(ITestEventListener listener, string filter) public void RunAsync(Action callback, string filter) { CheckLoadWasCalled(); - log.Info("Running {0} - see separate log file", _testAssembly.FullName); + log.Info("Running {0} - see separate log file", _testAssembly.ShouldNotBeNull().FullName!); ExecuteMethod(RUN_ASYNC_METHOD, new[] { typeof(Action), typeof(string) }, callback, filter); } @@ -161,8 +162,8 @@ public string Explore(string filter) { CheckLoadWasCalled(); - log.Info("Exploring {0} - see separate log file", _testAssembly.FullName); - return ExecuteMethod(EXPLORE_METHOD, filter) as string; + log.Info("Exploring {0} - see separate log file", _testAssembly.ShouldNotBeNull().FullName!); + return (string)ExecuteMethod(EXPLORE_METHOD, filter); } void CheckLoadWasCalled() @@ -171,41 +172,35 @@ void CheckLoadWasCalled() throw new InvalidOperationException(LOAD_MESSAGE); } - object CreateObject(string typeName, params object[] args) + object CreateObject(string typeName, params object?[]? args) { - var typeinfo = _frameworkAssembly.DefinedTypes.FirstOrDefault(t => t.FullName == typeName); - if (typeinfo == null) - { - log.Error("Could not find type {0}", typeName); - } - return Activator.CreateInstance(typeinfo.AsType(), args); + var type = _frameworkAssembly.ShouldNotBeNull().GetType(typeName, throwOnError: true)!; + return Activator.CreateInstance(type, args)!; } - object ExecuteMethod(string methodName, params object[] args) + object ExecuteMethod(string methodName, params object?[] args) { - var method = _frameworkControllerType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance); - if (method == null) - log.Error($"Method {methodName} was not found in {_frameworkControllerType.Name}"); - log.Debug($"Executing {method.DeclaringType}.{method.Name}"); + var method = _frameworkControllerType.ShouldNotBeNull().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance); return ExecuteMethod(method, args); } - object ExecuteMethod(string methodName, Type[] ptypes, params object[] args) + object ExecuteMethod(string methodName, Type[] ptypes, params object?[] args) { - var method = _frameworkControllerType.GetMethod(methodName, ptypes); + var method = _frameworkControllerType.ShouldNotBeNull().GetMethod(methodName, ptypes); return ExecuteMethod(method, args); } - object ExecuteMethod(MethodInfo method, params object[] args) + object ExecuteMethod(MethodInfo? method, params object?[] args) { if (method == null) { throw new NUnitEngineException(INVALID_FRAMEWORK_MESSAGE); } - using (_assemblyLoadContext.EnterContextualReflection()) + using (_assemblyLoadContext.ShouldNotBeNull().EnterContextualReflection()) { - return method.Invoke(_frameworkController, args); + log.Debug($"Executing {method.DeclaringType}.{method.Name}"); + return method.Invoke(_frameworkController, args).ShouldNotBeNull(); } } } diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NotRunnableFrameworkDriver.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NotRunnableFrameworkDriver.cs index 77250b61e..6967be78f 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NotRunnableFrameworkDriver.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NotRunnableFrameworkDriver.cs @@ -26,16 +26,18 @@ public abstract class NotRunnableFrameworkDriver : IFrameworkDriver "" + ""; - private string _name; - private string _fullname; - private string _message; - private string _type; + private readonly string _name; + private readonly string _fullname; + private readonly string _message; + private readonly string _type; protected string _runstate; protected string _result; protected string _label; +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public NotRunnableFrameworkDriver(string assemblyPath, string message) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. { _name = Escape(Path.GetFileName(assemblyPath)); _fullname = Escape(Path.GetFullPath(assemblyPath)); @@ -56,7 +58,7 @@ public int CountTestCases(string filter) return 0; } - public string Run(ITestEventListener listener, string filter) + public string Run(ITestEventListener? listener, string filter) { return string.Format(RUN_RESULT_FORMAT, _type, TestID, _name, _fullname, _runstate, _result, _label, _message); diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFile.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFile.cs index 6b090c318..8656de43c 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFile.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFile.cs @@ -31,19 +31,20 @@ public static AddinsFile Read(IFile file) /// All entries contained in the file. /// cannot be read /// If the executing system uses backslashes ('\') to separate directories, these will be substituted with slashes ('/'). - internal static AddinsFile Read(Stream stream, string fullName = null) + internal static AddinsFile Read(Stream stream, string fullName = "addins file") { using (var reader = new StreamReader(stream)) { var addinsFile = new AddinsFile(); int lineNumber = 0; - while (!reader.EndOfStream) + string? line; + while ((line = reader.ReadLine()) != null) { - var entry = new AddinsFileEntry(++lineNumber, reader.ReadLine()); + var entry = new AddinsFileEntry(++lineNumber, line); if (entry.Text != "" && !entry.IsValid) { - string msg = $"Invalid Entry in {fullName ?? "addins file"}:\r\n {entry}"; + string msg = $"Invalid Entry in {fullName}:\r\n {entry}"; throw new InvalidOperationException(msg); } @@ -64,7 +65,7 @@ public override string ToString() return sb.ToString(); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { var other = obj as AddinsFile; if (other == null) return false; diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFileEntry.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFileEntry.cs index 3bf6470d5..d7ecb2d07 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFileEntry.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/AddinsFileEntry.cs @@ -18,7 +18,7 @@ internal class AddinsFileEntry public bool IsPattern => Text.Contains("*"); public bool IsValid => PathUtils.IsValidPath(Text.Replace('*', 'X')); - public string DirectoryName => Path.GetDirectoryName(Text); + public string DirectoryName => Path.GetDirectoryName(Text)!; public string FileName => Path.GetFileName(Text); public AddinsFileEntry(int lineNumber, string rawText) @@ -34,7 +34,7 @@ public override string ToString() return $"{LineNumber}: {RawText}"; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { var other = obj as AddinsFileEntry; if (other == null) return false; diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs index 922038b2f..32e6b6cca 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs @@ -20,6 +20,7 @@ public ExtensionAssembly(string filePath, bool fromWildCard) AssemblyVersion = Assembly.Name.Version; } +#if ACTUALLY_USED // Internal constructor used for certain tests. AssemblyDefinition is not initialized. internal ExtensionAssembly(string filePath, bool fromWildCard, string assemblyName, Version version) { @@ -28,6 +29,7 @@ internal ExtensionAssembly(string filePath, bool fromWildCard, string assemblyNa AssemblyName = assemblyName; AssemblyVersion = version; } +#endif public string FilePath { get; } public bool FromWildCard { get; } diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs index 8163fa500..dad1e34bd 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs @@ -15,7 +15,7 @@ namespace NUnit.Engine.Extensibility { public class ExtensionManager { - static readonly Version CURRENT_ENGINE_VERSION = Assembly.GetExecutingAssembly().GetName().Version; + static readonly Version CURRENT_ENGINE_VERSION = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(); static readonly Logger log = InternalTrace.GetLogger(typeof(ExtensionManager)); @@ -30,7 +30,7 @@ public class ExtensionManager private readonly Dictionary _extensionPointIndex = new Dictionary(); // List of ExtensionNodes for all extensions discovered. - private List _extensions = new List(); + private readonly List _extensions = new List(); private bool _extensionsAreLoaded; @@ -88,7 +88,7 @@ public virtual void FindExtensionPoints(params Assembly[] targetAssemblies) { foreach (var assembly in targetAssemblies) { - log.Info("FindExtensionPoints scanning {0} assembly", assembly.GetName().Name); + log.Info("FindExtensionPoints scanning {0} assembly", assembly.GetName().Name!); foreach (ExtensionPointAttribute attr in assembly.GetCustomAttributes(typeof(ExtensionPointAttribute), false)) { @@ -175,7 +175,7 @@ public void FindExtensionAssemblies(Assembly hostAssembly) : new[] { "NUnit.Extension.*/**/tools/", "NUnit.Extension.*/**/tools/*/" }; - IDirectory startDir = _fileSystem.GetDirectory(AssemblyHelper.GetDirectoryName(hostAssembly)); + IDirectory? startDir = _fileSystem.GetDirectory(AssemblyHelper.GetDirectoryName(hostAssembly)); while (startDir != null) { @@ -190,9 +190,9 @@ public void FindExtensionAssemblies(Assembly hostAssembly) /// /// Get an ExtensionPoint based on its unique identifying path. /// - public IExtensionPoint GetExtensionPoint(string path) + public IExtensionPoint? GetExtensionPoint(string path) { - return _extensionPointIndex.TryGetValue(path, out ExtensionPoint ep) ? ep : null; + return _extensionPointIndex.TryGetValue(path, out ExtensionPoint? ep) ? ep : null; } /// @@ -222,7 +222,7 @@ public IEnumerable GetExtensionNodes(string path) /// /// The identifying path for an ExtensionPoint /// - public IExtensionNode GetExtensionNode(string path) + public IExtensionNode? GetExtensionNode(string path) { EnsureExtensionsAreLoaded(); @@ -264,7 +264,7 @@ public void EnableExtension(string typeName, bool enabled) /// /// Get an ExtensionPoint based on the required Type for extensions. /// - public ExtensionPoint GetExtensionPoint(Type type) + public ExtensionPoint? GetExtensionPoint(Type type) { foreach (var ep in _extensionPoints) if (ep.TypeName == type.FullName) @@ -276,7 +276,7 @@ public ExtensionPoint GetExtensionPoint(Type type) /// /// Get an ExtensionPoint based on a Cecil TypeReference. /// - public ExtensionPoint GetExtensionPoint(TypeReference type) + public ExtensionPoint? GetExtensionPoint(TypeReference type) { foreach (var ep in _extensionPoints) if (ep.TypeName == type.FullName) @@ -290,7 +290,7 @@ public ExtensionPoint GetExtensionPoint(TypeReference type) /// Returns null if no extension point can be found that would /// be satisfied by the provided Type. /// - private ExtensionPoint DeduceExtensionPointFromType(TypeReference typeRef) + private ExtensionPoint? DeduceExtensionPointFromType(TypeReference typeRef) { var ep = GetExtensionPoint(typeRef); if (ep != null) @@ -305,7 +305,7 @@ private ExtensionPoint DeduceExtensionPointFromType(TypeReference typeRef) return ep; } - TypeReference baseType = typeDef.BaseType; + TypeReference? baseType = typeDef.BaseType; return baseType != null && baseType.FullName != "System.Object" ? DeduceExtensionPointFromType(baseType) : null; @@ -426,7 +426,7 @@ private void ProcessCandidateAssembly(string filePath, bool fromWildCard) // Do we already have a copy of the same assembly at a different path? //if (_assemblies.ByName.ContainsKey(assemblyName)) - if (_assemblies.ByName.TryGetValue(assemblyName, out ExtensionAssembly existing)) + if (_assemblies.ByName.TryGetValue(assemblyName, out ExtensionAssembly? existing)) { if (candidateAssembly.IsBetterVersionOf(existing)) _assemblies.ByName[assemblyName] = candidateAssembly; @@ -449,7 +449,7 @@ private void ProcessCandidateAssembly(string filePath, bool fromWildCard) } // Dictionary containing all directory paths already visited. - private readonly Dictionary _visited = new Dictionary(); + private readonly Dictionary _visited = new Dictionary(); private bool WasVisited(string path, bool fromWildcard) { @@ -476,8 +476,9 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) return; } - IRuntimeFramework assemblyTargetFramework = null; + IRuntimeFramework? assemblyTargetFramework = null; #if NETFRAMEWORK +#if FIXED // Use special properties provided by our backport of RuntimeInformation Version currentVersion = RuntimeInformation.FrameworkVersion; var frameworkName = extensionAssembly.FrameworkName; @@ -494,6 +495,7 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) return; } } +#endif #endif foreach (var extensionType in extensionAssembly.Assembly.MainModule.GetTypes()) @@ -506,7 +508,7 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) // TODO: This is a remnant of older code. In principle, this should be generalized // to something like "HostVersion". However, this can safely remain until // we separate ExtensionManager into its own assembly. - string versionArg = extensionAttr.GetNamedArgument("EngineVersion") as string; + string? versionArg = extensionAttr.GetNamedArgument(nameof(ExtensionAttribute.EngineVersion)) as string; if (versionArg != null) { if (new Version(versionArg) > CURRENT_ENGINE_VERSION) @@ -516,21 +518,21 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) } } + string? extensionAttrPath = (string?)extensionAttr.GetNamedArgument(nameof(ExtensionAttribute.Path)); var node = new ExtensionNode(extensionAssembly.FilePath, extensionAssembly.AssemblyVersion, extensionType.FullName, assemblyTargetFramework) { - Path = extensionAttr.GetNamedArgument("Path") as string, - Description = extensionAttr.GetNamedArgument("Description") as string + Description = (string?)extensionAttr.GetNamedArgument(nameof(ExtensionAttribute.Description)), }; - object enabledArg = extensionAttr.GetNamedArgument("Enabled"); + object? enabledArg = extensionAttr.GetNamedArgument(nameof(ExtensionAttribute.Enabled)); node.Enabled = enabledArg == null || (bool)enabledArg; log.Info(" Found ExtensionAttribute on Type " + extensionType.Name); foreach (var attr in extensionType.GetAttributes("NUnit.Engine.Extensibility.ExtensionPropertyAttribute")) { - string name = attr.ConstructorArguments[0].Value as string; - string value = attr.ConstructorArguments[1].Value as string; + string? name = attr.ConstructorArguments[0].Value as string; + string? value = attr.ConstructorArguments[1].Value as string; if (name != null && value != null) { @@ -541,8 +543,8 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) _extensions.Add(node); - ExtensionPoint ep; - if (node.Path == null) + ExtensionPoint? ep; + if (extensionAttrPath == null) { ep = DeduceExtensionPointFromType(extensionType); if (ep == null) @@ -557,6 +559,8 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) } else { + node.Path = extensionAttrPath; + // TODO: Remove need for the cast ep = GetExtensionPoint(node.Path) as ExtensionPoint; if (ep == null) @@ -583,7 +587,7 @@ internal void FindExtensionsInAssembly(ExtensionAssembly extensionAssembly) /// /// The executing runner /// The extension we are attempting to load - internal bool CanLoadTargetFramework(Assembly runnerAsm, ExtensionAssembly extensionAsm) + internal bool CanLoadTargetFramework(Assembly? runnerAsm, ExtensionAssembly extensionAsm) { if (runnerAsm == null) return true; diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionNode.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionNode.cs index 031b2d914..ab4b12981 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionNode.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionNode.cs @@ -2,11 +2,8 @@ using System; using System.Collections.Generic; -using System.Reflection; - -#if !NETFRAMEWORK using System.Linq; -#endif +using System.Reflection; namespace NUnit.Engine.Extensibility { @@ -17,8 +14,8 @@ namespace NUnit.Engine.Extensibility /// public class ExtensionNode : IExtensionNode { - private object _extensionObject; - private Dictionary> _properties = new Dictionary>(); + private object? _extensionObject; + private readonly Dictionary> _properties = new Dictionary>(); /// @@ -28,7 +25,7 @@ public class ExtensionNode : IExtensionNode /// The version of the extension assembly. /// The full name of the Type of the extension object. /// The target framework of the extension assembly. - public ExtensionNode(string assemblyPath, Version assemblyVersion, string typeName, IRuntimeFramework targetFramework) + public ExtensionNode(string assemblyPath, Version assemblyVersion, string typeName, IRuntimeFramework? targetFramework) { AssemblyPath = assemblyPath; AssemblyVersion = assemblyVersion; @@ -45,7 +42,7 @@ public ExtensionNode(string assemblyPath, Version assemblyVersion, string typeNa /// /// The TargetFramework of the extension assembly. /// - public IRuntimeFramework TargetFramework { get; } + public IRuntimeFramework? TargetFramework { get; } /// /// Gets or sets a value indicating whether this is enabled. @@ -58,12 +55,12 @@ public ExtensionNode(string assemblyPath, Version assemblyVersion, string typeNa /// this Extension is intended. This identifier may be supplied by the attribute /// marking the extension or deduced by NUnit from the Type of the extension class. /// - public string Path { get; set; } + public string Path { get; set; } = string.Empty; // TODO: Set in non-public constructor and remove public setter. /// /// An optional description of what the extension does. /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Gets a collection of the names of all this extension's properties @@ -84,7 +81,7 @@ public IEnumerable GetValues(string name) if (_properties.ContainsKey(name)) return _properties[name]; else - return new string[0]; + return Enumerable.Empty(); } /// @@ -121,25 +118,20 @@ public object CreateExtensionObject(params object[] args) { #if !NETFRAMEWORK var assembly = Assembly.LoadFrom(AssemblyPath); - var typeinfo = assembly.DefinedTypes.FirstOrDefault(t => t.FullName == TypeName); - if (typeinfo == null) - { - return null; - } - return Activator.CreateInstance(typeinfo.AsType(), args); + var type = assembly.GetType(TypeName, throwOnError: true)!; + return Activator.CreateInstance(type, args)!; #else - return AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap(AssemblyPath, TypeName, false, 0, null, args, null, null); + return AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap(AssemblyPath, TypeName, false, 0, null, args, null, null)!; #endif } public void AddProperty(string name, string val) { - if (_properties.ContainsKey(name)) - _properties[name].Add(val); + if (_properties.TryGetValue(name, out List? list)) + list.Add(val); else { - var list = new List(); - list.Add(val); + list = new List { val }; _properties.Add(name, list); } } diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionPoint.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionPoint.cs index ef1937523..5f71a0a8f 100644 --- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionPoint.cs +++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionPoint.cs @@ -19,7 +19,7 @@ public class ExtensionPoint : IExtensionPoint public ExtensionPoint(string path, Type type) { Path = path; - TypeName = type.FullName; + TypeName = type.FullName!; Extensions = new List(); } @@ -31,7 +31,7 @@ public ExtensionPoint(string path, Type type) /// /// Gets and sets the optional description of this extension point. /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Gets the FullName of the Type required for any extension to be installed at this extension point. diff --git a/src/NUnitEngine/nunit.engine.core/Guard.cs b/src/NUnitEngine/nunit.engine.core/Guard.cs index 80da62849..4f7623bf3 100644 --- a/src/NUnitEngine/nunit.engine.core/Guard.cs +++ b/src/NUnitEngine/nunit.engine.core/Guard.cs @@ -1,6 +1,8 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace NUnit.Common { @@ -21,6 +23,22 @@ public static void ArgumentNotNull(object value, string name) throw new ArgumentNullException("Argument " + name + " must not be null", name); } + /// + /// Throws an exception if a result is null + /// + /// The type of the to check. + /// The value to check. + /// Compiler supplied parameter for the expression. + /// Value of if not null, throws otherwise. + /// + public static T ShouldNotBeNull(this T? result, [CallerArgumentExpression(nameof(result))] string expression = "") where T : class + { + if (result == null) + throw new InvalidOperationException($"Result {expression} must not be null"); + + return result; + } + /// /// Throws an exception if a string argument is null or empty /// @@ -40,7 +58,7 @@ public static void ArgumentNotNullOrEmpty(string value, string name) /// The condition that must be met /// The exception message to be used /// The name of the argument - public static void ArgumentInRange(bool condition, string message, string paramName) + public static void ArgumentInRange([DoesNotReturnIf(false)] bool condition, string message, string paramName) { if (!condition) throw new ArgumentOutOfRangeException(paramName, message); @@ -52,7 +70,7 @@ public static void ArgumentInRange(bool condition, string message, string paramN /// The condition that must be met /// The exception message to be used /// The name of the argument - public static void ArgumentValid(bool condition, string message, string paramName) + public static void ArgumentValid([DoesNotReturnIf(false)] bool condition, string message, string paramName) { if (!condition) throw new ArgumentException(message, paramName); @@ -63,7 +81,7 @@ public static void ArgumentValid(bool condition, string message, string paramNam /// /// The condition that must be met /// The exception message to be used - public static void OperationValid(bool condition, string message) + public static void OperationValid([DoesNotReturnIf(false)] bool condition, string message) { if (!condition) throw new InvalidOperationException(message); diff --git a/src/NUnitEngine/nunit.engine.core/Internal/AssemblyHelper.cs b/src/NUnitEngine/nunit.engine.core/Internal/AssemblyHelper.cs index df68aadff..d88494f98 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/AssemblyHelper.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/AssemblyHelper.cs @@ -18,7 +18,7 @@ public static class AssemblyHelper /// The path. public static string GetDirectoryName(Assembly assembly) { - return Path.GetDirectoryName(GetAssemblyPath(assembly)); + return Path.GetDirectoryName(GetAssemblyPath(assembly))!; } /// @@ -30,17 +30,24 @@ public static string GetDirectoryName(Assembly assembly) /// The path. public static string GetAssemblyPath(Assembly assembly) { - string codeBase = assembly.CodeBase; +#if NETFRAMEWORK + // https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.location + // .NET Framework only: + // If the loaded file was shadow-copied, the location is that of the file after being shadow-copied. + // To get the location before the file has been shadow-copied, use the CodeBase property. + string? codeBase = assembly.CodeBase; - if (IsFileUri(codeBase)) + if (codeBase != null && IsFileUri(codeBase)) return GetAssemblyPathFromCodeBase(codeBase); +#endif return assembly.Location; } +#if NETFRAMEWORK private static bool IsFileUri(string uri) { - return uri.ToLower().StartsWith(Uri.UriSchemeFile); + return uri.StartsWith(Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase); } /// @@ -69,5 +76,6 @@ public static string GetAssemblyPathFromCodeBase(string codeBase) return codeBase.Substring(start); } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs b/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs index cd3dedf3d..9ecb10b89 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs @@ -36,13 +36,14 @@ static RuntimeInformation() { FrameworkName = ".NET Framework"; + RegistryKey? key = null; switch (version.Major) { case 2: - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"); + key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"); if (key != null) { - string installRoot = key.GetValue("InstallRoot") as string; + string? installRoot = key.GetValue("InstallRoot") as string; if (installRoot != null) { if (Directory.Exists(System.IO.Path.Combine(installRoot, "v3.5"))) @@ -68,6 +69,8 @@ static RuntimeInformation() } break; } + + key?.Dispose(); } FrameworkVersion = version; diff --git a/src/NUnitEngine/nunit.engine.core/Internal/CallerArgumentExpressionAttribute.cs b/src/NUnitEngine/nunit.engine.core/Internal/CallerArgumentExpressionAttribute.cs new file mode 100644 index 000000000..f851c6a87 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Internal/CallerArgumentExpressionAttribute.cs @@ -0,0 +1,30 @@ +#if !NETCOREAPP + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates that a parameter captures the expression passed for another parameter as a string. + /// + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + internal sealed class CallerArgumentExpressionAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the parameter whose expression should be captured as a string. + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + + /// + /// Gets the name of the parameter whose expression should be captured as a string. + /// + public string ParameterName { get; } + } +} + +#endif diff --git a/src/NUnitEngine/nunit.engine.core/Internal/DomainDetailsBuilder.cs b/src/NUnitEngine/nunit.engine.core/Internal/DomainDetailsBuilder.cs index b7f2d91f3..a3e3d6691 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/DomainDetailsBuilder.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/DomainDetailsBuilder.cs @@ -22,7 +22,7 @@ internal static class DomainDetailsBuilder /// /// Application domain to get details on. /// An optional overall error message. - public static string DetailsFor(AppDomain domain, string errMsg = null) + public static string DetailsFor(AppDomain domain, string? errMsg = null) { var sb = new StringBuilder(); if (errMsg != null) sb.AppendLine(errMsg); diff --git a/src/NUnitEngine/nunit.engine.core/Internal/ExceptionHelper.cs b/src/NUnitEngine/nunit.engine.core/Internal/ExceptionHelper.cs index e336b7ac2..37e0f8ab7 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/ExceptionHelper.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/ExceptionHelper.cs @@ -2,10 +2,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Text; using NUnit.Engine; +#nullable enable + namespace NUnit.Common { internal static class ExceptionHelper @@ -65,13 +68,15 @@ public static string BuildMessageAndStackTrace(Exception exception) /// A string representation of the stack trace. private static string GetSafeStackTrace(Exception exception) { + const string NoStackTrace = "No stack trace available"; + try { - return exception.StackTrace; + return exception.StackTrace ?? NoStackTrace; } catch (Exception) { - return "No stack trace available"; + return NoStackTrace; } } @@ -89,11 +94,11 @@ private static List FlattenExceptionHierarchy(Exception exception) } var reflectionException = exception as ReflectionTypeLoadException; - if (reflectionException != null) + if (reflectionException != null && reflectionException.LoaderExceptions != null) { - result.AddRange(reflectionException.LoaderExceptions); + result.AddRange(reflectionException.LoaderExceptions.WhereNotNull()); - foreach (var innerException in reflectionException.LoaderExceptions) + foreach (var innerException in reflectionException.LoaderExceptions.WhereNotNull()) result.AddRange(FlattenExceptionHierarchy(innerException)); } @@ -119,5 +124,19 @@ private static string GetExceptionMessage(Exception ex) return ex.Message; } + + /// + /// Returns the specified sequence with all null references removed. + /// + /// The type of items in source. + /// An to filter. + /// + /// The sequence with all null references removed. + /// + private static IEnumerable WhereNotNull(this IEnumerable source) + where T : class + { + return source.Where(x => x is not null)!; + } } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/Directory.cs b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/Directory.cs index 1c0a74ad6..86864ca29 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/Directory.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/Directory.cs @@ -39,7 +39,7 @@ public Directory(string path) } /// - public IDirectory Parent { get; private set; } + public IDirectory? Parent { get; private set; } /// public string FullName => this.directory.FullName; diff --git a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/File.cs b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/File.cs index f14e12948..81a8a5cff 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/File.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/Default/File.cs @@ -1,6 +1,7 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System; +using NUnit.Common; using SIO = System.IO; namespace NUnit.Engine.Internal.FileSystemAccess.Default @@ -42,7 +43,7 @@ public File(string path) throw new ArgumentException("Filename contains invalid characters.", nameof(path)); } - var directory = SIO.Path.GetDirectoryName(path); + var directory = SIO.Path.GetDirectoryName(path)!; if (directory.IndexOfAny(SIO.Path.GetInvalidPathChars()) > -1) { throw new ArgumentException("Directory contains invalid characters.", nameof(path)); diff --git a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/IDirectory.cs b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/IDirectory.cs index 557fbc089..93cfc3e60 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/IDirectory.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/FileSystemAccess/IDirectory.cs @@ -14,7 +14,7 @@ public interface IDirectory /// Gets the parent directory. /// /// The parent directory or if the instance denotes a root (such as '\', 'c:\' , '\\server\share'). - IDirectory Parent { get; } + IDirectory? Parent { get; } /// /// Gets the full path of the directory. diff --git a/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTrace.cs b/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTrace.cs index c688bc0d1..2fc193110 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTrace.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTrace.cs @@ -1,6 +1,8 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System; +using System.Diagnostics.CodeAnalysis; +using NUnit.Common; namespace NUnit.Engine.Internal { @@ -21,13 +23,14 @@ namespace NUnit.Engine.Internal /// public static class InternalTrace { - private static InternalTraceWriter _traceWriter; + private static InternalTraceWriter? _traceWriter; public static InternalTraceLevel DefaultTraceLevel { get; private set; } /// /// Gets a flag indicating whether the InternalTrace is initialized /// + [MemberNotNullWhen(true, nameof(_traceWriter))] public static bool Initialized { get; private set; } /// @@ -60,7 +63,7 @@ public static void Initialize(string logName, InternalTraceLevel level) /// public static Logger GetLogger(string name, InternalTraceLevel level) { - return new Logger(name, () => level, () => _traceWriter); + return new Logger(name, () => level, () => _traceWriter.ShouldNotBeNull()); } /// @@ -68,7 +71,7 @@ public static Logger GetLogger(string name, InternalTraceLevel level) /// public static Logger GetLogger(Type type, InternalTraceLevel level) { - return GetLogger(type.FullName, level); + return GetLogger(type.FullName ?? type.Name, level); } /// @@ -76,7 +79,7 @@ public static Logger GetLogger(Type type, InternalTraceLevel level) /// public static Logger GetLogger(string name) { - return new Logger(name, () => DefaultTraceLevel, () => _traceWriter); + return new Logger(name, () => DefaultTraceLevel, () => _traceWriter.ShouldNotBeNull()); } /// @@ -84,7 +87,7 @@ public static Logger GetLogger(string name) /// public static Logger GetLogger(Type type) { - return GetLogger(type.FullName); + return GetLogger(type.FullName ?? type.Name); } } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTraceWriter.cs b/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTraceWriter.cs index e114ebfb3..3eec1f901 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTraceWriter.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/Logging/InternalTraceWriter.cs @@ -11,7 +11,7 @@ namespace NUnit.Engine.Internal internal class InternalTraceWriter : TextWriter { TextWriter _writer; - object _myLock = new object(); + readonly object _myLock = new object(); /// /// Construct an InternalTraceWriter that writes to a file. @@ -61,7 +61,7 @@ public override void Write(char value) /// Writes a string to the text string or stream. /// /// The string to write. - public override void Write(string value) + public override void Write(string? value) { lock (_myLock) { @@ -73,7 +73,7 @@ public override void Write(string value) /// Writes a string followed by a line terminator to the text string or stream. /// /// The string to write. If is null, only the line terminator is written. - public override void WriteLine(string value) + public override void WriteLine(string? value) { lock (_myLock) { @@ -93,7 +93,7 @@ protected override void Dispose(bool disposing) { _writer.Flush(); _writer.Dispose(); - _writer = null; + _writer = null!; } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs b/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs index 5c5aedf71..659388949 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs @@ -38,17 +38,19 @@ public static bool IsAssemblyFileType(string path) /// Returns the relative path from a base directory to another /// directory or file. /// - public static string RelativePath( string from, string to ) + public static string? RelativePath( string from, string to ) { if (from == null) throw new ArgumentNullException (from); if (to == null) throw new ArgumentNullException (to); - string toPathRoot = Path.GetPathRoot(to); + string? toPathRoot = Path.GetPathRoot(to); if (toPathRoot == null || toPathRoot == string.Empty) return to; - string fromPathRoot = Path.GetPathRoot(from); + string? fromPathRoot = Path.GetPathRoot(from); + if (fromPathRoot == null || fromPathRoot == string.Empty) + return null; if (!PathsEqual(toPathRoot, fromPathRoot)) return null; diff --git a/src/NUnitEngine/nunit.engine.core/Internal/ProcessUtils.cs b/src/NUnitEngine/nunit.engine.core/Internal/ProcessUtils.cs index 6655ca610..055d2c99e 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/ProcessUtils.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/ProcessUtils.cs @@ -34,7 +34,7 @@ public static class ProcessUtils /// Escapes arbitrary values so that the process receives the exact string you intend and injection is impossible. /// Spec: https://docs.microsoft.com/en-gb/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw /// - public static void EscapeProcessArgument(this StringBuilder builder, string literalValue, bool alwaysQuote = false) + public static void EscapeProcessArgument(this StringBuilder builder, string? literalValue, bool alwaysQuote = false) { if (string.IsNullOrEmpty(literalValue)) { diff --git a/src/NUnitEngine/nunit.engine.core/Internal/ProvidedPathsAssemblyResolver.cs b/src/NUnitEngine/nunit.engine.core/Internal/ProvidedPathsAssemblyResolver.cs index 803775e45..8b178e7bd 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/ProvidedPathsAssemblyResolver.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/ProvidedPathsAssemblyResolver.cs @@ -10,7 +10,7 @@ namespace NUnit.Engine.Internal { public class ProvidedPathsAssemblyResolver { - static ILogger log = InternalTrace.GetLogger(typeof(ProvidedPathsAssemblyResolver)); + static readonly ILogger log = InternalTrace.GetLogger(typeof(ProvidedPathsAssemblyResolver)); public ProvidedPathsAssemblyResolver() { @@ -34,7 +34,7 @@ public void AddPath(string dirPath) public void AddPathFromFile(string filePath) { - string dirPath = Path.GetDirectoryName(filePath); + string dirPath = Path.GetDirectoryName(filePath)!; AddPath(dirPath); } @@ -45,15 +45,15 @@ public void RemovePath(string dirPath) public void RemovePathFromFile(string filePath) { - string dirPath = Path.GetDirectoryName(filePath); + string dirPath = Path.GetDirectoryName(filePath)!; RemovePath(dirPath); } - Assembly AssemblyResolve(object sender, ResolveEventArgs args) + Assembly? AssemblyResolve(object? sender, ResolveEventArgs args) { foreach (string path in _resolutionPaths) { - string filename = new AssemblyName(args.Name).Name + ".dll"; + string filename = new AssemblyName(args.Name!).Name + ".dll"; string fullPath = Path.Combine(path, filename); try { @@ -71,6 +71,6 @@ Assembly AssemblyResolve(object sender, ResolveEventArgs args) return null; } - List _resolutionPaths; + readonly List _resolutionPaths; } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/ResultHelper.cs b/src/NUnitEngine/nunit.engine.core/Internal/ResultHelper.cs index 9e68d08da..574ceefb2 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/ResultHelper.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/ResultHelper.cs @@ -29,7 +29,7 @@ public static class ResultHelper /// The name of the . /// The full name of the . /// A TestEngineResult with a single top-level element. - public static TestEngineResult Aggregate(this TestEngineResult result, string elementName, string suiteType, string id, string name, string fullName) + public static TestEngineResult Aggregate(this TestEngineResult result, string elementName, string suiteType, string id, string? name, string? fullName) { return new TestEngineResult(Aggregate(elementName, suiteType, id, name, fullName, result.XmlNodes)); } @@ -43,7 +43,7 @@ public static TestEngineResult Aggregate(this TestEngineResult result, string el /// The name of the . /// The full name of the . /// A TestEngineResult with a single top-level element. - public static TestEngineResult Aggregate(this TestEngineResult result, string elementName, string id, string name, string fullName) + public static TestEngineResult Aggregate(this TestEngineResult result, string elementName, string id, string? name, string? fullName) { return new TestEngineResult(Aggregate(elementName, id, name, fullName, result.XmlNodes)); } @@ -96,7 +96,7 @@ public static TestEngineResult Merge(IList results) /// The full name to associated with the root node. /// A collection of XmlNodes to aggregate /// A single XmlNode containing the aggregated list of XmlNodes. - public static XmlNode Aggregate(string elementName, string id, string name, string fullName, IList resultNodes) + public static XmlNode Aggregate(string elementName, string id, string? name, string? fullName, IList resultNodes) { return Aggregate(elementName, null, id, name, fullName, resultNodes); } @@ -110,7 +110,7 @@ public static XmlNode Aggregate(string elementName, string id, string name, stri /// The full name to associated with the root node. /// A collection of XmlNodes to aggregate /// A single XmlNode containing the aggregated list of XmlNodes. - public static XmlNode Aggregate(string elementName, string testType, string id, string name, string fullName, IList resultNodes) + public static XmlNode Aggregate(string elementName, string? testType, string id, string? name, string? fullName, IList resultNodes) { XmlNode combinedNode = XmlHelper.CreateTopLevelElement(elementName); if (testType != null) @@ -123,8 +123,8 @@ public static XmlNode Aggregate(string elementName, string testType, string id, combinedNode.AddAttribute("runstate", "Runnable"); // If not, we would not have gotten this far string aggregateResult = "Inconclusive"; - string aggregateLabel = null; - string aggregateSite = null; + string? aggregateLabel = null; + string? aggregateSite = null; //double totalDuration = 0.0d; int testcasecount = 0; @@ -142,12 +142,12 @@ public static XmlNode Aggregate(string elementName, string testType, string id, { testcasecount += node.GetAttribute("testcasecount", 0); - XmlAttribute resultAttribute = node.Attributes["result"]; + XmlAttribute? resultAttribute = node.Attributes?["result"]; if (resultAttribute != null) { isTestRunResult = true; - string label = node.GetAttribute("label"); + string? label = node.GetAttribute("label"); switch (resultAttribute.Value) { @@ -183,8 +183,11 @@ public static XmlNode Aggregate(string elementName, string testType, string id, asserts += node.GetAttribute("asserts", 0); } - XmlNode import = combinedNode.OwnerDocument.ImportNode(node, true); - combinedNode.AppendChild(import); + if (combinedNode.OwnerDocument != null) + { + XmlNode import = combinedNode.OwnerDocument.ImportNode(node, true); + combinedNode.AppendChild(import); + } } combinedNode.AddAttribute("testcasecount", testcasecount.ToString()); diff --git a/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.ObservableServerChannelSinkProvider.cs b/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.ObservableServerChannelSinkProvider.cs index 3634cd237..750b9f084 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.ObservableServerChannelSinkProvider.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.ObservableServerChannelSinkProvider.cs @@ -32,7 +32,7 @@ public IServerChannelSink CreateSink(IChannelReceiver channel) return new ObservableServerChannelSink(_currentMessageCounter, Next.CreateSink(channel)); } - public IServerChannelSinkProvider Next { get; set; } + public IServerChannelSinkProvider? Next { get; set; } private sealed class ObservableServerChannelSink : IServerChannelSink diff --git a/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.cs b/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.cs index 22527ba5f..8eefcf4ae 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/TcpChannelUtils.cs @@ -28,7 +28,9 @@ public static partial class TcpChannelUtils /// The rate limit of the channel to create. /// An optional counter to provide the ability to wait for all current messages. /// A configured with the given name and port. - private static TcpChannel CreateTcpChannel(string name, int port, int limit, CurrentMessageCounter currentMessageCounter = null) +#pragma warning disable IDE0060 // Remove unused parameter + private static TcpChannel CreateTcpChannel(string name, int port, int limit, CurrentMessageCounter? currentMessageCounter = null) +#pragma warning restore IDE0060 // Remove unused parameter { var props = new Dictionary { @@ -58,7 +60,7 @@ private static TcpChannel CreateTcpChannel(string name, int port, int limit, Cur /// /// An optional counter to provide the ability to wait for all current messages. /// The specified or if it cannot be found and created. - public static TcpChannel GetTcpChannel(CurrentMessageCounter currentMessageCounter = null) + public static TcpChannel? GetTcpChannel(CurrentMessageCounter? currentMessageCounter = null) { return GetTcpChannel("", 0, 2, currentMessageCounter); } @@ -72,7 +74,7 @@ public static TcpChannel GetTcpChannel(CurrentMessageCounter currentMessageCount /// The port to use if the channel must be created. /// An optional counter to provide the ability to wait for all current messages. /// The specified or if it cannot be found and created. - public static TcpChannel GetTcpChannel(string name, int port, CurrentMessageCounter currentMessageCounter = null) + public static TcpChannel? GetTcpChannel(string name, int port, CurrentMessageCounter? currentMessageCounter = null) { return GetTcpChannel(name, port, 2, currentMessageCounter); } @@ -87,7 +89,7 @@ public static TcpChannel GetTcpChannel(string name, int port, CurrentMessageCoun /// The client connection limit or negative for the default. /// An optional counter to provide the ability to wait for all current messages. /// The specified or if it cannot be found and created. - public static TcpChannel GetTcpChannel(string name, int port, int limit, CurrentMessageCounter currentMessageCounter = null) + public static TcpChannel? GetTcpChannel(string name, int port, int limit, CurrentMessageCounter? currentMessageCounter = null) { var existingChannel = ChannelServices.GetChannel(name) as TcpChannel; if (existingChannel != null) return existingChannel; diff --git a/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs b/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs index c9d9661cb..54236f7b2 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs @@ -22,14 +22,14 @@ internal sealed class TestAssemblyLoadContext : AssemblyLoadContext public TestAssemblyLoadContext(string testAssemblyPath) { _resolver = new TestAssemblyResolver(this, testAssemblyPath); - _basePath = Path.GetDirectoryName(testAssemblyPath); + _basePath = Path.GetDirectoryName(testAssemblyPath)!; _runtimeResolver = new AssemblyDependencyResolver(testAssemblyPath); #if NET8_0_OR_GREATER AppContext.SetData("APP_CONTEXT_BASE_DIRECTORY", _basePath); #endif } - protected override Assembly Load(AssemblyName name) + protected override Assembly? Load(AssemblyName name) { log.Debug("Loading {0} assembly", name); @@ -78,7 +78,7 @@ protected override Assembly Load(AssemblyName name) return loadedAssembly; } - return loadedAssembly; + return null; } protected override IntPtr LoadUnmanagedDll(string name) @@ -92,7 +92,7 @@ protected override IntPtr LoadUnmanagedDll(string name) return loadedDllHandle; } - string runtimeResolverPath = _runtimeResolver.ResolveUnmanagedDllToPath(name); + string? runtimeResolverPath = _runtimeResolver.ResolveUnmanagedDllToPath(name); if (string.IsNullOrEmpty(runtimeResolverPath) == false && File.Exists(runtimeResolverPath)) { @@ -101,7 +101,7 @@ protected override IntPtr LoadUnmanagedDll(string name) if (loadedDllHandle != IntPtr.Zero) { - log.Info("Unmanaged DLL {0} ({1}) is loaded using the deps.json info", name, runtimeResolverPath); + log.Info("Unmanaged DLL {0} ({1}) is loaded using the deps.json info", name, runtimeResolverPath!); return loadedDllHandle; } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyResolver.cs b/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyResolver.cs index 60e71efeb..18ef04c96 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyResolver.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyResolver.cs @@ -5,8 +5,10 @@ using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel.Resolution; using Microsoft.Win32; +using NUnit.Common; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -31,7 +33,7 @@ internal sealed class TestAssemblyResolver : IDisposable static TestAssemblyResolver() { - INSTALL_DIR = GetDotNetInstallDirectory(); + INSTALL_DIR = GetDotNetInstallDirectory().ShouldNotBeNull(); WINDOWS_DESKTOP_DIR = Path.Combine(INSTALL_DIR, "shared", "Microsoft.WindowsDesktop.App"); ASP_NET_CORE_DIR = Path.Combine(INSTALL_DIR, "shared", "Microsoft.AspNetCore.App"); } @@ -45,6 +47,7 @@ public TestAssemblyResolver(AssemblyLoadContext loadContext, string testAssembly _loadContext.Resolving += OnResolving; } + [MemberNotNull(nameof(ResolutionStrategies))] private void InitializeResolutionStrategies(AssemblyLoadContext loadContext, string testAssemblyPath) { // First, looking only at direct references by the test assembly, try to determine if @@ -82,16 +85,16 @@ public void Dispose() _loadContext.Resolving -= OnResolving; } - public Assembly Resolve(AssemblyLoadContext context, AssemblyName assemblyName) + public Assembly? Resolve(AssemblyLoadContext context, AssemblyName assemblyName) { return OnResolving(context, assemblyName); } - private Assembly OnResolving(AssemblyLoadContext loadContext, AssemblyName assemblyName) + private Assembly? OnResolving(AssemblyLoadContext loadContext, AssemblyName assemblyName) { if (loadContext == null) throw new ArgumentNullException("context"); - Assembly loadedAssembly; + Assembly? loadedAssembly; foreach (var strategy in ResolutionStrategies) if (strategy.TryToResolve(loadContext, assemblyName, out loadedAssembly)) return loadedAssembly; @@ -105,19 +108,19 @@ private Assembly OnResolving(AssemblyLoadContext loadContext, AssemblyName assem public abstract class ResolutionStrategy { public abstract bool TryToResolve( - AssemblyLoadContext loadContext, AssemblyName assemblyName, out Assembly loadedAssembly); + AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly); } public class TrustedPlatformAssembliesStrategy : ResolutionStrategy { public override bool TryToResolve( - AssemblyLoadContext loadContext, AssemblyName assemblyName, out Assembly loadedAssembly) + AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly) { return TryLoadFromTrustedPlatformAssemblies(loadContext, assemblyName, out loadedAssembly); } private static bool TryLoadFromTrustedPlatformAssemblies( - AssemblyLoadContext loadContext, AssemblyName assemblyName, out Assembly loadedAssembly) + AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly) { // https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing loadedAssembly = null; @@ -149,7 +152,7 @@ bool FileMatchesAssembly(string fileName) => public class RuntimeLibrariesStrategy : ResolutionStrategy { - private DependencyContext _dependencyContext; + private DependencyContext? _dependencyContext; private readonly ICompilationAssemblyResolver _assemblyResolver; public RuntimeLibrariesStrategy(AssemblyLoadContext loadContext, string testAssemblyPath) @@ -158,15 +161,22 @@ public RuntimeLibrariesStrategy(AssemblyLoadContext loadContext, string testAsse _assemblyResolver = new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[] { - new AppBaseCompilationAssemblyResolver(Path.GetDirectoryName(testAssemblyPath)), + new AppBaseCompilationAssemblyResolver(Path.GetDirectoryName(testAssemblyPath)!), new ReferenceAssemblyPathResolver(), new PackageCompilationAssemblyResolver() }); } public override bool TryToResolve( - AssemblyLoadContext loadContext, AssemblyName assemblyName, out Assembly loadedAssembly) + AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly) { + if (_dependencyContext == null) + { + // TODO: Is this the intended behavior? + loadedAssembly = null; + return false; + } + foreach (var library in _dependencyContext.RuntimeLibraries) { var wrapper = new CompilationLibrary( @@ -211,7 +221,7 @@ public AdditionalDirectoryStrategy(string frameworkDirectory) } public override bool TryToResolve( - AssemblyLoadContext loadContext, AssemblyName assemblyName, out Assembly loadedAssembly) + AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly) { loadedAssembly = null; if (assemblyName.Version == null) @@ -248,27 +258,27 @@ public override bool TryToResolve( #region HelperMethods - private static string GetDotNetInstallDirectory() + private static string? GetDotNetInstallDirectory() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Running on Windows so use registry if (Environment.Is64BitProcess) { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\"); - return (string)key?.GetValue("Path"); + using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\")) + return (string?)key?.GetValue("Path"); } else { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\"); - return (string)key?.GetValue("InstallLocation"); + using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\")) + return (string?)key?.GetValue("InstallLocation"); } } else return "/usr/shared/dotnet/"; } - private static string FindBestVersionDir(string libraryDir, Version targetVersion) + private static string? FindBestVersionDir(string libraryDir, Version targetVersion) { string target = targetVersion.ToString(); Version bestVersion = new Version(0, 0); diff --git a/src/NUnitEngine/nunit.engine.core/Internal/XmlHelper.cs b/src/NUnitEngine/nunit.engine.core/Internal/XmlHelper.cs index 72bc5da1f..25c97b8b1 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/XmlHelper.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/XmlHelper.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.Xml; +using NUnit.Common; namespace System.Runtime.CompilerServices { @@ -27,14 +28,14 @@ public static XmlNode CreateTopLevelElement(string name) { XmlDocument doc = new XmlDocument(); doc.LoadXml( "<" + name + "/>" ); - return doc.FirstChild; + return doc.FirstChild.ShouldNotBeNull(); } public static XmlNode CreateXmlNode(string xml) { XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); - return doc.FirstChild; + return doc.FirstChild.ShouldNotBeNull(); } /// @@ -45,9 +46,11 @@ public static XmlNode CreateXmlNode(string xml) /// The value of the attribute. public static void AddAttribute(this XmlNode node, string name, string value) { - XmlAttribute attr = node.OwnerDocument.CreateAttribute(name); + XmlAttribute attr = node.OwnerDocument.ShouldNotBeNull().CreateAttribute(name); attr.Value = value; - node.Attributes.Append(attr); + + // TODO: How to create the Attributes collection if it doesn't exist? + node.Attributes!.Append(attr); } /// @@ -58,7 +61,7 @@ public static void AddAttribute(this XmlNode node, string name, string value) /// The newly created child element public static XmlNode AddElement(this XmlNode node, string name) { - XmlNode childNode = node.OwnerDocument.CreateElement(name); + XmlNode childNode = node.OwnerDocument.ShouldNotBeNull().CreateElement(name); node.AppendChild(childNode); return childNode; } @@ -74,7 +77,7 @@ public static XmlNode AddElement(this XmlNode node, string name) public static XmlNode AddElementWithCDataSection(this XmlNode node, string name, string data) { XmlNode childNode = node.AddElement(name); - childNode.AppendChild(node.OwnerDocument.CreateCDataSection(data)); + childNode.AppendChild(node.OwnerDocument.ShouldNotBeNull().CreateCDataSection(data)); return childNode; } @@ -84,9 +87,9 @@ public static XmlNode AddElementWithCDataSection(this XmlNode node, string name, /// The result. /// The name. /// - public static string GetAttribute(this XmlNode result, string name) + public static string? GetAttribute(this XmlNode result, string name) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? null : attr.Value; } @@ -100,7 +103,7 @@ public static string GetAttribute(this XmlNode result, string name) /// public static int GetAttribute(this XmlNode result, string name, int defaultValue) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? defaultValue @@ -116,7 +119,7 @@ public static int GetAttribute(this XmlNode result, string name, int defaultValu /// public static double GetAttribute(this XmlNode result, string name, double defaultValue) { - XmlAttribute attr = result.Attributes[name]; + XmlAttribute? attr = result.Attributes?[name]; return attr == null ? defaultValue @@ -132,7 +135,7 @@ public static double GetAttribute(this XmlNode result, string name, double defau /// public static DateTime GetAttribute(this XmlNode result, string name, DateTime defaultValue) { - string dateStr = GetAttribute(result, name); + string? dateStr = GetAttribute(result, name); if (dateStr == null) return defaultValue; diff --git a/src/NUnitEngine/nunit.engine.core/RunTestsCallbackHandler.cs b/src/NUnitEngine/nunit.engine.core/RunTestsCallbackHandler.cs index 704c81c24..1a25bde54 100644 --- a/src/NUnitEngine/nunit.engine.core/RunTestsCallbackHandler.cs +++ b/src/NUnitEngine/nunit.engine.core/RunTestsCallbackHandler.cs @@ -9,18 +9,18 @@ namespace NUnit.Engine { public class RunTestsCallbackHandler : MarshalByRefObject, ICallbackEventHandler { - private ITestEventListener _listener; + private readonly ITestEventListener _listener; - public string Result { get; private set; } + public string? Result { get; private set; } - public RunTestsCallbackHandler(ITestEventListener listener) + public RunTestsCallbackHandler(ITestEventListener? listener) { _listener = listener ?? new NullListener(); } public override object InitializeLifetimeService() { - return null; + return null!; } public string GetCallbackResult() diff --git a/src/NUnitEngine/nunit.engine.core/Runners/DomainManager.cs b/src/NUnitEngine/nunit.engine.core/Runners/DomainManager.cs index 567326fa7..55aff9dbd 100644 --- a/src/NUnitEngine/nunit.engine.core/Runners/DomainManager.cs +++ b/src/NUnitEngine/nunit.engine.core/Runners/DomainManager.cs @@ -12,6 +12,7 @@ using System.Security.Principal; using NUnit.Common; using NUnit.Engine.Internal; +using System.Linq; namespace NUnit.Engine.Runners { @@ -21,10 +22,10 @@ namespace NUnit.Engine.Runners /// public class DomainManager { - static Logger log = InternalTrace.GetLogger(typeof(DomainManager)); + static readonly Logger log = InternalTrace.GetLogger(typeof(DomainManager)); private static readonly PropertyInfo TargetFrameworkNameProperty = - typeof(AppDomainSetup).GetProperty("TargetFrameworkName", BindingFlags.Public | BindingFlags.Instance); + typeof(AppDomainSetup).GetProperty("TargetFrameworkName", BindingFlags.Public | BindingFlags.Instance)!; /// /// Construct an application domain for running a test package @@ -67,7 +68,7 @@ AppDomainSetup CreateAppDomainSetup(TestPackage package) //For parallel tests, we need to use distinct application name setup.ApplicationName = "Tests" + "_" + Environment.TickCount; - string appBase = GetApplicationBase(package); + string appBase = GetApplicationBase(package).ShouldNotBeNull(); setup.ApplicationBase = appBase; setup.ConfigurationFile = GetConfigFile(appBase, package); setup.PrivateBinPath = GetPrivateBinPath(appBase, package); @@ -108,8 +109,8 @@ public void Unload(AppDomain domain) class DomainUnloader { private readonly AppDomain _domain; - private Thread _unloadThread; - private NUnitEngineException _unloadException; + private Thread? _unloadThread; + private NUnitEngineException? _unloadException; public DomainUnloader(AppDomain domain) { @@ -123,7 +124,7 @@ public void Unload() var timeout = TimeSpan.FromSeconds(30); - if (!_unloadThread.Join((int)timeout.TotalMilliseconds)) + if (!_unloadThread.Join(timeout)) { var msg = DomainDetailsBuilder.DetailsFor(_domain, $"Unable to unload application domain: unload thread timed out after {timeout.TotalSeconds} seconds."); @@ -168,7 +169,7 @@ private void UnloadOnThread() /// /// The package /// The ApplicationBase - public static string GetApplicationBase(TestPackage package) + public static string? GetApplicationBase(TestPackage package) { Guard.ArgumentNotNull(package, "package"); @@ -189,7 +190,7 @@ public static string GetApplicationBase(TestPackage package) return appBase; } - public static string GetConfigFile(string appBase, TestPackage package) + public static string? GetConfigFile(string appBase, TestPackage package) { Guard.ArgumentNotNullOrEmpty(appBase, "appBase"); Guard.ArgumentNotNull(package, "package"); @@ -202,7 +203,7 @@ public static string GetConfigFile(string appBase, TestPackage package) // The ProjectService adds any project config to the settings. // So, at this point, we only want to handle assemblies or an // anonymous package created from the command-line. - string fullName = package.FullName; + string? fullName = package.FullName; if (IsExecutable(fullName)) return fullName + ".config"; @@ -218,7 +219,7 @@ public static string GetConfigFile(string appBase, TestPackage package) return null; } - private static bool IsExecutable(string fileName) + private static bool IsExecutable(string? fileName) { if (string.IsNullOrEmpty(fileName)) return false; @@ -227,37 +228,39 @@ private static bool IsExecutable(string fileName) return ext == ".dll" || ext == ".exe"; } - public static string GetCommonAppBase(IList packages) + public static string? GetCommonAppBase(IList packages) { var assemblies = new List(); - foreach (var package in packages) - assemblies.Add(package.FullName); + + // All subpackages have full names, but this is a public method in a public class so we have no control. + foreach (var package in packages.Where(p => p.FullName != null)) + assemblies.Add(package.FullName!); return GetCommonAppBase(assemblies); } - public static string GetCommonAppBase(IList assemblies) + public static string? GetCommonAppBase(IList assemblies) { - string commonBase = null; + string? commonBase = null; foreach (string assembly in assemblies) { - string dir = Path.GetDirectoryName(Path.GetFullPath(assembly)); + string? dir = Path.GetDirectoryName(Path.GetFullPath(assembly))!; if (commonBase == null) commonBase = dir; - else while (!PathUtils.SamePathOrUnder(commonBase, dir) && commonBase != null) - commonBase = Path.GetDirectoryName(commonBase); + else while (commonBase != null && !PathUtils.SamePathOrUnder(commonBase, dir)) + commonBase = Path.GetDirectoryName(commonBase)!; } return commonBase; } - public static string GetPrivateBinPath(string basePath, string fileName) + public static string? GetPrivateBinPath(string basePath, string fileName) { return GetPrivateBinPath(basePath, new string[] { fileName }); } - public static string GetPrivateBinPath(string appBase, TestPackage package) + public static string? GetPrivateBinPath(string appBase, TestPackage package) { var binPath = package.GetSetting(EnginePackageSettings.PrivateBinPath, string.Empty); @@ -271,25 +274,25 @@ public static string GetPrivateBinPath(string appBase, TestPackage package) return binPath; } - public static string GetPrivateBinPath(string basePath, IList packages) + public static string? GetPrivateBinPath(string basePath, IList packages) { var assemblies = new List(); - foreach (var package in packages) - assemblies.Add(package.FullName); + foreach (var package in packages.Where(p => p.FullName != null)) + assemblies.Add(package.FullName!); return GetPrivateBinPath(basePath, assemblies); } - public static string GetPrivateBinPath(string basePath, IList assemblies) + public static string? GetPrivateBinPath(string basePath, IList assemblies) { List dirList = new List(); StringBuilder sb = new StringBuilder(200); foreach( string assembly in assemblies ) { - string dir = PathUtils.RelativePath( + string? dir = PathUtils.RelativePath( Path.GetFullPath(basePath), - Path.GetDirectoryName( Path.GetFullPath(assembly) ) ); + Path.GetDirectoryName( Path.GetFullPath(assembly) )! ); if ( dir != null && dir != string.Empty && dir != "." && !dirList.Contains( dir ) ) { dirList.Add( dir ); diff --git a/src/NUnitEngine/nunit.engine.core/Runners/NotRunnableTestRunner.cs b/src/NUnitEngine/nunit.engine.core/Runners/NotRunnableTestRunner.cs index 86ec11a13..a65b2d0f4 100644 --- a/src/NUnitEngine/nunit.engine.core/Runners/NotRunnableTestRunner.cs +++ b/src/NUnitEngine/nunit.engine.core/Runners/NotRunnableTestRunner.cs @@ -31,16 +31,18 @@ public abstract class NotRunnableTestRunner : ITestEngineRunner "" + ""; - private string _name; - private string _fullname; - private string _message; - private string _type; + private readonly string _name; + private readonly string _fullname; + private readonly string _message; + private readonly string _type; protected string _runstate; protected string _result; protected string _label; +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public NotRunnableTestRunner(string assemblyPath, string message) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. { _name = Escape(Path.GetFileName(assemblyPath)); _fullname = Escape(Path.GetFullPath(assemblyPath)); diff --git a/src/NUnitEngine/nunit.engine.core/Runners/TestAgentRunner.cs b/src/NUnitEngine/nunit.engine.core/Runners/TestAgentRunner.cs index 3e0bd330f..d32bd7766 100644 --- a/src/NUnitEngine/nunit.engine.core/Runners/TestAgentRunner.cs +++ b/src/NUnitEngine/nunit.engine.core/Runners/TestAgentRunner.cs @@ -19,12 +19,12 @@ public abstract class TestAgentRunner : ITestEngineRunner { private readonly List _drivers = new List(); - private readonly ProvidedPathsAssemblyResolver _assemblyResolver; + private readonly ProvidedPathsAssemblyResolver? _assemblyResolver; - protected AppDomain TestDomain { get; set; } + protected AppDomain? TestDomain { get; set; } // Used to inject DriverService for testing - internal IDriverService DriverService { get; set; } + internal IDriverService? DriverService { get; set; } /// /// The TestPackage for which this is the runner @@ -34,7 +34,7 @@ public abstract class TestAgentRunner : ITestEngineRunner /// /// The result of the last call to Load /// - protected TestEngineResult LoadResult { get; set; } + protected TestEngineResult? LoadResult { get; set; } /// /// Gets an indicator of whether the package has been loaded. @@ -100,6 +100,9 @@ public TestEngineResult Explore(TestFilter filter) /// A TestEngineResult. public virtual TestEngineResult Load() { + Guard.OperationValid(TestDomain != null, "TestDomain is not set"); + AppDomain testDomain = TestDomain; + var result = new TestEngineResult(); // DirectRunner may be called with a single-assembly package, @@ -115,12 +118,12 @@ public virtual TestEngineResult Load() foreach (var subPackage in packagesToLoad) { - var testFile = subPackage.FullName; + var testFile = subPackage.FullName!; // We know it's an assembly - string targetFramework = subPackage.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, (string)null); + string? targetFramework = subPackage.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, (string?)null); bool skipNonTestAssemblies = subPackage.GetSetting(EnginePackageSettings.SkipNonTestAssemblies, false); - if (_assemblyResolver != null && !TestDomain.IsDefaultAppDomain() + if (_assemblyResolver != null && !testDomain.IsDefaultAppDomain() && subPackage.GetSetting(InternalEnginePackageSettings.ImageRequiresDefaultAppDomainAssemblyResolver, false)) { // It's OK to do this in the loop because the Add method @@ -128,7 +131,7 @@ public virtual TestEngineResult Load() _assemblyResolver.AddPathFromFile(testFile); } - IFrameworkDriver driver = DriverService.GetDriver(TestDomain, testFile, targetFramework, skipNonTestAssemblies); + IFrameworkDriver driver = DriverService.GetDriver(testDomain, testFile, targetFramework, skipNonTestAssemblies); driver.ID = subPackage.ID; result.Add(LoadDriver(driver, testFile, subPackage)); @@ -146,7 +149,7 @@ private static string LoadDriver(IFrameworkDriver driver, string testFile, TestP { return driver.Load(testFile, subPackage.Settings); } - catch (Exception ex) when (!(ex is NUnitEngineException)) + catch (Exception ex) when (ex is not NUnitEngineException) { throw new NUnitEngineException("An exception occurred in the driver while loading tests.", ex); } @@ -188,7 +191,7 @@ public int CountTestCases(TestFilter filter) /// /// A TestEngineResult giving the result of the test execution /// - public TestEngineResult Run(ITestEventListener listener, TestFilter filter) + public TestEngineResult Run(ITestEventListener? listener, TestFilter filter) { EnsurePackageIsLoaded(); @@ -213,13 +216,13 @@ public TestEngineResult Run(ITestEventListener listener, TestFilter filter) if (_assemblyResolver != null) { foreach (var package in TestPackage.Select(p => p.IsAssemblyPackage())) - _assemblyResolver.RemovePathFromFile(package.FullName); + _assemblyResolver.RemovePathFromFile(package.FullName!); // IsAssemblyPackage guarantees FullName is not null } return result; } - public AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter filter) + public AsyncTestEngineResult RunAsync(ITestEventListener? listener, TestFilter filter) { var testRun = new AsyncTestEngineResult(); diff --git a/src/NUnitEngine/nunit.engine.core/Runners/TestDomainRunner.cs b/src/NUnitEngine/nunit.engine.core/Runners/TestDomainRunner.cs index 9087d6281..b72026ed0 100644 --- a/src/NUnitEngine/nunit.engine.core/Runners/TestDomainRunner.cs +++ b/src/NUnitEngine/nunit.engine.core/Runners/TestDomainRunner.cs @@ -9,7 +9,7 @@ namespace NUnit.Engine.Runners /// public class TestDomainRunner : TestAgentRunner { - private DomainManager _domainManager; + private readonly DomainManager _domainManager; public TestDomainRunner(TestPackage package) : base(package) { diff --git a/src/NUnitEngine/nunit.engine.core/TestEngineResult.cs b/src/NUnitEngine/nunit.engine.core/TestEngineResult.cs index de1419163..ddcb5220f 100644 --- a/src/NUnitEngine/nunit.engine.core/TestEngineResult.cs +++ b/src/NUnitEngine/nunit.engine.core/TestEngineResult.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Reflection; using System.Xml; +using NUnit.Common; using NUnit.Engine.Internal; namespace NUnit.Engine @@ -29,7 +30,7 @@ namespace NUnit.Engine [Serializable] public class TestEngineResult { - private List _xmlText = new List(); + private readonly List _xmlText = new List(); [NonSerialized] private List _xmlNodes = new List(); @@ -84,7 +85,7 @@ public IList XmlNodes { XmlDocument doc = new XmlDocument(); doc.LoadXml(_xmlText[i]); - _xmlNodes.Add(doc.FirstChild); + _xmlNodes.Add(doc.FirstChild.ShouldNotBeNull()); } return _xmlNodes; diff --git a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj index 822f4d2be..5f6cc8ac1 100644 --- a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj +++ b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj @@ -4,13 +4,15 @@ NUnit.Engine net462;netcoreapp3.1;net6.0;net8.0 ../../../bin/$(Configuration) - $(NoWarn);SYSLIB0011;SYSLIB0012 + $(NoWarn);SYSLIB0011 true ..\..\nunit.snk portable true + + NUnit Engine ($(TargetFramework)) NUnit Engine Core diff --git a/src/NUnitEngine/nunit.engine.tests/Api/ServiceLocatorTests.cs b/src/NUnitEngine/nunit.engine.tests/Api/ServiceLocatorTests.cs index 32de01907..9b48e2c9d 100644 --- a/src/NUnitEngine/nunit.engine.tests/Api/ServiceLocatorTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Api/ServiceLocatorTests.cs @@ -26,7 +26,7 @@ public void TearDown() [TestCase(typeof(ITestRunnerFactory))] public void CanAccessService(Type serviceType) { - IService service = _testEngine.Services.GetService(serviceType) as IService; + IService? service = _testEngine.Services.GetService(serviceType) as IService; Assert.That(service, Is.Not.Null, "GetService(Type) returned null"); Assert.That(service, Is.InstanceOf(serviceType)); Assert.That(service.Status, Is.EqualTo(ServiceStatus.Started)); diff --git a/src/NUnitEngine/nunit.engine.tests/Helpers/StackEnumerator.cs b/src/NUnitEngine/nunit.engine.tests/Helpers/StackEnumerator.cs index a1d7470b4..e9b9e635f 100644 --- a/src/NUnitEngine/nunit.engine.tests/Helpers/StackEnumerator.cs +++ b/src/NUnitEngine/nunit.engine.tests/Helpers/StackEnumerator.cs @@ -49,11 +49,11 @@ public void Recurse(params T[] newCurrent) Recurse((IEnumerable)newCurrent); } - public StackEnumerator(IEnumerator initial) + public StackEnumerator(IEnumerator? initial) { current = initial ?? Enumerable.Empty().GetEnumerator(); } - public StackEnumerator(IEnumerable initial) : this(initial?.GetEnumerator()) + public StackEnumerator(IEnumerable? initial) : this(initial?.GetEnumerator()) { } public StackEnumerator(params T[] initial) : this((IEnumerable)initial) diff --git a/src/NUnitEngine/nunit.engine.tests/Runners/WorkItemTrackerTests.cs b/src/NUnitEngine/nunit.engine.tests/Runners/WorkItemTrackerTests.cs index 68acce754..a0ff5329f 100644 --- a/src/NUnitEngine/nunit.engine.tests/Runners/WorkItemTrackerTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Runners/WorkItemTrackerTests.cs @@ -17,7 +17,8 @@ public class WorkItemTrackerTests : ITestEventListener [SetUp] public void CreateTracker() { - _listener = _tracker = new WorkItemTracker(); + _tracker = new WorkItemTracker(); + _listener = _tracker; _pendingNotices = new List(); } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.cs index 4600e4eb9..6e5c6e2c2 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.cs @@ -15,6 +15,12 @@ public static partial class AgentStoreTests private static readonly Guid DummyAgentId = Guid.NewGuid(); private static readonly ITestAgent DummyAgent = new DummyTestAgent(DummyAgentId); + [OneTimeTearDown] + public static void OneTimeTearDown() + { + DummyProcess.Dispose(); + } + [Test] public static void IdCannotBeReused() { diff --git a/src/NUnitEngine/nunit.engine.tests/Services/FakeProjectService.cs b/src/NUnitEngine/nunit.engine.tests/Services/FakeProjectService.cs index 686e00478..aafd92ff3 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/FakeProjectService.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/FakeProjectService.cs @@ -24,9 +24,14 @@ public void Add(string projectName, params string[] assemblies) void IProjectService.ExpandProjectPackage(TestPackage package) { - if (_projects.ContainsKey(package.Name)) + if (package.Name == null) { - foreach (string assembly in _projects[package.Name]) + throw new ArgumentException("Package must have a name", nameof(package)); + } + + if (_projects.TryGetValue(package.Name, out string[]? projects)) + { + foreach (string assembly in projects) package.AddSubPackage(new TestPackage(assembly)); } } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/FakeService.cs b/src/NUnitEngine/nunit.engine.tests/Services/FakeService.cs index e72f5377a..ebe408c30 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/FakeService.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/FakeService.cs @@ -9,7 +9,7 @@ interface IFakeService public class FakeService : IFakeService, IService { - IServiceLocator IService.ServiceContext { get; set; } + IServiceLocator? IService.ServiceContext { get; set; } private ServiceStatus _status; ServiceStatus IService.Status diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ProjectServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ProjectServiceTests.cs index 9c3cf4cd5..6c83c3c7f 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ProjectServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ProjectServiceTests.cs @@ -21,6 +21,12 @@ public void CreateServiceContext() services.ServiceManager.StartServices(); } + [TearDown] + public void StopServices() + { + _projectService.Dispose(); + } + [Test] public void ServiceIsStarted() { diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs index f89cb9f36..7d8d41cfc 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs @@ -21,6 +21,13 @@ public void CreateService() services.ServiceManager.StartServices(); } + [TearDown] + public void StopService() + { + _resultService.StopService(); + _resultService.Dispose(); + } + [Test] public void ServiceIsStarted() { @@ -37,7 +44,7 @@ public void AvailableFormats() //[TestCase("nunit2", null, ExpectedResult = "NUnit2XmlResultWriter")] [TestCase("cases", null, ExpectedResult = "TestCaseResultWriter")] //[TestCase("user", new object[] { "TextSummary.xslt" }, ExpectedResult = "XmlTransformResultWriter")] - public string CanGetWriter(string format, object[] args) + public string CanGetWriter(string format, object[]? args) { var writer = _resultService.GetResultWriter(format, args); diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ResultWriters/XmlTransformResultWriterTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ResultWriters/XmlTransformResultWriterTests.cs index 56b8af55a..b97c59fc5 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ResultWriters/XmlTransformResultWriterTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ResultWriters/XmlTransformResultWriterTests.cs @@ -11,7 +11,10 @@ namespace NUnit.Engine.Services.ResultWriters [Ignore("Temporarily ignoring this fixture")] public class XmlTransformResultWriterTests { +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + // NUnit.Analyzer DiagnosticSuppressor doesn't check inside using statement because it cannot check if the returned item is still valid. private XmlNode _engineResult; +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private const string AssemblyName = "mock-assembly.dll"; [SetUp] @@ -43,13 +46,15 @@ public void SummaryTransformTest() StringWriter writer = new StringWriter(); new XmlTransformResultWriter(new object[] { transformPath }).WriteResultFile(_engineResult, writer); + Assert.That(_engineResult.Attributes, Is.Not.Null); + string summary = string.Format( "Tests Run: {0}, Passed: {1}, Failed: {2}, Inconclusive: {3}, Skipped: {4}", - _engineResult.Attributes["total"].Value, - _engineResult.Attributes["passed"].Value, - _engineResult.Attributes["failed"].Value, - _engineResult.Attributes["inconclusive"].Value, - _engineResult.Attributes["skipped"].Value); + _engineResult.Attributes["total"]?.Value, + _engineResult.Attributes["passed"]?.Value, + _engineResult.Attributes["failed"]?.Value, + _engineResult.Attributes["inconclusive"]?.Value, + _engineResult.Attributes["skipped"]?.Value); string output = writer.GetStringBuilder().ToString(); diff --git a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs index 8beb027fb..6d3c72ea5 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs @@ -36,6 +36,7 @@ public void CreateServiceContext() public void StopService() { _runtimeService.StopService(); + _runtimeService.Dispose(); } [Test] @@ -70,7 +71,7 @@ public void SelectRuntimeFramework(string runtime, bool runAsX86) public void CanGetCurrentFramework() { var framework = _runtimeService.CurrentFramework as RuntimeFramework; - + Assert.That(framework, Is.Not.Null); Assert.That(framework.Runtime, Is.EqualTo(_currentRuntime)); } @@ -87,6 +88,7 @@ public void AvailableFrameworks() public void CurrentFrameworkMustBeAvailable() { var current = _runtimeService.CurrentFramework; + Assert.That(current, Is.Not.Null); Console.WriteLine("Current framework is {0} ({1})", current.DisplayName, current.Id); Assert.That(_runtimeService.IsAvailable(current.Id, false), "{current} not available"); } @@ -95,9 +97,15 @@ public void CurrentFrameworkMustBeAvailable() public void AvailableFrameworksList_IncludesCurrentFramework() { var current = _runtimeService.CurrentFramework as RuntimeFramework; + Assert.That(current, Is.Not.Null); + foreach (var framework in _runtimeService.AvailableRuntimes) - if (current.Supports(framework as RuntimeFramework)) + { + RuntimeFramework? runtimeFramework = framework as RuntimeFramework; + Assert.That(runtimeFramework, Is.Not.Null); + if (current.Supports(runtimeFramework)) return; + } Assert.Fail("CurrentFramework not listed as available"); } @@ -123,7 +131,7 @@ public void EngineOptionPreferredOverImageTarget(string framework, int majorVers package.AddSetting(EnginePackageSettings.RequestedRuntimeFramework, requested); _runtimeService.SelectRuntimeFramework(package); - Assert.That(package.GetSetting(EnginePackageSettings.RequestedRuntimeFramework, null), Is.EqualTo(requested)); + Assert.That(package.GetSetting(EnginePackageSettings.RequestedRuntimeFramework, string.Empty), Is.EqualTo(requested)); } [Test] diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ServiceDependencyTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ServiceDependencyTests.cs index 33bcfa427..77db8986c 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ServiceDependencyTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ServiceDependencyTests.cs @@ -20,7 +20,7 @@ public void TestRunnerFactory_ProjectServiceMissing() { var service = new TestRunnerFactory(); _services.Add(service); - service.StartService(); + Assert.That(service.StartService, Throws.Exception); Assert.That(service.Status, Is.EqualTo(ServiceStatus.Error)); } } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ServiceManagerTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ServiceManagerTests.cs index 1bd6ff869..a5c698d63 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ServiceManagerTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ServiceManagerTests.cs @@ -20,6 +20,13 @@ public void SetUp() _serviceManager.AddService(new ExtensionService()); } + [TearDown] + public void TearDown() + { + _serviceManager.StopServices(); + _serviceManager.Dispose(); + } + [Test] public void InitializeServices() { diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResult.cs b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResult.cs index 6c3eb4830..84b9fc463 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResult.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResult.cs @@ -26,24 +26,19 @@ public static RunnerResult AggregatingTestRunner(int numSubRunners) => public static RunnerResult AggregatingTestRunner(RunnerResult subRunnerType, int numSubRunners) { - return new RunnerResult() - { - TestRunner = typeof(AggregatingTestRunner), - SubRunners = GetSubRunners(subRunnerType, numSubRunners) - }; + return new RunnerResult(typeof(AggregatingTestRunner), GetSubRunners(subRunnerType, numSubRunners)); } public static RunnerResult AggregatingTestRunner(params RunnerResult[] subRunners) { - return new RunnerResult() - { - TestRunner = typeof(AggregatingTestRunner), - SubRunners = subRunners - }; + return new RunnerResult(typeof(AggregatingTestRunner), subRunners); } - public RunnerResult() - { } + public RunnerResult(Type testRunner) + { + TestRunner = testRunner; + SubRunners = Array.Empty(); + } public RunnerResult(Type testRunner, params RunnerResult[] subRunners) { @@ -53,7 +48,7 @@ public RunnerResult(Type testRunner, params RunnerResult[] subRunners) public Type TestRunner { get; set; } - public ICollection SubRunners { get; set; } = new List(); + public ICollection SubRunners { get; } public override string ToString() { diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResultComparer.cs b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResultComparer.cs index 3667fa1b0..e2080ce29 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResultComparer.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerResultComparer.cs @@ -10,10 +10,13 @@ internal class RunnerResultComparer : IEqualityComparer { public static readonly RunnerResultComparer Instance = new RunnerResultComparer(); - public bool Equals(RunnerResult x, RunnerResult y) + public bool Equals(RunnerResult? x, RunnerResult? y) { - x = x ?? throw new ArgumentNullException(nameof(x)); - y = y ?? throw new ArgumentNullException(nameof(y)); + if (ReferenceEquals(x, y)) + return true; + + if (x is null || y is null) + return false; return x.TestRunner == y.TestRunner && x.SubRunners.SequenceEqual(y.SubRunners, Instance); diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerSelectionTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerSelectionTests.cs index 64052018d..e89c803d4 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerSelectionTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TestRunnerFactoryTests/RunnerSelectionTests.cs @@ -1,8 +1,11 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt +using System; +using System.Collections.Generic; using System.Linq; using NUnit.Engine.Runners; using NUnit.Framework; +using NUnitLite; namespace NUnit.Engine.Services.TestRunnerFactoryTests { @@ -36,6 +39,19 @@ public void SetUp() ((IService)fakeRuntimeService).StartService(); _services.Add(fakeRuntimeService); Assert.That(((IService)fakeRuntimeService).Status, Is.EqualTo(ServiceStatus.Started)); + +#if NETFRAMEWORK + var testAgency = new TestAgency(); + _services.Add(testAgency); + ((IService)testAgency).StartService(); + Assert.That(testAgency.Status, Is.EqualTo(ServiceStatus.Started)); +#endif + } + + [OneTimeTearDown] + public void TearDown() + { + _factory.Dispose(); } [TestCaseSource(typeof(TestRunnerFactoryData), nameof(TestRunnerFactoryData.TestCases))] @@ -49,12 +65,12 @@ public void RunnerSelectionTest(TestPackage package, RunnerResult expected) private static RunnerResult GetRunnerResult(ITestEngineRunner runner) { - var result = new RunnerResult(runner.GetType()); + var runnerType = runner.GetType(); if (runner is AggregatingTestRunner aggRunner) - result.SubRunners = aggRunner.Runners.Select(GetRunnerResult).ToList(); + return new RunnerResult(runnerType, aggRunner.Runners.Select(GetRunnerResult).ToArray()); - return result; + return new RunnerResult(runnerType); } } } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TestSelectionParserTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/TestSelectionParserTests.cs index 700663750..bf9d47b99 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TestSelectionParserTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TestSelectionParserTests.cs @@ -10,14 +10,6 @@ namespace NUnit.Engine.Services { public class TestSelectionParserTests { - private TestSelectionParser _parser; - - [SetUp] - public void CreateParser() - { - _parser = new TestSelectionParser(); - } - [TestCase("cat=Urgent", "Urgent")] [TestCase("cat==Urgent", "Urgent")] [TestCase("cat!=Urgent", "Urgent")] @@ -52,19 +44,19 @@ public void CreateParser() [TestCase("!(cat!=Urgent)", "Urgent")] public void TestParser(string input, string output) { - Assert.That(_parser.Parse(input), Is.EqualTo(output)); + Assert.That(TestSelectionParser.Parse(input), Is.EqualTo(output)); XmlDocument doc = new XmlDocument(); Assert.DoesNotThrow(() => doc.LoadXml(output)); } - [TestCase(null, typeof(ArgumentNullException))] + [TestCase(null!, typeof(ArgumentNullException))] [TestCase("", typeof(TestSelectionParserException))] [TestCase(" ", typeof(TestSelectionParserException))] [TestCase(" \t\t ", typeof(TestSelectionParserException))] public void TestParser_InvalidInput(string input, Type type) { - Assert.That(() => _parser.Parse(input), Throws.TypeOf(type)); + Assert.That(() => TestSelectionParser.Parse(input), Throws.TypeOf(type)); } } } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TokenizerTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/TokenizerTests.cs index e4d5335f6..5f760f435 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TokenizerTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TokenizerTests.cs @@ -12,7 +12,7 @@ public class TokenizerTests [Test] public void NullInputThrowsException() { - Assert.That(() => new Tokenizer(null), Throws.ArgumentNullException); + Assert.That(() => new Tokenizer(null!), Throws.ArgumentNullException); } [Test] diff --git a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj index 2632124c9..6a5efc1a4 100644 --- a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj +++ b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj @@ -12,6 +12,8 @@ 1505 + + NUnit Engine NUnit Engine Tests ($(TargetFramework)) @@ -24,6 +26,10 @@ + + + + diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs index c9d24b877..0e43d181c 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs @@ -27,7 +27,7 @@ public class TestAgencyRemotingTransport : MarshalByRefObject, ITestAgencyTransp private string _uri; private int _port; - private TcpChannel _channel; + private TcpChannel? _channel; private bool _isMarshalled; private object _theLock = new object(); @@ -56,7 +56,7 @@ public bool Start() if (_port == 0) { - ChannelDataStore store = this._channel.ChannelData as ChannelDataStore; + ChannelDataStore? store = _channel!.ChannelData as ChannelDataStore; if (store != null) { string channelUri = store.ChannelUris[0]; @@ -133,7 +133,7 @@ protected virtual void Dispose(bool disposing) /// public override object InitializeLifetimeService() { - return null; + return null!; } } } diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs index 705e5e60b..03cb770d9 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs @@ -15,12 +15,12 @@ public class TcpServer private const int GUID_BUFFER_SIZE = 16; TcpListener _tcpListener; - Thread _listenerThread; + Thread? _listenerThread; volatile bool _running; public delegate void ConnectionEventHandler(Socket clientSocket, Guid id); - public event ConnectionEventHandler ClientConnected; + public event ConnectionEventHandler? ClientConnected; public TcpServer(int port = 0) { diff --git a/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs b/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs index f88851802..78240c3b9 100644 --- a/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs @@ -34,7 +34,7 @@ public class AggregatingTestRunner : TestEngineRunner // TODO: Determine whether AggregatingTestRunner needs to create an XML result // node for a project or if that responsibility can be delegated to the individual // runners it creates. - private List _runners; + private List? _runners; // Exceptions from unloading individual runners are caught and rethrown // on AggregatingTestRunner disposal, to allow TestResults to be diff --git a/src/NUnitEngine/nunit.engine/Runners/MasterTestRunner.cs b/src/NUnitEngine/nunit.engine/Runners/MasterTestRunner.cs index 24985f841..48743493f 100644 --- a/src/NUnitEngine/nunit.engine/Runners/MasterTestRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/MasterTestRunner.cs @@ -32,7 +32,7 @@ public class MasterTestRunner : ITestRunner // element, which wraps all the individual assembly and project // results. - private ITestEngineRunner _engineRunner; + private ITestEngineRunner? _engineRunner; private readonly IServiceLocator _services; private readonly ExtensionService _extensionService; #if NETFRAMEWORK @@ -77,7 +77,7 @@ public MasterTestRunner(IServiceLocator services, TestPackage package) /// /// The result of the last call to LoadPackage /// - protected TestEngineResult LoadResult { get; set; } + protected TestEngineResult? LoadResult { get; set; } /// /// Gets an indicator of whether the package has been loaded. @@ -145,7 +145,7 @@ public int CountTestCases(TestFilter filter) /// An ITestEventHandler to receive events /// A TestFilter used to select tests /// An XmlNode giving the result of the test execution - public XmlNode Run(ITestEventListener listener, TestFilter filter) + public XmlNode Run(ITestEventListener? listener, TestFilter filter) { return RunTests(listener, filter).Xml; } @@ -158,7 +158,7 @@ public XmlNode Run(ITestEventListener listener, TestFilter filter) /// The listener that is notified as the run progresses /// A TestFilter used to select tests /// - public ITestRun RunAsync(ITestEventListener listener, TestFilter filter) + public ITestRun RunAsync(ITestEventListener? listener, TestFilter filter) { return RunTestsAsync(listener, filter); } @@ -169,6 +169,9 @@ public ITestRun RunAsync(ITestEventListener listener, TestFilter filter) /// If true, cancel any ongoing test threads, otherwise wait for them to complete. public void StopRun(bool force) { + if (_engineRunner is null) + return; // No test is was even started. + _engineRunner.StopRun(force); if (force) @@ -386,8 +389,7 @@ private void ValidatePackageSettings() private void UnloadPackage() { LoadResult = null; - if (_engineRunner != null) - _engineRunner.Unload(); + _engineRunner?.Unload(); } /// @@ -411,7 +413,7 @@ private int CountTests(TestFilter filter) /// An ITestEventHandler to receive events /// A TestFilter used to select tests /// A TestEngineResult giving the result of the test execution - private TestEngineResult RunTests(ITestEventListener listener, TestFilter filter) + private TestEngineResult RunTests(ITestEventListener? listener, TestFilter filter) { _workItemTracker.Clear(); _eventDispatcher.Listeners.Clear(); @@ -429,7 +431,7 @@ private TestEngineResult RunTests(ITestEventListener listener, TestFilter filter string engineVersion; clrVersion = Environment.Version.ToString(); - engineVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + engineVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0"; var startTime = DateTime.UtcNow; var startRunNode = XmlHelper.CreateTopLevelElement("start-run"); @@ -465,7 +467,7 @@ private TestEngineResult RunTests(ITestEventListener listener, TestFilter filter return result; } - private AsyncTestEngineResult RunTestsAsync(ITestEventListener listener, TestFilter filter) + private AsyncTestEngineResult RunTestsAsync(ITestEventListener? listener, TestFilter filter) { var testRun = new AsyncTestEngineResult(); diff --git a/src/NUnitEngine/nunit.engine/Runners/ParallelTaskWorkerPool.cs b/src/NUnitEngine/nunit.engine/Runners/ParallelTaskWorkerPool.cs index ee236c5ea..41fece7d8 100644 --- a/src/NUnitEngine/nunit.engine/Runners/ParallelTaskWorkerPool.cs +++ b/src/NUnitEngine/nunit.engine/Runners/ParallelTaskWorkerPool.cs @@ -49,7 +49,7 @@ private void ProcessTasksProc() { while (true) { - ITestExecutionTask task = null; + ITestExecutionTask? task = null; lock (_taskLock) { if (_tasks.Count > 0) diff --git a/src/NUnitEngine/nunit.engine/Runners/ProcessRunner.cs b/src/NUnitEngine/nunit.engine/Runners/ProcessRunner.cs index e9c95f0ff..f04d3b7cf 100644 --- a/src/NUnitEngine/nunit.engine/Runners/ProcessRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/ProcessRunner.cs @@ -3,6 +3,7 @@ #if NETFRAMEWORK using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using NUnit.Common; using NUnit.Engine.Internal; using NUnit.Engine.Services; @@ -24,9 +25,10 @@ public class ProcessRunner : TestEngineRunner private static readonly Logger log = InternalTrace.GetLogger(typeof(ProcessRunner)); - private ITestAgent _agent; - private ITestEngineRunner _remoteRunner; - private TestAgency _agency; + private readonly TestAgency _agency; + + private ITestAgent? _agent; + private ITestEngineRunner? _remoteRunner; public ProcessRunner(IServiceLocator services, TestPackage package) : base(services, package) { @@ -204,7 +206,7 @@ protected override void Dispose(bool disposing) { _disposed = true; - Exception unloadException = null; + Exception? unloadException = null; try { @@ -241,6 +243,7 @@ protected override void Dispose(bool disposing) } } + [MemberNotNull(nameof(_agent), nameof(_remoteRunner))] private void CreateAgentAndRunnerIfNeeded() { if (_agent == null) @@ -264,8 +267,8 @@ TestEngineResult CreateFailedResult(Exception e) var suite = XmlHelper.CreateTopLevelElement("test-suite"); XmlHelper.AddAttribute(suite, "type", "Assembly"); XmlHelper.AddAttribute(suite, "id", TestPackage.ID); - XmlHelper.AddAttribute(suite, "name", TestPackage.Name); - XmlHelper.AddAttribute(suite, "fullname", TestPackage.FullName); + XmlHelper.AddAttribute(suite, "name", TestPackage.Name ?? string.Empty); + XmlHelper.AddAttribute(suite, "fullname", TestPackage.FullName ?? string.Empty); XmlHelper.AddAttribute(suite, "runstate", "NotRunnable"); XmlHelper.AddAttribute(suite, "testcasecount", "1"); XmlHelper.AddAttribute(suite, "result", "Failed"); diff --git a/src/NUnitEngine/nunit.engine/Runners/TestEngineRunner.cs b/src/NUnitEngine/nunit.engine/Runners/TestEngineRunner.cs index b4ce0b000..521e5e36d 100644 --- a/src/NUnitEngine/nunit.engine/Runners/TestEngineRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/TestEngineRunner.cs @@ -37,7 +37,7 @@ public TestEngineRunner(IServiceLocator services, TestPackage package) /// /// The result of the last call to LoadPackage /// - protected TestEngineResult LoadResult { get; set; } + protected TestEngineResult? LoadResult { get; set; } /// /// Gets an indicator of whether the package has been loaded. diff --git a/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs b/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs index 4d5860bf7..efea913c2 100644 --- a/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs +++ b/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs @@ -37,7 +37,7 @@ public void OnTestEvent(string report) #endif public override object InitializeLifetimeService() { - return null; + return null!; } } } diff --git a/src/NUnitEngine/nunit.engine/Runners/TestExecutionTask.cs b/src/NUnitEngine/nunit.engine/Runners/TestExecutionTask.cs index 3af167618..3ddcf1652 100644 --- a/src/NUnitEngine/nunit.engine/Runners/TestExecutionTask.cs +++ b/src/NUnitEngine/nunit.engine/Runners/TestExecutionTask.cs @@ -10,10 +10,10 @@ public class TestExecutionTask : ITestExecutionTask private readonly ITestEngineRunner _runner; private readonly ITestEventListener _listener; private readonly TestFilter _filter; - private volatile TestEngineResult _result; + private volatile TestEngineResult? _result; private readonly bool _disposeRunner; private bool _hasExecuted = false; - private Exception _unloadException; + private Exception? _unloadException; public TestExecutionTask(ITestEngineRunner runner, ITestEventListener listener, TestFilter filter, bool disposeRunner) { @@ -44,7 +44,7 @@ public void Execute() } } - public TestEngineResult Result + public TestEngineResult? Result { get { @@ -56,7 +56,7 @@ public TestEngineResult Result /// /// Stored exception thrown during test assembly unload. /// - public Exception UnloadException + public Exception? UnloadException { get { diff --git a/src/NUnitEngine/nunit.engine/Runners/WorkItemTracker.cs b/src/NUnitEngine/nunit.engine/Runners/WorkItemTracker.cs index 0084a1f28..f18231a9b 100644 --- a/src/NUnitEngine/nunit.engine/Runners/WorkItemTracker.cs +++ b/src/NUnitEngine/nunit.engine/Runners/WorkItemTracker.cs @@ -53,10 +53,13 @@ public InProgressItem(int order, string name, XmlReader reader) public string Name { get; } public Dictionary Properties { get; } - public int CompareTo(InProgressItem other) + public int CompareTo(InProgressItem? other) { // for signaling purposes, return in reverse order - return _order.CompareTo(other._order) * -1; + if (other == null) + return -1; + + return _order.CompareTo(other._order) * -1; } } @@ -159,7 +162,9 @@ void ITestEventListener.OnTestEvent(string report) case "test-case": case "test-suite": - RemoveItem(reader.GetAttribute("id")); + string? id = reader.GetAttribute("id"); + if (id != null) // TODO: Should we throw if id is null? + RemoveItem(id); if (_itemsInProcess.Count == 0) _allItemsComplete.Set(); diff --git a/src/NUnitEngine/nunit.engine/Runtime.cs b/src/NUnitEngine/nunit.engine/Runtime.cs index 80b4c51e7..bf60da154 100644 --- a/src/NUnitEngine/nunit.engine/Runtime.cs +++ b/src/NUnitEngine/nunit.engine/Runtime.cs @@ -73,6 +73,8 @@ public virtual bool Supports(Version runtime, Version target) return runtime.Major == target.Major && runtime.Minor >= target.Minor; } + public abstract override string ToString(); + #endregion #region Nested Runtime Classes diff --git a/src/NUnitEngine/nunit.engine/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine/RuntimeFramework.cs index 1c408ca8f..75078880a 100644 --- a/src/NUnitEngine/nunit.engine/RuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine/RuntimeFramework.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.Versioning; @@ -37,7 +38,7 @@ public RuntimeFramework(Runtime runtime, Version version) /// A Runtime instance. /// The Version of the framework. /// A string representing the profile of the framework. Null if unspecified. - public RuntimeFramework(Runtime runtime, Version version, string profile) + public RuntimeFramework(Runtime runtime, Version version, string? profile) { Guard.ArgumentNotNull(runtime, nameof(runtime)); Guard.ArgumentValid(IsValidFrameworkVersion(version), $"{version} is not a valid framework version", nameof(version)); @@ -85,7 +86,7 @@ private bool IsValidFrameworkVersion(Version v) /// May be null and will have different sets of /// values for each Runtime. /// - public string Profile { get; private set; } + public string? Profile { get; private set; } /// /// Returns the Display name for this framework @@ -114,7 +115,7 @@ public static RuntimeFramework Parse(string s) return new RuntimeFramework(runtime, version); } - public static bool TryParse(string s, out RuntimeFramework runtimeFramework) + public static bool TryParse(string s, [NotNullWhen(true)] out RuntimeFramework? runtimeFramework) { try { @@ -168,7 +169,7 @@ public bool CanLoad(IRuntimeFramework requested) return FrameworkVersion >= requested.FrameworkVersion; } - private static string GetDefaultDisplayName(Runtime runtime, Version version, string profile) + private static string GetDefaultDisplayName(Runtime runtime, Version version, string? profile) { string displayName = $"{runtime.DisplayName} {version}"; @@ -187,7 +188,7 @@ private static string GetMonoPrefixFromAssembly(Assembly assembly) // files have been copied to some non-standard place, we check. for (int i = 0; i < 4; i++) { - string dir = Path.GetDirectoryName(prefix); + string? dir = Path.GetDirectoryName(prefix); if (string.IsNullOrEmpty(dir)) break; prefix = dir; diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs index 47ead8bf0..681ac4340 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs @@ -73,7 +73,11 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) if (Path.DirectorySeparatorChar != '\\') throw new Exception("Running .NET Core as X86 is currently only supported on Windows"); - var x86_dotnet_exe = Path.Combine(DotNet.GetX86InstallDirectory(), "dotnet.exe"); + string? installDirectory = DotNet.GetX86InstallDirectory(); + if (installDirectory == null) + throw new Exception("The X86 version of dotnet.exe is not installed"); + + var x86_dotnet_exe = Path.Combine(installDirectory, "dotnet.exe"); if (!File.Exists(x86_dotnet_exe)) throw new Exception("The X86 version of dotnet.exe is not installed"); @@ -155,7 +159,7 @@ public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool re break; default: log.Error($"Unknown runtime type: {targetRuntime.Runtime}"); - return null; + throw new NotSupportedException($"Unknown runtime type: {targetRuntime.Runtime}"); } return Path.Combine(agentsDir, agentSubDir, agentName + agentExtension); diff --git a/src/NUnitEngine/nunit.engine/Services/AgentStore.AgentRecord.cs b/src/NUnitEngine/nunit.engine/Services/AgentStore.AgentRecord.cs index c3464f1c1..27246d80f 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentStore.AgentRecord.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentStore.AgentRecord.cs @@ -3,27 +3,31 @@ #if !NETSTANDARD2_0 using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace NUnit.Engine.Services { internal sealed partial class AgentStore { - private struct AgentRecord + private readonly struct AgentRecord { - private AgentRecord(Process process, ITestAgent agent) + private AgentRecord(Process? process, ITestAgent? agent) { Process = process; Agent = agent; } - public Process Process { get; } - public ITestAgent Agent { get; } + public Process? Process { get; } + public ITestAgent? Agent { get; } public AgentStatus Status => Process is null ? AgentStatus.Terminated : Agent is null ? AgentStatus.Starting : AgentStatus.Ready; + [MemberNotNullWhen(true, nameof(Process), nameof(Agent))] + public bool IsReady => Process is not null && Agent is not null; + public static AgentRecord Starting(Process process) { if (process is null) throw new ArgumentNullException(nameof(process)); diff --git a/src/NUnitEngine/nunit.engine/Services/AgentStore.cs b/src/NUnitEngine/nunit.engine/Services/AgentStore.cs index cb3ca766a..6c7bd51f0 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentStore.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentStore.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace NUnit.Engine.Services { @@ -41,12 +42,11 @@ public void Register(ITestAgent agent) } } - public bool IsReady(Guid agentId, out ITestAgent agent) + public bool IsReady(Guid agentId, [NotNullWhen(true)] out ITestAgent? agent) { lock (_agentsById) { - if (_agentsById.TryGetValue(agentId, out var record) - && record.Status == AgentStatus.Ready) + if (_agentsById.TryGetValue(agentId, out var record) && record.IsReady) { agent = record.Agent; return true; @@ -57,7 +57,7 @@ public bool IsReady(Guid agentId, out ITestAgent agent) } } - public bool IsAgentProcessActive(Guid agentId, out Process process) + public bool IsAgentProcessActive(Guid agentId, [NotNullWhen(true)] out Process? process) { lock (_agentsById) { diff --git a/src/NUnitEngine/nunit.engine/Services/ExtensionService.cs b/src/NUnitEngine/nunit.engine/Services/ExtensionService.cs index 405d95dc0..330fa9dad 100644 --- a/src/NUnitEngine/nunit.engine/Services/ExtensionService.cs +++ b/src/NUnitEngine/nunit.engine/Services/ExtensionService.cs @@ -61,7 +61,7 @@ public void FindExtensionAssemblies(string initialDirectory) /// /// Get an ExtensionPoint based on its unique identifying path. /// - IExtensionPoint IExtensionService.GetExtensionPoint(string path) + IExtensionPoint? IExtensionService.GetExtensionPoint(string path) { return _extensionManager.GetExtensionPoint(path); } @@ -82,7 +82,7 @@ public void EnableExtension(string typeName, bool enabled) public IEnumerable GetExtensions() => _extensionManager.GetExtensions(); - public IExtensionNode GetExtensionNode(string path) => _extensionManager.GetExtensionNode(path); + public IExtensionNode? GetExtensionNode(string path) => _extensionManager.GetExtensionNode(path); public IEnumerable GetExtensionNodes() => _extensionManager.GetExtensionNodes(); diff --git a/src/NUnitEngine/nunit.engine/Services/ProjectService.cs b/src/NUnitEngine/nunit.engine/Services/ProjectService.cs index 10d0628f5..eaef24323 100644 --- a/src/NUnitEngine/nunit.engine/Services/ProjectService.cs +++ b/src/NUnitEngine/nunit.engine/Services/ProjectService.cs @@ -1,5 +1,6 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt +using System; using System.Collections.Generic; using System.IO; using NUnit.Common; @@ -16,11 +17,10 @@ public class ProjectService : Service, IProjectService public bool CanLoadFrom(string path) { - ExtensionNode node = GetNodeForPath(path); + ExtensionNode? node = GetNodeForPath(path); if (node != null) { - var loader = node.ExtensionObject as IProjectLoader; - if (loader.CanLoadFrom(path)) + if (node.ExtensionObject is IProjectLoader loader && loader.CanLoadFrom(path)) return true; } @@ -39,14 +39,13 @@ public void ExpandProjectPackage(TestPackage package) Guard.ArgumentNotNull(package, "package"); Guard.ArgumentValid(package.SubPackages.Count == 0, "Package is already expanded", "package"); - string path = package.FullName; + string path = package.FullName!; if (!File.Exists(path)) return; - IProject project = LoadFrom(path); - Guard.ArgumentValid(project != null, "Unable to load project " + path, "package"); + IProject project = LoadFrom(path).ShouldNotBeNull("Unable to load project " + path); - string activeConfig = package.GetSetting(EnginePackageSettings.ActiveConfig, (string)null); + string? activeConfig = package.GetSetting(EnginePackageSettings.ActiveConfig, (string?)null); if (activeConfig == null) activeConfig = project.ActiveConfigName; else @@ -84,6 +83,9 @@ public override void StartService() { try { + if (ServiceContext == null) + throw new InvalidOperationException("Only services that have a ServiceContext can be started."); + var extensionService = ServiceContext.GetService(); if (extensionService == null) @@ -118,15 +120,14 @@ public override void StartService() } } - private IProject LoadFrom(string path) + private IProject? LoadFrom(string path) { if (File.Exists(path)) { - ExtensionNode node = GetNodeForPath(path); + ExtensionNode? node = GetNodeForPath(path); if (node != null) { - var loader = node.ExtensionObject as IProjectLoader; - if (loader.CanLoadFrom(path)) + if (node.ExtensionObject is IProjectLoader loader && loader.CanLoadFrom(path)) return loader.LoadFrom(path); } } @@ -134,14 +135,14 @@ private IProject LoadFrom(string path) return null; } - private ExtensionNode GetNodeForPath(string path) + private ExtensionNode? GetNodeForPath(string path) { var ext = Path.GetExtension(path); - if (string.IsNullOrEmpty(ext) || !_extensionIndex.ContainsKey(ext)) + if (string.IsNullOrEmpty(ext) || !_extensionIndex.TryGetValue(ext, out ExtensionNode? node)) return null; - return _extensionIndex[ext]; + return node; } } } diff --git a/src/NUnitEngine/nunit.engine/Services/ResultService.cs b/src/NUnitEngine/nunit.engine/Services/ResultService.cs index 4746c663c..cc842c458 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultService.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using NUnit.Engine.Extensibility; namespace NUnit.Engine.Services @@ -9,9 +10,11 @@ namespace NUnit.Engine.Services public class ResultService : Service, IResultService { private readonly string[] BUILT_IN_FORMATS = new string[] { "nunit3", "cases", "user" }; - private IEnumerable _extensionNodes; + private IEnumerable? _extensionNodes; - private string[] _formats; + private string[]? _formats; + + [MemberNotNull(nameof(_formats))] public string[] Formats { get @@ -20,9 +23,10 @@ public string[] Formats { var formatList = new List(BUILT_IN_FORMATS); - foreach (var node in _extensionNodes) - foreach (var format in node.GetValues("Format")) - formatList.Add(format); + if (_extensionNodes != null) + foreach (var node in _extensionNodes) + foreach (var format in node.GetValues("Format")) + formatList.Add(format); _formats = formatList.ToArray(); } @@ -37,7 +41,7 @@ public string[] Formats /// The name of the format to be used /// A set of arguments to be used in constructing the writer or null if non arguments are needed /// An IResultWriter - public IResultWriter GetResultWriter(string format, object[] args) + public IResultWriter GetResultWriter(string format, params object?[]? args) { switch (format) { @@ -46,14 +50,15 @@ public IResultWriter GetResultWriter(string format, object[] args) case "cases": return new TestCaseResultWriter(); case "user": - return new XmlTransformResultWriter(args); + return new XmlTransformResultWriter(args!); default: - foreach (var node in _extensionNodes) - foreach (var supported in node.GetValues("Format")) - if (supported == format) - return node.ExtensionObject as IResultWriter; - return null; + if (_extensionNodes != null) + foreach (var node in _extensionNodes) + foreach (var supported in node.GetValues("Format")) + if (supported == format) + return (IResultWriter)node.ExtensionObject; + throw new NUnitEngineException("ResultWriter not found for format: " + format); } } @@ -61,10 +66,15 @@ public override void StartService() { try { + if (ServiceContext == null) + throw new InvalidOperationException("Only services that have a ServiceContext can be started."); + var extensionService = ServiceContext.GetService(); - if (extensionService != null && extensionService.Status == ServiceStatus.Started) - _extensionNodes = extensionService.GetExtensionNodes(); + if (extensionService.Status != ServiceStatus.Started) + throw new NUnitEngineException("ExtensionService must be added before ResultService"); + + _extensionNodes = extensionService.GetExtensionNodes(); // If there is no extension service, we start anyway using builtin writers Status = ServiceStatus.Started; diff --git a/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs b/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs index d92211f94..ccfb98f22 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs @@ -28,8 +28,10 @@ public void WriteResultFile(XmlNode resultNode, string outputPath) public void WriteResultFile(XmlNode resultNode, TextWriter writer) { - foreach (XmlNode node in resultNode.SelectNodes("//test-case")) - writer.WriteLine(node.Attributes["fullname"].Value); + XmlNodeList? testCases = resultNode.SelectNodes("//test-case"); + if (testCases != null) + foreach (XmlNode node in testCases) + writer.WriteLine(node.Attributes?["fullname"]?.Value); } } } diff --git a/src/NUnitEngine/nunit.engine/Services/ResultWriters/XmlTransformResultWriter.cs b/src/NUnitEngine/nunit.engine/Services/ResultWriters/XmlTransformResultWriter.cs index 5de2f322e..c07e850c2 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultWriters/XmlTransformResultWriter.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultWriters/XmlTransformResultWriter.cs @@ -12,13 +12,13 @@ namespace NUnit.Engine.Services { public class XmlTransformResultWriter : IResultWriter { - private string _xsltFile; + private string? _xsltFile; private readonly XslCompiledTransform _transform = new XslCompiledTransform(); - public XmlTransformResultWriter(object[] args) + public XmlTransformResultWriter(object?[] args) { Guard.ArgumentNotNull(args, "args"); - _xsltFile = args[0] as string; + _xsltFile = (string?)args[0]; Guard.ArgumentValid( !string.IsNullOrEmpty(_xsltFile), diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs index c117d328e..f155a3a63 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs @@ -3,6 +3,7 @@ #if NETFRAMEWORK using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.Versioning; @@ -25,9 +26,9 @@ public class RuntimeFrameworkService : Service, IRuntimeFrameworkService, IAvail /// Gets a RuntimeFramework instance representing the runtime under /// which the code is currently running. /// - public IRuntimeFramework CurrentFramework { get; private set; } + public IRuntimeFramework? CurrentFramework { get; private set; } - private static string MonoPrefix; + private static string? MonoPrefix; /// /// The path to the mono executable, if we are running on Mono. @@ -56,7 +57,7 @@ public bool IsAvailable(string name, bool needX86) { Guard.ArgumentNotNullOrEmpty(name, nameof(name)); - if (!RuntimeFramework.TryParse(name, out RuntimeFramework requestedFramework)) + if (!RuntimeFramework.TryParse(name, out RuntimeFramework? requestedFramework)) throw new NUnitEngineException("Invalid or unknown framework requested: " + name); var runtimes = needX86 ? _availableX86Runtimes : _availableRuntimes; @@ -119,6 +120,9 @@ public string SelectRuntimeFramework(TestPackage package) private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) { + if (CurrentFramework == null) + throw new InvalidOperationException("Service not Started"); + foreach (var subPackage in package.SubPackages) { SelectRuntimeFrameworkInner(subPackage); @@ -133,7 +137,7 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) if (frameworkSetting.Length > 0) { - if (!RuntimeFramework.TryParse(frameworkSetting, out RuntimeFramework requestedFramework)) + if (!RuntimeFramework.TryParse(frameworkSetting, out RuntimeFramework? requestedFramework)) throw new NUnitEngineException("Invalid or unknown framework requested: " + frameworkSetting); log.Debug($"Requested framework for {package.Name} is {requestedFramework}"); @@ -178,7 +182,6 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) targetVersion = new Version(3, 1); break; case "Unmanaged": - return null; default: throw new NUnitEngineException("Unsupported Target Framework: " + imageTargetFrameworkNameSetting); } @@ -204,29 +207,29 @@ public override void StartService() { SetCurrentFramework(); FindAvailableRuntimes(); + + Status = ServiceStatus.Started; } catch { Status = ServiceStatus.Error; throw; } - - Status = ServiceStatus.Started; } + [MemberNotNull(nameof(CurrentFramework))] private void SetCurrentFramework() { - Type monoRuntimeType = Type.GetType("Mono.Runtime", false); - bool isMono = monoRuntimeType != null; + Type? monoRuntimeType = Type.GetType("Mono.Runtime", throwOnError: false); - Runtime runtime = isMono + Runtime runtime = monoRuntimeType != null ? Runtime.Mono : Runtime.Net; int major = Environment.Version.Major; int minor = Environment.Version.Minor; - if (isMono) + if (monoRuntimeType != null) { switch (major) { @@ -242,10 +245,10 @@ private void SetCurrentFramework() else /* It's windows */ if (major == 2) { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"); + using RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"); if (key != null) { - string installRoot = key.GetValue("InstallRoot") as string; + string? installRoot = key.GetValue("InstallRoot") as string; if (installRoot != null) { if (Directory.Exists(Path.Combine(installRoot, "v3.5"))) @@ -268,15 +271,15 @@ private void SetCurrentFramework() var currentFramework = new RuntimeFramework(runtime, new Version(major, minor)); - if (isMono) + if (monoRuntimeType != null) { MonoPrefix = GetMonoPrefixFromAssembly(monoRuntimeType.Assembly); - MethodInfo getDisplayNameMethod = monoRuntimeType.GetMethod( + MethodInfo? getDisplayNameMethod = monoRuntimeType.GetMethod( "GetDisplayName", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding); if (getDisplayNameMethod != null) { - string displayName = (string)getDisplayNameMethod.Invoke(null, new object[0]); + string displayName = (string)getDisplayNameMethod.Invoke(null, new object[0])!; int space = displayName.IndexOf(' '); if (space >= 3) // Minimum length of a version @@ -303,7 +306,7 @@ private static string GetMonoPrefixFromAssembly(Assembly assembly) // files have been copied to some non-standard place, we check. for (int i = 0; i < 4; i++) { - string dir = Path.GetDirectoryName(prefix); + string? dir = Path.GetDirectoryName(prefix); if (string.IsNullOrEmpty(dir)) break; prefix = dir; @@ -336,10 +339,10 @@ private void FindAvailableRuntimes() /// private static void ApplyImageData(TestPackage package) { - string packageName = package.FullName; + string packageName = package.FullName ?? string.Empty; Version targetVersion = new Version(0, 0); - string frameworkName = null; + string? frameworkName = null; bool requiresX86 = false; bool requiresAssemblyResolver = false; diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs index 0a0629dce..22115b78e 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs @@ -38,7 +38,7 @@ public static IEnumerable FindRuntimes(bool forX86) private static IEnumerable GetRuntimeDirectories(bool x86) { - string installDir = DotNet.GetInstallDirectory(x86); + string? installDir = DotNet.GetInstallDirectory(x86); if (installDir != null && Directory.Exists(installDir) && File.Exists(Path.Combine(installDir, "dotnet.exe"))) @@ -77,9 +77,9 @@ private static IEnumerable GetRuntimeList() const string PREFIX = "Microsoft.NETCore.App "; const int VERSION_START = 22; - while (!process.StandardOutput.EndOfStream) + string? line; + while ((line = process.StandardOutput.ReadLine()) != null) { - var line = process.StandardOutput.ReadLine(); if (line.StartsWith(PREFIX)) yield return line.Substring(VERSION_START); } diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs index 1c7c6cd2a..bcadfa8de 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs @@ -18,7 +18,7 @@ public static IEnumerable FindRuntimes() foreach (var framework in FindExtremelyOldDotNetFrameworkVersions()) yield return framework; - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\NET Framework Setup\NDP"); + using RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\NET Framework Setup\NDP"); if (key != null) { foreach (string name in key.GetSubKeyNames()) @@ -48,7 +48,8 @@ public static IEnumerable FindRuntimes() private static IEnumerable FindExtremelyOldDotNetFrameworkVersions() { - RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\.NETFramework\policy\v1.0"); + using RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\.NETFramework\policy\v1.0"); + if (key == null) yield break; diff --git a/src/NUnitEngine/nunit.engine/Services/Service.cs b/src/NUnitEngine/nunit.engine/Services/Service.cs index 814dbbb28..1eb897c34 100644 --- a/src/NUnitEngine/nunit.engine/Services/Service.cs +++ b/src/NUnitEngine/nunit.engine/Services/Service.cs @@ -16,7 +16,7 @@ public abstract class Service : IService, IDisposable /// /// The ServiceContext /// - public IServiceLocator ServiceContext { get; set; } + public IServiceLocator? ServiceContext { get; set; } /// /// Gets the ServiceStatus of this service diff --git a/src/NUnitEngine/nunit.engine/Services/ServiceContext.cs b/src/NUnitEngine/nunit.engine/Services/ServiceContext.cs index 902d000ec..728a1edea 100644 --- a/src/NUnitEngine/nunit.engine/Services/ServiceContext.cs +++ b/src/NUnitEngine/nunit.engine/Services/ServiceContext.cs @@ -17,7 +17,7 @@ public ServiceContext() ServiceManager = new ServiceManager(); } - public ServiceManager ServiceManager { get; private set; } + public ServiceManager ServiceManager { get; } public int ServiceCount { get { return ServiceManager.ServiceCount; } } @@ -29,7 +29,7 @@ public void Add(IService service) public T GetService() where T : class { - return ServiceManager.GetService(typeof(T)) as T; + return (T)ServiceManager.GetService(typeof(T)); } public object GetService(Type serviceType) diff --git a/src/NUnitEngine/nunit.engine/Services/ServiceManager.cs b/src/NUnitEngine/nunit.engine/Services/ServiceManager.cs index 639b4515c..547d458da 100644 --- a/src/NUnitEngine/nunit.engine/Services/ServiceManager.cs +++ b/src/NUnitEngine/nunit.engine/Services/ServiceManager.cs @@ -24,7 +24,7 @@ public class ServiceManager : IDisposable public IService GetService( Type serviceType ) { - IService theService = null; + IService? theService = null; if (_serviceIndex.ContainsKey(serviceType)) theService = _serviceIndex[serviceType]; @@ -40,9 +40,13 @@ public IService GetService( Type serviceType ) } if (theService == null) - log.Error(string.Format("Requested service {0} was not found", serviceType.FullName)); - else - log.Debug(string.Format("Request for service {0} satisfied by {1}", serviceType.Name, theService.GetType().Name)); + { + string message = $"Requested service {serviceType.FullName} was not found"; + log.Error(message); + throw new NUnitEngineException(message); + } + + log.Debug(string.Format("Request for service {0} satisfied by {1}", serviceType.Name, theService.GetType().Name)); return theService; } @@ -127,8 +131,7 @@ protected virtual void Dispose(bool disposing) if (disposing) foreach (IService service in _services) { - IDisposable disposable = service as IDisposable; - if (disposable != null) + if (service is IDisposable disposable) disposable.Dispose(); } diff --git a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs index 1a7a1e0d2..c235735bc 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs @@ -8,6 +8,7 @@ using NUnit.Engine.Internal; using NUnit.Engine.Communication.Transports.Remoting; using NUnit.Engine.Communication.Transports.Tcp; +using System.Diagnostics.CodeAnalysis; namespace NUnit.Engine.Services { @@ -28,8 +29,8 @@ public partial class TestAgency : ITestAgency, IService private readonly AgentStore _agentStore = new AgentStore(); - private IRuntimeFrameworkService _runtimeService; - private IAvailableRuntimes _availableRuntimeService; + private IRuntimeFrameworkService? _runtimeService; + private IAvailableRuntimes? _availableRuntimeService; // Transports used for various target runtimes private TestAgencyRemotingTransport _remotingTransport; // .NET Framework @@ -51,8 +52,11 @@ public void Register(ITestAgent agent) _agentStore.Register(agent); } - public ITestAgent GetAgent(TestPackage package) + public ITestAgent? GetAgent(TestPackage package) { + if (_runtimeService == null || _availableRuntimeService == null) + throw new InvalidOperationException("TestAgency needs to be Started first"); + // Target Runtime must be specified by this point string runtimeSetting = package.GetSetting(EnginePackageSettings.TargetRuntimeFramework, ""); Guard.OperationValid(runtimeSetting.Length > 0, "LaunchAgentProcess called with no runtime specified"); @@ -75,7 +79,7 @@ public ITestAgent GetAgent(TestPackage package) var agentName = targetRuntime.Id + (runAsX86 ? "-x86" : "") + "-agent"; package.AddSetting("SelectedAgentName", agentName); - agentProcess.Exited += (sender, e) => OnAgentExit((Process)sender, agentId); + agentProcess.Exited += (sender, e) => OnAgentExit((Process)sender!, agentId); agentProcess.Start(); log.Debug("Launched Agent process {0} - see nunit-agent_{0}.log", agentProcess.Id); @@ -110,7 +114,7 @@ public ITestAgent GetAgent(TestPackage package) return null; } - internal bool IsAgentProcessActive(Guid agentId, out Process process) + internal bool IsAgentProcessActive(Guid agentId, out Process? process) { return _agentStore.IsAgentProcessActive(agentId, out process); } @@ -162,7 +166,7 @@ private void OnAgentExit(Process process, Guid agentId) throw new NUnitEngineException(errorMsg); } - public IServiceLocator ServiceContext { get; set; } + public IServiceLocator? ServiceContext { get; set; } public ServiceStatus Status { get; private set; } @@ -181,19 +185,17 @@ public void StopService() } } + [MemberNotNull(nameof(_runtimeService), nameof(_availableRuntimeService))] public void StartService() { - _runtimeService = ServiceContext.GetService(); - _availableRuntimeService = ServiceContext.GetService(); - - if (_runtimeService == null || _availableRuntimeService == null) - { - Status = ServiceStatus.Error; - return; - } - try { + if (ServiceContext == null) + throw new InvalidOperationException("ServiceContext is required for TestAgency"); + + _runtimeService = ServiceContext.GetService(); + _availableRuntimeService = ServiceContext.GetService(); + _remotingTransport.Start(); _tcpTransport.Start(); Status = ServiceStatus.Started; diff --git a/src/NUnitEngine/nunit.engine/Services/TestFilterBuilder.cs b/src/NUnitEngine/nunit.engine/Services/TestFilterBuilder.cs index 6dc437898..cc2cb4612 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestFilterBuilder.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestFilterBuilder.cs @@ -11,7 +11,7 @@ namespace NUnit.Engine public class TestFilterBuilder : ITestFilterBuilder { private List _testList = new List(); - private string _whereClause; + private string? _whereClause; /// /// Add a test to be selected @@ -51,7 +51,7 @@ public TestFilter GetFilter() if (_whereClause != null) - filter.Append(new TestSelectionParser().Parse(_whereClause)); + filter.Append(TestSelectionParser.Parse(_whereClause)); filter.Append(""); diff --git a/src/NUnitEngine/nunit.engine/Services/TestRunnerFactory.cs b/src/NUnitEngine/nunit.engine/Services/TestRunnerFactory.cs index 0a5c92edd..6a0790453 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestRunnerFactory.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestRunnerFactory.cs @@ -1,5 +1,7 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt +using System; +using System.Diagnostics.CodeAnalysis; using NUnit.Engine.Internal; using NUnit.Engine.Runners; @@ -12,17 +14,28 @@ namespace NUnit.Engine.Services /// public class TestRunnerFactory : Service, ITestRunnerFactory { - private IProjectService _projectService; + private IProjectService? _projectService; public override void StartService() { - // TestRunnerFactory requires the ProjectService - _projectService = ServiceContext.GetService(); + try + { + if (ServiceContext == null) + throw new InvalidOperationException("Only services that have a ServiceContext can be started."); + + // TestRunnerFactory requires the ProjectService + _projectService = ServiceContext.GetService(); - // Anything returned from ServiceContext is known to be an IService - Status = _projectService != null && ((IService)_projectService).Status == ServiceStatus.Started - ? ServiceStatus.Started - : ServiceStatus.Error; + // Anything returned from ServiceContext is known to be an IService + Status = ((IService)_projectService).Status == ServiceStatus.Started + ? ServiceStatus.Started + : ServiceStatus.Error; + } + catch + { + Status = ServiceStatus.Error; + throw; + } } /// @@ -35,6 +48,9 @@ public override void StartService() /// A TestRunner public ITestEngineRunner MakeTestRunner(TestPackage package) { + if (ServiceContext == null) + throw new InvalidOperationException("ServiceContext not set."); + #if !NETFRAMEWORK if (package.SubPackages.Count > 1) return new AggregatingTestRunner(ServiceContext, package); @@ -53,7 +69,7 @@ public ITestEngineRunner MakeTestRunner(TestPackage package) } #else if (package.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, "").StartsWith("Unmanaged,")) - return new UnmanagedExecutableTestRunner(package.FullName); + return new UnmanagedExecutableTestRunner(package.FullName ?? "Package Suite"); bool isNested = false; foreach (TestPackage subPackage in package.SubPackages) diff --git a/src/NUnitEngine/nunit.engine/Services/TestSelectionParser.cs b/src/NUnitEngine/nunit.engine/Services/TestSelectionParser.cs index 387836edf..f90109e5d 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestSelectionParser.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestSelectionParser.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text; // Missing XML Docs @@ -11,7 +12,7 @@ namespace NUnit.Engine { public class TestSelectionParser { - private Tokenizer _tokenizer; + private readonly Tokenizer _tokenizer; private static readonly Token LPAREN = new Token(TokenKind.Symbol, "("); private static readonly Token RPAREN = new Token(TokenKind.Symbol, ")"); @@ -38,10 +39,20 @@ public class TestSelectionParser private static readonly Token EOF = new Token(TokenKind.Eof); - public string Parse(string input) + public static string Parse(string input) { - _tokenizer = new Tokenizer(input); + var parser = new TestSelectionParser(new Tokenizer(input)); + return parser.Parse(); + } + + private TestSelectionParser(Tokenizer tokenizer) + { + _tokenizer = tokenizer; + } + + private string Parse() + { if (_tokenizer.LookAhead == EOF) throw new TestSelectionParserException("No input provided for test selection."); @@ -55,7 +66,7 @@ public string Parse(string input) /// Parse a single term or an or expression, returning the xml /// /// - public string ParseFilterExpression() + private string ParseFilterExpression() { var terms = new List(); terms.Add(ParseFilterTerm()); @@ -82,7 +93,7 @@ public string ParseFilterExpression() /// /// Parse a single element or an and expression and return the xml /// - public string ParseFilterTerm() + private string ParseFilterTerm() { var elements = new List(); elements.Add(ParseFilterElement()); @@ -110,7 +121,7 @@ public string ParseFilterTerm() /// Parse a single filter element such as a category expression /// and return the xml representation of the filter. /// - public string ParseFilterElement() + private string ParseFilterElement() { if (LookingAt(LPAREN, NOT_OP)) return ParseExpressionInParentheses(); @@ -144,7 +155,7 @@ public string ParseFilterElement() private static string EmitFilterElement(Token lhs, Token op, Token rhs) { - string fmt = null; + string? fmt = null; if (op == EQ_OP1 || op == EQ_OP2) fmt = "<{0}>{1}"; @@ -162,7 +173,7 @@ private static string EmitFilterElement(Token lhs, Token op, Token rhs) private static string EmitPropertyElement(Token lhs, Token op, Token rhs) { - string fmt = null; + string? fmt = null; if (op == EQ_OP1 || op == EQ_OP2) fmt = "{1}"; diff --git a/src/NUnitEngine/nunit.engine/Services/Tokenizer.cs b/src/NUnitEngine/nunit.engine/Services/Tokenizer.cs index e8d4fd4c5..062fcdbe4 100644 --- a/src/NUnitEngine/nunit.engine/Services/Tokenizer.cs +++ b/src/NUnitEngine/nunit.engine/Services/Tokenizer.cs @@ -34,9 +34,9 @@ public Token(TokenKind kind, string text) public int Pos { get; set; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj is Token && this == (Token)obj; + return obj is Token token && this == token; } public override int GetHashCode() @@ -51,15 +51,12 @@ public override string ToString() : Kind.ToString(); } - public static bool operator ==(Token t1, Token t2) + public static bool operator ==(Token? t1, Token? t2) { - bool t1Null = ReferenceEquals(t1, null); - bool t2Null = ReferenceEquals(t2, null); - - if (t1Null && t2Null) + if (ReferenceEquals(t1, t2)) return true; - if (t1Null || t2Null) + if (t1 is null || t2 is null) return false; return t1.Kind == t2.Kind && t1.Text == t2.Text; @@ -86,7 +83,7 @@ public class Tokenizer private const string WORD_BREAK_CHARS = "=!()&|"; private readonly string[] DOUBLE_CHAR_SYMBOLS = new string[] { "==", "=~", "!=", "!~", "&&", "||" }; - private Token _lookahead; + private Token? _lookahead; public Tokenizer(string input) { diff --git a/src/NUnitEngine/nunit.engine/nunit.engine.csproj b/src/NUnitEngine/nunit.engine/nunit.engine.csproj index d699651e1..0e58a3f5f 100644 --- a/src/NUnitEngine/nunit.engine/nunit.engine.csproj +++ b/src/NUnitEngine/nunit.engine/nunit.engine.csproj @@ -10,6 +10,8 @@ true + + NUnit Engine NUnit Engine ($(TargetFramework)) diff --git a/src/Nullable.props b/src/Nullable.props new file mode 100644 index 000000000..08f930068 --- /dev/null +++ b/src/Nullable.props @@ -0,0 +1,25 @@ + + + + enable + + true + + + + + 8.0.0 + false + + + + + + + + + + + + + diff --git a/src/TestData/FakeExtensions/FakeExtensions.cs b/src/TestData/FakeExtensions/FakeExtensions.cs index fa76265d2..f0ac2be6e 100644 --- a/src/TestData/FakeExtensions/FakeExtensions.cs +++ b/src/TestData/FakeExtensions/FakeExtensions.cs @@ -135,7 +135,7 @@ public string Load(string testAssemblyPath, IDictionary settings throw new NotImplementedException(); } - public string Run(ITestEventListener listener, string filter) + public string Run(ITestEventListener? listener, string filter) { throw new NotImplementedException(); } diff --git a/src/TestData/FakeExtensions/FakeExtensions.csproj b/src/TestData/FakeExtensions/FakeExtensions.csproj index 066d31608..135b138f8 100644 --- a/src/TestData/FakeExtensions/FakeExtensions.csproj +++ b/src/TestData/FakeExtensions/FakeExtensions.csproj @@ -8,6 +8,8 @@ ..\..\..\bin\$(Configuration)\fakes + +