microsoft / vstest

Visual Studio Test Platform is the runner and engine that powers test explorer and vstest.console.
MIT License
902 stars 323 forks source link

Port argument seems to be broken #10403

Closed jonash871j closed 1 month ago

jonash871j commented 1 month ago

I'm trying to run some unit tests in a docker container using dotnet vstest. The catch is that I'm doing it with host networking enabled which gives the container direct access to the host machine.

When I start the tests in the container with dotnet vstest Settings.Tests/bin/Release/net8.0/Settings.Tests.dll --Diag:log.txt, it fails and produces this output:

2024-10-24 12:32:21 VSTest version 17.11.1-release-24455-02 (x64)
2024-10-24 12:32:21 
2024-10-24 12:32:21 Starting test execution, please wait...
2024-10-24 12:32:21 Logging Vstest Diagnostics in file: /src/log.txt
2024-10-24 12:32:21 A total of 1 test files matched the specified pattern.
2024-10-24 12:33:51 
2024-10-24 12:33:51 vstest.console process failed to connect to testhost process after 90 seconds. This may occur due to machine slowness, please set environment variable VSTEST_CONNECTION_TIMEOUT to increase timeout.
2024-10-24 12:33:51 Test Run Aborted.

By taking a closer look in the log file, vstest seems to be starting a socket server on a randomly selected port and the client afterwards fails to connect to it.

Server logs:

TpTrace Verbose: 0 : 24, 5, 2024/10/24, 11:42:54.908, 18472446817718, vstest.console.dll, ParallelProxyExecutionManager.StartTestRunOnConcurrentManager: Initializing test run. Started clients: 0
TpTrace Verbose: 0 : 24, 5, 2024/10/24, 11:42:54.908, 18472446980825, vstest.console.dll, ProxyExecutionManager: Test host is always Lazy initialize.
TpTrace Verbose: 0 : 24, 5, 2024/10/24, 11:42:54.908, 18472447370742, vstest.console.dll, TestRequestSender.InitializeCommunication: initialize communication. 
TpTrace Verbose: 0 : 24, 1, 2024/10/24, 11:42:54.909, 18472447686656, vstest.console.dll, TestRunRequest.WaitForCompletion: Waiting with timeout -1.
TpTrace Information: 0 : 24, 5, 2024/10/24, 11:42:54.930, 18472469049297, vstest.console.dll, SocketServer.Start: Listening on endpoint : 127.0.0.1:44411

Client logs:

TpTrace Information: 0 : 37, 1, 2024/10/24, 11:42:55.162, 18472701433080, testhost.dll, DefaultEngineInvoker.Invoke: Start Request Processing.
TpTrace Information: 0 : 37, 11, 2024/10/24, 11:42:55.168, 18472706815112, testhost.dll, SocketClient.OnServerConnected: connected to server endpoint: 127.0.0.1:044411
TpTrace Information: 0 : 37, 12, 2024/10/24, 11:42:55.170, 18472709278718, testhost.dll, DefaultEngineInvoker.StartProcessingAsync: Connected to vstest.console, Starting process requests.
TpTrace Error: 0 : 37, 11, 2024/10/24, 11:42:55.184, 18472723009710, testhost.dll, MulticastDelegateUtilities.SafeInvoke: 1: Invoking callback 1/Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TestRequestHandler for .SocketClient: Server Failed to Connect, failed after 0 ms with: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.AggregateException: One or more errors occurred. (Connection refused)
 ---> System.Net.Sockets.SocketException (111): Connection refused

I believe the problem is with network host mode enabled, I'll need to specify which port the server is being hosted on in the docker-compose file, before the client would be able to connect to it. So it's a bit of a problem that the port is randomly selected.

I noticed in the documentation that there is a 'Port' argument that can be parsed to vstest to use a custom port, so something like this dotnet vstest Settings.Tests/bin/Release/net8.0/Settings.Tests.dll --Port:5126 --Diag:log.txt. But I haven't been able to get this argument to work both in the docker container, but also locally when I try it out in any random test project.

From looking in the logs it seems to have picked the correct specified port, but it's unable to start hosting the vstest server. I have tried to use different ports and tried to disable to firewall, but I still get the same error.

TpTrace Information: 0 : 24, 1, 2024/10/24, 12:03:15.051, 19692643140376, vstest.console.dll, Trying to connect to server on port : 5126
TpTrace Information: 0 : 24, 1, 2024/10/24, 12:03:15.056, 19692648272397, vstest.console.dll, Trying to connect to server on socket : 127.0.0.1:5126 
TpTrace Verbose: 0 : 24, 1, 2024/10/24, 12:03:15.059, 19692651571638, vstest.console.dll, SocketCommunicationManager : SetupClientAsync : Attempting to connect to the server.
TpTrace Error: 0 : 24, 5, 2024/10/24, 12:03:15.079, 19692671730906, vstest.console.dll, Connection Failed with error System.Net.Sockets.SocketException (111): Connection refused
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
nohwnd commented 1 month ago

Hello, which testing framework are you using? This would be ideal case for our new testing.platform. It compiles test projects as executables, so you can very easily put them into docker container. It is supported by MSTest (stable), Xunit (preview), NUnit (preview) and TUnit (stable).

This is how you can dockerize MSTest using the no-docker-file approach:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <EnableMSTestRunner>true</EnableMSTestRunner>
    <OutputType>Exe</OutputType>

    <!-- 
      docs: https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container 
      dotnet publish /t:PublishContainer -tl:false
      docker run helloworldtests
    -->
    <IsPublishable>true</IsPublishable>
    <EnableSdkContainerSupport>true</EnableSdkContainerSupport>

  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MSTest" Version="3.6.1" />

    <PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.12.6" />
    <PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.4.0" />

    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
  </ItemGroup>

  <ItemGroup>
    <Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
  </ItemGroup>

    <ItemGroup>
      <!-- Fix for TestingPlatform, before we release 1.5. -->
      <ContainerEnvironmentVariable Include="platformOptions__resultDirectory" Value="/tmp/TestResults"></ContainerEnvironmentVariable>
    </ItemGroup>

</Project>

The testing platform can still fallback to vstest in dotnet test, and in VS, so you should get great deal of backwards compatibility.

https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-mstest-intro

https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-platform-intro?tabs=dotnetcli

jonash871j commented 1 month ago

Hi, thank you for the quick response, I'm using NUnit. I'll take a look at the new testing platform.

nohwnd commented 1 month ago

This is the above example changed for NUnit

Image

<!-- file HelloWorldTests.csproj -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsPackable>false</IsPackable>

    <!-- Enable Testing.Platform based runner -->
    <EnableNUnitRunner>true</EnableNUnitRunner>
    <OutputType>exe</OutputType>

    <!--
      docs: https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container
      dotnet publish /t:PublishContainer -tl:false
      docker run helloworldtests
    -->
    <IsPublishable>true</IsPublishable>
    <EnableSdkContainerSupport>true</EnableSdkContainerSupport>

  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="coverlet.collector" Version="6.0.2" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
    <PackageReference Include="NUnit" Version="4.2.2" />
    <PackageReference Include="NUnit.Analyzers" Version="4.3.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="5.0.0-beta.3" />
  </ItemGroup>

  <ItemGroup>
    <Using Include="NUnit.Framework" />
  </ItemGroup>

        <PropertyGroup>
                <!-- fix for latest nunit adapter -->
                <GenerateSelfRegisteredExtensions>true</GenerateSelfRegisteredExtensions>
        </PropertyGroup>

    <ItemGroup>
      <!-- Customize output dir and hide telemetry notification. -->
      <ContainerEnvironmentVariable Include="DOTNET_CLI_TELEMETRY_OPTOUT" Value="1"></ContainerEnvironmentVariable>
      <ContainerEnvironmentVariable Include="platformOptions__resultDirectory" Value="/tmp/TestResults"></ContainerEnvironmentVariable>
    </ItemGroup>

</Project>
// file UnitTest1.cs
namespace NUnit1
{
    public class Tests
    {
        [SetUp]
        public void Setup()
        {
        }

        [Test]
        public void Test1()
        {
            Assert.Pass();
        }
    }
}

02_dockerize.zip

jonash871j commented 1 month ago

I have done a bit of testing now and this seems to do the trick, thank you for the help 😃