mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
Isolate Shared Test Code (#252)
The goal of this make sure that test code is correctly organized to ensure that test suites aren't dependent on each other.
* UnitTests get their own project now (renaming Microsoft.SqlTools.ServiceLayer.Test to Microsoft.SqlTools.ServiceLayer.UnitTests) which is about 90% of the changes to the files.
* IntegrationTests no longer depends on UnitTests, only Test.Common
* Any shared components from TestObjects that spins up a "live" connection has been moved to IntegrationTests Utility/LiveConnectionHelper.cs
* The dictionary-based mock file stream factory has been moved to Test.Common since it is used by UnitTests and IntegrationTests
* Added a overload that doesn't take a dictionary for when we don't care about monitoring the storage (about 90% of the time)
* The RunIf* wrapper methods have been moved to Test.Common
* OwnerUri and StandardQuery constants have been moved to Test.Common Constants file
* Updating to latest SDK version available at https://www.microsoft.com/net/core#windowscmd
* Moving unit tests to unit test folder
* Changing namespaces to UnitTests
* Moving some constants and shared functionality into common project, making the UnitTests reference it
* Unit tests are working!
* Integration tests are working
* Updating automated test runs
* Fixing one last broken unit test
* Exposing internals for other projects
* Moving edit data tests to UnitTest project
* Applying refactor fixes to unit tests
* Fixing flaky test that wasn't awaiting completion
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
|
||||
{
|
||||
public class SaveAsCsvFileStreamWriterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Something\rElse")]
|
||||
[InlineData("Something\nElse")]
|
||||
[InlineData("Something\"Else")]
|
||||
[InlineData("Something,Else")]
|
||||
[InlineData("\tSomething")]
|
||||
[InlineData("Something\t")]
|
||||
[InlineData(" Something")]
|
||||
[InlineData("Something ")]
|
||||
[InlineData(" \t\r\n\",\r\n\"\r ")]
|
||||
public void EncodeCsvFieldShouldWrap(string field)
|
||||
{
|
||||
// If: I CSV encode a field that has forbidden characters in it
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(field);
|
||||
|
||||
// Then: It should wrap it in quotes
|
||||
Assert.True(Regex.IsMatch(output, "^\".*")
|
||||
&& Regex.IsMatch(output, ".*\"$"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Something")]
|
||||
[InlineData("Something valid.")]
|
||||
[InlineData("Something\tvalid")]
|
||||
public void EncodeCsvFieldShouldNotWrap(string field)
|
||||
{
|
||||
// If: I CSV encode a field that does not have forbidden characters in it
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(field);
|
||||
|
||||
// Then: It should not wrap it in quotes
|
||||
Assert.False(Regex.IsMatch(output, "^\".*\"$"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodeCsvFieldReplace()
|
||||
{
|
||||
// If: I CSV encode a field that has a double quote in it,
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField("Some\"thing");
|
||||
|
||||
// Then: It should be replaced with double double quotes
|
||||
Assert.Equal("\"Some\"\"thing\"", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodeCsvFieldNull()
|
||||
{
|
||||
// If: I CSV encode a null
|
||||
string output = SaveAsCsvFileStreamWriter.EncodeCsvField(null);
|
||||
|
||||
// Then: there should be a string version of null returned
|
||||
Assert.Equal("NULL", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithoutColumnSelectionOrHeader()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams();
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then: It should write one line with 2 items, comma delimited
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
|
||||
Assert.Equal(1, lines.Length);
|
||||
string[] values = lines[0].Split(',');
|
||||
Assert.Equal(2, values.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithHeader()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made, headers should be printed
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
IncludeHeaders = true
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... It should have written two lines
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
Assert.Equal(2, lines.Length);
|
||||
|
||||
// ... It should have written a header line with two, comma separated names
|
||||
string[] headerValues = lines[0].Split(',');
|
||||
Assert.Equal(2, headerValues.Length);
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
Assert.Equal(columns[i].ColumnName, headerValues[i]);
|
||||
}
|
||||
|
||||
// Note: No need to check values, it is done as part of the previous test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that selects n-1 columns from the front and back
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var requestParams = new SaveResultsAsCsvRequestParams
|
||||
{
|
||||
ColumnStartIndex = 1,
|
||||
ColumnEndIndex = 2,
|
||||
RowStartIndex = 0, // Including b/c it is required to be a "save selection"
|
||||
RowEndIndex = 10,
|
||||
IncludeHeaders = true // Including headers to test both column selection logic
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1" },
|
||||
new DbCellValue { DisplayValue = "item2" },
|
||||
new DbCellValue { DisplayValue = "item3" },
|
||||
new DbCellValue { DisplayValue = "item4" }
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2")),
|
||||
new DbColumnWrapper(new TestDbColumn("column3")),
|
||||
new DbColumnWrapper(new TestDbColumn("column4"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write a row
|
||||
SaveAsCsvFileStreamWriter writer = new SaveAsCsvFileStreamWriter(new MemoryStream(output), requestParams);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... It should have written two lines
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0', '\r', '\n');
|
||||
string[] lines = outputString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
Assert.Equal(2, lines.Length);
|
||||
|
||||
// ... It should have written a header line with two, comma separated names
|
||||
string[] headerValues = lines[0].Split(',');
|
||||
Assert.Equal(2, headerValues.Length);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.Equal(columns[i].ColumnName, headerValues[i-1]);
|
||||
}
|
||||
|
||||
// ... The second line should have two, comma separated values
|
||||
string[] dataValues = lines[1].Split(',');
|
||||
Assert.Equal(2, dataValues.Length);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.Equal(data[i].DisplayValue, dataValues[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
|
||||
{
|
||||
public class SaveAsJsonFileStreamWriterTests
|
||||
{
|
||||
[Fact]
|
||||
public void ArrayWrapperTest()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create storage for the output
|
||||
byte[] output = new byte[8192];
|
||||
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams();
|
||||
|
||||
// If:
|
||||
// ... I create and then destruct a json writer
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
jsonWriter.Dispose();
|
||||
|
||||
// Then:
|
||||
// ... The output should be an empty array
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0');
|
||||
object[] outputArray = JsonConvert.DeserializeObject<object[]>(outputString);
|
||||
Assert.Equal(0, outputArray.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithoutColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that has no selection made
|
||||
// ... Create a set of data to write
|
||||
// ... Create storage for the output
|
||||
SaveResultsAsJsonRequestParams saveParams = new SaveResultsAsJsonRequestParams();
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue {DisplayValue = "item1", RawObject = "item1"},
|
||||
new DbCellValue {DisplayValue = "null", RawObject = null}
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If:
|
||||
// ... I write two rows
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
using (jsonWriter)
|
||||
{
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... Upon deserialization to an array of dictionaries
|
||||
string outputString = Encoding.UTF8.GetString(output).TrimEnd('\0');
|
||||
Dictionary<string, string>[] outputObject =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, string>[]>(outputString);
|
||||
|
||||
// ... There should be 2 items in the array,
|
||||
// ... The item should have two fields, and two values, assigned appropriately
|
||||
Assert.Equal(2, outputObject.Length);
|
||||
foreach (var item in outputObject)
|
||||
{
|
||||
Assert.Equal(2, item.Count);
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
Assert.True(item.ContainsKey(columns[i].ColumnName));
|
||||
Assert.Equal(data[i].RawObject == null ? null : data[i].DisplayValue, item[columns[i].ColumnName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRowWithColumnSelection()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create a request params that selects n-1 columns from the front and back
|
||||
// ... Create a set of data to write
|
||||
// ... Create a memory location to store the data
|
||||
var saveParams = new SaveResultsAsJsonRequestParams
|
||||
{
|
||||
ColumnStartIndex = 1,
|
||||
ColumnEndIndex = 2,
|
||||
RowStartIndex = 0, // Including b/c it is required to be a "save selection"
|
||||
RowEndIndex = 10
|
||||
};
|
||||
List<DbCellValue> data = new List<DbCellValue>
|
||||
{
|
||||
new DbCellValue { DisplayValue = "item1", RawObject = "item1"},
|
||||
new DbCellValue { DisplayValue = "item2", RawObject = "item2"},
|
||||
new DbCellValue { DisplayValue = "null", RawObject = null},
|
||||
new DbCellValue { DisplayValue = "null", RawObject = null}
|
||||
};
|
||||
List<DbColumnWrapper> columns = new List<DbColumnWrapper>
|
||||
{
|
||||
new DbColumnWrapper(new TestDbColumn("column1")),
|
||||
new DbColumnWrapper(new TestDbColumn("column2")),
|
||||
new DbColumnWrapper(new TestDbColumn("column3")),
|
||||
new DbColumnWrapper(new TestDbColumn("column4"))
|
||||
};
|
||||
byte[] output = new byte[8192];
|
||||
|
||||
// If: I write two rows
|
||||
var jsonWriter = new SaveAsJsonFileStreamWriter(new MemoryStream(output), saveParams);
|
||||
using (jsonWriter)
|
||||
{
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
jsonWriter.WriteRow(data, columns);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... Upon deserialization to an array of dictionaries
|
||||
string outputString = Encoding.UTF8.GetString(output).Trim('\0');
|
||||
Dictionary<string, string>[] outputObject =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, string>[]>(outputString);
|
||||
|
||||
// ... There should be 2 items in the array
|
||||
// ... The items should have 2 fields and values
|
||||
Assert.Equal(2, outputObject.Length);
|
||||
foreach (var item in outputObject)
|
||||
{
|
||||
Assert.Equal(2, item.Count);
|
||||
for (int i = 1; i <= 2; i++)
|
||||
{
|
||||
Assert.True(item.ContainsKey(columns[i].ColumnName));
|
||||
Assert.Equal(data[i].RawObject == null ? null : data[i].DisplayValue, item[columns[i].ColumnName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
//
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
|
||||
{
|
||||
public class ReaderWriterPairTest
|
||||
{
|
||||
[Fact]
|
||||
public void ReaderStreamNull()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with a null stream
|
||||
// Then: It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new ServiceBufferFileStreamReader(null, new QueryExecutionSettings()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderSettingsNull()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with null settings
|
||||
// Then: It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new ServiceBufferFileStreamReader(Stream.Null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderInvalidStreamCannotRead()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with a stream that cannot be read
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanRead).Returns(false);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(true);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object, new QueryExecutionSettings());
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderInvalidStreamCannotSeek()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with a stream that cannot seek
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanRead).Returns(true);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(false);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object, new QueryExecutionSettings());
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterStreamNull()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with a null stream
|
||||
// Then: It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new ServiceBufferFileStreamWriter(null, new QueryExecutionSettings()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterSettingsNull()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with null settings
|
||||
// Then: It should throw an exception
|
||||
Assert.Throws<ArgumentNullException>(() => new ServiceBufferFileStreamWriter(Stream.Null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterInvalidStreamCannotWrite()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with a stream that cannot be read
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanWrite).Returns(false);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(true);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, new QueryExecutionSettings());
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterInvalidStreamCannotSeek()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with a stream that cannot seek
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanWrite).Returns(true);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(false);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, new QueryExecutionSettings());
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
|
||||
private static string VerifyReadWrite<T>(int valueLength, T value,
|
||||
Func<ServiceBufferFileStreamWriter, T, int> writeFunc,
|
||||
Func<ServiceBufferFileStreamReader, FileStreamReadResult> readFunc,
|
||||
QueryExecutionSettings overrideSettings = null)
|
||||
{
|
||||
// Setup: Create a mock file stream
|
||||
byte[] storage = new byte[8192];
|
||||
overrideSettings = overrideSettings ?? new QueryExecutionSettings();
|
||||
|
||||
// If:
|
||||
// ... I write a type T to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(new MemoryStream(storage), overrideSettings))
|
||||
{
|
||||
int writtenBytes = writeFunc(writer, value);
|
||||
Assert.Equal(valueLength, writtenBytes);
|
||||
}
|
||||
|
||||
// ... And read the type T back
|
||||
FileStreamReadResult outValue;
|
||||
using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(new MemoryStream(storage), overrideSettings))
|
||||
{
|
||||
outValue = readFunc(reader);
|
||||
}
|
||||
|
||||
// Then:
|
||||
Assert.Equal(value, outValue.Value.RawObject);
|
||||
Assert.Equal(valueLength, outValue.TotalLength);
|
||||
Assert.NotNull(outValue.Value);
|
||||
|
||||
return outValue.Value.DisplayValue;
|
||||
}
|
||||
|
||||
[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, true)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, false)]
|
||||
public void Boolean(bool value, bool preferNumeric)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(bool) + 1, value,
|
||||
(writer, val) => writer.WriteBoolean(val),
|
||||
reader => reader.ReadBoolean(0),
|
||||
new QueryExecutionSettings {DisplayBitAsNumber = preferNumeric}
|
||||
);
|
||||
|
||||
// Validate the display value
|
||||
if (preferNumeric)
|
||||
{
|
||||
int output;
|
||||
Assert.True(int.TryParse(displayValue, out output));
|
||||
}
|
||||
else
|
||||
{
|
||||
bool output;
|
||||
Assert.True(bool.TryParse(displayValue, out output));
|
||||
}
|
||||
}
|
||||
|
||||
[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 DateTest()
|
||||
{
|
||||
// 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
|
||||
};
|
||||
|
||||
// Setup: Create a DATE column
|
||||
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTe"));
|
||||
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0, col));
|
||||
|
||||
// Make sure the display value does not have a time string
|
||||
Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2}$"));
|
||||
}
|
||||
}
|
||||
|
||||
[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
|
||||
};
|
||||
|
||||
// Setup: Create a DATETIME column
|
||||
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe"));
|
||||
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0, col));
|
||||
|
||||
// Make sure the display value has a time string with 3 milliseconds
|
||||
Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}\.[\d]{3}$"));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
[InlineData(5)]
|
||||
[InlineData(6)]
|
||||
[InlineData(7)]
|
||||
public void DateTime2Test(int precision)
|
||||
{
|
||||
// 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
|
||||
};
|
||||
|
||||
// Setup: Create a DATETIME column
|
||||
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", precision));
|
||||
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0, col));
|
||||
|
||||
// Make sure the display value has a time string with variable number of milliseconds
|
||||
Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}"));
|
||||
if (precision > 0)
|
||||
{
|
||||
Assert.True(Regex.IsMatch(displayValue, $@"\.[\d]{{{precision}}}$"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DateTime2ZeroScaleTest()
|
||||
{
|
||||
// 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
|
||||
};
|
||||
|
||||
// Setup: Create a DATETIME2 column
|
||||
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", 0));
|
||||
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0, col));
|
||||
|
||||
// Make sure the display value has a time string with 0 milliseconds
|
||||
Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}$"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DateTime2InvalidScaleTest()
|
||||
{
|
||||
// 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
|
||||
};
|
||||
|
||||
// Setup: Create a DATETIME2 column
|
||||
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", 255));
|
||||
|
||||
foreach (DateTime value in testValues)
|
||||
{
|
||||
string displayValue = VerifyReadWrite(sizeof(long) + 1, value, (writer, val) => writer.WriteDateTime(val), reader => reader.ReadDateTime(0, col));
|
||||
|
||||
// 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}$"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[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)*2 + 1, 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
|
||||
using (MemoryStream stream = new MemoryStream(new byte[8192]))
|
||||
{
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, new QueryExecutionSettings()))
|
||||
{
|
||||
// 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
|
||||
using (MemoryStream stream = new MemoryStream(new byte[8192]))
|
||||
{
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, new QueryExecutionSettings()))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteBytes(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[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), reader => reader.ReadBytes(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GuidTest()
|
||||
{
|
||||
// Setup:
|
||||
// ... Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because Guid type can't be written as constant expressions
|
||||
Guid[] guids =
|
||||
{
|
||||
Guid.Empty, Guid.NewGuid(), Guid.NewGuid()
|
||||
};
|
||||
foreach (Guid guid in guids)
|
||||
{
|
||||
VerifyReadWrite(guid.ToByteArray().Length + 1, new SqlGuid(guid), (writer, val) => writer.WriteGuid(guid), reader => reader.ReadGuid(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MoneyTest()
|
||||
{
|
||||
// Setup: Create some test values
|
||||
// NOTE: We are doing these here instead of InlineData because SqlMoney can't be written as a constant expression
|
||||
SqlMoney[] monies =
|
||||
{
|
||||
SqlMoney.Zero, SqlMoney.MinValue, SqlMoney.MaxValue, new SqlMoney(1.02)
|
||||
};
|
||||
foreach (SqlMoney money in monies)
|
||||
{
|
||||
VerifyReadWrite(sizeof(decimal) + 1, money, (writer, val) => writer.WriteMoney(money), reader => reader.ReadMoney(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user