Skip to content

Commit 6d984e2

Browse files
authored
feat: implement GetTempFileName for simulated Path (#570)
Implement the `GetTempFileName` methods for `Path` and allow initialization with `IRandomProvider` to test random edge cases.
1 parent 066fb69 commit 6d984e2

File tree

15 files changed

+134
-22
lines changed

15 files changed

+134
-22
lines changed

Source/Testably.Abstractions.Testing/Helpers/Execute.MacPath.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ internal partial class Execute
44
{
55
private sealed class MacPath(MockFileSystem fileSystem) : LinuxPath(fileSystem)
66
{
7+
private readonly MockFileSystem _fileSystem = fileSystem;
78
private string? _tempPath;
89

910
/// <inheritdoc cref="IPath.GetTempPath()" />
1011
public override string GetTempPath()
1112
{
12-
_tempPath ??= $"/var/folders/{RandomString(2)}/{RandomString(2)}_{RandomString(27)}/T/";
13+
_tempPath ??=
14+
$"/var/folders/{RandomString(_fileSystem, 2)}/{RandomString(_fileSystem, 2)}_{RandomString(_fileSystem, 27)}/T/";
1315
return _tempPath;
1416
}
1517
}

Source/Testably.Abstractions.Testing/Helpers/Execute.NativePath.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public string GetRelativePath(string relativeTo, string path)
198198
"Insecure temporary file creation methods should not be used. Use `Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())` instead.")]
199199
#endif
200200
public string GetTempFileName()
201-
=> System.IO.Path.GetTempFileName();
201+
=> CreateTempFileName(fileSystem);
202202

203203
/// <inheritdoc cref="Path.GetTempPath()" />
204204
public string GetTempPath()

Source/Testably.Abstractions.Testing/Helpers/Execute.SimulatedPath.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Diagnostics;
33
using System.Diagnostics.CodeAnalysis;
4-
using System.Linq;
54
using System.Text;
65
#if FEATURE_FILESYSTEM_NET7
76
using Testably.Abstractions.Testing.Storage;
@@ -284,7 +283,7 @@ public ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
284283

285284
/// <inheritdoc cref="IPath.GetRandomFileName()" />
286285
public string GetRandomFileName()
287-
=> $"{RandomString(8)}.{RandomString(3)}";
286+
=> $"{RandomString(fileSystem, 8)}.{RandomString(fileSystem, 3)}";
288287

289288
#if FEATURE_PATH_RELATIVE
290289
/// <inheritdoc cref="IPath.GetRelativePath(string, string)" />
@@ -306,7 +305,7 @@ public string GetRelativePath(string relativeTo, string path)
306305
"Insecure temporary file creation methods should not be used. Use `Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())` instead.")]
307306
#endif
308307
public string GetTempFileName()
309-
=> System.IO.Path.GetTempFileName();
308+
=> CreateTempFileName(fileSystem);
310309

311310
/// <inheritdoc cref="IPath.GetTempPath()" />
312311
public abstract string GetTempPath();
@@ -573,13 +572,6 @@ private string JoinInternal(string?[] paths)
573572

574573
protected abstract string NormalizeDirectorySeparators(string path);
575574

576-
protected string RandomString(int length)
577-
{
578-
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
579-
return new string(Enumerable.Repeat(chars, length)
580-
.Select(s => s[fileSystem.RandomSystem.Random.Shared.Next(s.Length)]).ToArray());
581-
}
582-
583575
/// <summary>
584576
/// Remove relative segments from the given path (without combining with a root).
585577
/// </summary>

Source/Testably.Abstractions.Testing/Helpers/Execute.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.IO;
3+
using System.Linq;
24
using System.Runtime.InteropServices;
35

46
namespace Testably.Abstractions.Testing.Helpers;
@@ -78,4 +80,32 @@ internal Execute(MockFileSystem fileSystem)
7880
: StringComparison.OrdinalIgnoreCase;
7981
Path = new NativePath(fileSystem);
8082
}
83+
84+
internal static string CreateTempFileName(MockFileSystem fileSystem)
85+
{
86+
int i = 0;
87+
string tempPath = fileSystem.Path.GetTempPath();
88+
fileSystem.Directory.CreateDirectory(tempPath);
89+
while (true)
90+
{
91+
string fileName = $"{RandomString(fileSystem, 8)}.tmp";
92+
string path = string.Concat(tempPath, fileName);
93+
try
94+
{
95+
fileSystem.File.Open(path, FileMode.CreateNew, FileAccess.Write).Dispose();
96+
return path;
97+
}
98+
catch (IOException) when (i < 100)
99+
{
100+
i++; // Don't let unforeseen circumstances cause us to loop forever
101+
}
102+
}
103+
}
104+
105+
internal static string RandomString(MockFileSystem fileSystem, int length)
106+
{
107+
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
108+
return new string(Enumerable.Repeat(chars, length)
109+
.Select(s => s[fileSystem.RandomSystem.Random.Shared.Next(s.Length)]).ToArray());
110+
}
81111
}

Source/Testably.Abstractions.Testing/MockFileSystem.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using Testably.Abstractions.Testing.FileSystem;
66
using Testably.Abstractions.Testing.Helpers;
7+
using Testably.Abstractions.Testing.RandomSystem;
78
using Testably.Abstractions.Testing.Statistics;
89
using Testably.Abstractions.Testing.Storage;
910

@@ -107,7 +108,7 @@ internal MockFileSystem(Action<Initialization> initializationCallback)
107108
: new Execute(this, SimulationMode);
108109
StatisticsRegistration = new FileSystemStatistics(this);
109110
using IDisposable release = StatisticsRegistration.Ignore();
110-
RandomSystem = new MockRandomSystem();
111+
RandomSystem = new MockRandomSystem(initialization.RandomProvider ?? RandomProvider.Default());
111112
TimeSystem = new MockTimeSystem(TimeProvider.Now());
112113
_pathMock = new PathMock(this);
113114
_storage = new InMemoryStorage(this);
@@ -231,6 +232,11 @@ internal class Initialization
231232
/// </summary>
232233
internal string? CurrentDirectory { get; private set; }
233234

235+
/// <summary>
236+
/// The <see cref="IRandomProvider" /> for the <see cref="RandomSystem" />.
237+
/// </summary>
238+
internal IRandomProvider? RandomProvider { get; private set; }
239+
234240
/// <summary>
235241
/// The simulated operating system.
236242
/// </summary>
@@ -262,5 +268,14 @@ internal Initialization UseCurrentDirectory()
262268
CurrentDirectory = System.IO.Directory.GetCurrentDirectory();
263269
return this;
264270
}
271+
272+
/// <summary>
273+
/// Use the given <paramref name="randomProvider" /> for the <see cref="RandomSystem" />.
274+
/// </summary>
275+
internal Initialization UseRandomProvider(IRandomProvider randomProvider)
276+
{
277+
RandomProvider = randomProvider;
278+
return this;
279+
}
265280
}
266281
}

Source/Testably.Abstractions.Testing/MockRandomSystem.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ public MockRandomSystem() : this(Testing.RandomProvider.Default())
2626
}
2727

2828
/// <summary>
29-
/// Initializes the <see cref="MockRandomSystem" /> with the specified <paramref name="randomProviderProvider" />.
29+
/// Initializes the <see cref="MockRandomSystem" /> with the specified <paramref name="randomProvider" />.
3030
/// </summary>
31-
public MockRandomSystem(IRandomProvider randomProviderProvider)
31+
public MockRandomSystem(IRandomProvider randomProvider)
3232
{
33-
RandomProvider = randomProviderProvider;
33+
RandomProvider = randomProvider;
3434
_guidMock = new GuidMock(this);
3535
_randomFactoryMock = new RandomFactoryMock(this);
3636
}

Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net6.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ namespace Testably.Abstractions.Testing
122122
public sealed class MockRandomSystem : Testably.Abstractions.IRandomSystem
123123
{
124124
public MockRandomSystem() { }
125-
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProviderProvider) { }
125+
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
126126
public Testably.Abstractions.RandomSystem.IGuid Guid { get; }
127127
public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; }
128128
public Testably.Abstractions.Testing.RandomSystem.IRandomProvider RandomProvider { get; }

Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net7.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ namespace Testably.Abstractions.Testing
122122
public sealed class MockRandomSystem : Testably.Abstractions.IRandomSystem
123123
{
124124
public MockRandomSystem() { }
125-
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProviderProvider) { }
125+
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
126126
public Testably.Abstractions.RandomSystem.IGuid Guid { get; }
127127
public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; }
128128
public Testably.Abstractions.Testing.RandomSystem.IRandomProvider RandomProvider { get; }

Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net8.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ namespace Testably.Abstractions.Testing
122122
public sealed class MockRandomSystem : Testably.Abstractions.IRandomSystem
123123
{
124124
public MockRandomSystem() { }
125-
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProviderProvider) { }
125+
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
126126
public Testably.Abstractions.RandomSystem.IGuid Guid { get; }
127127
public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; }
128128
public Testably.Abstractions.Testing.RandomSystem.IRandomProvider RandomProvider { get; }

Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_netstandard2.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ namespace Testably.Abstractions.Testing
120120
public sealed class MockRandomSystem : Testably.Abstractions.IRandomSystem
121121
{
122122
public MockRandomSystem() { }
123-
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProviderProvider) { }
123+
public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
124124
public Testably.Abstractions.RandomSystem.IGuid Guid { get; }
125125
public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; }
126126
public Testably.Abstractions.Testing.RandomSystem.IRandomProvider RandomProvider { get; }

0 commit comments

Comments
 (0)