* [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux @ 2015-09-11 18:53 marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable marcandre.lureau ` (4 more replies) 0 siblings, 5 replies; 11+ messages in thread From: marcandre.lureau @ 2015-09-11 18:53 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, mdroth From: Marc-André Lureau <marcandre.lureau@redhat.com> v1->v2: - do not loop forever when connection to qga (Michael Roth) - add some config file tests - fix config verbosity - swallow virt-what stderr - only compile and check on Linux Marc-André Lureau (4): qga: add QGA_CONF environment variable qga: do not override configuration verbosity qtest: add a few fd-level qmp helpers tests: add a local test for guest agent qga/main.c | 7 +- tests/Makefile | 3 + tests/libqtest.c | 45 +++- tests/libqtest.h | 7 + tests/test-qga.c | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 846 insertions(+), 7 deletions(-) create mode 100644 tests/test-qga.c -- 2.4.3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau @ 2015-09-11 18:53 ` marcandre.lureau 2015-10-01 20:27 ` Michael Roth 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity marcandre.lureau ` (3 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: marcandre.lureau @ 2015-09-11 18:53 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, mdroth From: Marc-André Lureau <marcandre.lureau@redhat.com> Having a environment variable allows to override default configuration path, useful for testing. Note that this can't easily be an argument, since loading config is done before parsing the arguments. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- qga/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index d8e063a..18e1e1d 100644 --- a/qga/main.c +++ b/qga/main.c @@ -945,10 +945,11 @@ static void config_load(GAConfig *config) { GError *gerr = NULL; GKeyFile *keyfile; + const char *conf = g_getenv("QGA_CONF") ?: QGA_CONF_DEFAULT; /* read system config */ keyfile = g_key_file_new(); - if (!g_key_file_load_from_file(keyfile, QGA_CONF_DEFAULT, 0, &gerr)) { + if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) { goto end; } if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) { -- 2.4.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable marcandre.lureau @ 2015-10-01 20:27 ` Michael Roth 0 siblings, 0 replies; 11+ messages in thread From: Michael Roth @ 2015-10-01 20:27 UTC (permalink / raw) To: marcandre.lureau, qemu-devel Quoting marcandre.lureau@redhat.com (2015-09-11 13:53:38) > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Having a environment variable allows to override default configuration > path, useful for testing. Note that this can't easily be an argument, > since loading config is done before parsing the arguments. I'd rather this be fixed by adding a config_parse_early() prior config_load() to handle a command-line specification of config file path, but I don't object to an environment variable to specify it in lieu of that so: > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com> > --- > qga/main.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/qga/main.c b/qga/main.c > index d8e063a..18e1e1d 100644 > --- a/qga/main.c > +++ b/qga/main.c > @@ -945,10 +945,11 @@ static void config_load(GAConfig *config) > { > GError *gerr = NULL; > GKeyFile *keyfile; > + const char *conf = g_getenv("QGA_CONF") ?: QGA_CONF_DEFAULT; > > /* read system config */ > keyfile = g_key_file_new(); > - if (!g_key_file_load_from_file(keyfile, QGA_CONF_DEFAULT, 0, &gerr)) { > + if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) { > goto end; > } > if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) { > -- > 2.4.3 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable marcandre.lureau @ 2015-09-11 18:53 ` marcandre.lureau 2015-10-01 20:38 ` Michael Roth 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 3/4] qtest: add a few fd-level qmp helpers marcandre.lureau ` (2 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: marcandre.lureau @ 2015-09-11 18:53 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, mdroth From: Marc-André Lureau <marcandre.lureau@redhat.com> Move the default verbosity settings before loading the configuration file, or it will overwrite it. Found thanks to writing qga tests :) Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- qga/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qga/main.c b/qga/main.c index 18e1e1d..aa6a063 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1083,8 +1083,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) { NULL, 0, NULL, 0 } }; - config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; - while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'm': @@ -1332,6 +1330,8 @@ int main(int argc, char **argv) GAState *s = g_new0(GAState, 1); GAConfig *config = g_new0(GAConfig, 1); + config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; + module_call_init(MODULE_INIT_QAPI); init_dfl_pathnames(); -- 2.4.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity marcandre.lureau @ 2015-10-01 20:38 ` Michael Roth 0 siblings, 0 replies; 11+ messages in thread From: Michael Roth @ 2015-10-01 20:38 UTC (permalink / raw) To: marcandre.lureau, qemu-devel Quoting marcandre.lureau@redhat.com (2015-09-11 13:53:39) > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Move the default verbosity settings before loading the configuration > file, or it will overwrite it. Found thanks to writing qga tests :) > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > qga/main.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/qga/main.c b/qga/main.c > index 18e1e1d..aa6a063 100644 > --- a/qga/main.c > +++ b/qga/main.c > @@ -1083,8 +1083,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) > { NULL, 0, NULL, 0 } > }; > > - config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; > - > while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { > switch (ch) { > case 'm': > @@ -1332,6 +1330,8 @@ int main(int argc, char **argv) > GAState *s = g_new0(GAState, 1); > GAConfig *config = g_new0(GAConfig, 1); > > + config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; > + We currently handle config->state_dir/method/channel_path defaults after config_parse via if (!config->blah) config->blah = ...; I'd rather we do it for this too for consistency, but not sure we have a reliable indicator for determining whether or not config->log_level is unset (GLogLevelFlags enum doesn't have a 0 value so that would work, but it's hacky to make such an assumption). This avoids any hacks so it's worth the trade-off in terms of consistency so: Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com> > module_call_init(MODULE_INIT_QAPI); > > init_dfl_pathnames(); > -- > 2.4.3 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH v2 3/4] qtest: add a few fd-level qmp helpers 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity marcandre.lureau @ 2015-09-11 18:53 ` marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent marcandre.lureau 2015-09-25 10:15 ` [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux Marc-André Lureau 4 siblings, 0 replies; 11+ messages in thread From: marcandre.lureau @ 2015-09-11 18:53 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, mdroth From: Marc-André Lureau <marcandre.lureau@redhat.com> Add a few functions to interact with qmp via a simple fd. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com> --- tests/libqtest.c | 45 +++++++++++++++++++++++++++++++++++++++++---- tests/libqtest.h | 7 +++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/libqtest.c b/tests/libqtest.c index e5188e0..11e541d 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -357,7 +357,7 @@ static void qmp_response(JSONMessageParser *parser, QList *tokens) qmp->response = (QDict *)obj; } -QDict *qtest_qmp_receive(QTestState *s) +QDict *qmp_fd_receive(int fd) { QMPResponseParser qmp; bool log = getenv("QTEST_LOG") != NULL; @@ -368,7 +368,7 @@ QDict *qtest_qmp_receive(QTestState *s) ssize_t len; char c; - len = read(s->qmp_fd, &c, 1); + len = read(fd, &c, 1); if (len == -1 && errno == EINTR) { continue; } @@ -388,12 +388,17 @@ QDict *qtest_qmp_receive(QTestState *s) return qmp.response; } +QDict *qtest_qmp_receive(QTestState *s) +{ + return qmp_fd_receive(s->qmp_fd); +} + /** * Allow users to send a message without waiting for the reply, * in the case that they choose to discard all replies up until * a particular EVENT is received. */ -void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) +void qmp_fd_sendv(int fd, const char *fmt, va_list ap) { va_list ap_copy; QObject *qobj; @@ -417,13 +422,25 @@ void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) fprintf(stderr, "%s", str); } /* Send QMP request */ - socket_send(s->qmp_fd, str, size); + socket_send(fd, str, size); QDECREF(qstr); qobject_decref(qobj); } } +void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) +{ + qmp_fd_sendv(s->qmp_fd, fmt, ap); +} + +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) +{ + qmp_fd_sendv(fd, fmt, ap); + + return qmp_fd_receive(fd); +} + QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) { qtest_async_qmpv(s, fmt, ap); @@ -432,6 +449,26 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) return qtest_qmp_receive(s); } +QDict *qmp_fd(int fd, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qmp_fdv(fd, fmt, ap); + va_end(ap); + return response; +} + +void qmp_fd_send(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_sendv(fd, fmt, ap); + va_end(ap); +} + QDict *qtest_qmp(QTestState *s, const char *fmt, ...) { va_list ap; diff --git a/tests/libqtest.h b/tests/libqtest.h index ec42031..ecd9872 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -818,4 +818,11 @@ static inline int64_t clock_set(int64_t val) */ bool qtest_big_endian(void); + +QDict *qmp_fd_receive(int fd); +void qmp_fd_sendv(int fd, const char *fmt, va_list ap); +void qmp_fd_send(int fd, const char *fmt, ...); +QDict *qmp_fdv(int fd, const char *fmt, va_list ap); +QDict *qmp_fd(int fd, const char *fmt, ...); + #endif -- 2.4.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau ` (2 preceding siblings ...) 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 3/4] qtest: add a few fd-level qmp helpers marcandre.lureau @ 2015-09-11 18:53 ` marcandre.lureau 2015-10-01 21:36 ` Michael Roth 2015-09-25 10:15 ` [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux Marc-André Lureau 4 siblings, 1 reply; 11+ messages in thread From: marcandre.lureau @ 2015-09-11 18:53 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, mdroth From: Marc-André Lureau <marcandre.lureau@redhat.com> Add some local guest agent tests (as it is better than nothing) only when CONFIG_LINUX. They can be run inside or outside a VM, when run inside a VM, they will do a bit more side effects, such as freezing/thawing the FS or changing the time. A better test would involve a managed VM (or container), but it might be better to leave that off to autotest/avocado. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- tests/Makefile | 3 + tests/test-qga.c | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 794 insertions(+) create mode 100644 tests/test-qga.c diff --git a/tests/Makefile b/tests/Makefile index 34c6136..d5837a4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -76,6 +76,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) +check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -432,6 +433,8 @@ endif qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a $(check-qtest-y): $(qtest-obj-y) +tests/test-qga: tests/test-qga.o $(qtest-obj-y) + .PHONY: check-help check-help: @echo "Regression testing targets:" diff --git a/tests/test-qga.c b/tests/test-qga.c new file mode 100644 index 0000000..cfee134 --- /dev/null +++ b/tests/test-qga.c @@ -0,0 +1,791 @@ +#include <locale.h> +#include <glib.h> +#include <glib/gstdio.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include "libqtest.h" +#include "config-host.h" + +typedef struct { + char *test_dir; + GMainLoop *loop; + int fd; + GPid pid; +} TestFixture; + +static int connect_qga(char *path) +{ + int s, ret, len, i = 0; + struct sockaddr_un remote; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + g_assert(s != -1); + + remote.sun_family = AF_UNIX; + do { + strcpy(remote.sun_path, path); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + ret = connect(s, (struct sockaddr *)&remote, len); + if (ret == -1) { + g_usleep(G_USEC_PER_SEC); + } + if (i++ == 5) { + return -1; + } + } while (ret == -1); + + return s; +} + +static void qga_watch(GPid pid, gint status, gpointer user_data) +{ + TestFixture *fixture = user_data; + + g_assert_cmpint(status, ==, 0); + g_main_loop_quit(fixture->loop); +} + +static void +fixture_setup(TestFixture *fixture, gconstpointer data) +{ + const gchar *extra_arg = data; + GError *error = NULL; + gchar *cwd, *path, *cmd, **argv = NULL; + + fixture->loop = g_main_loop_new(NULL, FALSE); + + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); + g_assert_nonnull(g_mkdtemp(fixture->test_dir)); + + path = g_build_filename(fixture->test_dir, "sock", NULL); + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqemu-ga -m unix-listen -t %s -p %s %s %s", + cwd, G_DIR_SEPARATOR, + fixture->test_dir, path, + getenv("QTEST_LOG") ? "-v" : "", + extra_arg ?: ""); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_assert_no_error(error); + + g_spawn_async(fixture->test_dir, argv, NULL, + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &fixture->pid, &error); + g_assert_no_error(error); + + g_child_watch_add(fixture->pid, qga_watch, fixture); + + fixture->fd = connect_qga(path); + + g_strfreev(argv); + g_free(cmd); + g_free(cwd); + g_free(path); +} + +static void +fixture_tear_down(TestFixture *fixture, gconstpointer data) +{ + gchar *tmp; + + kill(fixture->pid, SIGTERM); + + g_main_loop_run(fixture->loop); + g_main_loop_unref(fixture->loop); + + g_spawn_close_pid(fixture->pid); + + tmp = g_build_filename(fixture->test_dir, "foo", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "sock", NULL); + g_unlink(tmp); + g_free(tmp); + + g_rmdir(fixture->test_dir); + g_free(fixture->test_dir); +} + +static void qmp_assertion_message_error(const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + QDict *dict) +{ + const char *class, *desc; + char *s; + QDict *error; + + error = qdict_get_qdict(dict, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); + g_assertion_message(domain, file, line, func, s); + g_free(s); +} + +#define qmp_assert_no_error(err) do { \ + if (qdict_haskey(err, "error")) { \ + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ + G_STRFUNC, #err, err); \ + } \ +} while (0) + +static void test_qga_sync_delimited(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_random_int(); + unsigned char c; + QDict *ret; + gchar *cmd; + + cmd = g_strdup_printf("%c{'execute': 'guest-sync-delimited'," + " 'arguments': {'id': %u } }", 0xff, r); + qmp_fd_send(fixture->fd, cmd); + g_free(cmd); + + v = read(fixture->fd, &c, 1); + g_assert_cmpint(v, ==, 1); + g_assert_cmpint(c, ==, 0xff); + + ret = qmp_fd_receive(fixture->fd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + QDECREF(ret); +} + +static void test_qga_sync(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_random_int(); + QDict *ret; + gchar *cmd; + + cmd = g_strdup_printf("%c{'execute': 'guest-sync'," + " 'arguments': {'id': %u } }", 0xff, r); + ret = qmp_fd(fixture->fd, cmd); + g_free(cmd); + + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + QDECREF(ret); +} + +static void test_qga_ping(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + QDECREF(ret); +} + +static void test_qga_invalid_cmd(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const gchar *class, *desc; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_cmpint(strlen(desc), >, 0); + + QDECREF(ret); +} + +static void test_qga_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + const gchar *version; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + version = qdict_get_try_str(val, "version"); + g_assert_cmpstr(version, ==, QEMU_VERSION); + + QDECREF(ret); +} + +static void test_qga_get_vcpus(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least a cpu */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id")); + + QDECREF(ret); +} + +static void test_qga_get_fsinfo(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least a fs */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint")); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type")); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk")); + + QDECREF(ret); +} + +static void test_qga_get_memory_block_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + int64_t size; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least some memory */ + val = qdict_get_qdict(ret, "return"); + size = qdict_get_int(val, "size"); + g_assert_cmpint(size, >, 0); + + QDECREF(ret); +} + +static void test_qga_get_memory_blocks(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index")); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); + + QDECREF(ret); +} + +static void test_qga_network_get_interfaces(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least a cpu */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); + + QDECREF(ret); +} + +static void test_qga_file_ops(gconstpointer fix) +{ + const TestFixture *fixture = fix; + gchar *cmd, *path; + const gchar *b64; + QDict *ret, *val; + int64_t id, count, eof; + FILE *f; + char tmp[100]; + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + QDECREF(ret); + + /* write */ + cmd = g_strdup_printf("{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" G_GINT64_FORMAT "," + " 'buf-b64': 'aGVsbG8gd29ybGQK' } }", id); + ret = qmp_fd(fixture->fd, cmd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 12); + g_assert_cmpint(eof, ==, 0); + QDECREF(ret); + g_free(cmd); + + /* flush */ + cmd = g_strdup_printf("{'execute': 'guest-file-flush'," + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); + + /* close */ + cmd = g_strdup_printf("{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); + + /* check content */ + path = g_build_filename(fixture->test_dir, "foo", NULL); + f = fopen(path, "r"); + g_assert_nonnull(f); + count = fread(tmp, 1, sizeof(tmp), f); + g_assert_cmpint(count, ==, 12); + tmp[count] = 0; + g_assert_cmpstr(tmp, ==, "hello world\n"); + fclose(f); + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + QDECREF(ret); + + /* read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 12); + g_assert(eof); + g_assert_cmpstr(b64, ==, "aGVsbG8gd29ybGQK"); + + QDECREF(ret); + g_free(cmd); + + /* read eof */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); + QDECREF(ret); + g_free(cmd); + + /* seek */ + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" G_GINT64_FORMAT ", " + " 'offset': %d, 'whence': %d } }", + id, 6, SEEK_SET); + ret = qmp_fd(fixture->fd, cmd); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "position"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 6); + g_assert(!eof); + QDECREF(ret); + g_free(cmd); + + /* partial read */ + cmd = g_strdup_printf("{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 6); + g_assert(eof); + g_assert_cmpstr(b64, ==, "d29ybGQK"); + + QDECREF(ret); + g_free(cmd); + + /* close */ + cmd = g_strdup_printf("{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", + id); + ret = qmp_fd(fixture->fd, cmd); + QDECREF(ret); + g_free(cmd); +} + +static void test_qga_get_time(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + int64_t time; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time, >, 0); + + QDECREF(ret); +} + +static void test_qga_set_time(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + int64_t current, time; + gchar *cmd; + + /* get current time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + current = qdict_get_int(ret, "return"); + g_assert_cmpint(current, >, 0); + QDECREF(ret); + + /* set some old time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-set-time'," + " 'arguments': { 'time': 1000 } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); + + /* check old time */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time / 1000, <, G_USEC_PER_SEC * 10); + QDECREF(ret); + + /* set back current time */ + cmd = g_strdup_printf("{'execute': 'guest-set-time'," + " 'arguments': { 'time': %" G_GINT64_FORMAT " } }", + current + time * 1000); + ret = qmp_fd(fixture->fd, cmd); + g_free(cmd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); +} + +static void test_qga_fstrim(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fstrim'," + " arguments: { minimum: 4194304 } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "paths")); + + QDECREF(ret); +} + +static void test_qga_blacklist(gconstpointer data) +{ + TestFixture fix; + QDict *ret, *error; + const gchar *class, *desc; + + fixture_setup(&fix, "-b guest-ping,guest-get-time"); + + /* check blacklist */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + QDECREF(ret); + + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + QDECREF(ret); + + /* check something work */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); + qmp_assert_no_error(ret); + QDECREF(ret); + + fixture_tear_down(&fix, NULL); +} + +#if !GLIB_CHECK_VERSION(2, 38, 0) +#define g_assert_true(E) g_assert(E) +#define g_assert_false(E) g_assert(!E) +#endif + +static void test_qga_config(gconstpointer data) +{ + GError *error = NULL; + char *cwd, *cmd, *out, *err, *str, **strv, *conf, **argv = NULL; + char *env[2]; + int status, tmp; + gsize n; + GKeyFile *kf; + const char *qga_config = + "[general]\n" + "daemon=false\n" + "method=virtio-serial\n" + "path=/path/to/org.qemu.guest_agent.0\n" + "pidfile=/var/foo/qemu-ga.pid\n" + "statedir=/var/state\n" + "verbose=true\n" + "blacklist=guest-ping;guest-get-time\n"; + + tmp = g_file_open_tmp(NULL, &conf, &error); + g_assert_no_error(error); + g_assert_cmpint(tmp, >=, 0); + g_assert_cmpstr(conf, !=, ""); + + g_file_set_contents(conf, qga_config, -1, &error); + g_assert_no_error(error); + + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqemu-ga -D", + cwd, G_DIR_SEPARATOR); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_assert_no_error(error); + + env[0] = g_strdup_printf("QGA_CONF=%s", conf); + env[1] = NULL; + g_spawn_sync(NULL, argv, env, G_SPAWN_DEFAULT, + NULL, NULL, &out, &err, &status, &error); + g_assert_no_error(error); + g_assert_cmpstr(err, ==, ""); + g_assert_cmpint(status, ==, 0); + + kf = g_key_file_new(); + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); + g_assert_no_error(error); + + str = g_key_file_get_start_group(kf); + g_assert_cmpstr(str, ==, "general"); + g_free(str); + + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); + g_assert_no_error(error); + + str = g_key_file_get_string(kf, "general", "method", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "virtio-serial"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "path", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "pidfile", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "statedir", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/state"); + g_free(str); + + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); + g_assert_no_error(error); + + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); + g_assert_cmpint(n, ==, 2); +#if GLIB_CHECK_VERSION(2, 44, 0) + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-ping")); + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-get-time")); +#endif + g_assert_no_error(error); + g_strfreev(strv); + + g_free(out); + g_free(err); + g_free(conf); + g_free(env[0]); + g_key_file_free(kf); + + close(tmp); +} + +static void test_qga_fsfreeze_status(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + const gchar *status; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "thawed"); + + QDECREF(ret); +} + +static void test_qga_fsfreeze_and_thaw(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + const gchar *status; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-freeze'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "frozen"); + QDECREF(ret); + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-thaw'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + QDECREF(ret); +} + +static gboolean running_in_virt(const gchar *virt) +{ + gchar *virt_what = NULL; + gchar *err = NULL; + gint status; + gboolean success; + + success = g_spawn_command_line_sync("virt-what", &virt_what, &err, + &status, NULL); + + success = success && status == 0 && + g_strcmp0(g_strstrip(virt_what), virt) == 0; + + g_free(virt_what); + g_free(err); + + return success; +} +int main(int argc, char **argv) +{ + TestFixture fix; + int ret; + + setlocale (LC_ALL, ""); + g_test_init(&argc, &argv, NULL); + fixture_setup(&fix, NULL); + + g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); + g_test_add_data_func("/qga/info", &fix, test_qga_info); + g_test_add_data_func("/qga/network-get-interfaces", &fix, + test_qga_network_get_interfaces); + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-memory-block-info", &fix, + test_qga_get_memory_block_info); + g_test_add_data_func("/qga/get-memory-blocks", &fix, + test_qga_get_memory_blocks); + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); + g_test_add_data_func("/qga/fsfreeze-status", &fix, + test_qga_fsfreeze_status); + + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); + g_test_add_data_func("/qga/config", NULL, test_qga_config); + + if (running_in_virt("kvm")) { + g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix, + test_qga_fsfreeze_and_thaw); + g_test_add_data_func("/qga/set-time", &fix, test_qga_set_time); + if (g_test_thorough()) { + g_test_add_data_func("/qga/fstrim", &fix, test_qga_fstrim); + } + } + + ret = g_test_run(); + + fixture_tear_down(&fix, NULL); + + return ret; +} -- 2.4.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent marcandre.lureau @ 2015-10-01 21:36 ` Michael Roth 2015-10-01 22:24 ` Marc-André Lureau 0 siblings, 1 reply; 11+ messages in thread From: Michael Roth @ 2015-10-01 21:36 UTC (permalink / raw) To: marcandre.lureau, qemu-devel Quoting marcandre.lureau@redhat.com (2015-09-11 13:53:41) > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Add some local guest agent tests (as it is better than nothing) only > when CONFIG_LINUX. > > They can be run inside or outside a VM, when run inside a VM, they will > do a bit more side effects, such as freezing/thawing the FS or changing > the time. > > A better test would involve a managed VM (or container), but it might be > better to leave that off to autotest/avocado. > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Sorry for the delay in getting to this. Have some review comments but will make sure to stay on top of any follow-ups as I'm trying to get this into my next pull. > --- > tests/Makefile | 3 + > tests/test-qga.c | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 794 insertions(+) > create mode 100644 tests/test-qga.c > > diff --git a/tests/Makefile b/tests/Makefile > index 34c6136..d5837a4 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -76,6 +76,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF) > gcov-files-test-write-threshold-y = block/write-threshold.c > check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) > check-unit-y += tests/test-crypto-cipher$(EXESUF) > +check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) > > check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh > > @@ -432,6 +433,8 @@ endif > qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a > $(check-qtest-y): $(qtest-obj-y) > > +tests/test-qga: tests/test-qga.o $(qtest-obj-y) > + > .PHONY: check-help > check-help: > @echo "Regression testing targets:" > diff --git a/tests/test-qga.c b/tests/test-qga.c > new file mode 100644 > index 0000000..cfee134 > --- /dev/null > +++ b/tests/test-qga.c > @@ -0,0 +1,791 @@ > +#include <locale.h> > +#include <glib.h> > +#include <glib/gstdio.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <errno.h> > +#include <string.h> > +#include <sys/types.h> > +#include <sys/socket.h> > +#include <sys/un.h> > +#include <unistd.h> > + > +#include "libqtest.h" > +#include "config-host.h" > + > +typedef struct { > + char *test_dir; > + GMainLoop *loop; > + int fd; > + GPid pid; > +} TestFixture; > + > +static int connect_qga(char *path) > +{ > + int s, ret, len, i = 0; > + struct sockaddr_un remote; > + > + s = socket(AF_UNIX, SOCK_STREAM, 0); > + g_assert(s != -1); > + > + remote.sun_family = AF_UNIX; > + do { > + strcpy(remote.sun_path, path); > + len = strlen(remote.sun_path) + sizeof(remote.sun_family); > + ret = connect(s, (struct sockaddr *)&remote, len); > + if (ret == -1) { > + g_usleep(G_USEC_PER_SEC); > + } > + if (i++ == 5) { > + return -1; > + } > + } while (ret == -1); > + > + return s; > +} > + > +static void qga_watch(GPid pid, gint status, gpointer user_data) > +{ > + TestFixture *fixture = user_data; > + > + g_assert_cmpint(status, ==, 0); > + g_main_loop_quit(fixture->loop); > +} > + > +static void > +fixture_setup(TestFixture *fixture, gconstpointer data) > +{ > + const gchar *extra_arg = data; > + GError *error = NULL; > + gchar *cwd, *path, *cmd, **argv = NULL; > + > + fixture->loop = g_main_loop_new(NULL, FALSE); > + > + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); > + g_assert_nonnull(g_mkdtemp(fixture->test_dir)); > + > + path = g_build_filename(fixture->test_dir, "sock", NULL); > + cwd = g_get_current_dir(); > + cmd = g_strdup_printf("%s%cqemu-ga -m unix-listen -t %s -p %s %s %s", > + cwd, G_DIR_SEPARATOR, > + fixture->test_dir, path, > + getenv("QTEST_LOG") ? "-v" : "", > + extra_arg ?: ""); > + g_shell_parse_argv(cmd, NULL, &argv, &error); > + g_assert_no_error(error); > + > + g_spawn_async(fixture->test_dir, argv, NULL, > + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, > + NULL, NULL, &fixture->pid, &error); > + g_assert_no_error(error); > + > + g_child_watch_add(fixture->pid, qga_watch, fixture); > + > + fixture->fd = connect_qga(path); > + > + g_strfreev(argv); > + g_free(cmd); > + g_free(cwd); > + g_free(path); > +} > + > +static void > +fixture_tear_down(TestFixture *fixture, gconstpointer data) > +{ > + gchar *tmp; > + > + kill(fixture->pid, SIGTERM); > + > + g_main_loop_run(fixture->loop); > + g_main_loop_unref(fixture->loop); > + > + g_spawn_close_pid(fixture->pid); > + > + tmp = g_build_filename(fixture->test_dir, "foo", NULL); > + g_unlink(tmp); > + g_free(tmp); > + > + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); > + g_unlink(tmp); > + g_free(tmp); > + > + tmp = g_build_filename(fixture->test_dir, "sock", NULL); > + g_unlink(tmp); > + g_free(tmp); > + > + g_rmdir(fixture->test_dir); > + g_free(fixture->test_dir); > +} > + > +static void qmp_assertion_message_error(const char *domain, > + const char *file, > + int line, > + const char *func, > + const char *expr, > + QDict *dict) > +{ > + const char *class, *desc; > + char *s; > + QDict *error; > + > + error = qdict_get_qdict(dict, "error"); > + class = qdict_get_try_str(error, "class"); > + desc = qdict_get_try_str(error, "desc"); > + > + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); > + g_assertion_message(domain, file, line, func, s); > + g_free(s); > +} > + > +#define qmp_assert_no_error(err) do { \ > + if (qdict_haskey(err, "error")) { \ > + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ > + G_STRFUNC, #err, err); \ > + } \ > +} while (0) > + > +static void test_qga_sync_delimited(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + guint32 v, r = g_random_int(); > + unsigned char c; > + QDict *ret; > + gchar *cmd; > + > + cmd = g_strdup_printf("%c{'execute': 'guest-sync-delimited'," > + " 'arguments': {'id': %u } }", 0xff, r); > + qmp_fd_send(fixture->fd, cmd); > + g_free(cmd); > + > + v = read(fixture->fd, &c, 1); > + g_assert_cmpint(v, ==, 1); > + g_assert_cmpint(c, ==, 0xff); > + > + ret = qmp_fd_receive(fixture->fd); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + v = qdict_get_int(ret, "return"); > + g_assert_cmpint(r, ==, v); > + > + QDECREF(ret); > +} > + > +static void test_qga_sync(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + guint32 v, r = g_random_int(); > + QDict *ret; > + gchar *cmd; > + > + cmd = g_strdup_printf("%c{'execute': 'guest-sync'," > + " 'arguments': {'id': %u } }", 0xff, r); > + ret = qmp_fd(fixture->fd, cmd); > + g_free(cmd); > + > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + v = qdict_get_int(ret, "return"); > + g_assert_cmpint(r, ==, v); > + > + QDECREF(ret); > +} > + > +static void test_qga_ping(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + QDECREF(ret); > +} > + > +static void test_qga_invalid_cmd(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret, *error; > + const gchar *class, *desc; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); > + g_assert_nonnull(ret); > + > + error = qdict_get_qdict(ret, "error"); > + class = qdict_get_try_str(error, "class"); > + desc = qdict_get_try_str(error, "desc"); > + > + g_assert_cmpstr(class, ==, "CommandNotFound"); > + g_assert_cmpint(strlen(desc), >, 0); > + > + QDECREF(ret); > +} > + > +static void test_qga_info(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret, *val; > + const gchar *version; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + val = qdict_get_qdict(ret, "return"); > + version = qdict_get_try_str(val, "version"); > + g_assert_cmpstr(version, ==, QEMU_VERSION); > + > + QDECREF(ret); > +} > + > +static void test_qga_get_vcpus(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + QList *list; > + const QListEntry *entry; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + /* check there is at least a cpu */ > + list = qdict_get_qlist(ret, "return"); > + entry = qlist_first(list); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id")); > + > + QDECREF(ret); > +} > + > +static void test_qga_get_fsinfo(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + QList *list; > + const QListEntry *entry; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + /* check there is at least a fs */ > + list = qdict_get_qlist(ret, "return"); > + entry = qlist_first(list); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint")); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type")); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk")); > + > + QDECREF(ret); > +} > + > +static void test_qga_get_memory_block_info(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret, *val; > + int64_t size; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + /* check there is at least some memory */ > + val = qdict_get_qdict(ret, "return"); > + size = qdict_get_int(val, "size"); > + g_assert_cmpint(size, >, 0); > + > + QDECREF(ret); > +} > + > +static void test_qga_get_memory_blocks(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + QList *list; > + const QListEntry *entry; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + list = qdict_get_qlist(ret, "return"); > + entry = qlist_first(list); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index")); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > + > + QDECREF(ret); > +} > + > +static void test_qga_network_get_interfaces(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + QList *list; > + const QListEntry *entry; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + /* check there is at least a cpu */ copy/paste error > + list = qdict_get_qlist(ret, "return"); > + entry = qlist_first(list); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > + > + QDECREF(ret); > +} > + > +static void test_qga_file_ops(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + gchar *cmd, *path; > + const gchar *b64; > + QDict *ret, *val; > + int64_t id, count, eof; > + FILE *f; > + char tmp[100]; > + > + /* open */ > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + id = qdict_get_int(ret, "return"); > + QDECREF(ret); > + > + /* write */ > + cmd = g_strdup_printf("{'execute': 'guest-file-write'," > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "," > + " 'buf-b64': 'aGVsbG8gd29ybGQK' } }", id); base64 encoding the string here would be more readible, or at least just a comment that it's a b64 "hello world". A separate variable/constant to store it so it's not manually duplicated below would be good too in case we ever wanted to change it to check some fringe case with encodings. > + ret = qmp_fd(fixture->fd, cmd); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + val = qdict_get_qdict(ret, "return"); > + count = qdict_get_int(val, "count"); > + eof = qdict_get_bool(val, "eof"); > + g_assert_cmpint(count, ==, 12); > + g_assert_cmpint(eof, ==, 0); > + QDECREF(ret); > + g_free(cmd); > + > + /* flush */ > + cmd = g_strdup_printf("{'execute': 'guest-file-flush'," > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + QDECREF(ret); > + g_free(cmd); > + > + /* close */ > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + QDECREF(ret); > + g_free(cmd); > + > + /* check content */ > + path = g_build_filename(fixture->test_dir, "foo", NULL); > + f = fopen(path, "r"); > + g_assert_nonnull(f); > + count = fread(tmp, 1, sizeof(tmp), f); > + g_assert_cmpint(count, ==, 12); > + tmp[count] = 0; > + g_assert_cmpstr(tmp, ==, "hello world\n"); > + fclose(f); > + > + /* open */ > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + id = qdict_get_int(ret, "return"); > + QDECREF(ret); > + > + /* read */ > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + val = qdict_get_qdict(ret, "return"); > + count = qdict_get_int(val, "count"); > + eof = qdict_get_bool(val, "eof"); > + b64 = qdict_get_str(val, "buf-b64"); > + g_assert_cmpint(count, ==, 12); > + g_assert(eof); > + g_assert_cmpstr(b64, ==, "aGVsbG8gd29ybGQK"); > + > + QDECREF(ret); > + g_free(cmd); > + > + /* read eof */ > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + val = qdict_get_qdict(ret, "return"); > + count = qdict_get_int(val, "count"); > + eof = qdict_get_bool(val, "eof"); > + b64 = qdict_get_str(val, "buf-b64"); > + g_assert_cmpint(count, ==, 0); > + g_assert(eof); > + g_assert_cmpstr(b64, ==, ""); > + QDECREF(ret); > + g_free(cmd); > + > + /* seek */ > + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," > + " 'arguments': { 'handle': %" G_GINT64_FORMAT ", " > + " 'offset': %d, 'whence': %d } }", > + id, 6, SEEK_SET); > + ret = qmp_fd(fixture->fd, cmd); > + qmp_assert_no_error(ret); > + val = qdict_get_qdict(ret, "return"); > + count = qdict_get_int(val, "position"); > + eof = qdict_get_bool(val, "eof"); > + g_assert_cmpint(count, ==, 6); > + g_assert(!eof); > + QDECREF(ret); > + g_free(cmd); > + > + /* partial read */ > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + val = qdict_get_qdict(ret, "return"); > + count = qdict_get_int(val, "count"); > + eof = qdict_get_bool(val, "eof"); > + b64 = qdict_get_str(val, "buf-b64"); > + g_assert_cmpint(count, ==, 6); > + g_assert(eof); > + g_assert_cmpstr(b64, ==, "d29ybGQK"); This makes me think doing the b64 encode/decodes here would be a lot easier, especially given that offsets are relative to the plaintext. > + > + QDECREF(ret); > + g_free(cmd); > + > + /* close */ > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} }", > + id); > + ret = qmp_fd(fixture->fd, cmd); > + QDECREF(ret); > + g_free(cmd); > +} > + > +static void test_qga_get_time(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + int64_t time; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + time = qdict_get_int(ret, "return"); > + g_assert_cmpint(time, >, 0); > + > + QDECREF(ret); > +} > + > +static void test_qga_set_time(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + int64_t current, time; > + gchar *cmd; > + > + /* get current time */ > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + current = qdict_get_int(ret, "return"); > + g_assert_cmpint(current, >, 0); > + QDECREF(ret); > + > + /* set some old time */ > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-set-time'," > + " 'arguments': { 'time': 1000 } }"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + QDECREF(ret); > + > + /* check old time */ > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + time = qdict_get_int(ret, "return"); > + g_assert_cmpint(time / 1000, <, G_USEC_PER_SEC * 10); > + QDECREF(ret); > + > + /* set back current time */ > + cmd = g_strdup_printf("{'execute': 'guest-set-time'," > + " 'arguments': { 'time': %" G_GINT64_FORMAT " } }", > + current + time * 1000); > + ret = qmp_fd(fixture->fd, cmd); > + g_free(cmd); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + QDECREF(ret); > +} > + > +static void test_qga_fstrim(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + QList *list; > + const QListEntry *entry; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fstrim'," > + " arguments: { minimum: 4194304 } }"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + list = qdict_get_qlist(ret, "return"); > + entry = qlist_first(list); > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "paths")); > + > + QDECREF(ret); > +} > + > +static void test_qga_blacklist(gconstpointer data) > +{ > + TestFixture fix; > + QDict *ret, *error; > + const gchar *class, *desc; > + > + fixture_setup(&fix, "-b guest-ping,guest-get-time"); > + > + /* check blacklist */ > + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); > + g_assert_nonnull(ret); > + error = qdict_get_qdict(ret, "error"); > + class = qdict_get_try_str(error, "class"); > + desc = qdict_get_try_str(error, "desc"); > + g_assert_cmpstr(class, ==, "GenericError"); > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > + QDECREF(ret); > + > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); > + g_assert_nonnull(ret); > + error = qdict_get_qdict(ret, "error"); > + class = qdict_get_try_str(error, "class"); > + desc = qdict_get_try_str(error, "desc"); > + g_assert_cmpstr(class, ==, "GenericError"); > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > + QDECREF(ret); > + > + /* check something work */ > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); > + qmp_assert_no_error(ret); > + QDECREF(ret); > + > + fixture_tear_down(&fix, NULL); > +} > + > +#if !GLIB_CHECK_VERSION(2, 38, 0) > +#define g_assert_true(E) g_assert(E) > +#define g_assert_false(E) g_assert(!E) > +#endif > + > +static void test_qga_config(gconstpointer data) > +{ > + GError *error = NULL; > + char *cwd, *cmd, *out, *err, *str, **strv, *conf, **argv = NULL; > + char *env[2]; > + int status, tmp; > + gsize n; > + GKeyFile *kf; > + const char *qga_config = > + "[general]\n" > + "daemon=false\n" > + "method=virtio-serial\n" > + "path=/path/to/org.qemu.guest_agent.0\n" > + "pidfile=/var/foo/qemu-ga.pid\n" > + "statedir=/var/state\n" > + "verbose=true\n" > + "blacklist=guest-ping;guest-get-time\n"; > + > + tmp = g_file_open_tmp(NULL, &conf, &error); > + g_assert_no_error(error); > + g_assert_cmpint(tmp, >=, 0); > + g_assert_cmpstr(conf, !=, ""); > + > + g_file_set_contents(conf, qga_config, -1, &error); > + g_assert_no_error(error); > + > + cwd = g_get_current_dir(); > + cmd = g_strdup_printf("%s%cqemu-ga -D", > + cwd, G_DIR_SEPARATOR); > + g_shell_parse_argv(cmd, NULL, &argv, &error); > + g_assert_no_error(error); > + > + env[0] = g_strdup_printf("QGA_CONF=%s", conf); > + env[1] = NULL; > + g_spawn_sync(NULL, argv, env, G_SPAWN_DEFAULT, > + NULL, NULL, &out, &err, &status, &error); > + g_assert_no_error(error); > + g_assert_cmpstr(err, ==, ""); > + g_assert_cmpint(status, ==, 0); > + > + kf = g_key_file_new(); > + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); > + g_assert_no_error(error); > + > + str = g_key_file_get_start_group(kf); > + g_assert_cmpstr(str, ==, "general"); > + g_free(str); > + > + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); > + g_assert_no_error(error); > + > + str = g_key_file_get_string(kf, "general", "method", &error); > + g_assert_no_error(error); > + g_assert_cmpstr(str, ==, "virtio-serial"); > + g_free(str); > + > + str = g_key_file_get_string(kf, "general", "path", &error); > + g_assert_no_error(error); > + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); > + g_free(str); > + > + str = g_key_file_get_string(kf, "general", "pidfile", &error); > + g_assert_no_error(error); > + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); > + g_free(str); > + > + str = g_key_file_get_string(kf, "general", "statedir", &error); > + g_assert_no_error(error); > + g_assert_cmpstr(str, ==, "/var/state"); > + g_free(str); > + > + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); > + g_assert_no_error(error); > + > + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); > + g_assert_cmpint(n, ==, 2); > +#if GLIB_CHECK_VERSION(2, 44, 0) > + g_assert_true(g_strv_contains((const char * const *)strv, > + "guest-ping")); > + g_assert_true(g_strv_contains((const char * const *)strv, > + "guest-get-time")); > +#endif > + g_assert_no_error(error); > + g_strfreev(strv); > + > + g_free(out); > + g_free(err); > + g_free(conf); > + g_free(env[0]); > + g_key_file_free(kf); > + > + close(tmp); > +} > + > +static void test_qga_fsfreeze_status(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + const gchar *status; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + > + status = qdict_get_try_str(ret, "return"); > + g_assert_cmpstr(status, ==, "thawed"); > + > + QDECREF(ret); > +} > + > +static void test_qga_fsfreeze_and_thaw(gconstpointer fix) > +{ > + const TestFixture *fixture = fix; > + QDict *ret; > + const gchar *status; > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-freeze'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + QDECREF(ret); > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + status = qdict_get_try_str(ret, "return"); > + g_assert_cmpstr(status, ==, "frozen"); > + QDECREF(ret); > + > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-thaw'}"); > + g_assert_nonnull(ret); > + qmp_assert_no_error(ret); > + QDECREF(ret); > +} > + > +static gboolean running_in_virt(const gchar *virt) > +{ > + gchar *virt_what = NULL; > + gchar *err = NULL; > + gint status; > + gboolean success; > + > + success = g_spawn_command_line_sync("virt-what", &virt_what, &err, > + &status, NULL); > + > + success = success && status == 0 && > + g_strcmp0(g_strstrip(virt_what), virt) == 0; > + > + g_free(virt_what); > + g_free(err); > + > + return success; > +} > +int main(int argc, char **argv) > +{ > + TestFixture fix; > + int ret; > + > + setlocale (LC_ALL, ""); > + g_test_init(&argc, &argv, NULL); > + fixture_setup(&fix, NULL); > + > + g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); > + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); > + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); > + g_test_add_data_func("/qga/info", &fix, test_qga_info); > + g_test_add_data_func("/qga/network-get-interfaces", &fix, > + test_qga_network_get_interfaces); > + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); > + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); > + g_test_add_data_func("/qga/get-memory-block-info", &fix, > + test_qga_get_memory_block_info); > + g_test_add_data_func("/qga/get-memory-blocks", &fix, > + test_qga_get_memory_blocks); > + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); > + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); > + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); > + g_test_add_data_func("/qga/fsfreeze-status", &fix, > + test_qga_fsfreeze_status); > + > + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); > + g_test_add_data_func("/qga/config", NULL, test_qga_config); > + > + if (running_in_virt("kvm")) { I think we should go ahead and replace this with a QGA_TEST_SIDE_EFFECTING environment variable or something of the sort. It's not necessarily the case that a VM environment means we can do side-effecting tests. It could be an "official" build server or someone's primary development environment and fsfreeze can do some funky stuff if they aren't intentionally running it. > + g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix, > + test_qga_fsfreeze_and_thaw); > + g_test_add_data_func("/qga/set-time", &fix, test_qga_set_time); > + if (g_test_thorough()) { > + g_test_add_data_func("/qga/fstrim", &fix, test_qga_fstrim); > + } > + } > + > + ret = g_test_run(); > + > + fixture_tear_down(&fix, NULL); > + > + return ret; > +} > -- > 2.4.3 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent 2015-10-01 21:36 ` Michael Roth @ 2015-10-01 22:24 ` Marc-André Lureau 2015-10-01 23:36 ` Michael Roth 0 siblings, 1 reply; 11+ messages in thread From: Marc-André Lureau @ 2015-10-01 22:24 UTC (permalink / raw) To: Michael Roth; +Cc: marcandre lureau, qemu-devel ----- Original Message ----- > Quoting marcandre.lureau@redhat.com (2015-09-11 13:53:41) > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > Add some local guest agent tests (as it is better than nothing) only > > when CONFIG_LINUX. > > > > They can be run inside or outside a VM, when run inside a VM, they will > > do a bit more side effects, such as freezing/thawing the FS or changing > > the time. > > > > A better test would involve a managed VM (or container), but it might be > > better to leave that off to autotest/avocado. > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > Sorry for the delay in getting to this. Have some review comments but > will make sure to stay on top of any follow-ups as I'm trying to get > this into my next pull. > > > --- > > tests/Makefile | 3 + > > tests/test-qga.c | 791 > > +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 794 insertions(+) > > create mode 100644 tests/test-qga.c > > > > diff --git a/tests/Makefile b/tests/Makefile > > index 34c6136..d5837a4 100644 > > --- a/tests/Makefile > > +++ b/tests/Makefile > > @@ -76,6 +76,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF) > > gcov-files-test-write-threshold-y = block/write-threshold.c > > check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) > > check-unit-y += tests/test-crypto-cipher$(EXESUF) > > +check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) > > > > check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh > > > > @@ -432,6 +433,8 @@ endif > > qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a > > $(check-qtest-y): $(qtest-obj-y) > > > > +tests/test-qga: tests/test-qga.o $(qtest-obj-y) > > + > > .PHONY: check-help > > check-help: > > @echo "Regression testing targets:" > > diff --git a/tests/test-qga.c b/tests/test-qga.c > > new file mode 100644 > > index 0000000..cfee134 > > --- /dev/null > > +++ b/tests/test-qga.c > > @@ -0,0 +1,791 @@ > > +#include <locale.h> > > +#include <glib.h> > > +#include <glib/gstdio.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <errno.h> > > +#include <string.h> > > +#include <sys/types.h> > > +#include <sys/socket.h> > > +#include <sys/un.h> > > +#include <unistd.h> > > + > > +#include "libqtest.h" > > +#include "config-host.h" > > + > > +typedef struct { > > + char *test_dir; > > + GMainLoop *loop; > > + int fd; > > + GPid pid; > > +} TestFixture; > > + > > +static int connect_qga(char *path) > > +{ > > + int s, ret, len, i = 0; > > + struct sockaddr_un remote; > > + > > + s = socket(AF_UNIX, SOCK_STREAM, 0); > > + g_assert(s != -1); > > + > > + remote.sun_family = AF_UNIX; > > + do { > > + strcpy(remote.sun_path, path); > > + len = strlen(remote.sun_path) + sizeof(remote.sun_family); > > + ret = connect(s, (struct sockaddr *)&remote, len); > > + if (ret == -1) { > > + g_usleep(G_USEC_PER_SEC); > > + } > > + if (i++ == 5) { > > + return -1; > > + } > > + } while (ret == -1); > > + > > + return s; > > +} > > + > > +static void qga_watch(GPid pid, gint status, gpointer user_data) > > +{ > > + TestFixture *fixture = user_data; > > + > > + g_assert_cmpint(status, ==, 0); > > + g_main_loop_quit(fixture->loop); > > +} > > + > > +static void > > +fixture_setup(TestFixture *fixture, gconstpointer data) > > +{ > > + const gchar *extra_arg = data; > > + GError *error = NULL; > > + gchar *cwd, *path, *cmd, **argv = NULL; > > + > > + fixture->loop = g_main_loop_new(NULL, FALSE); > > + > > + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); > > + g_assert_nonnull(g_mkdtemp(fixture->test_dir)); > > + > > + path = g_build_filename(fixture->test_dir, "sock", NULL); > > + cwd = g_get_current_dir(); > > + cmd = g_strdup_printf("%s%cqemu-ga -m unix-listen -t %s -p %s %s %s", > > + cwd, G_DIR_SEPARATOR, > > + fixture->test_dir, path, > > + getenv("QTEST_LOG") ? "-v" : "", > > + extra_arg ?: ""); > > + g_shell_parse_argv(cmd, NULL, &argv, &error); > > + g_assert_no_error(error); > > + > > + g_spawn_async(fixture->test_dir, argv, NULL, > > + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, > > + NULL, NULL, &fixture->pid, &error); > > + g_assert_no_error(error); > > + > > + g_child_watch_add(fixture->pid, qga_watch, fixture); > > + > > + fixture->fd = connect_qga(path); > > + > > + g_strfreev(argv); > > + g_free(cmd); > > + g_free(cwd); > > + g_free(path); > > +} > > + > > +static void > > +fixture_tear_down(TestFixture *fixture, gconstpointer data) > > +{ > > + gchar *tmp; > > + > > + kill(fixture->pid, SIGTERM); > > + > > + g_main_loop_run(fixture->loop); > > + g_main_loop_unref(fixture->loop); > > + > > + g_spawn_close_pid(fixture->pid); > > + > > + tmp = g_build_filename(fixture->test_dir, "foo", NULL); > > + g_unlink(tmp); > > + g_free(tmp); > > + > > + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); > > + g_unlink(tmp); > > + g_free(tmp); > > + > > + tmp = g_build_filename(fixture->test_dir, "sock", NULL); > > + g_unlink(tmp); > > + g_free(tmp); > > + > > + g_rmdir(fixture->test_dir); > > + g_free(fixture->test_dir); > > +} > > + > > +static void qmp_assertion_message_error(const char *domain, > > + const char *file, > > + int line, > > + const char *func, > > + const char *expr, > > + QDict *dict) > > +{ > > + const char *class, *desc; > > + char *s; > > + QDict *error; > > + > > + error = qdict_get_qdict(dict, "error"); > > + class = qdict_get_try_str(error, "class"); > > + desc = qdict_get_try_str(error, "desc"); > > + > > + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); > > + g_assertion_message(domain, file, line, func, s); > > + g_free(s); > > +} > > + > > +#define qmp_assert_no_error(err) do { \ > > + if (qdict_haskey(err, "error")) { \ > > + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ > > + G_STRFUNC, #err, err); \ > > + } \ > > +} while (0) > > + > > +static void test_qga_sync_delimited(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + guint32 v, r = g_random_int(); > > + unsigned char c; > > + QDict *ret; > > + gchar *cmd; > > + > > + cmd = g_strdup_printf("%c{'execute': 'guest-sync-delimited'," > > + " 'arguments': {'id': %u } }", 0xff, r); > > + qmp_fd_send(fixture->fd, cmd); > > + g_free(cmd); > > + > > + v = read(fixture->fd, &c, 1); > > + g_assert_cmpint(v, ==, 1); > > + g_assert_cmpint(c, ==, 0xff); > > + > > + ret = qmp_fd_receive(fixture->fd); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + v = qdict_get_int(ret, "return"); > > + g_assert_cmpint(r, ==, v); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_sync(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + guint32 v, r = g_random_int(); > > + QDict *ret; > > + gchar *cmd; > > + > > + cmd = g_strdup_printf("%c{'execute': 'guest-sync'," > > + " 'arguments': {'id': %u } }", 0xff, r); > > + ret = qmp_fd(fixture->fd, cmd); > > + g_free(cmd); > > + > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + v = qdict_get_int(ret, "return"); > > + g_assert_cmpint(r, ==, v); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_ping(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_invalid_cmd(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret, *error; > > + const gchar *class, *desc; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); > > + g_assert_nonnull(ret); > > + > > + error = qdict_get_qdict(ret, "error"); > > + class = qdict_get_try_str(error, "class"); > > + desc = qdict_get_try_str(error, "desc"); > > + > > + g_assert_cmpstr(class, ==, "CommandNotFound"); > > + g_assert_cmpint(strlen(desc), >, 0); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_info(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret, *val; > > + const gchar *version; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + val = qdict_get_qdict(ret, "return"); > > + version = qdict_get_try_str(val, "version"); > > + g_assert_cmpstr(version, ==, QEMU_VERSION); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_get_vcpus(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + QList *list; > > + const QListEntry *entry; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + /* check there is at least a cpu */ > > + list = qdict_get_qlist(ret, "return"); > > + entry = qlist_first(list); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id")); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_get_fsinfo(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + QList *list; > > + const QListEntry *entry; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + /* check there is at least a fs */ > > + list = qdict_get_qlist(ret, "return"); > > + entry = qlist_first(list); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint")); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type")); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk")); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_get_memory_block_info(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret, *val; > > + int64_t size; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': > > 'guest-get-memory-block-info'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + /* check there is at least some memory */ > > + val = qdict_get_qdict(ret, "return"); > > + size = qdict_get_int(val, "size"); > > + g_assert_cmpint(size, >, 0); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_get_memory_blocks(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + QList *list; > > + const QListEntry *entry; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + list = qdict_get_qlist(ret, "return"); > > + entry = qlist_first(list); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index")); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_network_get_interfaces(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + QList *list; > > + const QListEntry *entry; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': > > 'guest-network-get-interfaces'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + /* check there is at least a cpu */ > > copy/paste error > > > + list = qdict_get_qlist(ret, "return"); > > + entry = qlist_first(list); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_file_ops(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + gchar *cmd, *path; > > + const gchar *b64; > > + QDict *ret, *val; > > + int64_t id, count, eof; > > + FILE *f; > > + char tmp[100]; > > + > > + /* open */ > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > > + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + id = qdict_get_int(ret, "return"); > > + QDECREF(ret); > > + > > + /* write */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-write'," > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT > > "," > > + " 'buf-b64': 'aGVsbG8gd29ybGQK' } }", id); > > base64 encoding the string here would be more readible, or at least just > a comment that it's a b64 "hello world". A separate variable/constant to > store it so it's not manually duplicated below would be good too in case > we ever wanted to change it to check some fringe case with encodings. > > > + ret = qmp_fd(fixture->fd, cmd); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + val = qdict_get_qdict(ret, "return"); > > + count = qdict_get_int(val, "count"); > > + eof = qdict_get_bool(val, "eof"); > > + g_assert_cmpint(count, ==, 12); > > + g_assert_cmpint(eof, ==, 0); > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* flush */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-flush'," > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* close */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* check content */ > > + path = g_build_filename(fixture->test_dir, "foo", NULL); > > + f = fopen(path, "r"); > > + g_assert_nonnull(f); > > + count = fread(tmp, 1, sizeof(tmp), f); > > + g_assert_cmpint(count, ==, 12); > > + tmp[count] = 0; > > + g_assert_cmpstr(tmp, ==, "hello world\n"); > > + fclose(f); > > + > > + /* open */ > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > > + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + id = qdict_get_int(ret, "return"); > > + QDECREF(ret); > > + > > + /* read */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + val = qdict_get_qdict(ret, "return"); > > + count = qdict_get_int(val, "count"); > > + eof = qdict_get_bool(val, "eof"); > > + b64 = qdict_get_str(val, "buf-b64"); > > + g_assert_cmpint(count, ==, 12); > > + g_assert(eof); > > + g_assert_cmpstr(b64, ==, "aGVsbG8gd29ybGQK"); > > + > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* read eof */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + val = qdict_get_qdict(ret, "return"); > > + count = qdict_get_int(val, "count"); > > + eof = qdict_get_bool(val, "eof"); > > + b64 = qdict_get_str(val, "buf-b64"); > > + g_assert_cmpint(count, ==, 0); > > + g_assert(eof); > > + g_assert_cmpstr(b64, ==, ""); > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* seek */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT ", > > " > > + " 'offset': %d, 'whence': %d } }", > > + id, 6, SEEK_SET); > > + ret = qmp_fd(fixture->fd, cmd); > > + qmp_assert_no_error(ret); > > + val = qdict_get_qdict(ret, "return"); > > + count = qdict_get_int(val, "position"); > > + eof = qdict_get_bool(val, "eof"); > > + g_assert_cmpint(count, ==, 6); > > + g_assert(!eof); > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* partial read */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + val = qdict_get_qdict(ret, "return"); > > + count = qdict_get_int(val, "count"); > > + eof = qdict_get_bool(val, "eof"); > > + b64 = qdict_get_str(val, "buf-b64"); > > + g_assert_cmpint(count, ==, 6); > > + g_assert(eof); > > + g_assert_cmpstr(b64, ==, "d29ybGQK"); > > This makes me think doing the b64 encode/decodes here would be a lot > easier, especially given that offsets are relative to the plaintext. > > > + > > + QDECREF(ret); > > + g_free(cmd); > > + > > + /* close */ > > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > }", > > + id); > > + ret = qmp_fd(fixture->fd, cmd); > > + QDECREF(ret); > > + g_free(cmd); > > +} > > + > > +static void test_qga_get_time(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + int64_t time; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + time = qdict_get_int(ret, "return"); > > + g_assert_cmpint(time, >, 0); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_set_time(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + int64_t current, time; > > + gchar *cmd; > > + > > + /* get current time */ > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + current = qdict_get_int(ret, "return"); > > + g_assert_cmpint(current, >, 0); > > + QDECREF(ret); > > + > > + /* set some old time */ > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-set-time'," > > + " 'arguments': { 'time': 1000 } }"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + QDECREF(ret); > > + > > + /* check old time */ > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + time = qdict_get_int(ret, "return"); > > + g_assert_cmpint(time / 1000, <, G_USEC_PER_SEC * 10); > > + QDECREF(ret); > > + > > + /* set back current time */ > > + cmd = g_strdup_printf("{'execute': 'guest-set-time'," > > + " 'arguments': { 'time': %" G_GINT64_FORMAT " } > > }", > > + current + time * 1000); > > + ret = qmp_fd(fixture->fd, cmd); > > + g_free(cmd); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + QDECREF(ret); > > +} > > + > > +static void test_qga_fstrim(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + QList *list; > > + const QListEntry *entry; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fstrim'," > > + " arguments: { minimum: 4194304 } }"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + list = qdict_get_qlist(ret, "return"); > > + entry = qlist_first(list); > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "paths")); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_blacklist(gconstpointer data) > > +{ > > + TestFixture fix; > > + QDict *ret, *error; > > + const gchar *class, *desc; > > + > > + fixture_setup(&fix, "-b guest-ping,guest-get-time"); > > + > > + /* check blacklist */ > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); > > + g_assert_nonnull(ret); > > + error = qdict_get_qdict(ret, "error"); > > + class = qdict_get_try_str(error, "class"); > > + desc = qdict_get_try_str(error, "desc"); > > + g_assert_cmpstr(class, ==, "GenericError"); > > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > > + QDECREF(ret); > > + > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); > > + g_assert_nonnull(ret); > > + error = qdict_get_qdict(ret, "error"); > > + class = qdict_get_try_str(error, "class"); > > + desc = qdict_get_try_str(error, "desc"); > > + g_assert_cmpstr(class, ==, "GenericError"); > > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > > + QDECREF(ret); > > + > > + /* check something work */ > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); > > + qmp_assert_no_error(ret); > > + QDECREF(ret); > > + > > + fixture_tear_down(&fix, NULL); > > +} > > + > > +#if !GLIB_CHECK_VERSION(2, 38, 0) > > +#define g_assert_true(E) g_assert(E) > > +#define g_assert_false(E) g_assert(!E) > > +#endif > > + > > +static void test_qga_config(gconstpointer data) > > +{ > > + GError *error = NULL; > > + char *cwd, *cmd, *out, *err, *str, **strv, *conf, **argv = NULL; > > + char *env[2]; > > + int status, tmp; > > + gsize n; > > + GKeyFile *kf; > > + const char *qga_config = > > + "[general]\n" > > + "daemon=false\n" > > + "method=virtio-serial\n" > > + "path=/path/to/org.qemu.guest_agent.0\n" > > + "pidfile=/var/foo/qemu-ga.pid\n" > > + "statedir=/var/state\n" > > + "verbose=true\n" > > + "blacklist=guest-ping;guest-get-time\n"; > > + > > + tmp = g_file_open_tmp(NULL, &conf, &error); > > + g_assert_no_error(error); > > + g_assert_cmpint(tmp, >=, 0); > > + g_assert_cmpstr(conf, !=, ""); > > + > > + g_file_set_contents(conf, qga_config, -1, &error); > > + g_assert_no_error(error); > > + > > + cwd = g_get_current_dir(); > > + cmd = g_strdup_printf("%s%cqemu-ga -D", > > + cwd, G_DIR_SEPARATOR); > > + g_shell_parse_argv(cmd, NULL, &argv, &error); > > + g_assert_no_error(error); > > + > > + env[0] = g_strdup_printf("QGA_CONF=%s", conf); > > + env[1] = NULL; > > + g_spawn_sync(NULL, argv, env, G_SPAWN_DEFAULT, > > + NULL, NULL, &out, &err, &status, &error); > > + g_assert_no_error(error); > > + g_assert_cmpstr(err, ==, ""); > > + g_assert_cmpint(status, ==, 0); > > + > > + kf = g_key_file_new(); > > + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); > > + g_assert_no_error(error); > > + > > + str = g_key_file_get_start_group(kf); > > + g_assert_cmpstr(str, ==, "general"); > > + g_free(str); > > + > > + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", > > &error)); > > + g_assert_no_error(error); > > + > > + str = g_key_file_get_string(kf, "general", "method", &error); > > + g_assert_no_error(error); > > + g_assert_cmpstr(str, ==, "virtio-serial"); > > + g_free(str); > > + > > + str = g_key_file_get_string(kf, "general", "path", &error); > > + g_assert_no_error(error); > > + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); > > + g_free(str); > > + > > + str = g_key_file_get_string(kf, "general", "pidfile", &error); > > + g_assert_no_error(error); > > + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); > > + g_free(str); > > + > > + str = g_key_file_get_string(kf, "general", "statedir", &error); > > + g_assert_no_error(error); > > + g_assert_cmpstr(str, ==, "/var/state"); > > + g_free(str); > > + > > + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", > > &error)); > > + g_assert_no_error(error); > > + > > + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, > > &error); > > + g_assert_cmpint(n, ==, 2); > > +#if GLIB_CHECK_VERSION(2, 44, 0) > > + g_assert_true(g_strv_contains((const char * const *)strv, > > + "guest-ping")); > > + g_assert_true(g_strv_contains((const char * const *)strv, > > + "guest-get-time")); > > +#endif > > + g_assert_no_error(error); > > + g_strfreev(strv); > > + > > + g_free(out); > > + g_free(err); > > + g_free(conf); > > + g_free(env[0]); > > + g_key_file_free(kf); > > + > > + close(tmp); > > +} > > + > > +static void test_qga_fsfreeze_status(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + const gchar *status; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + > > + status = qdict_get_try_str(ret, "return"); > > + g_assert_cmpstr(status, ==, "thawed"); > > + > > + QDECREF(ret); > > +} > > + > > +static void test_qga_fsfreeze_and_thaw(gconstpointer fix) > > +{ > > + const TestFixture *fixture = fix; > > + QDict *ret; > > + const gchar *status; > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-freeze'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + QDECREF(ret); > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + status = qdict_get_try_str(ret, "return"); > > + g_assert_cmpstr(status, ==, "frozen"); > > + QDECREF(ret); > > + > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-thaw'}"); > > + g_assert_nonnull(ret); > > + qmp_assert_no_error(ret); > > + QDECREF(ret); > > +} > > + > > +static gboolean running_in_virt(const gchar *virt) > > +{ > > + gchar *virt_what = NULL; > > + gchar *err = NULL; > > + gint status; > > + gboolean success; > > + > > + success = g_spawn_command_line_sync("virt-what", &virt_what, &err, > > + &status, NULL); > > + > > + success = success && status == 0 && > > + g_strcmp0(g_strstrip(virt_what), virt) == 0; > > + > > + g_free(virt_what); > > + g_free(err); > > + > > + return success; > > +} > > +int main(int argc, char **argv) > > +{ > > + TestFixture fix; > > + int ret; > > + > > + setlocale (LC_ALL, ""); > > + g_test_init(&argc, &argv, NULL); > > + fixture_setup(&fix, NULL); > > + > > + g_test_add_data_func("/qga/sync-delimited", &fix, > > test_qga_sync_delimited); > > + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); > > + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); > > + g_test_add_data_func("/qga/info", &fix, test_qga_info); > > + g_test_add_data_func("/qga/network-get-interfaces", &fix, > > + test_qga_network_get_interfaces); > > + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); > > + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); > > + g_test_add_data_func("/qga/get-memory-block-info", &fix, > > + test_qga_get_memory_block_info); > > + g_test_add_data_func("/qga/get-memory-blocks", &fix, > > + test_qga_get_memory_blocks); > > + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); > > + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); > > + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); > > + g_test_add_data_func("/qga/fsfreeze-status", &fix, > > + test_qga_fsfreeze_status); > > + > > + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); > > + g_test_add_data_func("/qga/config", NULL, test_qga_config); > > + > > + if (running_in_virt("kvm")) { > > I think we should go ahead and replace this with a QGA_TEST_SIDE_EFFECTING > environment variable or something of the sort. It's not necessarily the > case that a VM environment means we can do side-effecting tests. It > could be an "official" build server or someone's primary development But they wouldn't have root privileges though presumably. > environment and fsfreeze can do some funky stuff if they aren't > intentionally running it. Sounds good to me. Would you pick this patch and make the required modification or do you want a resend? > > > + g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix, > > + test_qga_fsfreeze_and_thaw); > > + g_test_add_data_func("/qga/set-time", &fix, test_qga_set_time); > > + if (g_test_thorough()) { > > + g_test_add_data_func("/qga/fstrim", &fix, test_qga_fstrim); > > + } > > + } > > + > > + ret = g_test_run(); > > + > > + fixture_tear_down(&fix, NULL); > > + > > + return ret; > > +} > > -- > > 2.4.3 > > > > thanks ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent 2015-10-01 22:24 ` Marc-André Lureau @ 2015-10-01 23:36 ` Michael Roth 0 siblings, 0 replies; 11+ messages in thread From: Michael Roth @ 2015-10-01 23:36 UTC (permalink / raw) To: Marc-André Lureau; +Cc: marcandre lureau, qemu-devel Quoting Marc-André Lureau (2015-10-01 17:24:53) > > > ----- Original Message ----- > > Quoting marcandre.lureau@redhat.com (2015-09-11 13:53:41) > > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > > > Add some local guest agent tests (as it is better than nothing) only > > > when CONFIG_LINUX. > > > > > > They can be run inside or outside a VM, when run inside a VM, they will > > > do a bit more side effects, such as freezing/thawing the FS or changing > > > the time. > > > > > > A better test would involve a managed VM (or container), but it might be > > > better to leave that off to autotest/avocado. > > > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > Sorry for the delay in getting to this. Have some review comments but > > will make sure to stay on top of any follow-ups as I'm trying to get > > this into my next pull. > > > > > --- > > > tests/Makefile | 3 + > > > tests/test-qga.c | 791 > > > +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > > 2 files changed, 794 insertions(+) > > > create mode 100644 tests/test-qga.c > > > > > > diff --git a/tests/Makefile b/tests/Makefile > > > index 34c6136..d5837a4 100644 > > > --- a/tests/Makefile > > > +++ b/tests/Makefile > > > @@ -76,6 +76,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF) > > > gcov-files-test-write-threshold-y = block/write-threshold.c > > > check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) > > > check-unit-y += tests/test-crypto-cipher$(EXESUF) > > > +check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) > > > > > > check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh > > > > > > @@ -432,6 +433,8 @@ endif > > > qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a > > > $(check-qtest-y): $(qtest-obj-y) > > > > > > +tests/test-qga: tests/test-qga.o $(qtest-obj-y) > > > + > > > .PHONY: check-help > > > check-help: > > > @echo "Regression testing targets:" > > > diff --git a/tests/test-qga.c b/tests/test-qga.c > > > new file mode 100644 > > > index 0000000..cfee134 > > > --- /dev/null > > > +++ b/tests/test-qga.c > > > @@ -0,0 +1,791 @@ > > > +#include <locale.h> > > > +#include <glib.h> > > > +#include <glib/gstdio.h> > > > +#include <stdio.h> > > > +#include <stdlib.h> > > > +#include <errno.h> > > > +#include <string.h> > > > +#include <sys/types.h> > > > +#include <sys/socket.h> > > > +#include <sys/un.h> > > > +#include <unistd.h> > > > + > > > +#include "libqtest.h" > > > +#include "config-host.h" > > > + > > > +typedef struct { > > > + char *test_dir; > > > + GMainLoop *loop; > > > + int fd; > > > + GPid pid; > > > +} TestFixture; > > > + > > > +static int connect_qga(char *path) > > > +{ > > > + int s, ret, len, i = 0; > > > + struct sockaddr_un remote; > > > + > > > + s = socket(AF_UNIX, SOCK_STREAM, 0); > > > + g_assert(s != -1); > > > + > > > + remote.sun_family = AF_UNIX; > > > + do { > > > + strcpy(remote.sun_path, path); > > > + len = strlen(remote.sun_path) + sizeof(remote.sun_family); > > > + ret = connect(s, (struct sockaddr *)&remote, len); > > > + if (ret == -1) { > > > + g_usleep(G_USEC_PER_SEC); > > > + } > > > + if (i++ == 5) { > > > + return -1; > > > + } > > > + } while (ret == -1); > > > + > > > + return s; > > > +} > > > + > > > +static void qga_watch(GPid pid, gint status, gpointer user_data) > > > +{ > > > + TestFixture *fixture = user_data; > > > + > > > + g_assert_cmpint(status, ==, 0); > > > + g_main_loop_quit(fixture->loop); > > > +} > > > + > > > +static void > > > +fixture_setup(TestFixture *fixture, gconstpointer data) > > > +{ > > > + const gchar *extra_arg = data; > > > + GError *error = NULL; > > > + gchar *cwd, *path, *cmd, **argv = NULL; > > > + > > > + fixture->loop = g_main_loop_new(NULL, FALSE); > > > + > > > + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); > > > + g_assert_nonnull(g_mkdtemp(fixture->test_dir)); > > > + > > > + path = g_build_filename(fixture->test_dir, "sock", NULL); > > > + cwd = g_get_current_dir(); > > > + cmd = g_strdup_printf("%s%cqemu-ga -m unix-listen -t %s -p %s %s %s", > > > + cwd, G_DIR_SEPARATOR, > > > + fixture->test_dir, path, > > > + getenv("QTEST_LOG") ? "-v" : "", > > > + extra_arg ?: ""); > > > + g_shell_parse_argv(cmd, NULL, &argv, &error); > > > + g_assert_no_error(error); > > > + > > > + g_spawn_async(fixture->test_dir, argv, NULL, > > > + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, > > > + NULL, NULL, &fixture->pid, &error); > > > + g_assert_no_error(error); > > > + > > > + g_child_watch_add(fixture->pid, qga_watch, fixture); > > > + > > > + fixture->fd = connect_qga(path); > > > + > > > + g_strfreev(argv); > > > + g_free(cmd); > > > + g_free(cwd); > > > + g_free(path); > > > +} > > > + > > > +static void > > > +fixture_tear_down(TestFixture *fixture, gconstpointer data) > > > +{ > > > + gchar *tmp; > > > + > > > + kill(fixture->pid, SIGTERM); > > > + > > > + g_main_loop_run(fixture->loop); > > > + g_main_loop_unref(fixture->loop); > > > + > > > + g_spawn_close_pid(fixture->pid); > > > + > > > + tmp = g_build_filename(fixture->test_dir, "foo", NULL); > > > + g_unlink(tmp); > > > + g_free(tmp); > > > + > > > + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); > > > + g_unlink(tmp); > > > + g_free(tmp); > > > + > > > + tmp = g_build_filename(fixture->test_dir, "sock", NULL); > > > + g_unlink(tmp); > > > + g_free(tmp); > > > + > > > + g_rmdir(fixture->test_dir); > > > + g_free(fixture->test_dir); > > > +} > > > + > > > +static void qmp_assertion_message_error(const char *domain, > > > + const char *file, > > > + int line, > > > + const char *func, > > > + const char *expr, > > > + QDict *dict) > > > +{ > > > + const char *class, *desc; > > > + char *s; > > > + QDict *error; > > > + > > > + error = qdict_get_qdict(dict, "error"); > > > + class = qdict_get_try_str(error, "class"); > > > + desc = qdict_get_try_str(error, "desc"); > > > + > > > + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); > > > + g_assertion_message(domain, file, line, func, s); > > > + g_free(s); > > > +} > > > + > > > +#define qmp_assert_no_error(err) do { \ > > > + if (qdict_haskey(err, "error")) { \ > > > + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ > > > + G_STRFUNC, #err, err); \ > > > + } \ > > > +} while (0) > > > + > > > +static void test_qga_sync_delimited(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + guint32 v, r = g_random_int(); > > > + unsigned char c; > > > + QDict *ret; > > > + gchar *cmd; > > > + > > > + cmd = g_strdup_printf("%c{'execute': 'guest-sync-delimited'," > > > + " 'arguments': {'id': %u } }", 0xff, r); > > > + qmp_fd_send(fixture->fd, cmd); > > > + g_free(cmd); > > > + > > > + v = read(fixture->fd, &c, 1); > > > + g_assert_cmpint(v, ==, 1); > > > + g_assert_cmpint(c, ==, 0xff); > > > + > > > + ret = qmp_fd_receive(fixture->fd); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + v = qdict_get_int(ret, "return"); > > > + g_assert_cmpint(r, ==, v); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_sync(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + guint32 v, r = g_random_int(); > > > + QDict *ret; > > > + gchar *cmd; > > > + > > > + cmd = g_strdup_printf("%c{'execute': 'guest-sync'," > > > + " 'arguments': {'id': %u } }", 0xff, r); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + g_free(cmd); > > > + > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + v = qdict_get_int(ret, "return"); > > > + g_assert_cmpint(r, ==, v); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_ping(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_invalid_cmd(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret, *error; > > > + const gchar *class, *desc; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); > > > + g_assert_nonnull(ret); > > > + > > > + error = qdict_get_qdict(ret, "error"); > > > + class = qdict_get_try_str(error, "class"); > > > + desc = qdict_get_try_str(error, "desc"); > > > + > > > + g_assert_cmpstr(class, ==, "CommandNotFound"); > > > + g_assert_cmpint(strlen(desc), >, 0); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_info(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret, *val; > > > + const gchar *version; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + val = qdict_get_qdict(ret, "return"); > > > + version = qdict_get_try_str(val, "version"); > > > + g_assert_cmpstr(version, ==, QEMU_VERSION); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_get_vcpus(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + QList *list; > > > + const QListEntry *entry; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + /* check there is at least a cpu */ > > > + list = qdict_get_qlist(ret, "return"); > > > + entry = qlist_first(list); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id")); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_get_fsinfo(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + QList *list; > > > + const QListEntry *entry; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + /* check there is at least a fs */ > > > + list = qdict_get_qlist(ret, "return"); > > > + entry = qlist_first(list); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint")); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type")); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk")); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_get_memory_block_info(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret, *val; > > > + int64_t size; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': > > > 'guest-get-memory-block-info'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + /* check there is at least some memory */ > > > + val = qdict_get_qdict(ret, "return"); > > > + size = qdict_get_int(val, "size"); > > > + g_assert_cmpint(size, >, 0); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_get_memory_blocks(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + QList *list; > > > + const QListEntry *entry; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + list = qdict_get_qlist(ret, "return"); > > > + entry = qlist_first(list); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index")); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_network_get_interfaces(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + QList *list; > > > + const QListEntry *entry; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': > > > 'guest-network-get-interfaces'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + /* check there is at least a cpu */ > > > > copy/paste error > > > > > + list = qdict_get_qlist(ret, "return"); > > > + entry = qlist_first(list); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_file_ops(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + gchar *cmd, *path; > > > + const gchar *b64; > > > + QDict *ret, *val; > > > + int64_t id, count, eof; > > > + FILE *f; > > > + char tmp[100]; > > > + > > > + /* open */ > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > > > + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + id = qdict_get_int(ret, "return"); > > > + QDECREF(ret); > > > + > > > + /* write */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-write'," > > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT > > > "," > > > + " 'buf-b64': 'aGVsbG8gd29ybGQK' } }", id); > > > > base64 encoding the string here would be more readible, or at least just > > a comment that it's a b64 "hello world". A separate variable/constant to > > store it so it's not manually duplicated below would be good too in case > > we ever wanted to change it to check some fringe case with encodings. > > > > > + ret = qmp_fd(fixture->fd, cmd); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + val = qdict_get_qdict(ret, "return"); > > > + count = qdict_get_int(val, "count"); > > > + eof = qdict_get_bool(val, "eof"); > > > + g_assert_cmpint(count, ==, 12); > > > + g_assert_cmpint(eof, ==, 0); > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* flush */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-flush'," > > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* close */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* check content */ > > > + path = g_build_filename(fixture->test_dir, "foo", NULL); > > > + f = fopen(path, "r"); > > > + g_assert_nonnull(f); > > > + count = fread(tmp, 1, sizeof(tmp), f); > > > + g_assert_cmpint(count, ==, 12); > > > + tmp[count] = 0; > > > + g_assert_cmpstr(tmp, ==, "hello world\n"); > > > + fclose(f); > > > + > > > + /* open */ > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," > > > + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + id = qdict_get_int(ret, "return"); > > > + QDECREF(ret); > > > + > > > + /* read */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + val = qdict_get_qdict(ret, "return"); > > > + count = qdict_get_int(val, "count"); > > > + eof = qdict_get_bool(val, "eof"); > > > + b64 = qdict_get_str(val, "buf-b64"); > > > + g_assert_cmpint(count, ==, 12); > > > + g_assert(eof); > > > + g_assert_cmpstr(b64, ==, "aGVsbG8gd29ybGQK"); > > > + > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* read eof */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + val = qdict_get_qdict(ret, "return"); > > > + count = qdict_get_int(val, "count"); > > > + eof = qdict_get_bool(val, "eof"); > > > + b64 = qdict_get_str(val, "buf-b64"); > > > + g_assert_cmpint(count, ==, 0); > > > + g_assert(eof); > > > + g_assert_cmpstr(b64, ==, ""); > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* seek */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-seek'," > > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT ", > > > " > > > + " 'offset': %d, 'whence': %d } }", > > > + id, 6, SEEK_SET); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + qmp_assert_no_error(ret); > > > + val = qdict_get_qdict(ret, "return"); > > > + count = qdict_get_int(val, "position"); > > > + eof = qdict_get_bool(val, "eof"); > > > + g_assert_cmpint(count, ==, 6); > > > + g_assert(!eof); > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* partial read */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-read'," > > > + " 'arguments': { 'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + val = qdict_get_qdict(ret, "return"); > > > + count = qdict_get_int(val, "count"); > > > + eof = qdict_get_bool(val, "eof"); > > > + b64 = qdict_get_str(val, "buf-b64"); > > > + g_assert_cmpint(count, ==, 6); > > > + g_assert(eof); > > > + g_assert_cmpstr(b64, ==, "d29ybGQK"); > > > > This makes me think doing the b64 encode/decodes here would be a lot > > easier, especially given that offsets are relative to the plaintext. > > > > > + > > > + QDECREF(ret); > > > + g_free(cmd); > > > + > > > + /* close */ > > > + cmd = g_strdup_printf("{'execute': 'guest-file-close'," > > > + " 'arguments': {'handle': %" G_GINT64_FORMAT "} > > > }", > > > + id); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + QDECREF(ret); > > > + g_free(cmd); > > > +} > > > + > > > +static void test_qga_get_time(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + int64_t time; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + time = qdict_get_int(ret, "return"); > > > + g_assert_cmpint(time, >, 0); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_set_time(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + int64_t current, time; > > > + gchar *cmd; > > > + > > > + /* get current time */ > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + current = qdict_get_int(ret, "return"); > > > + g_assert_cmpint(current, >, 0); > > > + QDECREF(ret); > > > + > > > + /* set some old time */ > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-set-time'," > > > + " 'arguments': { 'time': 1000 } }"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + QDECREF(ret); > > > + > > > + /* check old time */ > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + time = qdict_get_int(ret, "return"); > > > + g_assert_cmpint(time / 1000, <, G_USEC_PER_SEC * 10); > > > + QDECREF(ret); > > > + > > > + /* set back current time */ > > > + cmd = g_strdup_printf("{'execute': 'guest-set-time'," > > > + " 'arguments': { 'time': %" G_GINT64_FORMAT " } > > > }", > > > + current + time * 1000); > > > + ret = qmp_fd(fixture->fd, cmd); > > > + g_free(cmd); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_fstrim(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + QList *list; > > > + const QListEntry *entry; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fstrim'," > > > + " arguments: { minimum: 4194304 } }"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + list = qdict_get_qlist(ret, "return"); > > > + entry = qlist_first(list); > > > + g_assert(qdict_haskey(qobject_to_qdict(entry->value), "paths")); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_blacklist(gconstpointer data) > > > +{ > > > + TestFixture fix; > > > + QDict *ret, *error; > > > + const gchar *class, *desc; > > > + > > > + fixture_setup(&fix, "-b guest-ping,guest-get-time"); > > > + > > > + /* check blacklist */ > > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); > > > + g_assert_nonnull(ret); > > > + error = qdict_get_qdict(ret, "error"); > > > + class = qdict_get_try_str(error, "class"); > > > + desc = qdict_get_try_str(error, "desc"); > > > + g_assert_cmpstr(class, ==, "GenericError"); > > > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > > > + QDECREF(ret); > > > + > > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); > > > + g_assert_nonnull(ret); > > > + error = qdict_get_qdict(ret, "error"); > > > + class = qdict_get_try_str(error, "class"); > > > + desc = qdict_get_try_str(error, "desc"); > > > + g_assert_cmpstr(class, ==, "GenericError"); > > > + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); > > > + QDECREF(ret); > > > + > > > + /* check something work */ > > > + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); > > > + qmp_assert_no_error(ret); > > > + QDECREF(ret); > > > + > > > + fixture_tear_down(&fix, NULL); > > > +} > > > + > > > +#if !GLIB_CHECK_VERSION(2, 38, 0) > > > +#define g_assert_true(E) g_assert(E) > > > +#define g_assert_false(E) g_assert(!E) > > > +#endif > > > + > > > +static void test_qga_config(gconstpointer data) > > > +{ > > > + GError *error = NULL; > > > + char *cwd, *cmd, *out, *err, *str, **strv, *conf, **argv = NULL; > > > + char *env[2]; > > > + int status, tmp; > > > + gsize n; > > > + GKeyFile *kf; > > > + const char *qga_config = > > > + "[general]\n" > > > + "daemon=false\n" > > > + "method=virtio-serial\n" > > > + "path=/path/to/org.qemu.guest_agent.0\n" > > > + "pidfile=/var/foo/qemu-ga.pid\n" > > > + "statedir=/var/state\n" > > > + "verbose=true\n" > > > + "blacklist=guest-ping;guest-get-time\n"; > > > + > > > + tmp = g_file_open_tmp(NULL, &conf, &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpint(tmp, >=, 0); > > > + g_assert_cmpstr(conf, !=, ""); > > > + > > > + g_file_set_contents(conf, qga_config, -1, &error); > > > + g_assert_no_error(error); > > > + > > > + cwd = g_get_current_dir(); > > > + cmd = g_strdup_printf("%s%cqemu-ga -D", > > > + cwd, G_DIR_SEPARATOR); > > > + g_shell_parse_argv(cmd, NULL, &argv, &error); > > > + g_assert_no_error(error); > > > + > > > + env[0] = g_strdup_printf("QGA_CONF=%s", conf); > > > + env[1] = NULL; > > > + g_spawn_sync(NULL, argv, env, G_SPAWN_DEFAULT, > > > + NULL, NULL, &out, &err, &status, &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpstr(err, ==, ""); > > > + g_assert_cmpint(status, ==, 0); > > > + > > > + kf = g_key_file_new(); > > > + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); > > > + g_assert_no_error(error); > > > + > > > + str = g_key_file_get_start_group(kf); > > > + g_assert_cmpstr(str, ==, "general"); > > > + g_free(str); > > > + > > > + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", > > > &error)); > > > + g_assert_no_error(error); > > > + > > > + str = g_key_file_get_string(kf, "general", "method", &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpstr(str, ==, "virtio-serial"); > > > + g_free(str); > > > + > > > + str = g_key_file_get_string(kf, "general", "path", &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); > > > + g_free(str); > > > + > > > + str = g_key_file_get_string(kf, "general", "pidfile", &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); > > > + g_free(str); > > > + > > > + str = g_key_file_get_string(kf, "general", "statedir", &error); > > > + g_assert_no_error(error); > > > + g_assert_cmpstr(str, ==, "/var/state"); > > > + g_free(str); > > > + > > > + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", > > > &error)); > > > + g_assert_no_error(error); > > > + > > > + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, > > > &error); > > > + g_assert_cmpint(n, ==, 2); > > > +#if GLIB_CHECK_VERSION(2, 44, 0) > > > + g_assert_true(g_strv_contains((const char * const *)strv, > > > + "guest-ping")); > > > + g_assert_true(g_strv_contains((const char * const *)strv, > > > + "guest-get-time")); > > > +#endif > > > + g_assert_no_error(error); > > > + g_strfreev(strv); > > > + > > > + g_free(out); > > > + g_free(err); > > > + g_free(conf); > > > + g_free(env[0]); > > > + g_key_file_free(kf); > > > + > > > + close(tmp); > > > +} > > > + > > > +static void test_qga_fsfreeze_status(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + const gchar *status; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + > > > + status = qdict_get_try_str(ret, "return"); > > > + g_assert_cmpstr(status, ==, "thawed"); > > > + > > > + QDECREF(ret); > > > +} > > > + > > > +static void test_qga_fsfreeze_and_thaw(gconstpointer fix) > > > +{ > > > + const TestFixture *fixture = fix; > > > + QDict *ret; > > > + const gchar *status; > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-freeze'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + QDECREF(ret); > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + status = qdict_get_try_str(ret, "return"); > > > + g_assert_cmpstr(status, ==, "frozen"); > > > + QDECREF(ret); > > > + > > > + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-thaw'}"); > > > + g_assert_nonnull(ret); > > > + qmp_assert_no_error(ret); > > > + QDECREF(ret); > > > +} > > > + > > > +static gboolean running_in_virt(const gchar *virt) > > > +{ > > > + gchar *virt_what = NULL; > > > + gchar *err = NULL; > > > + gint status; > > > + gboolean success; > > > + > > > + success = g_spawn_command_line_sync("virt-what", &virt_what, &err, > > > + &status, NULL); > > > + > > > + success = success && status == 0 && > > > + g_strcmp0(g_strstrip(virt_what), virt) == 0; > > > + > > > + g_free(virt_what); > > > + g_free(err); > > > + > > > + return success; > > > +} > > > +int main(int argc, char **argv) > > > +{ > > > + TestFixture fix; > > > + int ret; > > > + > > > + setlocale (LC_ALL, ""); > > > + g_test_init(&argc, &argv, NULL); > > > + fixture_setup(&fix, NULL); > > > + > > > + g_test_add_data_func("/qga/sync-delimited", &fix, > > > test_qga_sync_delimited); > > > + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); > > > + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); > > > + g_test_add_data_func("/qga/info", &fix, test_qga_info); > > > + g_test_add_data_func("/qga/network-get-interfaces", &fix, > > > + test_qga_network_get_interfaces); > > > + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); > > > + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); > > > + g_test_add_data_func("/qga/get-memory-block-info", &fix, > > > + test_qga_get_memory_block_info); > > > + g_test_add_data_func("/qga/get-memory-blocks", &fix, > > > + test_qga_get_memory_blocks); > > > + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); > > > + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); > > > + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); > > > + g_test_add_data_func("/qga/fsfreeze-status", &fix, > > > + test_qga_fsfreeze_status); > > > + > > > + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); > > > + g_test_add_data_func("/qga/config", NULL, test_qga_config); > > > + > > > + if (running_in_virt("kvm")) { > > > > I think we should go ahead and replace this with a QGA_TEST_SIDE_EFFECTING > > environment variable or something of the sort. It's not necessarily the > > case that a VM environment means we can do side-effecting tests. It > > could be an "official" build server or someone's primary development > > But they wouldn't have root privileges though presumably. > > > environment and fsfreeze can do some funky stuff if they aren't > > intentionally running it. > > Sounds good to me. Would you pick this patch and make the required modification or do you want a resend? Actually, I just noticed g_assert_nonnull() was only added in 2.40, whereas our min is 2.22, so we'll need a wrapper for those. Can you go ahead and send a respin? We can probably leave the g_base64_decode/encode suggestion as a potential follow-up. > > > > > > + g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix, > > > + test_qga_fsfreeze_and_thaw); > > > + g_test_add_data_func("/qga/set-time", &fix, test_qga_set_time); > > > + if (g_test_thorough()) { > > > + g_test_add_data_func("/qga/fstrim", &fix, test_qga_fstrim); > > > + } > > > + } > > > + > > > + ret = g_test_run(); > > > + > > > + fixture_tear_down(&fix, NULL); > > > + > > > + return ret; > > > +} > > > -- > > > 2.4.3 > > > > > > > > > thanks > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau ` (3 preceding siblings ...) 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent marcandre.lureau @ 2015-09-25 10:15 ` Marc-André Lureau 4 siblings, 0 replies; 11+ messages in thread From: Marc-André Lureau @ 2015-09-25 10:15 UTC (permalink / raw) To: QEMU; +Cc: Marc-André Lureau, Michael Roth ping (that would be helpful to have basic qga testing in place) thanks On Fri, Sep 11, 2015 at 8:53 PM, <marcandre.lureau@redhat.com> wrote: > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > v1->v2: > - do not loop forever when connection to qga (Michael Roth) > - add some config file tests > - fix config verbosity > - swallow virt-what stderr > - only compile and check on Linux > > Marc-André Lureau (4): > qga: add QGA_CONF environment variable > qga: do not override configuration verbosity > qtest: add a few fd-level qmp helpers > tests: add a local test for guest agent > > qga/main.c | 7 +- > tests/Makefile | 3 + > tests/libqtest.c | 45 +++- > tests/libqtest.h | 7 + > tests/test-qga.c | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 846 insertions(+), 7 deletions(-) > create mode 100644 tests/test-qga.c > > -- > 2.4.3 > -- Marc-André Lureau ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2015-10-01 23:36 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-09-11 18:53 [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 1/4] qga: add QGA_CONF environment variable marcandre.lureau 2015-10-01 20:27 ` Michael Roth 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 2/4] qga: do not override configuration verbosity marcandre.lureau 2015-10-01 20:38 ` Michael Roth 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 3/4] qtest: add a few fd-level qmp helpers marcandre.lureau 2015-09-11 18:53 ` [Qemu-devel] [PATCH v2 4/4] tests: add a local test for guest agent marcandre.lureau 2015-10-01 21:36 ` Michael Roth 2015-10-01 22:24 ` Marc-André Lureau 2015-10-01 23:36 ` Michael Roth 2015-09-25 10:15 ` [Qemu-devel] [PATCH v2 0/4] qga: add local tests on linux Marc-André Lureau
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).