diff options
author | Jiri Denemark <jdenemar@redhat.com> | 2010-10-07 16:35:17 +0200 |
---|---|---|
committer | Jiri Denemark <jdenemar@redhat.com> | 2010-12-01 14:12:54 +0100 |
commit | 598b9fe4af09975d8da3daef1b37e89f8dfb4768 (patch) | |
tree | cf75ed0223e985ea8ecb2f11cd509094a7e4f20d /tests/cputest.c | |
parent | cpu: Add support for overriding path to CPU map XML file (diff) | |
download | libvirt-598b9fe4af09975d8da3daef1b37e89f8dfb4768.tar.gz libvirt-598b9fe4af09975d8da3daef1b37e89f8dfb4768.tar.bz2 libvirt-598b9fe4af09975d8da3daef1b37e89f8dfb4768.zip |
tests: Add unit tests for internal CPU APIs
Diffstat (limited to 'tests/cputest.c')
-rw-r--r-- | tests/cputest.c | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/tests/cputest.c b/tests/cputest.c new file mode 100644 index 000000000..bd06958a8 --- /dev/null +++ b/tests/cputest.c @@ -0,0 +1,633 @@ +/* + * cputest.c: Test the libvirtd internal CPU APIs + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Jiri Denemark <jdenemar@redhat.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "xml.h" +#include "memory.h" +#include "buf.h" +#include "testutils.h" +#include "cpu_conf.h" +#include "cpu/cpu.h" +#include "cpu/cpu_map.h" + +static const char *progname; +static const char *abs_srcdir; +static const char *abs_top_srcdir; + +#define VIR_FROM_THIS VIR_FROM_CPU +#define MAX_FILE 4096 + +enum compResultShadow { + ERROR = VIR_CPU_COMPARE_ERROR, + INCOMPATIBLE = VIR_CPU_COMPARE_INCOMPATIBLE, + IDENTICAL = VIR_CPU_COMPARE_IDENTICAL, + SUPERSET = VIR_CPU_COMPARE_SUPERSET +}; + +enum cpuTestBoolWithError { + FAIL = -1, + NO = 0, + YES = 1 +}; + +enum api { + API_COMPARE, + API_GUEST_DATA, + API_BASELINE, + API_UPDATE, + API_HAS_FEATURE +}; + +static const char *apis[] = { + "compare", + "guest data", + "baseline", + "update", + "has feature" +}; + +struct data { + const char *arch; + enum api api; + const char *host; + const char *name; + const char **models; + const char *modelsName; + unsigned int nmodels; + const char *preferred; + int result; +}; + + +static virCPUDefPtr +cpuTestLoadXML(const char *arch, const char *name) +{ + char xml[PATH_MAX]; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + virCPUDefPtr cpu = NULL; + + snprintf(xml, PATH_MAX, + "%s/cputestdata/%s-%s.xml", + abs_srcdir, arch, name); + + if (!(doc = virXMLParseFile(xml)) || + !(ctxt = xmlXPathNewContext(doc))) + goto cleanup; + + ctxt->node = xmlDocGetRootElement(doc); + cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO); + +cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return cpu; +} + + +static virCPUDefPtr * +cpuTestLoadMultiXML(const char *arch, + const char *name, + unsigned int *count) +{ + char xml[PATH_MAX]; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr *nodes = NULL; + virCPUDefPtr *cpus = NULL; + int n; + int i; + + snprintf(xml, PATH_MAX, + "%s/cputestdata/%s-%s.xml", + abs_srcdir, arch, name); + + if (!(doc = virXMLParseFile(xml)) || + !(ctxt = xmlXPathNewContext(doc))) + goto error; + + ctxt->node = xmlDocGetRootElement(doc); + + n = virXPathNodeSet("/cpuTest/cpu", ctxt, &nodes); + if (n <= 0 || !(cpus = calloc(n, sizeof(virCPUDefPtr)))) + goto error; + + for (i = 0; i < n; i++) { + ctxt->node = nodes[i]; + cpus[i] = virCPUDefParseXML(nodes[i], ctxt, VIR_CPU_TYPE_HOST); + if (!cpus[i]) + goto error; + } + + *count = n; + +cleanup: + free(nodes); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return cpus; + +error: + if (cpus) { + for (i = 0; i < n; i++) + virCPUDefFree(cpus[i]); + free(cpus); + cpus = NULL; + } + goto cleanup; +} + + +static int +cpuTestCompareXML(const char *arch, + const virCPUDefPtr cpu, + const char *name) +{ + char xml[PATH_MAX]; + char expected[MAX_FILE]; + char *expectedPtr = &(expected[0]); + char *actual = NULL; + int ret = -1; + + snprintf(xml, PATH_MAX, + "%s/cputestdata/%s-%s.xml", + abs_srcdir, arch, name); + + if (virtTestLoadFile(xml, &expectedPtr, MAX_FILE) < 0) + goto cleanup; + + if (!(actual = virCPUDefFormat(cpu, NULL, 0))) + goto cleanup; + + if (STRNEQ(expected, actual)) { + virtTestDifference(stderr, expected, actual); + goto cleanup; + } + + ret = 0; + +cleanup: + free(actual); + return ret; +} + + +static const char * +cpuTestCompResStr(virCPUCompareResult result) +{ + switch (result) { + case VIR_CPU_COMPARE_ERROR: return "ERROR"; + case VIR_CPU_COMPARE_INCOMPATIBLE: return "INCOMPATIBLE"; + case VIR_CPU_COMPARE_IDENTICAL: return "IDENTICAL"; + case VIR_CPU_COMPARE_SUPERSET: return "SUPERSET"; + } + + return "unknown"; +} + + +static const char * +cpuTestBoolWithErrorStr(enum cpuTestBoolWithError result) +{ + switch (result) { + case FAIL: return "FAIL"; + case NO: return "NO"; + case YES: return "YES"; + } + + return "unknown"; +} + + +static int +cpuTestCompare(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDefPtr host = NULL; + virCPUDefPtr cpu = NULL; + virCPUCompareResult result; + + if (!(host = cpuTestLoadXML(data->arch, data->host)) || + !(cpu = cpuTestLoadXML(data->arch, data->name))) + goto cleanup; + + result = cpuCompare(host, cpu); + if (data->result == VIR_CPU_COMPARE_ERROR) + virResetLastError(); + + if (data->result != result) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nExpected result %s, got %s\n", + cpuTestCompResStr(data->result), + cpuTestCompResStr(result)); + /* Pad to line up with test name ... in virTestRun */ + fprintf(stderr, "%74s", "... "); + } + goto cleanup; + } + + ret = 0; + +cleanup: + virCPUDefFree(host); + virCPUDefFree(cpu); + return ret; +} + + +static int +cpuTestGuestData(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDefPtr host = NULL; + virCPUDefPtr cpu = NULL; + virCPUDefPtr guest = NULL; + union cpuData *guestData = NULL; + virCPUCompareResult cmpResult; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *result = NULL; + + if (!(host = cpuTestLoadXML(data->arch, data->host)) || + !(cpu = cpuTestLoadXML(data->arch, data->name))) + goto cleanup; + + cmpResult = cpuGuestData(host, cpu, &guestData); + if (cmpResult == VIR_CPU_COMPARE_ERROR || + cmpResult == VIR_CPU_COMPARE_INCOMPATIBLE) + goto cleanup; + + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(host->arch))) + goto cleanup; + + guest->type = VIR_CPU_TYPE_GUEST; + guest->match = VIR_CPU_MATCH_EXACT; + if (cpuDecode(guest, guestData, data->models, + data->nmodels, data->preferred) < 0) { + if (data->result < 0) { + virResetLastError(); + ret = 0; + } + goto cleanup; + } + + virBufferVSprintf(&buf, "%s+%s", data->host, data->name); + if (data->nmodels) + virBufferVSprintf(&buf, ",%s", data->modelsName); + if (data->preferred) + virBufferVSprintf(&buf, ",%s", data->preferred); + virBufferAddLit(&buf, "-result"); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + goto cleanup; + } + result = virBufferContentAndReset(&buf); + + ret = cpuTestCompareXML(data->arch, guest, result); + +cleanup: + VIR_FREE(result); + if (host) + cpuDataFree(host->arch, guestData); + virCPUDefFree(host); + virCPUDefFree(cpu); + virCPUDefFree(guest); + return ret; +} + + +static int +cpuTestBaseline(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDefPtr *cpus = NULL; + virCPUDefPtr baseline = NULL; + unsigned int ncpus = 0; + char result[PATH_MAX]; + unsigned int i; + + if (!(cpus = cpuTestLoadMultiXML(data->arch, data->name, &ncpus))) + goto cleanup; + + baseline = cpuBaseline(cpus, ncpus, NULL, 0); + if (data->result < 0) { + virResetLastError(); + if (!baseline) + ret = 0; + else if (virTestGetVerbose()) { + fprintf(stderr, "\n%-70s... ", + "cpuBaseline was expected to fail but it succeeded"); + } + goto cleanup; + } + if (!baseline) + goto cleanup; + + snprintf(result, PATH_MAX, "%s-result", data->name); + if (cpuTestCompareXML(data->arch, baseline, result) < 0) + goto cleanup; + + for (i = 0; i < ncpus; i++) { + virCPUCompareResult cmp; + + cmp = cpuCompare(cpus[i], baseline); + if (cmp != VIR_CPU_COMPARE_SUPERSET && + cmp != VIR_CPU_COMPARE_IDENTICAL) { + if (virTestGetVerbose()) { + fprintf(stderr, + "\nbaseline CPU is incompatible with CPU %u\n", i); + fprintf(stderr, "%74s", "... "); + } + ret = -1; + goto cleanup; + } + } + + ret = 0; + +cleanup: + if (cpus) { + for (i = 0; i < ncpus; i++) + virCPUDefFree(cpus[i]); + free(cpus); + } + virCPUDefFree(baseline); + return ret; +} + + +static int +cpuTestUpdate(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDefPtr host = NULL; + virCPUDefPtr cpu = NULL; + char result[PATH_MAX]; + + if (!(host = cpuTestLoadXML(data->arch, data->host)) || + !(cpu = cpuTestLoadXML(data->arch, data->name))) + goto cleanup; + + if (cpuUpdate(cpu, host) < 0) + goto cleanup; + + snprintf(result, PATH_MAX, "%s+%s", data->host, data->name); + ret = cpuTestCompareXML(data->arch, cpu, result); + +cleanup: + virCPUDefFree(host); + virCPUDefFree(cpu); + return ret; +} + + +static int +cpuTestHasFeature(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDefPtr host = NULL; + union cpuData *hostData = NULL; + int result; + + if (!(host = cpuTestLoadXML(data->arch, data->host))) + goto cleanup; + + if (cpuEncode(host->arch, host, NULL, &hostData, + NULL, NULL, NULL, NULL) < 0) + goto cleanup; + + result = cpuHasFeature(host->arch, hostData, data->name); + if (data->result == -1) + virResetLastError(); + + if (data->result != result) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nExpected result %s, got %s\n", + cpuTestBoolWithErrorStr(data->result), + cpuTestBoolWithErrorStr(result)); + /* Pad to line up with test name ... in virTestRun */ + fprintf(stderr, "%74s", "... "); + } + goto cleanup; + } + + ret = 0; + +cleanup: + if (host) + cpuDataFree(host->arch, hostData); + virCPUDefFree(host); + return ret; +} + + +static int (*cpuTest[])(const void *) = { + cpuTestCompare, + cpuTestGuestData, + cpuTestBaseline, + cpuTestUpdate, + cpuTestHasFeature +}; + + +static int +cpuTestRun(const char *name, const struct data *data) +{ + char label[PATH_MAX]; + + snprintf(label, PATH_MAX, "CPU %s(%s): %s", + apis[data->api], data->arch, name); + + free(virtTestLogContentAndReset()); + + if (virtTestRun(label, 1, cpuTest[data->api], data) < 0) { + if (virTestGetDebug()) { + char *log; + if ((log = virtTestLogContentAndReset()) && + strlen(log) > 0) + fprintf(stderr, "\n%s\n", log); + free(log); + } + return -1; + } + + return 0; +} + + +static const char *model486[] = { "486" }; +static const char *nomodel[] = { "nomodel" }; +static const char *models[] = { "qemu64", "core2duo", "Nehalem" }; + +static int +mymain(int argc, char **argv) +{ + int ret = 0; + char cwd[PATH_MAX]; + char map[PATH_MAX]; + + progname = argv[0]; + + if (argc > 1) { + fprintf(stderr, "Usage: %s\n", progname); + return EXIT_FAILURE; + } + + abs_srcdir = getenv("abs_srcdir"); + if (!abs_srcdir) + abs_srcdir = getcwd(cwd, sizeof(cwd)); + + abs_top_srcdir = getenv("abs_top_srcdir"); + if (!abs_top_srcdir) + abs_top_srcdir = ".."; + + snprintf(map, PATH_MAX, "%s/src/cpu/cpu_map.xml", abs_top_srcdir); + if (cpuMapOverride(map) < 0) + return EXIT_FAILURE; + +#define DO_TEST(arch, api, name, host, cpu, \ + models, nmodels, preferred, result) \ + do { \ + struct data data = { \ + arch, api, host, cpu, models, \ + models == NULL ? NULL : #models, \ + nmodels, preferred, result \ + }; \ + if (cpuTestRun(name, &data) < 0) \ + ret = -1; \ + } while (0) + +#define DO_TEST_COMPARE(arch, host, cpu, result) \ + DO_TEST(arch, API_COMPARE, \ + host "/" cpu " (" #result ")", \ + host, cpu, NULL, 0, NULL, result) + +#define DO_TEST_UPDATE(arch, host, cpu, result) \ + do { \ + DO_TEST(arch, API_UPDATE, \ + cpu " on " host, \ + host, cpu, NULL, 0, NULL, 0); \ + DO_TEST_COMPARE(arch, host, host "+" cpu, result); \ + } while (0) + +#define DO_TEST_BASELINE(arch, name, result) \ + DO_TEST(arch, API_BASELINE, name, NULL, "baseline-" name, \ + NULL, 0, NULL, result) + +#define DO_TEST_HASFEATURE(arch, host, feature, result) \ + DO_TEST(arch, API_HAS_FEATURE, \ + host "/" feature " (" #result ")", \ + host, feature, NULL, 0, NULL, result) + +#define DO_TEST_GUESTDATA(arch, host, cpu, models, preferred, result) \ + DO_TEST(arch, API_GUEST_DATA, \ + host "/" cpu " (" #models ", pref=" #preferred ")", \ + host, cpu, models, \ + models == NULL ? 0 : sizeof(models) / sizeof(char *), \ + preferred, result) + + /* host to host comparison */ + DO_TEST_COMPARE("x86", "host", "host", IDENTICAL); + DO_TEST_COMPARE("x86", "host", "host-better", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "host-worse", SUPERSET); + DO_TEST_COMPARE("x86", "host", "host-amd-fake", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "host-incomp-arch", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "host-no-vendor", IDENTICAL); + DO_TEST_COMPARE("x86", "host-no-vendor", "host", INCOMPATIBLE); + + /* guest to host comparison */ + DO_TEST_COMPARE("x86", "host", "bogus-model", ERROR); + DO_TEST_COMPARE("x86", "host", "bogus-feature", ERROR); + DO_TEST_COMPARE("x86", "host", "min", SUPERSET); + DO_TEST_COMPARE("x86", "host", "pentium3", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-forbid", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "exact-forbid-extra", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-disable", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-disable2", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-disable-extra", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-require", SUPERSET); + DO_TEST_COMPARE("x86", "host", "exact-require-extra", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "exact-force", SUPERSET); + DO_TEST_COMPARE("x86", "host", "strict", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host", "strict-full", IDENTICAL); + DO_TEST_COMPARE("x86", "host", "strict-disable", IDENTICAL); + DO_TEST_COMPARE("x86", "host", "strict-force-extra", IDENTICAL); + DO_TEST_COMPARE("x86", "host", "guest", SUPERSET); + DO_TEST_COMPARE("x86", "host", "pentium3-amd", INCOMPATIBLE); + DO_TEST_COMPARE("x86", "host-amd", "pentium3-amd", SUPERSET); + DO_TEST_COMPARE("x86", "host-worse", "nehalem-force", IDENTICAL); + + /* guest updates for migration + * automatically compares host CPU with the result */ + DO_TEST_UPDATE("x86", "host", "min", IDENTICAL); + DO_TEST_UPDATE("x86", "host", "pentium3", IDENTICAL); + DO_TEST_UPDATE("x86", "host", "guest", SUPERSET); + + /* computing baseline CPUs */ + DO_TEST_BASELINE("x86", "incompatible-vendors", -1); + DO_TEST_BASELINE("x86", "no-vendor", 0); + DO_TEST_BASELINE("x86", "some-vendors", 0); + DO_TEST_BASELINE("x86", "1", 0); + DO_TEST_BASELINE("x86", "2", 0); + + /* CPU features */ + DO_TEST_HASFEATURE("x86", "host", "vmx", YES); + DO_TEST_HASFEATURE("x86", "host", "lm", YES); + DO_TEST_HASFEATURE("x86", "host", "sse4.1", YES); + DO_TEST_HASFEATURE("x86", "host", "3dnowext", NO); + DO_TEST_HASFEATURE("x86", "host", "skinit", NO); + DO_TEST_HASFEATURE("x86", "host", "foo", FAIL); + + /* computing guest data and decoding the data into a guest CPU XML */ + DO_TEST_GUESTDATA("x86", "host", "guest", NULL, NULL, 0); + DO_TEST_GUESTDATA("x86", "host-better", "pentium3", NULL, NULL, 0); + DO_TEST_GUESTDATA("x86", "host-better", "pentium3", NULL, "pentium3", 0); + DO_TEST_GUESTDATA("x86", "host-better", "pentium3", NULL, "core2duo", 0); + DO_TEST_GUESTDATA("x86", "host-worse", "guest", NULL, NULL, 0); + DO_TEST_GUESTDATA("x86", "host", "strict-force-extra", NULL, NULL, 0); + DO_TEST_GUESTDATA("x86", "host", "nehalem-force", NULL, NULL, 0); + DO_TEST_GUESTDATA("x86", "host", "guest", model486, NULL, 0); + DO_TEST_GUESTDATA("x86", "host", "guest", models, NULL, 0); + DO_TEST_GUESTDATA("x86", "host", "guest", models, "Penryn", 0); + DO_TEST_GUESTDATA("x86", "host", "guest", models, "qemu64", 0); + DO_TEST_GUESTDATA("x86", "host", "guest", nomodel, NULL, -1); + + return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +VIRT_TEST_MAIN(mymain) |