test: add isolated no-tide PBF retained cache scenario
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
21
docs/50_Step记录/STEP-118_tide失效引用受控排除与Fast真实验证_v1.md
Normal file
21
docs/50_Step记录/STEP-118_tide失效引用受控排除与Fast真实验证_v1.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# STEP-118 tide 失效引用受控排除与Fast真实验证
|
||||||
|
|
||||||
|
日期:2026-10-11 JST。用户授权:tide Source可以删除。最终状态依RDC的实际Android验收更新,不预先标PASS。
|
||||||
|
|
||||||
|
## 基线与数据事实
|
||||||
|
|
||||||
|
Integration最新已同步main=80667d6c343411d03a760425438e3d292f452118,Particle原main=fb8e78ac1ab65ccafa6fd2b181d038b90d9a00c3。STEP116旧实机原STATIC_RC:生产基础63层;Native65层、R[0,63)、C[63,65)、FastDelta0、FullDelta920,R Build/Reuse0、fallback=missing-source、missingSourceIds=[tide]、PlacementGate=false。STEP117生产Renderer/24Gate无改动,严格Android NDK C++20语法检查已通过。
|
||||||
|
|
||||||
|
完整旧设备JSON显示生产sources只有mapple和navsea_delivery,根本没有tide;63基础Layer中只有index2的tide-spots Symbol依赖tide。故删除不存在的Source本身不会改变Fast。
|
||||||
|
|
||||||
|
## 本轮受控实现
|
||||||
|
|
||||||
|
不修改远端正式PBF服务器Style、不改NavSea、Shared Core、ABI/公共API、iOS或Native Fast Gate。保留原五个STEP113场景及原63层原始定义和productionFingerprint。新增独立STATIC_RC_NO_TIDE:加载同样远端Style,断言生产无tide Source、唯一引用它的Layer必须是tide-spots Symbol;在仅供本机Test App测试的JSON副本中移除这一Layer,保留其他62个原定义/顺序、同样两天气层及相机/30fps/Pause和STEP116空Annotation保护。预期Native总64、R[0,62)、C[62,64)(尚须实际设备确认)。
|
||||||
|
|
||||||
|
Parity同时保留原production63清单/fingerprint与有效62清单/test fingerprint;明确baseLayersPreserved=false,删除唯一层id/source/count,禁止将修改后的场景宣称为原63场景。Python verifier新增场景,强制校验原63/有效62/Native64、tide-spots/tide唯一排除、原Source无tide、R[0,62)、空Annotation移除、24Gate和SHA;旧五场景仍严格要求basePreserved=true。
|
||||||
|
|
||||||
|
## 验收与风险
|
||||||
|
|
||||||
|
RDC先源码检查/scoped Git提交推送,再在用户既有授权下执行Mac标准OpenGL x86_64 Validation APK构建、设备192.168.200.139:5555安装、APK SHA与已安装base.apk SHA一致、preflight、派生场景连续90秒、真实Native Cache Build/Reuse、Fast/Full增量和24Gate及截图。无新设备结果前仅称IMPLEMENTED / WAITING_ANDROID_VALIDATION。
|
||||||
|
|
||||||
|
即使消除missing-source,R Symbol Placement changed/stale与#23可能继续阻塞。Fast0仍为目标FAIL;不得通过删其它Symbol、造Source、禁用fade、修改Gate或放宽24Gate得到假Fast。后续正式PBF Style是否永久删除tide-spots由Style/NavSea负责人按跨项目协调确定,本Test App派生结果不能直接替代正式Style发行验收。
|
||||||
12
docs/60_测试/STEP-118_tide移除Android实机验收_v1.md
Normal file
12
docs/60_测试/STEP-118_tide移除Android实机验收_v1.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# STEP-118 FZG1-2 tide移除专用TestApp验收
|
||||||
|
|
||||||
|
使用Mac仓库/现有标准build_validation_apk.sh和明确Android ADB serial 192.168.200.139:5555。严格串行,每一步退出值和实际产物均需PASS才能推进。完整证据存Git忽略artifacts目录。
|
||||||
|
|
||||||
|
1. Git源HEAD锁/Integration当前main,确认只改TestApp Kotlin、Python verifier、说明及报告,两份STEP110/111未跟踪用户任务书不动。
|
||||||
|
2. 标准 ./tools/particle_android_validation/build_validation_apk.sh。核验Gradle build SUCCESS、ZIP/strip/native x86_64、APK/source commit及sha256。
|
||||||
|
3. ADB -s 192.168.200.139:5555 install -r新APK,Success后pm path/设备base.apk SHA与本地APK一致。
|
||||||
|
4. python3 -B tools/particle_android_validation/verify_step113_fast_path.py --preflight-only。必须PREFLIGHT_PASS。
|
||||||
|
5. python3 -B tools/particle_android_validation/verify_step113_fast_path.py --scenario STATIC_RC_NO_TIDE --seconds 90。验证新scene的实际Native Layer64、原生产63层manifest及同旧fingerprint、有效62基础层、C天气2层、R[0,62)、tide Source确实不存在、missingSourceIds不再包含tide。
|
||||||
|
6. 读取result.json的真实FastDelta/FullDelta、R Build/Reuse/lastFallbackReason、Gate #3/#4/#12/#13/#22/#23、decisionMismatch。退出码2 NO_FAST_OBSERVED是负结论,不允许跳过。
|
||||||
|
7. 必要时通过设备菜单EXPORT LAST SNAPSHOT并用ADB run-as取原生JSON、截屏;Native120帧24Gate的各窗口失败计数和原动画/像素效果分开验收。
|
||||||
|
8. 新场景不等于原63层:原STATIC_RC仍保留严格basePreserved=true与真实missing-source负基线。若Fast仍为0且placementGatePassed=false,结论只能是缺源局部已解除、Fast仍BLOCKED;不能冒称已获得性能收益。真正Fast>0仍需要资源/Source/Style变更回退Full及用户实际画面验收。
|
||||||
@@ -28,7 +28,7 @@ internal class Step113PbfGateScene(
|
|||||||
private val map: MapLibreMap,
|
private val map: MapLibreMap,
|
||||||
private val display: (String) -> Unit
|
private val display: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
enum class Scenario { PBF63, STATIC_RC, PARTICLE_OFF, PARTIAL_RCRR, SOURCE_CHANGE }
|
enum class Scenario { PBF63, STATIC_RC, PARTICLE_OFF, PARTIAL_RCRR, SOURCE_CHANGE, STATIC_RC_NO_TIDE }
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
private var generation = 0
|
private var generation = 0
|
||||||
@@ -123,6 +123,32 @@ internal class Step113PbfGateScene(
|
|||||||
val ids = (0 until base.length()).map { base.getJSONObject(it).getString("id") }
|
val ids = (0 until base.length()).map { base.getJSONObject(it).getString("id") }
|
||||||
check(ids.distinct().size == ids.size && "clip-outline" in ids && "sea-area-fill" in ids &&
|
check(ids.distinct().size == ids.size && "clip-outline" in ids && "sea-area-fill" in ids &&
|
||||||
"special-pattern-line-754" in ids) { "正式 Style 边界 ID 缺失或重复" }
|
"special-pattern-line-754" in ids) { "正式 Style 边界 ID 缺失或重复" }
|
||||||
|
// STEP-118: Keep the original 63-layer PBF baseline unchanged.
|
||||||
|
// Only the explicitly named user-approved variant excludes one
|
||||||
|
// orphaned Symbol layer whose tide Source is already absent.
|
||||||
|
val omitTideLayer = scenario == Scenario.STATIC_RC_NO_TIDE
|
||||||
|
val tideDependent = (0 until base.length()).filter {
|
||||||
|
base.getJSONObject(it).optString("source") == TIDE_SOURCE
|
||||||
|
}
|
||||||
|
if (omitTideLayer) {
|
||||||
|
check(!root.getJSONObject("sources").has(TIDE_SOURCE)) {
|
||||||
|
"正式Style出现真实tide Source;停止本次仅移除孤立层实验"
|
||||||
|
}
|
||||||
|
check(tideDependent.size == 1 &&
|
||||||
|
base.getJSONObject(tideDependent.single()).optString("id") == TIDE_LAYER &&
|
||||||
|
base.getJSONObject(tideDependent.single()).optString("type") == "symbol") {
|
||||||
|
"tide引用结构不符合已核验的唯一孤立Symbol层;禁止扩大删除范围"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val effectiveBase = if (omitTideLayer) JSONArray().apply {
|
||||||
|
for (i in 0 until base.length()) {
|
||||||
|
if (i !in tideDependent) put(base.getJSONObject(i))
|
||||||
|
}
|
||||||
|
} else base
|
||||||
|
val effectiveIds = (0 until effectiveBase.length()).map { effectiveBase.getJSONObject(it).getString("id") }
|
||||||
|
check(!omitTideLayer || (effectiveBase.length() == 62 && TIDE_LAYER !in effectiveIds)) {
|
||||||
|
"tide移除数量不等于1;停止"
|
||||||
|
}
|
||||||
val source = root.getJSONObject("sources").getJSONObject("navsea_delivery")
|
val source = root.getJSONObject("sources").getJSONObject("navsea_delivery")
|
||||||
check(root.optString("sprite") == SPRITE && root.optString("glyphs") == GLYPHS &&
|
check(root.optString("sprite") == SPRITE && root.optString("glyphs") == GLYPHS &&
|
||||||
source.optString("type") == "vector" && source.optJSONArray("tiles")?.optString(0) == TILES) {
|
source.optString("type") == "vector" && source.optJSONArray("tiles")?.optString(0) == TILES) {
|
||||||
@@ -136,16 +162,27 @@ internal class Step113PbfGateScene(
|
|||||||
.put("styleName", root.optString("name"))
|
.put("styleName", root.optString("name"))
|
||||||
.put("fingerprintBasis", "SDK-returned-production-json-utf8")
|
.put("fingerprintBasis", "SDK-returned-production-json-utf8")
|
||||||
.put("productionFingerprintSha256", sha256(loaded.json))
|
.put("productionFingerprintSha256", sha256(loaded.json))
|
||||||
.put("baseLayerCount", base.length()).put("baseLayersPreserved", true)
|
.put("baseLayerCount", base.length()).put("baseLayersPreserved", !omitTideLayer)
|
||||||
|
.put("effectiveBaseLayerCount", effectiveBase.length())
|
||||||
|
.put("tideLayerExcluded", omitTideLayer)
|
||||||
|
.put("excludedLayerId", if (omitTideLayer) TIDE_LAYER else JSONObject.NULL)
|
||||||
|
.put("excludedLayerSource", if (omitTideLayer) TIDE_SOURCE else JSONObject.NULL)
|
||||||
|
.put("excludedLayerCount", if (omitTideLayer) 1 else 0)
|
||||||
|
.put("tideOriginalSourcePresent", root.getJSONObject("sources").has(TIDE_SOURCE))
|
||||||
.put("full113Equivalent", false).put("missingProductionDynamicLayerDefinitions", true)
|
.put("full113Equivalent", false).put("missingProductionDynamicLayerDefinitions", true)
|
||||||
.put("dynamicParity", "测试场结构模式;缺少同版本113层完整Source/Layer/visibility/数据快照")
|
.put("dynamicParity", "测试场结构模式;缺少同版本113层完整Source/Layer/visibility/数据快照")
|
||||||
.put("navSeaSourceHead", "ad9aa043fde37f87462f31de3fbd8ce008f8424b")
|
.put("navSeaSourceHead", "ad9aa043fde37f87462f31de3fbd8ce008f8424b")
|
||||||
.put("navSeaSourceSelector", "RenderSegmentHost.tsx:RC@rn-weather-color")
|
.put("navSeaSourceSelector", "RenderSegmentHost.tsx:RC@rn-weather-color")
|
||||||
.put("deviceReferenceSelector", "用户STEP-112:RCRR_PREFIX[0,13)/[13,33)/[33,107)/[107,113)")
|
.put("deviceReferenceSelector", "用户STEP-112:RCRR_PREFIX[0,13)/[13,33)/[33,107)/[107,113)")
|
||||||
.put("baseLayerManifest", layerManifest(base)).put("sprite", root.optString("sprite"))
|
.put("baseLayerManifest", layerManifest(base))
|
||||||
|
.put("effectiveBaseLayerManifest", layerManifest(effectiveBase))
|
||||||
|
.put("sprite", root.optString("sprite"))
|
||||||
.put("glyphs", root.optString("glyphs")).put("vectorSource", JSONObject(source.toString()))
|
.put("glyphs", root.optString("glyphs")).put("vectorSource", JSONObject(source.toString()))
|
||||||
.put("productionSources", JSONObject(root.getJSONObject("sources").toString()))
|
.put("productionSources", JSONObject(root.getJSONObject("sources").toString()))
|
||||||
.put("internalAnnotationPolicy", "仅STATIC_RC加载时移除空SDK辅助层;后续添加不再移除;原63层及Source原样")
|
.put("internalAnnotationPolicy", "仅STATIC_RC及STATIC_RC_NO_TIDE加载时移除空SDK辅助层;后续添加不再移除")
|
||||||
|
.put("styleAdjustmentScope", if (omitTideLayer)
|
||||||
|
"TEST-APP-ONLY;原生产Style63层不变;测试副本排除唯一tide-spots→tide孤立Symbol层" else
|
||||||
|
"NONE;生产Style基础63层原样")
|
||||||
val hasField = scenario != Scenario.PBF63
|
val hasField = scenario != Scenario.PBF63
|
||||||
if (hasField) {
|
if (hasField) {
|
||||||
val sources = root.getJSONObject("sources")
|
val sources = root.getJSONObject("sources")
|
||||||
@@ -153,9 +190,9 @@ internal class Step113PbfGateScene(
|
|||||||
sources.put(SOURCE, JSONObject().put("type", "raster-array").put("host-field-submission", true))
|
sources.put(SOURCE, JSONObject().put("type", "raster-array").put("host-field-submission", true))
|
||||||
val layers = JSONArray()
|
val layers = JSONArray()
|
||||||
val prefix = scenario == Scenario.PARTIAL_RCRR
|
val prefix = scenario == Scenario.PARTIAL_RCRR
|
||||||
for (i in 0 until base.length()) {
|
for (i in 0 until effectiveBase.length()) {
|
||||||
layers.put(base.getJSONObject(i))
|
layers.put(effectiveBase.getJSONObject(i))
|
||||||
if (prefix && base.getJSONObject(i).getString("id") == "clip-outline") addFields(layers)
|
if (prefix && effectiveBase.getJSONObject(i).getString("id") == "clip-outline") addFields(layers)
|
||||||
}
|
}
|
||||||
if (!prefix) addFields(layers) // NavSea current source fragment appends to top.
|
if (!prefix) addFields(layers) // NavSea current source fragment appends to top.
|
||||||
if (scenario == Scenario.SOURCE_CHANGE) {
|
if (scenario == Scenario.SOURCE_CHANGE) {
|
||||||
@@ -167,18 +204,25 @@ internal class Step113PbfGateScene(
|
|||||||
}
|
}
|
||||||
root.put("layers", layers)
|
root.put("layers", layers)
|
||||||
}
|
}
|
||||||
// Prove every original definition, including visibility, survives unchanged and in order.
|
// Prove all non-excluded production definitions, including
|
||||||
|
// visibility and order, remain byte-equivalent within the JSON.
|
||||||
|
// The original 63-layer manifest/fingerprint is kept separately.
|
||||||
val finalLayers = root.getJSONArray("layers")
|
val finalLayers = root.getJSONArray("layers")
|
||||||
val retainedBase = (0 until finalLayers.length()).map { finalLayers.getJSONObject(it) }
|
val retainedBase = (0 until finalLayers.length()).map { finalLayers.getJSONObject(it) }
|
||||||
.filter { it.getString("id") in ids }
|
.filter { it.getString("id") in ids }
|
||||||
check(retainedBase.size == base.length() && retainedBase.map { it.getString("id") } == ids &&
|
check(retainedBase.size == effectiveBase.length() &&
|
||||||
retainedBase.indices.all { retainedBase[it].toString() == base.getJSONObject(it).toString() }) {
|
retainedBase.map { it.getString("id") } == effectiveIds &&
|
||||||
"基础图层等价检查失败"
|
retainedBase.indices.all {
|
||||||
|
retainedBase[it].toString() == effectiveBase.getJSONObject(it).toString()
|
||||||
|
} && (!omitTideLayer || retainedBase.none { it.optString("source") == TIDE_SOURCE })) {
|
||||||
|
"修改后的基础图层集合与用户授权的单一tide排除不一致"
|
||||||
}
|
}
|
||||||
parity.put("testStyleFingerprintSha256", sha256(root.toString()))
|
parity.put("testStyleFingerprintSha256", sha256(root.toString()))
|
||||||
.put("testJsonLayerCount", finalLayers.length())
|
.put("testJsonLayerCount", finalLayers.length())
|
||||||
.put("baseParityBasis", "同次SDK生产Style回读;完整定义逐层比较,不是设备画面证明")
|
.put("baseParityBasis", if (omitTideLayer)
|
||||||
val plan = planJson(scenario, ids.last())
|
"同次SDK生产Style63层回读;测试副本仅排除tide-spots,其余62层定义逐项比较;不等同原63层像素验收" else
|
||||||
|
"同次SDK生产Style回读;完整定义逐层比较,不是设备画面证明")
|
||||||
|
val plan = planJson(scenario, effectiveIds.last())
|
||||||
parity.put("testPlanSelector", if (scenario == Scenario.PARTIAL_RCRR)
|
parity.put("testPlanSelector", if (scenario == Scenario.PARTIAL_RCRR)
|
||||||
"结构RCRR:sea-area-fill/special-pattern-line-754/最后生产层ID;非现场113边界" else
|
"结构RCRR:sea-area-fill/special-pattern-line-754/最后生产层ID;非现场113边界" else
|
||||||
if (hasField) "当前NavSea源码RC:rn-weather-color" else "基础63层全RETAINED")
|
if (hasField) "当前NavSea源码RC:rn-weather-color" else "基础63层全RETAINED")
|
||||||
@@ -187,7 +231,9 @@ internal class Step113PbfGateScene(
|
|||||||
map.setStyle(Style.Builder().fromJson(root.toString())) testStyle@ { style ->
|
map.setStyle(Style.Builder().fromJson(root.toString())) testStyle@ { style ->
|
||||||
if (!running || epoch != generation) return@testStyle
|
if (!running || epoch != generation) return@testStyle
|
||||||
try {
|
try {
|
||||||
if (scenario == Scenario.STATIC_RC) removeEmptyAnnotationLayer(style, ids)
|
if (scenario == Scenario.STATIC_RC || scenario == Scenario.STATIC_RC_NO_TIDE) {
|
||||||
|
removeEmptyAnnotationLayer(style, ids)
|
||||||
|
}
|
||||||
map.cameraPosition = CameraPosition.Builder().target(LatLng(33.45, 129.97))
|
map.cameraPosition = CameraPosition.Builder().target(LatLng(33.45, 129.97))
|
||||||
.zoom(10.5).bearing(0.0).tilt(0.0).build()
|
.zoom(10.5).bearing(0.0).tilt(0.0).build()
|
||||||
if (hasField) initializeFields()
|
if (hasField) initializeFields()
|
||||||
@@ -344,6 +390,12 @@ internal class Step113PbfGateScene(
|
|||||||
return JSONObject().put("schema", "step113-summary").put("schemaVersion", 1).put("scenario", scenario.name)
|
return JSONObject().put("schema", "step113-summary").put("schemaVersion", 1).put("scenario", scenario.name)
|
||||||
.put("sceneReady", ready).put("blockedReason", failureReason ?: JSONObject.NULL)
|
.put("sceneReady", ready).put("blockedReason", failureReason ?: JSONObject.NULL)
|
||||||
.put("sceneGeneration", generation).put("full113Equivalent", false).put("basePreserved", parity.optBoolean("baseLayersPreserved"))
|
.put("sceneGeneration", generation).put("full113Equivalent", false).put("basePreserved", parity.optBoolean("baseLayersPreserved"))
|
||||||
|
.put("originalBaseLayerCount", parity.optInt("baseLayerCount"))
|
||||||
|
.put("effectiveBaseLayerCount", parity.optInt("effectiveBaseLayerCount"))
|
||||||
|
.put("tideLayerExcluded", parity.optBoolean("tideLayerExcluded"))
|
||||||
|
.put("excludedLayerId", parity.opt("excludedLayerId") ?: JSONObject.NULL)
|
||||||
|
.put("excludedLayerSource", parity.opt("excludedLayerSource") ?: JSONObject.NULL)
|
||||||
|
.put("excludedLayerCount", parity.optInt("excludedLayerCount"))
|
||||||
.put("fingerprint", parity.optString("productionFingerprintSha256")).put("styleUrl", STYLE_URL)
|
.put("fingerprint", parity.optString("productionFingerprintSha256")).put("styleUrl", STYLE_URL)
|
||||||
.put("actualLayers", native.optInt("styleLayerCount")).put("planValid", native.optBoolean("valid"))
|
.put("actualLayers", native.optInt("styleLayerCount")).put("planValid", native.optBoolean("valid"))
|
||||||
.put("planReason", native.optString("lastReason")).put("lifecycle", t.optLong("lifecycle"))
|
.put("planReason", native.optString("lastReason")).put("lifecycle", t.optLong("lifecycle"))
|
||||||
@@ -436,6 +488,8 @@ internal class Step113PbfGateScene(
|
|||||||
private const val PARTICLE = "rn-weather-particles"
|
private const val PARTICLE = "rn-weather-particles"
|
||||||
private const val PROBE_SOURCE = "step113-source-probe"
|
private const val PROBE_SOURCE = "step113-source-probe"
|
||||||
private const val PROBE_LAYER = "step113-source-probe-circle"
|
private const val PROBE_LAYER = "step113-source-probe-circle"
|
||||||
|
private const val TIDE_SOURCE = "tide"
|
||||||
|
private const val TIDE_LAYER = "tide-spots"
|
||||||
private const val ANNOTATION_SOURCE = "org.maplibre.annotations"
|
private const val ANNOTATION_SOURCE = "org.maplibre.annotations"
|
||||||
private const val ANNOTATION_LAYER = "org.maplibre.annotations.points"
|
private const val ANNOTATION_LAYER = "org.maplibre.annotations.points"
|
||||||
private const val FORECAST_MS = 1786060800000L
|
private const val FORECAST_MS = 1786060800000L
|
||||||
|
|||||||
@@ -135,3 +135,14 @@ FAST_OBSERVED 仅表示实际 Fast 数字增长,不代表像素等价、性能
|
|||||||
本轮只交源码,缺 `tide` 与 Symbol R 缓存准入继续 fail-closed,不能宣称原 STATIC_RC 已出现 Fast。Native AnnotationManager 在后续真实 Symbol Annotation add/update 与 Style loaded 时同步恢复缺失 points;Test App stop 重加原空层,后续 Annotation/Style 变化撤销空层验收条件,不周期删除新内容。原63层和Source定义保留,内部Annotation Source不删除;源码预期STATIC_RC由66层变65层,须以最新设备manifest核验。
|
本轮只交源码,缺 `tide` 与 Symbol R 缓存准入继续 fail-closed,不能宣称原 STATIC_RC 已出现 Fast。Native AnnotationManager 在后续真实 Symbol Annotation add/update 与 Style loaded 时同步恢复缺失 points;Test App stop 重加原空层,后续 Annotation/Style 变化撤销空层验收条件,不周期删除新内容。原63层和Source定义保留,内部Annotation Source不删除;源码预期STATIC_RC由66层变65层,须以最新设备manifest核验。
|
||||||
|
|
||||||
复测脚本现要求至少75秒,在PID出现后等待完整请求时长;建议 `--scenario STATIC_RC --seconds 90`。同generation/lifecycle、plan/base parity、24Gate、mismatch0、计数单调及blocked撤销均为必要条件。summary新增缺源ID/Placement/commands/Source表;120帧仍需用户手动导出。精确命令、用户一次手工build/install、RDC Native定向测试及阴性结果解释见 [STEP-116 专用APP验收](../../docs/60_测试/STEP-116_STEP113原PBF专用APP验收_v1.md)。历史STEP115记录保留为当时事实,不代表本轮设备已验收。
|
复测脚本现要求至少75秒,在PID出现后等待完整请求时长;建议 `--scenario STATIC_RC --seconds 90`。同generation/lifecycle、plan/base parity、24Gate、mismatch0、计数单调及blocked撤销均为必要条件。summary新增缺源ID/Placement/commands/Source表;120帧仍需用户手动导出。精确命令、用户一次手工build/install、RDC Native定向测试及阴性结果解释见 [STEP-116 专用APP验收](../../docs/60_测试/STEP-116_STEP113原PBF专用APP验收_v1.md)。历史STEP115记录保留为当时事实,不代表本轮设备已验收。
|
||||||
|
|
||||||
|
## STEP-118 用户批准排除 tide 的独立实验
|
||||||
|
|
||||||
|
2026-10-11 用户明确允许删除 tide。正式PBF的 sources 中本就不存在 tide;唯一引用它的是原63基础层中的 tide-spots(Symbol)。只运行removeSource并不能移除这个依赖。新增独立 STATIC_RC_NO_TIDE 实验场景,在 TestApp 的临时Style副本中**只移除这一条孤立Layer**,原五场景、远端PBF Style和Native24 Gate不变。原生产63层及fingerprint完整保留在Parity数据中,派生场景明确标为基础62层(加两天气层总64,SDK空Annotation移除),不允许冒充原63层生产验收。
|
||||||
|
|
||||||
|
派生场景在读取原Style时严格断言当前tide Source不存在、唯一引用层是Symbol tide-spots,否则BLOCKED。旧场景验收器仍检查basePreserved=true;新场景验收器独立要求原63/有效62/总64、唯一排除tide-spots/tide、R[0,62)、Annotation Source保留、24 Gate、SHA同版本约束、真实Native增量。源缺失被解除不代表Symbol Placement、Cache Build或Fast已经成功。
|
||||||
|
|
||||||
|
启动命令:
|
||||||
|
python3 -B tools/particle_android_validation/verify_step113_fast_path.py --scenario STATIC_RC_NO_TIDE --seconds 90
|
||||||
|
|
||||||
|
完整步骤见 docs/60_测试/STEP-118_tide移除Android实机验收_v1.md。
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ APK = ARTIFACTS / "MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk"
|
|||||||
BUILD_INFO = ARTIFACTS / "build-info.txt"
|
BUILD_INFO = ARTIFACTS / "build-info.txt"
|
||||||
PACKAGE = "org.maplibre.android.testapp"
|
PACKAGE = "org.maplibre.android.testapp"
|
||||||
ACTIVITY = f"{PACKAGE}/{PACKAGE}.activity.particle.ParticleCoreLayerValidationActivity"
|
ACTIVITY = f"{PACKAGE}/{PACKAGE}.activity.particle.ParticleCoreLayerValidationActivity"
|
||||||
SCENARIOS = ("STATIC_RC", "PBF63", "PARTICLE_OFF", "PARTIAL_RCRR", "SOURCE_CHANGE")
|
SCENARIOS = ("STATIC_RC", "PBF63", "PARTICLE_OFF", "PARTIAL_RCRR", "SOURCE_CHANGE", "STATIC_RC_NO_TIDE")
|
||||||
LOG_ENTRY = re.compile(r"^[A-Z]/STEP113Gate\(\s*(\d+)\):\s*(\{.+\})\s*$")
|
LOG_ENTRY = re.compile(r"^[A-Z]/STEP113Gate\(\s*(\d+)\):\s*(\{.+\})\s*$")
|
||||||
|
|
||||||
|
|
||||||
@@ -106,6 +106,37 @@ def extract(logcat, pid, expected):
|
|||||||
return [x[1] for x in sorted(result, key=lambda item: item[0])]
|
return [x[1] for x in sorted(result, key=lambda item: item[0])]
|
||||||
|
|
||||||
|
|
||||||
|
def scene_parity_valid(sample, scenario):
|
||||||
|
"""Fail closed on the *declared* base style, not just a loose layer count.
|
||||||
|
|
||||||
|
Original five STEP-113 scenarios require all 63 original base layers.
|
||||||
|
STEP-118 explicitly names a user-approved test-copy exclusion of the
|
||||||
|
orphaned tide-spots layer: 63 remote originals -> 62 copied basics, plus
|
||||||
|
the two known weather layers (and no empty Annotation point layer).
|
||||||
|
It is not equivalent to the unmodified original 63-layer test.
|
||||||
|
"""
|
||||||
|
if not sample.get("planValid"):
|
||||||
|
return False
|
||||||
|
if scenario != "STATIC_RC_NO_TIDE":
|
||||||
|
return sample.get("basePreserved") is True and not sample.get("tideLayerExcluded", False)
|
||||||
|
return (
|
||||||
|
sample.get("basePreserved") is False
|
||||||
|
and sample.get("tideLayerExcluded") is True
|
||||||
|
and sample.get("originalBaseLayerCount") == 63
|
||||||
|
and sample.get("effectiveBaseLayerCount") == 62
|
||||||
|
and sample.get("excludedLayerId") == "tide-spots"
|
||||||
|
and sample.get("excludedLayerSource") == "tide"
|
||||||
|
and sample.get("excludedLayerCount") == 1
|
||||||
|
and sample.get("actualLayers") == 64
|
||||||
|
and sample.get("commands", {}).get("emptyBuiltinAnnotationLayerRemoved") is True
|
||||||
|
and sample.get("commands", {}).get("annotationSourcePreserved") is True
|
||||||
|
and all(source.get("sourceId") != "tide" for source in sample.get("sources", []))
|
||||||
|
and len(sample.get("retained", [])) == 1
|
||||||
|
and sample["retained"][0].get("begin") == 0
|
||||||
|
and sample["retained"][0].get("end") == 62
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
parser = argparse.ArgumentParser(description="Check actual STEP-113 PBF on-device Fast frames")
|
parser = argparse.ArgumentParser(description="Check actual STEP-113 PBF on-device Fast frames")
|
||||||
parser.add_argument("--serial", default="192.168.200.139:5555")
|
parser.add_argument("--serial", default="192.168.200.139:5555")
|
||||||
@@ -167,7 +198,7 @@ def run():
|
|||||||
elif any((s.get("sceneGeneration"), s.get("lifecycle")) !=
|
elif any((s.get("sceneGeneration"), s.get("lifecycle")) !=
|
||||||
(start.get("sceneGeneration"), start.get("lifecycle")) for s in samples):
|
(start.get("sceneGeneration"), start.get("lifecycle")) for s in samples):
|
||||||
status = "SUMMARY_LIFECYCLE_CHANGED"
|
status = "SUMMARY_LIFECYCLE_CHANGED"
|
||||||
elif any(not s.get("planValid") or not s.get("basePreserved") for s in samples):
|
elif any(not scene_parity_valid(s, args.scenario) for s in samples):
|
||||||
status = "INVALID_PLAN_OR_BASE_PARITY"
|
status = "INVALID_PLAN_OR_BASE_PARITY"
|
||||||
elif any(len(s.get("gateFailureCounts", [])) != 24 for s in samples):
|
elif any(len(s.get("gateFailureCounts", [])) != 24 for s in samples):
|
||||||
status = "INCOMPLETE_GATE_SUMMARY"
|
status = "INCOMPLETE_GATE_SUMMARY"
|
||||||
@@ -188,7 +219,12 @@ def run():
|
|||||||
"device": args.serial,
|
"device": args.serial,
|
||||||
"appPid": process_id,
|
"appPid": process_id,
|
||||||
"scenario": args.scenario,
|
"scenario": args.scenario,
|
||||||
"sourceReport": "STEP-113 original PBF test scene; not STEP-114 smoke",
|
"sourceReport": (
|
||||||
|
"STEP-118 user-approved TEST-APP-ONLY 62-layer derivative; "
|
||||||
|
"original 63-layer PBF Style remains unmodified; not STEP-114 smoke"
|
||||||
|
if args.scenario == "STATIC_RC_NO_TIDE"
|
||||||
|
else "STEP-113 original 63-layer PBF test scene; not STEP-114 smoke"
|
||||||
|
),
|
||||||
"durationSecondsRequested": args.seconds,
|
"durationSecondsRequested": args.seconds,
|
||||||
"durationSecondsObservedAfterPid": observation_elapsed,
|
"durationSecondsObservedAfterPid": observation_elapsed,
|
||||||
"completeSummarySamples": len(samples),
|
"completeSummarySamples": len(samples),
|
||||||
|
|||||||
@@ -1183,3 +1183,5 @@ Android V3-08 单次综合验收已完成并关闭。Native `13.4.1-particle.2`
|
|||||||
- 2026-10-11 STEP-117 RDC:新加7个C++非Jest安全回归用例源码,针对缺Source/旧GPU组、Symbol首次Build及源/图像/字形/相机变更fail-closed;两个test/renderer源文件由Android NDK OpenGL/x86_64实际compile_commands.json做C++20 -fsyntax-only,均exit0(6.3s/5.3s)。生产Native/Fast Gate/APK未改,本轮不重复构建相同APK,GTest运行/真实Fast仍BLOCKED。需要Shared Core结果证书和Integration核验真实tide语义,报告见docs/20_调查/STEP-117_PBF_R首次缓存Build安全证据_v1.md。
|
- 2026-10-11 STEP-117 RDC:新加7个C++非Jest安全回归用例源码,针对缺Source/旧GPU组、Symbol首次Build及源/图像/字形/相机变更fail-closed;两个test/renderer源文件由Android NDK OpenGL/x86_64实际compile_commands.json做C++20 -fsyntax-only,均exit0(6.3s/5.3s)。生产Native/Fast Gate/APK未改,本轮不重复构建相同APK,GTest运行/真实Fast仍BLOCKED。需要Shared Core结果证书和Integration核验真实tide语义,报告见docs/20_调查/STEP-117_PBF_R首次缓存Build安全证据_v1.md。
|
||||||
|
|
||||||
- 2026-10-11 STEP-117 RDC严格编译校验收尾:发现测试辅助UpdateParameters遗漏retainedUnderlayState初始化会产生Wmissing-designated-field警告,已只补该测试字段,并以真实Android NDK C++20 compile_commands.json、保留-Werror、不再添加-Wno-error,重跑两个测试源的-fsyntax-only,全部exit0无警告。非GTest实际运行、不代表GPU/Fast PASS。
|
- 2026-10-11 STEP-117 RDC严格编译校验收尾:发现测试辅助UpdateParameters遗漏retainedUnderlayState初始化会产生Wmissing-designated-field警告,已只补该测试字段,并以真实Android NDK C++20 compile_commands.json、保留-Werror、不再添加-Wno-error,重跑两个测试源的-fsyntax-only,全部exit0无警告。非GTest实际运行、不代表GPU/Fast PASS。
|
||||||
|
|
||||||
|
- 2026-10-11 STEP-118 用户允许删除tide:旧PBF正式sources实际无tide,唯一坏引用为63基础Layer的tide-spots Symbol。RDC新增TestApp独立STATIC_RC_NO_TIDE副本,只排除唯一孤立Layer,原五场景/63正式Style/Native24Gate不变;严格Python verifier校验原63→有效62加天气2(预期Native64)及真实SHA、来源和R边界。正式远端Style未改。待本轮Mac APK与设备实测判断R missing-source是否解除、Symbol/Cache/Fast是否改善。
|
||||||
|
|||||||
Reference in New Issue
Block a user