|
| 1 | +/** |
| 2 | + * ============================================================================ |
| 3 | + * FwStartInstrumentedTest.kt - Native startActivity 设备侧真实触发测试 |
| 4 | + * ============================================================================ |
| 5 | + * |
| 6 | + * 功能简介: |
| 7 | + * 在 Android 设备或模拟器上真实调用 C++ start 文件夹的统一入口, |
| 8 | + * 覆盖 application context、Activity context、只登记策略和异常入参。 |
| 9 | + * |
| 10 | + * 函数简介: |
| 11 | + * - startWithZeroMask_returnsInvalidArgument:验证空策略受控失败。 |
| 12 | + * - applicationContextStrategies_triggerNativeStart:验证应用上下文可执行策略。 |
| 13 | + * - activityContextStrategies_triggerNativeStart:验证 Activity 上下文可执行策略。 |
| 14 | + * - systemConstrainedStrategies_returnControlledResult:验证系统限制策略受控返回。 |
| 15 | + * - allStartStrategies_areCoveredByInstrumentedTests:验证所有策略均被测试覆盖。 |
| 16 | + * |
| 17 | + * @author Pangu-Immortal |
| 18 | + * @github https://github.com/Pangu-Immortal/KeepLiveService |
| 19 | + * @since 2.0.1 |
| 20 | + */ |
| 21 | +package com.google.services |
| 22 | + |
| 23 | +import android.app.Activity |
| 24 | +import android.content.Context |
| 25 | +import android.content.Intent |
| 26 | +import androidx.test.ext.junit.runners.AndroidJUnit4 |
| 27 | +import androidx.test.platform.app.InstrumentationRegistry |
| 28 | +import com.service.framework.start.FwStart |
| 29 | +import com.service.framework.start.FwStartResult |
| 30 | +import com.service.framework.start.FwStartStrategy |
| 31 | +import org.junit.After |
| 32 | +import org.junit.Assert.assertEquals |
| 33 | +import org.junit.Assert.assertNotEquals |
| 34 | +import org.junit.Assert.assertTrue |
| 35 | +import org.junit.Test |
| 36 | +import org.junit.runner.RunWith |
| 37 | + |
| 38 | +/** |
| 39 | + * Native startActivity 设备侧真实触发测试。 |
| 40 | + */ |
| 41 | +@RunWith(AndroidJUnit4::class) |
| 42 | +class FwStartInstrumentedTest { |
| 43 | + |
| 44 | + // application context 下稳定成功的公开策略。 |
| 45 | + private val applicationStableStrategies = listOf( |
| 46 | + FwStartStrategy.CONTEXT_NEW_TASK, |
| 47 | + FwStartStrategy.CONTEXT_NEW_TASK_EXCLUDE_RECENTS, |
| 48 | + FwStartStrategy.DOUBLE_START_ACTIVITIES |
| 49 | + ) |
| 50 | + |
| 51 | + // Activity context 下稳定成功的公开策略。 |
| 52 | + private val activityStableStrategies = listOf( |
| 53 | + FwStartStrategy.CONTEXT_DIRECT, |
| 54 | + FwStartStrategy.START_FOR_RESULT_HOOK |
| 55 | + ) |
| 56 | + |
| 57 | + // 受 Android 版本、权限、前后台状态或安全策略限制的策略。 |
| 58 | + private val systemConstrainedStrategies = listOf( |
| 59 | + FwStartStrategy.PENDING_INTENT_SEND, |
| 60 | + FwStartStrategy.BINDER_START_ACTIVITIES, |
| 61 | + FwStartStrategy.VIRTUAL_DISPLAY, |
| 62 | + FwStartStrategy.START_NEXT_MATCHING, |
| 63 | + FwStartStrategy.MOVE_TASK_TO_FRONT, |
| 64 | + FwStartStrategy.NOTIFICATION_BAL, |
| 65 | + FwStartStrategy.MEDIA_BUTTON_BAL, |
| 66 | + FwStartStrategy.CREDENTIAL_MANAGER, |
| 67 | + FwStartStrategy.PRINT_MANAGER, |
| 68 | + FwStartStrategy.SHELL_START_IN_VSYNC |
| 69 | + ) |
| 70 | + |
| 71 | + // 当前测试启动的 Activity,结束时统一关闭。 |
| 72 | + private var launchedActivity: Activity? = null |
| 73 | + |
| 74 | + /** |
| 75 | + * 测试结束后关闭由用例启动的 Activity。 |
| 76 | + */ |
| 77 | + @After |
| 78 | + fun finishLaunchedActivity() { |
| 79 | + // 避免多次 startActivity 后影响后续用例焦点。 |
| 80 | + launchedActivity?.finish() |
| 81 | + // 清空 Activity 引用。 |
| 82 | + launchedActivity = null |
| 83 | + } |
| 84 | + |
| 85 | + /** |
| 86 | + * 获取被测应用上下文。 |
| 87 | + */ |
| 88 | + private fun targetContext(): Context { |
| 89 | + // targetContext 指向 app 模块真实安装包。 |
| 90 | + return InstrumentationRegistry.getInstrumentation().targetContext |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * 构造指向 debug 测试 Activity 的真实 Intent。 |
| 95 | + */ |
| 96 | + private fun mainIntent(context: Context): Intent { |
| 97 | + // 使用字符串类名,避免 androidTest 编译期依赖 debug 源集类。 |
| 98 | + return Intent().setClassName(context.packageName, "com.google.services.TestStartActivity") |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * 启动一个真实 debug 测试 Activity,并返回 Activity context。 |
| 103 | + */ |
| 104 | + private fun launchMainActivity(): Activity { |
| 105 | + // 使用 instrumentation 在目标进程真实启动 Activity。 |
| 106 | + val instrumentation = InstrumentationRegistry.getInstrumentation() |
| 107 | + // Activity 启动需要 NEW_TASK 标志。 |
| 108 | + val intent = mainIntent(targetContext()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| 109 | + // 同步等待 Activity 创建完成。 |
| 110 | + val activity = instrumentation.startActivitySync(intent) |
| 111 | + // 保存 Activity,便于测试结束关闭。 |
| 112 | + launchedActivity = activity |
| 113 | + // 等待主线程空闲,确保窗口和 taskId 就绪。 |
| 114 | + instrumentation.waitForIdleSync() |
| 115 | + return activity |
| 116 | + } |
| 117 | + |
| 118 | + /** |
| 119 | + * 验证 modeMask=0 时不会进入 Native 执行,并返回参数错误。 |
| 120 | + */ |
| 121 | + @Test |
| 122 | + fun startWithZeroMask_returnsInvalidArgument() { |
| 123 | + // 获取真实应用上下文。 |
| 124 | + val context = targetContext() |
| 125 | + // 执行空策略启动。 |
| 126 | + val result = FwStart.startWithMask(context, mainIntent(context), 0) |
| 127 | + // 空策略必须返回参数错误。 |
| 128 | + assertEquals("空策略应返回参数错误", FwStartResult.INVALID_ARGUMENT, result.nativeCode) |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * 验证 application context 下的公开可执行 startActivity 策略能真实触发。 |
| 133 | + */ |
| 134 | + @Test |
| 135 | + fun applicationContextStrategies_triggerNativeStart() { |
| 136 | + // 获取真实应用上下文。 |
| 137 | + val context = targetContext() |
| 138 | + |
| 139 | + // 逐个策略单独触发,确保真实打到每个 Native 分支。 |
| 140 | + for (strategy in applicationStableStrategies) { |
| 141 | + // 每次使用新 Intent,避免前一个策略修改 flags 后影响后一个策略。 |
| 142 | + val result = FwStart.startWithStrategies(context, mainIntent(context), listOf(strategy)) |
| 143 | + // 公开 application context 策略应命中对应策略。 |
| 144 | + assertEquals("${strategy.displayName} 应成功命中", strategy, result.strategy) |
| 145 | + // 成功结果必须携带正数 Native 策略位。 |
| 146 | + assertTrue("${strategy.displayName} 返回码应为正数", result.nativeCode > 0) |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + /** |
| 151 | + * 验证 Activity context 下的公开可执行 startActivity 策略能真实触发或受控失败。 |
| 152 | + */ |
| 153 | + @Test |
| 154 | + fun activityContextStrategies_triggerNativeStart() { |
| 155 | + // 启动真实 Activity context。 |
| 156 | + val activity = launchMainActivity() |
| 157 | + |
| 158 | + // 逐个策略单独触发,确保真实打到每个 Native 分支。 |
| 159 | + for (strategy in activityStableStrategies) { |
| 160 | + // 每次使用 Activity 构造新 Intent。 |
| 161 | + val result = FwStart.startWithStrategies(activity, mainIntent(activity), listOf(strategy)) |
| 162 | + // Activity 直启和 startActivityForResult 属于公开稳定 API,应成功命中。 |
| 163 | + assertEquals("${strategy.displayName} 应成功命中", strategy, result.strategy) |
| 164 | + // 成功结果必须携带正数 Native 策略位。 |
| 165 | + assertTrue("${strategy.displayName} 返回码应为正数", result.nativeCode > 0) |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + /** |
| 170 | + * 验证受系统限制的策略会真实进入 Native 分支,并以成功或受控失败结束。 |
| 171 | + */ |
| 172 | + @Test |
| 173 | + fun systemConstrainedStrategies_returnControlledResult() { |
| 174 | + // 启动真实 Activity context,供需要 Activity 的系统策略使用。 |
| 175 | + val activity = launchMainActivity() |
| 176 | + |
| 177 | + // 逐个策略单独触发,确保真实打到每个 Native 分支。 |
| 178 | + for (strategy in systemConstrainedStrategies) { |
| 179 | + // Activity 相关策略使用 Activity context,其余策略使用 application context。 |
| 180 | + val context = when (strategy) { |
| 181 | + FwStartStrategy.START_NEXT_MATCHING, |
| 182 | + FwStartStrategy.MOVE_TASK_TO_FRONT -> activity |
| 183 | + else -> targetContext() |
| 184 | + } |
| 185 | + // 每次使用新 Intent,避免策略修改 flags 后影响下一个策略。 |
| 186 | + val result = FwStart.startWithStrategies(context, mainIntent(context), listOf(strategy)) |
| 187 | + // 这些策略必须真实进入 Native,不能被 Kotlin 层空策略拦截。 |
| 188 | + assertNotEquals("${strategy.displayName} 不应返回参数错误", FwStartResult.INVALID_ARGUMENT, result.nativeCode) |
| 189 | + // 系统限制策略允许成功或受控失败,但必须有明确返回码。 |
| 190 | + assertTrue("${strategy.displayName} 应返回明确结果", result.success || result.nativeCode < 0) |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + /** |
| 195 | + * 验证当前枚举中的每一个 startActivity 策略都被设备侧测试覆盖。 |
| 196 | + */ |
| 197 | + @Test |
| 198 | + fun allStartStrategies_areCoveredByInstrumentedTests() { |
| 199 | + // 汇总所有测试矩阵中声明覆盖的策略。 |
| 200 | + val coveredStrategies = ( |
| 201 | + applicationStableStrategies + |
| 202 | + activityStableStrategies + |
| 203 | + systemConstrainedStrategies |
| 204 | + ).toSet() |
| 205 | + // 当前枚举中的所有策略必须都在测试矩阵中。 |
| 206 | + assertEquals("所有 startActivity 策略都必须被设备侧测试覆盖", FwStartStrategy.entries.toSet(), coveredStrategies) |
| 207 | + } |
| 208 | +} |
0 commit comments