-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \
-device '{"driver":"pcie-pci-bridge","id":"pci.2","bus":"pci.1","addr":"0x0"}' \
-device '{"driver":"pcie-root-port","port":9,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x1"}' \
--add-fd set=0,fd=1751,opaque=serial0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=serial0-log \
-chardev pty,id=charserial0,logfile=/dev/fdset/0,logappend=on \
-device '{"driver":"pci-serial","chardev":"charserial0","id":"serial0","bus":"pci.2","addr":"0x1"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.0","addr":"0x3"}' \
-chardev '{"id":"charparallel0","backend":{"type":"parallel","data":{"device":"/dev/parport0"}}}' \
-device '{"driver":"isa-parallel","chardev":"charparallel0","id":"parallel0"}' \
--add-fd set=0,fd=1751,opaque=channel0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=channel0-log \
-chardev '{"id":"charchannel0","backend":{"type":"null","data":{"logfile":"/dev/fdset/0","logappend":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"chardev-null"}' \
--add-fd set=1,fd=1751,opaque=channel1-log \
+-add-fd set=1,fd=XXXXXXX,opaque=channel1-log \
-chardev '{"id":"charchannel1","backend":{"type":"vc","data":{"logfile":"/dev/fdset/1","logappend":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"chardev-vc"}' \
-chardev '{"id":"charchannel2","backend":{"type":"pty","data":{}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":3,"chardev":"charchannel2","id":"channel2","name":"chardev-pty"}' \
-chardev '{"id":"charchannel3","backend":{"type":"stdio","data":{}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":4,"chardev":"charchannel3","id":"channel3","name":"chardev-stdio"}' \
--add-fd set=2,fd=1750,opaque=channel4-source \
+-add-fd set=2,fd=XXXXXXX,opaque=channel4-source \
-chardev '{"id":"charchannel4","backend":{"type":"file","data":{"out":"/dev/fdset/2","append":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":5,"chardev":"charchannel4","id":"channel4","name":"chardev-file"}' \
-chardev '{"id":"charchannel5","backend":{"type":"pipe","data":{"device":"/path/to/pipe"}}}' \
-device '{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.0","addr":"0x3"}' \
-chardev '{"id":"charparallel0","backend":{"type":"parallel","data":{"device":"/dev/parport0"}}}' \
-device '{"driver":"isa-parallel","chardev":"charparallel0","id":"parallel0"}' \
--add-fd set=0,fd=1751,opaque=channel0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=channel0-log \
-chardev '{"id":"charchannel0","backend":{"type":"null","data":{"logfile":"/dev/fdset/0","logappend":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"chardev-null"}' \
--add-fd set=1,fd=1751,opaque=channel1-log \
+-add-fd set=1,fd=XXXXXXX,opaque=channel1-log \
-chardev '{"id":"charchannel1","backend":{"type":"vc","data":{"logfile":"/dev/fdset/1","logappend":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"chardev-vc"}' \
-chardev '{"id":"charchannel2","backend":{"type":"pty","data":{}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":3,"chardev":"charchannel2","id":"channel2","name":"chardev-pty"}' \
-chardev '{"id":"charchannel3","backend":{"type":"stdio","data":{}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":4,"chardev":"charchannel3","id":"channel3","name":"chardev-stdio"}' \
--add-fd set=2,fd=1750,opaque=channel4-source \
+-add-fd set=2,fd=XXXXXXX,opaque=channel4-source \
-chardev '{"id":"charchannel4","backend":{"type":"file","data":{"out":"/dev/fdset/2","append":true}}}' \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":5,"chardev":"charchannel4","id":"channel4","name":"chardev-file"}' \
-chardev '{"id":"charchannel5","backend":{"type":"pipe","data":{"device":"/path/to/pipe"}}}' \
-device '{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.0","addr":"0x3"}' \
-chardev parallel,id=charparallel0,path=/dev/parport0 \
-device '{"driver":"isa-parallel","chardev":"charparallel0","id":"parallel0"}' \
--add-fd set=0,fd=1751,opaque=channel0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=channel0-log \
-chardev null,id=charchannel0,logfile=/dev/fdset/0,logappend=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"chardev-null"}' \
--add-fd set=1,fd=1751,opaque=channel1-log \
+-add-fd set=1,fd=XXXXXXX,opaque=channel1-log \
-chardev vc,id=charchannel1,logfile=/dev/fdset/1,logappend=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"chardev-vc"}' \
-chardev pty,id=charchannel2 \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":3,"chardev":"charchannel2","id":"channel2","name":"chardev-pty"}' \
-chardev stdio,id=charchannel3 \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":4,"chardev":"charchannel3","id":"channel3","name":"chardev-stdio"}' \
--add-fd set=2,fd=1750,opaque=channel4-source \
+-add-fd set=2,fd=XXXXXXX,opaque=channel4-source \
-chardev file,id=charchannel4,path=/dev/fdset/2,append=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":5,"chardev":"charchannel4","id":"channel4","name":"chardev-file"}' \
-chardev pipe,id=charchannel5,path=/path/to/pipe \
-device '{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.0","addr":"0x3"}' \
-chardev parallel,id=charparallel0,path=/dev/parport0 \
-device '{"driver":"isa-parallel","chardev":"charparallel0","id":"parallel0"}' \
--add-fd set=0,fd=1751,opaque=channel0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=channel0-log \
-chardev null,id=charchannel0,logfile=/dev/fdset/0,logappend=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"chardev-null"}' \
--add-fd set=1,fd=1751,opaque=channel1-log \
+-add-fd set=1,fd=XXXXXXX,opaque=channel1-log \
-chardev vc,id=charchannel1,logfile=/dev/fdset/1,logappend=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"chardev-vc"}' \
-chardev pty,id=charchannel2 \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":3,"chardev":"charchannel2","id":"channel2","name":"chardev-pty"}' \
-chardev stdio,id=charchannel3 \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":4,"chardev":"charchannel3","id":"channel3","name":"chardev-stdio"}' \
--add-fd set=2,fd=1750,opaque=channel4-source \
+-add-fd set=2,fd=XXXXXXX,opaque=channel4-source \
-chardev file,id=charchannel4,path=/dev/fdset/2,append=on \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":5,"chardev":"charchannel4","id":"channel4","name":"chardev-file"}' \
-chardev pipe,id=charchannel5,path=/path/to/pipe \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=1750,opaque=serial0-source \
+-add-fd set=0,fd=XXXXXXX,opaque=serial0-source \
-chardev file,id=charserial0,path=/dev/fdset/0,append=on \
-device '{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0}' \
-chardev socket,id=charserial1,path=/tmp/serial.sock \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=5,fd=200,opaque=libvirt-6-storage0 \
--add-fd set=5,fd=205,opaque=libvirt-6-storage1 \
+-add-fd set=5,fd=XXXXXXX,opaque=libvirt-6-storage0 \
+-add-fd set=5,fd=XXXXXXX,opaque=libvirt-6-storage1 \
-blockdev '{"driver":"file","filename":"/dev/fdset/5","node-name":"libvirt-6-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-6-format","read-only":false,"driver":"qcow2","file":"libvirt-6-storage"}' \
-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-6-format","id":"virtio-disk4","bootindex":1}' \
--add-fd set=4,fd=209,opaque=libvirt-5-storage0 \
+-add-fd set=4,fd=XXXXXXX,opaque=libvirt-5-storage0 \
-blockdev '{"driver":"file","filename":"/dev/fdset/4","node-name":"libvirt-5-storage","read-only":false,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-5-format","read-only":true,"driver":"raw","file":"libvirt-5-storage"}' \
--add-fd set=3,fd=247,opaque=libvirt-4-storage0 \
--add-fd set=3,fd=248,opaque=libvirt-4-storage1 \
+-add-fd set=3,fd=XXXXXXX,opaque=libvirt-4-storage0 \
+-add-fd set=3,fd=XXXXXXX,opaque=libvirt-4-storage1 \
-blockdev '{"driver":"file","filename":"/dev/fdset/3","node-name":"libvirt-4-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-4-format","read-only":true,"driver":"qcow2","file":"libvirt-4-storage","backing":"libvirt-5-format"}' \
--add-fd set=2,fd=204,opaque=libvirt-3-storage0 \
+-add-fd set=2,fd=XXXXXXX,opaque=libvirt-3-storage0 \
-blockdev '{"driver":"file","filename":"/dev/fdset/2","node-name":"libvirt-3-storage","read-only":false,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-3-format","read-only":false,"driver":"qcow2","file":"libvirt-3-storage","backing":"libvirt-4-format"}' \
-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-3-format","id":"virtio-disk5"}' \
--add-fd set=1,fd=207,opaque=libvirt-2-storage0 \
+-add-fd set=1,fd=XXXXXXX,opaque=libvirt-2-storage0 \
-blockdev '{"driver":"file","filename":"/dev/fdset/1","node-name":"libvirt-2-storage","read-only":true}' \
-device '{"driver":"ide-cd","bus":"ide.0","unit":0,"drive":"libvirt-2-storage","id":"ide0-0-0"}' \
--add-fd set=0,fd=208,opaque=libvirt-1-storage0 \
+-add-fd set=0,fd=XXXXXXX,opaque=libvirt-1-storage0 \
-blockdev '{"driver":"file","filename":"/dev/fdset/0","node-name":"libvirt-1-storage","read-only":false,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
-device '{"driver":"ide-cd","bus":"ide.0","unit":1,"drive":"libvirt-1-format","id":"ide0-0-1"}' \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=201,opaque=libvirt-1-storage-vdpa \
+-add-fd set=0,fd=XXXXXXX,opaque=libvirt-1-storage-vdpa \
-blockdev '{"driver":"virtio-blk-vhost-vdpa","path":"/dev/fdset/0","node-name":"libvirt-1-storage","read-only":false,"cache":{"direct":true,"no-flush":false}}' \
-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-1-storage","id":"virtio-disk0","bootindex":1,"write-cache":"on"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"ccid-card-emulated","backend":"certificates","cert1":"cert1,foo","cert2":"cert2","cert3":"cert3","db":"/etc/pki/nssdb,foo","id":"smartcard0","bus":"ccid0.0"}' \
-chardev serial,id=charserial0,path=/dev/ttyS2,,foo \
-device '{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":1}' \
--add-fd set=0,fd=1750,opaque=serial1-source \
+-add-fd set=0,fd=XXXXXXX,opaque=serial1-source \
-chardev file,id=charserial1,path=/dev/fdset/0,append=on \
-device '{"driver":"isa-serial","chardev":"charserial1","id":"serial1","index":0}' \
-chardev pipe,id=charchannel0,path=/tmp/guestfwd,,foo \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=1732,opaque=net0-vdpa \
+-add-fd set=0,fd=XXXXXXX,opaque=net0-vdpa \
-netdev '{"type":"vhost-vdpa","vhostdev":"/dev/fdset/0","queues":2,"id":"hostnet0"}' \
-device '{"driver":"virtio-net-pci","mq":true,"vectors":6,"netdev":"hostnet0","id":"net0","mac":"52:54:00:95:db:c0","bus":"pci.0","addr":"0x2"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=1732,opaque=net0-vdpa \
+-add-fd set=0,fd=XXXXXXX,opaque=net0-vdpa \
-netdev '{"type":"vhost-vdpa","vhostdev":"/dev/fdset/0","id":"hostnet0"}' \
-device '{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:95:db:c0","bus":"pci.0","addr":"0x2"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","read-only":false}' \
-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-storage","id":"ide0-0-0","bootindex":1}' \
--add-fd set=0,fd=1750,opaque=serial0-source \
+-add-fd set=0,fd=XXXXXXX,opaque=serial0-source \
-chardev file,id=charserial0,path=/dev/fdset/0,append=on \
-device '{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","read-only":false}' \
-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-storage","id":"ide0-0-0","bootindex":1}' \
--add-fd set=0,fd=1750,opaque=serial0-source \
--add-fd set=1,fd=1751,opaque=serial0-log \
+-add-fd set=0,fd=XXXXXXX,opaque=serial0-source \
+-add-fd set=1,fd=XXXXXXX,opaque=serial0-log \
-chardev file,id=charserial0,path=/dev/fdset/0,append=on,logfile=/dev/fdset/1,logappend=on \
-device '{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-storage","id":"ide0-0-0","bootindex":1}' \
-chardev pty,id=charserial0 \
-device '{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0}' \
--add-fd set=0,fd=1750,opaque=serial1-source \
+-add-fd set=0,fd=XXXXXXX,opaque=serial1-source \
-chardev file,id=charserial1,path=/dev/fdset/0,append=on \
-device '{"driver":"isa-serial","chardev":"charserial1","id":"serial1","index":1}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-no-shutdown \
-boot menu=on,strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=1730,opaque=tpm0-tpm \
--add-fd set=1,fd=1731,opaque=tpm0-cancel \
+-add-fd set=0,fd=XXXXXXX,opaque=tpm0-tpm \
+-add-fd set=1,fd=XXXXXXX,opaque=tpm0-cancel \
-tpmdev passthrough,id=tpm-tpm0,path=/dev/fdset/0,cancel-path=/dev/fdset/1 \
-device '{"driver":"tpm-crb","tpmdev":"tpm-tpm0","id":"tpm0"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-no-shutdown \
-boot menu=on,strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
--add-fd set=0,fd=1730,opaque=tpm0-tpm \
--add-fd set=1,fd=1731,opaque=tpm0-cancel \
+-add-fd set=0,fd=XXXXXXX,opaque=tpm0-tpm \
+-add-fd set=1,fd=XXXXXXX,opaque=tpm0-cancel \
-tpmdev passthrough,id=tpm-tpm0,path=/dev/fdset/0,cancel-path=/dev/fdset/1 \
-device '{"driver":"tpm-tis","tpmdev":"tpm-tpm0","id":"tpm0"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
static virQEMUDriver driver;
+/* this test case uses virTestDummyFDContextNew so we want to mock dup() to
+ * track FD hints properly */
+#include "virmock.h"
+VIR_TEST_MAKE_DUMMY_FD_INSTALL_DUP_MOCK
static void
testQemuPrepareHostdevPCI(virDomainHostdevDef *hostdev)
}
+struct testCompareXMLToArgvStabilizeOneFindJSONObjectIterData {
+ const char *field;
+ bool recurse;
+ virJSONValue *ret;
+};
+
+static int
+testCompareXMLToArgvStabilizeOneFindJSONObjectIter(const char *key,
+ virJSONValue *value,
+ void *opaque)
+{
+ struct testCompareXMLToArgvStabilizeOneFindJSONObjectIterData *data = opaque;
+
+ /* negative value breaks iteration and returns specific return value */
+ if (STREQ(data->field, key))
+ return -1;
+
+ if (!data->recurse)
+ return 0;
+
+ if (!virJSONValueIsObject(value))
+ return 0;
+
+ if (virJSONValueObjectForeachKeyValue(value,
+ testCompareXMLToArgvStabilizeOneFindJSONObjectIter,
+ data) == -2) {
+ /* -2 means that the callback found something; now if it was the above
+ * call that found the key we need to fill the pointer to what we've
+ * called it with */
+ if (data->ret == NULL)
+ data->ret = value;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * testCompareXMLToArgvStabilizeOne:
+ * @arg: argument to modify (may be replaced by other memory)
+ * @field: field to replace
+ * @substitutions: optional hash-table to look up value of @field and replace
+ * it with the stored string
+ *
+ * Takes one qemu argument @arg and replaces the value of '@field' by a stable
+ * string, either 'XXXXXXX' if @substitutions is NULL or the value of @field
+ * wasn't found in @substitutions, or with the string from @substitutions if
+ * found.
+ *
+ * Works both on JSON and normal arguments.
+ */
+static void
+testCompareXMLToArgvStabilizeOne(char **arg,
+ const char *field,
+ GHashTable *substitutions,
+ bool recurseJSON)
+{
+ g_autofree char *oldarg = g_steal_pointer(arg);
+
+ if (*oldarg == '{') {
+ g_autoptr(virJSONValue) j = virJSONValueFromString(oldarg);
+ struct testCompareXMLToArgvStabilizeOneFindJSONObjectIterData data = {
+ .field = field,
+ .recurse = recurseJSON,
+ };
+ const char *curstr = NULL;
+
+ /* Any JSON args will be validated so this ought not to happen */
+ if (!j) {
+ *arg = g_steal_pointer(&oldarg);
+ return;
+ }
+
+ if (virJSONValueObjectForeachKeyValue(j,
+ testCompareXMLToArgvStabilizeOneFindJSONObjectIter,
+ &data) == -2) {
+ /* -2 means that the callback found something; now if it was the above
+ * call that found the key we need to fill the pointer to what we've
+ * called it with */
+ if (data.ret == NULL)
+ data.ret = j;
+ }
+
+ if (data.ret) {
+ virJSONValue *cur = virJSONValueObjectGet(data.ret, field);
+
+ if (cur) {
+ switch (virJSONValueGetType(cur)) {
+ case VIR_JSON_TYPE_STRING:
+ curstr = virJSONValueGetString(cur);
+ break;
+
+ case VIR_JSON_TYPE_NUMBER:
+ curstr = virJSONValueGetNumberString(cur);
+ break;
+
+ case VIR_JSON_TYPE_OBJECT:
+ case VIR_JSON_TYPE_ARRAY:
+ case VIR_JSON_TYPE_BOOLEAN:
+ case VIR_JSON_TYPE_NULL:
+ break;
+ }
+ }
+ }
+
+ if (curstr) {
+ g_autoptr(virJSONValue) newval = NULL;
+ const char *subst = NULL;
+
+ if (substitutions)
+ subst = g_hash_table_lookup(substitutions, curstr);
+
+ if (!subst)
+ subst = "XXXXXXX";
+
+ newval = virJSONValueNewString(g_strdup(subst));
+
+ ignore_value(virJSONValueObjectReplaceKey(data.ret, field, &newval));
+
+ *arg = virJSONValueToString(j, false);
+ return;
+ } else {
+ *arg = g_steal_pointer(&oldarg);
+ return;
+ }
+ } else {
+ g_autofree char *fieldmatch = g_strdup_printf("%s=", field);
+ char *match;
+ char *val;
+ char *rest;
+ const char *subst = NULL;
+
+ if (!(match = strstr(oldarg, fieldmatch))) {
+ *arg = g_steal_pointer(&oldarg);
+ return;
+ }
+
+ if ((rest = strchr(match, ',')))
+ *rest = '\0';
+
+ *match = '\0';
+
+ val = match + strlen(fieldmatch);
+
+ if (substitutions)
+ subst = g_hash_table_lookup(substitutions, val);
+
+ if (!subst)
+ subst = "XXXXXXX";
+
+ if (rest)
+ *rest = ',';
+
+ *arg = g_strdup_printf("%s%s%s%s", oldarg, fieldmatch, subst, NULLSTR_EMPTY(rest));
+ }
+}
+
+
+static void
+testCompareXMLToArgvStabilizeArgs(virCommand *cmd,
+ GHashTable *fdsubsts)
+{
+ char **args;
+ size_t nargs;
+ size_t a;
+
+ virCommandArgListAccess(cmd, &args, &nargs);
+
+ for (a = 0; a < nargs; a++) {
+ /* Any following replacements want also an argument for an option so
+ * so we guarantee that there's at least one extra arg */
+ if (a + 1 >= nargs)
+ break;
+
+ if (STREQ(args[a], "-add-fd")) {
+ testCompareXMLToArgvStabilizeOne(&args[a + 1], "fd", fdsubsts, false);
+
+ a++;
+ }
+ }
+}
+
+
static int
testInfoCheckDuplicate(testQemuInfo *info)
{
if (testCompareXMLToArgvValidateSchema(cmd, info) < 0)
goto cleanup;
+ testCompareXMLToArgvStabilizeArgs(cmd, info->fdsubsts);
+
if (virCommandToStringBuf(cmd, &actualBuf, true, false) < 0)
goto cleanup;
g_autofree char *name_argv = g_strdup_printf("QEMU XML def -> ARGV %s%s", name, suffix);
g_autoptr(testQemuInfo) info = g_new0(testQemuInfo, 1);
va_list ap;
+ g_autoptr(virTestDummyFDContext) fdsubsts = virTestDummyFDContextNew();
info->name = name;
info->suffix = suffix;
info->conf = testConf;
+ info->fdsubsts = fdsubsts;
va_start(ap, testConf);
testQemuInfoSetArgs(info, ap);
struct testQemuArgs args;
struct testQemuConf *conf;
+
+ GHashTable *fdsubsts;
};
typedef struct _testQemuInfo testQemuInfo;