Skip to content
 
 

Repository files navigation

StorageRedirectTest

StorageRedirectTest 是 srx_core 的回归测试套件,用来验证 Storage Redirect X 在修改核心代码后,文件重定向、路径映射、真实路径放行、MediaStore 访问等功能是否仍然正常。

本地默认配合源码目录:

C:\Users\12988\Desktop\srx_core

测试 App 包名:

me.fakerqu.test.storageredirect

项目结构

  • app:测试 App,包含 Compose 调试界面、TestService、广播入口和测试用例。
  • media-file-api:被测试 App 调用的文件与 MediaStore API 封装。
  • .github/workflows/android.yml:CI 稳定性测试流程。
  • .github/scripts/install-storage-redirect-module.sh:CI 中给 x86_64 模拟器安装 Magisk 与 Storage Redirect X 模块。
  • .github/scripts/run-storage-redirect-scenarios.sh / .ps1:CI/ADB 场景脚本,验证不同 SRX 配置下文件落点和 App 视角。

环境要求

  • JDK 21。
  • Android SDK / platform-tools,确保 adb 可用。
  • Gradle Wrapper 使用本仓库的 gradlew.bat 或 ./gradlew。
  • 需要一台可用 root 的测试设备或模拟器,并已刷入 Storage Redirect X 模块。
  • 默认设备侧测试使用设备上当前已安装的模块,不主动编译、刷入本地 srx_core,也不因此重启设备。
  • 只有在用户明确要求编译/刷入本地模块,或明确说明要验证当前本地 srx_core 模块产物时,才从 C:\Users\12988\Desktop\srx_core 构建并刷入本地模块包。

本项目已优先使用国内下载源:Gradle Wrapper 使用腾讯 Gradle 镜像,Gradle daemon JDK 21 使用清华 TUNA Adoptium 镜像,Maven/插件依赖优先走阿里云和腾讯 Maven 镜像,官方源仅作为兜底。

本地快速使用

先确认设备在线、root 可用,并记录当前已安装模块版本。默认不重新编译或刷入模块:

adb wait-for-device
adb shell "su -c 'cat /data/adb/modules/storage.redirect.x/module.prop'"

再构建并安装测试 App:

cd C:\Users\12988\Desktop\StorageRedirectTest
.\gradlew.bat --no-daemon :app:testDebugUnitTest :media-file-api:testDebugUnitTest :app:assembleDebug
adb install -r app\build\outputs\apk\debug\app-debug.apk

如用户明确要求验证本地 srx_core 模块产物,则先构建并刷入模块。模块更新后需要重启设备,重启完成后重新确认 module.prop:

cd C:\Users\12988\Desktop\srx_core
.\scripts\build-local-module.ps1
adb wait-for-device
adb shell "su -c 'cat /data/adb/modules/storage.redirect.x/module.prop'"

授予测试 App 必要权限:

adb shell pm grant me.fakerqu.test.storageredirect android.permission.READ_EXTERNAL_STORAGE 2>$null
adb shell pm grant me.fakerqu.test.storageredirect android.permission.WRITE_EXTERNAL_STORAGE 2>$null
adb shell pm grant me.fakerqu.test.storageredirect android.permission.READ_MEDIA_IMAGES 2>$null
adb shell pm grant me.fakerqu.test.storageredirect android.permission.READ_MEDIA_VIDEO 2>$null
adb shell pm grant me.fakerqu.test.storageredirect android.permission.READ_MEDIA_AUDIO 2>$null
adb shell pm grant me.fakerqu.test.storageredirect android.permission.POST_NOTIFICATIONS 2>$null
adb shell appops set me.fakerqu.test.storageredirect MANAGE_EXTERNAL_STORAGE allow

给 SRX 写入测试 App 配置,最小配置表示开启完整隔离模式:

$config = '{"users":{"0":{"enabled":true}}}'
adb shell "su -c 'mkdir -p /data/adb/modules/storage.redirect.x/config/apps'"
$config | adb shell "su -c 'cat > /data/adb/modules/storage.redirect.x/config/apps/me.fakerqu.test.storageredirect.json'"
adb shell am force-stop me.fakerqu.test.storageredirect

运行默认回归用例:

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case all

查看日志和结果:

adb logcat -d -s StorageRedirectTest
adb shell "su -c 'ls -t /sdcard/Android/data/me.fakerqu.test.storageredirect/files/test_case_result/result_*.txt 2>/dev/null | head -1'"
adb shell "su -c 'cat /sdcard/Android/data/me.fakerqu.test.storageredirect/files/test_case_result/result_*.txt 2>/dev/null | tail -80'"

test_case=all 会运行查询、创建、读取、写入、stat、access、truncate、ftruncate 和文件 API 的主要路径,但不会自动执行 MediaStore 删除、缩略图、chmod、link 或 symlink 用例。它会创建 srt_* 开头的测试媒体文件,并在用例结束时删除本次记录下来的 MediaStore URI 和 srt_file_tests bootstrap 目录。单个 MediaStore create 用例仍会保留返回的 URI,便于后续手动 read/write/delete。

单个用例

测试入口支持通过 test_case 和额外参数运行单个用例。

常用用例 id:

  • all
  • mediastore_query_image
  • mediastore_query_video
  • mediastore_query_audio
  • mediastore_query_file
  • mediastore_query_download
  • mediastore_query_read_only_image
  • mediastore_query_path_image
  • mediastore_query_path_video
  • mediastore_query_path_audio
  • mediastore_query_path_file
  • mediastore_query_path_download
  • mediastore_create_image
  • mediastore_create_image_relative_data
  • mediastore_read_image
  • mediastore_write_image
  • mediastore_delete_image
  • mediastore_thumbnail_image
  • file_list_dir
  • file_create
  • file_read
  • file_write
  • file_write_denied
  • file_delete
  • file_delete_denied
  • file_mkdir
  • file_mkdir_denied
  • file_rename
  • file_rename_denied
  • file_stat
  • file_access
  • file_readlink
  • file_truncate
  • file_truncate_denied
  • file_ftruncate
  • file_ftruncate_denied
  • file_chmod
  • file_chmod_denied
  • file_fchmod
  • file_fchmod_denied
  • file_link
  • file_link_denied
  • file_symlink
  • file_symlink_denied

File API 示例:

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case file_write --es file_path /storage/emulated/0/Download/SrtProbe/srt_ci_probe.txt --es payload "storage-redirect-test:file:manual" --es expected_payload "storage-redirect-test:file:manual"

只读或拒绝类用例用于验证 srx_core 是否正确阻止写操作。例如:

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case file_write_denied --es file_path /storage/emulated/0/Download/SrtReadOnly/write_denied.txt --es payload "blocked"

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case file_rename_denied --es file_path /storage/emulated/0/Download/SrtReadOnly/srt_read_only_seed.txt --es target_file_path /storage/emulated/0/Download/SrtReadOnly/renamed.txt

MediaStore 读写类用例需要先运行对应的 create 用例,从结果中的 uri= 取出 content://...,再作为 media_uri 传入:

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case mediastore_create_image

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case mediastore_read_image --es media_uri "content://media/external/images/media/12345"

使用相对 _data 创建图片时,relative_path 既可以传 Pictures/Album,也可以传包含文件名的 Pictures/Album/example.jpg。该用例会通过 ContentResolver.insert() 和 openOutputStream() 创建、发布并读回校验媒体内容:

adb shell am start-foreground-service -n me.fakerqu.test.storageredirect/.TestService -a me.fakerqu.test.storageredirection.TEST_CASE --es test_case mediastore_create_image_relative_data --es relative_path Pictures/SrtRelativeData --es file_name srt_relative.jpg

支持参数:

  • media_uri:MediaStore 读、写、删、缩略图用例的目标 URI。
  • file_path:文件读、写、删、创建、重命名源路径等用例的目标路径。
  • target_file_path:file_rename / file_rename_denied 的目标路径。
  • file_dir:目录列表和 mkdir 类用例的目标目录。
  • file_name:MediaStore 创建用例的文件名,或 mediastore_query_path_* / mediastore_query_read_only_image 用例用于定位目标行的文件名。
  • relative_path:普通 MediaStore 创建用例写入的相对目录,例如 Download/SrtMonitor;mediastore_create_image_relative_data 中表示传给 _data 的安全相对路径,可包含文件名。
  • keep_pending:MediaStore 创建用例是否保留 IS_PENDING=1;支持 1、true、yes,用于创建后立即由同一测试 APP 继续读写 URI。
  • payload:写入内容。
  • expected_payload:读回校验内容。
  • expected_path:file_readlink 的期望链接目标,或 mediastore_query_path_* / mediastore_query_read_only_image 的期望 cursor DATA 路径。
  • length:file_truncate / file_ftruncate 的目标长度。
  • mode:file_access、file_chmod、file_fchmod 的访问模式或权限模式;支持十进制、0600 八进制和 0o600 八进制写法。

场景脚本

已经安装模块和测试 App 后,可以使用 CI 同款场景脚本。Windows 下建议用 Git Bash 或 WSL 执行:

cd /c/Users/12988/Desktop/StorageRedirectTest
bash .github/scripts/run-storage-redirect-scenarios.sh

脚本覆盖 scenario 1-29:基础重定向与映射、4 个 FUSE daemon 混合模式、3 个默认 mount namespace 回退、文件监视与 MediaStore 系统代写、只读真实图片查询,以及运行中配置热更新。MediaStore 文件监视场景还会验证安全相对 _data 路径、根相对 /Pictures/Nnngram 保存链路,以及跨应用只读规则不能误伤当前调用方。可以通过 RUN_FUSE_DAEMON_SCENARIOS=1 强制运行 FUSE daemon 场景,或通过 RUN_FUSE_DAEMON_SCENARIOS=0 跳过这些场景。

脚本默认在每个服务用例前冷启动测试 App,并确认当前进程已经获得该场景要求的挂载;scenario 29 会临时保持同一进程验证配置热更新。测试 App 会写入固定的 result_current.txt,且每次写结果前重新创建父目录,避免存储视图刷新导致结果丢失。可用 SRT_FRESH_APP_PER_CASE=0 调试复用进程,只跑部分场景可设置 SRT_SCENARIOS=9,17,19,22,23,24,26,28,29。SRT_FAIL_FAST=1 会在首个失败场景停止,SRT_SCENARIO_TIMEOUT_SECONDS 控制单场景超时,SRT_SKIP_FINAL_CLEANUP=1 只适用于一次性 CI 模拟器。结果轮询、启动缓冲、挂载确认和用例间隔可分别通过 SRT_RESULT_POLL_MS、SRT_APP_LAUNCH_SETTLE_MS、SRT_MOUNT_CONFIRM_TIMEOUT_MS、SRT_SERVICE_CASE_SETTLE_MS 调整。

  • 未启用应用配置,验证默认真实路径写入。
  • 启用重定向,验证写入应用私有空间。
  • 启用路径映射,验证 Download/SrtProbe 写入真实 Download/Test。
  • 路径映射叠加真实路径放行,验证映射优先级。
  • 放行真实 Download,验证保持原路径写入。
  • 启用 mapping_mode_only 且未命中映射,验证保持真实路径写入。
  • 启用 mapping_mode_only 且命中映射,验证写入映射目标。
  • 启用 mapping_mode_only + sandboxed_paths,验证 .xlDownload/.xldownload 沙盒化。
  • 启用 read_only_paths,验证可读、可 stat/access,但拒绝写入、truncate、ftruncate、chmod、fchmod、link、symlink、删除、mkdir、rename。
  • 启用路径映射且映射目标为只读路径,验证从映射请求写入会被拒绝。
  • 启用 allowed_real_paths 内联排除和通配符排除,验证放行路径保持真实写入、排除目录可写入私有空间、通配符排除会命中并创建到应用私有空间。
  • 启用旧版 excluded_real_paths 字段,验证它会并入 allowed_real_paths 排除规则。
  • 启用 allowed_real_paths 的 ? 通配符,验证普通应用直写单字符匹配时保持真实路径写入,并验证 MediaStore 系统代写单字符匹配放行、多字符不匹配时仍进入私有空间。
  • 启用多条 path_mappings,验证最长前缀映射优先。
  • 启用字符串形式 sandboxed_paths 且同路径也命中 path_mappings,验证映射优先于局部沙盒。
  • 启用全局 fuse_daemon_redirect_enabled,验证普通放行规则和 */? 通配符放行规则可以并存:普通规则仍保持真实路径写入,普通应用直写与 MediaStore 系统代写命中的通配符路径也保持真实路径写入,不命中的同类路径进入应用私有空间。
  • 启用全局 fuse_daemon_redirect_enabled,验证 read_only_paths 中 ! 排除优先:父路径只读时,排除子路径可写,未排除子路径写入被拒绝。
  • 启用全局 fuse_daemon_redirect_enabled,验证路径映射和只读路径共同存在时,写权限由映射最终目标决定:最终目标被 ! 排除则可写,最终目标仍落在只读父路径下则拒绝写入。
  • 启用全局 fuse_daemon_redirect_enabled,验证同一父级路径下多个通配符规则互不污染:分别放行的子路径保持真实写入,只读通配符子路径拒绝写入,未命中的兄弟路径进入应用私有空间。
  • 关闭 fuse_daemon_redirect_enabled 时,验证默认 mount namespace 下 allowed_real_paths 的 */? 通配符不会被忽略,而是回退到预期的具体路径规则,普通应用直写与 MediaStore 系统代写命中时保持真实路径写入。
  • 关闭 fuse_daemon_redirect_enabled 时,验证默认 mount namespace 下 read_only_paths 的通配符不会被忽略,而是回退到预期的具体路径规则,并继续保持只读拒绝语义。
  • 关闭 fuse_daemon_redirect_enabled 时,验证默认 mount namespace 下路径映射和只读规则同时存在时,写权限仍由映射最终目标决定:请求路径不会被错误创建,最终目标被 ! 排除则可写,最终目标仍落在只读父路径下则拒绝写入。
  • 启用 read_only_paths=["Pictures/SrtReadOnlyMedia"],预置真实图片并扫描进 MediaStore,验证测试 App 通过 MediaStore 查询仍能看到只读真实路径下的图片行。
  • 启用 file_monitor_enabled 且普通应用关闭重定向时,验证普通公共路径保存和 MediaStore 系统代写保存成功后 file_monitor.log 都有成功记录。
  • 启用 file_monitor_enabled 且 fuse daemon 关闭/开启时,验证普通应用直接保存到放行路径或映射请求路径后都有成功记录;最终路径命中只读规则时写入失败且不落盘;最终路径命中只读排除规则时有成功记录。
  • 启用 file_monitor_enabled 且 fuse daemon 关闭/开启时,验证 MediaStore 系统代写方式在放行路径、映射请求路径、最终只读路径、最终只读排除路径下分别产生对应成功或失败记录。
  • 保持测试 App 进程运行,在线把配置从默认重定向切换为路径映射,验证后续写入无需重启应用即可使用新配置。

脚本输出 scenario-*-result.txt 和 media-health.txt。失败时会抓取模块配置、模块日志、存储挂载状态和相关 logcat。

脚本开跑前和清理后会重启 MediaProvider,避免上一轮 shell 侧 MediaStore 清理影响下一轮 MediaStore 写入归因。脚本结束时会执行设备侧清理,成功和失败都会尽量收尾。清理范围是白名单式的:

  • 测试 App 的 test_case_result 和 srt_file_tests 目录,包括被 SRX 重定向后的 App 私有镜像路径。
  • 场景脚本创建的 Download/Srt*、Pictures/SrtLocked、Pictures/SrtReadOnlyMedia、DCIM/SrtFuse* 等固定测试目录,以及固定文件 srt_ci_probe.part、srt_qmark*.txt、srt_mountns_*_media.bin、srt_fuse_*_media.bin、srt_read_only_media.jpg 和文件监视专用 srt_monitor_*.bin。
  • MediaStore 中名称匹配 srt_image_<数字>.jpg、srt_read_only_media.jpg、srt_video_<数字>.mp4、srt_audio_<数字>.mp3、srt_file_<数字>.txt、srt_download_<数字>.bin 和脚本固定媒体文件名的测试行,并且只限定在对应的 Pictures、Movies、Music、Documents、Download 或测试 App 私有镜像路径;同样清理 MediaProvider 可能留下的 .pending-<数字>-srt_* / .trashed-<数字>-srt_* 临时名和同名冲突 (数字) 后缀。
  • 真实物理目录中同样命名规则的随机测试文件,以及固定测试媒体文件的 .pending-* / .trashed-* 变体,只在上述公共媒体目录和测试 App 私有镜像目录的白名单层级删除。

清理不会递归删除整个 Download、Pictures 等用户目录,也不会删除白名单之外的普通 srt_* 文件。scenario-*-result.txt 和 media-health.txt 是脚本在本仓库工作区生成的诊断文件,会保留给 CI 或本地排查使用。

CI 行为

GitHub Actions 当前会:

  • 在 Android 13、14、15、16(API 33、34、35、36)的 x86_64 模拟器上运行完整 scenario 1-29。
  • 下载 Kindness-Kismet/Storage-redirection-X-Public 的最新 x86_64 Release 模块。
  • 构建测试 App 和 AndroidTest APK。
  • root 模拟器、安装 Magisk、安装 Storage Redirect X 模块、安装测试 App。
  • 执行 .github/scripts/run-storage-redirect-scenarios.sh。

注意:当前 CI 默认验证的是上游 Release 模块,不会自动使用本地 C:\Users\12988\Desktop\srx_core 的未发布修改。本地设备侧测试默认也使用当前设备已安装模块;只有在用户明确要求编译/刷入,或明确说明要验证本地模块产物时,才按本 README 的可选步骤构建并刷入 srx_core。

维护和提交规则

本仓库是个人 fork 工作区。当前 origin 指向:

https://github.com/z1298808165/StorageRedirectTest.git

当 StorageRedirectTest 本身发生修改并需要提交时,只提交并推送到自己的 fork。不要向原仓库提交 PR,除非用户明确要求“向原仓库提交 PR”。

如果原仓库更新,需要同步到本项目:

  • 先确认当前工作区是否有未提交修改,避免覆盖本地工作。
  • 先阅读上游变更内容,尤其是 Gradle、测试脚本、包名、CI、测试用例和结果格式相关变更。
  • 根据本项目作为 srx_core 回归测试套件的用途判断是否应该同步,不要机械合并。
  • 同步前后都要验证关键路径,至少运行 Gradle 单元测试和测试 App 构建。
  • 如果同步影响 .github/scripts/run-storage-redirect-scenarios.sh 或测试用例语义,需要在设备或模拟器上跑一遍相关场景。
  • 解决冲突时保留本项目已有的本地使用方式、fork 提交流程和 srx_core 配合测试说明。

About

测试训练场

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages