From f9fd4a766718a51e5e0c132071bd620e2aa62f26 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Sun, 23 Feb 2025 08:59:25 -0800 Subject: [PATCH] Allow enabling and disabling extensions --- package-tests.cake | 17 ++++++-- .../nunit3-console.tests/CommandLineTests.cs | 43 +++++++++---------- .../ConsoleRunnerTests.cs | 11 ++++- .../nunit3-console/ConsoleOptions.cs | 33 +++++++++++--- .../nunit3-console/ConsoleRunner.cs | 39 ++++++++++++----- .../nunit3-console/ConsoleTests.nunit | 9 ++++ .../RequiredExtensionException.cs | 22 ++-------- .../nunit3-console/nunit3-console.csproj | 7 +++ 8 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 src/NUnitConsole/nunit3-console/ConsoleTests.nunit diff --git a/package-tests.cake b/package-tests.cake index 6dc4d8a1f..7bfac7afa 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -319,12 +319,23 @@ public static class PackageTests }); // TeamCity Event Listener Test - StandardAndZipLists.Add(new PackageTest(1, "TeamCityListenerTest") + StandardAndZipLists.Add(new PackageTest(1, "TeamCityListenerTest1") { - Description = "Run mock-assembly with --teamcity enabled", + Description = "Run mock-assembly with --teamcity option", Arguments = "testdata/net462/mock-assembly.dll --teamcity", ExpectedResult = new MockAssemblyExpectedResult("net-4.6.2"), - ExtensionsNeeded = new[] { KnownExtensions.TeamCityEventListener } + ExtensionsNeeded = new[] { KnownExtensions.TeamCityEventListener }, + OutputCheck = new OutputContains("##teamcity") + }); + + // TeamCity Event Listener Test + StandardAndZipLists.Add(new PackageTest(1, "TeamCityListenerTest2") + { + Description = "Run mock-assembly with --enable teamcity option", + Arguments = "testdata/net462/mock-assembly.dll --enable:NUnit.Engine.Listeners.TeamCityEventListener", + ExpectedResult = new MockAssemblyExpectedResult("net-4.6.2"), + ExtensionsNeeded = new[] { KnownExtensions.TeamCityEventListener }, + OutputCheck = new OutputContains("##teamcity") }); // V2 Framework Driver Tests diff --git a/src/NUnitConsole/nunit3-console.tests/CommandLineTests.cs b/src/NUnitConsole/nunit3-console.tests/CommandLineTests.cs index 7b9770b39..caf100636 100644 --- a/src/NUnitConsole/nunit3-console.tests/CommandLineTests.cs +++ b/src/NUnitConsole/nunit3-console.tests/CommandLineTests.cs @@ -142,7 +142,6 @@ public void NoInputFiles() [TestCase("WaitBeforeExit", "wait")] [TestCase("NoHeader", "noheader|noh")] [TestCase("DisposeRunners", "dispose-runners")] - [TestCase("TeamCity", "teamcity")] [TestCase("SkipNonTestAssemblies", "skipnontestassemblies")] [TestCase("NoResult", "noresult")] #if NETFRAMEWORK @@ -289,6 +288,9 @@ public void CanRecognizeIntOptions(string propertyName, string pattern) [TestCase("--test-name-format")] [TestCase("--params")] [TestCase("--encoding")] + [TestCase("--extensionDirectory")] + [TestCase("--enable")] + [TestCase("--disable")] #if NETFRAMEWORK [TestCase("--process")] [TestCase("--domain")] @@ -595,26 +597,6 @@ public void ExploreOptionWithFilePathUsingEqualSign() Assert.That(options.ExploreOutputSpecifications[0].OutputPath, Is.EqualTo("C:/nunit/tests/bin/Debug/console-test.xml")); } - [TestCase(true, true)] - [TestCase(false, false)] - public void ShouldSetTeamCityFlagAccordingToArgsAndDefaults(bool hasTeamcityInCmd, bool expectedTeamCity) - { - // Given - List args = new List { "tests.dll" }; - if (hasTeamcityInCmd) - { - args.Add("--teamcity"); - } - - ConsoleOptions options = ConsoleMocks.Options(args.ToArray()); - - // When - var actualTeamCity = options.TeamCity; - - // Then - Assert.That(expectedTeamCity, Is.EqualTo(actualTeamCity)); - } - [Test] public void ShouldNotFailOnEmptyLine() { @@ -829,6 +811,7 @@ public void DeprecatedLabelsOptionsAreReplacedCorrectly(string oldOption, string Assert.That(options.DisplayTestLabels, Is.EqualTo(newOption)); } + [Test] public void UserExtensionDirectoryTest() { ConsoleOptions options = ConsoleMocks.Options("--extensionDirectory=/a/b/c"); @@ -836,6 +819,22 @@ public void UserExtensionDirectoryTest() Assert.That(options.ExtensionDirectories.Contains("/a/b/c")); } + [Test] + public void EnableExtensionTest() + { + ConsoleOptions options = ConsoleMocks.Options("--enable=NUnit.Engine.Listeners.TeamCityEventListener"); + Assert.That(options.Validate); + Assert.That(options.EnableExtensions.Contains("NUnit.Engine.Listeners.TeamCityEventListener")); + } + + [Test] + public void DisableExtensionTest() + { + ConsoleOptions options = ConsoleMocks.Options("--disable=NUnit.Engine.Listeners.TeamCityEventListener"); + Assert.That(options.Validate); + Assert.That(options.DisableExtensions.Contains("NUnit.Engine.Listeners.TeamCityEventListener")); + } + private static IFileSystem GetFileSystemContainingFile(string fileName) { var fileSystem = new VirtualFileSystem(); @@ -853,7 +852,7 @@ private static FieldInfo GetFieldInfo(string fieldName) private static PropertyInfo GetPropertyInfo(string propertyName) { PropertyInfo property = typeof(ConsoleOptions).GetProperty(propertyName); - Assert.That(property, Is.Not.Null, "The property '{0}' is not defined", propertyName); + Assert.That(property, Is.Not.Null, $"The property '{propertyName}' is not defined"); return property; } } diff --git a/src/NUnitConsole/nunit3-console.tests/ConsoleRunnerTests.cs b/src/NUnitConsole/nunit3-console.tests/ConsoleRunnerTests.cs index 17f038de4..7c45fa7ee 100644 --- a/src/NUnitConsole/nunit3-console.tests/ConsoleRunnerTests.cs +++ b/src/NUnitConsole/nunit3-console.tests/ConsoleRunnerTests.cs @@ -44,7 +44,16 @@ public void ThrowsRequiredExtensionExceptionWhenTeamcityOptionIsSpecifiedButNotA var ex = Assert.Throws( () => new ConsoleRunner(_testEngine, ConsoleMocks.Options("mock-assembly.dll", "--teamcity"), new ColorConsoleWriter())); - Assert.That(ex, Has.Message.Contains("teamcity")); + Assert.That(ex, Has.Message.EqualTo("Required extension 'NUnit.Engine.Listeners.TeamCityEventListener' is not installed.")); + } + + [Test] + public void ThrowsRequiredExtensionExceptionWhenEnableOptionSpecifiesUnavailableExtension() + { + var ex = Assert.Throws( + () => new ConsoleRunner(_testEngine, ConsoleMocks.Options("mock-assembly.dll", "--enable:Not.An.Extension"), new ColorConsoleWriter())); + + Assert.That(ex, Has.Message.EqualTo("Required extension 'Not.An.Extension' is not installed.")); } } diff --git a/src/NUnitConsole/nunit3-console/ConsoleOptions.cs b/src/NUnitConsole/nunit3-console/ConsoleOptions.cs index 648e76404..7875a7eb6 100644 --- a/src/NUnitConsole/nunit3-console/ConsoleOptions.cs +++ b/src/NUnitConsole/nunit3-console/ConsoleOptions.cs @@ -47,6 +47,9 @@ internal ConsoleOptions(IFileSystem fileSystem, params string[] args) public bool ListExtensions { get; private set; } + public List EnableExtensions { get; private set; } = new List(); + public List DisableExtensions { get; private set; } = new List(); + // Additional directories to be used to search for user extensions public List ExtensionDirectories { get; } = new List(); @@ -83,8 +86,6 @@ internal ConsoleOptions(IFileSystem fileSystem, params string[] args) public bool NoColor { get; private set; } - public bool TeamCity { get; private set; } - public string OutFile { get; private set; } public bool OutFileSpecified { get { return OutFile != null; } } @@ -301,9 +302,6 @@ private void ConfigureOptions() this.Add("trace=", "Set internal trace {LEVEL}.\nValues: Off, Error, Warning, Info, Verbose (Debug)", 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); - this.Add("noheader|noh", "Suppress display of program information at start of run.", v => NoHeader = v != null); @@ -381,8 +379,29 @@ private void ConfigureOptions() this.Add("list-extensions", "List all extension points and the extensions for each.", v => ListExtensions = v != null); - this.Add("extensionDirectory=", "Specifies an additional directory to be examined for extensions. May be repeated.", - v => { ExtensionDirectories.Add(Path.GetFullPath(v)); }); + this.Add("extensionDirectory=", "Specifies an additional directory to be examined for extensions. May be repeated.", v => + { + string dir = parser.RequiredValue(v, "--extensionDirectory"); + if (dir != null) + ExtensionDirectories.Add(dir); + }); + + this.Add("teamcity", "Turns on use of TeamCity service messages. TeamCity engine extension is required.", + v => EnableExtensions.Add("NUnit.Engine.Listeners.TeamCityEventListener")); //TeamCity = v != null); + + this.Add("enable=", "Enables the specified extension. May be repeated.", v => + { + string extension = parser.RequiredValue(v, "--enable"); + if (!string.IsNullOrEmpty(extension)) + EnableExtensions.Add(extension); + }); + + this.Add("disable=", "Disables the specified extension. May be repeated.", v => + { + string extension = parser.RequiredValue(v, "--disable"); + if (!string.IsNullOrEmpty(extension)) + DisableExtensions.Add(extension); + }); 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"))); diff --git a/src/NUnitConsole/nunit3-console/ConsoleRunner.cs b/src/NUnitConsole/nunit3-console/ConsoleRunner.cs index 01a72bdd5..0756d5fae 100644 --- a/src/NUnitConsole/nunit3-console/ConsoleRunner.cs +++ b/src/NUnitConsole/nunit3-console/ConsoleRunner.cs @@ -73,7 +73,7 @@ public ConsoleRunner(ITestEngine engine, ConsoleOptions options, ExtendedTextWri _extensionService.FindExtensionAssemblies(extensionDirectory); foreach (string extensionDirectory in _options.ExtensionDirectories) - _extensionService.FindExtensionAssemblies(extensionDirectory); + _extensionService.FindExtensionAssemblies(extensionDirectory); _workDirectory = options.WorkDirectory; if (_workDirectory != null) @@ -81,24 +81,41 @@ public ConsoleRunner(ITestEngine engine, ConsoleOptions options, ExtendedTextWri else _workDirectory = null; - if (_options.TeamCity || RunningUnderTeamCity) + // Attempt to enable extensions as requested by the user + foreach (string typeName in options.EnableExtensions) { - bool teamcityInstalled = false; - foreach (var node in _extensionService.GetExtensionNodes(EVENT_LISTENER_EXTENSION_PATH)) - if (teamcityInstalled = node.TypeName == TEAMCITY_EVENT_LISTENER_FULLNAME) - break; + // Throw if requested extension is not installed + if (!IsExtensionInstalled(typeName)) + throw new RequiredExtensionException(typeName); - if (teamcityInstalled) - // Enable TeamCityEventListener immediately, before the console is redirected - _extensionService.EnableExtension("NUnit.Engine.Listeners.TeamCityEventListener", true); - else if (_options.TeamCity) - throw new RequiredExtensionException(TEAMCITY_EVENT_LISTENER_NAME, "--teamcity"); + EnableExtension(typeName); } + + // Also enable TeamCity extension under TeamCity, if it is installed + if (RunningUnderTeamCity && IsExtensionInstalled(TEAMCITY_EVENT_LISTENER_FULLNAME)) + EnableExtension(TEAMCITY_EVENT_LISTENER_FULLNAME); + + // Disable extensions as requested by the user, ignoring any not installed + foreach (string typeName in options.DisableExtensions) + if (IsExtensionInstalled(typeName)) + DisableExtension(typeName); } private bool RunningUnderTeamCity => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME")); + private bool IsExtensionInstalled(string typeName) + { + foreach (var node in _extensionService.Extensions) + if (node.TypeName == typeName) return true; + + return false; + } + + private void EnableExtension(string name) => _extensionService.EnableExtension(name, true); + + private void DisableExtension(string name) => _extensionService.EnableExtension(name, false); + /// /// Executes tests according to the provided commandline options. diff --git a/src/NUnitConsole/nunit3-console/ConsoleTests.nunit b/src/NUnitConsole/nunit3-console/ConsoleTests.nunit new file mode 100644 index 000000000..be1fa8efe --- /dev/null +++ b/src/NUnitConsole/nunit3-console/ConsoleTests.nunit @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/NUnitConsole/nunit3-console/RequiredExtensionException.cs b/src/NUnitConsole/nunit3-console/RequiredExtensionException.cs index 38de0082a..d60873d2e 100644 --- a/src/NUnitConsole/nunit3-console/RequiredExtensionException.cs +++ b/src/NUnitConsole/nunit3-console/RequiredExtensionException.cs @@ -11,20 +11,12 @@ namespace NUnit.ConsoleRunner /// public class RequiredExtensionException : Exception { - private static string Message1(string extensionName) => $"Required extension {extensionName} is not installed."; - private static string Message2(string extensionName, string option) => $"Option {option} specified but {extensionName} is not installed."; + private static string BuildMessage(string extensionName) => $"Required extension '{extensionName}' is not installed."; /// /// Construct with the name of an extension /// - public RequiredExtensionException(string extensionName) : base(Message1(extensionName)) - { - } - - /// - /// Construct with the name of an extension and the command-line option requiring that extension. - /// - public RequiredExtensionException(string extensionName, string option) : base(Message2(extensionName, option)) + public RequiredExtensionException(string extensionName) : base(BuildMessage(extensionName)) { } @@ -32,15 +24,7 @@ public RequiredExtensionException(string extensionName, string option) : base(Me /// Construct with the name of an extension and inner exception /// public RequiredExtensionException(string extensionName, Exception innerException) - : base(Message1(extensionName), innerException) - { - } - - /// - /// Construct with the name of an extension, a command-line option and inner exception - /// - public RequiredExtensionException(string extensionName, string option, Exception innerException) - : base(Message2(extensionName, option), innerException) + : base(BuildMessage(extensionName), innerException) { } } diff --git a/src/NUnitConsole/nunit3-console/nunit3-console.csproj b/src/NUnitConsole/nunit3-console/nunit3-console.csproj index feef472f2..29eabe010 100644 --- a/src/NUnitConsole/nunit3-console/nunit3-console.csproj +++ b/src/NUnitConsole/nunit3-console/nunit3-console.csproj @@ -26,10 +26,17 @@ ..\..\..\nunit.ico + + + + + + PreserveNewest +