From acee1b41853c6c882a6fbeba588aa6700c8da883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Thu, 29 Aug 2019 10:42:15 +0200 Subject: [PATCH] Java TestKit expect msg all of broken (#27563) * Java API compile coverage of the classic testkit * API coverage of the typed Java testkit (looks good) * New name for allOf with duration to allow usage from Java #27492 --- .../typed/javadsl/ActorTestKitApiTest.java | 100 ++++++++++ .../mima-filters/2.5.x.backwards.excludes | 5 + .../scala/akka/testkit/javadsl/TestKit.scala | 39 +++- .../akka/testkit/javadsl/TestKitApiTest.java | 173 ++++++++++++++++++ 4 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitApiTest.java create mode 100644 akka-testkit/src/test/java/akka/testkit/javadsl/TestKitApiTest.java diff --git a/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitApiTest.java b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitApiTest.java new file mode 100644 index 0000000000..56ade5d0eb --- /dev/null +++ b/akka-actor-testkit-typed/src/test/java/akka/actor/testkit/typed/javadsl/ActorTestKitApiTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.actor.testkit.typed.javadsl; + +import akka.actor.typed.ActorRef; +import akka.actor.typed.Props; +import akka.actor.typed.Scheduler; +import akka.actor.typed.javadsl.Behaviors; + +import java.time.Duration; +import java.util.List; + +public class ActorTestKitApiTest { + + public void compileOnlyTestCase() { + ActorTestKit testKit = null; + + TestProbe probe1 = testKit.createTestProbe(); + TestProbe probe2 = testKit.createTestProbe("name"); + TestProbe probe3 = testKit.createTestProbe(Integer.class); + TestProbe probe4 = testKit.createTestProbe("name", Integer.class); + + Scheduler scheduler = testKit.scheduler(); + SerializationTestKit serializationTestKit = testKit.serializationTestKit(); + testKit.shutdownTestKit(); + + testKit.spawn(Behaviors.empty()); + testKit.spawn(Behaviors.empty(), "name"); + testKit.spawn(Behaviors.empty(), Props.empty()); + testKit.spawn(Behaviors.empty(), "name", Props.empty()); + + ActorRef actorRef = null; + testKit.stop(actorRef); + testKit.stop(actorRef, Duration.ofSeconds(3)); + } + + public void testProbeCompileOnlyTestCase() { + TestProbe probe = null; + + String awaitAssertSupplied1 = + probe.awaitAssert( + () -> { + return "supplied"; + }); + String awaitAssertSupplied2 = + probe.awaitAssert( + Duration.ofSeconds(3), + () -> { + return "supplied"; + }); + String awaitAssertSupplied3 = + probe.awaitAssert( + Duration.ofSeconds(3), + Duration.ofMillis(200), + () -> { + return "supplied"; + }); + + String expectMessage1 = probe.expectMessage("message-1"); + String expectMessage2 = probe.expectMessage(Duration.ofSeconds(3), "message-2"); + String expectMessage3 = probe.expectMessage(Duration.ofSeconds(3), "hint", "message-2"); + + String receiveMessage1 = probe.receiveMessage(); + String receiveMessage2 = probe.receiveMessage(Duration.ofSeconds(3)); + + probe.expectMessageClass(String.class); + probe.expectMessageClass(String.class, Duration.ofSeconds(3)); + + List fishedMessages1 = + probe.fishForMessage( + Duration.ofSeconds(3), + (message) -> { + return FishingOutcomes.complete(); + }); + List fishedMessages2 = + probe.fishForMessage( + Duration.ofSeconds(3), + "hint", + (message) -> { + return FishingOutcomes.complete(); + }); + + probe.expectNoMessage(); + probe.expectNoMessage(Duration.ofSeconds(3)); + + List tenMessages1 = probe.receiveSeveralMessages(10); + List tenMessages2 = probe.receiveSeveralMessages(10, Duration.ofSeconds(3)); + + ActorRef ref = probe.getRef(); + Duration remaining = probe.getRemaining(); + Duration remainingOr = probe.getRemainingOr(Duration.ofSeconds(3)); + Duration remainingOrDefault = probe.getRemainingOrDefault(); + + ActorRef actorRef = null; + probe.expectTerminated(actorRef); + probe.expectTerminated(actorRef, Duration.ofSeconds(3)); + } +} diff --git a/akka-testkit/src/main/mima-filters/2.5.x.backwards.excludes b/akka-testkit/src/main/mima-filters/2.5.x.backwards.excludes index d38e94defd..e3c0cdb7e8 100644 --- a/akka-testkit/src/main/mima-filters/2.5.x.backwards.excludes +++ b/akka-testkit/src/main/mima-filters/2.5.x.backwards.excludes @@ -5,3 +5,8 @@ ProblemFilters.exclude[MissingClassProblem]("akka.testkit.CachingPartialFunction # #22333 Disable Java serialization ProblemFilters.exclude[MissingClassProblem]("akka.testkit.TestMessageSerializer") + +# Some TestKit methods unaccessible from Java +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.testkit.javadsl.TestKit.expectMsgAnyOf") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.testkit.javadsl.TestKit.expectMsgAllOf") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.testkit.javadsl.TestKit.expectMsgAnyOf") \ No newline at end of file diff --git a/akka-testkit/src/main/scala/akka/testkit/javadsl/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/javadsl/TestKit.scala index 941813b9e8..c5197f09f2 100644 --- a/akka-testkit/src/main/scala/akka/testkit/javadsl/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/javadsl/TestKit.scala @@ -13,6 +13,8 @@ import akka.util.JavaDurationConverters._ import scala.annotation.varargs import akka.util.ccompat.JavaConverters._ +import com.github.ghik.silencer.silent + import scala.concurrent.duration._ /** @@ -516,6 +518,7 @@ class TestKit(system: ActorSystem) { * Use this variant to implement more complicated or conditional * processing. */ + @deprecated("Use the overloaded one which accepts java.time.Duration instead.", since = "2.6.0") def expectMsgPF[T](max: Duration, hint: String, f: JFunction[Any, T]): T = { tp.expectMsgPF(max, hint)(new CachingPartialFunction[Any, T] { @throws(classOf[Exception]) @@ -531,6 +534,7 @@ class TestKit(system: ActorSystem) { * Use this variant to implement more complicated or conditional * processing. */ + @silent("deprecated") def expectMsgPF[T](max: java.time.Duration, hint: String, f: JFunction[Any, T]): T = expectMsgPF(max.asScala, hint, f) /** @@ -558,7 +562,7 @@ class TestKit(system: ActorSystem) { * Same as `expectMsgAnyOf(remainingOrDefault, obj...)`, but correctly treating the timeFactor. */ @varargs - def expectMsgAnyOf[T](objs: T*): T = tp.expectMsgAnyOf(objs: _*) + def expectMsgAnyOf[T](first: T, objs: T*): T = tp.expectMsgAnyOf((first +: objs): _*) /** * Receive one message from the test actor and assert that it equals one of @@ -575,7 +579,8 @@ class TestKit(system: ActorSystem) { * the given objects. Wait time is bounded by the given duration, with an * AssertionFailure being thrown in case of timeout. */ - def expectMsgAnyOf[T](max: java.time.Duration, objs: T*): T = tp.expectMsgAnyOf(max.asScala, objs: _*) + @varargs + def expectMsgAnyOfWithin[T](max: java.time.Duration, objs: T*): T = tp.expectMsgAnyOf(max.asScala, objs: _*) /** * Same as `expectMsgAllOf(remainingOrDefault, obj...)`, but correctly treating the timeFactor. @@ -602,12 +607,13 @@ class TestKit(system: ActorSystem) { * which the objects are received is not fixed. Wait time is bounded by the * given duration, with an AssertionFailure being thrown in case of timeout. */ - def expectMsgAllOf[T](max: java.time.Duration, objs: T*): JList[T] = tp.expectMsgAllOf(max.asScala, objs: _*).asJava + @varargs + def expectMsgAllOfWithin[T](max: java.time.Duration, objs: T*): JList[T] = + tp.expectMsgAllOf(max.asScala, objs: _*).asJava /** * Same as `expectMsgAnyClassOf(remainingOrDefault, obj...)`, but correctly treating the timeFactor. */ - @varargs def expectMsgAnyClassOf[T](objs: Class[_]*): T = tp.expectMsgAnyClassOf(objs: _*).asInstanceOf[T] @@ -675,6 +681,7 @@ class TestKit(system: ActorSystem) { * @param target the actor ref expected to be Terminated * @return the received Terminated message */ + @deprecated("Use the overloaded one which accepts java.time.Duration instead.", since = "2.6.0") def expectTerminated(max: Duration, target: ActorRef): Terminated = tp.expectTerminated(target, max) /** @@ -706,16 +713,29 @@ class TestKit(system: ActorSystem) { * @return the last received message, i.e. the first one for which the * partial function returned true */ - def fishForMessage(max: Duration, hint: String, f: JFunction[Any, Boolean]): Any = { + @deprecated("Use the overloaded one which accepts java.time.Duration instead.", since = "2.6.0") + def fishForMessage(max: Duration, hint: String, f: JFunction[Any, Boolean]): Any = tp.fishForMessage(max, hint)(new CachingPartialFunction[Any, Boolean] { @throws(classOf[Exception]) override def `match`(x: Any): Boolean = f.apply(x) }) - } + + /** + * Hybrid of expectMsgPF and receiveWhile: receive messages while the + * partial function matches and returns false. Use it to ignore certain + * messages while waiting for a specific message. + * + * @return the last received message, i.e. the first one for which the + * partial function returned true + */ + @silent("deprecated") + def fishForMessage(max: java.time.Duration, hint: String, f: JFunction[Any, Boolean]): Any = + fishForMessage(max.asScala, hint, f) /** * Same as `fishForMessage`, but gets a different partial function and returns properly typed message. */ + @deprecated("Use the overloaded one which accepts java.time.Duration instead.", since = "2.6.0") def fishForSpecificMessage[T](max: Duration, hint: String, f: JFunction[Any, T]): T = { tp.fishForSpecificMessage(max, hint)(new CachingPartialFunction[Any, T] { @throws(classOf[Exception]) @@ -723,6 +743,13 @@ class TestKit(system: ActorSystem) { }) } + /** + * Same as `fishForMessage`, but gets a different partial function and returns properly typed message. + */ + @silent("deprecated") + def fishForSpecificMessage[T](max: java.time.Duration, hint: String, f: JFunction[Any, T]): T = + fishForSpecificMessage(max.asScala, hint, f) + /** * Same as `receiveN(n, remaining)` but correctly taking into account * Duration.timeFactor. diff --git a/akka-testkit/src/test/java/akka/testkit/javadsl/TestKitApiTest.java b/akka-testkit/src/test/java/akka/testkit/javadsl/TestKitApiTest.java new file mode 100644 index 0000000000..15c1f3d624 --- /dev/null +++ b/akka-testkit/src/test/java/akka/testkit/javadsl/TestKitApiTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka.testkit.javadsl; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.actor.SupervisorStrategy; +import akka.testkit.TestActor; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; + +public class TestKitApiTest { + + public void compileOnlyTestCase() { + TestKit testKit = null; + ActorRef actorRef = null; + TestActor.AutoPilot autoPilot = null; + + // just coverage of calling all testkit methods, actual impl is tested elsewhere + testKit.awaitAssert( + () -> { + return null; + }); + testKit.awaitAssert( + Duration.ofSeconds(3), + () -> { + return null; + }); + testKit.awaitAssert( + Duration.ofSeconds(3), + Duration.ofMillis(200), + () -> { + return null; + }); + + testKit.awaitCond( + () -> { + return true; + }); + testKit.awaitCond( + Duration.ofSeconds(3), + () -> { + return true; + }); + testKit.awaitCond( + Duration.ofSeconds(3), + Duration.ofMillis(200), + () -> { + return true; + }); + testKit.awaitCond( + Duration.ofSeconds(3), + Duration.ofMillis(200), + "details", + () -> { + return true; + }); + + testKit.childActorOf(Props.empty()); + testKit.childActorOf(Props.empty(), "name"); + testKit.childActorOf(Props.empty(), SupervisorStrategy.defaultStrategy()); + testKit.childActorOf(Props.empty(), "name", SupervisorStrategy.defaultStrategy()); + + testKit.dilated(Duration.ofSeconds(3)); + + testKit.expectMsg("message"); + testKit.expectMsg(Duration.ofSeconds(3), "message"); + testKit.expectMsg(Duration.ofSeconds(3), "message", "hint"); + + testKit.expectMsgClass(String.class); + testKit.expectMsgClass(Duration.ofSeconds(3), String.class); + + testKit.expectMsgEquals("message"); + testKit.expectMsgEquals(Duration.ofSeconds(3), "message"); + + // FIXME why are these called PF when they take a total function, how are they supposed to be + // used? #27562 + testKit.expectMsgPF( + Duration.ofSeconds(3), + "hint", + (value) -> { + return null; + }); + testKit.expectMsgPF( + "hint", + (value) -> { + return null; + }); + + testKit.expectNoMessage(); + testKit.expectNoMessage(Duration.ofSeconds(3)); + + testKit.expectTerminated(actorRef); + testKit.expectTerminated(Duration.ofSeconds(3), actorRef); + + // FIXME how is this supposed to be used, scaladoc talks about a partial function but it accepts + // a total function #27562 + Object fishResult1 = + testKit.fishForMessage( + Duration.ofSeconds(3), + "hint", + (message) -> { + return "fishResult1"; + }); + // FIXME how is this supposed to be used, scaladoc talks about a partial function but it accepts + // a total function #27562 + String result = + testKit.fishForSpecificMessage( + Duration.ofSeconds(3), + "hint", + (message) -> { + return "fishResult2"; + }); + + List tenMessages = testKit.receiveN(10); + List tenMoreMessages = testKit.receiveN(10, Duration.ofSeconds(3)); + Object oneMoreMessage = testKit.receiveOne(Duration.ofSeconds(3)); + + // FIXME how is this supposed to be used, scaladoc talks about a partial function but it accepts + // a total function #27562 + List receiveWhileResults1 = + testKit.receiveWhile( + Duration.ofSeconds(3), + (message) -> { + return message; + }); + // FIXME how is this supposed to be used, scaladoc talks about a partial function but it accepts + // a total function #27562 + List receiveWhileResults2 = + testKit.receiveWhile( + Duration.ofSeconds(3), + Duration.ofSeconds(1), + 10, + (message) -> { + return "result"; + }); + + // FIXME how is this supposed to be used, scaladoc talks about a partial function but it accepts + // a total function #27562 + testKit.ignoreMsg( + (message) -> { + return message; + }); + testKit.ignoreNoMsg(); + + testKit.expectMsgAllOf("one", "two"); + testKit.expectMsgAllOfWithin(Duration.ofSeconds(3), "one", "two"); + testKit.expectMsgAnyOf("one", "two"); + testKit.expectMsgAnyOfWithin(Duration.ofSeconds(3), "one", "two"); + testKit.expectMsgAnyClassOf(String.class, Integer.class); + testKit.expectMsgAnyClassOf(Duration.ofSeconds(3), String.class, Integer.class); + + ActorRef lastSender = testKit.getLastSender(); + ActorSystem system = testKit.getSystem(); + Duration remaining = testKit.getRemaining(); + Duration remainingOrDefault = testKit.getRemainingOrDefault(); + Duration dilated = testKit.dilated(Duration.ofSeconds(3)); + ActorRef testActor = testKit.getTestActor(); + boolean msgAvailable = testKit.msgAvailable(); + + testKit.forward(actorRef); + testKit.send(actorRef, "message"); + testKit.watch(actorRef); + testKit.unwatch(actorRef); + + testKit.setAutoPilot(autoPilot); + } +}