OnecardDemo 服务器一键稳定性测试实施计划
For agentic workers: REQUIRED SUB-SKILL: Use
executing-plansto implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: 在服务器单点功能页面实现固定 100 轮、轮间隔 1000 毫秒的一键连接稳定性测试,实时展示进度并保存完整本地报告。
Architecture: 将轮次执行、统计、报告格式化和文件存储拆成无 Android 依赖的 Java 类,由 ServerOperationsActivity 负责页面状态和真实 OnecardWebServiceClient 探测。每轮创建独立客户端,失败只记录不终止;统计与文件逻辑通过 JVM 测试验证,页面控件通过 instrumentation 测试验证。
Tech Stack: Java 8、AndroidX AppCompat、ExecutorService、HttpURLConnection、JUnit 4、Espresso。
文件结构
- 新建
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityRound.java:不可变单轮结果。 - 新建
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityResult.java:测试汇总和耗时统计。 - 新建
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityRunner.java:100 轮执行、失败继续、停止和进度回调。 - 新建
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportFormatter.java:页面摘要和完整报告文本。 - 新建
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportStore.java:UTF-8 报告文件写入。 - 修改
app/src/main/java/com/cpt/onecarddemo/ServerOperationsActivity.java:启动/停止、真实探测和页面更新。 - 修改
app/src/main/res/layout/activity_server_operations.xml:新增稳定性测试区域。 - 新建对应 JVM 测试并修改
ServerOperationsUiTest.java。 - 更新协议说明和
change_records/CHANGELOG的 Markdown/HTML 对。
Task 1:单轮结果与统计
Files:
Create:
app/src/test/java/com/cpt/onecarddemo/server/stability/ServerStabilityResultTest.javaCreate:
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityRound.javaCreate:
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityResult.javaStep 1:先写失败测试
覆盖 100 个成功样本的最短、最长、平均和 nearest-rank P95,以及成功/失败计数和零样本。
@Test
public void calculatesSuccessRateAndNearestRankP95() {
List<ServerStabilityRound> rounds = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
rounds.add(ServerStabilityRound.success(i, 1_000L + i, i, 25965));
}
ServerStabilityResult result = new ServerStabilityResult(100, 1_000L,
1_000L, 102_000L, false, rounds);
assertEquals(100, result.getSuccessCount());
assertEquals(0, result.getFailureCount());
assertEquals(100.0d, result.getSuccessRate(), 0.001d);
assertEquals(1L, result.getMinimumSuccessDurationMillis());
assertEquals(100L, result.getMaximumSuccessDurationMillis());
assertEquals(50.5d, result.getAverageSuccessDurationMillis(), 0.001d);
assertEquals(95L, result.getP95SuccessDurationMillis());
}
- Step 2:运行定向测试并确认 RED
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityResultTest'
Expected: 编译失败,提示稳定性结果类型不存在。
- Step 3:实现最小统计模型
实现成功/失败工厂方法、不可变字段、防御性列表复制、成功率和成功耗时统计;无成功样本时耗时访问器返回 -1,平均值返回 -1.0d。
- Step 4:运行定向测试并确认 GREEN
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityResultTest'
Expected: BUILD SUCCESSFUL。
Task 2:稳定性执行器
Files:
Create:
app/src/test/java/com/cpt/onecarddemo/server/stability/ServerStabilityRunnerTest.javaCreate:
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityRunner.javaStep 1:先写失败测试
测试三轮中第二轮抛错后第三轮仍执行,并测试停止信号生成部分结果。
@Test
public void continuesAfterFailureAndWaitsOnlyBetweenRounds() {
FakeTime time = new FakeTime();
AtomicInteger calls = new AtomicInteger();
ServerStabilityRunner runner = new ServerStabilityRunner(time, time::sleep);
ServerStabilityResult result = runner.run(3, 1000L, () -> {
int call = calls.incrementAndGet();
time.advance(call == 2 ? 40L : 20L);
if (call == 2) throw new IOException("temporary");
return 25965;
}, () -> false, round -> { });
assertEquals(3, calls.get());
assertEquals(2, result.getSuccessCount());
assertEquals(1, result.getFailureCount());
assertEquals(2000L, time.getSleptMillis());
}
- Step 2:运行测试并确认 RED
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityRunnerTest'
Expected: 编译失败,提示 ServerStabilityRunner 不存在。
- Step 3:实现执行器
定义 Probe、StopSignal、ProgressListener、TimeSource 和 Sleeper 接口;每轮捕获 Exception 并生成失败结果,轮间检查停止信号,最后返回 ServerStabilityResult。默认时间源使用 System.currentTimeMillis() 和 System.nanoTime()。
- Step 4:运行测试并确认 GREEN
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityRunnerTest'
Expected: BUILD SUCCESSFUL。
Task 3:报告格式与存储
Files:
Create:
app/src/test/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportFormatterTest.javaCreate:
app/src/test/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportStoreTest.javaCreate:
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportFormatter.javaCreate:
app/src/main/java/com/cpt/onecarddemo/server/stability/ServerStabilityReportStore.javaStep 1:先写报告失败测试
@Test
public void fullReportContainsSummaryRoundsAndGroupedErrors() {
ServerStabilityResult result = fixtureWithOneSuccessAndTwoSameFailures();
String report = ServerStabilityReportFormatter.fullReport(
"http://server/service.asmx", 6L, result);
assertTrue(report.contains("成功率:33.33%"));
assertTrue(report.contains("第 1 轮:成功"));
assertTrue(report.contains("IOException:timeout × 2"));
}
- Step 2:运行报告测试并确认 RED
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityReport*Test'
Expected: 编译失败,提示格式化器或存储类不存在。
- Step 3:实现格式化和存储
格式化器用 yyyy-MM-dd HH:mm:ss.SSS 和 Locale.CHINA 输出摘要、逐轮详情和错误分组。存储类在传入目录创建 server-stability-yyyy-MM-dd-HH-mm-ss-SSS.log,使用 StandardCharsets.UTF_8 覆盖写入并返回文件。
- Step 4:运行报告测试并确认 GREEN
Run: ./gradlew testDebugUnitTest --tests '*ServerStabilityReport*Test'
Expected: BUILD SUCCESSFUL,临时目录中存在 UTF-8 报告。
Task 4:页面交互和真实探测
Files:
Modify:
app/src/main/res/layout/activity_server_operations.xmlModify:
app/src/main/java/com/cpt/onecarddemo/ServerOperationsActivity.javaModify:
app/src/androidTest/java/com/cpt/onecarddemo/ServerOperationsUiTest.javaStep 1:先扩充 UI 测试
将以下 ID 加入 requiredViews:
R.id.button_server_stability_test,
R.id.text_server_stability_progress,
R.id.text_server_stability_result
- Step 2:编译 Android 测试并确认 RED
Run: ./gradlew compileDebugAndroidTestJavaWithJavac
Expected: 编译失败,提示新增资源 ID 不存在。
- Step 3:增加页面控件
在连接状态下增加标题、固定参数说明、按钮、进度和结果文本。结果文本允许选择,初始内容为“尚未执行稳定性测试”。
- Step 4:接入启动、停止和真实探测
Activity 固定使用:
private static final int STABILITY_ROUNDS = 100;
private static final long STABILITY_INTERVAL_MS = 1000L;
启动时快照 URL/AppID、清除普通连接状态并提交单线程任务。探测闭包每轮新建 OnecardWebServiceClient,finally 调用 disconnect();进度回调切换主线程更新 已完成 n/100。停止只设置 volatile 标记,当前请求完成或超时后生成部分报告。
- Step 5:保存报告并恢复页面状态
使用 getExternalFilesDir("server_stability");为空时退回 new File(getFilesDir(), "server_stability")。页面结果显示摘要和报告绝对路径;写入失败时保留摘要并显示异常。
- Step 6:运行 UI 编译和 JVM 回归
Run: ./gradlew testDebugUnitTest assembleDebugAndroidTest
Expected: BUILD SUCCESSFUL。
Task 5:文档、构建和真机验证
Files:
Modify:
/Users/liang/AndroidStudioProjects/zhct/zhctprompt/work_android/OnecardDemo/protocols/Change单点功能与字段说明.mdRegenerate: 同名
.htmlModify:
/Users/liang/AndroidStudioProjects/zhct/zhctprompt/work_android/OnecardDemo/change_records/CHANGELOG.mdRegenerate: 同名
.htmlModify: 本设计文档状态为“已实现”并同步 HTML。
Step 1:同步功能和字段文档
写明固定参数、成功标准、失败继续、停止语义、统计口径、报告路径和不适用于生产每秒重连的边界。
- Step 2:记录本次代码变更
在 CHANGELOG.md 顶部记录实现文件、TDD RED/GREEN 证据、构建结果和真机状态;不进行 Git 提交、暂存或推送。
- Step 3:执行完整验证
Run: ./gradlew testDebugUnitTest assembleDebug assembleDebugAndroidTest lintDebug --rerun-tasks
Expected: BUILD SUCCESSFUL,Lint 0 error。
- Step 4:安装并验证页面
Run: adb install -r app/build/outputs/apk/debug/app-debug.apk
Expected: Success。启动 ServerOperationsActivity 后确认新增区域可见、测试按钮可点击,且不自动发起 100 轮真实请求。
- Step 5:交付用户执行真实 100 轮
由用户在确认现场服务器允许测试后点击开始。现场已连续完成三轮,每轮 100 次;三轮原始报告、独立记录和 300 次综合对比报告均已归档。
执行约束
- 本计划由主会话内联执行,不调度子代理。
- 不执行 Git 暂存、提交、推送、分支切换或其他 Git 写操作。
- 不在自动构建/安装阶段自行启动真实 100 轮服务器请求,避免未经确认产生现场负载。