Skip to content

Commit c5b8453

Browse files
committed
claude: added JS unit test skills
Signed-off-by: IonutMuthi <Ionut.Muthi@analog.com>
1 parent 19996ef commit c5b8453

26 files changed

+2038
-1
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "scopy_dev_plugin",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Scopy development tools: code generation, documentation, testing, quality checks, and styling for IIO plugin development.",
55
"author": { "name": "Muthi Ionut Adrian" }
66
}

tools/scopy_dev_plugin/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Both must be on your `PATH`. On Ubuntu: `sudo apt install clang-format` and `pip
4040
| `/scopy_dev_plugin:verify-package <package>` | CI pre-flight validation (format + license) |
4141
| `/scopy_dev_plugin:validate-api <plugin>` | Validate API class implementation (checks A1–A7) |
4242
| `/scopy_dev_plugin:validate-automated-tests <plugin>` | Validate JS automated test scripts (checks T1–T7) |
43+
| `/scopy_dev_plugin:create-unit-tests <plugin>` | Generate JS unit test scripts for IIOWidget coverage |
44+
| `/scopy_dev_plugin:validate-unit-tests <plugin>` | Validate JS unit test scripts (checks U1–U7) |
4345

4446
## Knowledge Skills (auto-load)
4547

@@ -51,6 +53,8 @@ These skills are loaded automatically when relevant context is detected:
5153
- **scopy-doc-format** — RST documentation conventions
5254
- **scopy-test-format** — Test case UID and RBP conventions
5355
- **scopy-api-patterns** — API class structure and Q_INVOKABLE patterns
56+
- **unit-test-quality-checks** — Unit test validation rules (U1–U7) for IIOWidget coverage tests
57+
- **unit-test-patterns** — Code patterns for unit test helpers and complex test scenarios
5458

5559
## Hooks
5660

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# /create-unit-tests — Create JS unit test scripts for IIOWidget coverage
2+
3+
You are creating JavaScript unit test scripts for a Scopy plugin that test every IIOWidget attribute via `readWidget`/`writeWidget` and API getter/setter methods.
4+
5+
**Plugin:** `$ARGUMENTS`
6+
7+
## Step 0: Load context
8+
9+
Use the Read tool to check if a port state file exists:
10+
- Path: `tasks/$ARGUMENTS-port-state.md`
11+
- If the file does not exist, note "No state file — will discover from source files directly." and continue.
12+
13+
## Prerequisites check
14+
15+
Before writing tests, verify this input exists:
16+
1. **Plugin API class** — use the Glob tool to search for `*_api.h` in `scopy/packages/$ARGUMENTS/`
17+
18+
If the API class does not exist, stop and tell the user to run `/create-api $ARGUMENTS` first.
19+
20+
## Step 1: Discovery
21+
22+
Read these specific files to discover all testable widgets and API methods:
23+
24+
1. **Plugin API header** (all `Q_INVOKABLE` methods):
25+
- Use Glob: `scopy/packages/$ARGUMENTS/plugins/*/include/**/*_api.h`
26+
- Extract every getter/setter pair, standalone getters, and utility methods (`calibrate()`, `refresh()`, `loadProfile()`, etc.)
27+
28+
2. **Widget factory source** (all IIOWidgetBuilder calls):
29+
- Use Glob: `scopy/packages/$ARGUMENTS/plugins/*/src/**/*.cpp`
30+
- Identify files that use `IIOWidgetBuilder`
31+
- Extract: widget keys, UI strategy type (EditableUi, ComboUi, CheckBoxUi, RangeUi), range bounds, combo options, conversion functions
32+
33+
3. **EMU XML** (device structure and attribute defaults):
34+
- Use Glob: `scopy/packages/$ARGUMENTS/emu-xml/*.xml`
35+
- Extract: device name (for PHY prefix), channel structure, attribute names, default values, `_available` options
36+
37+
4. **Tool source files** (for tool names and advanced tool detection):
38+
- Search for `switchToTool()`, `getAdvancedTabs()`, `switchAdvancedTab()` in the API header
39+
- Check for `advanced/` subdirectory in the plugin source
40+
41+
5. **Existing test files** (avoid duplication):
42+
- Use Glob: `scopy/js/testAutomations/$ARGUMENTS/`
43+
44+
6. **Test framework API**:
45+
- `scopy/js/testAutomations/common/testFramework.js`
46+
47+
## Step 2: Classification — WAIT FOR APPROVAL
48+
49+
After reading all source material, present a structured classification report:
50+
51+
### Widget Key Prefix
52+
```
53+
var PHY = "<device-name>/"; // from EMU XML
54+
```
55+
56+
### Basic Tool Widgets
57+
58+
| Section | Widget Key | Type | Min | Max | Mid | Options | Test Helper | UID |
59+
|---------|-----------|------|-----|-----|-----|---------|-------------|-----|
60+
| Global | ensm_mode | combo | - | - | - | ["radio_on", "radio_off"] | testCombo + API | UNIT.GLOBAL.ENSM_MODE |
61+
| RX | voltage0_in/hardwaregain | range | 0 | 30 | 15 | - | testRange + testConversion | UNIT.RX.CH0_HARDWARE_GAIN |
62+
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
63+
64+
### Advanced Tool Widgets (if applicable)
65+
66+
Group by tab name:
67+
68+
| Tab | Widget Key | Type | Min | Max | Mid | Options | Test Helper | UID |
69+
|-----|-----------|------|-----|-----|-----|---------|-------------|-----|
70+
| CLK Settings | adi,clocks-device-clock_khz | range | 30720 | 320000 | 122880 | - | testRange | UNIT.CLK.DEVICE_CLOCK_KHZ |
71+
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
72+
73+
### API-Only Methods (no widget, tested via getter/setter)
74+
75+
| Method | Test Type | UID |
76+
|--------|-----------|-----|
77+
| getRxRssi(channel) (readonly) | testReadOnly via API | UNIT.RX.CH0_RSSI |
78+
| calibrate() | complex test | UNIT.CAL.CALIBRATE_TRIGGER |
79+
| ... | ... | ... |
80+
81+
### Widget Counts
82+
- Basic tool: X widgets
83+
- Advanced tool: Y widgets across Z tabs
84+
- API-only: W methods
85+
- Total attribute test cases: N
86+
87+
### Proposed Complex Tests
88+
89+
Scan the API header for these method signature triggers and list matching complex tests:
90+
91+
| # | Pattern | API Trigger | UID |
92+
|---|---------|-------------|-----|
93+
| C1 | Calibration Flow | `calibrate()` found | UNIT.CAL.FULL_CALIBRATION_FLOW |
94+
| C2 | Profile Loading | `loadProfile()` found | UNIT.PROFILE.LOAD_AND_VERIFY |
95+
| C3 | Gain Mode Interaction | `getXxxGainControlMode()` + `setXxxHardwareGain()` found | UNIT.RX.GAIN_MODE_INTERACTION |
96+
| C4 | State Transitions | `getEnsmMode()`/`setEnsmMode()` found | UNIT.GLOBAL.ENSM_STATE_TRANSITIONS |
97+
| C5 | DPD Operations | `dpdReset()` + `getDpdStatus()` found | UNIT.DPD.RESET_AND_STATUS_CH0 |
98+
| C6 | Channel Independence | Setter with `int channel`, 2+ channels | UNIT.TX.CHANNEL_INDEPENDENCE |
99+
| C7 | Phase Rotation | `getPhaseRotation()`/`setPhaseRotation()` found | UNIT.FPGA.PHASE_ROTATION_CH0 |
100+
| C8 | Frequency Tuning | Hz-to-MHz conversion in getter/setter | UNIT.RX.LO_FREQUENCY |
101+
| C9 | UDC LO Splitting | `hasUdc()`/`getUdcEnabled()` found | UNIT.UDC.LO_SPLITTING |
102+
| C10 | Refresh Cycle | `refresh()` found | UNIT.UTIL.REFRESH_ALL |
103+
104+
Only list patterns where the API trigger was actually found.
105+
106+
### Proposed File Structure
107+
108+
Apply adaptive splitting:
109+
- Always: `<plugin>_Basic_Unit_test.js` (or `<plugin>_Unit_test.js` if single file)
110+
- If advanced tool detected: `+ <plugin>_Advanced_Unit_test.js`
111+
- If complex tests approved: `+ <plugin>_Complex_Unit_test.js`
112+
- If multiple files: `+ <plugin>_Unit_test.js` (combined runner)
113+
114+
### Variant Detection
115+
116+
If the plugin supports device variants (e.g., AD9371 vs AD9375), describe:
117+
- How to detect the variant at runtime
118+
- Which widgets/tests need skip guards
119+
120+
**Wait for user approval before writing any JavaScript.**
121+
122+
## Step 3: Interactive Complex Test Discovery
123+
124+
After presenting the plan, ask the user:
125+
1. "Which complex tests should I include?" (present the matched list)
126+
2. "Are there any plugin-specific complex scenarios not in the standard patterns?"
127+
3. "Are there variant-specific features that need skip guards?"
128+
129+
## Step 4: Generate Files
130+
131+
Generate each file following the `unit-test-patterns` skill. Use the `file-structure.md` pattern for boilerplate.
132+
133+
**File locations:**
134+
- `scopy/js/testAutomations/$ARGUMENTS/<plugin>_Basic_Unit_test.js`
135+
- `scopy/js/testAutomations/$ARGUMENTS/<plugin>_Advanced_Unit_test.js` (if applicable)
136+
- `scopy/js/testAutomations/$ARGUMENTS/<plugin>_Complex_Unit_test.js` (if applicable)
137+
- `scopy/js/testAutomations/$ARGUMENTS/<plugin>_Unit_test.js` (combined runner or single file)
138+
139+
**Critical generation rules (non-negotiable):**
140+
141+
1. Every `writeWidget()` and setter call is followed by `msleep(500)`
142+
2. Every test saves original value before modifying, and restores it in ALL code paths (normal, early return, catch)
143+
3. Standard widget types use canonical helper functions (`testRange`, `testCombo`, `testCheckbox`, `testReadOnly`, `testConversion`)
144+
4. Sections with 3+ widgets of the same type use `runDataDrivenTests()` with test descriptor arrays
145+
5. Every range widget gets both `testRange()` and `testBadValueRange()` tests
146+
6. Every combo widget gets both `testCombo()` and `testBadValueCombo()` tests
147+
7. API getter/setter pairs with unit conversion get `testConversion()` tests
148+
8. Files end with `TestFramework.disconnectFromDevice()`, `TestFramework.printSummary()`, `scopy.exit()`
149+
9. UID format: `UNIT.<SECTION>.<ATTRIBUTE_NAME>` (uppercase, dots as separators)
150+
10. Never invent API methods — only use what's in the `*_api.h` header
151+
11. Never invent widget keys — only use keys discoverable from source code or EMU XML
152+
12. Variant-specific tests wrapped in skip guards (e.g., `if (!isAd9375) return "SKIP"`)
153+
154+
## Step 5: Validate
155+
156+
Run the `unit-test-quality-checks` skill rules (U1-U7) against the generated files:
157+
- [ ] [U1] Every discoverable widget has a test
158+
- [ ] [U2] Standard helpers used (no ad-hoc logic for standard types)
159+
- [ ] [U3] Every setter has `msleep(500)` after it
160+
- [ ] [U4] Original values saved and restored in all code paths
161+
- [ ] [U5] Bad value tests present for range and combo widgets
162+
- [ ] [U6] Complex tests isolated in marked section
163+
- [ ] [U7] File naming, license header, termination sequence correct
164+
165+
## Step 6: Update state file (if it exists)
166+
167+
```markdown
168+
## Status
169+
- Phase: UNIT_TESTS_COMPLETE
170+
```
171+
172+
## Rules
173+
174+
- Do NOT modify any C++ source code
175+
- Do NOT invent API methods that don't exist in the `*_api.h` header
176+
- Do NOT invent widget keys — discover them from source code and EMU XML
177+
- Getter return values are always strings — compare with `===` against string values
178+
- Every test must restore original hardware state
179+
- Use `"SKIP"` return for features not available on current hardware variant
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# /validate-unit-tests — Validate JS unit test scripts for a Scopy plugin
2+
3+
You are validating the JavaScript unit test scripts for the Scopy plugin: `$ARGUMENTS`
4+
5+
The `unit-test-quality-checks` skill rules (checks U1–U7) govern this analysis.
6+
7+
## Step 1: Discover files
8+
9+
Use the Glob tool to locate:
10+
- Unit test files: `js/testAutomations/$ARGUMENTS/*_Unit_test.js`, `js/testAutomations/$ARGUMENTS/*_Basic_Unit_test.js`, `js/testAutomations/$ARGUMENTS/*_Advanced_Unit_test.js`, `js/testAutomations/$ARGUMENTS/*_Complex_Unit_test.js`
11+
- Plugin API header: `scopy/packages/$ARGUMENTS/plugins/*/include/**/*_api.h`
12+
- Widget factory sources: `scopy/packages/$ARGUMENTS/plugins/*/src/**/*.cpp` (files using `IIOWidgetBuilder`)
13+
- EMU XML: `scopy/packages/$ARGUMENTS/emu-xml/*.xml`
14+
- Test framework: `js/testAutomations/common/testFramework.js`
15+
16+
If no JS unit test files are found, report "No unit test files found for `$ARGUMENTS`" and stop.
17+
18+
Read **all** discovered files before starting analysis.
19+
20+
## Step 2: Build expected widget set
21+
22+
From the source files, build the complete set of testable widgets and API methods:
23+
24+
1. **From widget factory sources**: Extract all IIOWidgetBuilder calls to get widget keys and their types (EditableUi → range, ComboUi → combo, CheckBoxUi → checkbox, read-only patterns)
25+
2. **From API header**: Extract all `Q_INVOKABLE` getter/setter pairs and standalone getters
26+
3. **From EMU XML**: Extract device name prefix, channel structure, attribute names
27+
28+
This is the "expected" set — every item should have at least one test.
29+
30+
## Step 3: Build actual test set
31+
32+
From the JS unit test files, extract:
33+
34+
1. **Widget keys tested**: All keys passed to `readWidget()`, `writeWidget()`, `testRange()`, `testCombo()`, `testCheckbox()`, `testReadOnly()`, `testConversion()`, `testBadValueRange()`, `testBadValueCombo()`, and `runDataDrivenTests()` calls
35+
2. **API methods exercised**: All `<apiObject>.<method>()` calls
36+
3. **Test UIDs**: All `TestFramework.runTest("<UID>", ...)` UIDs
37+
38+
## Step 4: Run checks U1–U7
39+
40+
### CRITICAL
41+
42+
**[U1] Widget Coverage**
43+
- Compare expected widget set against actual test set
44+
- Flag any widget key with no corresponding test
45+
- Flag any `Q_INVOKABLE` getter/setter pair with no test exercising it
46+
- Flag any test referencing a widget key not in the expected set
47+
- Report coverage: `X/Y widgets tested (Z%)`
48+
49+
**[U2] Helper Function Usage**
50+
- Scan each `TestFramework.runTest()` body
51+
- For non-complex tests: if the body manually writes/reads/compares widget values when a standard helper exists for that widget type, flag it
52+
- Verify `runDataDrivenTests()` is used for sections with 3+ widgets of the same type
53+
- Complex tests (in sections marked `// SECTION: Complex`) are exempt
54+
55+
**[U3] Sleep After Setters**
56+
- Identify every `writeWidget()`, `set*()`, or state-mutating call (`calibrate()`, `dpdReset()`, `refresh()`, `loadProfile()`)
57+
- Check that the very next non-empty line is `msleep(500)` or longer
58+
- Flag any setter not followed immediately by msleep
59+
- Check both in helper definitions and in individual test bodies
60+
61+
**[U4] State Restoration**
62+
- Identify every test that modifies state (calls a setter or writeWidget)
63+
- Check that it saves the original value before modification
64+
- Check that it restores the original value in: normal completion, early return, and catch block
65+
- Flag any test that modifies state without full restoration
66+
67+
### WARNING
68+
69+
**[U5] Bad Value Tests**
70+
- Count range widgets with `testRange()` calls vs those with `testBadValueRange()` calls
71+
- Count combo widgets with `testCombo()` calls vs those with `testBadValueCombo()` calls
72+
- Report coverage ratio for each
73+
- Warn if bad value test coverage drops below 80%
74+
75+
**[U6] Complex Test Isolation**
76+
- Check that complex multi-step tests are in a clearly marked section
77+
- Check each complex test has a descriptive comment (e.g., `// C1: Full Calibration Flow`)
78+
- Check complex test UIDs follow `UNIT.<SECTION>.<DESCRIPTIVE_NAME>` format
79+
- Flag any undocumented complex logic mixed into attribute test sections
80+
81+
### INFO
82+
83+
**[U7] File Structure**
84+
- Verify file naming convention (`*_Basic_Unit_test.js`, `*_Advanced_Unit_test.js`, etc.)
85+
- Verify GPL license header present in every file
86+
- Verify termination sequence: `disconnectFromDevice()``printSummary()``scopy.exit()`
87+
- Verify combined runner uses `evaluateFile()` and does not duplicate helpers or tests
88+
- Report file count and structure
89+
90+
## Step 5: Generate report
91+
92+
```
93+
## Unit Test Validation Report: $ARGUMENTS
94+
95+
### Summary
96+
| Check | Severity | Result |
97+
|-------|----------|--------|
98+
| [U1] Widget Coverage | CRITICAL | PASS/FAIL |
99+
| [U2] Helper Function Usage | CRITICAL | PASS/FAIL |
100+
| [U3] Sleep After Setters | CRITICAL | PASS/FAIL |
101+
| [U4] State Restoration | CRITICAL | PASS/FAIL |
102+
| [U5] Bad Value Tests | WARNING | PASS/WARN |
103+
| [U6] Complex Test Isolation | WARNING | PASS/WARN |
104+
| [U7] File Structure | INFO | PASS/INFO |
105+
106+
### Critical Issues
107+
**[U1] Missing widget coverage**
108+
`voltage0_in/rf_bandwidth` — no test found in any unit test file.
109+
> **Fix:** Add a testReadOnly() call for this widget.
110+
111+
**[U3] Missing sleep after setter**
112+
`ad9371_Basic_Unit_test.js:142` — `writeWidget()` not followed by `msleep(500)`.
113+
> **Fix:** Add `msleep(500);` on the next line.
114+
115+
### Warnings
116+
**[U5] Bad value test coverage**
117+
- Range widgets: 12/15 have testBadValueRange() (80%)
118+
- Combo widgets: 3/5 have testBadValueCombo() (60%) — below 80% threshold
119+
120+
### Info
121+
**[U7] File structure**
122+
- Files found: <plugin>_Basic_Unit_test.js, <plugin>_Advanced_Unit_test.js, <plugin>_Unit_test.js
123+
- License headers: OK
124+
- Termination sequence: OK
125+
126+
### Widget Coverage Detail
127+
| Widget Key | Type | Has Test | Has Bad Value Test |
128+
|-----------|------|----------|-------------------|
129+
| voltage0_in/hardwaregain | range | YES | YES |
130+
| voltage0_in/rf_bandwidth | readonly | NO | N/A |
131+
| ensm_mode | combo | YES | YES |
132+
| ... | ... | ... | ... |
133+
Coverage: X/Y widgets tested (Z%)
134+
135+
### API Method Coverage
136+
| Method | Has Test |
137+
|--------|----------|
138+
| getRxHardwareGain(channel) | YES |
139+
| setRxHardwareGain(channel, val) | YES |
140+
| getRxRssi(channel) | NO |
141+
| ... | ... |
142+
Coverage: X/Y methods tested (Z%)
143+
144+
### Verdict
145+
[PASS/FAIL] — [one sentence summary]
146+
```
147+
148+
PASS = zero critical issues. FAIL = one or more critical issues.

0 commit comments

Comments
 (0)