Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileStreamOptions overloads #52720

Merged
merged 12 commits into from
Jun 10, 2021
18 changes: 18 additions & 0 deletions src/libraries/System.IO.FileSystem/tests/File/Open.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ protected override FileStream CreateFileStream(string path, FileMode mode, FileA
}
}

public class File_Open_str_fm_fa_fs_buffer_fo : FileStream_ctor_str_fm_fa_fs_buffer_fo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test name correct? I thought the pattern was based on the arguments being accepted by the method, but these methods are all path,options rather than path,FileMode,FileAccess,FileShare,bufferSize,FileOptions.

Same for the subsequent change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have aligned these tests with the existing options-based test classes from FileStream.

{
protected override FileStream CreateFileStream(string path, FileMode mode)
{
return File.Open(path, new FileStreamOptions { Mode = mode, Access = (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite) });
}

protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access)
{
return File.Open(path, new FileStreamOptions { Mode = mode, Access = access });
}

protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
{
return File.Open(path, new FileStreamOptions { Mode = mode, Access = access, Share = share, Options = options, BufferSize = bufferSize });
}
}

public class File_OpenSpecial : FileStream_ctor_str_fm_fa_fs
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access)
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.IO.FileSystem/tests/FileInfo/Open.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ protected override FileStream CreateFileStream(string path, FileMode mode, FileA
}
}

public class FileInfo_Open_fm_fa_fs_buffer_fo : FileStream_ctor_str_fm_fa_fs_buffer_fo
{
protected override FileStream CreateFileStream(string path, FileMode mode)
{
return new FileInfo(path).Open(new FileStreamOptions { Mode = mode, Access = (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite) });
}

protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access)
{
return new FileInfo(path).Open(new FileStreamOptions { Mode = mode, Access = access });
}

protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
{
return new FileInfo(path).Open(new FileStreamOptions { Mode = mode, Access = access, Share = share, Options = options, BufferSize = bufferSize });
}
}

public class FileInfo_OpenSpecial : FileStream_ctor_str_fm_fa_fs
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@ public class StreamReader_StringCtorTests
public static void NullArgs_ThrowsArgumentNullException()
{
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, (FileStreamOptions)null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, (Encoding)null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, null, true));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, null, true, null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamReader((string)null, null, true, -1));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamReader("", null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamReader("", (Encoding)null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamReader("", null, true));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamReader("", null, true, null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamReader("", null, true, -1));
AssertExtensions.Throws<ArgumentNullException>("options", () => new StreamReader("path", (FileStreamOptions)null));
AssertExtensions.Throws<ArgumentNullException>("options", () => new StreamReader("path", Encoding.UTF8, true, null));

}

[Fact]
public static void EmptyPath_ThrowsArgumentException()
{
// No argument name for the empty path exception
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader(""));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader("", new FileStreamOptions()));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader("", Encoding.UTF8));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader("", Encoding.UTF8, true));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader("", Encoding.UTF8, true, new FileStreamOptions()));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamReader("", Encoding.UTF8, true, -1));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ public class StreamWriter_StringCtorTests
public static void NullArgs_ThrowsArgumentNullException()
{
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamWriter((string)null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamWriter((string)null, null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamWriter((string)null, true));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamWriter((string)null, true, null));
AssertExtensions.Throws<ArgumentNullException>("path", () => new StreamWriter((string)null, true, null, -1));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("path", true, null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("path", null, null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("path", true, null, -1));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("", true, null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("", null, null));
AssertExtensions.Throws<ArgumentNullException>("encoding", () => new StreamWriter("", true, null, -1));
}

Expand All @@ -29,8 +32,10 @@ public static void EmptyPath_ThrowsArgumentException()
{
// No argument name for the empty path exception
AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter(""));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter("", new FileStreamOptions()));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter("", true));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter("", true, Encoding.UTF8));
AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter("", Encoding.UTF8, new FileStreamOptions()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only see negative tests here for StreamReader/Writer. Are there tests that validate the ctors do what they're supposed to do when used correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully agree they are missing, but I did not find any 'positive' test covering the existing constructors with path, FileMode, FileAccess, FileShare, bufferSize and FileOptions parameters.
Which type of tests do you expect here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which type of tests do you expect here?

I was primarily thinking about tests to validate that we're correctly passing through the arguments, e.g. that if you pass CustomEncoding as the encoding, that's actually what gets used by the writer.

AssertExtensions.Throws<ArgumentException>(null, () => new StreamWriter("", true, Encoding.UTF8, -1));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,11 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\EnumerationOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\EndOfStreamException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\File.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\File.netcoreapp.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileAccess.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileInfo.netcoreapp.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileLoadException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileNotFoundException.cs" />
Expand Down
5 changes: 2 additions & 3 deletions src/libraries/System.Private.CoreLib/src/System/IO/File.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace System.IO
{
// Class for creating FileStream objects, and some basic file management
// routines such as Delete, etc.
public static class File
public static partial class File
{
private const int MaxByteArrayLength = 0x7FFFFFC7;
private static Encoding? s_UTF8NoBOM;
Expand Down Expand Up @@ -262,8 +262,7 @@ public static FileStream OpenRead(string path)

public static FileStream OpenWrite(string path)
{
return new FileStream(path, FileMode.OpenOrCreate,
FileAccess.Write, FileShare.None);
return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
}

public static string ReadAllText(string path)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.IO
{
public static partial class File
{
/// <summary>
/// Initializes a new instance of the <see cref="System.IO.FileStream" /> class with the specified path, creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size, additional file options and the allocation size.
/// </summary>
/// <remarks><see cref="System.IO.FileStream(string,System.IO.FileStreamOptions)"/> for information about exceptions.</remarks>
public static FileStream Open(string path, FileStreamOptions options) => new FileStream(path, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public FileStream Open(FileMode mode, FileAccess access, FileShare share)
=> new FileStream(NormalizedPath, mode, access, share);

public FileStream OpenRead()
=> new FileStream(NormalizedPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
=> new FileStream(NormalizedPath, FileMode.Open, FileAccess.Read, FileShare.Read, File.DefaultBufferSize, false);

public FileStream OpenWrite()
=> new FileStream(NormalizedPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.IO
{
public sealed partial class FileInfo : FileSystemInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="System.IO.FileStream" /> class with the specified creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size, additional file options and the allocation size.
/// </summary>
/// <remarks><see cref="System.IO.FileStream(string,System.IO.FileStreamOptions)"/> for information about exceptions.</remarks>
public FileStream Open(FileStreamOptions options) => File.Open(NormalizedPath, options);
}
}
36 changes: 30 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,23 +193,47 @@ public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteO
{
}

public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) :
this(ValidateArgsAndOpenPath(path, encoding, bufferSize), encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen: false)
public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
: this(ValidateArgsAndOpenPath(path, encoding, bufferSize), encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen: false)
{
}

public StreamReader(string path, FileStreamOptions options)
: this(path, Encoding.UTF8, true, options)
{
}

public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, FileStreamOptions options)
: this(ValidateArgsAndOpenPath(path, encoding, options), encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
{
}

private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, FileStreamOptions options)
{
ValidateArgs(path, encoding);
if (options == null)
throw new ArgumentNullException(nameof(options));

return new FileStream(path, options);
}

private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, int bufferSize)
{
ValidateArgs(path, encoding);
if (bufferSize <= 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to make this < rather than <= ? Or that'll be handled separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it should be handled separately in #53497

throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);

return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize);
}

private static void ValidateArgs(string path, Encoding encoding)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (encoding == null)
throw new ArgumentNullException(nameof(encoding));
if (path.Length == 0)
throw new ArgumentException(SR.Argument_EmptyPath);
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);

return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
}

public override void Close()
Expand Down
32 changes: 28 additions & 4 deletions src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,42 @@ public StreamWriter(string path, bool append, Encoding encoding)
{
}

public StreamWriter(string path, FileStreamOptions options)
: this(path, UTF8NoBOM, options)
{
}

public StreamWriter(string path, Encoding encoding, FileStreamOptions options)
: this(ValidateArgsAndOpenPath(path, encoding, options), encoding, DefaultFileStreamBufferSize)
{
}

private static Stream ValidateArgsAndOpenPath(string path, Encoding encoding, FileStreamOptions options)
{
ValidateArgs(path, encoding);
if (options == null)
throw new ArgumentNullException(nameof(options));

return new FileStream(path, options);
}

private static Stream ValidateArgsAndOpenPath(string path, bool append, Encoding encoding, int bufferSize)
{
ValidateArgs(path, encoding);
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);

return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize);
}

private static void ValidateArgs(string path, Encoding encoding)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (encoding == null)
throw new ArgumentNullException(nameof(encoding));
if (path.Length == 0)
throw new ArgumentException(SR.Argument_EmptyPath);
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);

return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
}

public override void Close()
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7564,6 +7564,7 @@ public static partial class File
public static System.IO.FileStream Open(string path, System.IO.FileMode mode) { throw null; }
public static System.IO.FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access) { throw null; }
public static System.IO.FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) { throw null; }
public static System.IO.FileStream Open(string path, System.IO.FileStreamOptions options) { throw null; }
public static System.IO.FileStream OpenRead(string path) { throw null; }
public static System.IO.StreamReader OpenText(string path) { throw null; }
public static System.IO.FileStream OpenWrite(string path) { throw null; }
Expand Down Expand Up @@ -7625,6 +7626,7 @@ public sealed partial class FileInfo : System.IO.FileSystemInfo
public System.IO.FileStream Open(System.IO.FileMode mode) { throw null; }
public System.IO.FileStream Open(System.IO.FileMode mode, System.IO.FileAccess access) { throw null; }
public System.IO.FileStream Open(System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) { throw null; }
public System.IO.FileStream Open(System.IO.FileStreamOptions options) { throw null; }
public System.IO.FileStream OpenRead() { throw null; }
public System.IO.StreamReader OpenText() { throw null; }
public System.IO.FileStream OpenWrite() { throw null; }
Expand Down Expand Up @@ -7821,10 +7823,12 @@ public partial class StreamReader : System.IO.TextReader
public StreamReader(System.IO.Stream stream, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) { }
public StreamReader(System.IO.Stream stream, System.Text.Encoding? encoding = null, bool detectEncodingFromByteOrderMarks = true, int bufferSize = -1, bool leaveOpen = false) { }
public StreamReader(string path) { }
public StreamReader(string path, System.IO.FileStreamOptions options) { }
public StreamReader(string path, bool detectEncodingFromByteOrderMarks) { }
public StreamReader(string path, System.Text.Encoding encoding) { }
public StreamReader(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks) { }
public StreamReader(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) { }
public StreamReader(string path, System.Text.Encoding encoding, bool detectEncodingFromByteOrderMarks, System.IO.FileStreamOptions options) { }
public virtual System.IO.Stream BaseStream { get { throw null; } }
public virtual System.Text.Encoding CurrentEncoding { get { throw null; } }
public bool EndOfStream { get { throw null; } }
Expand Down Expand Up @@ -7854,9 +7858,11 @@ public partial class StreamWriter : System.IO.TextWriter
public StreamWriter(System.IO.Stream stream, System.Text.Encoding encoding, int bufferSize) { }
public StreamWriter(System.IO.Stream stream, System.Text.Encoding? encoding = null, int bufferSize = -1, bool leaveOpen = false) { }
public StreamWriter(string path) { }
public StreamWriter(string path, System.IO.FileStreamOptions options) { }
public StreamWriter(string path, bool append) { }
public StreamWriter(string path, bool append, System.Text.Encoding encoding) { }
public StreamWriter(string path, bool append, System.Text.Encoding encoding, int bufferSize) { }
public StreamWriter(string path, System.Text.Encoding encoding, System.IO.FileStreamOptions options) { }
public virtual bool AutoFlush { get { throw null; } set { } }
public virtual System.IO.Stream BaseStream { get { throw null; } }
public override System.Text.Encoding Encoding { get { throw null; } }
Expand Down