From patchwork Thu Mar 15 13:37:40 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 146970 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 779AFB6FA1 for ; Fri, 16 Mar 2012 00:55:16 +1100 (EST) Received: from localhost ([::1]:35384 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S8AuM-0002Ag-3P for incoming@patchwork.ozlabs.org; Thu, 15 Mar 2012 09:39:58 -0400 Received: from eggs.gnu.org ([208.118.235.92]:57485) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S8Au1-0001dW-L1 for qemu-devel@nongnu.org; Thu, 15 Mar 2012 09:39:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S8Atq-00060n-R1 for qemu-devel@nongnu.org; Thu, 15 Mar 2012 09:39:37 -0400 Received: from e31.co.us.ibm.com ([32.97.110.149]:40943) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S8Atq-00060L-IK for qemu-devel@nongnu.org; Thu, 15 Mar 2012 09:39:26 -0400 Received: from /spool/local by e31.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 15 Mar 2012 07:39:22 -0600 Received: from d03dlp01.boulder.ibm.com (9.17.202.177) by e31.co.us.ibm.com (192.168.1.131) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 15 Mar 2012 07:38:29 -0600 Received: from d03relay04.boulder.ibm.com (d03relay04.boulder.ibm.com [9.17.195.106]) by d03dlp01.boulder.ibm.com (Postfix) with ESMTP id 2BA7AC40007 for ; Thu, 15 Mar 2012 07:38:28 -0600 (MDT) Received: from d03av05.boulder.ibm.com (d03av05.boulder.ibm.com [9.17.195.85]) by d03relay04.boulder.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q2FDbu8H039152 for ; Thu, 15 Mar 2012 07:37:57 -0600 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 q2FDbux0028558 for ; Thu, 15 Mar 2012 07:37:56 -0600 Received: from titi.austin.rr.com (sig-9-65-116-158.mts.ibm.com [9.65.116.158]) by d03av05.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q2FDbpEL028311; Thu, 15 Mar 2012 07:37:55 -0600 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Thu, 15 Mar 2012 08:37:40 -0500 Message-Id: <1331818666-31718-4-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1331818666-31718-1-git-send-email-aliguori@us.ibm.com> References: <1331818666-31718-1-git-send-email-aliguori@us.ibm.com> X-Content-Scanned: Fidelis XPS MAILER x-cbid: 12031513-7282-0000-0000-00000761E314 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 32.97.110.149 Cc: Anthony Liguori Subject: [Qemu-devel] [PATCH 3/9] 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 | 1 + tests/libqtest.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/libqtest.h | 63 ++++++++++ 4 files changed, 403 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 4fd09f2..9d7cfb3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -55,6 +55,7 @@ test-qmp-commands$(EXESUF): test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $ $(SRC_PATH)/tests/qemu-iotests-quick.sh: qemu-img qemu-io +tests/rtc-test: tests/rtc-test.o tests/libqtest.o .PHONY: check check-block diff --git a/tests/libqtest.c b/tests/libqtest.c new file mode 100644 index 0000000..dd07b07 --- /dev/null +++ b/tests/libqtest.c @@ -0,0 +1,334 @@ +/* + * 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 " + "-qtest-log /dev/null " + "-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) +{ + /* dummy operation in order to make sure irq is up to date */ + qtest_inb(s, 0); + + 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