From 5e3edf4bd706ad3d9a9082d7b4f8aff79fa7eecb Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 6 Feb 2018 09:19:45 +0300 Subject: [PATCH] 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 --- .../junit/src/main/kotlin/JUnitSupport.kt | 22 ++++++---- .../src/test/kotlin/JUnitContributorTest.kt | 17 ++++---- .../jvm/src/main/kotlin/AsserterLookup.kt | 43 +++---------------- .../testng/src/main/kotlin/TestNGSupport.kt | 21 +++++---- .../src/test/kotlin/TestNGContributorTest.kt | 16 ++++--- 5 files changed, 48 insertions(+), 71 deletions(-) diff --git a/libraries/kotlin.test/junit/src/main/kotlin/JUnitSupport.kt b/libraries/kotlin.test/junit/src/main/kotlin/JUnitSupport.kt index f47acaf622e..48676c7e0d9 100644 --- a/libraries/kotlin.test/junit/src/main/kotlin/JUnitSupport.kt +++ b/libraries/kotlin.test/junit/src/main/kotlin/JUnitSupport.kt @@ -3,21 +3,25 @@ package kotlin.test.junit import org.junit.* import kotlin.test.* +/** + * Provides [JUnitAsserter] if `org.junit.Assert` is found in the classpath. + */ class JUnitContributor : AsserterContributor { override fun contribute(): Asserter? { - for (stackFrame in currentStackTrace()) { - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - val className = stackFrame.className as java.lang.String + return if (hasJUnitInClassPath) JUnitAsserter else null + } - if (className.startsWith("org.junit.") || className.startsWith("junit.")) { - return JUnitAsserter - } - } - - return null + private val hasJUnitInClassPath = try { + Class.forName("org.junit.Assert") + true + } catch (_: ClassNotFoundException) { + false } } +/** + * Implements `kotlin.test` assertions by delegating them to `org.junit.Assert` class. + */ object JUnitAsserter : Asserter { override fun assertEquals(message: String?, expected: Any?, actual: Any?) { Assert.assertEquals(message, expected, actual) diff --git a/libraries/kotlin.test/junit/src/test/kotlin/JUnitContributorTest.kt b/libraries/kotlin.test/junit/src/test/kotlin/JUnitContributorTest.kt index 7339c199da2..ae61e6a13f2 100644 --- a/libraries/kotlin.test/junit/src/test/kotlin/JUnitContributorTest.kt +++ b/libraries/kotlin.test/junit/src/test/kotlin/JUnitContributorTest.kt @@ -1,27 +1,26 @@ package kotlin.test.junit.tests -import org.junit.* +import org.junit.Assert +import kotlin.test.* import java.util.concurrent.* +import kotlin.test.junit.JUnitAsserter class JUnitContributorTest { @Test 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 - fun `should fail to contribute if it was run outside of junit`() { - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - val q = ArrayBlockingQueue(1) + fun parallelThreadGetsTheSameAsserter() { + val q = ArrayBlockingQueue(1) Thread { q.put(asserter) }.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 } diff --git a/libraries/kotlin.test/jvm/src/main/kotlin/AsserterLookup.kt b/libraries/kotlin.test/jvm/src/main/kotlin/AsserterLookup.kt index 91eb2424cea..fc191aa3b61 100644 --- a/libraries/kotlin.test/jvm/src/main/kotlin/AsserterLookup.kt +++ b/libraries/kotlin.test/jvm/src/main/kotlin/AsserterLookup.kt @@ -1,48 +1,15 @@ package kotlin.test -import java.util.* -import java.util.concurrent.atomic.* -import java.util.concurrent.locks.* -import kotlin.concurrent.withLock +import java.util.ServiceLoader -private val inited = AtomicBoolean() -private val lock = ReentrantLock() -private val contributors = ArrayList() - -internal actual fun lookupAsserter(): Asserter = lookup() +private val contributors = ServiceLoader.load(AsserterContributor::class.java).toList() private val defaultAsserter = DefaultAsserter() -internal fun lookup(): Asserter { - initContributorsIfNeeded() - +internal actual fun lookupAsserter(): Asserter { for (contributor in contributors) { val asserter = contributor.contribute() - if (asserter != null) { - return asserter - } + if (asserter != null) return asserter } - 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() - } - } - } -} +} \ No newline at end of file diff --git a/libraries/kotlin.test/testng/src/main/kotlin/TestNGSupport.kt b/libraries/kotlin.test/testng/src/main/kotlin/TestNGSupport.kt index 17fbb9558c9..df70c782454 100644 --- a/libraries/kotlin.test/testng/src/main/kotlin/TestNGSupport.kt +++ b/libraries/kotlin.test/testng/src/main/kotlin/TestNGSupport.kt @@ -8,20 +8,25 @@ package kotlin.test.testng import org.testng.* import kotlin.test.* +/** + * Provides [TestNGAsserter] if `org.testng.Assert` is found in the classpath. + */ class TestNGContributor : AsserterContributor { override fun contribute(): Asserter? { - for (stackFrame in currentStackTrace()) { - val className = stackFrame.className + return if (hasTestNGInClassPath) TestNGAsserter else null + } - if (className.startsWith("org.testng.") || className.startsWith("testng.")) { - return TestNGAsserter - } - } - - return null + private val hasTestNGInClassPath = try { + Class.forName("org.testng.Assert") + true + } catch (_: ClassNotFoundException) { + false } } +/** + * Implements `kotlin.test` assertions by delegating them to `org.testng.Assert` class. + */ object TestNGAsserter : Asserter { override fun assertEquals(message: String?, expected: Any?, actual: Any?) { Assert.assertEquals(expected, actual, message) diff --git a/libraries/kotlin.test/testng/src/test/kotlin/TestNGContributorTest.kt b/libraries/kotlin.test/testng/src/test/kotlin/TestNGContributorTest.kt index 0d97a4bf10f..07b0fd39d08 100644 --- a/libraries/kotlin.test/testng/src/test/kotlin/TestNGContributorTest.kt +++ b/libraries/kotlin.test/testng/src/test/kotlin/TestNGContributorTest.kt @@ -1,25 +1,27 @@ package kotlin.test.testng.tests -import org.testng.annotations.* -import org.testng.* +import org.testng.Assert +import kotlin.test.* import java.util.concurrent.* +import kotlin.test.testng.TestNGAsserter class TestNGContributorTest { @Test 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 - fun `should fail to contribute if it was run outside of testng`() { + fun parallelThreadGetsTheSameAsserter() { val q = ArrayBlockingQueue(1) Thread { - q.put(kotlin.test.asserter) + q.put(asserter) }.start() - Assert.assertEquals("DefaultAsserter", q.take().javaClass.simpleName) + assertSame(asserter, q.take()) } -} +} \ No newline at end of file