Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Removed guava lib #90

Merged
merged 2 commits into from
Nov 30, 2024
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
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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" }
Expand Down
1 change: 0 additions & 1 deletion ultron-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.atiurin.ultron.custom.espresso.base

import android.os.Looper


internal 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()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.atiurin.ultron.custom.espresso.base

import org.hamcrest.Matcher

internal fun <T> filter(iterable: Iterable<T>, matcher: Matcher<T>): Iterable<T> {
return iterable.filter { matcher.matches(it) }
}

internal fun <T> filterToList(iterable: Iterable<T>, matcher: Matcher<T>): List<T> {
return filter(iterable, matcher).toList()
}

internal fun joinToString(iterable: Iterable<Any>, delimiter: String): String {
return iterable.joinToString(separator = delimiter)
}

internal fun <T> toArray(iterator: Iterator<T>, clazz: Class<T>): Array<T> {
val arrayList = ArrayList<T>()
while (iterator.hasNext()) {
arrayList.add(iterator.next())
}
return arrayList.toArray(
java.lang.reflect.Array.newInstance(
clazz,
arrayList.size
) as Array<T>
)
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -34,8 +26,10 @@ class UltronViewFinder<T>(val interaction: T) {
private val viewMatcher: Matcher<View> = when (interaction) {
is ViewInteraction -> interaction.getViewMatcher()
?: throw NullPointerException("Matcher<View> is null")

is DataInteraction -> interaction.getTargetMatcher()
?: throw NullPointerException("Matcher<View> is null")

else -> throw UltronException("Unknown type of interaction provided!")
}
private val root: View by lazy {
Expand All @@ -51,13 +45,11 @@ class UltronViewFinder<T>(val interaction: T) {

@Throws(AmbiguousViewMatcherException::class, NoMatchingViewException::class)
fun fetchView(): View {
checkMainThread()
val matcherPredicate: Predicate<View> =
MatcherPredicateAdapter(Preconditions.checkNotNull(viewMatcher))
val matchedViewIterator: Iterator<View> =
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!
Expand All @@ -67,7 +59,7 @@ class UltronViewFinder<T>(val interaction: T) {
.withView1(matchedView)
.withView2(matchedViewIterator.next())
.withOtherAmbiguousViews(
*Iterators.toArray(
*toArray(
matchedViewIterator,
View::class.java
)
Expand All @@ -78,61 +70,38 @@ class UltronViewFinder<T>(val interaction: T) {
}
}
if (null == matchedView) {
val adapterViewPredicate: Predicate<View> =
MatcherPredicateAdapter(
ViewMatchers.isAssignableFrom(
AdapterView::class.java
)
)
val adapterViews: List<View> = 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<T>(matcher: Matcher<in T>) : Predicate<T> {
private val matcher: Matcher<in T>

init {
this.matcher = Preconditions.checkNotNull(matcher)
}

override fun apply(input: T): Boolean {
return matcher.matches(input)
}
}
}


Expand Down