Modifying the reporting channels to make the AdapterChannel a client and leave the TestRunnerChannel a server. This will prevent port conflicts between dotnet test and the Adapter (VS) due to race conditions.

Added E2E tests for dotnet test interactions with an adapter (design time).
This commit is contained in:
Livar Cunha 2016-03-08 15:53:21 -08:00
parent ffedcb315f
commit 8d39adbdbf
22 changed files with 536 additions and 84 deletions

View file

@ -1,6 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25020.0
VisualStudioVersion = 14.0.25029.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED2FE3E2-F7E7-4389-8231-B65123F2076F}"
EndProject
@ -76,6 +77,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.TestFramew
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "dotnet-test.UnitTests", "test\dotnet-test.UnitTests\dotnet-test.UnitTests.xproj", "{857274AC-E741-4266-A7FD-14DEE0C1CC96}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "dotnet-test.Tests", "test\dotnet-test.Tests\dotnet-test.Tests.xproj", "{60C33D0A-A5D8-4AB0-9956-1F804654DF05}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -472,22 +475,6 @@ Global
{0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -504,22 +491,22 @@ Global
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{0B31C336-149D-471A-B7B1-27B0F1E80F83}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|x64.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|x64.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|Any CPU.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|x64.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|x64.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{0724ED7C-56E3-4604-9970-25E600611383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0724ED7C-56E3-4604-9970-25E600611383}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0724ED7C-56E3-4604-9970-25E600611383}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -536,6 +523,38 @@ Global
{0724ED7C-56E3-4604-9970-25E600611383}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{0724ED7C-56E3-4604-9970-25E600611383}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{0724ED7C-56E3-4604-9970-25E600611383}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|x64.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Debug|x64.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|Any CPU.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|x64.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.Release|x64.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{857274AC-E741-4266-A7FD-14DEE0C1CC96}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Debug|x64.ActiveCfg = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Debug|x64.Build.0 = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Release|Any CPU.Build.0 = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Release|x64.ActiveCfg = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.Release|x64.Build.0 = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{60C33D0A-A5D8-4AB0-9956-1F804654DF05}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -565,11 +584,12 @@ Global
{DA8E0E9E-A6D6-4583-864C-8F40465E3A48} = {713CBFBB-5392-438D-B766-A9A585EF1BB8}
{0138CB8F-4AA9-4029-A21E-C07C30F425BA} = {713CBFBB-5392-438D-B766-A9A585EF1BB8}
{BD4F0750-4E81-4AD2-90B5-E470881792C3} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{4A4711D8-4312-49FC-87B5-4F183F4C6A51} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{0745410A-6629-47EB-AAB5-08D6288CAD72} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{0E3300A4-DF54-40BF-87D8-E7658330C288} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{0B31C336-149D-471A-B7B1-27B0F1E80F83} = {0E3300A4-DF54-40BF-87D8-E7658330C288}
{857274AC-E741-4266-A7FD-14DEE0C1CC96} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{4A4711D8-4312-49FC-87B5-4F183F4C6A51} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{0724ED7C-56E3-4604-9970-25E600611383} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{857274AC-E741-4266-A7FD-14DEE0C1CC96} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{60C33D0A-A5D8-4AB0-9956-1F804654DF05} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Xunit;
namespace FakeTests
{
public class GivenThatIWantSomeFakeTests
{
[Fact]
public void It_succeeds()
{
Assert.True(true);
}
[Fact]
public void It_fails()
{
Assert.True(false);
}
}
}

View file

@ -0,0 +1,21 @@
{
"version": "1.0.0-*",
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-23911",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-dev-91790-12"
},
"frameworks": {
"netstandardapp1.5": {
"imports": [
"dnxcore50",
"portable-net45+win8"
]
}
},
"testRunner": "xunit"
}

View file

@ -36,7 +36,8 @@ namespace Microsoft.DotNet.Cli.Build
"Microsoft.DotNet.ProjectModel.Tests",
"Microsoft.Extensions.DependencyModel.Tests",
"ArgumentForwardingTests",
"dotnet-test.UnitTests"
"dotnet-test.UnitTests",
"dotnet-test.Tests"
};
[Target(nameof(PrepareTargets.Init), nameof(SetupTests), nameof(RestoreTests), nameof(BuildTests), nameof(RunTests), nameof(ValidateDependencies))]

View file

@ -41,7 +41,7 @@ namespace Microsoft.DotNet.Tools.Test
dotnetTest.StartListeningTo(testRunnerChannel);
testRunnerChannel.Accept();
testRunnerChannel.Connect();
var testRunner = _testRunnerFactory.CreateTestRunner(
new RunTestsArgumentsBuilder(dotnetTest.PathToAssemblyUnderTest, testRunnerChannel.Port, message));

View file

@ -55,7 +55,7 @@ namespace Microsoft.DotNet.Cli.Tools.Test
dotnetTest.StartListeningTo(testRunnerChannel);
testRunnerChannel.Accept();
testRunnerChannel.Connect();
var testRunner = _testRunnerFactory.CreateTestRunner(
new DiscoverTestsArgumentsBuilder(dotnetTest.PathToAssemblyUnderTest, testRunnerChannel.Port));

View file

@ -11,6 +11,7 @@ namespace Microsoft.DotNet.Tools.Test
public const string TestRunnerTestStarted = "TestExecution.TestStarted";
public const string TestRunnerTestCompleted = "TestRunner.TestCompleted";
public const string TestRunnerTestFound = "TestDiscovery.TestFound";
public const string TestSessionConnected = "TestSession.Connected";
public const string TestSessionTerminate = "TestSession.Terminate";
public const string VersionCheck = "ProtocolVersion";
public const string TestDiscoveryStart = "TestDiscovery.Start";

View file

@ -183,7 +183,7 @@ namespace Microsoft.DotNet.Tools.Test
dotnetTest.StartListeningTo(adapterChannel);
adapterChannel.Accept();
adapterChannel.Connect();
dotnetTest.StartHandlingMessages();
}

View file

@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Extensions.Testing.Abstractions;
using System.Net;
using System.Net.Sockets;
namespace Microsoft.DotNet.Tools.Test
{
public class AdapterReportingChannel : ReportingChannel
{
private readonly IPEndPoint _ipEndPoint;
public static AdapterReportingChannel ConnectTo(int port)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var ipEndPoint = new IPEndPoint(IPAddress.Loopback, port);
return new AdapterReportingChannel(socket, ipEndPoint);
}
private AdapterReportingChannel(Socket connectSocket, IPEndPoint ipEndPoint)
: base(connectSocket, ipEndPoint.Port)
{
_ipEndPoint = ipEndPoint;
}
public override void Connect()
{
Socket = ConnectSocket;
Socket.Connect(_ipEndPoint);
StartReadingMessages();
Send(new Message
{
MessageType = TestMessageTypes.TestSessionConnected
});
}
}
}

View file

@ -13,7 +13,7 @@ namespace Microsoft.DotNet.Tools.Test
int Port { get; }
void Accept();
void Connect();
void Send(Message message);

View file

@ -14,52 +14,26 @@ using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.Tools.Test
{
public class ReportingChannel : IReportingChannel
public abstract class ReportingChannel : IReportingChannel
{
public static ReportingChannel ListenOn(int port)
{
// This fixes the mono incompatibility but ties it to ipv4 connections
var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
listenSocket.Listen(10);
return new ReportingChannel(listenSocket);
}
private BinaryWriter _writer;
private BinaryReader _reader;
private Socket _listenSocket;
private ReportingChannel(Socket listenSocket)
protected ReportingChannel(Socket connectSocket, int port)
{
_listenSocket = listenSocket;
Port = ((IPEndPoint)listenSocket.LocalEndPoint).Port;
ConnectSocket = connectSocket;
Port = port;
}
protected Socket Socket { get; set; }
public event EventHandler<Message> MessageReceived;
public Socket Socket { get; private set; }
public Socket ConnectSocket { get; }
public int Port { get; }
public void Accept()
{
new Thread(() =>
{
using (_listenSocket)
{
Socket = _listenSocket.Accept();
var stream = new NetworkStream(Socket);
_writer = new BinaryWriter(stream);
_reader = new BinaryReader(stream);
// Read incoming messages on the background thread
new Thread(ReadMessages) { IsBackground = true }.Start();
}
}) { IsBackground = true }.Start();
}
public abstract void Connect();
public void Send(Message message)
{
@ -104,6 +78,16 @@ namespace Microsoft.DotNet.Tools.Test
SendError(ex.ToString());
}
protected void StartReadingMessages()
{
var stream = new NetworkStream(Socket);
_writer = new BinaryWriter(stream);
_reader = new BinaryReader(stream);
// Read incoming messages on the background thread
new Thread(ReadMessages) { IsBackground = true }.Start();
}
private void ReadMessages()
{
while (true)
@ -140,10 +124,7 @@ namespace Microsoft.DotNet.Tools.Test
public void Dispose()
{
if (Socket != null)
{
Socket.Dispose();
}
Socket?.Dispose();
}
}
}

View file

@ -11,7 +11,7 @@ namespace Microsoft.DotNet.Tools.Test
public IReportingChannel CreateTestRunnerChannel()
{
var testRunnerChannel = ReportingChannel.ListenOn(0);
var testRunnerChannel = TestRunnerReportingChannel.ListenOn(0);
TestRunnerChannelCreated?.Invoke(this, testRunnerChannel);
@ -20,7 +20,7 @@ namespace Microsoft.DotNet.Tools.Test
public IReportingChannel CreateAdapterChannel(int port)
{
return ReportingChannel.ListenOn(port);
return AdapterReportingChannel.ConnectTo(port);
}
}
}

View file

@ -0,0 +1,42 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Microsoft.DotNet.Tools.Test
{
public class TestRunnerReportingChannel : ReportingChannel
{
public static ReportingChannel ListenOn(int port)
{
// This fixes the mono incompatibility but ties it to ipv4 connections
var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
listenSocket.Listen(10);
return new TestRunnerReportingChannel(listenSocket, ((IPEndPoint)listenSocket.LocalEndPoint));
}
private TestRunnerReportingChannel(Socket connectSocket, IPEndPoint ipEndPoint)
: base(connectSocket, ipEndPoint.Port)
{
}
public override void Connect()
{
new Thread(() =>
{
using (ConnectSocket)
{
Socket = ConnectSocket.Accept();
StartReadingMessages();
}
})
{ IsBackground = true }.Start();
}
}
}

View file

@ -58,7 +58,8 @@
"Microsoft.Win32.Registry": {
"version": "4.0.0-rc2-23911",
"exclude": "Compile"
}
},
"System.Net.NameResolution": "4.0.0-rc2-23911"
},
"frameworks": {
"netstandardapp1.5": {

View file

@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.Tools.Test.Utilities
{
public class DotnetTestCommand : TestCommand
{
public DotnetTestCommand() : base("dotnet")
{
}
public override CommandResult Execute(string args = "")
{
args = $"test {args}";
return base.Execute(args);
}
}
}

View file

@ -0,0 +1,177 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.Extensions.Testing.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
namespace Microsoft.Dotnet.Tools.Test.Tests
{
public class Adapter : IDisposable
{
private readonly string _startMessage;
private BinaryWriter _writer;
private BinaryReader _reader;
private Socket _socket;
private Socket _listenSocket;
public IDictionary<string, List<Message>> Messages { get; }
public int Port { get; private set; }
public Adapter(string startMessage)
{
_startMessage = startMessage;
Messages = new Dictionary<string, List<Message>>();
_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endpoint = new IPEndPoint(IPAddress.Loopback, 0);
_listenSocket.Bind(endpoint);
Port = ((IPEndPoint)_listenSocket.LocalEndPoint).Port;
}
public void Listen()
{
var listenThread = new Thread(() =>
{
using (_listenSocket)
{
_listenSocket.Listen(1);
_socket = _listenSocket.Accept();
}
var stream = new NetworkStream(_socket);
_writer = new BinaryWriter(stream);
_reader = new BinaryReader(stream);
ReadMessages();
})
{
IsBackground = true
};
listenThread.Start();
}
public void Send(string messageType)
{
lock (_writer)
{
_writer.Write(JsonConvert.SerializeObject(new
{
MessageType = messageType,
PayLoad = JToken.FromObject(new
{
})
}));
}
}
private void ReadMessages()
{
while (true)
{
try
{
var message = GetMessage();
StoreMessage(message);
if (HandleMessage(message))
{
break;
}
}
catch (Exception)
{
throw;
}
}
}
private void StoreMessage(Message message)
{
if (!Messages.ContainsKey(message.MessageType))
{
Messages.Add(message.MessageType, new List<Message>());
}
Messages[message.MessageType].Add(message);
}
private bool HandleMessage(Message message)
{
if (message.MessageType == "TestSession.Connected")
{
Send(_startMessage);
}
if (message.MessageType == "TestExecution.TestRunnerProcessStartInfo")
{
StartTestRunner(message.Payload.ToObject<TestStartInfo>());
}
if (message.MessageType == "TestDiscovery.Completed")
{
Send("TestSession.Terminate");
return true;
}
if (message.MessageType == "TestExecution.Completed")
{
Send("TestSession.Terminate");
return true;
}
return false;
}
private static void StartTestRunner(TestStartInfo testPsiInfo)
{
var testPsi = new ProcessStartInfo(testPsiInfo.FileName, testPsiInfo.Arguments);
testPsi.RedirectStandardOutput = true;
testPsi.UseShellExecute = false;
var testProcess = new Process
{
StartInfo = testPsi
};
var testProcessThread = new Thread(() => { testProcess.Start(); })
{
IsBackground = true
};
testProcessThread.Start();
}
private Message GetMessage()
{
var rawMessage = _reader.ReadString();
Console.WriteLine("\nRECEIVING MESSAGE:");
Console.WriteLine($"{rawMessage}");
Console.WriteLine($"==============================\n");
var message = JsonConvert.DeserializeObject<Message>(rawMessage);
return message;
}
public void Dispose()
{
_socket?.Dispose();
}
private class TestStartInfo
{
public string FileName { get; set; }
public string Arguments { get; set; }
}
}
}

View file

@ -0,0 +1,72 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using System.IO;
using FluentAssertions;
using Xunit;
using Microsoft.Extensions.PlatformAbstractions;
using System.Linq;
namespace Microsoft.Dotnet.Tools.Test.Tests
{
public class GivenThatWeWantToUseDotnetTestE2EInDesignTime : TestBase
{
private string _projectFilePath;
private string _outputPath;
public GivenThatWeWantToUseDotnetTestE2EInDesignTime()
{
var testInstance = TestAssetsManager.CreateTestInstance("ProjectWithTests").WithLockFiles();
_projectFilePath = Path.Combine(testInstance.TestRoot, "project.json");
var contexts = ProjectContext.CreateContextForEachFramework(
_projectFilePath,
null,
PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers());
var runtime = contexts.FirstOrDefault(c => !string.IsNullOrEmpty(c.RuntimeIdentifier))?.RuntimeIdentifier;
_outputPath = Path.Combine(testInstance.TestRoot, "bin", "Debug", DefaultFramework, runtime);
var buildCommand = new BuildCommand(_projectFilePath);
var result = buildCommand.Execute();
result.Should().Pass();
}
[WindowsOnlyFact]
public void It_discovers_two_tests_for_the_ProjectWithTests()
{
using (var adapter = new Adapter("TestDiscovery.Start"))
{
adapter.Listen();
var testCommand = new DotnetTestCommand();
var result = testCommand.Execute($"{_projectFilePath} -o {_outputPath} --port {adapter.Port}");
result.Should().Pass();
adapter.Messages["TestSession.Connected"].Count.Should().Be(1);
adapter.Messages["TestDiscovery.TestFound"].Count.Should().Be(2);
adapter.Messages["TestDiscovery.Completed"].Count.Should().Be(1);
}
}
[Fact]
public void It_runs_two_tests_for_the_ProjectWithTests()
{
using (var adapter = new Adapter("TestExecution.GetTestRunnerProcessStartInfo"))
{
adapter.Listen();
var testCommand = new DotnetTestCommand();
var result = testCommand.Execute($"{_projectFilePath} -o {_outputPath} --port {adapter.Port}");
result.Should().Pass();
adapter.Messages["TestSession.Connected"].Count.Should().Be(1);
adapter.Messages["TestExecution.TestRunnerProcessStartInfo"].Count.Should().Be(1);
adapter.Messages["TestExecution.TestStarted"].Count.Should().Be(2);
adapter.Messages["TestExecution.TestResult"].Count.Should().Be(2);
adapter.Messages["TestExecution.Completed"].Count.Should().Be(1);
}
}
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>60c33d0a-a5d8-4ab0-9956-1f804654df05</ProjectGuid>
<RootNamespace>Microsoft.Dotnet.Tools.Test.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,33 @@
{
"version": "1.0.0-*",
"dependencies": {
"Newtonsoft.Json": "7.0.1",
"NETStandard.Library": "1.5.0-rc2-23911",
"Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" },
"Microsoft.DotNet.TestFramework": { "target": "project" },
"Microsoft.DotNet.ProjectModel": { "target": "project" },
"Microsoft.Extensions.Testing.Abstractions": { "target": "project" },
"System.Net.NameResolution": "4.0.0-rc2-23911",
"System.Net.Sockets": "4.1.0-rc2-23911",
"System.Runtime.Serialization.Primitives": "4.1.1-rc2-23911",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-dev-91790-12"
},
"frameworks": {
"netstandardapp1.5": {
"imports": [
"dnxcore50",
"portable-net45+win8"
]
}
},
"content": [
"../../TestAssets/TestProjects/ProjectWithTests/**/*",
"../../TestAssets/TestProjects/global.json"
],
"testRunner": "xunit"
}

View file

@ -141,7 +141,7 @@ namespace Microsoft.Dotnet.Tools.Test.Tests
_dotnetTestMock.Object,
_validMessage);
_testRunnerChannelMock.Verify(t => t.Accept(), Times.Once);
_testRunnerChannelMock.Verify(t => t.Connect(), Times.Once);
}
[Fact]

View file

@ -161,7 +161,7 @@ namespace Microsoft.Dotnet.Tools.Test.Tests
_dotnetTestMock.Object,
_validMessage);
_testRunnerChannelMock.Verify(t => t.Accept(), Times.Once);
_testRunnerChannelMock.Verify(t => t.Connect(), Times.Once);
}
[Fact]