Remove use of getClass in secondary constructors (#28355)
* Remove use of getClass in secondary constructors As this is not allowed anymore on newer versions of scala, and likely didn't work correctly in the past either This might make selecting unique actor system names for test actor systems a bit less reliable, but that didn't seem to be critical anyway. Thanks to @som-snytt for the heads-up and initial implementation in #28353 * Avoid AkkaSpec.getCallerName in MultiNodeClusterShardingConfig * StreamSpec can be abstract * Use more sophisticated test class name logic * scalafmt
This commit is contained in:
parent
8a019f86a1
commit
c337bf5287
4 changed files with 109 additions and 16 deletions
|
|
@ -4,15 +4,63 @@
|
|||
|
||||
package akka.cluster.sharding
|
||||
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import akka.cluster.MultiNodeClusterSpec
|
||||
import akka.persistence.journal.leveldb.SharedLeveldbJournal
|
||||
import akka.remote.testkit.MultiNodeConfig
|
||||
import akka.testkit.AkkaSpec
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
|
||||
object MultiNodeClusterShardingConfig {
|
||||
private[sharding] def testNameFromCallStack(classToStartFrom: Class[_]): String = {
|
||||
|
||||
def isAbstractClass(className: String): Boolean = {
|
||||
try {
|
||||
Modifier.isAbstract(Class.forName(className).getModifiers)
|
||||
} catch {
|
||||
case _: Throwable => false // yes catch everything, best effort check
|
||||
}
|
||||
}
|
||||
|
||||
val startFrom = classToStartFrom.getName
|
||||
val filteredStack = Thread.currentThread.getStackTrace.iterator
|
||||
.map(_.getClassName)
|
||||
// drop until we find the first occurrence of classToStartFrom
|
||||
.dropWhile(!_.startsWith(startFrom))
|
||||
// then continue to the next entry after classToStartFrom that makes sense
|
||||
.dropWhile {
|
||||
case `startFrom` => true
|
||||
case str if str.startsWith(startFrom + "$") => true // lambdas inside startFrom etc
|
||||
case str if isAbstractClass(str) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
if (filteredStack.isEmpty)
|
||||
throw new IllegalArgumentException(s"Couldn't find [${classToStartFrom.getName}] in call stack")
|
||||
|
||||
// sanitize for actor system name
|
||||
scrubActorSystemName(filteredStack.next())
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the `name` to be used as valid actor system name by
|
||||
* replacing invalid characters. `name` may for example be a fully qualified
|
||||
* class name and then the short class name will be used.
|
||||
*/
|
||||
def scrubActorSystemName(name: String): String = {
|
||||
name
|
||||
.replaceFirst("""^.*\.""", "") // drop package name
|
||||
.replaceAll("""\$\$?\w+""", "") // drop scala anonymous functions/classes
|
||||
.replaceAll("[^a-zA-Z_0-9]", "_")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A MultiNodeConfig for ClusterSharding. Implement the roles, etc. and create with the following:
|
||||
*
|
||||
* Note that this class is not used anywhere yet, but could be a good starting point
|
||||
* for new or refactored multi-node sharding specs
|
||||
*
|
||||
* @param mode the state store mode
|
||||
* @param rememberEntities defaults to off
|
||||
* @param overrides additional config
|
||||
|
|
@ -25,7 +73,10 @@ abstract class MultiNodeClusterShardingConfig(
|
|||
loglevel: String = "INFO")
|
||||
extends MultiNodeConfig {
|
||||
|
||||
val targetDir = s"target/ClusterSharding${AkkaSpec.getCallerName(getClass)}Spec-$mode-remember-$rememberEntities"
|
||||
import MultiNodeClusterShardingConfig._
|
||||
|
||||
val targetDir =
|
||||
s"target/ClusterSharding${testNameFromCallStack(classOf[MultiNodeClusterShardingConfig])}Spec-$mode-remember-$rememberEntities"
|
||||
|
||||
val modeConfig =
|
||||
if (mode == ClusterShardingSettings.StateStoreModeDData) ConfigFactory.empty
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ object MultiNodeClusterShardingSpec {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this class is not used anywhere yet, but could be a good starting point
|
||||
* for new or refactored multi-node sharding specs
|
||||
*/
|
||||
abstract class MultiNodeClusterShardingSpec(val config: MultiNodeClusterShardingConfig)
|
||||
extends MultiNodeSpec(config)
|
||||
with MultiNodeClusterSpec {
|
||||
|
|
|
|||
|
|
@ -14,15 +14,18 @@ import org.scalatest.Failed
|
|||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class StreamSpec(_system: ActorSystem) extends AkkaSpec(_system) {
|
||||
abstract class StreamSpec(_system: ActorSystem) extends AkkaSpec(_system) {
|
||||
def this(config: Config) =
|
||||
this(ActorSystem(AkkaSpec.getCallerName(getClass), ConfigFactory.load(config.withFallback(AkkaSpec.testConf))))
|
||||
this(
|
||||
ActorSystem(
|
||||
AkkaSpec.testNameFromCallStack(classOf[StreamSpec]),
|
||||
ConfigFactory.load(config.withFallback(AkkaSpec.testConf))))
|
||||
|
||||
def this(s: String) = this(ConfigFactory.parseString(s))
|
||||
|
||||
def this(configMap: Map[String, _]) = this(AkkaSpec.mapToConfig(configMap))
|
||||
|
||||
def this() = this(ActorSystem(AkkaSpec.getCallerName(getClass), AkkaSpec.testConf))
|
||||
def this() = this(ActorSystem(AkkaSpec.testNameFromCallStack(classOf[StreamSpec]), AkkaSpec.testConf))
|
||||
|
||||
override def withFixture(test: NoArgTest) = {
|
||||
super.withFixture(test) match {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package akka.testkit
|
||||
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import org.scalactic.{ CanEqual, TypeCheckedTripleEquals }
|
||||
|
||||
import language.postfixOps
|
||||
|
|
@ -14,6 +16,7 @@ import akka.event.{ Logging, LoggingAdapter }
|
|||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Future
|
||||
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import akka.dispatch.Dispatchers
|
||||
import akka.testkit.TestEvent._
|
||||
|
|
@ -44,18 +47,47 @@ object AkkaSpec {
|
|||
ConfigFactory.parseMap(map.asJava)
|
||||
}
|
||||
|
||||
def getCallerName(clazz: Class[_]): String = {
|
||||
val s = Thread.currentThread.getStackTrace
|
||||
.map(_.getClassName)
|
||||
.drop(1)
|
||||
.dropWhile(_.matches("(java.lang.Thread|.*AkkaSpec.*|.*\\.StreamSpec.*|.*MultiNodeSpec.*|.*\\.Abstract.*)"))
|
||||
val reduced = s.lastIndexWhere(_ == clazz.getName) match {
|
||||
case -1 => s
|
||||
case z => s.drop(z + 1)
|
||||
def testNameFromCallStack(classToStartFrom: Class[_]): String = {
|
||||
|
||||
def isAbstractClass(className: String): Boolean = {
|
||||
try {
|
||||
Modifier.isAbstract(Class.forName(className).getModifiers)
|
||||
} catch {
|
||||
case _: Throwable => false // yes catch everything, best effort check
|
||||
}
|
||||
}
|
||||
reduced.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_")
|
||||
|
||||
val startFrom = classToStartFrom.getName
|
||||
val filteredStack = Thread.currentThread.getStackTrace.iterator
|
||||
.map(_.getClassName)
|
||||
// drop until we find the first occurrence of classToStartFrom
|
||||
.dropWhile(!_.startsWith(startFrom))
|
||||
// then continue to the next entry after classToStartFrom that makes sense
|
||||
.dropWhile {
|
||||
case `startFrom` => true
|
||||
case str if str.startsWith(startFrom + "$") => true // lambdas inside startFrom etc
|
||||
case str if isAbstractClass(str) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
if (filteredStack.isEmpty)
|
||||
throw new IllegalArgumentException(s"Couldn't find [${classToStartFrom.getName}] in call stack")
|
||||
|
||||
// sanitize for actor system name
|
||||
scrubActorSystemName(filteredStack.next())
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the `name` to be used as valid actor system name by
|
||||
* replacing invalid characters. `name` may for example be a fully qualified
|
||||
* class name and then the short class name will be used.
|
||||
*/
|
||||
def scrubActorSystemName(name: String): String = {
|
||||
name
|
||||
.replaceFirst("""^.*\.""", "") // drop package name
|
||||
.replaceAll("""\$\$?\w+""", "") // drop scala anonymous functions/classes
|
||||
.replaceAll("[^a-zA-Z_0-9]", "_")
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AkkaSpec(_system: ActorSystem)
|
||||
|
|
@ -70,13 +102,16 @@ abstract class AkkaSpec(_system: ActorSystem)
|
|||
implicit val patience = PatienceConfig(testKitSettings.DefaultTimeout.duration, Span(100, Millis))
|
||||
|
||||
def this(config: Config) =
|
||||
this(ActorSystem(AkkaSpec.getCallerName(getClass), ConfigFactory.load(config.withFallback(AkkaSpec.testConf))))
|
||||
this(
|
||||
ActorSystem(
|
||||
AkkaSpec.testNameFromCallStack(classOf[AkkaSpec]),
|
||||
ConfigFactory.load(config.withFallback(AkkaSpec.testConf))))
|
||||
|
||||
def this(s: String) = this(ConfigFactory.parseString(s))
|
||||
|
||||
def this(configMap: Map[String, _]) = this(AkkaSpec.mapToConfig(configMap))
|
||||
|
||||
def this() = this(ActorSystem(AkkaSpec.getCallerName(getClass), AkkaSpec.testConf))
|
||||
def this() = this(ActorSystem(AkkaSpec.testNameFromCallStack(classOf[AkkaSpec]), AkkaSpec.testConf))
|
||||
|
||||
val log: LoggingAdapter = Logging(system, this.getClass)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue