diff --git a/docs/GUIDE.md b/docs/GUIDE.md
index fd1bb46f..358660ba 100644
--- a/docs/GUIDE.md
+++ b/docs/GUIDE.md
@@ -118,6 +118,21 @@ This option disables detection of messages/events/state based on type of argumen
This option disables detection of messages/events/state based on return type of the function given as argument to method. This detection is enabled by default. If you want to disable it, add the following setting:
`Compile / scalacOptions += "-P:serializability-checker-plugin:--disable-detection-higher-order-function"`
+- `--exclude-messages`
+
+This option disables serializability checks on all messages. These checks are enabled by default. If you want to disable them, add the following setting:
+`Compile / scalacOptions += "-P:serializability-checker-plugin:--exclude-messages"`
+
+- `--exclude-persistent-events`
+
+This option disables serializability checks on all persistent events. These checks are enabled by default. If you want to disable them, add the following setting:
+`Compile / scalacOptions += "-P:serializability-checker-plugin:--exclude-persistent-events"`
+
+- `--exclude-persistent-states`
+
+This option disables serializability checks on all persistent state classes. These checks are enabled by default. If you want to disable them, add the following setting:
+`Compile / scalacOptions += "-P:serializability-checker-plugin:--exclude-persistent-states"`
+
- `--types-explicitly-marked-as-serializable=,,...`
This option can be used to pass a comma-separated list of fully-qualified names of types that should be considered serializable by the checker, even if they do **not** extend a designated serializability trait.
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassKind.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassKind.scala
new file mode 100644
index 00000000..aeb485e4
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassKind.scala
@@ -0,0 +1,21 @@
+package org.virtuslab.ash
+
+sealed trait ClassKind {
+ val name: String
+}
+
+object ClassKind {
+ case object Message extends ClassKind {
+ val name = "message"
+ }
+ case object PersistentEvent extends ClassKind {
+ val name = "persistent event"
+ }
+ case object PersistentState extends ClassKind {
+ val name = "persistent state"
+ }
+
+ case object Ignore extends ClassKind {
+ val name = "ignore"
+ }
+}
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassType.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassType.scala
deleted file mode 100644
index 13c276a5..00000000
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/ClassType.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.virtuslab.ash
-
-sealed trait ClassType {
- val name: String
-}
-
-object ClassType {
- case object Message extends ClassType {
- val name = "message"
- }
- case object PersistentEvent extends ClassType {
- val name = "persistent event"
- }
- case object PersistentState extends ClassType {
- val name = "persistent state"
- }
-
- case object Ignore extends ClassType {
- val name = "ignore"
- }
-}
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
index 39604002..00c71e5a 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
@@ -22,11 +22,17 @@ class SerializabilityCheckerCompilerPlugin(override val global: Global) extends
return false
pluginOptions.verbose = options.contains(verbose)
+
pluginOptions.detectFromGenerics = !options.contains(disableGenerics)
pluginOptions.detectFromGenericMethods = !options.contains(disableGenericMethods)
pluginOptions.detectFromMethods = !options.contains(disableMethods)
pluginOptions.detectFromUntypedMethods = !options.contains(disableMethodsUntyped)
pluginOptions.detectFromHigherOrderFunctions = !options.contains(disableHigherOrderFunctions)
+
+ pluginOptions.includeMessages = !options.contains(excludeMessages)
+ pluginOptions.includePersistentEvents = !options.contains(excludePersistentEvents)
+ pluginOptions.includePersistentStates = !options.contains(excludePersistentStates)
+
options.find(_.startsWith(typesExplicitlyMarkedAsSerializable)).foreach { opt =>
pluginOptions.typesExplicitlyMarkedAsSerializable =
opt.stripPrefix(typesExplicitlyMarkedAsSerializable).split(",").toSeq.map(_.strip())
@@ -41,6 +47,9 @@ class SerializabilityCheckerCompilerPlugin(override val global: Global) extends
|$disableMethods - disables detection of messages/events/state based on type of arguments to a method, e.g. akka.actor.typed.ActorRef.tell
|$disableMethodsUntyped - disables detection of messages/events/state based on type of arguments to a method that takes Any, used for Akka Classic
|$disableHigherOrderFunctions - disables detection of messages/events/state based on return type of the function given as argument to method
+ |$excludeMessages - exclude all messages from the serializability check
+ |$excludePersistentEvents - exclude all events from the serializability check
+ |$excludePersistentStates - exclude all states from the serializability check
|$typesExplicitlyMarkedAsSerializable - comma-separated list of fully-qualified names of types that should be considered serializable by this checker, even if they do NOT extend a designated serializability trait
|""".stripMargin)
}
@@ -54,6 +63,9 @@ object SerializabilityCheckerCompilerPlugin {
val disableMethods = "--disable-detection-methods"
val disableMethodsUntyped = "--disable-detection-untyped-methods"
val disableHigherOrderFunctions = "--disable-detection-higher-order-function"
+ val excludeMessages = "--exclude-messages"
+ val excludePersistentEvents = "--exclude-persistent-events"
+ val excludePersistentStates = "--exclude-persistent-states"
val typesExplicitlyMarkedAsSerializable = "--types-explicitly-marked-as-serializable="
}
val serializabilityTraitType = "org.virtuslab.ash.annotation.SerializabilityTrait"
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
index 114f9241..62dc0db1 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
@@ -12,7 +12,7 @@ class SerializabilityCheckerCompilerPluginComponent(val pluginOptions: Serializa
import global._
// just to avoid using tuples where possible
- private case class TypeWithClassType(typ: Type, classType: ClassType)
+ private case class TypeWithClassKind(typ: Type, classKind: ClassKind)
override val phaseName: String = "serializability-checker"
override val runsAfter: List[String] = List("refchecks")
@@ -45,74 +45,74 @@ class SerializabilityCheckerCompilerPluginComponent(val pluginOptions: Serializa
private val ignoredTypePrefixes = List("akka.")
- private val genericsToTypes: Map[String, Seq[ClassType]] = Map(
- "akka.actor.typed.ActorSystem" -> Seq(ClassType.Message),
- "akka.actor.typed.ActorRef" -> Seq(ClassType.Message),
- "akka.actor.typed.Behavior" -> Seq(ClassType.Message),
- "akka.actor.typed.RecipientRef" -> Seq(ClassType.Message),
- "akka.pattern.PipeToSupport.PipeableFuture" -> Seq(ClassType.Message),
- "akka.pattern.PipeToSupport.PipeableCompletionStage" -> Seq(ClassType.Message),
- "akka.persistence.typed.scaladsl.ReplyEffect" -> Seq(ClassType.PersistentEvent, ClassType.PersistentState),
- "akka.persistence.typed.scaladsl.Effect" -> Seq(ClassType.PersistentEvent),
- "akka.persistence.typed.scaladsl.EffectBuilder" -> Seq(ClassType.PersistentEvent),
- "akka.projection.eventsourced.EventEnvelope" -> Seq(ClassType.PersistentEvent, ClassType.PersistentState))
-
- private val genericMethodsToTypes: Map[String, Seq[ClassType]] = Map(
- "akka.actor.typed.scaladsl.ActorContext.ask" -> Seq(ClassType.Message, ClassType.Message),
- "akka.actor.typed.scaladsl.AskPattern.Askable.$qmark" -> Seq(ClassType.Message),
- "akka.pattern.PipeToSupport.pipe" -> Seq(ClassType.Message),
- "akka.pattern.PipeToSupport.pipeCompletionStage" -> Seq(ClassType.Message))
-
- private val concreteMethodsToTypes: Map[String, Seq[ClassType]] = Map(
- "akka.actor.typed.ActorRef.ActorRefOps.$bang" -> Seq(ClassType.Message),
- "akka.actor.typed.ActorRef.tell" -> Seq(ClassType.Message),
- "akka.actor.typed.RecipientRef.tell" -> Seq(ClassType.Message))
-
- private val concreteUntypedMethodsToTypes: Map[String, Seq[ClassType]] = Map(
- "akka.actor.ActorRef.tell" -> Seq(ClassType.Message, ClassType.Ignore),
- "akka.actor.ActorRef.$bang" -> Seq(ClassType.Message),
- "akka.actor.ActorRef.forward" -> Seq(ClassType.Message),
- "akka.pattern.AskSupport.ask" -> Seq(ClassType.Ignore, ClassType.Message, ClassType.Ignore),
- "akka.pattern.AskSupport.askWithStatus" -> Seq(ClassType.Ignore, ClassType.Message, ClassType.Ignore),
- "akka.pattern.AskableActorRef.ask" -> Seq(ClassType.Message),
- "akka.pattern.AskableActorRef.askWithStatus" -> Seq(ClassType.Message),
- "akka.pattern.AskableActorRef.$qmark" -> Seq(ClassType.Message),
- "akka.pattern.AskableActorSelection.ask" -> Seq(ClassType.Message),
- "akka.pattern.AskableActorSelection.$qmark" -> Seq(ClassType.Message),
- "akka.pattern.ExplicitAskSupport.ask" -> Seq(ClassType.Ignore, ClassType.Message, ClassType.Ignore))
-
- private val concreteHigherOrderFunctionsToTypes: Map[String, Seq[ClassType]] = Map(
- "akka.pattern.ExplicitlyAskableActorRef.ask" -> Seq(ClassType.Message),
- "akka.pattern.ExplicitlyAskableActorRef.$qmark" -> Seq(ClassType.Message),
- "akka.pattern.ExplicitlyAskableActorSelection.ask" -> Seq(ClassType.Message),
- "akka.pattern.ExplicitlyAskableActorSelection.$qmark" -> Seq(ClassType.Message))
-
- private val combinedMap: Map[String, Seq[ClassType]] =
- genericsToTypes ++ genericMethodsToTypes ++ concreteMethodsToTypes ++ concreteUntypedMethodsToTypes ++ concreteHigherOrderFunctionsToTypes
+ private val genericsToKinds: Map[String, Seq[ClassKind]] = Map(
+ "akka.actor.typed.ActorSystem" -> Seq(ClassKind.Message),
+ "akka.actor.typed.ActorRef" -> Seq(ClassKind.Message),
+ "akka.actor.typed.Behavior" -> Seq(ClassKind.Message),
+ "akka.actor.typed.RecipientRef" -> Seq(ClassKind.Message),
+ "akka.pattern.PipeToSupport.PipeableFuture" -> Seq(ClassKind.Message),
+ "akka.pattern.PipeToSupport.PipeableCompletionStage" -> Seq(ClassKind.Message),
+ "akka.persistence.typed.scaladsl.ReplyEffect" -> Seq(ClassKind.PersistentEvent, ClassKind.PersistentState),
+ "akka.persistence.typed.scaladsl.Effect" -> Seq(ClassKind.PersistentEvent),
+ "akka.persistence.typed.scaladsl.EffectBuilder" -> Seq(ClassKind.PersistentEvent),
+ "akka.projection.eventsourced.EventEnvelope" -> Seq(ClassKind.PersistentEvent, ClassKind.PersistentState))
+
+ private val genericMethodsToKinds: Map[String, Seq[ClassKind]] = Map(
+ "akka.actor.typed.scaladsl.ActorContext.ask" -> Seq(ClassKind.Message, ClassKind.Message),
+ "akka.actor.typed.scaladsl.AskPattern.Askable.$qmark" -> Seq(ClassKind.Message),
+ "akka.pattern.PipeToSupport.pipe" -> Seq(ClassKind.Message),
+ "akka.pattern.PipeToSupport.pipeCompletionStage" -> Seq(ClassKind.Message))
+
+ private val concreteMethodsToKinds: Map[String, Seq[ClassKind]] = Map(
+ "akka.actor.typed.ActorRef.ActorRefOps.$bang" -> Seq(ClassKind.Message),
+ "akka.actor.typed.ActorRef.tell" -> Seq(ClassKind.Message),
+ "akka.actor.typed.RecipientRef.tell" -> Seq(ClassKind.Message))
+
+ private val concreteUntypedMethodsToKinds: Map[String, Seq[ClassKind]] = Map(
+ "akka.actor.ActorRef.tell" -> Seq(ClassKind.Message, ClassKind.Ignore),
+ "akka.actor.ActorRef.$bang" -> Seq(ClassKind.Message),
+ "akka.actor.ActorRef.forward" -> Seq(ClassKind.Message),
+ "akka.pattern.AskSupport.ask" -> Seq(ClassKind.Ignore, ClassKind.Message, ClassKind.Ignore),
+ "akka.pattern.AskSupport.askWithStatus" -> Seq(ClassKind.Ignore, ClassKind.Message, ClassKind.Ignore),
+ "akka.pattern.AskableActorRef.ask" -> Seq(ClassKind.Message),
+ "akka.pattern.AskableActorRef.askWithStatus" -> Seq(ClassKind.Message),
+ "akka.pattern.AskableActorRef.$qmark" -> Seq(ClassKind.Message),
+ "akka.pattern.AskableActorSelection.ask" -> Seq(ClassKind.Message),
+ "akka.pattern.AskableActorSelection.$qmark" -> Seq(ClassKind.Message),
+ "akka.pattern.ExplicitAskSupport.ask" -> Seq(ClassKind.Ignore, ClassKind.Message, ClassKind.Ignore))
+
+ private val concreteHigherOrderFunctionsToKinds: Map[String, Seq[ClassKind]] = Map(
+ "akka.pattern.ExplicitlyAskableActorRef.ask" -> Seq(ClassKind.Message),
+ "akka.pattern.ExplicitlyAskableActorRef.$qmark" -> Seq(ClassKind.Message),
+ "akka.pattern.ExplicitlyAskableActorSelection.ask" -> Seq(ClassKind.Message),
+ "akka.pattern.ExplicitlyAskableActorSelection.$qmark" -> Seq(ClassKind.Message))
+
+ private val symbolsToKinds: Map[String, Seq[ClassKind]] =
+ genericsToKinds ++ genericMethodsToKinds ++ concreteMethodsToKinds ++ concreteUntypedMethodsToKinds ++ concreteHigherOrderFunctionsToKinds
override def apply(unit: global.CompilationUnit): Unit = {
val body = unit.body
val reporter = CrossVersionReporter(global)
- val genericsNames = genericsToTypes.keySet
- val genericMethods = genericMethodsToTypes.keySet
- val concreteMethods = concreteMethodsToTypes.keySet
- val concreteUntypedMethods = concreteUntypedMethodsToTypes.keySet
- val concreteHigherOrderFunctions = concreteHigherOrderFunctionsToTypes.keySet
+ val genericsNames = genericsToKinds.keySet
+ val genericMethods = genericMethodsToKinds.keySet
+ val concreteMethods = concreteMethodsToKinds.keySet
+ val concreteUntypedMethods = concreteUntypedMethodsToKinds.keySet
+ val concreteHigherOrderFunctions = concreteHigherOrderFunctionsToKinds.keySet
- def extractTypes(args: List[Tree], x: Tree): List[(TypeWithClassType, Position)] =
+ def extractTypes(args: List[Tree], x: Tree): List[(TypeWithClassKind, Position)] =
args
.map(_.tpe)
- .zip(combinedMap(x.symbol.fullName))
- .map(typeClassTypeTuple => (TypeWithClassType(typeClassTypeTuple._1, typeClassTypeTuple._2), x.pos))
+ .zip(symbolsToKinds(x.symbol.fullName))
+ .map(typeClassTypeTuple => (TypeWithClassKind(typeClassTypeTuple._1, typeClassTypeTuple._2), x.pos))
- val detectedTypes: Iterable[(TypeWithClassType, Position)] = body
+ val detectedTypes: Iterable[(TypeWithClassKind, Position)] = body
.collect {
case _: ApplyToImplicitArgs => Nil
case x: TypeTree if genericsNames.contains(x.tpe.typeSymbol.fullName) && pluginOptions.detectFromGenerics =>
x.tpe.typeArgs
- .zip(combinedMap(x.tpe.typeSymbol.fullName))
- .map(typeClassTypeTuple => (TypeWithClassType(typeClassTypeTuple._1, typeClassTypeTuple._2), x.pos))
+ .zip(genericsToKinds(x.tpe.typeSymbol.fullName))
+ .map(typeClassTypeTuple => (TypeWithClassKind(typeClassTypeTuple._1, typeClassTypeTuple._2), x.pos))
case x @ TypeApply(_, args)
if genericMethods.contains(x.symbol.fullName) && pluginOptions.detectFromGenericMethods =>
extractTypes(args, x)
@@ -127,13 +127,16 @@ class SerializabilityCheckerCompilerPluginComponent(val pluginOptions: Serializa
pluginOptions.detectFromHigherOrderFunctions =>
extractTypes(args, x).flatMap { resultTuple =>
resultTuple._1.typ.typeArguments match {
- case List(_, out) => Some(resultTuple.copy(_1 = TypeWithClassType(out, resultTuple._1.classType)))
+ case List(_, out) => Some(resultTuple.copy(_1 = TypeWithClassKind(out, resultTuple._1.classKind)))
case _ => None
}
}
}
.flatten
- .filterNot(_._1.classType == ClassType.Ignore)
+ .filter(_._1.classKind != ClassKind.Ignore)
+ .filter(pluginOptions.includeMessages || _._1.classKind != ClassKind.Message)
+ .filter(pluginOptions.includePersistentEvents || _._1.classKind != ClassKind.PersistentEvent)
+ .filter(pluginOptions.includePersistentStates || _._1.classKind != ClassKind.PersistentState)
.groupBy(_._1.typ)
.map(_._2.head)
@@ -167,8 +170,8 @@ class SerializabilityCheckerCompilerPluginComponent(val pluginOptions: Serializa
reporter.error(
detectedPosition,
s"""${typeWithClassType.typ
- .toString()} is used as Akka ${typeWithClassType.classType.name} but does not extend a trait annotated with $serializabilityTraitType.
- |Passing an object of a class that does NOT extend a trait annotated with $serializabilityTraitType as a ${typeWithClassType.classType.name}
+ .toString()} is used as Akka ${typeWithClassType.classKind.name} but does not extend a trait annotated with $serializabilityTraitType.
+ |Passing an object of a class that does NOT extend a trait annotated with $serializabilityTraitType as a ${typeWithClassType.classKind.name}
|may cause Akka to fall back to Java serialization during runtime.
|
|""".stripMargin)
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
index aff6acc2..4a7126f8 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
@@ -11,4 +11,7 @@ class SerializabilityCheckerOptions(
var detectFromMethods: Boolean = true,
var detectFromUntypedMethods: Boolean = true,
var detectFromHigherOrderFunctions: Boolean = true,
+ var includeMessages: Boolean = true,
+ var includePersistentEvents: Boolean = true,
+ var includePersistentStates: Boolean = true,
var typesExplicitlyMarkedAsSerializable: Seq[String] = Seq.empty)
diff --git a/serializability-checker-compiler-plugin/src/test/resources/MySerializableYes.scala b/serializability-checker-compiler-plugin/src/test/resources/MySerializableAnnotated.scala
similarity index 100%
rename from serializability-checker-compiler-plugin/src/test/resources/MySerializableYes.scala
rename to serializability-checker-compiler-plugin/src/test/resources/MySerializableAnnotated.scala
diff --git a/serializability-checker-compiler-plugin/src/test/resources/MySerializableNo.scala b/serializability-checker-compiler-plugin/src/test/resources/MySerializableNotAnnotated.scala
similarity index 100%
rename from serializability-checker-compiler-plugin/src/test/resources/MySerializableNo.scala
rename to serializability-checker-compiler-plugin/src/test/resources/MySerializableNotAnnotated.scala
diff --git a/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEvent.scala b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEvent.scala
index 6c1e82cc..f204e173 100644
--- a/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEvent.scala
+++ b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEvent.scala
@@ -1,12 +1,6 @@
package org.random.project
-import akka.actor.typed.ActorSystem
-import akka.persistence.query.Offset
-import akka.projection.eventsourced.EventEnvelope
-import akka.projection.eventsourced.scaladsl.EventSourcedProvider
-import akka.projection.scaladsl.SourceProvider
import akka.persistence.typed.scaladsl.ReplyEffect
-import akka.actor.typed.Behavior
import org.virtuslab.ash.annotation.SerializabilityTrait
object ReplyEffectTestEvent {
diff --git a/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEventAndState.scala b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEventAndState.scala
new file mode 100644
index 00000000..164377b2
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestEventAndState.scala
@@ -0,0 +1,10 @@
+package org.random.project
+
+import akka.persistence.typed.scaladsl.ReplyEffect
+
+object ReplyEffectTestEventAndState {
+ trait Event extends MySerializable
+ trait State extends MySerializable
+
+ def test: ReplyEffect[Event, State] = ???
+}
diff --git a/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestState.scala b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestState.scala
index b1c9c106..c22a3ba7 100644
--- a/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestState.scala
+++ b/serializability-checker-compiler-plugin/src/test/resources/ReplyEffectTestState.scala
@@ -1,12 +1,6 @@
package org.random.project
-import akka.actor.typed.ActorSystem
-import akka.persistence.query.Offset
-import akka.projection.eventsourced.EventEnvelope
-import akka.projection.eventsourced.scaladsl.EventSourcedProvider
-import akka.projection.scaladsl.SourceProvider
import akka.persistence.typed.scaladsl.ReplyEffect
-import akka.actor.typed.Behavior
import org.virtuslab.ash.annotation.SerializabilityTrait
object ReplyEffectTestState {
diff --git a/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala b/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
index a1c6bbb1..f0d5bbab 100644
--- a/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
+++ b/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
@@ -11,154 +11,181 @@ class SerializabilityCheckerCompilerPluginComponentSpec extends AnyWordSpecLike
private def getResourceAsString(name: String) =
new String(File(getClass.getClassLoader.getResource(name)).loadBytes)
- private val serYesCode = getResourceAsString("MySerializableYes.scala")
- private val serNoCode = getResourceAsString("MySerializableNo.scala")
+ private val mySerializableAnnotated = getResourceAsString("MySerializableAnnotated.scala")
+ private val mySerializableNotAnnotated = getResourceAsString("MySerializableNotAnnotated.scala")
- private def testCode(resourceName: String, errorTypes: ClassType = ClassType.Message, chosenDisableFlags: List[String]) = {
+ private def expectSerializabilityErrors(
+ resourceName: String,
+ expectedErrorType: ClassKind = ClassKind.Message,
+ pluginFlags: List[String]) = {
val code = getResourceAsString(resourceName)
- SerializabilityCheckerCompiler.compileCode(List(code, serYesCode), chosenDisableFlags) should be("")
- val noOut = SerializabilityCheckerCompiler.compileCode(List(code, serNoCode), chosenDisableFlags)
- noOut should include("error")
- noOut should include(errorTypes.name)
+ SerializabilityCheckerCompiler.compileCode(List(code, mySerializableAnnotated), pluginFlags) should be("")
+ val output = SerializabilityCheckerCompiler.compileCode(List(code, mySerializableNotAnnotated), pluginFlags)
+ output should include("error")
+ output should include(expectedErrorType.name)
+ }
+
+ private def expectNoSerializabilityErrors(resourceName: String, pluginFlags: List[String]) = {
+ val code = getResourceAsString(resourceName)
+ SerializabilityCheckerCompiler.compileCode(List(code, mySerializableAnnotated), pluginFlags) should be("")
+ SerializabilityCheckerCompiler.compileCode(List(code, mySerializableNotAnnotated), pluginFlags) should be("")
}
"Serializability checker compiler plugin" should {
"correctly detect and traverse to serialization marker trait" when {
"given Behavior" in {
- testCode(
+ expectSerializabilityErrors(
"BehaviorTest.scala",
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given EventEnvelope" in {
- testCode(
+ expectSerializabilityErrors(
"EventEnvelopeTest.scala",
- ClassType.PersistentEvent,
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ ClassKind.PersistentEvent,
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given Persistent State in ReplyEffect" in {
- testCode(
+ expectSerializabilityErrors(
"ReplyEffectTestState.scala",
- ClassType.PersistentState,
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ ClassKind.PersistentState,
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"give Persistent Event in ReplyEffect" in {
- testCode(
+ expectSerializabilityErrors(
"ReplyEffectTestEvent.scala",
- ClassType.PersistentEvent,
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ ClassKind.PersistentEvent,
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given Effect" in {
- testCode(
+ expectSerializabilityErrors(
"EffectTest.scala",
- ClassType.PersistentEvent,
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ ClassKind.PersistentEvent,
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given ask pattern" in {
- testCode(
+ expectSerializabilityErrors(
"AskTest.scala",
- chosenDisableFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given tell pattern" in {
- testCode(
+ expectSerializabilityErrors(
"TellTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given ask pattern with sign" in {
- testCode(
+ expectSerializabilityErrors(
"AskSignTest.scala",
- chosenDisableFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given tell pattern with sign" in {
- testCode(
+ expectSerializabilityErrors(
"TellSignTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given pipe pattern" in {
- testCode(
+ expectSerializabilityErrors(
"PipeTest.scala",
- chosenDisableFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"given classic tell pattern" in {
- testCode(
+ expectSerializabilityErrors(
"TellClassicTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
}
"given classic tell sing pattern" in {
- testCode(
+ expectSerializabilityErrors(
"TellSignClassicTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
}
"given classic ask pattern" in {
- testCode(
+ expectSerializabilityErrors(
"AskClassicTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
}
"given classic ask pattern with sign" in {
- testCode(
+ expectSerializabilityErrors(
"AskSignClassicTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableHigherOrderFunctions))
}
"given classic ask pattern with higher order function" in {
- testCode(
+ expectSerializabilityErrors(
"AskHigherOrderClassicTest.scala",
- chosenDisableFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableMethodsUntyped))
+ pluginFlags = List(disableGenerics, disableGenericMethods, disableMethods, disableMethodsUntyped))
}
"RecipientRef type is used instead of ActorRef to reference an Actor" in {
- testCode(
+ expectSerializabilityErrors(
"AskRecipientRefTest.scala",
- chosenDisableFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenerics, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
}
- "whitelist all Akka types from checks" in {
- val akkaWhitelist = getResourceAsString("AkkaWhitelistTest.scala")
+ "exclude messages from checks" in {
+ expectNoSerializabilityErrors("AskTest.scala", pluginFlags = List(excludeMessages))
+ }
+
+ "exclude events and/or state from checks" in {
- val out = SerializabilityCheckerCompiler.compileCode(List(serYesCode, akkaWhitelist))
- out should have size 0
+ expectSerializabilityErrors(
+ "ReplyEffectTestEventAndState.scala",
+ expectedErrorType = ClassKind.PersistentState,
+ pluginFlags = List(excludePersistentEvents))
+
+ expectSerializabilityErrors(
+ "ReplyEffectTestEventAndState.scala",
+ expectedErrorType = ClassKind.PersistentEvent,
+ pluginFlags = List(excludePersistentStates))
+
+ expectNoSerializabilityErrors(
+ "ReplyEffectTestEventAndState.scala",
+ pluginFlags = List(excludePersistentEvents, excludePersistentStates))
+ }
+
+ "whitelist all Akka types from checks" in {
+ expectNoSerializabilityErrors("AkkaWhitelistTest.scala", pluginFlags = List())
}
"be able to detect serializer trait in generics" in {
- testCode(
+ expectSerializabilityErrors(
"GenericsTest.scala",
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"detect lack of upper bounds in generics" in {
val code = getResourceAsString("GenericsTest2.scala")
- SerializabilityCheckerCompiler.compileCode(List(serNoCode, code)) should include("error")
+ SerializabilityCheckerCompiler.compileCode(List(mySerializableNotAnnotated, code)) should include("error")
}
"ignore Any and Nothing" in {
- testCode(
+ expectSerializabilityErrors(
"AnyNothingTest.scala",
- chosenDisableFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
+ pluginFlags = List(disableGenericMethods, disableMethods, disableMethodsUntyped, disableHigherOrderFunctions))
}
"respect Akka serializers" in {
val code = getResourceAsString("AkkaSerializabilityTraitsTest.scala")
- SerializabilityCheckerCompiler.compileCode(List(serNoCode, code)) should be("")
+ SerializabilityCheckerCompiler.compileCode(List(mySerializableNotAnnotated, code)) should be("")
}
"recognize upper bound type for [_] wildcard usage as scala.Any for ._$ types" in {
val code = getResourceAsString("GenericsTest3.scala")
- SerializabilityCheckerCompiler.compileCode(List(serNoCode, code)) should be("")
+ SerializabilityCheckerCompiler.compileCode(List(mySerializableNotAnnotated, code)) should be("")
}
"fail on usage of Either as a message" in {