nunit / nunit-console

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

Unable to load DLL 'wpfgfx_cor3.dll' or one of its dependencies during unit test #1328

Open Ql1m4xx opened 1 year ago

Ql1m4xx commented 1 year ago

Description: currently we are migrating our Windows Application (WPF) from .NET Framework 4.8 to .NET 6. We are using NUnit to test our user interface but we are getting the error that the 'wpfgfx_cor3.dll' could not be loaded

Stacktrace: 1) Error : Productnamespace.GUIFoundation.Test.ControlTests.ExtendedControl.ExtendedControlTests.BaseElementViewModelTest System.DllNotFoundException : Unable to load DLL 'wpfgfx_cor3.dll' or one of its dependencies: The specified module could not be found. (0x8007007E) at MS.Win32.PresentationCore.UnsafeNativeMethods.MILFactory2.CreateFactory(IntPtr& ppIFactory, UInt32 SDKVersion) at System.Windows.Media.FactoryMaker..ctor() at System.Windows.Media.StreamAsIStream.IStreamFrom(IntPtr memoryBuffer, Int32 bufferSize) at System.Windows.Media.Imaging.BitmapDecoder.GetIStreamFromStream(Stream& bitmapStream) at System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle) at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache) at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation() at System.Windows.Media.Imaging.BitmapImage.EndInit() at System.Windows.Media.Imaging.BitmapImage..ctor(Uri uriSource, RequestCachePolicy uriCachePolicy) at System.Windows.Media.Imaging.BitmapImage..ctor(Uri uriSource) at Productnamespace.GUIFoundation.Test.ControlTests.ExtendedControl.ExtendedControlTests.SetUp() in C:\Git\Repos\Product\Libraries\GUIFoundation\Sources\GuiFoundation.Test\ControlTests\ExtendedControl\ExtendedControlTests.cs

It's the same issue like #1319

The workaround to adjust the nunit-agent.runtimeconfig.json is not feasible for us because we dont want to adjust the "installed" file that comes with the NUnit.Console

Used Version: 3.16.2

Thanks for your help in advance

OsirisTerje commented 1 year ago

Does it work with 3.15.2 ?

Ql1m4xx commented 1 year ago

Does it work with 3.15.2 ?

I got not the same problem with version 3.15.2 1) Invalid : C:/Git/Repos/Product/Libraries/GUIFoundation/Sources/GuiFoundation.Test/bin/Debug/Product.GUIFoundation.Test.dll Unable to load one or more of the requested types. Could not load file or assembly 'System.Configuration.ConfigurationManager, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified. Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. Could not load file or assembly 'PresentationCore, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. ----> Could not load file or assembly 'System.Configuration.ConfigurationManager, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified. ----> Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. ----> Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. ----> Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. ----> Could not load file or assembly 'PresentationFramework, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified. ----> Could not load file or assembly 'PresentationCore, Version=6.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.

OsirisTerje commented 1 year ago

Ok, then I assume it doesn't work with using dotnet test either?

It would also be great if you could provide a very small repro for this.

spkl commented 1 year ago

Hi OsirisTerje, I have created a repository with a repro for this: https://github.com/spkl/nunit-console-1328-repro The test shows and then closes a dialog from a WPF library. I have pasted the results from dotnet test and the two discussed NUnit Console versions as a readme.

I'm on the same dev team as Ql1m4xx. This is not a perfect repro for our scenario, as we are also using a third-party library (DevExpress), but the errors seem to be comparable for now. Thanks for looking into this.

OsirisTerje commented 1 year ago

@spkl @Ql1m4xx Thanks for the repro. I can confirm I do see the same. It works when using Visual Studio (2022) and it works using dotnet test, either on csproj or directly on the test dll.

I'll summarize the results that I see:

Visual Studio 2022, it works: image

dotnet test , project level , it works image

dotnet test nunit-console-1328-test.dll, in the bin folder, It works: image

All of these use the adapter version 4.2.1 (which has the engine 3.15.2 built-in)

It fails with the nunit console (version 3.15.2, and @spkl show it fails with 3.16.2 too) image

As @CharliePoole have pointed out earlier, the assembly loading with the adapter is done by the MS testhost, and not the engine, whereas the console will do the loading itself. So that's where it struggles:

Thanks again, we can use this to start working on it.

@CharliePoole Any suggestions? (and yes, I have read the other stuff on this, so let's start from that.) @rprouse Rings any bells?

carstencodes commented 1 year ago

I might have one additional clue.

The Microsoft Test SDK generates an entrypoint into each test assembly. It can be seen from any reflector like ILspy or dotpeek. The entrypoint is called from the runtime, which is selected using a runtime.config.json in version and experience/workload.

Hence the unit test is simply deriving from Microsoft.NET.SDK, the resulting runtime should be NetCoreApp, hence a simple console application. As the assembly under test actively uses WPF and this is executed during the test, it must use the WindowsDesktop runtime.

Hence adding

<UseWPF>true</UseWPF>

to the csproj of the unit test will actually enforce the runtime to select the WindowsDesktop App, which provides the WPF runtime libraries.

This way, the test should run.

@spkl @Ql1m4xx Can you please give it a try?

spkl commented 1 year ago

@carstencodes nunit-console-1328-test.runtimeconfig.json already contains Microsoft.NETCore.App and Microsoft.WindowsDesktop.App. I've tried adding UseWPF, but it changes nothing. If I remember correctly from other issues, NUnit Console does not evaluate the runtimeconfig.json of a test assembly.

carstencodes commented 1 year ago

@spkl Yeah, but it lead me on the right track.

I had to run

$ COREHOST_TRACE=1 dotnet test 2> dotnet-test.corehost_trace.txt
...
$ COREHOST_TRACE=1 dotnet nunit  nunit-console-1328-test/bin/Debug/net6.0-windows/nunit-console-1328-test.dll 2> dotnet-nunit.corehost_trace.txt

and diff the resulting files to actually find it.

The target runtime is not determined by the test assembly but in this case by the starting process.

I replaced

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "rollForward": "Major",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    },
    "configProperties": {
      "System.Reflection.Metadata.MetadataUpdater.IsSupported": false
    }
  }
}

with

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "rollForward": "Major",
    "frameworks": [
      {
        "name": "Microsoft.NETCore.App",
        "version": "6.0.0"
      },
      {
        "name": "Microsoft.WindowsDesktop.App",
        "version": "6.0.0"
      }
    ],
    "configProperties": {
      "System.Reflection.Metadata.MetadataUpdater.IsSupported": false
    }
  }
}

in the nunit-console's runtime config json file.

The result was:

$ dotnet nunit nunit-console-1328-test/bin/Debug/net6.0-windows/nunit-console-1328-test.dll
NUnit Console 3.16.2 (Release)
Copyright (c) 2022 Charlie Poole, Rob Prouse
Mittwoch, 15. März 2023 08:46:01

Runtime Environment
   OS Version: Microsoft Windows 10.0.19044
  Runtime: .NET 6.0.14

Test Files
    nunit-console-1328-test/bin/Debug/net6.0-windows/nunit-console-1328-test.dll

Run Settings
    DisposeRunners: True
    WorkDirectory: C:\git\external\github\nunit-console-1328-repro
    NumberOfTestWorkers: 12

Test Run Summary
  Overall result: Passed
  Test Count: 1, Passed: 1, Failed: 0, Warnings: 0, Inconclusive: 0, Skipped: 0
  Start time: 2023-03-15 07:46:02Z
    End time: 2023-03-15 07:46:08Z
    Duration: 6.272 seconds

Results (nunit3) saved as TestResult.xml

Hence, in order to support additional frameworks like WPF-Test, XUnit-STA, FlaUI or XAMLTest, it might be a good idea to create a desktop-runtime runner for these cases. (It cannot be added to the regular runtime as it might actually break linux/mac support).

rprouse commented 1 year ago

This is great info @carstencodes ,thanks for the work you put into it

OsirisTerje commented 1 year ago

@carstencodes @spkl @Ql1m4xx May I just ask: What is the reason you need to use NUnit.Console and not just use dotnet test?

Ql1m4xx commented 1 year ago

@OsirisTerje We are using NUnit.Console since 10 or more years to execute more than 10k tests with it. We parse the result and display it and it worked very well in the last decade. A switch to dotnet test for us is not a thing that we can do over night. We would highly appreciate that NUnit.Console supports us and I think other users, too, with a solution that dont requires the change of installed files.