From 4f349680bfb7ab0418a59c53373a18813a9839d7 Mon Sep 17 00:00:00 2001 From: Arun Mahapatra Date: Fri, 23 Sep 2016 21:56:21 +0530 Subject: [PATCH] Support for dotnet core deps files (#71) * WIP: Extract a test host manager for dotnet core. * WIP: Add tests for dotnet test host manager. * WIP: Minor test fixes. * Remove rc2 dependencies on system components. * Add nuget package for TestPlatform.TestHost. For .net core based test projects, test host will be a runtime dependency. * Add NoPackageAnalysis for TestPlatform.CLI package. * Move few more packages to dotnet dependencies with .netcoreapp1.0 support. * Use assembly.load for discovery of test plugins across platforms. Escape paths for runtimeconfig and deps. * Refactor ITestHostManager. * Introduce a Shared property in TestHostManager to indicate if a test host can be shared across test sources. * Minor fix to take parent directory for test engine location. --- dogfood/UnitTestProject/project.json | 8 +- scripts/build.ps1 | 13 +- .../DesignMode/DesignModeTestHostLauncher.cs | 16 +- .../DesignModeTestHostLauncherFactory.cs | 6 +- .../Discovery/DiscoveryRequest.cs | 79 ++-- .../Execution/TestRunRequest.cs | 120 +++--- .../TestPlatform.cs | 8 +- .../ExtensionFramework/TestPluginCache.cs | 1 + .../TestPluginDiscoverer.cs | 4 - .../ClientProtocol/IProxyDiscoveryManager.cs | 19 +- .../ClientProtocol/IProxyExecutionManager.cs | 24 +- .../ClientProtocol/IProxyOperationManager.cs | 26 -- .../Engine/ClientProtocol/ITestEngine.cs | 16 +- .../Engine/ClientProtocol/ITestHostManager.cs | 42 +- .../project.json | 2 +- .../Interfaces/ITestRequestHandler.cs | 2 +- .../TestRequestSender.cs | 2 +- .../project.json | 2 +- .../Helpers/FileHelper.cs | 2 +- .../Helpers/Interfaces/IFileHelper.cs | 2 +- .../Parallel/ParallelOperationManager.cs | 7 +- .../Parallel/ParallelProxyExecutionManager.cs | 104 ++--- .../Client/ProxyDiscoveryManager.cs | 76 ++-- .../Client/ProxyExecutionManager.cs | 74 ++-- ...ProxyExecutionManagerWithDataCollection.cs | 37 +- .../Client/ProxyOperationManager.cs | 94 ++--- .../Execution/ExecutionManager.cs | 60 ++- .../Friends.cs | 2 +- .../Helpers/Interfaces/IProcessHelper.cs | 8 +- .../Helpers/ProcessHelper.cs | 8 + .../Hosting/DefaultTestHostManager.cs | 93 ++--- .../Hosting/DotnetTestHostManager.cs | 178 +++++++++ .../TestEngine.cs | 27 +- .../project.json | 2 +- .../project.json | 2 +- .../Client/Interfaces/IDiscoveryRequest.cs | 35 +- .../Client/Interfaces/IRequest.cs | 12 +- .../Interfaces/ITestDiscoveryEventsHandler.cs | 3 + .../Client/Interfaces/ITestHostLauncher.cs | 3 +- .../Framework.cs | 4 + .../TestObject.cs | 5 +- .../project.json | 8 +- .../project.json | 7 +- src/TestPlatform.ObjectModel.nuspec | 20 + src/TestPlatform.TestHost.nuspec | 33 ++ .../TestPlatformHelpers/TestRequestManager.cs | 82 ++-- .../Discovery/DiscoveryRequestTests.cs | 60 +-- .../TestPlatformTests.cs | 77 ++-- .../Serialization/TestObjectConverterTests.cs | 13 + .../ParallelProxyExecutionManagerTests.cs | 4 +- .../Client/ProxyDiscoveryManagerTests.cs | 58 ++- .../Client/ProxyExecutionManagerTests.cs | 125 +++--- ...ExecutionManagerWithDataCollectionTests.cs | 12 +- .../Client/ProxyOperationManagerTests.cs | 105 +++-- .../Hosting/DefaultTestHostManagerTests.cs | 362 ++---------------- .../Hosting/DotnetTestHostManagerTests.cs | 285 ++++++++++++++ .../TestEngineTests.cs | 69 +++- .../FrameworkTests.cs | 16 + .../InferRunSettingsHelperTests.cs | 6 +- .../Properties/AssemblyInfo.cs | 36 ++ .../CompatTestExtension/UnitTest1.cs | 47 +++ .../Properties/AssemblyInfo.cs | 36 ++ .../TestAssets/CompatTestProject/UnitTest1.cs | 40 ++ .../Utilities/RunSettingsUtilitiesTests.cs | 6 +- 64 files changed, 1685 insertions(+), 1050 deletions(-) delete mode 100644 src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyOperationManager.cs create mode 100644 src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DotnetTestHostManager.cs create mode 100644 src/TestPlatform.TestHost.nuspec create mode 100644 test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DotnetTestHostManagerTests.cs create mode 100644 test/TestAssets/CompatTestExtension/Properties/AssemblyInfo.cs create mode 100644 test/TestAssets/CompatTestExtension/UnitTest1.cs create mode 100644 test/TestAssets/CompatTestProject/Properties/AssemblyInfo.cs create mode 100644 test/TestAssets/CompatTestProject/UnitTest1.cs diff --git a/dogfood/UnitTestProject/project.json b/dogfood/UnitTestProject/project.json index 4e2d3f5085..9ad20011b9 100644 --- a/dogfood/UnitTestProject/project.json +++ b/dogfood/UnitTestProject/project.json @@ -11,6 +11,7 @@ "dnxcore50", "portable-net45+win8" ], + "dependencies": { "Microsoft.NETCore.App": { "type": "platform", @@ -18,10 +19,11 @@ } } }, + "net46": { - "frameworkAssemblies": { - "System.Runtime": "" - } + "frameworkAssemblies": { + "System.Runtime": "" + } } } } diff --git a/scripts/build.ps1 b/scripts/build.ps1 index ffb08890d3..47f6173d25 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -246,7 +246,9 @@ function Create-NugetPackages $tpSrcDir = Join-Path $env:TP_ROOT_DIR "src" # Copy over the nuspecs to the staging directory - $nuspecFiles = @("TestPlatform.TranslationLayer.nuspec", "TestPlatform.ObjectModel.nuspec", "TestPlatform.nuspec", "TestPlatform.CLI.nuspec") + $nuspecFiles = @("TestPlatform.TranslationLayer.nuspec", "TestPlatform.ObjectModel.nuspec", "TestPlatform.TestHost.nuspec", "TestPlatform.nuspec", "TestPlatform.CLI.nuspec") + # Nuget pack analysis emits warnings if binaries are packaged as content. It is intentional for the below packages. + $skipAnalysis = @("TestPlatform.CLI.nuspec") foreach ($file in $nuspecFiles) { Copy-Item $tpSrcDir\$file $stagingDir -Force } @@ -255,8 +257,13 @@ function Create-NugetPackages $nugetExe = Join-Path $env:TP_PACKAGES_DIR -ChildPath "Nuget.CommandLine" | Join-Path -ChildPath $env:NUGET_EXE_Version | Join-Path -ChildPath "tools\NuGet.exe" foreach ($file in $nuspecFiles) { - Write-Verbose "$nugetExe pack $stagingDir\$file -OutputDirectory $stagingDir" - & $nugetExe pack $stagingDir\$file -OutputDirectory $stagingDir + $additionalArgs = "" + if ($skipAnalysis -contains $file) { + $additionalArgs = "-NoPackageAnalysis" + } + + Write-Verbose "$nugetExe pack $stagingDir\$file -OutputDirectory $stagingDir $additionalArgs" + & $nugetExe pack $stagingDir\$file -OutputDirectory $stagingDir $additionalArgs } Write-Log "Create-NugetPackages: Complete. {$(Get-ElapsedTime($timer))}" diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs index 3b1b50ad9b..22018573c9 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs @@ -2,27 +2,29 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode { - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; /// /// DesignMode TestHost Launcher for hosting of test process /// internal class DesignModeTestHostLauncher : ITestHostLauncher { - private IDesignModeClient designModeClient; + private readonly IDesignModeClient designModeClient; + /// + /// Initializes a new instance of the class. + /// + /// Design mode client instance. public DesignModeTestHostLauncher(IDesignModeClient designModeClient) { this.designModeClient = designModeClient; } + /// public virtual bool IsDebug => false; + /// public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo) { return this.designModeClient.LaunchCustomHost(defaultTestHostStartInfo); @@ -34,10 +36,12 @@ public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo) /// internal class DesignModeDebugTestHostLauncher : DesignModeTestHostLauncher { + /// public DesignModeDebugTestHostLauncher(IDesignModeClient designModeClient) : base(designModeClient) { } + /// public override bool IsDebug => true; } } diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs index 84242af75e..8e26204ae9 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs @@ -4,10 +4,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode { using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; /// /// Factory for providing the design mode test host launchers @@ -21,7 +17,7 @@ public static class DesignModeTestHostLauncherFactory public static ITestHostLauncher GetCustomHostLauncherForTestRun(IDesignModeClient designModeClient, TestRunRequestPayload testRunRequestPayload) { ITestHostLauncher testHostLauncher = null; - if(!testRunRequestPayload.DebuggingEnabled) + if (!testRunRequestPayload.DebuggingEnabled) { testHostLauncher = defaultLauncher = defaultLauncher ?? new DesignModeTestHostLauncher(designModeClient); } diff --git a/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs b/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs index ccb215991d..bf13d65791 100644 --- a/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs +++ b/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs @@ -9,11 +9,10 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.Discovery using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.Utilities; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; - /// /// The discovery request. /// @@ -22,8 +21,8 @@ public sealed class DiscoveryRequest : IDiscoveryRequest, ITestDiscoveryEventsHa /// /// Initializes a new instance of the class. /// - /// The criteria. - /// The discovery manager. + /// Discovery criterion. + /// Discovery manager instance. internal DiscoveryRequest(DiscoveryCriteria criteria, IProxyDiscoveryManager discoveryManager) { this.DiscoveryCriteria = criteria; @@ -42,7 +41,7 @@ void IDiscoveryRequest.DiscoverAsync() lock (this.syncObject) { - if (this.bDisposed) + if (this.disposed) { throw new ObjectDisposedException("DiscoveryRequest"); } @@ -80,7 +79,7 @@ void IDiscoveryRequest.Abort() lock (this.syncObject) { - if (this.bDisposed) + if (this.disposed) { throw new ObjectDisposedException("DiscoveryRequest"); } @@ -95,6 +94,7 @@ void IDiscoveryRequest.Abort() { EqtTrace.Info("DiscoveryRequest.Abort: No operation to abort."); } + return; } } @@ -116,7 +116,7 @@ bool IRequest.WaitForCompletion(int timeout) EqtTrace.Verbose("DiscoveryRequest.WaitForCompletion: Waiting with timeout {0}.", timeout); } - if (this.bDisposed) + if (this.disposed) { throw new ObjectDisposedException("DiscoveryRequest"); } @@ -169,7 +169,7 @@ public DiscoveryCriteria DiscoveryCriteria /// internal bool DiscoveryInProgress { - get { return discoveryInProgress; } + get { return this.discoveryInProgress; } } /// @@ -179,47 +179,48 @@ internal bool DiscoveryInProgress #region ITestDiscoveryEventsHandler Methods - /// - /// Dispatch DiscoveryComplete event to listeners. - /// + /// public void HandleDiscoveryComplete(long totalTests, IEnumerable lastChunk, bool isAborted) { if (EqtTrace.IsVerboseEnabled) { EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete: Starting. Aborted:{0}, TotalTests:{1}", isAborted, totalTests); } + lock (this.syncObject) { - if (this.bDisposed) + if (this.disposed) { if (EqtTrace.IsWarningEnabled) { EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Ignoring as the object is disposed."); } + return; } - //If discovery event is already raised, ignore current one. + + // If discovery event is already raised, ignore current one. if (this.discoveryCompleted.WaitOne(0)) { if (EqtTrace.IsVerboseEnabled) { EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete:Ignoring duplicate DiscoveryComplete. Aborted:{0}, TotalTests:{1}", isAborted, totalTests); } + return; } try { // Raise onDiscoveredTests event if there are some tests in the last chunk. - // // (We dont want to send the tests in the discovery complete event so that programming on top of - // RS client is easier i.e. user does not have to listen on discovery complete event.) + // RS client is easier i.e. user does not have to listen on discovery complete event.) if (lastChunk != null && lastChunk.Count() > 0) { - OnDiscoveredTests.SafeInvoke(this, new DiscoveredTestsEventArgs(lastChunk), "DiscoveryRequest.DiscoveryComplete"); + this.OnDiscoveredTests.SafeInvoke(this, new DiscoveredTestsEventArgs(lastChunk), "DiscoveryRequest.DiscoveryComplete"); } - OnDiscoveryComplete.SafeInvoke(this, new DiscoveryCompleteEventArgs(totalTests, isAborted), "DiscoveryRequest.DiscoveryComplete"); + this.OnDiscoveryComplete.SafeInvoke(this, new DiscoveryCompleteEventArgs(totalTests, isAborted), "DiscoveryRequest.DiscoveryComplete"); } finally { @@ -241,6 +242,7 @@ public void HandleDiscoveryComplete(long totalTests, IEnumerable lastC EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Discovery complete event was null."); } } + this.discoveryInProgress = false; } } @@ -251,11 +253,7 @@ public void HandleDiscoveryComplete(long totalTests, IEnumerable lastC } } - /// - /// Dispatch DiscoveredTest event to listeners. - /// - /// Instance of IDiscoveryManager that discovered tests. - /// Discovered test cases. + /// public void HandleDiscoveredTests(IEnumerable discoveredTestCases) { if (EqtTrace.IsVerboseEnabled) @@ -265,17 +263,19 @@ public void HandleDiscoveredTests(IEnumerable discoveredTestCases) lock (this.syncObject) { - if (this.bDisposed) + if (this.disposed) { if (EqtTrace.IsWarningEnabled) { EqtTrace.Warning("DiscoveryRequest.SendDiscoveredTests: Ignoring as the object is disposed."); } + return; } - OnDiscoveredTests.SafeInvoke(this, new DiscoveredTestsEventArgs(discoveredTestCases), "DiscoveryRequest.OnDiscoveredTests"); + this.OnDiscoveredTests.SafeInvoke(this, new DiscoveredTestsEventArgs(discoveredTestCases), "DiscoveryRequest.OnDiscoveredTests"); } + if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("DiscoveryRequest.SendDiscoveredTests: Completed."); @@ -285,8 +285,7 @@ public void HandleDiscoveredTests(IEnumerable discoveredTestCases) /// /// Dispatch TestRunMessage event to listeners. /// - /// Instnace of IDiscoveryManager that discovered tests - /// Ouput level of the message being sent. + /// Output level of the message being sent. /// Actual contents of the message public void HandleLogMessage(TestMessageLevel level, string message) { @@ -297,16 +296,17 @@ public void HandleLogMessage(TestMessageLevel level, string message) lock (this.syncObject) { - if (this.bDisposed) + if (this.disposed) { if (EqtTrace.IsWarningEnabled) { EqtTrace.Warning("DiscoveryRequest.SendDiscoveryMessage: Ignoring as the object is disposed."); } + return; } - OnDiscoveryMessage.SafeInvoke(this, new TestRunMessageEventArgs(level, message), "DiscoveryRequest.OnTestMessageRecieved"); + this.OnDiscoveryMessage.SafeInvoke(this, new TestRunMessageEventArgs(level, message), "DiscoveryRequest.OnTestMessageRecieved"); } if (EqtTrace.IsInfoEnabled) @@ -318,7 +318,7 @@ public void HandleLogMessage(TestMessageLevel level, string message) /// /// Handle Raw message directly from the host /// - /// + /// Raw message. public void HandleRawMessage(string rawMessage) { this.OnRawMessageReceived?.Invoke(this, rawMessage); @@ -328,20 +328,17 @@ public void HandleRawMessage(string rawMessage) #region IDisposable implementation - // Summary: - // Performs application-defined tasks associated with freeing, releasing, or - // resetting unmanaged resources. + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// public void Dispose() { - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } - /// - /// Dispose the discovery request. - /// - /// private void Dispose(bool disposing) { if (EqtTrace.IsVerboseEnabled) @@ -351,7 +348,7 @@ private void Dispose(bool disposing) lock (this.syncObject) { - if (!this.bDisposed) + if (!this.disposed) { if (disposing) { @@ -363,7 +360,7 @@ private void Dispose(bool disposing) // Indicate that object has been disposed this.discoveryCompleted = null; - this.bDisposed = true; + this.disposed = true; } } @@ -380,7 +377,7 @@ private void Dispose(bool disposing) /// /// If this request has been disposed. /// - private bool bDisposed = false; + private bool disposed = false; /// /// It get set when current discovery request is completed. @@ -390,7 +387,7 @@ private void Dispose(bool disposing) /// /// Sync object for various operations /// - private Object syncObject = new Object(); + private object syncObject = new Object(); /// /// Whether or not the test discovery is in progress. diff --git a/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs b/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs index 816ea65db3..4e3e92b0ef 100644 --- a/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs +++ b/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs @@ -2,17 +2,18 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.Execution { + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.Utilities; + using Resources = Microsoft.VisualStudio.TestPlatform.Client.Resources; - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Threading; public class TestRunRequest : ITestRunRequest, ITestRunEventsHandler { @@ -33,13 +34,14 @@ internal TestRunRequest(TestRunCriteria testRunCriteria, IProxyExecutionManager /// /// Execute the test run asynchronously /// + /// The process id of test host. public int ExecuteAsync() { EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Starting."); - lock (syncObject) + lock (this.syncObject) { - if (bDisposed) + if (this.disposed) { throw new ObjectDisposedException("testRunRequest"); } @@ -49,7 +51,7 @@ public int ExecuteAsync() throw new InvalidOperationException(Resources.InvalidStateForExecution); } - EqtTrace.Info("TestRunRequest.ExecuteAsync: Starting run with settings:{0}", testRunCriteria); + EqtTrace.Info("TestRunRequest.ExecuteAsync: Starting run with settings:{0}", this.testRunCriteria); // Waiting for warm up to be over. EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Wait for the first run request is over."); @@ -59,14 +61,15 @@ public int ExecuteAsync() // Reset the run completion event // (This needs to be done before queuing the test run because if the test run finishes fast then runCompletion event can // remain in non-signaled state even though run is actually complete. - runCompletionEvent.Reset(); + this.runCompletionEvent.Reset(); try { - runRequestTimeTracker = new Stopwatch(); + this.runRequestTimeTracker = new Stopwatch(); + // Start the stop watch for calculating the test run time taken overall - runRequestTimeTracker.Start(); - int processId = this.ExecutionManager.StartTestRun(testRunCriteria, this); + this.runRequestTimeTracker.Start(); + int processId = this.ExecutionManager.StartTestRun(this.testRunCriteria, this); EqtTrace.Info("TestRunRequest.ExecuteAsync: Started."); return processId; @@ -86,7 +89,7 @@ public bool WaitForCompletion(int timeout) { EqtTrace.Verbose("TestRunRequest.WaitForCompletion: Waiting with timeout {0}.", timeout); - if (bDisposed) + if (this.disposed) { throw new ObjectDisposedException("testRunRequest"); } @@ -94,8 +97,9 @@ public bool WaitForCompletion(int timeout) if (this.State != TestRunState.InProgress && !(this.State == TestRunState.Completed || this.State == TestRunState.Canceled - || this.State == TestRunState.Aborted)) // If run is already terminated, then we should not throw an exception. + || this.State == TestRunState.Aborted)) { + // If run is already terminated, then we should not throw an exception. throw new InvalidOperationException(Resources.WaitForCompletionOperationIsNotAllowedWhenNoTestRunIsActive); } @@ -103,9 +107,9 @@ public bool WaitForCompletion(int timeout) // (the runCompletionEvent cannot be raised unless that lock is released) // Wait for run completion (In case m_runCompletionEvent is closed, then waitOne will throw nice error) - if (runCompletionEvent != null) + if (this.runCompletionEvent != null) { - return runCompletionEvent.WaitOne(timeout); + return this.runCompletionEvent.WaitOne(timeout); } return true; @@ -118,9 +122,9 @@ public void CancelAsync() { EqtTrace.Verbose("TestRunRequest.CancelAsync: Canceling."); - lock (syncObject) + lock (this.syncObject) { - if (bDisposed) + if (this.disposed) { throw new ObjectDisposedException("testRunRequest"); } @@ -146,9 +150,9 @@ public void Abort() { EqtTrace.Verbose("TestRunRequest.Abort: Aborting."); - lock (syncObject) + lock (this.syncObject) { - if (bDisposed) + if (this.disposed) { throw new ObjectDisposedException("testRunRequest"); } @@ -166,12 +170,13 @@ public void Abort() EqtTrace.Info("TestRunRequest.Abort: Aborted."); } + /// /// Specifies the test run criteria /// public ITestRunConfiguration TestRunConfiguration { - get { return testRunCriteria; } + get { return this.testRunCriteria; } } /// @@ -189,11 +194,13 @@ public ITestRunConfiguration TestRunConfiguration /// public event EventHandler TestRunMessage; + /// /// Raised when the test run completes. /// public event EventHandler OnRunCompletion; + /// /// Raised when data collection message is received. /// @@ -221,11 +228,11 @@ internal IProxyExecutionManager ExecutionManager #region IDisposable implementation // Summary: - // Performs application-defined tasks associated with freeing, releasing, or - // resetting unmanaged resources. + // Performs application-defined tasks associated with freeing, releasing, or + // resetting unmanaged resources. public void Dispose() { - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } @@ -234,7 +241,7 @@ public void Dispose() public TestRunCriteria TestRunCriteria { - get { return testRunCriteria; } + get { return this.testRunCriteria; } } /// @@ -246,20 +253,22 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, Test { throw new ArgumentNullException(nameof(runCompleteArgs)); } + bool isAborted = runCompleteArgs.IsAborted; bool isCanceled = runCompleteArgs.IsCanceled; EqtTrace.Verbose("TestRunRequest:TestRunComplete: Starting. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled); - lock (syncObject) + lock (this.syncObject) { // If this object is disposed, dont do anything - if (bDisposed) + if (this.disposed) { EqtTrace.Warning("TestRunRequest.TestRunComplete: Ignoring as the object is disposed."); return; } - if (runCompletionEvent.WaitOne(0)) + + if (this.runCompletionEvent.WaitOne(0)) { EqtTrace.Info("TestRunRequest:TestRunComplete:Ignoring duplicate event. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled); return; @@ -267,24 +276,27 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, Test try { - runRequestTimeTracker.Stop(); + this.runRequestTimeTracker.Stop(); if (lastChunkArgs != null) { // Raised the changed event also - OnRunStatsChange.SafeInvoke(this, lastChunkArgs, "TestRun.RunStatsChanged"); + this.OnRunStatsChange.SafeInvoke(this, lastChunkArgs, "TestRun.RunStatsChanged"); } - TestRunCompleteEventArgs runCompletedEvent = new TestRunCompleteEventArgs(runCompleteArgs.TestRunStatistics, - runCompleteArgs.IsCanceled, - runCompleteArgs.IsAborted, - runCompleteArgs.Error, - null, - runRequestTimeTracker.Elapsed); + TestRunCompleteEventArgs runCompletedEvent = + new TestRunCompleteEventArgs( + runCompleteArgs.TestRunStatistics, + runCompleteArgs.IsCanceled, + runCompleteArgs.IsAborted, + runCompleteArgs.Error, + null, + this.runRequestTimeTracker.Elapsed); + // Ignore the time sent (runCompleteArgs.ElapsedTimeInRunningTests) // by either engines - as both calculate at different points // If we use them, it would be an apples and oranges comparison i.e, between TAEF and Rocksteady - OnRunCompletion.SafeInvoke(this, runCompletedEvent, "TestRun.TestRunComplete"); + this.OnRunCompletion.SafeInvoke(this, runCompletedEvent, "TestRun.TestRunComplete"); } finally { @@ -302,10 +314,10 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, Test } // Notify the waiting handle that run is complete - runCompletionEvent.Set(); + this.runCompletionEvent.Set(); // Disposing off the resources held by the execution manager so that the test host process can shut down. - this.ExecutionManager?.Dispose(); + this.ExecutionManager?.Close(); } EqtTrace.Info("TestRunRequest:TestRunComplete: Completed."); @@ -333,10 +345,10 @@ public virtual void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChan } } - lock (syncObject) + lock (this.syncObject) { // If this object is disposed, dont do anything - if (bDisposed) + if (this.disposed) { EqtTrace.Warning("TestRunRequest.SendTestRunStatsChange: Ignoring as the object is disposed."); return; @@ -344,13 +356,14 @@ public virtual void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChan // TODO: Invoke this event in a separate thread. // For now, I am setting the ConcurrencyMode on the callback attribute to Multiple - OnRunStatsChange.SafeInvoke(this, testRunChangedArgs, "TestRun.RunStatsChanged"); + this.OnRunStatsChange.SafeInvoke(this, testRunChangedArgs, "TestRun.RunStatsChanged"); } EqtTrace.Info("TestRunRequest:SendTestRunStatsChange: Completed."); } } + /// /// Invoked when log messages are received /// @@ -358,15 +371,16 @@ public void HandleLogMessage(TestMessageLevel level, string message) { EqtTrace.Verbose("TestRunRequest:SendTestRunMessage: Starting."); - lock (syncObject) + lock (this.syncObject) { // If this object is disposed, dont do anything - if (bDisposed) + if (this.disposed) { EqtTrace.Warning("TestRunRequest.SendTestRunMessage: Ignoring as the object is disposed."); return; } - TestRunMessage.SafeInvoke(this, new TestRunMessageEventArgs(level, message), "TestRun.LogMessages"); + + this.TestRunMessage.SafeInvoke(this, new TestRunMessageEventArgs(level, message), "TestRun.LogMessages"); } EqtTrace.Info("TestRunRequest:SendTestRunMessage: Completed."); @@ -407,20 +421,21 @@ protected virtual void Dispose(bool disposing) { EqtTrace.Verbose("TestRunRequest.Dispose: Starting."); - lock (syncObject) + lock (this.syncObject) { - if (!bDisposed) + if (!this.disposed) { if (disposing) { - runCompletionEvent?.Dispose(); + this.runCompletionEvent?.Dispose(); } // Indicate that object has been disposed - runCompletionEvent = null; - bDisposed = true; + this.runCompletionEvent = null; + this.disposed = true; } } + EqtTrace.Info("TestRunRequest.Dispose: Completed."); } @@ -432,18 +447,19 @@ protected virtual void Dispose(bool disposing) /// /// Specifies whether the run is disposed or not /// - private bool bDisposed; + private bool disposed; /// /// Sync object for various operations /// - private Object syncObject = new Object(); + private object syncObject = new Object(); /// /// The run completion event which will be signalled on completion of test run. /// private ManualResetEvent runCompletionEvent = new ManualResetEvent(true); + /// /// Tracks the time taken by each run request /// diff --git a/src/Microsoft.TestPlatform.Client/TestPlatform.cs b/src/Microsoft.TestPlatform.Client/TestPlatform.cs index c3e3184be1..097dd516d3 100644 --- a/src/Microsoft.TestPlatform.Client/TestPlatform.cs +++ b/src/Microsoft.TestPlatform.Client/TestPlatform.cs @@ -56,8 +56,8 @@ public IDiscoveryRequest CreateDiscoveryRequest(DiscoveryCriteria discoveryCrite var runconfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(discoveryCriteria.RunSettings); var testHostManager = this.TestEngine.GetDefaultTestHostManager(runconfiguration.TargetPlatform, runconfiguration.TargetFrameworkVersion); - var discoveryManager = this.TestEngine.GetDiscoveryManager(); - discoveryManager.Initialize(testHostManager); + var discoveryManager = this.TestEngine.GetDiscoveryManager(testHostManager); + discoveryManager.Initialize(); return new DiscoveryRequest(discoveryCriteria, discoveryManager); } @@ -83,8 +83,8 @@ public ITestRunRequest CreateTestRunRequest(TestRunCriteria testRunCriteria) testHostManager.SetCustomLauncher(testRunCriteria.TestHostLauncher); } - var executionManager = this.TestEngine.GetExecutionManager(testRunCriteria); - executionManager.Initialize(testHostManager); + var executionManager = this.TestEngine.GetExecutionManager(testHostManager, testRunCriteria); + executionManager.Initialize(); return new TestRunRequest(testRunCriteria, executionManager); } diff --git a/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginCache.cs b/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginCache.cs index c4b6170804..862305b15b 100644 --- a/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginCache.cs +++ b/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginCache.cs @@ -89,6 +89,7 @@ public static TestPluginCache Instance { return instance ?? (instance = new TestPluginCache()); } + internal set { instance = value; diff --git a/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginDiscoverer.cs b/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginDiscoverer.cs index efb43fc46a..68d5eb531f 100644 --- a/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginDiscoverer.cs +++ b/src/Microsoft.TestPlatform.Common/ExtensionFramework/TestPluginDiscoverer.cs @@ -160,11 +160,7 @@ private void GetTestExtensionsFromFiles( try { var assemblyName = Path.GetFileNameWithoutExtension(file); -#if NET46 assembly = Assembly.Load(new AssemblyName(assemblyName)); -#else - assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file); -#endif // Check whether this assembly is known or not. //if (loadOnlyWellKnownExtensions && assembly != null) diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyDiscoveryManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyDiscoveryManager.cs index ea4a27bf00..9f7af1b57b 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyDiscoveryManager.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyDiscoveryManager.cs @@ -2,19 +2,34 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine { - using System; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; /// /// Orchestrates discovery operations for the engine communicating with the client. /// - public interface IProxyDiscoveryManager : IProxyOperationManager + public interface IProxyDiscoveryManager { + /// + /// Initializes test discovery. Create the test host, setup channel and initialize extensions. + /// + void Initialize(); + /// /// Discovers tests /// /// Settings, parameters for the discovery request /// EventHandler for handling discovery events from Engine void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEventsHandler eventHandler); + + /// + /// Aborts the test operation. + /// + void Abort(); + + /// + /// Closes the current test operation. + /// Send a EndSession message to close the test host and channel gracefully. + /// + void Close(); } } diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyExecutionManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyExecutionManager.cs index fb6fdcff3b..0a3c8fc93c 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyExecutionManager.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyExecutionManager.cs @@ -7,19 +7,35 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine /// /// Orchestrates test execution related functionality for the engine communicating with the client. /// - public interface IProxyExecutionManager : IProxyOperationManager + public interface IProxyExecutionManager { /// - /// Starts the test run + /// Initializes test execution. Create the test host, setup channel and initialize extensions. + /// + void Initialize(); + + /// + /// Starts the test run. /// /// The settings/options for the test run. /// EventHandler for handling execution events from Engine. + /// The process id of the runner executing tests. int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler eventHandler); /// - /// Cancels the test run - /// TODO: what's the difference between abort and cancel + /// Cancels the test run. On the test host, this will send a message to adapters. /// void Cancel(); + + /// + /// Aborts the test operation. This will forcefully terminate the test host. + /// + void Abort(); + + /// + /// Closes the current test operation by sending a end session message. + /// Terminates the test host. + /// + void Close(); } } \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyOperationManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyOperationManager.cs deleted file mode 100644 index fe0f5f6800..0000000000 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyOperationManager.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine -{ - using System; - - /// - /// Base interface for discovery and execution operations. - /// - public interface IProxyOperationManager : IDisposable - { - /// - /// Ensure that the engine is ready for test operations. - /// Usually includes starting up the test host process. - /// - /// - /// Manager for the test host process - /// - void Initialize(ITestHostManager testHostManager); - - /// - /// Aborts the test operation. - /// - void Abort(); - } -} diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs index 23df385116..c9d083e0e7 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs @@ -1,4 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. + namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine { using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; @@ -12,26 +13,31 @@ public interface ITestEngine /// /// Fetches the DiscoveryManager for this engine. This manager would provide all functionality required for discovery. /// + /// Test host manager for the current test discovery. /// ITestDiscoveryManager object that can do discovery - IProxyDiscoveryManager GetDiscoveryManager(); + IProxyDiscoveryManager GetDiscoveryManager(ITestHostManager testHostManager); /// /// Fetches the ExecutionManager for this engine. This manager would provide all functionality required for execution. /// + /// Test host manager for current test run. /// TestRunCriteria of the current test run /// ITestExecutionManager object that can do execution - IProxyExecutionManager GetExecutionManager(TestRunCriteria testRunCriteria); + IProxyExecutionManager GetExecutionManager(ITestHostManager testHostManager, TestRunCriteria testRunCriteria); /// - /// Fetches the extension manager for this engine. This manager would provide extensibility features that this engine supports. + /// Fetches the extension manager for this engine. This manager would provide extensibility + /// features that this engine supports. /// /// ITestExtensionManager object that helps with extensibility ITestExtensionManager GetExtensionManager(); /// - /// Fetches the Test Host manager for this engine. This manager would provide extensibility features that this engine supports. + /// Fetches the Test Host manager for this engine. This manager would provide extensibility + /// features that this engine supports. /// - /// Architecture of the test run + /// Architecture of the test run. + /// Runtime framework for this run. /// Launcher for the test host process ITestHostManager GetDefaultTestHostManager(Architecture architecture, Framework framework); } diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestHostManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestHostManager.cs index a55028f847..9e104474cf 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestHostManager.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestHostManager.cs @@ -2,16 +2,23 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine { - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using System; using System.Collections.Generic; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + /// /// Interface for HostManager which manages test host processes for test engine. /// public interface ITestHostManager { + /// + /// Gets a value indicating whether the test host is specific to a test source. If yes, each test source + /// is launched in a separate host process. + /// + bool Shared { get; } + /// /// Sets a custom launcher /// @@ -21,18 +28,18 @@ public interface ITestHostManager /// /// Launches the test host for discovery/execution. /// - /// Environment variables for the process. - /// The command line arguments to pass to the process. + /// Start parameters for the test host. /// ProcessId of launched Process. 0 means not launched. - int LaunchTestHost(IDictionary environmentVariables, IList commandLineArguments); + int LaunchTestHost(TestProcessStartInfo testHostStartInfo); /// - /// Gives the ProcessStartInfo for the test host process + /// Gets the start parameters for the test host. /// - /// - /// - /// ProcessStartInfo of the test host - TestProcessStartInfo GetTestHostProcessStartInfo(IDictionary environmentVariables, IList commandLineArguments); + /// Test source paths. + /// Set of environment variables for the test host process. + /// Set of connection parameters for the test host process to communicate with test runner. + /// ProcessStartInfo of the test host. + TestProcessStartInfo GetTestHostProcessStartInfo(IEnumerable sources, IDictionary environmentVariables, TestRunnerConnectionInfo connectionInfo); /// /// Register for the exit event. @@ -45,4 +52,19 @@ public interface ITestHostManager /// void DeregisterForExitNotification(); } + + /// + /// Connection information for a test host to communicate with test runner. + /// + public struct TestRunnerConnectionInfo + { + /// + /// Gets or sets the port opened by test runner for host communication. + /// + public int Port + { + get; + set; + } + } } diff --git a/src/Microsoft.TestPlatform.Common/project.json b/src/Microsoft.TestPlatform.Common/project.json index be88cd7f09..cb924495c3 100644 --- a/src/Microsoft.TestPlatform.Common/project.json +++ b/src/Microsoft.TestPlatform.Common/project.json @@ -19,7 +19,7 @@ ], "dependencies": { "NETStandard.Library": "1.6.0", - "System.Runtime.Loader": "4.0.0-rc2-24027" + "System.Runtime.Loader": "4.0.0" } }, "net46": { diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestHandler.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestHandler.cs index a31f852d75..06d39fb146 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestHandler.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestHandler.cs @@ -5,7 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces using System.Collections.Generic; using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.TesthostProtocol; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs index 81136946da..563ac0b7a5 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs @@ -136,6 +136,7 @@ public void EndSession() EqtTrace.Error("Connection has been broken: not sending SessionEnd message"); return; } + this.communicationManager.SendMessage(MessageType.SessionEnd); } @@ -172,7 +173,6 @@ private void ListenAndReportTestResults(ITestRunEventsHandler testRunEventsHandl // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification. while (!isTestRunComplete) { - try { var rawMessage = this.communicationManager.ReceiveRawMessage(); diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/project.json b/src/Microsoft.TestPlatform.CommunicationUtilities/project.json index 9a50f03550..1e55131e18 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/project.json +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/project.json @@ -21,7 +21,7 @@ ], "dependencies": { "NETStandard.Library": "1.6.0", - "System.Runtime.Serialization.Primitives": "4.0.10-*" + "System.Runtime.Serialization.Primitives": "4.1.1" } }, "net46": {} diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs index b09d635935..3e35d01150 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers /// /// The file helper. /// - internal class FileHelper : IFileHelper + public class FileHelper : IFileHelper { /// /// Exists utility to check if file exists diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs index 1b7fa18f36..406b242cdb 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs @@ -5,7 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces /// /// The FileHelper interface. /// - internal interface IFileHelper + public interface IFileHelper { /// /// Exists utility to check if file exists diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelOperationManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelOperationManager.cs index de0cc91b70..6a40fca471 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelOperationManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelOperationManager.cs @@ -2,13 +2,13 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client { - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using System; using System.Collections.Generic; - using System.Linq; using System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + /// /// Abstract class having common parallel manager implementation /// @@ -35,6 +35,7 @@ internal abstract class ParallelOperationManager : IParallelOperationManager, protected ParallelOperationManager(Func createNewManager, int parallelLevel) { this.CreateNewConcurrentManager = createNewManager; + // Update Parallel Level UpdateParallelLevel(parallelLevel); } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs index 73c806d89c..a49dad19e7 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyExecutionManager.cs @@ -1,15 +1,15 @@ // Copyright (c) Microsoft. All rights reserved. - namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel { using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; - using System.Collections.Generic; - using System.Threading.Tasks; - using System.Collections; - using System.Linq; /// /// ParallelProxyExecutionManager that manages parallel execution @@ -56,9 +56,9 @@ public ParallelProxyExecutionManager(Func actualProxyMan #region IProxyExecutionManager - public void Initialize(ITestHostManager testHostManager) + public void Initialize() { - DoActionOnAllManagers((proxyManager) => proxyManager.Initialize(testHostManager), doActionsInParallel: true); + this.DoActionOnAllManagers((proxyManager) => proxyManager.Initialize(), doActionsInParallel: true); } public int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler eventHandler) @@ -66,7 +66,7 @@ public int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler e this.hasSpecificTestsRun = testRunCriteria.HasSpecificTests; this.actualTestRunCriteria = testRunCriteria; - if (hasSpecificTestsRun) + if (this.hasSpecificTestsRun) { var testCasesBySource = new Dictionary>(); foreach (var test in testRunCriteria.Tests) @@ -75,6 +75,7 @@ public int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler e { testCasesBySource.Add(test.Source, new List()); } + testCasesBySource[test.Source].Add(test); } @@ -91,17 +92,22 @@ public int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler e this.sourceEnumerator = testRunCriteria.Sources.GetEnumerator(); } - return StartTestRunPrivate(eventHandler); + return this.StartTestRunPrivate(eventHandler); } public void Abort() { - DoActionOnAllManagers((proxyManager) => proxyManager.Abort(), doActionsInParallel: true); + this.DoActionOnAllManagers((proxyManager) => proxyManager.Abort(), doActionsInParallel: true); } public void Cancel() { - DoActionOnAllManagers((proxyManager) => proxyManager.Cancel(), doActionsInParallel: true); + this.DoActionOnAllManagers((proxyManager) => proxyManager.Cancel(), doActionsInParallel: true); + } + + public void Close() + { + this.DoActionOnAllManagers(proxyManager => proxyManager.Close(), doActionsInParallel: true); } #endregion @@ -129,28 +135,28 @@ public bool HandlePartialRunComplete( // In Case of Cancel or Abort, no need to trigger run for rest of the data // If there are no more sources/testcases, a parallel executor is truly done with execution - if (testRunCompleteArgs.IsAborted || testRunCompleteArgs.IsCanceled || !StartTestRunOnConcurrentManager(proxyExecutionManager)) + if (testRunCompleteArgs.IsAborted || testRunCompleteArgs.IsCanceled || !this.StartTestRunOnConcurrentManager(proxyExecutionManager)) { - lock (executionStatusLockObject) + lock (this.executionStatusLockObject) { // Each concurrent Executor calls this method // So, we need to keep track of total runcomplete calls - runCompletedClients++; - allRunsCompleted = (runCompletedClients == concurrentManagerInstances.Length); + this.runCompletedClients++; + allRunsCompleted = this.runCompletedClients == this.concurrentManagerInstances.Length; } // verify that all executors are done with the execution and there are no more sources/testcases to execute if (allRunsCompleted) { // Reset enumerators - sourceEnumerator = null; - testCaseListEnumerator = null; + this.sourceEnumerator = null; + this.testCaseListEnumerator = null; // Dispose concurrent executors // Do not do the cleanuptask in the current thread as we will unncessarily add to execution time - lastParallelRunCleanUpTask = Task.Run(() => + this.lastParallelRunCleanUpTask = Task.Run(() => { - UpdateParallelLevel(0); + this.UpdateParallelLevel(0); }); } } @@ -168,7 +174,7 @@ protected override void DisposeInstance(IProxyExecutionManager managerInstance) { try { - managerInstance.Dispose(); + managerInstance.Close(); } catch (Exception) { @@ -183,11 +189,11 @@ private int StartTestRunPrivate(ITestRunEventsHandler runEventsHandler) { // Cleanup Task for cleaning up the parallel executors except for the default one // We do not do this in Sync so that this task does not add up to execution time - if (lastParallelRunCleanUpTask != null) + if (this.lastParallelRunCleanUpTask != null) { try { - lastParallelRunCleanUpTask.Wait(); + this.lastParallelRunCleanUpTask.Wait(); } catch (Exception ex) { @@ -197,25 +203,29 @@ private int StartTestRunPrivate(ITestRunEventsHandler runEventsHandler) EqtTrace.Warning("ParallelTestRunnerServiceClient: Exception while invoking an action on DiscoveryManager: {0}", ex); } } - lastParallelRunCleanUpTask = null; + + this.lastParallelRunCleanUpTask = null; } // Reset the runcomplete data - runCompletedClients = 0; + this.runCompletedClients = 0; // One data aggregator per parallel run var runDataAggregator = new ParallelRunDataAggregator(); - concurrentManagerHandlerMap = new Dictionary(); + this.concurrentManagerHandlerMap = new Dictionary(); - for (int i = 0; i < concurrentManagerInstances.Length; i++) + for (int i = 0; i < this.concurrentManagerInstances.Length; i++) { - var concurrentManager = concurrentManagerInstances[i]; + var concurrentManager = this.concurrentManagerInstances[i]; - var parallelEventsHandler = new ParallelRunEventsHandler(concurrentManager, runEventsHandler, - this, runDataAggregator); - concurrentManagerHandlerMap.Add(concurrentManager, parallelEventsHandler); + var parallelEventsHandler = new ParallelRunEventsHandler( + concurrentManager, + runEventsHandler, + this, + runDataAggregator); + this.concurrentManagerHandlerMap.Add(concurrentManager, parallelEventsHandler); - Task.Run(() => StartTestRunOnConcurrentManager(concurrentManager)); + Task.Run(() => this.StartTestRunOnConcurrentManager(concurrentManager)); } return 1; @@ -225,49 +235,39 @@ private int StartTestRunPrivate(ITestRunEventsHandler runEventsHandler) /// Triggers the execution for the next data object on the concurrent executor /// Each concurrent executor calls this method, once its completed working on previous data /// - /// + /// Proxy execution manager instance. /// True, if execution triggered private bool StartTestRunOnConcurrentManager(IProxyExecutionManager proxyExecutionManager) { TestRunCriteria testRunCriteria = null; - if (!hasSpecificTestsRun) + if (!this.hasSpecificTestsRun) { string nextSource = null; - if (FetchNextSource(sourceEnumerator, out nextSource)) + if (this.FetchNextSource(this.sourceEnumerator, out nextSource)) { EqtTrace.Info("ProxyParallelExecutionManager: Triggering test run for next source: {0}", nextSource); - testRunCriteria = new TestRunCriteria(new List() { nextSource }, - actualTestRunCriteria.FrequencyOfRunStatsChangeEvent, - actualTestRunCriteria.KeepAlive, - actualTestRunCriteria.TestRunSettings, - actualTestRunCriteria.RunStatsChangeEventTimeout, - actualTestRunCriteria.TestHostLauncher); + testRunCriteria = new TestRunCriteria(new List() { nextSource }, this.actualTestRunCriteria.FrequencyOfRunStatsChangeEvent, this.actualTestRunCriteria.KeepAlive, this.actualTestRunCriteria.TestRunSettings, this.actualTestRunCriteria.RunStatsChangeEventTimeout, this.actualTestRunCriteria.TestHostLauncher); } } else { List nextSetOfTests = null; - if (FetchNextSource(testCaseListEnumerator, out nextSetOfTests)) + if (this.FetchNextSource(this.testCaseListEnumerator, out nextSetOfTests)) { EqtTrace.Info("ProxyParallelExecutionManager: Triggering test run for next source: {0}", nextSetOfTests?.FirstOrDefault()?.Source); testRunCriteria = new TestRunCriteria( - nextSetOfTests, - actualTestRunCriteria.FrequencyOfRunStatsChangeEvent, - actualTestRunCriteria.KeepAlive, - actualTestRunCriteria.TestRunSettings, - actualTestRunCriteria.RunStatsChangeEventTimeout, - actualTestRunCriteria.TestHostLauncher); + nextSetOfTests, this.actualTestRunCriteria.FrequencyOfRunStatsChangeEvent, this.actualTestRunCriteria.KeepAlive, this.actualTestRunCriteria.TestRunSettings, this.actualTestRunCriteria.RunStatsChangeEventTimeout, this.actualTestRunCriteria.TestHostLauncher); } } if (testRunCriteria != null) { - proxyExecutionManager.StartTestRun(testRunCriteria, concurrentManagerHandlerMap[proxyExecutionManager]); + proxyExecutionManager.StartTestRun(testRunCriteria, this.concurrentManagerHandlerMap[proxyExecutionManager]); } - return (testRunCriteria != null); + return testRunCriteria != null; } /// @@ -279,12 +279,12 @@ private bool FetchNextSource(IEnumerator enumerator, out T source) { source = default(T); var hasNext = false; - lock (sourceEnumeratorLockObject) + lock (this.sourceEnumeratorLockObject) { if (enumerator.MoveNext()) { - source = (T)(enumerator.Current); - hasNext = (source != null); + source = (T)enumerator.Current; + hasNext = source != null; } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs index 8b7a999c71..0fc4b00bc1 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs @@ -2,9 +2,12 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client { + using System; + using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; @@ -14,17 +17,22 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client /// public class ProxyDiscoveryManager : ProxyOperationManager, IProxyDiscoveryManager { + private readonly ITestHostManager testHostManager; + #region Constructors /// /// Initializes a new instance of the class. /// - public ProxyDiscoveryManager() - : base() + /// Test host manager instance. + public ProxyDiscoveryManager(ITestHostManager testHostManager) + : this(new TestRequestSender(), testHostManager, Constants.ClientConnectionTimeout) { + this.testHostManager = testHostManager; } /// + /// Initializes a new instance of the class. /// Constructor with Dependency injection. Used for unit testing. /// /// @@ -36,9 +44,13 @@ public ProxyDiscoveryManager() /// /// The client Connection Timeout /// - internal ProxyDiscoveryManager(ITestRequestSender requestSender, ITestHostManager testHostManager, int clientConnectionTimeout) + internal ProxyDiscoveryManager( + ITestRequestSender requestSender, + ITestHostManager testHostManager, + int clientConnectionTimeout) : base(requestSender, testHostManager, clientConnectionTimeout) { + this.testHostManager = testHostManager; } #endregion @@ -48,26 +60,13 @@ internal ProxyDiscoveryManager(ITestRequestSender requestSender, ITestHostManage /// /// Ensure that the discovery component of engine is ready for discovery usually by loading extensions. /// - /// - /// The manager for the test host. - /// - /// - /// The test Run Settings. - /// - public override void Initialize(ITestHostManager testHostManager) + public void Initialize() { - base.Initialize(testHostManager); - - // Only send this if needed. - if (TestPluginCache.Instance.PathToAdditionalExtensions != null - && TestPluginCache.Instance.PathToAdditionalExtensions.Any()) + if (this.testHostManager.Shared) { - // Ensure that the client is conected. - this.EnsureInitialized(); - - this.RequestSender.InitializeDiscovery( - TestPluginCache.Instance.PathToAdditionalExtensions, - TestPluginCache.Instance.LoadOnlyWellKnownExtensions); + // If the test host manager supports sharing the test host across sources, we can + // initialize it early and assign sources later. + this.InitializeExtensions(Enumerable.Empty()); } } @@ -78,14 +77,43 @@ public override void Initialize(ITestHostManager testHostManager) /// EventHandler for handling discovery events from Engine public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEventsHandler eventHandler) { - // Ensure that initialize is called. - this.EnsureInitialized(); + if (!this.testHostManager.Shared) + { + // If the test host doesn't support sharing across sources, we must initialize it + // with sources. + this.InitializeExtensions(discoveryCriteria.Sources); + } + this.SetupChannel(discoveryCriteria.Sources); this.RequestSender.DiscoverTests(discoveryCriteria, eventHandler); + } - this.RequestSender.EndSession(); + /// + public void Abort() + { + // This is no-op for the moment. There is no discovery abort message? + } + + /// + public override void Close() + { + base.Close(); } #endregion + + private void InitializeExtensions(IEnumerable sources) + { + // Only send this if needed. + if (TestPluginCache.Instance.PathToAdditionalExtensions != null + && TestPluginCache.Instance.PathToAdditionalExtensions.Any()) + { + this.SetupChannel(sources); + + this.RequestSender.InitializeDiscovery( + TestPluginCache.Instance.PathToAdditionalExtensions, + TestPluginCache.Instance.LoadOnlyWellKnownExtensions); + } + } } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs index 89cf2cc033..a1fda342ed 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs @@ -2,31 +2,39 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client { + using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.ClientProtocol; + using Constants = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Constants; + /// /// Orchestrates test execution operations for the engine communicating with the client. /// internal class ProxyExecutionManager : ProxyOperationManager, IProxyExecutionManager { + private readonly ITestHostManager testHostManager; + #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ProxyExecutionManager() - : base() + /// Test host manager for this proxy. + public ProxyExecutionManager(ITestHostManager testHostManager) : this(new TestRequestSender(), testHostManager, Constants.ClientConnectionTimeout) { } /// + /// Initializes a new instance of the class. /// Constructor with Dependency injection. Used for unit testing. /// /// Request Sender instance @@ -35,6 +43,7 @@ public ProxyExecutionManager() internal ProxyExecutionManager(ITestRequestSender requestSender, ITestHostManager testHostManager, int clientConnectionTimeout) : base(requestSender, testHostManager, clientConnectionTimeout) { + this.testHostManager = testHostManager; } #endregion @@ -44,21 +53,14 @@ internal ProxyExecutionManager(ITestRequestSender requestSender, ITestHostManage /// /// Ensure that the Execution component of engine is ready for execution usually by loading extensions. /// - /// Manager for the test host. - public override void Initialize(ITestHostManager testHostManager) + public virtual void Initialize() { - base.Initialize(testHostManager); - - // Only send this if needed. - if (TestPluginCache.Instance.PathToAdditionalExtensions != null - && TestPluginCache.Instance.PathToAdditionalExtensions.Any()) + if (this.testHostManager.Shared) { - // Ensure that the client is conected. - this.EnsureInitialized(); - - this.RequestSender.InitializeExecution( - TestPluginCache.Instance.PathToAdditionalExtensions, - TestPluginCache.Instance.LoadOnlyWellKnownExtensions); + // Shared test hosts don't require test source information to launch. Start them early + // to allow fail fast. + EqtTrace.Verbose("ProxyExecutionManager: Test host is shared. SetupChannel it early."); + this.InitializeExtensions(Enumerable.Empty()); } } @@ -70,8 +72,24 @@ public override void Initialize(ITestHostManager testHostManager) /// The process id of the runner executing tests. public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler eventHandler) { - // Ensure that initialize is called. - this.EnsureInitialized(); + if (!this.testHostManager.Shared) + { + // Non shared test host requires test source information to launch. Provide the sources + // information and create the channel. + EqtTrace.Verbose("ProxyExecutionManager: Test host is non shared. Lazy initialize."); + var testSources = testRunCriteria.Sources; + + // If the test execution is with a test filter, group them by sources + if (testRunCriteria.HasSpecificTests) + { + testSources = testRunCriteria.Tests.GroupBy(tc => tc.Source).Select(g => g.Key); + } + + this.InitializeExtensions(testSources); + } + + this.SetupChannel(testRunCriteria.Sources); + var executionContext = new TestExecutionContext( testRunCriteria.FrequencyOfRunStatsChangeEvent, testRunCriteria.RunStatsChangeEventTimeout, @@ -116,17 +134,25 @@ public virtual void Cancel() /// /// Aborts the test run. /// - public override void Abort() + public void Abort() { this.RequestSender.SendTestRunAbort(); } - public override void Dispose() + #endregion + + private void InitializeExtensions(IEnumerable sources) { - this.RequestSender?.EndSession(); - base.Dispose(); - } + // Only send this if needed. + if (TestPluginCache.Instance.PathToAdditionalExtensions != null + && TestPluginCache.Instance.PathToAdditionalExtensions.Any()) + { + this.SetupChannel(sources); - #endregion + this.RequestSender.InitializeExecution( + TestPluginCache.Instance.PathToAdditionalExtensions, + TestPluginCache.Instance.LoadOnlyWellKnownExtensions); + } + } } } \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs index 2c4a2c8540..7dae8b4e27 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs @@ -4,24 +4,29 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client { using System; using System.Collections.Generic; - using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection; + + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - /// + + /// /// The proxy execution manager with data collection. /// internal class ProxyExecutionManagerWithDataCollection : ProxyExecutionManager { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// + /// + /// Test host manager for this operation. + /// /// /// The proxy Data Collection Manager. /// - public ProxyExecutionManagerWithDataCollection(IProxyDataCollectionManager proxyDataCollectionManager) : base() + public ProxyExecutionManagerWithDataCollection(ITestHostManager testHostManager, IProxyDataCollectionManager proxyDataCollectionManager) : base(testHostManager) { this.ProxyDataCollectionManager = proxyDataCollectionManager; this.DataCollectionRunEventsHandler = new DataCollectionRunEventsHandler(); @@ -46,10 +51,7 @@ internal IProxyDataCollectionManager ProxyDataCollectionManager /// /// Ensure that the Execution component of engine is ready for execution usually by loading extensions. /// - /// - /// The test Host Manager. - /// - public override void Initialize(ITestHostManager testHostManager) + public override void Initialize() { DataCollectionParameters dataCollectionParameters = null; try @@ -87,7 +89,7 @@ public override void Initialize(ITestHostManager testHostManager) } // todo : pass dataCollectionParameters.EnvironmentVariables while initializaing testhostprocess. - base.Initialize(testHostManager); + base.Initialize(); } /// @@ -116,17 +118,16 @@ public override int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEvents return base.StartTestRun(testRunCriteria, currentEventHandler); } - /// - /// Cancels the test run. - /// + /// public override void Cancel() { base.Cancel(); } - public override void Dispose() + /// + public override void Close() { - base.Dispose(); + base.Close(); } } @@ -140,7 +141,7 @@ internal class DataCollectionRunEventsHandler : ITestMessageEventHandler /// public DataCollectionRunEventsHandler() { - this.ExceptionMessages = new List(); + this.ExceptionMessages = new List(); } /// @@ -156,11 +157,13 @@ public DataCollectionRunEventsHandler() /// /// /// The message. - /// public void HandleLogMessage(TestMessageLevel level, string message) + /// + public void HandleLogMessage(TestMessageLevel level, string message) { this.ExceptionMessages.Add(message); } - /// + + /// /// The handle raw message. /// /// diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs index bafb7b8da2..f42fa48326 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs @@ -16,36 +16,28 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client /// /// Base class for any operations that the client needs to drive through the engine. /// - public class ProxyOperationManager : IProxyOperationManager + public abstract class ProxyOperationManager { - private ITestHostManager testHostManager; + private readonly ITestHostManager testHostManager; - private bool isInitialized; + private bool initialized; - private int connectionTimeout; - - #region Constructors. + private readonly int connectionTimeout; - /// - /// Initializes a new instance of the class. - /// - public ProxyOperationManager() - : this(new TestRequestSender(), null, Constants.ClientConnectionTimeout) - { - } + #region Constructors /// - /// Constructor with Dependency injection. Used for unit testing. + /// Initializes a new instance of the class. /// /// Request Sender instance. /// Test host manager instance. /// Client Connection Timeout. - internal ProxyOperationManager(ITestRequestSender requestSender, ITestHostManager testHostManager, int clientConnectionTimeout) + protected ProxyOperationManager(ITestRequestSender requestSender, ITestHostManager testHostManager, int clientConnectionTimeout) { this.RequestSender = requestSender; this.connectionTimeout = clientConnectionTimeout; this.testHostManager = testHostManager; - this.isInitialized = false; + this.initialized = false; } #endregion @@ -65,56 +57,46 @@ internal ProxyOperationManager(ITestRequestSender requestSender, ITestHostManage /// Ensure that the engine is ready for test operations. /// Usually includes starting up the test host process. /// - /// Manager for launching and maintaining the test host process - public virtual void Initialize(ITestHostManager testHostManager) + /// List of test sources. + public virtual void SetupChannel(IEnumerable sources) { - this.testHostManager = testHostManager; - - var portNumber = this.RequestSender.InitializeCommunication(); + if (!this.initialized) + { + var portNumber = this.RequestSender.InitializeCommunication(); - // TODO: Fix the environment variables usage - this.testHostManager.LaunchTestHost(null, this.GetCommandLineArguments(portNumber)); + // Get the test process start info + // TODO: Fix the environment variables usage + var testHostStartInfo = this.testHostManager.GetTestHostProcessStartInfo( + sources, + null, + new TestRunnerConnectionInfo { Port = portNumber }); - this.isInitialized = true; - } + // TODO: monitor test host exit and clean up + this.testHostManager.LaunchTestHost(testHostStartInfo); - /// - /// Dispose for this instance. - /// - public virtual void Dispose() - { - // Do Nothing. - } + this.initialized = true; + } - /// - /// Aborts the test discovery. - /// - public virtual void Abort() - { - throw new NotImplementedException(); + // Wait for a timeout for the client to connect. + if (!this.RequestSender.WaitForRequestHandlerConnection(this.connectionTimeout)) + { + throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CrossPlatEngine.Resources.InitializationFailed)); + } } - #endregion - - #region protected methods - /// - /// The ensure initialized. + /// Closes the channel, terminate test host process. /// - protected void EnsureInitialized() + public virtual void Close() { - if (!this.isInitialized) + // TODO dispose the testhost process + try { - this.Initialize(this.testHostManager); + this.RequestSender.EndSession(); } - - // Wait for a timeout for the client to connect. - var isHandlerConnected = this.RequestSender.WaitForRequestHandlerConnection(this.connectionTimeout); - - if (!isHandlerConnected) + finally { - throw new TestPlatformException( - string.Format(CultureInfo.CurrentUICulture, CrossPlatEngine.Resources.InitializationFailed)); + this.initialized = false; } } @@ -129,11 +111,7 @@ protected void EnsureInitialized() /// The commandLine arguments as a list. private IList GetCommandLineArguments(int portNumber) { - var commandlineArguments = new List(); - - commandlineArguments.Add(Constants.PortOption); - commandlineArguments.Add(portNumber.ToString()); - + var commandlineArguments = new List { Constants.PortOption, portNumber.ToString() }; return commandlineArguments; } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/ExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/ExecutionManager.cs index 4145d31311..04dcc03b77 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/ExecutionManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/ExecutionManager.cs @@ -7,13 +7,13 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Execution using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; using Microsoft.VisualStudio.TestPlatform.Common.SettingsProvider; + using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.ClientProtocol; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.TesthostProtocol; - using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; /// /// Orchestrates test execution related functionality for the engine communicating with the test host process. @@ -22,18 +22,24 @@ public class ExecutionManager : IExecutionManager { private ITestRunEventsHandler testRunEventsHandler; - private ITestPlatformEventSource testPlatformEventSource; + private readonly ITestPlatformEventSource testPlatformEventSource; private BaseRunTests activeTestRun; - protected ExecutionManager(ITestPlatformEventSource testPlatformEventSource):this() + /// + /// Initializes a new instance of the class. + /// + public ExecutionManager() : this(TestPlatformEventSource.Instance) { - this.testPlatformEventSource = testPlatformEventSource; } - public ExecutionManager() + /// + /// Initializes a new instance of the class. + /// + /// Test platform event source. + protected ExecutionManager(ITestPlatformEventSource testPlatformEventSource) { - this.testPlatformEventSource = TestPlatformEventSource.Instance; + this.testPlatformEventSource = testPlatformEventSource; } #region IExecutionManager Implementation @@ -45,9 +51,11 @@ public ExecutionManager() public void Initialize(IEnumerable pathToAdditionalExtensions) { this.testPlatformEventSource.AdapterSearchStart(); + // Start using these additional extensions TestPluginCache.Instance.UpdateAdditionalExtensions(pathToAdditionalExtensions, shouldLoadOnlyWellKnownExtensions: false); this.LoadExtensions(); + this.testPlatformEventSource.AdapterSearchStop(); } @@ -59,24 +67,28 @@ public void Initialize(IEnumerable pathToAdditionalExtensions) /// The test Execution Context. /// EventHandler for handling test cases level events from Engine. /// EventHandler for handling execution events from Engine. - public void StartTestRun(Dictionary> adapterSourceMap, string runSettings, TestExecutionContext testExecutionContext, - ITestCaseEventsHandler testCaseEventsHandler, ITestRunEventsHandler runEventsHandler) + public void StartTestRun( + Dictionary> adapterSourceMap, + string runSettings, + TestExecutionContext testExecutionContext, + ITestCaseEventsHandler testCaseEventsHandler, + ITestRunEventsHandler runEventsHandler) { this.testRunEventsHandler = runEventsHandler; try { - activeTestRun = new RunTestsWithSources( + this.activeTestRun = new RunTestsWithSources( adapterSourceMap, runSettings, testExecutionContext, testCaseEventsHandler, runEventsHandler); - activeTestRun.RunTests(); + this.activeTestRun.RunTests(); } finally { - activeTestRun = null; + this.activeTestRun = null; } } @@ -88,24 +100,29 @@ public void StartTestRun(Dictionary> adapterSourceMa /// The test Execution Context. /// EventHandler for handling test cases level events from Engine. /// EventHandler for handling execution events from Engine. - public void StartTestRun(IEnumerable tests, string runSettings, TestExecutionContext testExecutionContext, - ITestCaseEventsHandler testCaseEventsHandler, ITestRunEventsHandler runEventsHandler) + public void StartTestRun( + IEnumerable tests, + string runSettings, + TestExecutionContext testExecutionContext, + ITestCaseEventsHandler testCaseEventsHandler, + ITestRunEventsHandler runEventsHandler) { this.testRunEventsHandler = runEventsHandler; try { - activeTestRun = new RunTestsWithTests(tests, - runSettings, - testExecutionContext, - testCaseEventsHandler, - runEventsHandler); - - activeTestRun.RunTests(); + this.activeTestRun = new RunTestsWithTests( + tests, + runSettings, + testExecutionContext, + testCaseEventsHandler, + runEventsHandler); + + this.activeTestRun.RunTests(); } finally { - activeTestRun = null; + this.activeTestRun = null; } } @@ -174,6 +191,7 @@ private void LoadExtensions() } } } + #endregion } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Friends.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Friends.cs index bb8ed573fc..af7140b188 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Friends.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Friends.cs @@ -6,4 +6,4 @@ [assembly: InternalsVisibleTo("vstest.console.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("datacollector.x86, PublicKey = 002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("datacollector, PublicKey = 002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] - +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/Interfaces/IProcessHelper.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/Interfaces/IProcessHelper.cs index 723407b155..f88995c1f4 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/Interfaces/IProcessHelper.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/Interfaces/IProcessHelper.cs @@ -21,7 +21,13 @@ internal interface IProcessHelper /// /// Gets the current process file path. /// - /// The current process file path. + /// The current process file path. string GetCurrentProcessFileName(); + + /// + /// Gets the location of test engine. + /// + /// Location of test engine. + string GetTestEngineDirectory(); } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/ProcessHelper.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/ProcessHelper.cs index 91a1d0cf2f..9e2b562775 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/ProcessHelper.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Helpers/ProcessHelper.cs @@ -4,6 +4,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers { using System; using System.Diagnostics; + using System.IO; + using System.Reflection; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -57,5 +59,11 @@ public string GetCurrentProcessFileName() { return Process.GetCurrentProcess().MainModule.FileName; } + + /// + public string GetTestEngineDirectory() + { + return Path.GetDirectoryName(typeof(ProcessHelper).GetTypeInfo().Assembly.Location); + } } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DefaultTestHostManager.cs index 09e6a544ca..9e89ed2f6c 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DefaultTestHostManager.cs @@ -1,10 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. + namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; + using System.Linq; using System.Reflection; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers; @@ -13,6 +15,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + using Constants = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Constants; + /// /// The default test host launcher for the engine. /// This works for Desktop local scenarios @@ -25,12 +29,12 @@ public class DefaultTestHostManager : ITestHostManager private const string DotnetProcessNameXPlat = "dotnet"; private const string NetCoreDirectoryName = "NetCore"; - private Architecture architecture; - private Framework framework; + private readonly Architecture architecture; + private readonly Framework framework; private ITestHostLauncher customTestHostLauncher; private Process testHostProcess; - private IProcessHelper processHelper; + private readonly IProcessHelper processHelper; private EventHandler registeredExitHandler; @@ -58,6 +62,9 @@ internal DefaultTestHostManager(Architecture architecture, Framework framework, this.testHostProcess = null; } + /// + public bool Shared => true; + /// /// Gets the properties of the test executor launcher. These could be the targetID for emulator/phone specific scenarios. /// @@ -81,78 +88,52 @@ public void SetCustomLauncher(ITestHostLauncher customLauncher) /// /// Launches the test host for discovery/execution. /// - /// Environment variables for the process. - /// The command line arguments to pass to the process. + /// /// ProcessId of launched Process. 0 means not launched. - public virtual int LaunchTestHost(IDictionary environmentVariables, IList commandLineArguments) + public int LaunchTestHost(TestProcessStartInfo testHostStartInfo) { this.DeregisterForExitNotification(); - var testHostProcessInfo = this.GetTestHostProcessStartInfo(environmentVariables, commandLineArguments); - EqtTrace.Verbose("Launching default test Host Process {0} with arguments {1}", testHostProcessInfo.FileName, testHostProcessInfo.Arguments); + EqtTrace.Verbose("Launching default test Host Process {0} with arguments {1}", testHostStartInfo.FileName, testHostStartInfo.Arguments); if (this.customTestHostLauncher == null) { - this.testHostProcess = this.processHelper.LaunchProcess(testHostProcessInfo.FileName, testHostProcessInfo.Arguments, testHostProcessInfo.WorkingDirectory); + this.testHostProcess = this.processHelper.LaunchProcess(testHostStartInfo.FileName, testHostStartInfo.Arguments, testHostStartInfo.WorkingDirectory); } else { - int processId = this.customTestHostLauncher.LaunchTestHost(testHostProcessInfo); + int processId = this.customTestHostLauncher.LaunchTestHost(testHostStartInfo); this.testHostProcess = Process.GetProcessById(processId); } return this.testHostProcess.Id; } - /// - /// Gives the ProcessStartInfo for the test host process - /// - /// Set of environment variables. - /// Arguments for the test host process. - /// ProcessStartInfo of the test host - public virtual TestProcessStartInfo GetTestHostProcessStartInfo(IDictionary environmentVariables, IList commandLineArguments) + /// + public virtual TestProcessStartInfo GetTestHostProcessStartInfo( + IEnumerable sources, + IDictionary environmentVariables, + TestRunnerConnectionInfo connectionInfo) { - var testHostProcessName = X64TestHostProcessName; - string testHostDirectory = Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location); - string testhostProcessPath; - - // If we are running in the dotnet.exe context we do not want to launch testhost.exe but dotnet.exe with the testhost assembly. - // Since dotnet.exe is already built for multiple platforms this would avoid building testhost.exe also in multiple platforms. - var currentProcessFileName = this.processHelper.GetCurrentProcessFileName(); - if (currentProcessFileName.EndsWith(DotnetProcessName) || currentProcessFileName.EndsWith(DotnetProcessNameXPlat)) - { - testhostProcessPath = currentProcessFileName; - var testhostAssemblyPath = Path.Combine( - testHostDirectory, - testHostProcessName.Replace("exe", "dll")); - commandLineArguments.Insert(0, "\"" + testhostAssemblyPath + "\""); - } - else - { - // Running on Windows with vstest.console.exe for desktop (or VS IDE). Spawn the dotnet.exe - // on path with testhost bundled with vstest.console. - if (this.framework.Name.ToLower().Contains("netstandard") || this.framework.Name.ToLower().Contains("netcoreapp")) - { - testhostProcessPath = DotnetProcessName; - var testhostAssemblyPath = Path.Combine( - Path.GetDirectoryName(currentProcessFileName), - NetCoreDirectoryName, - testHostProcessName.Replace("exe", "dll")); - commandLineArguments.Insert(0, "\"" + testhostAssemblyPath + "\""); - } - else - { - testHostProcessName = (this.architecture == Architecture.X86) ? X86TestHostProcessName : X64TestHostProcessName; - testhostProcessPath = Path.Combine(testHostDirectory, testHostProcessName); - } - } + // Default test host manager supports shared test sources + var testHostProcessName = (this.architecture == Architecture.X86) ? X86TestHostProcessName : X64TestHostProcessName; + var currentWorkingDirectory = Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location); + var argumentsString = " " + Constants.PortOption + " " + connectionInfo.Port; + + var testhostProcessPath = Path.Combine(currentWorkingDirectory, testHostProcessName); - // For IDEs and other scenario - Current directory should be the working directory - not the vstest.console.exe location + // For IDEs and other scenario, current directory should be the + // working directory (not the vstest.console.exe location). // For VS - this becomes the solution directory for example // "TestResults" directory will be created at "current directory" of test host - string processWorkingDirectory = Directory.GetCurrentDirectory(); - - string argumentsString = string.Join(" ", commandLineArguments); - return new TestProcessStartInfo { FileName = testhostProcessPath, Arguments = argumentsString, EnvironmentVariables = environmentVariables, WorkingDirectory = processWorkingDirectory }; + var processWorkingDirectory = Directory.GetCurrentDirectory(); + + return new TestProcessStartInfo + { + FileName = testhostProcessPath, + Arguments = argumentsString, + EnvironmentVariables = environmentVariables ?? new Dictionary(), + WorkingDirectory = processWorkingDirectory + }; } /// diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DotnetTestHostManager.cs new file mode 100644 index 0000000000..068443e7d5 --- /dev/null +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Hosting/DotnetTestHostManager.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + + /// + /// A host manager for dotnet core runtime. + /// + public class DotnetTestHostManager : ITestHostManager + { + private readonly IProcessHelper processHelper; + + private readonly IFileHelper fileHelper; + + private ITestHostLauncher testHostLauncher; + + /// + /// Initializes a new instance of the class. + /// + public DotnetTestHostManager() : this(new DefaultTestHostLauncher(), new ProcessHelper(), new FileHelper()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A test host launcher instance. + /// Process helper instance. + /// File helper instance. + internal DotnetTestHostManager(ITestHostLauncher testHostLauncher, IProcessHelper processHelper, IFileHelper fileHelper) + { + this.testHostLauncher = testHostLauncher; + this.processHelper = processHelper; + this.fileHelper = fileHelper; + } + + /// + /// Gets a value indicating if the test host can be shared for multiple sources. + /// + /// + /// Dependency resolution for .net core projects are pivoted by the test project. Hence each test + /// project must be launched in a separate test host process. + /// + public bool Shared => false; + + /// + public void SetCustomLauncher(ITestHostLauncher customLauncher) + { + this.testHostLauncher = customLauncher; + } + + /// + public int LaunchTestHost(TestProcessStartInfo testHostStartInfo) + { + return this.testHostLauncher.LaunchTestHost(testHostStartInfo); + } + + /// + public virtual TestProcessStartInfo GetTestHostProcessStartInfo( + IEnumerable sources, + IDictionary environmentVariables, + TestRunnerConnectionInfo connectionInfo) + { + // This host manager can create process start info for dotnet core targets only. + // If already running with the dotnet executable, use it; otherwise pick up the dotnet available on path. + var startInfo = new TestProcessStartInfo { FileName = "dotnet" }; + var testHostExecutable = Path.Combine("NetCore", "testhost.dll"); + var currentProcessPath = this.processHelper.GetCurrentProcessFileName(); + var testRunnerDirectory = Path.GetDirectoryName(currentProcessPath); + if (currentProcessPath.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase) + || currentProcessPath.EndsWith("dotnet.exe", StringComparison.OrdinalIgnoreCase)) + { + startInfo.FileName = currentProcessPath; + testHostExecutable = "testhost.dll"; + testRunnerDirectory = this.processHelper.GetTestEngineDirectory(); + } + + // .NET core host manager is not a shared host. It will expect a single test source to be provided. + var args = "exec"; + var sourcePath = sources.Single(); + var sourceFile = Path.GetFileNameWithoutExtension(sourcePath); + var sourceDirectory = Path.GetDirectoryName(sourcePath); + + // Probe for runtimeconfig and deps file for the test source + var runtimeConfigPath = Path.Combine(sourceDirectory, string.Concat(sourceFile, ".runtimeconfig.json")); + if (this.fileHelper.Exists(runtimeConfigPath)) + { + args += " --runtimeconfig \"" + runtimeConfigPath + "\""; + } + + // Use the deps.json for test source + var depsFilePath = Path.Combine(sourceDirectory, string.Concat(sourceFile, ".deps.json")); + if (this.fileHelper.Exists(depsFilePath)) + { + args += " --depsfile \"" + depsFilePath + "\""; + } + + // Create a additional probing path args with Nuget.Client + // args += "--additionalprobingpath xxx" + // TODO this may be required in ASP.net, requires validation + + // Add the testhost path and other arguments + var testHostPath = Path.Combine(testRunnerDirectory, testHostExecutable); + args += " \"" + testHostPath + "\" " + CrossPlatEngine.Constants.PortOption + " " + connectionInfo.Port; + + // Sample command line for the spawned test host + // "D:\dd\gh\Microsoft\vstest\tools\dotnet\dotnet.exe" exec + // --runtimeconfig G:\tmp\netcore-test\bin\Debug\netcoreapp1.0\netcore-test.runtimeconfig.json + // --depsfile G:\tmp\netcore-test\bin\Debug\netcoreapp1.0\netcore-test.deps.json + // --additionalprobingpath C:\Users\armahapa\.nuget\packages\ + // G:\packages\testhost.dll + // G:\tmp\netcore-test\bin\Debug\netcoreapp1.0\netcore-test.dll + startInfo.Arguments = args; + startInfo.EnvironmentVariables = environmentVariables ?? new Dictionary(); + startInfo.WorkingDirectory = sourceDirectory; + + return startInfo; + } + + /// + public void RegisterForExitNotification(Action abortCallback) + { + throw new NotImplementedException(); + } + + /// + public void DeregisterForExitNotification() + { + throw new NotImplementedException(); + } + } + + public class DefaultTestHostLauncher : ITestHostLauncher + { + private readonly IProcessHelper processHelper; + + /// + /// Initializes a new instance of the class. + /// + public DefaultTestHostLauncher() : this(new ProcessHelper()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Process helper instance. + internal DefaultTestHostLauncher(IProcessHelper processHelper) + { + this.processHelper = processHelper; + } + + /// + public bool IsDebug => false; + + /// + public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo) + { + return this.processHelper.LaunchProcess( + defaultTestHostStartInfo.FileName, + defaultTestHostStartInfo.Arguments, + defaultTestHostStartInfo.WorkingDirectory).Id; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs index 9acbf82e0a..b0c3523fe9 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs @@ -35,22 +35,22 @@ public class TestEngine : ITestEngine /// /// Fetches the DiscoveryManager for this engine. This manager would provide all functionality required for discovery. /// + /// /// ITestDiscoveryManager object that can do discovery - public IProxyDiscoveryManager GetDiscoveryManager() + public IProxyDiscoveryManager GetDiscoveryManager(ITestHostManager testHostManager) { - return this.proxyDiscoveryManager ?? (this.proxyDiscoveryManager = new ProxyDiscoveryManager()); + return this.proxyDiscoveryManager ?? (this.proxyDiscoveryManager = new ProxyDiscoveryManager(testHostManager)); } /// /// Fetches the ExecutionManager for this engine. This manager would provide all functionality required for execution. /// - /// - /// The test Run Criteria. - /// + /// Test host manager. + /// Test run criterion. /// /// ITestExecutionManager object that can do execution /// - public IProxyExecutionManager GetExecutionManager(TestRunCriteria testRunCriteria) + public IProxyExecutionManager GetExecutionManager(ITestHostManager testHostManager, TestRunCriteria testRunCriteria) { int parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel(testRunCriteria); @@ -58,8 +58,12 @@ public IProxyExecutionManager GetExecutionManager(TestRunCriteria testRunCriteri var architecture = runconfiguration.TargetPlatform; var isDataCollectorEnabled = XmlRunSettingsUtilities.IsDataCollectionEnabled(testRunCriteria.TestRunSettings); - // Initialize ProxyExecutionManager with data collection if data collectors are specififed in run settings. - Func proxyExecutionManagerCreator = () => isDataCollectorEnabled ? new ProxyExecutionManagerWithDataCollection(this.GetDataCollectionManager(architecture, testRunCriteria.TestRunSettings)) : new ProxyExecutionManager(); + // SetupChannel ProxyExecutionManager with data collection if data collectors are specififed in run settings. + Func proxyExecutionManagerCreator = + () => + isDataCollectorEnabled + ? new ProxyExecutionManagerWithDataCollection(testHostManager, this.GetDataCollectionManager(architecture, testRunCriteria.TestRunSettings)) + : new ProxyExecutionManager(testHostManager); if (parallelLevel > 1) { @@ -93,10 +97,17 @@ public ITestExtensionManager GetExtensionManager() /// Retrieves the default test host manager for this engine. /// /// The architecture we want the test host manager for. + /// Framework for the test session. /// An instance of the test host manager. public ITestHostManager GetDefaultTestHostManager(Architecture architecture, Framework framework) { // This is expected to be called once every run so returning a new instance every time. + if (framework.Name.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) >= 0 + || framework.Name.IndexOf("netcoreapp", StringComparison.OrdinalIgnoreCase) >= 0) + { + return new DotnetTestHostManager(); + } + return new DefaultTestHostManager(architecture, framework); } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/project.json b/src/Microsoft.TestPlatform.CrossPlatEngine/project.json index 4fe4b92dcf..77b225a512 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/project.json +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/project.json @@ -21,7 +21,7 @@ ], "dependencies": { "NETStandard.Library": "1.6.0", - "System.Diagnostics.Process": "4.1.0-rc2-23704", + "System.Diagnostics.Process": "4.1.0", "System.Threading": "4.0.11" } }, diff --git a/src/Microsoft.TestPlatform.Extensions.TrxLogger/project.json b/src/Microsoft.TestPlatform.Extensions.TrxLogger/project.json index 519fa6fec9..71743356d9 100644 --- a/src/Microsoft.TestPlatform.Extensions.TrxLogger/project.json +++ b/src/Microsoft.TestPlatform.Extensions.TrxLogger/project.json @@ -21,7 +21,7 @@ "dependencies": { "NETStandard.Library": "1.6.0", "System.Collections.NonGeneric": "4.0.1", - "System.Security.Principal.Windows": "4.0.0-rc2-24027", + "System.Security.Principal.Windows": "4.0.0", "System.Collections.Specialized": "4.0.1" } }, diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IDiscoveryRequest.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IDiscoveryRequest.cs index 8819bcbfb6..2b42428012 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IDiscoveryRequest.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IDiscoveryRequest.cs @@ -3,48 +3,45 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client { using System; - using System.Collections.Generic; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; /// /// IDiscoverTestsRequest returned after calling GetDiscoveredTestsAsync /// - public interface IDiscoveryRequest: IRequest + public interface IDiscoveryRequest : IRequest { /// - /// Starts tests discovery async. - /// - void DiscoverAsync(); - - /// - /// Aborts the discovery request - /// - void Abort(); - - /// - /// - /// Handler for notifying discovery process is complete + /// Handler for notifying discovery process is complete /// event EventHandler OnDiscoveryComplete; /// - /// Handler for notifying when newly found tests are available for UI to fetch. + /// Handler for notifying when newly found tests are available for UI to fetch. /// event EventHandler OnDiscoveredTests; /// - /// Handler for receiving error during fetching/execution. This is used for when abnormal error - /// occurs; equivalent of IRunMessageLogger in the current RockSteady core + /// Handler for receiving error during fetching/execution. This is used for when abnormal error + /// occurs; equivalent of IRunMessageLogger in the current RockSteady core /// event EventHandler OnDiscoveryMessage; /// - /// Specifies the discovery criterion + /// Gets the discovery criterion. /// DiscoveryCriteria DiscoveryCriteria { get; } + + /// + /// Starts tests discovery async. + /// + void DiscoverAsync(); + + /// + /// Aborts the discovery request + /// + void Abort(); } } diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IRequest.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IRequest.cs index c240888b7c..30c3fd9a4a 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IRequest.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IRequest.cs @@ -5,6 +5,9 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client using System; using System.Threading; + /// + /// Represents any request to discover or run tests. + /// public interface IRequest : IDisposable { /// @@ -17,13 +20,20 @@ public interface IRequest : IDisposable /// /// Waits for the request to complete /// - /// Timeout + /// Time out /// True if the request timeouts bool WaitForCompletion(int timeout); } + /// + /// Extensions for . + /// public static class RequestExtensions { + /// + /// Waits for the request to complete. + /// + /// Request to wait on. public static void WaitForCompletion(this IRequest request) { request.WaitForCompletion(Timeout.Infinite); diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestDiscoveryEventsHandler.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestDiscoveryEventsHandler.cs index 47fe6ac6c5..4a5e5b5a94 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestDiscoveryEventsHandler.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestDiscoveryEventsHandler.cs @@ -12,6 +12,9 @@ public interface ITestDiscoveryEventsHandler : ITestMessageEventHandler /// /// Dispatch DiscoveryComplete event to listeners. /// + /// Total number of tests discovered. + /// Last set of test cases discovered. + /// True if the discovery operation is aborted. void HandleDiscoveryComplete(long totalTests, IEnumerable lastChunk, bool isAborted); diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestHostLauncher.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestHostLauncher.cs index 39c7769fb7..e2584fc7a1 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestHostLauncher.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestHostLauncher.cs @@ -13,14 +13,13 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces public interface ITestHostLauncher { /// - /// Is Debug Launcher + /// Gets a value indicating whether this is a debug launcher. /// bool IsDebug { get; } /// /// Launches custom test host using the default test process start info /// - /// Architecture for the test host /// Default TestHost Process Info /// Process id of the launched test host int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo); diff --git a/src/Microsoft.TestPlatform.ObjectModel/Framework.cs b/src/Microsoft.TestPlatform.ObjectModel/Framework.cs index 166d6d8651..15a44cbb1f 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Framework.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Framework.cs @@ -9,7 +9,11 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel /// public class Framework { +#if NET46 private static readonly Framework Default = Framework.FromString(".NETFramework,Version=v4.6"); +#else + private static readonly Framework Default = Framework.FromString(".NETCoreApp,Version=v1.0"); +#endif private Framework() { diff --git a/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs b/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs index 4a7d18eeee..57a5292ebd 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs @@ -272,7 +272,10 @@ private static object ConvertPropertyFrom(TestProperty property, CultureInfo var valueType = property.GetValueType(); - if (valueType != null && valueType.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())) + // Do not try conversion if the object is already of the type we're trying to convert. + // Note that typeof(T) may be object in case the value is getting deserialized via the StoreKvpList, however + // the deserializer could have converted it already, hence the runtime type check. + if (valueType != null && (valueType.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()) || valueType.GetTypeInfo().IsAssignableFrom(value?.GetType().GetTypeInfo()))) { return value; } diff --git a/src/Microsoft.TestPlatform.ObjectModel/project.json b/src/Microsoft.TestPlatform.ObjectModel/project.json index 989b82e940..7e25c1a260 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/project.json +++ b/src/Microsoft.TestPlatform.ObjectModel/project.json @@ -28,15 +28,15 @@ ], "dependencies": { "NETStandard.Library": "1.6.0", - "System.Runtime.Serialization.Primitives": "4.0.10", + "System.Runtime.Serialization.Primitives": "4.1.1", "System.Runtime.Extensions": "4.1.0", - "System.Xml.XPath.XmlDocument": "4.0.1-rc2-24027", - "System.ComponentModel.EventBasedAsync": "4.0.11-rc2-24018", + "System.Xml.XPath.XmlDocument": "4.0.1", + "System.ComponentModel.EventBasedAsync": "4.0.11", "System.Runtime.InteropServices": "4.1.0", "System.IO.FileSystem": "4.0.1", "System.ComponentModel.TypeConverter": "4.1.0", "System.Security.Cryptography.Algorithms": "4.2.0", - "System.Runtime.Serialization.Json": "4.0.0" + "System.Runtime.Serialization.Json": "4.0.2" } } } diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/project.json b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/project.json index f4f6cf11aa..ed0088f1ff 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/project.json +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/project.json @@ -14,14 +14,13 @@ ], "dependencies": { "NETStandard.Library": "1.6.0", - "System.Runtime.Serialization.Primitives": "4.0.10", + "System.Runtime.Serialization.Primitives": "4.1.1", "System.Runtime.Extensions": "4.1.0", - "System.Xml.XPath.XmlDocument": "4.0.1-rc2-24027", - "System.ComponentModel.EventBasedAsync": "4.0.11-rc2-24018", + "System.Xml.XPath.XmlDocument": "4.0.1", "System.Runtime.InteropServices": "4.1.0", "System.IO.FileSystem": "4.0.1", "System.ComponentModel.TypeConverter": "4.1.0", - "System.Diagnostics.Process": "4.1.0-rc2-23704", + "System.Diagnostics.Process": "4.1.0", "Microsoft.TestPlatform.CommunicationUtilities": "15.0.0-*", "Microsoft.TestPlatform.ObjectModel": "15.0.0-*" } diff --git a/src/TestPlatform.ObjectModel.nuspec b/src/TestPlatform.ObjectModel.nuspec index eccb114447..0bedf0ea76 100644 --- a/src/TestPlatform.ObjectModel.nuspec +++ b/src/TestPlatform.ObjectModel.nuspec @@ -11,6 +11,26 @@ http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm http://go.microsoft.com/fwlink/?LinkID=288859 Copyright © Microsoft Corporation + + + + + + + + + + + + + + + + + + + + diff --git a/src/TestPlatform.TestHost.nuspec b/src/TestPlatform.TestHost.nuspec new file mode 100644 index 0000000000..93614e234a --- /dev/null +++ b/src/TestPlatform.TestHost.nuspec @@ -0,0 +1,33 @@ + + + + Microsoft.TestPlatform.TestHost + 15.0.0 + Microsoft.TestPlatform.TestHost + Microsoft + Microsoft + true + Testplatform host executes the test using specified adapter. + http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm + http://go.microsoft.com/fwlink/?LinkID=288859 + Copyright © Microsoft Corporation + + + + + + + + + + + + + + + + + + + + diff --git a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs index 7b6d48d4f6..9b99a33853 100644 --- a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs +++ b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs @@ -6,17 +6,18 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers using System.Collections.Generic; using System.Linq; using System.Threading; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.Client; using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; - using Microsoft.VisualStudio.TestPlatform.Common.Logging; using Microsoft.VisualStudio.TestPlatform.CommandLine.Internal; using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.Common.Logging; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; /// /// Defines the TestRequestManger which can fire off discovery and test run requests @@ -43,7 +44,7 @@ internal class TestRequestManager : ITestRequestManager /// private ITestRunRequest currentTestRunRequest; - private EventWaitHandle runRequestCreatedEventHandle = new AutoResetEvent(false); + private readonly EventWaitHandle runRequestCreatedEventHandle = new AutoResetEvent(false); #region Constructor @@ -83,6 +84,7 @@ public static ITestRequestManager Instance { testRequestManagerInstance = new TestRequestManager(); } + return testRequestManagerInstance; } } @@ -90,12 +92,12 @@ public static ITestRequestManager Instance #region ITestRequestManager /// - /// Initializes the extensions while probing additional paths + /// Initializes the extensions while probing additional paths. /// /// Paths to Additional extensions public void InitializeExtensions(IEnumerable pathToAdditionalExtensions) { - testPlatform.Initialize(pathToAdditionalExtensions, false, true); + this.testPlatform.Initialize(pathToAdditionalExtensions, false, true); } /// @@ -107,10 +109,10 @@ public void ResetOptions() } /// - /// Discover Tests given a list of sources, runsettings + /// Discover Tests given a list of sources, run settings. /// /// Discovery payload - /// EventHandler for discovered tests + /// EventHandler for discovered tests /// True, if successful public bool DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscoveryEventsRegistrar discoveryEventsRegistrar) { @@ -122,7 +124,7 @@ public bool DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove { try { - testLoggerManager?.RegisterDiscoveryEvents(discoveryRequest); + this.testLoggerManager?.RegisterDiscoveryEvents(discoveryRequest); discoveryEventsRegistrar?.RegisterDiscoveryEvents(discoveryRequest); this.testPlatformEventSource.DiscoveryRequestStart(); @@ -152,7 +154,7 @@ ex is SettingsException || } finally { - testLoggerManager?.UnregisterDiscoveryEvents(discoveryRequest); + this.testLoggerManager?.UnregisterDiscoveryEvents(discoveryRequest); discoveryEventsRegistrar?.UnregisterDiscoveryEvents(discoveryRequest); } } @@ -161,36 +163,42 @@ ex is SettingsException || } /// - /// Run Tests with given a set of testcases + /// Run Tests with given a set of test cases. /// /// TestRun request Payload /// TestHost Launcher for the run /// event registrar for run events - /// True, if sucessful + /// True, if successful public bool RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLauncher testHostLauncher, ITestRunEventsRegistrar testRunEventsRegistrar) { TestRunCriteria runCriteria = null; - if (testRunRequestPayload.Sources != null && testRunRequestPayload.Sources.Count() > 0) + if (testRunRequestPayload.Sources != null && testRunRequestPayload.Sources.Any()) { - runCriteria = new TestRunCriteria(testRunRequestPayload.Sources, - commandLineOptions.BatchSize, - testRunRequestPayload.KeepAlive, - testRunRequestPayload.RunSettings, - commandLineOptions.TestRunStatsEventTimeout, - testHostLauncher); - runCriteria.TestCaseFilter = commandLineOptions.TestCaseFilterValue; + runCriteria = new TestRunCriteria( + testRunRequestPayload.Sources, + this.commandLineOptions.BatchSize, + testRunRequestPayload.KeepAlive, + testRunRequestPayload.RunSettings, + this.commandLineOptions.TestRunStatsEventTimeout, + testHostLauncher); + runCriteria.TestCaseFilter = this.commandLineOptions.TestCaseFilterValue; } else { - runCriteria = new TestRunCriteria(testRunRequestPayload.TestCases, commandLineOptions.BatchSize, testRunRequestPayload.KeepAlive, - testRunRequestPayload.RunSettings, commandLineOptions.TestRunStatsEventTimeout, testHostLauncher); + runCriteria = new TestRunCriteria( + testRunRequestPayload.TestCases, + this.commandLineOptions.BatchSize, + testRunRequestPayload.KeepAlive, + testRunRequestPayload.RunSettings, + this.commandLineOptions.TestRunStatsEventTimeout, + testHostLauncher); } - return RunTests(runCriteria, testRunEventsRegistrar); + return this.RunTests(runCriteria, testRunEventsRegistrar); } /// - /// Cancel the test run + /// Cancel the test run. /// public void CancelTestRun() { @@ -198,6 +206,9 @@ public void CancelTestRun() this.currentTestRunRequest?.CancelAsync(); } + /// + /// Aborts the test run. + /// public void AbortTestRun() { this.runRequestCreatedEventHandle.WaitOne(runRequestTimeout); @@ -209,21 +220,21 @@ public void AbortTestRun() private bool RunTests(TestRunCriteria testRunCriteria, ITestRunEventsRegistrar testRunEventsRegistrar) { bool success = true; - using (this.currentTestRunRequest = testPlatform.CreateTestRunRequest(testRunCriteria)) + using (this.currentTestRunRequest = this.testPlatform.CreateTestRunRequest(testRunCriteria)) { this.runRequestCreatedEventHandle.Set(); try { - testLoggerManager.RegisterTestRunEvents(currentTestRunRequest); - testRunResultAggregator.RegisterTestRunEvents(currentTestRunRequest); - testRunEventsRegistrar?.RegisterTestRunEvents(currentTestRunRequest); + this.testLoggerManager.RegisterTestRunEvents(this.currentTestRunRequest); + this.testRunResultAggregator.RegisterTestRunEvents(this.currentTestRunRequest); + testRunEventsRegistrar?.RegisterTestRunEvents(this.currentTestRunRequest); this.testPlatformEventSource.ExecutionRequestStart(); - currentTestRunRequest.ExecuteAsync(); + this.currentTestRunRequest.ExecuteAsync(); // Wait for the run completion event - currentTestRunRequest.WaitForCompletion(); + this.currentTestRunRequest.WaitForCompletion(); this.testPlatformEventSource.ExecutionRequestStop(); } @@ -233,7 +244,7 @@ private bool RunTests(TestRunCriteria testRunCriteria, ITestRunEventsRegistrar t ex is SettingsException || ex is InvalidOperationException) { - LoggerUtilities.RaiseTestRunError(testLoggerManager, testRunResultAggregator, ex); + LoggerUtilities.RaiseTestRunError(this.testLoggerManager, this.testRunResultAggregator, ex); success = false; } else @@ -243,11 +254,12 @@ ex is SettingsException || } finally { - testLoggerManager.UnregisterTestRunEvents(currentTestRunRequest); - testRunResultAggregator.UnregisterTestRunEvents(currentTestRunRequest); - testRunEventsRegistrar?.UnregisterTestRunEvents(currentTestRunRequest); + this.testLoggerManager.UnregisterTestRunEvents(this.currentTestRunRequest); + this.testRunResultAggregator.UnregisterTestRunEvents(this.currentTestRunRequest); + testRunEventsRegistrar?.UnregisterTestRunEvents(this.currentTestRunRequest); } } + this.currentTestRunRequest = null; return success; diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/Discovery/DiscoveryRequestTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/Discovery/DiscoveryRequestTests.cs index eed94a364e..a2d243d2b3 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/Discovery/DiscoveryRequestTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/Discovery/DiscoveryRequestTests.cs @@ -1,15 +1,18 @@ // Copyright (c) Microsoft. All rights reserved. - namespace Microsoft.VisualStudio.TestPlatform.Client.UnitTests.Discovery { + using System; + using System.Collections.Generic; + using Client.Discovery; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using ObjectModel.Client; using ObjectModel.Engine; - using System; - using System.Collections.Generic; - + [TestClass] public class DiscoveryRequestTests { @@ -17,85 +20,84 @@ public class DiscoveryRequestTests Mock discoveryManager; DiscoveryCriteria discoveryCriteria; - [TestInitialize] - public void TestInit() + public DiscoveryRequestTests() { - discoveryCriteria = new DiscoveryCriteria(new List { "foo" }, 1, null); - discoveryManager = new Mock(); - discoveryRequest = new DiscoveryRequest(discoveryCriteria, discoveryManager.Object); + this.discoveryCriteria = new DiscoveryCriteria(new List { "foo" }, 1, null); + this.discoveryManager = new Mock(); + this.discoveryRequest = new DiscoveryRequest(this.discoveryCriteria, this.discoveryManager.Object); } [TestMethod] public void ConstructorSetsDiscoveryCriteriaAndDiscoveryManager() { - Assert.AreEqual(discoveryCriteria, discoveryRequest.DiscoveryCriteria); - Assert.AreEqual(discoveryManager.Object, (discoveryRequest as DiscoveryRequest).DiscoveryManager); + Assert.AreEqual(this.discoveryCriteria, this.discoveryRequest.DiscoveryCriteria); + Assert.AreEqual(this.discoveryManager.Object, (this.discoveryRequest as DiscoveryRequest).DiscoveryManager); } [TestMethod] public void DiscoveryAsycIfDiscoveryRequestIsDisposedThrowsObjectDisposedException() { - discoveryRequest.Dispose(); + this.discoveryRequest.Dispose(); - Assert.ThrowsException(() => discoveryRequest.DiscoverAsync()); + Assert.ThrowsException(() => this.discoveryRequest.DiscoverAsync()); } [TestMethod] public void DiscoverAsyncSetsDiscoveryInProgressAndCallManagerToDiscoverTests() { - discoveryRequest.DiscoverAsync(); + this.discoveryRequest.DiscoverAsync(); - Assert.IsTrue((discoveryRequest as DiscoveryRequest).DiscoveryInProgress); - discoveryManager.Verify(dm => dm.DiscoverTests(discoveryCriteria, discoveryRequest as DiscoveryRequest), Times.Once); + Assert.IsTrue((this.discoveryRequest as DiscoveryRequest).DiscoveryInProgress); + this.discoveryManager.Verify(dm => dm.DiscoverTests(this.discoveryCriteria, this.discoveryRequest as DiscoveryRequest), Times.Once); } [TestMethod] public void DiscoveryAsyncIfDiscoverTestsThrowsExceptionSetsDiscoveryInProgressToFalseAndThrowsThatException() { - discoveryManager.Setup(dm => dm.DiscoverTests(discoveryCriteria, discoveryRequest as DiscoveryRequest)).Throws(new Exception("DummyException")); + this.discoveryManager.Setup(dm => dm.DiscoverTests(this.discoveryCriteria, this.discoveryRequest as DiscoveryRequest)).Throws(new Exception("DummyException")); try { - discoveryRequest.DiscoverAsync(); + this.discoveryRequest.DiscoverAsync(); } catch (Exception ex) { Assert.IsTrue(ex is Exception); Assert.AreEqual("DummyException", ex.Message); - Assert.IsFalse((discoveryRequest as DiscoveryRequest).DiscoveryInProgress); + Assert.IsFalse((this.discoveryRequest as DiscoveryRequest).DiscoveryInProgress); } } [TestMethod] public void AbortIfDiscoveryRequestDisposedShouldThrowObjectDisposedException() { - discoveryRequest.Dispose(); - Assert.ThrowsException(() => discoveryRequest.Abort()); + this.discoveryRequest.Dispose(); + Assert.ThrowsException(() => this.discoveryRequest.Abort()); } [TestMethod] public void AbortIfDiscoveryIsinProgressShouldCallDiscoveryManagerAbort() { // Just to set the IsDiscoveryInProgress flag - discoveryRequest.DiscoverAsync(); + this.discoveryRequest.DiscoverAsync(); - discoveryRequest.Abort(); - discoveryManager.Verify(dm => dm.Abort(), Times.Once); + this.discoveryRequest.Abort(); + this.discoveryManager.Verify(dm => dm.Abort(), Times.Once); } [TestMethod] public void AbortIfDiscoveryIsNotInProgressShouldNotCallDiscoveryManagerAbort() { - //DiscoveryAsyn has not been called, discoveryInProgress should be false - discoveryRequest.Abort(); - discoveryManager.Verify(dm => dm.Abort(), Times.Never); + // DiscoveryAsyn has not been called, discoveryInProgress should be false + this.discoveryRequest.Abort(); + this.discoveryManager.Verify(dm => dm.Abort(), Times.Never); } [TestMethod] public void WaitForCompletionIfDiscoveryRequestDisposedShouldThrowObjectDisposedException() { - discoveryRequest.Dispose(); - Assert.ThrowsException(() => discoveryRequest.WaitForCompletion()); + this.discoveryRequest.Dispose(); + Assert.ThrowsException(() => this.discoveryRequest.WaitForCompletion()); } } } diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/TestPlatformTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/TestPlatformTests.cs index 0ee522026a..f544c2e120 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/TestPlatformTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/TestPlatformTests.cs @@ -16,33 +16,33 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.UnitTests [TestClass] public class TestPlatformTests { - private Mock testEngine; - private Mock discoveryManager; - private Mock extensionManager; - private Mock hostManager; - private Mock executionManager; - - [TestInitialize] - public void Initialize() + private readonly Mock testEngine; + private readonly Mock discoveryManager; + private readonly Mock extensionManager; + private readonly Mock hostManager; + private readonly Mock executionManager; + + public TestPlatformTests() { - testEngine = new Mock(); - discoveryManager = new Mock(); - extensionManager = new Mock(); - executionManager = new Mock(); - hostManager = new Mock(); + this.testEngine = new Mock(); + this.discoveryManager = new Mock(); + this.extensionManager = new Mock(); + this.executionManager = new Mock(); + this.hostManager = new Mock(); } [TestMethod] public void CreateDiscoveryRequestShouldCreateDiscoveryRequestWithGivenCriteriaAndReturnIt() { - testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(hostManager.Object); - discoveryManager.Setup(dm => dm.Initialize(It.IsAny())).Verifiable(); - testEngine.Setup(te => te.GetDiscoveryManager()).Returns(discoveryManager.Object); - testEngine.Setup(te => te.GetExtensionManager()).Returns(extensionManager.Object); - var tp = new TestableTestPlatform(testEngine.Object); - + this.testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(this.hostManager.Object); + this.discoveryManager.Setup(dm => dm.Initialize()).Verifiable(); + this.testEngine.Setup(te => te.GetDiscoveryManager(this.hostManager.Object)).Returns(this.discoveryManager.Object); + this.testEngine.Setup(te => te.GetExtensionManager()).Returns(this.extensionManager.Object); + var tp = new TestableTestPlatform(this.testEngine.Object); var discoveryCriteria = new DiscoveryCriteria(new List { "foo" }, 1, null); + var discoveryRequest = tp.CreateDiscoveryRequest(discoveryCriteria); + Assert.AreEqual(discoveryCriteria, discoveryRequest.DiscoveryCriteria); } @@ -50,33 +50,34 @@ public void CreateDiscoveryRequestShouldCreateDiscoveryRequestWithGivenCriteriaA public void CreateDiscoveryRequestThrowsIfDiscoveryCriteriaIsNull() { TestPlatform tp = new TestPlatform(); + Assert.ThrowsException(() => tp.CreateDiscoveryRequest(null)); } [TestMethod] public void UpdateExtensionsShouldUpdateTheEngineWithAdditionalExtensions() { - testEngine.Setup(te => te.GetExtensionManager()).Returns(extensionManager.Object); - var tp = new TestableTestPlatform(testEngine.Object); - + this.testEngine.Setup(te => te.GetExtensionManager()).Returns(this.extensionManager.Object); + var tp = new TestableTestPlatform(this.testEngine.Object); var additionalExtensions = new List { "e1.dll", "e2.dll" }; tp.UpdateExtensions(additionalExtensions, loadOnlyWellKnownExtensions: true); - extensionManager.Verify(em => em.UseAdditionalExtensions(additionalExtensions, true)); + this.extensionManager.Verify(em => em.UseAdditionalExtensions(additionalExtensions, true)); } [TestMethod] public void CreateTestRunRequestShouldCreateTestRunRequestWithSpecifiedCriteria() { - testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(hostManager.Object); - executionManager.Setup(dm => dm.Initialize(It.IsAny())).Verifiable(); - testEngine.Setup(te => te.GetExecutionManager(It.IsAny())).Returns(executionManager.Object); - testEngine.Setup(te => te.GetExtensionManager()).Returns(extensionManager.Object); - var tp = new TestableTestPlatform(testEngine.Object); - + this.testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(this.hostManager.Object); + this.executionManager.Setup(dm => dm.Initialize()).Verifiable(); + this.testEngine.Setup(te => te.GetExecutionManager(this.hostManager.Object, It.IsAny())).Returns(this.executionManager.Object); + this.testEngine.Setup(te => te.GetExtensionManager()).Returns(this.extensionManager.Object); + var tp = new TestableTestPlatform(this.testEngine.Object); var testRunCriteria = new TestRunCriteria(new List { "foo" }, 10); + var testRunRequest = tp.CreateTestRunRequest(testRunCriteria); + var actualTestRunRequest = testRunRequest as TestRunRequest; Assert.AreEqual(testRunCriteria, actualTestRunRequest.TestRunCriteria); } @@ -85,25 +86,25 @@ public void CreateTestRunRequestShouldCreateTestRunRequestWithSpecifiedCriteria( public void CreateTestRunRequestShouldSetCustomHostLauncherOnEngineDefaultLauncherIfSpecified() { var mockCustomLauncher = new Mock(); - testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(hostManager.Object); - executionManager.Setup(dm => dm.Initialize(It.IsAny())).Verifiable(); - - testEngine.Setup(te => te.GetExecutionManager(It.IsAny())).Returns(executionManager.Object); - testEngine.Setup(te => te.GetExtensionManager()).Returns(extensionManager.Object); - var tp = new TestableTestPlatform(testEngine.Object); - + this.testEngine.Setup(te => te.GetDefaultTestHostManager(ObjectModel.Architecture.X86, ObjectModel.Framework.DefaultFramework)).Returns(this.hostManager.Object); + this.executionManager.Setup(dm => dm.Initialize()).Verifiable(); + this.testEngine.Setup(te => te.GetExecutionManager(this.hostManager.Object, It.IsAny())).Returns(this.executionManager.Object); + this.testEngine.Setup(te => te.GetExtensionManager()).Returns(this.extensionManager.Object); + var tp = new TestableTestPlatform(this.testEngine.Object); var testRunCriteria = new TestRunCriteria(new List { "foo" }, 10, false, null, TimeSpan.Zero, mockCustomLauncher.Object); + var testRunRequest = tp.CreateTestRunRequest(testRunCriteria); - var actualTestRunRequest = testRunRequest as TestRunRequest; + var actualTestRunRequest = testRunRequest as TestRunRequest; Assert.AreEqual(testRunCriteria, actualTestRunRequest.TestRunCriteria); - hostManager.Verify(hl => hl.SetCustomLauncher(mockCustomLauncher.Object), Times.Once); + this.hostManager.Verify(hl => hl.SetCustomLauncher(mockCustomLauncher.Object), Times.Once); } [TestMethod] public void CreateTestRunRequestThrowsIfTestRunCriteriaIsNull() { var tp = new TestPlatform(); + Assert.ThrowsException(() => tp.CreateTestRunRequest(null)); } diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestObjectConverterTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestObjectConverterTests.cs index f3a565618d..1e5ffc26e8 100644 --- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestObjectConverterTests.cs +++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestObjectConverterTests.cs @@ -143,6 +143,19 @@ public void TestObjectShouldAddPropertyToTestPropertyStoreOnDeserialize() Assert.AreEqual("DummyValue", test.GetPropertyValue(property)); } + [TestMethod] + public void TestObjectSetPropertyValueShouldNotConvertIfValueMatchesPropertyDataType() + { + var property = TestProperty.Register("98", "p1", typeof(bool), typeof(TestObject)); + var testobj = new TestableTestObject(); + + // This should not throw even if the runtime type of boolean where as specified + // type is object + testobj.SetPropertyValue(property, false); + + Assert.AreEqual(false, testobj.GetPropertyValue(property)); + } + private static string Serialize(T data) { return JsonDataSerializer.Instance.Serialize(data); diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs index e3339072b6..e9c1d0d433 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/Parallel/ParallelProxyExecutionManagerTests.cs @@ -36,13 +36,13 @@ public void InitializeShouldCallAllConcurrentManagersOnce() }; this.proxyParallelExecutionManager = new ParallelProxyExecutionManager(proxyManagerFunc, 3); - this.proxyParallelExecutionManager.Initialize(null); + this.proxyParallelExecutionManager.Initialize(); Assert.AreEqual(3, createdMockManagers.Count, "Number of Concurrent Managers created should be 3"); foreach (var manager in createdMockManagers) { - manager.Verify(m => m.Initialize(null), Times.Once); + manager.Verify(m => m.Initialize(), Times.Once); } } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyDiscoveryManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyDiscoveryManagerTests.cs index 001cbac15c..bfa1a64b37 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyDiscoveryManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyDiscoveryManagerTests.cs @@ -3,6 +3,7 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Client { using System.Collections.Generic; + using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -10,6 +11,7 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Client using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -31,12 +33,20 @@ public class ProxyDiscoveryManagerTests /// private int testableClientConnectionTimeout = 400; - [TestInitialize] - public void TestInit() + private DiscoveryCriteria discoveryCriteria; + + public ProxyDiscoveryManagerTests() { this.mockTestHostManager = new Mock(); this.mockRequestSender = new Mock(); - this.testDiscoveryManager = new ProxyDiscoveryManager(this.mockRequestSender.Object, this.mockTestHostManager.Object, this.testableClientConnectionTimeout); + this.testDiscoveryManager = new ProxyDiscoveryManager( + this.mockRequestSender.Object, + this.mockTestHostManager.Object, + this.testableClientConnectionTimeout); + this.discoveryCriteria = new DiscoveryCriteria(new[] { "test.dll" }, 1, string.Empty); + + // Default setup test host manager as shared (desktop) + this.mockTestHostManager.SetupGet(th => th.Shared).Returns(true); } [TestMethod] @@ -45,7 +55,7 @@ public void InitializeShouldNotInitializeExtensionsOnNoExtensions() // Make sure TestPlugincache is refreshed. TestPluginCache.Instance = null; - this.testDiscoveryManager.Initialize(this.mockTestHostManager.Object); + this.testDiscoveryManager.Initialize(); this.mockRequestSender.Verify(s => s.InitializeDiscovery(It.IsAny>(), It.IsAny()), Times.Never); } @@ -64,7 +74,7 @@ public void InitializeShouldInitializeExtensionsIfPresent() TestPluginCacheTests.SetupMockAdditionalPathExtensions(extensions); this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); - this.testDiscoveryManager.Initialize(this.mockTestHostManager.Object); + this.testDiscoveryManager.Initialize(); // Also verify that we have waited for client connection. this.mockRequestSender.Verify(s => s.WaitForRequestHandlerConnection(It.IsAny()), Times.Once); @@ -81,16 +91,16 @@ public void InitializeShouldInitializeExtensionsIfPresent() [TestMethod] public void DiscoverTestsShouldNotIntializeIfDoneSoAlready() { - this.testDiscoveryManager.Initialize(this.mockTestHostManager.Object); + this.testDiscoveryManager.Initialize(); // Setup mocks. this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); // Act. - this.testDiscoveryManager.DiscoverTests(null, null); + this.testDiscoveryManager.DiscoverTests(this.discoveryCriteria, null); this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.AtMostOnce); - this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(null, It.IsAny>()), Times.AtMostOnce); + this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(It.IsAny()), Times.AtMostOnce); } [TestMethod] @@ -100,10 +110,10 @@ public void DiscoverTestsShouldIntializeIfNotInitializedAlready() this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); // Act. - this.testDiscoveryManager.DiscoverTests(null, null); + this.testDiscoveryManager.DiscoverTests(this.discoveryCriteria, null); this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.Once); - this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(null, It.IsAny>()), Times.Once); + this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(It.IsAny()), Times.Once); } [TestMethod] @@ -114,10 +124,9 @@ public void DiscoverTestsShouldThrowExceptionIfClientConnectionTimeout() // Act. Assert.ThrowsException( - () => this.testDiscoveryManager.DiscoverTests(null, null)); + () => this.testDiscoveryManager.DiscoverTests(this.discoveryCriteria, null)); } - [TestMethod] public void DiscoverTestsShouldInitiateServerDiscoveryLoop() { @@ -125,31 +134,10 @@ public void DiscoverTestsShouldInitiateServerDiscoveryLoop() this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); // Act. - this.testDiscoveryManager.DiscoverTests(null, null); - - // Assert. - this.mockRequestSender.Verify(s => s.DiscoverTests(null, null), Times.Once); - } - - [TestMethod] - public void DiscoverTestsShouldEndSessionWithTheServer() - { - // Setup mocks. - this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); - - // Act. - this.testDiscoveryManager.DiscoverTests(null, null); + this.testDiscoveryManager.DiscoverTests(this.discoveryCriteria, null); // Assert. - this.mockRequestSender.Verify(s => s.EndSession(), Times.Once); - } - - private void SignalEvent(ManualResetEvent manualResetEvent) - { - // Wait for the 100 ms. - Task.Delay(200).Wait(); - - manualResetEvent.Set(); + this.mockRequestSender.Verify(s => s.DiscoverTests(It.IsAny(), null), Times.Once); } } } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerTests.cs index fb59d18198..4de0c23a82 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerTests.cs @@ -30,17 +30,22 @@ public class ProxyExecutionManagerTests private Mock mockRequestSender; + private Mock mockTestRunCriteria; + /// /// The client connection timeout in milliseconds for unit tests. /// - private int testableClientConnectionTimeout = 400; + private int clientConnectionTimeout = 400; - [TestInitialize] - public void TestInit() + public ProxyExecutionManagerTests() { this.mockTestHostManager = new Mock(); this.mockRequestSender = new Mock(); - this.testExecutionManager = new ProxyExecutionManager(this.mockRequestSender.Object, this.mockTestHostManager.Object, this.testableClientConnectionTimeout); + this.mockTestRunCriteria = new Mock(new List { "source.dll" }, 10); + this.testExecutionManager = new ProxyExecutionManager(this.mockRequestSender.Object, this.mockTestHostManager.Object, this.clientConnectionTimeout); + + // Default to shared test host + this.mockTestHostManager.SetupGet(th => th.Shared).Returns(true); } [TestMethod] @@ -49,7 +54,7 @@ public void InitializeShouldNotInitializeExtensionsOnNoExtensions() // Make sure TestPlugincache is refreshed. TestPluginCache.Instance = null; - this.testExecutionManager.Initialize(this.mockTestHostManager.Object); + this.testExecutionManager.Initialize(); this.mockRequestSender.Verify(s => s.InitializeExecution(It.IsAny>(), It.IsAny()), Times.Never); } @@ -68,7 +73,7 @@ public void InitializeShouldInitializeExtensionsIfPresent() TestPluginCacheTests.SetupMockAdditionalPathExtensions(extensions); this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); - this.testExecutionManager.Initialize(this.mockTestHostManager.Object); + this.testExecutionManager.Initialize(); // Also verify that we have waited for client connection. this.mockRequestSender.Verify(s => s.WaitForRequestHandlerConnection(It.IsAny()), Times.Once); @@ -83,53 +88,65 @@ public void InitializeShouldInitializeExtensionsIfPresent() } [TestMethod] - public void StartTestRunShouldNotIntializeIfDoneSoAlready() + public void InitializeShouldNotInitializeExtensionsIfTestHostIsNotShared() { - this.testExecutionManager.Initialize(this.mockTestHostManager.Object); + // Make sure TestPlugincache is refreshed. + TestPluginCache.Instance = null; + this.mockTestHostManager.SetupGet(th => th.Shared).Returns(false); + + this.testExecutionManager.Initialize(); - // Setup mocks. + this.mockRequestSender.Verify(s => s.InitializeExecution(It.IsAny>(), It.IsAny()), Times.Never); + } + + [TestMethod] + public void StartTestRunShouldNotIntializeIfDoneSoAlready() + { + this.testExecutionManager.Initialize(); this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); - var mockTestRunCriteria = new Mock(new List { "source.dll" }, 10); - // Act. - this.testExecutionManager.StartTestRun(mockTestRunCriteria.Object, null); + this.testExecutionManager.StartTestRun(this.mockTestRunCriteria.Object, null); this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.AtMostOnce); - this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(null, It.IsAny>()), Times.AtMostOnce); + this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(It.IsAny()), Times.AtMostOnce); } [TestMethod] - public void StartTestRunShouldIntializeIfNotInitializedAlready() + public void StartTestRunShouldInitializeIfNotInitializedAlready() { - // Setup mocks. this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); - var mockTestRunCriteria = new Mock(new List { "source.dll" }, 10); - // Act. - this.testExecutionManager.StartTestRun(mockTestRunCriteria.Object, null); + this.testExecutionManager.StartTestRun(this.mockTestRunCriteria.Object, null); this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.Once); - this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(null, It.IsAny>()), Times.Once); + this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(It.IsAny()), Times.Once); + } + + [TestMethod] + public void StartTestRunShouldInitializeExtensionsIfTestHostIsNotShared() + { + TestPluginCache.Instance = null; + TestPluginCacheTests.SetupMockAdditionalPathExtensions(new[] { "x.dll" }); + this.mockTestHostManager.SetupGet(th => th.Shared).Returns(false); + this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); + + this.testExecutionManager.StartTestRun(this.mockTestRunCriteria.Object, null); + + this.mockRequestSender.Verify(s => s.InitializeExecution(It.IsAny>(), It.IsAny()), Times.Once); } [TestMethod] public void StartTestRunShouldThrowExceptionIfClientConnectionTimeout() { - // Setup mocks. this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(false); - // Act. - Assert.ThrowsException( - () => this.testExecutionManager.StartTestRun(null, null)); + Assert.ThrowsException(() => this.testExecutionManager.StartTestRun(this.mockTestRunCriteria.Object, null)); } - [TestMethod] public void StartTestRunShouldInitiateTestRunForSourcesThroughTheServer() { TestRunCriteriaWithSources testRunCriteriaPassed = null; - - // Setup mocks. this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); this.mockRequestSender.Setup(s => s.StartTestRun(It.IsAny(), null)) .Callback( @@ -137,36 +154,21 @@ public void StartTestRunShouldInitiateTestRunForSourcesThroughTheServer() { testRunCriteriaPassed = criteria; }); - var mockTestRunCriteria = new Mock(new List { "source.dll" }, 10); - // Act. - this.testExecutionManager.StartTestRun(mockTestRunCriteria.Object, null); + this.testExecutionManager.StartTestRun(this.mockTestRunCriteria.Object, null); - // Assert. Assert.IsNotNull(testRunCriteriaPassed); - CollectionAssert.AreEqual( - mockTestRunCriteria.Object.AdapterSourceMap.Keys, - testRunCriteriaPassed.AdapterSourceMap.Keys); - CollectionAssert.AreEqual( - mockTestRunCriteria.Object.AdapterSourceMap.Values, - testRunCriteriaPassed.AdapterSourceMap.Values); - Assert.AreEqual( - mockTestRunCriteria.Object.FrequencyOfRunStatsChangeEvent, - testRunCriteriaPassed.TestExecutionContext.FrequencyOfRunStatsChangeEvent); - Assert.AreEqual( - mockTestRunCriteria.Object.RunStatsChangeEventTimeout, - testRunCriteriaPassed.TestExecutionContext.RunStatsChangeEventTimeout); - Assert.AreEqual( - mockTestRunCriteria.Object.TestRunSettings, - testRunCriteriaPassed.RunSettings); + CollectionAssert.AreEqual(this.mockTestRunCriteria.Object.AdapterSourceMap.Keys, testRunCriteriaPassed.AdapterSourceMap.Keys); + CollectionAssert.AreEqual(this.mockTestRunCriteria.Object.AdapterSourceMap.Values, testRunCriteriaPassed.AdapterSourceMap.Values); + Assert.AreEqual(this.mockTestRunCriteria.Object.FrequencyOfRunStatsChangeEvent, testRunCriteriaPassed.TestExecutionContext.FrequencyOfRunStatsChangeEvent); + Assert.AreEqual(this.mockTestRunCriteria.Object.RunStatsChangeEventTimeout, testRunCriteriaPassed.TestExecutionContext.RunStatsChangeEventTimeout); + Assert.AreEqual(this.mockTestRunCriteria.Object.TestRunSettings, testRunCriteriaPassed.RunSettings); } [TestMethod] public void StartTestRunShouldInitiateTestRunForTestsThroughTheServer() { TestRunCriteriaWithTests testRunCriteriaPassed = null; - - // Setup mocks. this.mockRequestSender.Setup(s => s.WaitForRequestHandlerConnection(It.IsAny())).Returns(true); this.mockRequestSender.Setup(s => s.StartTestRun(It.IsAny(), null)) .Callback( @@ -174,41 +176,38 @@ public void StartTestRunShouldInitiateTestRunForTestsThroughTheServer() { testRunCriteriaPassed = criteria; }); - var mockTestRunCriteria = - new Mock( - new List { new TestCase("A.C.M", new System.Uri("executor://dummy"), "source.dll") }, - 10); + var runCriteria = new Mock( + new List { new TestCase("A.C.M", new System.Uri("executor://dummy"), "source.dll") }, + 10); - // Act. - this.testExecutionManager.StartTestRun(mockTestRunCriteria.Object, null); + this.testExecutionManager.StartTestRun(runCriteria.Object, null); - // Assert. Assert.IsNotNull(testRunCriteriaPassed); - CollectionAssert.AreEqual(mockTestRunCriteria.Object.Tests.ToList(), testRunCriteriaPassed.Tests.ToList()); + CollectionAssert.AreEqual(runCriteria.Object.Tests.ToList(), testRunCriteriaPassed.Tests.ToList()); Assert.AreEqual( - mockTestRunCriteria.Object.FrequencyOfRunStatsChangeEvent, + runCriteria.Object.FrequencyOfRunStatsChangeEvent, testRunCriteriaPassed.TestExecutionContext.FrequencyOfRunStatsChangeEvent); Assert.AreEqual( - mockTestRunCriteria.Object.RunStatsChangeEventTimeout, + runCriteria.Object.RunStatsChangeEventTimeout, testRunCriteriaPassed.TestExecutionContext.RunStatsChangeEventTimeout); Assert.AreEqual( - mockTestRunCriteria.Object.TestRunSettings, + runCriteria.Object.TestRunSettings, testRunCriteriaPassed.RunSettings); } [TestMethod] - public void DisposeShouldSignalServerSessionEnd() + public void CloseShouldSignalServerSessionEnd() { - this.testExecutionManager.Dispose(); + this.testExecutionManager.Close(); this.mockRequestSender.Verify(s => s.EndSession(), Times.Once); } [TestMethod] - public void DisposeShouldSignalServerSessionEndEachTime() + public void CloseShouldSignalServerSessionEndEachTime() { - this.testExecutionManager.Dispose(); - this.testExecutionManager.Dispose(); + this.testExecutionManager.Close(); + this.testExecutionManager.Close(); this.mockRequestSender.Verify(s => s.EndSession(), Times.Exactly(2)); } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs index 5595b00e38..6c0c8a9494 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs @@ -43,8 +43,8 @@ public void TestInit() [TestMethod] public void InitializeShouldInitializeDataCollectionProcessIfDataCollectionIsEnabled() { - var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(this.mockDataCollectionClient.Object); - proxyExecutionManager.Initialize(this.mockTestHostManager.Object); + var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(this.mockTestHostManager.Object, this.mockDataCollectionClient.Object); + proxyExecutionManager.Initialize(); mockDataCollectionClient.Verify(dc => dc.BeforeTestRunStart(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } @@ -55,8 +55,8 @@ public void InitializeShouldCallAfterTestRunIfExceptionIsThrownWhileCreatingData mockDataCollectionClient.Setup(dc => dc.BeforeTestRunStart(It.IsAny(), It.IsAny(), It.IsAny())).Throws(new System.Exception("MyException")); mockDataCollectionClient.Setup(dc => dc.AfterTestRunEnd(It.IsAny(), It.IsAny())); - var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(this.mockDataCollectionClient.Object); - proxyExecutionManager.Initialize(this.mockTestHostManager.Object); + var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(this.mockTestHostManager.Object, this.mockDataCollectionClient.Object); + proxyExecutionManager.Initialize(); mockDataCollectionClient.Verify(dc => dc.BeforeTestRunStart(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); mockDataCollectionClient.Verify(dc => dc.AfterTestRunEnd(It.IsAny(), It.IsAny()), Times.Once); @@ -70,8 +70,8 @@ public void InitializeShouldSaveExceptionMessagesIfThrownByDataCollectionProcess ProxyDataCollectionManager proxyDataCollectonManager = new ProxyDataCollectionManager(Architecture.AnyCPU, string.Empty, new DummyDataCollectionRequestSender(), new DummyDataCollectionLauncher()); - var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(proxyDataCollectonManager); - proxyExecutionManager.Initialize(this.mockTestHostManager.Object); + var proxyExecutionManager = new ProxyExecutionManagerWithDataCollection(this.mockTestHostManager.Object, proxyDataCollectonManager); + proxyExecutionManager.Initialize(); Assert.IsNotNull(proxyExecutionManager.DataCollectionRunEventsHandler.ExceptionMessages); Assert.AreEqual(1, proxyExecutionManager.DataCollectionRunEventsHandler.ExceptionMessages.Count); } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs index 7f50a18cc6..95e7d38605 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs @@ -2,13 +2,13 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Client { + using System; using System.Collections.Generic; + using System.Linq; - using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; - using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,60 +17,109 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Client [TestClass] public class ProxyOperationManagerTests { - private TestableProxyOperationManager testOperationManager; + private readonly ProxyOperationManager testOperationManager; - private Mock mockTestHostManager; + private readonly Mock mockTestHostManager; - private Mock mockRequestSender; + private readonly Mock mockRequestSender; /// /// The client connection timeout in milliseconds for unit tests. /// - private int testableClientConnectionTimeout = 400; + private int connectionTimeout = 400; - [TestInitialize] - public void TestInit() + public ProxyOperationManagerTests() { this.mockTestHostManager = new Mock(); this.mockRequestSender = new Mock(); - this.testOperationManager = new TestableProxyOperationManager(this.mockRequestSender.Object, this.testableClientConnectionTimeout); + this.mockRequestSender.Setup(rs => rs.WaitForRequestHandlerConnection(this.connectionTimeout)).Returns(true); + this.testOperationManager = new TestableProxyOperationManager(this.mockRequestSender.Object, this.mockTestHostManager.Object, this.connectionTimeout); } [TestMethod] - public void InitializeShouldLaunchTestHost() + public void SetupChannelShouldLaunchTestHost() { - this.testOperationManager.Initialize(this.mockTestHostManager.Object); + var expectedStartInfo = new TestProcessStartInfo(); + this.mockRequestSender.Setup(rs => rs.InitializeCommunication()).Returns(123); + this.mockTestHostManager.Setup( + th => th.GetTestHostProcessStartInfo(Enumerable.Empty(), null, It.IsAny())) + .Returns(expectedStartInfo); - // construct the command line arguments. - var commandLineArguments = new List { "--port", "0" }; - this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(null, commandLineArguments), Times.Once); + this.testOperationManager.SetupChannel(Enumerable.Empty()); + + this.mockTestHostManager.Verify(thl => thl.LaunchTestHost(It.Is(si => si == expectedStartInfo)), Times.Once); } [TestMethod] - public void InitializeShouldSetupServerForCommunication() + public void SetupChannelShouldSetupServerForCommunication() { - this.testOperationManager.Initialize(this.mockTestHostManager.Object); + this.testOperationManager.SetupChannel(Enumerable.Empty()); this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.Once); } - #region Testable Implementation + [TestMethod] + public void SetupChannelShouldNotInitializeIfConnectionIsAlreadyInitialized() + { + this.testOperationManager.SetupChannel(Enumerable.Empty()); + this.testOperationManager.SetupChannel(Enumerable.Empty()); - private class TestableProxyOperationManager : ProxyOperationManager + this.mockRequestSender.Verify(s => s.InitializeCommunication(), Times.Once); + } + + [TestMethod] + public void SetupChannelShouldWaitForTestHostConnection() { - public TestableProxyOperationManager() - : base() - { - } + this.testOperationManager.SetupChannel(Enumerable.Empty()); + + this.mockRequestSender.Verify(rs => rs.WaitForRequestHandlerConnection(this.connectionTimeout), Times.Once); + } + + [TestMethod] + public void SetupChannelShouldWaitForTestHostConnectionEvenIfConnectionIsInitialized() + { + this.testOperationManager.SetupChannel(Enumerable.Empty()); + this.testOperationManager.SetupChannel(Enumerable.Empty()); + + this.mockRequestSender.Verify(rs => rs.WaitForRequestHandlerConnection(this.connectionTimeout), Times.Exactly(2)); + } + + [TestMethod] + public void SetupChannelShouldThrowIfWaitForTestHostConnectionTimesOut() + { + this.mockRequestSender.Setup(rs => rs.WaitForRequestHandlerConnection(this.connectionTimeout)).Returns(false); + + Assert.ThrowsException(() => this.testOperationManager.SetupChannel(Enumerable.Empty())); + } + + [TestMethod] + public void CloseShouldEndSession() + { + this.testOperationManager.Close(); + + this.mockRequestSender.Verify(rs => rs.EndSession(), Times.Once); + } - internal TestableProxyOperationManager( + [TestMethod] + public void CloseShouldResetChannelInitialization() + { + this.mockRequestSender.Setup(rs => rs.WaitForRequestHandlerConnection(this.connectionTimeout)).Returns(true); + this.testOperationManager.SetupChannel(Enumerable.Empty()); + + this.testOperationManager.Close(); + + this.testOperationManager.SetupChannel(Enumerable.Empty()); + this.mockTestHostManager.Verify(th => th.LaunchTestHost(It.IsAny()), Times.Exactly(2)); + } + + private class TestableProxyOperationManager : ProxyOperationManager + { + public TestableProxyOperationManager( ITestRequestSender requestSender, - int clientConnectionTimeout) - : base(requestSender, null, clientConnectionTimeout) + ITestHostManager testHostManager, + int clientConnectionTimeout) : base(requestSender, testHostManager, clientConnectionTimeout) { } } - - #endregion } } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DefaultTestHostManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DefaultTestHostManagerTests.cs index 97eedbe0c1..f25e129f5d 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DefaultTestHostManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DefaultTestHostManagerTests.cs @@ -2,16 +2,16 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Hosting { - using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; - using System.Reflection; + using System.Linq; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -20,384 +20,100 @@ namespace TestPlatform.CrossPlatEngine.UnitTests.Hosting public class DefaultTestHostManagerTests { private DefaultTestHostManager testHostManager; + private readonly Mock mockProcessHelper; + private readonly TestProcessStartInfo startInfo; - /// - /// The mock process helper. - /// - /// Doing this only because mocks currently does not support internalVisibleTo on signed assemblies yet for .Net Core. - private MockProcessHelper mockProcessHelper; - - [TestInitialize] - public void TestInit() + public DefaultTestHostManagerTests() { - this.mockProcessHelper = new MockProcessHelper(); + this.mockProcessHelper = new Mock(); + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("vstest.console.exe"); + + this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper.Object); + this.startInfo = this.testHostManager.GetTestHostProcessStartInfo(Enumerable.Empty(), null, default(TestRunnerConnectionInfo)); } [TestMethod] public void ConstructorShouldSetX86ProcessForX86Architecture() { - this.testHostManager = new DefaultTestHostManager(Architecture.X86, Framework.DefaultFramework, this.mockProcessHelper); - - // Setup mocks. - var processPath = string.Empty; - var times = 0; - - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - times++; - processPath = path; - return Process.GetCurrentProcess(); - }; + this.testHostManager = new DefaultTestHostManager(Architecture.X86, Framework.DefaultFramework, this.mockProcessHelper.Object); - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); + var info = this.testHostManager.GetTestHostProcessStartInfo(Enumerable.Empty(), null, default(TestRunnerConnectionInfo)); - StringAssert.EndsWith(processPath, "testhost.x86.exe"); - Assert.AreEqual(1, times); + StringAssert.EndsWith(info.FileName, "testhost.x86.exe"); } [TestMethod] public void ConstructorShouldSetX64ProcessForX64Architecture() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - // Setup mocks. - var processPath = string.Empty; - var times = 0; - - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - times++; - processPath = path; - return Process.GetCurrentProcess(); - }; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - StringAssert.EndsWith(processPath, "testhost.exe"); - Assert.AreEqual(1, times); + StringAssert.EndsWith(this.startInfo.FileName, "testhost.exe"); } [TestMethod] - public void LaunchTestHostShouldLaunchProcessWithOneArgument() + public void GetTestHostProcessStartInfoShouldIncludeConnectionInfo() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - // Setup mocks. - var cliargs = string.Empty; - var times = 0; - - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - times++; - cliargs = args; - return Process.GetCurrentProcess(); - }; - - var arguments = new List { "-p" }; - this.testHostManager.LaunchTestHost(new Dictionary(), arguments); + var connectionInfo = new TestRunnerConnectionInfo { Port = 123 }; - var commandLineArgumentsString = string.Join(" ", arguments); + var info = this.testHostManager.GetTestHostProcessStartInfo(Enumerable.Empty(), null, connectionInfo); - Assert.AreEqual(commandLineArgumentsString, cliargs); - Assert.AreEqual(1, times); + Assert.AreEqual(" --port 123", info.Arguments); } [TestMethod] - public void LaunchTestHostShouldLaunchProcessWithMultipleArguments() + public void GetTestHostProcessStartInfoShouldIncludeEmptyEnvironmentVariables() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - // Setup mocks. - var cliargs = string.Empty; - var times = 0; - - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - times++; - cliargs = args; - return Process.GetCurrentProcess(); - }; - - var arguments = new List { "-p", "23453" }; - this.testHostManager.LaunchTestHost(new Dictionary(), arguments); - - var commandLineArgumentsString = string.Join(" ", arguments); - - Assert.AreEqual(commandLineArgumentsString, cliargs); - Assert.AreEqual(1, times); + Assert.AreEqual(0, this.startInfo.EnvironmentVariables.Count); } [TestMethod] - public void LaunchTestHostShouldLaunchProcessWithCurrentWorkingDirectory() + public void GetTestHostProcessStartInfoShouldIncludeEnvironmentVariables() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - // Setup mocks. - var pwd = string.Empty; - var times = 0; - - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - times++; - pwd = wd; - return Process.GetCurrentProcess(); - }; + var environmentVariables = new Dictionary { { "k1", "v1" } }; - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); + var info = this.testHostManager.GetTestHostProcessStartInfo(Enumerable.Empty(), environmentVariables, default(TestRunnerConnectionInfo)); - var workingDirectory = Directory.GetCurrentDirectory(); - - Assert.AreEqual(workingDirectory, pwd); - Assert.AreEqual(1, times); + Assert.AreEqual(environmentVariables, info.EnvironmentVariables); } [TestMethod] - public void LaunchTestHostShouldReturnTestHostProcessId() + public void GetTestHostProcessStartInfoShouldIncludeCurrentWorkingDirectory() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); ; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - return Process.GetCurrentProcess(); - }; - - var processID = this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - Assert.AreEqual(Process.GetCurrentProcess().Id, processID); + Assert.AreEqual(Directory.GetCurrentDirectory(), this.startInfo.WorkingDirectory); } [TestMethod] - public void LaunchTestHostShouldLaunchDotnetExeIfRunningUnderDotnetCLIContext() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - string processPath = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - processPath = path; - return Process.GetCurrentProcess(); - }; - var currentProcessPath = "c:\\temp\\dotnet.exe"; - this.mockProcessHelper.CurrentProcessName = currentProcessPath; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - Assert.AreEqual(currentProcessPath, processPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostAssemblyInArgumentsIfRunningUnderDotnetCLIContextInX86() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X86, Framework.DefaultFramework, this.mockProcessHelper); - - string arguments = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - arguments = args; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\dotnet.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath =Path.Combine(Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location),"testhost.dll"); - - StringAssert.Contains(arguments, testhostAssemblyPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostAssemblyInArgumentsIfRunningUnderDotnetCLIContextInX64() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - string arguments = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - arguments = args; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\dotnet.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath = Path.Combine(Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location), "testhost.dll"); - - StringAssert.Contains(arguments, testhostAssemblyPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostAssemblyInArgumentsIfFrameworkIsNETCoreApp() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.FromString(".NETCoreApp,Version=1.0"), this.mockProcessHelper); - - string arguments = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - arguments = args; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\vstest.console.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath = Path.Combine(Path.GetDirectoryName("c:\\temp\\vstest.console.exe"), "NetCore", "testhost.dll"); - - StringAssert.Contains(arguments, testhostAssemblyPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostAssemblyInArgumentsIfFrameworkIsNETStandard() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.FromString(".NETStandard,Version=1.0"), this.mockProcessHelper); - - string arguments = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - arguments = args; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\vstest.console.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath = Path.Combine(Path.GetDirectoryName("c:\\temp\\vstest.console.exe"), "NetCore", "testhost.dll"); - - StringAssert.Contains(arguments, testhostAssemblyPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostX86ExeInFileNameIfRunningUnderFullDotnetCLIContextInX86() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X86, Framework.DefaultFramework, this.mockProcessHelper); - - string filename = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - filename = path; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\vstest.console.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath = Path.Combine(Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location), "testhost.x86.exe"); - - StringAssert.Contains(filename, testhostAssemblyPath); - } - - [TestMethod] - public void LaunchTestHostShouldPassTestHostExeInFileNameIfRunningUnderFullDotnetCLIContextInX64() - { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - string filename = null; - - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - filename = path; - return Process.GetCurrentProcess(); - }; - this.mockProcessHelper.CurrentProcessName = "c:\\temp\\vstest.console.exe"; - - this.testHostManager.LaunchTestHost(new Dictionary(), new List()); - - var testhostAssemblyPath = Path.Combine(Path.GetDirectoryName(typeof(DefaultTestHostManager).GetTypeInfo().Assembly.Location), "testhost.exe"); - - StringAssert.Contains(filename, testhostAssemblyPath); - } - - [TestMethod] - public void GetTestHostProcessStartInfoShouldSetWorkingDirectoryAsParentProcess() + public void LaunchTestHostShouldReturnTestHostProcessId() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); + this.mockProcessHelper.Setup(ph => ph.LaunchProcess(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Process.GetCurrentProcess()); - TestProcessStartInfo testProcessStartInfo = this.testHostManager.GetTestHostProcessStartInfo(null, new List()); + var processId = this.testHostManager.LaunchTestHost(this.startInfo); - Assert.AreEqual(Directory.GetCurrentDirectory(), testProcessStartInfo.WorkingDirectory); + Assert.AreEqual(Process.GetCurrentProcess().Id, processId); } [TestMethod] - public void GetTestHostProcessStartInfoShouldSetWorkingDirectoryAsParentProcessIfRunningUnderDotnetCLIContext() + public void PropertiesShouldReturnEmptyDictionary() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - this.mockProcessHelper.CurrentProcessName = "dotnet.exe"; - - TestProcessStartInfo testProcessStartInfo = this.testHostManager.GetTestHostProcessStartInfo(null, new List()); - - Assert.AreEqual(Directory.GetCurrentDirectory(), testProcessStartInfo.WorkingDirectory); + Assert.AreEqual(0, this.testHostManager.Properties.Count); } [TestMethod] - public void PropertiesShouldReturnEmptyDictionary() + public void DefaultTestHostManagerShouldBeShared() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); - - Assert.AreEqual(0, this.testHostManager.Properties.Count); + Assert.IsTrue(this.testHostManager.Shared); } [TestMethod] public void LaunchTestHostShouldUseCustomHostIfSet() { - this.testHostManager = new DefaultTestHostManager(Architecture.X64, Framework.DefaultFramework, this.mockProcessHelper); var mockCustomLauncher = new Mock(); this.testHostManager.SetCustomLauncher(mockCustomLauncher.Object); + var currentProcess = Process.GetCurrentProcess(); + mockCustomLauncher.Setup(mc => mc.LaunchTestHost(It.IsAny())).Returns(currentProcess.Id); - var isProcessHelperCalled = false; - var processToReturn = Process.GetCurrentProcess(); - // Setup mocks. - this.mockProcessHelper.LaunchProcessInvoker = (path, args, wd) => - { - isProcessHelperCalled = true; - return processToReturn; - }; - - mockCustomLauncher.Setup(mc => mc.LaunchTestHost(It.IsAny())).Returns(processToReturn.Id); - - var processID = this.testHostManager.LaunchTestHost(new Dictionary(), new List()); + var pid = this.testHostManager.LaunchTestHost(this.startInfo); - Assert.IsFalse(isProcessHelperCalled, "ProcessHelper must not be called if custom launcher is set."); - mockCustomLauncher.Verify(mc => mc.LaunchTestHost(It.IsAny()), Times.Once, "Custom launcher must be called if set."); + mockCustomLauncher.Verify(mc => mc.LaunchTestHost(It.IsAny()), Times.Once); + Assert.AreEqual(currentProcess.Id, pid); } - - #region implementations - - private class MockProcessHelper : IProcessHelper - { - public MockProcessHelper() - { - this.CurrentProcessName = "testhost.exe"; - } - - public Func LaunchProcessInvoker { get; set; } - - public string CurrentProcessName { get; set; } - - public string GetCurrentProcessFileName() - { - return this.CurrentProcessName; - } - - public Process LaunchProcess(string processPath, string arguments, string workingDirectory) - { - return this.LaunchProcessInvoker?.Invoke(processPath, arguments, workingDirectory); - } - } - - #endregion } } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DotnetTestHostManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DotnetTestHostManagerTests.cs new file mode 100644 index 0000000000..9356084805 --- /dev/null +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Hosting/DotnetTestHostManagerTests.cs @@ -0,0 +1,285 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace TestPlatform.CrossPlatEngine.UnitTests.Hosting +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Security.Cryptography; + + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + [TestClass] + public class DotnetTestHostManagerTests + { + private const string DefaultDotnetPath = "c:\\tmp\\dotnet.exe"; + + private readonly Mock mockTestHostLauncher; + + private readonly TestableDotnetTestHostManager dotnetHostManager; + + private readonly Mock mockProcessHelper; + + private readonly Mock mockFileHelper; + + private readonly TestRunnerConnectionInfo defaultConnectionInfo; + + private readonly string[] testSource = { "test.dll" }; + + public DotnetTestHostManagerTests() + { + this.mockTestHostLauncher = new Mock(); + this.mockProcessHelper = new Mock(); + this.mockFileHelper = new Mock(); + this.defaultConnectionInfo = default(TestRunnerConnectionInfo); + this.dotnetHostManager = new TestableDotnetTestHostManager( + this.mockTestHostLauncher.Object, + this.mockProcessHelper.Object, + this.mockFileHelper.Object); + + // Setup a dummy current process for tests + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns(DefaultDotnetPath); + this.mockProcessHelper.Setup(ph => ph.GetTestEngineDirectory()).Returns(DefaultDotnetPath); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldThrowIfSourceIsNull() + { + Action action = () => this.dotnetHostManager.GetTestHostProcessStartInfo(null, null, this.defaultConnectionInfo); + + Assert.ThrowsException(action); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldThrowIfMultipleSourcesAreProvided() + { + var sources = new[] { "test1.dll", "test2.dll" }; + Action action = () => this.dotnetHostManager.GetTestHostProcessStartInfo(sources, null, this.defaultConnectionInfo); + + Assert.ThrowsException(action); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldInvokeDotnetCommandline() + { + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns(DefaultDotnetPath); + + var startInfo = this.GetDefaultStartInfo(); + + Assert.AreEqual(DefaultDotnetPath, startInfo.FileName); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldInvokeDotnetXPlatOnLinux() + { + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("/tmp/dotnet"); + + var startInfo = this.GetDefaultStartInfo(); + + Assert.AreEqual("/tmp/dotnet", startInfo.FileName); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldInvokeDotnetOnWindows() + { + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("c:\\tmp\\vstest.console.exe"); + + var startInfo = this.GetDefaultStartInfo(); + + Assert.AreEqual("dotnet", startInfo.FileName); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldInvokeDotnetExec() + { + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.StartsWith(startInfo.Arguments, "exec"); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldAddRuntimeConfigJsonIfExists() + { + this.mockFileHelper.Setup(fh => fh.Exists("test.runtimeconfig.json")).Returns(true); + + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.Arguments, "--runtimeconfig \"test.runtimeconfig.json\""); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldNotAddRuntimeConfigJsonIfNotExists() + { + this.mockFileHelper.Setup(fh => fh.Exists("test.runtimeconfig.json")).Returns(false); + + var startInfo = this.GetDefaultStartInfo(); + + Assert.IsFalse(startInfo.Arguments.Contains("--runtimeconfig \"test.runtimeconfig.json\"")); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldAddDepsFileJsonIfExists() + { + this.mockFileHelper.Setup(fh => fh.Exists("test.deps.json")).Returns(true); + + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.Arguments, "--depsfile \"test.deps.json\""); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldNotAddDepsFileJsonIfNotExists() + { + this.mockFileHelper.Setup(fh => fh.Exists("test.deps.json")).Returns(false); + + var startInfo = this.GetDefaultStartInfo(); + + Assert.IsFalse(startInfo.Arguments.Contains("--depsfile \"test.deps.json\"")); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldProvidePathToTestHostForDesktopTarget() + { + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("c:\\tmp\\vstest.console.exe"); + + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.Arguments, "c:\\tmp\\NetCore\\testhost.dll"); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldProvidePathToTestHostForNetCoreTarget() + { + this.mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("/tmp/dotnet"); + this.mockProcessHelper.Setup(ph => ph.GetTestEngineDirectory()).Returns("/tmp/vstest"); + + var startInfo = this.GetDefaultStartInfo(); + + // Path.GetDirectoryName returns platform specific path separator char + StringAssert.Contains(startInfo.Arguments, this.GetTesthostPath("/tmp/vstest")); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldIncludeConnectionInfo() + { + var connectionInfo = new TestRunnerConnectionInfo { Port = 123 }; + + var startInfo = this.dotnetHostManager.GetTestHostProcessStartInfo(this.testSource, null, connectionInfo); + + StringAssert.Contains(startInfo.Arguments, "--port " + connectionInfo.Port); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldIncludeEnvironmentVariables() + { + var environmentVariables = new Dictionary { { "k1", "v1" }, { "k2", "v2" } }; + + var startInfo = this.dotnetHostManager.GetTestHostProcessStartInfo(this.testSource, environmentVariables, this.defaultConnectionInfo); + + Assert.AreEqual(environmentVariables, startInfo.EnvironmentVariables); + } + + [TestMethod] + public void LaunchTestHostShouldLaunchProcessWithNullEnvironmentVariablesOrArgs() + { + this.mockTestHostLauncher.Setup(thl => thl.LaunchTestHost(It.IsAny())).Returns(111); + var startInfo = this.GetDefaultStartInfo(); + + var processId = this.dotnetHostManager.LaunchTestHost(startInfo); + + Assert.AreEqual(111, processId); + } + + [TestMethod] + public void LaunchTestHostShouldLaunchProcessWithConnectionInfo() + { + this.mockProcessHelper.Setup(ph => ph.GetTestEngineDirectory()).Returns("/tmp/vstest"); + var startInfo = this.GetDefaultStartInfo(); + var expectedArgs = "exec \"" + this.GetTesthostPath("/tmp/vstest") + "\" --port 0"; + + this.dotnetHostManager.LaunchTestHost(startInfo); + + this.mockTestHostLauncher.Verify(thl => thl.LaunchTestHost(It.Is(x => x.Arguments.Equals(expectedArgs))), Times.Once); + } + + [TestMethod] + public void LaunchTestHostShouldLaunchProcessWithEnvironmentVariables() + { + var variables = new Dictionary { { "k1", "v1" }, { "k2", "v2" } }; + var startInfo = new TestProcessStartInfo { EnvironmentVariables = variables }; + + this.dotnetHostManager.LaunchTestHost(startInfo); + + this.mockTestHostLauncher.Verify(thl => thl.LaunchTestHost(It.Is(x => x.EnvironmentVariables.Equals(variables))), Times.Once); + } + + [TestMethod] + public void DotnetTestHostManagedShouldNotBeShared() + { + Assert.IsFalse(this.dotnetHostManager.Shared); + } + + private string GetTesthostPath(string engineDirectory) + { + // testhost.dll will be picked up from the same path as vstest.console.dll. In the test, we are setting up + // the path to current assembly location. + return Path.Combine(engineDirectory, "testhost.dll"); + } + + private TestProcessStartInfo GetDefaultStartInfo() + { + var startInfo = this.dotnetHostManager.GetTestHostProcessStartInfo( + this.testSource, + null, + this.defaultConnectionInfo); + return startInfo; + } + } + + internal class TestableDotnetTestHostManager : DotnetTestHostManager + { + public TestableDotnetTestHostManager(ITestHostLauncher testHostLauncher, IProcessHelper processHelper, IFileHelper fileHelper) + : base(testHostLauncher, processHelper, fileHelper) + { + } + } + + [TestClass] + public class DefaultTestHostLauncherTests + { + [TestMethod] + public void DefaultTestHostLauncherIsDebugShouldBeFalse() + { + var hostLauncher = new DefaultTestHostLauncher(); + + Assert.IsFalse(hostLauncher.IsDebug); + } + + [TestMethod] + public void DefaultTestHostLauncherShouldStartTestProcess() + { + var startInfo = new TestProcessStartInfo { FileName = "testhost.exe", Arguments = "a1", WorkingDirectory = "w" }; + var currentProcess = Process.GetCurrentProcess(); + var mockProcessHelper = new Mock(); + mockProcessHelper.Setup(ph => ph.LaunchProcess(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(currentProcess); + var hostLauncher = new DefaultTestHostLauncher(mockProcessHelper.Object); + + var processId = hostLauncher.LaunchTestHost(startInfo); + + Assert.AreEqual(currentProcess.Id, processId); + mockProcessHelper.Verify(ph => ph.LaunchProcess("testhost.exe", "a1", "w"), Times.Once); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/TestEngineTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/TestEngineTests.cs index f05feba4dd..3851c15b81 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/TestEngineTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/TestEngineTests.cs @@ -7,68 +7,95 @@ namespace TestPlatform.CrossPlatEngine.UnitTests using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + [TestClass] public class TestEngineTests { - private ITestEngine testEngine; + private readonly ITestEngine testEngine; + + private Mock mockTestHostManager; public TestEngineTests() { this.testEngine = new TestEngine(); + this.mockTestHostManager = new Mock(); } [TestMethod] public void GetDiscoveryManagerShouldReturnANonNullInstance() { - Assert.IsNotNull(this.testEngine.GetDiscoveryManager()); + Assert.IsNotNull(this.testEngine.GetDiscoveryManager(this.mockTestHostManager.Object)); } + [TestMethod] + public void GetDiscoveryManagerShouldReturnCachedInstance() + { + var discoveryManager = this.testEngine.GetDiscoveryManager(this.mockTestHostManager.Object); + + Assert.AreSame(discoveryManager, this.testEngine.GetDiscoveryManager(this.mockTestHostManager.Object)); + } [TestMethod] public void GetExecutionManagerShouldReturnANonNullInstance() { - var testRunCriteria = new TestRunCriteria(new List() { "1.dll" }, 100); - Assert.IsNotNull(this.testEngine.GetExecutionManager(testRunCriteria)); + var testRunCriteria = new TestRunCriteria(new List { "1.dll" }, 100); + + Assert.IsNotNull(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria)); + } + + [TestMethod] + public void GetExecutionManagerShouldReturnCachedInstance() + { + var testRunCriteria = new TestRunCriteria(new List { "1.dll" }, 100); + var executionManager = this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria); + + Assert.AreSame(executionManager, this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria)); } [TestMethod] public void GetExecutionManagerShouldReturnDefaultExecutionManagerIfParallelDisabled() { string settingXml = @""; - var testRunCriteria = new TestRunCriteria(new List() { "1.dll" }, 100, false, settingXml); - Assert.IsNotNull(this.testEngine.GetExecutionManager(testRunCriteria)); - Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(testRunCriteria), typeof(ProxyExecutionManager)); + var testRunCriteria = new TestRunCriteria(new List { "1.dll" }, 100, false, settingXml); + + Assert.IsNotNull(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria)); + Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria), typeof(ProxyExecutionManager)); } [TestMethod] public void GetExecutionManagerWithSingleSourceShouldReturnDefaultExecutionManagerEvenIfParallelEnabled() { string settingXml = @"2"; - var testRunCriteria = new TestRunCriteria(new List() { "1.dll" }, 100, false, settingXml); - Assert.IsNotNull(this.testEngine.GetExecutionManager(testRunCriteria)); - Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(testRunCriteria), typeof(ProxyExecutionManager)); + var testRunCriteria = new TestRunCriteria(new List { "1.dll" }, 100, false, settingXml); + + Assert.IsNotNull(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria)); + Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria), typeof(ProxyExecutionManager)); } [TestMethod] public void GetExecutionManagerShouldReturnParallelExecutionManagerIfParallelEnabled() { string settingXml = @"2"; - var testRunCriteria = new TestRunCriteria(new List() { "1.dll", "2.dll" }, 100, false, settingXml); - Assert.IsNotNull(this.testEngine.GetExecutionManager(testRunCriteria)); - Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(testRunCriteria), typeof(ParallelProxyExecutionManager)); + var testRunCriteria = new TestRunCriteria(new List { "1.dll", "2.dll" }, 100, false, settingXml); + + Assert.IsNotNull(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria)); + Assert.IsInstanceOfType(this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria), typeof(ParallelProxyExecutionManager)); } [TestMethod] public void GetExcecutionManagerShouldReturnExectuionManagerWithDataCollectionIfDataCollectionIsEnabled() { var settingXml = @""; - var testRunCriteria = new TestRunCriteria(new List() { "1.dll" }, 100, false, settingXml); - var result = this.testEngine.GetExecutionManager(testRunCriteria); + var testRunCriteria = new TestRunCriteria(new List { "1.dll" }, 100, false, settingXml); + var result = this.testEngine.GetExecutionManager(this.mockTestHostManager.Object, testRunCriteria); + Assert.IsNotNull(result); Assert.IsInstanceOfType(result, typeof(ProxyExecutionManagerWithDataCollection)); } @@ -79,7 +106,6 @@ public void GetExtensionManagerShouldReturnANonNullInstance() Assert.IsNotNull(this.testEngine.GetExtensionManager()); } - [TestMethod] public void GetDefaultTestHostManagerReturnsANonNullInstance() { @@ -91,7 +117,18 @@ public void GetDefaultTestHostManagerReturnsANewInstanceEverytime() { var instance1 = this.testEngine.GetDefaultTestHostManager(Architecture.X86, Framework.DefaultFramework); var instance2 = this.testEngine.GetDefaultTestHostManager(Architecture.X86, Framework.DefaultFramework); + Assert.AreNotEqual(instance1, instance2); } + + [TestMethod] + public void GetDefaultTestHostManagerReturnsDotnetCoreHostManagerIfFrameworkIsNetCore() + { + var testHostManager = this.testEngine.GetDefaultTestHostManager( + Architecture.X64, + Framework.FromString(".NETCoreApp,Version=v1.0")); + + Assert.AreEqual(typeof(DotnetTestHostManager), testHostManager.GetType()); + } } } diff --git a/test/Microsoft.TestPlatform.ObjectModel.UnitTests/FrameworkTests.cs b/test/Microsoft.TestPlatform.ObjectModel.UnitTests/FrameworkTests.cs index 53de8a4673..a9b39259fc 100644 --- a/test/Microsoft.TestPlatform.ObjectModel.UnitTests/FrameworkTests.cs +++ b/test/Microsoft.TestPlatform.ObjectModel.UnitTests/FrameworkTests.cs @@ -18,5 +18,21 @@ public void FrameworkFromStringShouldTrimSpacesAroundFrameworkString() Assert.AreEqual(".NETFramework,Version=v3.5", fx.Name); Assert.AreEqual("3.5", fx.Version); } + + [TestMethod] + public void DefaultFrameworkShouldBeNet46OnDesktop() + { +#if NET46 + Assert.AreEqual(".NETFramework,Version=v4.6", Framework.DefaultFramework.Name); +#endif + } + + [TestMethod] + public void DefaultFrameworkShouldBeNetCoreApp10OnNonDesktop() + { +#if !NET46 + Assert.AreEqual(".NETCoreApp,Version=v1.0", Framework.DefaultFramework.Name); +#endif + } } } diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs b/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs index fa09a9e1d3..e788b82659 100644 --- a/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs +++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs @@ -80,7 +80,7 @@ public void UpdateRunSettingsShouldUpdateWithFrameworkSettings() var xml = navigator.OuterXml; - StringAssert.Contains(xml, ".NETFramework,Version=v4.6"); + StringAssert.Contains(xml, $"{Framework.DefaultFramework.Name}"); } [TestMethod] @@ -161,7 +161,7 @@ public void UpdateRunSettingsWithAnEmptyRunSettingsShouldAddValuesSpecifiedInRun var xml = navigator.OuterXml; StringAssert.Contains(xml, "X64"); - StringAssert.Contains(xml, ".NETFramework,Version=v4.6"); + StringAssert.Contains(xml, $"{Framework.DefaultFramework.Name}"); StringAssert.Contains(xml, "temp"); } @@ -174,7 +174,7 @@ public void UpdateRunSettingsShouldReturnBackACompleteRunSettings() InferRunSettingsHelper.UpdateRunSettingsWithUserProvidedSwitches(navigator, Architecture.X64, Framework.DefaultFramework, "temp"); var xml = navigator.OuterXml; - var expectedRunSettings = "\r\n \r\n temp\r\n X64\r\n .NETFramework,Version=v4.6\r\n \r\n"; + var expectedRunSettings = string.Format("\r\n \r\n temp\r\n X64\r\n {0}\r\n \r\n", Framework.DefaultFramework.Name); Assert.AreEqual(expectedRunSettings, xml); } diff --git a/test/TestAssets/CompatTestExtension/Properties/AssemblyInfo.cs b/test/TestAssets/CompatTestExtension/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0c180bacb3 --- /dev/null +++ b/test/TestAssets/CompatTestExtension/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CompatTestExtension")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CompatTestExtension")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("37c76c3d-947e-48a6-af68-2d7c9ec35f6f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/TestAssets/CompatTestExtension/UnitTest1.cs b/test/TestAssets/CompatTestExtension/UnitTest1.cs new file mode 100644 index 0000000000..8a763b1e02 --- /dev/null +++ b/test/TestAssets/CompatTestExtension/UnitTest1.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace CompatTestExtension +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Reflection; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + + /// + /// Test discoverer built with older ObjectModel assemblies for compatibility testing. + /// + public class TestDiscoverer : ITestDiscoverer + { + /// + public void DiscoverTests( + IEnumerable sources, + IDiscoveryContext discoveryContext, + IMessageLogger logger, + ITestCaseDiscoverySink discoverySink) + { + var sourceList = sources.ToList(); + foreach (var source in sourceList) + { + Debug.Assert(File.Exists(source), $"Source path doesn't exist: {source}"); + } + + // TODO Add validation for runsettings + logger.SendMessage(TestMessageLevel.Informational, "Start discovery"); + logger.SendMessage(TestMessageLevel.Informational, $"COMPATEXTENSION:{discoveryContext.RunSettings.SettingsXml}"); + + // Create a few dummy tests + var test = new TestCase( + "Assembly.Class.Method", + new Uri("discoverer://compatTestDiscoverer"), + "/tmp/sampleTestFile.dll"); + test.DisplayName = "Sample test case"; + test.CodeFilePath = "/tmp/sampleTestFile.cs"; + } + } +} diff --git a/test/TestAssets/CompatTestProject/Properties/AssemblyInfo.cs b/test/TestAssets/CompatTestProject/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..66b0fac7e5 --- /dev/null +++ b/test/TestAssets/CompatTestProject/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SampleUnitTestProject")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SampleUnitTestProject")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("37c76c3d-947e-48a6-af68-2d7c9ec35f6f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/TestAssets/CompatTestProject/UnitTest1.cs b/test/TestAssets/CompatTestProject/UnitTest1.cs new file mode 100644 index 0000000000..41f772c5d3 --- /dev/null +++ b/test/TestAssets/CompatTestProject/UnitTest1.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace SampleUnitTestProject +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// The unit test 1. + /// + [TestClass] + public class UnitTest1 + { + /// + /// The passing test. + /// + [TestMethod] + public void PassingTest() + { + Assert.AreEqual(2, 2); + } + + /// + /// The failing test. + /// + [TestMethod] + public void FailingTest() + { + Assert.AreEqual(2, 3); + } + + /// + /// The skipping test. + /// + [Ignore] + [TestMethod] + public void SkippingTest() + { + } + } +} diff --git a/test/vstest.console.UnitTests/Processors/Utilities/RunSettingsUtilitiesTests.cs b/test/vstest.console.UnitTests/Processors/Utilities/RunSettingsUtilitiesTests.cs index 24b81ddb60..e82faed11f 100644 --- a/test/vstest.console.UnitTests/Processors/Utilities/RunSettingsUtilitiesTests.cs +++ b/test/vstest.console.UnitTests/Processors/Utilities/RunSettingsUtilitiesTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors.U public class RunSettingsUtilitiesTests { private const string DefaultRunSettingsTemplate = - "\r\n \r\n %ResultsDirectory%\r\n X86\r\n .NETFramework,Version=v4.6\r\n \r\n"; + "\r\n \r\n %ResultsDirectory%\r\n X86\r\n %DefaultFramework%\r\n \r\n"; [TestCleanup] public void TestCleanup() @@ -84,7 +84,7 @@ public void GetRunSettingsShouldReturnSettingsWithFrameworkSpecifiedInCommandLin var defaultRunSettings = this.GetDefaultRunSettings(); //Replace with the framework specified. - var expectedSettings = defaultRunSettings.Replace("Version=v4.6", "Version=v3.5"); + var expectedSettings = defaultRunSettings.Replace(Framework.DefaultFramework.Name, ".NETFramework,Version=v3.5"); Assert.AreEqual(expectedSettings, runSettings); } @@ -136,7 +136,7 @@ public void GetRunSettingsShouldReturnWithoutChangeIfUserProvidesBothParallelSwi private string GetDefaultRunSettings() { var defaultResultsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "TestResults"); - return DefaultRunSettingsTemplate.Replace("%ResultsDirectory%", defaultResultsDirectory); + return DefaultRunSettingsTemplate.Replace("%ResultsDirectory%", defaultResultsDirectory).Replace("%DefaultFramework%", Framework.DefaultFramework.Name); } private class TestableRunSettingsProvider : IRunSettingsProvider