summaryrefslogtreecommitdiff
path: root/scripts/testing/run-test.sh
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-27 17:22:55 -0600
committerCraig Jennings <c@cjennings.net>2026-01-27 17:22:55 -0600
commit70bb2d5ab1bf6787bc613e33f5398be2eca1f5fd (patch)
tree0d8cd5057dd32f5f312a7f3534d590b99c2f0f91 /scripts/testing/run-test.sh
parent0c6175bfc98f2c5ff2debc665fd8bf91f9171f4e (diff)
feat(testing): rewrite test infrastructure from libvirt to direct QEMU
Replace the never-fully-operational libvirt-based VM test infrastructure with direct QEMU management and archangel ISO for fully automated, unattended base VM creation. Key changes: - vm-utils.sh: complete rewrite — QEMU process mgmt via PID file, monitor socket for graceful shutdown, qemu-img snapshots, SSH port forwarding (localhost:2222) - create-base-vm.sh: boots archangel ISO, SSHs in, runs unattended install via config file, verifies, creates clean-install snapshot - run-test.sh: snapshot revert, git bundle transfer, detached archsetup execution with setsid, polling, validation, and report generation - debug-vm.sh: CoW overlay disk, GTK display, auto-cleanup on close - setup-testing-env.sh: reduced deps to qemu-full/sshpass/edk2-ovmf/socat - cleanup-tests.sh: PID-based process management, orphan detection - validation.sh: port-based SSH (backward compatible), fuzzel/foot for Hyprland, corrected package list paths - network-diagnostics.sh: getent/curl instead of nslookup/ping (SLIRP) New files: - archsetup-test.conf: archangel config for base VM (btrfs, no encrypt) - archsetup-vm.conf: archsetup config for unattended test execution - assets/archangel.conf.example: reference archangel config Deleted: - finalize-base-vm.sh: merged into create-base-vm.sh - archinstall-config.json: replaced by archangel .conf format Tested: full end-to-end run — 51 validations passed, 0 failures. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'scripts/testing/run-test.sh')
-rwxr-xr-xscripts/testing/run-test.sh173
1 files changed, 79 insertions, 94 deletions
diff --git a/scripts/testing/run-test.sh b/scripts/testing/run-test.sh
index 4c41cc3..a600870 100755
--- a/scripts/testing/run-test.sh
+++ b/scripts/testing/run-test.sh
@@ -5,7 +5,7 @@
#
# This script:
# 1. Reverts base VM to clean snapshot
-# 2. Starts the base VM
+# 2. Boots the VM via QEMU
# 3. Transfers archsetup and dotfiles
# 4. Executes archsetup in the VM
# 5. Captures logs and validates results
@@ -55,15 +55,20 @@ done
# Configuration
TIMESTAMP=$(date +'%Y%m%d-%H%M%S')
-VM_NAME="archsetup-base"
+VM_IMAGES_DIR="$PROJECT_ROOT/vm-images"
TEST_RESULTS_DIR="$PROJECT_ROOT/test-results/$TIMESTAMP"
ROOT_PASSWORD="archsetup"
ARCHZFS_INBOX="$HOME/code/archzfs/inbox"
+ARCHSETUP_VM_CONF="$SCRIPT_DIR/archsetup-vm.conf"
-# Initialize logging
+# Set VM_IP for validation.sh backward compatibility
+VM_IP="localhost"
+
+# Initialize logging and VM paths
mkdir -p "$TEST_RESULTS_DIR"
LOGFILE="$TEST_RESULTS_DIR/test.log"
init_logging "$LOGFILE"
+init_vm_paths "$VM_IMAGES_DIR"
section "ArchSetup Test Run: $TIMESTAMP"
@@ -72,9 +77,9 @@ if [ ! -f "$ARCHSETUP_SCRIPT" ]; then
fatal "ArchSetup script not found: $ARCHSETUP_SCRIPT"
fi
-# Check if VM exists
-if ! vm_exists "$VM_NAME"; then
- fatal "Base VM not found: $VM_NAME"
+# Check disk exists
+if [ ! -f "$DISK_PATH" ]; then
+ fatal "Base disk not found: $DISK_PATH"
info "Create it first: ./scripts/testing/create-base-vm.sh"
fi
@@ -82,72 +87,34 @@ fi
section "Preparing Test Environment"
step "Checking for snapshot: $SNAPSHOT_NAME"
-if ! virsh --connect "$LIBVIRT_URI" snapshot-list "$VM_NAME" --name 2>/dev/null | grep -q "^$SNAPSHOT_NAME$"; then
- fatal "Snapshot '$SNAPSHOT_NAME' not found on VM $VM_NAME"
+if ! snapshot_exists "$DISK_PATH" "$SNAPSHOT_NAME"; then
+ fatal "Snapshot '$SNAPSHOT_NAME' not found on $DISK_PATH"
info "Available snapshots:"
- virsh --connect "$LIBVIRT_URI" snapshot-list "$VM_NAME" 2>/dev/null || info " (none)"
+ list_snapshots "$DISK_PATH"
info ""
- info "Create snapshot with:"
- info " virsh snapshot-create-as $VM_NAME $SNAPSHOT_NAME --description 'Clean Arch install'"
+ info "Create base VM with: ./scripts/testing/create-base-vm.sh"
fi
success "Snapshot $SNAPSHOT_NAME exists"
-# Shut down VM if running
-if vm_is_running "$VM_NAME"; then
- warn "VM $VM_NAME is currently running - shutting down for snapshot revert"
- stop_vm "$VM_NAME"
-fi
+# Stop VM if running and restore snapshot
+stop_qemu 2>/dev/null || true
-# Revert to clean snapshot
step "Reverting to snapshot: $SNAPSHOT_NAME"
-if restore_snapshot "$VM_NAME" "$SNAPSHOT_NAME"; then
+if restore_snapshot "$DISK_PATH" "$SNAPSHOT_NAME"; then
success "Reverted to clean state"
else
fatal "Failed to revert snapshot"
fi
-# Start VM
+# Start VM and wait for SSH
start_timer "boot"
step "Starting VM and waiting for SSH..."
-if ! start_vm "$VM_NAME"; then
- fatal "Failed to start VM"
-fi
-
-sleep 10 # Give VM time to boot
-
-# Get VM IP address
-VM_IP=""
-for i in {1..30}; do
- VM_IP=$(get_vm_ip "$VM_NAME" 2>/dev/null || true)
- if [ -n "$VM_IP" ]; then
- break
- fi
- sleep 2
-done
-
-if [ -z "$VM_IP" ]; then
- error "Could not get VM IP address"
- info "VM may not have booted correctly"
- fatal "VM boot failed"
-fi
-
-success "VM is running at $VM_IP"
+start_qemu "$DISK_PATH" "disk" "" "none" || fatal "Failed to start VM"
+wait_for_ssh "$ROOT_PASSWORD" 120 || fatal "VM SSH not available"
stop_timer "boot"
-# Wait for SSH
-step "Waiting for SSH to become available..."
-for i in {1..60}; do
- if sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- -o ConnectTimeout=2 "root@$VM_IP" "echo connected" &>/dev/null; then
- break
- fi
- sleep 2
-done
-
-success "SSH is available"
-
# Run network diagnostics
-if ! run_network_diagnostics "$VM_IP"; then
+if ! run_network_diagnostics; then
fatal "Network diagnostics failed - aborting test"
fi
@@ -165,39 +132,59 @@ BUNDLE_FILE=$(mktemp)
git bundle create "$BUNDLE_FILE" HEAD >> "$LOGFILE" 2>&1
# Transfer bundle and extract on VM
-sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP" "rm -rf /tmp/archsetup-test && mkdir -p /tmp/archsetup-test" >> "$LOGFILE" 2>&1
+vm_exec "$ROOT_PASSWORD" "rm -rf /tmp/archsetup-test && mkdir -p /tmp/archsetup-test" >> "$LOGFILE" 2>&1
-sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "$BUNDLE_FILE" "root@$VM_IP:/tmp/archsetup.bundle" >> "$LOGFILE" 2>&1
+copy_to_vm "$BUNDLE_FILE" "/tmp/archsetup.bundle" "$ROOT_PASSWORD"
# Clone from bundle on VM (simulates git clone)
-sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP" "cd /tmp && git clone --depth 1 /tmp/archsetup.bundle archsetup-test && rm /tmp/archsetup.bundle" >> "$LOGFILE" 2>&1
+vm_exec "$ROOT_PASSWORD" \
+ "cd /tmp && git clone --depth 1 /tmp/archsetup.bundle archsetup-test && rm /tmp/archsetup.bundle" \
+ >> "$LOGFILE" 2>&1
rm -f "$BUNDLE_FILE"
success "Repository cloned to VM (simulating git clone --depth 1)"
+# Transfer archsetup VM config file
+step "Copying archsetup VM config"
+copy_to_vm "$ARCHSETUP_VM_CONF" "/tmp/archsetup-test/archsetup-vm.conf" "$ROOT_PASSWORD"
+
+# Pre-archsetup VM setup
+section "Pre-ArchSetup VM Setup"
+
+step "Importing archzfs PGP key"
+vm_exec "$ROOT_PASSWORD" \
+ "curl -sL https://archzfs.com/archzfs.gpg | pacman-key --add - && pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76" \
+ >> "$LOGFILE" 2>&1 && success "archzfs PGP key imported" || warn "Failed to import archzfs key (may already be present)"
+
+step "Syncing package databases"
+vm_exec "$ROOT_PASSWORD" "pacman -Sy --noconfirm" >> "$LOGFILE" 2>&1 && \
+ success "Package databases synced" || warn "Package database sync had issues"
+
# Execute archsetup
section "Executing ArchSetup"
start_timer "archsetup"
step "Starting archsetup script in detached session on VM..."
-info "This will take 30-60 minutes depending on network speed"
info "Log file: $LOGFILE"
-# Start archsetup in a detached session on the VM (resilient to SSH disconnections)
+# Start archsetup fully detached.
+# Use ssh -T -n to prevent PTY allocation and stdin forwarding, which allows
+# the SSH session to close immediately after the remote shell exits.
REMOTE_LOG="/tmp/archsetup-test/archsetup-output.log"
-sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP" "cd /tmp/archsetup-test && nohup bash archsetup > $REMOTE_LOG 2>&1 & echo \$!" \
+
+sshpass -p "$ROOT_PASSWORD" ssh -T -n $SSH_OPTS \
+ -p "$SSH_PORT" root@localhost \
+ "setsid bash -c 'cd /tmp/archsetup-test && bash archsetup --config-file /tmp/archsetup-test/archsetup-vm.conf > $REMOTE_LOG 2>&1' < /dev/null > /dev/null 2>&1 &" \
>> "$LOGFILE" 2>&1
-if [ $? -ne 0 ]; then
- fatal "Failed to start archsetup on VM"
+# Verify the process started
+sleep 3
+if vm_exec "$ROOT_PASSWORD" "pgrep -f 'bash archsetup'" >> "$LOGFILE" 2>/dev/null; then
+ success "ArchSetup started in background on VM"
+else
+ fatal "ArchSetup process not found after launch"
fi
-success "ArchSetup started in background on VM"
-
# Poll for completion
step "Monitoring archsetup progress (polling every 30 seconds)..."
POLL_COUNT=0
@@ -205,9 +192,7 @@ MAX_POLLS=180 # 90 minutes max (180 * 30 seconds)
while [ $POLL_COUNT -lt $MAX_POLLS ]; do
# Check if archsetup process is still running
- # Use ps to avoid pgrep matching its own SSH command
- if sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP" "ps aux | grep '[b]ash archsetup' > /dev/null" 2>/dev/null; then
+ if vm_exec "$ROOT_PASSWORD" "ps aux | grep '[b]ash archsetup' > /dev/null" 2>/dev/null; then
# Still running, wait and continue
sleep 30
POLL_COUNT=$((POLL_COUNT + 1))
@@ -229,8 +214,9 @@ if [ $POLL_COUNT -ge $MAX_POLLS ]; then
else
# Get exit code from the remote log
step "Retrieving archsetup exit status..."
- ARCHSETUP_EXIT_CODE=$(sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP" "grep -q 'ARCHSETUP_EXECUTION_COMPLETE' /var/log/archsetup-*.log 2>/dev/null && echo 0 || echo 1" 2>/dev/null)
+ ARCHSETUP_EXIT_CODE=$(vm_exec "$ROOT_PASSWORD" \
+ "grep -q 'ARCHSETUP_EXECUTION_COMPLETE' /var/log/archsetup-*.log 2>/dev/null && echo 0 || echo 1" \
+ 2>/dev/null)
if [ "$ARCHSETUP_EXIT_CODE" = "0" ]; then
success "ArchSetup completed successfully"
@@ -241,8 +227,7 @@ fi
# Copy the remote output log
step "Retrieving archsetup output from VM..."
-sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP:$REMOTE_LOG" "$TEST_RESULTS_DIR/archsetup-output.log" 2>> "$LOGFILE" || \
+copy_from_vm "$REMOTE_LOG" "$TEST_RESULTS_DIR/archsetup-output.log" "$ROOT_PASSWORD" || \
warn "Could not copy remote output log"
# Append remote output to main test log
@@ -256,15 +241,17 @@ stop_timer "archsetup"
section "Capturing Test Artifacts"
step "Copying archsetup log from VM"
-sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP:/var/log/archsetup-*.log" "$TEST_RESULTS_DIR/" 2>> "$LOGFILE" || \
+copy_from_vm "/var/log/archsetup-*.log" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \
warn "Could not copy archsetup log"
step "Copying package lists from VM"
-sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
- "root@$VM_IP:/root/.local/src/archsetup-*.txt" "$TEST_RESULTS_DIR/" 2>> "$LOGFILE" || \
+copy_from_vm "/var/log/archsetup-*-package-list.txt" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \
warn "Could not copy package lists"
+step "Copying installed packages list"
+copy_from_vm "/var/log/archsetup-installed-packages.txt" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \
+ warn "Could not copy installed packages list"
+
# Capture post-install state
capture_post_install_state "$TEST_RESULTS_DIR"
@@ -282,9 +269,9 @@ generate_issue_report "$TEST_RESULTS_DIR" "$ARCHZFS_INBOX"
# Set validation result based on failure count
if [ $VALIDATION_FAILED -eq 0 ]; then
- VALIDATION_PASSED=true
+ TEST_PASSED=true
else
- VALIDATION_PASSED=false
+ TEST_PASSED=false
fi
# Generate test report
@@ -298,16 +285,16 @@ ArchSetup Test Report
Test ID: $TIMESTAMP
Date: $(date +'%Y-%m-%d %H:%M:%S')
-Test Method: Snapshot-based
+Test Method: QEMU snapshot-based
VM Configuration:
- Name: $VM_NAME
- IP: $VM_IP
+ Disk: $DISK_PATH
Snapshot: $SNAPSHOT_NAME
+ SSH: localhost:$SSH_PORT
Results:
ArchSetup Exit Code: $ARCHSETUP_EXIT_CODE
- Validation: $(if $VALIDATION_PASSED; then echo "PASSED"; else echo "FAILED"; fi)
+ Validation: $(if $TEST_PASSED; then echo "PASSED"; else echo "FAILED"; fi)
Validation Summary:
Passed: $VALIDATION_PASSED
@@ -338,16 +325,14 @@ section "Cleanup"
if $KEEP_VM; then
info "VM is still running in post-test state (--keep flag was used)"
info "Connect with:"
- info " Console: virsh console $VM_NAME"
- info " SSH: ssh root@$VM_IP"
+ info " SSH: sshpass -p '$ROOT_PASSWORD' ssh -p $SSH_PORT root@localhost"
info ""
- info "To revert to clean state when done:"
- info " virsh shutdown $VM_NAME"
- info " virsh snapshot-revert $VM_NAME $SNAPSHOT_NAME"
+ info "To stop VM: kill \$(cat $PID_FILE)"
+ info "To revert: qemu-img snapshot -a $SNAPSHOT_NAME $DISK_PATH"
else
step "Shutting down VM and reverting to clean snapshot"
- stop_vm "$VM_NAME"
- if restore_snapshot "$VM_NAME" "$SNAPSHOT_NAME"; then
+ stop_qemu
+ if restore_snapshot "$DISK_PATH" "$SNAPSHOT_NAME"; then
success "VM reverted to clean state"
else
warn "Failed to revert snapshot - VM may be in modified state"
@@ -357,7 +342,7 @@ fi
# Final summary
section "Test Complete"
-if [ $ARCHSETUP_EXIT_CODE -eq 0 ] && $VALIDATION_PASSED; then
+if [ "$ARCHSETUP_EXIT_CODE" = "0" ] && $TEST_PASSED; then
success "TEST PASSED"
exit 0
else