Modular Structure of the Engine
@CharliePoole is already working on this.
Since Jan 20, 2025.
- Dominant language
- C#
- Stars
- 234
- Forks
- 163
- Avg merge
- 13h 32m
- Merged PRs (30d)
- 25
Description
@nunit/engine-team @nunit/core-team
We already have a number of individual issues around changing the engine's modular structure. I noticed that we don't have a single issue that incorporates an overview, so here it is. It's an Epic and some of it's individual parts are Epics in themselves. I've also labeled it as a Design Decision since should discuss and refine the plan.
Current Structure
Primary Modules
* nunit.engine
* nunit-agent-net462, nunit-agent-net80, etc.
Support Modules
* nunit.engine.core
* nunit.engine.api
* TestCentric.Metadata
Proposed Structure
Primary Modules
* nunit.engine
* nunit-agent-net462, nunit-agent-net80, etc. (now PLUGGABLE extensions)
|
>----nunit.agent.core
Support Modules
nunit.engine.api
NUnit.Extensibility
nunit.common
TestCentric.Metadata
Details
Engine
The engine is now dependent on nunit.engine.core, as are the agents. This creates an inter-dependency, which has frequently interfered with our ability to make changes. Any implementation code needed by the engine belongs in the engine. Issue #1578 deals with this.
Agents
As agents are becoming pluggable (see #909), they will need to be moved into individual projects and packaged separately. The engine and console runner will incorporate agents for .NET 4.6.2, 8.0 and 9.0 as dependencies. Depending on how soon we implement this, we may want to re-create the dropped .NET 6.0 agent as an optionally installable agent. In fact, we could, if desired, restore any of our past out-of-date agents as pluggable agents fairly easily, or we could support someone else who wanted to do so.
Agent Core
The nunit.agent.core assembly, also covered in #1578, will hold common code used by all our agents. We have the choice to either package it separately or use the source as a submodule. If separately packaged, it may either be referenced as a dependency by each agent package or used as a development-only dependency with the module distributed. There are advantages to each approach, which we should discuss in #909.
Extensibility
The new NUnit.Extensibility assembly will incorporate ExtensionManager and related classses in a separate package. ExtensionManager gives lower level access to extensions and is used by the engine's ExtensionService. The purpose of this split is to permit other modules than the engine to incorporate extensibility. Initially, the agents will make use of this, allowing extensions like the NUnit V2 Framework Driver to function. Eventually, we may use it for the console runner and it could even be used for NUnitLite, in order to avoid duplication of extension code. See issue #1049.
Metadata
At it's origins, NUnit used reflection to analyze assemblies before trying to load and run them. With NUnit 3, we switched to Mono.Cecil but eventually ran into problems for two reasons:
- Some users were testing code, which also used
Mono.Cecil, sometimes a different version from ours. - The package eliminated support for platforms we still wanted to support.
Facing the same problems with my TestCentric GUI, I created TestCentric.Metadata with some differences from the original:
- It has a subset of the capability of
Mono.Cecil, supporting only reading of assemblies. This makes it a lot smaller. - I retained support for all platforms we supported. It is currently built to target .NET Framework 2.0 and .NET Standard 2.0.
- Subsequently, I have made a few more changes. In particular, I changed the namespace to avoid collisions with user code when running under the .NET Framework. The package is currently at version 3.0.3.
For the present, I'm planning to keep assembly as is, only making changes as needed. The package is currently used by the ExtensionManager as well as by several engine services, so it will be referenced by nunit.engine, nunit.agent.core and NUnit.Extensibility.
NUnit Common
We currently provide internal trace facilities to our various modules by duplicating the source code. Providing logging to several new modules would increase the number of duplicate copies. I propose to create a package NUnit.InternalTracenunit.common to centralize this facility as well as other commonly used classes. In addition to eliminating code duplication, this might eventually help us combine multiple log files into a single file. I'll create a new issue for this one.
Engine Api
In issue #770, @ChrisMaddock proposed splitting the then-existing api assembly into multiple assemblies. In my plan for that issue, I didn't include that aspect of his proposal. I think should discuss it in the context of this overall issue. See further discussion below.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.