Rather than the ever growing standalone libvirt-gconfig/tests/test-domain-create program, this commit adds support for glib GTester framework as well as 2 smaller tests cases for GVirConfigDomain and GVirConfigDomainClock. --- Makefile.am | 2 +- build-aux/glib-tap.mk | 135 ++++++ configure.ac | 3 + m4/glibtests.m4 | 28 ++ tap-driver.sh | 652 ++++++++++++++++++++++++++ tap-test | 5 + tests/Makefile.am | 18 + tests/test-gconfig.c | 674 +++++++++++++++++++++++++++ tests/xml/gconfig-domain-clock.xml | 7 + tests/xml/gconfig-domain-cpu.xml | 7 + tests/xml/gconfig-domain-device-channel.xml | 7 + tests/xml/gconfig-domain-device-console.xml | 5 + tests/xml/gconfig-domain-device-disk.xml | 9 + tests/xml/gconfig-domain-device-graphics.xml | 7 + tests/xml/gconfig-domain-device-input.xml | 5 + tests/xml/gconfig-domain-device-network.xml | 13 + tests/xml/gconfig-domain-device-sound.xml | 5 + tests/xml/gconfig-domain-device-usbredir.xml | 28 ++ tests/xml/gconfig-domain-device-video.xml | 7 + tests/xml/gconfig-domain-os.xml | 7 + tests/xml/gconfig-domain.xml | 15 + 21 files changed, 1638 insertions(+), 1 deletion(-) create mode 100644 build-aux/glib-tap.mk create mode 100644 m4/glibtests.m4 create mode 100755 tap-driver.sh create mode 100755 tap-test create mode 100644 tests/Makefile.am create mode 100644 tests/test-gconfig.c create mode 100644 tests/xml/gconfig-domain-clock.xml create mode 100644 tests/xml/gconfig-domain-cpu.xml create mode 100644 tests/xml/gconfig-domain-device-channel.xml create mode 100644 tests/xml/gconfig-domain-device-console.xml create mode 100644 tests/xml/gconfig-domain-device-disk.xml create mode 100644 tests/xml/gconfig-domain-device-graphics.xml create mode 100644 tests/xml/gconfig-domain-device-input.xml create mode 100644 tests/xml/gconfig-domain-device-network.xml create mode 100644 tests/xml/gconfig-domain-device-sound.xml create mode 100644 tests/xml/gconfig-domain-device-usbredir.xml create mode 100644 tests/xml/gconfig-domain-device-video.xml create mode 100644 tests/xml/gconfig-domain-os.xml create mode 100644 tests/xml/gconfig-domain.xml diff --git a/Makefile.am b/Makefile.am index 9b03676..0eba6e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = libvirt-glib libvirt-gconfig libvirt-gobject python vapi examples docs po +SUBDIRS = libvirt-glib libvirt-gconfig libvirt-gobject python vapi examples docs po tests ACLOCAL_AMFLAGS = -I m4 diff --git a/build-aux/glib-tap.mk b/build-aux/glib-tap.mk new file mode 100644 index 0000000..016cb91 --- /dev/null +++ b/build-aux/glib-tap.mk @@ -0,0 +1,135 @@ +# GLIB - Library of useful C routines + +TESTS_ENVIRONMENT= \ + G_TEST_SRCDIR="$(abs_srcdir)" \ + G_TEST_BUILDDIR="$(abs_builddir)" \ + G_DEBUG=gc-friendly \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh +LOG_COMPILER = $(top_srcdir)/tap-test + +NULL = + +# initialize variables for unconditional += appending +BUILT_SOURCES = +BUILT_EXTRA_DIST = +CLEANFILES = *.log *.trs +DISTCLEANFILES = +MAINTAINERCLEANFILES = +EXTRA_DIST = +TESTS = + +installed_test_LTLIBRARIES = +installed_test_PROGRAMS = +installed_test_SCRIPTS = +nobase_installed_test_DATA = + +noinst_LTLIBRARIES = +noinst_PROGRAMS = +noinst_SCRIPTS = +noinst_DATA = + +check_LTLIBRARIES = +check_PROGRAMS = +check_SCRIPTS = +check_DATA = + +# We support a fairly large range of possible variables. It is expected that all types of files in a test suite +# will belong in exactly one of the following variables. +# +# First, we support the usual automake suffixes, but in lowercase, with the customary meaning: +# +# test_programs, test_scripts, test_data, test_ltlibraries +# +# The above are used to list files that are involved in both uninstalled and installed testing. The +# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite. +# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is +# installed in the same way as it appears in the package layout. +# +# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled', +# like so: +# +# installed_test_programs, uninstalled_test_programs +# installed_test_scripts, uninstalled_test_scripts +# installed_test_data, uninstalled_test_data +# installed_test_ltlibraries, uninstalled_test_ltlibraries +# +# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts +# that should not themselves be run as testcases (but exist to be used from other testcases): +# +# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs +# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts +# +# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data +# file automatically end up in the tarball. +# +# dist_test_scripts, dist_test_data, dist_test_extra_scripts +# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts +# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts +# +# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the +# standard automake convention of not disting programs scripts or data by default. +# +# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted +# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under +# gtester. That's a bit strange for scripts, but it's possible. + +TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \ + $(dist_test_scripts) $(dist_uninstalled_test_scripts) + +# Note: build even the installed-only targets during 'make check' to ensure that they still work. +# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to +# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were +# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'. +all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs) +all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts) +all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \ + $(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts) +all_test_scripts += $(all_dist_test_scripts) +EXTRA_DIST += $(all_dist_test_scripts) +all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data) +all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data) +all_test_data += $(all_dist_test_data) +EXTRA_DIST += $(all_dist_test_data) +all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries) + +if ENABLE_ALWAYS_BUILD_TESTS +noinst_LTLIBRARIES += $(all_test_ltlibs) +noinst_PROGRAMS += $(all_test_programs) +noinst_SCRIPTS += $(all_test_scripts) +noinst_DATA += $(all_test_data) +else +check_LTLIBRARIES += $(all_test_ltlibs) +check_PROGRAMS += $(all_test_programs) +check_SCRIPTS += $(all_test_scripts) +check_DATA += $(all_test_data) +endif + +if ENABLE_INSTALLED_TESTS +installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(installed_test_extra_programs) +installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(test_installed_extra_scripts) +installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \ + $(dist_installed_test_scripts) $(dist_installed_test_extra_scripts) +nobase_installed_test_DATA += $(test_data) $(installed_test_data) +nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data) +installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries) +installed_testcases = $(test_programs) $(installed_test_programs) \ + $(test_scripts) $(installed_test_scripts) \ + $(dist_test_scripts) $(dist_installed_test_scripts) + +installed_test_meta_DATA = $(installed_testcases:=.test) + +%.test: %$(EXEEXT) Makefile + $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ + echo 'Type=session' >> $@.tmp; \ + echo 'Exec=$(installed_testdir)/$(notdir $<) --tap' >> $@.tmp; \ + echo 'Output=TAP' >> $@.tmp; \ + mv $@.tmp $@) + +CLEANFILES += $(installed_test_meta_DATA) +endif diff --git a/configure.ac b/configure.ac index e507e16..4ad636a 100644 --- a/configure.ac +++ b/configure.ac @@ -109,6 +109,8 @@ GTK_DOC_CHECK([1.10],[--flavour no-tmpl]) GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` AC_SUBST(GLIB_MKENUMS) +GLIB_TESTS + dnl Extra link-time flags for Cygwin. dnl Copied from libxml2 configure.in, but I removed mingw changes dnl for now since I'm not supporting mingw at present. - RWMJ @@ -289,6 +291,7 @@ AC_OUTPUT(Makefile docs/libvirt-gobject/Makefile docs/libvirt-gconfig/Makefile po/Makefile.in + tests/Makefile libvirt-glib-1.0.pc libvirt-gconfig-1.0.pc libvirt-gobject-1.0.pc diff --git a/m4/glibtests.m4 b/m4/glibtests.m4 new file mode 100644 index 0000000..7d5920a --- /dev/null +++ b/m4/glibtests.m4 @@ -0,0 +1,28 @@ +dnl GLIB_TESTS +dnl + +AC_DEFUN([GLIB_TESTS], +[ + AC_ARG_ENABLE(installed-tests, + AS_HELP_STRING([--enable-installed-tests], + [Enable installation of some test cases]), + [case ${enableval} in + yes) ENABLE_INSTALLED_TESTS="1" ;; + no) ENABLE_INSTALLED_TESTS="" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;; + esac]) + AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1") + AC_ARG_ENABLE(always-build-tests, + AS_HELP_STRING([--enable-always-build-tests], + [Enable always building tests during 'make all']), + [case ${enableval} in + yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;; + no) ENABLE_ALWAYS_BUILD_TESTS="" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;; + esac]) + AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1") + if test "$ENABLE_INSTALLED_TESTS" = "1"; then + AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME) + AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME) + fi +]) diff --git a/tap-driver.sh b/tap-driver.sh new file mode 100755 index 0000000..19aa531 --- /dev/null +++ b/tap-driver.sh @@ -0,0 +1,652 @@ +#! /bin/sh +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@xxxxxxx> or send patches to +# <automake-patches@xxxxxxx>. + +scriptversion=2011-12-27.17; # UTC + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +me=tap-driver.sh + +fatal () +{ + echo "$me: fatal: $*" >&2 + exit 1 +} + +usage_error () +{ + echo "$me: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <<END +Usage: + tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH + [--expect-failure={yes|no}] [--color-tests={yes|no}] + [--enable-hard-errors={yes|no}] [--ignore-exit] + [--diagnostic-string=STRING] [--merge|--no-merge] + [--comments|--no-comments] [--] TEST-COMMAND +The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory. +END +} + +# TODO: better error handling in option parsing (in particular, ensure +# TODO: $log_file, $trs_file and $test_name are defined). +test_name= # Used for reporting. +log_file= # Where to save the result and output of the test script. +trs_file= # Where to save the metadata of the test run. +expect_failure=0 +color_tests=0 +merge=0 +ignore_exit=0 +comments=0 +diag_string='#' +while test $# -gt 0; do + case $1 in + --help) print_usage; exit $?;; + --version) echo "$me $scriptversion"; exit $?;; + --test-name) test_name=$2; shift;; + --log-file) log_file=$2; shift;; + --trs-file) trs_file=$2; shift;; + --color-tests) color_tests=$2; shift;; + --expect-failure) expect_failure=$2; shift;; + --enable-hard-errors) shift;; # No-op. + --merge) merge=1;; + --no-merge) merge=0;; + --ignore-exit) ignore_exit=1;; + --comments) comments=1;; + --no-comments) comments=0;; + --diagnostic-string) diag_string=$2; shift;; + --) shift; break;; + -*) usage_error "invalid option: '$1'";; + esac + shift +done + +test $# -gt 0 || usage_error "missing test command" + +case $expect_failure in + yes) expect_failure=1;; + *) expect_failure=0;; +esac + +if test $color_tests = yes; then + init_colors=' + color_map["red"]="[0;31m" # Red. + color_map["grn"]="[0;32m" # Green. + color_map["lgn"]="[1;32m" # Light green. + color_map["blu"]="[1;34m" # Blue. + color_map["mgn"]="[0;35m" # Magenta. + color_map["std"]="[m" # No color. + color_for_result["ERROR"] = "mgn" + color_for_result["PASS"] = "grn" + color_for_result["XPASS"] = "red" + color_for_result["FAIL"] = "red" + color_for_result["XFAIL"] = "lgn" + color_for_result["SKIP"] = "blu"' +else + init_colors='' +fi + +# :; is there to work around a bug in bash 3.2 (and earlier) which +# does not always set '$?' properly on redirection failure. +# See the Autoconf manual for more details. +:;{ + ( + # Ignore common signals (in this subshell only!), to avoid potential + # problems with Korn shells. Some Korn shells are known to propagate + # to themselves signals that have killed a child process they were + # waiting for; this is done at least for SIGINT (and usually only for + # it, in truth). Without the `trap' below, such a behaviour could + # cause a premature exit in the current subshell, e.g., in case the + # test command it runs gets terminated by a SIGINT. Thus, the awk + # script we are piping into would never seen the exit status it + # expects on its last input line (which is displayed below by the + # last `echo $?' statement), and would thus die reporting an internal + # error. + # For more information, see the Autoconf manual and the threads: + # <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html> + # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html> + trap : 1 3 2 13 15 + if test $merge -gt 0; then + exec 2>&1 + else + exec 2>&3 + fi + "$@" + echo $? + ) | LC_ALL=C ${AM_TAP_AWK-awk} \ + -v me="$me" \ + -v test_script_name="$test_name" \ + -v log_file="$log_file" \ + -v trs_file="$trs_file" \ + -v expect_failure="$expect_failure" \ + -v merge="$merge" \ + -v ignore_exit="$ignore_exit" \ + -v comments="$comments" \ + -v diag_string="$diag_string" \ +' +# FIXME: the usages of "cat >&3" below could be optimized when using +# FIXME: GNU awk, and/on on systems that supports /dev/fd/. + +# Implementation note: in what follows, `result_obj` will be an +# associative array that (partly) simulates a TAP result object +# from the `TAP::Parser` perl module. + +## ----------- ## +## FUNCTIONS ## +## ----------- ## + +function fatal(msg) +{ + print me ": " msg | "cat >&2" + exit 1 +} + +function abort(where) +{ + fatal("internal error " where) +} + +# Convert a boolean to a "yes"/"no" string. +function yn(bool) +{ + return bool ? "yes" : "no"; +} + +function add_test_result(result) +{ + if (!test_results_index) + test_results_index = 0 + test_results_list[test_results_index] = result + test_results_index += 1 + test_results_seen[result] = 1; +} + +# Whether the test script should be re-run by "make recheck". +function must_recheck() +{ + for (k in test_results_seen) + if (k != "XFAIL" && k != "PASS" && k != "SKIP") + return 1 + return 0 +} + +# Whether the content of the log file associated to this test should +# be copied into the "global" test-suite.log. +function copy_in_global_log() +{ + for (k in test_results_seen) + if (k != "PASS") + return 1 + return 0 +} + +# FIXME: this can certainly be improved ... +function get_global_test_result() +{ + if ("ERROR" in test_results_seen) + return "ERROR" + if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) + return "FAIL" + all_skipped = 1 + for (k in test_results_seen) + if (k != "SKIP") + all_skipped = 0 + if (all_skipped) + return "SKIP" + return "PASS"; +} + +function stringify_result_obj(result_obj) +{ + if (result_obj["is_unplanned"] || result_obj["number"] != testno) + return "ERROR" + + if (plan_seen == LATE_PLAN) + return "ERROR" + + if (result_obj["directive"] == "TODO") + return result_obj["is_ok"] ? "XPASS" : "XFAIL" + + if (result_obj["directive"] == "SKIP") + return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; + + if (length(result_obj["directive"])) + abort("in function stringify_result_obj()") + + return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL +} + +function decorate_result(result) +{ + color_name = color_for_result[result] + if (color_name) + return color_map[color_name] "" result "" color_map["std"] + # If we are not using colorized output, or if we do not know how + # to colorize the given result, we should return it unchanged. + return result +} + +function report(result, details) +{ + if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) + { + msg = ": " test_script_name + add_test_result(result) + } + else if (result == "#") + { + msg = " " test_script_name ":" + } + else + { + abort("in function report()") + } + if (length(details)) + msg = msg " " details + # Output on console might be colorized. + print decorate_result(result) msg + # Log the result in the log file too, to help debugging (this is + # especially true when said result is a TAP error or "Bail out!"). + print result msg | "cat >&3"; +} + +function testsuite_error(error_message) +{ + report("ERROR", "- " error_message) +} + +function handle_tap_result() +{ + details = result_obj["number"]; + if (length(result_obj["description"])) + details = details " " result_obj["description"] + + if (plan_seen == LATE_PLAN) + { + details = details " # AFTER LATE PLAN"; + } + else if (result_obj["is_unplanned"]) + { + details = details " # UNPLANNED"; + } + else if (result_obj["number"] != testno) + { + details = sprintf("%s # OUT-OF-ORDER (expecting %d)", + details, testno); + } + else if (result_obj["directive"]) + { + details = details " # " result_obj["directive"]; + if (length(result_obj["explanation"])) + details = details " " result_obj["explanation"] + } + + report(stringify_result_obj(result_obj), details) +} + +# `skip_reason` should be empty whenever planned > 0. +function handle_tap_plan(planned, skip_reason) +{ + planned += 0 # Avoid getting confused if, say, `planned` is "00" + if (length(skip_reason) && planned > 0) + abort("in function handle_tap_plan()") + if (plan_seen) + { + # Error, only one plan per stream is acceptable. + testsuite_error("multiple test plans") + return; + } + planned_tests = planned + # The TAP plan can come before or after *all* the TAP results; we speak + # respectively of an "early" or a "late" plan. If we see the plan line + # after at least one TAP result has been seen, assume we have a late + # plan; in this case, any further test result seen after the plan will + # be flagged as an error. + plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) + # If testno > 0, we have an error ("too many tests run") that will be + # automatically dealt with later, so do not worry about it here. If + # $plan_seen is true, we have an error due to a repeated plan, and that + # has already been dealt with above. Otherwise, we have a valid "plan + # with SKIP" specification, and should report it as a particular kind + # of SKIP result. + if (planned == 0 && testno == 0) + { + if (length(skip_reason)) + skip_reason = "- " skip_reason; + report("SKIP", skip_reason); + } +} + +function extract_tap_comment(line) +{ + if (index(line, diag_string) == 1) + { + # Strip leading `diag_string` from `line`. + line = substr(line, length(diag_string) + 1) + # And strip any leading and trailing whitespace left. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + # Return what is left (if any). + return line; + } + return ""; +} + +# When this function is called, we know that line is a TAP result line, +# so that it matches the (perl) RE "^(not )?ok\b". +function setup_result_obj(line) +{ + # Get the result, and remove it from the line. + result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) + sub("^(not )?ok[ \t]*", "", line) + + # If the result has an explicit number, get it and strip it; otherwise, + # automatically assing the next progresive number to it. + if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) + { + match(line, "^[0-9]+") + # The final `+ 0` is to normalize numbers with leading zeros. + result_obj["number"] = substr(line, 1, RLENGTH) + 0 + line = substr(line, RLENGTH + 1) + } + else + { + result_obj["number"] = testno + } + + if (plan_seen == LATE_PLAN) + # No further test results are acceptable after a "late" TAP plan + # has been seen. + result_obj["is_unplanned"] = 1 + else if (plan_seen && testno > planned_tests) + result_obj["is_unplanned"] = 1 + else + result_obj["is_unplanned"] = 0 + + # Strip trailing and leading whitespace. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + + # This will have to be corrected if we have a "TODO"/"SKIP" directive. + result_obj["description"] = line + result_obj["directive"] = "" + result_obj["explanation"] = "" + + if (index(line, "#") == 0) + return # No possible directive, nothing more to do. + + # Directives are case-insensitive. + rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" + + # See whether we have the directive, and if yes, where. + pos = match(line, rx "$") + if (!pos) + pos = match(line, rx "[^a-zA-Z0-9_]") + + # If there was no TAP directive, we have nothing more to do. + if (!pos) + return + + # Let`s now see if the TAP directive has been escaped. For example: + # escaped: ok \# SKIP + # not escaped: ok \\# SKIP + # escaped: ok \\\\\# SKIP + # not escaped: ok \ # SKIP + if (substr(line, pos, 1) == "#") + { + bslash_count = 0 + for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) + bslash_count += 1 + if (bslash_count % 2) + return # Directive was escaped. + } + + # Strip the directive and its explanation (if any) from the test + # description. + result_obj["description"] = substr(line, 1, pos - 1) + # Now remove the test description from the line, that has been dealt + # with already. + line = substr(line, pos) + # Strip the directive, and save its value (normalized to upper case). + sub("^[ \t]*#[ \t]*", "", line) + result_obj["directive"] = toupper(substr(line, 1, 4)) + line = substr(line, 5) + # Now get the explanation for the directive (if any), with leading + # and trailing whitespace removed. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + result_obj["explanation"] = line +} + +function get_test_exit_message(status) +{ + if (status == 0) + return "" + if (status !~ /^[1-9][0-9]*$/) + abort("getting exit status") + if (status < 127) + exit_details = "" + else if (status == 127) + exit_details = " (command not found?)" + else if (status >= 128 && status <= 255) + exit_details = sprintf(" (terminated by signal %d?)", status - 128) + else if (status > 256 && status <= 384) + # We used to report an "abnormal termination" here, but some Korn + # shells, when a child process die due to signal number n, can leave + # in $? an exit status of 256+n instead of the more standard 128+n. + # Apparently, both behaviours are allowed by POSIX (2008), so be + # prepared to handle them both. See also Austing Group report ID + # 0000051 <http://www.austingroupbugs.net/view.php?id=51> + exit_details = sprintf(" (terminated by signal %d?)", status - 256) + else + # Never seen in practice. + exit_details = " (abnormal termination)" + return sprintf("exited with status %d%s", status, exit_details) +} + +function write_test_results() +{ + print ":global-test-result: " get_global_test_result() > trs_file + print ":recheck: " yn(must_recheck()) > trs_file + print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file + for (i = 0; i < test_results_index; i += 1) + print ":test-result: " test_results_list[i] > trs_file + close(trs_file); +} + +BEGIN { + +## ------- ## +## SETUP ## +## ------- ## + +'"$init_colors"' + +# Properly initialized once the TAP plan is seen. +planned_tests = 0 + +COOKED_PASS = expect_failure ? "XPASS": "PASS"; +COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; + +# Enumeration-like constants to remember which kind of plan (if any) +# has been seen. It is important that NO_PLAN evaluates "false" as +# a boolean. +NO_PLAN = 0 +EARLY_PLAN = 1 +LATE_PLAN = 2 + +testno = 0 # Number of test results seen so far. +bailed_out = 0 # Whether a "Bail out!" directive has been seen. + +# Whether the TAP plan has been seen or not, and if yes, which kind +# it is ("early" is seen before any test result, "late" otherwise). +plan_seen = NO_PLAN + +## --------- ## +## PARSING ## +## --------- ## + +is_first_read = 1 + +while (1) + { + # Involutions required so that we are able to read the exit status + # from the last input line. + st = getline + if (st < 0) # I/O error. + fatal("I/O error while reading from input stream") + else if (st == 0) # End-of-input + { + if (is_first_read) + abort("in input loop: only one input line") + break + } + if (is_first_read) + { + is_first_read = 0 + nextline = $0 + continue + } + else + { + curline = nextline + nextline = $0 + $0 = curline + } + # Copy any input line verbatim into the log file. + print | "cat >&3" + # Parsing of TAP input should stop after a "Bail out!" directive. + if (bailed_out) + continue + + # TAP test result. + if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) + { + testno += 1 + setup_result_obj($0) + handle_tap_result() + } + # TAP plan (normal or "SKIP" without explanation). + else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) + { + # The next two lines will put the number of planned tests in $0. + sub("^1\\.\\.", "") + sub("[^0-9]*$", "") + handle_tap_plan($0, "") + continue + } + # TAP "SKIP" plan, with an explanation. + else if ($0 ~ /^1\.\.0+[ \t]*#/) + { + # The next lines will put the skip explanation in $0, stripping + # any leading and trailing whitespace. This is a little more + # tricky in truth, since we want to also strip a potential leading + # "SKIP" string from the message. + sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") + sub("[ \t]*$", ""); + handle_tap_plan(0, $0) + } + # "Bail out!" magic. + # Older versions of prove and TAP::Harness (e.g., 3.17) did not + # recognize a "Bail out!" directive when preceded by leading + # whitespace, but more modern versions (e.g., 3.23) do. So we + # emulate the latter, "more modern" behaviour. + else if ($0 ~ /^[ \t]*Bail out!/) + { + bailed_out = 1 + # Get the bailout message (if any), with leading and trailing + # whitespace stripped. The message remains stored in `$0`. + sub("^[ \t]*Bail out![ \t]*", ""); + sub("[ \t]*$", ""); + # Format the error message for the + bailout_message = "Bail out!" + if (length($0)) + bailout_message = bailout_message " " $0 + testsuite_error(bailout_message) + } + # Maybe we have too look for dianogtic comments too. + else if (comments != 0) + { + comment = extract_tap_comment($0); + if (length(comment)) + report("#", comment); + } + } + +## -------- ## +## FINISH ## +## -------- ## + +# A "Bail out!" directive should cause us to ignore any following TAP +# error, as well as a non-zero exit status from the TAP producer. +if (!bailed_out) + { + if (!plan_seen) + { + testsuite_error("missing test plan") + } + else if (planned_tests != testno) + { + bad_amount = testno > planned_tests ? "many" : "few" + testsuite_error(sprintf("too %s tests run (expected %d, got %d)", + bad_amount, planned_tests, testno)) + } + if (!ignore_exit) + { + # Fetch exit status from the last line. + exit_message = get_test_exit_message(nextline) + if (exit_message) + testsuite_error(exit_message) + } + } + +write_test_results() + +exit 0 + +} # End of "BEGIN" block. +' + +# TODO: document that we consume the file descriptor 3 :-( +} 3>"$log_file" + +test $? -eq 0 || fatal "I/O or internal error" + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tap-test b/tap-test new file mode 100755 index 0000000..481e333 --- /dev/null +++ b/tap-test @@ -0,0 +1,5 @@ +#! /bin/sh + +# run a GTest in tap mode. The test binary is passed as $1 + +$1 -k --tap diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..3ad0819 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,18 @@ +include $(top_srcdir)/build-aux/glib-tap.mk + +AM_CFLAGS = \ + $(COVERAGE_CFLAGS) \ + -I$(top_srcdir) \ + $(GOBJECT2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(WARN_CFLAGS) + +LDADD = \ + $(top_builddir)/libvirt-gconfig/libvirt-gconfig-1.0.la + +test_programs = test-gconfig + +dist_test_data = \ + xml/gconfig-domain.xml \ + xml/gconfig-domain-clock.xml \ + xml/gconfig-domain-os.xml diff --git a/tests/test-gconfig.c b/tests/test-gconfig.c new file mode 100644 index 0000000..bc9aac0 --- /dev/null +++ b/tests/test-gconfig.c @@ -0,0 +1,674 @@ +/* + * test-gconfig.c: unit tests for libvirt-gconfig + * + * Copyright (C) 2014 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * The Software is provided "as is", without warranty of any kind, express + * or implied, including but not limited to the warranties of + * merchantability, fitness for a particular purpose and noninfringement. + * In no event shall the authors or copyright holders be liable for any + * claim, damages or other liability, whether in an action of contract, + * tort or otherwise, arising from, out of or in connection with the + * software or the use or other dealings in the Software. + * + * Author: Christophe Fergeau <cfergeau@xxxxxxxxxx> + */ +#include <libvirt-gconfig/libvirt-gconfig.h> + +#define g_assert_cmpstr_free1(str1, op, str2) G_STMT_START { \ + char *alloced_str = (str1); \ + g_assert_cmpstr(alloced_str, op, (str2)); \ + g_free(alloced_str); \ +} G_STMT_END + +static void check_xml(GVirConfigDomain *domain, const char *reference_file) +{ + const char *reference_path; + GError *error = NULL; + char *reference_xml; + char *xml; + + reference_path = g_test_get_filename(G_TEST_DIST, "xml", + reference_file, NULL); + g_file_get_contents(reference_path, &reference_xml, NULL, &error); + g_assert_no_error(error); + /* I could not generate text files without a trailing \n with vim or + * gedit, workaround this issue by removing trailing whitespace from + * the reference file */ + g_strchomp(reference_xml); + xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(domain)); + g_assert_cmpstr(xml, ==, reference_xml); + g_free(xml); +} + + +static GList *get_devices_by_type(GVirConfigDomain *domain, GType type) +{ + GList *devices; + GList *it; + GList *matches = NULL; + + devices = gvir_config_domain_get_devices(domain); + for (it = devices; it != NULL; it = it->next) { + if (g_type_is_a(G_OBJECT_TYPE(it->data), type)) { + matches = g_list_prepend(matches, g_object_ref(G_OBJECT(it->data))); + } + } + g_list_free_full(devices, g_object_unref); + + return g_list_reverse(matches); +} + + +static void test_domain(void) +{ + GVirConfigDomain *domain; + const char *features[] = { "foo", "bar", "baz", NULL }; + GStrv feat; + unsigned int i; + + domain = gvir_config_domain_new(); + g_assert(domain != NULL); + + gvir_config_domain_set_virt_type(domain, GVIR_CONFIG_DOMAIN_VIRT_KVM); + g_assert_cmpint(gvir_config_domain_get_virt_type(domain), ==, GVIR_CONFIG_DOMAIN_VIRT_KVM); + + gvir_config_domain_set_name(domain, "foo"); + g_assert_cmpstr(gvir_config_domain_get_name(domain), ==, "foo"); + + gvir_config_domain_set_memory(domain, 1234); + g_assert_cmpint(gvir_config_domain_get_memory(domain), ==, 1234); + gvir_config_domain_set_vcpus(domain, 3); + g_assert_cmpint(gvir_config_domain_get_vcpus(domain), ==, 3); + + gvir_config_domain_set_features(domain, (const GStrv)features); + feat = gvir_config_domain_get_features(domain); + for (i = 0; features[i] != NULL; i++) { + g_assert(feat[i] != NULL); + g_assert_cmpstr(feat[i], ==, features[i]); + } + g_strfreev(feat); + + gvir_config_domain_set_lifecycle(domain, + GVIR_CONFIG_DOMAIN_LIFECYCLE_ON_POWEROFF, + GVIR_CONFIG_DOMAIN_LIFECYCLE_RESTART); + + gvir_config_domain_set_custom_xml(domain, "<foo/>", "ns", "http://foo", NULL); + gvir_config_domain_set_custom_xml(domain, "<foo/>", "nsbar", "http://bar", NULL); + gvir_config_domain_set_custom_xml(domain, "<foo/>", "ns", "http://bar", NULL); + gvir_config_domain_set_custom_xml(domain, "<bar/>", "ns", "http://foo", NULL); + + g_assert_cmpstr_free1(gvir_config_domain_get_custom_xml(domain, "http://foo"), ==, "<ns:bar xmlns:ns=\"http://foo\"/>"); + g_assert_cmpstr_free1(gvir_config_domain_get_custom_xml(domain, "http://bar"), ==, "<ns:foo xmlns:ns=\"http://bar\"/>"); + + check_xml(domain, "gconfig-domain.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_clock(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainClock *klock; + GVirConfigDomainTimerPit *pit; + GVirConfigDomainTimerRtc *rtc; + GVirConfigDomainTimerHpet *hpet; + + domain = gvir_config_domain_new(); + + klock = gvir_config_domain_clock_new(); + gvir_config_domain_clock_set_offset(klock, GVIR_CONFIG_DOMAIN_CLOCK_TIMEZONE); + gvir_config_domain_clock_set_timezone(klock, "CEST"); + g_assert_cmpint(gvir_config_domain_clock_get_offset(klock), ==, GVIR_CONFIG_DOMAIN_CLOCK_TIMEZONE); + g_assert_cmpstr(gvir_config_domain_clock_get_timezone(klock), ==, "CEST"); + + pit = gvir_config_domain_timer_pit_new(); + gvir_config_domain_timer_set_tick_policy(GVIR_CONFIG_DOMAIN_TIMER(pit), + GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_DELAY); + gvir_config_domain_clock_add_timer(klock, GVIR_CONFIG_DOMAIN_TIMER(pit)); + g_assert_cmpint(gvir_config_domain_timer_get_tick_policy(GVIR_CONFIG_DOMAIN_TIMER(pit)), ==, GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_DELAY); + g_assert(gvir_config_domain_timer_get_present(GVIR_CONFIG_DOMAIN_TIMER(pit)) != FALSE); + g_object_unref(G_OBJECT(pit)); + + rtc = gvir_config_domain_timer_rtc_new(); + gvir_config_domain_timer_set_tick_policy(GVIR_CONFIG_DOMAIN_TIMER(rtc), + GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_CATCHUP); + gvir_config_domain_clock_add_timer(klock, GVIR_CONFIG_DOMAIN_TIMER(rtc)); + g_assert_cmpint(gvir_config_domain_timer_get_tick_policy(GVIR_CONFIG_DOMAIN_TIMER(rtc)), ==, GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_CATCHUP); + g_assert(gvir_config_domain_timer_get_present(GVIR_CONFIG_DOMAIN_TIMER(rtc)) != FALSE); + g_object_unref(G_OBJECT(rtc)); + + hpet = gvir_config_domain_timer_hpet_new(); + gvir_config_domain_timer_set_present(GVIR_CONFIG_DOMAIN_TIMER(hpet), FALSE); + gvir_config_domain_clock_add_timer(klock, GVIR_CONFIG_DOMAIN_TIMER(hpet)); + g_assert(gvir_config_domain_timer_get_present(GVIR_CONFIG_DOMAIN_TIMER(hpet)) == FALSE); + g_object_unref(G_OBJECT(hpet)); + + gvir_config_domain_set_clock(domain, klock); + g_object_unref(G_OBJECT(klock)); + + klock = gvir_config_domain_get_clock(domain); + g_assert(klock != NULL); + g_assert_cmpint(gvir_config_domain_clock_get_offset(klock), ==, GVIR_CONFIG_DOMAIN_CLOCK_TIMEZONE); + g_assert_cmpstr(gvir_config_domain_clock_get_timezone(klock), ==, "CEST"); + g_object_unref(G_OBJECT(klock)); + /* FIXME: gvir_config_domain_clock_get_timers() */ + + check_xml(domain, "gconfig-domain-clock.xml"); + + gvir_config_domain_set_clock(domain, NULL); + klock = gvir_config_domain_get_clock(domain); + g_assert(klock == NULL); + + g_object_unref(G_OBJECT(domain)); +} + + +static void domain_os_check_boot_devices(GVirConfigDomainOs *os) +{ + GList *devices = NULL; + GList *it; + + devices = gvir_config_domain_os_get_boot_devices(os); + it = devices; + g_assert_cmpint(GPOINTER_TO_INT(it->data), ==, GVIR_CONFIG_DOMAIN_OS_BOOT_DEVICE_CDROM); + it = it->next; + g_assert_cmpint(GPOINTER_TO_INT(it->data), ==, GVIR_CONFIG_DOMAIN_OS_BOOT_DEVICE_NETWORK); + g_assert(it->next == NULL); + g_list_free(devices); +} + + +static void test_domain_os(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainOs *os; + GList *devices = NULL; + + domain = gvir_config_domain_new(); + os = gvir_config_domain_os_new(); + gvir_config_domain_os_set_os_type(os, GVIR_CONFIG_DOMAIN_OS_TYPE_HVM); + gvir_config_domain_os_set_arch(os, "x86_64"); + g_assert_cmpint(gvir_config_domain_os_get_os_type(os), ==, GVIR_CONFIG_DOMAIN_OS_TYPE_HVM); + g_assert_cmpstr(gvir_config_domain_os_get_arch(os), ==, "x86_64"); + devices = g_list_append(devices, + GINT_TO_POINTER(GVIR_CONFIG_DOMAIN_OS_BOOT_DEVICE_CDROM)); + devices = g_list_append(devices, + GINT_TO_POINTER(GVIR_CONFIG_DOMAIN_OS_BOOT_DEVICE_NETWORK)); + gvir_config_domain_os_set_boot_devices(os, devices); + g_list_free(devices); + devices = NULL; + domain_os_check_boot_devices(os); + gvir_config_domain_set_os(domain, os); + g_object_unref(G_OBJECT(os)); + + os = gvir_config_domain_get_os(domain); + g_assert(os != NULL); + g_assert_cmpstr(gvir_config_domain_os_get_arch(os), ==, "x86_64"); + g_assert_cmpint(gvir_config_domain_os_get_os_type(os), ==, GVIR_CONFIG_DOMAIN_OS_TYPE_HVM); + domain_os_check_boot_devices(os); + g_object_unref(G_OBJECT(os)); + + check_xml(domain, "gconfig-domain-os.xml"); + + gvir_config_domain_set_os(domain, NULL); + os = gvir_config_domain_get_os(domain); + g_assert(os == NULL); +} + + +static void test_domain_cpu(void) +{ + GVirConfigDomain *domain; + /* cpu node */ + GVirConfigDomainCpu *cpu; + + domain = gvir_config_domain_new(); + cpu = gvir_config_domain_cpu_new(); + gvir_config_domain_set_vcpus(domain, 3); + + /* cpu/feature nodes */ + GVirConfigDomainCpuFeature *feature; + GList *cpu_features = NULL; + + gvir_config_domain_cpu_set_match_policy(cpu, GVIR_CONFIG_DOMAIN_CPU_MATCH_POLICY_STRICT); + gvir_config_domain_cpu_set_mode(cpu, GVIR_CONFIG_DOMAIN_CPU_MODE_HOST_PASSTHROUGH); + feature = gvir_config_domain_cpu_feature_new(); + gvir_config_capabilities_cpu_feature_set_name(GVIR_CONFIG_CAPABILITIES_CPU_FEATURE(feature), + "foo"); + gvir_config_domain_cpu_feature_set_policy(feature, GVIR_CONFIG_DOMAIN_CPU_FEATURE_POLICY_REQUIRE); + gvir_config_capabilities_cpu_add_feature(GVIR_CONFIG_CAPABILITIES_CPU(cpu), + GVIR_CONFIG_CAPABILITIES_CPU_FEATURE(feature)); + g_object_unref(feature); + + /* cpu/topology nodes */ + GVirConfigCapabilitiesCpuTopology *topology; + + topology = gvir_config_capabilities_cpu_topology_new(); + gvir_config_capabilities_cpu_topology_set_cores(topology, 1); + gvir_config_capabilities_cpu_topology_set_sockets(topology, 2); + gvir_config_capabilities_cpu_topology_set_threads(topology, 3); + gvir_config_capabilities_cpu_set_topology(GVIR_CONFIG_CAPABILITIES_CPU(cpu), + topology); + g_object_unref(topology); + gvir_config_domain_set_cpu(domain, cpu); + g_object_unref(cpu); + + + cpu = gvir_config_domain_get_cpu(domain); + g_assert(cpu != NULL); + g_assert(GVIR_CONFIG_IS_CAPABILITIES_CPU(cpu)); + g_assert_cmpint(gvir_config_domain_cpu_get_match_policy(cpu), ==, GVIR_CONFIG_DOMAIN_CPU_MATCH_POLICY_STRICT); + g_assert_cmpint(gvir_config_domain_cpu_get_mode(cpu), ==, GVIR_CONFIG_DOMAIN_CPU_MODE_HOST_PASSTHROUGH); + + cpu_features = gvir_config_capabilities_cpu_get_features(GVIR_CONFIG_CAPABILITIES_CPU(cpu)); + g_assert_cmpint(g_list_length(cpu_features), ==, 1); + g_assert(GVIR_CONFIG_IS_DOMAIN_CPU_FEATURE(cpu_features->data)); + feature = GVIR_CONFIG_DOMAIN_CPU_FEATURE(cpu_features->data); + g_assert_cmpstr(gvir_config_capabilities_cpu_feature_get_name(GVIR_CONFIG_CAPABILITIES_CPU_FEATURE(feature)), ==, "foo"); + g_assert_cmpint(gvir_config_domain_cpu_feature_get_policy(feature), ==, GVIR_CONFIG_DOMAIN_CPU_FEATURE_POLICY_REQUIRE); + g_list_free_full(cpu_features, g_object_unref); + + topology = gvir_config_capabilities_cpu_get_topology(GVIR_CONFIG_CAPABILITIES_CPU(cpu)); + g_assert(topology != NULL); + g_assert_cmpint(gvir_config_capabilities_cpu_topology_get_cores(topology), ==, 1); + g_assert_cmpint(gvir_config_capabilities_cpu_topology_get_sockets(topology), ==, 2); + g_assert_cmpint(gvir_config_capabilities_cpu_topology_get_threads(topology), ==, 3); + g_object_unref(topology); + g_object_unref(cpu); + + check_xml(domain, "gconfig-domain-cpu.xml"); + + cpu = gvir_config_domain_get_cpu(domain); + gvir_config_capabilities_cpu_set_topology(GVIR_CONFIG_CAPABILITIES_CPU(cpu), + NULL); + topology = gvir_config_capabilities_cpu_get_topology(GVIR_CONFIG_CAPABILITIES_CPU(cpu)); + g_assert(topology == NULL); + gvir_config_domain_set_cpu(domain, NULL); + cpu = gvir_config_domain_get_cpu(domain); + g_assert(cpu == NULL); +} + + +static void test_domain_device_disk(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainDisk *disk; + GVirConfigDomainDiskDriver *driver; + GList *disks; + + domain = gvir_config_domain_new(); + + driver = gvir_config_domain_disk_driver_new(); + gvir_config_domain_disk_driver_set_name(driver, "foo"); + gvir_config_domain_disk_driver_set_format(driver, GVIR_CONFIG_DOMAIN_DISK_FORMAT_BOCHS); + gvir_config_domain_disk_driver_set_name(driver, "qemu"); + gvir_config_domain_disk_driver_set_cache(driver, GVIR_CONFIG_DOMAIN_DISK_CACHE_NONE); + gvir_config_domain_disk_driver_set_format(driver, GVIR_CONFIG_DOMAIN_DISK_FORMAT_QCOW2); + gvir_config_domain_disk_driver_set_copy_on_read(driver, TRUE); + + disk = gvir_config_domain_disk_new(); + gvir_config_domain_disk_set_type(disk, GVIR_CONFIG_DOMAIN_DISK_FILE); + gvir_config_domain_disk_set_guest_device_type(disk, GVIR_CONFIG_DOMAIN_DISK_GUEST_DEVICE_DISK); + gvir_config_domain_disk_set_source(disk, "/tmp/foo/bar"); + gvir_config_domain_disk_set_startup_policy (disk, GVIR_CONFIG_DOMAIN_DISK_STARTUP_POLICY_REQUISITE); + gvir_config_domain_disk_set_target_bus(disk, GVIR_CONFIG_DOMAIN_DISK_BUS_IDE); + gvir_config_domain_disk_set_target_dev(disk, "hda"); + gvir_config_domain_disk_set_driver(disk, driver); + g_object_unref(G_OBJECT(driver)); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(disk)); + g_object_unref(disk); + + check_xml(domain, "gconfig-domain-device-disk.xml"); + + disks = get_devices_by_type(domain, GVIR_CONFIG_TYPE_DOMAIN_DISK); + g_assert_cmpint(g_list_length(disks), ==, 1); + g_assert(GVIR_CONFIG_IS_DOMAIN_DISK(disks->data)); + disk = GVIR_CONFIG_DOMAIN_DISK(disks->data); + g_assert_cmpint(gvir_config_domain_disk_get_disk_type(disk), ==, GVIR_CONFIG_DOMAIN_DISK_FILE); + g_assert_cmpint(gvir_config_domain_disk_get_guest_device_type(disk), ==, GVIR_CONFIG_DOMAIN_DISK_GUEST_DEVICE_DISK); + g_assert_cmpint(gvir_config_domain_disk_get_startup_policy (disk), ==, GVIR_CONFIG_DOMAIN_DISK_STARTUP_POLICY_REQUISITE); + g_assert_cmpstr(gvir_config_domain_disk_get_source(disk), ==, "/tmp/foo/bar"); + driver = gvir_config_domain_disk_get_driver(disk); + g_assert(driver != NULL); + g_assert_cmpint(gvir_config_domain_disk_driver_get_cache(driver), ==, GVIR_CONFIG_DOMAIN_DISK_CACHE_NONE); + g_assert_cmpstr(gvir_config_domain_disk_driver_get_name(driver), ==, "qemu"); + g_assert_cmpint(gvir_config_domain_disk_driver_get_format(driver), ==, GVIR_CONFIG_DOMAIN_DISK_FORMAT_QCOW2); + g_assert(gvir_config_domain_disk_driver_get_copy_on_read(driver)); + g_assert_cmpint(gvir_config_domain_disk_get_target_bus(disk), ==, GVIR_CONFIG_DOMAIN_DISK_BUS_IDE); + g_assert_cmpstr(gvir_config_domain_disk_get_target_dev(disk), ==, "hda"); + + gvir_config_domain_disk_set_driver(disk, NULL); + driver = gvir_config_domain_disk_get_driver(disk); + g_assert(driver == NULL); + + g_list_free_full(disks, g_object_unref); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_network(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainInterface *interface; + + domain = gvir_config_domain_new(); + + interface = GVIR_CONFIG_DOMAIN_INTERFACE(gvir_config_domain_interface_network_new()); + gvir_config_domain_interface_network_set_source(GVIR_CONFIG_DOMAIN_INTERFACE_NETWORK(interface), + "default"); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(interface)); + g_object_unref(G_OBJECT(interface)); + + interface = GVIR_CONFIG_DOMAIN_INTERFACE(gvir_config_domain_interface_user_new()); + gvir_config_domain_interface_set_ifname(interface, "eth0"); + gvir_config_domain_interface_set_link_state(interface, + GVIR_CONFIG_DOMAIN_INTERFACE_LINK_STATE_UP); + gvir_config_domain_interface_set_mac(interface, "00:11:22:33:44:55"); + gvir_config_domain_interface_set_model(interface, "foo"); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(interface)); + g_object_unref(G_OBJECT(interface)); + + check_xml(domain, "gconfig-domain-device-network.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_input(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainInput *input; + GList *inputs; + + domain = gvir_config_domain_new(); + + input = gvir_config_domain_input_new(); + gvir_config_domain_input_set_device_type(input, + GVIR_CONFIG_DOMAIN_INPUT_DEVICE_TABLET); + gvir_config_domain_input_set_bus(input, GVIR_CONFIG_DOMAIN_INPUT_BUS_USB); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(input)); + + check_xml(domain, "gconfig-domain-device-input.xml"); + + inputs = get_devices_by_type(domain, GVIR_CONFIG_TYPE_DOMAIN_INPUT); + g_assert_cmpint(g_list_length(inputs), ==, 1); + g_assert(GVIR_CONFIG_IS_DOMAIN_INPUT(inputs->data)); + input = GVIR_CONFIG_DOMAIN_INPUT(inputs->data); + g_assert_cmpint(gvir_config_domain_input_get_device_type(input), ==, GVIR_CONFIG_DOMAIN_INPUT_DEVICE_TABLET); + g_assert_cmpint(gvir_config_domain_input_get_bus(input), ==, GVIR_CONFIG_DOMAIN_INPUT_BUS_USB); + g_list_free_full(inputs, g_object_unref); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_graphics(void) +{ + GVirConfigDomain *domain; + GVirConfigDomainGraphicsSpice *graphics; + + domain = gvir_config_domain_new(); + graphics = gvir_config_domain_graphics_spice_new(); + gvir_config_domain_graphics_spice_set_port(graphics, 1234); + g_assert_cmpint(gvir_config_domain_graphics_spice_get_port(graphics), ==, 1234); + + /* SPICE image compression configuration */ + gvir_config_domain_graphics_spice_set_image_compression + (graphics, GVIR_CONFIG_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_AUTO_LZ); + g_assert_cmpint(gvir_config_domain_graphics_spice_get_image_compression(graphics), ==, + GVIR_CONFIG_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_AUTO_LZ); + + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(graphics)); + g_object_unref(G_OBJECT(graphics)); + + check_xml(domain, "gconfig-domain-device-graphics.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_video(void) +{ + GVirConfigDomain *domain; + + domain = gvir_config_domain_new(); + + /* video node */ + GVirConfigDomainVideo *video; + + video = gvir_config_domain_video_new(); + gvir_config_domain_video_set_model(video, + GVIR_CONFIG_DOMAIN_VIDEO_MODEL_QXL); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(video)); + g_object_unref(G_OBJECT(video)); + + check_xml(domain, "gconfig-domain-device-video.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_sound(void) +{ + GVirConfigDomain *domain; + + domain = gvir_config_domain_new(); + /* sound node */ + GVirConfigDomainSound *sound; + + sound = gvir_config_domain_sound_new(); + gvir_config_domain_sound_set_model(sound, + GVIR_CONFIG_DOMAIN_SOUND_MODEL_ES1370); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(sound)); + g_object_unref(G_OBJECT(sound)); + + check_xml(domain, "gconfig-domain-device-sound.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_console(void) +{ + GVirConfigDomain *domain; + + domain = gvir_config_domain_new(); + /* console node */ + GVirConfigDomainConsole *console; + GVirConfigDomainChardevSourcePty *pty; + + console = gvir_config_domain_console_new(); + pty = gvir_config_domain_chardev_source_pty_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(console), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(pty)); + g_object_unref(G_OBJECT(pty)); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(console)); + g_object_unref(G_OBJECT(console)); + + check_xml(domain, "gconfig-domain-device-console.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static void test_domain_device_channel(void) +{ + GVirConfigDomain *domain; + + domain = gvir_config_domain_new(); + /* spice agent channel */ + GVirConfigDomainChannel *channel; + GVirConfigDomainChardevSourceSpiceVmc *spicevmc; + + channel = gvir_config_domain_channel_new(); + gvir_config_domain_channel_set_target_type(channel, + GVIR_CONFIG_DOMAIN_CHANNEL_TARGET_VIRTIO); + spicevmc = gvir_config_domain_chardev_source_spicevmc_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(channel), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(spicevmc)); + g_object_unref(G_OBJECT(spicevmc)); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(channel)); + g_object_unref(G_OBJECT(channel)); + + check_xml(domain, "gconfig-domain-device-channel.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +static GVirConfigDomainControllerUsb * +domain_device_usb_redir_create_usb_controller(GVirConfigDomainControllerUsbModel model, + guint indx, + GVirConfigDomainControllerUsb *master, + guint start_port, + guint domain, guint bus, + guint slot, guint function, + gboolean multifunction) +{ + GVirConfigDomainControllerUsb *controller; + GVirConfigDomainAddressPci *address; + + controller = gvir_config_domain_controller_usb_new(); + gvir_config_domain_controller_usb_set_model(controller, model); + gvir_config_domain_controller_set_index(GVIR_CONFIG_DOMAIN_CONTROLLER(controller), indx); + if (master) + gvir_config_domain_controller_usb_set_master(controller, master, start_port); + address = gvir_config_domain_address_pci_new(); + gvir_config_domain_address_pci_set_domain(address, domain); + gvir_config_domain_address_pci_set_bus(address, bus); + gvir_config_domain_address_pci_set_slot(address, slot); + gvir_config_domain_address_pci_set_function(address, function); + if (multifunction) + gvir_config_domain_address_pci_set_multifunction(address, multifunction); + gvir_config_domain_controller_set_address(GVIR_CONFIG_DOMAIN_CONTROLLER(controller), + GVIR_CONFIG_DOMAIN_ADDRESS(address)); + g_object_unref(G_OBJECT(address)); + + return controller; +} + + +static GVirConfigDomainRedirdev * +domain_device_usb_redir_create_redirdev(guint bus, guint port) +{ + GVirConfigDomainRedirdev *redirdev; + GVirConfigDomainAddressUsb *address; + GVirConfigDomainChardevSourceSpiceVmc *spicevmc; + gchar *port_str; + + redirdev = gvir_config_domain_redirdev_new(); + gvir_config_domain_redirdev_set_bus(redirdev, + GVIR_CONFIG_DOMAIN_REDIRDEV_BUS_USB); + spicevmc = gvir_config_domain_chardev_source_spicevmc_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(redirdev), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(spicevmc)); + g_object_unref(G_OBJECT(spicevmc)); + + address = gvir_config_domain_address_usb_new(); + gvir_config_domain_address_usb_set_bus(address, bus); + port_str = g_strdup_printf("%d", port); + gvir_config_domain_address_usb_set_port(address, port_str); + g_free(port_str); + gvir_config_domain_redirdev_set_address(redirdev, + GVIR_CONFIG_DOMAIN_ADDRESS(address)); + g_object_unref(G_OBJECT(address)); + + return redirdev; +} + + +static void test_domain_device_usb_redir(void) +{ + GVirConfigDomain *domain; + + domain = gvir_config_domain_new(); + /* spice usb redirection */ + GVirConfigDomainControllerUsb *ehci; + GVirConfigDomainControllerUsb *uhci1; + GVirConfigDomainControllerUsb *uhci2; + GVirConfigDomainControllerUsb *uhci3; + GVirConfigDomainRedirdev *redirdev; + + ehci = domain_device_usb_redir_create_usb_controller(GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_EHCI1, + 1, NULL, 0, 0, 0, 8, 7, FALSE); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(ehci)); + uhci1 = domain_device_usb_redir_create_usb_controller(GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI1, + 7, ehci, 0, 0, 0, 8, 0, TRUE); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(uhci1)); + uhci2 = domain_device_usb_redir_create_usb_controller(GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI2, + 7, ehci, 2, 0, 0, 8, 1, FALSE); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(uhci2)); + uhci3 = domain_device_usb_redir_create_usb_controller(GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI3, + 7, ehci, 4, 0, 0, 8, 2, FALSE); + g_assert_cmpint(gvir_config_domain_controller_get_index(GVIR_CONFIG_DOMAIN_CONTROLLER(uhci1)), ==, 1); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(uhci3)); + g_object_unref(G_OBJECT(uhci1)); + g_object_unref(G_OBJECT(uhci2)); + g_object_unref(G_OBJECT(uhci3)); + g_object_unref(G_OBJECT(ehci)); + + + /* three redirdev channels allows to redirect a maximum of 3 USB + * devices at a time. The address which create_redirdev assigns to the + * redirdev object is optional + */ + redirdev = domain_device_usb_redir_create_redirdev(0, 3); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(redirdev)); + g_object_unref(G_OBJECT(redirdev)); + redirdev = domain_device_usb_redir_create_redirdev(0, 4); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(redirdev)); + g_object_unref(G_OBJECT(redirdev)); + redirdev = domain_device_usb_redir_create_redirdev(0, 5); + gvir_config_domain_add_device(domain, GVIR_CONFIG_DOMAIN_DEVICE(redirdev)); + g_object_unref(G_OBJECT(redirdev)); + + check_xml(domain, "gconfig-domain-device-usbredir.xml"); + + g_object_unref(G_OBJECT(domain)); +} + + +int main(int argc, char **argv) +{ + gvir_config_init(&argc, &argv); + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/libvirt-gconfig/domain", test_domain); + g_test_add_func("/libvirt-gconfig/domain-clock", test_domain_clock); + g_test_add_func("/libvirt-gconfig/domain-os", test_domain_os); + g_test_add_func("/libvirt-gconfig/domain-cpu", test_domain_cpu); + g_test_add_func("/libvirt-gconfig/domain-device-disk", + test_domain_device_disk); + g_test_add_func("/libvirt-gconfig/domain-device-network", + test_domain_device_network); + g_test_add_func("/libvirt-gconfig/domain-device-input", + test_domain_device_input); + g_test_add_func("/libvirt-gconfig/domain-device-graphics", + test_domain_device_graphics); + g_test_add_func("/libvirt-gconfig/domain-device-video", + test_domain_device_video); + g_test_add_func("/libvirt-gconfig/domain-device-sound", + test_domain_device_sound); + g_test_add_func("/libvirt-gconfig/domain-device-console", + test_domain_device_console); + g_test_add_func("/libvirt-gconfig/domain-device-channel", + test_domain_device_channel); + g_test_add_func("/libvirt-gconfig/domain-device-usb-redir", + test_domain_device_usb_redir); + + return g_test_run(); +} diff --git a/tests/xml/gconfig-domain-clock.xml b/tests/xml/gconfig-domain-clock.xml new file mode 100644 index 0000000..ef06a0c --- /dev/null +++ b/tests/xml/gconfig-domain-clock.xml @@ -0,0 +1,7 @@ +<domain> + <clock offset="timezone" timezone="CEST"> + <timer name="pit" tickpolicy="delay"/> + <timer name="rtc" tickpolicy="catchup"/> + <timer name="hpet" present="no"/> + </clock> +</domain> diff --git a/tests/xml/gconfig-domain-cpu.xml b/tests/xml/gconfig-domain-cpu.xml new file mode 100644 index 0000000..cf0a5dd --- /dev/null +++ b/tests/xml/gconfig-domain-cpu.xml @@ -0,0 +1,7 @@ +<domain> + <vcpu>3</vcpu> + <cpu match="strict" mode="host-passthrough"> + <feature name="foo" policy="require"/> + <topology cores="1" sockets="2" threads="3"/> + </cpu> +</domain> diff --git a/tests/xml/gconfig-domain-device-channel.xml b/tests/xml/gconfig-domain-device-channel.xml new file mode 100644 index 0000000..7963b89 --- /dev/null +++ b/tests/xml/gconfig-domain-device-channel.xml @@ -0,0 +1,7 @@ +<domain> + <devices> + <channel type="spicevmc"> + <target type="virtio"/> + </channel> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-console.xml b/tests/xml/gconfig-domain-device-console.xml new file mode 100644 index 0000000..5cb2f21 --- /dev/null +++ b/tests/xml/gconfig-domain-device-console.xml @@ -0,0 +1,5 @@ +<domain> + <devices> + <console type="pty"/> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-disk.xml b/tests/xml/gconfig-domain-device-disk.xml new file mode 100644 index 0000000..45d39bf --- /dev/null +++ b/tests/xml/gconfig-domain-device-disk.xml @@ -0,0 +1,9 @@ +<domain> + <devices> + <disk type="file" device="disk"> + <source file="/tmp/foo/bar" startupPolicy="requisite"/> + <target bus="ide" dev="hda"/> + <driver name="qemu" cache="none" type="qcow2" copy_on_read="on"/> + </disk> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-graphics.xml b/tests/xml/gconfig-domain-device-graphics.xml new file mode 100644 index 0000000..5d90c5c --- /dev/null +++ b/tests/xml/gconfig-domain-device-graphics.xml @@ -0,0 +1,7 @@ +<domain> + <devices> + <graphics type="spice" port="1234"> + <image compression="auto_lz"/> + </graphics> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-input.xml b/tests/xml/gconfig-domain-device-input.xml new file mode 100644 index 0000000..41a3ee5 --- /dev/null +++ b/tests/xml/gconfig-domain-device-input.xml @@ -0,0 +1,5 @@ +<domain> + <devices> + <input type="tablet" bus="usb"/> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-network.xml b/tests/xml/gconfig-domain-device-network.xml new file mode 100644 index 0000000..67f2c88 --- /dev/null +++ b/tests/xml/gconfig-domain-device-network.xml @@ -0,0 +1,13 @@ +<domain> + <devices> + <interface type="network"> + <source network="default"/> + </interface> + <interface type="user"> + <target dev="eth0"/> + <link state="up"/> + <mac address="00:11:22:33:44:55"/> + <model type="foo"/> + </interface> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-sound.xml b/tests/xml/gconfig-domain-device-sound.xml new file mode 100644 index 0000000..3f1120d --- /dev/null +++ b/tests/xml/gconfig-domain-device-sound.xml @@ -0,0 +1,5 @@ +<domain> + <devices> + <sound model="es1370"/> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-usbredir.xml b/tests/xml/gconfig-domain-device-usbredir.xml new file mode 100644 index 0000000..ff9c5e1 --- /dev/null +++ b/tests/xml/gconfig-domain-device-usbredir.xml @@ -0,0 +1,28 @@ +<domain> + <devices> + <controller type="usb" model="ich9-ehci1" index="1"> + <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x7"/> + </controller> + <controller type="usb" model="ich9-uhci1" index="1"> + <master startport="0"/> + <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x0" multifunction="on"/> + </controller> + <controller type="usb" model="ich9-uhci2" index="1"> + <master startport="2"/> + <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x1"/> + </controller> + <controller type="usb" model="ich9-uhci3" index="1"> + <master startport="4"/> + <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x2"/> + </controller> + <redirdev type="spicevmc" bus="usb"> + <address type="usb" bus="0x000" port="3"/> + </redirdev> + <redirdev type="spicevmc" bus="usb"> + <address type="usb" bus="0x000" port="4"/> + </redirdev> + <redirdev type="spicevmc" bus="usb"> + <address type="usb" bus="0x000" port="5"/> + </redirdev> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-device-video.xml b/tests/xml/gconfig-domain-device-video.xml new file mode 100644 index 0000000..a6155e2 --- /dev/null +++ b/tests/xml/gconfig-domain-device-video.xml @@ -0,0 +1,7 @@ +<domain> + <devices> + <video> + <model type="qxl"/> + </video> + </devices> +</domain> diff --git a/tests/xml/gconfig-domain-os.xml b/tests/xml/gconfig-domain-os.xml new file mode 100644 index 0000000..f712bb0 --- /dev/null +++ b/tests/xml/gconfig-domain-os.xml @@ -0,0 +1,7 @@ +<domain> + <os> + <type arch="x86_64">hvm</type> + <boot dev="cdrom"/> + <boot dev="network"/> + </os> +</domain> diff --git a/tests/xml/gconfig-domain.xml b/tests/xml/gconfig-domain.xml new file mode 100644 index 0000000..4118487 --- /dev/null +++ b/tests/xml/gconfig-domain.xml @@ -0,0 +1,15 @@ +<domain type="kvm"> + <name>foo</name> + <memory unit="KiB">1234</memory> + <vcpu>3</vcpu> + <features> + <foo/> + <bar/> + <baz/> + </features> + <on_poweroff>restart</on_poweroff> + <metadata> + <ns:foo xmlns:ns="http://bar"/> + <ns:bar xmlns:ns="http://foo"/> + </metadata> +</domain> -- 1.8.5.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list