SteveGilham / altcover

Cross-platform coverage gathering and processing tool set for dotnet/.Net Framework and Mono
MIT License
498 stars 18 forks source link

`dotnet test /p:AltCover=true /p:AltCoverInPlace=true` results in System.IO.IOException: The process cannot access the file 'C:\src\testprj\bin\Debug\net8.0\AltCover.Recorder.g.dll' because it is being used by another process. #204

Closed CeesKaas closed 5 months ago

CeesKaas commented 8 months ago

I was running into issues while trying to integrate altcover in our build process. because of some of how we built some of our tests we required the inplace option but running into an IOException (and a couple seconds hang) at the end of the testing process

used version : altcover nuget package version 8.6.125

reproduction:

dotnet new nunit -n testprj
cd testprj
dotnet add package altcover
dotnet test /p:AltCover=true /p:AltCoverInPlace=true

result:

  Determining projects to restore...
  All projects are up-to-date for restore.
C:\src\testprj\UnitTest1.cs(6,17): warning S1186: Add a nested comment explaining why this method is empty, throw a 'No
tSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [C:\src\testprj\
testprj.csproj]
  testprj -> C:\src\testprj\bin\Debug\net8.0\testprj.dll
  Creating folder C:\src\testprj\bin\Debug\net8.0\__Saved_testprj\
  Saving files to C:\src\testprj\bin\Debug\net8.0\__Saved_testprj\
  Instrumenting files in C:\src\testprj\bin\Debug\net8.0\
     => C:\src\testprj\bin\Debug\net8.0\__Saved_testprj\AltCover.Monitor.dll
     => C:\src\testprj\bin\Debug\net8.0\__Saved_testprj\NUnit3.TestAdapter.dll
     => C:\src\testprj\bin\Debug\net8.0\__Saved_testprj\testprj.dll

  Coverage Report: C:\src\testprj\coverage.xml

      C:\src\testprj\bin\Debug\net8.0\testprj.dll
                  <=  testprj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
      C:\src\testprj\bin\Debug\net8.0\NUnit3.TestAdapter.dll
                  <=  NUnit3.TestAdapter, Version=4.2.0.0, Culture=neutral, PublicKeyToken=null
      C:\src\testprj\bin\Debug\net8.0\AltCover.Monitor.dll
                  <=  AltCover.Monitor, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null
  Settings Before:
  Settings After: C:\Users\ckaas\AppData\Local\Temp\tmpvkftnj.altcover.runsettings
Test run for C:\src\testprj\bin\Debug\net8.0\testprj.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.8.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 22 ms - testprj.dll (net8.0)
  ... C:\src\testprj\coverage.xml.0.acv (2,999b)
  2,583 visits recorded in 00:00:00.0237510 (108,753 visits/sec)
  A total of 2,583 visits recorded
  Coverage statistics flushing took 0.18 seconds
  Visited Classes 50 of 103 (48.54)
  Visited Methods 197 of 501 (39.32)
  Visited Points 834 of 2364 (35.28)
  Visited Branches 316 of 1559 (20.27)
  Maximum CRAP score 342

  ==== Alternative Results (includes all methods including those without corresponding source) ====
  Alternative Visited Classes 50 of 104 (48.08)
  Alternative Visited Methods 200 of 520 (38.46)
  Alternative maximum CRAP score 342

couple of seconds nothing and then

  System.IO.IOException: The process cannot access the file 'C:\src\testprj\bin\Debug\net8.0\AltCover.Recorder.g.dll' b
  ecause it is being used by another process.
     at System.IO.FileSystem.DeleteFile(String fullPath)
     at <StartupCode$AltCover-Engine>.$Tasks.Execute@542-2.Invoke(String path)
     at AltCover.CommandLine.I.doRetry[a,b](FSharpFunc`2 action, FSharpFunc`2 log, Int32 limit, Int32 rest, Int32 depth
  , a f) in /_//AltCover.Engine/CommandLine.fs:line 209
kbilsted commented 7 months ago

I get the same result when running my tests of my project as well.

dotnet test /p:AltCover=true /p:AltCoverAssemblyExcludeFilter="Microsoft|testhost|NUnit3.TestAdapter|AltCover.Monitor|GreenFeetWorkFlow.Tests" works

SteveGilham commented 5 months ago

This is an unfortunate effect of the way that, on Windows, at least, file locks can take some appreciable time to release even after the process that took them out has exited, so the dotnet test script takes a back-off and retry approach when it encounters such a lingering lock.

What is going on is that with the --inplace option, the as-built files are saved away, and instrumented files, including the generated recorder assembly are placed in the build target directory. The dotnet test script runs the test, runs the coverage collection (which involves reading the location of the coverage report from the recorder), then goes to restore the target directory to the original state, copying back the uninstrumented files and deleting the added recorder assembly - and it's this last step that's causing the observed behaviour.

Changes that can be made would be cosmetic - not just echoing the exception message, but putting out something more anodyne; and increasing the retry frequency.

SteveGilham commented 5 months ago

The message is emitted only after a number of attempts have failed; so I've added some mitigations - tightening the scope of the use of the recorder file, so the locks will be released sooner, increasing the frequency of retries, and finally replacing the exception message with a more informational Failed to delete file [name].

SteveGilham commented 5 months ago

Release 8.8.21 mitigates the likelihood of the issue, and improves the resulting error message if the file cannot be promptly deleted.

SteveGilham commented 5 months ago

Assuming sufficiently resolved.