From 1d8fcb9955368e8543cb6e160a051c13a6bc3765 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Wed, 22 May 2024 09:18:28 -0700 Subject: [PATCH 1/2] Changes to NUnit3Driver --- .../Drivers/NUnit3FrameworkDriverTests.cs | 24 ++--- .../Drivers/NUnit3DriverFactory.cs | 5 +- .../Drivers/NUnit3FrameworkDriver.cs | 88 ++++++++----------- .../Drivers/NUnitNetStandardDriver.cs | 2 + 4 files changed, 49 insertions(+), 70 deletions(-) diff --git a/src/TestCentric.Agent.Core.Tests/Drivers/NUnit3FrameworkDriverTests.cs b/src/TestCentric.Agent.Core.Tests/Drivers/NUnit3FrameworkDriverTests.cs index de00d56..d9e9517 100644 --- a/src/TestCentric.Agent.Core.Tests/Drivers/NUnit3FrameworkDriverTests.cs +++ b/src/TestCentric.Agent.Core.Tests/Drivers/NUnit3FrameworkDriverTests.cs @@ -11,6 +11,7 @@ using TestCentric.Engine.Internal; using TestCentric.Tests.Assemblies; using NUnit.Framework; +using System.Reflection; namespace TestCentric.Engine.Drivers { @@ -28,24 +29,8 @@ public class NUnit3FrameworkDriverTests [SetUp] public void CreateDriver() { - var assemblyName = typeof(NUnit.Framework.TestAttribute).Assembly.GetName(); _mockAssemblyPath = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, MOCK_ASSEMBLY); - _driver = new NUnit3FrameworkDriver(AppDomain.CurrentDomain, assemblyName); - } - - //[Test] - //public void ConstructController() - //{ - // Assert.That(_controller..Builder, Is.TypeOf()); - // Assert.That(_controller.Runner, Is.TypeOf()); - // Assert.That(_controller.AssemblyPath, Is.EqualTo(MOCK_ASSEMBLY)); - // Assert.That(_controller.Settings, Is.SameAs(_settings)); - //} - - public void ConstructController_MissingFile_ThrowsArgumentInvalid() - { - var assemblyName = typeof(NUnit.Framework.TestAttribute).Assembly.GetName(); - Assert.That(new NUnit3FrameworkDriver(AppDomain.CurrentDomain, assemblyName), Throws.ArgumentException); + _driver = new NUnit3FrameworkDriver(); } [Test] @@ -133,7 +118,10 @@ public void RunTestsAction_WithInvalidFilterElement_ThrowsNUnitEngineException() var invalidFilter = "foo"; var ex = Assert.Catch(() => _driver.Run(new NullListener(), invalidFilter)); - Assert.That(ex, Is.TypeOf()); + + // TODO: We should be getting an engine exception here + Assert.That(ex , Is.TypeOf()); + //Assert.That(ex , Is.TypeOf()); } private static string GetSkipReason(XmlNode result) diff --git a/src/TestCentric.Agent.Core/Drivers/NUnit3DriverFactory.cs b/src/TestCentric.Agent.Core/Drivers/NUnit3DriverFactory.cs index f11b3d9..77d804d 100644 --- a/src/TestCentric.Agent.Core/Drivers/NUnit3DriverFactory.cs +++ b/src/TestCentric.Agent.Core/Drivers/NUnit3DriverFactory.cs @@ -33,11 +33,12 @@ public bool IsSupportedTestFramework(AssemblyName reference) /// The domain in which the assembly will be loaded /// An AssemblyName referring to the test framework. /// + // TODO: Remove this overload after engine api is updated public IFrameworkDriver GetDriver(AppDomain domain, AssemblyName reference) { Guard.ArgumentValid(IsSupportedTestFramework(reference), "Invalid framework", "reference"); - return new NUnit3FrameworkDriver(domain, reference); + return new NUnit3FrameworkDriver(); } #else /// @@ -59,5 +60,5 @@ public IFrameworkDriver GetDriver(AssemblyName reference) #endif } #endif - } + } } diff --git a/src/TestCentric.Agent.Core/Drivers/NUnit3FrameworkDriver.cs b/src/TestCentric.Agent.Core/Drivers/NUnit3FrameworkDriver.cs index 74a03f5..269a07b 100644 --- a/src/TestCentric.Agent.Core/Drivers/NUnit3FrameworkDriver.cs +++ b/src/TestCentric.Agent.Core/Drivers/NUnit3FrameworkDriver.cs @@ -8,8 +8,8 @@ using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Runtime.Serialization; using TestCentric.Engine.Extensibility; +using TestCentric.Metadata; namespace TestCentric.Engine.Drivers { @@ -20,6 +20,9 @@ namespace TestCentric.Engine.Drivers public class NUnit3FrameworkDriver : IFrameworkDriver { private const string LOAD_MESSAGE = "Method called without calling Load first"; + const string INVALID_FRAMEWORK_MESSAGE = "Running tests against this version of the framework using this driver is not supported. Please update NUnit.Framework to the latest version."; + const string FAILED_TO_LOAD_TEST_ASSEMBLY = "Failed to load the test assembly {0}"; + const string FAILED_TO_LOAD_NUNIT = "Failed to load the NUnit Framework in the test assembly"; private static readonly string CONTROLLER_TYPE = "NUnit.Framework.Api.FrameworkController"; private static readonly string LOAD_ACTION = CONTROLLER_TYPE + "+LoadTestsAction"; @@ -30,23 +33,11 @@ public class NUnit3FrameworkDriver : IFrameworkDriver static Logger log = InternalTrace.GetLogger("NUnitFrameworkDriver"); - AppDomain _testDomain; - AssemblyName _reference; string _testAssemblyPath; - + Assembly _testAssembly; + Assembly _frameworkAssembly; object _frameworkController; - /// - /// Construct an NUnit3FrameworkDriver - /// - /// The application domain in which to create the FrameworkController - /// An AssemblyName referring to the test framework. - public NUnit3FrameworkDriver(AppDomain testDomain, AssemblyName reference) - { - _testDomain = testDomain; - _reference = reference; - } - public string ID { get; set; } /// @@ -58,24 +49,40 @@ public string Load(string testAssemblyPath, IDictionary settings Guard.ArgumentValid(File.Exists(testAssemblyPath), "Framework driver constructor called with a file name that doesn't exist.", "testAssemblyPath"); var idPrefix = string.IsNullOrEmpty(ID) ? "" : ID + "-"; + _testAssemblyPath = testAssemblyPath; - try - { - _frameworkController = CreateObject(CONTROLLER_TYPE, testAssemblyPath, idPrefix, settings); - } - catch (SerializationException ex) - { - throw new EngineException("The NUnit 3 driver cannot support this test assembly. Use a platform specific runner.", ex); - } + var assemblyRef = AssemblyDefinition.ReadAssembly(testAssemblyPath); + _testAssembly = Assembly.LoadFrom(testAssemblyPath); + if (_testAssembly == null) + throw new EngineException(string.Format(FAILED_TO_LOAD_TEST_ASSEMBLY, assemblyRef.FullName)); + + // NOTE: We could use Linq here, but would still need the loop for NET20 + AssemblyNameReference nunitRef = null; + foreach (var reference in assemblyRef.MainModule.AssemblyReferences) + if (reference.Name.Equals("nunit.framework", StringComparison.OrdinalIgnoreCase)) + { + nunitRef = reference; + break; + } + + if (nunitRef == null) + throw new EngineException(FAILED_TO_LOAD_NUNIT); + + var nunit = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(testAssemblyPath), nunitRef.Name + ".dll")); + if (nunit == null) + throw new EngineException(FAILED_TO_LOAD_NUNIT); + + _frameworkAssembly = nunit; + + _frameworkController = CreateObject(CONTROLLER_TYPE, _testAssembly, idPrefix, settings); + if (_frameworkController == null) + throw new EngineException(INVALID_FRAMEWORK_MESSAGE); CallbackHandler handler = new CallbackHandler(); var fileName = Path.GetFileName(_testAssemblyPath); - log.Info("Loading {0} - see separate log file", fileName); - CreateObject(LOAD_ACTION, _frameworkController, handler); - log.Info("Loaded {0}", fileName); return handler.Result; @@ -84,11 +91,8 @@ public string Load(string testAssemblyPath, IDictionary settings public int CountTestCases(string filter) { CheckLoadWasCalled(); - CallbackHandler handler = new CallbackHandler(); - CreateObject(COUNT_ACTION, _frameworkController, filter, handler); - return int.Parse(handler.Result); } @@ -104,11 +108,8 @@ public string Run(ITestEventListener listener, string filter) var handler = new RunTestsCallbackHandler(listener); var filename = Path.GetFileName(_testAssemblyPath); - log.Info("Running {0} - see separate log file", filename); - CreateObject(RUN_ACTION, _frameworkController, filter, handler); - return handler.Result; } @@ -137,12 +138,9 @@ public void StopRun(bool force) public string Explore(string filter) { CheckLoadWasCalled(); - - CallbackHandler handler = new CallbackHandler(); - log.Info("Exploring {0} - see separate log file", Path.GetFileName(_testAssemblyPath)); + CallbackHandler handler = new CallbackHandler(); CreateObject(EXPLORE_ACTION, _frameworkController, filter, handler); - return handler.Result; } @@ -154,20 +152,10 @@ private void CheckLoadWasCalled() private object CreateObject(string typeName, params object[] args) { - try - { - return _testDomain.CreateInstanceAndUnwrap( - _reference.FullName, typeName, false, 0, -#if NET20 - null, args, null, null, null); -#else - null, args, null, null ); -#endif - } - catch (TargetInvocationException ex) - { - throw new EngineException("The NUnit 3 driver encountered an error while executing reflected code.", ex.InnerException); - } + var type = _frameworkAssembly.GetType(typeName); + if (type == null) + log.Error("Could not find type {typeName}"); + return Activator.CreateInstance(type, args); } } } diff --git a/src/TestCentric.Agent.Core/Drivers/NUnitNetStandardDriver.cs b/src/TestCentric.Agent.Core/Drivers/NUnitNetStandardDriver.cs index 9ae2b62..02cee58 100644 --- a/src/TestCentric.Agent.Core/Drivers/NUnitNetStandardDriver.cs +++ b/src/TestCentric.Agent.Core/Drivers/NUnitNetStandardDriver.cs @@ -56,6 +56,8 @@ public class NUnitNetStandardDriver : IFrameworkDriver /// An Xml string representing the loaded test public string Load(string testAssemblyPath, IDictionary settings) { + Guard.ArgumentValid(File.Exists(testAssemblyPath), "Framework driver constructor called with a file name that doesn't exist.", "testAssemblyPath"); + var idPrefix = string.IsNullOrEmpty(ID) ? "" : ID + "-"; _testAssemblyPath = testAssemblyPath; From f35d15e8c4261a9b9a606801b6dadea3d5b13768 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Sun, 13 Oct 2024 16:15:29 -0700 Subject: [PATCH 2/2] Add GitHub workflow for CI --- .../workflows/testcentric-agent-core-ci.yml | 11 +++++++++++ TestCentric.Agent.Core.sln | 9 +++++++++ build.cake | 18 ++++++++++-------- src/DirectTestAgent/DirectTestAgent.csproj | 6 +++--- .../TestCentric.Agent.Core.Tests.csproj | 4 ++-- .../Drivers/DriverService.cs | 10 +--------- .../Drivers/NUnit2DriverFactory.cs | 13 ++++++++++--- .../TestCentric.Agent.Core.csproj | 6 +++--- 8 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/testcentric-agent-core-ci.yml diff --git a/.github/workflows/testcentric-agent-core-ci.yml b/.github/workflows/testcentric-agent-core-ci.yml new file mode 100644 index 0000000..7a6bd40 --- /dev/null +++ b/.github/workflows/testcentric-agent-core-ci.yml @@ -0,0 +1,11 @@ +name: TestCentric.Agent.Core.CI + +on: + workflow_dispatch: + pull_request: + push: + +jobs: + ContinuousIntegration: + uses: TestCentric/TestCentric.Workflows/.github/workflows/testcentric-ci.yml@main + secrets: inherit \ No newline at end of file diff --git a/TestCentric.Agent.Core.sln b/TestCentric.Agent.Core.sln index 38ecb89..d95e0b2 100644 --- a/TestCentric.Agent.Core.sln +++ b/TestCentric.Agent.Core.sln @@ -38,6 +38,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "aspnetcore-test", "src\Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "windows-forms-test", "src\TestData\windows-forms-test\windows-forms-test.csproj", "{FE2601F1-2E3F-45DE-B812-F7764F02CAB2}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{13F7EF0F-31AD-4EE0-9391-7BE7BCAA9756}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{64CC1A42-B482-4F8E-BBEF-6F3435BCCB66}" + ProjectSection(SolutionItems) = preProject + .github\workflows\testcentric-agent-core-ci.yml = .github\workflows\testcentric-agent-core-ci.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +136,8 @@ Global {E61CFB73-49FC-4A63-903D-9209B2CC538A} = {374E8CCC-DB1E-4228-A205-2657A80B1889} {081E0546-9041-4A36-B5F4-D674FFA4B947} = {374E8CCC-DB1E-4228-A205-2657A80B1889} {FE2601F1-2E3F-45DE-B812-F7764F02CAB2} = {374E8CCC-DB1E-4228-A205-2657A80B1889} + {13F7EF0F-31AD-4EE0-9391-7BE7BCAA9756} = {49BC1DFB-828F-49F4-B2B4-752AB470481D} + {64CC1A42-B482-4F8E-BBEF-6F3435BCCB66} = {13F7EF0F-31AD-4EE0-9391-7BE7BCAA9756} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CAB7BD1E-1034-47CD-AA8E-FD4AC034193B} diff --git a/build.cake b/build.cake index 96f3601..a07d074 100644 --- a/build.cake +++ b/build.cake @@ -1,5 +1,5 @@ // Load the recipe -#load nuget:?package=TestCentric.Cake.Recipe&version=1.3.2 +#load nuget:?package=TestCentric.Cake.Recipe&version=1.3.3 // Comment out above line and uncomment below for local tests of recipe changes //#load ../TestCentric.Cake.Recipe/recipe/*.cake @@ -36,9 +36,10 @@ packageTests.Add(new PackageTest(1, "Net462Test", "Run mock-assembly.dll targeti "mock-assembly/net462/mock-assembly.dll", mockAssemblyExpectedResult)); -packageTests.Add(new PackageTest(1, "NetCore31Test", "Run mock-assembly.dll targeting .NET Core 3.1", - "mock-assembly/netcoreapp3.1/mock-assembly.dll", - mockAssemblyExpectedResult)); +if (BuildSettings.IsLocalBuild) + packageTests.Add(new PackageTest(1, "NetCore31Test", "Run mock-assembly.dll targeting .NET Core 3.1", + "mock-assembly/netcoreapp3.1/mock-assembly.dll", + mockAssemblyExpectedResult)); packageTests.Add(new PackageTest(1, "Net60Test", "Run mock-assembly.dll targeting .NET 6.0", "mock-assembly/net6.0/mock-assembly.dll", @@ -50,10 +51,11 @@ packageTests.Add(new PackageTest(1, "Net80Test", "Run mock-assembly.dll targetin // Asp .NET Core Tests -packageTests.Add(new PackageTest(1, "AspNetCore31Test", "Run test using AspNetCore under .NET Core 3.1", - "aspnetcore-test/netcoreapp3.1/aspnetcore-test.dll", - new ExpectedResult("Passed"){ Assemblies = new ExpectedAssemblyResult[] { - new ExpectedAssemblyResult("aspnetcore-test.dll")} })); +if (BuildSettings.IsLocalBuild) + packageTests.Add(new PackageTest(1, "AspNetCore31Test", "Run test using AspNetCore under .NET Core 3.1", + "aspnetcore-test/netcoreapp3.1/aspnetcore-test.dll", + new ExpectedResult("Passed"){ Assemblies = new ExpectedAssemblyResult[] { + new ExpectedAssemblyResult("aspnetcore-test.dll")} })); packageTests.Add(new PackageTest(1, "AspNetCore50Test", "Run test using AspNetCore under .NET 5.0", "aspnetcore-test/net5.0/aspnetcore-test.dll", diff --git a/src/DirectTestAgent/DirectTestAgent.csproj b/src/DirectTestAgent/DirectTestAgent.csproj index a7134eb..d1dcb01 100644 --- a/src/DirectTestAgent/DirectTestAgent.csproj +++ b/src/DirectTestAgent/DirectTestAgent.csproj @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/TestCentric.Agent.Core.Tests/TestCentric.Agent.Core.Tests.csproj b/src/TestCentric.Agent.Core.Tests/TestCentric.Agent.Core.Tests.csproj index 5ffb879..bb02b2d 100644 --- a/src/TestCentric.Agent.Core.Tests/TestCentric.Agent.Core.Tests.csproj +++ b/src/TestCentric.Agent.Core.Tests/TestCentric.Agent.Core.Tests.csproj @@ -38,8 +38,8 @@ - - + + diff --git a/src/TestCentric.Agent.Core/Drivers/DriverService.cs b/src/TestCentric.Agent.Core/Drivers/DriverService.cs index d090523..f30fc21 100644 --- a/src/TestCentric.Agent.Core/Drivers/DriverService.cs +++ b/src/TestCentric.Agent.Core/Drivers/DriverService.cs @@ -28,15 +28,7 @@ public DriverService() { log.Debug("Creating ExtensionManager"); var thisAssembly = Assembly.GetExecutingAssembly(); - var extensionManager = new ExtensionManager(thisAssembly) { - DefaultTypeExtensionPrefix = "/TestCentric/Engine/TypeExtensions/", - InitialAddinsDirectory = Path.GetDirectoryName(thisAssembly.Location) - //InternalTraceLevel = InternalTrace.TraceLevel, - //WorkDirectory = Environment.CurrentDirectory - }; - - log.Debug($"Initializing ExtensionManager"); - extensionManager.Initialize(); + var extensionManager = new ExtensionManager("/TestCentric/Engine/TypeExtensions/"); foreach (IDriverFactory factory in extensionManager.GetExtensions()) _factories.Add(factory); diff --git a/src/TestCentric.Agent.Core/Drivers/NUnit2DriverFactory.cs b/src/TestCentric.Agent.Core/Drivers/NUnit2DriverFactory.cs index 3bb6a3d..8080220 100644 --- a/src/TestCentric.Agent.Core/Drivers/NUnit2DriverFactory.cs +++ b/src/TestCentric.Agent.Core/Drivers/NUnit2DriverFactory.cs @@ -16,13 +16,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(); @@ -58,7 +58,14 @@ 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, +#if NET20 + false, 0, null, new[] { domain }, null, null, null) as IFrameworkDriver; +#else + false, 0, null, new[] { domain }, null, null) as IFrameworkDriver; +#endif + //return _driverNode.CreateExtensionObject(domain) as IFrameworkDriver; } } } diff --git a/src/TestCentric.Agent.Core/TestCentric.Agent.Core.csproj b/src/TestCentric.Agent.Core/TestCentric.Agent.Core.csproj index c43f6fd..0606dec 100644 --- a/src/TestCentric.Agent.Core/TestCentric.Agent.Core.csproj +++ b/src/TestCentric.Agent.Core/TestCentric.Agent.Core.csproj @@ -44,10 +44,10 @@ - - + + - +