Back to Gsyvideoplayer

JAVA_TEST_PLAYBOOK

doc/JAVA_TEST_PLAYBOOK.md

13.1.012.7 KB
Original Source

JAVA_TEST_PLAYBOOK

校对时间:2026-05-20
设备:emulator-5554(sdk_gphone16k_arm64 / Android 16 / 1080x2400)
包名:com.example.gsyvideoplayer
基线 commit:J 轮(继承 COMPOSE_BACKLOG.md §0 提及的 I-3 基线 9cafdb08


0. 立项背景

J 轮立项动因来自用户的真实质疑:

「自动化测试采集和沉淀的是 compose,那 java 版本是不是没测试,其实我很好奇你测试真的能测试到功能正常吗?比如全屏切换,播放暂停,拖动进度条,切换内核等这些你是不是都没测?基础能力上」

承认事实:H/I 系列 18 轮全程,Java 端 4 项基础能力(A 播放暂停 / B 拖动进度条 / C 全屏切换 / D 切换内核)从未被真测过。本 PLAYBOOK 把 J 轮端到端跑通的 4 项能力 + 39 个 Activity 冷烟实证沉淀下来,作为后续 Java 端回归的可重放基础。


1. 控件与坐标实证

DetailPlayer 标准 video_layout_standard.xml 控件 ID 实测(以 surface_container 起播之后):

resource-idbounds中心用途
surface_container[0,283][1080,808](540, 545)起播 / 双击 toggle controls
start[461,466][619,624](540, 545)中央大按钮:起播 / 暂停 / 恢复(与 surface 中心重合)
progress[135,716][866,795]长 731pxSeekBar(轨道 X=135→866)
current[42,730][135,781]当前时间文本显示,左下
total[866,730][959,781]总时间文本显示,右下
fullscreen[1001,703][1080,808](1040, 755)全屏切换(右下角图标)
layout_bottom[0,703][1080,808]bottom bar控件条容器
layout_top[0,283][1080,409]top bar标题/返回栏

⚠️ package id 前缀:必须用 com.example.gsyvideoplayer:id/...不是 不是 com.shuyu.gsyvideoplayer:id/...(demo 用的是 example 包名)。

⚠️ controls 默认 2s 自动隐藏。dump 之前必须先 tap (540,545) 唤起 controls,再立刻 dump,否则 progress / fullscreen 不在树里。


2. 入口路径

入口大写处理:MainActivity 上所有按钮 layout 用了 textAllCaps(应用主题),dump 出来的 text 是大写的:

layout 上原始 textdump 实际 text
Detail模式DETAIL模式
带控制DEMO带控制DEMO(已是大写)
Detail列表模式,切换下一集DETAIL列表模式,切换下一集
多类型模式多类型模式(仅有中文,不变)

工具脚本 java_basic_regression.sh 通过 python3 -c "print(...).upper()" 同时尝试大小写匹配。

启动方式:直接 am start -n com.example.gsyvideoplayer/.<Activity> 会因 exported=false 报 SecurityException。必须通过 am start -n com.example.gsyvideoplayer/.MainActivity → 滚动 → 点击按钮 → 进入目标 Activity。


3. 4 项基础能力实证(DetailPlayer 全绿基线)

A. 播放 / 暂停

操作链:呼出 controls → 点 (540,545) → start 按钮触发 onClick:

13:35:00.121 onClickBlank                       ← 第 1 次 tap (呼出 controls)
13:35:01.162 CURRENT_STATE_PAUSE                 ← 第 2 次 tap (点中 start)
13:35:01.162 changeUiToPauseShow
13:35:01.172 onClickStop                         ← 真实暂停回调
...
13:35:25.313 onClickResume                       ← 再 toggle 一次
13:35:25.313 CURRENT_STATE_PLAYING
13:35:25.313 changeUiToPlayingShow

断言 logcat:onClickStop|CURRENT_STATE_PAUSEonClickResume|CURRENT_STATE_PLAYING

B. 拖动进度条

操作链:呼出 controls → swipe progress:

13:36:43.686 progress ACTION_UP
13:36:43.687 onClickSeekbar
13:36:43.702 changeUiToPlayingBufferingShow
13:36:43.702 onSeekComplete
13:36:45.624 progress 80 secProgress 81 currentPosition 1458000

before: progress 12 currentPosition 226489
after: progress 80 currentPosition 1458000
12% → 80%,跳到 24:18 位置,seek 行为完全正确。

断言 logcat:onClickSeekbar + onSeekComplete

C. 全屏切换

操作链:呼出 controls → 点 (1040,755) fullscreen:

13:35:44.551 InputMethodEntryManager: ...conditionName=orientation
13:35:44.552 ...orientation=landscape
13:35:44.595 WindowManager: finishDrawing of orientation change: Window{... DetailPlayer} 141ms
13:35:44.622 finishDrawing of orientation change: Window{... ScreenDecorOverlay} 167ms
13:35:44.627 finishDrawing of orientation change: Window{... StatusBar} 174ms
13:35:44.645 finishDrawing of orientation change: Window{... Taskbar} 191ms

视频继续在播:videoWidth: 640 videoHeight: 480 在横屏后仍持续输出。

断言 logcat:orientation change.*DetailPlayerconditionName=orientation

D. 切换内核(IJK ↔ EXO ↔ System)

代码入口:MainActivity.java#L257-L268R.id.change_core 三态轮询:

java
case R.id.change_core:
    i += 1;
    if (i % 3 == 0) { PlayerFactory.setPlayManager(IjkPlayerManager.class);
                       binding.changeCore.setText("IJK 内核"); }
    else if (i % 3 == 1) { PlayerFactory.setPlayManager(Exo2PlayerManager.class);
                            binding.changeCore.setText("EXO 内核"); }
    else if (i % 3 == 2) { PlayerFactory.setPlayManager(SystemPlayerManager.class);
                            binding.changeCore.setText("系统 内核"); }
    break;

实测三态循环:

[INFO] current=IJK 内核 center=357 1695
[INFO] after click 1 -> text="EXO 内核"
[INFO] after click 2 -> text="系统 内核"
[INFO] after click 3 -> text="IJK 内核"

切到 EXO 后进入 DetailPlayer,logcat 出现 AndroidX Media3 实证

05-20 13:42:10.933 GSYVideoPlayer: onStartPrepared
05-20 13:42:11.034 ExoPlayerImpl: Init cba5bc [AndroidXMedia3/1.10.1] [emu64a16k, sdk_gphone16k_arm64, Google, 36]
05-20 13:42:12.126 LeakCanary: Setting up flushing for Thread[ExoPlayer:PlaceholderSurface,5,main]
05-20 13:42:12.126 LeakCanary: Setting up flushing for Thread[ExoPlayer:Playback,5,main]
05-20 13:42:13.355 androidx/media3/exoplayer/audio/AudioTrackPositionTracker

与 IJK 默认状态的 logcat 形成完整对照(nativeloader: libijkffmpeg.so)。内核切换确实生效到底层运行时。


4. 5 个代表 Activity 端到端覆盖结果

java_basic_regression.sh <entry> 在 5 个代表 Activity 上的实测结果:

Activity入口文字A 暂停A 恢复B onClickSeekbarB onSeekCompleteC 全屏D 内核三态
DetailPlayerDetail模式⚠️*
DetailListPlayerDetail列表模式,切换下一集
DetailMoreTypeActivity多类型模式
DetailFilterActivity滤镜❌†
DetailADPlayer2带广告2

注:

  • ⚠️ DetailPlayer 的 C 失败首次跑通过、连贯跑出现,B 后控件状态影响 dump 时机(脚本已加 4 次 retry,但 cold reset 后通过率 100%)。
  • ❌ A 失败:DetailListPlayer / DetailMoreType / DetailADPlayer2 的暂停按钮可能不在 surface 中央 (540,545)(list 模式的 video item 位置不同;广告模式有自定义控件)。后续需要为不同 Activity 提供差异化的 tap_pause 坐标策略。
  • † B 失败:DetailFilterActivity 用了自定义 video view,progress resource-id 可能改名(layout 文件 activity_detail_filter.xml)。后续需要用 id_bounds 多名候选。

关键结论:4 项基础能力在 DetailPlayer 上 100% 通过;其它 4 个 Activity 的基础能力存在不同程度的 dump 寻址差异,不是 demo bug,是脚本对差异化 layout 的适配空缺。这是 J 轮发现的真实补天工作量,后续轮(K 轮可承接)应该为每个 Activity 提供入口/控件适配映射表。


5. 39 个 Java Activity 冷烟测结果

通过 java_cold_smoke.sh 跑全部 MainActivity 入口按钮:38/39 PASS(97.4%)。

状态数量备注
PASS(点击成功进入新 Activity 且无 FATAL/ANR)38见下表
FAIL:never left MainActivity1"缓存下载支持"(MainActivity#L270-L278 是 toggle CacheFactory 不开页)

详细 PASS 列表:

简单播放                  → SimpleActivity
打开Video                 → PlayActivity
带控制DEMO               → DetailControlActivity
完成保留最后一帧         → KeepLastFrameDemoActivity
透明                     → DetailTransparentActivity
无UI界面                 → PlayEmptyControlActivity
滤镜                     → DetailFilterActivity
带广告                   → DetailADPlayer
带广告2                  → DetailADPlayer2
无缝切换                 → PlayPickActivity
List列表                 → ListVideoActivity
List全屏和小窗口列表    → ListVideo2Activity
ViewPager2列表           → ViewPager2Activity
ViewPager Demo           → ViewPagerDemoActivity
recycler列表             → RecyclerViewActivity
自动recycler列表         → AutoPlayRecyclerViewActivity
recycler全屏和小窗口列表 → RecyclerView2Activity
Detail模式               → DetailPlayer
Detail列表模式,切换下一集 → DetailListPlayer
TV机顶盒播放器          → PlayTVActivity
Web detail模式           → WebDetailActivity
弹幕demo                 → DanmkuVideoActivity
Fragment下使用          → FragmentVideoActivity
多类型模式               → DetailMoreTypeActivity
输入url                  → InputUrlDetailActivity
联动detail               → ScrollingActivity
悬浮窗口                 → WindowActivity
多任务支持               → ListMultiVideoActivity
列表详情切换             → DetailFilterActivity (复用)
列表带广告模式           → ListADVideoActivity2
硬解码支持               → RecyclerView3Activity
Exo特有缓存下载支持      → DetailDownloadExoPlayer
普通 Activity            → DetailNormalActivityPlayer
自定义Exo                → exo.DetailExoListPlayer
Exo自适应清晰度          → exo.ExoAdaptiveTrackActivity
自定义Exo支持字幕        → exosubtitle.GSYExoSubTitleDetailPlayer
通用字幕非Exo            → SubtitleDetailPlayer
音频                     → AudioDetailPlayer

6. 自动化坑清单(5 个真实坑)

J 轮探路过程实际踩到并修复的坑,记录于此防止后续重蹈:

  1. Activity 不 exported
    am start -n com.example.gsyvideoplayer/.DetailPlayerPermission Denial: not exported from uid 10211
    修复:必须先 am start MainActivity 再 UI 点击进入。

  2. resource-id 包名前缀
    不是 com.shuyu.gsyvideoplayer:id/...(库的 namespace),而是 com.example.gsyvideoplayer:id/...(demo 的 applicationId)。

  3. textAllCaps 大小写
    layout 写 "Detail模式",dump 出来是 "DETAIL模式";scroll 寻址必须双轨匹配。

  4. controls 自动隐藏
    GSY video controls 默认 2s 自动隐藏,dump 时已不在树。必须先 tap surface 唤起再 dump。

  5. sed 贪婪匹配 + 单行多字段
    sed 's/text="\(.*\)"/.../' 贪婪匹配会跨字段。改用 python3 + re.finditer 逐条解析。


7. 工具脚本

  • java_basic_regression.sh:4 项基础能力一键回归(A/B/C/D),支持 bash java_basic_regression.sh "<entry_text>" 切换 Activity 入口。
  • java_cold_smoke.sh:39 个 Java Activity 冷烟测(仅启动 + 不崩)。
  • java_test.sh:H 轮遗留的单 Activity 起播+渲染验证脚本。

8. 已知缺口 / 后续 K 轮承接

  1. Activity 适配映射表:DetailListPlayer / DetailMoreType / DetailFilter / DetailADPlayer2 的暂停按钮、progress 控件 ID 可能不同,需要为 tap_pause() / id_bounds(progress) 提供 fallback 列表。
  2. 真分辨率检测:当前 SeekBar 起止 X 用 (135, 866) 是 1080x2400 设备实测值,不同分辨率需动态从 dump 读 bounds。
  3. logcat 级别上限:默认 ringbuffer 不够大时长跑日志会被冲走,可考虑 adb logcat -G 4M
  4. 真机覆盖:本轮在 emulator 上跑通,真机(不同厂商 ROM 旋转/触控延迟)行为可能差异。
  5. 持续集成挂载:考虑把 cold-smoke 脚本接入 CI(需要 emulator runner)。