# The QEMU argv, as printed by dryrun.

"$VMM" create av1 64M >/dev/null 2>&1
IMG=$VMMDIR/av1/disk.qcow2

cfg av1 <<EOF
HOSTFWD="2222:22"
EOF
outputs 'hostfwd=tcp:127.0.0.1:2222-:22' "two field HOSTFWD binds loopback" dryrun av1
outputs "-vnc 'none'" "GRAPHICS=no still arms -vnc none" dryrun av1
outputs 'free-page-reporting=on' "balloon is on by default" dryrun av1
outputs 'strict=on' "the boot list is a restriction, not a hint" dryrun av1
outputs "sandbox 'on,.*spawn=deny" "the sandbox denies spawning" dryrun av1
outputs 'panic=pause' "a panicking guest is paused, not killed" dryrun av1
omits 'aio=io_uring' "the disk uses qemu's default AIO backend" dryrun av1

cfg av1 <<EOF
HOSTFWD="0.0.0.0:2222:22"
EOF
outputs 'hostfwd=tcp:0.0.0.0:2222-:22' "three field HOSTFWD honours the address" dryrun av1

cfg av1 <<EOF
HOSTFWD="2222:22,0.0.0.0:8080:80"
EOF
outputs 'hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:0.0.0.0:8080-:80' \
	"both HOSTFWD forms in one value" dryrun av1

cfg av1 <<EOF
BALLOON=no
EOF
omits balloon "BALLOON=no omits the device" dryrun av1

cfg av1 <<EOF
NETWORK=hostonly
EOF
outputs 'restrict=on' "NETWORK=hostonly restricts the guest" dryrun av1

cfg av1 <<EOF
NETWORK=no
EOF
outputs "-nic 'none'" "NETWORK=no gives no NIC" dryrun av1

# The CDROM hangs off virtio-scsi, which every machine type has, and not
# off an IDE bus, which only x86 has.
cfg av1 <<EOF
CDROM="$IMG"
BOOT_ORDER=dc
EOF
outputs 'scsi-cd' "the CDROM is a SCSI device" dryrun av1
outputs 'bus=scsi0.0,bootindex=1' "the CDROM takes its place in the boot order" dryrun av1

# A comma in the fixed disk path must be doubled, not passed through.
COMMA_DIR=$WORK/vms,comma
VMMDIR="$COMMA_DIR" "$VMM" create avc 64M >/dev/null 2>&1
out=$(VMMDIR="$COMMA_DIR" "$VMM" dryrun avc 2>&1) && got=0 || got=$?
if [ "$got" = 0 ] && printf '%s\n' "$out" | grep -q 'vms,,comma'; then
	ok "a comma in VMMDIR is escaped"
else
	notok "a comma in VMMDIR is escaped" "exit $got: $out"
fi

: > "$VMMDIR/av1/config"

# Two VMs must not share a MAC.
"$VMM" create av2 64M >/dev/null 2>&1
m1=$("$VMM" dryrun av1 2>/dev/null | sed -n 's/.*mac=\([0-9a-f:]*\).*/\1/p')
m2=$("$VMM" dryrun av2 2>/dev/null | sed -n 's/.*mac=\([0-9a-f:]*\).*/\1/p')
if [ -n "$m1" ] && [ "$m1" != "$m2" ]; then
	ok "each VM gets its own MAC"
else
	notok "each VM gets its own MAC" "av1=$m1 av2=$m2"
fi

# dryrun output must be pasteable, so every argument is single quoted.
outputs "'node-name=disk0f,driver=file,filename=$IMG" \
	"dryrun shell-quotes its argv" dryrun av1

# A malformed uuid must never reach QEMU or be silently regenerated.
cp "$VMMDIR/av1/uuid" "$WORK/uuid.bak"
: > "$VMMDIR/av1/uuid"
refuses 'uuid is missing or malformed' "an empty uuid is refused" dryrun av1
refuses 'uuid is missing or malformed' "start does not repair an empty uuid" start av1
holds "a refused start leaves the empty uuid untouched" \
	test ! -s "$VMMDIR/av1/uuid"
printf 'zzzzzzzz-1111-2222-3333-444444444444\n' > "$VMMDIR/av1/uuid"
refuses 'uuid is missing or malformed' "a non-hex uuid is refused" dryrun av1
printf '12345678-1111-2222-3333-44444444444z\n' > "$VMMDIR/av1/uuid"
refuses 'uuid is missing or malformed' "every uuid group is validated" dryrun av1
cp "$WORK/uuid.bak" "$VMMDIR/av1/uuid"

# Graphics means a display and an absolute pointer. Guest agents and
# clipboard sharing are separate policy and do not belong on this argv.
cfg av1 <<EOF
GRAPHICS=vnc
EOF
outputs 'usb-tablet' "VNC includes an absolute pointer" dryrun av1
omits 'vdagent' "VNC includes no guest agent" dryrun av1
omits 'virtio-serial' "VNC includes no unused agent transport" dryrun av1

cfg av1 <<EOF
GRAPHICS=spice
EOF
outputs 'spice.sock' "SPICE names its display socket" dryrun av1
omits 'clipboard' "SPICE does not enable clipboard sharing" dryrun av1
omits 'spicevmc' "SPICE includes no guest agent" dryrun av1

: > "$VMMDIR/av1/config"

# dryrun launches nothing, so it must work on a host with no qemu at all.
# The README promises it as a config checker anywhere.
BIN=$WORK/dryrun-bin
mkdir -p "$BIN"
for b in uname cat sed tr grep od awk; do
	ln -sf "$(command -v "$b")" "$BIN/$b"
done
holds "dryrun works with no qemu installed" \
	env PATH="$BIN" "$VMM" dryrun av1

# What dryrun prints is what start runs, so the printed argv must boot a
# guest vmm then recognises as that VM. The CDROM path carries a comma,
# which reaches QEMU whole only if the doubling is right.
if command -v "qemu-system-$(uname -m)" >/dev/null 2>&1; then
	: > "$WORK/comma,name.iso"
	"$VMM" create av3 64M >/dev/null 2>&1
	cfg av3 <<EOF
MEM=256
CPUS=1
HWACCEL=no
CDROM="$WORK/comma,name.iso"
BOOT_ORDER=cd
EOF
	"$VMM" dryrun av3 > "$WORK/av3.sh"
	sh "$WORK/av3.sh" > "$WORK/av3.out" 2>&1 &
	pasted=$!
	i=0
	while [ ! -s "$VMMDIR/av3/pid" ] && [ "$i" -lt 15 ]; do
		sleep 1
		i=$((i + 1))
	done
	out=$("$VMM" status av3 2>&1) && got=0 || got=$?
	if [ "$got" = 0 ]; then
		ok "the argv dryrun prints boots a guest vmm knows"
	else
		notok "the argv dryrun prints boots a guest vmm knows" \
			"status exit $got: $out" "$(tail -n 5 "$WORK/av3.out")"
	fi
	exits 0 "and vmm stops what dryrun started" kill av3
	wait "$pasted" 2>/dev/null || :
else
	skip 2 "no qemu for the host architecture"
fi
