From a230da5df1650a09abe4994a43e017fccaba2ff1 Mon Sep 17 00:00:00 2001 From: "so.potapov" Date: Thu, 12 Sep 2024 10:39:30 +0300 Subject: [PATCH 1/2] remove guava lib --- gradle/libs.versions.toml | 2 - ultron-android/build.gradle.kts | 1 - .../ultron/custom/espresso/base/Checker.kt | 52 ++++++++++++++ .../custom/espresso/base/IterableUtils.kt | 28 ++++++++ .../custom/espresso/base/UltronViewFinder.kt | 69 +++++-------------- 5 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt create mode 100644 ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cf0343c..ab8c929 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,6 @@ [versions] agp = "8.3.2" atomicfu = "0.24.0" -guava = "33.2.1-android" kotlin = "2.0.0" compose = "1.6.7" compose-compiler = "1.5.4" @@ -31,7 +30,6 @@ uiTestJunit4Android = "1.6.8" androidx-ui-test-junit4-android = { module = "androidx.compose.ui:ui-test-junit4-android", version.ref = "uiTestJunit4Android" } androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiTestJunit4Android" } atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomicfu" } -guava = { module = "com.google.guava:guava", version.ref = "guava" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } diff --git a/ultron-android/build.gradle.kts b/ultron-android/build.gradle.kts index 536e881..3a447c4 100644 --- a/ultron-android/build.gradle.kts +++ b/ultron-android/build.gradle.kts @@ -39,7 +39,6 @@ dependencies { implementation(Libs.kotlinReflect) implementation(Libs.kotlinStdlib) implementation(Libs.recyclerView) - implementation(libs.guava) api(Libs.espressoCore) api(Libs.espressoContrib) api(Libs.espressoWeb) diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt new file mode 100644 index 0000000..2c8711e --- /dev/null +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt @@ -0,0 +1,52 @@ +package com.atiurin.ultron.custom.espresso.base + +import android.os.Looper + + +object Checker { + fun checkMainThread() { + checkState( + Thread.currentThread() == Looper.getMainLooper().thread, + "Method cannot be called off the main application thread (on: %s)", + Thread.currentThread().name + ) + } + + fun checkState( + expression: Boolean, errorMessageTemplate: String, vararg errorMessageArgs: Any, + ) { + check(expression) { format(errorMessageTemplate, *errorMessageArgs) } + } + + private fun format(template: String?, vararg args: Any): String { + var templ = template + templ = templ.toString() + + // start substituting the arguments into the '%s' placeholders + val builder = StringBuilder(templ.length + 16 * args.size) + var templateStart = 0 + var i = 0 + while (i < args.size) { + val placeholderStart = templ.indexOf("%s", templateStart) + if (placeholderStart == -1) { + break + } + builder.append(templ.substring(templateStart, placeholderStart)) + builder.append(args[i++]) + templateStart = placeholderStart + 2 + } + builder.append(templ.substring(templateStart)) + + // if we run out of placeholders, append the extra args in square braces + if (i < args.size) { + builder.append(" [") + builder.append(args[i++]) + while (i < args.size) { + builder.append(", ") + builder.append(args[i++]) + } + builder.append(']') + } + return builder.toString() + } +} \ No newline at end of file diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt new file mode 100644 index 0000000..ee379a4 --- /dev/null +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt @@ -0,0 +1,28 @@ +package com.atiurin.ultron.custom.espresso.base + +import org.hamcrest.Matcher + +fun filter(iterable: Iterable, matcher: Matcher): Iterable { + return iterable.filter { matcher.matches(it) } +} + +fun filterToList(iterable: Iterable, matcher: Matcher): List { + return filter(iterable, matcher).toList() +} + +fun joinToString(iterable: Iterable, delimiter: String): String { + return iterable.joinToString(separator = delimiter) +} + +fun toArray(iterator: Iterator, clazz: Class): Array { + val arrayList = ArrayList() + while (iterator.hasNext()) { + arrayList.add(iterator.next()) + } + return arrayList.toArray( + java.lang.reflect.Array.newInstance( + clazz, + arrayList.size + ) as Array + ) +} \ No newline at end of file diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/UltronViewFinder.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/UltronViewFinder.kt index cc4d723..e74a824 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/UltronViewFinder.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/UltronViewFinder.kt @@ -1,6 +1,5 @@ package com.atiurin.ultron.custom.espresso.base -import android.os.Looper import android.view.View import android.widget.AdapterView import androidx.test.espresso.AmbiguousViewMatcherException @@ -9,8 +8,7 @@ import androidx.test.espresso.DataInteraction import androidx.test.espresso.Espresso.onView import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.espresso.util.EspressoOptional +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.util.TreeIterables import com.atiurin.ultron.core.espresso.UltronEspressoInteraction import com.atiurin.ultron.custom.espresso.action.CustomEspressoActionType.GET_VIEW_FORCIBLY @@ -19,12 +17,6 @@ import com.atiurin.ultron.extensions.getTargetMatcher import com.atiurin.ultron.extensions.getViewMatcher import com.atiurin.ultron.extensions.simpleClassName import com.atiurin.ultron.utils.runOnUiThread -import com.google.common.base.Joiner -import com.google.common.base.Preconditions -import com.google.common.base.Predicate -import com.google.common.collect.Iterables -import com.google.common.collect.Iterators -import com.google.common.collect.Lists import org.hamcrest.Matcher import java.util.Locale import java.util.concurrent.atomic.AtomicReference @@ -34,8 +26,10 @@ class UltronViewFinder(val interaction: T) { private val viewMatcher: Matcher = when (interaction) { is ViewInteraction -> interaction.getViewMatcher() ?: throw NullPointerException("Matcher is null") + is DataInteraction -> interaction.getTargetMatcher() ?: throw NullPointerException("Matcher is null") + else -> throw UltronException("Unknown type of interaction provided!") } private val root: View by lazy { @@ -51,13 +45,11 @@ class UltronViewFinder(val interaction: T) { @Throws(AmbiguousViewMatcherException::class, NoMatchingViewException::class) fun fetchView(): View { - checkMainThread() - val matcherPredicate: Predicate = - MatcherPredicateAdapter(Preconditions.checkNotNull(viewMatcher)) - val matchedViewIterator: Iterator = - Iterables.filter(TreeIterables.breadthFirstViewTraversal(root), matcherPredicate) - .iterator() + Checker.checkMainThread() + val matchedViewIterator = + filter(TreeIterables.breadthFirstViewTraversal(root), viewMatcher).iterator() var matchedView: View? = null + while (matchedViewIterator.hasNext()) { if (matchedView != null) { // Ambiguous! @@ -67,7 +59,7 @@ class UltronViewFinder(val interaction: T) { .withView1(matchedView) .withView2(matchedViewIterator.next()) .withOtherAmbiguousViews( - *Iterators.toArray( + *toArray( matchedViewIterator, View::class.java ) @@ -78,61 +70,38 @@ class UltronViewFinder(val interaction: T) { } } if (null == matchedView) { - val adapterViewPredicate: Predicate = - MatcherPredicateAdapter( - ViewMatchers.isAssignableFrom( - AdapterView::class.java - ) - ) - val adapterViews: List = Lists.newArrayList( - Iterables.filter( + val adapterViews = + filterToList( TreeIterables.breadthFirstViewTraversal(root), - adapterViewPredicate - ).iterator() - ) + isAssignableFrom(AdapterView::class.java) + ) + if (adapterViews.isEmpty()) { throw NoMatchingViewException.Builder() .withViewMatcher(viewMatcher) .withRootView(root) .build() } + val warning = String.format( Locale.ROOT, - "\n" - + "If the target view is not part of the view hierarchy, you may need to use" - + " Espresso.onData to load it from one of the following AdapterViews:%s", - Joiner.on("\n- ").join(adapterViews) + """ + If the target view is not part of the view hierarchy, you may need to use Espresso.onData to load it from one of the following AdapterViews:%s + """.trimIndent(), + joinToString(adapterViews, "\n- ") ) throw NoMatchingViewException.Builder() .withViewMatcher(viewMatcher) .withRootView(root) .withAdapterViews(adapterViews) - .withAdapterViewWarning(EspressoOptional.of(warning)) + .withAdapterViewWarning(warning) .build() } else { return matchedView } } - private fun checkMainThread() { - Preconditions.checkState( - (Thread.currentThread() == Looper.getMainLooper().thread), - "Executing a query on the view hierarchy outside of the main thread (on: %s)", - Thread.currentThread().name - ) - } - - private class MatcherPredicateAdapter(matcher: Matcher) : Predicate { - private val matcher: Matcher - init { - this.matcher = Preconditions.checkNotNull(matcher) - } - - override fun apply(input: T): Boolean { - return matcher.matches(input) - } - } } From 51eb1d3d5a86f320646e5c14ddceead803aa2f3f Mon Sep 17 00:00:00 2001 From: "so.potapov" Date: Wed, 2 Oct 2024 08:26:49 +0300 Subject: [PATCH 2/2] fix comments --- .../com/atiurin/ultron/custom/espresso/base/Checker.kt | 2 +- .../atiurin/ultron/custom/espresso/base/IterableUtils.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt index 2c8711e..1ac5938 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/Checker.kt @@ -3,7 +3,7 @@ package com.atiurin.ultron.custom.espresso.base import android.os.Looper -object Checker { +internal object Checker { fun checkMainThread() { checkState( Thread.currentThread() == Looper.getMainLooper().thread, diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt index ee379a4..8fd640a 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/custom/espresso/base/IterableUtils.kt @@ -2,19 +2,19 @@ package com.atiurin.ultron.custom.espresso.base import org.hamcrest.Matcher -fun filter(iterable: Iterable, matcher: Matcher): Iterable { +internal fun filter(iterable: Iterable, matcher: Matcher): Iterable { return iterable.filter { matcher.matches(it) } } -fun filterToList(iterable: Iterable, matcher: Matcher): List { +internal fun filterToList(iterable: Iterable, matcher: Matcher): List { return filter(iterable, matcher).toList() } -fun joinToString(iterable: Iterable, delimiter: String): String { +internal fun joinToString(iterable: Iterable, delimiter: String): String { return iterable.joinToString(separator = delimiter) } -fun toArray(iterator: Iterator, clazz: Class): Array { +internal fun toArray(iterator: Iterator, clazz: Class): Array { val arrayList = ArrayList() while (iterator.hasNext()) { arrayList.add(iterator.next())