Skip to content

Commit

Permalink
feat: add overwrite option to File.Move and FileInfo.MoveTo (#678)
Browse files Browse the repository at this point in the history
  • Loading branch information
fgreinacher committed Nov 25, 2020
1 parent bb2a49d commit 566afec
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 11 deletions.
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

0 comments on commit 566afec

Please sign in to comment.