mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 17:23:27 -05:00
- TSqlFormatterService with support for formatting document and text range inside document - Settings support for all formatting options. - Extensibility support so that the service can be initialized using MEF extensibility, and can find all necessary TSqlFormatters using the same process Fix Initialize request error on startup - Messages were being read from the input channel before all request handlers were registered - In particular, the Initialize request which is key for any server to talk to the client was getting lost because the message reader thread begins consuming, and we take an extra few hundred milliseconds due to MEF startup before we register the handler - The solution is to initialize the message handler so request handlers can register, but not actually start processing incoming messages until all handers are ready. This is a safer way to go and should improve reliability overall Improvements from internal prototype: - Normalizing baselines to handle the line ending differences on Mac & Linux vs. Windows - Significantly shortened most lines by implementing base class methods to wrap common objects from Visitor.Context and removing unnecessary "this." syntax - Refactored the SqlCommonTableExpressionFormatter and related classes to reduce code count significantly. This provides a pattern to follow when refactoring other classes for similar clarity. It's likely a lot of common logic could be found and reused across these. - Reduced overall code size by adding utility methods
168 lines
6.8 KiB
C#
168 lines
6.8 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.ExecuteRequests;
|
|
|
|
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 SQLTOOLSSERVICE_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
|
|
this.protocolClient.Initialize();
|
|
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(QueryCompleteEvent.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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |