Select the asserter based on test framework presence in classpath

Simplify asserter lookup: enumerate all AsserterContributors when
the file class with lookupAsserter function is initialized.

It's assumed that AsserterContributor initialization should be fast.

#KT-21154 Fixed
This commit is contained in:
Ilya Gorbunov
2018-02-06 09:19:45 +03:00
parent 9dfa54eb62
commit 5e3edf4bd7
5 changed files with 48 additions and 71 deletions
@@ -3,21 +3,25 @@ package kotlin.test.junit
import org.junit.* import org.junit.*
import kotlin.test.* import kotlin.test.*
/**
* Provides [JUnitAsserter] if `org.junit.Assert` is found in the classpath.
*/
class JUnitContributor : AsserterContributor { class JUnitContributor : AsserterContributor {
override fun contribute(): Asserter? { override fun contribute(): Asserter? {
for (stackFrame in currentStackTrace()) { return if (hasJUnitInClassPath) JUnitAsserter else null
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") }
val className = stackFrame.className as java.lang.String
if (className.startsWith("org.junit.") || className.startsWith("junit.")) { private val hasJUnitInClassPath = try {
return JUnitAsserter Class.forName("org.junit.Assert")
} true
} } catch (_: ClassNotFoundException) {
false
return null
} }
} }
/**
* Implements `kotlin.test` assertions by delegating them to `org.junit.Assert` class.
*/
object JUnitAsserter : Asserter { object JUnitAsserter : Asserter {
override fun assertEquals(message: String?, expected: Any?, actual: Any?) { override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
Assert.assertEquals(message, expected, actual) Assert.assertEquals(message, expected, actual)
@@ -1,27 +1,26 @@
package kotlin.test.junit.tests package kotlin.test.junit.tests
import org.junit.* import org.junit.Assert
import kotlin.test.*
import java.util.concurrent.* import java.util.concurrent.*
import kotlin.test.junit.JUnitAsserter
class JUnitContributorTest { class JUnitContributorTest {
@Test @Test
fun smokeTest() { fun smokeTest() {
Assert.assertEquals("JUnitAsserter", asserter.`class`.simpleName) assertSame(JUnitAsserter, kotlin.test.asserter)
Assert.assertEquals(JUnitAsserter::class.java.simpleName, kotlin.test.asserter.javaClass.simpleName)
} }
@Test @Test
fun `should fail to contribute if it was run outside of junit`() { fun parallelThreadGetsTheSameAsserter() {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") val q = ArrayBlockingQueue<Any>(1)
val q = ArrayBlockingQueue<java.lang.Object>(1)
Thread { Thread {
q.put(asserter) q.put(asserter)
}.start() }.start()
Assert.assertEquals("DefaultAsserter", q.take().`class`.simpleName) assertSame(kotlin.test.asserter, q.take())
} }
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private val asserter: java.lang.Object
get() = Class.forName("kotlin.test.AssertionsKt").getMethod("getAsserter").invoke(null) as java.lang.Object
} }
@@ -1,48 +1,15 @@
package kotlin.test package kotlin.test
import java.util.* import java.util.ServiceLoader
import java.util.concurrent.atomic.*
import java.util.concurrent.locks.*
import kotlin.concurrent.withLock
private val inited = AtomicBoolean() private val contributors = ServiceLoader.load(AsserterContributor::class.java).toList()
private val lock = ReentrantLock()
private val contributors = ArrayList<AsserterContributor>()
internal actual fun lookupAsserter(): Asserter = lookup()
private val defaultAsserter = DefaultAsserter() private val defaultAsserter = DefaultAsserter()
internal fun lookup(): Asserter { internal actual fun lookupAsserter(): Asserter {
initContributorsIfNeeded()
for (contributor in contributors) { for (contributor in contributors) {
val asserter = contributor.contribute() val asserter = contributor.contribute()
if (asserter != null) { if (asserter != null) return asserter
return asserter
}
} }
return defaultAsserter return defaultAsserter
} }
private fun initContributors() {
contributors.clear()
val loader = ServiceLoader.load(AsserterContributor::class.java)
for (contributor in loader) {
if (contributor != null) {
contributors.add(contributor)
}
}
}
private fun initContributorsIfNeeded() {
if (!inited.get()) {
lock.withLock {
if (inited.compareAndSet(false, true)) {
initContributors()
}
}
}
}
@@ -8,20 +8,25 @@ package kotlin.test.testng
import org.testng.* import org.testng.*
import kotlin.test.* import kotlin.test.*
/**
* Provides [TestNGAsserter] if `org.testng.Assert` is found in the classpath.
*/
class TestNGContributor : AsserterContributor { class TestNGContributor : AsserterContributor {
override fun contribute(): Asserter? { override fun contribute(): Asserter? {
for (stackFrame in currentStackTrace()) { return if (hasTestNGInClassPath) TestNGAsserter else null
val className = stackFrame.className }
if (className.startsWith("org.testng.") || className.startsWith("testng.")) { private val hasTestNGInClassPath = try {
return TestNGAsserter Class.forName("org.testng.Assert")
} true
} } catch (_: ClassNotFoundException) {
false
return null
} }
} }
/**
* Implements `kotlin.test` assertions by delegating them to `org.testng.Assert` class.
*/
object TestNGAsserter : Asserter { object TestNGAsserter : Asserter {
override fun assertEquals(message: String?, expected: Any?, actual: Any?) { override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
Assert.assertEquals(expected, actual, message) Assert.assertEquals(expected, actual, message)
@@ -1,25 +1,27 @@
package kotlin.test.testng.tests package kotlin.test.testng.tests
import org.testng.annotations.* import org.testng.Assert
import org.testng.* import kotlin.test.*
import java.util.concurrent.* import java.util.concurrent.*
import kotlin.test.testng.TestNGAsserter
class TestNGContributorTest { class TestNGContributorTest {
@Test @Test
fun smokeTest() { fun smokeTest() {
Assert.assertEquals("TestNGAsserter", kotlin.test.asserter.javaClass.simpleName) assertSame(TestNGAsserter, asserter)
Assert.assertEquals(TestNGAsserter::class.java.simpleName, kotlin.test.asserter.javaClass.simpleName)
} }
@Test @Test
fun `should fail to contribute if it was run outside of testng`() { fun parallelThreadGetsTheSameAsserter() {
val q = ArrayBlockingQueue<Any>(1) val q = ArrayBlockingQueue<Any>(1)
Thread { Thread {
q.put(kotlin.test.asserter) q.put(asserter)
}.start() }.start()
Assert.assertEquals("DefaultAsserter", q.take().javaClass.simpleName) assertSame(asserter, q.take())
} }
} }