Skip to content

Commit

Permalink
Troupe tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Emil Koutanov committed Jan 28, 2024
1 parent 0ad39bf commit 7fad999
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"Schedulable"
]
}
87 changes: 82 additions & 5 deletions Actors.Tests/TroupeTest.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,92 @@
using System.Diagnostics;

namespace Actors.Tests;

static class IEnumerableExtensions
{
public static void AssertNoError(this PausingActor[] actors)
{
foreach (var actor in actors)
{
actor.AssertNoError();
}
}
}

[TestClass]
public class TroupeTest
{
[TestMethod]
public void TestMembers()
{
PausingActor[] actors = [new PausingActor(), new PausingActor()];
var troupe = Troupe.Of(actors);
CollectionAssert.AreEqual(actors, troupe.Members);
}

[TestMethod]
public async Task TestDrainAny()
{
// PausingActor[] actors = [new PausingActor(), new PausingActor()];
// IEnumerable<PausingActor> x = actors.AsEnumerable();
// var troupe = Troupe<PausingActor>.Of(x);
PausingActor[] actors = [new PausingActor(), new PausingActor()];
var troupe = Troupe.Of(actors);

var m0 = new Barrier();
var m1 = new Barrier();

actors[0].Send(m0);
actors[1].Send(m1);

var drain = troupe.DrainAny();

// no drain task is completed if all actors are still in Perform()
await m0.Entered.Task;
await m1.Entered.Task;
Assert.IsTrue(actors[0].Scheduled);
Assert.IsTrue(actors[1].Scheduled);
Assert.IsFalse(drain.IsCompleted);

// allow one actor to complete and await drainage
m0.Resume.SetResult();
await drain;
Assert.IsFalse(actors[0].Scheduled);

// complete the second actor and let it drain
m1.Resume.SetResult();
await actors[1].Drain();
Assert.IsFalse(actors[1].Scheduled);

actors.AssertNoError();
}
[TestMethod]
public async Task TestDrainAll()
{
PausingActor[] actors = [new PausingActor(), new PausingActor()];
var troupe = Troupe.Of(actors);

var m0 = new Barrier();
var m1 = new Barrier();

actors[0].Send(m0);
actors[1].Send(m1);

var drain = troupe.DrainAll();

// no drain task is completed if all actors are still in Perform()
await m0.Entered.Task;
await m1.Entered.Task;
Assert.IsTrue(actors[0].Scheduled);
Assert.IsTrue(actors[1].Scheduled);
Assert.IsFalse(drain.IsCompleted);

// allow one actor to complete and drain it... troupe drain will still not be completed
m0.Resume.SetResult();
await actors[0].Drain();
Assert.IsFalse(actors[0].Scheduled);
Assert.IsFalse(drain.IsCompleted);

// complete the second actor and await troupe drainage
m1.Resume.SetResult();
await drain;
Assert.IsFalse(actors[1].Scheduled);

actors.AssertNoError();
}
}
2 changes: 1 addition & 1 deletion Actors/Actor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/// order of their submission.
/// </summary>
/// <typeparam name="M">The message type.</typeparam>
public abstract class Actor<M>
public abstract class Actor<M> : ISchedulable
{
/// <summary>
/// A property indicating that an actor is scheduled, meaning that there
Expand Down
8 changes: 8 additions & 0 deletions Actors/ISchedulable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Actors;

public interface ISchedulable
{
bool Scheduled { get; }

Task Drain();
}
33 changes: 17 additions & 16 deletions Actors/Troupe.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
namespace Actors;

/// <summary>
/// A grouping of actors for composing drain requests.
/// A grouping of schedulable instances for composing drain requests.
/// </summary>
/// <typeparam name="M">The message type.</typeparam>
public sealed class Troupe<M>
public sealed class Troupe
{
/// <summary>
/// The actors in the troupe.
/// The members of the troupe.
/// </summary>
public List<Actor<M>> Actors
public List<ISchedulable> Members
{
get
{
return actors;
return members;
}
}

private readonly List<Actor<M>> actors;
private readonly List<ISchedulable> members;

private Troupe(List<Actor<M>> actors)
private Troupe(List<ISchedulable> members)
{
this.actors = actors;
this.members = members;
}

/// <summary>
/// Obtains a task that is completed when all actors in the troupe have been drained.
/// Obtains a task that is completed when all members of the troupe have been drained of
/// their backlogs and unscheduled.
/// </summary>
/// <returns>A <c>Task</c>.</returns>
public Task DrainAll()
Expand All @@ -34,7 +34,8 @@ public Task DrainAll()
}

/// <summary>
/// Obtains a task that is completed when at least one actor in the troupe has been drained.
/// Obtains a task that is completed when at least one member of the troupe has been drained
/// of its backlog and unscheduled.
/// </summary>
/// <returns>A <c>Task</c>.</returns>
public Task DrainAny()
Expand All @@ -44,16 +45,16 @@ public Task DrainAny()

private Task[] DrainTasks()
{
return actors.Select(actor => actor.Drain()).ToArray();
return members.Select(x => x.Drain()).ToArray();
}

public static Troupe<M> Of(IEnumerable<Actor<M>> actors)
public static Troupe Of(IEnumerable<ISchedulable> members)
{
return new Troupe<M>(actors.ToList());
return new Troupe(members.ToList());
}

public static Troupe<M> OfNullable(IEnumerable<Actor<M>?> actors)
public static Troupe OfNullable(IEnumerable<ISchedulable?> members)
{
return Troupe<M>.Of(actors.Where(actor => actor is not null).Select(actor => actor!));
return Of(members.Where(member => member is not null).Select(member => member!));
}
}
4 changes: 2 additions & 2 deletions Examples/Throttle/Throttle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private async Task DelegateWork(WorkItem workItem)
/// <returns></returns>
private async Task DisposeSomeActorsAsync()
{
await Troupe<int>.OfNullable(children).DrainAny();
await Troupe.OfNullable(children).DrainAny();

foreach (var child in children)
{
Expand All @@ -143,7 +143,7 @@ private async Task DisposeSomeActorsAsync()
private async Task DrainAllActorsAsync()
{
Console.WriteLine("draining all child actors");
await Troupe<int>.OfNullable(children).DrainAll();
await Troupe.OfNullable(children).DrainAll();
Console.WriteLine("drained");
}
}
Expand Down

0 comments on commit 7fad999

Please sign in to comment.