Open
docs(framework): fix stale roadmap labels, cross-repo dead links, skill version tag #2
liu_wentaoAdd on 27 days ago
docs(framework): fix stale roadmap labels, cross-repo dead links, skill version tag #2
Open
liu_wentaoAdd on 27 days ago
Total: 34 files changed+3155-2156
M.gitignore+1-0File content is under review, please try again later
@@ -11,24 +11,24 @@ Audit and fix the entire `docs/` directory — **not limited to a single file or
11 11 
12**Hard requirements**:12**Hard requirements**:
13 13 
14-- **Bilingual alignment**: When editing `zh-cn` or `en`, if a counterpart exists, both sides must be edited together. See [references/bilingual-sync.md](.quality-toolkit/skills/docs-quality-audit/references/bilingual-sync.md).14+- **Bilingual alignment**: When editing `zh-cn` or `en`, if a counterpart exists, both sides must be edited together. See [references/bilingual-sync.md](../../.quality-toolkit/skills/docs-quality-audit/references/bilingual-sync.md).
15- **Wording**: In `docs/` prose the product name must be written **React Native** (two words); **ReactNative** is forbidden.15- **Wording**: In `docs/` prose the product name must be written **React Native** (two words); **ReactNative** is forbidden.
16 16 
17## Audit Scope17## Audit Scope
18 18 
19-| Include | Exclude (unless user names them) |19+| Include | Exclude (unless user names them) |
20-|---------|----------------------------------|20+| ----------------------------------------------- | ----------------------------------- |
21-| `docs/**/*.md` | `node_modules`, `oh_modules` |21+| `docs/**/*.md` | `node_modules`, `oh_modules` |
22| `docs/**/figures/**` and image references in md | project deps (`package.json`, etc.) |22| `docs/**/figures/**` and image references in md | project deps (`package.json`, etc.) |
23-| `docs/Samples/**/README*.md` | Samples source code and config |23+| `docs/Samples/**/README*.md` | Samples source code and config |
24 24 
25-For the locale path map, see [locale-map.md](.quality-toolkit/skills/upgrade-version/references/locale-map.md).25+For the locale path map, see [locale-map.md](../../.quality-toolkit/skills/upgrade-version/references/locale-map.md).
26 26 
27## 1. Images Not Displaying27## 1. Images Not Displaying
28 28 
29Covers: wrong reference paths, missing files, double extensions (e.g. `.png.png`), miscalculated relative paths, zh/en figures out of sync, **filenames containing `()` not percent-encoded** (e.g. `RN-alter-getNode().png`).29Covers: wrong reference paths, missing files, double extensions (e.g. `.png.png`), miscalculated relative paths, zh/en figures out of sync, **filenames containing `()` not percent-encoded** (e.g. `RN-alter-getNode().png`).
30 30 
31-**Detailed source of truth**: [references/image-audit.md](.quality-toolkit/skills/docs-quality-audit/references/image-audit.md) (encoding table, fix rules, check script).31+**Detailed source of truth**: [references/image-audit.md](../../.quality-toolkit/skills/docs-quality-audit/references/image-audit.md) (encoding table, fix rules, check script).
32 32 
331. Run the check script (recommended):331. Run the check script (recommended):
34 ```bash34 ```bash
@@ -55,7 +55,7 @@ Covers: reader-facing Chinese paragraphs in `docs/en/**`; inappropriate large En
55 55 
56Covers: external 404s, invalid anchors, outdated repo/branch references, wrong relative paths between docs, and historical version links not labeled with applicable version.56Covers: external 404s, invalid anchors, outdated repo/branch references, wrong relative paths between docs, and historical version links not labeled with applicable version.
57 57 
58-**Detailed source of truth**: [references/link-audit.md](.quality-toolkit/skills/docs-quality-audit/references/link-audit.md) (upstream RN external-link map, common relative-path mistakes, false-positive filters).58+**Detailed source of truth**: [references/link-audit.md](../../.quality-toolkit/skills/docs-quality-audit/references/link-audit.md) (upstream RN external-link map, common relative-path mistakes, false-positive filters).
59 59 
601. Collect links (markdown links and bare URLs) in `docs/**/*.md`.601. Collect links (markdown links and bare URLs) in `docs/**/*.md`.
612. Internal links: whether the relative path points to an existing file (count `../` from **the file containing the link**; after edits, must resolve-verify). Key pitfalls:612. Internal links: whether the relative path points to an existing file (count `../` from **the file containing the link**; after edits, must resolve-verify). Key pitfalls:
@@ -94,13 +94,13 @@ Covers: external 404s, invalid anchors, outdated repo/branch references, wrong r
94 94 
95**Problem**: In `environment-setup.md` / `getting-started.md` package.json examples or `npm install` / install commands, `react-native` / `@react-native/*` has been upgraded, but **a related harmony package (and cli) is still on an old major** (e.g. RN=`0.86.3`, harmony=`0.84.1`). Common after a major upgrade with missed fields, or a merge conflict where only one side was applied.95**Problem**: In `environment-setup.md` / `getting-started.md` package.json examples or `npm install` / install commands, `react-native` / `@react-native/*` has been upgraded, but **a related harmony package (and cli) is still on an old major** (e.g. RN=`0.86.3`, harmony=`0.84.1`). Common after a major upgrade with missed fields, or a merge conflict where only one side was applied.
96 96 
97-**Rule** (consistent with [upgrade-version](.quality-toolkit/skills/upgrade-version/), see [version-number-style.md](.quality-toolkit/skills/upgrade-version/references/version-number-style.md)):97+**Rule** (consistent with [upgrade-version](../../.quality-toolkit/skills/upgrade-version/), see [version-number-style.md](../../.quality-toolkit/skills/upgrade-version/references/version-number-style.md)):
98 98 
99- **Main docs** dependency/install examples use **A exact number** (matching the source of truth, e.g. `0.86.3`); do not change to `.xx`, and do not mix two major lines.99- **Main docs** dependency/install examples use **A exact number** (matching the source of truth, e.g. `0.86.3`); do not change to `.xx`, and do not mix two major lines.
100- **Samples README** dependency-install lines use **B `{MAJOR}.{MINOR}.xx`**; do not write an exact patch.100- **Samples README** dependency-install lines use **B `{MAJOR}.{MINOR}.xx`**; do not write an exact patch.
101- **release note / version index** uses **A exact number**; do not change to `.xx`.101- **release note / version index** uses **A exact number**; do not change to `.xx`.
102- Edit zh/en in pairs: `environment-setup.md` ↔ its localized counterpart; the `getting-started.md` script numbers must match.102- Edit zh/en in pairs: `environment-setup.md` ↔ its localized counterpart; the `getting-started.md` script numbers must match.
103-- **`getting-started.md` unreleased WARNING**: while the current version is **not yet officially released**, keep the TODO + WARNING in the file (see [bilingual-sync.md](.quality-toolkit/skills/upgrade-version/references/bilingual-sync.md) environment-setup section). During a quality audit, **do not** delete it as outdated content; it may only be removed after official release.103+- **`getting-started.md` unreleased WARNING**: while the current version is **not yet officially released**, keep the TODO + WARNING in the file (see [bilingual-sync.md](../../.quality-toolkit/skills/upgrade-version/references/bilingual-sync.md) environment-setup section). During a quality audit, **do not** delete it as outdated content; it may only be removed after official release.
104 104 
105**Audit steps**:105**Audit steps**:
106 106 
@@ -125,7 +125,7 @@ Covers: external 404s, invalid anchors, outdated repo/branch references, wrong r
125 125 
1261. Confirm scope: all categories, or user-specified categories/subtrees.1261. Confirm scope: all categories, or user-specified categories/subtrees.
1272. Audit by category → fix → bilingual pairing.1272. Audit by category → fix → bilingual pairing.
128-3. Verify: images per [image-audit.md](.quality-toolkit/skills/docs-quality-audit/references/image-audit.md) via script, links per [link-audit.md](.quality-toolkit/skills/docs-quality-audit/references/link-audit.md) via spot-check + resolve, locale language correct, no inappropriate `ReactNative`, environment-setup version fields consistent, paired docs `zh ✓ en ✓` or gap recorded.128+3. Verify: images per [image-audit.md](../../.quality-toolkit/skills/docs-quality-audit/references/image-audit.md) via script, links per [link-audit.md](../../.quality-toolkit/skills/docs-quality-audit/references/link-audit.md) via spot-check + resolve, locale language correct, no inappropriate `ReactNative`, environment-setup version fields consistent, paired docs `zh ✓ en ✓` or gap recorded.
1294. Output summary.1294. Output summary.
130 130 
131## Output Summary131## Output Summary
@@ -158,7 +158,7 @@ Covers: external 404s, invalid anchors, outdated repo/branch references, wrong r
158- Do not change `package.json` or other project deps unless the user explicitly asks.158- Do not change `package.json` or other project deps unless the user explicitly asks.
159- **Never edit only zh or only en** (when a paired path exists).159- **Never edit only zh or only en** (when a paired path exists).
160- **Never** use `ReactNative` in reader-facing `docs/` prose; must write `React Native`.160- **Never** use `ReactNative` in reader-facing `docs/` prose; must write `React Native`.
161-- **Do not** do release version-number matrix replacement → [upgrade-version](.quality-toolkit/skills/upgrade-version/)161+- **Do not** do release version-number matrix replacement → [upgrade-version](../../.quality-toolkit/skills/upgrade-version/)
162-- **Do not** do general English spellchecking / full cspell → [docs-spellcheck](.quality-toolkit/skills/docs-spellcheck/) (prose `ReactNative` is still fixed per this skill's rules)162+- **Do not** do general English spellchecking / full cspell → [docs-spellcheck](../../.quality-toolkit/skills/docs-spellcheck/) (prose `ReactNative` is still fixed per this skill's rules)
163 163 
164-Top-level index: [upgrade-version/SKILL.md](.quality-toolkit/skills/upgrade-version/SKILL.md)164+Top-level index: [upgrade-version/SKILL.md](../../.quality-toolkit/skills/upgrade-version/SKILL.md)
@@ -10,30 +10,31 @@ mode: subagent
10Audit and fix **spelling correctness** in `docs/` Markdown, using the repo's existing **cspell** (root `.cspell.json`) as the authority.10Audit and fix **spelling correctness** in `docs/` Markdown, using the repo's existing **cspell** (root `.cspell.json`) as the authority.
11 11 
12> **Scope boundary**: This skill only handles spelling (typos / misspellings).12> **Scope boundary**: This skill only handles spelling (typos / misspellings).
13-> - Product name `ReactNative` → `React Native` → [docs-quality-audit](.quality-toolkit/skills/docs-quality-audit/)13+>
14-> - Version-number upgrades → [upgrade-version](.quality-toolkit/skills/upgrade-version/) (docs-version-upgrade)14+> - Product name `ReactNative` → `React Native` → [docs-quality-audit](../../.quality-toolkit/skills/docs-quality-audit/)
15-> - Samples `package.json` → [upgrade-version](.quality-toolkit/skills/upgrade-version/) (samples-version-bump)15+> - Version-number upgrades → [upgrade-version](../../.quality-toolkit/skills/upgrade-version/) (docs-version-upgrade)
16-> - Third-party submodule alignment → [upgrade-version](.quality-toolkit/skills/upgrade-version/) (upgrade-third-party)16+> - Samples `package.json` → [upgrade-version](../../.quality-toolkit/skills/upgrade-version/) (samples-version-bump)
17+> - Third-party submodule alignment → [upgrade-version](../../.quality-toolkit/skills/upgrade-version/) (upgrade-third-party)
17 18 
18## Audit Scope19## Audit Scope
19 20 
20-| Include | Exclude (unless user names them) |21+| Include | Exclude (unless user names them) |
21-|---------|----------------------------------|22+| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
22-| `docs/**/*.md` (incl. `zh-cn`, `en`, `Samples/**/README*.md`) | `node_modules`, `oh_modules` |23+| `docs/**/*.md` (incl. `zh-cn`, `en`, `Samples/**/README*.md`) | `node_modules`, `oh_modules` |
23| English words in reader-facing prose, headings, lists, tables | identifiers inside code blocks, command args, URLs, file paths (unless an obvious typo safe to fix) |24| English words in reader-facing prose, headings, lists, tables | identifiers inside code blocks, command args, URLs, file paths (unless an obvious typo safe to fix) |
24-| | project deps / non-md files |25+| | project deps / non-md files |
25 26 
26-Locale path map: [locale-map.md](.quality-toolkit/skills/upgrade-version/references/locale-map.md).27+Locale path map: [locale-map.md](../../.quality-toolkit/skills/upgrade-version/references/locale-map.md).
27 28 
28**Note**: the current `.cspell.json` `ignorePaths` includes `docs/zh-cn` and `docs/Samples`. When doing a full docs audit, you must **explicitly scan these paths** (see commands below); do not treat running the default cspell as "docs passed".29**Note**: the current `.cspell.json` `ignorePaths` includes `docs/zh-cn` and `docs/Samples`. When doing a full docs audit, you must **explicitly scan these paths** (see commands below); do not treat running the default cspell as "docs passed".
29 30 
30## Tools and Sources of Truth31## Tools and Sources of Truth
31 32 
32-| Item | Path / Command |33+| Item | Path / Command |
33-|------|----------------|34+| ---------------------- | ------------------------------------------------------------------------------------------ |
34-| Dictionary and ignores | root `.cspell.json` (see `tools/spellcheck/`) |35+| Dictionary and ignores | root `.cspell.json` (see `tools/spellcheck/`) |
35-| Local run | `pnpm exec cspell --no-must-find-files <paths>` (depends on root `devDependencies.cspell`) |36+| Local run | `pnpm exec cspell --no-must-find-files <paths>` (depends on root `devDependencies.cspell`) |
36-| lint-staged | `package.json` `*.md` → `cspell --no-must-find-files` |37+| lint-staged | `package.json` `*.md` → `cspell --no-must-find-files` |
37 38 
38**When cspell is unavailable**: if `pnpm exec cspell` reports `Command "cspell" not found`, first run `pnpm i`; if it still fails, **do not just end with an empty run** — fall back to the "known typos regex" below + manual spot-check, and note in the summary that "cspell did not run".39**When cspell is unavailable**: if `pnpm exec cspell` reports `Command "cspell" not found`, first run `pnpm i`; if it still fails, **do not just end with an empty run** — fall back to the "known typos regex" below + manual spot-check, and note in the summary that "cspell did not run".
39 40 
@@ -62,15 +63,15 @@ You can also first run cspell on the **non-ignored** docs paths, then spot-check
62 63 
63For each warning / regex hit, pick one (do **not** blindly dump everything into `words`):64For each warning / regex hit, pick one (do **not** blindly dump everything into `words`):
64 65 
65-| Case | Action |66+| Case | Action |
66-|------|--------|67+| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
67-| Obvious English typo (e.g. `enviroment`, `jsBundlePrivider`, `CMakeList.txt`, `*.est` mis-write) | **Fix the prose**; if paired zh/en docs exist, sync both sides (where semantically corresponding) |68+| Obvious English typo (e.g. `enviroment`, `jsBundlePrivider`, `CMakeList.txt`, `*.est` mis-write) | **Fix the prose**; if paired zh/en docs exist, sync both sides (where semantically corresponding) |
68-| Legitimate proper noun / package name / API / HarmonyOS term (e.g. `hvigor`, `ohpm`, `ArkTS`, `FrameNode`) | If it recurs and is correct → add to `.cspell.json` `words` (lowercase entry is enough) |69+| Legitimate proper noun / package name / API / HarmonyOS term (e.g. `hvigor`, `ohpm`, `ArkTS`, `FrameNode`) | If it recurs and is correct → add to `.cspell.json` `words` (lowercase entry is enough) |
69-| **Real directory name / project path** (e.g. `MutilBundleSample/`) | **Do not change the path** (must match the repo dir); if prose writes Multi* but the link still points to that dir, keep the link and dir name consistent |70+| **Real directory name / project path** (e.g. `MutilBundleSample/`) | **Do not change the path** (must match the repo dir); if prose writes Multi\* but the link still points to that dir, keep the link and dir name consistent |
70-| External URL path fragment (e.g. a Huawei doc `decrease_pakage_size`) | **Do not change the URL** |71+| External URL path fragment (e.g. a Huawei doc `decrease_pakage_size`) | **Do not change the URL** |
71-| Correct API / class / package name in a code block | **Do not change by default** |72+| Correct API / class / package name in a code block | **Do not change by default** |
72-| A **typo** in prose or inline backticks (e.g. `` `Index.est` ``, `` `jsBundlePrivider` ``, comment `// index.est`) | **Fix it** (this is an error readers will copy; not a legitimate identifier) |73+| A **typo** in prose or inline backticks (e.g. `` `Index.est` ``, `` `jsBundlePrivider` ``, comment `// index.est`) | **Fix it** (this is an error readers will copy; not a legitimate identifier) |
73-| Cannot confirm | Mark `pending` in summary; **do not guess** |74+| Cannot confirm | Mark `pending` in summary; **do not guess** |
74 75 
75**Dictionary hygiene**: if a suspected typo is whitelisted in `.cspell.json` `words`, change it to **fix the doc** and delete the erroneous entry.76**Dictionary hygiene**: if a suspected typo is whitelisted in `.cspell.json` `words`, change it to **fix the doc** and delete the erroneous entry.
76 77 
@@ -95,11 +96,11 @@ rg -n "jsBundlePrivider|NodeControllerd|CMakeList\.txt|MyApplicaton|\.est\`|\.es
95```96```
96 97 
97Handle hits per step 3.98Handle hits per step 3.
98-**Do not** "fix" `MutilBundleSample` (a real directory name) into Multi*.99+**Do not** "fix" `MutilBundleSample` (a real directory name) into Multi\*.
99 100 
100### 6. Relationship with React Native wording101### 6. Relationship with React Native wording
101 102 
102-- Concatenated **`ReactNative`** (reader-facing prose, not a code identifier) → change to **`React Native`**; rule source: [docs-quality-audit](.quality-toolkit/skills/docs-quality-audit/)103+- Concatenated **`ReactNative`** (reader-facing prose, not a code identifier) → change to **`React Native`**; rule source: [docs-quality-audit](../../.quality-toolkit/skills/docs-quality-audit/)
103- If a spelling scan finds `ReactNative` in prose, **fix it directly per quality-audit and sync zh/en**; do not skip it because "it belongs to another skill"104- If a spelling scan finds `ReactNative` in prose, **fix it directly per quality-audit and sync zh/en**; do not skip it because "it belongs to another skill"
104- Legitimate symbols in code / paths (e.g. `ReactNativePage`, `Libraries/ReactNative/`) **must not** be force-split105- Legitimate symbols in code / paths (e.g. `ReactNativePage`, `Libraries/ReactNative/`) **must not** be force-split
105 106 
@@ -116,22 +117,26 @@ Handle hits per step 3.
116```markdown117```markdown
117## docs/ Spelling Audit Summary118## docs/ Spelling Audit Summary
118 119 
119-| Scope | Tool | Result |120+| Scope | Tool | Result |
120-|-------|------|--------|121+| -------------- | --------------- | -------------------- |
121-| docs/en … | cspell | N warnings / M fixed |122+| docs/en … | cspell | N warnings / M fixed |
122-| docs/zh-cn … | cspell / manual | … |123+| docs/zh-cn … | cspell / manual | … |
123-| Samples README | … | … |124+| Samples README | … | … |
124 125 
125### Fixed (prose edits)126### Fixed (prose edits)
127+ 
126| file:line | typo | correction |128| file:line | typo | correction |
127 129 
128### Added to dictionary (.cspell.json words)130### Added to dictionary (.cspell.json words)
131+ 
129| word | reason |132| word | reason |
130 133 
131### Pending134### Pending
135+ 
132| file:line | word | note |136| file:line | word | note |
133 137 
134### Bilingual gaps / not synced138### Bilingual gaps / not synced
139+ 
135| Chinese | English | note |140| Chinese | English | note |
136```141```
137 142 
@@ -143,4 +148,4 @@ Handle hits per step 3.
143- When unsure, mark pending; do not "guess" a correct spelling into the dictionary to fake a pass.148- When unsure, mark pending; do not "guess" a correct spelling into the dictionary to fake a pass.
144- **Do not** do release version-number matrix / Samples `package.json` upgrades.149- **Do not** do release version-number matrix / Samples `package.json` upgrades.
145 150 
146-Top-level index: [upgrade-version/SKILL.md](.quality-toolkit/skills/upgrade-version/SKILL.md)151+Top-level index: [upgrade-version/SKILL.md](../../.quality-toolkit/skills/upgrade-version/SKILL.md)
@@ -0,0 +1,301 @@
1+---
2+description: Use when editing C++ code that manages multiple coexisting instances (e.g., multiple engine instances, multiple rendering contexts, multiple view hierarchies). Checks that per-instance state is properly isolated: global singleton registry cross-instance pollution, cache clearing scope, listener deduplication, instance-scoped key naming, move-semantics registry consistency.
3+mode: subagent
4+---
5+ 
6+> **Toolkit location**: this repo vendors the quality-toolkit at `.quality-toolkit/`. Toolkit-relative paths mentioned below (e.g. `tools/…`, `standards/…`) resolve under that directory.
7+ 
8+# Instance Isolation Checks
9+ 
10+Sourced from isolation-check patterns in the target project's C++ layer actual bug-fix commits (example source path: `src/main/cpp/`).
11+ 
12+## Applicable Scenarios
13+ 
14+| Scenario | Description |
15+| ---------------------------------------- | ---------------------------------------------------------------------------------------------------- |
16+| Editing a global singleton registry | E.g. `HarmonyReactMarker`, `TextMeasureRegistry` — data structures shared across threads/instances |
17+| Implementing move-semantics classes | E.g. `ArkUINode` — classes that maintain handle→owner mappings and need move construction/assignment |
18+| Handling cache invalidation and recovery | E.g. cache loss recovery after `registerFont()` in `TextComponentInstance` |
19+| Multi-instance scenarios | Any isolation and clearing logic involving multiple coexisting instances or rendering contexts |
20+ 
21+## 1. Thread-Safe Listener Registry
22+ 
23+**Problem:** A bare `static std::vector<Listener*> listeners` field has no mutex, no cross-translation-unit initialization-order guarantee, and no deduplication. Repeated `onInit` calls silently register the same listener N times, amplifying each marker log.
24+ 
25+**Pattern:** Replace the static member with a function-local static accessed via an accessor method, guarded by a dedicated mutex.
26+ 
27+```cpp
28+// .h
29+class HarmonyReactMarker {
30+ static std::mutex& listenersMutex();
31+ static std::vector<HarmonyReactMarkerListener*>& listenersStorage();
32+ // Remove the old `static std::vector<...> listeners;` field
33+};
34+ 
35+// .cpp
36+std::mutex& HarmonyReactMarker::listenersMutex() {
37+ static std::mutex m;
38+ return m;
39+}
40+std::vector<HarmonyReactMarkerListener*>& HarmonyReactMarker::listenersStorage() {
41+ static std::vector<HarmonyReactMarkerListener*> v;
42+ return v;
43+}
44+```
45+ 
46+**Dispatch rule:** Take a snapshot of listeners inside the lock, then dispatch outside the critical section. This prevents a listener that re-enters `addListener` / `removeListener` from causing a deadlock.
47+ 
48+```cpp
49+void logMarker(...) {
50+ std::vector<HarmonyReactMarkerListener*> snapshot;
51+ {
52+ std::lock_guard<std::mutex> lock(listenersMutex());
53+ snapshot = listenersStorage();
54+ }
55+ for (auto* listener : snapshot) {
56+ listener->logMarker(markerId, tag, timestamp);
57+ }
58+}
59+```
60+ 
61+**`addListener` deduplication:**
62+ 
63+```cpp
64+void addListener(HarmonyReactMarkerListener& listener) {
65+ std::lock_guard<std::mutex> lock(listenersMutex());
66+ auto& storage = listenersStorage();
67+ if (std::find(storage.begin(), storage.end(), &listener) != storage.end()) {
68+ return;
69+ }
70+ storage.push_back(&listener);
71+}
72+```
73+ 
74+**`removeListener` must also lock:**
75+ 
76+```cpp
77+void removeListener(HarmonyReactMarkerListener& listener) {
78+ std::lock_guard<std::mutex> lock(listenersMutex());
79+ auto& storage = listenersStorage();
80+ storage.erase(
81+ std::remove(storage.begin(), storage.end(), &listener), storage.end());
82+}
83+```
84+ 
85+## 2. Per-Instance Cache Clearing (TextMeasureRegistry)
86+ 
87+**Problem:** `TextMeasurer::registerFont()` calls `TextMeasureRegistry::clear()`, erasing cached measurement data for **all** engine instances/rendering contexts. Other instances' `TextComponentInstance` may lose their styled strings on re-render.
88+ 
89+**Pattern:** Add `clearForRnInstance(int instanceId)` that only clears entries with the target instance's prefix key, and erases the shared `m_textMeasureInfoCache` as a whole (because `TextMeasureCacheKey` does not contain an instance ID — polluted entries cannot be cheaply identified).
90+ 
91+```cpp
92+void TextMeasureRegistry::clearForRnInstance(int instanceId) {
93+ std::lock_guard<std::mutex> lock(m_mutex);
94+ 
95+ const std::string prefix = std::to_string(instanceId) + "_";
96+ for (auto it = m_keyToMeasureInfo.begin();
97+ it != m_keyToMeasureInfo.end();) {
98+ if (it->first.compare(0, prefix.size(), prefix) == 0) {
99+ m_keyToCacheKey.erase(it->first);
100+ it = m_keyToMeasureInfo.erase(it);
101+ } else {
102+ ++it;
103+ }
104+ }
105+ 
106+ m_textMeasureInfoCache.clear();
107+}
108+```
109+ 
110+**In `TextMeasurer::registerFont`:**
111+ 
112+```cpp
113+m_fontCollection.reset();
114+TextMeasureRegistry::getTextMeasureRegistry().clearForRnInstance(m_instanceId);
115+```
116+ 
117+**Also fix `clear()` itself:** The old code had a duplicate `m_keyToMeasureInfo.clear()` call, missing the clearing of `m_keyToCacheKey` and `m_textMeasureInfoCache`. A correct `clear()` must clear all three maps.
118+ 
119+## 3. ArkUINode Move Semantics
120+ 
121+**Problem:** `ArkUINode`'s move constructor steals `m_nodeHandle`, but the `NODE_BY_HANDLE` registry still points to the moved-from object. The moved-from object's destructor later unregisters that handle, so the new owner no longer receives events. Double erasure may also occur.
122+ 
123+**Pattern:**
124+ 
125+### Destructor — nullptr guard
126+ 
127+```cpp
128+ArkUINode::~ArkUINode() noexcept {
129+ if (m_nodeHandle == nullptr) {
130+ return; // moved-from; new owner is responsible for cleanup
131+ }
132+ // ... normal cleanup ...
133+}
134+```
135+ 
136+### Destructor — conditional registry erasure
137+ 
138+```cpp
139+auto it = NODE_BY_HANDLE.find(m_nodeHandle);
140+if (it != NODE_BY_HANDLE.end() && it->second == this) {
141+ NODE_BY_HANDLE.erase(it);
142+}
143+```
144+ 
145+### Move constructor — redirect the registry
146+ 
147+```cpp
148+ArkUINode::ArkUINode(ArkUINode&& other) noexcept
149+ : m_nodeHandle(other.m_nodeHandle) {
150+ other.m_nodeHandle = nullptr;
151+ if (m_nodeHandle != nullptr) {
152+ std::lock_guard<std::mutex> lock(NODE_BY_HANDLE_MUTEX);
153+ NODE_BY_HANDLE[m_nodeHandle] = this;
154+ }
155+}
156+```
157+ 
158+### Move assignment — swap + update both entries
159+ 
160+```cpp
161+ArkUINode& ArkUINode::operator=(ArkUINode&& other) noexcept {
162+ std::swap(m_nodeHandle, other.m_nodeHandle);
163+ std::lock_guard<std::mutex> lock(NODE_BY_HANDLE_MUTEX);
164+ if (m_nodeHandle != nullptr) {
165+ NODE_BY_HANDLE[m_nodeHandle] = this;
166+ }
167+ if (other.m_nodeHandle != nullptr) {
168+ NODE_BY_HANDLE[other.m_nodeHandle] = &other;
169+ }
170+ return *this;
171+}
172+```
173+ 
174+## 4. TextComponentInstance Cache-Miss Recovery
175+ 
176+**Problem:** After `registerFont()` clears the current instance's cache bucket, subsequent `onStateChanged` can't find the `TextMeasureInfo` for the corresponding key in the registry and silently skips — text nodes don't display styled content.
177+ 
178+**Pattern:** Add `buildTextMeasureInfoFromState()` that re-runs the standard measurement path when the registry lookup misses:
179+ 
180+```cpp
181+std::optional<std::shared_ptr<TextMeasureInfo>>
182+TextComponentInstance::buildTextMeasureInfoFromState(
183+ facebook::react::ParagraphState const& stateData) {
184+ auto textLayoutManager = stateData.paragraphLayoutManager.getTextLayoutManager();
185+ if (textLayoutManager == nullptr) return std::nullopt;
186+ auto nativeTextLayoutManager = textLayoutManager->getNativeTextLayoutManager();
187+ auto textMeasurer = static_cast<TextMeasurer*>(nativeTextLayoutManager);
188+ if (textMeasurer == nullptr) return std::nullopt;
189+ 
190+ auto contentSize = m_layoutMetrics.getContentFrame().size;
191+ if (std::isnan(contentSize.width) || contentSize.width <= 0) return std::nullopt;
192+ 
193+ auto attributedString = stateData.attributedString;
194+ auto paragraphAttributes = stateData.paragraphAttributes;
195+ auto layoutConstraints = facebook::react::LayoutConstraints{
196+ {.width = 0, .height = 0},
197+ contentSize,
198+ facebook::react::LayoutDirection::Undefined};
199+ 
200+ textMeasurer->measure(attributedString, paragraphAttributes, layoutConstraints);
201+ return TextMeasureRegistry::getTextMeasureRegistry().getTextMeasureInfoByKey(m_key);
202+}
203+```
204+ 
205+**In `onStateChanged`:**
206+ 
207+```cpp
208+auto info = TextMeasureRegistry::getTextMeasureRegistry().getTextMeasureInfoByKey(m_key);
209+if (!info.has_value()) {
210+ info = buildTextMeasureInfoFromState(stateData);
211+}
212+```
213+ 
214+**Also simplify `onStateChanged`:** Remove the `m_childNodes` vector and the child-node removal loop in the destructor and `onStateChanged`. Remove the old duplicate `m_textNode.setTextContent()` calls. Instead, always call `setTextContent` **outside** the `info.has_value()` branch, ensuring plain text is always set even when styled content is unavailable.
215+ 
216+## 5. Multi-engine instance and Multi-context Isolation
217+ 
218+The target project runs multiple `engine instance`s in the same process (each instance has multiple `rendering context`s). Any global/singleton registry or cache must respect this topology: an operation triggered by one instance **must never** corrupt state owned by another instance or rendering context.
219+ 
220+### 5.1 Singleton registries must be instance-aware
221+ 
222+**Anti-pattern:** The singleton `TextMeasureRegistry` stores all instances' entries in one flat map. Calling `clear()` on it (e.g. triggered from `registerFont`) erases all instances' data.
223+ 
224+**Correct pattern:** Use a `{instanceId}_` prefix to name keyed entries, so per-instance clearing can be selective:
225+ 
226+```
227+key = "<instanceId>_<tag>_<rendering contextId>"
228+```
229+ 
230+When one instance triggers invalidation, use `clearForRnInstance(id)` to erase only entries matching that prefix. Other instances' entries are preserved.
231+ 
232+### 5.2 Shared caches without an instance ID must be handled carefully
233+ 
234+Some caches (e.g. `m_textMeasureInfoCache` keyed by `TextMeasureCacheKey = {attributedString, paragraphAttributes, layoutConstraints}`) do not contain `instanceId`. Two instances may produce the same cacheKey but have different `fontCollection` / `TextMeasureInfo` state. After instance A registers a new font, instance B may still hold a stale `TextMeasureInfo` cache measured with the old font.
235+ 
236+**Strategy:** When erasing keyed entries per instance, also erase the shared cache as a whole. Polluted entries cannot be cheaply identified; the conservative approach is to clear all and let each instance lazily rebuild on the next `measure()` call. This is safe:
237+ 
238+- Other instances' keyed entries (`m_keyToMeasureInfo`) are preserved; their `TextComponentInstance::onStateChanged` lookups still succeed.
239+- `m_textMeasureInfoCache` entries will be correctly rebuilt by each instance's next standard `measure()` path, using its own `fontCollection`.
240+ 
241+> ⚠️ **Never skip the shared cache erasure** — skipping it "to preserve other instances' measurement data" is wrong. Stale cache entries silently replace fallback glyphs with the newly registered font in whichever instance hits them first.
242+ 
243+### 5.3 Multi-instance Listener registration
244+ 
245+`HarmonyReactMarker` is a process-level singleton. Multiple `engine instance`s each add their own `HarmonyReactMarkerListener`. On reload, `onInit` runs again for the same instance, possibly re-adding the same listener.
246+ 
247+**Rules:**
248+ 
249+- Deduplicate in `addListener` — `std::find` check prevents N-fold registration from repeated `onInit`.
250+- `removeListener` must erase all occurrences (erase-remove idiom), not just the first — defending against pre-existing duplicates from before the fix.
251+- Never assume "only one instance exists" when designing listener lifetimes. A second instance can `addListener` during the first's dispatch; the snapshot pattern (§1) handles this correctly.
252+ 
253+### 5.4 ComponentInstance key naming (multi-context)
254+ 
255+`TextComponentInstance` constructs the registry key as:
256+ 
257+```
258+m_key = "<instanceId>_<tag>_<rendering contextId>"
259+```
260+ 
261+`rendering contextId` is **required**. Without it, two rendering contexts in the same instance that render the same shadow-view tag `<Text>` would collide on the same key, and clearing one rendering context's entry would break the other.
262+ 
263+**Rule:** In a global registry, the registry key identifying a component must include **all** scope dimensions: `instanceId + tag + rendering contextId`. Never use just `tag` or just `tag + rendering contextId` in a global registry.
264+ 
265+### 5.5 Cross-instance move semantics and NODE_BY_HANDLE
266+ 
267+`NODE_BY_HANDLE` is a global mapping: `ArkUI_NodeHandle → ArkUINode*`. Multiple instances concurrently create nodes, and nodes may be moved (e.g. during reconciliation). A move must:
268+ 
269+1. Null out the moved-from object's `m_nodeHandle` (making its destructor a no-op).
270+2. Update `NODE_BY_HANDLE` to point to the new owner.
271+3. The moved-from object's destructor must erase the registry entry only when `it->second == this` — otherwise it may erase the entry the new owner just installed.
272+ 
273+**Rule:** Never assume the global handle→owner mapping has only one writer. Lock before every change; check identity before erasing.
274+ 
275+### 5.6 Test example: TextMutationWithoutMeasureExample
276+ 
277+When adding or modifying test examples for multi-instance scenarios, avoid triggering text re-measurement across unrelated instances. `TextMutationWithoutMeasureExample` demonstrates:
278+ 
279+- Toggling `selectable` / `accessibilityLabel` (non-layout properties) rather than changing text content or style — avoiding layout recalculation that could spill into the shared `TextMeasureRegistry`.
280+- Mutating a sibling `View` (backgroundColor/opacity) rather than `<Text>` itself — sibling mutations don't invalidate Text's measurement cache.
281+- This pattern is especially important in multi-context scenarios, where one rendering context's animation should not force another rendering context to re-measure its text.
282+ 
283+## Prohibitions
284+ 
285+| ❌ Don't | ✅ Do |
286+| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
287+| Call global `clear()` in per-instance code | Use `clearForRnInstance(id)` or a similar scope-limited invalidation |
288+| Use only `tag` or only `tag+rendering contextId` as the key in a global registry | The key must include `instanceId + tag + rendering contextId` |
289+| Skip shared-cache erasure "to preserve other instances' data" | Erase the shared cache as a whole; stale entries silently produce wrong results |
290+| Register listeners without deduplication | `addListener` must `std::find` to deduplicate |
291+| Mutate the global handle→owner mapping without locking | Lock before every change; check `it->second == this` before erasing |
292+| Silently skip on cache miss in `onStateChanged` | Re-run the standard measurement path to recover the cache |
293+| Use a bare `static std::vector<>` as a cross-thread registry | Function-local static + mutex + accessor |
294+ 
295+## General Rules
296+ 
297+- Any static registry shared across threads **must** have a mutex. Use a function-local static to guarantee safe initialization order.
298+- A global registry's `clear()` is almost always wrong when the trigger source is per-instance. Prefer prefix-scoped clearing + whole shared-cache erasure.
299+- Classes with move semantics that maintain a handle→owner mapping **must** update the mapping in move construction/assignment and guard with a nullptr check in the destructor.
300+- A cache miss in `onStateChanged` should be recovered by re-running the standard measurement path, not silently skipped.
301+- Always include `<algorithm>` when using `std::find` or `std::remove`.
@@ -7,6 +7,18 @@ mode: subagent
7 7 
8# Pre-commit Best Practices8# Pre-commit Best Practices
9 9 
10+## ⛔ Hook Integrity Rules (MANDATORY)
11+ 
12+When setting up or reviewing hooks, NEVER:
13+ 
14+1. Use `--no-verify` on any git command without explicit user confirmation
15+2. Clear `core.hooksPath` to disable hooks
16+3. Delete or rename hook files to bypass checks
17+4. Modify hook content to weaken validations
18+ 
19+If a hook fails, the correct response is to **fix the code, not weaken the hook**.
20+If a hook produces a false positive, report to the user and wait for confirmation before any bypass.
21+ 
10## Architecture: Husky + lint-staged (Node/pnpm ecosystem)22## Architecture: Husky + lint-staged (Node/pnpm ecosystem)
11 23 
12Use Husky to manage Git hooks, and lint-staged to run targeted checks on staged files. Do not use the Python `pre-commit` framework.24Use Husky to manage Git hooks, and lint-staged to run targeted checks on staged files. Do not use the Python `pre-commit` framework.
@@ -15,86 +27,87 @@ Use Husky to manage Git hooks, and lint-staged to run targeted checks on staged
15 27 
16## Hook Layout28## Hook Layout
17 29 
18-| Hook | File | Purpose | Tool |30+| Hook | File | Purpose | Tool |
19-| ------------ | ------------------- | ------------------------------------ | ---------------------- |31+| ------------ | ------------------- | -------------------------------------- | ---------------------- |
20-| `pre-commit` | `.husky/pre-commit` | Fast checks on staged files | gitleaks + lint-staged |32+| `pre-commit` | `.husky/pre-commit` | Fast checks on staged files | gitleaks + lint-staged |
21-| `commit-msg` | `.husky/commit-msg` | Commit message spec | commitlint |33+| `commit-msg` | `.husky/commit-msg` | Commit message spec | commitlint |
22| `pre-push` | `.husky/pre-push` | Heavy checks (typecheck + test + docs) | verify command |34| `pre-push` | `.husky/pre-push` | Heavy checks (typecheck + test + docs) | verify command |
23 35 
24## lint-staged Rules (package.json)36## lint-staged Rules (package.json)
25 37 
26-| glob match | Command | Description |38+The authoritative source is `tools/lint-staged/lint-staged.config.js` (location-independent, `__dirname`-based tool paths). The table below mirrors it exactly:
27-| --------------------------------------- | --------------------------------------------- | ------------------------------------ |39+ 
28-| `*.{js,jsx,ts,tsx,ets}` | prettier --write + eslint --fix | JS/TS/ArkTS formatting and lint |40+| glob match | Command | Description |
29-| `*.{json,yaml,yml}` | check-config-files.sh + prettier --write | JSON/YAML syntax validation + format |41+| ----------------------- | ------------------------------------------------ | ---------------------------------------------------------- |
30-| `*.md` | cspell --no-must-find-files + prettier --write | Spell check + format |42+| `*.{js,jsx,ts,tsx,ets}` | prettier --write | JS/TS/ArkTS formatting |
31-| `*.{cpp,h}` | ts-node format-cpp.ts | C++ clang-format auto-fix |43+| `*.{json,yaml,yml}` | ts-node check-config-files.ts + prettier --write | JSON/YAML syntax validation + format |
32-| `*` | pre-commit-checks.sh | Whitespace/line-endings/large files/merge conflict markers |44+| `*.md` | cspell --no-must-find-files + prettier --write | Spell check + format |
33-| `*.{ts,tsx,js,jsx,ets,cpp,h,c,java,kt}` | check-license-header.sh | License/Copyright header check |45+| `*.{cpp,h}` | ts-node format-cpp.ts | C++ clang-format auto-fix |
46+| `*` | ts-node pre-commit-checks.ts | Whitespace/line-endings/large files/merge conflict markers |
47+ 
48+> **Note**: eslint and license-header checks are NOT part of the toolkit's baseline lint-staged config. Projects that need them should add their own entries (e.g. `"*.{ts,tsx}": ["eslint --fix"]`) in a project-level `.lintstagedrc.json` layered over this config.
34 49 
35## commitlint Custom Rules50## commitlint Custom Rules
36 51 
37The tool layer `tools/commitlint/commitlint.config.js` is based on `@commitlint/config-conventional`; projects can layer custom business rules on this baseline. The table below lists **example custom rules** sourced from the RNOH project, for target projects to adopt or trim as needed:52The tool layer `tools/commitlint/commitlint.config.js` is based on `@commitlint/config-conventional`; projects can layer custom business rules on this baseline. The table below lists **example custom rules** sourced from the RNOH project, for target projects to adopt or trim as needed:
38 53 
39-| Example rule name (from RNOH) | Level | Description | Applicable scenario |54+| Example rule name (from RNOH) | Level | Description | Applicable scenario |
40-| -------------------------------- | ---------- | ------------------------------------------------------------------------ | ---------------------- |55+| ------------------------------------ | ---------- | --------------------------------------------------------------------------------- | ------------------------------------ |
41-| `dco-email-match` (example) | error | Signed-off-by must contain an email address | DCO compliance |56+| `dco-email-match` (example) | error | Signed-off-by must contain an email address | DCO compliance |
42-| `body-required` (example) | error | feat/fix/refactor/perf/deprecated/removed/security types must include body | Significant changes need explanation |57+| `body-required` (example) | error | feat/fix/refactor/perf/deprecated/removed/security types must include body | Significant changes need explanation |
43-| `symbols-change-warning` (example) | info | Symbol changes should explain symbol-handling status | Native-layer release |58+| `symbols-change-warning` (example) | info | Symbol changes should explain symbol-handling status | Native-layer release |
44-| `native-change-warning` (example) | info | Native-layer changes should explain `.so` impact | Native-layer release |59+| `native-change-warning` (example) | info | Native-layer changes should explain `.so` impact | Native-layer release |
45-| `build-change-warning` (example) | info | Build changes should explain Debug/Release impact | Build config changes |60+| `build-change-warning` (example) | info | Build changes should explain Debug/Release impact | Build config changes |
46-| `component-change-warning` (example) | info | Component changes should explain compatibility and API changes | Component library release |61+| `component-change-warning` (example) | info | Component changes should explain compatibility and API changes | Component library release |
47-| `breaking-change-format` (example) | error/warn | BREAKING CHANGE must have a colon; suggest adding impact scope and migration plan | Breaking changes |62+| `breaking-change-format` (example) | error/warn | BREAKING CHANGE must have a colon; suggest adding impact scope and migration plan | Breaking changes |
48-| `stability-ref-warning` (example) | warn | Stability-related changes should reference historical fix docs | Stability governance |63+| `stability-ref-warning` (example) | warn | Stability-related changes should reference historical fix docs | Stability governance |
49 64 
50All custom rule error messages can carry documentation links so developers can quickly find the spec (corresponding specs are under this repository's `standards/`).65All custom rule error messages can carry documentation links so developers can quickly find the spec (corresponding specs are under this repository's `standards/`).
51 66 
52## Key Files67## Key Files
53 68 
54-| File | Purpose |69+| File | Purpose |
55-| --------------------------------- | ------------------------------------------------------------ |70+| ----------------------------------------------- | --------------------------------------------------------------------------------------------------- |
56-| `package.json` | lint-staged config, devDependencies |71+| `package.json` | lint-staged config, devDependencies |
57-| `.husky/pre-commit` | gitleaks + lint-staged |72+| `.husky/pre-commit` | gitleaks + lint-staged |
58-| `.husky/commit-msg` | commitlint |73+| `.husky/commit-msg` | commitlint |
59-| `.husky/pre-push` | verify |74+| `.husky/pre-push` | verify |
60-| `.gitleaks.toml` | gitleaks allowlist |75+| `.gitleaks.toml` | gitleaks allowlist |
61-| `.cspell.json` | cspell dictionary + ignore paths |76+| `.cspell.json` | cspell dictionary + ignore paths |
62-| `.gitmessage` | commit template (auto-configured by setup script) |77+| `.gitmessage` | commit template (auto-configured by setup script) |
63-| `commitlint.config.js` | commit spec + custom business rules (see tools/commitlint/) |78+| `commitlint.config.js` | commit spec + custom business rules (see tools/commitlint/) |
64-| `tools/gitleaks/gitleaks-check.sh` | gitleaks pre-commit wrapper script |79+| `tools/gitleaks/gitleaks-check.ts` | gitleaks pre-commit wrapper (ts-node) |
65-| `tools/pre-commit-checks/pre-commit-checks.sh` | Whitespace/line-endings/large files/merge conflict markers (accepts lint-staged file args) |80+| `tools/pre-commit-checks/pre-commit-checks.ts` | Whitespace/line-endings/large files/merge conflict markers (ts-node, accepts lint-staged file args) |
66-| `tools/pre-commit-checks/check-config-files.sh` | JSON/YAML syntax validation (accepts lint-staged file args) |81+| `tools/pre-commit-checks/check-config-files.ts` | JSON/YAML syntax validation (ts-node, accepts lint-staged file args) |
67-| `tools/pre-commit-checks/check-license-header.sh` | License/Copyright header check (accepts lint-staged file args)|82+| `tools/husky/setup-husky.ts` | Auto-create hooks + configure commit.template |
68-| `tools/husky/setup-husky.ts` | Auto-create hooks + configure commit.template |83+| `tools/format/format-cpp.ts` | C++ clang-format auto-fix |
69-| `tools/format/format-cpp.ts` | C++ clang-format auto-fix |
70 84 
71## Community Spec Coverage85## Community Spec Coverage
72 86 
73-| Community spec requirement | pre-commit coverage |87+| Community spec requirement | pre-commit coverage |
74-| --------------------------------------- | -------------------------------------------------- |88+| -------------------------------------------- | --------------------------------------------------------------------------------- |
75-| Commit format (type/scope/subject) | ✅ fully covered by commitlint |89+| Commit format (type/scope/subject) | ✅ fully covered by commitlint |
76-| Signed-off-by (DCO) | ✅ commitlint + dco-email-match example rule |90+| Signed-off-by (DCO) | ✅ commitlint + dco-email-match example rule |
77-| BREAKING CHANGE annotation | ✅ commitlint breaking-change-format example rule |91+| BREAKING CHANGE annotation | ✅ commitlint breaking-change-format example rule |
78-| Symbols/Native/Build/Component special notes | ✅ commitlint 4 warning example rules |92+| Symbols/Native/Build/Component special notes | ✅ commitlint 4 warning example rules |
79-| New file License/Copyright header | ✅ lint-staged check-license-header.sh |93+| Secret/sensitive info detection | ✅ gitleaks (pre-commit) |
80-| Secret/sensitive info detection | ✅ gitleaks (pre-commit) |94+| Commit template guidance | ✅ .gitmessage + setup script auto-config |
81-| Commit template guidance | ✅ .gitmessage + setup script auto-config |95+| Code formatting (JS/TS/C++) | ✅ prettier + clang-format (eslint is project-level, not in the toolkit baseline) |
82-| Code formatting (JS/TS/C++) | ✅ prettier + eslint + clang-format |96+| Spell check | ✅ cspell (lint-staged) |
83-| Spell check | ✅ cspell (lint-staged) |97+| Large file blocking | ✅ pre-commit-checks.ts |
84-| Large file blocking | ✅ pre-commit-checks.sh |98+| Whitespace/line-ending normalization | ✅ pre-commit-checks.ts |
85-| Whitespace/line-ending normalization | ✅ pre-commit-checks.sh |99+| JSON/YAML syntax | ✅ check-config-files.ts |
86-| JSON/YAML syntax | ✅ check-config-files.sh |100+| merge conflict markers | ✅ pre-commit-checks.ts |
87-| merge conflict markers | ✅ pre-commit-checks.sh |101+| Changelog automation | ✅ standard-version (pnpm run release) |
88-| Changelog automation | ✅ standard-version (pnpm run release) |102+| License/Copyright header | ❌ not in toolkit baseline; add per-project if needed |
89-| Branch naming spec | ⏳ Not suitable for pre-commit; recommend CI-level checks |103+| Branch naming spec | ⏳ Not suitable for pre-commit; recommend CI-level checks |
90-| Test coverage | ⏳ Not suitable for pre-commit; covered by pre-push verify + CI |104+| Test coverage | ⏳ Not suitable for pre-commit; covered by pre-push verify + CI |
91 105 
92## Verification106## Verification
93 107 
94```bash108```bash
95pnpm exec lint-staged --diff="HEAD" --verbose109pnpm exec lint-staged --diff="HEAD" --verbose
96pnpm exec cspell --no-must-find-files README.md110pnpm exec cspell --no-must-find-files README.md
97-sh ./tools/pre-commit-checks/check-license-header.sh src/App.tsx111+pnpm exec ts-node ./tools/gitleaks/gitleaks-check.ts
98-sh ./tools/gitleaks/gitleaks-check.sh
99node -e "require('./commitlint.config.js'); console.log('commitlint OK')"112node -e "require('./commitlint.config.js'); console.log('commitlint OK')"
100```113```
@@ -0,0 +1,205 @@
1+---
2+description: Perform a local pre-commit code review on staged changes before they are committed. Reads the staged diff, applies evidence-based review methodology (logic correctness, architecture, performance, thread safety), and outputs structured findings with severity levels. Triggers when the user mentions pre-commit review, local code review, review before commit, or when the pre-commit-review tool is invoked.
3+mode: subagent
4+---
5+ 
6+> **Toolkit location**: this repo vendors the quality-toolkit at `.quality-toolkit/`. Toolkit-relative paths mentioned below (e.g. `tools/…`, `standards/…`) resolve under that directory.
7+ 
8+# Pre-Commit Code Review (Closed Loop)
9+ 
10+Review staged changes locally before committing, using the evidence-based methodology from [`../../standards/review/review-guidelines.md`](../../.quality-toolkit/standards/review/review-guidelines.md). **Critical findings block the commit** until resolved.
11+ 
12+## ⛔ Bypass Protection (MANDATORY)
13+ 
14+You MUST NOT bypass the quality gate. Specifically:
15+ 
16+1. **NEVER use `git commit --no-verify`** unless the user has explicitly confirmed after you reported the issue.
17+2. **NEVER use `git push --no-verify`** — the push-guard will catch bypassed commits.
18+3. **NEVER clear `core.hooksPath`** to skip hooks.
19+4. **NEVER delete or modify hook files** (`.husky/*`, `.githooks/*`).
20+ 
21+If the pre-commit hook blocks your commit:
22+ 
23+1. Read the error output — it lists the exact Critical/Major findings with file:line and trigger paths
24+2. **Fix the underlying issue** — do not weaken the check or bypass the gate
25+3. Re-stage: `git add <fixed-files>`
26+4. Re-commit (the gate will re-run the AI review automatically)
27+5. Only if you believe a finding is a false positive:
28+ - Report to the user with the finding details and your reasoning
29+ - **Wait for explicit user confirmation** ("yes, skip this" or similar)
30+ - Only then use `--no-verify`, and document why in the commit message body
31+ 
32+The push-guard (`push-guard.js` in `.husky/pre-push`) independently validates ALL commits in the push range. Even if a commit was made with `--no-verify`, the push will be blocked if it violates format/DCO/test-reference rules. **Bypassing at commit time does NOT bypass at push time.**
33+ 
34+## Closed-loop flow
35+ 
36+```
37+┌─────────────────────────────────────────────────────────────────┐
38+│ 1. AI Agent reviews staged diff │
39+│ (this skill + review-guidelines.md) │
40+│ Output: structured findings (Critical/Major/Minor) │
41+├─────────────────────────────────────────────────────────────────┤
42+│ 2. AI Agent records findings │
43+│ node .quality-toolkit/tools/pre-commit-review/ │
44+│ pre-commit-review.js --record '{"findings":[…]}' │
45+├─────────────────────────────────────────────────────────────────┤
46+│ 3. git commit → .husky/pre-commit → --gate │
47+│ Critical unresolved → ❌ BLOCKED (fix → re-stage → re-review) │
48+│ Critical resolved → ⚠️ WARN (Major/Minor shown) → proceed │
49+│ Clean → ✓ commit allowed │
50+├─────────────────────────────────────────────────────────────────┤
51+│ 4. git push → .husky/pre-push → pnpm harness │
52+│ Full 185-rule validation (all files, all categories) │
53+└─────────────────────────────────────────────────────────────────┘
54+```
55+ 
56+## How to trigger
57+ 
58+When you (the AI agent) are about to commit code:
59+ 
60+```bash
61+# Step 1: Generate review context
62+node .quality-toolkit/tools/pre-commit-review/pre-commit-review.js
63+ 
64+# Step 2: Review the staged diff using the guidelines (see below)
65+ 
66+# Step 3: Record your findings
67+node .quality-toolkit/tools/pre-commit-review/pre-commit-review.js \
68+ --record '{"findings":[
69+ {"severity":"critical","file":"src/a.ts","line":42,"description":"...","triggerPath":"...","status":"open"},
70+ {"severity":"major","file":"src/b.ts","line":10,"description":"...","status":"open"}
71+ ]}'
72+ 
73+# Step 4: If Critical findings → fix them, re-stage, go to Step 1
74+# Step 5: When clean → git commit (the --gate in pre-commit hook verifies)
75+```
76+ 
77+## Review execution
78+ 
79+1. **Read the guidelines** (mandatory): [`../../standards/review/review-guidelines.md`](../../.quality-toolkit/standards/review/review-guidelines.md) — 366-line evidence-based methodology covering logic correctness, architecture, performance, thread safety, polymorphic call-path detection, and evidence requirements.
80+ 
81+2. **Read the review context**: The `pre-commit-review.js` script outputs a JSON context with:
82+ 
83+ - Changed files (sorted by priority: code > config > other)
84+ - Diff hunks with context lines
85+ - File metadata (added/removed line counts)
86+ - Surrounding file context for small files
87+ 
88+3. **Perform the review** following the guidelines:
89+ 
90+ - Focus on logic correctness, architecture, performance, thread safety
91+ - Avoid generic coding style feedback (naming, formatting, missing comments)
92+ - Every finding MUST have: specific file + line, trigger path, code evidence
93+ - Use deterministic language — no "possibly", "maybe", "seems like"
94+ 
95+4. **Output structured findings**:
96+ 
97+```
98+## Pre-Commit Review Findings
99+ 
100+### 🔴 Critical (blocks commit)
101+- **[代码逻辑错误]** `src/example.ts:42` — The callback captures `this` but the
102+ owning object is destroyed before the callback fires.
103+ Trigger path: `onMount() → setTimeout(5000) → component unmounted at 2000ms →
104+ callback fires → this.refs.list is null → crash`
105+ Evidence: Read `src/example.ts:38-50`, confirmed no null guard.
106+ 
107+### 🟡 Major (should fix before commit)
108+- **[性能问题]** `src/render.ts:120` — `JSON.parse(JSON.stringify(data))` in a
109+ render loop for a 10k-item list. O(n) per frame at 60fps = 600k ops/sec.
110+ Suggestion: Use structured clone or memoize.
111+ 
112+### 🟢 Minor (can fix later)
113+- **[软件设计]** `src/utils.ts:15` — Circular dependency between utils.ts and
114+ helpers.ts. Consider extracting shared types to a separate file.
115+ 
116+### Summary
117+| Severity | Count | Verdict |
118+|----------|-------|---------|
119+| Critical | 1 | ❌ BLOCK — fix before commit |
120+| Major | 1 | ⚠️ WARN — strongly recommended |
121+| Minor | 1 | ✅ OK — can proceed |
122+```
123+ 
124+## Evidence requirements (from review-guidelines.md)
125+ 
126+Every finding must satisfy ALL of:
127+ 
128+1. **Specific code reference**: file + line number, no vague "somewhere in this file"
129+2. **Trigger path**: complete execution path (function → condition → timing → consequence)
130+3. **Based on read code**: judgments from actual file content, not speculation
131+4. **Excluded safe paths**: if the code path is unreachable (protected by caller), don't report
132+5. **Deterministic wording**: no "possibly/maybe/probably" — either it's a definite issue or a risk with clear conditions
133+6. **Distinguish certainty from risk**: "this WILL cause X" vs "under condition Y, this COULD cause X"
134+ 
135+## Severity levels
136+ 
137+| Level | Color | Meaning | Action |
138+| -------- | ----- | ---------------------------------------------------------------- | ---------------------- |
139+| Critical | 🔴 | Will cause crash, data loss, security vulnerability | Block commit |
140+| Major | 🟡 | Performance degradation, race condition, architectural violation | Strongly recommend fix |
141+| Minor | 🟢 | Code smell, maintainability concern | Can proceed |
142+ 
143+## Integration
144+ 
145+The review runs as part of the pre-commit hook chain:
146+ 
147+```
148+.husky/pre-commit:
149+ 1. gitleaks (secret scan)
150+ 2. lint-staged (formatting + syntax)
151+ 3. pre-commit-review (AI review) ← this skill
152+```
153+ 
154+## Project-specific review guides
155+ 
156+Projects can layer domain-specific review guides on top of the generic guidelines. Place them in `.quality/review-guides/` and they will be read alongside the generic guidelines.
157+ 
158+Example structure:
159+ 
160+```
161+.quality/review-guides/
162+ stability.md ← project-specific stability patterns
163+ security.md ← project-specific security requirements
164+ performance.md ← project-specific performance budgets
165+```
166+ 
167+## Gate enforcement (in .husky/pre-commit)
168+ 
169+The pre-commit hook chain enforces the closed loop:
170+ 
171+```bash
172+# .husky/pre-commit (chained after gitleaks and lint-staged):
173+node ./.quality-toolkit/tools/pre-commit-review/pre-commit-review.js --gate
174+```
175+ 
176+Gate behavior:
177+ 
178+- **Critical unresolved** → `exit 1` (commit BLOCKED, findings listed with trigger paths)
179+- **Major/Minor only** → `exit 0` (warnings shown, commit proceeds)
180+- **No findings file** → `exit 0` by default (set `QUALITY_REQUIRE_REVIEW=1` to enforce mandatory review)
181+ 
182+Bypass: `git commit --no-verify` (skips ALL hooks — use only in emergencies, CI will catch it).
183+ 
184+## Findings file format
185+ 
186+`.quality/review-findings.json`:
187+ 
188+```json
189+{
190+ "recordedAt": "2026-09-27T...",
191+ "stagedSha": "abc123def456",
192+ "findings": [
193+ {
194+ "severity": "critical|major|minor",
195+ "file": "src/example.ts",
196+ "line": 42,
197+ "description": "Clear description of the problem",
198+ "triggerPath": "function A → condition B → timing C → consequence D",
199+ "status": "open|resolved"
200+ }
201+ ]
202+}
203+```
204+ 
205+The file is auto-cleaned after successful commit (add to .gitignore or clean in post-commit).
M.opencode/agent/quality-harness.md+72-9File content is under review, please try again later
@@ -7,11 +7,21 @@ mode: subagent
7 7 
8# Submit PRs8# Submit PRs
9 9 
10+## ⛔ Bypass Protection (MANDATORY)
11+ 
12+Before any commit or push in this workflow:
13+ 
14+1. **NEVER use `--no-verify`** without explicit user confirmation after reporting the issue.
15+2. **NEVER skip the pre-commit AI review** — the gate runs automatically on `git commit`.
16+3. If the gate blocks: fix the issue, re-stage, re-commit. Do not bypass.
17+4. If you believe a finding is a false positive: report to user → wait for confirmation → only then bypass.
18+5. The push-guard in `.husky/pre-push` validates ALL commits in the push range — bypassing at commit does NOT bypass at push.
19+ 
10## Overview20## Overview
11 21 
12Automate the contribution flow for a multi-branch maintained project: apply selected diffs to target local repositories, create signed commits, push branches, create Issues/PRs from the community templates, link them, and comment `start build`.22Automate the contribution flow for a multi-branch maintained project: apply selected diffs to target local repositories, create signed commits, push branches, create Issues/PRs from the community templates, link them, and comment `start build`.
13 23 
14-> This skill depends on the tools layer `tools/generate-branch-pr/` (commit/PR generation scripts) and the standards layer `standards/community/` (commit and PR spec, Issue spec).24+> This skill depends on the tools layer `tools/submit-prs/` (multi-branch Python automation), `tools/static-check/` (static analysis), `tools/submit/` (one-command Node.js) and the standards layer `standards/community/` (commit and PR spec, Issue spec).
15 25 
16## Required Inputs26## Required Inputs
17 27 
@@ -34,7 +44,7 @@ Default primary language is `zh-CN`. If no language config is found, generate co
34Before drafting commit, PR, or Issue text, run:44Before drafting commit, PR, or Issue text, run:
35 45 
36```bash46```bash
37-python3 <skill>/scripts/submit.py language47+python3 .quality-toolkit/tools/submit-prs/scripts/submit_prs.py language
38```48```
39 49 
40Use the returned `primary_language` for all generated text. Language priority:50Use the returned `primary_language` for all generated text. Language priority:
@@ -91,10 +101,10 @@ Issue rules from this workflow:
91 101 
92The skill supports two modes. Infer the mode from the user's instruction; when ambiguous, ask.102The skill supports two modes. Infer the mode from the user's instruction; when ambiguous, ask.
93 103 
94-| Mode | When to use | Steps executed |104+| Mode | When to use | Steps executed |
95-|---|---|---|105+| --------------- | -------------------------------------------------------------------------------------------------- | -------------- |
96-| **commit-push** | User says "just commit/push", "don't create a PR", "commit first", "help me commit the code" | Steps 1–4 only |106+| **commit-push** | User says "just commit/push", "don't create a PR", "commit first", "help me commit the code" | Steps 1–4 only |
97-| **full** | User says "commit and create a PR", "run the full flow", or gives no special instruction (default) | Steps 1–9 |107+| **full** | User says "commit and create a PR", "run the full flow", or gives no special instruction (default) | Steps 1–9 |
98 108 
99In **commit-push** mode, stop after all branches are pushed. Do not create Issue, PR, or comment `start build`.109In **commit-push** mode, stop after all branches are pushed. Do not create Issue, PR, or comment `start build`.
100 110 
@@ -106,13 +116,13 @@ In **commit-push** mode, stop after all branches are pushed. Do not create Issue
106 - Before creating or switching to the feature branch, fetch the latest target baseline branch from the upstream remote, for example `git fetch upstream <branch> --prune`.116 - Before creating or switching to the feature branch, fetch the latest target baseline branch from the upstream remote, for example `git fetch upstream <branch> --prune`.
107 - Verify the fetched remote ref exists, then create/reset the feature branch from the freshly fetched upstream ref, for example `git checkout -B <feature-branch> upstream/<branch>`. Never create a feature branch from a stale local branch or previously fetched baseline.117 - Verify the fetched remote ref exists, then create/reset the feature branch from the freshly fetched upstream ref, for example `git checkout -B <feature-branch> upstream/<branch>`. Never create a feature branch from a stale local branch or previously fetched baseline.
108 - Read community docs from the freshly checked-out target baseline when available, and use those docs as the current source of truth before drafting commit/PR/Issue text:118 - Read community docs from the freshly checked-out target baseline when available, and use those docs as the current source of truth before drafting commit/PR/Issue text:
109- - `standards/community/commit-and-pr-spec.md`119+ - `standards/community/commit-and-pr-spec.md`
110- - `standards/community/issue-spec.md`120+ - `standards/community/issue-spec.md`
111- - Query the effective primary language with `python3 <skill>/scripts/submit.py language` and follow it when drafting commit/PR/Issue text.121+ - Query the effective primary language with `python3 .quality-toolkit/tools/submit-prs/scripts/submit_prs.py language` and follow it when drafting commit/PR/Issue text.
112 - Apply patch with `git apply --check` then `git apply`.122 - Apply patch with `git apply --check` then `git apply`.
113 - Run `git diff --check`.123 - Run `git diff --check`.
114 - Stage the patch (`git add <files>`).124 - Stage the patch (`git add <files>`).
115- - Run `python3 <skill>/scripts/static_check.py --staged --fix`. If the private key is not configured this is a silent no-op. Then run `clang-format --style=file:.clang-format -i` for touched `.cpp`/`.h` files, re-stage touched files, run `python3 <skill>/scripts/static_check.py --staged`, and finish with `git diff --check --cached`.125+ - Run `python3 .quality-toolkit/tools/static-check/scripts/static_check.py --staged --fix`. If the private key is not configured this is a silent no-op. Then run `clang-format --style=file:.clang-format -i` for touched `.cpp`/`.h` files, re-stage touched files, run `python3 .quality-toolkit/tools/static-check/scripts/static_check.py --staged`, and finish with `git diff --check --cached`.
116 - Commit with `git commit -s`.126 - Commit with `git commit -s`.
117 - Push to `origin`.127 - Push to `origin`.
1183. For each pushed branch, create a PR to the matching target branch.1283. For each pushed branch, create a PR to the matching target branch.
@@ -121,7 +131,7 @@ In **commit-push** mode, stop after all branches are pushed. Do not create Issue
1216. Comment `start build` on each PR.1316. Comment `start build` on each PR.
1227. Verify by querying PR URL, Issue URL, linked Issue list, and comment list.1327. Verify by querying PR URL, Issue URL, linked Issue list, and comment list.
123 133 
124-Use `scripts/submit.py` (located in this skill package, or in the tools layer `tools/generate-branch-pr/`) for repeatable operations. Run `--help` before first use.134+Use the appropriate tool: `python3 .quality-toolkit/tools/submit-prs/scripts/submit_prs.py` for multi-branch operations, or `pnpm submit` (Node.js, `tools/submit/submit.js`) for single-branch work. Run `--help` before first use.
125 135 
126## Branch Matrix Guidance136## Branch Matrix Guidance
127 137 
@@ -172,10 +182,10 @@ Commit flow requirements:
172Manual command for maintainers:182Manual command for maintainers:
173 183 
174```bash184```bash
175-python3 <skill>/scripts/static_check.py --staged --fix185+python3 .quality-toolkit/tools/static-check/scripts/static_check.py --staged --fix
176clang-format --style=file:.clang-format -i <touched-cpp-or-h-files>186clang-format --style=file:.clang-format -i <touched-cpp-or-h-files>
177git add <files>187git add <files>
178-python3 <skill>/scripts/static_check.py --staged188+python3 .quality-toolkit/tools/static-check/scripts/static_check.py --staged
179git diff --check --cached189git diff --check --cached
180```190```
181 191 
@@ -188,7 +198,7 @@ git diff --check --cached
188 - `~/.config/codex-secrets/gitcode.env`: `<HOST>_TOKEN=...`, `<HOST>_FORK_PATH=...`, or `SUBMIT_PRIMARY_LANGUAGE=...` for zsh/bash/Linux/macOS/Windows Git Bash.198 - `~/.config/codex-secrets/gitcode.env`: `<HOST>_TOKEN=...`, `<HOST>_FORK_PATH=...`, or `SUBMIT_PRIMARY_LANGUAGE=...` for zsh/bash/Linux/macOS/Windows Git Bash.
189 - `~/.config/codex-secrets/gitcode.fish`: `set -gx <HOST>_TOKEN ...`, `set -gx <HOST>_FORK_PATH ...`, or `set -gx SUBMIT_PRIMARY_LANGUAGE ...` for fish.199 - `~/.config/codex-secrets/gitcode.fish`: `set -gx <HOST>_TOKEN ...`, `set -gx <HOST>_FORK_PATH ...`, or `set -gx SUBMIT_PRIMARY_LANGUAGE ...` for fish.
190 - Validate without printing the token: `test -n "$<HOST>_TOKEN" && echo TOKEN_SET`.200 - Validate without printing the token: `test -n "$<HOST>_TOKEN" && echo TOKEN_SET`.
191- - Validate the effective primary language: `python3 <skill>/scripts/submit.py language`.201+ - Validate the effective primary language: `python3 .quality-toolkit/tools/submit-prs/scripts/submit_prs.py language`.
192 - Never print, echo, log, or write the token value.202 - Never print, echo, log, or write the token value.
193- PR create endpoint: `POST /api/v5/repos/<org>/<repo>/pulls` (adjust to target project).203- PR create endpoint: `POST /api/v5/repos/<org>/<repo>/pulls` (adjust to target project).
194- Cross-fork `head`: `<fork-owner>:<branch>`, for example from `<HOST>_FORK_PATH=yourname/repo`.204- Cross-fork `head`: `<fork-owner>:<branch>`, for example from `<HOST>_FORK_PATH=yourname/repo`.
@@ -217,3 +227,18 @@ rg -n 'PRIVATE-TOKEN: [^$]|TOKEN=.*[A-Za-z0-9_-]{20}|@[A-Za-z0-9_.-]+\\.[A-Za-z]
217```227```
218 228 
219Expected matches should only be documentation examples or generic token variable names, not token values.229Expected matches should only be documentation examples or generic token variable names, not token values.
230+ 
231+## Static-check integration
232+ 
233+PRs created via `submit_prs.py` automatically run `tools/static-check/scripts/static_check.py` on the changed files before submission. Cyclomatic complexity > 20, function length > 50 NBNC lines, or nesting depth > 4 blocks the PR until fixed. Rule sets are external (see `tools/static-check/rules/`).
234+ 
235+## Multi-version submission
236+ 
237+For projects maintaining multiple release lines (e.g., 0.84-main + 0.82-main), `submit_prs.py` creates PRs for each version simultaneously:
238+ 
239+```bash
240+python3 .quality-toolkit/tools/submit-prs/scripts/submit_prs.py create-pr \
241+ --versions 0.84 0.82 \
242+ --patch-file fix.patch \
243+ --title "fix(Component): description"
244+```
@@ -1,6 +1,6 @@
1---1---
2name: cptf-test-api2name: cptf-test-api
3-description: Use when writing user-facing test cases that consume CPTF public APIs (Test / Step / PerformanceTest / FunctionalTest / StabilityTest / UIAutomation / ExpectExecutor) for v0.2.1+. Triggers: "@Test", "@Step", "TestConfig", "StepConfig", "uiComparison", "SceneContract", "UIAutomation", "expect", "MetricCollector", "VisualComparator", "stability", "whiteScreen", "memoryLeak", "SmartMonkey", "from '@cptf/framework'", "scene contract", "Section". Do NOT use for framework internals (use cptf-framework-dev), CLI commands (use cptf-cli), or troubleshooting (use cptf-troubleshoot).3+description: Use when writing user-facing test cases that consume CPTF public APIs (Test / Step / PerformanceTest / FunctionalTest / StabilityTest / UIAutomation / ExpectExecutor) for v0.3.x. Triggers: "@Test", "@Step", "TestConfig", "StepConfig", "uiComparison", "SceneContract", "UIAutomation", "expect", "MetricCollector", "VisualComparator", "stability", "whiteScreen", "memoryLeak", "SmartMonkey", "from '@cptf/framework'", "scene contract", "Section". Do NOT use for framework internals (use cptf-framework-dev), CLI commands (use cptf-cli), or troubleshooting (use cptf-troubleshoot).
4---4---
5 5 
6# CPTF Test API Reference6# CPTF Test API Reference
@@ -35,6 +35,7 @@ export interface SceneDefinition {
35```35```
36 36 
37**Rules**:37**Rules**:
38+ 
38- Scene root testID MUST use `SCENE_TESTID_PREFIX` prefix (R4, R5)39- Scene root testID MUST use `SCENE_TESTID_PREFIX` prefix (R4, R5)
39- Section container testID MUST use `SECTION_TESTID_PREFIX` prefix (R44)40- Section container testID MUST use `SECTION_TESTID_PREFIX` prefix (R44)
40- Application layer MUST use `import type` for cptf type imports; runtime constants defined locally (R48, R49)41- Application layer MUST use `import type` for cptf type imports; runtime constants defined locally (R48, R49)
@@ -47,103 +48,139 @@ export interface SceneDefinition {
47## Enum Types (Complete Reference)48## Enum Types (Complete Reference)
48 49 
49### Platform50### Platform
51+ 
50```typescript52```typescript
51type Platform = 'harmony' | 'android' | 'ios';53type Platform = 'harmony' | 'android' | 'ios';
52```54```
55+ 
53Default: `'harmony'` (when device platform unspecified)56Default: `'harmony'` (when device platform unspecified)
54 57 
55### TestModule58### TestModule
59+ 
56```typescript60```typescript
57type TestModule = 'functional' | 'performance' | 'stability';61type TestModule = 'functional' | 'performance' | 'stability';
58```62```
59-| Value | BaseTest Default | StabilityTest Default | Usage |63+ 
60-|-------|-----------------|----------------------|-------|64+| Value | BaseTest Default | StabilityTest Default | Usage |
61-| `'performance'` | **YES** | | PerformanceTest |65+| --------------- | ---------------- | --------------------- | ------------------------------ |
62-| `'functional'` | | | FunctionalTest(含跨平台对比) |66+| `'performance'` | **YES** | | PerformanceTest |
63-| `'stability'` | | **YES** | StabilityTest |67+| `'functional'` | | | FunctionalTest(含跨平台对比) |
68+| `'stability'` | | **YES** | StabilityTest |
64 69 
65### TestPlatform70### TestPlatform
71+ 
66```typescript72```typescript
67type TestPlatform = Platform | 'all' | Platform[];73type TestPlatform = Platform | 'all' | Platform[];
68```74```
75+ 
69Default: `'all'`76Default: `'all'`
70 77 
71-| Value | Meaning |78+| Value | Meaning |
72-|-------|---------|79+| ------------------------ | ------------------------------------- |
73-| `'harmony'` | HarmonyOS only |80+| `'harmony'` | HarmonyOS only |
74-| `'android'` | Android only |81+| `'android'` | Android only |
75-| `'ios'` | iOS only (reserved) |82+| `'ios'` | iOS only (reserved) |
76-| `'all'` | All available platforms (**default**) |83+| `'all'` | All available platforms (**default**) |
77-| `['harmony', 'android']` | Array of specific platforms |84+| `['harmony', 'android']` | Array of specific platforms |
78 85 
79### PerfMetricType (13 values)86### PerfMetricType (13 values)
87+ 
80```typescript88```typescript
81type PerfMetricType =89type PerfMetricType =
82- | 'startLatency' | 'finishLatency' | 'exitLatency'90+ | 'startLatency'
83- | 'fps' | 'fpsMin' | 'fpsMax' | 'fpsJank' | 'fpsStdDev'91+ | 'finishLatency'
92+ | 'exitLatency'
93+ | 'fps'
94+ | 'fpsMin'
95+ | 'fpsMax'
96+ | 'fpsJank'
97+ | 'fpsStdDev'
84 | 'blankScreenRatio'98 | 'blankScreenRatio'
85- | 'memoryPeak' | 'memoryAvg'99+ | 'memoryPeak'
86- | 'cpuPeak' | 'cpuAvg';100+ | 'memoryAvg'
101+ | 'cpuPeak'
102+ | 'cpuAvg';
87```103```
88-| Value | Unit | Description |104+ 
89-|-------|------|-------------|105+| Value | Unit | Description |
90-| `startLatency` | ms | App start latency |106+| ------------------ | ------- | ---------------------- |
91-| `finishLatency` | ms | App finish latency |107+| `startLatency` | ms | App start latency |
92-| `exitLatency` | ms | App exit latency |108+| `finishLatency` | ms | App finish latency |
93-| `fps` | fps | Average FPS |109+| `exitLatency` | ms | App exit latency |
94-| `fpsMin` | fps | Minimum FPS |110+| `fps` | fps | Average FPS |
95-| `fpsMax` | fps | Maximum FPS |111+| `fpsMin` | fps | Minimum FPS |
96-| `fpsJank` | count | Jank frame count |112+| `fpsMax` | fps | Maximum FPS |
97-| `fpsStdDev` | fps | FPS standard deviation |113+| `fpsJank` | count | Jank frame count |
98-| `blankScreenRatio` | 0.0-1.0 | Blank screen ratio |114+| `fpsStdDev` | fps | FPS standard deviation |
99-| `memoryPeak` | KB | Peak memory usage |115+| `blankScreenRatio` | 0.0-1.0 | Blank screen ratio |
100-| `memoryAvg` | KB | Average memory usage |116+| `memoryPeak` | KB | Peak memory usage |
101-| `cpuPeak` | % | Peak CPU usage |117+| `memoryAvg` | KB | Average memory usage |
102-| `cpuAvg` | % | Average CPU usage |118+| `cpuPeak` | % | Peak CPU usage |
119+| `cpuAvg` | % | Average CPU usage |
103 120 
104### MetricType (16 values — broader than PerfMetricType)121### MetricType (16 values — broader than PerfMetricType)
122+ 
105```typescript123```typescript
106type MetricType =124type MetricType =
107- | 'fps' | 'fpsMin' | 'fpsMax' | 'fpsStdDev'125+ | 'fps'
108- | 'memoryPeak' | 'memoryAvg' | 'cpuPeak' | 'cpuAvg'126+ | 'fpsMin'
109- | 'whiteScreenRate' | 'whiteScreenCount'127+ | 'fpsMax'
110- | 'completionLatency' | 'completionLatencyAvg' | 'completionLatencyMax'128+ | 'fpsStdDev'
111- | 'crashCount' | 'jankCount' | 'droppedFrames';129+ | 'memoryPeak'
130+ | 'memoryAvg'
131+ | 'cpuPeak'
132+ | 'cpuAvg'
133+ | 'whiteScreenRate'
134+ | 'whiteScreenCount'
135+ | 'completionLatency'
136+ | 'completionLatencyAvg'
137+ | 'completionLatencyMax'
138+ | 'crashCount'
139+ | 'jankCount'
140+ | 'droppedFrames';
112```141```
113 142 
114### AggregateStrategy143### AggregateStrategy
144+ 
115```typescript145```typescript
116type AggregateStrategy = 'average' | 'median' | 'max' | 'min';146type AggregateStrategy = 'average' | 'median' | 'max' | 'min';
117```147```
118-| Value | Description | Use Case |148+ 
119-|-------|-------------|----------|149+| Value | Description | Use Case |
150+| ----------- | --------------- | ------------------------------------ |
120| `'average'` | Arithmetic mean | General (**default recommendation**) |151| `'average'` | Arithmetic mean | General (**default recommendation**) |
121-| `'median'` | Median value | Outlier-resistant |152+| `'median'` | Median value | Outlier-resistant |
122-| `'max'` | Maximum value | Worst-case analysis |153+| `'max'` | Maximum value | Worst-case analysis |
123-| `'min'` | Minimum value | Best-case analysis |154+| `'min'` | Minimum value | Best-case analysis |
124 155 
125### AlertLevel156### AlertLevel
157+ 
126```typescript158```typescript
127type AlertLevel = 'warning' | 'error' | 'critical';159type AlertLevel = 'warning' | 'error' | 'critical';
128```160```
129 161 
130### ReportFormat162### ReportFormat
163+ 
131```typescript164```typescript
132type ReportFormat = 'html' | 'markdown' | 'json' | 'all';165type ReportFormat = 'html' | 'markdown' | 'json' | 'all';
133```166```
167+ 
134Default: `'all'` (test-runner.ts); `'html'` (cptf.config.json init)168Default: `'all'` (test-runner.ts); `'html'` (cptf.config.json init)
135 169 
136### LoggerLevel170### LoggerLevel
171+ 
137```typescript172```typescript
138type LoggerLevel = 'debug' | 'info' | 'warn' | 'error';173type LoggerLevel = 'debug' | 'info' | 'warn' | 'error';
139```174```
140 175 
141### AppState.state176### AppState.state
177+ 
142```typescript178```typescript
143type AppStateValue = 'foreground' | 'background' | 'crashed' | 'not_running';179type AppStateValue = 'foreground' | 'background' | 'crashed' | 'not_running';
144```180```
145 181 
146### DeviceInfo.status182### DeviceInfo.status
183+ 
147```typescript184```typescript
148type DeviceStatus = 'connected' | 'disconnected' | 'error';185type DeviceStatus = 'connected' | 'disconnected' | 'error';
149```186```
@@ -199,60 +236,63 @@ export interface TestConfig {
199 236 
200### TestConfig Complete Property Reference237### TestConfig Complete Property Reference
201 238 
202-| Property | Type | Required | BaseTest Default | StabilityTest Default | Boundary / Notes |239+| Property | Type | Required | BaseTest Default | StabilityTest Default | Boundary / Notes |
203-|----------|------|----------|-----------------|----------------------|------------------|240+| --------------------- | --------------------- | -------- | ---------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
204-| `id` | string | YES | `'test'` | `'stability-test'` | Must match `^[a-zA-Z0-9_-]+$`; maps to `test-case-{id}` |241+| `id` | string | YES | `'test'` | `'stability-test'` | Must match `^[a-zA-Z0-9_-]+$`; maps to `test-case-{id}` |
205-| `name` | string | YES | `'测试'` | `'稳定性测试'` | Display name in reports |242+| `name` | string | YES | `'测试'` | `'稳定性测试'` | Display name in reports |
206-| `module` | TestModule | YES | `'performance'` | `'stability'` | 3 values: functional/performance/stability |243+| `module` | TestModule | YES | `'performance'` | `'stability'` | 3 values: functional/performance/stability |
207-| `platform` | TestPlatform | NO | `'all'` | `'all'` | 5 single values + Platform[] |244+| `platform` | TestPlatform | NO | `'all'` | `'all'` | 5 single values + Platform[] |
208-| `description` | string | NO | undefined | undefined | Free text |245+| `description` | string | NO | undefined | undefined | Free text |
209-| `scene` | string | NO | undefined | undefined | Must match `^[a-zA-Z0-9_-]+$`; WITHOUT "scene-" prefix; auto-prefixed by framework |246+| `scene` | string | NO | undefined | undefined | Must match `^[a-zA-Z0-9_-]+$`; WITHOUT "scene-" prefix; auto-prefixed by framework |
210-| `category` | string | NO | undefined | undefined | Must match `^[a-zA-Z0-9_-]+$`; auto-prefixed "category-" in navigation |247+| `category` | string | NO | undefined | undefined | Must match `^[a-zA-Z0-9_-]+$`; auto-prefixed "category-" in navigation |
211-| `sections` | string[] | NO | undefined | undefined | Section IDs declared in this test |248+| `sections` | string[] | NO | undefined | undefined | Section IDs declared in this test |
212-| `captureScreenshots` | boolean | NO | `true` | `false` | Stability tests default off for performance |249+| `captureScreenshots` | boolean | NO | `true` | `false` | Stability tests default off for performance |
213-| `waitAfterAction` | number | NO | `300` | undefined | ms; min=0, max=60000 practical |250+| `waitAfterAction` | number | NO | `300` | undefined | ms; min=0, max=60000 practical |
214-| `autoRecoverScene` | boolean | NO | undefined | undefined | Auto-recover when not in correct scene |251+| `autoRecoverScene` | boolean | NO | undefined | undefined | Auto-recover when not in correct scene |
215-| `autoLogProgress` | boolean | NO | undefined | undefined | Log progress every `reportInterval` iterations |252+| `autoLogProgress` | boolean | NO | undefined | undefined | Log progress every `reportInterval` iterations |
216-| `baselines` | Record\<string, any\> | NO | undefined | undefined | Key-value baseline data |253+| `baselines` | Record\<string, any\> | NO | undefined | undefined | Key-value baseline data |
217-| `expects` | ExpectCheck[] | NO | undefined | undefined | See ExpectCheck section |254+| `expects` | ExpectCheck[] | NO | undefined | undefined | See ExpectCheck section |
218-| `compareWithBaseline` | boolean | NO | undefined | undefined | Enable baseline comparison |255+| `compareWithBaseline` | boolean | NO | undefined | undefined | Enable baseline comparison |
219-| `baselinePlatform` | string | NO | undefined | undefined | Platform for baseline |256+| `baselinePlatform` | string | NO | undefined | undefined | Platform for baseline |
220-| `allowedDeviation` | number | NO | undefined | undefined | Deviation tolerance; 0=exact; typical 0-100% |257+| `allowedDeviation` | number | NO | undefined | undefined | Deviation tolerance; 0=exact; typical 0-100% |
221-| `collect` | PerfMetricType[] | NO | undefined | undefined | 1-16 metric types from PerfMetricType |258+| `collect` | PerfMetricType[] | NO | undefined | undefined | Metric types from PerfMetricType (19-member union: 17 built-in + 2 plugin). Raw channels `memory`/`cpu` are NOT members — use aggregated `memoryPeak`/`cpuAvg` etc. |
222-| `executeTimes` | number | NO | undefined | undefined | Min=1; performance test iterations |259+| `executeTimes` | number | NO | undefined | undefined | Min=1; performance test iterations |
223-| `aggregate` | AggregateStrategy | NO | undefined | undefined | 4 values: average/median/max/min |260+| `aggregate` | AggregateStrategy | NO | undefined | undefined | 4 values: average/median/max/min |
224-| `warmupIterations` | number | NO | undefined | undefined | Min=0; iterations to discard |261+| `warmupIterations` | number | NO | undefined | undefined | Min=0; iterations to discard |
225-| `cooldownMs` | number | NO | undefined | undefined | Min=0; ms between iterations |262+| `cooldownMs` | number | NO | undefined | undefined | Min=0; ms between iterations |
226-| `thresholds` | TestThreshold[] | NO | undefined | undefined | See TestThreshold section |263+| `thresholds` | TestThreshold[] | NO | undefined | undefined | See TestThreshold section |
227-| `executeTimes` | number | NO | module-dependent | Min=1; stability/performance iterations (R52: NOT repeatTimes) |264+| `executeTimes` | number | NO | module-dependent | Min=1; stability/performance iterations (R52: NOT repeatTimes) |
228-| `durationMs` | number | NO | undefined | undefined | Min=0; stability duration mode (overrides executeTimes) |265+| `durationMs` | number | NO | undefined | undefined | Min=0; stability duration mode (overrides executeTimes) |
229-| `checkAppAlive` | boolean | NO | undefined | `true` | Check app alive each iteration |266+| `checkAppAlive` | boolean | NO | undefined | `true` | Check app alive each iteration |
230-| `collectCrashLogs` | boolean | NO | undefined | `true` | Collect crash logs on crash |267+| `collectCrashLogs` | boolean | NO | undefined | `true` | Collect crash logs on crash |
231-| `intervalMs` | number | NO | `100` (fallback) | `100` | Min=0; ms between iterations; 0=no delay |268+| `intervalMs` | number | NO | `100` (fallback) | `100` | Min=0; ms between iterations; 0=no delay |
232-| `reportInterval` | number | NO | `10` (fallback) | `10` | Min=1; iterations between progress reports |269+| `reportInterval` | number | NO | `10` (fallback) | `10` | Min=1; iterations between progress reports |
233-| `checkANR` | boolean | NO | undefined | undefined | Check for ANR each iteration |270+| `checkANR` | boolean | NO | undefined | undefined | Check for ANR each iteration |
234-| `checkWhiteScreen` | boolean | NO | undefined | undefined | Check for white screen periodically (every sceneCheckInterval) |271+| `checkWhiteScreen` | boolean | NO | undefined | undefined | Check for white screen periodically (every sceneCheckInterval) |
235-| `checkMemoryLeak` | boolean | NO | undefined | undefined | Run memory leak detection at test end |272+| `checkMemoryLeak` | boolean | NO | undefined | undefined | Run memory leak detection at test end |
236-| `recoverFromCrash` | boolean | NO | undefined | undefined | Auto-recover: forceStop → launchApp → ensureInScene |273+| `recoverFromCrash` | boolean | NO | undefined | undefined | Auto-recover: forceStop → launchApp → ensureInScene |
237-| `sceneCheckInterval` | number | NO | `10` (fallback) | undefined | Iterations between scene recovery AND white screen checks; dual-use |274+| `sceneCheckInterval` | number | NO | `10` (fallback) | undefined | Iterations between scene recovery AND white screen checks; dual-use |
238-| `useSmartMonkey` | boolean | NO | undefined | undefined | Use SmartMonkey for random actions |275+| `useSmartMonkey` | boolean | NO | undefined | undefined | Use SmartMonkey for random actions |
239 276 
240**Required fields**: `id`, `name`, `module`, `scene` (R20)277**Required fields**: `id`, `name`, `module`, `scene` (R20)
241 278 
242**Navigation ID Naming Convention** (enforced at compile-time by scanner + runtime by autoNavigate):279**Navigation ID Naming Convention** (enforced at compile-time by scanner + runtime by autoNavigate):
280+ 
243- All navigation identifiers (`id`, `category`, `module`, `scene`) MUST match `^[a-zA-Z0-9_-]+$`281- All navigation identifiers (`id`, `category`, `module`, `scene`) MUST match `^[a-zA-Z0-9_-]+$`
244-- Allowed: letters (a-z, A-Z), digits (0-9), hyphens (-), underscores (_)282+- Allowed: letters (a-z, A-Z), digits (0-9), hyphens (-), underscores (\_)
245- Prohibited: Chinese characters, spaces, dots, slashes, special symbols283- Prohibited: Chinese characters, spaces, dots, slashes, special symbols
246- Reason: search navigation uses `adb input text` which only supports ASCII; search boxes match by these IDs284- Reason: search navigation uses `adb input text` which only supports ASCII; search boxes match by these IDs
247- Validation: `cptf index` (compile-time warning) + `autoNavigate` (runtime error)285- Validation: `cptf index` (compile-time warning) + `autoNavigate` (runtime error)
248 286 
249**Search Navigation Contract** (framework default navigation):287**Search Navigation Contract** (framework default navigation):
288+ 
250- App pages MUST provide search inputs: `module-search`, `category-search`, `test-case-search`289- App pages MUST provide search inputs: `module-search`, `category-search`, `test-case-search`
251- List items MUST follow testID pattern: `module-{id}`, `category-{id}`, `test-case-{id}`, `scene-{id}`290- List items MUST follow testID pattern: `module-{id}`, `category-{id}`, `test-case-{id}`, `scene-{id}`
252- Navigation strategy: search box input → close keyboard → click result; fallback: scroll-to-find291- Navigation strategy: search box input → close keyboard → click result; fallback: scroll-to-find
253- Page transitions clear search state automatically292- Page transitions clear search state automatically
254 293 
255**Key rules**:294**Key rules**:
295+ 
256- TestCase.scene: use name WITHOUT "scene-" prefix (R4)296- TestCase.scene: use name WITHOUT "scene-" prefix (R4)
257- Use declarative pattern for 95% cases (R15)297- Use declarative pattern for 95% cases (R15)
258- Stability tests: MUST set `executeTimes` or `durationMs`, plus `stability.enabled[]` detectors (R21/R52)298- Stability tests: MUST set `executeTimes` or `durationMs`, plus `stability.enabled[]` detectors (R21/R52)
@@ -263,25 +303,25 @@ export interface TestConfig {
263 303 
264**Step Naming & Manual Verification Rule (R52)**:304**Step Naming & Manual Verification Rule (R52)**:
265 305 
266-Step设计必须满足手工验证者能理解预期行为:306+Step 设计必须满足手工验证者能理解预期行为:
267 307 
268-1. **Step名称必须描述属性核心差异** — 禁止对不区分属性值的通用行为命名(如"验证Switch可点击"对hitSlop无区分度)308+1. **Step 名称必须描述属性核心差异** — 禁止对不区分属性值的通用行为命名(如"验证 Switch 可点击"对 hitSlop 无区分度)
269-2. **每个Step必须让验证者知道**:309+2. **每个 Step 必须让验证者知道**:
270 - 观察什么(哪个元素/状态文本)310 - 观察什么(哪个元素/状态文本)
271 - 理想预期是什么(属性语义应有的行为)311 - 理想预期是什么(属性语义应有的行为)
272 - 实际可能是什么(平台限制导致的行为差异)312 - 实际可能是什么(平台限制导致的行为差异)
273-3. **差异属性必须有对照基准** — hitSlop/pointerEvents/disabled等必须提供"有/无"属性的两个场景对比313+3. **差异属性必须有对照基准** — hitSlop/pointerEvents/disabled 等必须提供"有/无"属性的两个场景对比
274-4. **禁止冗余Step** — 多个Step断言完全相同时只保留一个(如hitSlop=0等价无hitSlop)314+4. **禁止冗余 Step** — 多个 Step 断言完全相同时只保留一个(如 hitSlop=0 等价无 hitSlop)
275-5. **禁止验证与属性无关的行为** — hitSlop不验证clickable/checked(不区分有/无hitSlop),应验证边界外点击响应315+5. **禁止验证与属性无关的行为** — hitSlop 不验证 clickable/checked(不区分有/无 hitSlop),应验证边界外点击响应
276 316 
277**示例**:317**示例**:
278 318 
279-| 属性 | ❌ 禁止的Step | ✅ 正确的Step |319+| 属性 | ❌ 禁止的 Step | ✅ 正确的 Step |
280-|------|--------------|--------------|320+| ------------- | ------------------------------------------------------- | ------------------------------------------------- |
281-| hitSlop | "验证无hitSlop开关clickable=true" (与hitSlop无关) | "点击有hitSlop开关右边界外10px验证外扩区域" |321+| hitSlop | "验证无 hitSlop 开关 clickable=true" (与 hitSlop 无关) | "点击有 hitSlop 开关右边界外 10px 验证外扩区域" |
282-| hitSlop | "点击hitSlop=20开关验证可交互" (所有Switch都可交互) | "点击无hitSlop开关右边界外10px验证无外扩响应" |322+| hitSlop | "点击 hitSlop=20 开关验证可交互" (所有 Switch 都可交互) | "点击无 hitSlop 开关右边界外 10px 验证无外扩响应" |
283-| disabled | "验证enabled开关clickable=true" (通用行为) | "点击disabled开关验证无响应" (反向验证) |323+| disabled | "验证 enabled 开关 clickable=true" (通用行为) | "点击 disabled 开关验证无响应" (反向验证) |
284-| pointerEvents | "验证pointerEvents=auto的Switch可交互" (通用) | "验证pointerEvents=none的Switch不可交互" |324+| pointerEvents | "验证 pointerEvents=auto 的 Switch 可交互" (通用) | "验证 pointerEvents=none 的 Switch 不可交互" |
285 325 
286---326---
287 327 
@@ -307,21 +347,21 @@ export interface StepConfig {
307 347 
308### StepConfig Complete Property Reference348### StepConfig Complete Property Reference
309 349 
310-| Property | Type | Required | Default | Boundary / Notes |350+| Property | Type | Required | Default | Boundary / Notes |
311-|----------|------|----------|---------|------------------|351+| ------------------- | ------------------ | -------- | -------------------- | ----------------------------------------------------------------------------- |
312-| `name` | string | YES | (decorator arg) | Display name; MUST describe the property-specific behavior being tested (R52) |352+| `name` | string | YES | (decorator arg) | Display name; MUST describe the property-specific behavior being tested (R52) |
313-| `method` | string | YES | (auto: propertyKey) | Method name on test class |353+| `method` | string | YES | (auto: propertyKey) | Method name on test class |
314-| `order` | number | YES | (auto: steps.length) | 0-indexed; auto-assigned |354+| `order` | number | YES | (auto: steps.length) | 0-indexed; auto-assigned |
315-| `waitForStable` | number | NO | `300` | ms; min=0; wait after auto-action |355+| `waitForStable` | number | NO | `300` | ms; min=0; wait after auto-action |
316-| `repeatCount` | number | NO | undefined | Min=1; falls back to TestConfig.executeTimes or 100 |356+| `repeatCount` | number | NO | undefined | Min=1; falls back to TestConfig.executeTimes or 100 |
317-| `checkAppAlive` | boolean | NO | undefined | Step-level app alive check |357+| `checkAppAlive` | boolean | NO | undefined | Step-level app alive check |
318-| `captureScreenshot` | boolean | NO | `true` | Defaults to true via `?? true` |358+| `captureScreenshot` | boolean | NO | `true` | Defaults to true via `?? true` |
319-| `autoLog` | boolean | NO | `true` | Defaults to true via `?? true` |359+| `autoLog` | boolean | NO | `true` | Defaults to true via `?? true` |
320-| `expects` | ExpectConfig[] | NO | undefined | See ExpectConfig section |360+| `expects` | ExpectConfig[] | NO | undefined | See ExpectConfig section |
321-| `uiComparison` | UIComparisonConfig | NO | undefined | See UI Comparison section |361+| `uiComparison` | UIComparisonConfig | NO | undefined | See UI Comparison section |
322-| `action` | enum | NO | undefined | 6 values: click/input/scroll/swipe/longpress/back |362+| `action` | enum | NO | undefined | 6 values: click/input/scroll/swipe/longpress/back |
323-| `target` | string | NO | undefined | TestID for auto-action target |363+| `target` | string | NO | undefined | TestID for auto-action target |
324-| `value` | string | NO | undefined | Input value for 'input' action |364+| `value` | string | NO | undefined | Input value for 'input' action |
325 365 
326---366---
327 367 
@@ -339,23 +379,23 @@ interface TestThreshold {
339}379}
340```380```
341 381 
342-| Property | Type | Required | Boundary / Notes |382+| Property | Type | Required | Boundary / Notes |
343-|----------|------|----------|------------------|383+| --------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------ |
344-| `metric` | string | YES | Any metric name string |384+| `metric` | string | YES | Any metric name string |
345-| `operator` | enum (6 values) | NO | `'>'`/`'<'`/`'>='`/`'<='`/`'equal'`/`'contains'` |385+| `operator` | enum (6 values) | NO | `'>'`/`'<'`/`'>='`/`'<='`/`'equal'`/`'contains'` |
346-| `value` | number \| string | YES | Number for comparison operators; string for 'contains'/'equal' |386+| `value` | number \| string | YES | Number for comparison operators; string for 'contains'/'equal' |
347-| `maxDeviationPercent` | number | NO | 0-100; 0=exact match |387+| `maxDeviationPercent` | number | NO | 0-100; 0=exact match |
348-| `maxCrashCount` | number | NO | Min=0; max allowed crashes |388+| `maxCrashCount` | number | NO | Min=0; max allowed crashes |
349-| `minSuccessRate` | number | NO | 0.0-1.0; stability test 默认 >= 0.95(可通过 `stability.successRateThreshold` 配置)|389+| `minSuccessRate` | number | NO | 0.0-1.0; stability test 默认 >= 0.95(可通过 `stability.successRateThreshold` 配置) |
350-| `message` | string | NO | Custom violation message |390+| `message` | string | NO | Custom violation message |
351 391 
352### Threshold Operators Comparison392### Threshold Operators Comparison
353 393 
354-| Context | Type | Operators |394+| Context | Type | Operators |
355-|---------|------|-----------|395+| --------------- | -------- | ----------------------------------------------------- |
356-| TestThreshold | 6 values | `'>'`, `'<'`, `'>='`, `'<='`, `'equal'`, `'contains'` |396+| TestThreshold | 6 values | `'>'`, `'<'`, `'>='`, `'<='`, `'equal'`, `'contains'` |
357-| PerfThreshold | 4 values | `'>'`, `'<'`, `'>='`, `'<='` |397+| PerfThreshold | 4 values | `'>'`, `'<'`, `'>='`, `'<='` |
358-| MetricThreshold | 6 values | `'>'`, `'<'`, `'='`, `'>='`, `'<='`, `'!='` |398+| MetricThreshold | 6 values | `'>'`, `'<'`, `'='`, `'>='`, `'<='`, `'!='` |
359 399 
360---400---
361 401 
@@ -375,10 +415,12 @@ interface ExpectCheck {
375```415```
376 416 
377**NOTE**: ExpectCheck.type has 5 values. ExpectConfig.type (step-level) has 8 values. They differ:417**NOTE**: ExpectCheck.type has 5 values. ExpectConfig.type (step-level) has 8 values. They differ:
418+ 
378- ExpectCheck has `'keyboard'` (not in ExpectConfig)419- ExpectCheck has `'keyboard'` (not in ExpectConfig)
379- ExpectConfig has `'behavior'`, `'layout'`, `'interaction'`, `'ui'` (not in ExpectCheck)420- ExpectConfig has `'behavior'`, `'layout'`, `'interaction'`, `'ui'` (not in ExpectCheck)
380 421 
381**NOTE**: ExpectCheck.attribute has 8 values. ExpectAttributeConfig.attribute has 11 values:422**NOTE**: ExpectCheck.attribute has 8 values. ExpectAttributeConfig.attribute has 11 values:
423+ 
382- Shared: `text`, `placeholder`, `type`, `clickable`, `enabled`, `visible`, `focused`, `checked`424- Shared: `text`, `placeholder`, `type`, `clickable`, `enabled`, `visible`, `focused`, `checked`
383- ExpectAttributeConfig only: `value`, `selected`, `scrollable`425- ExpectAttributeConfig only: `value`, `selected`, `scrollable`
384 426 
@@ -391,6 +433,7 @@ type ExpectType = 'attribute' | 'visual' | 'behavior' | 'layout' | 'state' | 'in
391```433```
392 434 
393### ExpectAttributeConfig (11 attributes)435### ExpectAttributeConfig (11 attributes)
436+ 
394```typescript437```typescript
395attribute: 'text' | 'placeholder' | 'type' | 'value' | 'enabled' | 'visible' | 'focused' | 'checked' | 'selected' | 'clickable' | 'scrollable';438attribute: 'text' | 'placeholder' | 'type' | 'value' | 'enabled' | 'visible' | 'focused' | 'checked' | 'selected' | 'clickable' | 'scrollable';
396expected?: unknown;439expected?: unknown;
@@ -407,7 +450,7 @@ ignoreCase?: boolean;
407 450 
408```typescript451```typescript
409// L0 dump 签名检测(零开销,推荐)452// L0 dump 签名检测(零开销,推荐)
410-const overlays = await this.getOverlays(); // BaseTest 门面453+const overlays = await this.getOverlays(); // BaseTest 门面
411const kbUp = overlays.some(o => o.kind === 'keyboard');454const kbUp = overlays.some(o => o.kind === 'keyboard');
412 455 
413// L1 hidumper 精确窗口枚举(~1.4s/次,需精确 bounds 时)456// L1 hidumper 精确窗口枚举(~1.4s/次,需精确 bounds 时)
@@ -417,17 +460,18 @@ const overlays = await this.getOverlays({ precise: true });
417await this.ui.dismissOverlayAndWait('keyboard', 3000);460await this.ui.dismissOverlayAndWait('keyboard', 3000);
418 461 
419// 键盘守卫(断言前收键盘——鸿蒙 resize 陷阱,见 rn-test-case skill)462// 键盘守卫(断言前收键盘——鸿蒙 resize 陷阱,见 rn-test-case skill)
420-await this.dismissKeyboardIfOpen(); // BaseTest 方法463+await this.dismissKeyboardIfOpen(); // BaseTest 方法
421// 或声明式:@Step({ dismissKeyboardBeforeAssert: true, ... })464// 或声明式:@Step({ dismissKeyboardBeforeAssert: true, ... })
422```465```
423 466 
424### OverlayInfo(Overlay 统一方案——2026-09-16)467### OverlayInfo(Overlay 统一方案——2026-09-16)
468+ 
425```typescript469```typescript
426type OverlayKind = 'keyboard' | 'dialog' | 'systemBar';470type OverlayKind = 'keyboard' | 'dialog' | 'systemBar';
427type OverlayOcclusion = 'resize' | 'cover' | 'replace' | 'none';471type OverlayOcclusion = 'resize' | 'cover' | 'replace' | 'none';
428interface OverlayInfo {472interface OverlayInfo {
429 kind: OverlayKind;473 kind: OverlayKind;
430- name?: string; // L0=dump节点id / L1=hidumper WindowName474+ name?: string; // L0=dump节点id / L1=hidumper WindowName
431 bounds?: { left; top; right; bottom; width; height };475 bounds?: { left; top; right; bottom; width; height };
432 occlusion: OverlayOcclusion; // resize=窗口收缩 / cover=点击拦截 / replace=app退后台476 occlusion: OverlayOcclusion; // resize=窗口收缩 / cover=点击拦截 / replace=app退后台
433 source: 'layout-dump' | 'hidumper';477 source: 'layout-dump' | 'hidumper';
@@ -439,6 +483,7 @@ interface OverlayInfo {
439~~bounds/relative/alignment~~ **已删除**(零使用)。483~~bounds/relative/alignment~~ **已删除**(零使用)。
440 484 
441### ExpectStateConfig485### ExpectStateConfig
486+ 
442```typescript487```typescript
443focused?: boolean;488focused?: boolean;
444enabled?: boolean;489enabled?: boolean;
@@ -451,6 +496,7 @@ customState?: string;
451```496```
452 497 
453### ExpectInteractionConfig498### ExpectInteractionConfig
499+ 
454```typescript500```typescript
455interactionType: 'click' | 'input' | 'scroll' | 'swipe' | 'longpress' | 'drag' | 'pinch';501interactionType: 'click' | 'input' | 'scroll' | 'swipe' | 'longpress' | 'drag' | 'pinch';
456clickSuccess?: boolean;502clickSuccess?: boolean;
@@ -469,6 +515,7 @@ eventPayload?: unknown;
469```515```
470 516 
471### ExpectResponseConfig517### ExpectResponseConfig
518+ 
472```typescript519```typescript
473maxResponseTime?: number; // min=0, ms520maxResponseTime?: number; // min=0, ms
474avgResponseTime?: number; // min=0, ms521avgResponseTime?: number; // min=0, ms
@@ -478,4 +525,3 @@ measureIterations?: number; // min=1
478```525```
479 526 
480---527---
481- 
M.quality-toolkit+1-1File content is under review, please try again later
MREADME.md+61-39
@@ -6,18 +6,19 @@
6 6 
7## 为什么选择 CPTF?7## 为什么选择 CPTF?
8 8 
9-| 特性 | CPTF | 其他框架 |9+| 特性 | CPTF | 其他框架 |
10-|------|------|---------|10+| ------------------ | ----------------------------------------------------- | --------------------- |
11-| **HarmonyOS 支持** | ✅ 原生支持 | ❌ 不支持 |11+| **HarmonyOS 支持** | ✅ 原生支持 | ❌ 不支持 |
12-| **性能指标采集** | ✅ 19种性能指标+3种稳定性指标(真实FPS) | ⚠️ 2-3种(模拟FPS) |12+| **性能指标采集** | ✅ 19 个性能指标(17 内置 + 2 插件,真实 FPS) | ⚠️ 2-3 种(模拟 FPS) |
13-| **稳定性测试** | ✅ SmartMonkey + ANR + 白屏检测 + 内存泄漏 + 崩溃恢复 | ❌ 需自行搭建 |13+| **稳定性测试** | ✅ SmartMonkey + ANR + 白屏检测 + 内存泄漏 + 崩溃恢复 | ❌ 需自行搭建 |
14-| **跨平台对比** | ✅ HarmonyOS vs Android | ⚠️ 需自行搭建 |14+| **跨平台对比** | ✅ HarmonyOS vs Android | ⚠️ 需自行搭建 |
15-| **用例编写** | ✅ 声明式(减少70-90%代码) | ⚠️ 命令式 |15+| **用例编写** | ✅ 声明式(减少 70-90%代码) | ⚠️ 命令式 |
16 16 
17**核心亮点**:17**核心亮点**:
18+ 
18- **唯一性**:业界唯一支持 HarmonyOS 自动化测试19- **唯一性**:业界唯一支持 HarmonyOS 自动化测试
19- **精准性**:真实 FPS 采集(RenderService fpsCount + hitchs),非模拟数据20- **精准性**:真实 FPS 采集(RenderService fpsCount + hitchs),非模拟数据
20-- **完整性**:19种性能指标 + 3种稳定性指标 + 完整稳定性测试套件(SmartMonkey/白屏/内存泄漏/ANR/崩溃恢复)21+- **完整性**:19 个性能指标(17 内置 + 2 插件)+ 3 个稳定性检测器 + 完整稳定性测试套件(SmartMonkey/白屏/内存泄漏/ANR/崩溃恢复)
21- **简洁性**:声明式框架 `@Test/@Step`,代码量减少 70-90%22- **简洁性**:声明式框架 `@Test/@Step`,代码量减少 70-90%
22- **专业性**:五层架构设计(src/ 目录即五层),职责分离清晰,易于扩展23- **专业性**:五层架构设计(src/ 目录即五层),职责分离清晰,易于扩展
23 24 
@@ -26,31 +27,33 @@
26**React Native for OpenHarmony (RNOH)** 基于 CPTF 框架完成全量功能、性能、稳定性测试,是目前业界最大的 HarmonyOS 跨端测试实践:27**React Native for OpenHarmony (RNOH)** 基于 CPTF 框架完成全量功能、性能、稳定性测试,是目前业界最大的 HarmonyOS 跨端测试实践:
27 28 
28- **功能测试**:覆盖 Switch、Button、RefreshControl 等核心组件的全量属性跨平台一致性验证29- **功能测试**:覆盖 Switch、Button、RefreshControl 等核心组件的全量属性跨平台一致性验证
29-- **性能测试**:19 种指标采集 + Warmup迭代 + 阈值告警 + HTML趋势图30+- **性能测试**:19 个指标采集(17 内置 + 2 插件)+ Warmup 迭代 + 阈值告警 + HTML 趋势图
30-- **稳定性测试**:SmartMonkey + ANR检测 + 白屏检测 + 内存泄漏 + 崩溃自动恢复31+- **稳定性测试**:SmartMonkey + ANR 检测 + 白屏检测 + 内存泄漏 + 崩溃自动恢复
31-- **跨平台对比**:HarmonyOS vs Android UI结构4维对比(属性/样式/位置/层级)32+- **跨平台对比**:HarmonyOS vs Android UI 结构 4 维对比(属性/样式/位置/层级)
32 33 
33> 测试用例工程:[RNOH Test App](https://atomgit.com/CPF-RN/rnoh_test)34> 测试用例工程:[RNOH Test App](https://atomgit.com/CPF-RN/rnoh_test)
34 35 
36+> 框架源码双托管:[GitCode](https://gitcode.com/CPF-RN/CPTF)(主)· [AtomGit](https://atomgit.com/react-native/CPTF)(镜像,`packages/framework/package.json` `repository`)
37+ 
35## 平台支持38## 平台支持
36 39 
37-| 平台 | 状态 |40+| 平台 | 状态 |
38-|------|------|41+| --------- | --------- |
39| HarmonyOS | ✅ 已支持 |42| HarmonyOS | ✅ 已支持 |
40-| Android | ✅ 已支持 |43+| Android | ✅ 已支持 |
41-| iOS | 🚧 开发中 |44+| iOS | 🚧 开发中 |
42 45 
43## 项目结构46## 项目结构
44 47 
45```48```
46cptf/49cptf/
47├── packages/framework/ # 框架层(npm 包 @cptf/framework)50├── packages/framework/ # 框架层(npm 包 @cptf/framework)
48-│ └── src/ # 五层架构源码(base/infrastructure/system/capability/orchestration)51+│ └── src/ # 五层架构源码(对外概览三组合并:base+infrastructure=基础设施、capability+orchestration=调度、system=系统)
49-│ ├── base/ # 基础设施层(类型/配置/工具/设备管理/端口管理)52+│ ├── base/ # 基础层(类型/配置/工具/设备管理/端口管理)
50│ ├── infrastructure/ # 基础设施层(扫描/索引/设备发现)53│ ├── infrastructure/ # 基础设施层(扫描/索引/设备发现)
51│ ├── system/ # 系统层(ISystem接口+平台实现+Agent客户端)54│ ├── system/ # 系统层(ISystem接口+平台实现+Agent客户端)
52-│ ├── capability/ # 调度层(UI/断言/对比/采集)55+│ ├── capability/ # 能力层(UI/断言/对比/采集)
53-│ ├── orchestration/ # 调度层(运行器/测试类/报告)56+│ ├── orchestration/ # 编排层(运行器/测试类/报告)
54│ ├── cli/ # CLI命令57│ ├── cli/ # CLI命令
55│ └── __tests__/ # 框架单元测试58│ └── __tests__/ # 框架单元测试
56```59```
@@ -65,17 +68,27 @@ pnpm install
65pnpm build68pnpm build
66```69```
67 70 
68-### CLI命令71+### CLI 命令
69 72 
70```bash73```bash
71cptf devices # 列出设备74cptf devices # 列出设备
72cptf devices -p harmony # 列出HarmonyOS设备75cptf devices -p harmony # 列出HarmonyOS设备
73cptf scan # 扫描测试用例76cptf scan # 扫描测试用例
77+cptf index # 生成测试用例索引文件
74cptf run # 执行所有测试(自动平台选择)78cptf run # 执行所有测试(自动平台选择)
75cptf run -p harmony # HarmonyOS测试79cptf run -p harmony # HarmonyOS测试
76cptf run -m performance # 指定模块测试80cptf run -m performance # 指定模块测试
81+cptf run -C Button # 按分类测试
77cptf run -t all-metrics-performance # 指定用例测试82cptf run -t all-metrics-performance # 指定用例测试
83+cptf run --driver native # 驱动模式(native灰盒 | external黑盒 | auto,默认auto:优先native失败降级external)
84+cptf run --strategy by-module # 多设备分配策略(round-robin | by-module | by-category | by-case | single)
85+cptf run --timeout 600 # 全局超时(秒),超时后生成部分报告
86+cptf run --include-unsupported # 包含无法自动化的用例(默认跳过并从通过率分母排除)
87+cptf run --force-baseline # 强制覆盖已有视觉基线(覆盖前自动备份到 .history/)
78cptf compare # 跨平台对比测试88cptf compare # 跨平台对比测试
89+cptf compare --driver native # 对比测试同样支持驱动模式
90+cptf index # 生成测试用例索引(test-cases-index.ts)
91+cptf install # 安装应用到连接设备(--atx 同时装 Android ATX Agent)
79cptf init # 初始化配置92cptf init # 初始化配置
80cptf check-env # 检查环境依赖93cptf check-env # 检查环境依赖
81cptf template performance -n my-test # 生成测试模板94cptf template performance -n my-test # 生成测试模板
@@ -126,11 +139,9 @@ cptf run -p harmony
126 139 
127### 性能采集140### 性能采集
128 141 
129-- FPS 监控(真实FPS:RenderService fpsCount + hitchs)142+- FPS 监控(真实 FPS:RenderService fpsCount + hitchs)
130- CPU 使用率143- CPU 使用率
131-- 内存占用(含泄漏检测:线性回归+R²置信度)144+- 19 个性能指标(17 内置 + 2 插件)+ 3 个稳定性检测器 + HTML 趋势图
132-- 白屏检测(PNG+zlib解析,亮度>240+白像素比>85%)
133-- 19种性能指标 + 3种稳定性指标 + HTML趋势图
134 145 
135### 稳定性测试146### 稳定性测试
136 147 
@@ -149,7 +160,7 @@ cptf run -p harmony
149 160 
150### 报告生成161### 报告生成
151 162 
152-- HTML 格式(默认)163+- HTML 格式
153- Markdown 格式164- Markdown 格式
154- JSON 格式165- JSON 格式
155- 性能告警166- 性能告警
@@ -159,12 +170,23 @@ cptf run -p harmony
159```bash170```bash
160cptf devices # 列出设备171cptf devices # 列出设备
161cptf devices -p harmony # 列出平台设备172cptf devices -p harmony # 列出平台设备
173+cptf scan # 扫描用例
174+cptf index # 生成测试索引文件
162cptf run # 执行所有测试(自动平台选择)175cptf run # 执行所有测试(自动平台选择)
163cptf run -p harmony # HarmonyOS测试176cptf run -p harmony # HarmonyOS测试
164cptf run -m performance # 指定模块177cptf run -m performance # 指定模块
178+cptf run -C Button # 指定分类
165cptf run -t calendar-perf-declarative # 指定用例179cptf run -t calendar-perf-declarative # 指定用例
180+cptf run --driver native # 驱动模式 native|external|auto(默认auto)
181+cptf run --strategy by-category # 多设备分配策略
182+cptf run --timeout 600 # 全局超时(秒),超时生成部分报告
183+cptf run --include-unsupported # 包含不可自动化用例
166cptf run -p harmony --rn-version rn84 --update-baseline # 按 RN 版本分段建立/验证视觉基线184cptf run -p harmony --rn-version rn84 --update-baseline # 按 RN 版本分段建立/验证视觉基线
185+cptf run -p harmony --rn-version rn84 --force-baseline # 强制覆盖视觉基线(自动备份旧基线)
167cptf compare # 跨平台对比测试186cptf compare # 跨平台对比测试
187+cptf compare --driver native # 对比测试同样支持驱动模式
188+cptf index # 生成测试用例索引
189+cptf install # 安装应用到设备
168cptf init # 初始化配置文件190cptf init # 初始化配置文件
169cptf check-env # 检查环境依赖191cptf check-env # 检查环境依赖
170cptf template <module> -n <name> # 生成测试用例模板192cptf template <module> -n <name> # 生成测试用例模板
@@ -172,21 +194,21 @@ cptf template <module> -n <name> # 生成测试用例模板
172 194 
173## 文档195## 文档
174 196 
175-| 文档 | 说明 |197+| 文档 | 说明 |
176-|------|------|198+| ----------------------------------------------------------------------------------- | ----------------------------------- |
177-| [架构总览](packages/framework/docs/architecture/ARCHITECTURE.md) | 框架架构、源码目录、依赖规则 |199+| [架构总览](packages/framework/docs/architecture/ARCHITECTURE.md) | 框架架构、源码目录、依赖规则 |
178-| [架构优势](packages/framework/docs/architecture/ARCHITECTURE-OVERVIEW.md) | 五层架构、三平台加速、性能优势 |200+| [架构优势](packages/framework/docs/architecture/ARCHITECTURE-OVERVIEW.md) | 五层架构、三平台加速、性能优势 |
179-| [能力规格](packages/framework/docs/architecture/CAPABILITY-SPEC.md) | 性能指标、API 规格、数据结构 |201+| [能力规格](packages/framework/docs/architecture/CAPABILITY-SPEC.md) | 性能指标、API 规格、数据结构 |
180-| [用户指南](packages/framework/docs/getting-started/USER-GUIDE.md) | 安装、配置、CLI、API 使用 |202+| [用户指南](packages/framework/docs/getting-started/USER-GUIDE.md) | 安装、配置、CLI、API 使用 |
181| [声明式测试指南](packages/framework/docs/getting-started/DECLARATIVE-TEST-GUIDE.md) | @Test/@Step 装饰器、TestConfig 详解 |203| [声明式测试指南](packages/framework/docs/getting-started/DECLARATIVE-TEST-GUIDE.md) | @Test/@Step 装饰器、TestConfig 详解 |
182-| [命令式测试指南](packages/framework/docs/advanced/IMPERATIVE-TEST-GUIDE.md) | 命令式测试编写 |204+| [命令式测试指南](packages/framework/docs/advanced/IMPERATIVE-TEST-GUIDE.md) | 命令式测试编写 |
183-| [Expect 断言指南](packages/framework/docs/advanced/EXPECT-GUIDE.md) | 声明式断言使用 |205+| [Expect 断言指南](packages/framework/docs/advanced/EXPECT-GUIDE.md) | 声明式断言使用 |
184-| [UI 对比指南](packages/framework/docs/advanced/UI-COMPARISON.md) | 跨平台 UI 结构对比 |206+| [UI 对比指南](packages/framework/docs/advanced/UI-COMPARISON.md) | 跨平台 UI 结构对比 |
185-| [扩展开发](packages/framework/docs/development/DEVELOPMENT-GUIDE.md) | 新增平台、能力、报告生成 |207+| [扩展开发](packages/framework/docs/development/DEVELOPMENT-GUIDE.md) | 新增平台、能力、报告生成 |
186-| [框架对比分析](packages/framework/docs/reference/FRAMEWORK-COMPARISON.md) | 与 Jest/Appium/Cypress 等对比 |208+| [框架对比分析](packages/framework/docs/reference/FRAMEWORK-COMPARISON.md) | 与 Jest/Appium/Cypress 等对比 |
187-| [稳定性对比分析](packages/framework/docs/reference/STABILITY-COMPARISON.md) | 7 维度对标业界稳定性方案 |209+| [稳定性对比分析](packages/framework/docs/reference/STABILITY-COMPARISON.md) | 7 维度对标业界稳定性方案 |
188-| [npm 包说明](packages/framework/README.md) | CLI 命令、快速开始、架构设计 |210+| [npm 包说明](packages/framework/README.md) | CLI 命令、快速开始、架构设计 |
189 211 
190## License212## License
191 213 
192-MIT214+MIT
@@ -0,0 +1,21 @@
1+MIT License
2+ 
3+Copyright 2026 gaoyang (gyange@163.com)
4+ 
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+ 
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+ 
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
@@ -4,7 +4,7 @@
4 "description": "CPTF benchmark plugin - React Native device-side measurement (RTNTimeToRender + MeasureContainer + scenes)",4 "description": "CPTF benchmark plugin - React Native device-side measurement (RTNTimeToRender + MeasureContainer + scenes)",
5 "main": "src/index",5 "main": "src/index",
6 "scripts": {6 "scripts": {
7- "pack": "npm pack"7+ "pack": "pnpm pack"
8 },8 },
9 "codegenConfig": {9 "codegenConfig": {
10 "name": "RenderTimerSpec",10 "name": "RenderTimerSpec",
@@ -0,0 +1,21 @@
1+MIT License
2+ 
3+Copyright 2026 gaoyang (gyange@163.com)
4+ 
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+ 
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+ 
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
@@ -19,7 +19,7 @@
19 "build": "tsc -p tsconfig.build.json",19 "build": "tsc -p tsconfig.build.json",
20 "typecheck": "tsc --noEmit -p tsconfig.build.json",20 "typecheck": "tsc --noEmit -p tsconfig.build.json",
21 "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",21 "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
22- "pack": "pnpm run clean && pnpm run build && npm pack",22+ "pack": "pnpm run clean && pnpm run build && pnpm pack",
23 "prepublishOnly": "pnpm run clean && pnpm run build"23 "prepublishOnly": "pnpm run clean && pnpm run build"
24 },24 },
25 "keywords": ["cptf", "benchmark", "render-time", "metrics"],25 "keywords": ["cptf", "benchmark", "render-time", "metrics"],
Mpackages/framework/CHANGELOG.md+22-0File content is under review, please try again later
@@ -12,6 +12,7 @@
12CPTF(Cross-Platform Test Framework)是一个跨平台自动化测试框架,支持 HarmonyOS、Android、iOS 平台的自动化测试和跨平台对比测试。12CPTF(Cross-Platform Test Framework)是一个跨平台自动化测试框架,支持 HarmonyOS、Android、iOS 平台的自动化测试和跨平台对比测试。
13 13 
14适用于多种跨平台技术栈:14适用于多种跨平台技术栈:
15+ 
15- React Native / RN16- React Native / RN
16- Flutter17- Flutter
17- Kotlin Multiplatform (KMP)18- Kotlin Multiplatform (KMP)
@@ -21,68 +22,74 @@ CPTF(Cross-Platform Test Framework)是一个跨平台自动化测试框架
21## 核心特性22## 核心特性
22 23 
23### 1. 声明式测试框架24### 1. 声明式测试框架
25+ 
24- ✅ `@Test` 装饰器 - 配置化测试定义26- ✅ `@Test` 装饰器 - 配置化测试定义
25- ✅ `@Step` 装饰器 - 自动步骤日志27- ✅ `@Step` 装饰器 - 自动步骤日志
26-- ✅ 自动导航 - setup自动完成28+- ✅ 自动导航 - setup 自动完成
27- ✅ 场景自动恢复 - autoRecoverScene29- ✅ 场景自动恢复 - autoRecoverScene
28-- ✅ 配置化基线 - baselines配置30+- ✅ 配置化基线 - baselines 配置
29 31 
30### 2. 三类测试基类32### 2. 三类测试基类
33+ 
31- ✅ **PerformanceTest** - 性能测试(FPS、时延、阈值)34- ✅ **PerformanceTest** - 性能测试(FPS、时延、阈值)
32- ✅ **FunctionalTest** - 功能测试(属性验证、视觉检查、跨平台一致性)35- ✅ **FunctionalTest** - 功能测试(属性验证、视觉检查、跨平台一致性)
33- ✅ **StabilityTest** - 稳定性测试(反复执行、崩溃检测)36- ✅ **StabilityTest** - 稳定性测试(反复执行、崩溃检测)
34 37 
35### 3. 跨平台测试支持38### 3. 跨平台测试支持
39+ 
36- ✅ HarmonyOS 自动化测试(hdc + UITest 双引擎)40- ✅ HarmonyOS 自动化测试(hdc + UITest 双引擎)
37- ✅ Android 自动化测试(adb + U2 双引擎)41- ✅ Android 自动化测试(adb + U2 双引擎)
38- ✅ iOS 自动化测试(simctl + WDA + xctrace)42- ✅ iOS 自动化测试(simctl + WDA + xctrace)
39 43 
40### 4. 性能监控44### 4. 性能监控
45+ 
41- ✅ FPS 实时采集46- ✅ FPS 实时采集
42- ✅ 内存占用监控47- ✅ 内存占用监控
43- ✅ CPU 使用率统计48- ✅ CPU 使用率统计
44- ✅ 白屏率分析49- ✅ 白屏率分析
45-- ✅ 用例级采集配置(MonitoringConfig:指标选择+频率)50+- ✅ 用例级采集配置(TestConfig.collect:指标选择)
46- ✅ FPS/内存/CPU 时序趋势(TrendPoint)51- ✅ FPS/内存/CPU 时序趋势(TrendPoint)
47-- ✅ 19 个 PerfMetricType 全部落地52+- ✅ 19 个 PerfMetricType 全部落地(17 内置 collector + 2 个 @cptf/benchmark 插件指标)
48 53 
49### 5. 稳定性测试54### 5. 稳定性测试
55+ 
50- ✅ SmartMonkey(带应用崩溃自动恢复)56- ✅ SmartMonkey(带应用崩溃自动恢复)
51- ✅ 白屏检测(像素级)57- ✅ 白屏检测(像素级)
52- ✅ 内存泄漏检测(线性回归)58- ✅ 内存泄漏检测(线性回归)
53- ✅ ANR / Crash 检测59- ✅ ANR / Crash 检测
54 60 
55### 6. 跨平台 UI 对比61### 6. 跨平台 UI 对比
62+ 
56- ✅ 自动扫描 baseline63- ✅ 自动扫描 baseline
57- ✅ 像素级 diff.png 生成64- ✅ 像素级 diff.png 生成
58- ✅ ignoreTestID / ignoreTestIDPatterns65- ✅ ignoreTestID / ignoreTestIDPatterns
59- ✅ 跨平台一致性评分66- ✅ 跨平台一致性评分
60-- 📋 ignoreRegions(0.3.0 路线图)67+- ✅ ignoreRegions / compareRegions(物理像素矩形,交集语义)
61-- 📋 pixelmatch / pHash / SSIM 真实像素对比(0.3.0 路线图)68+- ✅ 逐像素 diff(perPixelTolerance / diffThreshold 分离)
62 69 
63## Evidence(数据支撑)70## Evidence(数据支撑)
64 71 
65### 双引擎加速效果72### 双引擎加速效果
66 73 
67-| 操作 | CPTF (HTX/ATX) | SoloPi (adb) | 提升 |74+| 操作 | CPTF (HTX/ATX) | SoloPi (adb) | 提升 |
68-|------|------------------|---------------|------|75+| --------------- | -------------- | ------------ | -------- |
69-| 单步 click 响应 | 80-120ms | 250-400ms | **3-4x** |76+| 单步 click 响应 | 80-120ms | 250-400ms | **3-4x** |
70-| 截图采集 | 60-80ms | 150-250ms | **2-3x** |77+| 截图采集 | 60-80ms | 150-250ms | **2-3x** |
71-| FPS 实时采集 | 100-200ms | 500-1000ms | **3-5x** |78+| FPS 实时采集 | 100-200ms | 500-1000ms | **3-5x** |
72-| 启动用例加载 | 200-300ms | 800-1500ms | **3-5x** |79+| 启动用例加载 | 200-300ms | 800-1500ms | **3-5x** |
73 80 
74> 测试环境:HarmonyOS 4.0 Mate 60 / Android 13 Pixel 7。基线对比工具:SoloPi v1.7.6。81> 测试环境:HarmonyOS 4.0 Mate 60 / Android 13 Pixel 7。基线对比工具:SoloPi v1.7.6。
75 82 
76### 自动化就绪度83### 自动化就绪度
77 84 
78-| 指标 | 数值 | 来源 |85+| 指标 | 数值 | 来源 |
79-|------|------|------|86+| ------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------- |
80-| 单元测试用例 | 以 `pnpm test` 实际输出为准 | `pnpm test` |87+| 单元测试用例 | 以 `pnpm test` 实际输出为准 | `pnpm test` |
81-| 框架验证项 | 以 `pnpm verify` 实际输出为准 | `pnpm verify`(架构 / 边界 / 版权 / 类型 / 平台 / Logger / API / Config / Docs / Skill / Perf / Coverage) |88+| 框架验证项 | 以 `pnpm verify` 实际输出为准 | `pnpm verify`(架构 / 边界 / 版权 / 类型 / 平台 / Logger / API / Config / Docs / Skill / Perf / Coverage) |
82-| TS strict 模式 | ✅ | `tsconfig.json` |89+| TS strict 模式 | ✅ | `tsconfig.json` |
83-| 业务代码 `as any` | **0 处** | `verify.ts` 强制 |90+| 业务代码 `as any` | **0 处** | `verify.ts` 强制 |
84-| 同步 I/O 阻塞主线程 | **0 处**(除 worker 线程内) | 0.2.1 整改完成 |91+| 同步 I/O 阻塞主线程 | **0 处**(除 worker 线程内) | 0.2.1 整改完成 |
85-| 巨型类(>1000 行) | 4 个(已在拆分 Roadmap) | 见 [OPTIMIZATION-ANALYSIS.md](CHANGELOG.md) |92+| 巨型类(>1000 行) | 4 个(已在拆分 Roadmap) | 见 [OPTIMIZATION-ANALYSIS.md](CHANGELOG.md) |
86 93 
87### CI 集成示例94### CI 集成示例
88 95 
@@ -109,8 +116,9 @@ CPTF(Cross-Platform Test Framework)是一个跨平台自动化测试框架
109### 用户案例116### 用户案例
110 117 
111> CPTF 0.1.x 起在 `rnoh_test` 仓库使用,覆盖:118> CPTF 0.1.x 起在 `rnoh_test` 仓库使用,覆盖:
119+>
112> - 35 个核心组件(Button / Switch / Text / List / Form 等)120> - 35 个核心组件(Button / Switch / Text / List / Form 等)
113-> - 13 个性能指标采集121+> - 19 个性能指标采集(17 内置 + 2 插件)
114> - 跨 HarmonyOS 4.0/5.0 + Android 13/14 双平台对比122> - 跨 HarmonyOS 4.0/5.0 + Android 13/14 双平台对比
115> - Deep Link 导航节省 75% 用例切换耗时123> - Deep Link 导航节省 75% 用例切换耗时
116 124 
@@ -128,19 +136,20 @@ CPTF(Cross-Platform Test Framework)是一个跨平台自动化测试框架
128- [迁移指南](docs/MIGRATION.md)136- [迁移指南](docs/MIGRATION.md)
129- [变更日志](CHANGELOG.md)137- [变更日志](CHANGELOG.md)
130 138 
139+### 5. UI 自动化
131 140 
132-### 5. UI自动化
133- ✅ 元素查找(testID、文本)141- ✅ 元素查找(testID、文本)
134- ✅ 点击/滑动/输入操作142- ✅ 点击/滑动/输入操作
135- ✅ 滚动查找元素143- ✅ 滚动查找元素
136- ✅ 截图对比144- ✅ 截图对比
137 145 
138### 6. 报告生成146### 6. 报告生成
139-- ✅ HTML可视化报告(Tab式趋势面板+可滚动数据表格)147+ 
140-- ✅ Markdown文档报告148+- ✅ HTML 可视化报告(Tab 式趋势面板+可滚动数据表格)
141-- ✅ JSON数据报告149+- ✅ Markdown 文档报告
150+- ✅ JSON 数据报告
142- ✅ 智能建议生成151- ✅ 智能建议生成
143-- ✅ 邮件通知(Outlook/Modern双格式)152+- ✅ 邮件通知(Outlook/Modern 双格式)
144 153 
145## 快速开始154## 快速开始
146 155 
@@ -166,9 +175,9 @@ pnpm test
166pnpm typecheck175pnpm typecheck
167```176```
168 177 
169-### 4. 使用CLI命令178+### 4. 使用 CLI 命令
170 179 
171-> **完整CLI命令**: 参见 [根目录README.md](../../README.md) 命令速查章节180+> **完整 CLI 命令**: 参见 [根目录 README.md](../../README.md) 命令速查章节
172 181 
173```bash182```bash
174# 列出设备183# 列出设备
@@ -206,12 +215,13 @@ cptf template stability -n monkey-test -i monkey-stab -c Monkey
206| 选项 | 说明 | 默认值 |215| 选项 | 说明 | 默认值 |
207|------|------|--------|216|------|------|--------|
208| `-n, --name` | 测试用例名称 | `test-case` |217| `-n, --name` | 测试用例名称 | `test-case` |
209-| `-i, --id` | 测试用例ID | `test-case-id` |218+| `-i, --id` | 测试用例 ID | `test-case-id` |
210| `-o, --output` | 输出目录 | `tests/cases` |219| `-o, --output` | 输出目录 | `tests/cases` |
211| `-c, --category` | 测试分类 | 模块默认值 |220| `-c, --category` | 测试分类 | 模块默认值 |
212| `-s, --scene` | 测试场景 | 模块默认值 |221| `-s, --scene` | 测试场景 | 模块默认值 |
213 222 
214**生成模板示例**(性能测试):223**生成模板示例**(性能测试):
224+ 
215```typescript225```typescript
216@Test({226@Test({
217 id: 'list-scroll-perf',227 id: 'list-scroll-perf',
@@ -219,9 +229,7 @@ cptf template stability -n monkey-test -i monkey-stab -c Monkey
219 module: 'performance',229 module: 'performance',
220 collect: ['startLatency', 'fps', 'fpsMin', 'blankScreenRatio'],230 collect: ['startLatency', 'fps', 'fpsMin', 'blankScreenRatio'],
221 executeTimes: 5,231 executeTimes: 5,
222- thresholds: [232+ thresholds: [{ metric: 'fps', operator: '<', value: 55 }],
223- { metric: 'fps', operator: '<', value: 55 },
224- ],
225})233})
226class ListScroll extends PerformanceTest {234class ListScroll extends PerformanceTest {
227 @Step('执行测试操作', { waitForStable: 500 })235 @Step('执行测试操作', { waitForStable: 500 })
@@ -231,11 +239,11 @@ class ListScroll extends PerformanceTest {
231}239}
232```240```
233 241 
234-**说明**:实际测试执行在 `../../../app` 目录中进行,参见根目录 README.md。242+**说明**:实际测试执行在被测应用的 `app/` 目录(配套的 rnoh_test 仓)中进行,参见根目录 README.md。
235 243 
236## 架构设计244## 架构设计
237 245 
238-CPTF采用**五层架构**设计(src/ 目录即五层;对外概览常合并叙述为 3 组:基础设施/系统/调度):246+CPTF 采用**五层架构**设计(src/ 目录即五层;对外概览常合并叙述为 3 组:基础设施/系统/调度):
239 247 
240```248```
241基础层(base) → types、config、utils(Logger、Helpers、CacheManager)249基础层(base) → types、config、utils(Logger、Helpers、CacheManager)
@@ -255,6 +263,7 @@ CPTF采用**五层架构**设计(src/ 目录即五层;对外概览常合并
255```263```
256 264 
257**声明式框架**:265**声明式框架**:
266+ 
258```267```
259orchestration/core/268orchestration/core/
260├── decorators.ts → @Test、@Step装饰器269├── decorators.ts → @Test、@Step装饰器
@@ -269,41 +278,46 @@ orchestration/core/
269## 文档导航278## 文档导航
270 279 
271### 核心文档280### 核心文档
281+ 
272- [架构总览](docs/architecture/ARCHITECTURE.md) - 五层架构、性能优化、层级依赖282- [架构总览](docs/architecture/ARCHITECTURE.md) - 五层架构、性能优化、层级依赖
273-- [能力规格](docs/architecture/CAPABILITY-SPEC.md) - 性能指标、平台采集、API规格、数据结构283+- [能力规格](docs/architecture/CAPABILITY-SPEC.md) - 性能指标、平台采集、API 规格、数据结构
274-- [用户指南](docs/getting-started/USER-GUIDE.md) - 快速开始、配置说明、API使用、最佳实践284+- [用户指南](docs/getting-started/USER-GUIDE.md) - 快速开始、配置说明、API 使用、最佳实践
275- [扩展开发](docs/development/DEVELOPMENT-GUIDE.md) - 新增平台、能力、报告生成285- [扩展开发](docs/development/DEVELOPMENT-GUIDE.md) - 新增平台、能力、报告生成
276- [框架对比](docs/reference/FRAMEWORK-COMPARISON.md) - CPTF vs Jest/Appium/Cypress/Detox/Playwright286- [框架对比](docs/reference/FRAMEWORK-COMPARISON.md) - CPTF vs Jest/Appium/Cypress/Detox/Playwright
277-- [UI对比指南](docs/advanced/UI-COMPARISON.md) - 视觉对比能力使用指南287+- [UI 对比指南](docs/advanced/UI-COMPARISON.md) - 视觉对比能力使用指南
278-- [Expect断言指南](docs/advanced/EXPECT-GUIDE.md) - 声明式断言使用指南288+- [Expect 断言指南](docs/advanced/EXPECT-GUIDE.md) - 声明式断言使用指南
279- [声明式测试指南](docs/getting-started/DECLARATIVE-TEST-GUIDE.md) - 声明式测试编写模式289- [声明式测试指南](docs/getting-started/DECLARATIVE-TEST-GUIDE.md) - 声明式测试编写模式
280- [命令式测试指南](docs/advanced/IMPERATIVE-TEST-GUIDE.md) - 命令式测试编写模式290- [命令式测试指南](docs/advanced/IMPERATIVE-TEST-GUIDE.md) - 命令式测试编写模式
281 291 
282### 应用文档292### 应用文档
283-- [测试用例编写](../../../app/tests/README.md) - Executor模式、声明式模式293+- [UI 对比指南](docs/advanced/UI-COMPARISON.md) - 跨平台 UI 结构对比(属性/样式/位置/结构四种模式)
284-- [对比测试指南](../../../app/tests/COMPARISON-GUIDE.md) - TextInput跨平台对比294+- [用户指南](docs/getting-started/USER-GUIDE.md) - 配置、CLI、API 使用与指标清单
285 295 
286### 辅助文档296### 辅助文档
297+ 
287- [构建文档](BUILD.md) - 构建流程298- [构建文档](BUILD.md) - 构建流程
288- [变更日志](CHANGELOG.md) - 版本历史299- [变更日志](CHANGELOG.md) - 版本历史
289 300 
290## 示例项目301## 示例项目
291 302 
292-完整的示例项目位于:`../../../app/`303+示例与实战配置位于配套的 RNOH 测试仓(`rnoh_test`,与 `@cptf/framework` 通过 `workspace:*` 协作)。
293 304 
294### 测试用例示例305### 测试用例示例
306+ 
295```307```
296app/tests/308app/tests/
297├── cases/ # 测试用例目录309├── cases/ # 测试用例目录
298│ ├── performance/ # 性能测试用例310│ ├── performance/ # 性能测试用例
299-│ └── functional/ # 功能测试用例(含跨平台对比)311+│ ├── functional/ # 功能测试用例(含跨平台对比)
300-├── TextInput-Comparison-Manifest.json # TextInput对比配置312+│ └── stability/ # 稳定性测试用例
301-└── scenes/ # 测试场景组件313+├── scenes/ # 测试场景组件
314+└── test-cases-index.ts # cptf index 生成的用例索引
302```315```
303 316 
304### 执行示例317### 执行示例
305 318 
306-框架层CLI命令(需先 `pnpm build`):319+框架层 CLI 命令(需先 `pnpm build`):
320+ 
307```bash321```bash
308# 在项目根目录执行322# 在项目根目录执行
309node packages/framework/dist/cli.js run -p harmony -m performance323node packages/framework/dist/cli.js run -p harmony -m performance
@@ -314,7 +328,7 @@ node packages/framework/dist/cli.js compare -t textinput-placeholder-compatibili
314cptf run -m performance328cptf run -m performance
315cptf compare -t textinput-placeholder-compatibility329cptf compare -t textinput-placeholder-compatibility
316 330 
317-# 应用层执行(cd app)331+# 应用层执行(cd app,位于 rnoh_test 仓)
318pnpm functional:run # 功能模块批量运行(app 侧脚本)332pnpm functional:run # 功能模块批量运行(app 侧脚本)
319pnpm test:compare # 委托给 cptf compare333pnpm test:compare # 委托给 cptf compare
320cptf compare -t <case-id> # 单用例对比334cptf compare -t <case-id> # 单用例对比
@@ -322,12 +336,13 @@ cptf compare -t <case-id> # 单用例对比
322 336 
323## 测试覆盖337## 测试覆盖
324 338 
325-### Executor测试用例339+### Executor 测试用例
340+ 
326- 性能测试(全指标、日历、列表滚动、内存泄漏等)341- 性能测试(全指标、日历、列表滚动、内存泄漏等)
327- 稳定性测试(后台恢复、点击、崩溃恢复、内存压力、Monkey、时长)342- 稳定性测试(后台恢复、点击、崩溃恢复、内存压力、Monkey、时长)
328- 功能测试(基础组件、跨平台一致性)343- 功能测试(基础组件、跨平台一致性)
329 344 
330-详见:[对比测试指南](../../../app/tests/COMPARISON-GUIDE.md)345+详见:[UI 对比指南](docs/advanced/UI-COMPARISON.md)
331 346 
332## 性能优化成果347## 性能优化成果
333 348 
@@ -363,7 +378,7 @@ cptf compare -t <case-id> # 单用例对比
363// 1. 实现IosSystem类(约300行)378// 1. 实现IosSystem类(约300行)
364class IosSystem extends BaseSystem implements ISystem {379class IosSystem extends BaseSystem implements ISystem {
365 getDeviceFlag(): string { return '-u'; }380 getDeviceFlag(): string { return '-u'; }
366- 381+ 
367 async launchApp(bundleId: string): Promise<boolean> {382 async launchApp(bundleId: string): Promise<boolean> {
368 await this.execCommand(`ios-deploy --bundle_id=${bundleId}`);383 await this.execCommand(`ios-deploy --bundle_id=${bundleId}`);
369 return true;384 return true;
@@ -389,8 +404,8 @@ const system = platform === 'ios' ? new IosSystem(config, logger) : ...
389 404 
390```typescript405```typescript
391// TestRunner.run()自动清理406// TestRunner.run()自动清理
392-await this.performanceCollector.destroy(); // 清理性能监控定时器407+await this.performanceCollector.destroy(); // 清理性能监控定时器
393-await this.system.destroy(); // 清理系统缓存定时器408+await this.system.destroy(); // 清理系统缓存定时器
394```409```
395 410 
396## License411## License
@@ -408,9 +423,10 @@ v0.3.1
408---423---
409 424 
410**相关链接**:425**相关链接**:
411-- [React Native官方文档](https://www.react-native.cn/docs)426+ 
412-- [Flutter官方文档](https://flutter.dev/docs)427+- [React Native 官方文档](https://www.react-native.cn/docs)
428+- [Flutter 官方文档](https://flutter.dev/docs)
413- [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html)429- [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html)
414- [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/)430- [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/)
415-- [HarmonyOS开发文档](https://developer.harmonyos.com)431+- [HarmonyOS 开发文档](https://developer.harmonyos.com)
416-- [Android开发文档](https://developer.android.com)432+- [Android 开发文档](https://developer.android.com)
Mpackages/framework/docs/MIGRATION.md+134-18File content is under review, please try again later
@@ -1,6 +1,6 @@
1# 性能调优指南1# 性能调优指南
2 2 
3-> 调优 `PerformanceCollector` + 16 个 `BaseMetricCollector` 的间隔、阈值、采样策略。3+> 调优 `PerformanceCollector` + 17 个 `BaseMetricCollector` 的间隔、阈值、采样策略。
4> 配合 [PERFORMANCE-METRICS-SPEC.md](architecture/PERFORMANCE-METRICS-SPEC.md) 和 [STABILITY-SPEC.md](architecture/STABILITY-SPEC.md) 使用。4> 配合 [PERFORMANCE-METRICS-SPEC.md](architecture/PERFORMANCE-METRICS-SPEC.md) 和 [STABILITY-SPEC.md](architecture/STABILITY-SPEC.md) 使用。
5 5 
6---6---
@@ -18,18 +18,18 @@
18})18})
19```19```
20 20 
21-| 场景 | 推荐 interval | 理由 |21+| 场景 | 推荐 interval | 理由 |
22-|------|---------------|------|22+| ------------------ | ------------- | -------------- |
23-| 启动耗时测试 | 200ms | 抓瞬时峰值 |23+| 启动耗时测试 | 200ms | 抓瞬时峰值 |
24-| 滚动 / 动画 | 100ms | 抓丢帧 |24+| 滚动 / 动画 | 100ms | 抓丢帧 |
25-| 长时压测(>10min) | 2000ms | 控制样本量 |25+| 长时压测(>10min) | 2000ms | 控制样本量 |
26-| 默认 | 1000ms | 平衡精度与开销 |26+| 默认 | 1000ms | 平衡精度与开销 |
27 27 
28`historySize`(默认 720 = 12 分钟):28`historySize`(默认 720 = 12 分钟):
29 29 
30```ts30```ts
31import { globalConfig } from '@cptf/framework';31import { globalConfig } from '@cptf/framework';
32-globalConfig.getPerformance().historySize = 1800; // 30 分钟32+globalConfig.getPerformance().historySize = 1800; // 30 分钟
33```33```
34 34 
35## 2. 阈值配置35## 2. 阈值配置
@@ -52,12 +52,12 @@ globalConfig.getPerformance().historySize = 1800; // 30 分钟
52})52})
53```53```
54 54 
55-| 指标 | 字段 | 单位 | 用途 |55+| 指标 | 字段 | 单位 | 用途 |
56-|------|------|------|------|56+| ------------------------ | -------------------------------- | ------ | -------- |
57-| `fps` | `min` / `avg` / `max` / `stdDev` | Hz | 帧率波动 |57+| `fps` | `min` / `avg` / `max` / `stdDev` | Hz | 帧率波动 |
58-| `memory` | `peak` / `avg` / `growthRate` | KB | 内存压力 |58+| `memory` | `peak` / `avg` / `growthRate` | KB | 内存压力 |
59-| `cpu` | `avg` / `peak` | % | CPU 压力 |59+| `cpu` | `avg` / `peak` | % | CPU 压力 |
60-| `fpsJank` / `fpsBigJank` | `max` / `count` | frames | 卡顿 |60+| `fpsJank` / `fpsBigJank` | `max` / `count` | frames | 卡顿 |
61 61 
62## 3. 稳定性 detector 调优62## 3. 稳定性 detector 调优
63 63 
@@ -90,20 +90,20 @@ globalConfig.getPerformance().historySize = 1800; // 30 分钟
90 90 
91### 3.1 OLED vs LCD91### 3.1 OLED vs LCD
92 92 
93-| 屏幕类型 | brightnessThreshold | whiteRatioThreshold |93+| 屏幕类型 | brightnessThreshold | whiteRatioThreshold |
94-|----------|---------------------|---------------------|94+| ------------ | ------------------- | ------------------- |
95-| LCD(高亮) | 245 | 0.90 |95+| LCD(高亮) | 245 | 0.90 |
96-| OLED(深黑) | 235 | 0.80 |96+| OLED(深黑) | 235 | 0.80 |
97-| 折叠屏 | 240 | 0.85 |97+| 折叠屏 | 240 | 0.85 |
98 98 
99### 3.2 短压测 vs 长压测99### 3.2 短压测 vs 长压测
100 100 
101| 测试时长 | growthThresholdKBPerMin | minSamples | minDurationMs |101| 测试时长 | growthThresholdKBPerMin | minSamples | minDurationMs |
102-|----------|--------------------------|------------|----------------|102+| -------- | ----------------------- | ---------- | ------------- |
103-| < 1min | 不检查 | 0 | 0 |103+| < 1min | 不检查 | 0 | 0 |
104-| 1-5min | 50 | 5 | 30_000 |104+| 1-5min | 50 | 5 | 30_000 |
105-| 5-30min | 100 | 10 | 60_000 |105+| 5-30min | 100 | 10 | 60_000 |
106-| > 30min | 200 | 30 | 300_000 |106+| > 30min | 200 | 30 | 300_000 |
107 107 
108## 4. 自定义 collector108## 4. 自定义 collector
109 109 
@@ -125,7 +125,7 @@ class FirstPaintCollector extends BaseMetricCollector {
125 async collect(ctx: MetricCollectorContext): Promise<Record<string, number>> {125 async collect(ctx: MetricCollectorContext): Promise<Record<string, number>> {
126 // 通过自定义 hook 检测首屏渲染完成126 // 通过自定义 hook 检测首屏渲染完成
127 if (this._renderedAt === 0) {127 if (this._renderedAt === 0) {
128- this._renderedAt = await ctx.device.executeShell?.('getprop cptf.firstRenderAt') ?? 0;128+ this._renderedAt = (await ctx.device.executeShell?.('getprop cptf.firstRenderAt')) ?? 0;
129 }129 }
130 return { firstPaint: this._renderedAt - this.startTime };130 return { firstPaint: this._renderedAt - this.startTime };
131 }131 }
@@ -139,11 +139,7 @@ MetricCollectorFactory.register('firstPaint', FirstPaintCollector);
139`PerformanceCollector` 默认并行采集 `fps | memory | cpu`:139`PerformanceCollector` 默认并行采集 `fps | memory | cpu`:
140 140 
141```ts141```ts
142-const [fps, mem, cpu] = await Promise.all([142+const [fps, mem, cpu] = await Promise.all([this.getFPS(), this.getMemory(), this.getCPU()]);
143- this.getFPS(),
144- this.getMemory(),
145- this.getCPU(),
146-]);
147```143```
148 144 
149如需禁用其中某项(节省开销):145如需禁用其中某项(节省开销):
@@ -163,7 +159,7 @@ const [fps, mem, cpu] = await Promise.all([
163```ts159```ts
164import { PerformanceCollector } from '@cptf/framework';160import { PerformanceCollector } from '@cptf/framework';
165const collector = new PerformanceCollector(system, historySize, logger, {161const collector = new PerformanceCollector(system, historySize, logger, {
166- driftThresholdPct: 0.2, // 默认 20%162+ driftThresholdPct: 0.2, // 默认 20%
167});163});
168```164```
169 165 
@@ -181,7 +177,7 @@ CPTF_ASYNC_REPORTS=false cptf run # 关闭异步(同步生成)
181长 monkey 测试(> 5000 次)启用并发桶(0.2.x 已实现):177长 monkey 测试(> 5000 次)启用并发桶(0.2.x 已实现):
182 178 
183```ts179```ts
184-await system.executeMonkey(10_000, 50); // 10000 次,50ms 间隔180+await system.executeMonkey(10_000, 50); // 10000 次,50ms 间隔
185```181```
186 182 
187- `MONKEY_CONCURRENCY` 默认 4(`HarmonySystem.MONKEY_CONCURRENCY`)183- `MONKEY_CONCURRENCY` 默认 4(`HarmonySystem.MONKEY_CONCURRENCY`)
@@ -192,37 +188,37 @@ await system.executeMonkey(10_000, 50); // 10000 次,50ms 间隔
192 188 
193### Harmony189### Harmony
194 190 
195-| 命令 | 超时(默认) | 调优建议 |191+| 命令 | 超时(默认) | 调优建议 |
196-|------|--------------|----------|192+| ----------------------------------------- | ------------ | -------------------- |
197-| `hidumper --mem` | 8s | 大量进程时调高到 15s |193+| `hidumper --mem` | 8s | 大量进程时调高到 15s |
198-| `hidumper --cpuusage` | 5s | 多核设备调高到 10s |194+| `hidumper --cpuusage` | 5s | 多核设备调高到 10s |
199-| `hidumper -s RenderService -a 'fpsCount'` | 3s | 锁定窗口名后稳定 |195+| `hidumper -s RenderService -a 'fpsCount'` | 3s | 锁定窗口名后稳定 |
200 196 
201### Android197### Android
202 198 
203-| 命令 | 超时 | 调优 |199+| 命令 | 超时 | 调优 |
204-|------|------|------|200+| ----------------- | ---- | ------------------------------ |
205-| `dumpsys gfxinfo` | 5s | 压测前 `dumpsys gfxinfo reset` |201+| `dumpsys gfxinfo` | 5s | 压测前 `dumpsys gfxinfo reset` |
206-| `dumpsys meminfo` | 8s | 大量进程时调高 |202+| `dumpsys meminfo` | 8s | 大量进程时调高 |
207-| `top -n 1` | 3s | 性能压测建议 |203+| `top -n 1` | 3s | 性能压测建议 |
208 204 
209### iOS205### iOS
210 206 
211-| 命令 | 超时 | 调优 |207+| 命令 | 超时 | 调优 |
212-|------|------|------|208+| ------------------------------- | ------------- | ----------------- |
213-| `xctrace record` | 15s + 2s 录屏 | 失败时 retry 2 次 |209+| `xctrace record` | 15s + 2s 录屏 | 失败时 retry 2 次 |
214-| `xcrun simctl spawn ... sysctl` | 5s | 模拟器专属 |210+| `xcrun simctl spawn ... sysctl` | 5s | 模拟器专属 |
215 211 
216## 10. 性能基准212## 10. 性能基准
217 213 
218以下数据基于内部测试(仅参考):214以下数据基于内部测试(仅参考):
219 215 
220-| 场景 | CPTF (HTX/ATX) | SoloPi (adb) | 差异 |216+| 场景 | CPTF (HTX/ATX) | SoloPi (adb) | 差异 |
221-|------|------------------|---------------|------|217+| ----------------- | -------------- | ------------ | -------- |
222-| 单用例 click 响应 | 80-120ms | 250-400ms | **3-4x** |218+| 单用例 click 响应 | 80-120ms | 250-400ms | **3-4x** |
223-| 截图采集 | 60-80ms | 150-250ms | **2-3x** |219+| 截图采集 | 60-80ms | 150-250ms | **2-3x** |
224-| FPS 实时采集 | 100-200ms | 500-1000ms | **3-5x** |220+| FPS 实时采集 | 100-200ms | 500-1000ms | **3-5x** |
225-| 跨平台对比报告 | 1-2s | N/A | 独有 |221+| 跨平台对比报告 | 1-2s | N/A | 独有 |
226 222 
227详细 benchmark 数据待补充(见 README §Evidence 章节)。223详细 benchmark 数据待补充(见 README §Evidence 章节)。
228 224 
@@ -244,11 +240,11 @@ cptf run -m performance --monitoring.interval=2000
244 240 
245`cptf` CLI 启动时(`packages/framework/src/cli/index.ts`)检测当前 V8 堆上限,若低于阈值则 re-exec 加大堆(`--max-old-space-size`),避免大 comparison 任务(34+ cases × full UI tree)OOM。阈值、re-exec 目标与 ts-node 内存缓存**可经环境变量配置,默认与上游一致**(8GB 阈值 / 16GB re-exec / 不设 memoryCache),不影响默认行为。241`cptf` CLI 启动时(`packages/framework/src/cli/index.ts`)检测当前 V8 堆上限,若低于阈值则 re-exec 加大堆(`--max-old-space-size`),避免大 comparison 任务(34+ cases × full UI tree)OOM。阈值、re-exec 目标与 ts-node 内存缓存**可经环境变量配置,默认与上游一致**(8GB 阈值 / 16GB re-exec / 不设 memoryCache),不影响默认行为。
246 242 
247-| 环境变量 | 含义 | 默认 | 16GB Mac 示例 |243+| 环境变量 | 含义 | 默认 | 16GB Mac 示例 |
248-|---|---|---|---|244+| --------------------------- | --------------------------------------- | ---------- | ------------- |
249-| `CPTF_HEAP_THRESHOLD` | 触发 re-exec 的堆阈值(GB) | `8` | `4` |245+| `CPTF_HEAP_THRESHOLD` | 触发 re-exec 的堆阈值(GB) | `8` | `4` |
250-| `CPTF_MAX_OLD_SPACE` | re-exec 目标 `--max-old-space-size`(MB) | `16384` | `8192` |246+| `CPTF_MAX_OLD_SPACE` | re-exec 目标 `--max-old-space-size`(MB) | `16384` | `8192` |
251-| `CPTF_TS_NODE_MEMORY_CACHE` | ts-node `memoryCache`,设 `false` 关闭 | 不设(官方) | `false` |247+| `CPTF_TS_NODE_MEMORY_CACHE` | ts-node `memoryCache`,设 `false` 关闭 | 不设(官方) | `false` |
252 248 
253**默认行为**(不设环境变量):阈值 8GB,re-exec 到 16GB——适合 32GB+ 内存机器跑大 comparison。249**默认行为**(不设环境变量):阈值 8GB,re-exec 到 16GB——适合 32GB+ 内存机器跑大 comparison。
254 250 
@@ -6,75 +6,75 @@
6 6 
7## 快速开始7## 快速开始
8 8 
9-| 文档 | 说明 | 适用读者 |9+| 文档 | 说明 | 适用读者 |
10-|------|------|----------|10+| ----------------------------------------------------------- | ------------------------------------------------------------ | ---------- |
11-| [用户指南](getting-started/USER-GUIDE.md) | 安装、配置、CLI命令、API使用、最佳实践 | 测试工程师 |11+| [用户指南](getting-started/USER-GUIDE.md) | 安装、配置、CLI 命令、API 使用、最佳实践 | 测试工程师 |
12-| [声明式测试指南](getting-started/DECLARATIVE-TEST-GUIDE.md) | @Test/@Step装饰器、TestConfig/StepConfig配置、性能测试编写 | 测试工程师 |12+| [声明式测试指南](getting-started/DECLARATIVE-TEST-GUIDE.md) | @Test/@Step 装饰器、TestConfig/StepConfig 配置、性能测试编写 | 测试工程师 |
13 13 
14## 进阶使用14## 进阶使用
15 15 
16-| 文档 | 说明 | 适用读者 |16+| 文档 | 说明 | 适用读者 |
17-|------|------|----------|17+| --------------------------------------------------- | ------------------------------------------------ | -------------- |
18-| [命令式测试指南](advanced/IMPERATIVE-TEST-GUIDE.md) | TestExecutor模式、自定义导航/迭代/聚合 | 高级测试工程师 |18+| [命令式测试指南](advanced/IMPERATIVE-TEST-GUIDE.md) | TestExecutor 模式、自定义导航/迭代/聚合 | 高级测试工程师 |
19-| [Expect断言指南](advanced/EXPECT-GUIDE.md) | 声明式断言、ExpectConfig、验证策略 | 测试工程师 |19+| [Expect 断言指南](advanced/EXPECT-GUIDE.md) | 声明式断言、ExpectConfig、验证策略 | 测试工程师 |
20-| [UI对比指南](advanced/UI-COMPARISON.md) | 跨平台UI结构对比、属性/样式/位置/层级4维对比 | 测试工程师 |20+| [UI 对比指南](advanced/UI-COMPARISON.md) | 跨平台 UI 结构对比、属性/样式/位置/层级 4 维对比 | 测试工程师 |
21 21 
22## 架构与规格22## 架构与规格
23 23 
24-| 文档 | 说明 | 适用读者 |24+| 文档 | 说明 | 适用读者 |
25-|------|------|----------|25+| -------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------- |
26-| [架构详解](architecture/ARCHITECTURE.md) | 五层架构、源码目录、依赖规则、接口分离 | 框架开发者 |26+| [架构详解](architecture/ARCHITECTURE.md) | 五层架构、源码目录、依赖规则、接口分离 | 框架开发者 |
27-| [架构优势](architecture/ARCHITECTURE-OVERVIEW.md) | 双引擎加速、Deep Link导航、可扩展性 | 选型决策者 |27+| [架构优势](architecture/ARCHITECTURE-OVERVIEW.md) | 双引擎加速、Deep Link 导航、可扩展性 | 选型决策者 |
28-| [能力规格](architecture/CAPABILITY-SPEC.md) | 性能指标规格、API规格、数据结构(含实现状态) | 框架开发者 |28+| [能力规格](architecture/CAPABILITY-SPEC.md) | 性能指标规格、API 规格、数据结构(含实现状态) | 框架开发者 |
29-| [性能指标规格](architecture/PERFORMANCE-METRICS-SPEC.md) | 16个性能指标定义、采集方式、业界对标 | 性能测试工程师 |29+| [性能指标规格](architecture/PERFORMANCE-METRICS-SPEC.md) | 19 个性能指标定义(17 内置 + 2 插件)、采集方式、业界对标 | 性能测试工程师 |
30-| [设备指标采集设计](architecture/DEVICE-METRICS-COLLECTION-DESIGN.md) | FPS/内存/CPU/指令数采集通道最佳实践(SP_daemon server 模式,真机实测) | 框架开发者 |30+| [设备指标采集设计](architecture/DEVICE-METRICS-COLLECTION-DESIGN.md) | FPS/内存/CPU/指令数采集通道最佳实践(SP_daemon server 模式,真机实测) | 框架开发者 |
31-| [流畅度指标规范 v2](architecture/SMOOTHNESS-METRICS-SPEC.md) | stability/dropRate/fpsP5 业界口径(Vitals/PerfDog/GameBench),fpsMin 已删除 | 框架开发者、性能测试工程师 |31+| [流畅度指标规范 v2](architecture/SMOOTHNESS-METRICS-SPEC.md) | stability/dropRate/fpsP5 业界口径(Vitals/PerfDog/GameBench),fpsMin 已删除 | 框架开发者、性能测试工程师 |
32-| [稳定性检测参数规格](architecture/STABILITY-SPEC.md) | 9 个 detector 参数 + 3 检测器调优 + 与 13 指标关系 | 稳定性测试工程师 |32+| [稳定性检测参数规格](architecture/STABILITY-SPEC.md) | 9 个 detector 参数 + 3 检测器调优 + 与性能指标边界 | 稳定性测试工程师 |
33-| [UITest 连接稳定性设计](architecture/UITEST-STABILITY-DESIGN.md) | TCP 重连/健康检查/布局提供者设计规范(连接层/系统层/编排层修改必读) | 连接层开发者 |33+| [UITest 连接稳定性设计](architecture/UITEST-STABILITY-DESIGN.md) | TCP 重连/健康检查/布局提供者设计规范(连接层/系统层/编排层修改必读) | 连接层开发者 |
34 34 
35## 框架开发35## 框架开发
36 36 
37-| 文档 | 说明 | 适用读者 |37+| 文档 | 说明 | 适用读者 |
38-|------|------|----------|38+| ------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------- |
39-| [扩展开发指南](development/DEVELOPMENT-GUIDE.md) | 新增平台、能力、报告生成器 | 框架开发者 |39+| [扩展开发指南](development/DEVELOPMENT-GUIDE.md) | 新增平台、能力、报告生成器 | 框架开发者 |
40-| [插件开发指南](development/PLUGIN-GUIDE.md) | 新平台/指标/报告/导航/跨平台原生指标桥接 5 大扩展点注册 + iOS Phase 路线图 | 插件作者 |40+| [插件开发指南](development/PLUGIN-GUIDE.md) | 新平台/指标/报告/导航/跨平台原生指标桥接 5 大扩展点注册 + iOS Phase 路线图 | 插件作者 |
41-| [自定义指标采集器指南](development/METRIC-COLLECTOR-GUIDE.md) | 通过 BaseMetricCollector + MetricCollectorFactory 扩展性能指标体系 | 框架扩展者 |41+| [自定义指标采集器指南](development/METRIC-COLLECTOR-GUIDE.md) | 通过 BaseMetricCollector + MetricCollectorFactory 扩展性能指标体系 | 框架扩展者 |
42-| [iOS 扩展方案](design/IOS-EXTENSION-PLAN.md) | iOS 平台接入详细设计(双引擎/WDA/xctrace) | iOS 接入开发者 |42+| [iOS 扩展方案](design/IOS-EXTENSION-PLAN.md) | iOS 平台接入详细设计(双引擎/WDA/xctrace) | iOS 接入开发者 |
43-| [录屏与屏幕分析规划](design/SCREEN-RECORDING-PLAN.md) | 三级录制链(CLI/设备端组件/截图流)+ 分析器演进路线(规划中未实施) | 框架开发者 |43+| [录屏与屏幕分析规划](design/SCREEN-RECORDING-PLAN.md) | 三级录制链(CLI/设备端组件/截图流)+ 分析器演进路线(规划中未实施) | 框架开发者 |
44 44 
45## 运维与迁移45## 运维与迁移
46 46 
47| 文档 | 说明 | 适用读者 |47| 文档 | 说明 | 适用读者 |
48|------|------|----------|48|------|------|----------|
49| [故障排查手册](TROUBLESHOOTING.md) | 设备 / 用例 / 性能 / 对比 / 报告 6 类常见问题 + 排查步骤 | 所有人 |49| [故障排查手册](TROUBLESHOOTING.md) | 设备 / 用例 / 性能 / 对比 / 报告 6 类常见问题 + 排查步骤 | 所有人 |
50-| [迁移指南](MIGRATION.md) | v0.1.x → v0.2.x 破坏性变更 + 升级步骤 | 升级用户 |50+| [迁移指南](MIGRATION.md) | v0.1.x → v0.2.x、v0.2.x → v0.3.x 破坏性变更 + 升级步骤 | 升级用户 |
51-| [Baseline 管理](BASELINE-MANAGEMENT.md) | baseline 存放 / 版本化 / 跨设备共享 / 0.3.0 路线图 | 测试工程师 |51+| [Baseline 管理](BASELINE-MANAGEMENT.md) | baseline 存放 / 版本化(`--rn-version` 分段)/ 跨设备共享 / `.history` 备份 | 测试工程师 |
52| [性能调优指南](PERFORMANCE-TUNING.md) | 采集间隔 / 阈值 / detector 调优 / 自定义 collector | 性能测试工程师 |52| [性能调优指南](PERFORMANCE-TUNING.md) | 采集间隔 / 阈值 / detector 调优 / 自定义 collector | 性能测试工程师 |
53 53 
54## 选型参考54## 选型参考
55 55 
56-| 文档 | 说明 | 适用读者 |56+| 文档 | 说明 | 适用读者 |
57-|------|------|----------|57+| -------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------- |
58-| [框架对比分析](reference/FRAMEWORK-COMPARISON.md) | CPTF vs Jest/Appium/Cypress/Detox/Playwright | 选型决策者 |58+| [框架对比分析](reference/FRAMEWORK-COMPARISON.md) | CPTF vs Jest/Appium/Cypress/Detox/Playwright | 选型决策者 |
59| [五框架深度对比](reference/FIVE-FRAMEWORK-COMPARISON.md) | CPTF vs Detox/Hypium/Maestro/Appium 14 维度详细对比 + 代码示例 + 决策树 | 选型决策者 / 架构师 |59| [五框架深度对比](reference/FIVE-FRAMEWORK-COMPARISON.md) | CPTF vs Detox/Hypium/Maestro/Appium 14 维度详细对比 + 代码示例 + 决策树 | 选型决策者 / 架构师 |
60-| [稳定性对比分析](reference/STABILITY-COMPARISON.md) | 7维度对标业界稳定性方案 | 选型决策者 |60+| [稳定性对比分析](reference/STABILITY-COMPARISON.md) | 7 维度对标业界稳定性方案 | 选型决策者 |
61 61 
62## 内部优化分析62## 内部优化分析
63 63 
64-| 文档 | 说明 | 适用读者 |64+| 文档 | 说明 | 适用读者 |
65-|------|------|----------|65+| ------------------- | ------------------------------------------------------ | ---------- |
66| [待办清单](TODO.md) | 待做事项(性能优化/真机回归/发版)+ 开发约束与不做清单 | 框架维护者 |66| [待办清单](TODO.md) | 待做事项(性能优化/真机回归/发版)+ 开发约束与不做清单 | 框架维护者 |
67 67 
68## 设计记录与调研(历史资料)68## 设计记录与调研(历史资料)
69 69 
70> 设计阶段决策记录与第三方框架调研,反映立项时点的判断;当前架构以 [ARCHITECTURE.md](architecture/ARCHITECTURE.md) 为准。70> 设计阶段决策记录与第三方框架调研,反映立项时点的判断;当前架构以 [ARCHITECTURE.md](architecture/ARCHITECTURE.md) 为准。
71 71 
72-| 文档 | 说明 | 适用读者 |72+| 文档 | 说明 | 适用读者 |
73-|------|------|----------|73+| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------- |
74-| [场景设计](design/01-SCENARIOS.md) | 立项阶段核心场景定位(跨端 UI 自动化/性能/稳定性) | 了解设计初衷 |74+| [场景设计](design/01-SCENARIOS.md) | 立项阶段核心场景定位(跨端 UI 自动化/性能/稳定性) | 了解设计初衷 |
75-| [需求分析](design/02-REQUIREMENTS.md) | 立项阶段功能/非功能需求清单 | 了解设计初衷 |75+| [需求分析](design/02-REQUIREMENTS.md) | 立项阶段功能/非功能需求清单 | 了解设计初衷 |
76-| [Detox 扩展分析](analysis/detox/README.md) | Detox 框架扩展 HarmonyOS 的 6 篇深度分析(能力/双通道架构/集成方案),CPTF 跨平台方案选型的对照参考 | 选型决策者 / 架构师 |76+| [Detox 扩展分析](analysis/detox/README.md) | Detox 框架扩展 HarmonyOS 的 6 篇深度分析(能力/双通道架构/集成方案),CPTF 跨平台方案选型的对照参考 | 选型决策者 / 架构师 |
77-| [@ohos.UiTest API 参考](dependence/UiTest.md) | HarmonyOS UiTest 官方 API 整理(控件查找/注入/截图/dumpLayout),`uitest-transport`/`uitest-driver` 实现的协议依据 | 系统层开发者 |77+| [@ohos.UiTest API 参考](dependence/UiTest.md) | HarmonyOS UiTest 官方 API 整理(控件查找/注入/截图/dumpLayout),`uitest-transport`/`uitest-driver` 实现的协议依据 | 系统层开发者 |
78 78 
79---79---
80 80 
@@ -96,21 +96,21 @@
96 96 
97### 框架架构图97### 框架架构图
98 98 
99-| 图 | 说明 |99+| 图 | 说明 |
100-|----|------|100+| -------------------------------------------------------------- | ----------------------------------------------------- |
101-| [分层架构总览](architecture/architecture-logical.svg) | 五层(对外概览常合并叙述为 3 组:基础设施/系统/调度) |101+| [分层架构总览](architecture/architecture-logical.svg) | 五层(对外概览常合并叙述为 3 组:基础设施/系统/调度) |
102-| [UI自动化双引擎](architecture/architecture-ui-automation.svg) | HTX/ATX通信架构 |102+| [UI 自动化双引擎](architecture/architecture-ui-automation.svg) | HTX/ATX 通信架构 |
103-| [智能导航流程](architecture/architecture-navigation.svg) | autoNavigate + smartNavigateBack |103+| [智能导航流程](architecture/architecture-navigation.svg) | autoNavigate + smartNavigateBack |
104 104 
105### 设计阶段架构图(design/svg)105### 设计阶段架构图(design/svg)
106 106 
107-| 图 | 说明 |107+| 图 | 说明 |
108-|----|------|108+| --------------------------------------------------------------------- | -------------------------------------------------------- |
109-| [01-overview-developmental](design/svg/01-overview-developmental.svg) | 整体发展视角概览 |109+| [01-overview-developmental](design/svg/01-overview-developmental.svg) | 整体发展视角概览 |
110-| [02-layer-base](design/svg/02-layer-base.svg) | 基础层(types/config/utils/workers) |110+| [02-layer-base](design/svg/02-layer-base.svg) | 基础层(types/config/utils/workers) |
111-| [03-layer-system](design/svg/03-layer-system.svg) | 系统层(ISystem 7 子接口) |111+| [03-layer-system](design/svg/03-layer-system.svg) | 系统层(ISystem 7 子接口) |
112-| [04-layer-capability](design/svg/04-layer-capability.svg) | 能力层(ui/measurement/analysis/recorder) |112+| [04-layer-capability](design/svg/04-layer-capability.svg) | 能力层(ui/measurement/analysis/recorder) |
113-| [05-layer-orchestration](design/svg/05-layer-orchestration.svg) | 编排层(TestRunner/ComparisonRunner/ParallelTestRunner) |113+| [05-layer-orchestration](design/svg/05-layer-orchestration.svg) | 编排层(TestRunner/ComparisonRunner/ParallelTestRunner) |
114-| [06-physical-view](design/svg/06-physical-view.svg) | 物理视图(npm 包 + monorepo) |114+| [06-physical-view](design/svg/06-physical-view.svg) | 物理视图(npm 包 + monorepo) |
115-| [07-scenario-view](design/svg/07-scenario-view.svg) | 场景视图(典型执行流程) |115+| [07-scenario-view](design/svg/07-scenario-view.svg) | 场景视图(典型执行流程) |
116-| [08-process-view](design/svg/08-process-view.svg) | 进程视图(事件流 / 异步链) |116+| [08-process-view](design/svg/08-process-view.svg) | 进程视图(事件流 / 异步链) |
@@ -2,27 +2,41 @@
2 2 
3## 版本3## 版本
4 4 
5-- 版本号: 0.2.15+- 版本号: 0.3.1
6-- 更新日期: 2026-07-016+- 更新日期: 2026-10-13
7- 框架: CPTF (Cross-Platform Test Framework)7- 框架: CPTF (Cross-Platform Test Framework)
8 8 
9-### v0.2.1 同步说明9+### v0.3.1 同步说明
10 10 
11-- 同步 3.1 整改后真实采集口径:底层 `PerformanceCollector` 实际只采集 `fps | memory | cpu` 3 个原始指标;`memoryPeak` / `cpuPeak` / `blankScreenRatio` 是 8 个 `BaseMetricCollector` 聚合(`MemoryPeakCollector` / `CPUPeakCollector` / `BlankScreenRatioCollector`)。11+- 同步指标计数口径:`PerfMetricType` 联合 19 成员 = 17 内置 collector + 2 插件(`@cptf/benchmark` 的 renderTime/unmountTime);`fpsMin`/`fpsMax` 已删除(2026-08-25),`stability`/`dropRate`/`fpsP5` 已加入(SMOOTHNESS-METRICS-SPEC v2);`exitLatency` 幽灵成员已从类型联合移除。
12-- 同步 iOS xctrace 真实能力:xctrace FPS 已支持结果缓存(2s TTL)+ 并发 list/export + 2 次重试;录屏已实现(simulator 用 `simctl io recordVideo`,真机用 `idevicerecordvideo`)。12+- 保留 v0.2.1 说明:底层 `PerformanceCollector` 实际只采集 `fps | memory | cpu` 3 个原始指标;`memoryPeak` / `cpuPeak` / `blankScreenRatio` 是 `BaseMetricCollector` 聚合(`MemoryPeakCollector` / `CPUPeakCollector` / `BlankScreenRatioCollector`)。
13+- 保留 v0.2.1 说明:iOS xctrace FPS 已支持结果缓存(2s TTL)+ 并发 list/export + 2 次重试;录屏已实现(simulator 用 `simctl io recordVideo`,真机用 `idevicerecordvideo`)。
13 14 
14---15---
15 16 
16## 一、性能指标规格17## 一、性能指标规格
17 18 
18-### 1.0 实现状态(v0.2.1)19+### 1.0 实现状态(v0.3.1)
19 20 
20-| 类别 | 已实现 | 规划中 | 总计 |21+**计数口径(单一真相 = `src/base/types/index.ts` 的 `PerfMetricType` 联合 + `MetricCollectorFactory` 注册表)**:
21-|------|--------|--------|------|22+ 
22-| **PerfMetricType** | `fps / memory / cpu`(3 个真实采集) + `fpsMin / fpsMax / fpsJank / fpsBigJank / fpsStdDev / memoryPeak / memoryAvg / memoryGrowthRate / cpuPeak / cpuAvg / blankScreenRatio`(11 个聚合) + `startLatency / finishLatency / hotStartLatency / interactionLatency`(4 个 lifecycle 信号) | (无,全部 16 类已落地) | 16 |23+- `PerfMetricType` 联合 **19 成员**
23-| **MetricType**(扩展) | `crashCount`(报告层聚合) | `whiteScreenCount / jankCount / droppedFrames / completionLatency{,Avg,Max}`(6 个) | 7 |24+- 框架内置 collector **17 个**(联合成员中除 2 个插件指标外全部有默认注册)
25+- `@cptf/benchmark` 插件注册 **2 个**(`renderTime` / `unmountTime`)
26+- 已实现合计 **19 个**;**无**「仅声明未实现」成员
27+ 
28+| 类别 | 成员 | 计数 |
29+|------|------|------|
30+| **直接采集**(`PerfMetricType` 成员,每轮一个 sample) | `fps` | 1 |
31+| **聚合指标**(`BaseMetricCollector` 从历史 buffer 计算) | `fpsJank` / `fpsBigJank` / `fpsStdDev` / `stability` / `dropRate` / `fpsP5` / `blankScreenRatio` / `memoryPeak` / `memoryAvg` / `memoryGrowthRate` / `cpuPeak` / `cpuAvg` | 12 |
32+| **lifecycle 信号**(Collector 在 onStart/onFinish 记录时间戳) | `startLatency` / `finishLatency` / `interactionLatency` / `hotStartLatency` | 4 |
33+| **插件扩展**(`@cptf/benchmark` 注册) | `renderTime` / `unmountTime` | 2 |
34+| **合计** | | **19**(1 + 12 + 4 + 2 = 联合 17 内置 + 2 插件) |
35+ 
36+> **原始采集通道 ≠ 联合成员**:`performance-collector.ts` 的 `enabledMetrics` 实际采集 `fps | memory | cpu` 三路原始信号(`MonitoringOptions.metrics`),其中 `memory`/`cpu` **不是 `PerfMetricType` 成员**——它们是 `MemoryPeakCollector`/`CPUAvgCollector` 等聚合 collector 的输入字段,不能出现在 `collect`/`thresholds` 中。上表计数只统计联合成员,故 `fps` 计 1 而非 3。
37+ 
38+> **历史口径**:`fpsMin` / `fpsMax` 已于 2026-08-25 端到端删除(1s 采样粒度下单秒噪声不可控,替代为 `fpsP5`,见 [SMOOTHNESS-METRICS-SPEC.md](SMOOTHNESS-METRICS-SPEC.md));`exitLatency` 曾为「仅声明未实现」成员,已于 v0.3.1 从类型联合移除。
24 39 
25-> **重要**:`performance-collector.ts:61` `enabledMetrics` 实际仅采集 `fps | memory | cpu`(每轮一个 sample)。其余 11 个聚合指标通过 `BaseMetricCollector` 在 `collect()` 阶段从历史 buffer 计算(注册到 `MetricCollectorFactory`,详见 `capability/measurement/metric-collectors.ts`)。
26> 详见 [STABILITY-SPEC.md §6](STABILITY-SPEC.md) — 性能指标与稳定性 detector 边界。40> 详见 [STABILITY-SPEC.md §6](STABILITY-SPEC.md) — 性能指标与稳定性 detector 边界。
27 41 
28### 1.1 PerfMetricType 指标类型42### 1.1 PerfMetricType 指标类型
@@ -34,19 +48,20 @@
34| `interactionLatency` | 延迟 | ms | 交互响应时延 | Step action执行到UI状态变化可验证 | 交互响应测试 | ✅ 实现(lifecycle) |48| `interactionLatency` | 延迟 | ms | 交互响应时延 | Step action执行到UI状态变化可验证 | 交互响应测试 | ✅ 实现(lifecycle) |
35| `hotStartLatency` | 延迟 | ms | 热启动时延 | 后台切前台到页面可交互 | 热启动测试 | ✅ 实现(lifecycle) |49| `hotStartLatency` | 延迟 | ms | 热启动时延 | 后台切前台到页面可交互 | 热启动测试 | ✅ 实现(lifecycle) |
36| `fps` | FPS | Hz | 平均帧率 | `PerformanceCollector` 直接采集 + 聚合 | 滚动、动画测试 | ✅ 实现(`FPSCollector`,实际采集) |50| `fps` | FPS | Hz | 平均帧率 | `PerformanceCollector` 直接采集 + 聚合 | 滚动、动画测试 | ✅ 实现(`FPSCollector`,实际采集) |
37-| `fpsMin` | FPS | Hz | 最小帧率 | `FPSMinCollector` 从历史 buffer 取最小值 | 峰值性能测试 | ✅ 聚合(`FPSMinCollector`) |
38-| `fpsMax` | FPS | Hz | 最大帧率 | `FPSMaxCollector` 从历史 buffer 取最大值 | 渲染能力测试 | ✅ 聚合(`FPSMaxCollector`) |
39| `fpsJank` | FPS | count | 卡顿次数 | `FPSJankCollector` 累计 hitch16ms + hitch33ms 帧数 | 卡顿检测 | ✅ 聚合(`FPSJankCollector`) |51| `fpsJank` | FPS | count | 卡顿次数 | `FPSJankCollector` 累计 hitch16ms + hitch33ms 帧数 | 卡顿检测 | ✅ 聚合(`FPSJankCollector`) |
40| `fpsBigJank` | FPS | count | 严重卡顿次数 | `BigJankCollector` 累计超过 66ms 的帧数 (hitch66ms) | 严重卡顿检测 | ✅ 聚合(`BigJankCollector`) |52| `fpsBigJank` | FPS | count | 严重卡顿次数 | `BigJankCollector` 累计超过 66ms 的帧数 (hitch66ms) | 严重卡顿检测 | ✅ 聚合(`BigJankCollector`) |
41| `fpsStdDev` | FPS | Hz | 帧率标准差 | `FPSStdDevCollector` 从历史 buffer 计算方差根 | 帧率稳定性测试 | ✅ 聚合(`FPSStdDevCollector`) |53| `fpsStdDev` | FPS | Hz | 帧率标准差 | `FPSStdDevCollector` 从历史 buffer 计算方差根 | 帧率稳定性测试 | ✅ 聚合(`FPSStdDevCollector`) |
54+| `stability` | 流畅度 | % | 卡顿率(越低越好) | `StabilityCollector` jankFrames / totalFrames × 100 | 流畅度测试 | ✅ 聚合(`StabilityCollector`,PerfDog 口径) |
55+| `dropRate` | 流畅度 | % | 掉帧率 | `DropRateCollector` 期望帧数与实际帧数偏差比 | 掉帧检测 | ✅ 聚合(`DropRateCollector`) |
56+| `fpsP5` | 流畅度 | Hz | 5% 分位帧率 | `FpsP5Collector` 逐帧间隔倒数序列 P5 分位 | 低帧率尾部分析(fpsMin 替代) | ✅ 聚合(`FpsP5Collector`) |
42| `blankScreenRatio` | 白屏 | ratio | 白屏率 | `BlankScreenRatioCollector` 视频帧亮度分析(>240 且白像素比>85% 判定白屏) | 启动白屏检测 | ✅ 聚合(`BlankScreenRatioCollector`) |57| `blankScreenRatio` | 白屏 | ratio | 白屏率 | `BlankScreenRatioCollector` 视频帧亮度分析(>240 且白像素比>85% 判定白屏) | 启动白屏检测 | ✅ 聚合(`BlankScreenRatioCollector`) |
43| `memoryPeak` | 内存 | KB | 内存峰值 | `MemoryPeakCollector` 从历史 buffer 取最大值 | 内存压力测试 | ✅ 聚合(`MemoryPeakCollector`) |58| `memoryPeak` | 内存 | KB | 内存峰值 | `MemoryPeakCollector` 从历史 buffer 取最大值 | 内存压力测试 | ✅ 聚合(`MemoryPeakCollector`) |
44| `memoryAvg` | 内存 | KB | 内存平均值 | `MemoryAvgCollector` 从历史 buffer 计算平均值 | 内存趋势分析 | ✅ 聚合(`MemoryAvgCollector`) |59| `memoryAvg` | 内存 | KB | 内存平均值 | `MemoryAvgCollector` 从历史 buffer 计算平均值 | 内存趋势分析 | ✅ 聚合(`MemoryAvgCollector`) |
45| `memoryGrowthRate` | 内存 | KB/分钟 | 内存增长率 | `MemoryGrowthRateCollector` 内存采样线性回归斜率 | 内存泄漏检测 | ✅ 聚合(`MemoryGrowthRateCollector`) |60| `memoryGrowthRate` | 内存 | KB/分钟 | 内存增长率 | `MemoryGrowthRateCollector` 内存采样线性回归斜率 | 内存泄漏检测 | ✅ 聚合(`MemoryGrowthRateCollector`) |
46| `cpuPeak` | CPU | % | CPU峰值 | `CPUPeakCollector` 从历史 buffer 取最大值 | CPU压力测试 | ✅ 聚合(`CPUPeakCollector`) |61| `cpuPeak` | CPU | % | CPU峰值 | `CPUPeakCollector` 从历史 buffer 取最大值 | CPU压力测试 | ✅ 聚合(`CPUPeakCollector`) |
47| `cpuAvg` | CPU | % | CPU平均值 | `CPUAvgCollector` 从历史 buffer 计算平均值 | CPU趋势分析 | ✅ 聚合(`CPUAvgCollector`) |62| `cpuAvg` | CPU | % | CPU平均值 | `CPUAvgCollector` 从历史 buffer 计算平均值 | CPU趋势分析 | ✅ 聚合(`CPUAvgCollector`) |
48- 63+| `renderTime` | 渲染 | ms | 组件渲染时间 | `@cptf/benchmark` 插件(`RenderTimeCollector`) | RN 组件渲染性能 | ✅ 实现(插件) |
49-> **对齐实现**:PerfMetricType = 16 个枚举成员已全部有对应 collector 注册。实际底层只采 `fps | memory | cpu` 3 个原始指标,其余 13 个通过 `BaseMetricCollector.computeAllFromHistory` 在 `collect()` 阶段从历史 buffer 计算。64+| `unmountTime` | 渲染 | ms | 组件卸载时间 | `@cptf/benchmark` 插件(`UnmountTimeCollector`) | RN 组件卸载性能 | ✅ 实现(插件) |
50 65 
51### 1.2 MetricType 扩展指标66### 1.2 MetricType 扩展指标
52 67 
Mpackages/framework/docs/architecture/PERFORMANCE-METRICS-SPEC.md+225-225File content is under review, please try again later
Mpackages/framework/docs/architecture/STABILITY-SPEC.md+58-56File content is under review, please try again later
Mpackages/framework/docs/development/METRIC-COLLECTOR-GUIDE.md+45-43File content is under review, please try again later
@@ -2,8 +2,8 @@
2 2 
3## 版本3## 版本
4 4 
5-- 版本号: 0.2.05+- 版本号: 0.3.1
6-- 更新日期: 2026-06-086+- 更新日期: 2026-10-13
7- 适用对象: 测试工程师、自动化测试开发者7- 适用对象: 测试工程师、自动化测试开发者
8 8 
9---9---
@@ -34,32 +34,35 @@ pnpm install
34 34 
35#### 配置文件创建35#### 配置文件创建
36 36 
37-创建 `cptf.config.json`:37+创建 `cptf.config.json`(对齐 `resolveConfigAsync` 当前校验规则,`deepLinkScheme` 必填,缺失时直接报错):
38 38 
39```json39```json
40{40{
41+ "deepLinkScheme": "rnoh-test",
42+ "reportDir": "test-reports",
43+ "reportFormat": "all",
44+ "casesDir": "tests/cases",
41 "platforms": {45 "platforms": {
42 "harmony": {46 "harmony": {
43 "toolPath": "hdc",47 "toolPath": "hdc",
44- "deviceId": "",
45 "bundleId": "com.example.myapp",48 "bundleId": "com.example.myapp",
46- "screenWidth": 1080,49+ "appPath": "harmony/entry/build/default/outputs/default/entry-default-signed.hap",
47- "screenHeight": 234050+ "devices": "auto"
48 },51 },
49 "android": {52 "android": {
50 "toolPath": "adb",53 "toolPath": "adb",
51- "deviceId": "",
52 "bundleId": "com.example.myapp",54 "bundleId": "com.example.myapp",
53- "screenWidth": 1080,55+ "appPath": "android/app/build/outputs/apk/release/app-release.apk",
54- "screenHeight": 234056+ "devices": "auto"
55 }57 }
56- },58+ }
57- "outputDir": "./test-output",
58- "baselineDir": "./baseline",
59- "logLevel": "info"
60}59}
61```60```
62 61 
62+> **`deepLinkScheme` 必填**:v0.3.0 起框架不再提供默认值(见 loader `resolveConfigAsync`),按被测应用的实际 Deep Link scheme 填写(如 `"rnoh-test"`、`"flutter"`、`"myapp"`)。
63+> **`devices: "auto"`**:自动发现已连接设备并探测屏幕尺寸;也可改为数组显式指定(见 2.1)。
64+> **报告目录**:CLI `-o/--output` 优先,其次配置 `reportDir`,缺省 `test-reports`。
65+ 
63#### CLI命令使用66#### CLI命令使用
64 67 
65```bash68```bash
@@ -130,24 +133,53 @@ cptf template stability -n monkey-test -i monkey-stab -c Monkey -s stability-sce
130 133 
131## 二、配置详解134## 二、配置详解
132 135 
133-### 2.1 平台配置136+配置结构以 `CPTFConfigFile`(`src/infrastructure/config/loader.ts`)为单一真相。全局键与 `platforms` 键并列;平台级键只覆盖该平台的差异项。
134 137 
135-| 参数 | 类型 | 必填 | 说明 |138+### 2.1 平台配置(platforms.harmony / android / ios)
136-|------|------|------|------|139+ 
137-| `toolPath` | string | 是 | 平台工具路径(hdc/adb) |140+| 参数 | 类型 | 必填 | 默认值 | 说明 |
138-| `deviceId` | string | 是 | 设备ID(空字符串时自动选择) |141+|------|------|------|--------|------|
139-| `bundleId` | string | 是 | 应用包名/Bundle ID |142+| `toolPath` | string | 否 | 平台默认(harmony=`hdc`,android=`adb`,ios=`xcrun`) | 平台工具路径 |
140-| `screenWidth` | number | 是 | 屏幕宽度 |143+| `bundleId` | string | 否 | `''` | 应用包名/Bundle ID(空则回退全局 `bundleId`) |
141-| `screenHeight` | number | 是 | 屏幕高度 |144+| `appPath` | string | 否 | `''` | 应用包路径(harmony=`.hap`,android=`.apk`,ios=`.ipa`),`--install` / `cptf install` 时使用 |
145+| `statusBarHeight` | number | 否 | 自动探测(探测失败回退密度估算) | 状态栏高度(px),显式配置可跳过探测 |
146+| `devices` | `'auto'` \| `Array<{id?, screenWidth?, screenHeight?}>` | 否 | 自动发现 | 设备选择(见下) |
147+ 
148+**`devices` 三种写法**:
149+ 
150+```jsonc
151+// 1. 自动发现第一台已连接设备 + 探测屏幕尺寸(推荐)
152+"devices": "auto"
153+ 
154+// 2. 显式指定设备与尺寸(尺寸写死后跳过探测)
155+"devices": [{ "id": "7000001234", "screenWidth": 1280, "screenHeight": 2832 }]
156+ 
157+// 3. 数组首项 id 为 "auto" 或缺省 —— 自动发现,但保留后续扩展位
158+"devices": [{ "id": "auto" }]
159+```
160+ 
161+多设备并行与分配策略见 `--device` / `--strategy` CLI 参数(第一章命令速查)。
162+ 
163+iOS 平台额外支持 `wdaProjectPath` / `wdaScheme` / `devTeam` / `wdaPort`(默认 8100)/ `wdaBundleId` / `wdaGitUrl` / `wdaGitCommit` / `wdaAutoDownload`,详见 [iOS 平台说明](../architecture/UITEST-STABILITY-DESIGN.md)。
142 164 
143### 2.2 全局配置165### 2.2 全局配置
144 166 
145| 参数 | 类型 | 必填 | 默认值 | 说明 |167| 参数 | 类型 | 必填 | 默认值 | 说明 |
146|------|------|------|--------|------|168|------|------|------|--------|------|
147-| `outputDir` | string | 否 | `./test-output` | 测试输出目录 |169+| `deepLinkScheme` | string | **是** | 无(缺失直接报错) | Deep Link scheme(如 `"rnoh-test"`),v0.3.0 起必填 |
148-| `baselineDir` | string | 否 | `./baseline` | 基线数据目录 |170+| `bundleId` | string | 否 | — | 全局包名(平台级未配置时的回退) |
149-| `logLevel` | string | 否 | `info` | 日志级别(debug/info/warn/error) |171+| `reportDir` | string | 否 | `test-reports` | 报告根目录(CLI `-o/--output` 优先) |
150-| `reportFormat` | `'html'\|'markdown'\|'json'\|'all'` | 否 | `'all'` | 报告格式 |172+| `reportFormat` | `'html'` \| `'markdown'` \| `'json'` \| `'all'` | 否 | `'all'` | 报告格式(`'all'` = HTML + Markdown + JSON 全部生成) |
173+| `casesDir` | string | 否 | `tests/cases` | 测试用例目录 |
174+| `deviceAllocation` | `'auto'` \| `'round-robin'` \| `'by-module'` \| `'by-category'` \| `'by-case'` \| `'single'` | 否 | — | 多设备用例分配策略(CLI `--strategy` 优先;`'auto'` = 用例带 category 时按 Category 亲和分组,否则 round-robin) |
175+| `rnVersion` | string | 否 | — | 视觉基线按 RN 版本分段存放(`test-baseline/<rnVersion>/...`),缺省不分段 |
176+| `navigationStrategy` | string | 否 | `'default'` | 导航策略 ID(内置 `'default'` / `'native'`,native 驱动模式下自动切 `'native'`) |
177+| `qualityHook` | string | 否 | — | 测试结束后执行的钩子脚本路径(接收 JSON 格式的结果摘要) |
178+| `timezone` | string | 否 | — | 报告时间戳时区(如 `"BeiJing"`) |
179+| `scoring.excludeUnsupported` | boolean | 否 | — | 通过率统计是否排除无法自动化的用例 |
180+| `hostVersions` | `Record<string, string>` | 否 | — | 宿主版本信息(写入报告;也可经环境变量 `CPTF_HOST_VERSIONS` 注入) |
181+| `email` | object | 否 | — | 邮件通知(见 2.3) |
182+| `aiAnalysis` | object | 否 | — | AI 崩溃分析(`enabled`/`apiKey`/`endpoint`/`model`) |
151 183 
152### 2.3 邮件通知配置184### 2.3 邮件通知配置
153 185 
@@ -262,46 +294,36 @@ cptf template stability -n monkey-test -i monkey-stab -c Monkey -s stability-sce
2621. 开启两步验证 → 生成应用专用密码2941. 开启两步验证 → 生成应用专用密码
2632. `host`: `smtp.gmail.com`,`port`: `465`,`secure`: `true`2952. `host`: `smtp.gmail.com`,`port`: `465`,`secure`: `true`
264 296 
265-### 2.4 指标采集配置(monitoring)297+### 2.4 指标采集配置(collect)
266 298 
267-每个测试用例可通过 `monitoring` 配置控制采集哪些性能指标和采集频率。未配置时使用基类默认值。299+每个测试用例通过 `@Test` 装饰器的 `collect` 字段声明采集哪些性能指标。未配置时使用基类默认值。
268 300 
269#### 配置结构301#### 配置结构
270 302 
271```typescript303```typescript
272-interface MonitoringConfig {304+collect?: PerfMetricType[];
273- metrics?: ('fps' | 'memory' | 'cpu' | 'whiteScreen')[];305+// 如:collect: ['fps', 'memoryPeak', 'cpuPeak']
274- interval?: number;
275-}
276```306```
277 307 
278-| 参数 | 类型 | 必填 | 说明 |
279-|------|------|------|------|
280-| `metrics` | string[] | 否 | 采集指标列表,可选值:`fps`、`memory`、`cpu`、`whiteScreen` |
281-| `interval` | number | 否 | 采集间隔(毫秒) |
282- 
283#### 三类测试默认配置308#### 三类测试默认配置
284 309 
285-| 测试类型 | 默认 metrics | 默认 interval | 说明 |310+| 测试类型 | 默认 collect | 其他默认 | 说明 |
286-|---------|-------------|---------------|------|311+|---------|-------------|---------|------|
287-| StabilityTest | `['fps', 'memory', 'cpu', 'whiteScreen']` | 1000ms | 稳定性测试全量采集,含白屏检测 |312+| PerformanceTest | `['startLatency', 'fps', 'blankScreenRatio']` | executeTimes=5, cooldownMs=500, aggregate='average' | 性能测试默认含启动时延/帧率/白屏率 |
288-| PerformanceTest | `['fps', 'memory', 'cpu']` | 500ms | 性能测试高频采集,精确趋势 |313+| StabilityTest | 不采集性能指标 | executeTimes=100, cooldownMs=100,默认启用 appAlive/crashLogs/whiteScreen/memoryLeak 四检测器 | 指标采集与稳定性检测是两条独立链路(见 [STABILITY-SPEC §6](../architecture/STABILITY-SPEC.md)) |
289 314 
290#### 用例级自定义315#### 用例级自定义
291 316 
292-在 `@Test` 装饰器中配置 `monitoring`:317+在 `@Test` 装饰器中配置 `collect`:
293 318 
294```typescript319```typescript
295@Test({320@Test({
296 id: 'my-memory-test',321 id: 'my-memory-test',
297 name: '内存专项测试',322 name: '内存专项测试',
298- module: 'functional',323+ module: 'performance',
299- monitoring: {324+ collect: ['memoryPeak', 'memoryAvg'],
300- metrics: ['memory'],
301- interval: 500,
302- },
303})325})
304-class MyMemoryTest extends FunctionalTest { ... }326+class MyMemoryTest extends PerformanceTest { ... }
305```327```
306 328 
307```typescript329```typescript
@@ -309,26 +331,20 @@ class MyMemoryTest extends FunctionalTest { ... }
309 id: 'high-fps-test',331 id: 'high-fps-test',
310 name: 'FPS高频采集测试',332 name: 'FPS高频采集测试',
311 module: 'performance',333 module: 'performance',
312- monitoring: {334+ collect: ['fps', 'fpsP5', 'stability', 'dropRate'],
313- metrics: ['fps', 'cpu'],
314- interval: 300,
315- },
316})335})
317class HighFpsTest extends PerformanceTest { ... }336class HighFpsTest extends PerformanceTest { ... }
318```337```
319 338 
320-#### 报告展示339+#### 底层采集通道与报告展示
321 340 
322-用例开启了哪些指标,报告detail卡片中就会展示对应Tab:341+- 底层真实采集只有 `fps` / `memory` / `cpu` 三路原始信号,其余指标(`fpsP5`/`stability`/`dropRate`/`memoryPeak`/`cpuAvg` 等)由 `MetricCollectorFactory` 注册的聚合器从历史 buffer 计算;`renderTime`/`unmountTime` 由 `@cptf/benchmark` 插件提供(见 [PERFORMANCE-METRICS-SPEC](../architecture/PERFORMANCE-METRICS-SPEC.md))
323- 342+- 报告 detail 卡片按用例实际 collect 的指标展示 Tab(FPS/内存/CPU/白屏等彩色徽章 + sparkline 趋势)
324-- 顶部彩色徽章标识采集的指标(FPS=蓝、内存=橙、CPU=绿、白屏=紫)
325-- Tab式面板:每个指标独立Tab,含sparkline折线图 + 可滚动数据表格 + 统计摘要
326-- 未采集的指标不显示Tab和metric-box
327 343 
328#### 配置优先级344#### 配置优先级
329 345 
330```346```
331-用例 @Test monitoring 配置 → 基类 getDefaultConfig() 默认值 → 全量采集347+用例 @Test collect 配置 → 基类 getDefaultConfig() 默认值
332```348```
333 349 
334### 2.5 测试用例配置350### 2.5 测试用例配置
@@ -364,10 +380,12 @@ class HighFpsTest extends PerformanceTest { ... }
364 "module": "stability",380 "module": "stability",
365 "platform": "harmony",381 "platform": "harmony",
366 "config": {382 "config": {
367- "iterations": 1000,383+ "executeTimes": 1000,
368- "throttleMs": 500,384+ "cooldownMs": 500,
369- "seed": 12345,385+ "stability": {
370- "actionTypes": ["click", "swipe", "keyEvent"]386+ "enabled": ["appAlive", "crashLogs", "whiteScreen", "memoryLeak"],
387+ "successRateThreshold": 0.95
388+ }
371 }389 }
372}390}
373```391```
@@ -486,27 +504,40 @@ console.log('CPU Peak:', metrics.cpuPeak);
486 504 
487#### 指标类型505#### 指标类型
488 506 
507+`PerfMetricType` 联合共 19 个成员(v0.3.1 口径,以 `src/base/types/index.ts` 为单一真相):
508+ 
489```typescript509```typescript
490// 支持的指标类型510// 支持的指标类型
491-type PerfMetricType = 511+type PerfMetricType =
492- | 'startLatency'512+ // 时延类(lifecycle 信号)
493- | 'finishLatency'513+ | 'startLatency' // 启动时延
494- | 'interactionLatency'514+ | 'finishLatency' // 完成时延
495- | 'hotStartLatency'515+ | 'interactionLatency' // 交互响应时延
496- | 'fps'516+ | 'hotStartLatency' // 热启动时延
497- | 'fpsMin'517+ // 流畅度类
498- | 'fpsMax'518+ | 'fps' // 平均帧率(真实采集)
499- | 'fpsJank'519+ | 'fpsJank' // 卡顿次数
500- | 'fpsBigJank'520+ | 'fpsBigJank' // 严重卡顿次数
501- | 'fpsStdDev'521+ | 'fpsStdDev' // 帧率标准差
502- | 'blankScreenRatio'522+ | 'stability' // 卡顿率(越低越好,PerfDog 口径)
503- | 'memoryPeak'523+ | 'dropRate' // 掉帧率
504- | 'memoryAvg'524+ | 'fpsP5' // 5% 分位帧率(fpsMin 的替代)
505- | 'memoryGrowthRate'525+ // 质量类
506- | 'cpuPeak'526+ | 'blankScreenRatio' // 白屏率
507- | 'cpuAvg';527+ // 内存类
528+ | 'memoryPeak' // 内存峰值
529+ | 'memoryAvg' // 平均内存
530+ | 'memoryGrowthRate' // 内存增长率(泄漏检测)
531+ // CPU 类
532+ | 'cpuPeak' // CPU 峰值
533+ | 'cpuAvg' // 平均 CPU
534+ // 插件扩展(@cptf/benchmark 提供)
535+ | 'renderTime' // 渲染时间
536+ | 'unmountTime'; // 卸载时间
508```537```
509 538 
539+> 框架内置 17 个 collector(上表除 `renderTime`/`unmountTime`),`renderTime`/`unmountTime` 由 `@cptf/benchmark` 插件注册。`fpsMin`/`fpsMax` 已于 v0.2.1 删除(1s 采样粒度下噪声不可控,替代为 `fpsP5`,见 [SMOOTHNESS-METRICS-SPEC](../architecture/SMOOTHNESS-METRICS-SPEC.md));`exitLatency` 是「仅声明未实现」的幽灵成员(`MetricCollectorFactory.create` 会抛 `Unknown metric type`),配套 PR !48 已将其从类型联合移除——若当前代码仍含该成员,以 !48 合入后的 19 成员联合为准。各指标定义详见 [PERFORMANCE-METRICS-SPEC](../architecture/PERFORMANCE-METRICS-SPEC.md)。
540+ 
510### 3.3 稳定性测试541### 3.3 稳定性测试
511 542 
512#### Monkey测试543#### Monkey测试
Mpackages/framework/src/__tests__/capability/metric-collectors.test.ts+649-104File content is under review, please try again later
Mpackages/framework/src/__tests__/system/app-installer.test.ts+26-25File content is under review, please try again later
Mpackages/framework/src/orchestration/parallel-test-runner.ts+1-1File content is under review, please try again later
Mpackages/framework/src/system/app-installer.ts+42-37File content is under review, please try again later