Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Emil Koutanov committed Jan 28, 2024
1 parent fad328c commit 8fc6a9f
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 43 deletions.
135 changes: 93 additions & 42 deletions Actors.Tests/ActorTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;

namespace Actors.Tests;

[TestClass]
Expand All @@ -9,7 +11,7 @@ class Barrier
internal TaskCompletionSource resume = new();
}

class PausingActor : Actor<Barrier>
class PausingActor : ErrorTrappingActor<Barrier>
{
protected override async Task Perform(Inbox inbox)
{
Expand All @@ -25,73 +27,122 @@ protected override async Task Perform(Inbox inbox)
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task TestScheduledWithOne()
public async Task TestScheduledOnFirst()
{
var actor = new PausingActor();

// initial test to transition from unscheduled to scheduled and back
{
// should be unscheduled in initial state
Assert.IsFalse(actor.Scheduled);

// send a message to wake the actor — should immediately be scheduled
var barrier = new Barrier();
actor.Send(barrier);
Assert.IsTrue(actor.Scheduled);

await barrier.entered.Task;
// actor is now in Perform() but hasn't yet finished — should still be scheduled
Assert.IsTrue(actor.Scheduled);
barrier.resume.SetResult();

// wait for the actor to exit Perform() — should become unscheduled
await actor.Drain();
Assert.IsFalse(actor.Scheduled);
}
// should be unscheduled in initial state
Assert.IsFalse(actor.Scheduled);

// send a message to wake the actor — should immediately be scheduled
var m1 = new Barrier();
actor.Send(m1);
Assert.IsTrue(actor.Scheduled);

await m1.entered.Task;
// actor is now in Perform() but hasn't yet finished — should still be scheduled
Assert.IsTrue(actor.Scheduled);
m1.resume.SetResult();

// wait for the actor to exit Perform() — should become unscheduled
await actor.Drain();
Assert.IsFalse(actor.Scheduled);

// repeat the test to check that after getting unscheduled, the actor can become scheduled again
{
var barrier = new Barrier();
actor.Send(barrier);
await barrier.entered.Task;
Assert.IsTrue(actor.Scheduled);

barrier.resume.SetResult();
await actor.Drain();
Assert.IsFalse(actor.Scheduled);
}
var m2 = new Barrier();
actor.Send(m2);
await m2.entered.Task;
Assert.IsTrue(actor.Scheduled);

m2.resume.SetResult();
await actor.Drain();
Assert.IsFalse(actor.Scheduled);

actor.AssertNoError();
}

/// <summary>
/// Tests that posting a second message into an empty inbox while the first is still being processed,
/// retains the scheduled state.
/// </summary>
/// <returns></returns>
[TestMethod]
public async Task TestScheduledOnRepeat()
{
var actor = new PausingActor();
Assert.IsFalse(actor.Scheduled);

// send the first message and verify scheduled state
var b1 = new Barrier();
actor.Send(b1);
var m1 = new Barrier();
actor.Send(m1);
Assert.IsTrue(actor.Scheduled);

await b1.entered.Task;
await m1.entered.Task;
Assert.IsTrue(actor.Scheduled);

// send the second message while actor is still in Perform()
var b2 = new Barrier();
actor.Send(b2);
var m2 = new Barrier();
actor.Send(m2);
Assert.IsTrue(actor.Scheduled);

// upon completion of b1, the actor should remain scheduled
b1.resume.SetResult();
// upon completion of m1, the actor should remain scheduled
m1.resume.SetResult();
Assert.IsTrue(actor.Scheduled);

// wait for b2's evaluation to start — the actor is still scheduled
await b2.entered.Task;
// wait for m2's evaluation to start — the actor is still scheduled
await m2.entered.Task;
Assert.IsTrue(actor.Scheduled);

// after b2 completes, the actor should eventually become unscheduled
b2.resume.SetResult();
// after m2 completes, the actor should eventually become unscheduled
m2.resume.SetResult();
await actor.Drain();
Assert.IsFalse(actor.Scheduled);

actor.AssertNoError();
}

class FaultyActor : ErrorTrappingActor<object>
{
protected override Task Perform(Inbox inbox)
{
inbox.Receive();
throw new InvalidOperationException();
}
}

[TestMethod]
public async Task TestUncaughtExceptionHandling()
{
var actor = new FaultyActor();
actor.AssertNoError();
actor.Send(new object());
await actor.Drain();
Assert.IsInstanceOfType(actor.Exception, typeof(InvalidOperationException));
}

class BatchActor : ErrorTrappingActor<int>
{
internal TaskCompletionSource resume = new();

protected override async Task Perform(Inbox inbox)
{
await resume.Task;
var items = inbox.ReceiveAll();
Assert.AreEqual(3, items.Count);
CollectionAssert.AreEqual(new List<int>{0, 1, 2}, items);
}
}

[TestMethod]
public async Task TestReceiveAll()
{
var actor = new BatchActor();
for (int i = 0; i < 3; i++)
{
actor.Send(i);
}
actor.resume.SetResult();
await actor.Drain();
actor.AssertNoError();
}
}
20 changes: 20 additions & 0 deletions Actors.Tests/ErrorTrappingActor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Actors.Tests;

public abstract class ErrorTrappingActor<M> : Actor<M>
{
public Exception? Exception { get; set; }

protected override void OnError(Exception e)
{
Exception = e;
base.OnError(e);
}

public void AssertNoError()
{
if (Exception is not null)
{
throw new AssertFailedException("An unexpected exception was caught.", Exception);
}
}
}
7 changes: 6 additions & 1 deletion Actors/Actor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,9 @@ protected virtual void OnError(Exception e)
/// An actor specialization that expects messages of type <c>object</c> and most
/// likely handles multiple concrete message types.
/// </summary>
public abstract class Actor : Actor<object>;
public abstract class Actor : Actor<object>
{
protected Actor() : base() { }

protected Actor(int initialInboxCapacity) : base(initialInboxCapacity) { }
}
4 changes: 4 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ run *ARGS:
# runs the test cases
test:
dotnet test

# loops through test cases
loop:
while [ true ]; do dotnet test; done

0 comments on commit 8fc6a9f

Please sign in to comment.