nunit / nunit-console

NUnit Console runner and test engine
MIT License
215 stars 152 forks source link

Problem loading unmanaged assemblies #1253

Closed CharliePoole closed 10 months ago

CharliePoole commented 2 years ago

Issue #1208 became a sort of anthology of somewhat related problems, leading (me) to a certain amount of confusion. All issues are solved, except the one described in this comment by @cw397. I'm making this problem a separate issue. The extract below gives the original description of the problem.


Our issue is with an unmanaged/native library. We are using a NuGet package (Esri.ArcGISRuntime) that has some unmanaged dependencies. As seems to be the convention, these get copied to a runtimes/{rid}/native folder within our test project bin/Debug directory, e.g. runtimes/win-x64/native. As with all the other issues, tests that use this library run fine in Visual Studio but fail with the error "Could not load ArcGIS Runtime (RuntimeCoreNet100_15.dll) or one of its dependencies" when run with NUnit Console.

I can workaround the issue in a couple of ways:

Copy the relevant files out of the runtimes/{rid}/native folder and put them directly in the bin/Debug directory.
Copy the entire runtimes folder into the .nuget\packages\nunit.consolerunner\3.15.2\tools\agents\net6.0 folder and also copy the relevant items from our test project's .deps.json file into the nunit-agent.deps.json file.

I have found some explanation about how native assemblies are probed for (https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing), and potential ways of configuring it (https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md), however I haven't been able to figure out any better workarounds than the two listed above.

CharliePoole commented 1 year ago

@cw397 Would you please try running with version 3.16.0-dev00076, available from our MyGet feed?

cw397 commented 1 year ago

That version doesn't seem to help I'm afraid, I still get the same behaviour.

CharliePoole commented 1 year ago

@cw397 Thanks... I had made substantial changes to how dependencies are analyzed but I guess unmanaged dependencies still need to be addressed.

vgriph commented 1 year ago

I've been able to work around this issue by using a SetUpFixture as follows:

[SetUpFixture, Order(1)] // Order doesn't work, so it has to be in a namespace higher than other namespaces if other setup fixtures depend on unmanaged dlls
public class SetUpUnmanagedAssembliesResolving
{
    private AssemblyDependencyResolver runtimeResolver;

    [OneTimeSetUp]
    public void SetupAssemblyLoadContextToHandleUnmanagedAssemblyLoadFailure()
    {
        var assembly = typeof(SetUpUnmanagedAssembliesResolving).Assembly;
        var testAssemblyLoadContext = AssemblyLoadContext.GetLoadContext(assembly) ?? AssemblyLoadContext.Default;
        testAssemblyLoadContext.ResolvingUnmanagedDll += OnResolvingUnmanagedDll;

        var assemblyLocation = assembly.Location;
        runtimeResolver = new AssemblyDependencyResolver(assemblyLocation);
    }

    private IntPtr OnResolvingUnmanagedDll(Assembly requestingAssembly, string dllName)
    {
        var path = runtimeResolver.ResolveUnmanagedDllToPath(dllName);
        if (!string.IsNullOrEmpty(path))
        {
            return NativeLibrary.Load(path);
        }

        return IntPtr.Zero;
    }
}
veleek commented 10 months ago

I'm working on a fix for this.

bilbothebaggins commented 10 months ago

I have exactly this issue and would just like to add my details here to maybe make this more discoverable for future readers. I'm using the latest nunit-consolerunner 3.16.3 on Windows 10.

We are using System.Data.SQLite.Core/1.0.118 which uses Stub.System.Data.SQLite.Core.NetStandard. This nuget package contains runtimes\win-x64\native\SQLite.Interop.dll which is copied to this runtimes location in the ouput folder. It is also entered into the ..deps.json file of my project and the nunit project.

Running from within VS or with dotnet test works fine, The native DLL is loaded from (TestAssemblyDir)\runtimes\win-x64\native\SQLite.Interop.dll Running with nuget3-console.exe fails with

1) Error : ...
System.DllNotFoundException : Unable to load DLL 'SQLite.Interop.dll' or one of its dependencies: Das angegebene Modul wurde nicht gefunden. (0x8007007E)
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_config_none(SQLiteConfigOpsEnum op)
   at System.Data.SQLite.SQLite3.StaticIsInitialized()

Adding the workaround code posted by https://github.com/nunit/nunit-console/issues/1253#issuecomment-1782861277 above does work 💯

Obvioulsy, having #1317 make it into production will be ideal, but I fear I'll have to live with the workaround for now.