mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-16 09:35:36 -05:00
Feature: Writing Execute Results to Temp File (#35)
* WIP for buffering in temporary file * Adding support for writing to disk for buffering * WIP - Adding file reader, factory for reader/writer * Making long list use generics and implement IEnumerable * Reading/Writing from file is working * Removing unused 'skipValue' logic * More tweaks to file buffer Adding logic for cleaning up the temp files Adding fix for empty/null column names * Adding comments and cleanup * Unit tests for FileStreamWrapper * WIP adding more unit tests, and finishing up wiring up the output writers * Finishing up initial unit tests * Fixing bugs with long fields * Squashed commit of the following: commit df0ffc12a46cb286d801d08689964eac08ad71dd Author: Benjamin Russell <beruss@microsoft.com> Date: Wed Sep 7 14:45:39 2016 -0700 Removing last bit of async for file writing. We're seeing a 8x improvement of file write speeds! commit 08a4b9f32e825512ca24d5dc03ef5acbf7cc6d94 Author: Benjamin Russell <beruss@microsoft.com> Date: Wed Sep 7 11:23:06 2016 -0700 Removing async wrappers * Rolling back test code for Program.cs * Changes as per code review * Fixing broken unit tests * More fixes for codereview
This commit is contained in:
@@ -3,9 +3,11 @@
|
||||
// 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;
|
||||
using System.Data.Common;
|
||||
using System.IO;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
@@ -16,6 +18,7 @@ using Microsoft.SqlServer.Management.SqlParser.Binder;
|
||||
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
@@ -71,7 +74,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
|
||||
public static Batch GetBasicExecutedBatch()
|
||||
{
|
||||
Batch batch = new Batch(StandardQuery, 1);
|
||||
Batch batch = new Batch(StandardQuery, 1, GetFileStreamFactory());
|
||||
batch.Execute(CreateTestConnection(new[] {StandardTestData}, false), CancellationToken.None).Wait();
|
||||
return batch;
|
||||
}
|
||||
@@ -79,11 +82,78 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
public static Query GetBasicExecutedQuery()
|
||||
{
|
||||
ConnectionInfo ci = CreateTestConnectionInfo(new[] {StandardTestData}, false);
|
||||
Query query = new Query(StandardQuery, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(StandardQuery, ci, new QueryExecutionSettings(), GetFileStreamFactory());
|
||||
query.Execute().Wait();
|
||||
return query;
|
||||
}
|
||||
|
||||
#region FileStreamWriteMocking
|
||||
|
||||
public static IFileStreamFactory GetFileStreamFactory()
|
||||
{
|
||||
Mock<IFileStreamFactory> mock = new Mock<IFileStreamFactory>();
|
||||
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
|
||||
.Returns(new ServiceBufferFileStreamReader(new InMemoryWrapper(), It.IsAny<string>()));
|
||||
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(new ServiceBufferFileStreamWriter(new InMemoryWrapper(), It.IsAny<string>(), 1024,
|
||||
1024));
|
||||
|
||||
return mock.Object;
|
||||
}
|
||||
|
||||
public class InMemoryWrapper : IFileStreamWrapper
|
||||
{
|
||||
private readonly byte[] storage = new byte[8192];
|
||||
private readonly MemoryStream memoryStream;
|
||||
private bool readingOnly;
|
||||
|
||||
public InMemoryWrapper()
|
||||
{
|
||||
memoryStream = new MemoryStream(storage);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// We'll dispose this via a special method
|
||||
}
|
||||
|
||||
public void Init(string fileName, int bufferSize, FileAccess fAccess)
|
||||
{
|
||||
readingOnly = fAccess == FileAccess.Read;
|
||||
}
|
||||
|
||||
public int ReadData(byte[] buffer, int bytes)
|
||||
{
|
||||
return ReadData(buffer, bytes, memoryStream.Position);
|
||||
}
|
||||
|
||||
public int ReadData(byte[] buffer, int bytes, long fileOffset)
|
||||
{
|
||||
memoryStream.Seek(fileOffset, SeekOrigin.Begin);
|
||||
return memoryStream.Read(buffer, 0, bytes);
|
||||
}
|
||||
|
||||
public int WriteData(byte[] buffer, int bytes)
|
||||
{
|
||||
if (readingOnly) { throw new InvalidOperationException(); }
|
||||
memoryStream.Write(buffer, 0, bytes);
|
||||
memoryStream.Flush();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (readingOnly) { throw new InvalidOperationException(); }
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
memoryStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DbConnection Mocking
|
||||
|
||||
public static DbCommand CreateTestCommand(Dictionary<string, string>[][] data, bool throwOnRead)
|
||||
@@ -151,12 +221,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
out ConnectionInfo connInfo
|
||||
)
|
||||
{
|
||||
textDocument = new TextDocumentPosition();
|
||||
textDocument.TextDocument = new TextDocumentIdentifier();
|
||||
textDocument.TextDocument.Uri = Common.OwnerUri;
|
||||
textDocument.Position = new Position();
|
||||
textDocument.Position.Line = 0;
|
||||
textDocument.Position.Character = 0;
|
||||
textDocument = new TextDocumentPosition
|
||||
{
|
||||
TextDocument = new TextDocumentIdentifier {Uri = OwnerUri},
|
||||
Position = new Position
|
||||
{
|
||||
Line = 0,
|
||||
Character = 0
|
||||
}
|
||||
};
|
||||
|
||||
connInfo = Common.CreateTestConnectionInfo(null, false);
|
||||
|
||||
@@ -166,15 +239,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
var binder = BinderProvider.CreateBinder(metadataProvider);
|
||||
|
||||
LanguageService.Instance.ScriptParseInfoMap.Add(textDocument.TextDocument.Uri,
|
||||
new ScriptParseInfo()
|
||||
new ScriptParseInfo
|
||||
{
|
||||
Binder = binder,
|
||||
MetadataProvider = metadataProvider,
|
||||
MetadataDisplayInfoProvider = displayInfoProvider
|
||||
});
|
||||
|
||||
scriptFile = new ScriptFile();
|
||||
scriptFile.ClientFilePath = textDocument.TextDocument.Uri;
|
||||
scriptFile = new ScriptFile {ClientFilePath = textDocument.TextDocument.Uri};
|
||||
|
||||
}
|
||||
|
||||
public static ServerConnection GetServerConnection(ConnectionInfo connection)
|
||||
@@ -206,7 +279,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
OwnerUri = OwnerUri
|
||||
});
|
||||
}
|
||||
return new QueryExecutionService(connectionService);
|
||||
return new QueryExecutionService(connectionService) {BufferFileStreamFactory = GetFileStreamFactory()};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class FileStreamWrapperTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void InitInvalidFilenameParameter(string fileName)
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with invalid fileName
|
||||
// Then:
|
||||
// ... It should throw an argument null exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => fsw.Init(fileName, 8192, FileAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(-1)]
|
||||
public void InitInvalidBufferLength(int bufferLength)
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with an invalid buffer length
|
||||
// Then:
|
||||
// ... I should throw an argument out of range exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => fsw.Init("validFileName", bufferLength, FileAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitInvalidFileAccessMode()
|
||||
{
|
||||
// If:
|
||||
// ... I attempt to open a file stream wrapper that is initialized with an invalid file
|
||||
// access mode
|
||||
// Then:
|
||||
// ... I should get an invalid argument exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => fsw.Init("validFileName", 8192, FileAccess.Write));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitSuccessful()
|
||||
{
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with valid parameters
|
||||
fsw.Init(fileName, 8192, FileAccess.ReadWrite);
|
||||
|
||||
// Then:
|
||||
// ... The file should exist
|
||||
FileInfo fileInfo = new FileInfo(fileName);
|
||||
Assert.True(fileInfo.Exists);
|
||||
|
||||
// ... The file should be marked as hidden
|
||||
Assert.True((fileInfo.Attributes & FileAttributes.Hidden) != 0);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the file that was created
|
||||
try { File.Delete(fileName); } catch { /* Don't care */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerformOpWithoutInit()
|
||||
{
|
||||
byte[] buf = new byte[10];
|
||||
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that hasn't been initialized
|
||||
// Then:
|
||||
// ... Attempting to perform any operation will result in an exception
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.ReadData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.ReadData(buf, 1, 0));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.WriteData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.Flush());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerformWriteOpOnReadOnlyWrapper()
|
||||
{
|
||||
byte[] buf = new byte[10];
|
||||
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a readonly file stream wrapper
|
||||
// Then:
|
||||
// ... Attempting to perform any write operation should result in an exception
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.WriteData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.Flush());
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1024, 20, 10)] // Standard scenario
|
||||
[InlineData(1024, 100, 100)] // Requested more bytes than there are
|
||||
[InlineData(5, 20, 10)] // Internal buffer too small, force a move-to operation
|
||||
public void ReadData(int internalBufferLength, int outBufferLength, int requestedBytes)
|
||||
{
|
||||
// Setup:
|
||||
// ... I have a file that has a handful of bytes in it
|
||||
string fileName = Path.GetTempFileName();
|
||||
const string stringToWrite = "hello";
|
||||
CreateTestFile(fileName, stringToWrite);
|
||||
byte[] targetBytes = Encoding.Unicode.GetBytes(stringToWrite);
|
||||
|
||||
try
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that has been initialized to an existing file
|
||||
// ... And I read some bytes from it
|
||||
int bytesRead;
|
||||
byte[] buf = new byte[outBufferLength];
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
fsw.Init(fileName, internalBufferLength, FileAccess.Read);
|
||||
bytesRead = fsw.ReadData(buf, targetBytes.Length);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... I should get those bytes back
|
||||
Assert.Equal(targetBytes.Length, bytesRead);
|
||||
Assert.True(targetBytes.Take(targetBytes.Length).SequenceEqual(buf.Take(targetBytes.Length)));
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the test file
|
||||
CleanupTestFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1024)] // Standard scenario
|
||||
[InlineData(10)] // Internal buffer too small, forces a flush
|
||||
public void WriteData(int internalBufferLength)
|
||||
{
|
||||
string fileName = Path.GetTempFileName();
|
||||
byte[] bytesToWrite = Encoding.Unicode.GetBytes("hello");
|
||||
|
||||
try
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream that has been initialized
|
||||
// ... And I write some bytes to it
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
fsw.Init(fileName, internalBufferLength, FileAccess.ReadWrite);
|
||||
int bytesWritten = fsw.WriteData(bytesToWrite, bytesToWrite.Length);
|
||||
|
||||
Assert.Equal(bytesToWrite.Length, bytesWritten);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... The file I wrote to should contain only the bytes I wrote out
|
||||
using (FileStream fs = File.OpenRead(fileName))
|
||||
{
|
||||
byte[] readBackBytes = new byte[1024];
|
||||
int bytesRead = fs.Read(readBackBytes, 0, readBackBytes.Length);
|
||||
|
||||
Assert.Equal(bytesToWrite.Length, bytesRead); // If bytes read is not equal, then more or less of the original string was written to the file
|
||||
Assert.True(bytesToWrite.SequenceEqual(readBackBytes.Take(bytesRead)));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the test file
|
||||
CleanupTestFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateTestFile(string fileName, string value)
|
||||
{
|
||||
using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
||||
{
|
||||
byte[] bytesToWrite = Encoding.Unicode.GetBytes(value);
|
||||
fs.Write(bytesToWrite, 0, bytesToWrite.Length);
|
||||
fs.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupTestFile(string fileName)
|
||||
{
|
||||
try { File.Delete(fileName); } catch { /* Don't Care */}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
//
|
||||
// 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.SqlTypes;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class ReaderWriterPairTest
|
||||
{
|
||||
private static void VerifyReadWrite<T>(int valueLength, T value, Func<ServiceBufferFileStreamWriter, T, int> writeFunc, Func<ServiceBufferFileStreamReader, FileStreamReadResult<T>> readFunc)
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper();
|
||||
try
|
||||
{
|
||||
// If:
|
||||
// ... I write a type T to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
{
|
||||
int writtenBytes = writeFunc(writer, value);
|
||||
Assert.Equal(valueLength, writtenBytes);
|
||||
}
|
||||
|
||||
// ... And read the type T back
|
||||
FileStreamReadResult<T> outValue;
|
||||
using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(mockWrapper, "abc"))
|
||||
{
|
||||
outValue = readFunc(reader);
|
||||
}
|
||||
|
||||
// Then:
|
||||
Assert.Equal(value, outValue.Value);
|
||||
Assert.Equal(valueLength, outValue.TotalLength);
|
||||
Assert.False(outValue.IsNull);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup: Close the wrapper
|
||||
mockWrapper.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10)]
|
||||
[InlineData(-10)]
|
||||
[InlineData(short.MaxValue)] // Two byte number
|
||||
[InlineData(short.MinValue)] // Negative two byte number
|
||||
public void Int16(short value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(short) + 1, value, (writer, val) => writer.WriteInt16(val), reader => reader.ReadInt16(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10)]
|
||||
[InlineData(-10)]
|
||||
[InlineData(short.MaxValue)] // Two byte number
|
||||
[InlineData(short.MinValue)] // Negative two byte number
|
||||
[InlineData(int.MaxValue)] // Four byte number
|
||||
[InlineData(int.MinValue)] // Negative four byte number
|
||||
public void Int32(int value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(int) + 1, value, (writer, val) => writer.WriteInt32(val), reader => reader.ReadInt32(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10)]
|
||||
[InlineData(-10)]
|
||||
[InlineData(short.MaxValue)] // Two byte number
|
||||
[InlineData(short.MinValue)] // Negative two byte number
|
||||
[InlineData(int.MaxValue)] // Four byte number
|
||||
[InlineData(int.MinValue)] // Negative four byte number
|
||||
[InlineData(long.MaxValue)] // Eight byte number
|
||||
[InlineData(long.MinValue)] // Negative eight byte number
|
||||
public void Int64(long value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteInt64(val), reader => reader.ReadInt64(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10)]
|
||||
public void Byte(byte value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(byte) + 1, value, (writer, val) => writer.WriteByte(val), reader => reader.ReadByte(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData('a')]
|
||||
[InlineData('1')]
|
||||
[InlineData((char)0x9152)] // Test something in the UTF-16 space
|
||||
public void Char(char value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(char) + 1, value, (writer, val) => writer.WriteChar(val), reader => reader.ReadChar(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void Boolean(bool value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(bool) + 1, value, (writer, val) => writer.WriteBoolean(val), reader => reader.ReadBoolean(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10.1)]
|
||||
[InlineData(-10.1)]
|
||||
[InlineData(float.MinValue)]
|
||||
[InlineData(float.MaxValue)]
|
||||
[InlineData(float.PositiveInfinity)]
|
||||
[InlineData(float.NegativeInfinity)]
|
||||
public void Single(float value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(float) + 1, value, (writer, val) => writer.WriteSingle(val), reader => reader.ReadSingle(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10.1)]
|
||||
[InlineData(-10.1)]
|
||||
[InlineData(float.MinValue)]
|
||||
[InlineData(float.MaxValue)]
|
||||
[InlineData(float.PositiveInfinity)]
|
||||
[InlineData(float.NegativeInfinity)]
|
||||
[InlineData(double.PositiveInfinity)]
|
||||
[InlineData(double.NegativeInfinity)]
|
||||
[InlineData(double.MinValue)]
|
||||
[InlineData(double.MaxValue)]
|
||||
public void Double(double value)
|
||||
{
|
||||
VerifyReadWrite(sizeof(double) + 1, value, (writer, val) => writer.WriteDouble(val), reader => reader.ReadDouble(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SqlDecimalTest()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because SqlDecimal values can't be written as constant expressions
|
||||
SqlDecimal[] testValues =
|
||||
{
|
||||
SqlDecimal.MaxValue, SqlDecimal.MinValue, new SqlDecimal(0x01, 0x01, true, 0, 0, 0, 0)
|
||||
};
|
||||
foreach (SqlDecimal value in testValues)
|
||||
{
|
||||
int valueLength = 4 + value.BinData.Length;
|
||||
VerifyReadWrite(valueLength, value, (writer, val) => writer.WriteSqlDecimal(val), reader => reader.ReadSqlDecimal(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decimal()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because Decimal values can't be written as constant expressions
|
||||
decimal[] testValues =
|
||||
{
|
||||
decimal.Zero, decimal.One, decimal.MinusOne, decimal.MinValue, decimal.MaxValue
|
||||
};
|
||||
|
||||
foreach (decimal value in testValues)
|
||||
{
|
||||
int valueLength = decimal.GetBits(value).Length*4 + 1;
|
||||
VerifyReadWrite(valueLength, value, (writer, val) => writer.WriteDecimal(val), reader => reader.ReadDecimal(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DateTimeTest()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because DateTime values can't be written as constant expressions
|
||||
DateTime[] testValues =
|
||||
{
|
||||
DateTime.Now, DateTime.UtcNow, DateTime.MinValue, DateTime.MaxValue
|
||||
};
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DateTimeOffsetTest()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because DateTimeOffset values can't be written as constant expressions
|
||||
DateTimeOffset[] testValues =
|
||||
{
|
||||
DateTimeOffset.Now, DateTimeOffset.UtcNow, DateTimeOffset.MinValue, DateTimeOffset.MaxValue
|
||||
};
|
||||
foreach (DateTimeOffset value in testValues)
|
||||
{
|
||||
VerifyReadWrite((sizeof(long) + 1)*2, value, (writer, val) => writer.WriteDateTimeOffset(val), reader => reader.ReadDateTimeOffset(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TimeSpanTest()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because TimeSpan values can't be written as constant expressions
|
||||
TimeSpan[] testValues =
|
||||
{
|
||||
TimeSpan.Zero, TimeSpan.MinValue, TimeSpan.MaxValue, TimeSpan.FromMinutes(60)
|
||||
};
|
||||
foreach (TimeSpan value in testValues)
|
||||
{
|
||||
VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteTimeSpan(val), reader => reader.ReadTimeSpan(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StringNullTest()
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper();
|
||||
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteString(null));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, null)] // Test of empty string
|
||||
[InlineData(1, new[] { 'j' })]
|
||||
[InlineData(1, new[] { (char)0x9152 })]
|
||||
[InlineData(100, new[] { 'j', (char)0x9152 })] // Test alternating utf-16/ascii characters
|
||||
[InlineData(512, new[] { 'j', (char)0x9152 })] // Test that requires a 4 byte length
|
||||
public void StringTest(int length, char[] values)
|
||||
{
|
||||
// Setup:
|
||||
// ... Generate the test value
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sb.Append(values[i%values.Length]);
|
||||
}
|
||||
string value = sb.ToString();
|
||||
int lengthLength = length == 0 || length > 255 ? 5 : 1;
|
||||
VerifyReadWrite(sizeof(char)*length + lengthLength, value, (writer, val) => writer.WriteString(value), reader => reader.ReadString(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BytesNullTest()
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper();
|
||||
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteBytes(null, 0));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, new byte[] { 0x00 })] // Test of empty byte[]
|
||||
[InlineData(1, new byte[] { 0x00 })]
|
||||
[InlineData(1, new byte[] { 0xFF })]
|
||||
[InlineData(100, new byte[] { 0x10, 0xFF, 0x00 })]
|
||||
[InlineData(512, new byte[] { 0x10, 0xFF, 0x00 })] // Test that requires a 4 byte length
|
||||
public void Bytes(int length, byte[] values)
|
||||
{
|
||||
// Setup:
|
||||
// ... Generate the test value
|
||||
List<byte> sb = new List<byte>();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sb.Add(values[i % values.Length]);
|
||||
}
|
||||
byte[] value = sb.ToArray();
|
||||
int lengthLength = length == 0 || length > 255 ? 5 : 1;
|
||||
int valueLength = sizeof(byte)*length + lengthLength;
|
||||
VerifyReadWrite(valueLength, value, (writer, val) => writer.WriteBytes(value, length), reader => reader.ReadBytes(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
public void BatchCreationTest()
|
||||
{
|
||||
// If I create a new batch...
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... The text of the batch should be stored
|
||||
@@ -52,7 +52,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
public void BatchExecuteNoResultSets()
|
||||
{
|
||||
// If I execute a query that should get no result sets
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
batch.Execute(GetConnection(Common.CreateTestConnectionInfo(null, false)), CancellationToken.None).Wait();
|
||||
|
||||
// Then:
|
||||
@@ -79,7 +79,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
|
||||
|
||||
// If I execute a query that should get one result set
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
|
||||
|
||||
// Then:
|
||||
@@ -92,11 +92,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
Assert.Equal(resultSets, batch.ResultSummaries.Length);
|
||||
|
||||
// ... Inside the result set should be with 5 rows
|
||||
Assert.Equal(Common.StandardRows, batch.ResultSets.First().Rows.Count);
|
||||
Assert.Equal(Common.StandardRows, batch.ResultSets.First().RowCount);
|
||||
Assert.Equal(Common.StandardRows, batch.ResultSummaries[0].RowCount);
|
||||
|
||||
// ... Inside the result set should have 5 columns and 5 column definitions
|
||||
Assert.Equal(Common.StandardColumns, batch.ResultSets.First().Rows[0].Length);
|
||||
// ... Inside the result set should have 5 columns
|
||||
Assert.Equal(Common.StandardColumns, batch.ResultSets.First().Columns.Length);
|
||||
Assert.Equal(Common.StandardColumns, batch.ResultSummaries[0].ColumnInfo.Length);
|
||||
|
||||
@@ -112,7 +111,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(dataset, false);
|
||||
|
||||
// If I execute a query that should get two result sets
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
|
||||
|
||||
// Then:
|
||||
@@ -126,10 +125,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
foreach (ResultSet rs in batch.ResultSets)
|
||||
{
|
||||
// ... Each result set should have 5 rows
|
||||
Assert.Equal(Common.StandardRows, rs.Rows.Count);
|
||||
Assert.Equal(Common.StandardRows, rs.RowCount);
|
||||
|
||||
// ... Inside each result set should be 5 columns and 5 column definitions
|
||||
Assert.Equal(Common.StandardColumns, rs.Rows[0].Length);
|
||||
// ... Inside each result set should be 5 columns
|
||||
Assert.Equal(Common.StandardColumns, rs.Columns.Length);
|
||||
}
|
||||
|
||||
@@ -155,7 +153,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
|
||||
|
||||
// If I execute a batch that is invalid
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
|
||||
|
||||
// Then:
|
||||
@@ -177,7 +175,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(new[] { Common.StandardTestData }, false);
|
||||
|
||||
// If I execute a batch
|
||||
Batch batch = new Batch(Common.StandardQuery, 1);
|
||||
Batch batch = new Batch(Common.StandardQuery, 1, Common.GetFileStreamFactory());
|
||||
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
|
||||
|
||||
// Then:
|
||||
@@ -207,7 +205,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... I create a batch that has an empty query
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new Batch(query, 1));
|
||||
Assert.Throws<ArgumentException>(() => new Batch(query, 1, Common.GetFileStreamFactory()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchNoBufferFactory()
|
||||
{
|
||||
// If:
|
||||
// ... I create a batch that has no file stream factory
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new Batch("stuff", 1, null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -222,7 +230,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
new Query(null, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings()));
|
||||
new Query(null, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), Common.GetFileStreamFactory()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -232,7 +240,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... I create a query that has a null connection info
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new Query("Some Query", null, new QueryExecutionSettings()));
|
||||
Assert.Throws<ArgumentNullException>(() => new Query("Some Query", null, new QueryExecutionSettings(), Common.GetFileStreamFactory()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -243,7 +251,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
new Query("Some query", Common.CreateTestConnectionInfo(null, false), null));
|
||||
new Query("Some query", Common.CreateTestConnectionInfo(null, false), null, Common.GetFileStreamFactory()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QueryExecuteNoBufferFactory()
|
||||
{
|
||||
// If:
|
||||
// ... I create a query that has a null file stream factory
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
new Query("Some query", Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(),null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -252,7 +271,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// If:
|
||||
// ... I create a query from a single batch (without separator)
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
|
||||
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... I should get a single batch to execute that hasn't been executed
|
||||
@@ -279,7 +298,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// If:
|
||||
// ... I create a query from a single batch that does nothing
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
|
||||
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... I should get no batches back
|
||||
@@ -305,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... I create a query from two batches (with separator)
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
|
||||
string queryText = string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery);
|
||||
Query query = new Query(queryText, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(queryText, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... I should get back two batches to execute that haven't been executed
|
||||
@@ -333,7 +352,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... I create a query from a two batches (with separator)
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
|
||||
string queryText = string.Format("{0}\r\nGO\r\n{1}", Common.StandardQuery, Common.NoOpQuery);
|
||||
Query query = new Query(queryText, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(queryText, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... I should get back one batch to execute that hasn't been executed
|
||||
@@ -359,7 +378,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// If:
|
||||
// ... I create a query from an invalid batch
|
||||
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
|
||||
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings());
|
||||
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// Then:
|
||||
// ... I should get back a query with one batch not executed
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
Batch b = Common.GetBasicExecutedBatch();
|
||||
|
||||
// ... And I ask for a subset with valid arguments
|
||||
ResultSetSubset subset = b.GetSubset(0, 0, rowCount);
|
||||
ResultSetSubset subset = b.GetSubset(0, 0, rowCount).Result;
|
||||
|
||||
// Then:
|
||||
// I should get the requested number of rows
|
||||
@@ -51,7 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... And I ask for a subset with an invalid result set index
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => b.GetSubset(resultSetIndex, rowStartInex, rowCount));
|
||||
Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => b.GetSubset(resultSetIndex, rowStartInex, rowCount)).Wait();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -62,12 +62,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
public void SubsetUnexecutedQueryTest()
|
||||
{
|
||||
// If I have a query that has *not* been executed
|
||||
Query q = new Query(Common.StandardQuery, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings());
|
||||
Query q = new Query(Common.StandardQuery, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), Common.GetFileStreamFactory());
|
||||
|
||||
// ... And I ask for a subset with valid arguments
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<InvalidOperationException>(() => q.GetSubset(0, 0, 0, 2));
|
||||
Assert.ThrowsAsync<InvalidOperationException>(() => q.GetSubset(0, 0, 0, 2)).Wait();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -81,7 +81,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
// ... And I ask for a subset with an invalid result set index
|
||||
// Then:
|
||||
// ... It should throw an exception
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => q.GetSubset(batchIndex, 0, 0, 1));
|
||||
Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => q.GetSubset(batchIndex, 0, 0, 1)).Wait();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user