TSQL Formatter Service (#229)

- 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
This commit is contained in:
Kevin Cunnane
2017-02-14 23:40:17 -08:00
committed by GitHub
parent eb4f2f2b91
commit 7477642854
212 changed files with 7793 additions and 184 deletions

View File

@@ -81,7 +81,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
var contextMock = RequestContextMocks.Create<bool>(null).AddErrorHandling(obj => errorResponse = obj);
await service.HandleSaveCredentialRequest(new Credential(null), contextMock.Object);
VerifyErrorSent(contextMock);
TestUtils.VerifyErrorSent(contextMock);
Assert.True(((string)errorResponse).Contains("ArgumentException"));
}
@@ -92,14 +92,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
var contextMock = RequestContextMocks.Create<bool>(null).AddErrorHandling(obj => errorResponse = obj);
await service.HandleSaveCredentialRequest(new Credential(credentialId), contextMock.Object);
VerifyErrorSent(contextMock);
TestUtils.VerifyErrorSent(contextMock);
Assert.True(((string)errorResponse).Contains("ArgumentException"));
}
[Fact]
public async Task SaveCredentialWorksForSingleCredential()
{
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual)));
}
@@ -107,11 +107,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
[Fact]
public async Task SaveCredentialSupportsSavingCredentialMultipleTimes()
{
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual)));
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual)));
}
@@ -120,13 +120,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
public async Task ReadCredentialWorksForSingleCredential()
{
// Given we have saved the credential
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual, "Expect Credential to be saved successfully")));
// Expect read of the credential to return the password
await RunAndVerify<Credential>(
await TestUtils.RunAndVerify<Credential>(
test: (requestContext) => service.HandleReadCredentialRequest(new Credential(credentialId, null), requestContext),
verify: (actual =>
{
@@ -139,22 +139,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
{
// Given we have saved multiple credentials
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual, "Expect Credential to be saved successfully")));
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(otherCredId, otherPassword), requestContext),
verify: (actual => Assert.True(actual, "Expect Credential to be saved successfully")));
// Expect read of the credentials to return the right password
await RunAndVerify<Credential>(
await TestUtils.RunAndVerify<Credential>(
test: (requestContext) => service.HandleReadCredentialRequest(new Credential(credentialId, null), requestContext),
verify: (actual =>
{
Assert.Equal(password1, actual.Password);
}));
await RunAndVerify<Credential>(
await TestUtils.RunAndVerify<Credential>(
test: (requestContext) => service.HandleReadCredentialRequest(new Credential(otherCredId, null), requestContext),
verify: (actual =>
{
@@ -166,17 +166,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
public async Task ReadCredentialHandlesPasswordUpdate()
{
// Given we have saved twice with a different password
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual)));
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password2), requestContext),
verify: (actual => Assert.True(actual)));
// When we read the value for this credential
// Then we expect only the last saved password to be found
await RunAndVerify<Credential>(
await TestUtils.RunAndVerify<Credential>(
test: (requestContext) => service.HandleReadCredentialRequest(new Credential(credentialId), requestContext),
verify: (actual =>
{
@@ -192,7 +192,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
// Verify throws on null, and this is sent as an error
await service.HandleReadCredentialRequest(null, contextMock.Object);
VerifyErrorSent(contextMock);
TestUtils.VerifyErrorSent(contextMock);
Assert.True(((string)errorResponse).Contains("ArgumentNullException"));
}
@@ -204,7 +204,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
// Verify throws with no ID
await service.HandleReadCredentialRequest(new Credential(), contextMock.Object);
VerifyErrorSent(contextMock);
TestUtils.VerifyErrorSent(contextMock);
Assert.True(((string)errorResponse).Contains("ArgumentException"));
}
@@ -216,7 +216,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
// When reading the credential
// Then expect the credential to be returned but password left blank
await RunAndVerify<Credential>(
await TestUtils.RunAndVerify<Credential>(
test: (requestContext) => service.HandleReadCredentialRequest(new Credential(credWithNoPassword, null), requestContext),
verify: (actual =>
{
@@ -234,7 +234,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
// Verify throws with no ID
await service.HandleDeleteCredentialRequest(new Credential(), contextMock.Object);
VerifyErrorSent(contextMock);
TestUtils.VerifyErrorSent(contextMock);
Assert.True(((string)errorResponse).Contains("ArgumentException"));
}
@@ -242,49 +242,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
public async Task DeleteCredentialReturnsTrueOnlyIfCredentialExisted()
{
// Save should be true
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleSaveCredentialRequest(new Credential(credentialId, password1), requestContext),
verify: (actual => Assert.True(actual)));
// Then delete - should return true
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleDeleteCredentialRequest(new Credential(credentialId), requestContext),
verify: (actual => Assert.True(actual)));
// Then delete - should return false as no longer exists
await RunAndVerify<bool>(
await TestUtils.RunAndVerify<bool>(
test: (requestContext) => service.HandleDeleteCredentialRequest(new Credential(credentialId), requestContext),
verify: (actual => Assert.False(actual)));
}
private async Task RunAndVerify<T>(Func<RequestContext<T>, Task> test, Action<T> verify)
{
T result = default(T);
var contextMock = RequestContextMocks.Create<T>(r => result = r).AddErrorHandling(null);
await test(contextMock.Object);
VerifyResult(contextMock, verify, result);
}
private void VerifyErrorSent<T>(Mock<RequestContext<T>> contextMock)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Never);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Once);
}
private void VerifyResult<T, U>(Mock<RequestContext<T>> contextMock, U expected, U actual)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
Assert.Equal(expected, actual);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
}
private void VerifyResult<T>(Mock<RequestContext<T>> contextMock, Action<T> verify, T actual)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
verify(actual);
}
}
}

View File

@@ -0,0 +1,88 @@
//
// 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.Composition;
using System.Linq;
using System.Reflection;
using Microsoft.SqlTools.ServiceLayer.Extensibility;
using Microsoft.SqlTools.ServiceLayer.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Extensibility
{
public class ExtensionTests
{
[Fact]
public void CreateAssemblyStoreShouldFindTypesInAssembly()
{
// Given a store for MyExportType
ExtensionStore store = ExtensionStore.CreateAssemblyStore<MyExportType>(GetType().GetTypeInfo().Assembly);
// Then should get any export for this type and subtypes
Assert.Equal(2, store.GetExports<MyExportType>().Count());
// But for a different type, expect throw as the store only contains MyExportType
Assert.Throws<InvalidCastException>(() => store.GetExports<MyOtherType>().Count());
}
[Fact]
public void CreateDefaultLoaderShouldOnlyFindTypesInMainAssembly()
{
// Given a store created using CreateDefaultLoader
// Then should not find exports from a different assembly
ExtensionStore store = ExtensionStore.CreateDefaultLoader<MyExportType>();
Assert.Equal(0, store.GetExports<MyExportType>().Count());
// But should find exports that are defined in the main assembly
store = ExtensionStore.CreateDefaultLoader<ASTNodeFormatterFactory>();
Assert.NotEmpty(store.GetExports<ASTNodeFormatterFactory>());
}
[Fact]
public void CreateDefaultServiceProviderShouldOnlyFindTypesInMainAssembly()
{
// Given a default ExtensionServiceProvider
// Then should not find exports from a different assembly
ExtensionServiceProvider serviceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider();
Assert.Empty(serviceProvider.GetServices<MyExportType>());
// But should find exports that are defined in the main assembly
Assert.NotEmpty(serviceProvider.GetServices<ASTNodeFormatterFactory>());
}
[Fact]
public void CreateStoreForCurrentDirectoryShouldFindExportsInDirectory()
{
// Given stores created for types in different assemblies
ExtensionStore myStore = ExtensionStore.CreateStoreForCurrentDirectory<MyExportType>();
ExtensionStore querierStore = ExtensionStore.CreateStoreForCurrentDirectory<ASTNodeFormatterFactory>();
// When I query exports
// Then exports for all assemblies should be found
Assert.Equal(2, myStore.GetExports<MyExportType>().Count());
Assert.NotEmpty(querierStore.GetExports<ASTNodeFormatterFactory>());
}
}
// Note: in order for the MEF lookup to succeed, one class must have
[Export(typeof(MyExportType))]
public class MyExportType
{
}
public class MyExportSubType : MyExportType
{
}
public class MyOtherType
{
}
}

View File

@@ -0,0 +1,115 @@
//
// 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 Microsoft.SqlTools.ServiceLayer.Extensibility;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Extensibility
{
public class ServiceProviderTests
{
private RegisteredServiceProvider provider;
public ServiceProviderTests()
{
provider = new RegisteredServiceProvider();
}
[Fact]
public void GetServiceShouldReturnNullIfNoServicesRegistered()
{
// Given no service registered
// When I call GetService
var service = provider.GetService<MyProviderService>();
// Then I expect null to be returned
Assert.Null(service);
}
[Fact]
public void GetSingleServiceThrowsMultipleServicesRegistered()
{
// Given 2 services registered
provider.Register<MyProviderService>(() => new[] { new MyProviderService(), new MyProviderService() });
// When I call GetService
// Then I expect to throw
Assert.Throws<InvalidOperationException>(() => provider.GetService<MyProviderService>());
}
[Fact]
public void GetServicesShouldReturnEmptyIfNoServicesRegistered()
{
// Given no service regisstered
// When I call GetService
var services = provider.GetServices<MyProviderService>();
// Then I expect empty enumerable to be returned
Assert.NotNull(services);
Assert.Equal(0, services.Count());
}
[Fact]
public void GetServiceShouldReturnRegisteredService()
{
MyProviderService service = new MyProviderService();
provider.RegisterSingleService(service);
var returnedService = provider.GetService<MyProviderService>();
Assert.Equal(service, returnedService);
}
[Fact]
public void GetServicesShouldReturnRegisteredServiceWhenMultipleServicesRegistered()
{
MyProviderService service = new MyProviderService();
provider.RegisterSingleService(service);
var returnedServices = provider.GetServices<MyProviderService>();
Assert.Equal(service, returnedServices.Single());
}
[Fact]
public void RegisterServiceProviderShouldThrowIfServiceIsIncompatible()
{
MyProviderService service = new MyProviderService();
Assert.Throws<InvalidOperationException>(() => provider.RegisterSingleService(typeof(OtherService), service));
}
[Fact]
public void RegisterServiceProviderShouldThrowIfServiceAlreadyRegistered()
{
MyProviderService service = new MyProviderService();
provider.RegisterSingleService(service);
Assert.Throws<InvalidOperationException>(() => provider.RegisterSingleService(service));
}
[Fact]
public void RegisterShouldThrowIfServiceAlreadyRegistered()
{
MyProviderService service = new MyProviderService();
provider.RegisterSingleService(service);
Assert.Throws<InvalidOperationException>(() => provider.Register(() => service.SingleItemAsEnumerable()));
}
[Fact]
public void RegisterShouldThrowIfServicesAlreadyRegistered()
{
provider.Register<MyProviderService>(() => new [] { new MyProviderService(), new MyProviderService() });
Assert.Throws<InvalidOperationException>(() => provider.Register(() => new MyProviderService().SingleItemAsEnumerable()));
}
}
public class MyProviderService
{
}
public class OtherService
{
}
}

View File

@@ -0,0 +1,57 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class BinaryQueryExpressionFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void BQE_IndentOperands()
{
FormatOptions options = new FormatOptions();
//options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("BQE_IndentOperands", GetInputFile("BQE_IndentOperands.sql"),
GetBaselineFile("BQE_IndentOperands.sql"), options, true);
}
[Fact]
public void BQE_KeywordCasing_UpperCase()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Uppercase;
LoadAndFormatAndCompare("BQE_KeywordCasing_UpperCase", GetInputFile("BQE_KeywordCasing.sql"),
GetBaselineFile("BQE_KeywordCasing_UpperCase.sql"), options, true);
}
[Fact]
public void BQE_KeywordCasing_LowerCase()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Lowercase;
LoadAndFormatAndCompare("BQE_KeywordCasing_LowerCase", GetInputFile("BQE_KeywordCasing.sql"),
GetBaselineFile("BQE_KeywordCasing_LowerCase.sql"), options, true);
}
[Fact]
public void BQE_KeywordCasing_NoFormat()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.None;
LoadAndFormatAndCompare("BQE_KeywordCasing_NoFormat", GetInputFile("BQE_KeywordCasing.sql"),
GetBaselineFile("BQE_KeywordCasing_NoFormat.sql"), options, true);
}
[Fact]
public void BQE_OperatorsOnNewLine()
{
FormatOptions options = new FormatOptions();
LoadAndFormatAndCompare("BQE_OperatorsOnNewLine", GetInputFile("BQE_OperatorsOnNewLine.sql"),
GetBaselineFile("BQE_OperatorsOnNewLine.sql"), options, true);
}
}
}

View File

@@ -0,0 +1,110 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class CommonTableExpressionFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void CTE()
{
LoadAndFormatAndCompare("CTE", GetInputFile("CTE.sql"),
GetBaselineFile("CTE.sql"), new FormatOptions(), true);
}
[Fact]
public void CTE_OneColumn()
{
LoadAndFormatAndCompare("CTE_OneColumn", GetInputFile("CTE_OneColumn.sql"),
GetBaselineFile("CTE_OneColumn.sql"), new FormatOptions(), true);
}
[Fact]
public void CTE_MultipleExpressions()
{
LoadAndFormatAndCompare("CTE_MultipleExpressions", GetInputFile("CTE_MultipleExpressions.sql"),
GetBaselineFile("CTE_MultipleExpressions.sql"), new FormatOptions(), true);
}
[Fact]
public void CTE_CommasBeforeDefinition()
{
FormatOptions options = new FormatOptions();
options.PlaceCommasBeforeNextStatement = true;
// TODO: fix verify to account for commma placement - this can
LoadAndFormatAndCompare("CTE_CommasBeforeDefinition", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_CommasBeforeDefinition.sql"), options, false);
}
[Fact]
public void CTE_EachReferenceOnNewLine()
{
FormatOptions options = new FormatOptions();
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("CTE_EachReferenceOnNewLine", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_EachReferenceOnNewLine.sql"), options, true);
}
[Fact]
public void CTE_EachReferenceOnNewLine_CommasBeforeDefinition()
{
FormatOptions options = new FormatOptions();
options.PlaceCommasBeforeNextStatement = true;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
// TODO: fix verify to account for commma placement - this can
LoadAndFormatAndCompare("CTE_EachReferenceOnNewLine_CommasBeforeDefinition", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_EachReferenceOnNewLine_CommasBeforeDefinition.sql"), options, false);
}
[Fact]
public void CTE_UseTabs()
{
FormatOptions options = new FormatOptions();
options.UseTabs = true;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("CTE_UseTabs", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_UseTabs.sql"), options, true);
}
[Fact]
public void CTE_20Spaces()
{
FormatOptions options = new FormatOptions();
options.SpacesPerIndent = 20;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("CTE_20Spaces", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_20Spaces.sql"), options, true);
}
[Fact]
public void CTE_UpperCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Uppercase;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("CTE_UpperCaseKeywords", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_UpperCaseKeywords.sql"), options, true);
}
[Fact]
public void CTE_LowerCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Lowercase;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("CTE_LowerCaseKeywords", GetInputFile("CTE.sql"),
GetBaselineFile("CTE_LowerCaseKeywords.sql"), options, true);
}
}
}

View File

@@ -0,0 +1,104 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class CreateProcedureFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void CreateProcedure_BackwardsCompatible()
{
LoadAndFormatAndCompare("CreateProcedure_BackwardsCompatible", GetInputFile("CreateProcedure_BackwardsCompatible.sql"),
GetBaselineFile("CreateProcedure_BackwardsCompatible.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_BeginEnd()
{
LoadAndFormatAndCompare("CreateProcedure_BeginEnd", GetInputFile("CreateProcedure_BeginEnd.sql"),
GetBaselineFile("CreateProcedure_BeginEnd.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_Minimal()
{
LoadAndFormatAndCompare("CreateProcedure_Minimal", GetInputFile("CreateProcedure_Minimal.sql"),
GetBaselineFile("CreateProcedure_Minimal.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_MultipleBatches()
{
LoadAndFormatAndCompare("CreateProcedure_MultipleBatches", GetInputFile("CreateProcedure_MultipleBatches.sql"),
GetBaselineFile("CreateProcedure_MultipleBatches.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_MultipleParams()
{
LoadAndFormatAndCompare("CreateProcedure_MultipleParams", GetInputFile("CreateProcedure_MultipleParams.sql"),
GetBaselineFile("CreateProcedure_MultipleParams.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_OneParam()
{
LoadAndFormatAndCompare("CreateProcedure_OneParam", GetInputFile("CreateProcedure_OneParam.sql"),
GetBaselineFile("CreateProcedure_OneParam.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_ParamsRecompileReturn()
{
LoadAndFormatAndCompare("CreateProcedure_ParamsRecompileReturn", GetInputFile("CreateProcedure_ParamsRecompileReturn.sql"),
GetBaselineFile("CreateProcedure_ParamsRecompileReturn.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_Select()
{
LoadAndFormatAndCompare("CreateProcedure_Select", GetInputFile("CreateProcedure_Select.sql"),
GetBaselineFile("CreateProcedure_Select.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_TwoPartName()
{
LoadAndFormatAndCompare("CreateProcedure_TwoPartName", GetInputFile("CreateProcedure_TwoPartName.sql"),
GetBaselineFile("CreateProcedure_TwoPartName.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_WithCTE()
{
LoadAndFormatAndCompare("CreateProcedure_WithCTE", GetInputFile("CreateProcedure_WithCTE.sql"),
GetBaselineFile("CreateProcedure_WithCTE.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_WithEncryptionModule()
{
LoadAndFormatAndCompare("CreateProcedure_WithEncryptionModule", GetInputFile("CreateProcedure_WithEncryptionModule.sql"),
GetBaselineFile("CreateProcedure_WithEncryptionModule.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_WithExecuteAsModule()
{
LoadAndFormatAndCompare("CreateProcedure_WithExecuteAsModule", GetInputFile("CreateProcedure_WithExecuteAsModule.sql"),
GetBaselineFile("CreateProcedure_WithExecuteAsModule.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateProcedure_WithThreeModules()
{
LoadAndFormatAndCompare("CreateProcedure_WithThreeModules", GetInputFile("CreateProcedure_WithThreeModules.sql"),
GetBaselineFile("CreateProcedure_WithThreeModules.sql"), new FormatOptions(), true);
}
}
}

View File

@@ -0,0 +1,146 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class CreateTableFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void CreateTable()
{
LoadAndFormatAndCompare("CreateTable", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable.sql"), new FormatOptions(), true);
}
/**
* The test contains a timestamp column, which is the shortest (1 token) possible length for a column item.
*/
[Fact]
public void CreateTable_Timestamp()
{
FormatOptions options = new FormatOptions();
options.AlignColumnDefinitionsInColumns = true;
LoadAndFormatAndCompare("CreateTable_Timestamp", GetInputFile("CreateTable_Timestamp.sql"), GetBaselineFile("CreateTable_Timestamp.sql"), options, true);
}
[Fact]
public void CreateTable_CommasBeforeDefinition()
{
FormatOptions options = new FormatOptions();
options.PlaceCommasBeforeNextStatement = true;
// TODO: fix verify to account for commma placement - this can
LoadAndFormatAndCompare("CreateTable_CommasBeforeDefinition", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_CommasBeforeDefinition.sql"), options, false);
}
[Fact]
public void CreateTable_UseTabs()
{
FormatOptions options = new FormatOptions();
options.UseTabs = true;
LoadAndFormatAndCompare("CreateTable_UseTabs", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_UseTabs.sql"), options, true);
}
[Fact]
public void CreateTable_20Spaces()
{
FormatOptions options = new FormatOptions();
options.SpacesPerIndent = 20;
LoadAndFormatAndCompare("CreateTable_20Spaces", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_20Spaces.sql"), options, true);
}
[Fact]
public void CreateTable_UpperCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Uppercase;
LoadAndFormatAndCompare("CreateTable_UpperCaseKeywords", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_UpperCaseKeywords.sql"), options, true);
}
[Fact]
public void CreateTable_LowerCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Lowercase;
LoadAndFormatAndCompare("CreateTable_LowerCaseKeywords", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_LowerCaseKeywords.sql"), options, true);
}
[Fact]
public void CreateTable_UpperCaseDataTypes()
{
FormatOptions options = new FormatOptions();
options.DatatypeCasing = CasingOptions.Uppercase;
LoadAndFormatAndCompare("CreateTable_UpperCaseDataTypes", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_UpperCaseDataTypes.sql"), options, true);
}
[Fact]
public void CreateTable_LowerCaseDataTypes()
{
FormatOptions options = new FormatOptions();
options.DatatypeCasing = CasingOptions.Lowercase;
LoadAndFormatAndCompare("CreateTable_LowerCaseDataTypes", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_LowerCaseDataTypes.sql"), options, true);
}
[Fact]
public void CreateTable_AlignInColumns()
{
FormatOptions options = new FormatOptions() { AlignColumnDefinitionsInColumns = true };
LoadAndFormatAndCompare("CreateTable_AlignInColumns", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_AlignInColumns.sql"), options, true);
}
[Fact]
public void CreateTable_AlignInColumnsUseTabs()
{
FormatOptions options = new FormatOptions();
options.UseTabs = true;
options.AlignColumnDefinitionsInColumns = true;
LoadAndFormatAndCompare("CreateTable_AlignInColumnsUseTabs", GetInputFile("CreateTable.sql"), GetBaselineFile("CreateTable_AlignInColumnsUseTabs.sql"), options, true);
}
[Fact]
public void CreateTable_On()
{
LoadAndFormatAndCompare("CreateTableOn", GetInputFile("CreateTableFull.sql"), GetBaselineFile("CreateTableOn.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateTable_Formatted()
{
LoadAndFormatAndCompare("CreateTable_Formatted", GetInputFile("CreateTable_Formatted.sql"), GetBaselineFile("CreateTable_Formatted.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateTable_CommentsBeforeComma()
{
FormatOptions options = new FormatOptions();
options.UseTabs = false;
options.AlignColumnDefinitionsInColumns = true;
options.PlaceCommasBeforeNextStatement = true;
LoadAndFormatAndCompare("CreateTable_CommentsBeforeComma", GetInputFile("CreateTable_CommentBeforeComma.sql"), GetBaselineFile("CreateTable_CommentBeforeComma.sql"), options, true);
}
[Fact]
public void CreateTableAddress_AlignInColumns()
{
FormatOptions options = new FormatOptions();
options.AlignColumnDefinitionsInColumns = true;
LoadAndFormatAndCompare("CreateTableAddress_AlignInColumns", GetInputFile("Address.sql"), GetBaselineFile("CreateTableAddress_AlignInColumns.sql"), options, true);
}
[Fact]
public void CreateTableAddress_AlignInColumnsUseTabs()
{
FormatOptions options = new FormatOptions();
options.UseTabs = true;
options.AlignColumnDefinitionsInColumns = true;
LoadAndFormatAndCompare("CreateTableAddress_AlignInColumnsUseTabs", GetInputFile("Address.sql"), GetBaselineFile("CreateTableAddress_AlignInColumnsUseTabs.sql"), options, true);
}
}
}

View File

@@ -0,0 +1,70 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class CreateViewFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void CreateView_Full()
{
LoadAndFormatAndCompare("CreateView_Full", GetInputFile("CreateView_Full.sql"),
GetBaselineFile("CreateView_Full.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_FullWithComments()
{
LoadAndFormatAndCompare("CreateView_FullWithComments", GetInputFile("CreateView_FullWithComments.sql"), GetBaselineFile("CreateView_FullWithComments.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_MultipleColumns()
{
LoadAndFormatAndCompare("CreateView_MultipleColumns", GetInputFile("CreateView_MultipleColumns.sql"),
GetBaselineFile("CreateView_MultipleColumns.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_MultipleOptions()
{
LoadAndFormatAndCompare("CreateView_MultipleOptions", GetInputFile("CreateView_MultipleOptions.sql"),
GetBaselineFile("CreateView_MultipleOptions.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_OneColumn()
{
LoadAndFormatAndCompare("CreateView_OneColumn", GetInputFile("CreateView_OneColumn.sql"),
GetBaselineFile("CreateView_OneColumn.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_OneColumnOneOption()
{
LoadAndFormatAndCompare("CreateView_OneColumnOneOption", GetInputFile("CreateView_OneColumnOneOption.sql"),
GetBaselineFile("CreateView_OneColumnOneOption.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_OneOption()
{
LoadAndFormatAndCompare("CreateView_OneOption", GetInputFile("CreateView_OneOption.sql"),
GetBaselineFile("CreateView_OneOption.sql"), new FormatOptions(), true);
}
[Fact]
public void CreateView_Simple()
{
LoadAndFormatAndCompare("CreateView_Simple", GetInputFile("CreateView_Simple.sql"),
GetBaselineFile("CreateView_Simple.sql"), new FormatOptions(), true);
}
}
}

View File

@@ -0,0 +1,108 @@
//
// 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.Formatter;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class FormatterSettingsTests
{
[Fact]
public void ValidateFormatterServiceDefaults()
{
var sqlToolsSettings = new SqlToolsSettings();
Assert.Null(sqlToolsSettings.SqlTools.Format.AlignColumnDefinitionsInColumns);
Assert.Equal(CasingOptions.None, sqlToolsSettings.SqlTools.Format.DatatypeCasing);
Assert.Equal(CasingOptions.None, sqlToolsSettings.SqlTools.Format.KeywordCasing);
Assert.Null(sqlToolsSettings.SqlTools.Format.PlaceCommasBeforeNextStatement);
Assert.Null(sqlToolsSettings.SqlTools.Format.PlaceSelectStatementReferencesOnNewLine);
Assert.Null(sqlToolsSettings.SqlTools.Format.UseBracketForIdentifiers);
}
[Fact]
public void ValidateFormatSettingsParsedFromJson()
{
const string settingsJson = @"
{
""params"": {
""mssql"": {
""format"": {
useBracketForIdentifiers: true,
placeCommasBeforeNextStatement: true,
placeSelectStatementReferencesOnNewLine: true,
keywordCasing: ""uppercase"",
datatypeCasing: ""lowercase"",
alignColumnDefinitionsInColumns: true
}
}
}
}";
JObject message = JObject.Parse(settingsJson);
JToken messageParams = null;
message.TryGetValue("params", out messageParams);
SqlToolsSettings sqlToolsSettings = messageParams.ToObject<SqlToolsSettings>();
Assert.True(sqlToolsSettings.SqlTools.Format.AlignColumnDefinitionsInColumns);
Assert.Equal(CasingOptions.Lowercase, sqlToolsSettings.SqlTools.Format.DatatypeCasing);
Assert.Equal(CasingOptions.Uppercase, sqlToolsSettings.SqlTools.Format.KeywordCasing);
Assert.True(sqlToolsSettings.SqlTools.Format.PlaceCommasBeforeNextStatement);
Assert.True(sqlToolsSettings.SqlTools.Format.PlaceSelectStatementReferencesOnNewLine);
Assert.True(sqlToolsSettings.SqlTools.Format.UseBracketForIdentifiers);
}
[Fact]
public void FormatOptionsMatchDefaultSettings()
{
var options = new FormatOptions();
AssertOptionsHaveDefaultValues(options);
}
private static void AssertOptionsHaveDefaultValues(FormatOptions options)
{
Assert.False(options.AlignColumnDefinitionsInColumns);
Assert.Equal(CasingOptions.None, options.DatatypeCasing);
Assert.Equal(CasingOptions.None, options.KeywordCasing);
Assert.False(options.PlaceCommasBeforeNextStatement);
Assert.False(options.PlaceEachReferenceOnNewLineInQueryStatements);
Assert.False(options.EncloseIdentifiersInSquareBrackets);
}
[Fact]
public void CanCopyDefaultFormatSettingsToOptions()
{
var sqlToolsSettings = new SqlToolsSettings();
FormatOptions options = new FormatOptions();
TSqlFormatterService.UpdateFormatOptionsFromSettings(options, sqlToolsSettings.SqlTools.Format);
AssertOptionsHaveDefaultValues(options);
}
[Fact]
public void CanCopyAlteredFormatSettingsToOptions()
{
var sqlToolsSettings = new SqlToolsSettings();
sqlToolsSettings.SqlTools.Format.AlignColumnDefinitionsInColumns = true;
sqlToolsSettings.SqlTools.Format.DatatypeCasing = CasingOptions.Lowercase;
sqlToolsSettings.SqlTools.Format.KeywordCasing = CasingOptions.Uppercase;
sqlToolsSettings.SqlTools.Format.PlaceCommasBeforeNextStatement = true;
sqlToolsSettings.SqlTools.Format.PlaceSelectStatementReferencesOnNewLine = true;
sqlToolsSettings.SqlTools.Format.UseBracketForIdentifiers = true;
FormatOptions options = new FormatOptions();
TSqlFormatterService.UpdateFormatOptionsFromSettings(options, sqlToolsSettings.SqlTools.Format);
Assert.True(options.AlignColumnDefinitionsInColumns);
Assert.Equal(CasingOptions.Lowercase, options.DatatypeCasing);
Assert.Equal(CasingOptions.Uppercase, options.KeywordCasing);
Assert.True(options.PlaceCommasBeforeNextStatement);
Assert.True(options.PlaceEachReferenceOnNewLineInQueryStatements);
Assert.True(options.EncloseIdentifiersInSquareBrackets);
}
}
}

View File

@@ -0,0 +1,90 @@
//
// 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.IO;
using System.Reflection;
using Microsoft.SqlTools.ServiceLayer.Extensibility;
using Microsoft.SqlTools.ServiceLayer.Formatter;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Moq;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class FormatterUnitTestsBase
{
public FormatterUnitTestsBase()
{
HostMock = new Mock<IProtocolEndpoint>();
WorkspaceServiceMock = new Mock<WorkspaceService<SqlToolsSettings>>();
ServiceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider();
ServiceProvider.RegisterSingleService(WorkspaceServiceMock.Object);
HostLoader.InitializeHostedServices(ServiceProvider, HostMock.Object);
FormatterService = ServiceProvider.GetService<TSqlFormatterService>();
}
protected ExtensionServiceProvider ServiceProvider { get; private set; }
protected Mock<IProtocolEndpoint> HostMock { get; private set; }
protected Mock<WorkspaceService<SqlToolsSettings>> WorkspaceServiceMock { get; private set; }
protected TSqlFormatterService FormatterService { get; private set; }
protected void LoadAndFormatAndCompare(string testName, FileInfo inputFile, FileInfo baselineFile, FormatOptions options, bool verifyFormat)
{
string inputSql = TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName);
string formattedSql = string.Empty;
formattedSql = FormatterService.Format(inputSql, options, verifyFormat);
formattedSql = TestUtilities.NormalizeLineEndings(formattedSql);
string assemblyPath = GetType().GetTypeInfo().Assembly.Location;
string directory = Path.Combine(Path.GetDirectoryName(assemblyPath), "FormatterTests");
Directory.CreateDirectory(directory);
FileInfo outputFile = new FileInfo(Path.Combine(directory, testName + ".out"));
File.WriteAllText(outputFile.FullName, formattedSql);
TestUtilities.CompareTestFiles(baselineFile, outputFile);
}
public FileInfo GetInputFile(string fileName)
{
return new FileInfo(Path.Combine(InputFileDirectory.FullName, fileName));
}
public FileInfo GetBaselineFile(string fileName)
{
return new FileInfo(Path.Combine(BaselineDirectory.FullName, fileName));
}
public DirectoryInfo BaselineDirectory
{
get
{
string d = Path.Combine(TestLocationDirectory, "BaselineFiles");
return new DirectoryInfo(d);
}
}
public DirectoryInfo InputFileDirectory
{
get
{
string d = Path.Combine(TestLocationDirectory, "TestFiles");
return new DirectoryInfo(d);
}
}
private static string TestLocationDirectory
{
get
{
return Path.Combine(RunEnvironmentInfo.GetTestDataLocation(), "TSqlFormatter");
}
}
}
}

View File

@@ -0,0 +1,82 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class InsertFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void Insert_DefaultValues()
{
LoadAndFormatAndCompare("Insert_DefaultValues", GetInputFile("Insert_DefaultValues.sql"),
GetBaselineFile("Insert_DefaultValues.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_OpenQuery()
{
LoadAndFormatAndCompare("Insert_OpenQuery", GetInputFile("Insert_OpenQuery.sql"),
GetBaselineFile("Insert_OpenQuery.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_OutputInto()
{
LoadAndFormatAndCompare("Insert_OutputInto", GetInputFile("Insert_OutputInto.sql"),
GetBaselineFile("Insert_OutputInto.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_OutputStatement()
{
LoadAndFormatAndCompare("Insert_OutputStatement", GetInputFile("Insert_OutputStatement.sql"),
GetBaselineFile("Insert_OutputStatement.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_Select()
{
FormatOptions options = new FormatOptions();
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("Insert_Select", GetInputFile("Insert_Select.sql"),
GetBaselineFile("Insert_Select.sql"), options, true);
}
[Fact]
public void Insert_SelectSource()
{
FormatOptions options = new FormatOptions();
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("Insert_SelectSource", GetInputFile("Insert_SelectSource.sql"),
GetBaselineFile("Insert_SelectSource.sql"), options, true);
}
[Fact]
public void Insert_TopSpecification()
{
LoadAndFormatAndCompare("Insert_TopSpecification", GetInputFile("Insert_TopSpecification.sql"),
GetBaselineFile("Insert_TopSpecification.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_TopWithComments()
{
LoadAndFormatAndCompare("Insert_TopWithComments", GetInputFile("Insert_TopWithComments.sql"),
GetBaselineFile("Insert_TopWithComments.sql"), new FormatOptions(), true);
}
[Fact]
public void Insert_Full()
{
LoadAndFormatAndCompare("Insert_Full", GetInputFile("Insert_Full.sql"),
GetBaselineFile("Insert_Full.sql"), new FormatOptions(), true);
}
}
}

View File

@@ -0,0 +1,108 @@
//
// 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.Formatter;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class SqlSelectStatementFormatterTests : FormatterUnitTestsBase
{
[Fact]
public void SimpleQuery()
{
LoadAndFormatAndCompare("SimpleQuery", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery.sql"), new FormatOptions(), true);
}
[Fact]
public void SimpleQuery_CommasBeforeDefinition()
{
FormatOptions options = new FormatOptions();
options.PlaceCommasBeforeNextStatement = true;
// TODO: fix verify to account for commma placement - this can
LoadAndFormatAndCompare("SimpleQuery_CommasBeforeDefinition", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_CommasBeforeDefinition.sql"), options, false);
}
[Fact]
public void SimpleQuery_EachReferenceOnNewLine()
{
FormatOptions options = new FormatOptions();
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("SimpleQuery_EachReferenceOnNewLine", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_EachReferenceOnNewLine.sql"), options, true);
}
[Fact]
public void SimpleQuery_EachReferenceOnNewLine_CommasBeforeDefinition()
{
FormatOptions options = new FormatOptions();
options.PlaceCommasBeforeNextStatement = true;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
// TODO: fix verify to account for commma placement - this can
LoadAndFormatAndCompare("SimpleQuery_EachReferenceOnNewLine_CommasBeforeDefinition",
GetInputFile("SimpleQuery.sql"), GetBaselineFile("SimpleQuery_EachReferenceOnNewLine_CommasBeforeDefinition.sql"), options, false);
}
[Fact]
public void SimpleQuery_UseTabs()
{
FormatOptions options = new FormatOptions();
options.UseTabs = true;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("SimpleQuery_UseTabs", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_UseTabs.sql"), options, true);
}
[Fact]
public void SimpleQuery_20Spaces()
{
FormatOptions options = new FormatOptions();
options.SpacesPerIndent = 20;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("SimpleQuery_20Spaces", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_20Spaces.sql"), options, true);
}
[Fact]
public void SimpleQuery_UpperCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Uppercase;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("SimpleQuery_UpperCaseKeywords", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_UpperCaseKeywords.sql"), options, true);
}
[Fact]
public void SimpleQuery_LowerCaseKeywords()
{
FormatOptions options = new FormatOptions();
options.KeywordCasing = CasingOptions.Lowercase;
options.PlaceEachReferenceOnNewLineInQueryStatements = true;
LoadAndFormatAndCompare("SimpleQuery_LowerCaseKeywords", GetInputFile("SimpleQuery.sql"),
GetBaselineFile("SimpleQuery_LowerCaseKeywords.sql"), options, true);
}
[Fact]
public void SimpleQuery_ForBrowseClause()
{
LoadAndFormatAndCompare("SimpleQuery_ForBrowseClause", GetInputFile("SimpleQuery_ForBrowseClause.sql"),
GetBaselineFile("SimpleQuery_ForBrowseClause.sql"), new FormatOptions(), true);
}
[Fact]
public void SimpleQuery_ForXmlClause()
{
LoadAndFormatAndCompare("SimpleQuery_ForXmlClause", GetInputFile("SimpleQuery_ForXmlClause.sql"),
GetBaselineFile("SimpleQuery_ForXmlClause.sql"), new FormatOptions(), true);
}
}
}

View File

@@ -0,0 +1,95 @@
//
// 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.Text;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Formatter.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Formatter
{
public class TSqlFormatterServiceTests : FormatterUnitTestsBase
{
private Mock<ServiceLayer.Workspace.Workspace> workspaceMock = new Mock<ServiceLayer.Workspace.Workspace>();
private TextDocumentIdentifier textDocument;
DocumentFormattingParams docFormatParams;
public TSqlFormatterServiceTests()
{
textDocument = new TextDocumentIdentifier
{
Uri = "script file"
};
docFormatParams = new DocumentFormattingParams()
{
TextDocument = textDocument,
Options = new FormattingOptions() { InsertSpaces = true, TabSize = 4 }
};
}
private string defaultSqlContents = TestUtilities.NormalizeLineEndings(@"create TABLE T1 ( C1 int NOT NULL, C2 nvarchar(50) NULL)");
// TODO fix bug where '\r' is appended
private string formattedSqlContents = TestUtilities.NormalizeLineEndings(@"create TABLE T1
(
C1 int NOT NULL,
C2 nvarchar(50) NULL
)");
[Fact]
public async Task FormatDocumentShouldReturnSingleEdit()
{
// Given a document that we want to format
SetupScriptFile(defaultSqlContents);
// When format document is called
await TestUtils.RunAndVerify<TextEdit[]>(
test: (requestContext) => FormatterService.HandleDocFormatRequest(docFormatParams, requestContext),
verify: (edits =>
{
// Then expect a single edit to be returned and for it to match the standard formatting
Assert.Equal(1, edits.Length);
AssertFormattingEqual(formattedSqlContents, edits[0].NewText);
}));
}
private static void AssertFormattingEqual(string expected, string actual)
{
if (string.Compare(expected, actual) != 0)
{
StringBuilder error = new StringBuilder();
error.AppendLine("======================");
error.AppendLine("Comparison failed:");
error.AppendLine("==Expected==");
error.AppendLine(expected);
error.AppendLine("==Actual==");
error.AppendLine(actual);
Assert.False(false, error.ToString());
}
}
private void SetupScriptFile(string fileContents)
{
WorkspaceServiceMock.SetupGet(service => service.Workspace).Returns(workspaceMock.Object);
workspaceMock.Setup(w => w.GetFile(It.IsAny<string>())).Returns(CreateScriptFile(fileContents));
}
private ScriptFile CreateScriptFile(string content)
{
ScriptFile scriptFile = new ScriptFile()
{
Contents = content
};
return scriptFile;
}
}
}

View File

@@ -141,11 +141,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
[Fact]
public void GetLocationFromFileForValidFilePathTest()
{
String filePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "C:\\test\\script.sql" : "/test/script.sql";
string filePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "C:\\test\\script.sql" : "/test/script.sql";
PeekDefinition peekDefinition = new PeekDefinition(null, null);
Location[] locations = peekDefinition.GetLocationFromFile(filePath, 0);
String expectedFilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "file:///C:/test/script.sql" : "file:/test/script.sql";
string expectedFilePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "file:///C:/test/script.sql" : "file:/test/script.sql";
Assert.Equal(locations[0].Uri, expectedFilePath);
}

View File

@@ -119,7 +119,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplySingleLineInsert()
{
this.AssertFileChange(
AssertFileChange(
"This is a test.",
"This is a working test.",
new FileChange
@@ -135,7 +135,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplySingleLineReplace()
{
this.AssertFileChange(
AssertFileChange(
"This is a potentially broken test.",
"This is a working test.",
new FileChange
@@ -151,7 +151,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplySingleLineDelete()
{
this.AssertFileChange(
AssertFileChange(
"This is a test of the emergency broadcasting system.",
"This is a test.",
new FileChange
@@ -167,7 +167,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplyMultiLineInsert()
{
this.AssertFileChange(
AssertFileChange(
"first\r\nsecond\r\nfifth",
"first\r\nsecond\r\nthird\r\nfourth\r\nfifth",
new FileChange
@@ -183,7 +183,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplyMultiLineReplace()
{
this.AssertFileChange(
AssertFileChange(
"first\r\nsecoXX\r\nXXfth",
"first\r\nsecond\r\nthird\r\nfourth\r\nfifth",
new FileChange
@@ -199,7 +199,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplyMultiLineReplaceWithRemovedLines()
{
this.AssertFileChange(
AssertFileChange(
"first\r\nsecoXX\r\nREMOVE\r\nTHESE\r\nLINES\r\nXXfth",
"first\r\nsecond\r\nthird\r\nfourth\r\nfifth",
new FileChange
@@ -215,7 +215,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
[Fact]
public void CanApplyMultiLineDelete()
{
this.AssertFileChange(
AssertFileChange(
"first\r\nsecond\r\nREMOVE\r\nTHESE\r\nLINES\r\nthird",
"first\r\nsecond\r\nthird",
new FileChange
@@ -235,7 +235,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
typeof(ArgumentOutOfRangeException),
() =>
{
this.AssertFileChange(
AssertFileChange(
"first\r\nsecond\r\nREMOVE\r\nTHESE\r\nLINES\r\nthird",
"first\r\nsecond\r\nthird",
new FileChange
@@ -275,7 +275,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public ScriptFileGetLinesTests()
{
this.scriptFile =
scriptFile =
ScriptFileTests.GetTestScriptFile(
"Line One\r\nLine Two\r\nLine Three\r\nLine Four\r\nLine Five\r\n");
}
@@ -284,7 +284,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public void CanGetWholeLine()
{
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(5, 1, 5, 10));
Assert.Equal(1, lines.Length);
@@ -295,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public void CanGetMultipleWholeLines()
{
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(2, 1, 4, 10));
Assert.Equal(TestStringLines.Skip(1).Take(3), lines);
@@ -305,7 +305,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public void CanGetSubstringInSingleLine()
{
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(4, 3, 4, 8));
Assert.Equal(1, lines.Length);
@@ -316,7 +316,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public void CanGetEmptySubstringRange()
{
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(4, 3, 4, 3));
Assert.Equal(1, lines.Length);
@@ -334,7 +334,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
};
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(2, 6, 4, 9));
Assert.Equal(expectedLines, lines);
@@ -351,7 +351,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
};
string[] lines =
this.scriptFile.GetLinesInRange(
scriptFile.GetLinesInRange(
new BufferRange(2, 9, 4, 1));
Assert.Equal(expectedLines, lines);
@@ -364,7 +364,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
public ScriptFilePositionTests()
{
this.scriptFile =
scriptFile =
ScriptFileTests.GetTestScriptFile(@"
First line
Second line is longer
@@ -375,12 +375,12 @@ First line
[Fact]
public void CanOffsetByLine()
{
this.AssertNewPosition(
AssertNewPosition(
1, 1,
2, 0,
3, 1);
this.AssertNewPosition(
AssertNewPosition(
3, 1,
-2, 0,
1, 1);
@@ -389,12 +389,12 @@ First line
[Fact]
public void CanOffsetByColumn()
{
this.AssertNewPosition(
AssertNewPosition(
2, 1,
0, 2,
2, 3);
this.AssertNewPosition(
AssertNewPosition(
2, 5,
0, -3,
2, 2);
@@ -447,7 +447,7 @@ First line
[Fact]
public void CanFindBeginningOfLine()
{
this.AssertNewPosition(
AssertNewPosition(
4, 12,
pos => pos.GetLineStart(),
4, 5);
@@ -456,7 +456,7 @@ First line
[Fact]
public void CanFindEndOfLine()
{
this.AssertNewPosition(
AssertNewPosition(
4, 12,
pos => pos.GetLineEnd(),
4, 15);
@@ -465,7 +465,7 @@ First line
[Fact]
public void CanComposePositionOperations()
{
this.AssertNewPosition(
AssertNewPosition(
4, 12,
pos => pos.AddOffset(-1, 1).GetLineStart(),
3, 3);
@@ -476,7 +476,7 @@ First line
int lineOffset, int columnOffset,
int expectedLine, int expectedColumn)
{
this.AssertNewPosition(
AssertNewPosition(
originalLine, originalColumn,
pos => pos.AddOffset(lineOffset, columnOffset),
expectedLine, expectedColumn);
@@ -490,7 +490,7 @@ First line
var newPosition =
positionOperation(
new FilePosition(
this.scriptFile,
scriptFile,
originalLine,
originalColumn));

View File

@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
Assert.False(sqlToolsSettings.IsSuggestionsEnabled);
}
/// <summary>
/// <summary>
/// Validate that the IsQuickInfoEnabled flag behavior
/// </summary>
[Fact]

View File

@@ -2,6 +2,9 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
{
@@ -49,5 +52,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
return (count < intervalCount);
}
public static async Task RunAndVerify<T>(Func<RequestContext<T>, Task> test, Action<T> verify)
{
T result = default(T);
var contextMock = RequestContextMocks.Create<T>(r => result = r).AddErrorHandling(null);
await test(contextMock.Object);
VerifyResult(contextMock, verify, result);
}
public static void VerifyErrorSent<T>(Mock<RequestContext<T>> contextMock)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Never);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Once);
}
public static void VerifyResult<T, U>(Mock<RequestContext<T>> contextMock, U expected, U actual)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
Assert.Equal(expected, actual);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
}
public static void VerifyResult<T>(Mock<RequestContext<T>> contextMock, Action<T> verify, T actual)
{
contextMock.Verify(c => c.SendResult(It.IsAny<T>()), Times.Once);
contextMock.Verify(c => c.SendError(It.IsAny<string>()), Times.Never);
verify(actual);
}
}
}