I try to run flaky test with espresso framework (and with Junit4) on Android Studio.
I want to set how many times it should to repeat.
Before I can use
#FlakyTest(tolerance=5)
// (5 is number for repeat, for example)
But this annotation was deprecated in API level 24. - (link on android.developers.com)
Now is availible new #FlakyTest annotation - without tolerance variable. (link on android.developers.com)
I need to set how many times test can be repeated, but don't know how to do it. Any idea?
This annotation has been deprecated because the entire testing framework was replaced by a new one. Thus, the annotation has been also deprecated in favor of a new one.
Unfortunately, comparing to the old annotation this one can not be used to re-run a failed test. That makes it less useful for a practical purpose.
However, you can still use it for something useful. As the documentation says when running tests you can filter out those that are flaky. To do so you need to adjust a build script:
android {
defaultConfig {
testInstrumentationRunnerArgument "notAnnotation", "android.support.test.filters.FlakyTest"
}
}
More about options can be found here.
I found this online https://gist.github.com/abyx/897229#gistcomment-2851489
You create your own test rule
class RetryTestRule(val retryCount: Int = 3) : TestRule {
private val TAG = RetryTestRule::class.java.simpleName
override fun apply(base: Statement, description: Description): Statement {
return statement(base, description)
}
private fun statement(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
Log.e(TAG, "Evaluating ${description.methodName}")
var caughtThrowable: Throwable? = null
for (i in 0 until retryCount) {
try {
base.evaluate()
return
} catch (t: Throwable) {
caughtThrowable = t
Log.e(TAG, description.methodName + ": run " + (i + 1) + " failed")
}
}
Log.e(TAG, description.methodName + ": giving up after " + retryCount + " failures")
if (caughtThrowable != null)
throw caughtThrowable
}
}
}
}
and then add it to your test, like this
#Rule
#JvmField
val mRetryTestRule = RetryTestRule()
Related
I want upgrade kotlin gradle plugin from 1.4.32 to 1.5.20,but some code occurred error in lower android version device,such as Xiaomi 5.1.1 & Oppo 6.0.1 & Pixel2 6.0, but it normal in Android 10 devices.
The error info:
java.lang.IncompatibleClassChangeError: Couldn't find com.example.kotlinupgradedemo.ProgressDialogKt.<clinit>[]
at libcore.reflect.AnnotationAccess.indexToMethod(AnnotationAccess.java:608)
at libcore.reflect.AnnotationAccess.getEnclosingMethodOrConstructor(AnnotationAccess.java:405)
at java.lang.Class.isLocalClass(Class.java:1334)
at java.lang.Class.getCanonicalName(Class.java:378)
at androidx.lifecycle.Lifecycling.resolveObserverCallbackType(Lifecycling.java:153)
at androidx.lifecycle.Lifecycling.getObserverConstructorType(Lifecycling.java:146)
at androidx.lifecycle.Lifecycling.lifecycleEventObserver(Lifecycling.java:83)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.<init>(LifecycleRegistry.java:347)
at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:174)
at com.example.kotlinupgradedemo.ProgressDialogKt.showProgress(ProgressDialog.kt:36)
at com.example.kotlinupgradedemo.MainActivity.onCreate(MainActivity.kt:12)
My some code(ProgressDialog.kt):
private val ownerToProgressMap = mutableMapOf<LifecycleOwner, Dialog>()
private val progressCleaner = object : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy(owner : LifecycleOwner) {
ownerToProgressMap.remove(owner)?.dismiss()
owner.lifecycle.removeObserver(this)
}
}
fun LifecycleOwner.showProgress() {
val context = when (this) {
is Activity -> this
is Fragment -> this.context
else -> null
} ?: return
ownerToProgressMap[this]
?.apply { show() }
?: Dialog(context).also {
it.setTitle("Tips")
it.show()
}.let {
ownerToProgressMap[this] = it
this.lifecycle.addObserver(progressCleaner)
}
}
fun LifecycleOwner.dismissProgress() {
ownerToProgressMap[this]?.dismiss()
}
I just call it in MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showProgress()
}
}
For the complete code, see demo
Take a look to AnnotationAccess.java.
Especially lines
try {
return name.equals("<init>")
? declaringClass.getDeclaredConstructor(parametersArray)
: declaringClass.getDeclaredMethod(name, parametersArray);
} catch (NoSuchMethodException e) {
throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName()
+ "." + name + Arrays.toString(parametersArray));
}
Then look to Class::getDeclaredMethod documentation.
Returns a Method object that reflects the specified declared method of the class or interface represented by this Class object. ... If the name is "<init>"or "<clinit>" a NoSuchMethodException is raised.
Kotlin global property and companion object property initialization compiles to Java static initialization block aka <clinit>.
Puzzle complete. Move LifecycleObserver declaration from static property somewhere else and problem will be solved.
Why Android 5 and 6 only? I think it's just getDeclaredMethod implementation differences. We see crashes on Android 5 and 6 only too.
Basically I have to make a network request using OkHttp in parallel to various addresses. I only care about the result of the first one that succeeds. Can I do this with Flow on Kotlin?
I've been looking around but I'm struggling with getting the requests to run in parallel, the always run in sequence.
The code basically takes a list of addresses and should return the only address that worked or null if none worked.
Thanks.
Edit: I should mention I plan on using this on Android. I can probably do it with RX but wanted to learn Flow. Also trying to limit the libraries I add to the app.
Edit: I have marked an answer as correct however that isn't how I did but it took me very close to how I did it but since I'm new to Flow I have no idea if how I did it is correct though I'm pretty sure it works after my testing.
I have a function that throws NoSuchElementException when not found. It calls searchForIPAsync which is a suspend function that does all the OkHttp work and returns true|false.
#Throws(NoSuchElementException::class)
private suspend fun findWorkingIP(ipsToTest: MutableList<String>): String? = ipsToTest
.asFlow()
.flatMapMerge(ipsToTest.size)
{ impl ->
flow<String?> {
val res = connectionHelper.searchForIPAsync(getURLToTest(impl))
if (res) {
emit(impl)
} else {
}
}
}.first()
Then I call this and catch the exception in case nothing is found:
try {
val ipFound = findWorkingIP(ipsToTest)
Log.w(TAG, "find: Got something " + ipFound);
return ipFound
} catch (ex: NoSuchElementException) {
Log.w(TAG, "find: not found");
}
Although the Flow-based solution in another answer is a close match to what you need, unfortunately as of Kotlin 1.3.2 the Flow implementation has a bug that breaks it. The bug already has a proposed fix so this should be resolved with the next patch release of Kotlin. In the meantime, here's a similar solution that uses async and Channel instead:
suspend fun getShortUrl(urls: List<String>): String = coroutineScope {
val chan = Channel<String?>()
urls.forEach { url ->
launch {
try {
fetchUrl(url)
} catch (e: Exception) {
null
}.also { chan.send(it) }
}
}
try {
(1..urls.size).forEach { _ ->
chan.receive()?.also { return#coroutineScope it }
}
throw Exception("All services failed")
} finally {
coroutineContext[Job]!!.cancelChildren()
}
}
I'm playing with Kotlin and found interesting behavior.
So lets say i want to have some kind of a Factory :
internal interface SomeStupidInterface {
companion object FACTORY {
fun createNew(): ChangeListener {
val time = System.currentTimeMillis()
return ChangeListener { element -> Log.e("J2KO", "time " + time) }
}
fun createTheSame(): ChangeListener {
return ChangeListener { element -> Log.e("J2KO", "time " + System.currentTimeMillis()) }
}
}
fun notifyChanged()
}
where ChangeListener defined in java file:
interface ChangeListener {
void notifyChange(Object element);
}
And then I try to use it from Java like so:
ChangeListener a = SomeStupidInterface.FACTORY.createNew();
ChangeListener b = SomeStupidInterface.FACTORY.createNew();
ChangeListener c = SomeStupidInterface.FACTORY.createTheSame();
ChangeListener d = SomeStupidInterface.FACTORY.createTheSame();
Log.e("J2KO", "createNew a == b -> " + (a == b));
Log.e("J2KO", "createTheSame c == d -> " + (c == d));
The results are:
createNew: a == b -> false
createTheSame: c == d -> true
I can understand why createNew returns new objects due to closure.
But why I'm receiving the same instance from createTheSame method?
P.S. I know that code above is not idiomatic :)
This has to do with performance. Creating less objects obviously is better for performance, so that is what Kotlin tries to do.
For each lambda, Kotlin generates a class that implements the proper interface. So for example the following Kotlin code:
fun create() : () -> Unit {
return { println("Hello, World!") }
}
corresponds with something like:
Function0 create() {
return create$1.INSTANCE;
}
final class create$1 implements Function0 {
static final create$1 INSTANCE = new create$1();
void invoke() {
System.out.println("Hello, World!");
}
}
You can see here that the same instance is always returned.
If you reference a variable that is outside of the lamdba scope however, this won't work: there is no way for the singleton instance to access that variable.
fun create(text: String) : () -> Unit {
return { println(text) }
}
Instead, for each invocation of create, a new instance of the class needs to be instantiated which has access to the text variable:
Function0 create(String text) {
return new create$1(text);
}
final class create$1 implements Function0 {
final String text;
create$1(String text) {
this.text = text;
}
void invoke() {
System.out.println(text);
}
}
That is why your a and b instances are the same, but c and d are not.
First note: your example code doesn't work as is: the interface has to be written in Java in order to be available for use with SAM constructors.
As for the actual question, you've already touched on why this behavior is happening. Lambdas (in this case, the SAM constructors) are compiled to anonymous classes (unless they're inlined). If they capture any outside variables, then for every invocation, a new instance of the anonymous class will be created. Otherwise, since they don't have to have any state, only a single instance will back every invocation of the lambda. I suppose this is for performance reasons, if nothing else. (Credit to the Kotlin in Action book for the information in this paragraph.)
If you want to return a new instance every time without capturing any variables, you can use the full object notation:
fun createNotQUiteTheSame(): ChangeListener {
return object : ChangeListener {
override fun notifyChanged(element: Any?) {
println("time " + System.currentTimeMillis())
}
}
}
Calling the above function multiple times will return different instances for each call. Interestingly, IntelliJ will suggest converting this to the original SAM conversion syntax instead:
fun createNotQUiteTheSame(): ChangeListener {
return ChangeListener { println("time " + System.currentTimeMillis()) }
}
Which, as you've already found out, returns the same instance every time.
I suppose this conversion is offered because comparing whether these stateless instances are equal is very much an edge case. If you need to be able to do comparison between the instances that are returned, you're probably best off with the full object notation. Then you can even add some additional state to each listener, in the form of an id for example.
it looks like you try to use SAM conversion with Kotlin interface.
Note that SAM conversions only work for interfaces, not for abstract classes, even if those also have just a single abstract method.
Also note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.
For implementing interface like you want, you need to use object expression.
Also look at high order functions - I think you need them for your solution.
internal interface SomeStupidInterface {
interface ChangeListener {
fun notifyChanged(element: Any)
}
companion object FACTORY {
fun createNew(): ChangeListener {
val time = System.currentTimeMillis()
return object : ChangeListener {
override fun notifyChanged(element: Any) {
println("J2KO" + "time " + time)
}
}
}
fun createTheSame(): ChangeListener {
return object : ChangeListener {
override fun notifyChanged(element: Any) {
println("J2KO" + "time " + System.currentTimeMillis())
}
}
}
}
fun notifyChanged()
}
Also In IntelliJ IDEA I can't compile your code.
I'm developing a Kotlin Android app and I'm having an issue integrating Google Sign In, when I get the GoogleSignInAccount in order to extract properties, the property names seem to be obfuscated (or otherwise jumbled up), here's a screenshot of the way the properties look on AS 2.3 debugger:
Here's the snippet of code that tries to access those properties:
private fun googleSignInResult(data : GoogleSignInResult) {
if (data.isSuccess) {
if (data.signInAccount != null) {
val account = data.signInAccount
val authData = HashMap<String, String>()
authData["id_token"] = account?.idToken.let { it } ?: return
authData["id"] = account?.id.let { it } ?: return
val task = ParseUser.logInWithInBackground("google", authData)
task.continueWith { user ->
if (task.isCancelled) {
Log.d(TAG, "User cancelled Google login")
} else if (task.isFaulted) {
Log.d(TAG, "Failed: " + task.error)
} else {
this.user = task.result
this.user?.put("email", account?.email)
this.user?.put("name", account?.displayName)
this.user?.put("firstName", account?.displayName)
this.user?.saveInBackground({ error ->
if(error != null) {
Log.d(TAG, "Error: " + error.message)
this.user?.deleteInBackground()
ParseUser.logOutInBackground()
} else {
//Logged in successfully
}
})
}
}
}
}
}
Can anyone shed some light on why is it that properties look like that?, when I try to access idToken or id they're always null, however, the property names that are "obfuscated" can't be accessed, is this a kotlin bug or is it my error?
Any help will be much appreciated!
The following content is originally posted by #EugenPechanec as a comment to the question body. Some modification is applied to promote reading experience.
Fields, a term from JVM, is not properties, which is a Kotlin term. You're on JVM, and what you see in the debugger are obfuscated fields backing the Kotlin properties. The getters are public and retain original names. Java .getDisplayName() is .displayName in Kotlin.
Kotlin extension function is great. But how could I perform unit test on them? Especially those that is of Android SDK provided class (e.g. Context, Dialog).
I provide two examples below, and if anyone could share how I could unit test them, or if I need to write them differently if I really want to unit test them.
fun Context.getColorById(colorId: Int): Int {
if (Build.VERSION.SDK_INT >= 23)
return ContextCompat.getColor(this, colorId)
else return resources.getColor(colorId)
}
and
fun Dialog.setupErrorDialog(body : String, onOkFunc: () -> Unit = {}): Dialog {
window.requestFeature(Window.FEATURE_NO_TITLE)
this.setContentView(R.layout.dialog_error_layout)
(findViewById(R.id.txt_body) as TextView).text = body
(findViewById(R.id.txt_header) as TextView).text = context.getString(R.string.dialog_title_error)
(findViewById(R.id.txt_okay)).setOnClickListener{
onOkFunc()
dismiss()
}
return this
}
Any suggestion would help. Thanks!
The way I'm testing extension functions on Android classes at the moment is by mocking the Android class. I know, this is not an optimal solution as it mocks the class under test and requires certain knowledge about how the function works (as it is always the case when mocking), but as extension functions are internally implemented as static functions I guess it's acceptable until someone comes up with something better.
As an example consider the JsonArray class. We've defined an extension function for receiving the last item's index:
fun JSONArray.lastIndex() = length() - 1
The according test (using the Spek test framework and mockito-kotlin) looks like this.
#RunWith(JUnitPlatform::class)
object JsonExtensionTestSpec : Spek({
given("a JSON array with three entries") {
val jsonArray = mock<JSONArray> {
on { length() } doReturn 3
}
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be 2") {
lastIndex shouldBe 2
}
}
}
given("a JSON array with no entries") {
val jsonArray = mock<JSONArray>({
on { length() } doReturn 0
})
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be -1") {
lastIndex shouldBe -1
}
}
}
})
The difficulty with your functions is, that they also use Android classes internally. Unfortunately I don't have a solution for this right now.