Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix: Fix withClassName filter #1233

Merged
merged 5 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.test_app.similar

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.test_app.BaseInstrumentedTest
import org.junit.Ignore
import androidx.test.filters.Suppress
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SimilarNameTest1 : BaseInstrumentedTest() {

@Test
fun test1() = testMethod()

@Test
fun test2() = testMethod()

@Test
fun test19() = testMethod()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.test_app.similar

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.test_app.BaseInstrumentedTest
import org.junit.Ignore
import androidx.test.filters.Suppress
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SimilarNameTest10 : BaseInstrumentedTest() {

@Test
fun test1() = testMethod()

@Test
fun test2() = testMethod()

@Test
fun test19() = testMethod()
}
23 changes: 18 additions & 5 deletions test_runner/src/main/kotlin/ftl/filter/TestFilters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,25 @@ object TestFilters {
}
)

private fun withClassName(classNames: List<String>): TestFilter = TestFilter(
describe = "withClassName (${classNames.joinToString(", ")})",
shouldRun = { testMethod ->
withPackageName(classNames).shouldRun(testMethod)
private fun withClassName(classNames: List<String>): TestFilter {
// splits foo.bar.TestClass1#testMethod1 into [foo.bar.TestClass1, testMethod1]
fun String.extractClassAndTestNames() = split("#")
val classFilters = classNames.map { it.extractClassAndTestNames() }
return TestFilter(
describe = "withClassName (${classNames.joinToString(", ")})",
shouldRun = { testMethod -> testMethod.testName.extractClassAndTestNames().matchFilters(classFilters) }
)
}

private fun List<String>.matchFilters(classFilters: List<List<String>>): Boolean {
fun List<String>.className() = first()
fun List<String>.methodName() = last()
return classFilters.any { filter ->
// When filter.size == 1 all test methods from the class should run therefore we do not compare method names
// When filter.size != 1 only particular test from the class should be launched and we need to compare method names as well
className() == filter.className() && (filter.size == 1 || methodName() == filter.methodName())
}
)
}

private fun withAnnotation(annotations: List<String>): TestFilter = TestFilter(
describe = "withAnnotation (${annotations.joinToString(", ")})",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,15 @@ internal fun InstrumentationTestContext.getFlankTestMethods(
.filter(testFilter.shouldRun)
.filterNot(parameterizedClasses::belong)
.map(TestMethod::toFlankTestMethod).toList()
.plus(parameterizedClasses.map(String::toFlankTestMethod))
.plus(parameterizedClasses.onlyShouldRun(testFilter))
}

private fun List<String>.belong(method: TestMethod) = any { className -> method.testName.startsWith(className) }

private fun List<String>.onlyShouldRun(filter: TestFilter) = this
.filter { filter.shouldRun(TestMethod(it, emptyList())) }
.map { FlankTestMethod("class $it", ignored = false, isParameterizedClass = true) }

private fun TestMethod.toFlankTestMethod() = FlankTestMethod(
testName = "class $testName",
ignored = annotations.any { it.name in ignoredAnnotations }
Expand All @@ -86,9 +90,8 @@ private val ignoredAnnotations = listOf(
"android.support.test.filters.Suppress"
)

private fun String.toFlankTestMethod() = FlankTestMethod("class $this", ignored = false, isParameterizedClass = true)

private fun InstrumentationTestContext.getParametrizedClasses(): List<String> =
@VisibleForTesting
internal fun InstrumentationTestContext.getParametrizedClasses(): List<String> =
DexParser.readDexFiles(test.local).fold(emptyList()) { accumulator, file: DexFile ->
accumulator + file.classDefs
.filter(file::isParametrizedClass)
Expand Down
36 changes: 36 additions & 0 deletions test_runner/src/test/kotlin/ftl/filter/TestFiltersTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,42 @@ class TestFiltersTest {
assertThat(filter.shouldRun(BAR_PACKAGE)).isFalse()
assertThat(filter.shouldRun(BAR_CLASSNAME)).isTrue()
}

@Test
fun `withClassName should correctly filter classes with similar name`() {
// test-targets:
// - class foo.bar.Class1
// should filter foo.bar.Class11, foo.bar.Class101 ...
// the same is applicable for methods

val filter = fromTestTargets(
listOf(
"class anyPackage_1.anyClass_1",
"class anyPackage_3.anyClass_3#anyMethod_3"
)
)

val tests = listOf(
TargetsHelper(pack = "anyPackage_1", cl = "anyClass_1", m = "anyMethod_1", annotation = "Foo"),
TargetsHelper(pack = "anyPackage_1", cl = "anyClass_1", m = "anyMethod_2", annotation = "Foo"),
TargetsHelper(pack = "anyPackage_1", cl = "anyClass_12", m = "anyMethod_1", annotation = "Bar"),
TargetsHelper(pack = "anyPackage_1", cl = "anyClass_12", m = "anyMethod_12", annotation = "Bar"),
TargetsHelper(pack = "anyPackage_3", cl = "anyClass_3", m = "anyMethod_3", annotation = "Bar"),
TargetsHelper(pack = "anyPackage_3", cl = "anyClass_3", m = "anyMethod_32", annotation = "Bar"),
TargetsHelper(pack = "anyPackage_3", cl = "anyClass_32", m = "anyMethod_3", annotation = "Bar"),
TargetsHelper(pack = "anyPackage_3", cl = "anyClass_32", m = "anyMethod_32", annotation = "Bar")
).map { getDefaultTestMethod(it.fullView, it.annotation) }

val expected = listOf(
"anyPackage_1.anyClass_1#anyMethod_1",
"anyPackage_1.anyClass_1#anyMethod_2",
"anyPackage_3.anyClass_3#anyMethod_3"
)

val result = tests.withFilter(filter)

assertThat(result).isEqualTo(expected)
}
}

private fun getDefaultTestMethod(testName: String, annotation: String) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
package ftl.run.platform.android

import com.google.common.truth.Truth.assertThat
import com.linkedin.dex.parser.DexParser
import com.linkedin.dex.parser.DexParser.Companion.findTestMethods
import com.linkedin.dex.parser.TestMethod
import ftl.args.AndroidArgs
import ftl.filter.TestFilter
import ftl.filter.TestFilters
import ftl.run.model.AndroidTestContext
import ftl.run.model.InstrumentationTestContext
import ftl.run.model.RoboTestContext
import ftl.test.util.mixedConfigYaml
import ftl.test.util.should
import ftl.util.FileReference
import ftl.util.FlankTestMethod
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Test

class CreateAndroidTestContextKtTest {

@After
fun tearDown() = unmockkAll()

@Test
fun `create AndroidTestConfig for mixed tests`() {
// given
Expand Down Expand Up @@ -79,4 +88,31 @@ class CreateAndroidTestContextKtTest {
// given
assertEquals(actual, 2)
}

@Test
fun `should not append all parameterized classes to list of test methods`() {
val testInstrumentationContext = InstrumentationTestContext(
FileReference("./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk", ""),
FileReference("./src/test/kotlin/ftl/fixtures/tmp/apk/app-single-success-debug-androidTest.apk", "")
)

mockkObject(DexParser) {
every { findTestMethods(any()) } returns listOf(
TestMethod("foo.bar.TestClass1#test1", emptyList()),
TestMethod("foo.bar.TestClass1#test2", emptyList()),
TestMethod("foo.bar.TestClass2#test1", emptyList()),
TestMethod("foo.bar.TestClass2#test2", emptyList()),
TestMethod("foo.bar.ParamClass#testParam", emptyList()),
)
mockkStatic("ftl.run.platform.android.CreateAndroidTestContextKt")
every { testInstrumentationContext.getParametrizedClasses() } returns listOf("foo.bar.ParamClass")

val actual = testInstrumentationContext.getFlankTestMethods(TestFilters.fromTestTargets(listOf("class foo.bar.TestClass1")))
val expected = listOf(
FlankTestMethod("class foo.bar.TestClass1#test1"),
FlankTestMethod("class foo.bar.TestClass1#test2")
)
assertThat(actual).isEqualTo(expected)
}
}
}