// Copyright © 2012-2020 VLINGO LABS. All rights reserved. // // This Source Code Form is subject to the terms of the // Mozilla Public License, v. 2.0. If a copy of the MPL // was not distributed with this file, You can obtain // one at https://mozilla.org/MPL/2.0/. package io.vlingo.actors.supervision; import static org.junit.Assert.assertEquals; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import io.vlingo.actors.Actor; import io.vlingo.actors.ActorsTest; import io.vlingo.actors.Definition; import io.vlingo.actors.Supervised; import io.vlingo.actors.SupervisionStrategy; import io.vlingo.actors.Supervisor; import io.vlingo.actors.supervision.FailureControlActor.FailureControlTestResults; import io.vlingo.actors.testkit.AccessSafely; import io.vlingo.actors.testkit.TestActor; public class BasicSupervisionTest extends ActorsTest { @Test public void testPublicRootDefaultParentSupervisor() { // this test cannot use the TestActor because the timing // and message enqueue is important to testing some // aspects of supervision. for example, in a restarting // situation, as is the case here with the default // supervisor, the suspending and subsequent stowed // will never be delivered unless time is given to // empty to stowed messages final FailureControlTestResults failureControlTestResults = new FailureControlTestResults(); final FailureControl failure = world.actorFor( FailureControl.class, Definition.has(FailureControlActor.class, Definition.parameters(failureControlTestResults), world.defaultParent(), "failure-for-default")); AccessSafely access = failureControlTestResults.afterCompleting(3); failure.failNow(); assertEquals(1, (int) access.readFrom("failNowCount")); // actor may or may not be resumed by now assertEquals(1, (int) access.readFromExpecting("afterRestartCount", 1, 1_000)); access = failureControlTestResults.afterCompleting(1); failure.afterFailure(); assertEquals(1, (int) access.readFrom("afterFailureCount")); } @Test public void testStoppingSupervisor() { final TestActor<Supervisor> supervisor = testWorld.actorFor( Supervisor.class, Definition.has(StoppingSupervisorActor.class, Definition.NoParameters, "stopping-supervisor")); final FailureControlTestResults failureControlTestResults = new FailureControlTestResults(); final TestActor<FailureControl> failure = testWorld.actorFor( FailureControl.class, Definition.has(FailureControlActor.class, Definition.parameters(failureControlTestResults), supervisor.actorInside(), "failure-for-stop")); AccessSafely access = failureControlTestResults.afterCompleting(2); failure.actor().failNow(); assertEquals(1, (int) access.readFrom("failNowCount")); failure.actor().afterFailure(); assertEquals(1, (int) access.readFrom("stoppedCount")); assertEquals(0, (int) access.readFrom("afterFailureCount")); } @Test public void testRestartSupervisor() { final RestartSupervisorTestResults restartSupervisorTestResults = new RestartSupervisorTestResults(); final TestActor<Supervisor> supervisor = testWorld.actorFor( Supervisor.class, Definition.has(RestartSupervisorActor.class, Definition.parameters(restartSupervisorTestResults), "restart-supervisor")); final FailureControlTestResults failureControlTestResults = new FailureControlTestResults(); final TestActor<FailureControl> failure = testWorld.actorFor( FailureControl.class, Definition.has(FailureControlActor.class, Definition.parameters(failureControlTestResults), supervisor.actorInside(), "failure-for-restart")); AccessSafely failureAccess = failureControlTestResults.afterCompleting(6); AccessSafely restartAccess = restartSupervisorTestResults.afterCompleting(1); failure.actor().failNow(); assertEquals(1, (int) restartAccess.readFrom("informedCount")); assertEquals(2, (int) failureAccess.readFrom("beforeStartCount")); assertEquals(1, (int) failureAccess.readFrom("failNowCount")); assertEquals(1, (int) failureAccess.readFrom("afterRestartCount")); assertEquals(1, (int) failureAccess.readFrom("afterStopCount")); assertEquals(1, (int) failureAccess.readFrom("beforeRestartCount")); AccessSafely afterFailureAccess = failureControlTestResults.afterCompleting(1); failure.actor().afterFailure(); assertEquals(1, (int) afterFailureAccess.readFrom("afterFailureCount")); assertEquals(0, (int) afterFailureAccess.readFrom("stoppedCount")); } public static class StoppingSupervisorActor extends Actor implements Supervisor { public StoppingSupervisorActor() { } @Override public void inform(final Throwable throwable, final Supervised supervised) { //logger().log("StoppingSupervisorActor informed of failure in: " + supervised.address().name() + " because: " + throwable.getMessage(), throwable); supervised.stop(supervisionStrategy().scope()); } @Override public SupervisionStrategy supervisionStrategy() { return new SupervisionStrategy() { @Override public int intensity() { return SupervisionStrategy.DefaultIntensity; } @Override public long period() { return SupervisionStrategy.DefaultPeriod; } @Override public Scope scope() { return Scope.One; } }; } } public static class RestartSupervisorActor extends Actor implements Supervisor { private final RestartSupervisorTestResults testResults; public RestartSupervisorActor(final RestartSupervisorTestResults testResults) { this.testResults = testResults; } private final SupervisionStrategy strategy = new SupervisionStrategy() { @Override public int intensity() { return 2; } @Override public long period() { return SupervisionStrategy.DefaultPeriod; } @Override public Scope scope() { return Scope.One; } }; @Override public void inform(final Throwable throwable, final Supervised supervised) { //logger().log("RestartSupervisorActor informed of failure in: " + supervised.address().name() + " because: " + throwable.getMessage(), throwable); supervised.restartWithin(strategy.period(), strategy.intensity(), strategy.scope()); System.out.println("SUPERVISOR RESTART"); testResults.access.writeUsing("informedCount", 1); } @Override public SupervisionStrategy supervisionStrategy() { return strategy; } } private static class RestartSupervisorTestResults { public AccessSafely access = afterCompleting(0); public AtomicInteger informedCount = new AtomicInteger(0); public AccessSafely afterCompleting(final int times) { access = AccessSafely.afterCompleting(times) .writingWith("informedCount", (Integer increment) -> informedCount.incrementAndGet()) .readingWith("informedCount", () -> informedCount.get()); return access; } } }