mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Feature/connect cancel (#74)
* Implemented connection cancellation * Made connect requests return immediately and created a separate connection complete notification * Fix spelling * Fix sorting * Add separate lock for cancellation source map
This commit is contained in:
@@ -4,10 +4,12 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
@@ -47,6 +49,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
|
||||
private Dictionary<string, ConnectionInfo> ownerToConnectionMap = new Dictionary<string, ConnectionInfo>();
|
||||
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> ownerToCancellationTokenSourceMap = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
|
||||
private Object cancellationTokenSourceLock = new Object();
|
||||
|
||||
/// <summary>
|
||||
/// Map from script URIs to ConnectionInfo objects
|
||||
/// This is internal for testing access only
|
||||
@@ -131,21 +137,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
/// Open a connection with the specified connection details
|
||||
/// </summary>
|
||||
/// <param name="connectionParams"></param>
|
||||
public ConnectResponse Connect(ConnectParams connectionParams)
|
||||
public async Task<ConnectionCompleteParams> Connect(ConnectParams connectionParams)
|
||||
{
|
||||
// Validate parameters
|
||||
string paramValidationErrorMessage;
|
||||
if (connectionParams == null)
|
||||
{
|
||||
return new ConnectResponse
|
||||
return new ConnectionCompleteParams
|
||||
{
|
||||
Messages = SR.ConnectionServiceConnectErrorNullParams
|
||||
};
|
||||
}
|
||||
if (!connectionParams.IsValid(out paramValidationErrorMessage))
|
||||
{
|
||||
return new ConnectResponse
|
||||
return new ConnectionCompleteParams
|
||||
{
|
||||
OwnerUri = connectionParams.OwnerUri,
|
||||
Messages = paramValidationErrorMessage
|
||||
};
|
||||
}
|
||||
@@ -164,7 +171,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);
|
||||
|
||||
// try to connect
|
||||
var response = new ConnectResponse();
|
||||
var response = new ConnectionCompleteParams();
|
||||
response.OwnerUri = connectionParams.OwnerUri;
|
||||
CancellationTokenSource source = null;
|
||||
try
|
||||
{
|
||||
// build the connection string from the input parameters
|
||||
@@ -177,7 +186,36 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
// we'll remove this once ConnectionService is refactored to not own the LanguageService connection
|
||||
connectionInfo.ConnectionDetails.MultipleActiveResultSets = true;
|
||||
|
||||
connectionInfo.SqlConnection.Open();
|
||||
// Add a cancellation token source so that the connection OpenAsync() can be cancelled
|
||||
using (source = new CancellationTokenSource())
|
||||
{
|
||||
// Locking here to perform two operations as one atomic operation
|
||||
lock (cancellationTokenSourceLock)
|
||||
{
|
||||
// If the URI is currently connecting from a different request, cancel it before we try to connect
|
||||
CancellationTokenSource currentSource;
|
||||
if (ownerToCancellationTokenSourceMap.TryGetValue(connectionParams.OwnerUri, out currentSource))
|
||||
{
|
||||
currentSource.Cancel();
|
||||
}
|
||||
ownerToCancellationTokenSourceMap[connectionParams.OwnerUri] = source;
|
||||
}
|
||||
|
||||
// Create a task to handle cancellation requests
|
||||
var cancellationTask = Task.Run(() =>
|
||||
{
|
||||
source.Token.WaitHandle.WaitOne();
|
||||
source.Token.ThrowIfCancellationRequested();
|
||||
});
|
||||
|
||||
var openTask = Task.Run(async () => {
|
||||
await connectionInfo.SqlConnection.OpenAsync(source.Token);
|
||||
});
|
||||
|
||||
// Open the connection
|
||||
await Task.WhenAny(openTask, cancellationTask).Unwrap();
|
||||
source.Cancel();
|
||||
}
|
||||
}
|
||||
catch (SqlException ex)
|
||||
{
|
||||
@@ -186,12 +224,32 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
response.Messages = ex.ToString();
|
||||
return response;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// OpenAsync was cancelled
|
||||
response.Messages = SR.ConnectionServiceConnectionCanceled;
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.ErrorMessage = ex.Message;
|
||||
response.Messages = ex.ToString();
|
||||
return response;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Remove our cancellation token from the map since we're no longer connecting
|
||||
// Using a lock here to perform two operations as one atomic operation
|
||||
lock (cancellationTokenSourceLock)
|
||||
{
|
||||
// Only remove the token from the map if it is the same one created by this request
|
||||
CancellationTokenSource sourceValue;
|
||||
if (ownerToCancellationTokenSourceMap.TryGetValue(connectionParams.OwnerUri, out sourceValue) && sourceValue == source)
|
||||
{
|
||||
ownerToCancellationTokenSourceMap.TryRemove(connectionParams.OwnerUri, out sourceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ownerToConnectionMap[connectionParams.OwnerUri] = connectionInfo;
|
||||
|
||||
@@ -208,7 +266,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
// invoke callback notifications
|
||||
foreach (var activity in this.onConnectionActivities)
|
||||
{
|
||||
activity(connectionInfo);
|
||||
await activity(connectionInfo);
|
||||
}
|
||||
|
||||
// try to get information about the connected SQL Server instance
|
||||
@@ -242,6 +300,37 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel a connection that is in the process of opening.
|
||||
/// </summary>
|
||||
public bool CancelConnect(CancelConnectParams cancelParams)
|
||||
{
|
||||
// Validate parameters
|
||||
if (cancelParams == null || string.IsNullOrEmpty(cancelParams.OwnerUri))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel any current connection attempts for this URI
|
||||
CancellationTokenSource source;
|
||||
if (ownerToCancellationTokenSourceMap.TryGetValue(cancelParams.OwnerUri, out source))
|
||||
{
|
||||
try
|
||||
{
|
||||
source.Cancel();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close a connection with the specified connection details.
|
||||
/// </summary>
|
||||
@@ -253,6 +342,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel if we are in the middle of connecting
|
||||
if (CancelConnect(new CancelConnectParams() { OwnerUri = disconnectParams.OwnerUri }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lookup the connection owned by the URI
|
||||
ConnectionInfo info;
|
||||
if (!ownerToConnectionMap.TryGetValue(disconnectParams.OwnerUri, out info))
|
||||
@@ -327,6 +422,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
|
||||
// Register request and event handlers with the Service Host
|
||||
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
|
||||
serviceHost.SetRequestHandler(CancelConnectRequest.Type, HandleCancelConnectRequest);
|
||||
serviceHost.SetRequestHandler(DisconnectRequest.Type, HandleDisconnectRequest);
|
||||
serviceHost.SetRequestHandler(ListDatabasesRequest.Type, HandleListDatabasesRequest);
|
||||
|
||||
@@ -359,14 +455,50 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
/// <returns></returns>
|
||||
protected async Task HandleConnectRequest(
|
||||
ConnectParams connectParams,
|
||||
RequestContext<ConnectResponse> requestContext)
|
||||
RequestContext<bool> requestContext)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleConnectRequest");
|
||||
|
||||
try
|
||||
{
|
||||
// open connection base on request details
|
||||
ConnectResponse result = ConnectionService.Instance.Connect(connectParams);
|
||||
// create a task to connect asyncronously so that other requests are not blocked in the meantime
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// open connection based on request details
|
||||
ConnectionCompleteParams result = await ConnectionService.Instance.Connect(connectParams);
|
||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConnectionCompleteParams result = new ConnectionCompleteParams()
|
||||
{
|
||||
Messages = ex.ToString()
|
||||
};
|
||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||
}
|
||||
});
|
||||
await requestContext.SendResult(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await requestContext.SendResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle cancel connect requests
|
||||
/// </summary>
|
||||
protected async Task HandleCancelConnectRequest(
|
||||
CancelConnectParams cancelParams,
|
||||
RequestContext<bool> requestContext)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleCancelConnectRequest");
|
||||
|
||||
try
|
||||
{
|
||||
bool result = ConnectionService.Instance.CancelConnect(cancelParams);
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch(Exception ex)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for the Cancel Connect Request.
|
||||
/// </summary>
|
||||
public class CancelConnectParams
|
||||
{
|
||||
/// <summary>
|
||||
/// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
|
||||
/// or a virtual file representing an object in a database.
|
||||
/// </summary>
|
||||
public string OwnerUri { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Cancel connect request mapping entry
|
||||
/// </summary>
|
||||
public class CancelConnectRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<CancelConnectParams, bool> Type =
|
||||
RequestType<CancelConnectParams, bool>.Create("connection/cancelconnect");
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,21 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Message format for the connection result response
|
||||
/// Parameters to be sent back with a connection complete event
|
||||
/// </summary>
|
||||
public class ConnectResponse
|
||||
public class ConnectionCompleteParams
|
||||
{
|
||||
/// <summary>
|
||||
/// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
|
||||
/// or a virtual file representing an object in a database.
|
||||
/// </summary>
|
||||
public string OwnerUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A GUID representing a unique connection ID
|
||||
/// </summary>
|
||||
@@ -40,4 +48,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
/// </summary>
|
||||
public ConnectionSummary ConnectionSummary { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ConnectionComplete notification mapping entry
|
||||
/// </summary>
|
||||
public class ConnectionCompleteNotification
|
||||
{
|
||||
public static readonly
|
||||
EventType<ConnectionCompleteParams> Type =
|
||||
EventType<ConnectionCompleteParams>.Create("connection/complete");
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
public class ConnectionRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<ConnectParams, ConnectResponse> Type =
|
||||
RequestType<ConnectParams, ConnectResponse>.Create("connection/connect");
|
||||
RequestType<ConnectParams, bool> Type =
|
||||
RequestType<ConnectParams, bool>.Create("connection/connect");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionServiceConnectionCanceled
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ConnectionServiceConnectionCanceled);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionParamsValidateNullOwnerUri
|
||||
{
|
||||
get
|
||||
@@ -368,7 +376,7 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
static ResourceManager resourceManager = new ResourceManager(typeof(SR));
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
@@ -388,6 +396,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string ConnectionServiceConnStringInvalidIntent = "ConnectionServiceConnStringInvalidIntent";
|
||||
|
||||
|
||||
public const string ConnectionServiceConnectionCanceled = "ConnectionServiceConnectionCanceled";
|
||||
|
||||
|
||||
public const string ConnectionParamsValidateNullOwnerUri = "ConnectionParamsValidateNullOwnerUri";
|
||||
|
||||
|
||||
|
||||
@@ -140,6 +140,10 @@
|
||||
<comment>.
|
||||
Parameters: 0 - intent (string) </comment>
|
||||
</data>
|
||||
<data name="ConnectionServiceConnectionCanceled" xml:space="preserve">
|
||||
<value>Connection canceled</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ConnectionParamsValidateNullOwnerUri" xml:space="preserve">
|
||||
<value>OwnerUri cannot be null or empty</value>
|
||||
<comment></comment>
|
||||
|
||||
@@ -33,6 +33,8 @@ ConnectionServiceConnStringInvalidAuthType(string authType) = Invalid value '{0}
|
||||
|
||||
ConnectionServiceConnStringInvalidIntent(string intent) = Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.
|
||||
|
||||
ConnectionServiceConnectionCanceled = Connection canceled
|
||||
|
||||
######
|
||||
### Connection Params Validation Errors
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
@@ -52,6 +53,214 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
return connectionMock.Object;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCancelConnectRequest()
|
||||
{
|
||||
var testFile = "file:///my/test/file.sql";
|
||||
|
||||
// Given a connection that times out and responds to cancellation
|
||||
var mockConnection = new Mock<DbConnection> { CallBase = true };
|
||||
CancellationToken token;
|
||||
bool ready = false;
|
||||
mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny<CancellationToken>()))
|
||||
.Callback<CancellationToken>(t =>
|
||||
{
|
||||
// Pass the token to the return handler and signal the main thread to cancel
|
||||
token = t;
|
||||
ready = true;
|
||||
})
|
||||
.Returns(() =>
|
||||
{
|
||||
if (TestUtils.WaitFor(() => token.IsCancellationRequested))
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
});
|
||||
|
||||
var mockFactory = new Mock<ISqlConnectionFactory>();
|
||||
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>()))
|
||||
.Returns(mockConnection.Object);
|
||||
|
||||
|
||||
var connectionService = new ConnectionService(mockFactory.Object);
|
||||
|
||||
// Connect the connection asynchronously in a background thread
|
||||
var connectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
var connectTask = Task.Run(async () =>
|
||||
{
|
||||
return await connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = testFile,
|
||||
Connection = connectionDetails
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the connection to call OpenAsync()
|
||||
Assert.True(TestUtils.WaitFor(() => ready));
|
||||
|
||||
// Send a cancellation request
|
||||
var cancelResult = connectionService
|
||||
.CancelConnect(new CancelConnectParams()
|
||||
{
|
||||
OwnerUri = testFile
|
||||
});
|
||||
|
||||
// Wait for the connection task to finish
|
||||
connectTask.Wait();
|
||||
|
||||
// Verify that the connection was cancelled (no connection was created)
|
||||
Assert.Null(connectTask.Result.ConnectionId);
|
||||
|
||||
// Verify that the cancel succeeded
|
||||
Assert.True(cancelResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void CanCancelConnectRequestByConnecting()
|
||||
{
|
||||
var testFile = "file:///my/test/file.sql";
|
||||
|
||||
// Given a connection that times out and responds to cancellation
|
||||
var mockConnection = new Mock<DbConnection> { CallBase = true };
|
||||
CancellationToken token;
|
||||
bool ready = false;
|
||||
mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny<CancellationToken>()))
|
||||
.Callback<CancellationToken>(t =>
|
||||
{
|
||||
// Pass the token to the return handler and signal the main thread to cancel
|
||||
token = t;
|
||||
ready = true;
|
||||
})
|
||||
.Returns(() =>
|
||||
{
|
||||
if (TestUtils.WaitFor(() => token.IsCancellationRequested))
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Given a second connection that succeeds
|
||||
var mockConnection2 = new Mock<DbConnection> { CallBase = true };
|
||||
mockConnection2.Setup(x => x.OpenAsync(Moq.It.IsAny<CancellationToken>()))
|
||||
.Returns(() => Task.Run(() => {}));
|
||||
|
||||
var mockFactory = new Mock<ISqlConnectionFactory>();
|
||||
mockFactory.SetupSequence(factory => factory.CreateSqlConnection(It.IsAny<string>()))
|
||||
.Returns(mockConnection.Object)
|
||||
.Returns(mockConnection2.Object);
|
||||
|
||||
|
||||
var connectionService = new ConnectionService(mockFactory.Object);
|
||||
|
||||
// Connect the first connection asynchronously in a background thread
|
||||
var connectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
var connectTask = Task.Run(async () =>
|
||||
{
|
||||
return await connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = testFile,
|
||||
Connection = connectionDetails
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the connection to call OpenAsync()
|
||||
Assert.True(TestUtils.WaitFor(() => ready));
|
||||
|
||||
// Send a cancellation by trying to connect again
|
||||
var connectResult = await connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = testFile,
|
||||
Connection = connectionDetails
|
||||
});
|
||||
|
||||
// Wait for the first connection task to finish
|
||||
connectTask.Wait();
|
||||
|
||||
// Verify that the first connection was cancelled (no connection was created)
|
||||
Assert.Null(connectTask.Result.ConnectionId);
|
||||
|
||||
// Verify that the second connection succeeded
|
||||
Assert.NotEmpty(connectResult.ConnectionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCancelConnectRequestByDisconnecting()
|
||||
{
|
||||
var testFile = "file:///my/test/file.sql";
|
||||
|
||||
// Given a connection that times out and responds to cancellation
|
||||
var mockConnection = new Mock<DbConnection> { CallBase = true };
|
||||
CancellationToken token;
|
||||
bool ready = false;
|
||||
mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny<CancellationToken>()))
|
||||
.Callback<CancellationToken>(t =>
|
||||
{
|
||||
// Pass the token to the return handler and signal the main thread to cancel
|
||||
token = t;
|
||||
ready = true;
|
||||
})
|
||||
.Returns(() =>
|
||||
{
|
||||
if (TestUtils.WaitFor(() => token.IsCancellationRequested))
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
});
|
||||
|
||||
var mockFactory = new Mock<ISqlConnectionFactory>();
|
||||
mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny<string>()))
|
||||
.Returns(mockConnection.Object);
|
||||
|
||||
|
||||
var connectionService = new ConnectionService(mockFactory.Object);
|
||||
|
||||
// Connect the first connection asynchronously in a background thread
|
||||
var connectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
var connectTask = Task.Run(async () =>
|
||||
{
|
||||
return await connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = testFile,
|
||||
Connection = connectionDetails
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the connection to call OpenAsync()
|
||||
Assert.True(TestUtils.WaitFor(() => ready));
|
||||
|
||||
// Send a cancellation by trying to disconnect
|
||||
var disconnectResult = connectionService
|
||||
.Disconnect(new DisconnectParams()
|
||||
{
|
||||
OwnerUri = testFile
|
||||
});
|
||||
|
||||
// Wait for the first connection task to finish
|
||||
connectTask.Wait();
|
||||
|
||||
// Verify that the first connection was cancelled (no connection was created)
|
||||
Assert.Null(connectTask.Result.ConnectionId);
|
||||
|
||||
// Verify that the disconnect failed (since it caused a cancellation)
|
||||
Assert.False(disconnectResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that we can connect to the default database when no database name is
|
||||
/// provided as a parameter.
|
||||
@@ -59,12 +268,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
[Theory]
|
||||
[InlineDataAttribute(null)]
|
||||
[InlineDataAttribute("")]
|
||||
public void CanConnectWithEmptyDatabaseName(string databaseName)
|
||||
public async void CanConnectWithEmptyDatabaseName(string databaseName)
|
||||
{
|
||||
// Connect
|
||||
var connectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
connectionDetails.DatabaseName = databaseName;
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -83,7 +292,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
[Theory]
|
||||
[InlineDataAttribute("master")]
|
||||
[InlineDataAttribute("nonMasterDb")]
|
||||
public void ConnectToDefaultDatabaseRespondsWithActualDbName(string expectedDbName)
|
||||
public async void ConnectToDefaultDatabaseRespondsWithActualDbName(string expectedDbName)
|
||||
{
|
||||
// Given connecting with empty database name will return the expected DB name
|
||||
var connectionMock = new Mock<DbConnection> { CallBase = true };
|
||||
@@ -99,7 +308,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
var connectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
connectionDetails.DatabaseName = string.Empty;
|
||||
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -118,14 +327,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// connection, we disconnect first before connecting.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectingWhenConnectionExistCausesDisconnectThenConnect()
|
||||
public async void ConnectingWhenConnectionExistCausesDisconnectThenConnect()
|
||||
{
|
||||
bool callbackInvoked = false;
|
||||
|
||||
// first connect
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -146,7 +355,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
);
|
||||
|
||||
// send annother connect request
|
||||
connectionResult =
|
||||
connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -165,7 +374,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that when connecting with invalid credentials, an error is thrown.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectingWithInvalidCredentialsYieldsErrorMessage()
|
||||
public async void ConnectingWithInvalidCredentialsYieldsErrorMessage()
|
||||
{
|
||||
var testConnectionDetails = TestObjects.GetTestConnectionDetails();
|
||||
var invalidConnectionDetails = new ConnectionDetails();
|
||||
@@ -175,7 +384,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
invalidConnectionDetails.Password = "invalidPassword";
|
||||
|
||||
// Connect to test db with invalid credentials
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -204,10 +413,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
[InlineData("Integrated", "file://my/sample/file.sql", null, "test", "sa", "123456")]
|
||||
[InlineData("Integrated", "", "my-server", "test", "sa", "123456")]
|
||||
[InlineData("Integrated", "file://my/sample/file.sql", "", "test", "sa", "123456")]
|
||||
public void ConnectingWithInvalidParametersYieldsErrorMessage(string authType, string ownerUri, string server, string database, string userName, string password)
|
||||
public async void ConnectingWithInvalidParametersYieldsErrorMessage(string authType, string ownerUri, string server, string database, string userName, string password)
|
||||
{
|
||||
// Connect with invalid parameters
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -238,10 +447,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
[InlineData("sa", "")]
|
||||
[InlineData(null, "12345678")]
|
||||
[InlineData("", "12345678")]
|
||||
public void ConnectingWithNoUsernameOrPasswordWorksForIntegratedAuth(string userName, string password)
|
||||
public async void ConnectingWithNoUsernameOrPasswordWorksForIntegratedAuth(string userName, string password)
|
||||
{
|
||||
// Connect
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -263,10 +472,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that when connecting with a null parameters object, an error is thrown.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectingWithNullParametersObjectYieldsErrorMessage()
|
||||
public async void ConnectingWithNullParametersObjectYieldsErrorMessage()
|
||||
{
|
||||
// Connect with null parameters
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(null);
|
||||
|
||||
@@ -330,7 +539,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that a connection changed event is fired when the database context changes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectionChangedEventIsFiredWhenDatabaseContextChanges()
|
||||
public async void ConnectionChangedEventIsFiredWhenDatabaseContextChanges()
|
||||
{
|
||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
|
||||
@@ -339,7 +548,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
|
||||
// Set up an initial connection
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -364,11 +573,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that the SQL parser correctly detects errors in text
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ConnectToDatabaseTest()
|
||||
public async void ConnectToDatabaseTest()
|
||||
{
|
||||
// connect to a database instance
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
TestObjects.GetTestConnectionService()
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -384,12 +593,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that we can disconnect from an active connection succesfully
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DisconnectFromDatabaseTest()
|
||||
public async void DisconnectFromDatabaseTest()
|
||||
{
|
||||
// first connect
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -414,14 +623,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Test that when a disconnect is performed, the callback event is fired
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DisconnectFiresCallbackEvent()
|
||||
public async void DisconnectFiresCallbackEvent()
|
||||
{
|
||||
bool callbackInvoked = false;
|
||||
|
||||
// first connect
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -458,12 +667,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Test that disconnecting an active connection removes the Owner URI -> ConnectionInfo mapping
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DisconnectRemovesOwnerMapping()
|
||||
public async void DisconnectRemovesOwnerMapping()
|
||||
{
|
||||
// first connect
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -498,12 +707,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
[InlineDataAttribute(null)]
|
||||
[InlineDataAttribute("")]
|
||||
|
||||
public void DisconnectValidatesParameters(string disconnectUri)
|
||||
public async void DisconnectValidatesParameters(string disconnectUri)
|
||||
{
|
||||
// first connect
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -530,7 +739,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verifies the the list databases operation lists database names for the server used by a connection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ListDatabasesOnServerForCurrentConnectionReturnsDatabaseNames()
|
||||
public async void ListDatabasesOnServerForCurrentConnectionReturnsDatabaseNames()
|
||||
{
|
||||
// Result set for the query of database names
|
||||
Dictionary<string, string>[] data =
|
||||
@@ -550,7 +759,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
|
||||
// connect to a database instance
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionResult =
|
||||
var connectionResult = await
|
||||
connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
@@ -579,7 +788,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify that the SQL parser correctly detects errors in text
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OnConnectionCallbackHandlerTest()
|
||||
public async void OnConnectionCallbackHandlerTest()
|
||||
{
|
||||
bool callbackInvoked = false;
|
||||
|
||||
@@ -593,7 +802,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
);
|
||||
|
||||
// connect to a database instance
|
||||
var connectionResult = connectionService.Connect(TestObjects.GetTestConnectionParams());
|
||||
var connectionResult = await connectionService.Connect(TestObjects.GetTestConnectionParams());
|
||||
|
||||
// verify that a valid connection id was returned
|
||||
Assert.True(callbackInvoked);
|
||||
@@ -603,14 +812,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||
/// Verify when a connection is created that the URI -> Connection mapping is created in the connection service.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestConnectRequestRegistersOwner()
|
||||
public async void TestConnectRequestRegistersOwner()
|
||||
{
|
||||
// Given a request to connect to a database
|
||||
var service = TestObjects.GetTestConnectionService();
|
||||
var connectParams = TestObjects.GetTestConnectionParams();
|
||||
|
||||
// connect to a database instance
|
||||
var connectionResult = service.Connect(connectParams);
|
||||
var connectionResult = await service.Connect(connectParams);
|
||||
|
||||
// verify that a valid connection id was returned
|
||||
Assert.NotNull(connectionResult.ConnectionId);
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
|
||||
string ownerUri = "file://my/sample/file.sql";
|
||||
var connectionService = TestObjects.GetTestConnectionService();
|
||||
var connectionResult =
|
||||
connectionService
|
||||
await connectionService
|
||||
.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
public class CancelTests
|
||||
{
|
||||
[Fact]
|
||||
public void CancelInProgressQueryTest()
|
||||
public async void CancelInProgressQueryTest()
|
||||
{
|
||||
// Set up file for returning the query
|
||||
var fileMock = new Mock<ScriptFile>();
|
||||
@@ -32,7 +32,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
// If:
|
||||
// ... I request a query (doesn't matter what kind) and execute it
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.GetSubSectionDocument(), OwnerUri = Common.OwnerUri };
|
||||
var executeRequest =
|
||||
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
|
||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CancelExecutedQueryTest()
|
||||
public async void CancelExecutedQueryTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -67,7 +67,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request a query (doesn't matter what kind) and wait for execution
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams {QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
|
||||
var executeRequest =
|
||||
RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
|
||||
@@ -89,13 +89,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CancelNonExistantTest()
|
||||
public async void CancelNonExistantTest()
|
||||
{
|
||||
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// If:
|
||||
// ... I request to cancel a query that doesn't exist
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var cancelParams = new QueryCancelParams {OwnerUri = "Doesn't Exist"};
|
||||
QueryCancelResult result = null;
|
||||
var cancelRequest = GetQueryCancelResultContextMock(qcr => result = qcr, null);
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Data.Common;
|
||||
using System.IO;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
@@ -277,12 +278,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
};
|
||||
}
|
||||
|
||||
public static QueryExecutionService GetPrimedExecutionService(ISqlConnectionFactory factory, bool isConnected, WorkspaceService<SqlToolsSettings> workspaceService)
|
||||
public static async Task<QueryExecutionService> GetPrimedExecutionService(ISqlConnectionFactory factory, bool isConnected, WorkspaceService<SqlToolsSettings> workspaceService)
|
||||
{
|
||||
var connectionService = new ConnectionService(factory);
|
||||
if (isConnected)
|
||||
{
|
||||
connectionService.Connect(new ConnectParams
|
||||
await connectionService.Connect(new ConnectParams
|
||||
{
|
||||
Connection = GetTestConnectionDetails(),
|
||||
OwnerUri = OwnerUri
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposeExecutedQuery()
|
||||
public async void DisposeExecutedQuery()
|
||||
{
|
||||
// Set up file for returning the query
|
||||
var fileMock = new Mock<ScriptFile>();
|
||||
@@ -48,7 +48,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request a query (doesn't matter what kind)
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
|
||||
var executeRequest = RequestContextMocks.SetupRequestContextMock<QueryExecuteResult, QueryExecuteCompleteParams>(null, QueryExecuteCompleteEvent.Type, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -70,12 +70,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryDisposeMissingQuery()
|
||||
public async void QueryDisposeMissingQuery()
|
||||
{
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// If:
|
||||
// ... I attempt to dispose a query that doesn't exist
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var disposeParams = new QueryDisposeParams {OwnerUri = Common.OwnerUri};
|
||||
QueryDisposeResult result = null;
|
||||
var disposeRequest = GetQueryDisposeResultContextMock(qdr => result = qdr, null);
|
||||
@@ -99,7 +99,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
||||
.Returns(fileMock.Object);
|
||||
// ... We need a query service
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true,
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true,
|
||||
workspaceService.Object);
|
||||
|
||||
// If:
|
||||
|
||||
@@ -418,7 +418,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
#region Service Tests
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteValidNoResultsTest()
|
||||
public async void QueryExecuteValidNoResultsTest()
|
||||
{
|
||||
// Given:
|
||||
// ... Default settings are stored in the workspace service
|
||||
@@ -433,7 +433,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request to execute a valid query with no results
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
|
||||
QueryExecuteResult result = null;
|
||||
@@ -461,7 +461,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteValidResultsTest()
|
||||
public async void QueryExecuteValidResultsTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -473,7 +473,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request to execute a valid query with results
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true,
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true,
|
||||
workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
|
||||
|
||||
@@ -503,13 +503,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteUnconnectedUriTest()
|
||||
public async void QueryExecuteUnconnectedUriTest()
|
||||
{
|
||||
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// If:
|
||||
// ... I request to execute a query using a file URI that isn't connected
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), false, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = "notConnected", QuerySelection = Common.WholeDocument };
|
||||
|
||||
QueryExecuteResult result = null;
|
||||
@@ -528,7 +528,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteInProgressTest()
|
||||
public async void QueryExecuteInProgressTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -541,7 +541,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
// If:
|
||||
// ... I request to execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
|
||||
|
||||
// Note, we don't care about the results of the first request
|
||||
@@ -566,7 +566,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteCompletedTest()
|
||||
public async void QueryExecuteCompletedTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -579,7 +579,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
// If:
|
||||
// ... I request to execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
|
||||
|
||||
// Note, we don't care about the results of the first request
|
||||
@@ -606,7 +606,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
public void QueryExecuteMissingSelectionTest(SelectionData selection)
|
||||
public async void QueryExecuteMissingSelectionTest(SelectionData selection)
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -618,7 +618,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request to execute a query with a missing query string
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = selection };
|
||||
|
||||
QueryExecuteResult result = null;
|
||||
@@ -639,7 +639,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteInvalidQueryTest()
|
||||
public async void QueryExecuteInvalidQueryTest()
|
||||
{
|
||||
// Set up file for returning the query
|
||||
var fileMock = new Mock<ScriptFile>();
|
||||
@@ -650,7 +650,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I request to execute a query that is invalid
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, true), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, true), true, workspaceService.Object);
|
||||
var queryParams = new QueryExecuteParams { OwnerUri = Common.OwnerUri, QuerySelection = Common.WholeDocument };
|
||||
|
||||
QueryExecuteResult result = null;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test save results to a file as CSV with correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsCsvSuccessTest()
|
||||
public async void SaveResultsAsCsvSuccessTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -37,7 +37,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
||||
.Returns(fileMock.Object);
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -72,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test save results to a file as CSV with a selection of cells and correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsCsvWithSelectionSuccessTest()
|
||||
public async void SaveResultsAsCsvWithSelectionSuccessTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -84,7 +84,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -123,7 +123,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test handling exception in saving results to CSV file
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsCsvExceptionTest()
|
||||
public async void SaveResultsAsCsvExceptionTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -135,7 +135,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -164,12 +164,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test saving results to CSV file when the requested result set is no longer active
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsCsvQueryNotFoundTest()
|
||||
public async void SaveResultsAsCsvQueryNotFoundTest()
|
||||
{
|
||||
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -196,7 +196,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test save results to a file as JSON with correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsJsonSuccessTest()
|
||||
public async void SaveResultsAsJsonSuccessTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -207,7 +207,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
||||
.Returns(fileMock.Object);
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -241,7 +241,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test save results to a file as JSON with a selection of cells and correct parameters
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsJsonWithSelectionSuccessTest()
|
||||
public async void SaveResultsAsJsonWithSelectionSuccessTest()
|
||||
{
|
||||
// Set up file for returning the query
|
||||
var fileMock = new Mock<ScriptFile>();
|
||||
@@ -252,7 +252,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument , OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -290,7 +290,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test handling exception in saving results to JSON file
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsJsonExceptionTest()
|
||||
public async void SaveResultsAsJsonExceptionTest()
|
||||
{
|
||||
// Set up file for returning the query
|
||||
var fileMock = new Mock<ScriptFile>();
|
||||
@@ -300,7 +300,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny<string>()))
|
||||
.Returns(fileMock.Object);
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
@@ -329,11 +329,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
/// Test saving results to JSON file when the requested result set is no longer active
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SaveResultsAsJsonQueryNotFoundTest()
|
||||
public async void SaveResultsAsJsonQueryNotFoundTest()
|
||||
{
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// Execute a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri };
|
||||
var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
|
||||
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I have a query that has results (doesn't matter what)
|
||||
var queryService = Common.GetPrimedExecutionService(
|
||||
var queryService = await Common.GetPrimedExecutionService(
|
||||
Common.CreateMockFactory(new[] {Common.StandardTestData}, false), true,
|
||||
workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams {QuerySelection = null, OwnerUri = Common.OwnerUri};
|
||||
@@ -165,13 +165,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubsetServiceMissingQueryTest()
|
||||
public async void SubsetServiceMissingQueryTest()
|
||||
{
|
||||
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// If:
|
||||
// ... I ask for a set of results for a file that hasn't executed a query
|
||||
var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var queryService = await Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true, workspaceService.Object);
|
||||
var subsetParams = new QueryExecuteSubsetParams { OwnerUri = Common.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
|
||||
QueryExecuteSubsetResult result = null;
|
||||
var subsetRequest = GetQuerySubsetResultContextMock(qesr => result = qesr, null);
|
||||
@@ -187,7 +187,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubsetServiceUnexecutedQueryTest()
|
||||
public async void SubsetServiceUnexecutedQueryTest()
|
||||
{
|
||||
|
||||
// Set up file for returning the query
|
||||
@@ -199,7 +199,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
.Returns(fileMock.Object);
|
||||
// If:
|
||||
// ... I have a query that hasn't finished executing (doesn't matter what)
|
||||
var queryService = Common.GetPrimedExecutionService(
|
||||
var queryService = await Common.GetPrimedExecutionService(
|
||||
Common.CreateMockFactory(new[] { Common.StandardTestData }, false), true,
|
||||
workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
@@ -223,13 +223,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubsetServiceOutOfRangeSubsetTest()
|
||||
public async void SubsetServiceOutOfRangeSubsetTest()
|
||||
{
|
||||
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
// If:
|
||||
// ... I have a query that doesn't have any result sets
|
||||
var queryService = Common.GetPrimedExecutionService(
|
||||
var queryService = await Common.GetPrimedExecutionService(
|
||||
Common.CreateMockFactory(null, false), true,
|
||||
workspaceService.Object);
|
||||
var executeParams = new QueryExecuteParams { QuerySelection = null, OwnerUri = Common.OwnerUri };
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
|
||||
{
|
||||
@@ -21,5 +22,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for a condition to be true for a limited amount of time.
|
||||
/// </summary>
|
||||
/// <param name="condition">Function that returns a boolean on a condition</param>
|
||||
/// <param name="intervalMilliseconds">Number of milliseconds to wait between test intervals.</param>
|
||||
/// <param name="intervalCount">Number of test intervals to perform before giving up.</param>
|
||||
/// <returns>True if the condition was met before the test interval limit.</returns>
|
||||
public static bool WaitFor(Func<bool> condition, int intervalMilliseconds = 10, int intervalCount = 200)
|
||||
{
|
||||
int count = 0;
|
||||
while (count++ < intervalCount && !condition.Invoke())
|
||||
{
|
||||
Thread.Sleep(intervalMilliseconds);
|
||||
}
|
||||
|
||||
return (count < intervalCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user