Files
libvirt/tests/testutils.c
T

1222 lines
33 KiB
C
Raw Normal View History

2006-05-09 15:35:46 +00:00
/*
* testutils.c: basic test utils
2006-05-09 15:35:46 +00:00
*
* Copyright (C) 2005-2015 Red Hat, Inc.
2006-05-09 15:35:46 +00:00
*
2012-07-27 17:39:53 +08:00
* 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, see
2012-07-27 17:39:53 +08:00
* <http://www.gnu.org/licenses/>.
2006-05-09 15:35:46 +00:00
*/
#include <config.h>
2016-05-20 16:27:00 +02:00
#include <stdarg.h>
2006-05-09 15:35:46 +00:00
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
2006-05-09 15:35:46 +00:00
#include "testutils.h"
#include "internal.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
#include "virerror.h"
2012-12-04 12:04:07 +00:00
#include "virbuffer.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "virstring.h"
2008-05-29 15:21:45 +00:00
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("tests.testutils");
2016-06-17 14:09:30 +02:00
#include "virbitmap.h"
2011-07-19 12:32:58 -06:00
#include "virfile.h"
static unsigned int testDebug = -1;
2009-11-30 19:01:31 +00:00
static unsigned int testVerbose = -1;
static unsigned int testExpensive = -1;
static unsigned int testRegenerate = -1;
2013-09-23 14:21:52 +01:00
2014-10-28 12:38:04 -06:00
static size_t testCounter;
2021-03-11 08:16:13 +01:00
static virBitmap *testBitmap;
static virBitmap *failedTests;
2008-05-29 15:21:45 +00:00
static virArch virTestHostArch = VIR_ARCH_X86_64;
virArch
virArchFromHost(void)
{
return virTestHostArch;
}
void
virTestSetHostArch(virArch arch)
{
virTestHostArch = arch;
}
static int virTestUseTerminalColors(void)
{
return isatty(STDOUT_FILENO);
}
2015-04-23 14:02:57 -04:00
static unsigned int
virTestGetFlag(const char *name)
{
char *flagStr;
unsigned int flag;
if ((flagStr = getenv(name)) == NULL)
return 0;
if (virStrToLong_ui(flagStr, NULL, 10, &flag) < 0)
return 0;
return flag;
}
2013-09-23 14:21:52 +01:00
/**
* virTestPropagateLibvirtError:
*
* In cases when a libvirt utility function which reports libvirt errors is
* used in the test suite outside of the virTestRun call and the failure of such
* a function would cause an test failure the error message reported by that
* function will not be propagated to the user as the error callback is not
* invoked.
*
* In cases when the error message may be beneficial in debugging this helper
* provides means to dispatch the errors including invocation of the error
* callback.
*/
void
virTestPropagateLibvirtError(void)
{
if (virGetLastErrorCode() == VIR_ERR_OK)
return;
if (virTestGetVerbose() || virTestGetDebug())
virDispatchError(NULL);
}
/*
2013-09-20 19:13:35 +01:00
* Runs test
*
* returns: -1 = error, 0 = success
2006-05-09 15:35:46 +00:00
*/
int
2016-05-26 17:01:50 +02:00
virTestRun(const char *title,
int (*body)(const void *data), const void *data)
2006-05-09 15:35:46 +00:00
{
int ret = 0;
/* Some test are fragile about environ settings. If that's
* the case, don't poison it. */
if (getenv("VIR_TEST_MOCK_PROGNAME"))
2019-12-18 17:16:19 +00:00
g_setenv("VIR_TEST_MOCK_TESTNAME", title, TRUE);
if (testCounter == 0 && !virTestGetVerbose())
fprintf(stderr, " ");
2008-05-29 15:21:45 +00:00
testCounter++;
2013-07-18 15:02:19 +01:00
/* Skip tests if out of range */
2016-06-17 14:09:30 +02:00
if (testBitmap && !virBitmapIsBitSet(testBitmap, testCounter))
2013-07-18 15:02:19 +01:00
return 0;
2013-09-20 19:11:02 +01:00
if (virTestGetVerbose())
fprintf(stderr, "%2zu) %-65s ... ", testCounter, title);
2013-09-20 19:13:35 +01:00
virResetLastError();
ret = body(data);
virTestPropagateLibvirtError();
2013-09-20 19:13:35 +01:00
2013-09-20 19:11:02 +01:00
if (virTestGetVerbose()) {
2013-09-20 19:13:35 +01:00
if (ret == 0)
if (virTestUseTerminalColors())
fprintf(stderr, "\e[32mOK\e[0m\n"); /* green */
else
fprintf(stderr, "OK\n");
2013-09-20 19:11:02 +01:00
else if (ret == EXIT_AM_SKIP)
if (virTestUseTerminalColors())
fprintf(stderr, "\e[34m\e[1mSKIP\e[0m\n"); /* bold blue */
else
fprintf(stderr, "SKIP\n");
2013-09-20 19:11:02 +01:00
else
if (virTestUseTerminalColors())
fprintf(stderr, "\e[31m\e[1mFAILED\e[0m\n"); /* bold red */
else
fprintf(stderr, "FAILED\n");
2013-09-20 19:11:02 +01:00
} else {
if (testCounter != 1 &&
!((testCounter-1) % 40)) {
fprintf(stderr, " %-3zu\n", (testCounter-1));
fprintf(stderr, " ");
2013-09-23 14:21:52 +01:00
}
2013-09-20 19:11:02 +01:00
if (ret == 0)
2009-11-30 19:01:31 +00:00
fprintf(stderr, ".");
2013-09-20 19:11:02 +01:00
else if (ret == EXIT_AM_SKIP)
fprintf(stderr, "_");
else
fprintf(stderr, "!");
2008-05-29 15:21:45 +00:00
}
if (ret != 0 && ret != EXIT_AM_SKIP)
2021-12-06 15:55:46 +01:00
virBitmapSetBitExpand(failedTests, testCounter);
2019-12-18 17:16:19 +00:00
g_unsetenv("VIR_TEST_MOCK_TESTNAME");
return ret;
2006-05-09 15:35:46 +00:00
}
/*
* A wrapper for virTestRun that resets the log content before each run
* and sets ret to -1 on failure. On success, ret is untouched.
*/
void
virTestRunLog(int *ret,
const char *title,
int (*body)(const void *data),
const void *data)
{
int rc;
g_free(virTestLogContentAndReset());
rc = virTestRun(title, body, data);
if (rc >= 0)
return;
*ret = -1;
if (virTestGetDebug()) {
g_autofree char *log = virTestLogContentAndReset();
if (strlen(log) > 0)
VIR_TEST_DEBUG("\n%s", log);
}
}
/**
* virTestLoadFile:
* @file: name of the file to load
* @buf: buffer to load the file into
*
* Allocates @buf to the size of FILE. Reads FILE into buffer BUF.
* Upon any failure, error is printed to stderr and -1 is returned. 'errno' is
* not preserved. On success 0 is returned. Caller is responsible for freeing
* @buf.
*/
2011-04-25 00:25:10 +02:00
int
2016-05-26 17:01:52 +02:00
virTestLoadFile(const char *file, char **buf)
2011-04-25 00:25:10 +02:00
{
g_autoptr(FILE) fp = fopen(file, "r");
struct stat st;
2011-04-25 00:25:10 +02:00
char *tmp;
int len, tmplen, buflen;
2009-02-02 20:35:14 +00:00
if (!fp) {
fprintf(stderr, "%s: failed to open: %s\n", file, g_strerror(errno));
return -1;
2009-02-02 20:35:14 +00:00
}
if (fstat(fileno(fp), &st) < 0) {
fprintf(stderr, "%s: failed to fstat: %s\n", file, g_strerror(errno));
return -1;
}
2011-04-25 00:25:10 +02:00
tmplen = buflen = st.st_size + 1;
2020-09-23 01:04:17 +02:00
*buf = g_new0(char, buflen);
2011-04-25 00:25:10 +02:00
tmp = *buf;
(*buf)[0] = '\0';
if (st.st_size) {
/* read the file line by line */
while (fgets(tmp, tmplen, fp) != NULL) {
len = strlen(tmp);
2011-04-25 00:25:10 +02:00
/* stop on an empty line */
if (len == 0)
break;
/* remove trailing backslash-newline pair */
if (len >= 2 && tmp[len-2] == '\\' && tmp[len-1] == '\n') {
len -= 2;
tmp[len] = '\0';
}
/* advance the temporary buffer pointer */
tmp += len;
tmplen -= len;
}
if (ferror(fp)) {
fprintf(stderr, "%s: read failed: %s\n", file, g_strerror(errno));
2012-02-02 16:16:43 -07:00
VIR_FREE(*buf);
return -1;
}
}
return 0;
}
static char *
virTestLoadFileGetPath(const char *p,
va_list ap)
{
2020-07-02 19:35:41 -04:00
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
char *path = NULL;
virBufferAddLit(&buf, abs_srcdir "/");
if (p) {
virBufferAdd(&buf, p, -1);
virBufferStrcatVArgs(&buf, ap);
}
if (!(path = virBufferContentAndReset(&buf)))
VIR_TEST_VERBOSE("failed to format file path");
return path;
}
/**
* virTestLoadFilePath:
* @...: file name components terminated with a NULL
*
* Constructs the test file path from variable arguments and loads the file.
* 'abs_srcdir' is automatically prepended.
*/
char *
virTestLoadFilePath(const char *p, ...)
{
2020-02-09 01:47:22 +01:00
g_autofree char *path = NULL;
char *ret = NULL;
va_list ap;
va_start(ap, p);
if (!(path = virTestLoadFileGetPath(p, ap)))
goto cleanup;
ignore_value(virTestLoadFile(path, &ret));
cleanup:
va_end(ap);
return ret;
}
/**
* virTestLoadFileJSON:
* @...: name components terminated with a NULL
*
* Constructs the test file path from variable arguments and loads and parses
* the JSON file. 'abs_srcdir' is automatically prepended to the path.
*/
2021-03-11 08:16:13 +01:00
virJSONValue *
virTestLoadFileJSON(const char *p, ...)
{
2021-03-11 08:16:13 +01:00
virJSONValue *ret = NULL;
2020-02-09 01:47:22 +01:00
g_autofree char *jsonstr = NULL;
g_autofree char *path = NULL;
va_list ap;
va_start(ap, p);
if (!(path = virTestLoadFileGetPath(p, ap)))
goto cleanup;
if (virFileReadAll(path, INT_MAX, &jsonstr) < 0)
goto cleanup;
if (!(ret = virJSONValueFromString(jsonstr)))
VIR_TEST_VERBOSE("failed to parse json from file '%s'", path);
cleanup:
va_end(ap);
return ret;
}
/**
2015-04-28 17:43:12 +02:00
* @param stream: output stream to write differences to
* @param expect: expected output text
* @param expectName: name designator of the expected text
* @param actual: actual output text
* @param actualName: name designator of the actual text
* @param regenerate: enable or disable regenerate functionality
* @param rewrap: enable or disable rewrapping when regenerating
*
* Display expected and actual output text, trimmed to first and last
* characters at which differences occur. Displays names of the text strings if
* non-NULL.
*/
static int
virTestDifferenceFullInternal(FILE *stream,
const char *expect,
const char *expectName,
const char *actual,
const char *actualName,
bool regenerate)
{
const char *expectStart;
const char *expectEnd;
const char *actualStart;
const char *actualEnd;
if (!expect)
expect = "";
if (!actual)
actual = "";
expectStart = expect;
expectEnd = expect + (strlen(expect)-1);
actualStart = actual;
actualEnd = actual + (strlen(actual)-1);
if (expectName && regenerate && (virTestGetRegenerate() > 0)) {
if (virFileWriteStr(expectName, actual, 0666) < 0) {
virDispatchError(NULL);
return -1;
}
}
2009-11-30 19:01:31 +00:00
if (!virTestGetDebug())
2008-05-29 15:21:45 +00:00
return 0;
2009-11-30 19:01:31 +00:00
if (virTestGetDebug() < 2) {
/* Skip to first character where they differ */
while (*expectStart && *actualStart &&
*actualStart == *expectStart) {
actualStart++;
expectStart++;
}
/* Work backwards to last character where they differ */
while (actualEnd > actualStart &&
expectEnd > expectStart &&
*actualEnd == *expectEnd) {
actualEnd--;
expectEnd--;
}
}
/* Show the trimmed differences */
if (expectName)
fprintf(stream, "\nIn '%s':", expectName);
2011-09-17 07:57:26 -06:00
fprintf(stream, "\nOffset %d\nExpect [", (int) (expectStart - expect));
if ((expectEnd - expectStart + 1) &&
fwrite(expectStart, (expectEnd-expectStart+1), 1, stream) != 1)
return -1;
fprintf(stream, "]\n");
if (actualName)
fprintf(stream, "In '%s':\n", actualName);
fprintf(stream, "Actual [");
if ((actualEnd - actualStart + 1) &&
fwrite(actualStart, (actualEnd-actualStart+1), 1, stream) != 1)
return -1;
fprintf(stream, "]\n");
/* Pad to line up with test name ... in virTestRun */
fprintf(stream, " ... ");
return 0;
}
2008-05-29 15:21:45 +00:00
/**
* @param stream: output stream to write differences to
* @param expect: expected output text
* @param expectName: name designator of the expected text
* @param actual: actual output text
* @param actualName: name designator of the actual text
*
* Display expected and actual output text, trimmed to first and last
* characters at which differences occur. Displays names of the text strings if
* non-NULL. If VIR_TEST_REGENERATE_OUTPUT is used, this function will
* regenerate the expected file.
*/
int
virTestDifferenceFull(FILE *stream,
const char *expect,
const char *expectName,
const char *actual,
const char *actualName)
{
return virTestDifferenceFullInternal(stream, expect, expectName,
actual, actualName, true);
}
/**
* @param stream: output stream to write differences to
* @param expect: expected output text
* @param expectName: name designator of the expected text
* @param actual: actual output text
* @param actualName: name designator of the actual text
*
* Display expected and actual output text, trimmed to first and last
* characters at which differences occur. Displays names of the text strings if
* non-NULL. If VIR_TEST_REGENERATE_OUTPUT is used, this function will not
* regenerate the expected file.
*/
int
virTestDifferenceFullNoRegenerate(FILE *stream,
const char *expect,
const char *expectName,
const char *actual,
const char *actualName)
{
return virTestDifferenceFullInternal(stream, expect, expectName,
actual, actualName, false);
}
/**
2015-04-28 17:43:12 +02:00
* @param stream: output stream to write differences to
* @param expect: expected output text
* @param actual: actual output text
*
* Display expected and actual output text, trimmed to
* first and last characters at which differences occur
*/
int
virTestDifference(FILE *stream,
const char *expect,
const char *actual)
{
return virTestDifferenceFullNoRegenerate(stream,
expect, NULL,
actual, NULL);
}
/**
2015-04-28 17:43:12 +02:00
* @param stream: output stream to write differences to
* @param expect: expected output text
* @param actual: actual output text
*
* Display expected and actual output text, trimmed to
* first and last characters at which differences occur
*/
int virTestDifferenceBin(FILE *stream,
const char *expect,
const char *actual,
size_t length)
{
size_t start = 0, end = length;
ssize_t i;
if (!virTestGetDebug())
return 0;
if (virTestGetDebug() < 2) {
/* Skip to first character where they differ */
2013-05-21 15:53:48 +08:00
for (i = 0; i < length; i++) {
if (expect[i] != actual[i]) {
start = i;
break;
}
}
/* Work backwards to last character where they differ */
2013-05-21 15:53:48 +08:00
for (i = (length -1); i >= 0; i--) {
if (expect[i] != actual[i]) {
end = i;
break;
}
}
}
2011-06-24 08:01:10 -06:00
/* Round to nearest boundary of 4, except that last word can be short */
start -= (start % 4);
end += 4 - (end % 4);
if (end >= length)
end = length - 1;
/* Show the trimmed differences */
fprintf(stream, "\nExpect [ Region %d-%d", (int)start, (int)end);
2013-05-21 15:53:48 +08:00
for (i = start; i < end; i++) {
if ((i % 4) == 0)
fprintf(stream, "\n ");
fprintf(stream, "0x%02x, ", ((int)expect[i])&0xff);
}
fprintf(stream, "]\n");
fprintf(stream, "Actual [ Region %d-%d", (int)start, (int)end);
2013-05-21 15:53:48 +08:00
for (i = start; i < end; i++) {
if ((i % 4) == 0)
fprintf(stream, "\n ");
fprintf(stream, "0x%02x, ", ((int)actual[i])&0xff);
}
fprintf(stream, "]\n");
/* Pad to line up with test name ... in virTestRun */
fprintf(stream, " ... ");
return 0;
}
2015-04-23 11:14:26 -04:00
/*
* @param actual: String input content
* @param filename: File to compare @actual against
* @param unwrap: Remove '\\\n' sequences from file content before comparison
*
* If @actual is NULL, it's treated as an empty string.
2015-04-23 11:14:26 -04:00
*/
int
virTestCompareToFileFull(const char *actual,
const char *filename,
bool unwrap)
2015-04-23 11:14:26 -04:00
{
2020-02-09 01:47:22 +01:00
g_autofree char *filecontent = NULL;
g_autofree char *fixedcontent = NULL;
const char *cmpcontent = actual;
size_t cmpcontentLen;
2015-04-23 11:14:26 -04:00
if (!cmpcontent)
cmpcontent = "";
if (unwrap) {
if (virTestLoadFile(filename, &filecontent) < 0 && !virTestGetRegenerate())
return -1;
} else {
if (virFileReadAll(filename, INT_MAX, &filecontent) < 0 && !virTestGetRegenerate())
return -1;
}
2015-04-23 11:14:26 -04:00
cmpcontentLen = strlen(cmpcontent);
if (cmpcontentLen > 0 &&
cmpcontent[cmpcontentLen - 1] != '\n') {
fixedcontent = g_strdup_printf("%s\n", cmpcontent);
cmpcontent = fixedcontent;
2015-04-23 11:14:26 -04:00
}
2016-09-19 13:44:21 -04:00
if (STRNEQ_NULLABLE(cmpcontent, filecontent)) {
virTestDifferenceFullInternal(stderr, filecontent, filename,
cmpcontent, NULL, true);
2020-02-09 01:57:06 +01:00
return -1;
2015-04-23 11:14:26 -04:00
}
2020-02-09 01:57:06 +01:00
return 0;
2015-04-23 11:14:26 -04:00
}
/*
* @param actual: String input content
* @param filename: File to compare @actual against
*
* If @actual is NULL, it's treated as an empty string.
*/
int
virTestCompareToFile(const char *actual,
const char *filename)
{
return virTestCompareToFileFull(actual, filename, true);
}
2017-03-23 14:29:43 +01:00
int
virTestCompareToULL(unsigned long long expect,
unsigned long long actual)
2017-03-23 14:29:43 +01:00
{
2019-10-15 15:16:31 +02:00
g_autofree char *expectStr = NULL;
g_autofree char *actualStr = NULL;
2017-03-23 14:29:43 +01:00
expectStr = g_strdup_printf("%llu", expect);
2017-03-23 14:29:43 +01:00
actualStr = g_strdup_printf("%llu", actual);
2017-03-23 14:29:43 +01:00
2019-02-20 14:14:31 +01:00
return virTestCompareToString(expectStr, actualStr);
2017-03-23 14:29:43 +01:00
}
2016-04-20 15:14:34 -06:00
int
virTestCompareToString(const char *expect,
const char *actual)
2016-04-20 15:14:34 -06:00
{
if (STRNEQ_NULLABLE(expect, actual)) {
virTestDifference(stderr, expect, actual);
2016-04-20 15:14:34 -06:00
return -1;
}
return 0;
}
2008-05-29 15:21:45 +00:00
static void
2019-10-14 14:45:03 +02:00
virTestErrorFuncQuiet(void *data G_GNUC_UNUSED,
virErrorPtr err G_GNUC_UNUSED)
2008-05-29 15:21:45 +00:00
{ }
/* register an error handler in tests when using connections */
void
virTestQuiesceLibvirtErrors(bool always)
{
if (always || !virTestGetVerbose())
virSetErrorFunc(NULL, virTestErrorFuncQuiet);
}
2008-05-29 15:21:45 +00:00
2010-09-10 10:25:49 -06:00
struct virtTestLogData {
virBuffer buf;
};
static struct virtTestLogData testLog = { VIR_BUFFER_INITIALIZER };
static void
2021-03-11 08:16:13 +01:00
virtTestLogOutput(virLogSource *source G_GNUC_UNUSED,
2019-10-14 14:45:03 +02:00
virLogPriority priority G_GNUC_UNUSED,
const char *filename G_GNUC_UNUSED,
int lineno G_GNUC_UNUSED,
const char *funcname G_GNUC_UNUSED,
const char *timestamp,
2021-03-11 08:16:13 +01:00
struct _virLogMetadata *metadata G_GNUC_UNUSED,
2019-10-14 14:45:03 +02:00
const char *rawstr G_GNUC_UNUSED,
const char *str,
void *data)
2010-09-10 10:25:49 -06:00
{
struct virtTestLogData *log = data;
virBufferAsprintf(&log->buf, "%s: %s", timestamp, str);
2010-09-10 10:25:49 -06:00
}
static void
virtTestLogClose(void *data)
{
struct virtTestLogData *log = data;
virBufferFreeAndReset(&log->buf);
}
/* Return a malloc'd string (possibly with strlen of 0) of all data
* logged since the last call to this function, or NULL on failure. */
char *
virTestLogContentAndReset(void)
2010-09-10 10:25:49 -06:00
{
char *ret;
ret = virBufferContentAndReset(&testLog.buf);
if (!ret)
ret = g_strdup("");
return ret;
2010-09-10 10:25:49 -06:00
}
2008-05-29 15:21:45 +00:00
2009-11-30 19:01:31 +00:00
unsigned int
2014-03-18 09:13:43 +01:00
virTestGetDebug(void)
{
2009-11-30 19:01:31 +00:00
if (testDebug == -1)
testDebug = virTestGetFlag("VIR_TEST_DEBUG");
return testDebug;
}
2008-05-29 15:21:45 +00:00
2009-11-30 19:01:31 +00:00
unsigned int
2014-03-18 09:13:43 +01:00
virTestGetVerbose(void)
{
2009-11-30 19:01:31 +00:00
if (testVerbose == -1)
testVerbose = virTestGetFlag("VIR_TEST_VERBOSE");
return testVerbose || virTestGetDebug();
}
unsigned int
2014-03-18 09:13:43 +01:00
virTestGetExpensive(void)
{
if (testExpensive == -1)
testExpensive = virTestGetFlag("VIR_TEST_EXPENSIVE");
return testExpensive;
}
unsigned int
virTestGetRegenerate(void)
{
if (testRegenerate == -1)
testRegenerate = virTestGetFlag("VIR_TEST_REGENERATE_OUTPUT");
return testRegenerate;
}
/**
* virTestHasRangeBitmap:
*
* Returns whether the test was invoked with VIR_TEST_RANGE declared thus
* limiting the run only on specific test cases.
*/
bool
virTestHasRangeBitmap(void)
{
return !!testBitmap;
}
2016-03-17 13:30:42 +01:00
static int
virTestSetEnvPath(void)
{
const char *path = getenv("PATH");
2020-02-09 01:47:22 +01:00
g_autofree char *new_path = NULL;
2016-03-17 13:30:42 +01:00
2016-03-24 14:57:42 +01:00
if (path) {
if (strstr(path, abs_builddir) != path)
new_path = g_strdup_printf("%s:%s", abs_builddir, path);
2016-03-24 14:57:42 +01:00
} else {
2019-10-20 13:49:46 +02:00
new_path = g_strdup(abs_builddir);
2016-03-24 14:57:42 +01:00
}
2016-03-25 10:20:28 +01:00
if (new_path &&
2020-02-09 01:26:08 +01:00
g_setenv("PATH", new_path, TRUE) == FALSE)
2020-02-09 01:57:06 +01:00
return -1;
2016-03-17 13:30:42 +01:00
2020-02-09 01:57:06 +01:00
return 0;
2016-03-17 13:30:42 +01:00
}
#define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
char*
virTestFakeRootDirInit(void)
{
g_autofree char *fakerootdir = g_strdup(FAKEROOTDIRTEMPLATE);
if (!g_mkdtemp(fakerootdir)) {
fprintf(stderr, "Cannot create fakerootdir");
return NULL;
}
g_setenv("LIBVIRT_FAKE_ROOT_DIR", fakerootdir, TRUE);
return g_steal_pointer(&fakerootdir);
}
void
virTestFakeRootDirCleanup(char *fakerootdir)
{
g_unsetenv("LIBVIRT_FAKE_ROOT_DIR");
if (!g_getenv("LIBVIRT_SKIP_CLEANUP"))
virFileDeleteTree(fakerootdir);
else
fprintf(stderr, "Test data ready for inspection: %s\n", fakerootdir);
}
2016-05-26 17:02:08 +02:00
int virTestMain(int argc,
char **argv,
int (*func)(void),
...)
2008-05-29 15:21:45 +00:00
{
2016-05-20 16:27:00 +02:00
const char *lib;
va_list ap;
2008-05-29 15:21:45 +00:00
int ret;
2013-07-18 15:02:19 +01:00
char *testRange = NULL;
size_t noutputs = 0;
2021-03-11 08:16:13 +01:00
virLogOutput *output = NULL;
virLogOutput **outputs = NULL;
g_autofree char *progname = NULL;
g_autofree const char **preloads = NULL;
size_t npreloads = 0;
g_autofree char *mock = NULL;
g_autofree char *fakerootdir = NULL;
if (getenv("VIR_TEST_FILE_ACCESS")) {
preloads = g_renew(const char *, preloads, npreloads + 2);
preloads[npreloads++] = VIR_TEST_MOCK("virtest");
preloads[npreloads] = NULL;
}
g_setenv("HOME", "/bad-test-used-env-home", TRUE);
g_setenv("XDG_RUNTIME_DIR", "/bad-test-used-env-xdg-runtime-dir", TRUE);
2023-03-02 17:59:06 +01:00
g_setenv("XDG_DATA_HOME", "/bad-test-used-env-xdg-data-home", TRUE);
g_setenv("XDG_CACHE_HOME", "/bad-test-used-env-xdg-cache-home", TRUE);
g_setenv("XDG_CONFIG_HOME", "/bad-test-used-env-xdg-config-home", TRUE);
2016-05-20 16:27:00 +02:00
va_start(ap, func);
while ((lib = va_arg(ap, const char *))) {
if (!virFileIsExecutable(lib)) {
perror(lib);
2020-11-16 16:12:09 +01:00
va_end(ap);
return EXIT_FAILURE;
}
preloads = g_renew(const char *, preloads, npreloads + 2);
preloads[npreloads++] = lib;
preloads[npreloads] = NULL;
}
2016-05-20 16:27:00 +02:00
va_end(ap);
if (preloads) {
mock = g_strjoinv(":", (char **)preloads);
VIR_TEST_PRELOAD(mock);
}
progname = g_path_get_basename(argv[0]);
2019-12-18 17:16:19 +00:00
g_setenv("VIR_TEST_MOCK_PROGNAME", progname, TRUE);
virFileActivateDirOverrideForProg(argv[0]);
2016-03-17 13:30:42 +01:00
if (virTestSetEnvPath() < 0)
return EXIT_AM_HARDFAIL;
2013-11-27 14:31:53 -07:00
if (!virFileExists(abs_srcdir))
2013-02-22 15:42:39 -07:00
return EXIT_AM_HARDFAIL;
2011-04-29 10:21:20 -06:00
if (argc > 1) {
fprintf(stderr, "Usage: %s\n", argv[0]);
2012-05-08 20:46:45 +03:00
fputs("effective environment variables:\n"
"VIR_TEST_VERBOSE set to show names of individual tests\n"
"VIR_TEST_DEBUG set to show information for debugging failures\n",
2012-05-08 20:46:45 +03:00
stderr);
2011-04-29 10:21:20 -06:00
return EXIT_FAILURE;
}
fprintf(stderr, "TEST: %s\n", progname);
2009-11-30 19:01:31 +00:00
2019-09-16 17:44:23 +01:00
if (virErrorInitialize() < 0)
2013-02-22 15:42:39 -07:00
return EXIT_FAILURE;
2021-12-15 16:34:16 +01:00
if (virLogSetFromEnv() < 0)
return EXIT_FAILURE;
if (!getenv("LIBVIRT_DEBUG") && !virLogGetNbOutputs()) {
if (!(output = virLogOutputNew(virtTestLogOutput, virtTestLogClose,
&testLog, VIR_LOG_DEBUG,
VIR_LOG_TO_STDERR, NULL)))
return EXIT_FAILURE;
VIR_APPEND_ELEMENT(outputs, noutputs, output);
if (virLogDefineOutputs(outputs, noutputs) < 0) {
virLogOutputListFree(outputs, noutputs);
2013-02-22 15:42:39 -07:00
return EXIT_FAILURE;
}
}
2008-05-29 15:21:45 +00:00
2013-07-18 15:02:19 +01:00
if ((testRange = getenv("VIR_TEST_RANGE")) != NULL) {
if (!(testBitmap = virBitmapParseUnlimited(testRange))) {
2013-07-18 15:02:19 +01:00
fprintf(stderr, "Cannot parse range %s\n", testRange);
return EXIT_FAILURE;
}
}
2020-10-01 17:42:11 +02:00
failedTests = virBitmapNew(1);
if (!(fakerootdir = virTestFakeRootDirInit()))
return EXIT_FAILURE;
2011-04-29 10:21:20 -06:00
ret = (func)();
virResetLastError();
if (!virTestGetVerbose() && ret != EXIT_AM_SKIP) {
2011-07-11 09:19:11 -06:00
if (testCounter == 0 || testCounter % 40)
2013-07-18 15:02:19 +01:00
fprintf(stderr, "%*s", 40 - (int)(testCounter % 40), "");
fprintf(stderr, " %-3zu %s\n", testCounter, ret == 0 ? "OK" : "FAIL");
2009-11-30 19:01:31 +00:00
}
virTestFakeRootDirCleanup(fakerootdir);
switch (ret) {
case EXIT_FAILURE:
case EXIT_SUCCESS:
case EXIT_AM_SKIP:
case EXIT_AM_HARDFAIL:
break;
default:
fprintf(stderr, "Test callback returned invalid value: %d\n", ret);
ret = EXIT_AM_HARDFAIL;
break;
}
if (ret == EXIT_FAILURE && !virBitmapIsAllClear(failedTests)) {
g_autofree char *failed = virBitmapFormat(failedTests);
2023-04-03 13:33:52 +02:00
fprintf(stderr, "%zu tests failed. Run them using:\n", virBitmapCountBits(failedTests));
fprintf(stderr, "VIR_TEST_DEBUG=1 VIR_TEST_RANGE=%s %s\n", failed, argv[0]);
}
virBitmapFree(testBitmap);
virBitmapFree(failedTests);
virLogReset();
return ret;
2008-05-29 15:21:45 +00:00
}
2021-03-11 08:16:13 +01:00
virCaps *
virTestGenericCapsInit(void)
{
2020-02-09 01:51:37 +01:00
g_autoptr(virCaps) caps = NULL;
2021-03-11 08:16:13 +01:00
virCapsGuest *guest;
if ((caps = virCapabilitiesNew(VIR_ARCH_X86_64,
2014-07-14 06:56:13 -06:00
false, false)) == NULL)
return NULL;
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_I686,
"/usr/bin/acme-virt", NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_TEST,
NULL, NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_QEMU,
NULL, NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_KVM,
NULL, NULL, 0, NULL);
guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM, VIR_ARCH_X86_64,
"/usr/bin/acme-virt", NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_TEST,
NULL, NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_QEMU,
NULL, NULL, 0, NULL);
virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_KVM,
NULL, NULL, 0, NULL);
if (virTestGetDebug() > 1) {
2020-02-09 01:47:22 +01:00
g_autofree char *caps_str = NULL;
caps_str = virCapabilitiesFormatXML(caps);
if (!caps_str)
2020-02-09 01:57:06 +01:00
return NULL;
VIR_TEST_DEBUG("Generic driver capabilities:\n%s", caps_str);
}
2020-02-09 01:51:37 +01:00
return g_steal_pointer(&caps);
}
#define MAX_CELLS 4
#define MAX_CPUS_IN_CELL 2
#define MAX_MEM_IN_CELL 2097152
/*
* Build NUMA topology with cell id starting from (0 + seq)
* for testing
*/
2021-03-11 08:16:13 +01:00
virCapsHostNUMA *
virTestCapsBuildNUMATopology(int seq)
{
2020-02-09 01:51:37 +01:00
g_autoptr(virCapsHostNUMA) caps = virCapabilitiesHostNUMANew();
2021-03-11 08:16:13 +01:00
virCapsHostNUMACellCPU *cell_cpus = NULL;
int core_id, cell_id;
int id;
id = 0;
for (cell_id = 0; cell_id < MAX_CELLS; cell_id++) {
2020-09-23 01:04:17 +02:00
cell_cpus = g_new0(virCapsHostNUMACellCPU, MAX_CPUS_IN_CELL);
for (core_id = 0; core_id < MAX_CPUS_IN_CELL; core_id++) {
cell_cpus[core_id].id = id + core_id;
cell_cpus[core_id].socket_id = cell_id + seq;
cell_cpus[core_id].core_id = id + core_id;
2020-10-01 17:42:11 +02:00
cell_cpus[core_id].siblings = virBitmapNew(MAX_CPUS_IN_CELL);
ignore_value(virBitmapSetBit(cell_cpus[core_id].siblings, id));
}
id++;
virCapabilitiesHostNUMAAddCell(caps, cell_id + seq,
MAX_MEM_IN_CELL,
MAX_CPUS_IN_CELL, &cell_cpus,
0, NULL,
2021-04-29 18:58:43 +02:00
0, NULL,
NULL);
cell_cpus = NULL;
}
2020-02-09 01:51:37 +01:00
return g_steal_pointer(&caps);
}
2016-08-02 17:58:43 +02:00
static virDomainDefParserConfig virTestGenericDomainDefParserConfig = {
.features = VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS,
};
2021-03-11 08:16:13 +01:00
virDomainXMLOption *virTestGenericDomainXMLConfInit(void)
{
return virDomainXMLOptionNew(&virTestGenericDomainDefParserConfig,
NULL, NULL, NULL, NULL, NULL);
}
2016-01-08 15:55:44 -05:00
int
2021-03-11 08:16:13 +01:00
testCompareDomXML2XMLFiles(virCaps *caps G_GNUC_UNUSED,
virDomainXMLOption *xmlopt,
const char *infile, const char *outfile, bool live,
unsigned int parse_flags,
testCompareDomXML2XMLResult expectResult)
2016-01-08 15:55:44 -05:00
{
2020-02-09 01:47:22 +01:00
g_autofree char *actual = NULL;
2016-01-08 15:55:44 -05:00
int ret = -1;
testCompareDomXML2XMLResult result;
2021-09-04 21:50:02 +02:00
g_autoptr(virDomainDef) def = NULL;
2016-01-08 15:55:44 -05:00
unsigned int format_flags = VIR_DOMAIN_DEF_FORMAT_SECURE;
if (!virFileExists(infile)) {
VIR_TEST_DEBUG("Test input file '%s' is missing", infile);
return -1;
}
if (!live) {
2016-01-08 15:55:44 -05:00
format_flags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE;
parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE;
}
2016-01-08 15:55:44 -05:00
if (!(def = virDomainDefParseFile(infile, xmlopt, NULL, parse_flags))) {
result = TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE;
goto out;
}
2016-01-08 15:55:44 -05:00
if (!virDomainDefCheckABIStability(def, def, xmlopt)) {
2016-01-08 15:55:44 -05:00
VIR_TEST_DEBUG("ABI stability check failed on %s", infile);
result = TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_STABILITY;
goto out;
2016-01-08 15:55:44 -05:00
}
if (!(actual = virDomainDefFormat(def, xmlopt, format_flags))) {
result = TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_FORMAT;
goto out;
}
2016-01-08 15:55:44 -05:00
if (virTestCompareToFile(actual, outfile) < 0) {
result = TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_COMPARE;
goto out;
}
result = TEST_COMPARE_DOM_XML2XML_RESULT_SUCCESS;
out:
if (result == expectResult) {
ret = 0;
if (expectResult != TEST_COMPARE_DOM_XML2XML_RESULT_SUCCESS) {
VIR_TEST_DEBUG("Got expected failure code=%d msg=%s",
result, virGetLastErrorMessage());
}
} else {
ret = -1;
VIR_TEST_DEBUG("Expected result code=%d but received code=%d",
expectResult, result);
}
2016-01-08 15:55:44 -05:00
return ret;
}
static int virtTestCounter;
static char virtTestCounterStr[128];
static char *virtTestCounterPrefixEndOffset;
/**
* virTestCounterReset:
* @prefix: name of the test group
*
* Resets the counter and sets up the test group name to use with
* virTestCounterNext(). This function is not thread safe.
*
* Note: The buffer for the assembled message is 128 bytes long. Longer test
* case names (including the number index) will be silently truncated.
*/
void
virTestCounterReset(const char *prefix)
{
virtTestCounter = 0;
ignore_value(virStrcpyStatic(virtTestCounterStr, prefix));
2020-01-14 10:43:37 +00:00
virtTestCounterPrefixEndOffset = virtTestCounterStr + strlen(virtTestCounterStr);
}
/**
* virTestCounterNext:
*
* This function is designed to ease test creation and reordering by adding
* a way to do automagic test case numbering.
*
* Returns string consisting of test name prefix configured via
* virTestCounterReset() and a number that increments in every call of this
* function. This function is not thread safe.
*
* Note: The buffer for the assembled message is 128 bytes long. Longer test
* case names (including the number index) will be silently truncated.
*/
const char
*virTestCounterNext(void)
{
size_t len = G_N_ELEMENTS(virtTestCounterStr);
/* calculate length of the rest of the string */
len -= (virtTestCounterPrefixEndOffset - virtTestCounterStr);
g_snprintf(virtTestCounterPrefixEndOffset, len, "%d", ++virtTestCounter);
return virtTestCounterStr;
}
/**
* virTestStablePath:
* @path: path to make stable
*
* If @path starts with the absolute source directory path, the prefix
* is replaced with the string "ABS_SRCDIR" and similarly the build directory
* is replaced by "ABS_BUILDDIR". This is useful when paths e.g. in output
* test files need to be made stable.
*
* If @path is NULL the equivalent to NULLSTR(path) is returned.
*
* The caller is responsible for freeing the returned buffer.
*/
char *
virTestStablePath(const char *path)
{
const char *tmp;
path = NULLSTR(path);
if ((tmp = STRSKIP(path, abs_srcdir)))
return g_strdup_printf("ABS_SRCDIR%s", tmp);
if ((tmp = STRSKIP(path, abs_builddir)))
return g_strdup_printf("ABS_BUILDDIR%s", tmp);
return g_strdup(path);
}
2021-10-20 11:30:31 +03:00
#ifdef __linux__
/**
* virCreateAnonymousFile:
* @data: a pointer to data to be written into a new file.
* @len: the length of data to be written (in bytes).
*
* Create a fake fd, write initial data to it.
*
*/
int
virCreateAnonymousFile(const uint8_t *data, size_t len)
{
int fd = -1;
char path[] = abs_builddir "testutils-memfd-XXXXXX";
/* A temp file is used since not all supported distributions support memfd. */
if ((fd = g_mkstemp_full(path, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR)) < 0) {
return fd;
}
g_unlink(path);
if (safewrite(fd, data, len) != len) {
VIR_TEST_DEBUG("%s: %s", "failed to write to an anonymous file",
g_strerror(errno));
goto cleanup;
}
return fd;
cleanup:
if (VIR_CLOSE(fd) < 0) {
VIR_TEST_DEBUG("%s: %s", "failed to close an anonymous file",
g_strerror(errno));
}
return -1;
}
#endif