qemu/tests/unit/test-qga.c
<<
>>
Prefs
   1#include "qemu/osdep.h"
   2#include <locale.h>
   3#include <glib/gstdio.h>
   4#include <sys/socket.h>
   5#include <sys/un.h>
   6
   7#include "../qtest/libqos/libqtest.h"
   8#include "qapi/qmp/qdict.h"
   9#include "qapi/qmp/qlist.h"
  10
  11typedef struct {
  12    char *test_dir;
  13    GMainLoop *loop;
  14    int fd;
  15    GPid pid;
  16} TestFixture;
  17
  18static int connect_qga(char *path)
  19{
  20    int s, ret, len, i = 0;
  21    struct sockaddr_un remote;
  22
  23    s = socket(AF_UNIX, SOCK_STREAM, 0);
  24    g_assert(s != -1);
  25
  26    remote.sun_family = AF_UNIX;
  27    do {
  28        strcpy(remote.sun_path, path);
  29        len = strlen(remote.sun_path) + sizeof(remote.sun_family);
  30        ret = connect(s, (struct sockaddr *)&remote, len);
  31        if (ret == -1) {
  32            g_usleep(G_USEC_PER_SEC);
  33        }
  34        if (i++ == 10) {
  35            return -1;
  36        }
  37    } while (ret == -1);
  38
  39    return s;
  40}
  41
  42static void qga_watch(GPid pid, gint status, gpointer user_data)
  43{
  44    TestFixture *fixture = user_data;
  45
  46    g_assert_cmpint(status, ==, 0);
  47    g_main_loop_quit(fixture->loop);
  48}
  49
  50static void
  51fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp)
  52{
  53    const gchar *extra_arg = data;
  54    GError *error = NULL;
  55    gchar *cwd, *path, *cmd, **argv = NULL;
  56
  57    fixture->loop = g_main_loop_new(NULL, FALSE);
  58
  59    fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX");
  60    g_assert_nonnull(mkdtemp(fixture->test_dir));
  61
  62    path = g_build_filename(fixture->test_dir, "sock", NULL);
  63    cwd = g_get_current_dir();
  64    cmd = g_strdup_printf("%s%cqga%cqemu-ga -m unix-listen -t %s -p %s %s %s",
  65                          cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR,
  66                          fixture->test_dir, path,
  67                          getenv("QTEST_LOG") ? "-v" : "",
  68                          extra_arg ?: "");
  69    g_shell_parse_argv(cmd, NULL, &argv, &error);
  70    g_assert_no_error(error);
  71
  72    g_spawn_async(fixture->test_dir, argv, envp,
  73                  G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,
  74                  NULL, NULL, &fixture->pid, &error);
  75    g_assert_no_error(error);
  76
  77    g_child_watch_add(fixture->pid, qga_watch, fixture);
  78
  79    fixture->fd = connect_qga(path);
  80    g_assert_cmpint(fixture->fd, !=, -1);
  81
  82    g_strfreev(argv);
  83    g_free(cmd);
  84    g_free(cwd);
  85    g_free(path);
  86}
  87
  88static void
  89fixture_tear_down(TestFixture *fixture, gconstpointer data)
  90{
  91    gchar *tmp;
  92
  93    kill(fixture->pid, SIGTERM);
  94
  95    g_main_loop_run(fixture->loop);
  96    g_main_loop_unref(fixture->loop);
  97
  98    g_spawn_close_pid(fixture->pid);
  99
 100    tmp = g_build_filename(fixture->test_dir, "foo", NULL);
 101    g_unlink(tmp);
 102    g_free(tmp);
 103
 104    tmp = g_build_filename(fixture->test_dir, "qga.state", NULL);
 105    g_unlink(tmp);
 106    g_free(tmp);
 107
 108    tmp = g_build_filename(fixture->test_dir, "sock", NULL);
 109    g_unlink(tmp);
 110    g_free(tmp);
 111
 112    g_rmdir(fixture->test_dir);
 113    g_free(fixture->test_dir);
 114    close(fixture->fd);
 115}
 116
 117static void qmp_assertion_message_error(const char     *domain,
 118                                        const char     *file,
 119                                        int             line,
 120                                        const char     *func,
 121                                        const char     *expr,
 122                                        QDict          *dict)
 123{
 124    const char *class, *desc;
 125    char *s;
 126    QDict *error;
 127
 128    error = qdict_get_qdict(dict, "error");
 129    class = qdict_get_try_str(error, "class");
 130    desc = qdict_get_try_str(error, "desc");
 131
 132    s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc);
 133    g_assertion_message(domain, file, line, func, s);
 134    g_free(s);
 135}
 136
 137#define qmp_assert_no_error(err) do {                                   \
 138    if (qdict_haskey(err, "error")) {                                   \
 139        qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__,   \
 140                                    G_STRFUNC, #err, err);              \
 141    }                                                                   \
 142} while (0)
 143
 144static void test_qga_sync_delimited(gconstpointer fix)
 145{
 146    const TestFixture *fixture = fix;
 147    guint32 v, r = g_test_rand_int();
 148    unsigned char c;
 149    QDict *ret;
 150
 151    qmp_fd_send_raw(fixture->fd, "\xff");
 152    qmp_fd_send(fixture->fd,
 153                "{'execute': 'guest-sync-delimited',"
 154                " 'arguments': {'id': %u } }",
 155                r);
 156
 157    /*
 158     * Read and ignore garbage until resynchronized.
 159     *
 160     * Note that the full reset sequence would involve checking the
 161     * response of guest-sync-delimited and repeating the loop if
 162     * 'id' field of the response does not match the 'id' field of
 163     * the request. Testing this fully would require inserting
 164     * garbage in the response stream and is left as a future test
 165     * to implement.
 166     *
 167     * TODO: The server shouldn't emit so much garbage (among other
 168     * things, it loudly complains about the client's \xff being
 169     * invalid JSON, even though it is a documented part of the
 170     * handshake.
 171     */
 172    do {
 173        v = read(fixture->fd, &c, 1);
 174        g_assert_cmpint(v, ==, 1);
 175    } while (c != 0xff);
 176
 177    ret = qmp_fd_receive(fixture->fd);
 178    g_assert_nonnull(ret);
 179    qmp_assert_no_error(ret);
 180
 181    v = qdict_get_int(ret, "return");
 182    g_assert_cmpint(r, ==, v);
 183
 184    qobject_unref(ret);
 185}
 186
 187static void test_qga_sync(gconstpointer fix)
 188{
 189    const TestFixture *fixture = fix;
 190    guint32 v, r = g_test_rand_int();
 191    QDict *ret;
 192
 193    /*
 194     * TODO guest-sync is inherently limited: we cannot distinguish
 195     * failure caused by reacting to garbage on the wire prior to this
 196     * command, from failure of this actual command. Clients are
 197     * supposed to be able to send a raw '\xff' byte to at least
 198     * re-synchronize the server's parser prior to this command, but
 199     * we are not in a position to test that here because (at least
 200     * for now) it causes the server to issue an error message about
 201     * invalid JSON. Testing of '\xff' handling is done in
 202     * guest-sync-delimited instead.
 203     */
 204    ret = qmp_fd(fixture->fd,
 205                 "{'execute': 'guest-sync', 'arguments': {'id': %u } }",
 206                 r);
 207
 208    g_assert_nonnull(ret);
 209    qmp_assert_no_error(ret);
 210
 211    v = qdict_get_int(ret, "return");
 212    g_assert_cmpint(r, ==, v);
 213
 214    qobject_unref(ret);
 215}
 216
 217static void test_qga_ping(gconstpointer fix)
 218{
 219    const TestFixture *fixture = fix;
 220    QDict *ret;
 221
 222    ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}");
 223    g_assert_nonnull(ret);
 224    qmp_assert_no_error(ret);
 225
 226    qobject_unref(ret);
 227}
 228
 229static void test_qga_id(gconstpointer fix)
 230{
 231    const TestFixture *fixture = fix;
 232    QDict *ret;
 233
 234    ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
 235    g_assert_nonnull(ret);
 236    qmp_assert_no_error(ret);
 237    g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1);
 238
 239    qobject_unref(ret);
 240}
 241
 242static void test_qga_invalid_oob(gconstpointer fix)
 243{
 244    const TestFixture *fixture = fix;
 245    QDict *ret;
 246
 247    ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}");
 248    g_assert_nonnull(ret);
 249
 250    qmp_expect_error_and_unref(ret, "GenericError");
 251}
 252
 253static void test_qga_invalid_args(gconstpointer fix)
 254{
 255    const TestFixture *fixture = fix;
 256    QDict *ret, *error;
 257    const gchar *class, *desc;
 258
 259    ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', "
 260                 "'arguments': {'foo': 42 }}");
 261    g_assert_nonnull(ret);
 262
 263    error = qdict_get_qdict(ret, "error");
 264    class = qdict_get_try_str(error, "class");
 265    desc = qdict_get_try_str(error, "desc");
 266
 267    g_assert_cmpstr(class, ==, "GenericError");
 268    g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected");
 269
 270    qobject_unref(ret);
 271}
 272
 273static void test_qga_invalid_cmd(gconstpointer fix)
 274{
 275    const TestFixture *fixture = fix;
 276    QDict *ret, *error;
 277    const gchar *class, *desc;
 278
 279    ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}");
 280    g_assert_nonnull(ret);
 281
 282    error = qdict_get_qdict(ret, "error");
 283    class = qdict_get_try_str(error, "class");
 284    desc = qdict_get_try_str(error, "desc");
 285
 286    g_assert_cmpstr(class, ==, "CommandNotFound");
 287    g_assert_cmpint(strlen(desc), >, 0);
 288
 289    qobject_unref(ret);
 290}
 291
 292static void test_qga_info(gconstpointer fix)
 293{
 294    const TestFixture *fixture = fix;
 295    QDict *ret, *val;
 296    const gchar *version;
 297
 298    ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}");
 299    g_assert_nonnull(ret);
 300    qmp_assert_no_error(ret);
 301
 302    val = qdict_get_qdict(ret, "return");
 303    version = qdict_get_try_str(val, "version");
 304    g_assert_cmpstr(version, ==, QEMU_VERSION);
 305
 306    qobject_unref(ret);
 307}
 308
 309static void test_qga_get_vcpus(gconstpointer fix)
 310{
 311    const TestFixture *fixture = fix;
 312    QDict *ret;
 313    QList *list;
 314    const QListEntry *entry;
 315
 316    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}");
 317    g_assert_nonnull(ret);
 318    qmp_assert_no_error(ret);
 319
 320    /* check there is at least a cpu */
 321    list = qdict_get_qlist(ret, "return");
 322    entry = qlist_first(list);
 323    g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
 324    g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id"));
 325
 326    qobject_unref(ret);
 327}
 328
 329static void test_qga_get_fsinfo(gconstpointer fix)
 330{
 331    const TestFixture *fixture = fix;
 332    QDict *ret;
 333    QList *list;
 334    const QListEntry *entry;
 335
 336    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}");
 337    g_assert_nonnull(ret);
 338    qmp_assert_no_error(ret);
 339
 340    /* sanity-check the response if there are any filesystems */
 341    list = qdict_get_qlist(ret, "return");
 342    entry = qlist_first(list);
 343    if (entry) {
 344        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
 345        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint"));
 346        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type"));
 347        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk"));
 348    }
 349
 350    qobject_unref(ret);
 351}
 352
 353static void test_qga_get_memory_block_info(gconstpointer fix)
 354{
 355    const TestFixture *fixture = fix;
 356    QDict *ret, *val;
 357    int64_t size;
 358
 359    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}");
 360    g_assert_nonnull(ret);
 361
 362    /* some systems might not expose memory block info in sysfs */
 363    if (!qdict_haskey(ret, "error")) {
 364        /* check there is at least some memory */
 365        val = qdict_get_qdict(ret, "return");
 366        size = qdict_get_int(val, "size");
 367        g_assert_cmpint(size, >, 0);
 368    }
 369
 370    qobject_unref(ret);
 371}
 372
 373static void test_qga_get_memory_blocks(gconstpointer fix)
 374{
 375    const TestFixture *fixture = fix;
 376    QDict *ret;
 377    QList *list;
 378    const QListEntry *entry;
 379
 380    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}");
 381    g_assert_nonnull(ret);
 382
 383    /* some systems might not expose memory block info in sysfs */
 384    if (!qdict_haskey(ret, "error")) {
 385        list = qdict_get_qlist(ret, "return");
 386        entry = qlist_first(list);
 387        /* newer versions of qga may return empty list without error */
 388        if (entry) {
 389            g_assert(qdict_haskey(qobject_to(QDict, entry->value),
 390                                  "phys-index"));
 391            g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
 392        }
 393    }
 394
 395    qobject_unref(ret);
 396}
 397
 398static void test_qga_network_get_interfaces(gconstpointer fix)
 399{
 400    const TestFixture *fixture = fix;
 401    QDict *ret;
 402    QList *list;
 403    const QListEntry *entry;
 404
 405    ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}");
 406    g_assert_nonnull(ret);
 407    qmp_assert_no_error(ret);
 408
 409    /* check there is at least an interface */
 410    list = qdict_get_qlist(ret, "return");
 411    entry = qlist_first(list);
 412    g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
 413
 414    qobject_unref(ret);
 415}
 416
 417static void test_qga_file_ops(gconstpointer fix)
 418{
 419    const TestFixture *fixture = fix;
 420    const unsigned char helloworld[] = "Hello World!\n";
 421    const char *b64;
 422    gchar *path, *enc;
 423    unsigned char *dec;
 424    QDict *ret, *val;
 425    int64_t id, eof;
 426    gsize count;
 427    FILE *f;
 428    char tmp[100];
 429
 430    /* open */
 431    ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open',"
 432                 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }");
 433    g_assert_nonnull(ret);
 434    qmp_assert_no_error(ret);
 435    id = qdict_get_int(ret, "return");
 436    qobject_unref(ret);
 437
 438    enc = g_base64_encode(helloworld, sizeof(helloworld));
 439    /* write */
 440    ret = qmp_fd(fixture->fd,
 441                 "{'execute': 'guest-file-write',"
 442                 " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }",
 443                 id, enc);
 444    g_assert_nonnull(ret);
 445    qmp_assert_no_error(ret);
 446
 447    val = qdict_get_qdict(ret, "return");
 448    count = qdict_get_int(val, "count");
 449    eof = qdict_get_bool(val, "eof");
 450    g_assert_cmpint(count, ==, sizeof(helloworld));
 451    g_assert_cmpint(eof, ==, 0);
 452    qobject_unref(ret);
 453
 454    /* flush */
 455    ret = qmp_fd(fixture->fd,
 456                 "{'execute': 'guest-file-flush',"
 457                 " 'arguments': {'handle': %" PRId64 "} }",
 458                 id);
 459    qobject_unref(ret);
 460
 461    /* close */
 462    ret = qmp_fd(fixture->fd,
 463                 "{'execute': 'guest-file-close',"
 464                 " 'arguments': {'handle': %" PRId64 "} }",
 465                 id);
 466    qobject_unref(ret);
 467
 468    /* check content */
 469    path = g_build_filename(fixture->test_dir, "foo", NULL);
 470    f = fopen(path, "r");
 471    g_free(path);
 472    g_assert_nonnull(f);
 473    count = fread(tmp, 1, sizeof(tmp), f);
 474    g_assert_cmpint(count, ==, sizeof(helloworld));
 475    tmp[count] = 0;
 476    g_assert_cmpstr(tmp, ==, (char *)helloworld);
 477    fclose(f);
 478
 479    /* open */
 480    ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open',"
 481                 " 'arguments': { 'path': 'foo', 'mode': 'r' } }");
 482    g_assert_nonnull(ret);
 483    qmp_assert_no_error(ret);
 484    id = qdict_get_int(ret, "return");
 485    qobject_unref(ret);
 486
 487    /* read */
 488    ret = qmp_fd(fixture->fd,
 489                 "{'execute': 'guest-file-read',"
 490                 " 'arguments': { 'handle': %" PRId64 "} }",
 491                 id);
 492    val = qdict_get_qdict(ret, "return");
 493    count = qdict_get_int(val, "count");
 494    eof = qdict_get_bool(val, "eof");
 495    b64 = qdict_get_str(val, "buf-b64");
 496    g_assert_cmpint(count, ==, sizeof(helloworld));
 497    g_assert(eof);
 498    g_assert_cmpstr(b64, ==, enc);
 499
 500    qobject_unref(ret);
 501    g_free(enc);
 502
 503    /* read eof */
 504    ret = qmp_fd(fixture->fd,
 505                 "{'execute': 'guest-file-read',"
 506                 " 'arguments': { 'handle': %" PRId64 "} }",
 507                 id);
 508    val = qdict_get_qdict(ret, "return");
 509    count = qdict_get_int(val, "count");
 510    eof = qdict_get_bool(val, "eof");
 511    b64 = qdict_get_str(val, "buf-b64");
 512    g_assert_cmpint(count, ==, 0);
 513    g_assert(eof);
 514    g_assert_cmpstr(b64, ==, "");
 515    qobject_unref(ret);
 516
 517    /* seek */
 518    ret = qmp_fd(fixture->fd,
 519                 "{'execute': 'guest-file-seek',"
 520                 " 'arguments': { 'handle': %" PRId64 ", "
 521                 " 'offset': %d, 'whence': %s } }",
 522                 id, 6, "set");
 523    qmp_assert_no_error(ret);
 524    val = qdict_get_qdict(ret, "return");
 525    count = qdict_get_int(val, "position");
 526    eof = qdict_get_bool(val, "eof");
 527    g_assert_cmpint(count, ==, 6);
 528    g_assert(!eof);
 529    qobject_unref(ret);
 530
 531    /* partial read */
 532    ret = qmp_fd(fixture->fd,
 533                 "{'execute': 'guest-file-read',"
 534                 " 'arguments': { 'handle': %" PRId64 "} }",
 535                 id);
 536    val = qdict_get_qdict(ret, "return");
 537    count = qdict_get_int(val, "count");
 538    eof = qdict_get_bool(val, "eof");
 539    b64 = qdict_get_str(val, "buf-b64");
 540    g_assert_cmpint(count, ==, sizeof(helloworld) - 6);
 541    g_assert(eof);
 542    dec = g_base64_decode(b64, &count);
 543    g_assert_cmpint(count, ==, sizeof(helloworld) - 6);
 544    g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6);
 545    g_free(dec);
 546
 547    qobject_unref(ret);
 548
 549    /* close */
 550    ret = qmp_fd(fixture->fd,
 551                 "{'execute': 'guest-file-close',"
 552                 " 'arguments': {'handle': %" PRId64 "} }",
 553                 id);
 554    qobject_unref(ret);
 555}
 556
 557static void test_qga_file_write_read(gconstpointer fix)
 558{
 559    const TestFixture *fixture = fix;
 560    const unsigned char helloworld[] = "Hello World!\n";
 561    const char *b64;
 562    gchar *enc;
 563    QDict *ret, *val;
 564    int64_t id, eof;
 565    gsize count;
 566
 567    /* open */
 568    ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open',"
 569                 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }");
 570    g_assert_nonnull(ret);
 571    qmp_assert_no_error(ret);
 572    id = qdict_get_int(ret, "return");
 573    qobject_unref(ret);
 574
 575    enc = g_base64_encode(helloworld, sizeof(helloworld));
 576    /* write */
 577    ret = qmp_fd(fixture->fd,
 578                 "{'execute': 'guest-file-write',"
 579                 " 'arguments': { 'handle': %" PRId64 ","
 580                 " 'buf-b64': %s } }", id, enc);
 581    g_assert_nonnull(ret);
 582    qmp_assert_no_error(ret);
 583
 584    val = qdict_get_qdict(ret, "return");
 585    count = qdict_get_int(val, "count");
 586    eof = qdict_get_bool(val, "eof");
 587    g_assert_cmpint(count, ==, sizeof(helloworld));
 588    g_assert_cmpint(eof, ==, 0);
 589    qobject_unref(ret);
 590
 591    /* read (check implicit flush) */
 592    ret = qmp_fd(fixture->fd,
 593                 "{'execute': 'guest-file-read',"
 594                 " 'arguments': { 'handle': %" PRId64 "} }",
 595                 id);
 596    val = qdict_get_qdict(ret, "return");
 597    count = qdict_get_int(val, "count");
 598    eof = qdict_get_bool(val, "eof");
 599    b64 = qdict_get_str(val, "buf-b64");
 600    g_assert_cmpint(count, ==, 0);
 601    g_assert(eof);
 602    g_assert_cmpstr(b64, ==, "");
 603    qobject_unref(ret);
 604
 605    /* seek to 0 */
 606    ret = qmp_fd(fixture->fd,
 607                 "{'execute': 'guest-file-seek',"
 608                 " 'arguments': { 'handle': %" PRId64 ", "
 609                 " 'offset': %d, 'whence': %s } }",
 610                 id, 0, "set");
 611    qmp_assert_no_error(ret);
 612    val = qdict_get_qdict(ret, "return");
 613    count = qdict_get_int(val, "position");
 614    eof = qdict_get_bool(val, "eof");
 615    g_assert_cmpint(count, ==, 0);
 616    g_assert(!eof);
 617    qobject_unref(ret);
 618
 619    /* read */
 620    ret = qmp_fd(fixture->fd,
 621                 "{'execute': 'guest-file-read',"
 622                 " 'arguments': { 'handle': %" PRId64 "} }",
 623                 id);
 624    val = qdict_get_qdict(ret, "return");
 625    count = qdict_get_int(val, "count");
 626    eof = qdict_get_bool(val, "eof");
 627    b64 = qdict_get_str(val, "buf-b64");
 628    g_assert_cmpint(count, ==, sizeof(helloworld));
 629    g_assert(eof);
 630    g_assert_cmpstr(b64, ==, enc);
 631    qobject_unref(ret);
 632    g_free(enc);
 633
 634    /* close */
 635    ret = qmp_fd(fixture->fd,
 636                 "{'execute': 'guest-file-close',"
 637                 " 'arguments': {'handle': %" PRId64 "} }",
 638                 id);
 639    qobject_unref(ret);
 640}
 641
 642static void test_qga_get_time(gconstpointer fix)
 643{
 644    const TestFixture *fixture = fix;
 645    QDict *ret;
 646    int64_t time;
 647
 648    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}");
 649    g_assert_nonnull(ret);
 650    qmp_assert_no_error(ret);
 651
 652    time = qdict_get_int(ret, "return");
 653    g_assert_cmpint(time, >, 0);
 654
 655    qobject_unref(ret);
 656}
 657
 658static void test_qga_blacklist(gconstpointer data)
 659{
 660    TestFixture fix;
 661    QDict *ret, *error;
 662    const gchar *class, *desc;
 663
 664    fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL);
 665
 666    /* check blacklist */
 667    ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}");
 668    g_assert_nonnull(ret);
 669    error = qdict_get_qdict(ret, "error");
 670    class = qdict_get_try_str(error, "class");
 671    desc = qdict_get_try_str(error, "desc");
 672    g_assert_cmpstr(class, ==, "CommandNotFound");
 673    g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled"));
 674    qobject_unref(ret);
 675
 676    ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}");
 677    g_assert_nonnull(ret);
 678    error = qdict_get_qdict(ret, "error");
 679    class = qdict_get_try_str(error, "class");
 680    desc = qdict_get_try_str(error, "desc");
 681    g_assert_cmpstr(class, ==, "CommandNotFound");
 682    g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled"));
 683    qobject_unref(ret);
 684
 685    /* check something work */
 686    ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}");
 687    qmp_assert_no_error(ret);
 688    qobject_unref(ret);
 689
 690    fixture_tear_down(&fix, NULL);
 691}
 692
 693static void test_qga_config(gconstpointer data)
 694{
 695    GError *error = NULL;
 696    char *cwd, *cmd, *out, *err, *str, **strv, **argv = NULL;
 697    char *env[2];
 698    int status;
 699    gsize n;
 700    GKeyFile *kf;
 701
 702    cwd = g_get_current_dir();
 703    cmd = g_strdup_printf("%s%cqga%cqemu-ga -D",
 704                          cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR);
 705    g_free(cwd);
 706    g_shell_parse_argv(cmd, NULL, &argv, &error);
 707    g_free(cmd);
 708    g_assert_no_error(error);
 709
 710    env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config",
 711                             G_DIR_SEPARATOR, G_DIR_SEPARATOR);
 712    env[1] = NULL;
 713    g_spawn_sync(NULL, argv, env, 0,
 714                 NULL, NULL, &out, &err, &status, &error);
 715    g_strfreev(argv);
 716
 717    g_assert_no_error(error);
 718    g_assert_cmpstr(err, ==, "");
 719    g_assert_cmpint(status, ==, 0);
 720
 721    kf = g_key_file_new();
 722    g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error);
 723    g_assert_no_error(error);
 724
 725    str = g_key_file_get_start_group(kf);
 726    g_assert_cmpstr(str, ==, "general");
 727    g_free(str);
 728
 729    g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error));
 730    g_assert_no_error(error);
 731
 732    str = g_key_file_get_string(kf, "general", "method", &error);
 733    g_assert_no_error(error);
 734    g_assert_cmpstr(str, ==, "virtio-serial");
 735    g_free(str);
 736
 737    str = g_key_file_get_string(kf, "general", "path", &error);
 738    g_assert_no_error(error);
 739    g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0");
 740    g_free(str);
 741
 742    str = g_key_file_get_string(kf, "general", "pidfile", &error);
 743    g_assert_no_error(error);
 744    g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid");
 745    g_free(str);
 746
 747    str = g_key_file_get_string(kf, "general", "statedir", &error);
 748    g_assert_no_error(error);
 749    g_assert_cmpstr(str, ==, "/var/state");
 750    g_free(str);
 751
 752    g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error));
 753    g_assert_no_error(error);
 754
 755    strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error);
 756    g_assert_cmpint(n, ==, 2);
 757    g_assert_true(g_strv_contains((const char * const *)strv,
 758                                  "guest-ping"));
 759    g_assert_true(g_strv_contains((const char * const *)strv,
 760                                  "guest-get-time"));
 761    g_assert_no_error(error);
 762    g_strfreev(strv);
 763
 764    g_free(out);
 765    g_free(err);
 766    g_free(env[0]);
 767    g_key_file_free(kf);
 768}
 769
 770static void test_qga_fsfreeze_status(gconstpointer fix)
 771{
 772    const TestFixture *fixture = fix;
 773    QDict *ret;
 774    const gchar *status;
 775
 776    ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}");
 777    g_assert_nonnull(ret);
 778    qmp_assert_no_error(ret);
 779
 780    status = qdict_get_try_str(ret, "return");
 781    g_assert_cmpstr(status, ==, "thawed");
 782
 783    qobject_unref(ret);
 784}
 785
 786static void test_qga_guest_exec(gconstpointer fix)
 787{
 788    const TestFixture *fixture = fix;
 789    QDict *ret, *val;
 790    const gchar *out;
 791    guchar *decoded;
 792    int64_t pid, now, exitcode;
 793    gsize len;
 794    bool exited;
 795
 796    /* exec 'echo foo bar' */
 797    ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {"
 798                 " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ],"
 799                 " 'capture-output': true } }");
 800    g_assert_nonnull(ret);
 801    qmp_assert_no_error(ret);
 802    val = qdict_get_qdict(ret, "return");
 803    pid = qdict_get_int(val, "pid");
 804    g_assert_cmpint(pid, >, 0);
 805    qobject_unref(ret);
 806
 807    /* wait for completion */
 808    now = g_get_monotonic_time();
 809    do {
 810        ret = qmp_fd(fixture->fd,
 811                     "{'execute': 'guest-exec-status',"
 812                     " 'arguments': { 'pid': %" PRId64 " } }", pid);
 813        g_assert_nonnull(ret);
 814        val = qdict_get_qdict(ret, "return");
 815        exited = qdict_get_bool(val, "exited");
 816        if (!exited) {
 817            qobject_unref(ret);
 818        }
 819    } while (!exited &&
 820             g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND);
 821    g_assert(exited);
 822
 823    /* check stdout */
 824    exitcode = qdict_get_int(val, "exitcode");
 825    g_assert_cmpint(exitcode, ==, 0);
 826    out = qdict_get_str(val, "out-data");
 827    decoded = g_base64_decode(out, &len);
 828    g_assert_cmpint(len, ==, 12);
 829    g_assert_cmpstr((char *)decoded, ==, "\" test_str \"");
 830    g_free(decoded);
 831    qobject_unref(ret);
 832}
 833
 834static void test_qga_guest_exec_invalid(gconstpointer fix)
 835{
 836    const TestFixture *fixture = fix;
 837    QDict *ret, *error;
 838    const gchar *class, *desc;
 839
 840    /* invalid command */
 841    ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {"
 842                 " 'path': '/bin/invalid-cmd42' } }");
 843    g_assert_nonnull(ret);
 844    error = qdict_get_qdict(ret, "error");
 845    g_assert_nonnull(error);
 846    class = qdict_get_str(error, "class");
 847    desc = qdict_get_str(error, "desc");
 848    g_assert_cmpstr(class, ==, "GenericError");
 849    g_assert_cmpint(strlen(desc), >, 0);
 850    qobject_unref(ret);
 851
 852    /* invalid pid */
 853    ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status',"
 854                 " 'arguments': { 'pid': 0 } }");
 855    g_assert_nonnull(ret);
 856    error = qdict_get_qdict(ret, "error");
 857    g_assert_nonnull(error);
 858    class = qdict_get_str(error, "class");
 859    desc = qdict_get_str(error, "desc");
 860    g_assert_cmpstr(class, ==, "GenericError");
 861    g_assert_cmpint(strlen(desc), >, 0);
 862    qobject_unref(ret);
 863}
 864
 865static void test_qga_guest_get_host_name(gconstpointer fix)
 866{
 867    const TestFixture *fixture = fix;
 868    QDict *ret, *val;
 869
 870    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}");
 871    g_assert_nonnull(ret);
 872    qmp_assert_no_error(ret);
 873
 874    val = qdict_get_qdict(ret, "return");
 875    g_assert(qdict_haskey(val, "host-name"));
 876
 877    qobject_unref(ret);
 878}
 879
 880static void test_qga_guest_get_timezone(gconstpointer fix)
 881{
 882    const TestFixture *fixture = fix;
 883    QDict *ret, *val;
 884
 885    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}");
 886    g_assert_nonnull(ret);
 887    qmp_assert_no_error(ret);
 888
 889    /* Make sure there's at least offset */
 890    val = qdict_get_qdict(ret, "return");
 891    g_assert(qdict_haskey(val, "offset"));
 892
 893    qobject_unref(ret);
 894}
 895
 896static void test_qga_guest_get_users(gconstpointer fix)
 897{
 898    const TestFixture *fixture = fix;
 899    QDict *ret;
 900    QList *val;
 901
 902    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}");
 903    g_assert_nonnull(ret);
 904    qmp_assert_no_error(ret);
 905
 906    /* There is not much to test here */
 907    val = qdict_get_qlist(ret, "return");
 908    g_assert_nonnull(val);
 909
 910    qobject_unref(ret);
 911}
 912
 913static void test_qga_guest_get_osinfo(gconstpointer data)
 914{
 915    TestFixture fixture;
 916    const gchar *str;
 917    gchar *cwd, *env[2];
 918    QDict *ret, *val;
 919
 920    cwd = g_get_current_dir();
 921    env[0] = g_strdup_printf(
 922        "QGA_OS_RELEASE=%s%ctests%cdata%ctest-qga-os-release",
 923        cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR);
 924    env[1] = NULL;
 925    g_free(cwd);
 926    fixture_setup(&fixture, NULL, env);
 927
 928    ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}");
 929    g_assert_nonnull(ret);
 930    qmp_assert_no_error(ret);
 931
 932    val = qdict_get_qdict(ret, "return");
 933
 934    str = qdict_get_try_str(val, "id");
 935    g_assert_nonnull(str);
 936    g_assert_cmpstr(str, ==, "qemu-ga-test");
 937
 938    str = qdict_get_try_str(val, "name");
 939    g_assert_nonnull(str);
 940    g_assert_cmpstr(str, ==, "QEMU-GA");
 941
 942    str = qdict_get_try_str(val, "pretty-name");
 943    g_assert_nonnull(str);
 944    g_assert_cmpstr(str, ==, "QEMU Guest Agent test");
 945
 946    str = qdict_get_try_str(val, "version");
 947    g_assert_nonnull(str);
 948    g_assert_cmpstr(str, ==, "Test 1");
 949
 950    str = qdict_get_try_str(val, "version-id");
 951    g_assert_nonnull(str);
 952    g_assert_cmpstr(str, ==, "1");
 953
 954    str = qdict_get_try_str(val, "variant");
 955    g_assert_nonnull(str);
 956    g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc.");
 957
 958    str = qdict_get_try_str(val, "variant-id");
 959    g_assert_nonnull(str);
 960    g_assert_cmpstr(str, ==, "unit-test");
 961
 962    qobject_unref(ret);
 963    g_free(env[0]);
 964    fixture_tear_down(&fixture, NULL);
 965}
 966
 967int main(int argc, char **argv)
 968{
 969    TestFixture fix;
 970    int ret;
 971
 972    setlocale (LC_ALL, "");
 973    g_test_init(&argc, &argv, NULL);
 974    fixture_setup(&fix, NULL, NULL);
 975
 976    g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited);
 977    g_test_add_data_func("/qga/sync", &fix, test_qga_sync);
 978    g_test_add_data_func("/qga/ping", &fix, test_qga_ping);
 979    g_test_add_data_func("/qga/info", &fix, test_qga_info);
 980    g_test_add_data_func("/qga/network-get-interfaces", &fix,
 981                         test_qga_network_get_interfaces);
 982    if (!access("/sys/devices/system/cpu/cpu0", F_OK)) {
 983        g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus);
 984    }
 985    g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo);
 986    g_test_add_data_func("/qga/get-memory-block-info", &fix,
 987                         test_qga_get_memory_block_info);
 988    g_test_add_data_func("/qga/get-memory-blocks", &fix,
 989                         test_qga_get_memory_blocks);
 990    g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
 991    g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
 992    g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
 993    g_test_add_data_func("/qga/id", &fix, test_qga_id);
 994    g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
 995    g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
 996    g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
 997    g_test_add_data_func("/qga/fsfreeze-status", &fix,
 998                         test_qga_fsfreeze_status);
 999
1000    g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist);
1001    g_test_add_data_func("/qga/config", NULL, test_qga_config);
1002    g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec);
1003    g_test_add_data_func("/qga/guest-exec-invalid", &fix,
1004                         test_qga_guest_exec_invalid);
1005    g_test_add_data_func("/qga/guest-get-osinfo", &fix,
1006                         test_qga_guest_get_osinfo);
1007    g_test_add_data_func("/qga/guest-get-host-name", &fix,
1008                         test_qga_guest_get_host_name);
1009    g_test_add_data_func("/qga/guest-get-timezone", &fix,
1010                         test_qga_guest_get_timezone);
1011    g_test_add_data_func("/qga/guest-get-users", &fix,
1012                         test_qga_guest_get_users);
1013
1014    ret = g_test_run();
1015
1016    fixture_tear_down(&fix, NULL);
1017
1018    return ret;
1019}
1020