test: close V3 Linux freeze
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-09-11 11:51:34 +08:00
parent fcc533dc95
commit a4bf6b7aea
6 changed files with 159 additions and 13 deletions

View File

@@ -0,0 +1,40 @@
# STEP-071A V3-07 Linux 综合冻结:测试 Fixture 修正
## 基线
- 仓库:`/mnt/sda1/MapLibreParticleLab`
- 分支:`particle/main`
- 基线 HEAD:`fcc533dc9518395d0c25794131f53f0fae20d51c`
- 只允许修改:`test/style/source.test.cpp`
## 已确认问题 1:Host duplicate 测试数据错误
`HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField` 当前用同一份 `uniform-east` payload 提交 T0/T1。
Runtime 先按 GPU content identity 返回 `Duplicate` 是正确行为;测试预期 `InitialWindowReady` 已过时。
修正要求:
- T0 使用 `uniform-east/field.f32`。
- T1 使用兼容但内容不同的 `uniform-north/field.f32`。
- 后续 invalid/non-monotonic 检查使用不会与当前 lastValid 重复的 payload;尤其 non-monotonic 必须真正到达 non-monotonic 判定,而不是先命中 Duplicate。
- 不修改 `sameFieldGpuContent`、Duplicate 顺序或任何 Runtime 生产逻辑。
## 已确认问题 2:Timeline 测试时间重复
`RasterArrayTimelineCandidatesReachReadyThroughFieldAdapter` 使用 east/north/vortex 三份旧 fixture;三份 metadata 的 `timeIndex` 均为 0。
当前 `OfflineFixtureParticleAdapter` 保留 `validTime = timeIndex + 1`,因此旧测试会形成重复时间,Timeline 正确 fail-closed,而测试无限等待。
修正要求:
- 不改共享 fixture 文件。
- 仅在该测试内部把 north/vortex metadata 的 `timeIndex` 调整为严格递增值,例如 east=0、north=1、vortex=2。
- 保持 grid/bounds/vector semantics/encoding 不变。
- 保持测试仍通过真实 `OfflineFixtureParticleAdapter` / Source preload / Timeline 路径验证,不改成绕过适配器的伪测试。
- 若需要小型局部 helper,只能放在 `test/style/source.test.cpp`,不得扩散生产 API。
## 禁止事项
- 不修改 `src/`、`include/`、shader、JNI、Android、RN、WeatherV2、NavSea。
- 不修改 STEP-062 用户 dirty 文件。
- 不执行编译、测试、代码审计、搜索、Android Gradle、ADB、Jest。
- 不执行 git add/commit/push。
- 不创建额外完成报告;验证与 Git 收尾由 ChatGPT 在 tmux `rdc` 中直接完成。
## 完成输出
修改完成后只报告:
1. 修改了 `test/style/source.test.cpp` 哪两个测试;
2. 每处如何避免 Duplicate / 重复 validTime;
3. 是否修改了任何生产代码(必须为“否”)。

View File

@@ -0,0 +1,27 @@
# STEP-071A V3-07 Linux 综合冻结:测试 Fixture 修正 v2
## 基线与现状
- 当前基线:`fcc533dc9518395d0c25794131f53f0fae20d51c` + 未提交的 v1 测试修正。
- v1 的 Host duplicate 修正已实测 PASS,不得回退。
- 只允许修改:`test/style/source.test.cpp`。
## 唯一问题
`RasterArrayTimelineCandidatesReachReadyThroughFieldAdapter` 中 v1 使用:
`northMetadata.find("\"timeIndex\":0")`
实际共享 fixture metadata 格式是:
`"timeIndex": 0`
因此 `find()` 返回 `npos`。
## 必做修正
- 保留 v1 已完成的 east/north distinct payload 修正。
- 将 north/vortex 的测试内 `timeIndex` 替换逻辑改为匹配实际 JSON 文本格式。
- north 必须得到 `timeIndex = 1`,vortex 必须得到 `timeIndex = 2`。
- 不修改共享 fixture 文件。
- 不修改任何生产代码或 Runtime 行为。
## 禁止
- 不编译、不测试、不搜索/审计、不运行 Git。
- 不修改其他文件。
- 不运行 Android/ADB/Jest。
完成后只报告修改内容和是否修改生产代码。

View File

@@ -0,0 +1,37 @@
# STEP-071 — MapLibreParticleLab V3-07 Linux 综合冻结
状态:CLOSED / PASS(Linux)
日期:2026-09-11
基线:`fcc533dc9518395d0c25794131f53f0fae20d51c`
## 目标
在进入 V3-08 Android 统一验收前,对 V3-02~V3-06 的 canonical field、Host ingress、Timeline、RasterParticle 与 RN 公共桥接做一次 Linux 综合冻结。
## 冻结链结果
- [x] canonical fixture validator:PASS。
- [x] `packages/maplibre-particle-rn` TypeScript:PASS。
- [x] RN Test Host `npm run typecheck`:PASS。
- [x] RN TurboModule codegen:PASS。
- [x] `mbgl-test` / `mbgl-test-runner` Linux OpenGL target:PASS / up-to-date。
- [x] V3 综合过滤测试:21/21 PASS。
- [x] `git diff --check`:PASS。
- [ ] Android Gradle/AAR/APK/ADB/Jest:本 Step 不运行。
## 综合测试覆盖
过滤范围:`Source.ParticleField*`、`Source.HostField*`、`Source.RasterArrayTimeline*`、`Source.RasterArraySharesCanonicalSnapshotsWithWeatherColorLayer`、`Layer.RasterParticle*`。
最终 18 个 Source + 3 个 Layer,共 21 项全部 PASS。
## 冻结中发现并修正的测试问题
1. `HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField` 旧测试用同一 `uniform-east` payload 构造 T0/T1,正确 Runtime 会先返回 Duplicate。测试改为 east→north 两份兼容但不同内容。
2. `RasterArrayTimelineCandidatesReachReadyThroughFieldAdapter` 使用的 east/north/vortex 旧 fixture 均为 `timeIndex=0`;当前 Adapter 保留 `validTime=timeIndex+1`,导致 Timeline 正确 fail-closed。测试仅在局部 metadata 副本中改为 0/1/2,未修改共享 fixture。
3. 两处均为 test fixture 陈旧,不修改 Runtime、Duplicate 判定顺序、Timeline fail-closed 或生产 API。
对应 Codex 任务:
- `docs/40_任务/STEP-071A_V3-07_Linux综合冻结_测试Fixture修正_Codex任务_v1.md`
- `docs/40_任务/STEP-071A_V3-07_Linux综合冻结_测试Fixture修正_Codex任务_v2.md`
## 边界
- 未修改 `src/`、`include/`、shader、JNI、Android、RN runtime、WeatherV2 或 NavSea。
- 已知既有 `Layer.WeatherColorUsesIndependentFixedOpacity` 断言债不属于本 Step,不修、不作为 V3-07 gate。
- STEP-062 用户 dirty 与历史 untracked/build/cache/tgz 均保持隔离。
- 下一步:V3-08,一次 Android 编译/安装/实体机综合验收。

View File

@@ -0,0 +1,29 @@
# STEP-071 — MapLibreParticleLab V3-07 Linux 综合冻结完成报告
结论:CLOSED / PASS(Linux)。
日期:2026-09-11
## 实际完成
V3-07 在 `particle/main` 上完成 Linux 综合冻结。最终验证链从 canonical fixture、RN/TS/codegen 到 Linux OpenGL test target 和 V3 相关 Source/Layer 回归全部通过。
最终结果:
- fixture validator:PASS;
- package TypeScript:PASS;
- RN Test Host typecheck:PASS;
- TurboModule codegen:PASS;
- `mbgl-test` / `mbgl-test-runner`:PASS / up-to-date;
- V3 综合过滤:21/21 PASS;
- `git diff --check`:PASS;
- 最终验证链退出码:0。
## 冻结中修正
综合回归暴露两个旧测试 fixture 问题:Host T0/T1 使用重复 payload,以及 Timeline preload 三帧 `timeIndex` 全为 0。两处都通过测试侧局部修正解决;生产 Runtime 行为未改变。
## 未执行与边界
- 未运行 Android Gradle/AAR/APK/ADB/Jest,因此没有 Android PASS 声明。
- 未修改 `src/`、`include/`、shader、JNI、Android、RN runtime、WeatherV2 或 NavSea。
- 已知 `Layer.WeatherColorUsesIndependentFixedOpacity` 既有断言债未纳入本 Step gate,也未修复。
- STEP-062 两份用户 dirty 与历史无关 untracked/build/cache/tgz 未纳入。
## 下一步
进入 V3-08:按既定策略仅安排一次 Android 编译、安装和实体机综合验收,覆盖 V3-05/V3-06 尚未完成的 Android/JNI/RN v2 multi-plane 与四类场路径。

View File

@@ -701,12 +701,14 @@ TEST(Source, HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField) {
style::RasterArraySource source{"host-submission"};
source.setObserver(&test.styleObserver);
source.loadDescription(*test.fileSource);
const auto field = fixtureBytes("uniform-east", "field.f32");
const std::vector<char> payload(field.begin(), field.end());
const auto eastField = fixtureBytes("uniform-east", "field.f32");
const auto northField = fixtureBytes("uniform-north", "field.f32");
const std::vector<char> eastPayload(eastField.begin(), eastField.end());
const std::vector<char> northPayload(northField.begin(), northField.end());
EXPECT_EQ(source.submitHostField(localMetadata(0), payload),
EXPECT_EQ(source.submitHostField(localMetadata(0), eastPayload),
style::RasterArrayHostFieldSubmitResult::AcceptedPendingInitialWindow);
EXPECT_EQ(source.submitHostField(localMetadata(1), payload),
EXPECT_EQ(source.submitHostField(localMetadata(1), northPayload),
style::RasterArrayHostFieldSubmitResult::InitialWindowReady);
ASSERT_TRUE(source.impl().timeline);
const auto lastValid = source.impl().timeline->lastEntry().field;
@@ -715,31 +717,31 @@ TEST(Source, HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField) {
const auto fieldID = invalidIdentity.find("production-t2");
ASSERT_NE(fieldID, std::string::npos);
invalidIdentity.replace(fieldID, std::string("production-t2").size(), "");
EXPECT_EQ(source.submitHostField(invalidIdentity, payload),
EXPECT_EQ(source.submitHostField(invalidIdentity, eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
auto unsupportedLayout = localMetadata(2);
const auto layout = unsupportedLayout.find("interleaved-uv");
ASSERT_NE(layout, std::string::npos);
unsupportedLayout.replace(layout, std::string("interleaved-uv").size(), "planar-uv");
EXPECT_EQ(source.submitHostField(unsupportedLayout, payload),
EXPECT_EQ(source.submitHostField(unsupportedLayout, eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
auto unsupportedEncoding = localMetadata(2);
const auto encoding = unsupportedEncoding.find("float32-le");
ASSERT_NE(encoding, std::string::npos);
unsupportedEncoding.replace(encoding, std::string("float32-le").size(), "float64-le");
EXPECT_EQ(source.submitHostField(unsupportedEncoding, payload),
EXPECT_EQ(source.submitHostField(unsupportedEncoding, eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
auto invalidDimensions = localMetadata(2);
const auto width = invalidDimensions.find("\"width\":16");
ASSERT_NE(width, std::string::npos);
invalidDimensions.replace(width, std::string("\"width\":16").size(), "\"width\":0");
EXPECT_EQ(source.submitHostField(invalidDimensions, payload),
EXPECT_EQ(source.submitHostField(invalidDimensions, eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
auto shortPayload = payload;
auto shortPayload = eastPayload;
shortPayload.pop_back();
EXPECT_EQ(source.submitHostField(localMetadata(2), shortPayload),
style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
@@ -748,7 +750,7 @@ TEST(Source, HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField) {
const auto validTime = nonMonotonic.find("1704074400000");
ASSERT_NE(validTime, std::string::npos);
nonMonotonic.replace(validTime, std::string("1704074400000").size(), "1704069000000");
EXPECT_EQ(source.submitHostField(nonMonotonic, payload),
EXPECT_EQ(source.submitHostField(nonMonotonic, eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedNonMonotonicSnapshot);
EXPECT_EQ(source.impl().timeline->size(), 2u);
@@ -756,7 +758,7 @@ TEST(Source, HostFieldSubmissionRejectsInvalidWithoutChangingLastValidField) {
EXPECT_EQ(source.impl().timeline->frame().state, style::RasterArrayPlaybackState::Ready);
style::RasterArraySource fixtureSource{"fixture", "uniform-east", "metadata", "field"};
EXPECT_EQ(fixtureSource.submitHostField(localMetadata(2), payload),
EXPECT_EQ(fixtureSource.submitHostField(localMetadata(2), eastPayload),
style::RasterArrayHostFieldSubmitResult::RejectedSourceMode);
}
@@ -764,10 +766,16 @@ TEST(Source, RasterArrayTimelineCandidatesReachReadyThroughFieldAdapter) {
SourceTest test;
const auto eastMetadata = fixtureBytes("uniform-east", "metadata.json");
const auto eastField = fixtureBytes("uniform-east", "field.f32");
const auto northMetadata = fixtureBytes("uniform-north", "metadata.json");
auto northMetadata = fixtureBytes("uniform-north", "metadata.json");
const auto northField = fixtureBytes("uniform-north", "field.f32");
const auto vortexMetadata = fixtureBytes("vortex", "metadata.json");
auto vortexMetadata = fixtureBytes("vortex", "metadata.json");
const auto vortexField = fixtureBytes("vortex", "field.f32");
const auto northTimeIndex = northMetadata.find("\"timeIndex\": 0");
ASSERT_NE(northTimeIndex, std::string::npos);
northMetadata.replace(northTimeIndex, std::string("\"timeIndex\": 0").size(), "\"timeIndex\": 1");
const auto vortexTimeIndex = vortexMetadata.find("\"timeIndex\": 0");
ASSERT_NE(vortexTimeIndex, std::string::npos);
vortexMetadata.replace(vortexTimeIndex, std::string("\"timeIndex\": 0").size(), "\"timeIndex\": 2");
test.fileSource->sourceResponse = [&](const Resource& resource) -> Response {
Response response;
const std::string* text = nullptr;

View File

@@ -668,3 +668,8 @@ WeatherColorLayer 的 `ParticleGpuValidationUBO` designated initializer 补齐 `
- Rain 仍为明确“非真实场值”的 PMTiles decoder fallback,generation=1/2;RN Host 已将 Wave/Tide 指向 real fixture,并继续只用 `.3` `FieldRuntime` 公共 API。
- fixture validator、package tsc、Host typecheck、RN codegen 与 `/tmp/step070-ccache` 精确 Core object PASS。随后 Linux OpenGL 完整依赖闭包构建 459/459 完成,`mbgl-test-runner` 成功链接,`Source.ParticleFieldV3*` 过滤测试 6/6 PASS,验证链退出码 0,`git diff --check` PASS。Android Gradle/AAR/APK/ADB/Jest 均未运行,STEP-070 保持 IMPLEMENTED / WAITING_ANDROID_VALIDATION。
- STEP-070 implementation `8588e6fcb4f3f5d874159c2e42619c5227cb7921` 已 scoped push 到 `origin/particle/main`;STEP-062 与历史 untracked/build/cache/tgz 未纳入。
## 2026-09-11 — STEP-071 V3-07 Linux 综合冻结
- 最终冻结链:fixture validator、package tsc、RN Test Host typecheck、RN codegen、Linux OpenGL `mbgl-test`/`mbgl-test-runner` target 均 PASS;V3 综合过滤 21/21 PASS,`git diff --check` PASS,验证链退出码 0。
- 综合回归发现并修正两个测试 fixture 陈旧问题:Host T0/T1 重复 GPU content 导致 Duplicate,以及旧 east/north/vortex `timeIndex=0` 导致 Timeline fail-closed。只改测试数据组合,不改 Runtime/Timeline/Renderer 生产逻辑。
- Android Gradle/AAR/APK/ADB/Jest 未运行;V3-07 Linux 冻结关闭,下一步统一进入 V3-08 Android 综合验收。STEP-062 用户 dirty 与历史 untracked/build/cache/tgz 未纳入。