microsoft/xdp-testing
XDP-for-Windows test execution and debugging workflow. Use when running tests (functional, spinxsk, pktfuzz, ringperf, rxfilter, xskmaprx, xskfwdkm), debugging bugchecks, attaching kd, recovering test machines after crashes, or invoking any tools/*.ps1 script that runs on hardware. Covers PowerShell remoting via -ComputerName, kd remote pipe debugging, and the inject/build/bugcheck/.reboot/check-drivers recovery loop.
npx skills add https://github.com/microsoft/xdp-for-windows --skill xdp-testing
This skill covers everything an agent needs to execute tests on the
required separate test machine, attach kd for crash analysis, and
recover the machine to a known-good state after a bugcheck.
> [!IMPORTANT]
> All XDP testing must occur on a separate Windows test machine — never
> the dev machine. Always pass -ComputerName <machine> explicitly on
> every test command. Do not rely on
> tools\remote-set-default.ps1 alone — the session default lives in
> the PowerShell global scope and is silently lost when a new terminal
> spawns (after Start-Process, kd sessions, or whenever the tool host
> opens a fresh shell). A test script invoked without -ComputerName
> and without an active session default will run **against the dev
> machine** and may damage local state. Treat -ComputerName as
> required.
functional.ps1, spinxsk.ps1, prepare-machine.ps1(non-build scenarios), check-drivers.ps1, rxfilter.ps1,
rxfilterperf.ps1, ringperf.ps1, xskmaprx.ps1, xskfwdkm.ps1,
pktfuzz.ps1.
build.ps1, merge-artifacts.ps1, publish-nuget.ps1,
prepare-machine.ps1 -ForBuild / -ForEbpfBuild,
create-test-archive.ps1, update-nuspec.ps1.
*"Which test machine should I run tests on?"*. Accept hostname,
FQDN, or IP. Remember the value for the rest of the session and
pass it as -ComputerName <machine> on every test invocation.
not replace -ComputerName for agent-driven runs:
.\tools\remote-set-default.ps1 <machine>
-ComputerName — it auto-deploysartifacts\bin\<plat>_<cfg>\ and streams output back:
.\tools\functional.ps1 -ComputerName <machine> -ListTestCases
.\tools\functional.ps1 -ComputerName <machine> -TestCaseFilter "Name=GenericBinding"
.\tools\spinxsk.ps1 -ComputerName <machine> -Minutes 5
TrustedHosts (UAC prompt) and, if WinRM rejects with auth error,
prompts at the console (Read-Host, no GUI) for username and
password. Credentials are cached in
$global:XdpRemoteCredentialCache for the rest of the session. Do
not generate Get-Credential calls or store passwords in
scripts.
> [!IMPORTANT]
> When run by an agent, console password prompts are invisible to
> the user (the terminal is not interactive and any typed
> characters would be shown in plaintext). If a test command stalls
> waiting for credentials, do not send them to the terminal.
> Instead, collect username and password through the chat UI
> (vscode_askQuestions) and pass them to subsequent commands via
> the -Credential parameter or by pre-populating
> $global:XdpRemoteCredentialCache before re-running the script.
elevation):
.\tools\remote.ps1 -EnableRemoting
If the user hasn't done this, the connection fails with "WinRM
client cannot process the request" — surface this and ask them to
run it.
.\tools\build.ps1 (or rely on arecent build) before remote test commands, since deploy pulls from
artifacts\bin\<plat>_<cfg>\.
When the user doesn't specify parameters, **derive sensible defaults
from the matching job in .github/workflows/ci.yml**.
That file is the source of truth for which switches each test exercises
in CI.
-Minutes <N> -XdpmpPollProvider <NDIS|FNDIS>
-SuccessThresholdPercent <pct> -EnableEbpf [-TxInspect]`. Scale
-Minutes down for smoke runs as requested.
-Config, -Platform, and any-TestCaseFilter / -XdpInstaller flags from the CI job.
duration/iteration counts when the user asks for a shorter run.
Respect explicit user overrides; leave everything else at CI defaults.
Test machines typically have a kd "head" already attached and exposing
a debug server over a named pipe (started on the head with
.server npipe:pipe=<name>). To watch for breaks during a test run,
attach a passive remote kd client from the dev machine in a
separate background terminal before kicking off the test:
# Run with mode=async; <pipe-name> defaults to the same value as
# -ComputerName / Set-XdpRemoteDefault.
kd.exe -remote npipe:server=localhost,pipe=<pipe-name>
-remote npipe:server=localhost,pipe=<name> — never-k com:pipe,port=... (that's the serial-pipe transport for VMs and
will not connect to a kd debug server).
pipe to the value of -ComputerName /Set-XdpRemoteDefault. If that fails to connect, ask the user once
for the correct pipe name.
mode=async so test invocations stream output in parallel.kd> aftera break) or bugcheck banners; surface them to the user, otherwise
let kd run idle.
head is currently attached. Inform the user and continue without kd.
.reboot, leave the kdterminal running — secondary breaks (early-boot Driver Verifier hits,
watchdog timeouts, follow-on bugchecks from corrupted state) often
happen during the next boot.
.reboot non-interactively from the dev machine. Usekd.exe -cf <scriptfile> with a hidden window:
$cmd = New-TemporaryFile
Set-Content -Path $cmd -Value ".reboot`r`nqd"
Start-Process kd.exe -ArgumentList @(
"-remote","npipe:server=localhost,pipe=<machine>",
"-cf",$cmd.FullName) -PassThru -WindowStyle Hidden |
ForEach-Object { $_.WaitForExit(60000) | Out-Null }
Remove-Item $cmd.FullName -Force
Do not try to drive an interactive kd> prompt via PowerShell's
terminal stdin — that connects you to the *host* shell, not kd.
Then poll Test-WSMan -ComputerName <machine> until it succeeds.
terminal.
Bugchecks frequently leave the test machine in a partially-corrupted
state that prevents the next iteration from running. The recovery
script is tools\check-drivers.ps1 -Force. **The dev loop cannot move
on until that command reports "No loaded XDP drivers found!".**
in the seconds before the crash may be truncated, partially written,
or filled with NUL bytes. Driver registry keys, INF/catalog files,
PnP metadata, and Windows Installer product databases are common
victims.
installer/MSI/INF reports the product is missing from its database
but the on-disk binaries are still present, *reinstall* on top of
the existing layout (which reseeds the database) and then run the
normal uninstall. Prefer this over hand-rolled service/driver
teardown.
reinstall over corruption, or paper over partial state belong
behind a -Force switch on the recovery script (e.g.
tools\check-drivers.ps1 -Force). Don't change default
install/uninstall behavior — that would silently mask real bugs.
different* corruption (NUL-filled scripts last time, wedged eBPF
service this time, something else next time). When
check-drivers.ps1 fails:
stuck.
tools\setup.ps1 (or tools\check-drivers.ps1) with atargeted fix for that specific failure mode — add a stuck
service to the cleanup list, detect a specific MSI exit code,
add a NUL-byte check for a specific file path, etc.
check-drivers.ps1 -Force and repeat until it succeeds.Do not stop at "the recovery is partially working" — partial
recovery means the next test run will hit the residual broken
state and waste an iteration. Avoid broad ignore-everything error
handling; recovery should be targeted and understandable.
# Functional tests
.\tools\prepare-machine.ps1 -ComputerName <machine> -ForFunctionalTest
.\tools\functional.ps1 -ComputerName <machine> -Config Debug -Platform x64
.\tools\functional.ps1 -ComputerName <machine> -TestCaseFilter "Name=GenericBinding"
.\tools\functional.ps1 -ComputerName <machine> -ListTestCases
.\tools\log.ps1 -Convert -Name xdpfunc
# Stress tests (spinxsk)
.\tools\prepare-machine.ps1 -ComputerName <machine> -ForSpinxskTest
.\tools\spinxsk.ps1 -ComputerName <machine> -XdpmpPollProvider FNDIS -QueueCount 2 -Minutes 10
.\tools\log.ps1 -Convert -Name spinxsk
# Recovery
.\tools\check-drivers.ps1 -ComputerName <machine> -Force
See docs/remote-testing.md for the
human-facing reference.
Take microsoft/xdp-testing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.