Files
libvirt/tests/networkxmlconftest.c
T
Michal Privoznik 7a3f0273b7 networkxmlconftest: Detect unused files
Use newly introduced testutils APIs to detect unused files.
This is pretty much straightforward except for one test case:
hostdev. This test case queries sysfs under the hood and thus is
expected to succeed on Linux and fail everywhere else. Though,
hostdev.expect.xml is thus used on Linux only. Therefore, do not
collect it on non-Linux platforms.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
2026-05-14 14:25:28 +02:00

383 lines
12 KiB
C

#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include "internal.h"
#include "testutils.h"
#include "network_conf.h"
#include "bridge_driver.h"
#define LIBVIRT_BRIDGE_DRIVER_PRIV_H_ALLOW
#include "bridge_driver_priv.h"
#define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
#include "vircommandpriv.h"
#define VIR_FROM_THIS VIR_FROM_NONE
typedef enum {
TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE,
} testCompareNetXML2XMLResult;
struct _testInfo {
const char *name;
unsigned int flags;
testCompareNetXML2XMLResult expectResult;
virNetworkXMLOption *xmlopt; /* borrowed, immutable */
dnsmasqCaps *caps;
virNetworkDef *def;
char *inxml;
char *outxml;
char *outconf;
char *outhostsfile;
GHashTable *existingTestCases;
};
typedef struct _testInfo testInfo;
void testInfoFree(testInfo *info);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(testInfo, testInfoFree);
void testInfoFree(testInfo *info)
{
if (!info)
return;
virNetworkDefFree(info->def);
VIR_FREE(info->inxml);
VIR_FREE(info->outxml);
VIR_FREE(info->outconf);
VIR_FREE(info->outhostsfile);
VIR_FREE(info);
}
static int
testCompareXMLToXMLFiles(const void *data)
{
testInfo *info = (void *) data;
g_autofree char *actual = NULL;
int ret;
testCompareNetXML2XMLResult result = TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS;
g_autoptr(virNetworkDef) def = NULL;
virTestCaseMarkUsed(info->existingTestCases, info->inxml);
if (!(def = virNetworkDefParse(NULL, info->inxml, info->xmlopt, false))) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE)
goto cleanup;
if (networkValidateTests(def) < 0) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE)
goto cleanup;
if (!(actual = virNetworkDefFormat(def, info->xmlopt, info->flags))) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT)
goto cleanup;
virTestCaseMarkUsed(info->existingTestCases, info->outxml);
if (virTestCompareToFile(actual, info->outxml) < 0) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE)
goto cleanup;
cleanup:
if (result == info->expectResult) {
ret = 0;
if (info->expectResult != TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS) {
VIR_TEST_DEBUG("Got expected failure code=%d msg=%s",
result, virGetLastErrorMessage());
} else {
info->def = g_steal_pointer(&def);
}
} else {
ret = -1;
VIR_TEST_DEBUG("Expected result code=%d but received code=%d",
info->expectResult, result);
}
virResetLastError();
return ret;
}
static int
testCompareXMLToConfFiles(const void *data)
{
testInfo *info = (void *) data;
char *confactual = NULL;
g_autofree char *hostsfileactual = NULL;
int ret = -1;
virNetworkDef *def = NULL;
virNetworkObj *obj = NULL;
g_autofree char *pidfile = NULL;
g_autoptr(dnsmasqContext) dctx = NULL;
bool compareFailed = false;
if (!(obj = virNetworkObjNew()))
goto cleanup;
if (!(def = g_steal_pointer(&info->def))) {
/* Previous test wasn't executed. */
virTestCaseMarkUsed(info->existingTestCases, info->inxml);
if (!(def = virNetworkDefParse(NULL, info->inxml, info->xmlopt, false)))
goto cleanup;
if (networkValidateTests(def) < 0) {
virNetworkDefFree(def);
goto cleanup;
}
}
virNetworkObjSetDef(obj, def);
if (!networkNeedsDnsmasq(def)) {
ret = EXIT_AM_SKIP;
goto cleanup;
}
dctx = dnsmasqContextNew(def->name, "/var/lib/libvirt/dnsmasq");
if (dctx == NULL)
goto cleanup;
if (networkDnsmasqConfContents(obj, pidfile, &confactual,
&hostsfileactual, dctx, info->caps) < 0)
goto cleanup;
/* Any changes to this function ^^ should be reflected here too. */
#ifndef __linux__
{
char * tmp;
if (!(tmp = virStringReplace(confactual,
"except-interface=lo0\n",
"except-interface=lo\n")))
goto cleanup;
VIR_FREE(confactual);
confactual = g_steal_pointer(&tmp);
}
#endif
virTestCaseMarkUsed(info->existingTestCases, info->outconf);
if (virTestCompareToFile(confactual, info->outconf) < 0)
compareFailed = true;
if (hostsfileactual) {
virTestCaseMarkUsed(info->existingTestCases, info->outhostsfile);
if (virTestCompareToFile(hostsfileactual, info->outhostsfile) < 0) {
compareFailed = true;
}
} else {
if (virFileExists(info->outhostsfile)) {
VIR_TEST_DEBUG("%s: hostsfile exists but the configuration did not specify any host",
info->outhostsfile);
compareFailed = true;
}
}
if (compareFailed)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(confactual);
virNetworkObjEndAPI(&obj);
return ret;
}
static void
buildCapsCallback(const char *const*args,
const char *const*env G_GNUC_UNUSED,
const char *input G_GNUC_UNUSED,
char **output,
char **error G_GNUC_UNUSED,
int *status,
void *opaque G_GNUC_UNUSED)
{
if (STREQ(args[0], "/usr/sbin/dnsmasq") && STREQ(args[1], "--version")) {
*output = g_strdup("Dnsmasq version 2.67\n");
*status = EXIT_SUCCESS;
} else {
*status = EXIT_FAILURE;
}
}
static dnsmasqCaps *
buildCaps(void)
{
g_autoptr(dnsmasqCaps) caps = NULL;
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, NULL, true, true, buildCapsCallback, NULL);
caps = dnsmasqCapsNewFromBinary();
return g_steal_pointer(&caps);
}
static void
testRun(const char *name,
int *ret,
virNetworkXMLOption *xmlopt,
dnsmasqCaps *caps,
testCompareNetXML2XMLResult expectResult,
GHashTable *existingTestCases,
unsigned int flags)
{
g_autofree char *name_xml2xml = g_strdup_printf("Network XML-2-XML %s", name);
g_autofree char *name_xml2conf = g_strdup_printf("Network XML-2-Conf %s", name);
g_autoptr(testInfo) info = g_new0(testInfo, 1);
info->name = name;
info->flags = flags;
info->expectResult = expectResult;
info->xmlopt = xmlopt;
info->caps = caps;
info->inxml = g_strdup_printf("%s/networkxmlconfdata/%s.xml", abs_srcdir, name);
info->outxml = g_strdup_printf("%s/networkxmlconfdata/%s.expect.xml", abs_srcdir, name);
info->outconf = g_strdup_printf("%s/networkxmlconfdata/%s.conf", abs_srcdir, name);
info->outhostsfile = g_strdup_printf("%s/networkxmlconfdata/%s.hostsfile", abs_srcdir, name);
info->existingTestCases = existingTestCases;
virTestRunLog(ret, name_xml2xml, testCompareXMLToXMLFiles, info);
if (expectResult == TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
virTestRunLog(ret, name_xml2conf, testCompareXMLToConfFiles, info);
}
static bool
testCaseEnumerate(struct dirent *ent)
{
#ifndef __linux__
/* This test case is ran only on Linux. See comment in mymain(). */
if (STREQ(ent->d_name, "hostdev.expect.xml"))
return false;
#endif
return virStringHasSuffix(ent->d_name, ".xml") ||
virStringHasSuffix(ent->d_name, ".conf") ||
virStringHasSuffix(ent->d_name, ".hostsfile");
}
static int
mymain(void)
{
g_autoptr(virNetworkXMLOption) xmlopt = NULL;
g_autoptr(dnsmasqCaps) caps = NULL;
g_autoptr(GHashTable) existingTestCases = NULL;
int ret = 0;
if (virTestEnumerateTestCases(abs_srcdir "/networkxmlconfdata",
testCaseEnumerate,
&existingTestCases) < 0) {
return -1;
}
if (!(xmlopt = networkDnsmasqCreateXMLConf()))
return -1;
if (!(caps = buildCaps()))
return -1;
#define DO_TEST_FULL(name, flags, expectResult) \
testRun(name, &ret, xmlopt, caps, expectResult, existingTestCases, flags)
#define DO_TEST(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
#define DO_TEST_FLAGS(name, flags) \
DO_TEST_FULL(name, flags, TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
#define DO_TEST_PARSE_ERROR(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE)
#define DO_TEST_VALIDATE_ERROR(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE)
DO_TEST("dhcp6-network");
DO_TEST("dhcp6-nat-network");
DO_TEST("dhcp6host-routed-network");
DO_TEST("empty-allow-ipv6");
DO_TEST("isolated-network");
DO_TEST("routed-network");
DO_TEST("routed-network-no-dns");
DO_TEST_PARSE_ERROR("routed-network-no-dns-extra-elements");
DO_TEST("open-network");
DO_TEST_PARSE_ERROR("open-network-with-forward-dev");
DO_TEST("nat-network");
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("netboot-tftp");
DO_TEST("nat-network-dns-txt-record");
DO_TEST("nat-network-dns-srv-record");
DO_TEST("nat-network-dns-srv-records");
DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
DO_TEST("nat-network-dns-forward-plain");
DO_TEST("nat-network-dns-forwarders");
DO_TEST("nat-network-dns-forwarder-no-resolv");
DO_TEST("nat-network-dns-local-domain");
DO_TEST("nat-network-forward-nat-ipv6");
DO_TEST("nat-network-forward-nat-address");
DO_TEST("nat-network-forward-nat-no-address");
DO_TEST("nat-network-name-with-quotes");
DO_TEST("nat-network-mtu");
DO_TEST("8021Qbh-net");
DO_TEST("direct-net");
DO_TEST("host-bridge-net");
DO_TEST("vepa-net");
DO_TEST("bandwidth-network");
DO_TEST("openvswitch-net");
DO_TEST_VALIDATE_ERROR("passthrough-pf");
#ifdef __linux__
DO_TEST("hostdev");
#else
/* Our test runners call networkValidateTests() which for
* <forward mode='hostdev'/> means validating that PCI
* devices are VFs. It's done so by querying sysfs which
* obviously works on Linux only. Thus, expect a validation
* error elsewhere. */
DO_TEST_VALIDATE_ERROR("hostdev");
#endif
DO_TEST_FLAGS("hostdev-pf", VIR_NETWORK_XML_INACTIVE);
DO_TEST_FLAGS("hostdev-pf-driver-model", VIR_NETWORK_XML_INACTIVE);
DO_TEST("ptr-domains-auto");
DO_TEST_VALIDATE_ERROR("passthrough-address-crash");
DO_TEST("nat-network-explicit-flood");
DO_TEST("host-bridge-no-flood");
DO_TEST_PARSE_ERROR("hostdev-duplicate");
DO_TEST_PARSE_ERROR("passthrough-duplicate");
DO_TEST("metadata");
DO_TEST("set-mtu");
DO_TEST("dnsmasq-options");
DO_TEST("leasetime-seconds");
DO_TEST("leasetime-minutes");
DO_TEST("leasetime-hours");
DO_TEST("leasetime-infinite");
DO_TEST("isolated-ports");
if (virTestCheckUnusedTestCases(existingTestCases) < 0)
ret = -1;
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN_PRELOAD(mymain,
VIR_TEST_MOCK("virpci"),
VIR_TEST_MOCK("virdnsmasq"),
VIR_TEST_MOCK("virrandom"))