From patchwork Tue Jan 10 19:10:54 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 135285 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 580A8B6FC8 for ; Wed, 11 Jan 2012 06:12:32 +1100 (EST) Received: from localhost ([::1]:56136 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rkh7R-0001iO-6z for incoming@patchwork.ozlabs.org; Tue, 10 Jan 2012 14:12:25 -0500 Received: from eggs.gnu.org ([140.186.70.92]:60151) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rkh7C-0001WY-4s for qemu-devel@nongnu.org; Tue, 10 Jan 2012 14:12:12 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Rkh75-0006Gg-2t for qemu-devel@nongnu.org; Tue, 10 Jan 2012 14:12:09 -0500 Received: from e37.co.us.ibm.com ([32.97.110.158]:45273) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rkh74-0006GW-OT for qemu-devel@nongnu.org; Tue, 10 Jan 2012 14:12:03 -0500 Received: from /spool/local by e37.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 10 Jan 2012 12:12:01 -0700 Received: from d03relay05.boulder.ibm.com (9.17.195.107) by e37.co.us.ibm.com (192.168.1.137) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 10 Jan 2012 12:11:40 -0700 Received: from d03av05.boulder.ibm.com (d03av05.boulder.ibm.com [9.17.195.85]) by d03relay05.boulder.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q0AJBK9W029782 for ; Tue, 10 Jan 2012 12:11:23 -0700 Received: from d03av05.boulder.ibm.com (loopback [127.0.0.1]) by d03av05.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id q0AJBJ5s012158 for ; Tue, 10 Jan 2012 12:11:19 -0700 Received: from titi.austin.rr.com ([9.57.66.237]) by d03av05.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q0AJAuFq011135; Tue, 10 Jan 2012 12:11:18 -0700 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Tue, 10 Jan 2012 13:10:54 -0600 Message-Id: <1326222656-26588-13-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1326222656-26588-1-git-send-email-aliguori@us.ibm.com> References: <1326222656-26588-1-git-send-email-aliguori@us.ibm.com> x-cbid: 12011019-7408-0000-0000-000001CA9179 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 32.97.110.158 Cc: Anthony Liguori Subject: [Qemu-devel] [PATCH 13/15] qtest: add C version of test infrastructure X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This also includes a qtest wrapper script to make it easier to launch qtest tests directly. Signed-off-by: Anthony Liguori --- scripts/qtest | 5 + tests/Makefile | 2 + tests/libqtest.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/libqtest.h | 63 ++++++++++ 4 files changed, 400 insertions(+), 0 deletions(-) create mode 100755 scripts/qtest create mode 100644 tests/libqtest.c create mode 100644 tests/libqtest.h diff --git a/scripts/qtest b/scripts/qtest new file mode 100755 index 0000000..5cff3d4 --- /dev/null +++ b/scripts/qtest @@ -0,0 +1,5 @@ +#!/bin/sh + +export QTEST_QEMU_BINARY=$1 +shift +eval "$@" diff --git a/tests/Makefile b/tests/Makefile index 1105004..0005764 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -33,6 +33,8 @@ test-qmp-input-visitor: test-qmp-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o +tests/rtc-test: tests/rtc-test.o tests/libqtest.o + .PHONY: check check: $(CHECKS) gtester $(CHECKS) diff --git a/tests/libqtest.c b/tests/libqtest.c new file mode 100644 index 0000000..3f57c29 --- /dev/null +++ b/tests/libqtest.c @@ -0,0 +1,330 @@ +/* + * QTest + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * 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 "libqtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_IRQ 256 + +QTestState *global_qtest; + +struct QTestState +{ + int fd; + bool irq_level[MAX_IRQ]; + GString *rx; + gchar *pid_file; +}; + +#define g_assert_no_errno(ret) do { \ + g_assert_cmpint(ret, !=, -1); \ +} while (0) + +QTestState *qtest_init(const char *extra_args) +{ + QTestState *s; + struct sockaddr_un addr; + int sock, ret, i; + gchar *socket_path; + gchar *pid_file; + gchar *command; + const char *qemu_binary; + pid_t pid; + + qemu_binary = getenv("QTEST_QEMU_BINARY"); + g_assert(qemu_binary != NULL); + + socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); + pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid()); + + s = g_malloc(sizeof(*s)); + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + g_assert_no_errno(sock); + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); + + pid = fork(); + if (pid == 0) { + command = g_strdup_printf("%s " + "-qtest unix:%s,server,nowait " + "-pidfile %s " + "-machine accel=qtest " + "%s", qemu_binary, socket_path, + pid_file, + extra_args ?: ""); + + ret = system(command); + exit(ret); + g_free(command); + } + + do { + sleep(1); + ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + } while (ret == -1); + g_assert_no_errno(ret); + + s->fd = sock; + s->rx = g_string_new(""); + s->pid_file = pid_file; + for (i = 0; i < MAX_IRQ; i++) { + s->irq_level[i] = false; + } + + g_free(socket_path); + + return s; +} + +void qtest_quit(QTestState *s) +{ + FILE *f; + char buffer[1024]; + + f = fopen(s->pid_file, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f)) { + pid_t pid = atoi(buffer); + int status = 0; + + kill(pid, SIGTERM); + waitpid(pid, &status, 0); + } + + fclose(f); + } +} + +static void qtest_sendf(QTestState *s, const char *fmt, ...) +{ + va_list ap; + gchar *str; + size_t size, offset; + + va_start(ap, fmt); + str = g_strdup_vprintf(fmt, ap); + va_end(ap); + size = strlen(str); + + offset = 0; + while (offset < size) { + ssize_t len; + + len = write(s->fd, str + offset, size - offset); + if (len == -1 && errno == EINTR) { + continue; + } + + g_assert_no_errno(len); + g_assert_cmpint(len, >, 0); + + offset += len; + } +} + +static GString *qtest_recv_line(QTestState *s) +{ + GString *line; + size_t offset; + char *eol; + + while ((eol = strchr(s->rx->str, '\n')) == NULL) { + ssize_t len; + char buffer[1024]; + + len = read(s->fd, buffer, sizeof(buffer)); + if (len == -1 && errno == EINTR) { + continue; + } + + if (len == -1 || len == 0) { + fprintf(stderr, "Broken pipe\n"); + exit(1); + } + + g_string_append_len(s->rx, buffer, len); + } + + offset = eol - s->rx->str; + line = g_string_new_len(s->rx->str, offset); + g_string_erase(s->rx, 0, offset + 1); + + return line; +} + +static gchar **qtest_rsp(QTestState *s, int expected_args) +{ + GString *line; + gchar **words; + int i; + +redo: + line = qtest_recv_line(s); + words = g_strsplit(line->str, " ", 0); + g_string_free(line, TRUE); + + if (strcmp(words[0], "IRQ") == 0) { + int irq; + + g_assert(words[1] != NULL); + g_assert(words[2] != NULL); + + irq = strtoul(words[2], NULL, 0); + g_assert_cmpint(irq, >=, 0); + g_assert_cmpint(irq, <, MAX_IRQ); + + if (strcmp(words[1], "raise") == 0) { + s->irq_level[irq] = true; + } else { + s->irq_level[irq] = false; + } + + g_strfreev(words); + goto redo; + } + + g_assert(words[0] != NULL); + g_assert_cmpstr(words[0], ==, "OK"); + + if (expected_args) { + for (i = 0; i < expected_args; i++) { + g_assert(words[i] != NULL); + } + } else { + g_strfreev(words); + } + + return words; +} + +const char *qtest_get_arch(void) +{ + const char *qemu = getenv("QTEST_QEMU_BINARY"); + const char *end = strrchr(qemu, '/'); + + return end + strlen("/qemu-system-"); +} + +bool qtest_get_irq(QTestState *s, int num) +{ + return s->irq_level[num]; +} + +static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) +{ + qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); + qtest_rsp(s, 0); +} + +void qtest_outb(QTestState *s, uint16_t addr, uint8_t value) +{ + qtest_out(s, "outb", addr, value); +} + +void qtest_outw(QTestState *s, uint16_t addr, uint16_t value) +{ + qtest_out(s, "outw", addr, value); +} + +void qtest_outl(QTestState *s, uint16_t addr, uint32_t value) +{ + qtest_out(s, "outl", addr, value); +} + +static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr) +{ + gchar **args; + uint32_t value; + + qtest_sendf(s, "%s 0x%x\n", cmd, addr); + args = qtest_rsp(s, 2); + value = strtoul(args[1], NULL, 0); + g_strfreev(args); + + return value; +} + +uint8_t qtest_inb(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inb", addr); +} + +uint16_t qtest_inw(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inw", addr); +} + +uint32_t qtest_inl(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inl", addr); +} + +static int hex2nib(char ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + return 10 + (ch - 'a'); + } else { + return -1; + } +} + +void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size) +{ + uint8_t *ptr = data; + gchar **args; + size_t i; + + qtest_sendf(s, "read 0x%x 0x%x\n", addr, size); + args = qtest_rsp(s, 2); + + for (i = 0; i < size; i++) { + ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4; + ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]); + } + + g_strfreev(args); +} + +void qtest_add_func(const char *str, void (*fn)) +{ + gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); + g_test_add_func(path, fn); +} + +void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) +{ + const uint8_t *ptr = data; + size_t i; + + qtest_sendf(s, "write 0x%x 0x%x 0x", addr, size); + for (i = 0; i < size; i++) { + qtest_sendf(s, "%02x", ptr[i]); + } + qtest_sendf(s, "\n"); + qtest_rsp(s, 0); +} diff --git a/tests/libqtest.h b/tests/libqtest.h new file mode 100644 index 0000000..dd82926 --- /dev/null +++ b/tests/libqtest.h @@ -0,0 +1,63 @@ +/* + * QTest + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#ifndef LIBQTEST_H +#define LIBQTEST_H + +#include +#include +#include + +typedef struct QTestState QTestState; + +extern QTestState *global_qtest; + +QTestState *qtest_init(const char *extra_args); +void qtest_quit(QTestState *s); + +bool qtest_get_irq(QTestState *s, int num); + +void qtest_outb(QTestState *s, uint16_t addr, uint8_t value); + +void qtest_outw(QTestState *s, uint16_t addr, uint16_t value); + +void qtest_outl(QTestState *s, uint16_t addr, uint32_t value); + +uint8_t qtest_inb(QTestState *s, uint16_t addr); + +uint16_t qtest_inw(QTestState *s, uint16_t addr); + +uint32_t qtest_inl(QTestState *s, uint16_t addr); + +void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size); + +void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size); + +const char *qtest_get_arch(void); + +void qtest_add_func(const char *str, void (*fn)); + +#define qtest_start(args) ( \ + global_qtest = qtest_init((args)) \ + ) + +#define get_irq(num) qtest_get_irq(global_qtest, (num)) +#define outb(addr, val) qtest_outb(global_qtest, (addr), (val)) +#define outw(addr, val) qtest_outw(global_qtest, (addr), (val)) +#define outl(addr, val) qtest_outl(global_qtest, (addr), (val)) +#define inb(addr) qtest_inb(global_qtest, (addr)) +#define inw(addr) qtest_inw(global_qtest, (addr)) +#define inl(addr) qtest_inl(global_qtest, (addr)) +#define memread(addr, data, size) qtest_memread(global_qtest, (addr), (data), (size)) +#define memwrite(addr, data, size) qtest_memwrite(global_qtest, (addr), (data), (size)) + +#endif