mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Added test driver program for service host (#113)
* Added test driver program for service host * Fix typo
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Test driver for the service host
|
||||||
|
/// </summary>
|
||||||
|
public class ServiceTestDriver : TestDriverBase
|
||||||
|
{
|
||||||
|
public ServiceTestDriver(string serviceHostExecutable)
|
||||||
|
{
|
||||||
|
var clientChannel = new StdioClientChannel(serviceHostExecutable);
|
||||||
|
this.protocolClient = new ProtocolEndpoint(clientChannel, MessageProtocolType.LanguageServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Start()
|
||||||
|
{
|
||||||
|
await this.protocolClient.Start();
|
||||||
|
await Task.Delay(1000); // Wait for the service host to start
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Stop()
|
||||||
|
{
|
||||||
|
await this.protocolClient.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
//
|
||||||
|
// 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.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps the ProtocolEndpoint class with queues to handle events/requests
|
||||||
|
/// </summary>
|
||||||
|
public class TestDriverBase
|
||||||
|
{
|
||||||
|
protected ProtocolEndpoint protocolClient;
|
||||||
|
|
||||||
|
private ConcurrentDictionary<string, AsyncQueue<object>> eventQueuePerType =
|
||||||
|
new ConcurrentDictionary<string, AsyncQueue<object>>();
|
||||||
|
|
||||||
|
private ConcurrentDictionary<string, AsyncQueue<object>> requestQueuePerType =
|
||||||
|
new ConcurrentDictionary<string, AsyncQueue<object>>();
|
||||||
|
|
||||||
|
public Task<TResult> SendRequest<TParams, TResult>(
|
||||||
|
RequestType<TParams, TResult> requestType,
|
||||||
|
TParams requestParams)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
this.protocolClient.SendRequest(
|
||||||
|
requestType,
|
||||||
|
requestParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
this.protocolClient.SendEvent(
|
||||||
|
eventType,
|
||||||
|
eventParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueEventsForType<TParams>(EventType<TParams> eventType)
|
||||||
|
{
|
||||||
|
var eventQueue =
|
||||||
|
this.eventQueuePerType.AddOrUpdate(
|
||||||
|
eventType.MethodName,
|
||||||
|
new AsyncQueue<object>(),
|
||||||
|
(key, queue) => queue);
|
||||||
|
|
||||||
|
this.protocolClient.SetEventHandler(
|
||||||
|
eventType,
|
||||||
|
(p, ctx) =>
|
||||||
|
{
|
||||||
|
return eventQueue.EnqueueAsync(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TParams> WaitForEvent<TParams>(
|
||||||
|
EventType<TParams> eventType,
|
||||||
|
int timeoutMilliseconds = 5000)
|
||||||
|
{
|
||||||
|
Task<TParams> eventTask = null;
|
||||||
|
|
||||||
|
// Use the event queue if one has been registered
|
||||||
|
AsyncQueue<object> eventQueue = null;
|
||||||
|
if (this.eventQueuePerType.TryGetValue(eventType.MethodName, out eventQueue))
|
||||||
|
{
|
||||||
|
eventTask =
|
||||||
|
eventQueue
|
||||||
|
.DequeueAsync()
|
||||||
|
.ContinueWith<TParams>(
|
||||||
|
task => (TParams)task.Result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TaskCompletionSource<TParams> eventTaskSource = new TaskCompletionSource<TParams>();
|
||||||
|
|
||||||
|
this.protocolClient.SetEventHandler(
|
||||||
|
eventType,
|
||||||
|
(p, ctx) =>
|
||||||
|
{
|
||||||
|
if (!eventTaskSource.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
eventTaskSource.SetResult(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
},
|
||||||
|
true); // Override any existing handler
|
||||||
|
|
||||||
|
eventTask = eventTaskSource.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
await
|
||||||
|
Task.WhenAny(
|
||||||
|
eventTask,
|
||||||
|
Task.Delay(timeoutMilliseconds));
|
||||||
|
|
||||||
|
if (!eventTask.IsCompleted)
|
||||||
|
{
|
||||||
|
throw new TimeoutException(
|
||||||
|
string.Format(
|
||||||
|
"Timed out waiting for '{0}' event!",
|
||||||
|
eventType.MethodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await eventTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Tuple<TParams, RequestContext<TResponse>>> WaitForRequest<TParams, TResponse>(
|
||||||
|
RequestType<TParams, TResponse> requestType,
|
||||||
|
int timeoutMilliseconds = 5000)
|
||||||
|
{
|
||||||
|
Task<Tuple<TParams, RequestContext<TResponse>>> requestTask = null;
|
||||||
|
|
||||||
|
// Use the request queue if one has been registered
|
||||||
|
AsyncQueue<object> requestQueue = null;
|
||||||
|
if (this.requestQueuePerType.TryGetValue(requestType.MethodName, out requestQueue))
|
||||||
|
{
|
||||||
|
requestTask =
|
||||||
|
requestQueue
|
||||||
|
.DequeueAsync()
|
||||||
|
.ContinueWith(
|
||||||
|
task => (Tuple<TParams, RequestContext<TResponse>>)task.Result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var requestTaskSource =
|
||||||
|
new TaskCompletionSource<Tuple<TParams, RequestContext<TResponse>>>();
|
||||||
|
|
||||||
|
this.protocolClient.SetRequestHandler(
|
||||||
|
requestType,
|
||||||
|
(p, ctx) =>
|
||||||
|
{
|
||||||
|
if (!requestTaskSource.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
requestTaskSource.SetResult(
|
||||||
|
new Tuple<TParams, RequestContext<TResponse>>(p, ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
requestTask = requestTaskSource.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
await
|
||||||
|
Task.WhenAny(
|
||||||
|
requestTask,
|
||||||
|
Task.Delay(timeoutMilliseconds));
|
||||||
|
|
||||||
|
if (!requestTask.IsCompleted)
|
||||||
|
{
|
||||||
|
throw new TimeoutException(
|
||||||
|
string.Format(
|
||||||
|
"Timed out waiting for '{0}' request!",
|
||||||
|
requestType.MethodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await requestTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
66
test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs
Normal file
66
test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.TestDriver
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
internal static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine( "Microsoft.SqlTools.ServiceLayer.TestDriver.exe <service host executable> [tests]" + Environment.NewLine +
|
||||||
|
" <service host executable> is the path to the Microsoft.SqlTools.ServiceLayer.exe executable" + Environment.NewLine +
|
||||||
|
" [tests] is a space-separated list of tests to run." + Environment.NewLine +
|
||||||
|
" They are qualified within the Microsoft.SqlTools.ServiceLayer.TestDriver.Tests namespace");
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var serviceHostExecutable = args[0];
|
||||||
|
var tests = args.Skip(1);
|
||||||
|
|
||||||
|
foreach (var test in tests)
|
||||||
|
{
|
||||||
|
ServiceTestDriver driver = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
driver = new ServiceTestDriver(serviceHostExecutable);
|
||||||
|
|
||||||
|
var className = test.Substring(0, test.LastIndexOf('.'));
|
||||||
|
var methodName = test.Substring(test.LastIndexOf('.') + 1);
|
||||||
|
|
||||||
|
var type = Type.GetType("Microsoft.SqlTools.ServiceLayer.TestDriver.Tests." + className);
|
||||||
|
var typeInstance = Activator.CreateInstance(type);
|
||||||
|
MethodInfo methodInfo = type.GetMethod(methodName);
|
||||||
|
|
||||||
|
await driver.Start();
|
||||||
|
Console.WriteLine("Running test " + test);
|
||||||
|
await (Task)methodInfo.Invoke(typeInstance, new object[] {driver});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (driver != null)
|
||||||
|
{
|
||||||
|
await driver.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests
|
||||||
|
{
|
||||||
|
public class ExampleTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Example test that performs a connect, then disconnect.
|
||||||
|
/// All tests must have the same signature of returning an async Task
|
||||||
|
/// and taking in a ServiceTestDriver as a parameter.
|
||||||
|
/// </summary>
|
||||||
|
public async Task ConnectDisconnectTest(ServiceTestDriver driver)
|
||||||
|
{
|
||||||
|
var connectParams = new ConnectParams();
|
||||||
|
connectParams.OwnerUri = "file";
|
||||||
|
connectParams.Connection = new ConnectionDetails();
|
||||||
|
connectParams.Connection.ServerName = "localhost";
|
||||||
|
connectParams.Connection.AuthenticationType = "Integrated";
|
||||||
|
|
||||||
|
var result = await driver.SendRequest(ConnectionRequest.Type, connectParams);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
await driver.WaitForEvent(ConnectionCompleteNotification.Type);
|
||||||
|
|
||||||
|
var disconnectParams = new DisconnectParams();
|
||||||
|
disconnectParams.OwnerUri = "file";
|
||||||
|
var result2 = await driver.SendRequest(DisconnectRequest.Type, disconnectParams);
|
||||||
|
if (result2)
|
||||||
|
{
|
||||||
|
Console.WriteLine("success");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json
Normal file
29
test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "Microsoft.SqlTools.ServiceLayer.TestDriver",
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"buildOptions": {
|
||||||
|
"debugType": "portable",
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.SqlTools.ServiceLayer": {
|
||||||
|
"target": "project"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"dotnet5.4",
|
||||||
|
"portable-net451+win8"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
"win7-x64": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user