diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 27c4998d8..f4d291703 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -131,6 +131,7 @@ dependencies { androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.rules) androidTestImplementation(libs.androidx.uiautomator) + androidTestImplementation(libs.camera.lifecycle) // to reset CameraX between tests androidTestImplementation(libs.truth) androidTestImplementation(project(":ui:components:capture")) androidTestUtil(libs.androidx.orchestrator) diff --git a/app/src/androidTest/java/com/google/jetpackcamera/SingleLensModeTest.kt b/app/src/androidTest/java/com/google/jetpackcamera/SingleLensModeTest.kt index 9cf3c220c..004dec830 100644 --- a/app/src/androidTest/java/com/google/jetpackcamera/SingleLensModeTest.kt +++ b/app/src/androidTest/java/com/google/jetpackcamera/SingleLensModeTest.kt @@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import com.google.common.truth.TruthJUnit.assume import com.google.jetpackcamera.ui.components.capture.FLIP_CAMERA_BUTTON +import com.google.jetpackcamera.utils.CameraXResetRule import com.google.jetpackcamera.utils.TEST_REQUIRED_PERMISSIONS import com.google.jetpackcamera.utils.isEmulatorWithFakeFrontCamera import com.google.jetpackcamera.utils.runMainActivityScenarioTest @@ -35,6 +36,9 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) class SingleLensModeTest(private val lensFacing: String) { + @get:Rule + val cameraXResetRule = CameraXResetRule() + @get:Rule val permissionsRule: GrantPermissionRule = GrantPermissionRule.grant(*(TEST_REQUIRED_PERMISSIONS).toTypedArray()) diff --git a/app/src/androidTest/java/com/google/jetpackcamera/utils/AppTestUtil.kt b/app/src/androidTest/java/com/google/jetpackcamera/utils/AppTestUtil.kt index 5dbf4f054..4307b04e6 100644 --- a/app/src/androidTest/java/com/google/jetpackcamera/utils/AppTestUtil.kt +++ b/app/src/androidTest/java/com/google/jetpackcamera/utils/AppTestUtil.kt @@ -24,13 +24,53 @@ import android.os.Environment import android.provider.BaseColumns import android.provider.MediaStore import android.util.Log +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.test.platform.app.InstrumentationRegistry import java.io.File +import java.util.concurrent.TimeUnit import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.transform +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement private const val TAG = "AppTestUtil" +private const val CAMERAX_RESET_TIMEOUT_SECONDS = 10L + +/** + * A [TestRule] that resets CameraX before each test. + * + * This is useful for tests that need to configure CameraX (e.g. using + * [ProcessCameraProvider.configureInstance]), which can only be done once per process. + */ +class CameraXResetRule : TestRule { + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + override fun evaluate() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + initializeAndShutdown(context) + try { + base.evaluate() + } finally { + initializeAndShutdown(context) + } + } + } + } + + private fun initializeAndShutdown(context: android.content.Context) { + try { + ProcessCameraProvider.getInstance(context) + .get(CAMERAX_RESET_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .shutdownAsync() + .get(CAMERAX_RESET_TIMEOUT_SECONDS, TimeUnit.SECONDS) + } catch (e: Exception) { + Log.w(TAG, "Failed to shutdown CameraX", e) + } + } +} internal val APP_REQUIRED_PERMISSIONS: List = buildList { add(android.Manifest.permission.CAMERA)