test-foundry is a QEMU-based Windows guest automation test tool.
It covers VM setup, snapshot creation, test execution, file upload/download, screenshot capture, and QMP-event-based waits such as wait-panic and wait-reset.
- English: README.md
- Korean: README-ko.md
- QEMU VM setup and snapshot-based test execution
- Split execution and file-transfer methods for Windows guests (
SSH/WinRM) - GitHub workflow-like step-based test definition (
wait-boot,exec,file-upload,file-download,screenshot,shutdown, and more) preboot.stepssupport for offline disk patching before boot (efi-add-file, etc.)- expression support in test step params
${{ test.dir }}${{ env.<name> }}${{ vmconfig.<json-key> }}
- QEMU
- OVMF / UEFI firmware
- qemu-img
- swtpm (optional. linux only)
The example flow is primarily prepared for running Windows guests on Linux hosts.
go build ./cmd/test-foundryOr:
go run ./cmd/test-foundry --helpRunning the root command shows output like this:
$ go run ./cmd/test-foundry/
test-foundry automates testing of Windows drivers and UEFI applications
using QEMU virtual machines. It provides VM lifecycle management,
snapshot-based test execution, and step-by-step test automation.
Usage:
test-foundry [command]
Available Commands:
action Execute individual actions against a running VM
completion Generate the autocompletion script for the specified shell
help Help about any command
test Run tests against a VM snapshot
vm-destroy Destroy VM context directory
vm-setup Create VM context and prepare a snapshot
Flags:
--headless Headless mode (VNC only, no display)
-h, --help help for test-foundry
--qemu string QEMU binary path (default "qemu-system-x86_64")
--verbose Verbose logging
--vm-name string VM context name (required)
--workdir string VM context directory root (default ".testfoundry")
Use "test-foundry [command] --help" for more information about a command.
The action subcommand help looks like this:
$ go run ./cmd/test-foundry/ action --help
Execute individual actions against a running VM session via the IPC daemon.
Each action communicates with the daemon over HTTP to perform operations
on the guest OS or QEMU instance.
Usage:
test-foundry action [command]
Available Commands:
dump Dump guest memory via QMP
exec Execute a command on the guest via SSH
file-download Download a file from the guest via SFTP
file-upload Upload a file to the guest via SFTP
poweroff Forcefully power off the VM
reboot Reboot the guest
screenshot Capture a screenshot via VNC
shutdown Gracefully shut down the guest
sleep Wait for a specified duration
wait-boot Wait until the guest OS is reachable via SSH
wait-oobe Wait until Windows OOBE is completed
wait-panic Wait for a pvpanic event from the guest
Flags:
-h, --help help for action
Global Flags:
--headless Headless mode (VNC only, no display)
--qemu string QEMU binary path (default "qemu-system-x86_64")
--verbose Verbose logging
--vm-name string VM context name (required)
--workdir string VM context directory root (default ".testfoundry")
Use "test-foundry action [command] --help" for more information about a command.
- Prepare a base image.
- Run
vm-setupto create a VM context and snapshot. - Run
testto restore the snapshot and execute test steps.
The default workdir is .testfoundry, and --vm-name is used to distinguish VM contexts.
./scripts/vagrant-extract-qcow2.sh gusztavvargadr/windows-11 2601.0.0 ./imagesThis prepares the qcow2 image under ./images for the sample image definition at examples/images/windows-11.yaml.
test-foundry --vm-name="win11" vm-setup --image ./examples/images/windows-11.yamlThis step performs:
- VM context creation
- QEMU boot
- waiting for OOBE completion
- WinRM / SSH preparation
- setup step execution
- shutdown and snapshot creation
test-foundry --vm-name="win11" test --output ./temp --test ./examples/tests/01-hello-world/test.yamlIn this command:
--output ./tempis the location fortest-result.json- the sample test itself writes output files to
./output/01/...as defined in examples/tests/01-hello-world/test.yaml
So after the run, artifacts are split across two locations:
./temp/test-result.json./output/01/...
find output -type fExpected output:
output/01/hello-output.txt
output/01/screenshot.png
video_test02_bsod.mp4
The sample examples/tests/01-hello-world/test.yaml runs this sequence:
wait-bootfile-uploadexecfile-downloadscreenshotshutdown
Test parameters can also use expressions:
params:
src: "${{ test.dir }}/hello.bat"
name: "${{ vmconfig.machine_name }}"
ssh_port: "${{ vmconfig.ssh_host_port }}"preboot.steps is used to modify the qcow2 disk offline before QEMU boots. The current built-in preboot action is efi-add-file, which writes files into the EFI System Partition FAT32 filesystem.
preboot.stepsin image YAMLpreboot.stepsin test YAML
Both are supported, so you can patch EFI content either while preparing the base VM or right before an individual test run.
Example:
preboot:
steps:
- action: efi-add-file
params:
src: "${{ test.dir }}/bootx64.efi"
dst: /EFI/Boot/bootx64.efiFor a full example, see examples/tests/03-patch-efi/test.yaml and examples/tests/03-patch-efi/shellx64.efi. That sample patches EFI boot paths, captures a screenshot, and then powers the VM off.
Expression format:
${{ ... }}
Currently supported:
${{ test.dir }}- the directory containing the currently running test YAML
${{ env.<name> }}- access to the current process environment variables
${{ vmconfig.<key> }}- access to runtime
MachineConfigJSON fields
- access to runtime
Examples:
${{ vmconfig.machine_name }}${{ vmconfig.qmp_socket_path }}${{ vmconfig.ssh_host_port }}${{ env.HOME }}
- Linux guest support