Skip to content
Draft
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
30 changes: 30 additions & 0 deletions SharpTools.Tools.Tests/CancellationTokenUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections.Immutable;

namespace SharpTools.Tools.Tests;
public static class CancellationTokenUtils
{
public static TimeoutWatcher ApplyTimeout(int seconds, ref CancellationToken cancellationToken)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(seconds));

var ct = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);

cancellationToken = ct.Token;

return new TimeoutWatcher(cts.Token, [cts, ct]);
}

public record TimeoutWatcher(
CancellationToken CancellationToken,
ImmutableArray<IDisposable> Disposables
) : IDisposable
{
public bool TimedOut => CancellationToken.IsCancellationRequested;

public void Dispose() {
foreach(var disposable in Disposables)
disposable.Dispose();
}
}
}
80 changes: 80 additions & 0 deletions SharpTools.Tools.Tests/Retry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Diagnostics;

namespace SharpTools.Tools.Tests;

[DebuggerStepThrough]
public static class Retry
{
public static async Task Until(int timeLimit, Func<Task<bool>> assertion)
{
var cancellationToken = TestContext.Current.CancellationToken;

using var timeoutWatcher = CancellationTokenUtils.ApplyTimeout(
timeLimit,
ref cancellationToken
);

try
{
while (!cancellationToken.IsCancellationRequested)
{
var result = await assertion();

if (result)
return;

await Task.Delay(TimeSpan.FromMilliseconds(200), cancellationToken);
}
}
catch (TaskCanceledException)
{
if (!timeoutWatcher.TimedOut)
throw;
}

await assertion();
}

public static async Task UntilPasses(Action condition)
{
await UntilPasses(5, condition);
}

public static async Task UntilPasses(Func<Task> condition)
{
await UntilPasses(5, condition);
}

public static async Task UntilPasses(int timeLimit, Func<Task> condition)
{
await Until(
timeLimit,
async () =>
{
try
{
await condition();
return true;
}
catch
{
return false;
}
}
);

await condition();
}

public static async Task UntilPasses(int timeLimit, Action condition)
{
await UntilPasses(
timeLimit,
() =>
{
condition();
return Task.CompletedTask;
}
);
}
}
20 changes: 20 additions & 0 deletions SharpTools.Tools.Tests/Services/FileChangeListenerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using SharpTools.Tools.Services;

namespace SharpTools.Tools.Tests.Services;

public class FileChangeListenerTests {
[Theory]
[InlineData("c:\\root\\obj", true)]
[InlineData("c:\\root\\.git", true)]
[InlineData("c:\\root\\bin", true)]
[InlineData("c:\\root\\BIN", true)]
[InlineData("c:\\root\\foo", false)]
[InlineData("c:\\root\\nested\\obj", true)]
[InlineData("c:\\root\\nested\\.git", true)]
[InlineData("c:\\root\\nested\\bin", true)]
[InlineData("c:\\root\\nested\\BIN", true)]
[InlineData("c:\\root\\nested\\foo", false)]
public void IsPathIgnored_ignores_git_bin_obj(string path, bool isIgnored) {
Assert.Equal(isIgnored, FileChangeListener.IsPathIgnored("c:\\root", path, ['\\']));
}
}
Loading