From 26e389501be4622667a715ed5de21497da5bd852 Mon Sep 17 00:00:00 2001
From: Amine <amine.bouabdallaoui@pm.me>
Date: Tue, 25 Mar 2025 13:36:35 +0100
Subject: [PATCH] ci: Instrumentation tests coverage in ci.

---
 .gitea/workflows/on_pr.yml                    |  2 +-
 .../android/1-LoginActivityTest.kt            |  1 +
 .../readerforselfossv2/android/Helpers.kt     | 40 +++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/.gitea/workflows/on_pr.yml b/.gitea/workflows/on_pr.yml
index 57a1b31..3b46f4c 100644
--- a/.gitea/workflows/on_pr.yml
+++ b/.gitea/workflows/on_pr.yml
@@ -48,7 +48,7 @@ jobs:
             adb logcat -G 16M
             ./gradlew JacocoDebugCodeCoverage || true
             ./gradlew androidApp:fetchScreenshots
-            adb logcat 'InputReader:S' 'chatty:S' 'audio_hw_generic:S' '*:I' -d > ./androidApp/build/reports/androidTests/connected/screenshots/logs.txt
+            adb logcat 'InputReader:S' 'chatty:S' 'audio_hw_generic:S' 'LogApiCalls:D' '*:I' -d > ./androidApp/build/reports/androidTests/connected/screenshots/logs.txt
       - uses: actions/upload-artifact@v3
         if: always()
         with:
diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/1-LoginActivityTest.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/1-LoginActivityTest.kt
index 2dea4a4..4542e8b 100644
--- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/1-LoginActivityTest.kt
+++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/1-LoginActivityTest.kt
@@ -93,5 +93,6 @@ class `1-LoginActivityTest` : WithANRException() {
         performLogin()
         onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
         onView(withText("OK")).perform(click())
+        onView(withId(R.id.swipeRefreshLayout)).perform(waitUntilNotLoading(300000))
     }
 }
diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/Helpers.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/Helpers.kt
index 207afcf..68ebd38 100644
--- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/Helpers.kt
+++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/Helpers.kt
@@ -8,9 +8,13 @@ import android.widget.RelativeLayout
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import androidx.core.graphics.drawable.toBitmap
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
+import androidx.test.espresso.PerformException
 import androidx.test.espresso.Root
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
 import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
 import androidx.test.espresso.matcher.ViewMatchers.hasSibling
 import androidx.test.espresso.matcher.ViewMatchers.withChild
@@ -19,11 +23,14 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
 import androidx.test.espresso.matcher.ViewMatchers.withParent
 import androidx.test.espresso.matcher.ViewMatchers.withResourceName
 import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.espresso.util.HumanReadables
 import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.any
 import org.hamcrest.Description
 import org.hamcrest.Matcher
 import org.hamcrest.Matchers
 import org.hamcrest.TypeSafeMatcher
+import java.util.concurrent.TimeoutException
 
 fun withError(
     @StringRes id: Int,
@@ -44,6 +51,39 @@ fun withError(
     }
 }
 
+fun waitUntilNotLoading(millis: Long): ViewAction {
+    return object : ViewAction {
+        override fun getConstraints(): Matcher<View> = any(View::class.java)
+
+        override fun getDescription(): String = "wait for a specific view is hidden during $millis millis."
+
+        override fun perform(
+            uiController: UiController,
+            view: View?,
+        ) {
+            uiController.loopMainThreadUntilIdle()
+            val startTime = System.currentTimeMillis()
+            val endTime = startTime + millis
+
+            do {
+                // found view with required ID
+                if (view is SwipeRefreshLayout && !view.isRefreshing) {
+                    return
+                }
+                uiController.loopMainThreadForAtLeast(100)
+            } while (System.currentTimeMillis() < endTime)
+
+            // timeout happens
+            throw PerformException
+                .Builder()
+                .withActionDescription(this.description)
+                .withViewDescription(HumanReadables.describe(view))
+                .withCause(TimeoutException())
+                .build()
+        }
+    }
+}
+
 fun isPopupWindow(): Matcher<Root> = isPlatformPopup()
 
 fun withDrawable(