org.jetbrains.spek.engine.SpekTestEngine.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spek-junit-platform-engine Show documentation
Show all versions of spek-junit-platform-engine Show documentation
Spek TestEngine for the JUnit Platform
package org.jetbrains.spek.engine
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.SubjectSpek
import org.jetbrains.spek.api.dsl.Dsl
import org.jetbrains.spek.api.dsl.Pending
import org.jetbrains.spek.api.dsl.SubjectDsl
import org.jetbrains.spek.api.memoized.CachingMode
import org.jetbrains.spek.api.memoized.Subject
import org.jetbrains.spek.engine.extension.ExtensionRegistryImpl
import org.jetbrains.spek.engine.memoized.SubjectAdapter
import org.jetbrains.spek.engine.memoized.SubjectImpl
import org.jetbrains.spek.extension.Extension
import org.jetbrains.spek.extension.SpekExtension
import org.junit.platform.commons.util.ReflectionUtils
import org.junit.platform.engine.*
import org.junit.platform.engine.discovery.ClassSelector
import org.junit.platform.engine.discovery.ClasspathSelector
import org.junit.platform.engine.discovery.PackageSelector
import org.junit.platform.engine.discovery.UniqueIdSelector
import org.junit.platform.engine.support.descriptor.EngineDescriptor
import org.junit.platform.engine.support.descriptor.JavaClassSource
import org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine
import java.util.*
import java.util.function.Consumer
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.primaryConstructor
/**
* @author Ranie Jade Ramiso
*/
class SpekTestEngine: HierarchicalTestEngine() {
override fun discover(discoveryRequest: EngineDiscoveryRequest, uniqueId: UniqueId): TestDescriptor {
val engineDescriptor = SpekEngineDescriptor(uniqueId)
resolveSpecs(discoveryRequest, engineDescriptor)
return engineDescriptor
}
override fun getId(): String = "spek"
override fun createExecutionContext(request: ExecutionRequest) = SpekExecutionContext(ExtensionRegistryImpl())
private fun resolveSpecs(discoveryRequest: EngineDiscoveryRequest, engineDescriptor: EngineDescriptor) {
val isSpec = java.util.function.Predicate> {
Spek::class.java.isAssignableFrom(it) || SubjectSpek::class.java.isAssignableFrom(it)
}
discoveryRequest.getSelectorsByType(ClasspathSelector::class.java).forEach {
ReflectionUtils.findAllClassesInClasspathRoot(it.classpathRoot, isSpec).forEach {
resolveSpec(engineDescriptor, it)
}
}
discoveryRequest.getSelectorsByType(PackageSelector::class.java).forEach {
ReflectionUtils.findAllClassesInPackage(it.packageName, isSpec).forEach {
resolveSpec(engineDescriptor, it)
}
}
discoveryRequest.getSelectorsByType(ClassSelector::class.java).forEach {
if (isSpec.test(it.javaClass)) {
resolveSpec(engineDescriptor, it.javaClass as Class)
}
}
discoveryRequest.getSelectorsByType(UniqueIdSelector::class.java).forEach {
engineDescriptor.findByUniqueId(it.uniqueId).ifPresent(Consumer {
filterOutUniqueId(it, engineDescriptor)
})
}
}
private fun filterOutUniqueId(target: TestDescriptor, root: TestDescriptor) {
if (!target.equals(root)) {
if (root.allDescendants.contains(target)) {
val descriptors = LinkedList()
root.children.forEach {
descriptors.add(it)
}
descriptors.forEach { filterOutUniqueId(target, it) }
} else {
root.removeFromHierarchy()
}
}
}
private fun resolveSpec(engineDescriptor: EngineDescriptor, klass: Class<*>) {
val registry = ExtensionRegistryImpl().apply {
registerExtension(FixturesAdapter())
registerExtension(SubjectAdapter())
}
getSpekExtensions(klass.kotlin)
.forEach { registry.registerExtension(it) }
val instance = klass.kotlin.primaryConstructor!!.call()
val root = Scope.Spec(
engineDescriptor.uniqueId.append(SPEC_SEGMENT_TYPE, klass.name),
JavaClassSource(klass), registry, false
)
engineDescriptor.addChild(root)
when(instance) {
is SubjectSpek<*> -> (instance as SubjectSpek).spec.invoke(
SubjectCollector(root, root.registry)
)
is Spek -> instance.spec.invoke(Collector(root, root.registry))
}
}
open class Collector(val root: Scope.Group, val registry: ExtensionRegistryImpl): Dsl {
override fun group(description: String, pending: Pending, body: Dsl.() -> Unit) {
val group = Scope.Group(root.uniqueId.append(GROUP_SEGMENT_TYPE, description), pending, getSource())
root.addChild(group)
body.invoke(Collector(group, registry))
}
override fun test(description: String, pending: Pending, body: () -> Unit) {
val test = Scope.Test(root.uniqueId.append(TEST_SEGMENT_TYPE, description), pending, getSource(), body)
root.addChild(test)
}
override fun beforeEach(callback: () -> Unit) {
registry.getExtension(FixturesAdapter::class)!!.registerBeforeEach(root, callback)
}
override fun afterEach(callback: () -> Unit) {
registry.getExtension(FixturesAdapter::class)!!.registerAfterEach(root, callback)
}
}
open class SubjectCollector(root: Scope.Group, registry: ExtensionRegistryImpl)
: Collector(root, registry), SubjectDsl {
var _subject: SubjectImpl? = null
override fun subject(mode: CachingMode, factory: () -> T): Subject {
return registry.getExtension(SubjectAdapter::class)!!
.registerSubject(mode, root, factory).apply { _subject = this }
}
override val subject: T
get() {
if (_subject != null) {
return _subject!!.get()
}
throw SpekException("Subject not configured.")
}
override fun > includeSubjectSpec(spec: KClass) {
val instance = spec.primaryConstructor!!.call()
val nestedRegistry = ExtensionRegistryImpl()
registry.extensions().forEach { nestedRegistry.registerExtension(it) }
getSpekExtensions(spec)
.forEach { nestedRegistry.registerExtension(it) }
val scope = Scope.Spec(
root.uniqueId.append(SPEC_SEGMENT_TYPE, spec.java.name),
JavaClassSource(spec.java), nestedRegistry, true
)
root.addChild(scope)
instance.spec.invoke(NestedSubjectCollector(scope, nestedRegistry, this as SubjectCollector))
}
}
class NestedSubjectCollector(root: Scope.Group, registry: ExtensionRegistryImpl, val parent: SubjectCollector)
: SubjectCollector(root, registry) {
override fun subject(mode: CachingMode, factory: () -> T): Subject {
return object: Subject {
override fun getValue(ref: Any?, property: KProperty<*>): T {
return parent.subject
}
}
}
override val subject: T
get() = parent.subject
}
companion object {
const val SPEC_SEGMENT_TYPE = "spec";
const val GROUP_SEGMENT_TYPE = "group";
const val TEST_SEGMENT_TYPE = "test";
// TODO: fix me
fun getSource(): TestSource? = null
fun getSpekExtensions(spec: KClass<*>): List {
return spec.annotations
.map {
if (it is SpekExtension) {
it
} else {
it.annotationClass.annotations.find {
it.annotationClass == SpekExtension::class
} as SpekExtension?
}
}
.filter { it != null }
.map { it!!.extension.primaryConstructor!!.call() }
}
}
}