ワンクリックで
test-driven-development
// Use when implementing any amd-smi feature, bug fix, or behavior change — before writing implementation code. Enforces strict RED-GREEN-REFACTOR: failing test first, watch it fail, minimal code to pass, refactor.
// Use when implementing any amd-smi feature, bug fix, or behavior change — before writing implementation code. Enforces strict RED-GREEN-REFACTOR: failing test first, watch it fail, minimal code to pass, refactor.
Use before any creative work — designing a new amdsmi_* API, adding a CLI command, building a feature, or modifying behavior. Explores intent, requirements, and design before any code is written.
Use when facing 2+ independent problems with no shared state — e.g., unrelated test failures in different subsystems, multiple independent bug investigations, parallel research tasks. Dispatch one focused subagent per domain instead of investigating sequentially.
Use when a written implementation plan exists at docs/dev/plans/ and you need to execute it task-by-task in the current session with verification at each step.
Use when finishing an amd-smi development branch — consolidating commits into logical groups with clean messages AND deciding how to integrate the work (merge to develop, push and open PR, keep as-is, or discard). Covers commit restructuring plus the merge/PR/cleanup workflow.
Use when encountering any bug, test failure, build failure, or unexpected behavior in amd-smi — before proposing any fix. Enforces root-cause investigation before symptom patching.
Use when starting amd-smi feature work, executing an implementation plan, or reviewing a PR that needs an isolated workspace away from the main checkout. Sets up a worktree following the rocm-systems-pr<PR#> convention.
| name | test-driven-development |
| description | Use when implementing any amd-smi feature, bug fix, or behavior change — before writing implementation code. Enforces strict RED-GREEN-REFACTOR: failing test first, watch it fail, minimal code to pass, refactor. |
Write the test first. Watch it fail. Write minimal code to make it pass.
Core principle: If you didn't watch the test fail, you don't know what it tests.
Violating the letter of the rules is violating the spirit of the rules.
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
Wrote code before the test? Delete it. Start over from the test.
No exceptions:
Always:
amdsmi_* API functionsExceptions (ask first):
amdsmi_wrapper.py)Thinking "this is too simple for a test"? Stop. That's rationalization.
RED → write one minimal failing test
↓
verify it fails (and fails for the right reason)
↓
GREEN → minimal code to make it pass
↓
verify it passes; all other tests still pass
↓
REFACTOR → clean up while staying green
↓
repeat for the next behavior
One behavior. Clear name. Real code (no mocks unless unavoidable).
C++ (GTest) example:
TEST_F(AmdSmiTest, GetGpuPowerCapHandlesInvalidHandle) {
uint64_t cap = 0;
amdsmi_status_t status = amdsmi_get_power_cap(nullptr, 0, &cap);
EXPECT_EQ(status, AMDSMI_STATUS_INVAL);
}
Python example:
def test_get_gpu_power_cap_raises_on_invalid_handle():
with pytest.raises(AmdSmiLibraryException) as exc:
amdsmi.amdsmi_get_power_cap(None)
assert exc.value.err_code == amdsmi.AmdSmiRetCode.STATUS_INVAL
MANDATORY. Never skip.
# C++
cd build && make -j$(nproc) amdsmitst && \
./tests/amd_smi_test/amdsmitst --gtest_filter=AmdSmiTest.GetGpuPowerCapHandlesInvalidHandle
# Python
cd tests/python_unittest && python3 -m pytest -v -k test_get_gpu_power_cap_raises_on_invalid_handle
Confirm:
Test passes immediately? You're testing existing behavior. Fix the test.
Simplest code that makes the failing test pass. No extra features, no flexibility flags, no "while I'm here" improvements.
MANDATORY.
Re-run the same test. Confirm pass. Then run the relevant suite to confirm nothing else broke.
After green only. Remove duplication, improve names, extract helpers. Keep tests green. Don't add behavior.
One failing test for the next behavior.
| Quality | Good | Bad |
|---|---|---|
| Minimal | One thing. "and" in the name? Split it. | test('validates handle and reads register and formats output') |
| Clear name | test_get_power_cap_returns_inval_on_null_handle | test_power_1 |
| Real code | Calls the actual amdsmi_* function | Mocks the function being tested |
| Observable | Tests behavior the user sees | Tests internal state nobody else can read |
| Excuse | Reality |
|---|---|
| "I'll write tests after to verify it works" | Tests-after pass immediately and prove nothing. You never saw them catch the bug. |
| "I already manually tested all edge cases" | Manual testing is ad-hoc, undocumented, and doesn't re-run on CI. |
| "Deleting X hours of work is wasteful" | Sunk cost. Working code without real tests is technical debt. |
| "TDD is dogmatic, being pragmatic means adapting" | TDD IS pragmatic — finds bugs before commit, prevents regressions, enables refactoring. |
| "Tests-after achieve the same goal — it's about spirit" | No. Tests-after answer "what does this do?" Tests-first answer "what should this do?" |
| "This is just a one-line fix" | One-line fixes have caused production outages. Write the test. |
| "The CI will catch it" | CI catches what you tested. If you didn't write the test, CI is blind. |
All of these mean: delete the code, start over with the test.
systematic-debugging — when a test fails in a way you don't understandverification-before-completion — after every commit