diff --git a/ci/android/src/androidTest/kotlin/rs/pushups/ci/PushupsTest.kt b/ci/android/src/androidTest/kotlin/rs/pushups/ci/PushupsTest.kt index 611aab0..d25e44d 100644 --- a/ci/android/src/androidTest/kotlin/rs/pushups/ci/PushupsTest.kt +++ b/ci/android/src/androidTest/kotlin/rs/pushups/ci/PushupsTest.kt @@ -1,8 +1,12 @@ package rs.pushups.ci import android.app.Activity +import android.content.BroadcastReceiver +import android.content.ComponentName import android.content.Context +import android.content.Intent import android.os.Build +import android.os.ParcelFileDescriptor import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry @@ -26,6 +30,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import java.util.UUID +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import rs.pushups.PushupsMessagingService @@ -50,9 +55,37 @@ class PushupsTest { @BeforeClass @JvmStatic fun fetchToken() { + leaveStoppedState() token = Tasks.await(FirebaseMessaging.getInstance().token, 120, TimeUnit.SECONDS) } + /** + * Takes the package out of the stopped state `am instrument` leaves it in, where FCM holds + * its pushes until an Activity starts, through a broadcast to a manifest receiver, which + * creates no Activity, so the first push still finds a process that never had one. + */ + private fun leaveStoppedState() { + val delivered = CountDownLatch(1) + context.sendOrderedBroadcast( + Intent().setComponent(ComponentName(context, UnstoppedReceiver::class.java)), + null, + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) = delivered.countDown() + }, + null, + Activity.RESULT_OK, + null, + null, + ) + check(delivered.await(30, TimeUnit.SECONDS)) { "the receiver got no broadcast within 30 s" } + val state = instrumentation.uiAutomation + .executeShellCommand("dumpsys package ${context.packageName}") + .let { descriptor -> + ParcelFileDescriptor.AutoCloseInputStream(descriptor).bufferedReader().use { it.readText() } + } + check(" stopped=false" in state) { "the package is still stopped:\n$state" } + } + @AfterClass @JvmStatic fun finish() { diff --git a/ci/android/src/androidTest/kotlin/rs/pushups/ci/UnstoppedReceiver.kt b/ci/android/src/androidTest/kotlin/rs/pushups/ci/UnstoppedReceiver.kt new file mode 100644 index 0000000..9e419af --- /dev/null +++ b/ci/android/src/androidTest/kotlin/rs/pushups/ci/UnstoppedReceiver.kt @@ -0,0 +1,14 @@ +package rs.pushups.ci + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent + +/** + * The target of an explicit broadcast that takes the package out of the stopped state + * `am instrument` leaves it in, since delivering to a manifest receiver clears that state + * without creating an Activity. + */ +class UnstoppedReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) = Unit +} diff --git a/ci/android/src/androidTest/manifests/default/AndroidManifest.xml b/ci/android/src/androidTest/manifests/default/AndroidManifest.xml index 837a84a..395f9cc 100644 --- a/ci/android/src/androidTest/manifests/default/AndroidManifest.xml +++ b/ci/android/src/androidTest/manifests/default/AndroidManifest.xml @@ -16,5 +16,10 @@ + + +