1 /* <lambda>null2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.testing.impl 18 19 import android.util.Log 20 import androidx.camera.testing.impl.CameraPipeConfigTestRule.Companion.CAMERA_PIPE_TEST_FLAG 21 import org.junit.AssumptionViolatedException 22 import org.junit.rules.TestRule 23 import org.junit.runner.Description 24 import org.junit.runners.model.Statement 25 26 /** 27 * A [TestRule] to convert test failures into AssumptionViolatedException 28 * 29 * All the test methods will be executed as usual when [active] is set to false. 30 * 31 * When the [active] is true and DUT doesn't set the debug [CAMERA_PIPE_TEST_FLAG], all the test 32 * failures will be converted to AssumptionViolatedException. Otherwise, test failures will be shown 33 * as usual. 34 * 35 * The [CAMERA_PIPE_TEST_FLAG] can be enabled on the DUT by the command: 36 * ``` 37 * adb shell setprop log.tag.CAMERA_PIPE_TESTING DEBUG 38 * ``` 39 * 40 * To apply the [TestRule] , please create the [CameraPipeConfigTestRule] directly. For Camera-Pipe 41 * related tests, please set [active] to true. 42 * 43 * ``` 44 * @get:Rule 45 * val testRule: CameraPipeConfigTestRule = CameraPipeConfigTestRule( 46 * active = true 47 * ) 48 * ``` 49 * 50 * @property active true to activate this rule. 51 */ 52 public class CameraPipeConfigTestRule( 53 public val active: Boolean, 54 ) : TestRule { 55 56 override fun apply(base: Statement, description: Description): Statement = 57 object : Statement() { 58 private var standardHandler: Thread.UncaughtExceptionHandler? = null 59 60 override fun evaluate() { 61 if (active) { 62 if (Log.isLoggable(CAMERA_PIPE_MH_FLAG, Log.DEBUG)) { 63 testInPipeLab() 64 } else { 65 testNotInPipeLab() 66 } 67 } else { 68 if (Log.isLoggable(CAMERA2_TEST_DISABLE, Log.DEBUG)) { 69 throw AssumptionViolatedException( 70 "Ignore Camera2 tests since CAMERA2_TEST_DISABLE flag is turned on." 71 ) 72 } 73 base.evaluate() 74 } 75 } 76 77 private fun testInPipeLab() { 78 try { 79 log("started: ${description.displayName}") 80 logUncaughtExceptions() 81 base.evaluate() 82 } catch (e: AssumptionViolatedException) { 83 log("AssumptionViolatedException: ${description.displayName}", e) 84 handleException(e) 85 } catch (e: Throwable) { 86 log("failed: ${description.displayName}", e) 87 handleException(e) 88 } finally { 89 restoreUncaughtExceptionHandler() 90 log("finished: ${description.displayName}") 91 } 92 } 93 94 private fun testNotInPipeLab() { 95 if (testInAllowList() && !Log.isLoggable(CAMERA_MH_FLAG, Log.DEBUG)) { 96 // Run the test when (1) In the allow list && (2) It is not MH daily test. 97 base.evaluate() 98 } else { 99 throw AssumptionViolatedException( 100 "Ignore Camera-pipe tests since there's no debug flag" 101 ) 102 } 103 } 104 105 private fun testInAllowList() = 106 allowPresubmitTests.any { description.displayName.contains(it, ignoreCase = true) } 107 108 private fun handleException(e: Throwable) { 109 if (Log.isLoggable(CAMERA_PIPE_TEST_FLAG, Log.DEBUG)) { 110 throw e 111 } else { 112 throw AssumptionViolatedException("CameraPipeTestFailure", e) 113 } 114 } 115 116 private fun logUncaughtExceptions() { 117 standardHandler = Thread.getDefaultUncaughtExceptionHandler() 118 Thread.setDefaultUncaughtExceptionHandler { _, e -> 119 log("Invoking uncaught exception handler: ${description.displayName}", e) 120 handleException(e) 121 } 122 } 123 124 private fun restoreUncaughtExceptionHandler() { 125 Thread.setDefaultUncaughtExceptionHandler(standardHandler) 126 } 127 } 128 129 private companion object { 130 private const val CAMERA2_TEST_DISABLE = "CAMERA2_TEST_DISABLE" 131 private const val CAMERA_PIPE_TEST_FLAG = "CAMERA_PIPE_TESTING" 132 private const val CAMERA_PIPE_MH_FLAG = "CameraPipeMH" 133 private const val CAMERA_MH_FLAG = "MH" 134 private const val LOG_TAG = "CameraPipeTest" 135 136 private val allowPresubmitTests = 137 listOf( 138 // CoreTestApps 139 "androidx.camera.integration.core.", 140 // Camera-View 141 "androidx.camera.view.", 142 // Camera-Video 143 "androidx.camera.video.", 144 // UIWidgets 145 "androidx.camera.integration.uiwidgets.", 146 ) 147 148 fun log(message: String, throwable: Throwable? = null) { 149 if (throwable != null) { 150 Log.e(LOG_TAG, message, throwable) 151 } else { 152 Log.i(LOG_TAG, message) 153 } 154 } 155 } 156 } 157