diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests2.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests2.cs deleted file mode 100644 index dd05a39d0..000000000 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests2.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using NSubstitute; -using NUnit.Engine.Internal.FileSystemAccess; -using NUnit.Framework; -using System.IO; - -namespace NUnit.Engine.Internal.Tests -{ - /// - /// Tests the implementation of . - /// - /// All tests in this fixture modify the file-system. - [TestFixture, Category("WritesToDisk"), NonParallelizable] - public class AddinsFileReaderTests2 - { - private readonly string tempFileLocation; - - public AddinsFileReaderTests2() - { - string[] content = new string[] - { - "# This line is a comment and is ignored. The next (blank) line is ignored as well.", - "", - "*.dll # include all dlls in the same directory", - "addins/*.dll # include all dlls in the addins directory too", - "special/myassembly.dll # include a specific dll in a special directory", - "some/other/directory/ # process another directory, which may contain its own addins file", - "# note that an absolute path is allowed, but is probably not a good idea in most cases", - "/unix/absolute/directory" - }; - - this.tempFileLocation = Path.GetTempFileName(); - - using (var writer = new StreamWriter(this.tempFileLocation)) - { - foreach (var line in content) - { - writer.WriteLine(line); - } - } - } - - [TearDown] - public void DeleteTestFile() - { - File.Delete(this.tempFileLocation); - } - - [Test] - public void Read_IFile() - { - var reader = new AddinsFileReader(); - var file = Substitute.For(); - file.FullName.Returns(this.tempFileLocation); - - var result = reader.Read(file); - - Assert.That(result, Has.Count.EqualTo(5)); - Assert.That(result, Contains.Item("*.dll")); - Assert.That(result, Contains.Item("addins/*.dll")); - Assert.That(result, Contains.Item("special/myassembly.dll")); - Assert.That(result, Contains.Item("some/other/directory/")); - Assert.That(result, Contains.Item("/unix/absolute/directory")); - } - } -} diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs similarity index 51% rename from src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests.cs rename to src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs index 0c46db71e..25d4363b1 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileReaderTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/AddinsFileTests.cs @@ -5,28 +5,27 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; namespace NUnit.Engine.Internal.Tests { /// - /// Tests the implementation of . + /// Tests the implementation of . /// [TestFixture] - public class AddinsFileReaderTests + public class AddinsFileTests { [Test] public void Read_IFile_Null() { - var reader = new AddinsFileReader(); - - Assert.That(() => reader.Read((IFile)null), Throws.ArgumentNullException); + Assert.That(() => AddinsFile.Read((IFile)null), Throws.ArgumentNullException); } [Test] public void Read_Stream() { - var input = string.Join(Environment.NewLine, new string[] + var content = new[] { "# This line is a comment and is ignored. The next (blank) line is ignored as well.", "", @@ -36,59 +35,59 @@ public void Read_Stream() "some/other/directory/ # process another directory, which may contain its own addins file", "# note that an absolute path is allowed, but is probably not a good idea in most cases", "/unix/absolute/directory" - }); - - IEnumerable result; + }; - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input))) + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(string.Join(Environment.NewLine, content)))) { - // Act - result = AddinsFileReader.Read(stream); + var result = AddinsFile.Read(stream); + + Assert.That(result, Has.Count.EqualTo(8)); + for (int i = 0; i < 8; i++) + Assert.That(result[i], Is.EqualTo( + new AddinsFileEntry(i + 1, content[i]))); } + } - Assert.That(result, Has.Count.EqualTo(5)); - Assert.That(result, Contains.Item("*.dll")); - Assert.That(result, Contains.Item("addins/*.dll")); - Assert.That(result, Contains.Item("special/myassembly.dll")); - Assert.That(result, Contains.Item("some/other/directory/")); - Assert.That(result, Contains.Item("/unix/absolute/directory")); + [Test] + public void Read_InvalidEntry() + { + var content = "// This is not valid"; + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) + { + Assert.That(() => AddinsFile.Read(stream), Throws.Exception); + } } [Test] [Platform("win")] public void Read_Stream_TransformBackslash_Windows() { - var input = string.Join(Environment.NewLine, new string[] - { - "c:\\windows\\absolute\\directory" - }); + var content = "c:\\windows\\absolute\\directory"; - IEnumerable result; - - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input))) + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) { - // Act - result = AddinsFileReader.Read(stream); - } + var result = AddinsFile.Read(stream); - Assert.That(result, Has.Count.EqualTo(1)); - Assert.That(result, Contains.Item("c:/windows/absolute/directory")); + Assert.That(result, Has.Count.EqualTo(1)); + Assert.That(result[0], Is.EqualTo(new AddinsFileEntry(1, content))); + Assert.That(result[0].Text, Is.EqualTo("c:/windows/absolute/directory")); + } } [Test] [Platform("linux,macosx,unix")] public void Read_Stream_TransformBackslash_NonWindows() { - IEnumerable result; + var content = "this/is/a\\ path\\ with\\ spaces/"; - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("this/is/a\\ path\\ with\\ spaces/"))) + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) { - // Act - result = AddinsFileReader.Read(stream); - } + var result = AddinsFile.Read(stream); - Assert.That(result, Has.Count.EqualTo(1)); - Assert.That(result, Contains.Item("this/is/a\\ path\\ with\\ spaces/")); + Assert.That(result, Has.Count.EqualTo(1)); + Assert.That(result[0], Is.EqualTo(new AddinsFileEntry(1, content))); + Assert.That(result[0].Text, Is.EqualTo(content)); + } } } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs deleted file mode 100644 index 19034cf35..000000000 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/Backports/PathTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using NUnit.Framework; - -namespace NUnit.Engine.Internal.Backports.Tests -{ - [TestFixture] - public sealed class PathTests - { - [Platform("win")] - [TestCase("c:\\", ExpectedResult = true)] - [TestCase("c:\\foo\\bar\\", ExpectedResult = true)] - [TestCase("c:/foo/bar/", ExpectedResult = true)] - [TestCase("c:\\foo\\bar", ExpectedResult = true)] - [TestCase("c:/foo/bar", ExpectedResult = true)] - [TestCase("c:bar\\", ExpectedResult = false)] - [TestCase("c:bar/", ExpectedResult = false)] - [TestCase("c:bar", ExpectedResult = false)] - [TestCase("ä:\\bar", ExpectedResult = false)] - [TestCase("ä://bar", ExpectedResult = false)] - [TestCase("\\\\server01\\foo", ExpectedResult = true)] - [TestCase("\\server01\\foo", ExpectedResult = false)] - [TestCase("c:", ExpectedResult = false)] - [TestCase("/foo/bar", ExpectedResult = false)] - [TestCase("/", ExpectedResult = false)] - [TestCase("\\a\\b", ExpectedResult = false)] - public bool IsPathFullyQualified_Windows(string path) - { - return Path.IsPathFullyQualified(path); - } - - [Platform("linux,macosx,unix")] - [TestCase("/foo/bar", ExpectedResult = true)] - [TestCase("/", ExpectedResult = true)] - [TestCase("/z", ExpectedResult = true)] - [TestCase("c:\\foo\\bar\\", ExpectedResult = false)] - [TestCase("c:/foo/bar/", ExpectedResult = false)] - [TestCase("c:\\foo\\bar", ExpectedResult = false)] - [TestCase("c:/foo/bar", ExpectedResult = false)] - [TestCase("c:bar\\", ExpectedResult = false)] - [TestCase("c:bar/", ExpectedResult = false)] - [TestCase("c:bar", ExpectedResult = false)] - [TestCase("ä:\\bar", ExpectedResult = false)] - [TestCase("ä://bar", ExpectedResult = false)] - [TestCase("\\\\server01\\foo", ExpectedResult = false)] - [TestCase("\\server01\\foo", ExpectedResult = false)] - [TestCase("c:", ExpectedResult = false)] - [TestCase("\\a\\b", ExpectedResult = false)] - public bool IsPathFullyQualified_NonWindows(string path) - { - return Path.IsPathFullyQualified(path); - } - - [Test] - public void IsPathFullyQualified_PathIsNull() - { - Assert.That(() => Path.IsPathFullyQualified(null), Throws.ArgumentNullException); - } - } -} diff --git a/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs index 44cc4799c..c8d6fd1d4 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Internal/PathUtilTests.cs @@ -63,9 +63,36 @@ public void IsFullyQualifiedWindowsPath_PathIsNull() { Assert.That(() => PathUtils.IsFullyQualifiedWindowsPath(null), Throws.ArgumentNullException); } + + [TestCase("X")] + [TestCase("X/")] + [TestCase("/X/")] + [TestCase("\\X\\")] + [TestCase("X/Y/Z")] + [TestCase("X/Y/Z/")] + [TestCase("/X/Y/Z")] + [TestCase("/X/Y/Z/")] + [TestCase("\\X\\Y\\Z\\")] + [TestCase("C:X/Y/Z/")] + [TestCase("C:/X/Y/Z")] + [TestCase("C:/X/Y/Z/")] + [TestCase("C:\\X\\Y\\Z\\")] + public void IsValidPath(string path) + { + Assert.That(PathUtils.IsValidPath(path), Is.True); + } + + [TestCase(":")] + [TestCase("?")] + [TestCase("*")] + [TestCase("// Spurious comment")] + public void IsValidPath_Fails(string path) + { + Assert.That(PathUtils.IsValidPath(path), Is.False); + } } - [TestFixture] + [TestFixture] public class PathUtilDefaultsTests : PathUtils { [Test] diff --git a/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs index c74972521..14afab18b 100644 --- a/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs +++ b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionManagerTests.cs @@ -37,39 +37,53 @@ public class ExtensionManagerTests }; private static readonly int[] KnownExtensionPointCounts = { 1, 1, 1, 2, 1, 1 }; + + private readonly string[] KnownExtensions = { + "NUnit.Engine.Tests.DummyFrameworkDriverExtension", + "NUnit.Engine.Tests.DummyProjectLoaderExtension", + "NUnit.Engine.Tests.DummyResultWriterExtension", + "NUnit.Engine.Tests.DummyEventListenerExtension", + "NUnit.Engine.Tests.DummyServiceExtension", + "NUnit.Engine.Tests.DummyDisabledExtension", + "NUnit.Engine.Tests.V2DriverExtension" + }; #pragma warning restore 414 [SetUp] - public void CreateService() + public void CreateExtensionManager() { _extensionManager = new ExtensionManager(); - // Rather than actually starting the service, which would result - // in finding the extensions actually in use on the current system, - // we simulate the start using this assemblies dummy extensions. + // Find actual extension points. _extensionManager.FindExtensionPoints(typeof(CoreEngine).Assembly); _extensionManager.FindExtensionPoints(typeof(ITestEngine).Assembly); + // Find dummy extensions in this test assembly. _extensionManager.FindExtensionsInAssembly(new ExtensionAssembly(GetType().Assembly.Location, false)); } [Test] - public void StartService_UseFileSystemAbstraction() + public void AllKnownExtensionPointsAreFound() { - var addinsReader = Substitute.For(); - var fileSystem = Substitute.For(); - var service = new ExtensionService(addinsReader, fileSystem); - var workingDir = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - - service.StartService(); + Assert.That(_extensionManager.ExtensionPoints.Select(ep => ep.Path), + Is.EquivalentTo(KnownExtensionPointPaths)); + } - fileSystem.Received().GetDirectory(workingDir); + [Test] + public void AllKnownExtensionsAreFound() + { + Assert.That(_extensionManager.Extensions.Select(ext => ext.TypeName), + Is.EquivalentTo(KnownExtensions)); } [Test] - public void AllExtensionPointsAreKnown() + public void AllKnownExtensionsAreEnabledAsRequired() { - Assert.That(_extensionManager.ExtensionPoints.Select(ep => ep.Path), Is.EquivalentTo(KnownExtensionPointPaths)); + foreach (var node in _extensionManager.Extensions) + { + var shouldBeEnabled = node.TypeName != "NUnit.Engine.Tests.DummyDisabledExtension"; + Assert.That(node.Enabled, Is.EqualTo(shouldBeEnabled)); + } } [Test, Sequential] @@ -94,25 +108,6 @@ public void CanGetExtensionPointByType( Assert.That(ep.TypeName, Is.EqualTo(type.FullName)); } -#pragma warning disable 414 - private static readonly string[] KnownExtensions = { - "NUnit.Engine.Tests.DummyFrameworkDriverExtension", - "NUnit.Engine.Tests.DummyProjectLoaderExtension", - "NUnit.Engine.Tests.DummyResultWriterExtension", - "NUnit.Engine.Tests.DummyEventListenerExtension", - "NUnit.Engine.Tests.DummyServiceExtension", - "NUnit.Engine.Tests.V2DriverExtension" - }; -#pragma warning restore 414 - - [TestCaseSource(nameof(KnownExtensions))] - public void CanListExtensions(string typeName) - { - Assert.That(_extensionManager.Extensions, - Has.One.Property(nameof(ExtensionNode.TypeName)).EqualTo(typeName) - .And.Property(nameof(ExtensionNode.Enabled)).True); - } - [Test, Sequential] public void ExtensionsAreAddedToExtensionPoint( [ValueSource(nameof(KnownExtensionPointPaths))] string path, @@ -280,477 +275,5 @@ private static string GetSiblingDirectory(string dir) var file = new FileInfo(typeof(ExtensionManagerTests).Assembly.Location); return Path.Combine(file.Directory.Parent.FullName, dir); } - - [Test] - public void StartService_ReadsAddinsFile() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile); - } - - [Test] - public void StartService_ReadsAddinsFilesFromMultipleDirectories() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var subdirectoryPath = Path.Combine(startDirectoryPath, "subdirectory"); - var subdirectory = Substitute.For(); - subdirectory.FullName.Returns(subdirectoryPath); - subdirectory.GetDirectories(Arg.Any(), Arg.Any()).Returns(new IDirectory[] { }); - subdirectory.GetFiles(Arg.Any()).Returns(new IFile[] { }); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - var addinsFile2 = Substitute.For(); - addinsFile2.Parent.Returns(subdirectory); - addinsFile2.FullName.Returns(Path.Combine(subdirectoryPath, "second.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - startDirectory.GetDirectories("subdirectory", SearchOption.TopDirectoryOnly).Returns(new[] { subdirectory }); - subdirectory.GetFiles(Arg.Any()).Returns(ci => (string)ci[0] == "*.addins" ? new[] { addinsFile2 } : new IFile[] { }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - fileSystem.GetDirectory(subdirectoryPath).Returns(subdirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "subdirectory/" }); - var sut = new ExtensionService(addinsReader, fileSystem); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile); - subdirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile2); - } - - [Test] - public void ProcessAddinsFile_RelativePaths() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns( - new[] - { - "path/to/directory/", - "directory2/", - "**/wildcard-directory/", - "path/to/file/file1.dll", - "file2.dll", - "**/wildcard-file.dll" - }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetDirectories(startDirectory, "path/to/directory/"); - directoryFinder.Received().GetDirectories(startDirectory, "directory2/"); - directoryFinder.Received().GetDirectories(startDirectory, "**/wildcard-directory/"); - directoryFinder.Received().GetFiles(startDirectory, "path/to/file/file1.dll"); - directoryFinder.Received().GetFiles(startDirectory, "file2.dll"); - directoryFinder.Received().GetFiles(startDirectory, "**/wildcard-file.dll"); - } - - [Test] - [Platform("win")] - public void ProcessAddinsFile_AbsolutePath_Windows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var metamorphosatorDirectoryPath = "c:/tools/metamorphosator"; - var metamorphosatorDirectory = Substitute.For(); - metamorphosatorDirectory.FullName.Returns(metamorphosatorDirectoryPath); - var toolsDirectoryPath = "d:/tools"; - var toolsDirectory = Substitute.For(); - toolsDirectory.FullName.Returns(toolsDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - fileSystem.GetDirectory(metamorphosatorDirectoryPath + "/").Returns(metamorphosatorDirectory); - fileSystem.GetDirectory("d:\\tools").Returns(toolsDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "c:/tools/metamorphosator/", "d:/tools/frobuscator.dll" }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetDirectories(metamorphosatorDirectory, string.Empty); - directoryFinder.Received().GetFiles(toolsDirectory, "frobuscator.dll"); - directoryFinder.DidNotReceive().GetDirectories(toolsDirectory, Arg.Is(s => s != "frobuscator.dll")); - } - - [Test] - [Platform("linux,macosx,unix")] - public void ProcessAddinsFile_AbsolutePath_NonWindows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var metamorphosatorDirectoryPath = "/tmp/tools/metamorphosator"; - var metamorphosatorDirectory = Substitute.For(); - metamorphosatorDirectory.FullName.Returns(metamorphosatorDirectoryPath); - var usrDirectoryPath = "/usr"; - var toolsDirectory = Substitute.For(); - toolsDirectory.FullName.Returns(usrDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - fileSystem.GetDirectory(metamorphosatorDirectoryPath + "/").Returns(metamorphosatorDirectory); - fileSystem.GetDirectory(usrDirectoryPath).Returns(toolsDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "/tmp/tools/metamorphosator/", "/usr/frobuscator.dll" }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetDirectories(metamorphosatorDirectory, string.Empty); - directoryFinder.Received().GetFiles(toolsDirectory, "frobuscator.dll"); - directoryFinder.DidNotReceive().GetDirectories(toolsDirectory, Arg.Is(s => s != "frobuscator.dll")); - } - - [Test] - [Platform("win")] - public void ProcessAddinsFile_InvalidAbsolutePathToFile_Windows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "/absolute/unix/path" }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetFiles(startDirectory, "/absolute/unix/path"); - } - - [Test] - [Platform("win")] - public void ProcessAddinsFile_InvalidAbsolutePathToDirectory_Windows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "/absolute/unix/path/" }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetDirectories(startDirectory, "/absolute/unix/path/"); - } - - [TestCase("c:/absolute/windows/path")] - [TestCase("c:\\absolute\\windows\\path")] - [TestCase("c:\\absolute\\windows\\path\\")] - [Platform("linux,macosx,unix")] - public void ProcessAddinsFile_InvalidAbsolutePathToFile_NonWindows(string windowsPath) - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { windowsPath }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetFiles(startDirectory, windowsPath); - } - - [TestCase("c:/absolute/windows/path/")] - [Platform("linux,macosx,unix")] - public void ProcessAddinsFile_InvalidAbsolutePathToDirectory_NonWindows(string windowsPath) - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { windowsPath }); - var directoryFinder = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - addinsReader.Received().Read(addinsFile); - directoryFinder.Received().GetDirectories(startDirectory, windowsPath); - } - - [Test] - public void StartService_ReadsMultipleAddinsFilesFromSingleDirectory() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var addinsFile1 = Substitute.For(); - addinsFile1.Parent.Returns(startDirectory); - addinsFile1.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile1 }); - var addinsFile2 = Substitute.For(); - addinsFile1.Parent.Returns(startDirectory); - addinsFile1.FullName.Returns(Path.Combine(startDirectoryPath, "test2.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile1, addinsFile2 }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - var sut = new ExtensionService(addinsReader, fileSystem); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile1); - addinsReader.Received().Read(addinsFile2); - } - - [Test] - public void ProcessAddinsFile_ReadsAddinsFileFromReferencedDirectory() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - var referencedDirectoryPath = Path.Combine(startDirectoryPath, "metamorphosator"); - var referencedDirectory = Substitute.For(); - referencedDirectory.FullName.Returns(referencedDirectoryPath); - referencedDirectory.Parent.Returns(startDirectory); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "test.addins")); - var referencedAddinsFile = Substitute.For(); - referencedAddinsFile.Parent.Returns(referencedDirectory); - referencedAddinsFile.FullName.Returns(Path.Combine(referencedDirectoryPath, "test2.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - referencedDirectory.GetFiles("*.addins").Returns(new[] { referencedAddinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(referencedDirectoryPath).Returns(referencedDirectory); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - addinsReader.Read(addinsFile).Returns(new[] { "./metamorphosator/" }); - var directoryFinder = Substitute.For(); - directoryFinder.GetDirectories(startDirectory, "./metamorphosator/").Returns(new[] { referencedDirectory }); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile); - referencedDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(referencedAddinsFile); - } - - [Test] - [Platform("win")] - public void ProcessAddinsFile_Issue915_AddinsFilePointsToContainingDirectory_Windows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - - // Faking the presence of the assembly is required to reproduce the error described in GitHub issue 915... - var testAssembly = Substitute.For(); - testAssembly.Parent.Returns(startDirectory); - testAssembly.FullName.Returns(typeof(ExtensionService).Assembly.Location); - startDirectory.GetFiles("*.dll").Returns(new[] { testAssembly }); - - var parentPath = new DirectoryInfo(startDirectoryPath).Parent.FullName; - var parentDirectory = Substitute.For(); - parentDirectory.FullName.Returns(parentPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "my.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - var addinsReader = Substitute.For(); - var addinsContent = new[] { - "./", - startDirectoryPath + Path.DirectorySeparatorChar, - $"..{Path.DirectorySeparatorChar}{Path.GetFileName(startDirectoryPath)}{Path.DirectorySeparatorChar}", - @"*\..\", - @"**\..\", - @"**\.\" - }; - addinsReader.Read(addinsFile).Returns(addinsContent); - var directoryFinder = Substitute.For(); - directoryFinder.GetDirectories(startDirectory, "./").Returns(new[] { startDirectory }); - directoryFinder.GetFiles(startDirectory, string.Empty).Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, $"..{Path.DirectorySeparatorChar}{Path.GetFileName(startDirectoryPath)}{Path.DirectorySeparatorChar}").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, @"*\..\").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, @"**\..\").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, @"**\.\").Returns(new[] { testAssembly }); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - startDirectory.DidNotReceive().GetFiles("*.dll"); - parentDirectory.DidNotReceive().GetFiles("*.dll"); - directoryFinder.Received().GetDirectories(startDirectory, "./"); - directoryFinder.Received().GetFiles(startDirectory, string.Empty); - directoryFinder.Received().GetFiles(startDirectory, $"..{Path.DirectorySeparatorChar}{Path.GetFileName(startDirectoryPath)}{Path.DirectorySeparatorChar}"); - directoryFinder.Received().GetFiles(startDirectory, @"*\..\"); - directoryFinder.Received().GetFiles(startDirectory, @"**\..\"); - directoryFinder.Received().GetFiles(startDirectory, @"**\.\"); - addinsReader.Received().Read(addinsFile); - } - - [Test] - [Platform("linux,macosx,unix")] - public void ProcessAddinsFile_Issue915_AddinsFilePointsToContainingDirectory_NonWindows() - { - // Arrange - var startDirectoryPath = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); - var startDirectory = Substitute.For(); - startDirectory.FullName.Returns(startDirectoryPath); - - // Faking the presence of the assembly is required to reproduce the error described in GitHub issue 915... - var testAssembly = Substitute.For(); - testAssembly.Parent.Returns(startDirectory); - testAssembly.FullName.Returns(typeof(ExtensionService).Assembly.Location); - startDirectory.GetFiles("*.dll").Returns(new[] { testAssembly }); - - var parentPath = new DirectoryInfo(startDirectoryPath).Parent.FullName; - var parentDirectory = Substitute.For(); - parentDirectory.FullName.Returns(parentPath); - var addinsFile = Substitute.For(); - addinsFile.Parent.Returns(startDirectory); - addinsFile.FullName.Returns(Path.Combine(startDirectoryPath, "my.addins")); - startDirectory.GetFiles("*.addins").Returns(new[] { addinsFile }); - var fileSystem = Substitute.For(); - fileSystem.GetDirectory(startDirectoryPath).Returns(startDirectory); - fileSystem.GetDirectory(startDirectoryPath + "/").Returns(startDirectory); - var addinsReader = Substitute.For(); - var addinsContent = new[] { - "./", - startDirectoryPath + "/", - $"../{Path.GetFileName(startDirectoryPath)}/", - "*/../", - "**/../", - "**/./" - }; - addinsReader.Read(addinsFile).Returns(addinsContent); - var directoryFinder = Substitute.For(); - directoryFinder.GetDirectories(startDirectory, "./").Returns(new[] { startDirectory }); - directoryFinder.GetFiles(startDirectory, string.Empty).Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, $"../{Path.GetFileName(startDirectoryPath)}/").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, "*/../").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, "**/../").Returns(new[] { testAssembly }); - directoryFinder.GetFiles(startDirectory, "**/./").Returns(new[] { testAssembly }); - var sut = new ExtensionService(addinsReader, fileSystem, directoryFinder); - - // Act - sut.StartService(); - - // Assert - startDirectory.Received().GetFiles("*.addins"); - addinsReader.Received().Read(addinsFile); - startDirectory.DidNotReceive().GetFiles("*.dll"); - parentDirectory.DidNotReceive().GetFiles("*.dll"); - directoryFinder.Received().GetDirectories(startDirectory, "./"); - directoryFinder.Received().GetDirectories(startDirectory, string.Empty); - directoryFinder.Received().GetDirectories(startDirectory, $"../{Path.GetFileName(startDirectoryPath)}/"); - directoryFinder.Received().GetDirectories(startDirectory, "*/../"); - directoryFinder.Received().GetDirectories(startDirectory, "**/../"); - directoryFinder.Received().GetDirectories(startDirectory, "**/./"); - } } } diff --git a/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionServiceTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionServiceTests.cs new file mode 100644 index 000000000..3f6a5664f --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core.tests/Services/ExtensionServiceTests.cs @@ -0,0 +1,106 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using NSubstitute; +using NUnit.Engine.Extensibility; +using NUnit.Engine.Internal; +using NUnit.Engine.Internal.FileSystemAccess; +using NUnit.Framework; + +namespace NUnit.Engine.Services.Tests +{ + public class ExtensionServiceTests + { + private ExtensionManager _extensionManager; + private ExtensionService _extensionService; + +#pragma warning disable 414 + private static readonly string[] KnownExtensionPointPaths = { + "/NUnit/Engine/TypeExtensions/IDriverFactory", + "/NUnit/Engine/TypeExtensions/IProjectLoader", + "/NUnit/Engine/TypeExtensions/IResultWriter", + "/NUnit/Engine/TypeExtensions/ITestEventListener", + "/NUnit/Engine/TypeExtensions/IService", + "/NUnit/Engine/NUnitV2Driver" + }; + + private static readonly Type[] KnownExtensionPointTypes = { + typeof(IDriverFactory), + typeof(IProjectLoader), + typeof(IResultWriter), + typeof(ITestEventListener), + typeof(IService), + typeof(IFrameworkDriver) + }; + + private static readonly int[] KnownExtensionPointCounts = { 1, 1, 1, 2, 1, 1 }; +#pragma warning restore 414 + + [SetUp] + public void CreateService() + { + _extensionManager = Substitute.For(); + _extensionService = new ExtensionService(_extensionManager); + } + + [Test] + public void StartServiceInitializesExtensionManager() + { + var workingDir = AssemblyHelper.GetDirectoryName(typeof(ExtensionService).Assembly); + + _extensionService.StartService(); + + _extensionManager.ReceivedWithAnyArgs().FindExtensionPoints(typeof(ExtensionService).Assembly, typeof(ITestEngine).Assembly); + _extensionManager.Received().FindExtensions(workingDir); + Assert.That(_extensionService.Status, Is.EqualTo(ServiceStatus.Started)); + } + + [Test] + public void GetExtensionPointCallsExtensionManager() + { + ((IExtensionService)_extensionService).GetExtensionPoint("SOMEPATH"); + _extensionManager.Received().GetExtensionPoint("SOMEPATH"); + } + + [Test] + public void GetExtensionNodesCallsExtensionManager() + { + ((IExtensionService)_extensionService).GetExtensionNodes("SOMEPATH"); + _extensionManager.Received().GetExtensionNodes("SOMEPATH"); + } + + [Test] + public void EnableExtensionCallsExtensionManager() + { + _extensionService.EnableExtension("TYPENAME", true); + _extensionManager.Received().EnableExtension("TYPENAME", true); + } + + [Test] + public void GetExtensionsTCallsExtensionManager() + { + _extensionService.GetExtensions(); + _extensionManager.Received().GetExtensions(); + } + + [Test] + public void GetExtensionNodeCallsExtensionManager() + { + _extensionService.GetExtensionNode("SOMEPATH"); + _extensionManager.Received().GetExtensionNode("SOMEPATH"); + } + + [Test] + public void GetExtensionNodesTCallsExtensionManager() + { + _extensionService.GetExtensionNodes(); + _extensionManager.Received().GetExtensionNodes(); + } + + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs index 6bc490cc8..5ab8ffa2f 100644 --- a/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs +++ b/src/NUnitEngine/nunit.engine.core/Drivers/NUnit2DriverFactory.cs @@ -3,6 +3,7 @@ #if NETFRAMEWORK using System; using System.Collections.Generic; +using System.Configuration.Assemblies; using System.Reflection; using NUnit.Engine.Extensibility; using NUnit.Engine.Internal; @@ -13,13 +14,13 @@ public class NUnit2DriverFactory : IDriverFactory { private const string NUNIT_FRAMEWORK = "nunit.framework"; private const string NUNITLITE_FRAMEWORK = "nunitlite"; - private ExtensionNode _driverNode; + private IExtensionNode _driverNode; // TODO: This should be a central service but for now it's local private ProvidedPathsAssemblyResolver _resolver; bool _resolverInstalled; - public NUnit2DriverFactory(ExtensionNode driverNode) + public NUnit2DriverFactory(IExtensionNode driverNode) { _driverNode = driverNode; _resolver = new ProvidedPathsAssemblyResolver(); @@ -55,7 +56,9 @@ public IFrameworkDriver GetDriver(AppDomain domain, AssemblyName reference) _resolver.AddPathFromFile(_driverNode.AssemblyPath); } - return _driverNode.CreateExtensionObject(domain) as IFrameworkDriver; + return AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap( + _driverNode.AssemblyPath, _driverNode.TypeName, + false, 0, null, null, null, null) as IFrameworkDriver; } } } diff --git a/src/NUnitEngine/nunit.engine.core/Internal/AddinsFile.cs b/src/NUnitEngine/nunit.engine.core/Internal/AddinsFile.cs new file mode 100644 index 000000000..6b090c318 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Internal/AddinsFile.cs @@ -0,0 +1,85 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using NUnit.Engine.Internal.FileSystemAccess; + +namespace NUnit.Engine.Internal +{ + internal class AddinsFile : List + { + public static AddinsFile Read(IFile file) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + + using (var stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return Read(stream, file.FullName); + } + } + + /// + /// Reads the content of an addins-file from a stream. + /// + /// Input stream. Must be readable and positioned at the beginning of the 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) + { + using (var reader = new StreamReader(stream)) + { + var addinsFile = new AddinsFile(); + + int lineNumber = 0; + while (!reader.EndOfStream) + { + var entry = new AddinsFileEntry(++lineNumber, reader.ReadLine()); + if (entry.Text != "" && !entry.IsValid) + { + string msg = $"Invalid Entry in {fullName ?? "addins file"}:\r\n {entry}"; + throw new InvalidOperationException(msg); + } + + addinsFile.Add(entry); + } + + return addinsFile; + } + } + + private AddinsFile() { } + + public override string ToString() + { + var sb = new StringBuilder("AddinsFile:"); + foreach (var entry in this) + sb.Append($" {entry}"); + return sb.ToString(); + } + + public override bool Equals(object obj) + { + var other = obj as AddinsFile; + if (other == null) return false; + + if (Count != other.Count) return false; + + for (int i = 0; i < Count; i++) + if (this[i] != other[i]) return false; + + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileEntry.cs b/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileEntry.cs new file mode 100644 index 000000000..3bf6470d5 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileEntry.cs @@ -0,0 +1,50 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace NUnit.Engine.Internal +{ + // Class representing a single line in an Addins file + internal class AddinsFileEntry + { + public int LineNumber { get; } + public string RawText { get; } + public string Text { get; } + + public bool IsFullyQualified => PathUtils.IsFullyQualifiedPath(Text); + public bool IsDirectory => Text.EndsWith("/"); + public bool IsPattern => Text.Contains("*"); + public bool IsValid => PathUtils.IsValidPath(Text.Replace('*', 'X')); + + public string DirectoryName => Path.GetDirectoryName(Text); + public string FileName => Path.GetFileName(Text); + + public AddinsFileEntry(int lineNumber, string rawText) + { + LineNumber = lineNumber; + RawText = rawText; + Text = rawText.Split(new char[] { '#' })[0].Trim() + .Replace(Path.DirectorySeparatorChar, '/'); + } + + public override string ToString() + { + return $"{LineNumber}: {RawText}"; + } + + public override bool Equals(object obj) + { + var other = obj as AddinsFileEntry; + if (other == null) return false; + + return LineNumber == other.LineNumber && RawText == other.RawText; + } + + public override int GetHashCode() + { + return LineNumber.GetHashCode() ^ RawText.GetHashCode(); + } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileReader.cs b/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileReader.cs deleted file mode 100644 index 3e0407898..000000000 --- a/src/NUnitEngine/nunit.engine.core/Internal/AddinsFileReader.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using NUnit.Engine.Internal.FileSystemAccess; -using System; -using System.Collections.Generic; -using System.IO; - -namespace NUnit.Engine.Internal -{ - /// - /// A reader for NUnit addins-files. - /// - /// - /// The format of an addins-file can be found at https://docs.nunit.org/articles/nunit-engine/extensions/Installing-Extensions.html. - /// - internal sealed class AddinsFileReader : IAddinsFileReader - { - /// - public IEnumerable Read(IFile file) - { - if (file == null) - { - throw new ArgumentNullException(nameof(file)); - } - - using (var reader = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return Read(reader); - } - } - - /// - /// Reads the content of an addins-file from a stream. - /// - /// Input stream. Must be readable and positioned at the beginning of the 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 IEnumerable Read(Stream stream) - { - var result = new List(); - using (var reader = new StreamReader(stream)) - { - for(var line = reader.ReadLine(); line != null; line = reader.ReadLine()) - { - line = line.Split(new char[] { '#' })[0].Trim(); - if (line != string.Empty) - { - result.Add(line.Replace(Path.DirectorySeparatorChar, '/')); - } - } - } - - return result; - } - } -} diff --git a/src/NUnitEngine/nunit.engine.core/Internal/Backports/Path.cs b/src/NUnitEngine/nunit.engine.core/Internal/Backports/Path.cs deleted file mode 100644 index 2d800d68a..000000000 --- a/src/NUnitEngine/nunit.engine.core/Internal/Backports/Path.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using System; - -namespace NUnit.Engine.Internal.Backports -{ - /// - /// Backports of functionality that is only available in newer .NET versions. - /// - public static class Path - { - /// - /// Returns a value that indicates whether the specified file path is absolute or not. - /// - /// Path to check - /// if is an absolute or UNC path; otherwhise, false. - /// - /// See https://docs.microsoft.com/en-us/dotnet/api/system.io.path.ispathfullyqualified for original implementation. - public static bool IsPathFullyQualified(string path) - { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - - return RunningOnWindows() ? PathUtils.IsFullyQualifiedWindowsPath(path) : PathUtils.IsFullyQualifiedUnixPath(path); - } - - private static bool RunningOnWindows() - { - return System.IO.Path.DirectorySeparatorChar == '\\'; - } - } -} diff --git a/src/NUnitEngine/nunit.engine.core/Internal/IAddinsFileReader.cs b/src/NUnitEngine/nunit.engine.core/Internal/IAddinsFileReader.cs deleted file mode 100644 index 511b38b1a..000000000 --- a/src/NUnitEngine/nunit.engine.core/Internal/IAddinsFileReader.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using NUnit.Engine.Internal.FileSystemAccess; -using System.Collections.Generic; - -namespace NUnit.Engine.Internal -{ - /// - /// A reader for NUnit addins-files. - /// - /// - /// The format of an addins-file can be found at https://docs.nunit.org/articles/nunit-engine/extensions/Installing-Extensions.html. - /// - internal interface IAddinsFileReader - { - /// - /// Reads all entries from an addins-file. - /// - /// Location of the file. - /// All entries contained in the file. - /// is - /// cannot be found or read - IEnumerable Read(IFile file); - } -} diff --git a/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs b/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs index b7d0c2220..bec00090b 100644 --- a/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs +++ b/src/NUnitEngine/nunit.engine.core/Internal/PathUtils.cs @@ -144,10 +144,10 @@ public static bool SamePathOrUnder( string path1, string path2 ) // if lengths are the same, check for equality if ( length1 == length2 ) - return string.Compare( path1, path2, IsWindows() ) == 0; + return string.Compare( path1, path2, RunningOnWindows ) == 0; // path 2 is longer than path 1: see if initial parts match - if ( string.Compare( path1, path2.Substring( 0, length1 ), IsWindows() ) != 0 ) + if ( string.Compare( path1, path2.Substring( 0, length1 ), RunningOnWindows ) != 0 ) return false; // must match through or up to a directory separator boundary @@ -167,7 +167,20 @@ public static string Combine(string path1, params string[] morePaths) } /// - /// Returns a value that indicates whether the specified file path is absolute or not on Windows operating systems. + /// Returns a value that indicates whether the specified file path is fully qualified. + /// + /// Path to check + /// if is an absolute path; otherwhise, false. + /// + public static bool IsFullyQualifiedPath(string path ) + { + return RunningOnWindows + ? IsFullyQualifiedWindowsPath(path) + : IsFullyQualifiedUnixPath(path); + } + + /// + /// Returns a value that indicates whether the specified file path is fully qualified or not on Windows operating systems. /// /// Path to check /// if is an absolute or UNC path; otherwhise, false. @@ -206,6 +219,27 @@ public static bool IsFullyQualifiedUnixPath(string path) return path.Length > 0 && path[0] == '/'; } + public static bool IsValidPath(string path) + { + try + { + var info = GetFileSystemInfo(path); +#if NETCOREAPP2_1_OR_GREATER + var creation = info.CreationTime; +#endif + return true; // Whether it exists or not! + } + catch + { + return false; + } + } + + private static FileSystemInfo GetFileSystemInfo(string path) => + path.EndsWith("/") || path.EndsWith(@"\\") + ? new DirectoryInfo(path) as FileSystemInfo + : new FileInfo(path) as FileSystemInfo; + private static bool IsWindowsDirectorySeparator(char c) { return c == '\\' || c == '/'; @@ -216,14 +250,11 @@ private static bool IsValidDriveSpecifier(char c) return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } - private static bool IsWindows() - { - return PathUtils.DirectorySeparatorChar == '\\'; - } + private static bool RunningOnWindows => DirectorySeparatorChar == '\\'; private static string[] SplitPath(string path) { - char[] separators = new char[] { PathUtils.DirectorySeparatorChar, PathUtils.AltDirectorySeparatorChar }; + char[] separators = new char[] { DirectorySeparatorChar, AltDirectorySeparatorChar }; string[] trialSplit = path.Split(separators); @@ -246,7 +277,7 @@ private static string[] SplitPath(string path) private static bool PathsEqual(string path1, string path2) { - if (PathUtils.IsWindows()) + if (RunningOnWindows) return path1.ToLower().Equals(path2.ToLower()); else return path1.Equals(path2); diff --git a/src/NUnitEngine/nunit.engine.core/Services/ExtensionManager.cs b/src/NUnitEngine/nunit.engine.core/Services/ExtensionManager.cs index 40fb360e2..476ceaee5 100644 --- a/src/NUnitEngine/nunit.engine.core/Services/ExtensionManager.cs +++ b/src/NUnitEngine/nunit.engine.core/Services/ExtensionManager.cs @@ -2,28 +2,24 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; using TestCentric.Metadata; using NUnit.Engine.Extensibility; using NUnit.Engine.Internal; using NUnit.Engine.Internal.FileSystemAccess; using NUnit.Engine.Internal.FileSystemAccess.Default; - -#if NET462 || NETSTANDARD2_0 -using Path = NUnit.Engine.Internal.Backports.Path; -#else -using Path = System.IO.Path; -#endif +using System.Linq; namespace NUnit.Engine.Services { - public sealed class ExtensionManager : IDisposable + public class ExtensionManager : IExtensionManager { static readonly Logger log = InternalTrace.GetLogger(typeof(ExtensionService)); static readonly Version ENGINE_VERSION = typeof(ExtensionService).Assembly.GetName().Version; private readonly IFileSystem _fileSystem; - private readonly IAddinsFileReader _addinsReader; + //private readonly IAddinsFileReader _addinsReader; private readonly IDirectoryFinder _directoryFinder; private readonly List _extensionPoints = new List(); @@ -33,32 +29,22 @@ public sealed class ExtensionManager : IDisposable private readonly List _assemblies = new List(); public ExtensionManager() - : this(new AddinsFileReader(), new FileSystem()) + : this(new FileSystem()) { } - internal ExtensionManager(IAddinsFileReader addinsReader, IFileSystem fileSystem) - : this(addinsReader, fileSystem, new DirectoryFinder(fileSystem)) + internal ExtensionManager(IFileSystem fileSystem) + : this(fileSystem, new DirectoryFinder(fileSystem)) { } - internal ExtensionManager(IAddinsFileReader addinsReader, IFileSystem fileSystem, IDirectoryFinder directoryFinder) + internal ExtensionManager(IFileSystem fileSystem, IDirectoryFinder directoryFinder) { - _addinsReader = addinsReader; _fileSystem = fileSystem; _directoryFinder = directoryFinder; } - internal void FindExtensions(string startDir) - { - // Create the list of possible extension assemblies, - // eliminating duplicates, start in the provided directory. - FindExtensionAssemblies(_fileSystem.GetDirectory(startDir)); - - // Check each assembly to see if it contains extensions - foreach (var candidate in _assemblies) - FindExtensionsInAssembly(candidate); - } + #region IExtensionManager Implementation /// /// Gets an enumeration of all ExtensionPoints in the engine. @@ -76,82 +62,10 @@ public IEnumerable Extensions get { return _extensions.ToArray(); } } - /// - /// Enable or disable an extension - /// - public void EnableExtension(string typeName, bool enabled) - { - foreach (var node in _extensions) - if (node.TypeName == typeName) - node.Enabled = enabled; - } - - /// - /// Get an ExtensionPoint based on its unique identifying path. - /// - public ExtensionPoint GetExtensionPoint(string path) - { - return _pathIndex.ContainsKey(path) ? _pathIndex[path] : null; - } - - /// - /// Get an ExtensionPoint based on the required Type for extensions. - /// - public ExtensionPoint GetExtensionPoint(Type type) - { - foreach (var ep in _extensionPoints) - if (ep.TypeName == type.FullName) - return ep; - - return null; - } - - /// - /// Get an ExtensionPoint based on a Cecil TypeReference. - /// - public ExtensionPoint GetExtensionPoint(TypeReference type) - { - foreach (var ep in _extensionPoints) - if (ep.TypeName == type.FullName) - return ep; - - return null; - } - - public IEnumerable GetExtensionNodes(string path) - { - var ep = GetExtensionPoint(path); - if (ep != null) - foreach (var node in ep.Extensions) - yield return node; - } - - public ExtensionNode GetExtensionNode(string path) - { - var ep = GetExtensionPoint(path); - - return ep != null && ep.Extensions.Count > 0 ? ep.Extensions[0] : null; - } - - public IEnumerable GetExtensionNodes(bool includeDisabled = false) - { - var ep = GetExtensionPoint(typeof(T)); - if (ep != null) - foreach (var node in ep.Extensions) - if (includeDisabled || node.Enabled) - yield return node; - } - - public IEnumerable GetExtensions() - { - foreach (var node in GetExtensionNodes()) - yield return (T)node.ExtensionObject; - } - /// /// Find the extension points in a loaded assembly. /// - public void FindExtensionPoints(params Assembly[] targetAssemblies) + public virtual void FindExtensionPoints(params Assembly[] targetAssemblies) { foreach (var assembly in targetAssemblies) { @@ -206,6 +120,92 @@ public void FindExtensionPoints(params Assembly[] targetAssemblies) } } + public void FindExtensions(string startDir) + { + // Create the list of possible extension assemblies, + // eliminating duplicates, start in the provided directory. + FindExtensionAssemblies(_fileSystem.GetDirectory(startDir)); + + // Check each assembly to see if it contains extensions + foreach (var candidate in _assemblies) + FindExtensionsInAssembly(candidate); + } + + /// + /// Get an ExtensionPoint based on its unique identifying path. + /// + public IExtensionPoint GetExtensionPoint(string path) + { + return _pathIndex.ContainsKey(path) ? _pathIndex[path] : null; + } + + public IEnumerable GetExtensionNodes(string path) + { + var ep = GetExtensionPoint(path); + if (ep != null) + foreach (var node in ep.Extensions) + yield return node; + } + + public IExtensionNode GetExtensionNode(string path) + { + // TODO: Remove need for the cast + var ep = GetExtensionPoint(path) as ExtensionPoint; + + return ep != null && ep.Extensions.Count > 0 ? ep.Extensions[0] : null; + } + + public IEnumerable GetExtensionNodes(bool includeDisabled = false) + { + var ep = GetExtensionPoint(typeof(T)); + if (ep != null) + foreach (var node in ep.Extensions) + if (includeDisabled || node.Enabled) + yield return node; + } + + public IEnumerable GetExtensions() + { + foreach (var node in GetExtensionNodes()) + yield return (T)((ExtensionNode)node).ExtensionObject; // HACK + } + + /// + /// Enable or disable an extension + /// + public void EnableExtension(string typeName, bool enabled) + { + foreach (var node in _extensions) + if (node.TypeName == typeName) + node.Enabled = enabled; + } + + #endregion + + /// + /// Get an ExtensionPoint based on the required Type for extensions. + /// + public ExtensionPoint GetExtensionPoint(Type type) + { + foreach (var ep in _extensionPoints) + if (ep.TypeName == type.FullName) + return ep; + + return null; + } + + /// + /// Get an ExtensionPoint based on a Cecil TypeReference. + /// + public ExtensionPoint GetExtensionPoint(TypeReference type) + { + foreach (var ep in _extensionPoints) + if (ep.TypeName == type.FullName) + return ep; + + return null; + } + /// /// Deduce the extension point based on the Type of an extension. /// Returns null if no extension point can be found that would @@ -278,14 +278,12 @@ private int ProcessAddinsFiles(IDirectory startDir, bool fromWildCard) var addinsFiles = startDir.GetFiles("*.addins"); var addinsFileCount = 0; - if (addinsFiles.Any()) + foreach (var file in addinsFiles) { - foreach (var file in addinsFiles) - { - ProcessAddinsFile(startDir, file, fromWildCard); - addinsFileCount += 1; - } + ProcessAddinsFile(file, fromWildCard); + addinsFileCount++; } + return addinsFileCount; } @@ -295,56 +293,43 @@ private int ProcessAddinsFiles(IDirectory startDir, bool fromWildCard) /// path or a wildcard pattern used to find assemblies. Blank /// lines and comments started by # are ignored. /// - private void ProcessAddinsFile(IDirectory baseDir, IFile addinsFile, bool fromWildCard) + private void ProcessAddinsFile(IFile addinsFile, bool fromWildCard) { log.Info("Processing file " + addinsFile.FullName); - foreach (var entry in _addinsReader.Read(addinsFile)) + foreach (var entry in AddinsFile.Read(addinsFile).Where(e => e.Text != string.Empty)) { - bool isWild = fromWildCard || entry.Contains("*"); - var args = GetBaseDirAndPattern(baseDir, entry); - // TODO: See if we can handle '/tools/*/' efficiently by examining every - // assembly in the directory. Otherwise try this approach: - // 1. Check entry for ending with '/tools/*/' - // 2. If so, examine the directory name to see if it matches a tfm. - // 3. If it does, check to see if the implied runtime would be loadable. - // 4. If so, process it, if not, skip it. - if (entry.EndsWith("/")) + bool isWild = fromWildCard || entry.IsPattern; + IDirectory baseDir = addinsFile.Parent; + string entryDir = entry.DirectoryName; + string entryFile = entry.FileName; + + if (entry.IsDirectory) { - foreach (var dir in _directoryFinder.GetDirectories(args.Item1, args.Item2)) + if (entry.IsFullyQualified) { - ProcessDirectory(dir, isWild); + baseDir = _fileSystem.GetDirectory(entry.Text); + foreach (var dir in _directoryFinder.GetDirectories(_fileSystem.GetDirectory(entryDir), "")) + ProcessDirectory(dir, isWild); } + else + foreach (var dir in _directoryFinder.GetDirectories(baseDir, entry.Text)) + ProcessDirectory(dir, isWild); } else { - foreach (var file in _directoryFinder.GetFiles(args.Item1, args.Item2)) + if (entry.IsFullyQualified) { - ProcessCandidateAssembly(file.FullName, isWild); + foreach (var file in _directoryFinder.GetFiles(_fileSystem.GetDirectory(entryDir), entryFile)) + ProcessCandidateAssembly(file.FullName, isWild); } + else + foreach (var file in _directoryFinder.GetFiles(baseDir, entry.Text)) + ProcessCandidateAssembly(file.FullName, isWild); } } } - private Tuple GetBaseDirAndPattern(IDirectory baseDir, string path) - { - if (Path.IsPathFullyQualified(path)) - { - if (path.EndsWith("/")) - { - return new Tuple(_fileSystem.GetDirectory(path), string.Empty); - } - else - { - return new Tuple(_fileSystem.GetDirectory(System.IO.Path.GetDirectoryName(path)), System.IO.Path.GetFileName(path)); - } - } - else - { - return new Tuple(baseDir, path); - } - } - private void ProcessCandidateAssembly(string filePath, bool fromWildCard) { if (WasVisited(filePath, fromWildCard)) @@ -483,7 +468,8 @@ internal void FindExtensionsInAssembly(ExtensionAssembly assembly) } else { - ep = GetExtensionPoint(node.Path); + // TODO: Remove need for the cast + ep = GetExtensionPoint(node.Path) as ExtensionPoint; if (ep == null) { string msg = string.Format( diff --git a/src/NUnitEngine/nunit.engine.core/Services/ExtensionService.cs b/src/NUnitEngine/nunit.engine.core/Services/ExtensionService.cs index af3af5665..74ef1abcf 100644 --- a/src/NUnitEngine/nunit.engine.core/Services/ExtensionService.cs +++ b/src/NUnitEngine/nunit.engine.core/Services/ExtensionService.cs @@ -6,17 +6,9 @@ using TestCentric.Metadata; using NUnit.Engine.Extensibility; using NUnit.Engine.Internal; -using NUnit.Engine.Internal.Backports; using NUnit.Engine.Internal.FileSystemAccess; using NUnit.Engine.Internal.FileSystemAccess.Default; -using Backports = NUnit.Engine.Internal.Backports; -#if NETFRAMEWORK || NETSTANDARD2_0 -using Path = NUnit.Engine.Internal.Backports.Path; -#else -using Path = System.IO.Path; -#endif - namespace NUnit.Engine.Services { /// @@ -26,22 +18,27 @@ namespace NUnit.Engine.Services /// public class ExtensionService : Service, IExtensionService { - private readonly ExtensionManager _extensionManager; + private readonly IExtensionManager _extensionManager; public ExtensionService() { _extensionManager = new ExtensionManager(); } - internal ExtensionService(IAddinsFileReader addinsReader, IFileSystem fileSystem) - : this(addinsReader, fileSystem, new DirectoryFinder(fileSystem)) + public ExtensionService(ExtensionManager extensionManager) + { + _extensionManager = extensionManager; + } + + internal ExtensionService(IFileSystem fileSystem) + : this(fileSystem, new DirectoryFinder(fileSystem)) { - _extensionManager = new ExtensionManager(addinsReader, fileSystem); + _extensionManager = new ExtensionManager(fileSystem); } - internal ExtensionService(IAddinsFileReader addinsReader, IFileSystem fileSystem, IDirectoryFinder directoryFinder) + internal ExtensionService(IFileSystem fileSystem, IDirectoryFinder directoryFinder) { - _extensionManager = new ExtensionManager(addinsReader, fileSystem, directoryFinder); + _extensionManager = new ExtensionManager(fileSystem, directoryFinder); } public IEnumerable ExtensionPoints => _extensionManager.ExtensionPoints; @@ -72,7 +69,7 @@ public void EnableExtension(string typeName, bool enabled) public IEnumerable GetExtensions() => _extensionManager.GetExtensions(); - public ExtensionNode 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.core/Services/IExtensionManager.cs b/src/NUnitEngine/nunit.engine.core/Services/IExtensionManager.cs new file mode 100644 index 000000000..c02704d5a --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Services/IExtensionManager.cs @@ -0,0 +1,43 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Collections.Generic; +using System.Reflection; + +using NUnit.Engine.Extensibility; + +namespace NUnit.Engine.Services +{ + public interface IExtensionManager : IDisposable + { + IEnumerable ExtensionPoints { get; } + IEnumerable Extensions { get; } + + void FindExtensionPoints(params Assembly[] targetAssemblies); + void FindExtensions(string startDir); + + IExtensionPoint GetExtensionPoint(string path); + + IEnumerable GetExtensions(); + + IEnumerable GetExtensionNodes(string path); + IExtensionNode GetExtensionNode(string path); + /// + /// Returns all extension nodes for a given Type. + /// + /// The Type of the node + /// If true, disabled nodes are included + /// An enumeration of ExtensionNodes + /// + /// Unlike other methods, this method returns an actual ExtensionNode rather + /// than an IExtensionNode. It is required in order for classes that support + /// extensions to create the actual extension object. + /// + /// + // NOTE: + IEnumerable GetExtensionNodes(bool includeDisabled = false); + + void EnableExtension(string typeName, bool enabled); + } + +}