mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 09:59:48 -05:00
This is another large code review. I want to make a few more changes, but since these changes will stand on their own, I'll hold back on making this change set any larger than it already is. Changes in this request: To address Microsoft/vscode-mssql#326, instead of doing taskkill on the service layer when WaitForExit is executed, we now make an educated guess at which service layer was spawned when the test starts and do a Process.Kill on it when we shut down the test. All the perf tests have been moved into a new project. This was done to keep them easily separated from code coverage test runs. At the same time the perf tests were separated into separate classes for logical categorization. This process will likely be repeated on the stress tests. The tests can still easily be ran from Visual Studio Test Explorer To address Microsoft/vscode-mssql#349, a new SelfCleaningFile class was created to allow for easy cleanup of temporary files generated for integration tests via using blocks. Due to some of the refactoring done while moving the perf tests to a new project, the TestBase class had to be switched to more of a helper class style. As such, all tests that use inherit from TestBase now create a TestBase object on start via a using block. This also simplifies the cleanup at the end of the test. * Solution for hanging code coverage runs Code coverage runs would hang in certain scenarios if a test failed before the service process could be spawned. The taskkill command would fail to find the service process. The test would then wait for opencover to exit, but it would not since the service process it had spawned would still be running, causing the test run to hang indefinitely. Solution was to capture the service process after it launched and explicitly kill it when shutting down the test driver. * Setting the test name in the propery in the class and removign the parameter from each method * New project for perf tests * Reworking integration tests to cleanup temp files * Changes as per @llali review comments * Adding copyright notices * Renaming TestBase => TestHelper * Renaming SelfCleaningFile => SelfCleaningTempFile * Removing code that sets TestName property * Fixing compilation error due to removed code
167 lines
6.7 KiB
C#
167 lines
6.7 KiB
C#
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
//
|
|
|
|
//
|
|
// The following is based upon code from PowerShell Editor Services
|
|
// License: https://github.com/PowerShell/PowerShellEditorServices/blob/develop/LICENSE
|
|
//
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
|
{
|
|
/// <summary>
|
|
/// Test driver for the service host
|
|
/// </summary>
|
|
public class ServiceTestDriver : TestDriverBase
|
|
{
|
|
|
|
public const string ServiceCodeCoverageEnvironmentVariable = "SERVICECODECOVERAGE";
|
|
|
|
public const string CodeCoverageToolEnvironmentVariable = "CODECOVERAGETOOL";
|
|
|
|
public const string CodeCoverageOutputEnvironmentVariable = "CODECOVERAGEOUTPUT";
|
|
|
|
public const string ServiceHostEnvironmentVariable = "SQLTOOLSSERVICE_EXE";
|
|
|
|
public bool IsCoverageRun { get; set; }
|
|
|
|
private Process[] serviceProcesses;
|
|
|
|
private DateTime startTime;
|
|
|
|
public ServiceTestDriver()
|
|
{
|
|
string serviceHostExecutable = Environment.GetEnvironmentVariable(ServiceHostEnvironmentVariable);
|
|
string serviceHostArguments = "--enable-logging";
|
|
if (string.IsNullOrWhiteSpace(serviceHostExecutable))
|
|
{
|
|
// Include a fallback value to for running tests within visual studio
|
|
serviceHostExecutable =
|
|
@"..\..\src\Microsoft.SqlTools.ServiceLayer\bin\Debug\netcoreapp1.0\win7-x64\Microsoft.SqlTools.ServiceLayer.exe";
|
|
}
|
|
|
|
// Make sure it exists before continuing
|
|
if (!File.Exists(serviceHostExecutable))
|
|
{
|
|
throw new FileNotFoundException($"Failed to find Microsoft.SqlTools.ServiceLayer.exe at provided location '{serviceHostExecutable}'. " +
|
|
"Please set SQLTOOLSERVICE_EXE environment variable to location of exe");
|
|
}
|
|
|
|
//setup the service host for code coverage if the envvar is enabled
|
|
if (Environment.GetEnvironmentVariable(ServiceCodeCoverageEnvironmentVariable) == "True")
|
|
{
|
|
string coverageToolPath = Environment.GetEnvironmentVariable(CodeCoverageToolEnvironmentVariable);
|
|
if (!string.IsNullOrWhiteSpace(coverageToolPath))
|
|
{
|
|
string serviceHostDirectory = Path.GetDirectoryName(serviceHostExecutable);
|
|
if (string.IsNullOrWhiteSpace(serviceHostDirectory))
|
|
{
|
|
serviceHostDirectory = ".";
|
|
}
|
|
|
|
string coverageOutput = Environment.GetEnvironmentVariable(CodeCoverageOutputEnvironmentVariable);
|
|
if (string.IsNullOrWhiteSpace(coverageOutput))
|
|
{
|
|
coverageOutput = "coverage.xml";
|
|
}
|
|
|
|
serviceHostArguments = $"-mergeoutput -target:{serviceHostExecutable} -targetargs:{serviceHostArguments} " +
|
|
$"-register:user -oldstyle -filter:\"+[Microsoft.SqlTools.*]* -[xunit*]*\" -output:{coverageOutput} " +
|
|
$"-searchdirs:{serviceHostDirectory};";
|
|
serviceHostExecutable = coverageToolPath;
|
|
|
|
this.IsCoverageRun = true;
|
|
}
|
|
}
|
|
|
|
this.clientChannel = new StdioClientChannel(serviceHostExecutable, serviceHostArguments);
|
|
this.protocolClient = new ProtocolEndpoint(clientChannel, MessageProtocolType.LanguageServer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the test driver, and launch the sqltoolsservice executable
|
|
/// </summary>
|
|
public async Task Start()
|
|
{
|
|
// Store the time we started
|
|
startTime = DateTime.Now;
|
|
|
|
// Launch the process
|
|
await this.protocolClient.Start();
|
|
await Task.Delay(1000); // Wait for the service host to start
|
|
|
|
// If this is a code coverage run, we need access to the service layer separate from open cover
|
|
if (IsCoverageRun)
|
|
{
|
|
CancellationTokenSource cancelSource = new CancellationTokenSource();
|
|
Task getServiceProcess = GetServiceProcess(cancelSource.Token);
|
|
Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(15), cancelSource.Token);
|
|
if (await Task.WhenAny(getServiceProcess, timeoutTask) == timeoutTask)
|
|
{
|
|
cancelSource.Cancel();
|
|
throw new Exception("Failed to capture service process");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("Successfully launched service");
|
|
|
|
// Setup events to queue for testing
|
|
this.QueueEventsForType(ConnectionCompleteNotification.Type);
|
|
this.QueueEventsForType(IntelliSenseReadyNotification.Type);
|
|
this.QueueEventsForType(QueryExecuteCompleteEvent.Type);
|
|
this.QueueEventsForType(PublishDiagnosticsNotification.Type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop the test driver, and shutdown the sqltoolsservice executable
|
|
/// </summary>
|
|
public async Task Stop()
|
|
{
|
|
if (IsCoverageRun)
|
|
{
|
|
// Kill all the processes in the list
|
|
foreach (Process p in serviceProcesses.Where(p => !p.HasExited))
|
|
{
|
|
p.Kill();
|
|
}
|
|
ServiceProcess?.WaitForExit();
|
|
}
|
|
else
|
|
{
|
|
await this.protocolClient.Stop();
|
|
}
|
|
}
|
|
|
|
private async Task GetServiceProcess(CancellationToken token)
|
|
{
|
|
while (serviceProcesses == null && !token.IsCancellationRequested)
|
|
{
|
|
var processes = Process.GetProcessesByName("Microsoft.SqlTools.ServiceLayer")
|
|
.Where(p => p.StartTime >= startTime).ToArray();
|
|
|
|
// Wait a second if we can't find the process
|
|
if (processes.Any())
|
|
{
|
|
serviceProcesses = processes;
|
|
}
|
|
else
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |