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 commit 431dfa4156.

* 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 commit 431dfa4156.

* 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:
Aditya Bist
2017-02-10 16:51:26 -08:00
committed by GitHub
parent 1a5685c387
commit eb4f2f2b91
412 changed files with 82047 additions and 704 deletions

View File

@@ -0,0 +1,2 @@
print 'hi'
:r cycle2.txt

View File

@@ -0,0 +1,2 @@
print '2'
:r TS-err-cycle1.txt

View File

@@ -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) { }
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}
}

View File

@@ -0,0 +1,57 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using 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;
}
}
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
/* unterminated block comment *

View File

@@ -0,0 +1 @@
/* unterminated block comment

View File

@@ -0,0 +1,2 @@
print 'hi'
:r cycle2.txt

View File

@@ -0,0 +1,2 @@
:setvar 0var
-- invalid variable name

View File

@@ -0,0 +1,2 @@
:setvar a b c
-- invalid syntax (too many params)

View File

@@ -0,0 +1,2 @@
-- no variable defined (at EOF)
:setvar

View File

@@ -0,0 +1,2 @@
:setvar
-- no variable defined

View File

@@ -0,0 +1 @@
:setvar a "unterminated string

View File

@@ -0,0 +1 @@
Variable reference with no name: $()

View File

@@ -0,0 +1 @@
Variable reference with invalid name: $(7var)

View File

@@ -0,0 +1,2 @@
Unterminated variable reference: $(var
iable)

View File

@@ -0,0 +1 @@
Unterminated variable reference (at EOF): $(var

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
/**//***//* *//** */ */
one comment: /*/*/ */*/
not a comment: / */
/* block comment */
/* block
comment */

View File

@@ -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

View File

@@ -0,0 +1,5 @@
-- Line comment
a--b also a comment
-- comment
--comment
-- comment -- comment

View File

@@ -0,0 +1,4 @@
/*
-- not a line comment
*/
- - not a comment

View File

@@ -0,0 +1,2 @@
:setvar a "variable with
new line"

View File

@@ -0,0 +1,2 @@
:setvar a [unterminated_brace
$(b)]

View File

@@ -0,0 +1,6 @@
:setvar a ""
:setvar a
:setvar a """"
/* double-quote followed by new line: */
:setvar a """
"

View File

@@ -0,0 +1,2 @@
print '2'
:r TS-err-cycle1.txt

View File

@@ -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();
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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]

View File

@@ -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()
{

View File

@@ -29,5 +29,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility
{
base.NumericScale = scale;
}
}
}

View File

@@ -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;

View File

@@ -2,7 +2,8 @@
"name": "Microsoft.SqlTools.ServiceLayer.Test",
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
"debugType": "portable",
"define": []
},
"configurations": {
"Integration": {

View File

@@ -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