//
// 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.Connection;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.Test.Utility;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer
{
///
/// Tests for the ServiceHost Language Service tests
///
public class LanguageServiceTests
{
///
/// Verify that the latest SqlParser (2016 as of this writing) is used by default
///
[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);
}
///
/// Verify that the SQL parser correctly detects errors in text
///
[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);
}
///
/// Verify that the SQL parser correctly detects errors in text
///
[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);
}
///
/// Verify that the SQL parser correctly detects errors in text
///
[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);
}
///
/// Verify that GetSignatureHelp returns null when the provided TextDocumentPosition
/// has no associated ScriptParseInfo.
///
[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);
}
[Fact]
public void SetWorkspaceServiceInstanceTest()
{
AutoCompleteHelper.WorkspaceServiceInstance = null;
// workspace will be recreated if it's set to null
Assert.NotNull(AutoCompleteHelper.WorkspaceServiceInstance);
}
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);
}
}
}