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

feat: add overwrite option to File.Move and FileInfo.MoveTo #678

Merged
merged 1 commit into from Nov 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Build.props
Expand Up @@ -12,6 +12,7 @@
<PackageProjectUrl>https://github.com/System-IO-Abstractions/System.IO.Abstractions</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<DefineConstants Condition="'$(TargetFramework)' == 'net5.0' OR '$(TargetFramework)' == 'netcoreapp3.1' OR '$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONSFEATURE_PATH_JOIN_WITH_SPAN</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net5.0'">$(DefineConstants);FEATURE_FILE_MOVE_WITH_OVERWRITE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37">
Expand Down
46 changes: 46 additions & 0 deletions src/System.IO.Abstractions.TestingHelpers/MockFile.cs
Expand Up @@ -357,6 +357,52 @@ public override void Move(string sourceFileName, string destFileName)
mockFileDataAccessor.RemoveFile(sourceFileName);
}

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
public override void Move(string sourceFileName, string destFileName, bool overwrite)
{
if (sourceFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(sourceFileName));
}

if (destFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(destFileName));
}

mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(sourceFileName, nameof(sourceFileName));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(destFileName, nameof(destFileName));

if (mockFileDataAccessor.GetFile(destFileName) != null)
{
if (destFileName.Equals(sourceFileName))
{
return;
}
else if (!overwrite)
{
throw new IOException("A file can not be created if it already exists.");
}
}


var sourceFile = mockFileDataAccessor.GetFile(sourceFileName);

if (sourceFile == null)
{
throw CommonExceptions.FileNotFound(sourceFileName);
}
if (!sourceFile.AllowedFileShare.HasFlag(FileShare.Delete))
{
throw CommonExceptions.ProcessCannotAccessFileInUse();
}
VerifyDirectoryExists(destFileName);

mockFileDataAccessor.AddFile(destFileName, new MockFileData(sourceFile));
mockFileDataAccessor.RemoveFile(sourceFileName);
}
#endif

public override Stream Open(string path, FileMode mode)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
Expand Down
13 changes: 13 additions & 0 deletions src/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs
Expand Up @@ -236,6 +236,19 @@ public override void MoveTo(string destFileName)
path = movedFileInfo.FullName;
}

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
public override void MoveTo(string destFileName, bool overwrite)
{
var movedFileInfo = CopyTo(destFileName, overwrite);
if (destFileName == FullName)
{
return;
}
Delete();
path = movedFileInfo.FullName;
}
#endif

public override Stream Open(FileMode mode)
{
return new MockFile(mockFileSystem).Open(FullName, mode);
Expand Down
5 changes: 5 additions & 0 deletions src/System.IO.Abstractions/FileBase.cs
Expand Up @@ -260,6 +260,11 @@ protected FileBase(IFileSystem fileSystem)
/// <inheritdoc cref="File.Move"/>
public abstract void Move(string sourceFileName, string destFileName);

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
/// <inheritdoc cref="File.Move(string,string,bool)"/>
public abstract void Move(string sourceFileName, string destFileName, bool overwrite);
#endif

/// <inheritdoc cref="File.Open(string,FileMode)"/>
public abstract Stream Open(string path, FileMode mode);

Expand Down
5 changes: 5 additions & 0 deletions src/System.IO.Abstractions/FileInfoBase.cs
Expand Up @@ -43,6 +43,11 @@ protected FileInfoBase(IFileSystem fileSystem) : base(fileSystem)
/// <inheritdoc cref="FileInfo.MoveTo"/>
public abstract void MoveTo(string destFileName);

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
/// <inheritdoc cref="FileInfo.MoveTo(string,bool)"/>
public abstract void MoveTo(string destFileName, bool overwrite);
#endif

/// <inheritdoc cref="FileInfo.Open(FileMode)"/>
public abstract Stream Open(FileMode mode);

Expand Down
7 changes: 7 additions & 0 deletions src/System.IO.Abstractions/FileInfoWrapper.cs
Expand Up @@ -134,6 +134,13 @@ public override void MoveTo(string destFileName)
instance.MoveTo(destFileName);
}

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
public override void MoveTo(string destFileName, bool overwrite)
{
instance.MoveTo(destFileName, overwrite);
}
#endif

public override Stream Open(FileMode mode)
{
return instance.Open(mode);
Expand Down
8 changes: 8 additions & 0 deletions src/System.IO.Abstractions/FileWrapper.cs
Expand Up @@ -149,6 +149,14 @@ public override void Move(string sourceFileName, string destFileName)
File.Move(sourceFileName, destFileName);
}

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
public override void Move(string sourceFileName, string destFileName, bool overwrite)
{
File.Move(sourceFileName, destFileName, overwrite);
}
#endif


public override Stream Open(string path, FileMode mode)
{
return File.Open(path, mode);
Expand Down
6 changes: 5 additions & 1 deletion src/System.IO.Abstractions/IFile.cs
Expand Up @@ -61,8 +61,12 @@ public partial interface IFile
DateTime GetLastWriteTime(string path);
/// <inheritdoc cref="File.GetLastWriteTimeUtc"/>
DateTime GetLastWriteTimeUtc(string path);
/// <inheritdoc cref="File.Move"/>
/// <inheritdoc cref="File.Move(string,string)"/>
void Move(string sourceFileName, string destFileName);
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
/// <inheritdoc cref="File.Move(string,string,bool)"/>
void Move(string sourceFileName, string destFileName, bool overwrite);
#endif
/// <inheritdoc cref="File.Open(string,FileMode)"/>
Stream Open(string path, FileMode mode);
/// <inheritdoc cref="File.Open(string,FileMode,FileAccess)"/>
Expand Down
6 changes: 5 additions & 1 deletion src/System.IO.Abstractions/IFileInfo.cs
Expand Up @@ -22,8 +22,12 @@ public interface IFileInfo : IFileSystemInfo
FileSecurity GetAccessControl();
/// <inheritdoc cref="FileInfo.GetAccessControl(AccessControlSections)"/>
FileSecurity GetAccessControl(AccessControlSections includeSections);
/// <inheritdoc cref="FileInfo.MoveTo"/>
/// <inheritdoc cref="FileInfo.MoveTo(string)"/>
void MoveTo(string destFileName);
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
/// <inheritdoc cref="FileInfo.MoveTo(string,bool)"/>
void MoveTo(string destFileName, bool overwrite);
#endif
/// <inheritdoc cref="FileInfo.Open(FileMode)"/>
Stream Open(FileMode mode);
/// <inheritdoc cref="FileInfo.Open(FileMode,FileAccess)"/>
Expand Down
Expand Up @@ -527,6 +527,27 @@ public void MockFileInfo_MoveTo_ThrowsExceptionIfSourceDoesntExist()
Assert.Throws<FileNotFoundException>(action);
}



#if FEATURE_FILE_MOVE_WITH_OVERWRITE
[Test]
public void MockFileInfo_MoveToWithOverwrite_ShouldSucceedWhenTargetAlreadyExists()
{
string sourceFilePath = XFS.Path(@"c:\something\demo.txt");
string sourceFileContent = "this is some content";
string destFilePath = XFS.Path(@"c:\somethingelse\demo1.txt");
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
{
{sourceFilePath, new MockFileData(sourceFileContent)},
{destFilePath, new MockFileData(sourceFileContent)}
});

fileSystem.FileInfo.FromFileName(sourceFilePath).MoveTo(destFilePath, overwrite: true);

Assert.That(fileSystem.File.ReadAllText(destFilePath), Is.EqualTo(sourceFileContent));
}
#endif

[Test]
public void MockFileInfo_CopyTo_ThrowsExceptionIfSourceDoesntExist()
{
Expand Down
Expand Up @@ -186,6 +186,6 @@ public void MockFile_Lock_FileShareDeleteDoesNotThrowDelete()
Assert.DoesNotThrow(() => filesystem.File.Delete(filepath));
}

private static IResolveConstraint IOException() => Is.TypeOf<IOException>().And.Property("HResult").EqualTo(unchecked((int) 0x80070020));
private static IResolveConstraint IOException() => Is.TypeOf<IOException>().And.Property("HResult").EqualTo(unchecked((int)0x80070020));
}
}
Expand Up @@ -63,6 +63,25 @@ public void MockFile_Move_ShouldThrowIOExceptionWhenTargetAlreadyExists()
Assert.That(exception.Message, Is.EqualTo("A file can not be created if it already exists."));
}

#if FEATURE_FILE_MOVE_WITH_OVERWRITE
[Test]
public void MockFile_MoveWithOverwrite_ShouldSucceedWhenTargetAlreadyExists()
{
string sourceFilePath = XFS.Path(@"c:\something\demo.txt");
string sourceFileContent = "this is some content";
string destFilePath = XFS.Path(@"c:\somethingelse\demo1.txt");
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
{
{sourceFilePath, new MockFileData(sourceFileContent)},
{destFilePath, new MockFileData(sourceFileContent)}
});

fileSystem.File.Move(sourceFilePath, destFilePath, overwrite: true);

Assert.That(fileSystem.File.ReadAllText(destFilePath), Is.EqualTo(sourceFileContent));
}
#endif

[Test]
public void MockFile_Move_ShouldThrowArgumentNullExceptionWhenSourceIsNull_Message()
{
Expand Down Expand Up @@ -227,7 +246,8 @@ public void MockFile_Move_ShouldThrowArgumentExceptionWhenSourceIsEmpty_Message(
}

[Test]
public void MockFile_Move_ShouldThrowArgumentExceptionWhenSourceIsEmpty_ParamName() {
public void MockFile_Move_ShouldThrowArgumentExceptionWhenSourceIsEmpty_ParamName()
{
string destFilePath = XFS.Path(@"c:\something\demo.txt");
var fileSystem = new MockFileSystem();

Expand Down Expand Up @@ -260,7 +280,8 @@ public void MockFile_Move_ShouldThrowArgumentNullExceptionWhenTargetIsNull_Messa
}

[Test]
public void MockFile_Move_ShouldThrowArgumentNullExceptionWhenTargetIsNull_ParamName() {
public void MockFile_Move_ShouldThrowArgumentNullExceptionWhenTargetIsNull_ParamName()
{
string sourceFilePath = XFS.Path(@"c:\something\demo.txt");
var fileSystem = new MockFileSystem();

Expand Down
Expand Up @@ -8,7 +8,6 @@
"System.Object GetLifetimeService()",
"System.Object InitializeLifetimeService()",
"Void .ctor(System.String)",
"Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)",
"Void MoveTo(System.String, Boolean)"
"Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)"
]
}
Expand Up @@ -6,7 +6,5 @@
"System.Threading.Tasks.Task WriteAllLinesAsync(System.String, System.String[], System.Threading.CancellationToken)",
"Void SetAccessControl(System.String, System.Security.AccessControl.FileSecurity)"
],
"MissingMembers": [
"Void Move(System.String, System.String, Boolean)"
]
"MissingMembers": []
}
2 changes: 1 addition & 1 deletion version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "13.0",
"version": "13.1",
"assemblyVersion": {
"precision": "major"
},
Expand Down