The tests directory itself is pretty overcrowded, and it's hard to see which test belongs to which test subsystem (unit, qtest, ...). Let's move the qtests to a separate folder for more clarity. Message-Id: <20191218103059.11729-6-thuth@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
		
			
				
	
	
		
			168 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * QTest migration helpers
 | 
						|
 *
 | 
						|
 * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
 | 
						|
 *   based on the vhost-user-test.c that is:
 | 
						|
 *      Copyright (c) 2014 Virtual Open Systems Sarl.
 | 
						|
 *
 | 
						|
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
						|
 * See the COPYING file in the top-level directory.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu/osdep.h"
 | 
						|
#include "qapi/qmp/qjson.h"
 | 
						|
 | 
						|
#include "migration-helpers.h"
 | 
						|
 | 
						|
bool got_stop;
 | 
						|
 | 
						|
static void stop_cb(void *opaque, const char *name, QDict *data)
 | 
						|
{
 | 
						|
    if (!strcmp(name, "STOP")) {
 | 
						|
        got_stop = true;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Events can get in the way of responses we are actually waiting for.
 | 
						|
 */
 | 
						|
QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
 | 
						|
    va_start(ap, command);
 | 
						|
    qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
 | 
						|
    va_end(ap);
 | 
						|
 | 
						|
    return qtest_qmp_receive_success(who, stop_cb, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Events can get in the way of responses we are actually waiting for.
 | 
						|
 */
 | 
						|
QDict *wait_command(QTestState *who, const char *command, ...)
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
 | 
						|
    va_start(ap, command);
 | 
						|
    qtest_qmp_vsend(who, command, ap);
 | 
						|
    va_end(ap);
 | 
						|
 | 
						|
    return qtest_qmp_receive_success(who, stop_cb, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send QMP command "migrate".
 | 
						|
 * Arguments are built from @fmt... (formatted like
 | 
						|
 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
 | 
						|
 */
 | 
						|
void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
    QDict *args, *rsp;
 | 
						|
 | 
						|
    va_start(ap, fmt);
 | 
						|
    args = qdict_from_vjsonf_nofail(fmt, ap);
 | 
						|
    va_end(ap);
 | 
						|
 | 
						|
    g_assert(!qdict_haskey(args, "uri"));
 | 
						|
    qdict_put_str(args, "uri", uri);
 | 
						|
 | 
						|
    rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
 | 
						|
 | 
						|
    g_assert(qdict_haskey(rsp, "return"));
 | 
						|
    qobject_unref(rsp);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Note: caller is responsible to free the returned object via
 | 
						|
 * qobject_unref() after use
 | 
						|
 */
 | 
						|
QDict *migrate_query(QTestState *who)
 | 
						|
{
 | 
						|
    return wait_command(who, "{ 'execute': 'query-migrate' }");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Note: caller is responsible to free the returned object via
 | 
						|
 * g_free() after use
 | 
						|
 */
 | 
						|
static gchar *migrate_query_status(QTestState *who)
 | 
						|
{
 | 
						|
    QDict *rsp_return = migrate_query(who);
 | 
						|
    gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
 | 
						|
 | 
						|
    g_assert(status);
 | 
						|
    qobject_unref(rsp_return);
 | 
						|
 | 
						|
    return status;
 | 
						|
}
 | 
						|
 | 
						|
static bool check_migration_status(QTestState *who, const char *goal,
 | 
						|
                                   const char **ungoals)
 | 
						|
{
 | 
						|
    bool ready;
 | 
						|
    char *current_status;
 | 
						|
    const char **ungoal;
 | 
						|
 | 
						|
    current_status = migrate_query_status(who);
 | 
						|
    ready = strcmp(current_status, goal) == 0;
 | 
						|
    if (!ungoals) {
 | 
						|
        g_assert_cmpstr(current_status, !=, "failed");
 | 
						|
        /*
 | 
						|
         * If looking for a state other than completed,
 | 
						|
         * completion of migration would cause the test to
 | 
						|
         * hang.
 | 
						|
         */
 | 
						|
        if (strcmp(goal, "completed") != 0) {
 | 
						|
            g_assert_cmpstr(current_status, !=, "completed");
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        for (ungoal = ungoals; *ungoal; ungoal++) {
 | 
						|
            g_assert_cmpstr(current_status, !=,  *ungoal);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    g_free(current_status);
 | 
						|
    return ready;
 | 
						|
}
 | 
						|
 | 
						|
void wait_for_migration_status(QTestState *who,
 | 
						|
                               const char *goal, const char **ungoals)
 | 
						|
{
 | 
						|
    while (!check_migration_status(who, goal, ungoals)) {
 | 
						|
        usleep(1000);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wait_for_migration_complete(QTestState *who)
 | 
						|
{
 | 
						|
    wait_for_migration_status(who, "completed", NULL);
 | 
						|
}
 | 
						|
 | 
						|
void wait_for_migration_fail(QTestState *from, bool allow_active)
 | 
						|
{
 | 
						|
    QDict *rsp_return;
 | 
						|
    char *status;
 | 
						|
    bool failed;
 | 
						|
 | 
						|
    do {
 | 
						|
        status = migrate_query_status(from);
 | 
						|
        bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
 | 
						|
            (allow_active && !strcmp(status, "active"));
 | 
						|
        if (!result) {
 | 
						|
            fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
 | 
						|
                    __func__, status, allow_active);
 | 
						|
        }
 | 
						|
        g_assert(result);
 | 
						|
        failed = !strcmp(status, "failed");
 | 
						|
        g_free(status);
 | 
						|
    } while (!failed);
 | 
						|
 | 
						|
    /* Is the machine currently running? */
 | 
						|
    rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
 | 
						|
    g_assert(qdict_haskey(rsp_return, "running"));
 | 
						|
    g_assert(qdict_get_bool(rsp_return, "running"));
 | 
						|
    qobject_unref(rsp_return);
 | 
						|
}
 |