Skip to content

Commit 3065590

Browse files
authored
Add advanced path operations (#555)
* Add IPath.IsPathFullyQualified (resolves #548) * Add IPath.GetRelativePath (resolves #399)
1 parent 9fea189 commit 3065590

9 files changed

Lines changed: 109 additions & 25 deletions

File tree

Directory.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<Authors>Tatham Oddie &amp; friends</Authors>
66
<SignAssembly Condition="'$(Configuration)' == 'Release'">True</SignAssembly>
77
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)StrongName.snk</AssemblyOriginatorKeyFile>
8+
<DefineConstants Condition="'$(TargetFramework)' == 'netcoreapp3.0' OR '$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS</DefineConstants>
9+
810
</PropertyGroup>
911
<ItemGroup>
1012
<PackageReference Include="Nerdbank.GitVersioning" Version="3.0.50">

System.IO.Abstractions.TestingHelpers.Tests/MockPathTests.cs

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,13 @@ public static IEnumerable<string[]> GetFullPath_RelativePaths_Cases
143143
{
144144
get
145145
{
146-
yield return new [] { XFS.Path(@"c:\a"), "b", XFS.Path(@"c:\a\b") };
147-
yield return new [] { XFS.Path(@"c:\a\b"), "c", XFS.Path(@"c:\a\b\c") };
148-
yield return new [] { XFS.Path(@"c:\a\b"), XFS.Path(@"c\"), XFS.Path(@"c:\a\b\c\") };
149-
yield return new [] { XFS.Path(@"c:\a\b"), XFS.Path(@"..\c"), XFS.Path(@"c:\a\c") };
150-
yield return new [] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\c\..\"), XFS.Path(@"c:\a\b\") };
151-
yield return new [] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\..\..\..\..\d"), XFS.Path(@"c:\d") };
152-
yield return new [] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\..\..\..\..\d\"), XFS.Path(@"c:\d\") };
146+
yield return new[] { XFS.Path(@"c:\a"), "b", XFS.Path(@"c:\a\b") };
147+
yield return new[] { XFS.Path(@"c:\a\b"), "c", XFS.Path(@"c:\a\b\c") };
148+
yield return new[] { XFS.Path(@"c:\a\b"), XFS.Path(@"c\"), XFS.Path(@"c:\a\b\c\") };
149+
yield return new[] { XFS.Path(@"c:\a\b"), XFS.Path(@"..\c"), XFS.Path(@"c:\a\c") };
150+
yield return new[] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\c\..\"), XFS.Path(@"c:\a\b\") };
151+
yield return new[] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\..\..\..\..\d"), XFS.Path(@"c:\d") };
152+
yield return new[] { XFS.Path(@"c:\a\b\c"), XFS.Path(@"..\..\..\..\..\d\"), XFS.Path(@"c:\d\") };
153153
}
154154
}
155155

@@ -172,11 +172,11 @@ public static IEnumerable<string[]> GetFullPath_RootedPathWithRelativeSegments_C
172172
{
173173
get
174174
{
175-
yield return new [] { XFS.Path(@"c:\a\b\..\c"), XFS.Path(@"c:\a\c") };
176-
yield return new [] { XFS.Path(@"c:\a\b\.\.\..\.\c"), XFS.Path(@"c:\a\c") };
177-
yield return new [] { XFS.Path(@"c:\a\b\.\c"), XFS.Path(@"c:\a\b\c") };
178-
yield return new [] { XFS.Path(@"c:\a\b\.\.\.\.\c"), XFS.Path(@"c:\a\b\c") };
179-
yield return new [] { XFS.Path(@"c:\a\..\..\c"), XFS.Path(@"c:\c") };
175+
yield return new[] { XFS.Path(@"c:\a\b\..\c"), XFS.Path(@"c:\a\c") };
176+
yield return new[] { XFS.Path(@"c:\a\b\.\.\..\.\c"), XFS.Path(@"c:\a\c") };
177+
yield return new[] { XFS.Path(@"c:\a\b\.\c"), XFS.Path(@"c:\a\b\c") };
178+
yield return new[] { XFS.Path(@"c:\a\b\.\.\.\.\c"), XFS.Path(@"c:\a\b\c") };
179+
yield return new[] { XFS.Path(@"c:\a\..\..\c"), XFS.Path(@"c:\c") };
180180
}
181181
}
182182

@@ -198,14 +198,14 @@ public static IEnumerable<string[]> GetFullPath_AbsolutePaths_Cases
198198
{
199199
get
200200
{
201-
yield return new [] { XFS.Path(@"c:\a"), XFS.Path(@"/b"), XFS.Path(@"c:\b") };
202-
yield return new [] { XFS.Path(@"c:\a"), XFS.Path(@"/b\"), XFS.Path(@"c:\b\") };
203-
yield return new [] { XFS.Path(@"c:\a"), XFS.Path(@"\b"), XFS.Path(@"c:\b") };
204-
yield return new [] { XFS.Path(@"c:\a"), XFS.Path(@"\b\..\c"), XFS.Path(@"c:\c") };
205-
yield return new [] { XFS.Path(@"z:\a"), XFS.Path(@"\b\..\c"), XFS.Path(@"z:\c") };
206-
yield return new [] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c"), XFS.Path(@"\\computer\share\c") };
207-
yield return new [] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c\..\d"), XFS.Path(@"\\computer\share\d") };
208-
yield return new [] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c\..\..\d"), XFS.Path(@"\\computer\share\d") };
201+
yield return new[] { XFS.Path(@"c:\a"), XFS.Path(@"/b"), XFS.Path(@"c:\b") };
202+
yield return new[] { XFS.Path(@"c:\a"), XFS.Path(@"/b\"), XFS.Path(@"c:\b\") };
203+
yield return new[] { XFS.Path(@"c:\a"), XFS.Path(@"\b"), XFS.Path(@"c:\b") };
204+
yield return new[] { XFS.Path(@"c:\a"), XFS.Path(@"\b\..\c"), XFS.Path(@"c:\c") };
205+
yield return new[] { XFS.Path(@"z:\a"), XFS.Path(@"\b\..\c"), XFS.Path(@"z:\c") };
206+
yield return new[] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c"), XFS.Path(@"\\computer\share\c") };
207+
yield return new[] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c\..\d"), XFS.Path(@"\\computer\share\d") };
208+
yield return new[] { XFS.Path(@"z:\a"), XFS.Path(@"\\computer\share\c\..\..\d"), XFS.Path(@"\\computer\share\d") };
209209
}
210210
}
211211

@@ -274,7 +274,7 @@ public void GetFullPath_WithMultipleDirectorySeparators_ShouldReturnTheNormalize
274274
var mockPath = new MockPath(mockFileSystem);
275275

276276
//Act
277-
var actualFullPath = mockPath.GetFullPath(XFS.Path(@"c:\foo\\//bar\file.dat"));
277+
var actualFullPath = mockPath.GetFullPath(XFS.Path(@"c:\foo\\//bar\file.dat"));
278278

279279
//Assert
280280
Assert.AreEqual(XFS.Path(@"c:\foo\bar\file.dat"), actualFullPath);
@@ -397,5 +397,61 @@ public void IsPathRooted_PathSentIn_DeterminesPathExists()
397397
//Assert
398398
Assert.AreEqual(true, result);
399399
}
400+
401+
#if FEATURE_ADVANCED_PATH_OPERATIONS
402+
[Test]
403+
public void IsPathFullyQualified_WithAbsolutePath_ReturnsTrue()
404+
{
405+
//Arrange
406+
var mockPath = new MockPath(new MockFileSystem());
407+
408+
//Act
409+
var result = mockPath.IsPathFullyQualified(XFS.Path("C:\\directory\\file.txt"));
410+
411+
//Assert
412+
Assert.IsTrue(result);
413+
}
414+
415+
[Test]
416+
public void IsPathFullyQualified_WithRelativePath_ReturnsFalse()
417+
{
418+
//Arrange
419+
var mockPath = new MockPath(new MockFileSystem());
420+
421+
//Act
422+
var result = mockPath.IsPathRooted(XFS.Path("directory\\file.txt"));
423+
424+
//Assert
425+
Assert.IsFalse(result);
426+
}
427+
428+
[Test]
429+
public void IsPathFullyQualified_WithRelativePathParts_ReturnsFalse()
430+
{
431+
//Arrange
432+
var mockPath = new MockPath(new MockFileSystem());
433+
434+
//Act
435+
var result = mockPath.IsPathRooted(XFS.Path("directory\\..\\file.txt"));
436+
437+
//Assert
438+
Assert.IsFalse(result);
439+
}
440+
441+
442+
443+
[Test]
444+
public void GetRelativePath_Works()
445+
{
446+
//Arrange
447+
var mockPath = new MockPath(new MockFileSystem());
448+
449+
//Act
450+
var result = mockPath.GetRelativePath(XFS.Path("c:\\d"), XFS.Path("c:\\d\\e\\f.txt"));
451+
452+
//Assert
453+
Assert.AreEqual(XFS.Path("e\\f.txt"), result);
454+
}
455+
#endif
400456
}
401457
}

System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
<RootNamespace>System.IO.Abstractions.TestingHelpers.Tests</RootNamespace>
88
<IsPackable>false</IsPackable>
99
<IsTestable>true</IsTestable>
10-
<DefineConstants Condition="'$(TargetFramework)' == 'netcoreapp3.0'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS</DefineConstants>
1110
</PropertyGroup>
1211
<ItemGroup>
1312
<None Remove="TestFiles\SecondTestFile.txt" />

System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
<PackageProjectUrl>https://github.com/System-IO-Abstractions/System.IO.Abstractions</PackageProjectUrl>
88
<PackageLicenseExpression>MIT</PackageLicenseExpression>
99
<PackageTags>testing</PackageTags>
10-
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS</DefineConstants>
1110
</PropertyGroup>
1211
<ItemGroup>
1312
<ProjectReference Include="..\System.IO.Abstractions\System.IO.Abstractions.csproj" />

System.IO.Abstractions/IPath.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,13 @@ public interface IPath
5151
bool HasExtension(string path);
5252
/// <inheritdoc cref="Path.IsPathRooted(string)"/>
5353
bool IsPathRooted(string path);
54+
55+
#if FEATURE_ADVANCED_PATH_OPERATIONS
56+
/// <inheritdoc cref="Path.IsPathFullyQualified(string)"/>
57+
bool IsPathFullyQualified(string path);
58+
59+
/// <inheritdoc cref="Path.GetRelativePath(string,string)"/>
60+
string GetRelativePath(string relativeTo, string path);
61+
#endif
5462
}
5563
}

System.IO.Abstractions/PathBase.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,13 @@ internal PathBase() { }
8686

8787
/// <inheritdoc cref="Path.IsPathRooted(string)"/>
8888
public abstract bool IsPathRooted(string path);
89+
90+
#if FEATURE_ADVANCED_PATH_OPERATIONS
91+
/// <inheritdoc />
92+
public abstract bool IsPathFullyQualified(string path);
93+
94+
/// <inheritdoc />
95+
public abstract string GetRelativePath(string relativeTo, string path);
96+
#endif
8997
}
9098
}

System.IO.Abstractions/PathWrapper.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ public override bool HasExtension(string path)
118118
return Path.HasExtension(path);
119119
}
120120

121+
#if FEATURE_ADVANCED_PATH_OPERATIONS
122+
public override bool IsPathFullyQualified(string path)
123+
{
124+
return Path.IsPathFullyQualified(path);
125+
}
126+
127+
public override string GetRelativePath(string relativeTo, string path)
128+
{
129+
return Path.GetRelativePath(relativeTo, path);
130+
}
131+
#endif
132+
121133
public override bool IsPathRooted(string path)
122134
{
123135
return Path.IsPathRooted(path);

System.IO.Abstractions/System.IO.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PackageProjectUrl>https://github.com/System-IO-Abstractions/System.IO.Abstractions</PackageProjectUrl>
88
<PackageLicenseExpression>MIT</PackageLicenseExpression>
99
<PackageTags>testing</PackageTags>
10-
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS</DefineConstants>
10+
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS</DefineConstants>
1111
</PropertyGroup>
1212
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
1313
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.7.0"/>

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
3-
"version": "8.1",
3+
"version": "9.0",
44
"assemblyVersion": {
55
"precision": "major"
66
},

0 commit comments

Comments
 (0)