Skip to content

Commit cf27218

Browse files
committed
(GH-4636) Enhance FileSystem Abstractions & tests
Added timestamps, Unix modes, performance improvements and dedicated for Fake File/Directory. - Add comprehensive timestamp and Unix file mode support across IFile/IDirectory interfaces - Implement LastWriteTimeUtc, CreationTimeUtc, LastAccessTimeUtc, and UnixFileMode properties - Add Set* methods for timestamp and Unix file mode manipulation - Improve performance by replacing GetFiles/GetDirectories with EnumerateFiles/EnumerateDirectories - Create dedicated Cake.Testing.Tests project with comprehensive unit test coverage - Add snapshot testing with Verify for FakeFile and FakeDirectory operations - Fill gaps in file system abstraction with cross-platform Unix file mode support - Integrate Microsoft.Extensions.TimeProvider.Testing for reliable time handling in tests - Refactor FakeDirectory to use yield return for lazy evaluation performance benefits - Add extensive test coverage for Create, Move, Delete, GetDirectories, and GetFiles operations - fixes #4636 ### Breaking Changes - [x] There shouldn't be any breaking changes identified, all new interface members have default implementations / values. #### IFileSystemInfo Interface (Base interface) - [x] `DateTime? LastWriteTimeUtc` - Default: `null` - [x] `DateTime? CreationTimeUtc` - Default: `null` - [x] `DateTime? LastAccessTimeUtc` - Default: `null` - [x] `UnixFileMode? UnixFileMode` - Default: `null` #### IDirectory Interface - [x] `IDirectory SetCreationTime(DateTime creationTime)` - Default: `return this` - [x] `IDirectory SetCreationTimeUtc(DateTime creationTimeUtc)` - Default: `return this` - [x] `IDirectory SetLastAccessTime(DateTime lastAccessTime)` - Default: `return this` - [x] `IDirectory SetLastAccessTimeUtc(DateTime lastAccessTimeUtc)` - Default: `return this` - [x] `IDirectory SetLastWriteTime(DateTime lastWriteTime)` - Default: `return this` - [x] `IDirectory SetLastWriteTimeUtc(DateTime lastWriteTimeUtc)` - Default: `return this` - [x] `IDirectory SetUnixFileMode(UnixFileMode unixFileMode)` - Default: `return this` #### IFile Interface - [x] `IFile SetUnixFileMode(UnixFileMode unixFileMode)` - Default: `return this` ### Key Changes Made - [x] Added UTC time properties to IFileSystemInfo with default null values - [x] Added Unix file mode support with UnixFileMode property and setter methods - [x] Added time setter methods for both IDirectory and IFile interfaces - [x] Performance improvement: Changed from GetDirectories()/GetFiles() to EnumerateDirectories()/EnumerateFiles() - [x] Added _file.Refresh() calls after time-setting operations to ensure consistency - [x] Added platform-specific attributes ([UnsupportedOSPlatform("windows")]) for Unix-specific methods
1 parent 9c605ab commit cf27218

File tree

138 files changed

+4365
-234
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+4365
-234
lines changed

build.cake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Task("Restore-NuGet-Packages")
8888
.IsDependentOn("Clean")
8989
.Does<BuildParameters>((context, parameters) =>
9090
{
91-
DotNetRestore("./src/Cake.sln", new DotNetRestoreSettings
91+
DotNetRestore("./src/Cake.slnx", new DotNetRestoreSettings
9292
{
9393
Verbosity = DotNetVerbosity.Minimal,
9494
Sources = new [] { "https://api.nuget.org/v3/index.json" },
@@ -101,7 +101,7 @@ Task("Build")
101101
.Does<BuildParameters>((context, parameters) =>
102102
{
103103
// Build the solution.
104-
var path = MakeAbsolute(new DirectoryPath("./src/Cake.sln"));
104+
var path = MakeAbsolute(new DirectoryPath("./src/Cake.slnx"));
105105
DotNetBuild(path.FullPath, new DotNetBuildSettings
106106
{
107107
Configuration = parameters.Configuration,

src/Cake.Core/IO/Directory.cs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.IO;
88
using System.Linq;
9+
using System.Runtime.Versioning;
910

1011
namespace Cake.Core.IO
1112
{
@@ -23,6 +24,11 @@ internal sealed class Directory : IDirectory
2324

2425
public DateTime LastWriteTime => _directory.LastWriteTime;
2526

27+
public DateTime? LastWriteTimeUtc => _directory.LastWriteTimeUtc;
28+
public DateTime? CreationTimeUtc => _directory.CreationTimeUtc;
29+
public DateTime? LastAccessTimeUtc => _directory.LastAccessTimeUtc;
30+
public UnixFileMode? UnixFileMode => _directory.UnixFileMode;
31+
2632
public Directory(DirectoryPath path)
2733
{
2834
Path = path;
@@ -48,15 +54,72 @@ public void Delete(bool recursive)
4854
public IEnumerable<IDirectory> GetDirectories(string filter, SearchScope scope)
4955
{
5056
var option = scope == SearchScope.Current ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
51-
return _directory.GetDirectories(filter, option)
57+
return _directory.EnumerateDirectories(filter, option)
5258
.Select(directory => new Directory(directory.FullName));
5359
}
5460

5561
public IEnumerable<IFile> GetFiles(string filter, SearchScope scope)
5662
{
5763
var option = scope == SearchScope.Current ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
58-
return _directory.GetFiles(filter, option)
64+
return _directory.EnumerateFiles(filter, option)
5965
.Select(file => new File(file.FullName));
6066
}
67+
68+
/// <inheritdoc/>
69+
public IDirectory SetCreationTime(DateTime creationTime)
70+
{
71+
System.IO.Directory.SetCreationTime(Path.FullPath, creationTime);
72+
_directory.Refresh();
73+
return this;
74+
}
75+
76+
/// <inheritdoc/>
77+
public IDirectory SetCreationTimeUtc(DateTime creationTimeUtc)
78+
{
79+
System.IO.Directory.SetCreationTimeUtc(Path.FullPath, creationTimeUtc);
80+
_directory.Refresh();
81+
return this;
82+
}
83+
84+
/// <inheritdoc/>
85+
public IDirectory SetLastAccessTime(DateTime lastAccessTime)
86+
{
87+
System.IO.Directory.SetLastAccessTime(Path.FullPath, lastAccessTime);
88+
_directory.Refresh();
89+
return this;
90+
}
91+
92+
/// <inheritdoc/>
93+
public IDirectory SetLastAccessTimeUtc(DateTime lastAccessTimeUtc)
94+
{
95+
System.IO.Directory.SetLastAccessTimeUtc(Path.FullPath, lastAccessTimeUtc);
96+
_directory.Refresh();
97+
return this;
98+
}
99+
100+
/// <inheritdoc/>
101+
public IDirectory SetLastWriteTime(DateTime lastWriteTime)
102+
{
103+
System.IO.Directory.SetLastWriteTime(Path.FullPath, lastWriteTime);
104+
_directory.Refresh();
105+
return this;
106+
}
107+
108+
/// <inheritdoc/>
109+
public IDirectory SetLastWriteTimeUtc(DateTime lastWriteTimeUtc)
110+
{
111+
System.IO.Directory.SetLastWriteTimeUtc(Path.FullPath, lastWriteTimeUtc);
112+
_directory.Refresh();
113+
return this;
114+
}
115+
116+
/// <inheritdoc/>
117+
[UnsupportedOSPlatform("windows")]
118+
public IDirectory SetUnixFileMode(UnixFileMode unixFileMode)
119+
{
120+
_directory.UnixFileMode = unixFileMode;
121+
_directory.Refresh();
122+
return this;
123+
}
61124
}
62125
}

src/Cake.Core/IO/File.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.IO;
7+
using System.Runtime.Versioning;
78

89
namespace Cake.Core.IO
910
{
@@ -21,6 +22,10 @@ internal sealed class File : IFile
2122

2223
public DateTime LastWriteTime => _file.LastWriteTime;
2324

25+
public DateTime? LastWriteTimeUtc => _file.LastWriteTimeUtc;
26+
public DateTime? CreationTimeUtc => _file.CreationTimeUtc;
27+
public DateTime? LastAccessTimeUtc => _file.LastAccessTimeUtc;
28+
2429
public long Length => _file.Length;
2530

2631
public FileAttributes Attributes
@@ -29,6 +34,8 @@ public FileAttributes Attributes
2934
set { _file.Attributes = value; }
3035
}
3136

37+
public UnixFileMode? UnixFileMode => _file.UnixFileMode;
38+
3239
public File(FilePath path)
3340
{
3441
Path = path;
@@ -61,41 +68,56 @@ public Stream Open(FileMode fileMode, FileAccess fileAccess, FileShare fileShare
6168
public IFile SetCreationTime(DateTime creationTime)
6269
{
6370
System.IO.File.SetCreationTime(Path.FullPath, creationTime);
71+
_file.Refresh();
6472
return this;
6573
}
6674

6775
/// <inheritdoc/>
6876
public IFile SetCreationTimeUtc(DateTime creationTimeUtc)
6977
{
7078
System.IO.File.SetCreationTimeUtc(Path.FullPath, creationTimeUtc);
79+
_file.Refresh();
7180
return this;
7281
}
7382

7483
/// <inheritdoc/>
7584
public IFile SetLastAccessTime(DateTime lastAccessTime)
7685
{
7786
System.IO.File.SetLastAccessTime(Path.FullPath, lastAccessTime);
87+
_file.Refresh();
7888
return this;
7989
}
8090

8191
/// <inheritdoc/>
8292
public IFile SetLastAccessTimeUtc(DateTime lastAccessTimeUtc)
8393
{
8494
System.IO.File.SetLastAccessTimeUtc(Path.FullPath, lastAccessTimeUtc);
95+
_file.Refresh();
8596
return this;
8697
}
8798

8899
/// <inheritdoc/>
89100
public IFile SetLastWriteTime(DateTime lastWriteTime)
90101
{
91102
System.IO.File.SetLastWriteTime(Path.FullPath, lastWriteTime);
103+
_file.Refresh();
92104
return this;
93105
}
94106

95107
/// <inheritdoc/>
96108
public IFile SetLastWriteTimeUtc(DateTime lastWriteTimeUtc)
97109
{
98110
System.IO.File.SetLastWriteTimeUtc(Path.FullPath, lastWriteTimeUtc);
111+
_file.Refresh();
112+
return this;
113+
}
114+
115+
/// <inheritdoc/>
116+
[UnsupportedOSPlatform("windows")]
117+
public IFile SetUnixFileMode(UnixFileMode unixFileMode)
118+
{
119+
_file.UnixFileMode = unixFileMode;
120+
_file.Refresh();
99121
return this;
100122
}
101123
}

src/Cake.Core/IO/IDirectory.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
56
using System.Collections.Generic;
7+
using System.IO;
68

79
namespace Cake.Core.IO
810
{
@@ -49,5 +51,54 @@ public interface IDirectory : IFileSystemInfo
4951
/// <param name="scope">The search scope.</param>
5052
/// <returns>Files matching the specified filter and scope.</returns>
5153
IEnumerable<IFile> GetFiles(string filter, SearchScope scope);
54+
55+
/// <summary>
56+
/// Sets the date and time that the file was created.
57+
/// </summary>
58+
/// <param name="creationTime">A <see cref="DateTime"/> containing the value to set for the creation date and time of path. This value is expressed in local time.</param>
59+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
60+
IDirectory SetCreationTime(DateTime creationTime) => this;
61+
62+
/// <summary>
63+
/// Sets the date and time, in Coordinated Universal Time (UTC), that the file was created.
64+
/// </summary>
65+
/// <param name="creationTimeUtc">A <see cref="DateTime"/> containing the value to set for the creation date and time of path. This value is expressed in UTC time.</param>
66+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
67+
IDirectory SetCreationTimeUtc(DateTime creationTimeUtc) => this;
68+
69+
/// <summary>
70+
/// Sets the date and time that the specified file or directory was last accessed.
71+
/// </summary>
72+
/// <param name="lastAccessTime">A <see cref="DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
73+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
74+
IDirectory SetLastAccessTime(DateTime lastAccessTime) => this;
75+
76+
/// <summary>
77+
/// Sets the date and time, in Coordinated Universal Time (UTC), that the specified file or directory was last accessed.
78+
/// </summary>
79+
/// <param name="lastAccessTimeUtc">A <see cref="DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
80+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
81+
IDirectory SetLastAccessTimeUtc(DateTime lastAccessTimeUtc) => this;
82+
83+
/// <summary>
84+
/// Sets the date and time that the specified file or directory was last written to.
85+
/// </summary>
86+
/// <param name="lastWriteTime">A <see cref="DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
87+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
88+
IDirectory SetLastWriteTime(DateTime lastWriteTime) => this;
89+
90+
/// <summary>
91+
/// Sets the date and time, in Coordinated Universal Time (UTC), that the specified file or directory was last written to.
92+
/// </summary>
93+
/// <param name="lastWriteTimeUtc">A <see cref="DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
94+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
95+
IDirectory SetLastWriteTimeUtc(DateTime lastWriteTimeUtc) => this;
96+
97+
/// <summary>
98+
/// Sets the Unix file mode for the directory.
99+
/// </summary>
100+
/// <param name="unixFileMode">The <see cref="UnixFileMode"/> to set.</param>
101+
/// <returns>The <see cref="IDirectory"/> instance representing the specified path.</returns>
102+
IDirectory SetUnixFileMode(UnixFileMode unixFileMode) => this;
52103
}
53104
}

src/Cake.Core/IO/IFile.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,12 @@ public interface IFile : IFileSystemInfo
9898
/// <param name="lastWriteTimeUtc">A <see cref="DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
9999
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
100100
IFile SetLastWriteTimeUtc(DateTime lastWriteTimeUtc) => this;
101+
102+
/// <summary>
103+
/// Sets the Unix file mode for the file.
104+
/// </summary>
105+
/// <param name="unixFileMode">The <see cref="UnixFileMode"/> to set.</param>
106+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
107+
IFile SetUnixFileMode(UnixFileMode unixFileMode) => this;
101108
}
102109
}

src/Cake.Core/IO/IFileSystemInfo.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
6+
57
namespace Cake.Core.IO
68
{
79
/// <summary>
@@ -30,5 +32,37 @@ public interface IFileSystemInfo
3032
/// <c>true</c> if the entry is hidden; otherwise, <c>false</c>.
3133
/// </value>
3234
bool Hidden { get; }
35+
36+
/// <summary>
37+
/// Gets the date and time, in Coordinated Universal Time (UTC), that the file was last written to.
38+
/// </summary>
39+
/// <value>
40+
/// A <see cref="DateTime"/> value that represents the last write time in UTC, or <c>null</c> if not available.
41+
/// </value>
42+
DateTime? LastWriteTimeUtc => null;
43+
44+
/// <summary>
45+
/// Gets the date and time, in Coordinated Universal Time (UTC), that the file was created.
46+
/// </summary>
47+
/// <value>
48+
/// A <see cref="DateTime"/> value that represents the creation time in UTC, or <c>null</c> if not available.
49+
/// </value>
50+
DateTime? CreationTimeUtc => null;
51+
52+
/// <summary>
53+
/// Gets the date and time, in Coordinated Universal Time (UTC), that the file was last accessed.
54+
/// </summary>
55+
/// <value>
56+
/// A <see cref="DateTime"/> value that represents the last access time in UTC, or <c>null</c> if not available.
57+
/// </value>
58+
DateTime? LastAccessTimeUtc => null;
59+
60+
/// <summary>
61+
/// Gets the Unix file mode of the entry.
62+
/// </summary>
63+
/// <value>
64+
/// A <see cref="System.IO.UnixFileMode"/> value that represents the Unix file mode of the entry.
65+
/// </value>
66+
System.IO.UnixFileMode? UnixFileMode => null;
3367
}
3468
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<AssemblyName>Cake.Testing.Tests</AssemblyName>
4+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
5+
<IsCakeTestProject>true</IsCakeTestProject>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
<!-- Import shared functionality -->
9+
<Import Project="..\Shared.msbuild" />
10+
<!-- Project references -->
11+
<ItemGroup>
12+
<ProjectReference Include="..\Cake.Core\Cake.Core.csproj" />
13+
<ProjectReference Include="..\Cake.Testing\Cake.Testing.csproj" />
14+
<ProjectReference Include="..\Cake.Testing.Xunit.v3\Cake.Testing.Xunit.v3.csproj" />
15+
</ItemGroup>
16+
<!-- Global packages -->
17+
<ItemGroup>
18+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
19+
<PackageReference Include="Spectre.Verify.Extensions" />
20+
<PackageReference Include="Verify.DiffPlex" />
21+
<PackageReference Include="Verify.XunitV3" />
22+
<PackageReference Include="xunit.v3" />
23+
<PackageReference Include="xunit.runner.visualstudio">
24+
<PrivateAssets>all</PrivateAssets>
25+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
26+
</PackageReference>
27+
<PackageReference Include="NSubstitute" />
28+
<PackageReference Include="Castle.Core" />
29+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
30+
</ItemGroup>
31+
<ItemGroup>
32+
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
33+
</ItemGroup>
34+
<ItemGroup>
35+
<Folder Include="Expectations\" />
36+
</ItemGroup>
37+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
directory: {
3+
Exists: true,
4+
Hidden: false,
5+
LastWriteTime: 2014-05-27 21:56:01 Local,
6+
LastWriteTimeUtc: 2014-05-27 19:56:01,
7+
CreationTimeUtc: 2014-05-27 19:56:01,
8+
LastAccessTimeUtc: 2014-05-27 19:56:01,
9+
UnixFileMode: OtherExecute, OtherWrite, OtherRead, GroupExecute, GroupWrite, GroupRead, UserExecute, UserWrite, UserRead,
10+
Path: /test/new_directory
11+
},
12+
beforeState: {
13+
Path: /test/new_directory,
14+
Exists: false,
15+
Hidden: false,
16+
LastWriteTimeUtc: null,
17+
CreationTimeUtc: null,
18+
LastAccessTimeUtc: null,
19+
UnixFileMode: null
20+
},
21+
afterState: {
22+
Path: /test/new_directory,
23+
Exists: true,
24+
Hidden: false,
25+
LastWriteTimeUtc: 2014-05-27 19:56:01,
26+
CreationTimeUtc: 2014-05-27 19:56:01,
27+
LastAccessTimeUtc: 2014-05-27 19:56:01,
28+
UnixFileMode: OtherExecute, OtherWrite, OtherRead, GroupExecute, GroupWrite, GroupRead, UserExecute, UserWrite, UserRead
29+
}
30+
}

0 commit comments

Comments
 (0)