|
| 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 |
0 commit comments