From 0841ad7cf4b43074b27d21cccbcdd117cf5562a9 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Tue, 22 Nov 2016 17:21:41 -0800 Subject: [PATCH] Removing the FileStreamWrapper as it is unecessary (#156) --- .../DataStorage/FileStreamWrapper.cs | 273 ------------------ .../DataStorage/IFileStreamWrapper.cs | 22 -- .../ServiceBufferFileStreamFactory.cs | 13 +- .../ServiceBufferFileStreamReader.cs | 23 +- .../ServiceBufferFileStreamWriter.cs | 56 ++-- .../{QueryExecution => Utility}/FileUtils.cs | 0 .../QueryExecution/Common.cs | 54 +--- .../DataStorage/FileStreamWrapperTests.cs | 218 -------------- ...erviceBufferFileStreamReaderWriterTests.cs | 141 ++++++--- 9 files changed, 149 insertions(+), 651 deletions(-) delete mode 100644 src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/FileStreamWrapper.cs delete mode 100644 src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWrapper.cs rename src/Microsoft.SqlTools.ServiceLayer/{QueryExecution => Utility}/FileUtils.cs (100%) delete mode 100644 test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/FileStreamWrapperTests.cs diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/FileStreamWrapper.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/FileStreamWrapper.cs deleted file mode 100644 index 74297a42..00000000 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/FileStreamWrapper.cs +++ /dev/null @@ -1,273 +0,0 @@ -// -// 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 Microsoft.SqlTools.ServiceLayer.Utility; - -namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage -{ - /// - /// Wrapper for a file stream, providing simplified creation, deletion, read, and write - /// functionality. - /// - public class FileStreamWrapper : IFileStreamWrapper - { - #region Member Variables - - private byte[] buffer; - private int bufferDataSize; - private FileStream fileStream; - private long startOffset; - private long currentOffset; - - #endregion - - /// - /// Constructs a new FileStreamWrapper and initializes its state. - /// - public FileStreamWrapper() - { - // Initialize the internal state - bufferDataSize = 0; - startOffset = 0; - currentOffset = 0; - } - - #region IFileStreamWrapper Implementation - - /// - /// Initializes the wrapper by creating the internal buffer and opening the requested file. - /// If the file does not already exist, it will be created. - /// - /// Name of the file to open/create - /// The length of the internal buffer - /// - /// Whether or not the wrapper will be used for reading. If true, any calls to a - /// method that writes will cause an InvalidOperationException - /// - public void Init(string fileName, int bufferLength, FileAccess accessMethod) - { - // Sanity check for valid buffer length, fileName, and accessMethod - Validate.IsGreaterThan(nameof(bufferLength), bufferLength, 0); - Validate.IsNotNullOrWhitespaceString(nameof(fileName), fileName); - if (accessMethod == FileAccess.Write) - { - throw new ArgumentException(SR.QueryServiceFileWrapperWriteOnly, nameof(fileName)); - } - - // Setup the buffer - buffer = new byte[bufferLength]; - - // Open the requested file for reading/writing, creating one if it doesn't exist - fileStream = new FileStream(fileName, FileMode.OpenOrCreate, accessMethod, FileShare.ReadWrite, - bufferLength, false /*don't use asyncio*/); - } - - /// - /// Reads data into a buffer from the current offset into the file - /// - /// The buffer to output the read data to - /// The number of bytes to read into the buffer - /// The number of bytes read - public int ReadData(byte[] buf, int bytes) - { - return ReadData(buf, bytes, currentOffset); - } - - /// - /// Reads data into a buffer from the specified offset into the file - /// - /// The buffer to output the read data to - /// The number of bytes to read into the buffer - /// The offset into the file to start reading bytes from - /// The number of bytes read - public int ReadData(byte[] buf, int bytes, long offset) - { - // Make sure that we're initialized before performing operations - if (buffer == null) - { - throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized); - } - - MoveTo(offset); - - int bytesCopied = 0; - while (bytesCopied < bytes) - { - int bufferOffset, bytesToCopy; - GetByteCounts(bytes, bytesCopied, out bufferOffset, out bytesToCopy); - Buffer.BlockCopy(buffer, bufferOffset, buf, bytesCopied, bytesToCopy); - bytesCopied += bytesToCopy; - - if (bytesCopied < bytes && // did not get all the bytes yet - bufferDataSize == buffer.Length) // since current data buffer is full we should continue reading the file - { - // move forward one full length of the buffer - MoveTo(startOffset + buffer.Length); - } - else - { - // copied all the bytes requested or possible, adjust the current buffer pointer - currentOffset += bytesToCopy; - break; - } - } - return bytesCopied; - } - - /// - /// Writes data to the underlying filestream, with buffering. - /// - /// The buffer of bytes to write to the filestream - /// The number of bytes to write - /// The number of bytes written - public int WriteData(byte[] buf, int bytes) - { - // Make sure that we're initialized before performing operations - if (buffer == null) - { - throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized); - } - if (!fileStream.CanWrite) - { - throw new InvalidOperationException(SR.QueryServiceFileWrapperReadOnly); - } - - int bytesCopied = 0; - while (bytesCopied < bytes) - { - int bufferOffset, bytesToCopy; - GetByteCounts(bytes, bytesCopied, out bufferOffset, out bytesToCopy); - Buffer.BlockCopy(buf, bytesCopied, buffer, bufferOffset, bytesToCopy); - bytesCopied += bytesToCopy; - - // adjust the current buffer pointer - currentOffset += bytesToCopy; - - if (bytesCopied < bytes) // did not get all the bytes yet - { - Debug.Assert((int)(currentOffset - startOffset) == buffer.Length); - // flush buffer - Flush(); - } - } - Debug.Assert(bytesCopied == bytes); - return bytesCopied; - } - - /// - /// Flushes the internal buffer to the filestream - /// - public void Flush() - { - // Make sure that we're initialized before performing operations - if (buffer == null) - { - throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized); - } - if (!fileStream.CanWrite) - { - throw new InvalidOperationException(SR.QueryServiceFileWrapperReadOnly); - } - - // Make sure we are at the right place in the file - Debug.Assert(fileStream.Position == startOffset); - - int bytesToWrite = (int)(currentOffset - startOffset); - fileStream.Write(buffer, 0, bytesToWrite); - startOffset += bytesToWrite; - fileStream.Flush(); - - Debug.Assert(startOffset == currentOffset); - } - - /// - /// Deletes the given file (ideally, created with this wrapper) from the filesystem - /// - /// The path to the file to delete - public static void DeleteFile(string fileName) - { - File.Delete(fileName); - } - - #endregion - - /// - /// Perform calculations to determine how many bytes to copy and what the new buffer offset - /// will be for copying. - /// - /// Number of bytes requested to copy - /// Number of bytes copied so far - /// New offset to start copying from/to - /// Number of bytes to copy in this iteration - private void GetByteCounts(int bytes, int bytesCopied, out int bufferOffset, out int bytesToCopy) - { - bufferOffset = (int) (currentOffset - startOffset); - bytesToCopy = bytes - bytesCopied; - if (bytesToCopy > buffer.Length - bufferOffset) - { - bytesToCopy = buffer.Length - bufferOffset; - } - } - - /// - /// Moves the internal buffer to the specified offset into the file - /// - /// Offset into the file to move to - private void MoveTo(long offset) - { - if (buffer.Length > bufferDataSize || // buffer is not completely filled - offset < startOffset || // before current buffer start - offset >= (startOffset + buffer.Length)) // beyond current buffer end - { - // init the offset - startOffset = offset; - - // position file pointer - fileStream.Seek(startOffset, SeekOrigin.Begin); - - // fill in the buffer - bufferDataSize = fileStream.Read(buffer, 0, buffer.Length); - } - // make sure to record where we are - currentOffset = offset; - } - - #region IDisposable Implementation - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposed) - { - return; - } - - if (disposing && fileStream != null) - { - if(fileStream.CanWrite) { Flush(); } - fileStream.Dispose(); - } - - disposed = true; - } - - ~FileStreamWrapper() - { - Dispose(false); - } - - #endregion - } -} diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWrapper.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWrapper.cs deleted file mode 100644 index 38c283c5..00000000 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWrapper.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// 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; - -namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage -{ - /// - /// Interface for a wrapper around a filesystem reader/writer, mainly for unit testing purposes - /// - public interface IFileStreamWrapper : IDisposable - { - void Init(string fileName, int bufferSize, FileAccess fileAccessMode); - int ReadData(byte[] buffer, int bytes); - int ReadData(byte[] buffer, int bytes, long fileOffset); - int WriteData(byte[] buffer, int bytes); - void Flush(); - } -} diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs index c06a13ac..573a62d4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs @@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage /// A public IFileStreamReader GetReader(string fileName) { - return new ServiceBufferFileStreamReader(new FileStreamWrapper(), fileName); + return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read)); } /// @@ -42,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage /// A public IFileStreamWriter GetWriter(string fileName, int maxCharsToStore, int maxXmlCharsToStore) { - return new ServiceBufferFileStreamWriter(new FileStreamWrapper(), fileName, maxCharsToStore, maxXmlCharsToStore); + return new ServiceBufferFileStreamWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite), maxCharsToStore, maxXmlCharsToStore); } /// @@ -51,14 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage /// The file to dispose of public void DisposeFile(string fileName) { - try - { - FileStreamWrapper.DeleteFile(fileName); - } - catch - { - // If we have problems deleting the file from a temp location, we don't really care - } + FileUtils.SafeFileDelete(fileName); } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs index 8547999b..5df33596 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs @@ -23,22 +23,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage private byte[] buffer; - private readonly IFileStreamWrapper fileStream; + private readonly Stream fileStream; - private Dictionary> readMethods; + private readonly Dictionary> readMethods; #endregion /// /// Constructs a new ServiceBufferFileStreamReader and initializes its state /// - /// The filestream wrapper to read from - /// The name of the file to read from - public ServiceBufferFileStreamReader(IFileStreamWrapper fileWrapper, string fileName) + /// The filestream to read from + public ServiceBufferFileStreamReader(Stream stream) { // Open file for reading/writing - fileStream = fileWrapper; - fileStream.Init(fileName, DefaultBufferSize, FileAccess.Read); + if (!stream.CanRead || !stream.CanSeek) + { + throw new InvalidOperationException("Stream must be readable and seekable"); + } + fileStream = stream; // Create internal buffer buffer = new byte[DefaultBufferSize]; @@ -372,7 +374,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage { // read in length information int lengthValue; - int lengthLength = fileStream.ReadData(buffer, 1, offset); + fileStream.Seek(offset, SeekOrigin.Begin); + int lengthLength = fileStream.Read(buffer, 0, 1); if (buffer[0] != 0xFF) { // one byte is enough @@ -381,7 +384,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage else { // read in next 4 bytes - lengthLength += fileStream.ReadData(buffer, 4); + lengthLength += fileStream.Read(buffer, 0, 4); // reconstruct the length lengthValue = BitConverter.ToInt32(buffer, 0); @@ -433,7 +436,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage else { AssureBufferLength(length.ValueLength); - fileStream.ReadData(buffer, length.ValueLength); + fileStream.Read(buffer, 0, length.ValueLength); T resultObject = convertFunc(length.ValueLength); result.RawObject = resultObject; result.DisplayValue = toStringFunc == null ? result.RawObject.ToString() : toStringFunc(resultObject); diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs index 2e4360d2..bb7167e9 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs @@ -23,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage #region Member Variables - private readonly IFileStreamWrapper fileStream; + private readonly Stream fileStream; private readonly int maxCharsToStore; private readonly int maxXmlCharsToStore; @@ -45,15 +45,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage /// /// Constructs a new writer /// - /// The file wrapper to use as the underlying file stream - /// Name of the file to write to + /// The file wrapper to use as the underlying file stream /// Maximum number of characters to store for long text fields /// Maximum number of characters to store for XML fields - public ServiceBufferFileStreamWriter(IFileStreamWrapper fileWrapper, string fileName, int maxCharsToStore, int maxXmlCharsToStore) + public ServiceBufferFileStreamWriter(Stream stream, int maxCharsToStore, int maxXmlCharsToStore) { // open file for reading/writing - fileStream = fileWrapper; - fileStream.Init(fileName, DefaultBufferLength, FileAccess.ReadWrite); + if (!stream.CanWrite || !stream.CanSeek) + { + throw new InvalidOperationException("Stream must be writable and seekable."); + } + fileStream = stream; // create internal buffer byteBuffer = new byte[DefaultBufferLength]; @@ -212,7 +214,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage public int WriteNull() { byteBuffer[0] = 0x00; - return fileStream.WriteData(byteBuffer, 1); + return WriteHelper(byteBuffer, 1); } /// @@ -224,7 +226,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x02; // length shortBuffer[0] = val; Buffer.BlockCopy(shortBuffer, 0, byteBuffer, 1, 2); - return fileStream.WriteData(byteBuffer, 3); + return WriteHelper(byteBuffer, 3); } /// @@ -236,7 +238,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x04; // length intBuffer[0] = val; Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4); - return fileStream.WriteData(byteBuffer, 5); + return WriteHelper(byteBuffer, 5); } /// @@ -248,7 +250,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x08; // length longBuffer[0] = val; Buffer.BlockCopy(longBuffer, 0, byteBuffer, 1, 8); - return fileStream.WriteData(byteBuffer, 9); + return WriteHelper(byteBuffer, 9); } /// @@ -260,7 +262,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x02; // length charBuffer[0] = val; Buffer.BlockCopy(charBuffer, 0, byteBuffer, 1, 2); - return fileStream.WriteData(byteBuffer, 3); + return WriteHelper(byteBuffer, 3); } /// @@ -271,7 +273,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage { byteBuffer[0] = 0x01; // length byteBuffer[1] = (byte) (val ? 0x01 : 0x00); - return fileStream.WriteData(byteBuffer, 2); + return WriteHelper(byteBuffer, 2); } /// @@ -282,7 +284,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage { byteBuffer[0] = 0x01; // length byteBuffer[1] = val; - return fileStream.WriteData(byteBuffer, 2); + return WriteHelper(byteBuffer, 2); } /// @@ -294,7 +296,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x04; // length floatBuffer[0] = val; Buffer.BlockCopy(floatBuffer, 0, byteBuffer, 1, 4); - return fileStream.WriteData(byteBuffer, 5); + return WriteHelper(byteBuffer, 5); } /// @@ -306,7 +308,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[0] = 0x08; // length doubleBuffer[0] = val; Buffer.BlockCopy(doubleBuffer, 0, byteBuffer, 1, 8); - return fileStream.WriteData(byteBuffer, 9); + return WriteHelper(byteBuffer, 9); } /// @@ -330,7 +332,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage // data value Buffer.BlockCopy(arrInt32, 0, byteBuffer, 3, iLen - 3); - iTotalLen += fileStream.WriteData(byteBuffer, iLen); + iTotalLen += WriteHelper(byteBuffer, iLen); return iTotalLen; // len+data } @@ -346,7 +348,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage int iTotalLen = WriteLength(iLen); // length Buffer.BlockCopy(arrInt32, 0, byteBuffer, 0, iLen); - iTotalLen += fileStream.WriteData(byteBuffer, iLen); + iTotalLen += WriteHelper(byteBuffer, iLen); return iTotalLen; // len+data } @@ -374,7 +376,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage longBufferOffset[0] = dtoVal.Ticks; longBufferOffset[1] = dtoVal.Offset.Ticks; Buffer.BlockCopy(longBufferOffset, 0, byteBuffer, 1, 16); - return fileStream.WriteData(byteBuffer, 17); + return WriteHelper(byteBuffer, 17); } /// @@ -406,7 +408,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[3] = 0x00; byteBuffer[4] = 0x00; - iTotalLen = fileStream.WriteData(byteBuffer, 5); + iTotalLen = WriteHelper(byteBuffer, 5); } else { @@ -415,7 +417,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage // convert char array into byte array and write it out iTotalLen = WriteLength(bytes.Length); - iTotalLen += fileStream.WriteData(bytes, bytes.Length); + iTotalLen += WriteHelper(bytes, bytes.Length); } return iTotalLen; // len+data } @@ -438,12 +440,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage byteBuffer[3] = 0x00; byteBuffer[4] = 0x00; - iTotalLen = fileStream.WriteData(byteBuffer, 5); + iTotalLen = WriteHelper(byteBuffer, 5); } else { iTotalLen = WriteLength(bytesVal.Length); - iTotalLen += fileStream.WriteData(bytesVal, bytesVal.Length); + iTotalLen += WriteHelper(bytesVal, bytesVal.Length); } return iTotalLen; // len+data } @@ -507,7 +509,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage int iTmp = iLen & 0x000000FF; byteBuffer[0] = Convert.ToByte(iTmp); - return fileStream.WriteData(byteBuffer, 1); + return WriteHelper(byteBuffer, 1); } // The length won't fit in 1 byte, so we need to use 1 byte to signify that the length // is a full 4 bytes. @@ -516,7 +518,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage // convert int32 into array of bytes intBuffer[0] = iLen; Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4); - return fileStream.WriteData(byteBuffer, 5); + return WriteHelper(byteBuffer, 5); } /// @@ -532,6 +534,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage return val.IsNull ? WriteNull() : valueWriteFunc(val); } + private int WriteHelper(byte[] buffer, int length) + { + fileStream.Write(buffer, 0, length); + return length; + } + #endregion #region IDisposable Implementation diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/FileUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs similarity index 100% rename from src/Microsoft.SqlTools.ServiceLayer/QueryExecution/FileUtils.cs rename to src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Common.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Common.cs index c8f8c15f..c136b2ba 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Common.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Common.cs @@ -122,64 +122,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution return fileName; }); mock.Setup(fsf => fsf.GetReader(It.IsAny())) - .Returns(output => new ServiceBufferFileStreamReader(new InMemoryWrapper(storage[output]), output)); + .Returns(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output]))); mock.Setup(fsf => fsf.GetWriter(It.IsAny(), It.IsAny(), It.IsAny())) .Returns((output, chars, xml) => new ServiceBufferFileStreamWriter( - new InMemoryWrapper(storage[output]), output, chars, xml)); + new MemoryStream(storage[output]), chars, xml)); return mock.Object; } - public class InMemoryWrapper : IFileStreamWrapper - { - private readonly MemoryStream memoryStream; - private bool readingOnly; - - public InMemoryWrapper(byte[] storage) - { - memoryStream = new MemoryStream(storage); - } - - public void Close() - { - memoryStream.Dispose(); - } - - public void Dispose() - { - // We'll dispose this via a special method - } - - public void Flush() - { - if (readingOnly) { throw new InvalidOperationException(); } - } - - 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; - } - } - #endregion #region DbConnection Mocking diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/FileStreamWrapperTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/FileStreamWrapperTests.cs deleted file mode 100644 index 1ee471fd..00000000 --- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/FileStreamWrapperTests.cs +++ /dev/null @@ -1,218 +0,0 @@ -// -// 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(() => 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(() => 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(() => 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); - } - } - 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(() => fsw.ReadData(buf, 1)); - Assert.Throws(() => fsw.ReadData(buf, 1, 0)); - Assert.Throws(() => fsw.WriteData(buf, 1)); - Assert.Throws(() => 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(() => fsw.WriteData(buf, 1)); - Assert.Throws(() => 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 */} - } - } -} diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs index 80ca7fbc..b454eafb 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs @@ -6,45 +6,100 @@ using System; using System.Collections.Generic; using System.Data.SqlTypes; +using System.IO; using System.Text; using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage; +using Moq; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage { public class ReaderWriterPairTest { + [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(); + invalidStream.SetupGet(s => s.CanRead).Returns(false); + invalidStream.SetupGet(s => s.CanSeek).Returns(true); + Assert.Throws(() => + { + ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object); + 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(); + invalidStream.SetupGet(s => s.CanRead).Returns(true); + invalidStream.SetupGet(s => s.CanSeek).Returns(false); + Assert.Throws(() => + { + ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object); + obj.Dispose(); + }); + } + + [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(); + invalidStream.SetupGet(s => s.CanWrite).Returns(false); + invalidStream.SetupGet(s => s.CanSeek).Returns(true); + Assert.Throws(() => + { + ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, 1024, 1024); + 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(); + invalidStream.SetupGet(s => s.CanWrite).Returns(true); + invalidStream.SetupGet(s => s.CanSeek).Returns(false); + Assert.Throws(() => + { + ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, 1024, 1024); + obj.Dispose(); + }); + } + private static void VerifyReadWrite(int valueLength, T value, Func writeFunc, Func readFunc) { - // Setup: Create a mock file stream wrapper - Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]); - try + // Setup: Create a mock file stream + byte[] storage = new byte[8192]; + + // If: + // ... I write a type T to the writer + using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(new MemoryStream(storage), 10, 10)) { - // 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 outValue; - using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(mockWrapper, "abc")) - { - outValue = readFunc(reader); - } - - // Then: - Assert.Equal(value, outValue.Value.RawObject); - Assert.Equal(valueLength, outValue.TotalLength); - Assert.NotNull(outValue.Value); + int writtenBytes = writeFunc(writer, value); + Assert.Equal(valueLength, writtenBytes); } - finally + + // ... And read the type T back + FileStreamReadResult outValue; + using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(new MemoryStream(storage))) { - // Cleanup: Close the wrapper - mockWrapper.Close(); + outValue = readFunc(reader); } + + // Then: + Assert.Equal(value, outValue.Value.RawObject); + Assert.Equal(valueLength, outValue.TotalLength); + Assert.NotNull(outValue.Value); } [Theory] @@ -222,16 +277,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage [Fact] public void StringNullTest() { - // Setup: Create a mock file stream wrapper - Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]); - - // If: - // ... I write null as a string to the writer - using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10)) + // Setup: Create a mock file stream + using (MemoryStream stream = new MemoryStream(new byte[8192])) { - // Then: - // ... I should get an argument null exception - Assert.Throws(() => writer.WriteString(null)); + // If: + // ... I write null as a string to the writer + using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, 10, 10)) + { + // Then: + // ... I should get an argument null exception + Assert.Throws(() => writer.WriteString(null)); + } } } @@ -259,15 +315,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage public void BytesNullTest() { // Setup: Create a mock file stream wrapper - Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]); - - // If: - // ... I write null as a string to the writer - using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10)) + using (MemoryStream stream = new MemoryStream(new byte[8192])) { - // Then: - // ... I should get an argument null exception - Assert.Throws(() => writer.WriteBytes(null)); + // If: + // ... I write null as a string to the writer + using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, 10, 10)) + { + // Then: + // ... I should get an argument null exception + Assert.Throws(() => writer.WriteBytes(null)); + } } }