본문 바로가기
내가만드는것_만든것/프로그램-공개

Windows 드라이버와 UEFI 테스트를 자동화하기 위한 test-foundry

by Joseph.Lee 2026. 4. 25.

Windows 드라이버나 UEFI 애플리케이션을 테스트하다 보면 반복적으로 마주치는 일이 있습니다. VM을 켜고, 부팅을 기다리고, 파일을 복사하고, 명령을 실행하고, 결과물을 다시 가져오고, 필요하면 스크린샷이나 메모리 덤프까지 남겨야 합니다. 실패가 발생하면 다시 깨끗한 상태로 되돌려 재현해야 하고, 때로는 부팅 전에 EFI 파티션을 직접 패치해야 할 수도 있습니다.

이런 작업은 한두 번이면 수동으로 처리할 수 있지만, 테스트 케이스가 늘어나고 재현성이 중요해지는 순간부터는 자동화가 필요해집니다.

 

처음엔 Vagrant을 써보려고 하다가 한계에 다달아서 만들기 시작한 도구가 test-foundry입니다.

 

test-foundry는 QEMU 기반의 Windows 게스트 자동화 테스트 도구입니다. VM setup, snapshot 생성, 테스트 실행, 파일 업로드/다운로드, 스크린샷 캡처, 그리고 wait-panic, wait-reset 같은 QMP 이벤트 기반 대기를 하나의 테스트 흐름 안에서 처리할 수 있도록 설계했습니다.

왜 만들었나

처음 목표는 단순했습니다. Windows 게스트에서 반복적으로 실행해야 하는 테스트를 사람이 매번 손으로 하지 않게 만드는 것이었습니다.

예를 들어 다음과 같은 흐름입니다.

  1. VMWare 로 Windows 을 부팅한다.
  2. 테스트용 파일(드라이버)을 게스트로 업로드한다.
  3. 게스트 안에서 명령을 실행한다.
  4. 결과 파일을 호스트로 다운로드한다.
  5. 앗! 버그발생! WinDbg 을 연결해 디버깅한다.
  6. VM을 종료하고, 스냅샷을 돌린다.

test-foundry 은 이러한 과정을 자동화 합니다. 먼저 베이스 이미지를 준비하고, vm-setup으로 VM context와 스냅샷을 만든 다음, test 명령으로 스냅샷을 복원해 테스트 단계를 실행합니다.

 
# VM Setup (OOBE 이미지 셋업을 기다리고, 공통 설정 (bcdedit 등))
$ test-foundry --vm-name="win11" vm-setup --image ./examples/images/windows-11.yaml
# 테스트 실행 (항상 VM Setup 이 끝난 시점의 스냅샷 사용)
$ test-foundry --vm-name="win11" test --output ./temp --test ./examples/tests/01-hello-world/test.yaml
 

GitHub Actions처럼 테스트를 YAML로 정의하기

test-foundry에서 테스트는 YAML 파일로 정의합니다. GitHub Actions의 workflow처럼 step 기반으로 작성할 수 있도록 했습니다. 아래 예제에서는 wait-boot, file-upload, exec, file-download, screenshot, shutdown 같은 액션을 순서대로 실행합니다.

예를 들어 hello-world 테스트는 게스트가 부팅될 때까지 기다린 뒤, bat 파일을 업로드하고, 실행 결과를 파일로 저장한 다음, 그 파일을 다시 다운로드하고 스크린샷을 캡처한 뒤 종료합니다.

 

name: "hello-world"
description: "Upload and run a hello-world bat file, capture stdout and screenshot"

steps:
  - action: wait-boot
    timeout: 120s
    params:
      retry_interval: 5s

  - action: file-upload
    timeout: 30s
    params:
      src: "${{ test.dir }}/hello.bat"
      dst: "C:\\Temp\\hello.bat"

  - action: exec
    timeout: 30s
    params:
      cmd: "cmd.exe"
      args: ["/c", "C:\\Temp\\hello.bat > C:\\Temp\\hello-output.txt 2>&1"]
      expect_exit_code: 0

  - action: file-download
    timeout: 30s
    params:
      src: "C:\\Temp\\hello-output.txt"
      dst: "./output/01/hello-output.txt"

  - action: screenshot
    timeout: 10s
    params:
      output: "./output/01/screenshot.png"

  - action: shutdown
    timeout: 120s
 
 

이 방식의 장점은 테스트 절차가 코드처럼 남는다는 점입니다. “VM을 켠 뒤 어떤 파일을 넣고 어떤 명령을 실행했는지”가 YAML 안에 그대로 남기 때문에, 나중에 같은 테스트를 다시 실행하거나 CI에 연결하기 쉬워집니다.

이미지 정의와 테스트 정의를 분리하기

test-foundry는 VM 이미지 설정과 테스트 시나리오를 분리합니다.

이미지 정의 YAML은 재사용 가능한 VM 베이스 이미지와 접속 방법을 설명합니다. vm-setup 단계에서는 이 정의를 사용해 VM을 부팅하고 setup step을 실행한 뒤, 이후 테스트 실행에 사용할 스냅샷을 만듭니다.

이미지 정의에는 qcow2 이미지 경로, OVMF/UEFI firmware, 메모리, CPU, QEMU 추가 인자, SSH/WinRM 접속 정보 등이 들어갑니다. exec_method와 file_method는 각각 ssh 또는 winrm을 사용할 수 있고, 실행과 파일 전송 방식을 분리할 수도 있습니다.

 
name: "windows-11-24h2"
os: "windows"

qemu:
  image: "./images/windows-11.qcow2"
  firmware: "/usr/share/OVMF/OVMF_CODE_4M.fd"
  firmware_vars: "/usr/share/OVMF/OVMF_VARS_4M.fd"
  memory: "4G"
  cpus: 2
  cpu: host

connection:
  exec_method: "winrm"
  file_method: "ssh"
  username: "vagrant"
  password: "vagrant"
  ssh_port: 22
  winrm_port: 5985

setup:
  steps:
    - action: wait-boot
      timeout: 10m
      params:
        retry_interval: 5s
    - action: shutdown
      timeout: 120s
 

반면 테스트 정의 YAML은 이미 만들어진 VM 스냅샷 위에서 어떤 작업을 실행할지 설명합니다. 테스트 정의는 steps 외에도 preboot, panic 같은 섹션을 가질 수 있습니다.

이 분리는 꽤 중요합니다. 베이스 이미지 준비는 느리고 무거운 작업일 수 있지만, 개별 테스트는 스냅샷을 복원해서 빠르게 반복할 수 있어야 하기 때문입니다.

부팅 전 디스크 패치: preboot.steps

Windows 드라이버나 UEFI 쪽 테스트를 하다 보면, 게스트 OS가 부팅되기 전에 디스크를 수정해야 하는 경우가 있습니다. 예를 들어 EFI System Partition 안에 특정 EFI 바이너리를 넣거나, 부팅 경로를 바꾸고 싶은 경우입니다.

이를 위해 test-foundry는 preboot.steps를 지원합니다. preboot.steps는 QEMU를 부팅하기 전에 qcow2 디스크를 오프라인으로 수정하는 단계입니다. 현재 efi-add-file, efi-get-file 액션을 제공합니다.

preboot:
  steps:
    - action: efi-add-file
      params:
        src: "${{ test.dir }}/shellx64.efi"
        dst: "/EFI/Boot/bootx64.efi"
 

예제 03-patch-efi는 shellx64.efi를 EFI 부트 경로에 넣고, VM을 잠깐 실행한 뒤 스크린샷을 찍고 poweroff로 종료합니다.

이 기능은 특히 UEFI 애플리케이션이나 부트 단계에서 동작하는 구성요소를 테스트할 때 유용합니다. OS가 완전히 올라온 뒤 실행하는 테스트와 달리, 부팅 경로 자체를 테스트 흐름에 포함할 수 있기 때문입니다.

Panic 감지 및 덤프

Windows 커널/드라이버 테스트에서 중요한 것은 실패 자체보다 실패를 재현하고 분석할 수 있는 정보를 남기는 일입니다.

test-foundry의 테스트 정의는 panic.steps를 지원합니다. 게스트가 pvpanic 이벤트를 발생시키고 테스트 러너가 crash를 감지하면, panic 단계에서 스크린샷을 찍거나 QMP를 통해 메모리 덤프를 남길 수 있습니다.

panic:
  steps:
    - action: screenshot
      timeout: 10s
      params:
        output: "./output/bsod-screenshot.png"

    - action: dump
      timeout: 300s
      params:
        format: "win-dmp"
        output: "./output/memory.dump"
 

테스트가 실패했을 때 “실패했다”는 로그만 남는 것이 아니라, 화면 상태와 덤프 같은 분석 가능한 산출물을 자동으로 남기는 것이 목표입니다.

 

참고로 qemu 의 win-dmp 는 모든 메모리 영역을 덤프하기 때문에 일반적인 minidump 보다 훨신 큽니다. 아쉽...

yaml 에서 Expression 지원

테스트 YAML 안에서는 ${{ ... }} 형태의 expression을 사용할 수 있습니다. 예를 들어 현재 테스트 YAML이 위치한 디렉터리인 ${{ test.dir }}, 환경 변수인 ${{ env.NAME }}, 런타임 VM 설정 값인 ${{ vmconfig.<key> }}를 참조할 수 있습니다.

params:
  src: "${{ test.dir }}/hello.bat"
  name: "${{ vmconfig.machine_name }}"
  ssh_port: "${{ vmconfig.ssh_host_port }}"
 

이 기능은 테스트 파일과 테스트 리소스를 같은 디렉터리에 두고 함께 이동하거나, VM마다 달라지는 포트·소켓·머신 이름 같은 값을 하드코딩하지 않기 위해 넣었습니다.

현재 상태와 앞으로

아직 linux 은 제대로 지원하지 않습니다. 이후 linux guest 지원도 예정입니다.

host 는 Linux & Windows 모두 가능합니다.

마무리

test-foundry는 “VM을 켜고 명령을 실행하는 도구”라기보다, VM 기반 테스트를 반복 가능하고 추적 가능한 형태로 정리하기 위한 도구입니다.

특히 다음과 같은 상황에서 유용합니다.

  • Windows 드라이버 테스트를 반복해야 하는 경우
  • UEFI 애플리케이션이나 EFI 부트 경로를 자동으로 검증하고 싶은 경우
  • QEMU snapshot 기반으로 깨끗한 테스트 환경을 계속 재사용하고 싶은 경우
  • 실패 시 스크린샷, 메모리 덤프, 출력 파일 같은 분석 산출물을 자동으로 남기고 싶은 경우
  • 테스트 절차를 YAML로 남겨 CI나 다른 자동화 흐름에 연결하고 싶은 경우

아직 초기 버전이지만, 제가 직접 필요해서 만든 만큼 실제 테스트 흐름에서 마주치는 불편함을 하나씩 줄여가는 방향으로 발전시키고 있습니다.

 
다운로드는 아래 Github releases 에서 가능하며, 오픈소스(GPL v2) 으로써 자유롭게 사용 가능합니다.
 
 

GitHub - jc-lab/test-foundry

Contribute to jc-lab/test-foundry development by creating an account on GitHub.

github.com

 

반응형

'내가만드는것_만든것 > 프로그램-공개' 카테고리의 다른 글

Online Utils  (0) 2025.10.22
Har to markdown  (0) 2025.09.22
G2B 입찰 공고 검색/알림 서비스  (0) 2025.01.28
ping-watcher  (0) 2024.09.15
local-tls-proxy: 자동으로 모든 포트를 HTTPS으로  (0) 2024.09.05

댓글