Skip to main content

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.

Ir a la instalación

Datos de origen

Repositorio
ROCm/rocm-systems
Última actividad en el origen
17 de junio de 2026 a las 14:24
Idioma detectado de SKILL.md
inglés
Estrellas
497
Forks
406

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
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.
# Test-Driven Development — amd-smi 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.** ## The Iron Law ``` NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST ``` Wrote code before the test? Delete it. Start over from the test. **No exceptions:** - Don't keep it as "reference" - Don't "adapt" it while writing the test - Don't peek at it - Delete means delete ## When to Use **Always:** - New `amdsmi_*` API functions - CLI command additions or behavior changes - Bug fixes (write the test that reproduces the bug first) - Refactors that change observable behavior **Exceptions (ask first):** - Pure auto-generated files (`amdsmi_wrapper.py`) - Build/packaging-only changes with no testable behavior - Throwaway diagnostic scripts Thinking "this is too simple for a test"? Stop. That's rationalization. ## Red-Green-Refactor ``` 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 ``` ### RED — Write the Failing Test One behavior. Clear name. Real code (no mocks unless unavoidable). **C++ (GTest) example:** ```cpp 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:** ```python 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 ``` ### Verify RED — Watch It Fail **MANDATORY. Never skip.** ```bash # 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 **fails** (not errors out) - Failure message matches what you expected - It fails because the behavior is missing, not because of a typo **Test passes immediately?** You're testing existing behavior. Fix the test. ### GREEN — Minimal Code Simplest code that makes the failing test pass. No extra features, no flexibility flags, no "while I'm here" improvements. ### Verify GREEN **MANDATORY.** Re-run the same test. Confirm pass. Then run the relevant suite to confirm nothing else broke. ### REFACTOR — Clean Up After green only. Remove duplication, improve names, extract helpers. Keep tests green. Don't add behavior. ### Repeat One failing test for the next behavior. ## One Test at a Time — No Horizontal Slicing **Do NOT write all the tests first, then all the implementation.** Treating RED as "write every test" and GREEN as "write every implementation" is *horizontal slicing* and it produces crap tests: - Bulk-written tests verify *imagined* behavior, not *actual* behavior. - You end up testing the *shape* of things (signatures, struct fields) instead of observable behavior. - The tests go insensitive to real changes — they pass when behavior breaks. **Correct approach — vertical slices (tracer bullets):** one test → one minimal implementation → repeat. Each test responds to what the previous cycle taught you. ``` WRONG (horizontal): RED: test1, test2, test3, test4 GREEN: impl1, impl2, impl3, impl4 RIGHT (vertical): RED→GREEN: test1→impl1 RED→GREEN: test2→impl2 ... ``` **amd-smi cascade caution:** it's tempting, when adding a C API function, to write the GTest *and* the Python unit test *and* the CLI test all up front. Don't. Drive one tracer bullet end-to-end first (e.g. C++ behavior), then add the next layer's test once that layer exists. The cascade is still fully tested — just one slice at a time, not all tests before any code. ## Good Tests | 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 | ## Rationalizations — Reality | 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. | ## Red Flags — STOP and Start Over - Code before test - "I already verified it manually" - "Tests after achieve the same purpose" - "It's about the spirit, not the ritual" - "This case is different because..." - Watching the test pass without ever seeing it fail - Adding production code while writing the test - Writing every layer's test up front before any implementation (horizontal slicing) **All of these mean: delete the code, start over with the test.** ## amd-smi-Specific Notes - **Cascade testing:** A new API function needs tests at multiple layers — at minimum the C++ GTest and a Python unit test. CLI changes need a CLI test. - **Hardware dependency:** Most amd-smi tests require an AMD GPU. If your test cannot run on the dev box, say so in the test plan in the spec; don't silently skip the RED step. - **Auto-generated wrapper:** When adding a C function, the wrapper regen is part of the GREEN step, not a separate "fix later". ## Required Companion Skills - **`systematic-debugging`** — when a test fails in a way you don't understand - **`verification-before-completion`** — after every commit
Ver en GitHub