Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nunit-console ignores deps.json file #1311

Closed
svg2003 opened this issue Jan 28, 2023 · 22 comments
Closed

nunit-console ignores deps.json file #1311

svg2003 opened this issue Jan 28, 2023 · 22 comments

Comments

@svg2003
Copy link
Contributor

svg2003 commented Jan 28, 2023

Could you please somehow provide a way to load dependencies for netcore test assemblies from .deps.json file, instead of loading dependencies from files next to the test dll?

I'm using latest 3.16.2 nunit console and have below test.
In output - (in g:\Net6.Test folder) mytest.dll and System.Threading.AccessControl.dll.
When I start it from nunit-console, test throws an error due to wrong dependencies.

    [Test]
    public void TestDeps()
    {
        var allowEveryoneRule = new EventWaitHandleAccessRule(identity,
            EventWaitHandleRights.FullControl,
            AccessControlType.Allow);
        var securitySettings = new EventWaitHandleSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
    }
  1. Error : Amat.CgaFE.Bridge.TemporaryNet6Tests.Test.NUnitTestFixture1.TestDeps
    System.ArgumentNullException : Value cannot be null. (Parameter 'identity')
    at System.Security.AccessControl.AuthorizationRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
    at System.Security.AccessControl.AccessRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type)
    at System.Security.AccessControl.EventWaitHandleAccessRule..ctor(IdentityReference identity, EventWaitHandleRights eventRights, AccessControlType type)
    at Amat.CgaFE.Bridge.TemporaryNet6Tests.Test.NUnitTestFixture1.TestDeps() in G:\TemporaryNet6Tests.Test\NUnitTestFixture1.cs:line 71

Run Settings
DisposeRunners: True
InternalTraceLevel: Verbose
ImageRuntimeVersion: 4.0.30319
ImageTargetFrameworkName: .NETCoreApp,Version=v6.0
ImageRequiresX86: False
ImageRequiresDefaultAppDomainAssemblyResolver: False
TargetRuntimeFramework: netcore-6.0
NumberOfTestWorkers: 32

So, I started to print assemblies location into console and here is the difference:

    [Test]
    public void TestDeps()
    {
        var identity = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
        Console.WriteLine("IdentityAssembly: {0}, {1}",
            identity.GetType().Assembly.Location,
            identity.GetType().Assembly.ImageRuntimeVersion);
        Console.WriteLine(" EventWaitHandleAccessRule: {0}, {1}",
            typeof(EventWaitHandleAccessRule).Assembly.Location,
            typeof(EventWaitHandleAccessRule).Assembly.ImageRuntimeVersion);

        var allowEveryoneRule = new EventWaitHandleAccessRule(identity,
            EventWaitHandleRights.FullControl,
            AccessControlType.Allow);
        var securitySettings = new EventWaitHandleSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
    }

From MSVS (I'm using Resharper.TestRunner there):

IdentityAssembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.11\System.Security.Principal.Windows.dll, v4.0.30319
EventWaitHandleAccessRule: G:\Net6.Test\runtimes\win\lib\net6.0\System.Threading.AccessControl.dll, v4.0.30319

This is correct - System.Threading.AccessControl.dll is loaded from deps.json file for test assembly.

From nunit-console: - System.Threading.AccessControl.dll assembly loaded from wrong path, next to the mytest.dll.
IdentityAssembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.11\System.Security.Principal.Windows.dll, v4.0.30319
EventWaitHandleAccessRule: G:\Net6.Test\System.Threading.AccessControl.dll, v4.0.30319

Errors, Failures and Warnings

  1. Error : Amat.CgaFE.Bridge.TemporaryNet6Tests.Test.NUnitTestFixture1.TestDeps
    System.ArgumentNullException : Value cannot be null. (Parameter 'identity')
    at System.Security.AccessControl.AuthorizationRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
    at System.Security.AccessControl.AccessRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type)
    at System.Security.AccessControl.EventWaitHandleAccessRule..ctor(IdentityReference identity, EventWaitHandleRights eventRights, AccessControlType type)
    at Amat.CgaFE.Bridge.TemporaryNet6Tests.Test.NUnitTestFixture1.TestDeps() ...

From nunit-console, but I manually deleted System.Threading.AccessControl.dll file from a folder - now test passed, but assembly is loaded NOT from real dependencies again:

IdentityAssembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.11\System.Security.Principal.Windows.dll, v4.0.30319
EventWaitHandleAccessRule: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.11\System.Threading.AccessControl.dll, v4.0.30319

Test Run Summary
Overall result: Warning
Test Count: 8, Passed: 6, Failed: 0, Warnings: 0, Inconclusive: 0, Skipped: 2

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 28, 2023

Yes, and you can use System.Runtime.Loader.AssemblyDependencyResolver for that to parse dependencies:

System.Runtime.Loader.AssemblyDependencyResolver resolver = new AssemblyDependencyResolver("testAssemby.dll");
string typeAssemblyPath = resolver.ResolveAssemblyToPath("missedDependency);

@CharliePoole
Copy link
Collaborator

@svg2003 Actually the 3.16 engine does use the .deps.json file to locate assemblies. The existing code, uses the Microsoft.Extensions.DependencyModel package, but it uses an older version because the loading is done under .NET Core 3.1. I think this should probably be updated to use .NET 6.0 and a newer version of the package, but I've since left the project.

This is a difficult and almost completely undocumented area of .NET. If you know something about it you may want to consider making a contribution to solve this problem.

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 28, 2023

Interesting. When I got source code, build it and replace my official version with new net6.0 agent - it seems starts load them smoothly. And I noticed - it (new version) doesn't have Microsoft.Extensions.DependencyModel.dll file in net6.0 folder at all.
Need to compare what it's building and what is posted binaries (https://github.com/nunit/nunit-console/releases/download/3.16.2/NUnit.Console-3.16.2.zip)

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 28, 2023

@CharliePoole ,

Seems Microsoft.Extensions.DependencyModel (or some logic, based on it) broke everything. Test works fine in 3.15.2 (where I don't see Microsoft.Extensions.DependencyModel.dll in agents\net6.0 folder), and fails in 3.16.0, 3.16.1 and 3.16.2

@CharliePoole
Copy link
Collaborator

Of course, some tests don't pass in 3.15.2, and pass in 3.16.2. In particular, assemblies from dependent packages, which are not copied by .NET cannot be opened. Hopefully, someone still on the @nunit/engine-team is reading this. :-)

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 28, 2023

I recall, I fixed some magic errors about "Cannot load XXX or their dependencies", changing framework in runtimeconfig.json for agent. Yes, it's not too good to change official config manually, and I'd rather to pass them (deps.json and runtimeconfig.json) as some command line parameters, but it is, what it is.

"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.WindowsDesktop.App",
"version": "6.0.0"
},

PS. Let's keep original error, so google could index it and help other people to resolve the similar error

12:05:16.046 281>EXEC : 18) error : TestXXX.TestProcessMessageWithSpecialChars []
System.IO.FileNotFoundException : Could not load file or assembly 'System.Diagnostics.EventLog, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.

@CharliePoole
Copy link
Collaborator

In fact, at one point, I had that as an additional framework (along with asp.net core) but that led to #1274. :-(

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 28, 2023

People usually know what target they are going to test (including target and runtime), so, might be, if they could have option to pass runtimeconfig.json for agent as command line option, it would help.
Now I just copied everything into bin-WindowsDesktop folder, change configs there and execute for my targets, but it's very not straightforward for anyone who will support it after.
Or probably some humane-readable option for target, like "WindowsDesktopApp" and runner would resolve it into appropriate config file.

@CharliePoole
Copy link
Collaborator

That's possible, although ideally I'd rather see the console just know how to load things the way Microsoft does. Hopefully, somebody will pick up this project soon and improve this area one way or another.

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 29, 2023

After more tests, I've decided to keep 3.16.2. Indeed some assemblies are not loaded in 3.15.2, some in 3.16.2.
So, workaround I chose - delete particular assembly file from output in static constructor for test.
Hopefully, at some point it would be fixed in later nunit version and I will get rid of it.
Might be it's just our not exactly right usage - we have huge project that need to be switched from net472 to net6, and we cannot do it at one time - need to keep mix, and somehow keep it working with all breaking changes our lovely MS did.

@CharliePoole
Copy link
Collaborator

@svg2003 Is the case where a reference assembly is present in the output directory the only regression you have seen? I have a suspicion as to where that error may be found in the code.

@svg2003
Copy link
Contributor Author

svg2003 commented Jan 29, 2023

@CharliePoole,

If I remember correct, for latest 3.16.2, I have 2 issues (actually there are more, but they are different). Sorry, after all edits, message became pretty big. Hopefully, it will help

Let's look on them with System.Threading.AccessControl package structure. I'm not sure about terminology, so would reference on files and folders structure from appropriate nuget package.
My test output folder is g:\net6.test.

1. My test assembly (targeted to net6) use class from System.Threading.AccessControl explicitly in test code

    private static void CurrentDomainOnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
    {
        Console.WriteLine("{0}: Loaded assembly: {1}", ++s_id, args.LoadedAssembly.Location);
    }

    [Test]
    public void TestDeps()
    {
        var identity = new SecurityIdentifier(WellKnownSidType.WorldSid, null);

       Console.WriteLine("{0}: IdentityAssembly: {1}",
            ++s_id,
            identity.GetType().Assembly.Location);
        Console.WriteLine("{0}: EventWaitHandleAccessRule: {1}",
            ++s_id,
            typeof(EventWaitHandleAccessRule).Assembly.Location);

        var allowEveryoneRule = new EventWaitHandleAccessRule(identity,              => exception is here
            EventWaitHandleRights.FullControl,
            AccessControlType.Allow);
        var securitySettings = new EventWaitHandleSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
    }

Then, I have System.Threading.AccessControl.dll file from system.threading.accesscontrol.6.0.0\lib\net6.0\ package folder next to my test.dll test assembly and nunit-console would load this file.
And I will get following assemblies loaded and exception at the end

1: Loaded assembly: G:\Net6.Test\Rhino.Mocks.Standard.dll
2: Loaded assembly: G:\Net6.Test\Castle.Core.dll
3: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.NonGeneric.dll
4: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.dll
5: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
6: Loaded assembly: G:\Net6.Test\System.Threading.AccessControl.dll
7: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.AccessControl.dll
8: IdentityAssembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
9: EventWaitHandleAccessRule: G:\Net6.Test\System.Threading.AccessControl.dll
10: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Diagnostics.StackTrace.dll
11: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.Metadata.dll
12: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Immutable.dll
13: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Specialized.dll

Errors, Failures and Warnings

  1. Error : NUnitTestFixture1.TestDeps
    System.ArgumentNullException : Value cannot be null. (Parameter 'identity')
    at System.Security.AccessControl.AuthorizationRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
    at System.Security.AccessControl.AccessRule..ctor(IdentityReference identity, Int32 accessMask, Boolean isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type)
    at System.Security.AccessControl.EventWaitHandleAccessRule..ctor(IdentityReference identity, EventWaitHandleRights eventRights, AccessControlType type)
    at NUnitTestFixture1.TestDeps() in NUnitTestFixture1.cs:line 108

When I manually delete a file, test passed with below dependencies:

1: Loaded assembly: G:\Net6.Test\Rhino.Mocks.Standard.dll
2: Loaded assembly: G:\Net6.Test\Castle.Core.dll
3: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.NonGeneric.dll
4: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.dll
5: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
6: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.13\System.Threading.AccessControl.dll
7: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.AccessControl.dll
8: IdentityAssembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
9: EventWaitHandleAccessRule: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.13\System.Threading.AccessControl.dll
10: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Specialized.dll

Here is a deps.json and if I will manually copy (or what 3.15.2 version does, load it from runtime folder), test also would pass

  "System.Threading.AccessControl/6.0.0": {
    "dependencies": {
      "System.Security.AccessControl": "6.0.0"
    },
    "runtime": {
      "lib/net6.0/System.Threading.AccessControl.dll": {
        "assemblyVersion": "6.0.0.0",
        "fileVersion": "6.0.21.52210"
      }
    },
    "runtimeTargets": {
      "runtimes/win/lib/net6.0/System.Threading.AccessControl.dll": {
        "rid": "win",
        "assetType": "runtime",
        "assemblyVersion": "6.0.0.0",
        "fileVersion": "6.0.21.52210"
      }
    }
  },

2. My test assembly (targeted to net6) does not use class from System.Threading.AccessControl explicitly in test code. But it's used in tested class, targeted to net472, might be through the some dependencies chain like AssemblyA.Foo -> AssemblyB.Goo

    [Test]
    public void TestCreateEvent()
    {
        EventWaitHandleControl.CreatePublic(true, EventResetMode.ManualReset, "test-Event", out _); => essentially EventWaitHandleAcl.Create(initialState, mode, name, out createdNew, security) call from System.Threading.AccessControl assembly
    }

1: Loaded assembly: G:\Net6.Test\Rhino.Mocks.Standard.dll
2: Loaded assembly: G:\Net6.Test\Castle.Core.dll
3: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.NonGeneric.dll
4: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.dll
5: Loaded assembly: G:\Net6.Test\OurTestedAssembly.dll
6: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.13\System.Threading.AccessControl.dll
7: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.AccessControl.dll
8: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
9: Loaded assembly: G:\Net6.Test\System.Threading.AccessControl.dll
10: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Diagnostics.StackTrace.dll
11: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.Metadata.dll
12: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Specialized.dll

Errors, Failures and Warnings

  1. Error : NUnitTestFixture1.TestCreateEvent
    System.MissingMethodException : Method not found: 'System.Threading.EventWaitHandle System.Threading.EventWaitHandleAcl.Create(Boolean, System.Threading.EventResetMode, System.String, Boolean ByRef, System.Security.AccessControl.EventWaitHandleSecurity)'.
    at XXX.AccessControl.EventWaitHandleControl.Create(Boolean initialState, EventResetMode mode, String name, Boolean& createdNew, EventWaitHandleSecurity security)
    at XXX.AccessControl.EventWaitHandleControl.CreatePublic(Boolean initialState, EventResetMode mode, String name, Boolean& createdNew) in EventWaitHandleControl.cs:line 32
    at NUnitTestFixture1.TestCreateEvent() in NUnitTestFixture1.cs:line 135

With manually deleted file - test is passed:

1: Loaded assembly: G:\Net6.Test\Rhino.Mocks.Standard.dll
2: Loaded assembly: G:\Net6.Test\Castle.Core.dll
3: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.NonGeneric.dll
4: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.dll
5: Loaded assembly: G:\Net6.Test\OurTestedAssembly.dll
6: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.13\System.Threading.AccessControl.dll
7: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.AccessControl.dll
8: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
9: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Specialized.dll

From MSVS test run (trough the Reshaper):

1: Loaded assembly: G:\Net6.Test\Rhino.Mocks.Standard.dll
2: Loaded assembly: G:\Net6.Test\Castle.Core.dll
3: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Reflection.dll
4: Loaded assembly: G:\Net6.Test\OurTestedAssembly.dll
5: Loaded assembly: G:\Net6.Test\runtimes\win\lib\net6.0\System.Threading.AccessControl.dll
6: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.AccessControl.dll
7: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Security.Principal.Windows.dll
8: Loaded assembly: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.13\System.Collections.Specialized.dll

@CharliePoole
Copy link
Collaborator

@svg2003 Thanks for all that! I'll experiment with it and post if I come up with anything.

@svg2003
Copy link
Contributor Author

svg2003 commented Feb 2, 2023

Hi @CharliePoole ,

Did you have a chance to look on it?

And seems for System.Configurationion.ConfigurationManager it's even worse - we have 2 loaded assemblies and class, that is using configuration.GetSection() call (a lot of legacy code could do it) throw an exception about wrong inheritance.
Deletion of System.Configuration.ConfigurationManager.dll from test folder helps.

System.Configuration.ConfigurationErrorsException : An error occurred creating the configuration section handler: Type 'XxxSection' does not inherit from 'System.Configuration.IConfigurationSectionHandler'.

In domain: System.Configuration.ConfigurationManager, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 at C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\6.0.13\System.Configuration.ConfigurationManager.dll
In domain: System.Configuration.ConfigurationManager, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 at G:\Net6.Test\System.Configuration.ConfigurationManager.dll

@CharliePoole
Copy link
Collaborator

Sorry @svg2003, I am impacted by this issue too (in TestCentric GUI) so I'd like to see a solution but I haven't had the time to dig any deeper as this week is very busy for me.

@svg2003
Copy link
Contributor Author

svg2003 commented Feb 2, 2023

That's fine, don't worry. I hope I could have a time to take a look tomorrow or on Friday.

@svg2003
Copy link
Contributor Author

svg2003 commented Feb 2, 2023

@CharliePoole,

I quick looked through the code and added some debug prints, and seems here are the issues.
Will check proposed code on my tests tomorrow to see if nothing new would break.
Hopefully, it will help

1. Assembly can be loaded twice:

https://github.com/nunit/nunit-console/blob/main/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs#L31

 if (loadedAssembly != null)
            loadedAssembly = base.Load(name);

If assembly in domain, code will execute base.Load() that may produce undefined behavior. Sometimes it will return you null assembly, sometimes, loaded from another place.
That's why we see issue with System.Configuration.ConfigurationManager assembly.

I'd suggest you either delete it completely or change condition to ' == null '

2. OnResolving callback would be never called, if assembly is next to the test assembly.

Seems callback will be called only if you return null in Load(), so this condition would be executed first

https://github.com/nunit/nunit-console/blob/main/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs#L34

You will return wrong assembly (not from deps.json file). That's why I see my original issue with System.ThreadingAccessControl.dll

So, probably need to call resolver explicitly here

3. Resolving logic works incorrect

I frankly have no idea how resolver class is working, but (when I deleted assembly manually), it's NOT resolved from deps.json.
I don't know if you need that logic, but suggest try to use just AssemblyDependencyResolver.ResolveAssemblyFromPath() here before.

So, to summarize, I'd do something like this:

internal sealed class TestAssemblyLoadContext : AssemblyLoadContext
{
private readonly string _testAssemblyPath;
private readonly string _basePath;
private readonly TestAssemblyResolver _resolver;
private readonly System.Runtime.Loader.AssemblyDependencyResolver _runtimeResolver;

    public TestAssemblyLoadContext(string testAssemblyPath)
    {
        _testAssemblyPath = testAssemblyPath;
        _resolver = new TestAssemblyResolver(this, testAssemblyPath);
        _basePath = Path.GetDirectoryName(testAssemblyPath);
        _runtimeResolver = new AssemblyDependencyResolver(testAssemblyPath);
    }

    protected override Assembly Load(AssemblyName name)
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var loadedAssembly = assemblies.FirstOrDefault(x => x.GetName().Name == name.Name);
        if (loadedAssembly != null)
        {
            return loadedAssembly;
        }

        loadedAssembly = base.Load(name);
        if (loadedAssembly != null)
        {
            return loadedAssembly;
        }

        var runtimeResolverPath = _runtimeResolver.ResolveAssemblyToPath(name);
        if (string.IsNullOrEmpty(runtimeResolverPath) == false &&
            File.Exists(runtimeResolverPath))
        {
            loadedAssembly = LoadFromAssemblyPath(runtimeResolverPath);
        }

        if (loadedAssembly != null)
        {
            return loadedAssembly;
        }

        loadedAssembly = _resolver.Resolve(this, name); // basically just  TestAssemblyResolver.OnResolving(context, name) call        
        if (loadedAssembly != null)
        {
            return loadedAssembly;
        }

        // Load assemblies that are dependencies, and in the same folder as the test assembly,
        // but are not fully specified in test assembly deps.json file. This happens when the
        // dependencies reference in the csproj file has CopyLocal=false, and for example, the
        // reference is a projectReference and has the same output directory as the parent.
        string assemblyPath = Path.Combine(_basePath, name.Name + ".dll");
        if (File.Exists(assemblyPath))
        {
            loadedAssembly = LoadFromAssemblyPath(assemblyPath);
        }

        return loadedAssembly;
    }

@svg2003
Copy link
Contributor Author

svg2003 commented Feb 3, 2023

@CharliePoole ,

I used above code in our project and it solved both mentioned cases and seems does not bring any new issues. I'd appreciate, if you would ask someone from nunit team (or could do it yourselves), to create PR and deploy new version with fix.

@OsirisTerje
Copy link
Member

@svg2003 Did you actually get rid of the deps.json issue with the PR? When working with the adapter now, it doesn't stop that. (Note: I haven't read all of the text above in detail.....)

@svg2003
Copy link
Contributor Author

svg2003 commented Feb 13, 2023

@OsirisTerje ,

Could you please elaborate, what are you talking about? I don't use adapter and don't know what is it and how it affects anything. Just for test dll, it fixed the issue for cases with managed assemblies. Native DLLs still cannot be loaded properly at some cases, but I frankly, didn't check why.

@OsirisTerje
Copy link
Member

OsirisTerje commented Feb 13, 2023

@svg2003 I assumed the error you saw when raising this issue is the same one I see when I add 3.16.2 to the adapter code, not being able to find the Microsoft.Extensions.DependencyModel package. But I see now you have a different error message.

@svg2003
Copy link
Contributor Author

svg2003 commented Apr 5, 2023

Closed as it fixed here
#1317

@svg2003 svg2003 closed this as completed Apr 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants