mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 09:59:48 -05:00
* Add LanguageFlavorNotification handling and refactor LanguageService - Added new notification handler for language flavor changed - Refactored the LanguageService so that it no longer relies on so many intertwined static calls, which meant it was impossible to test without modifying the static instance. This will help with test reliability in the future, and prep for replacing the instance with a service provider. * Skip if not an MSSQL doc and add test * Handle definition requests * Fix diagnostics handling
213 lines
8.2 KiB
C#
213 lines
8.2 KiB
C#
//
|
|
// 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.LanguageServices;
|
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion;
|
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
|
|
{
|
|
/// <summary>
|
|
/// Tests for the ServiceHost Language Service tests
|
|
/// </summary>
|
|
public class LanguageServiceTests
|
|
{
|
|
/// <summary>
|
|
/// Verify that the latest SqlParser (2016 as of this writing) is used by default
|
|
/// </summary>
|
|
[Fact]
|
|
public void LatestSqlParserIsUsedByDefault()
|
|
{
|
|
// This should only parse correctly on SQL server 2016 or newer
|
|
const string sql2016Text =
|
|
@"CREATE SECURITY POLICY [FederatedSecurityPolicy]" + "\r\n" +
|
|
@"ADD FILTER PREDICATE [rls].[fn_securitypredicate]([CustomerId])" + "\r\n" +
|
|
@"ON [dbo].[Customer];";
|
|
|
|
LanguageService service = TestObjects.GetTestLanguageService();
|
|
|
|
// parse
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents(sql2016Text);
|
|
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
|
|
|
|
// verify that no errors are detected
|
|
Assert.Equal(0, fileMarkers.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that the SQL parser correctly detects errors in text
|
|
/// </summary>
|
|
[Fact]
|
|
public void ParseSelectStatementWithoutErrors()
|
|
{
|
|
// sql statement with no errors
|
|
const string sqlWithErrors = "SELECT * FROM sys.objects";
|
|
|
|
// get the test service
|
|
LanguageService service = TestObjects.GetTestLanguageService();
|
|
|
|
// parse the sql statement
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents(sqlWithErrors);
|
|
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
|
|
|
|
// verify there are no errors
|
|
Assert.Equal(0, fileMarkers.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that the SQL parser correctly detects errors in text
|
|
/// </summary>
|
|
[Fact]
|
|
public void ParseSelectStatementWithError()
|
|
{
|
|
// sql statement with errors
|
|
const string sqlWithErrors = "SELECT *** FROM sys.objects";
|
|
|
|
// get test service
|
|
LanguageService service = TestObjects.GetTestLanguageService();
|
|
|
|
// parse sql statement
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents(sqlWithErrors);
|
|
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
|
|
|
|
// verify there is one error
|
|
Assert.Equal(1, fileMarkers.Length);
|
|
|
|
// verify the position of the error
|
|
Assert.Equal(9, fileMarkers[0].ScriptRegion.StartColumnNumber);
|
|
Assert.Equal(1, fileMarkers[0].ScriptRegion.StartLineNumber);
|
|
Assert.Equal(10, fileMarkers[0].ScriptRegion.EndColumnNumber);
|
|
Assert.Equal(1, fileMarkers[0].ScriptRegion.EndLineNumber);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that the SQL parser correctly detects errors in text
|
|
/// </summary>
|
|
[Fact]
|
|
public void ParseMultilineSqlWithErrors()
|
|
{
|
|
// multiline sql with errors
|
|
const string sqlWithErrors =
|
|
"SELECT *** FROM sys.objects;\n" +
|
|
"GO\n" +
|
|
"SELECT *** FROM sys.objects;\n";
|
|
|
|
// get test service
|
|
LanguageService service = TestObjects.GetTestLanguageService();
|
|
|
|
// parse sql
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents(sqlWithErrors);
|
|
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
|
|
|
|
// verify there are two errors
|
|
Assert.Equal(2, fileMarkers.Length);
|
|
|
|
// check position of first error
|
|
Assert.Equal(9, fileMarkers[0].ScriptRegion.StartColumnNumber);
|
|
Assert.Equal(1, fileMarkers[0].ScriptRegion.StartLineNumber);
|
|
Assert.Equal(10, fileMarkers[0].ScriptRegion.EndColumnNumber);
|
|
Assert.Equal(1, fileMarkers[0].ScriptRegion.EndLineNumber);
|
|
|
|
// check position of second error
|
|
Assert.Equal(9, fileMarkers[1].ScriptRegion.StartColumnNumber);
|
|
Assert.Equal(3, fileMarkers[1].ScriptRegion.StartLineNumber);
|
|
Assert.Equal(10, fileMarkers[1].ScriptRegion.EndColumnNumber);
|
|
Assert.Equal(3, fileMarkers[1].ScriptRegion.EndLineNumber);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify that GetSignatureHelp returns null when the provided TextDocumentPosition
|
|
/// has no associated ScriptParseInfo.
|
|
/// </summary>
|
|
[Fact]
|
|
public void GetSignatureHelpReturnsNullIfParseInfoNotInitialized()
|
|
{
|
|
// Given service doesn't have parseinfo intialized for a document
|
|
const string docContent = "SELECT * FROM sys.objects";
|
|
LanguageService service = TestObjects.GetTestLanguageService();
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents(docContent);
|
|
|
|
// When requesting SignatureHelp
|
|
SignatureHelp signatureHelp = service.GetSignatureHelp(TestObjects.GetTestDocPosition(), scriptFile);
|
|
|
|
// Then null is returned as no parse info can be used to find the signature
|
|
Assert.Null(signatureHelp);
|
|
}
|
|
|
|
[Fact]
|
|
public void EmptyCompletionListTest()
|
|
{
|
|
Assert.Equal(AutoCompleteHelper.EmptyCompletionList.Length, 0);
|
|
}
|
|
|
|
internal class TestScriptDocumentInfo : ScriptDocumentInfo
|
|
{
|
|
public TestScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo,
|
|
string tokenText = null)
|
|
:base(textDocumentPosition, scriptFile, scriptParseInfo)
|
|
{
|
|
this.tokenText = string.IsNullOrEmpty(tokenText) ? "doesntmatchanythingintheintellisensedefaultlist" : tokenText;
|
|
}
|
|
|
|
private string tokenText;
|
|
|
|
public override string TokenText
|
|
{
|
|
get
|
|
{
|
|
return this.tokenText;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCompletionListWithNoMatchesTest()
|
|
{
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents("koko wants a bananas");
|
|
|
|
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = false };
|
|
|
|
var scriptDocumentInfo = new TestScriptDocumentInfo(
|
|
new TextDocumentPosition()
|
|
{
|
|
TextDocument = new TextDocumentIdentifier() { Uri = TestObjects.ScriptUri },
|
|
Position = new Position() { Line = 0, Character = 0 }
|
|
}, scriptFile, scriptInfo);
|
|
|
|
AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, false);
|
|
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCompletionListWithMatchesTest()
|
|
{
|
|
var scriptFile = new ScriptFile();
|
|
scriptFile.SetFileContents("koko wants a bananas");
|
|
|
|
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = false };
|
|
|
|
var scriptDocumentInfo = new TestScriptDocumentInfo(
|
|
new TextDocumentPosition()
|
|
{
|
|
TextDocument = new TextDocumentIdentifier() { Uri = TestObjects.ScriptUri },
|
|
Position = new Position() { Line = 0, Character = 0 }
|
|
}, scriptFile, scriptInfo, "all");
|
|
|
|
CompletionItem[] result = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, false);
|
|
Assert.Equal(result.Length, 1);
|
|
|
|
}
|
|
}
|
|
}
|