mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-29 17:24:34 -05:00
port batch parser wrapper (#232)
* Initial commit for GitHub IO pages * Add initial doxfx content * Update manifest.json * Update manifest.json * Set theme jekyll-theme-cayman * Set theme jekyll-theme-slate * Set theme jekyll-theme-minimal * Set theme jekyll-theme-tactile * Clear out theme setting * Remove API docs * Revert "Adding Milliseconds to DateTime fields (#173)" (#197) This reverts commit431dfa4156. * ported new BatchParser * added BatchParser tests * fixing merge conflicts * fix build issues * cleaned code and addressed comments from code review * addressed code review and made BatchParser logic more efficient * fixed batch parser tests * changed class name to fix build issues * fixed merge conflicts * added path for lab mode baseline tests * changed env path for lab mode * added env variable to appveyor * testing env variable for appveyor * fixed lab build * debug appveyor build * testing changes for appveyor * changed trace env path * debugging appveyor build * changed baseline env path * debugging * debugging * debugging * switched on trace flag * debugging * debugging * changed build config * changed baseline files * checking baseline output * changed baseline files * debug baseline tests * debugging baseline * debugging * debugging * debug * debugging * testing baseline format * debug * debug * debug * debug * debug * newline debug * changed baseline file * debug * test * try new way to read * added execution engine tests * change test * testing file encoding * moved execution engine tests to integration * try compare without spaces * removed no op test * added env variable for travis * put batch parser tests to integration too * put batch parser in integration * try new baseline string match * compare baseline test logic changed * changed baseline logic as well as cleaned tests * fix build for travis CI * fix travis CI issues * fixed highlighting bugs on vscode * code review changes * ported new BatchParser * added BatchParser tests * Initial commit for GitHub IO pages * Add initial doxfx content * Update manifest.json * Update manifest.json * Set theme jekyll-theme-cayman * Set theme jekyll-theme-slate * Set theme jekyll-theme-minimal * Set theme jekyll-theme-tactile * Clear out theme setting * Remove API docs * Revert "Adding Milliseconds to DateTime fields (#173)" (#197) This reverts commit431dfa4156. * fixing merge conflicts * fix build issues * cleaned code and addressed comments from code review * addressed code review and made BatchParser logic more efficient * fixed batch parser tests * changed class name to fix build issues * fixed merge conflicts * added path for lab mode baseline tests changed env path for lab mode added env variable to appveyor testing env variable for appveyor fixed lab build debug appveyor build testing changes for appveyor changed trace env path debugging appveyor build changed baseline env path debugging debugging debugging switched on trace flag debugging debugging changed build config changed baseline files checking baseline output changed baseline files debug baseline tests debugging baseline debugging debugging debug debugging testing baseline format debug debug debug debug debug newline debug changed baseline file debug test try new way to read added execution engine tests change test testing file encoding moved execution engine tests to integration try compare without spaces removed no op test added env variable for travis * put batch parser tests to integration too * put batch parser in integration try new baseline string match * compare baseline test logic changed * changed baseline logic as well as cleaned tests * fix build for travis CI * fix travis CI issues * fixed highlighting bugs on vscode * code review changes * fixed filestream writer test * added localization string * added localization string * generated new string files again * code review changes
This commit is contained in:
2
test/CodeCoverage/TS-err-cycle1.txt
Normal file
2
test/CodeCoverage/TS-err-cycle1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
print 'hi'
|
||||
:r cycle2.txt
|
||||
2
test/CodeCoverage/cycle2.txt
Normal file
2
test/CodeCoverage/cycle2.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
print '2'
|
||||
:r TS-err-cycle1.txt
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// 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.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
internal class BatchParserMockEventHandler : IBatchEventsHandler
|
||||
{
|
||||
public SqlError Error { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// fired when there is an error message from the server
|
||||
/// </summary>
|
||||
public void OnBatchError(object sender, BatchErrorEventArgs args)
|
||||
{
|
||||
Debug.WriteLine("{0}", args.Message);
|
||||
Error = args.Error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// fired when there is a message from the server
|
||||
/// </summary>
|
||||
public void OnBatchMessage(object sender, BatchMessageEventArgs args)
|
||||
{
|
||||
Debug.WriteLine("{0}", args.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// fired when there is a new result set available. It is guarnteed
|
||||
/// to be fired from the same thread that called Execute method
|
||||
/// </summary>
|
||||
public void OnBatchResultSetProcessing(object sender, BatchResultSetEventArgs args) { }
|
||||
|
||||
/// <summary>
|
||||
/// fired when we've done absolutely all actions for the current result set
|
||||
/// </summary>
|
||||
public void OnBatchResultSetFinished(object sender, EventArgs args) { }
|
||||
|
||||
/// <summary>
|
||||
/// fired when the batch recieved cancel request BEFORE it
|
||||
/// initiates cancel operation. Note that it is fired from a
|
||||
/// different thread then the one used to kick off execution
|
||||
/// </summary>
|
||||
public void OnBatchCancelling(object sender, EventArgs args) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// 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 Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
public class BatchParserSqlCmdTests : IDisposable
|
||||
{
|
||||
private BatchParserSqlCmd bpcmd;
|
||||
private PositionStruct testPOS;
|
||||
public BatchParserSqlCmdTests()
|
||||
{
|
||||
bpcmd = new BatchParserSqlCmd();
|
||||
testPOS = new PositionStruct();
|
||||
bpcmd.SetVariable(testPOS, "variable1", "test1");
|
||||
bpcmd.SetVariable(testPOS, "variable2", "test2");
|
||||
bpcmd.SetVariable(testPOS, "variable3", "test3");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (bpcmd != null)
|
||||
{
|
||||
bpcmd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckSetVariable()
|
||||
{
|
||||
Assert.Equal(bpcmd.InternalVariables.Count, 3);
|
||||
bpcmd.SetVariable(testPOS, "variable4", "test4");
|
||||
bpcmd.SetVariable(testPOS, "variable5", "test5");
|
||||
bpcmd.SetVariable(testPOS, "variable6", "test6");
|
||||
Assert.Equal(bpcmd.InternalVariables.Count, 6);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckGetVariable()
|
||||
{
|
||||
string value = bpcmd.GetVariable(testPOS, "variable1");
|
||||
Assert.Equal("test1", value);
|
||||
value = bpcmd.GetVariable(testPOS, "variable2");
|
||||
Assert.Equal("test2", value);
|
||||
value = bpcmd.GetVariable(testPOS, "variable3");
|
||||
Assert.Equal("test3", value);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
public class BatchParserTests : BaselinedTest
|
||||
{
|
||||
private bool testFailed = false;
|
||||
|
||||
public BatchParserTests()
|
||||
{
|
||||
InitializeTest();
|
||||
}
|
||||
|
||||
public void InitializeTest()
|
||||
{
|
||||
CategoryName = "BatchParser";
|
||||
this.TraceOutputDirectory = RunEnvironmentInfo.GetTestDataLocation();
|
||||
TestInitialize();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyThrowOnUnresolvedVariable()
|
||||
{
|
||||
string script = "print '$(NotDefined)'";
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
TestCommandHandler handler = new TestCommandHandler(output);
|
||||
IVariableResolver resolver = new TestVariableResolver(new StringBuilder());
|
||||
Parser p = new Parser(
|
||||
handler,
|
||||
resolver,
|
||||
new StringReader(script),
|
||||
"test");
|
||||
p.ThrowOnUnresolvedVariable = true;
|
||||
|
||||
handler.SetParser(p);
|
||||
|
||||
Assert.Throws<BatchParserException>(() => p.Parse());
|
||||
}
|
||||
|
||||
public void TokenizeWithLexer(string filename, StringBuilder output)
|
||||
{
|
||||
|
||||
using (Lexer lexer = new Lexer(new StreamReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)), filename))
|
||||
{
|
||||
|
||||
string inputText = File.ReadAllText(filename);
|
||||
inputText = inputText.Replace("\r\n", "\n");
|
||||
StringBuilder roundtripTextBuilder = new StringBuilder();
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
StringBuilder tokenizedInput = new StringBuilder();
|
||||
bool lexerError = false;
|
||||
|
||||
Token token = null;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
lexer.ConsumeToken();
|
||||
token = lexer.CurrentToken;
|
||||
roundtripTextBuilder.Append(token.Text);
|
||||
outputBuilder.AppendLine(GetTokenString(token));
|
||||
tokenizedInput.Append('[').Append(GetTokenCode(token.TokenType)).Append(':').Append(token.Text).Append(']');
|
||||
} while (token.TokenType != LexerTokenType.Eof);
|
||||
}
|
||||
catch (BatchParserException ex)
|
||||
{
|
||||
lexerError = true;
|
||||
outputBuilder.AppendLine(string.Format(CultureInfo.CurrentCulture, "[ERROR: code {0} at {1} - {2} in {3}, message: {4}]", ex.ErrorCode, GetPositionString(ex.Begin), GetPositionString(ex.End), GetFilenameOnly(ex.Begin.Filename), ex.Message));
|
||||
}
|
||||
output.AppendLine("Lexer tokenized input:");
|
||||
output.AppendLine("======================");
|
||||
output.AppendLine(tokenizedInput.ToString());
|
||||
output.AppendLine("Tokens:");
|
||||
output.AppendLine("=======");
|
||||
output.AppendLine(outputBuilder.ToString());
|
||||
|
||||
if (lexerError == false)
|
||||
{
|
||||
// Verify that all text from tokens can be recombined into original string
|
||||
Assert.Equal<string>(inputText, roundtripTextBuilder.ToString().Replace("\r\n", "\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTokenCode(LexerTokenType lexerTokenType)
|
||||
{
|
||||
switch (lexerTokenType)
|
||||
{
|
||||
case LexerTokenType.Text:
|
||||
return "T";
|
||||
case LexerTokenType.Whitespace:
|
||||
return "WS";
|
||||
case LexerTokenType.NewLine:
|
||||
return "NL";
|
||||
case LexerTokenType.Comment:
|
||||
return "C";
|
||||
default:
|
||||
return lexerTokenType.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyToOutput(string sourceDirectory, string filename)
|
||||
{
|
||||
File.Copy(Path.Combine(sourceDirectory, filename), filename, true);
|
||||
FileUtilities.SetFileReadWrite(filename);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchParserTest()
|
||||
{
|
||||
CopyToOutput(FilesLocation, "TS-err-cycle1.txt");
|
||||
CopyToOutput(FilesLocation, "cycle2.txt");
|
||||
|
||||
Start("err-blockComment");
|
||||
Start("err-blockComment2");
|
||||
Start("err-varDefinition");
|
||||
Start("err-varDefinition2");
|
||||
Start("err-varDefinition3");
|
||||
Start("err-varDefinition4");
|
||||
Start("err-varDefinition5");
|
||||
Start("err-varDefinition6");
|
||||
Start("err-varDefinition7");
|
||||
Start("err-varDefinition8");
|
||||
Start("err-varDefinition9");
|
||||
Start("err-variableRef");
|
||||
Start("err-variableRef2");
|
||||
Start("err-variableRef3");
|
||||
Start("err-variableRef4");
|
||||
Start("err-cycle1");
|
||||
Start("input");
|
||||
Start("input2");
|
||||
Start("pass-blockComment");
|
||||
Start("pass-lineComment");
|
||||
Start("pass-lineComment2");
|
||||
Start("pass-noBlockComments");
|
||||
Start("pass-noLineComments");
|
||||
Start("pass-varDefinition");
|
||||
Start("pass-varDefinition2");
|
||||
Start("pass-varDefinition3");
|
||||
Start("pass-varDefinition4");
|
||||
Start("pass-command-and-comment");
|
||||
Assert.False(testFailed, "At least one of test cases failed. Check output for details.");
|
||||
}
|
||||
|
||||
public void TestParser(string filename, StringBuilder output)
|
||||
{
|
||||
try
|
||||
{
|
||||
TestCommandHandler commandHandler = new TestCommandHandler(output);
|
||||
|
||||
Parser parser = new Parser(
|
||||
commandHandler,
|
||||
new TestVariableResolver(output),
|
||||
new StreamReader(File.Open(filename, FileMode.Open)),
|
||||
filename);
|
||||
|
||||
commandHandler.SetParser(parser);
|
||||
|
||||
parser.Parse();
|
||||
}
|
||||
catch (BatchParserException ex)
|
||||
{
|
||||
output.AppendLine(string.Format(CultureInfo.CurrentCulture, "[PARSER ERROR: code {0} at {1} - {2} in {3}, token text: {4}, message: {5}]", ex.ErrorCode, GetPositionString(ex.Begin), GetPositionString(ex.End), GetFilenameOnly(ex.Begin.Filename), ex.Text, ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPositionString(PositionStruct pos)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}:{1} [{2}]", pos.Line, pos.Column, pos.Offset);
|
||||
}
|
||||
|
||||
private string GetTokenString(Token token)
|
||||
{
|
||||
if (token == null)
|
||||
{
|
||||
return "(null)";
|
||||
}
|
||||
else
|
||||
{
|
||||
string tokenText = token.Text;
|
||||
if (tokenText != null)
|
||||
{
|
||||
tokenText = tokenText.Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t");
|
||||
}
|
||||
string tokenFilename = token.Filename;
|
||||
tokenFilename = GetFilenameOnly(tokenFilename);
|
||||
return string.Format(CultureInfo.CurrentCulture, "[Token {0} at {1}({2}:{3} [{4}] - {5}:{6} [{7}]): '{8}']",
|
||||
token.TokenType,
|
||||
tokenFilename,
|
||||
token.Begin.Line, token.Begin.Column, token.Begin.Offset,
|
||||
token.End.Line, token.End.Column, token.End.Offset,
|
||||
tokenText);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetFilenameOnly(string fullPath)
|
||||
{
|
||||
return fullPath != null ? Path.GetFileName(fullPath) : null;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
string inputFilename = GetTestscriptFilePath(CurrentTestName);
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
TokenizeWithLexer(inputFilename, output);
|
||||
TestParser(inputFilename, output);
|
||||
|
||||
string baselineFilename = GetBaselineFilePath(CurrentTestName);
|
||||
string baseline;
|
||||
|
||||
try
|
||||
{
|
||||
baseline = GetFileContent(baselineFilename);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
baseline = string.Empty;
|
||||
}
|
||||
|
||||
string outputString = output.ToString();
|
||||
|
||||
Console.WriteLine(baselineFilename);
|
||||
|
||||
if (string.Compare(baseline, outputString, StringComparison.Ordinal) != 0)
|
||||
{
|
||||
DumpToTrace(CurrentTestName, outputString);
|
||||
string outputFilename = Path.Combine(TraceFilePath, GetBaselineFileName(CurrentTestName));
|
||||
Console.WriteLine(":: Output does not match the baseline!");
|
||||
Console.WriteLine("code --diff \"" + baselineFilename + "\" \"" + outputFilename + "\"");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(":: To update the baseline:");
|
||||
Console.WriteLine("copy \"" + outputFilename + "\" \"" + baselineFilename + "\"");
|
||||
Console.WriteLine();
|
||||
testFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
public class BatchParserWrapperTests
|
||||
{
|
||||
private BatchParserWrapper parserWrapper;
|
||||
public BatchParserWrapperTests()
|
||||
{
|
||||
parserWrapper = new BatchParserWrapper();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckSimpleSingleSQLBatchStatement()
|
||||
{
|
||||
string sqlScript = "select * from sys.objects";
|
||||
var batches = parserWrapper.GetBatches(sqlScript);
|
||||
Assert.Equal(1, batches.Count);
|
||||
BatchDefinition batch = batches[0];
|
||||
Assert.Equal(sqlScript, batch.BatchText);
|
||||
Assert.Equal(1, batch.StartLine);
|
||||
Assert.Equal(1, batch.StartColumn);
|
||||
Assert.Equal(2, batch.EndLine);
|
||||
Assert.Equal(sqlScript.Length, batch.EndColumn);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckComment()
|
||||
{
|
||||
string sqlScript = "-- this is a comment --";
|
||||
var batches = parserWrapper.GetBatches(sqlScript);
|
||||
Assert.Equal(1, batches.Count);
|
||||
BatchDefinition batch = batches[0];
|
||||
Assert.Equal(sqlScript, batch.BatchText);
|
||||
Assert.Equal(1, batch.StartLine);
|
||||
Assert.Equal(1, batch.StartColumn);
|
||||
Assert.Equal(2, batch.EndLine);
|
||||
Assert.Equal(sqlScript.Length, batch.EndColumn);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckNoOps()
|
||||
{
|
||||
string sqlScript = "GO";
|
||||
var batches = parserWrapper.GetBatches(sqlScript);
|
||||
Assert.Equal(0, batches.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// 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.Text;
|
||||
using System.Globalization;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
internal class TestCommandHandler : ICommandHandler
|
||||
{
|
||||
private Parser parser;
|
||||
private StringBuilder outputString;
|
||||
|
||||
public TestCommandHandler(StringBuilder outputString)
|
||||
{
|
||||
this.outputString = outputString;
|
||||
}
|
||||
|
||||
public void SetParser(Parser parser)
|
||||
{
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public BatchParserAction Go(TextBlock batch, int repeatCount)
|
||||
{
|
||||
string textWithVariablesResolved;
|
||||
string textWithVariablesUnresolved;
|
||||
LineInfo lineInfoVarsResolved;
|
||||
LineInfo lineInfoVarsUnresolved;
|
||||
|
||||
batch.GetText(true, out textWithVariablesResolved, out lineInfoVarsResolved);
|
||||
batch.GetText(false, out textWithVariablesUnresolved, out lineInfoVarsUnresolved);
|
||||
outputString.AppendFormat(CultureInfo.InvariantCulture, "*** Execute batch ({0})\n", repeatCount);
|
||||
|
||||
if (string.Compare(textWithVariablesUnresolved, textWithVariablesResolved, StringComparison.Ordinal) != 0)
|
||||
{
|
||||
outputString.AppendLine("Text with variables not resolved:");
|
||||
outputString.AppendLine(textWithVariablesResolved);
|
||||
outputString.AppendLine("Text with variables not resolved:");
|
||||
outputString.AppendLine(textWithVariablesUnresolved);
|
||||
int i = 0;
|
||||
outputString.AppendLine("Mapping from resolved string to unresolved:");
|
||||
while (i <= textWithVariablesResolved.Length)
|
||||
{
|
||||
PositionStruct pos = lineInfoVarsResolved.GetStreamPositionForOffset(i);
|
||||
string character = i < textWithVariablesResolved.Length ? ("" + textWithVariablesResolved[i]).Replace("\n", @"\n").Replace("\r", @"\r") : "EOF";
|
||||
outputString.AppendFormat(CultureInfo.InvariantCulture, "Pos [{0}] {1}:{2} \"{3}\"",
|
||||
i,
|
||||
BatchParserTests.GetFilenameOnly(pos.Filename),
|
||||
pos.Offset,
|
||||
character);
|
||||
outputString.AppendLine();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputString.AppendLine("Batch text:");
|
||||
outputString.AppendLine(textWithVariablesUnresolved);
|
||||
}
|
||||
outputString.AppendLine();
|
||||
return BatchParserAction.Continue;
|
||||
}
|
||||
|
||||
public BatchParserAction OnError(Token token, OnErrorAction action)
|
||||
{
|
||||
outputString.AppendFormat(CultureInfo.InvariantCulture, "*** PARSER: On error: {0}", action.ToString());
|
||||
outputString.AppendLine();
|
||||
return BatchParserAction.Continue;
|
||||
}
|
||||
|
||||
public BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename)
|
||||
{
|
||||
string resolvedFilename;
|
||||
LineInfo lineInfo;
|
||||
|
||||
filename.GetText(true, out resolvedFilename, out lineInfo);
|
||||
outputString.AppendFormat(CultureInfo.InvariantCulture, "*** PARSER: Include file \"{0}\"\n", resolvedFilename);
|
||||
outputString.AppendLine();
|
||||
string currentFilename = lineInfo.GetStreamPositionForOffset(0).Filename;
|
||||
string currentFilePath = Path.Combine(Path.GetDirectoryName(currentFilename), resolvedFilename);
|
||||
stream = new StreamReader(File.Open(currentFilePath, FileMode.Open));
|
||||
newFilename = resolvedFilename;
|
||||
return BatchParserAction.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
{
|
||||
internal sealed class TestVariableResolver : IVariableResolver
|
||||
{
|
||||
Dictionary<string, string> variables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
private StringBuilder outputString;
|
||||
|
||||
public TestVariableResolver(StringBuilder outputString)
|
||||
{
|
||||
this.outputString = outputString;
|
||||
}
|
||||
|
||||
public string GetVariable(PositionStruct pos, string name)
|
||||
{
|
||||
if (variables.ContainsKey(name))
|
||||
{
|
||||
return variables[name];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVariable(PositionStruct pos, string name, string value)
|
||||
{
|
||||
outputString.AppendFormat("Setting variable {0} to [{1}]\n", name, value);
|
||||
if (value == null)
|
||||
{
|
||||
variables.Remove(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
variables[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
{
|
||||
internal class BatchEventHandler: IBatchEventsHandler
|
||||
{
|
||||
List<int> resultCounts = new List<int>();
|
||||
List<string> sqlMessages = new List<string>();
|
||||
List<string> errorMessage = new List<string>();
|
||||
int batchfinishedEventCounter = 0;
|
||||
SqlDataReader dr = null;
|
||||
bool cancelEventFired = false;
|
||||
|
||||
#region Public properties
|
||||
public List<int> ResultCounts
|
||||
{
|
||||
get
|
||||
{
|
||||
return resultCounts;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> SqlMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return sqlMessages;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> ErrorMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public int BatchfinishedEventCounter
|
||||
{
|
||||
get
|
||||
{
|
||||
return batchfinishedEventCounter;
|
||||
}
|
||||
}
|
||||
public bool CancelFired
|
||||
{
|
||||
get
|
||||
{
|
||||
return cancelEventFired;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IBatchEventHandlers Members
|
||||
public void OnBatchCancelling(object sender, EventArgs args)
|
||||
{
|
||||
Console.WriteLine("\tOnBatchCancelling:");
|
||||
cancelEventFired = true;
|
||||
}
|
||||
|
||||
public void OnBatchError(object sender, BatchErrorEventArgs args)
|
||||
{
|
||||
Console.WriteLine("\tOnBatchError:");
|
||||
Console.WriteLine("\t\tLine {0} has error: ", args.Line);
|
||||
Console.WriteLine("\t\tError description: " + args.Description);
|
||||
Console.WriteLine("\t\tError message: " + args.Message);
|
||||
Console.WriteLine("\t\tError Line: " + args.TextSpan.iStartLine);
|
||||
|
||||
errorMessage.Add(args.Description);
|
||||
}
|
||||
|
||||
public void OnBatchMessage(object sender, BatchMessageEventArgs args)
|
||||
{
|
||||
Console.WriteLine("\tOnBatchMessage ...");
|
||||
Console.WriteLine("\t\tMessage: " + args.Message);
|
||||
Console.WriteLine("\t\tDetail message:" + args.DetailedMessage);
|
||||
|
||||
if (args.DetailedMessage != "")
|
||||
{
|
||||
sqlMessages.Add(args.DetailedMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SqlMessages.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBatchResultSetFinished(object sender, EventArgs args)
|
||||
{
|
||||
Console.WriteLine("\tOnBatchResultSetFinished...");
|
||||
Console.WriteLine("\t\tBatch result set finished");
|
||||
batchfinishedEventCounter++;
|
||||
}
|
||||
|
||||
public void OnBatchResultSetProcessing(object sender, BatchResultSetEventArgs args)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
Console.WriteLine("\tOnBatchResultProcessing...");
|
||||
dr = args.DataReader as SqlDataReader;
|
||||
int count = 0;
|
||||
while (dr.Read() && !cancelEventFired)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
Console.WriteLine("\t\tOnBatchResultProcessing: Records returned: " + count);
|
||||
resultCounts.Add(count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,787 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.Test.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
{
|
||||
/// <summary>
|
||||
///This is a test class for Microsoft.Data.Tools.Schema.Common.ExecutionEngine.ExecutionEngine and is intended
|
||||
///to contain all Microsoft.Data.Tools.Schema.Common.ExecutionEngine.ExecutionEngine Unit Tests
|
||||
///</summary>
|
||||
|
||||
public class ExecutionEngineTest : IDisposable
|
||||
{
|
||||
private SqlConnection connection;
|
||||
private List<int> expResultCounts = new List<int>();
|
||||
private List<string> expErrorMessage = new List<string>();
|
||||
|
||||
#region Test Initialize And Cleanup
|
||||
|
||||
public ExecutionEngineTest()
|
||||
{
|
||||
TestInitialize();
|
||||
}
|
||||
|
||||
// Initialize the tests
|
||||
public void TestInitialize()
|
||||
{
|
||||
expResultCounts = new List<int>();
|
||||
expErrorMessage = new List<string>();
|
||||
connection = SetUpConnection("test");
|
||||
}
|
||||
|
||||
// helper method to set up a Sql Connection to a database
|
||||
private SqlConnection SetUpConnection(string name)
|
||||
{
|
||||
SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, name);
|
||||
ConnectionInfo connInfo = TestObjects.InitLiveConnectionInfoForDefinition(testDb.DatabaseName);
|
||||
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
|
||||
SqlConnection resultConnection = new SqlConnection(connectionString);
|
||||
resultConnection.Open();
|
||||
return resultConnection;
|
||||
}
|
||||
|
||||
// Helper method to close a connection completely
|
||||
private void CloseConnection(SqlConnection conn)
|
||||
{
|
||||
if (conn != null)
|
||||
{
|
||||
conn.Close();
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//Use Dispose to close connection after each test has run
|
||||
//
|
||||
public void Dispose()
|
||||
{
|
||||
CloseConnection(connection);
|
||||
connection = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Valid scripts
|
||||
/// <summary>
|
||||
///A test for a simple SQL script
|
||||
///</summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_SimpleTest()
|
||||
{
|
||||
string sqlStatement = "SELECT * FROM sysobjects";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
Assert.Equal(1, executor.BatchFinshedEventCounter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with a valid script using default execution condition
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_DefaultCondition_ValidScript()
|
||||
{
|
||||
string sqlStatement = "select * from sysobjects\nGo\n";
|
||||
|
||||
//Use default execution condition
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, false);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// Test with multiple valid scripts in multiple batches
|
||||
// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MultiValidScripts()
|
||||
{
|
||||
string sqlStatement = "select * from sys.databases\ngo\nselect name from sys.databases\ngo\nprint 'test'\ngo";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, false);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with SQL comment
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_TestComment()
|
||||
{
|
||||
string sqlStatement = "/*test comments*/";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Invalid Scripts
|
||||
/// <summary>
|
||||
/// Test with a invalid query using the default execution condition
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_DefaultCondition_InvalidScript()
|
||||
{
|
||||
string sqlStatement = "select ** from sysobjects";
|
||||
//Use default execution condition
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, false);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal( ScriptExecutionResult.Success | ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
Assert.Equal(0, executor.BatchFinshedEventCounter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with an invalid query using a defined execution condition
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_InvalidScriptWithCondition()
|
||||
{
|
||||
string sqlStatement = "select * from authors";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(executor.ExecutionResult, ScriptExecutionResult.Success | ScriptExecutionResult.Failure);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with multiple invalid scripts in multiple batches
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MultipleInvalidScript()
|
||||
{
|
||||
string sqlStatement = "select ** from products \ngo\n insert into products values (1,'abc')\n go \n";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(executor.ExecutionResult, ScriptExecutionResult.Success | ScriptExecutionResult.Failure);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with invalid scripts within a single batch
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MultipleInvalidScript_SingleBatch()
|
||||
{
|
||||
string sqlStatement = "select ** from products \n insert into products values (1,'abc')\n go \n";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success | ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with mixed valid and invalid scripts
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MixedValidandInvalidScript()
|
||||
{
|
||||
string sqlStatement = "SELECT * FROM Authors \n Go\n select * from sysobjects \n go\nif exists (select * from sysobjects where id = object_id('MyTab')) DROP TABLE MyTab2";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(executor.ExecutionResult, ScriptExecutionResult.Success | ScriptExecutionResult.Failure);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Different execution conditions
|
||||
/// <summary>
|
||||
/// Test HaltOnError execution condition
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_HaltOnError()
|
||||
{
|
||||
string sqlStatement = "select * from authors\n go\n select * from sysbojects \n go \n";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = true;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Halted | ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
Assert.True(executor.ResultCountQueue.Count == 0);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HaltOnError with a single batch
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_HaltOnError_OneBatch()
|
||||
{
|
||||
string sqlStatement = "select * from authors\n go 30\n";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = true;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Halted | ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
Assert.True(CompareTwoIntLists(executor.ResultCountQueue, expResultCounts));
|
||||
Assert.True(executor.ResultCountQueue.Count == 0);
|
||||
Assert.Equal(0, executor.BatchFinshedEventCounter);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test ParseOnly execution condition with valid scripts
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_ParseOnly_ValidScript()
|
||||
{
|
||||
string sqlStatement = "select * from sysobjects";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = true;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(executor.ResultCountQueue.Count == 0);
|
||||
Assert.Equal(0, executor.BatchFinshedEventCounter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test HaltOnError execution condition with invalid scripts
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_ParseOnly_InvalidScript()
|
||||
{
|
||||
string sqlStatement = "select ** from authors";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = true;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Get the expected values
|
||||
List<string> batchScripts = executor.BatchScripts;
|
||||
ExecuteSqlBatch(batchScripts, connection);
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success | ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
Assert.True(!executor.ParserExecutionError);
|
||||
Assert.True(executor.ResultCountQueue.Count == 0);
|
||||
Assert.True(CompareTwoStringLists(executor.ErrorMessageQueue, expErrorMessage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse script only without transaction wrapper
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_ParseOnly_ValidScriptWithoutTransaction()
|
||||
{
|
||||
string sqlStatement = "select * from sysobjects";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = false;
|
||||
conditions.IsParseOnly = true;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
Assert.Equal(ScriptExecutionResult.Success, executor.ExecutionResult);
|
||||
Assert.True(executor.ResultCountQueue.Count == 0);
|
||||
Assert.Equal(0, executor.BatchFinshedEventCounter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with execution timeout value
|
||||
/// </summary>
|
||||
//TEST_DOESNOTWORK[TestMethod()]
|
||||
public void ExecutionEngineTest_TimeOut()
|
||||
{
|
||||
string sqlStatement = "select * from sysobjects\n go 10\n";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, -1);
|
||||
executor.Run();
|
||||
Assert.Equal(executor.ExecutionResult, ScriptExecutionResult.Success);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test with invalid connection
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_InvalidConnection()
|
||||
{
|
||||
string sqlStatement = "select * from sysobjects\n go 100\n";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
connection.Close();
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
// Note: this used to also return Halted at some point in the distant past.
|
||||
// However since that gets mapped to Failure anyhow, consider "Failure" as acceptable here
|
||||
Assert.True(executor.ExecutionResult.HasFlag(ScriptExecutionResult.Failure), "Expected failure when invalid connection is present" );
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SQL Commands
|
||||
/// <summary>
|
||||
/// Test with SQL commands
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_SQLCmds()
|
||||
{
|
||||
string[] sqlStatements = {
|
||||
"select $(INVALIDVAR) from sysobjects",
|
||||
":help",
|
||||
"exit",
|
||||
"quit",
|
||||
"!! dir",
|
||||
"ed",
|
||||
"reset",
|
||||
":list",
|
||||
":listvar",
|
||||
":serverlist",
|
||||
":on error ignore",
|
||||
":connect hypothermia -t 300 -U foo -P bar",
|
||||
":out $(SystemDrive)\\test.txt",
|
||||
":r $(SystemDrive)\\test.txt",
|
||||
":error STDOUT",
|
||||
":perftrace STDOUT",
|
||||
"exit (Select count(*) from sysobjects)"
|
||||
};
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
|
||||
foreach (string stmt in sqlStatements)
|
||||
{
|
||||
TestExecutor executor = new TestExecutor(stmt, connection, conditions);
|
||||
executor.Run();
|
||||
|
||||
//Assert.AreEqual(ScriptExecutionResult.Failure, executor.ExecutionResult);
|
||||
//Assert.IsTrue(executor.ResultCountQueue.Count == 0);
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Threading
|
||||
/// <summary>
|
||||
/// Test synchous cancel
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_SyncCancel()
|
||||
{
|
||||
string sqlStatement = "waitfor delay '0:0:10'";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, true);
|
||||
executor.CancelTimeOut = 3000;
|
||||
executor.Run();
|
||||
|
||||
Assert.NotNull(executor.ScriptExecuteThread);
|
||||
Assert.Equal(ScriptExecutionResult.Cancel, executor.ExecutionResult);
|
||||
Assert.True(executor.CancelEventFired);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test asynchronous cancel
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_ASyncCancel()
|
||||
{
|
||||
//string sqlStatement = "--This is a test\nSELECT * FROM sysobjects as t\nGO 50\n use pubsplus \n select * from titles\n go" ;
|
||||
|
||||
string sqlStatement = "waitfor delay '0:0:10'";
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, true);
|
||||
executor.SyncCancel = false;
|
||||
executor.Run();
|
||||
|
||||
Assert.True(executor.CancelEventFired);
|
||||
Assert.NotNull(executor.ScriptExecuteThread);
|
||||
if (executor.ScriptExecuteThread != null)
|
||||
Assert.True(!executor.ScriptExecuteThread.IsAlive);
|
||||
Assert.Equal(ScriptExecutionResult.Cancel, executor.ExecutionResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test sync cancel when the execution is done
|
||||
/// </summary>
|
||||
///
|
||||
/// Disabled test, has race condition where Sql statement will finish
|
||||
/// before harness has an opportunity to cancel.
|
||||
//TEST_DOESNOTWORK[TestMethod()]
|
||||
public void ExecutionEngineTest_SyncCancelAfterExecutionDone()
|
||||
{
|
||||
string sqlStatement = "select 1" ;
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, true);
|
||||
executor.Run();
|
||||
|
||||
Assert.True(!executor.CancelEventFired);
|
||||
Assert.NotNull(executor.ScriptExecuteThread);
|
||||
if (executor.ScriptExecuteThread != null)
|
||||
Assert.True(!executor.ScriptExecuteThread.IsAlive);
|
||||
Assert.Equal(ScriptExecutionResult.Success | ScriptExecutionResult.Cancel, executor.ExecutionResult);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test async cancel when the execution is done
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_ASyncCancelAfterExecutionDone()
|
||||
{
|
||||
string sqlStatement ="select 1";
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
TestExecutor executor = new TestExecutor(sqlStatement, connection, conditions, true);
|
||||
executor.SyncCancel = false;
|
||||
executor.Run();
|
||||
|
||||
Assert.True(!executor.CancelEventFired);
|
||||
Assert.NotNull(executor.ScriptExecuteThread);
|
||||
if (executor.ScriptExecuteThread != null)
|
||||
Assert.True(!executor.ScriptExecuteThread.IsAlive);
|
||||
Assert.Equal(ScriptExecutionResult.Success | ScriptExecutionResult.Cancel, executor.ExecutionResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test multiple threads of execution engine with cancel operation
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MultiThreading_WithCancel()
|
||||
{
|
||||
string[] sqlStatement = { "waitfor delay '0:0:10'",
|
||||
"waitfor delay '0:0:10'",
|
||||
"waitfor delay '0:0:10'"
|
||||
};
|
||||
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
conditions.IsTransactionWrapped = true;
|
||||
conditions.IsParseOnly = false;
|
||||
conditions.IsHaltOnError = false;
|
||||
|
||||
SqlConnection connection2 = SetUpConnection("test4");
|
||||
SqlConnection connection3 = SetUpConnection("test5");
|
||||
|
||||
TestExecutor executor1 = new TestExecutor(sqlStatement[0], connection, conditions, true);
|
||||
executor1.CancelTimeOut = 2000;
|
||||
TestExecutor executor2 = new TestExecutor(sqlStatement[1], connection2, conditions, true);
|
||||
executor1.CancelTimeOut = 2500;
|
||||
TestExecutor executor3 = new TestExecutor(sqlStatement[2], connection3, conditions, true);
|
||||
executor1.CancelTimeOut = 3000;
|
||||
|
||||
Thread t1 = new Thread(new ThreadStart(executor1.Run));
|
||||
Thread t2 = new Thread(new ThreadStart(executor2.Run));
|
||||
Thread t3 = new Thread(new ThreadStart(executor3.Run));
|
||||
|
||||
t1.Name = "Executor1";
|
||||
t1.Start();
|
||||
t2.Name = "Executor2";
|
||||
t2.Start();
|
||||
t3.Name = "Executor3";
|
||||
t3.Start();
|
||||
|
||||
while ((t1.ThreadState != ThreadState.Stopped) &&
|
||||
(t2.ThreadState != ThreadState.Stopped) &&
|
||||
(t3.ThreadState != ThreadState.Stopped))
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
Assert.True(!executor1.ScriptExecuteThread.IsAlive);
|
||||
Assert.True(!executor2.ScriptExecuteThread.IsAlive);
|
||||
Assert.True(!executor3.ScriptExecuteThread.IsAlive);
|
||||
|
||||
Assert.True(executor1.CancelEventFired);
|
||||
Assert.True(executor2.CancelEventFired);
|
||||
Assert.True(executor3.CancelEventFired);
|
||||
|
||||
CloseConnection(connection2);
|
||||
CloseConnection(connection3);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
/// <summary>
|
||||
/// Connection to a database
|
||||
/// </summary>
|
||||
/// <param name="server">Server name</param>
|
||||
/// <param name="database">DB name</param>
|
||||
/// <returns></returns>
|
||||
private SqlConnection ConnectToDB(string server, string database)
|
||||
{
|
||||
return new SqlConnection(string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True;", server, database));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution a script batch
|
||||
/// </summary>
|
||||
/// <param name="sqlBatch">A list of SQL queries</param>
|
||||
/// <param name="connection">SQL connection</param>
|
||||
private void ExecuteSqlBatch(List<string> sqlBatch, SqlConnection connection)
|
||||
{
|
||||
foreach (string script in sqlBatch)
|
||||
{
|
||||
ExecuteSqlCommand(script, connection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execution one sql command
|
||||
/// </summary>
|
||||
/// <param name="sqlCmdTxt">SQL query</param>
|
||||
/// <param name="connection">SQL connection</param>
|
||||
private void ExecuteSqlCommand(string sqlCmdTxt, SqlConnection connection)
|
||||
{
|
||||
SqlCommand cmd = new SqlCommand(sqlCmdTxt, connection);
|
||||
SqlTransaction transaction = connection.BeginTransaction();
|
||||
cmd.Transaction = transaction;
|
||||
|
||||
try
|
||||
{
|
||||
using (SqlDataReader dr = cmd.ExecuteReader())
|
||||
{
|
||||
int count = 0;
|
||||
while (dr.Read())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
expResultCounts.Add(count);
|
||||
}
|
||||
}
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Executing command throws exception: " + e.Message);
|
||||
expErrorMessage.Add(e.Message);
|
||||
try
|
||||
{
|
||||
transaction.Rollback();
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
Console.WriteLine("Rollback throws exception");
|
||||
Console.WriteLine("Message: " + e2.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two string lists
|
||||
/// </summary>
|
||||
/// <param name="l1">first list</param>
|
||||
/// <param name="l2">second list</param>
|
||||
/// <returns>True if the contents are same, otherwise false</returns>
|
||||
private bool CompareTwoStringLists(List<string> l1, List<string> l2)
|
||||
{
|
||||
bool isSame = true;
|
||||
if(l1.Count != l2.Count)
|
||||
{
|
||||
isSame = false;
|
||||
Console.WriteLine("The count of elements in two lists are not the same");
|
||||
return isSame;
|
||||
}
|
||||
|
||||
for (int i = 0; i < l1.Count; i++)
|
||||
{
|
||||
if (l1[i] != l2[i])
|
||||
{
|
||||
isSame = false;
|
||||
Console.WriteLine("l1: {0}, l2: {1}", l1[i], l2[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isSame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare with integer list
|
||||
/// </summary>
|
||||
/// <param name="l1">first list</param>
|
||||
/// <param name="l2">second list</param>
|
||||
/// <returns>True if the two list's contents are same, otherwise false</returns>
|
||||
private bool CompareTwoIntLists(List<int> l1, List<int> l2)
|
||||
{
|
||||
bool isSame = true;
|
||||
if (l1.Count != l2.Count)
|
||||
{
|
||||
isSame = false;
|
||||
Console.WriteLine("The count of elements in two lists are not the same");
|
||||
return isSame;
|
||||
}
|
||||
|
||||
for (int i = 0; i < l1.Count; i++)
|
||||
{
|
||||
if (l1[i] != l2[i])
|
||||
{
|
||||
isSame = false;
|
||||
Console.WriteLine("l1: {0}, l2: {1}", l1[i], l2[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isSame;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
{
|
||||
class TestExecutor : IDisposable
|
||||
{
|
||||
#region Private variables
|
||||
string sqlStatement;
|
||||
ExecutionEngineConditions conditions = new ExecutionEngineConditions();
|
||||
BatchEventHandler eventHandler = new BatchEventHandler();
|
||||
SqlConnection connection = null;
|
||||
static Thread _executionThread;
|
||||
bool _syncCancel = true;
|
||||
bool _isFinished = false;
|
||||
bool _cancel = false;
|
||||
int _cancelTimeout = 500;
|
||||
int exeTimeOut = 0;
|
||||
|
||||
//For verification
|
||||
List<int> resultCounts = new List<int>();
|
||||
List<string> sqlMessages = new List<string>();
|
||||
List<string> errorMessage = new List<string>();
|
||||
List<bool> batchFinished = new List<bool>();
|
||||
static ScriptExecutionResult execResult = ScriptExecutionResult.All;
|
||||
static List<string> batchScripts = new List<string>();
|
||||
static Thread exeThread = null;
|
||||
static bool parserExecutionError = false;
|
||||
#endregion
|
||||
|
||||
#region private methods
|
||||
/// <summary>
|
||||
/// Execut the script
|
||||
/// </summary>
|
||||
/// <param name="exec">Execution Engine</param>
|
||||
/// <param name="connection">SQL connection</param>
|
||||
/// <param name="script">script text</param>
|
||||
/// <param name="conditions">Execution condition</param>
|
||||
/// <param name="batchHandler">Batch event handler</param>
|
||||
/// <param name="timeout">time out value</param>
|
||||
static void ExecuteScript(ExecutionEngine exec, SqlConnection connection, string script, ExecutionEngineConditions conditions, IBatchEventsHandler batchHandler, int timeout)
|
||||
{
|
||||
Validate.IsNotNull(nameof(exec), exec);
|
||||
Validate.IsNotNull(nameof(connection), connection);
|
||||
Validate.IsNotNullOrEmptyString(nameof(script), script);
|
||||
Validate.IsNotNull(nameof(conditions), conditions);
|
||||
|
||||
Console.WriteLine("------------------------ Executing Script ----------------------");
|
||||
|
||||
//exec.BeginScriptExecution(script, connection, timeout, conditions, batchConsumer);
|
||||
ScriptExecutionArgs args = new ScriptExecutionArgs(script, connection, timeout, conditions, batchHandler);
|
||||
//exec.ExecuteScript(args);
|
||||
|
||||
_executionThread = new Thread(new ParameterizedThreadStart(exec.ExecuteScript));
|
||||
_executionThread.Start(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the execution
|
||||
/// </summary>
|
||||
/// <param name="exec">Execution Engine</param>
|
||||
/// <param name="isSynchronous">Cancel the execution synchronously or not</param>
|
||||
/// <param name="timeout">sycn canceo timeout</param>
|
||||
static void Cancel(ExecutionEngine exec, bool isSynchronous, int millisecondsTimeOut)
|
||||
{
|
||||
//exec.BeginCancellingExecution(isSynchronous, timeout);
|
||||
|
||||
if (_executionThread == null ||
|
||||
_executionThread.ThreadState == System.Threading.ThreadState.Unstarted ||
|
||||
_executionThread.ThreadState == System.Threading.ThreadState.Stopped)
|
||||
{
|
||||
exec.Close(isSynchronous, /* isDiscard */ false, /* isFinishExecution */ true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// activates the cancel thread
|
||||
Thread cancelThread = new Thread(new ThreadStart(exec.CancelCurrentBatch));
|
||||
cancelThread.Name = "Cancelling thread";
|
||||
cancelThread.Start();
|
||||
|
||||
// in a syncrhonous call, we need to block and wait until the thread is stopped
|
||||
if (isSynchronous)
|
||||
{
|
||||
int totalSleep = 0;
|
||||
while (totalSleep < millisecondsTimeOut && _executionThread != null && _executionThread.IsAlive)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
totalSleep += 50;
|
||||
}
|
||||
|
||||
if (_executionThread != null && _executionThread.IsAlive)
|
||||
{
|
||||
exec.Close(isSynchronous, /* isDiscard */ true);
|
||||
}
|
||||
else
|
||||
{
|
||||
exec.Close(/* isCloseConnection */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
public bool SyncCancel
|
||||
{
|
||||
get
|
||||
{
|
||||
return _syncCancel;
|
||||
}
|
||||
set
|
||||
{
|
||||
_syncCancel = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int CancelTimeOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cancelTimeout;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cancelTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptExecutionResult ExecutionResult
|
||||
{
|
||||
get
|
||||
{
|
||||
return execResult;
|
||||
}
|
||||
}
|
||||
|
||||
public List<int> ResultCountQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return resultCounts;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> SQLMessageQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return sqlMessages;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> ErrorMessageQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> BatchScripts
|
||||
{
|
||||
get
|
||||
{
|
||||
return batchScripts;
|
||||
}
|
||||
}
|
||||
|
||||
public int BatchFinshedEventCounter
|
||||
{
|
||||
get
|
||||
{
|
||||
return eventHandler.BatchfinishedEventCounter;
|
||||
}
|
||||
}
|
||||
|
||||
public Thread ScriptExecuteThread
|
||||
{
|
||||
get
|
||||
{
|
||||
return exeThread;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CancelEventFired
|
||||
{
|
||||
get
|
||||
{
|
||||
return eventHandler.CancelFired;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ParserExecutionError
|
||||
{
|
||||
get
|
||||
{
|
||||
return parserExecutionError;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public TestExecutor(string batch, SqlConnection conn, ExecutionEngineConditions exeCondition): this(batch, conn, exeCondition, false)
|
||||
{
|
||||
}
|
||||
|
||||
public TestExecutor(string batch, SqlConnection conn, ExecutionEngineConditions exeCondition, bool cancelExecution)
|
||||
{
|
||||
sqlStatement = batch;
|
||||
conditions.IsHaltOnError = exeCondition.IsHaltOnError;
|
||||
conditions.IsParseOnly = exeCondition.IsParseOnly;
|
||||
conditions.IsTransactionWrapped = exeCondition.IsTransactionWrapped;
|
||||
|
||||
_cancel = cancelExecution;
|
||||
connection = conn;
|
||||
|
||||
//Initialize the static variables
|
||||
execResult = ScriptExecutionResult.All;
|
||||
batchScripts = new List<string>();
|
||||
exeThread = null;
|
||||
parserExecutionError = false;
|
||||
|
||||
}
|
||||
public TestExecutor(string batch, SqlConnection conn, ExecutionEngineConditions exeCondition, int timeOut)
|
||||
: this(batch, conn, exeCondition, false)
|
||||
{
|
||||
exeTimeOut = timeOut;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region public methods
|
||||
/// <summary>
|
||||
/// Execute the test engine
|
||||
/// </summary>
|
||||
public void Run()
|
||||
{
|
||||
Console.WriteLine("Executing scripts {0} ...", sqlStatement);
|
||||
|
||||
using (ExecutionEngine exec = new ExecutionEngine())
|
||||
{
|
||||
_isFinished = false;
|
||||
exec.BatchParserExecutionStart += new EventHandler<BatchParserExecutionStartEventArgs>(OnBatchParserExecutionStart);
|
||||
exec.BatchParserExecutionFinished += new EventHandler<BatchParserExecutionFinishedEventArgs>(OnBatchParserExecutionFinished);
|
||||
exec.BatchParserExecutionError += new EventHandler<BatchParserExecutionErrorEventArgs>(OnBatchParserExecutionError);
|
||||
exec.ScriptExecutionFinished += new EventHandler<ScriptExecutionFinishedEventArgs>(OnExecutionFinished);
|
||||
|
||||
ExecuteScript(exec, connection, sqlStatement, conditions, eventHandler, exeTimeOut);
|
||||
|
||||
if (!_cancel)
|
||||
{
|
||||
//Do not cancel the execution engine
|
||||
while (!_isFinished)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_isFinished)
|
||||
{
|
||||
Console.WriteLine("Need to cancel while the batch execution is not finished!");
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Canceling after the exe engine is disposed...");
|
||||
}
|
||||
Cancel(exec, _syncCancel, _cancelTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ParserEvent
|
||||
/// <summary>
|
||||
/// Called when batch is called
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
static void OnBatchParserExecutionStart(object sender, BatchParserExecutionStartEventArgs e)
|
||||
{
|
||||
Console.WriteLine("****************");
|
||||
Console.WriteLine(e.Batch.Text);
|
||||
batchScripts.Add(e.Batch.Text);
|
||||
Console.WriteLine("****************");
|
||||
|
||||
Console.WriteLine("ON_BATCH_PARSER_EXECUTION_START : Start executing batch... " + e.Batch + " at line " + e.TextSpan.iStartLine);
|
||||
exeThread = Thread.CurrentThread;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when batch is done
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
static void OnBatchParserExecutionFinished(object sender, BatchParserExecutionFinishedEventArgs e)
|
||||
{
|
||||
Console.WriteLine("ON_BATCH_PARSER_EXECUTION_FINISHED : Done executing batch \n\t{0}\n\t with result... {1} ", e.Batch.Text, e.ExecutionResult);
|
||||
if (execResult == ScriptExecutionResult.All)
|
||||
execResult = e.ExecutionResult;
|
||||
else
|
||||
execResult = execResult | e.ExecutionResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when batch pasing found a warning/error
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
static void OnBatchParserExecutionError(object sender, BatchParserExecutionErrorEventArgs e)
|
||||
{
|
||||
Console.WriteLine("ON_BATCH_PARSER_EXECUTION_ERROR : {0} found... at line {1}: {2}", e.MessageType.ToString(), e.Line.ToString(), e.Message);
|
||||
Console.WriteLine("\t Error Description: " + e.Description);
|
||||
parserExecutionError = true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when script is done
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnExecutionFinished(object sender, ScriptExecutionFinishedEventArgs e)
|
||||
{
|
||||
Console.WriteLine("ON_EXECUTION_FINISHED : Script execution done with result ..." + e.ExecutionResult);
|
||||
_isFinished = true;
|
||||
|
||||
if (execResult == ScriptExecutionResult.All)
|
||||
execResult = e.ExecutionResult;
|
||||
else
|
||||
execResult = execResult|e.ExecutionResult;
|
||||
|
||||
resultCounts = eventHandler.ResultCounts;
|
||||
sqlMessages = eventHandler.SqlMessages;
|
||||
errorMessage = eventHandler.ErrorMessages;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined
|
||||
{
|
||||
/// <summary>
|
||||
/// This class serves as the base class for all baselined tests
|
||||
/// It will provide easy services for you to interact with your test files and their baselines
|
||||
/// </summary>
|
||||
public abstract class BaselinedTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the extension for the TestScripts
|
||||
/// </summary>
|
||||
private string _testScriptExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the extensionf or the Baseline files
|
||||
/// </summary>
|
||||
private string _baselineExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the path to the base location of both TestScripts and Baselines
|
||||
/// </summary>
|
||||
private string _testCategoryName;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the ROOT Dir for trace output
|
||||
/// </summary>
|
||||
private string _traceOutputDir;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the prefix for the baseline
|
||||
/// </summary>
|
||||
private string _baselinePrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the prefix for the Testscript
|
||||
/// </summary>
|
||||
private string _testscriptPrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the name of the current test
|
||||
/// </summary>
|
||||
private string _currentTestname;
|
||||
|
||||
private string _baselineSubDir = string.Empty;
|
||||
|
||||
public const string TestScriptDirectory = @"Testscripts\";
|
||||
public const string BaselineDirectory = @"Baselines\";
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the extension for the Testscript files
|
||||
/// </summary>
|
||||
public string TestscriptFileExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return _testScriptExtension;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("TestscriptFileExtension needs a value");
|
||||
_testScriptExtension = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the extension for the Baseline files
|
||||
/// </summary>
|
||||
public string BaselineFileExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return _baselineExtension;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("BaselineFileExtension needs a value");
|
||||
_baselineExtension = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the path to the base location of both test scripts and baseline files
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Just use the SubDir name
|
||||
/// TestScripts should be in FileBaseLocation\Testscripts; and Baselines should be in FileBaseLocation\Baselines
|
||||
/// The value of this will be appended to ROOT_DIR (QA\SrcUTest\Common)
|
||||
/// </remarks>
|
||||
public string CategoryName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _testCategoryName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("FileBaseLocation needs a value");
|
||||
_testCategoryName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the output base directory for trace output (null = no trace output)
|
||||
/// </summary>
|
||||
public string TraceOutputDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return _traceOutputDir;
|
||||
}
|
||||
set
|
||||
{
|
||||
_traceOutputDir = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of where the files will be pulled from
|
||||
/// </summary>
|
||||
public string FilesLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(RunEnvironmentInfo.GetTestDataLocation(), CategoryName, TestScriptDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the sub directory in Baselines where the exected baseline results are located
|
||||
/// </summary>
|
||||
public string BaselinesSubdir
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baselineSubDir == null)
|
||||
this._baselineSubDir = string.Empty;
|
||||
return this._baselineSubDir;
|
||||
}
|
||||
set { this._baselineSubDir = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of where the baseline files will be pulled from
|
||||
/// </summary>
|
||||
public string BaselineFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(RunEnvironmentInfo.GetTestDataLocation(), CategoryName, Path.Combine( BaselineDirectory, BaselinesSubdir ));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path of where the Trace will output
|
||||
/// </summary>
|
||||
public string TraceFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Path.GetFullPath(TraceOutputDirectory), this.CategoryName, this.BaselinesSubdir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the prefix used for baseline files
|
||||
/// </summary>
|
||||
public string BaselinePrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return _baselinePrefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("BaselinePrefix needs a value");
|
||||
_baselinePrefix = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the prefix used for testscript files
|
||||
/// </summary>
|
||||
public string TestscriptPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return _testscriptPrefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("TestscriptPrefix needs a value");
|
||||
_testscriptPrefix = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the name of the current test
|
||||
/// </summary>
|
||||
public string CurrentTestName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTestname;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public BaselinedTest()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the class
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
_testScriptExtension = _baselineExtension = "txt"; //default to txt
|
||||
_testCategoryName = null;
|
||||
string projectPath = Environment.GetEnvironmentVariable(Constants.ProjectPath);
|
||||
if (projectPath != null)
|
||||
{
|
||||
_traceOutputDir = Path.Combine(projectPath, "trace");
|
||||
}
|
||||
else
|
||||
{
|
||||
_traceOutputDir = Environment.ExpandEnvironmentVariables(@"%SystemDrive%\trace\");
|
||||
}
|
||||
_baselinePrefix = "BL";
|
||||
_testscriptPrefix = "TS";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method should be called whenever you do a [TestInitialize]
|
||||
/// </summary>
|
||||
public virtual void TestInitialize()
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(_testCategoryName))
|
||||
throw new ArgumentException("Set CategoryName to the name of the directory containing your Testscripts and Baseline files");
|
||||
|
||||
if (!Directory.Exists(FilesLocation))
|
||||
throw new FileNotFoundException(string.Format("Path to Testscripts ([{0}]) does not exist.", FilesLocation));
|
||||
if (!Directory.Exists(BaselineFilePath))
|
||||
throw new FileNotFoundException(string.Format("Path to Baseline Files [{0}] does not exist.", BaselineFilePath));
|
||||
if (!string.IsNullOrEmpty(TraceFilePath) && !Directory.Exists(TraceFilePath)) //if this does not exist, then we want it (pronto)
|
||||
Directory.CreateDirectory(TraceFilePath);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two strings and gives appropriate output
|
||||
/// </summary>
|
||||
/// <param name="actualContent">Actual string</param>
|
||||
/// <param name="baselineContent">Expected string</param>
|
||||
/// <remarks>Fails test if strings do not match; comparison is done using an InvariantCulture StringComparer</remarks>
|
||||
public void CompareActualWithBaseline(string actualContent, string baselineContent)
|
||||
{
|
||||
|
||||
int _compareResult = string.Compare(actualContent, baselineContent, StringComparison.OrdinalIgnoreCase);
|
||||
if (_compareResult != 0)
|
||||
{
|
||||
Trace.WriteLine("Debug Info:");
|
||||
Trace.WriteLine("========BEGIN=EXPECTED========");
|
||||
Trace.WriteLine(baselineContent);
|
||||
Trace.WriteLine("=========END=EXPECTED=========");
|
||||
Trace.WriteLine("=========BEGIN=ACTUAL=========");
|
||||
Trace.WriteLine(actualContent);
|
||||
Trace.WriteLine("==========END=ACTUAL==========");
|
||||
Assert.True(false, string.Format("Comparison failed! (actualContent {0} baselineContent)", (_compareResult < 0 ? "<" : ">"))); //we already know it is not equal
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.WriteLine("Compare match! All is fine...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the testscript with the provided name
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the test</param>
|
||||
/// <returns>the path to the baseline file</returns>
|
||||
/// <remarks>Asserts that file exists</remarks>
|
||||
public string GetTestscriptFilePath(string name)
|
||||
{
|
||||
string retVal = Path.Combine(FilesLocation, string.Format("{0}-{1}.{2}", TestscriptPrefix, name, TestscriptFileExtension));
|
||||
Assert.True(File.Exists(retVal), string.Format("TestScript [{0}] does not exist", retVal));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the test script with the provided name and the provided index
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the test</param>
|
||||
/// <param name="index">File index</param>
|
||||
/// <returns>the path to the baseline file</returns>
|
||||
/// <remarks>Asserts that file exists</remarks>
|
||||
public string GetTestscriptFilePath(string name, int index)
|
||||
{
|
||||
string retVal = Path.Combine(FilesLocation, string.Format("{0}-{1}{2}.{3}", TestscriptPrefix, name, index.ToString(), TestscriptFileExtension));
|
||||
Assert.True(File.Exists(retVal), string.Format("TestScript [{0}] does not exist", retVal));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted baseline file name
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the test</param>
|
||||
public string GetBaselineFileName(string name)
|
||||
{
|
||||
return string.Format("{0}-{1}.{2}", BaselinePrefix, name, BaselineFileExtension);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path to the baseline file for the named case
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the test</param>
|
||||
/// <returns>the path to the baseline file</returns>
|
||||
/// <remarks>Asserts that file exists</remarks>
|
||||
public string GetBaselineFilePath(string name, bool assertIfNotFound)
|
||||
{
|
||||
string retVal = Path.Combine(BaselineFilePath, GetBaselineFileName(name));
|
||||
|
||||
if (assertIfNotFound)
|
||||
{
|
||||
Assert.True(File.Exists(retVal), string.Format("Baseline [{0}] does not exist", retVal));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public string GetBaselineFilePath(string name)
|
||||
{
|
||||
return GetBaselineFilePath(name, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of a file
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file to read</param>
|
||||
/// <returns>The contents of the file</returns>
|
||||
public string GetFileContent(string path)
|
||||
{
|
||||
Trace.WriteLine(string.Format("GetFileContent for [{0}]", Path.GetFullPath(path)));
|
||||
|
||||
using (StreamReader sr = new StreamReader(File.Open(path, FileMode.Open), Encoding.Unicode))
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the text to a Trace file
|
||||
/// </summary>
|
||||
/// <param name="testName">Test name used to create file name</param>
|
||||
/// <param name="text">Text to dump to the trace file</param>
|
||||
/// <remarks>Overwrites whatever is already in the file (if anything)</remarks>
|
||||
public string DumpToTrace(string testName, string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(TraceFilePath))
|
||||
{
|
||||
return string.Empty; //nothing to do
|
||||
}
|
||||
|
||||
string traceFile = Path.Combine(TraceFilePath, GetBaselineFileName(testName));
|
||||
|
||||
if (File.Exists(traceFile))
|
||||
{
|
||||
Trace.Write(string.Format("Overwriting existing trace file [{0}]", traceFile));
|
||||
File.Delete(traceFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Write(string.Format("Dumping to trace file [{0}]", traceFile));
|
||||
}
|
||||
|
||||
if (Directory.Exists(TraceFilePath) == false)
|
||||
{
|
||||
Directory.CreateDirectory(TraceFilePath);
|
||||
}
|
||||
WriteTraceFile(traceFile, text);
|
||||
return traceFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the context to the trace file
|
||||
/// </summary>
|
||||
/// <param name="traceFile">The file name for the trace output</param>
|
||||
/// <param name="text">The content for the trace file</param>
|
||||
public void WriteTraceFile(string traceFile, string text)
|
||||
{
|
||||
Stream traceStream = GetStreamFromString(traceFile);
|
||||
using (StreamWriter sw = new StreamWriter(traceStream, Encoding.Unicode))
|
||||
{
|
||||
sw.Write(text);
|
||||
sw.Flush();
|
||||
sw.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to a stream
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
private Stream GetStreamFromString(string s)
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
StreamWriter writer = new StreamWriter(stream);
|
||||
writer.Write(s);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the actual running of the test after nicely initializing
|
||||
/// </summary>
|
||||
/// <param name="testname">Name of the test</param>
|
||||
public void Start(string testname)
|
||||
{
|
||||
Trace.WriteLine(string.Format("Starting test named [{0}]", testname));
|
||||
_currentTestname = testname;
|
||||
|
||||
Run();
|
||||
|
||||
Trace.WriteLine("Test Completed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the actual test
|
||||
/// </summary>
|
||||
/// <remarks>Override this method to put in your test logic</remarks>
|
||||
public abstract void Run();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined
|
||||
{
|
||||
public abstract class BaselinedTestWithMultipleScripts : BaselinedTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the number of files that are associated with this test
|
||||
/// </summary>
|
||||
private int _fileCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the amount of scripts to process
|
||||
/// </summary>
|
||||
public int FileCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fileCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 1)
|
||||
throw new ArgumentOutOfRangeException("FileCount", value, "FileCount must be > 0");
|
||||
_fileCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public BaselinedTestWithMultipleScripts()
|
||||
: base()
|
||||
{
|
||||
//set invalid value
|
||||
_fileCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the test
|
||||
/// </summary>
|
||||
public override void Run()
|
||||
{
|
||||
//little self-assigning sanity check there (if invalid value, it will throw)
|
||||
FileCount = FileCount;
|
||||
|
||||
//process all files
|
||||
for (int n = 0; n < FileCount; n++)
|
||||
{
|
||||
ProcessFile(GetTestscriptFilePath(this.CurrentTestName, n));
|
||||
}
|
||||
|
||||
PostProcessFiles();
|
||||
|
||||
Verify();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a test with the specified fileCount
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the test</param>
|
||||
/// <param name="fileCount">Number of files</param>
|
||||
public void Start(string name, int fileCount)
|
||||
{
|
||||
FileCount = fileCount;
|
||||
base.Start(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gives you an opportunity to handle one specific file
|
||||
/// </summary>
|
||||
/// <param name="filePath">Path to the current file</param>
|
||||
public abstract void ProcessFile(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// This method gives you an opportunity to perform any actions after files are processed
|
||||
/// </summary>
|
||||
public virtual void PostProcessFiles()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method will be called when all files have been processed; add verification logic here
|
||||
/// </summary>
|
||||
public abstract void Verify();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,27 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
public static class Constants
|
||||
{
|
||||
public const string SqlConectionSettingsEnvironmentVariable = "SettingsFileName";
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable used to get the TSDATA source directory root.
|
||||
/// K2 is under it.
|
||||
/// </summary>
|
||||
public const string SourceDirectoryEnvVariable = "Enlistment_Root";
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable used to get the build output directory.
|
||||
/// DTRun will set this automatically
|
||||
/// </summary>
|
||||
public const string BinariesDirectoryEnvVariable = "DacFxBuildOutputDir";
|
||||
|
||||
public const string DDSuiteBuiltTarget = "DD_SuitesTarget";
|
||||
|
||||
public const string DBBackupFileLocation = "DBBackupPath";
|
||||
|
||||
public const string ProjectPath = "ProjectPath";
|
||||
|
||||
public const string BVTLocalRoot = "BVT_LOCALROOT";
|
||||
|
||||
public const string DBIMode = "DBI_MODE";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
public class RunEnvironmentInfo
|
||||
{
|
||||
private static string cachedTestFolderPath;
|
||||
|
||||
public static bool IsLabMode()
|
||||
{
|
||||
string bvtLabRoot = Environment.GetEnvironmentVariable(Constants.BVTLocalRoot);
|
||||
if (string.IsNullOrEmpty(bvtLabRoot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Location of all test data (baselines, etc).
|
||||
/// </summary>
|
||||
/// <returns>The full path to the test data directory</returns>
|
||||
public static string GetTestDataLocation()
|
||||
{
|
||||
string testFolderPath;
|
||||
string testPath = @"test\Microsoft.SqlTools.ServiceLayer.Test.Common\TestData";
|
||||
string projectPath = Environment.GetEnvironmentVariable(Constants.ProjectPath);
|
||||
|
||||
if (projectPath != null)
|
||||
{
|
||||
testFolderPath = Path.Combine(projectPath, testPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cachedTestFolderPath != null)
|
||||
{
|
||||
testFolderPath = cachedTestFolderPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
string defaultPath = Path.Combine(typeof(Scripts).GetTypeInfo().Assembly.Location, @"..\..\..\..\..");
|
||||
testFolderPath = Path.Combine(defaultPath, @"Microsoft.SqlTools.ServiceLayer.Test.Common\TestData");
|
||||
cachedTestFolderPath = testFolderPath;
|
||||
}
|
||||
}
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
@@ -80,6 +83,33 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns connection info after making a connection to the database
|
||||
/// </summary>
|
||||
/// <param name="serverType"></param>
|
||||
/// <param name="databaseName"></param>
|
||||
/// <param name="scriptFilePath"></param>
|
||||
/// <returns></returns>
|
||||
public ConnectionInfo InitLiveConnectionInfo(TestServerType serverType, string databaseName, string scriptFilePath)
|
||||
{
|
||||
ConnectParams connectParams = TestConnectionProfileService.Instance.GetConnectionParameters(serverType, databaseName);
|
||||
|
||||
string ownerUri = scriptFilePath;
|
||||
var connectionService = ConnectionService.Instance;
|
||||
var connectionResult = connectionService.Connect(new ConnectParams()
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = connectParams.Connection
|
||||
});
|
||||
|
||||
connectionResult.Wait();
|
||||
|
||||
ConnectionInfo connInfo = null;
|
||||
connectionService.TryFindConnection(ownerUri, out connInfo);
|
||||
Assert.NotNull(connInfo);
|
||||
return connInfo;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
@@ -92,9 +92,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
InstanceInfo instanceInfo = GetInstance(key);
|
||||
if (instanceInfo != null)
|
||||
{
|
||||
ConnectParams connenctParam = CreateConnectParams(instanceInfo, key, databaseName);
|
||||
ConnectParams connectParam = CreateConnectParams(instanceInfo, key, databaseName);
|
||||
|
||||
return connenctParam;
|
||||
return connectParam;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
/* unterminated block comment *
|
||||
@@ -0,0 +1 @@
|
||||
/* unterminated block comment
|
||||
@@ -0,0 +1,2 @@
|
||||
print 'hi'
|
||||
:r cycle2.txt
|
||||
@@ -0,0 +1,2 @@
|
||||
:setvar 0var
|
||||
-- invalid variable name
|
||||
@@ -0,0 +1,2 @@
|
||||
:setvar a b c
|
||||
-- invalid syntax (too many params)
|
||||
@@ -0,0 +1,2 @@
|
||||
-- no variable defined (at EOF)
|
||||
:setvar
|
||||
@@ -0,0 +1,2 @@
|
||||
:setvar
|
||||
-- no variable defined
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a "unterminated string
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a "b"c"d"
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a b"c"d
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a b""
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a ""b
|
||||
@@ -0,0 +1 @@
|
||||
Variable reference with no name: $()
|
||||
@@ -0,0 +1 @@
|
||||
Variable reference with invalid name: $(7var)
|
||||
@@ -0,0 +1,2 @@
|
||||
Unterminated variable reference: $(var
|
||||
iable)
|
||||
@@ -0,0 +1 @@
|
||||
Unterminated variable reference (at EOF): $(var
|
||||
@@ -0,0 +1,24 @@
|
||||
GO 2
|
||||
BEGIN
|
||||
:r input-2.txt
|
||||
:r "input-2.txt"
|
||||
:setvar ABC "My Value"
|
||||
MIDDLE
|
||||
#$(ABC)#
|
||||
:setvar ABC "NNNNNNNNNNNNNNNNN"
|
||||
END
|
||||
GO
|
||||
:setvar ABC
|
||||
:setvar B "value of B"
|
||||
:setvar A "Long value with 'fake' variable ref: $(B)"
|
||||
~$(ABC)~$~$$(A)~
|
||||
GO
|
||||
:setvar _var_1-1 VALUE
|
||||
select [1] from x
|
||||
GO 2
|
||||
:on error ignore
|
||||
:on error exit -- comment
|
||||
select 1
|
||||
/ * fake comment */
|
||||
- - fake comment
|
||||
:setv
|
||||
@@ -0,0 +1,70 @@
|
||||
/* Lexer tests */
|
||||
:setvar a "df df""
|
||||
"
|
||||
ABC
|
||||
:r
|
||||
:r input2.txt
|
||||
:r
|
||||
:setvar
|
||||
:setvar
|
||||
:setvariable asd
|
||||
GOTO
|
||||
:on errors
|
||||
:on error exit
|
||||
:on error ignore
|
||||
DEF
|
||||
|
||||
go
|
||||
go
|
||||
abc--def
|
||||
select '$$' '$$(s)asd)' werwer$$(Y)ss
|
||||
:Setvar x y
|
||||
:Setvar Y "VALUE OF Y"
|
||||
from x
|
||||
GO 2
|
||||
:SETVAR Z "WE"
|
||||
:include x
|
||||
This is a comment: /*
|
||||
:SETVAR w e
|
||||
"
|
||||
*/
|
||||
Select 1
|
||||
go 3
|
||||
:on error exit
|
||||
go
|
||||
select 2Y==>$(Y)
|
||||
A==>$(a)<==
|
||||
:setvar x "y"
|
||||
:setvar z "z"
|
||||
select $(x_y)
|
||||
from DUAL
|
||||
|
||||
-- on error commands
|
||||
:on error exit
|
||||
:on error ignore
|
||||
:on error $(variable)
|
||||
:on error invalid value
|
||||
|
||||
-- Line comment
|
||||
/* Multi line
|
||||
block comment
|
||||
-- with nested line comment */
|
||||
|
||||
/*** block comment 2 ***/
|
||||
select '/*', '*/'
|
||||
|
||||
-- Unsupported commands
|
||||
:reset
|
||||
:ed
|
||||
:!!
|
||||
:quit
|
||||
:exit
|
||||
:serverlist
|
||||
:list
|
||||
:error errorfile.txt
|
||||
:out stdout
|
||||
:perftrace perftrace.txt
|
||||
:connect server\instance
|
||||
:help
|
||||
:xml on
|
||||
:listvar
|
||||
@@ -0,0 +1,6 @@
|
||||
/**//***//* *//** */ */
|
||||
one comment: /*/*/ */*/
|
||||
not a comment: / */
|
||||
/* block comment */
|
||||
/* block
|
||||
comment */
|
||||
@@ -0,0 +1,16 @@
|
||||
print 1
|
||||
GO--comment1
|
||||
GO --comment2
|
||||
GO 1--comment3
|
||||
:setvar a --
|
||||
:setvar b c--notcomment
|
||||
:setvar d e --comment
|
||||
:setvar f /*notcomment*/
|
||||
:setvar g h--notcomment --comment
|
||||
:setvar j k--notcomment--notcomment
|
||||
:setvar a-- b--
|
||||
:r input-2.txt --comment
|
||||
-- next line will error in parser
|
||||
:r --comment.txt
|
||||
:on error exit--comment
|
||||
:on error exit --comment
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Line comment
|
||||
a--b also a comment
|
||||
-- comment
|
||||
--comment
|
||||
-- comment -- comment
|
||||
@@ -0,0 +1,2 @@
|
||||
--
|
||||
--
|
||||
@@ -0,0 +1,3 @@
|
||||
/ * */
|
||||
-- /*
|
||||
-- */
|
||||
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
-- not a line comment
|
||||
*/
|
||||
- - not a comment
|
||||
@@ -0,0 +1,2 @@
|
||||
:setvar a "variable with
|
||||
new line"
|
||||
@@ -0,0 +1,2 @@
|
||||
:setvar a [unterminated_brace
|
||||
$(b)]
|
||||
@@ -0,0 +1 @@
|
||||
:setvar a "b""c""d"
|
||||
@@ -0,0 +1,6 @@
|
||||
:setvar a ""
|
||||
:setvar a
|
||||
:setvar a """"
|
||||
/* double-quote followed by new line: */
|
||||
:setvar a """
|
||||
"
|
||||
@@ -0,0 +1,2 @@
|
||||
print '2'
|
||||
:r TS-err-cycle1.txt
|
||||
@@ -0,0 +1 @@
|
||||
select 1
|
||||
@@ -203,9 +203,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
|
||||
{
|
||||
PeekDefinition peekDefinition = new PeekDefinition(null, null);
|
||||
var languageService = LanguageService.Instance;
|
||||
Assert.True(Directory.Exists(ServiceLayer.QueryExecution.FileUtils.PeekDefinitionTempFolder));
|
||||
Assert.True(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
|
||||
languageService.DeletePeekDefinitionScripts();
|
||||
Assert.False(Directory.Exists(ServiceLayer.QueryExecution.FileUtils.PeekDefinitionTempFolder));
|
||||
Assert.False(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -216,8 +216,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
|
||||
{
|
||||
var languageService = LanguageService.Instance;
|
||||
PeekDefinition peekDefinition = new PeekDefinition(null, null);
|
||||
ServiceLayer.QueryExecution.FileUtils.SafeDirectoryDelete(ServiceLayer.QueryExecution.FileUtils.PeekDefinitionTempFolder, true);
|
||||
Assert.False(Directory.Exists(ServiceLayer.QueryExecution.FileUtils.PeekDefinitionTempFolder));
|
||||
FileUtilities.SafeDirectoryDelete(FileUtilities.PeekDefinitionTempFolder, true);
|
||||
Assert.False(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
|
||||
// Expected not to throw any exception
|
||||
languageService.DeletePeekDefinitionScripts();
|
||||
}
|
||||
|
||||
@@ -354,6 +354,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
|
||||
// Make sure the display value has a time string with 7 milliseconds
|
||||
Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}\.[\d]{7}$"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,4 +494,4 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Castle.Components.DictionaryAdapter;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
@@ -136,20 +135,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
|
||||
// If:
|
||||
// ... I Then execute the query
|
||||
query.Execute();
|
||||
query.ExecutionTask.Wait();
|
||||
|
||||
// Then:
|
||||
// ... There should be no batches
|
||||
Assert.Empty(query.Batches);
|
||||
Assert.Equal(1, query.Batches.Length);
|
||||
|
||||
// ... The query should have completed successfully with no batch summaries returned
|
||||
Assert.True(query.HasExecuted);
|
||||
Assert.Empty(query.BatchSummaries);
|
||||
// ... The query shouldn't have completed successfully
|
||||
Assert.False(query.HasExecuted);
|
||||
|
||||
// ... The message callback should have been called exactly once
|
||||
// ... The message must not have a batch associated with it
|
||||
Assert.Equal(1, messages.Count);
|
||||
Assert.Null(messages[0].BatchId);
|
||||
// ... The message callback should have been called 0 times
|
||||
Assert.Equal(0, messages.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -215,26 +210,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
|
||||
|
||||
// .. I then execute the query
|
||||
query.Execute();
|
||||
query.ExecutionTask.Wait();
|
||||
|
||||
// Then:
|
||||
// ... I should get back a query with one batch (no op batch is not included)
|
||||
// ... I should get back a query with two batches
|
||||
Assert.NotEmpty(query.Batches);
|
||||
Assert.Equal(1, query.Batches.Length);
|
||||
Assert.Equal(2, query.Batches.Length);
|
||||
|
||||
// ... The query should have completed successfully with one batch summary returned
|
||||
Assert.True(query.HasExecuted);
|
||||
Assert.NotEmpty(query.BatchSummaries);
|
||||
Assert.Equal(1, query.BatchSummaries.Length);
|
||||
//// ... The query shouldn't have completed successfully unless all batches were executed
|
||||
Assert.False(query.HasExecuted);
|
||||
|
||||
// ... The batch callbacks should have been called precisely 1 time
|
||||
Assert.Equal(1, batchStartCallbacksReceived);
|
||||
Assert.Equal(1, batchCompletionCallbacksReceived);
|
||||
Assert.Equal(1, batchMessageCallbacksReceived);
|
||||
//// ... The batch callbacks should have been called 0 times
|
||||
Assert.Equal(0, batchStartCallbacksReceived);
|
||||
Assert.Equal(0, batchCompletionCallbacksReceived);
|
||||
Assert.Equal(0, batchMessageCallbacksReceived);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task QueryExecuteMultipleNoOpBatches()
|
||||
public void QueryExecuteMultipleNoOpBatches()
|
||||
{
|
||||
// Setup:
|
||||
// ... Keep track of how many messages were sent
|
||||
@@ -253,20 +245,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
|
||||
|
||||
// .. I then execute the query
|
||||
query.Execute();
|
||||
await query.ExecutionTask;
|
||||
|
||||
// Then:
|
||||
// ... I should get back a query with no batches
|
||||
Assert.Empty(query.Batches);
|
||||
Assert.Equal(2, query.Batches.Length);
|
||||
|
||||
// ... The query should have completed successfully with one zero batch summaries returned
|
||||
Assert.True(query.HasExecuted);
|
||||
Assert.Empty(query.BatchSummaries);
|
||||
// ... The query shouldn't have completed successfully
|
||||
Assert.False(query.HasExecuted);
|
||||
|
||||
// ... The message callback should have been called exactly once
|
||||
// ... The message must not have a batch associated with it
|
||||
Assert.Equal(1, messages.Count);
|
||||
Assert.Null(messages[0].BatchId);
|
||||
Assert.Equal(0, messages.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
|
||||
{
|
||||
public class ServiceIntegrationTests
|
||||
{
|
||||
|
||||
#region Get SQL Tests
|
||||
|
||||
[Fact]
|
||||
@@ -183,35 +184,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
|
||||
// NOTE: In order to limit test duplication, we're running the ExecuteDocumentSelection
|
||||
// version of execute query. The code paths are almost identical.
|
||||
|
||||
[Fact]
|
||||
private async Task QueryExecuteAllBatchesNoOp()
|
||||
{
|
||||
// If:
|
||||
// ... I request to execute a valid query with all batches as no op
|
||||
var workspaceService = GetDefaultWorkspaceService(string.Format("{0}\r\nGO\r\n{0}", Common.NoOpQuery));
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
|
||||
var queryParams = new ExecuteDocumentSelectionParams { QuerySelection = Common.WholeDocument, OwnerUri = Common.OwnerUri};
|
||||
|
||||
var efv = new EventFlowValidator<ExecuteRequestResult>()
|
||||
.AddStandardQueryResultValidator()
|
||||
.AddStandardMessageValidator()
|
||||
.AddEventValidation(QueryCompleteEvent.Type, p =>
|
||||
{
|
||||
// Validate OwnerURI matches
|
||||
Assert.Equal(Common.OwnerUri, p.OwnerUri);
|
||||
Assert.NotNull(p.BatchSummaries);
|
||||
Assert.Equal(0, p.BatchSummaries.Length);
|
||||
}).Complete();
|
||||
await Common.AwaitExecution(queryService, queryParams, efv.Object);
|
||||
|
||||
// Then:
|
||||
// ... All events should have been called as per their flow validator
|
||||
efv.Validate();
|
||||
|
||||
// ... There should be one active query
|
||||
Assert.Equal(1, queryService.ActiveQueries.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task QueryExecuteSingleBatchNoResultsTest()
|
||||
{
|
||||
|
||||
@@ -29,5 +29,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
|
||||
{
|
||||
base.NumericScale = scale;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,12 @@ using System.Data.SqlClient;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"name": "Microsoft.SqlTools.ServiceLayer.Test",
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"debugType": "portable"
|
||||
"debugType": "portable",
|
||||
"define": []
|
||||
},
|
||||
"configurations": {
|
||||
"Integration": {
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||
if (!File.Exists(serviceHostExecutable))
|
||||
{
|
||||
throw new FileNotFoundException($"Failed to find Microsoft.SqlTools.ServiceLayer.exe at provided location '{serviceHostExecutable}'. " +
|
||||
"Please set SQLTOOLSERVICE_EXE environment variable to location of exe");
|
||||
"Please set SQLTOOLSSERVICE_EXE environment variable to location of exe");
|
||||
}
|
||||
|
||||
//setup the service host for code coverage if the envvar is enabled
|
||||
|
||||
Reference in New Issue
Block a user