Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'net461'">$(DefineConstants);FEATURE_FILE_SYSTEM_ACL_EXTENSIONS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net5.0' OR '$(TargetFramework)' == 'netcoreapp3.1' OR '$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS;FEATURE_PATH_JOIN_WITH_SPAN</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net5.0'">$(DefineConstants);FEATURE_FILE_MOVE_WITH_OVERWRITE;FEATURE_SUPPORTED_OS_ATTRIBUTE;FEATURE_FILE_SYSTEM_WATCHER_FILTERS;FEATURE_ENDS_IN_DIRECTORY_SEPARATOR;FEATURE_PATH_JOIN_WITH_PARAMS;FEATURE_PATH_JOIN_WITH_FOUR_PATHS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0'">$(DefineConstants);FEATURE_FILE_SYSTEM_INFO_LINK_TARGET</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0'">$(DefineConstants);FEATURE_FILE_SYSTEM_INFO_LINK_TARGET;FEATURE_CREATE_SYMBOLIC_LINK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.109">
Expand Down
24 changes: 24 additions & 0 deletions src/System.IO.Abstractions.TestingHelpers/MockDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ private IDirectoryInfo CreateDirectoryInternal(string path, DirectorySecurity di
return created;
}

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc />
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(pathToTarget, nameof(pathToTarget));

if (Exists(path))
{
throw CommonExceptions.FileAlreadyExists(nameof(path));
}

var targetExists = Exists(pathToTarget);
if (!targetExists)
{
throw CommonExceptions.FileNotFound(pathToTarget);
}

mockFileDataAccessor.AddDirectory(path);
mockFileDataAccessor.GetFile(path).LinkTarget = pathToTarget;

return new MockDirectoryInfo(mockFileDataAccessor, path);
}
#endif

/// <inheritdoc />
public override void Delete(string path)
Expand Down
40 changes: 40 additions & 0 deletions src/System.IO.Abstractions.TestingHelpers/MockFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,46 @@ private Stream CreateInternal(string path, FileAccess access, FileOptions option
return OpenInternal(path, FileMode.Open, access, options);
}

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc />
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
if (path == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(path));
}

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

mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(pathToTarget, nameof(pathToTarget));

if (Exists(path))
{
throw CommonExceptions.FileAlreadyExists(nameof(path));
}

VerifyDirectoryExists(path);

var fileExists = mockFileDataAccessor.FileExists(pathToTarget);
if (!fileExists)
{
throw CommonExceptions.FileNotFound(pathToTarget);
}

var sourceFileData = mockFileDataAccessor.GetFile(pathToTarget);
sourceFileData.CheckFileAccess(pathToTarget, FileAccess.Read);
var destFileData = new MockFileData(new byte[0]);
destFileData.CreationTime = destFileData.LastAccessTime = DateTime.Now;
destFileData.LinkTarget = pathToTarget;
mockFileDataAccessor.AddFile(path, destFileData);

return new MockFileInfo(mockFileDataAccessor, path);
}
#endif
/// <inheritdoc />
public override StreamWriter CreateText(string path)
{
Expand Down
3 changes: 3 additions & 0 deletions src/System.IO.Abstractions/Converters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ internal static IEnumerable<FileSystemInfoBase> WrapFileSystemInfos(this IEnumer
internal static FileSystemInfoBase[] WrapFileSystemInfos(this FileSystemInfo[] input, IFileSystem fileSystem)
=> input.Select(info => WrapFileSystemInfo(fileSystem, info)).ToArray();

internal static FileSystemInfoBase WrapFileSystemInfo(this FileSystemInfo input, IFileSystem fileSystem)
=> WrapFileSystemInfo(fileSystem, input);

internal static IEnumerable<DirectoryInfoBase> WrapDirectories(this IEnumerable<DirectoryInfo> input, IFileSystem fileSystem)
=> input.Select(info => WrapDirectoryInfo(fileSystem, info));

Expand Down
5 changes: 4 additions & 1 deletion src/System.IO.Abstractions/DirectoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ internal DirectoryBase() { }
/// <inheritdoc cref="IDirectory.CreateDirectory(string,DirectorySecurity)"/>
[SupportedOSPlatform("windows")]
public abstract IDirectoryInfo CreateDirectory(string path, DirectorySecurity directorySecurity);

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc cref="IDirectory.CreateSymbolicLink(string, string)"/>
public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
/// <inheritdoc cref="IDirectory.Delete(string)"/>
public abstract void Delete(string path);

Expand Down
8 changes: 7 additions & 1 deletion src/System.IO.Abstractions/DirectoryWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ public override IDirectoryInfo CreateDirectory(string path, DirectorySecurity di
directoryInfo.Create(directorySecurity);
return new DirectoryInfoWrapper(FileSystem, directoryInfo);
}

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc />
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
return Directory.CreateSymbolicLink(path, pathToTarget).WrapFileSystemInfo(FileSystem);
}
#endif
/// <inheritdoc />
public override void Delete(string path)
{
Expand Down
5 changes: 4 additions & 1 deletion src/System.IO.Abstractions/FileBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ internal FileBase() { }

/// <inheritdoc cref="IFile.Create(string,int,FileOptions)"/>
public abstract Stream Create(string path, int bufferSize, FileOptions options);

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc cref="IFile.CreateSymbolicLink(string, string)"/>
public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
/// <inheritdoc cref="IFile.CreateText"/>
public abstract StreamWriter CreateText(string path);
/// <inheritdoc cref="IFile.Decrypt"/>
Expand Down
7 changes: 7 additions & 0 deletions src/System.IO.Abstractions/FileWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ public override Stream Create(string path, int bufferSize, FileOptions options)
return File.Create(path, bufferSize, options);
}

#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc />
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
return File.CreateSymbolicLink(path, pathToTarget).WrapFileSystemInfo(FileSystem);
}
#endif
/// <inheritdoc />
public override StreamWriter CreateText(string path)
{
Expand Down
4 changes: 4 additions & 0 deletions src/System.IO.Abstractions/IDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public interface IDirectory
/// <inheritdoc cref="Directory.CreateDirectory(string,DirectorySecurity)"/>
#endif
IDirectoryInfo CreateDirectory(string path, DirectorySecurity directorySecurity);
#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc cref="Directory.CreateSymbolicLink"/>
IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
/// <inheritdoc cref="Directory.Delete(string)"/>
void Delete(string path);
/// <inheritdoc cref="Directory.Delete(string,bool)"/>
Expand Down
4 changes: 4 additions & 0 deletions src/System.IO.Abstractions/IFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public partial interface IFile
Stream Create(string path, int bufferSize);
/// <inheritdoc cref="File.Create(string,int,FileOptions)"/>
Stream Create(string path, int bufferSize, FileOptions options);
#if FEATURE_CREATE_SYMBOLIC_LINK
/// <inheritdoc cref="File.CreateSymbolicLink"/>
IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
/// <inheritdoc cref="File.CreateText"/>
StreamWriter CreateText(string path);
/// <inheritdoc cref="File.Decrypt"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.AccessControl;
using NUnit.Framework;

namespace System.IO.Abstractions.TestingHelpers.Tests
{
using XFS = MockUnixSupport;

[TestFixture]
public class MockDirectorySymlinkTests
{

#if FEATURE_CREATE_SYMBOLIC_LINK

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldReturnFileSystemInfo()
{
// Arrange
var fileSystem = new MockFileSystem();
var pathToTarget = XFS.Path(@"C:\Folder\foo");
var path = XFS.Path(@"C:\bar");
fileSystem.AddDirectory(pathToTarget);

// Act
IFileSystemInfo fileSystemInfo = fileSystem.Directory.CreateSymbolicLink(path, pathToTarget);

// Assert
Assert.AreEqual(path, fileSystemInfo.FullName);
Assert.AreEqual(pathToTarget, fileSystemInfo.LinkTarget);
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldSucceedFromDirectoryInfo()
{
// Arrange
var fileSystem = new MockFileSystem();
var pathToTarget = XFS.Path(@"C:\Folder\foo");
var path = XFS.Path(@"C:\bar");
fileSystem.AddDirectory(pathToTarget);

// Act
fileSystem.Directory.CreateSymbolicLink(path, pathToTarget);
IDirectoryInfo directoryInfo = fileSystem.DirectoryInfo.FromDirectoryName(path);

// Assert
Assert.AreEqual(path, directoryInfo.FullName);
Assert.AreEqual(pathToTarget, directoryInfo.LinkTarget);
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithNullPath()
{
// Arrange
var fileSystem = new MockFileSystem();
var pathToTarget = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(pathToTarget);

// Act
var ex = Assert.Throws<ArgumentNullException>(() => fileSystem.Directory.CreateSymbolicLink(null, pathToTarget));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("path"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithNullTarget()
{
// Arrange
var fileSystem = new MockFileSystem();
var path = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(path);

// Act
var ex = Assert.Throws<ArgumentNullException>(() => fileSystem.Directory.CreateSymbolicLink(path, null));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("pathToTarget"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithEmptyPath()
{
// Arrange
var fileSystem = new MockFileSystem();
var pathToTarget = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(pathToTarget);

// Act
var ex = Assert.Throws<ArgumentException>(() => fileSystem.Directory.CreateSymbolicLink("", pathToTarget));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("path"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithEmptyTarget()
{
// Arrange
var fileSystem = new MockFileSystem();
string path = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(path);

// Act
var ex = Assert.Throws<ArgumentException>(() => fileSystem.Directory.CreateSymbolicLink(path, ""));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("pathToTarget"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithIllegalPath()
{
// Arrange
var fileSystem = new MockFileSystem();
string pathToTarget = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(pathToTarget);

// Act
var ex = Assert.Throws<ArgumentException>(() => fileSystem.Directory.CreateSymbolicLink(" ", pathToTarget));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("path"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithIllegalTarget()
{
// Arrange
var fileSystem = new MockFileSystem();
string path = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(path);

// Act
var ex = Assert.Throws<ArgumentException>(() => fileSystem.Directory.CreateSymbolicLink(path, " "));

// Assert
Assert.That(ex.ParamName, Is.EqualTo("pathToTarget"));
}

[Test]
[WindowsOnly(WindowsSpecifics.StrictPathRules)]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithIllegalCharactersInPath()
{
// Arrange
var fileSystem = new MockFileSystem();
string pathToTarget = XFS.Path(@"C:\Folder\foo");
fileSystem.AddDirectory(pathToTarget);

// Act
TestDelegate ex = () => fileSystem.Directory.CreateSymbolicLink(@"C:\bar_?_", pathToTarget);

// Assert
Assert.Throws<ArgumentException>(ex);
}

[Test]
[WindowsOnly(WindowsSpecifics.StrictPathRules)]
public void MockDirectory_CreateSymbolicLink_ShouldFailWithIllegalCharactersInTarget()
{
// Arrange
var fileSystem = new MockFileSystem();
string path = XFS.Path(@"C:\foo");

// Act
TestDelegate ex = () => fileSystem.Directory.CreateSymbolicLink(path, @"C:\bar_?_");

// Assert
Assert.Throws<ArgumentException>(ex);
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailIfPathExists()
{
// Arrange
var fileSystem = new MockFileSystem();
string pathToTarget = XFS.Path(@"C:\Folder\foo");
string path = XFS.Path(@"C:\Folder\bar");
fileSystem.AddDirectory(pathToTarget);
fileSystem.AddDirectory(path);

// Act
var ex = Assert.Throws<IOException>(() => fileSystem.Directory.CreateSymbolicLink(path, pathToTarget));

// Assert
Assert.That(ex.Message.Contains("path"));
}

[Test]
public void MockDirectory_CreateSymbolicLink_ShouldFailIfTargetDoesNotExist()
{
// Arrange
var fileSystem = new MockFileSystem();
string path = XFS.Path(@"C:\Folder\foo");
string pathToTarget = XFS.Path(@"C:\Target");

// Act
var ex = Assert.Throws<FileNotFoundException>(() => fileSystem.Directory.CreateSymbolicLink(path, pathToTarget));

// Assert
Assert.That(ex.Message.Contains(pathToTarget));
}
#endif
}
}
Loading