mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
tests: qgraph API for the qtest driver framework
Add qgraph API that allows to add/remove nodes and edges from the graph, implementation of Depth First Search to discover the paths and basic unit test to check correctness of the API. Included also a main executable that takes care of starting the framework, create the nodes, set the available drivers/machines, discover the path and run tests. graph.h provides the public API to manage the graph nodes/edges graph_extra.h provides a more private API used successively by the gtest integration part qos-test.c provides the main executable Signed-off-by: Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> [Paolo's changes compared to the Google Summer of Code submission: * added subprocess to test options * refactored object creation to support live migration tests * removed driver .before callback (unused) * removed test .after callbacks (replaced by GTest destruction queue)] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
committed by
Paolo Bonzini
parent
eb5937bad6
commit
fc281c8020
@@ -7647,7 +7647,7 @@ fi
|
||||
# tests might fail. Prefer to keep the relevant files in their own
|
||||
# directory and symlink the directory instead.
|
||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm"
|
||||
DIRS="$DIRS tests/fp"
|
||||
DIRS="$DIRS tests/fp tests/qgraph"
|
||||
DIRS="$DIRS docs docs/interop fsdev scsi"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
|
||||
@@ -45,6 +45,7 @@ typedef enum {
|
||||
MODULE_INIT_QOM,
|
||||
MODULE_INIT_TRACE,
|
||||
MODULE_INIT_XEN_BACKEND,
|
||||
MODULE_INIT_LIBQOS,
|
||||
MODULE_INIT_MAX
|
||||
} module_init_type;
|
||||
|
||||
@@ -54,6 +55,7 @@ typedef enum {
|
||||
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)
|
||||
#define xen_backend_init(function) module_init(function, \
|
||||
MODULE_INIT_XEN_BACKEND)
|
||||
#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
|
||||
|
||||
#define block_module_load_one(lib) module_load_one("block-", lib)
|
||||
#define ui_module_load_one(lib) module_load_one("ui-", lib)
|
||||
|
||||
+11
-2
@@ -732,7 +732,10 @@ tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y)
|
||||
|
||||
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||
libqgraph-obj-y = tests/libqos/qgraph.o
|
||||
|
||||
libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o
|
||||
libqos-obj-y += tests/libqos/malloc.o tests/libqos/malloc-generic.o
|
||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||
libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o
|
||||
libqos-spapr-obj-y += tests/libqos/libqos-spapr.o
|
||||
@@ -744,7 +747,13 @@ libqos-pc-obj-y += tests/libqos/ahci.o
|
||||
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
|
||||
libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
|
||||
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
|
||||
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
|
||||
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o
|
||||
|
||||
check-unit-y += tests/test-qgraph$(EXESUF)
|
||||
tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y)
|
||||
|
||||
check-qtest-generic-y += tests/qos-test$(EXESUF)
|
||||
tests/qos-test$(EXESUF): tests/qos-test.o $(libqgraph-obj-y)
|
||||
|
||||
tests/qmp-test$(EXESUF): tests/qmp-test.o
|
||||
tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef QGRAPH_EXTRA_H
|
||||
#define QGRAPH_EXTRA_H
|
||||
|
||||
/* This header is declaring additional helper functions defined in
|
||||
* libqos/qgraph.c
|
||||
* It should not be included in tests
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
typedef struct QOSGraphMachine QOSGraphMachine;
|
||||
typedef enum QOSEdgeType QOSEdgeType;
|
||||
typedef enum QOSNodeType QOSNodeType;
|
||||
|
||||
/* callback called when the walk path algorithm found a
|
||||
* valid path
|
||||
*/
|
||||
typedef void (*QOSTestCallback) (QOSGraphNode *path, int len);
|
||||
|
||||
/* edge types*/
|
||||
enum QOSEdgeType {
|
||||
QEDGE_CONTAINS,
|
||||
QEDGE_PRODUCES,
|
||||
QEDGE_CONSUMED_BY
|
||||
};
|
||||
|
||||
/* node types*/
|
||||
enum QOSNodeType {
|
||||
QNODE_MACHINE,
|
||||
QNODE_DRIVER,
|
||||
QNODE_INTERFACE,
|
||||
QNODE_TEST
|
||||
};
|
||||
|
||||
/* Graph Node */
|
||||
struct QOSGraphNode {
|
||||
QOSNodeType type;
|
||||
bool available; /* set by QEMU via QMP, used during graph walk */
|
||||
bool visited; /* used during graph walk */
|
||||
char *name; /* used to identify the node */
|
||||
char *command_line; /* used to start QEMU at test execution */
|
||||
union {
|
||||
struct {
|
||||
QOSCreateDriverFunc constructor;
|
||||
} driver;
|
||||
struct {
|
||||
QOSCreateMachineFunc constructor;
|
||||
} machine;
|
||||
struct {
|
||||
QOSTestFunc function;
|
||||
void *arg;
|
||||
QOSBeforeTest before;
|
||||
bool subprocess;
|
||||
} test;
|
||||
} u;
|
||||
|
||||
/**
|
||||
* only used when traversing the path, never rely on that except in the
|
||||
* qos_traverse_graph callback function
|
||||
*/
|
||||
QOSGraphEdge *path_edge;
|
||||
};
|
||||
|
||||
/**
|
||||
* qos_graph_get_node(): returns the node mapped to that @key.
|
||||
* It performs an hash map search O(1)
|
||||
*
|
||||
* Returns: on success: the %QOSGraphNode
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphNode *qos_graph_get_node(const char *key);
|
||||
|
||||
/**
|
||||
* qos_graph_has_node(): returns #TRUE if the node
|
||||
* has map has a node mapped to that @key.
|
||||
*/
|
||||
bool qos_graph_has_node(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_node_type(): returns the %QOSNodeType
|
||||
* of the node @node.
|
||||
* It performs an hash map search O(1)
|
||||
* Returns: on success: the %QOSNodeType
|
||||
* otherwise: #-1
|
||||
*/
|
||||
QOSNodeType qos_graph_get_node_type(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_node_availability(): returns the availability (boolean)
|
||||
* of the node @node.
|
||||
*/
|
||||
bool qos_graph_get_node_availability(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_edge(): returns the edge
|
||||
* linking of the node @node with @dest.
|
||||
*
|
||||
* Returns: on success: the %QOSGraphEdge
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_type(): returns the edge type
|
||||
* of the edge @edge.
|
||||
*
|
||||
* Returns: on success: the %QOSEdgeType
|
||||
* otherwise: #-1
|
||||
*/
|
||||
QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_dest(): returns the name of the node
|
||||
* pointed as destination of edge @edge.
|
||||
*
|
||||
* Returns: on success: the destination
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_dest(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_has_edge(): returns #TRUE if there
|
||||
* exists an edge from @start to @dest.
|
||||
*/
|
||||
bool qos_graph_has_edge(const char *start, const char *dest);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_arg(): returns the args assigned
|
||||
* to that @edge.
|
||||
*
|
||||
* Returns: on success: the arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
void *qos_graph_edge_get_arg(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_after_cmd_line(): returns the edge
|
||||
* command line that will be added after all the node arguments
|
||||
* and all the before_cmd_line arguments.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_before_cmd_line(): returns the edge
|
||||
* command line that will be added before the node command
|
||||
* line argument.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_extra_device_opts(): returns the arg
|
||||
* command line that will be added to the node command
|
||||
* line argument.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_name(): returns the name
|
||||
* assigned to the destination node (different only)
|
||||
* if there are multiple devices with the same node name
|
||||
* e.g. a node has two "generic-sdhci", "emmc" and "sdcard"
|
||||
* there will be two edges with edge_name ="emmc" and "sdcard"
|
||||
*
|
||||
* Returns always the char* edge_name
|
||||
*/
|
||||
char *qos_graph_edge_get_name(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_get_machine(): returns the machine assigned
|
||||
* to that @node name.
|
||||
*
|
||||
* It performs a search only trough the list of machines
|
||||
* (i.e. the QOS_ROOT child).
|
||||
*
|
||||
* Returns: on success: the %QOSGraphNode
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphNode *qos_graph_get_machine(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_has_machine(): returns #TRUE if the node
|
||||
* has map has a node mapped to that @node.
|
||||
*/
|
||||
bool qos_graph_has_machine(const char *node);
|
||||
|
||||
|
||||
/**
|
||||
* qos_print_graph(): walks the graph and prints
|
||||
* all machine-to-test paths.
|
||||
*/
|
||||
void qos_print_graph(void);
|
||||
|
||||
/**
|
||||
* qos_graph_foreach_test_path(): executes the Depth First search
|
||||
* algorithm and applies @fn to all discovered paths.
|
||||
*
|
||||
* See qos_traverse_graph() in qgraph.c for more info on
|
||||
* how it works.
|
||||
*/
|
||||
void qos_graph_foreach_test_path(QOSTestCallback fn);
|
||||
|
||||
/**
|
||||
* qos_get_machine_type(): return QEMU machine type for a machine node.
|
||||
* This function requires every machine @name to be in the form
|
||||
* <arch>/<machine_name>, like "arm/raspi2" or "x86_64/pc".
|
||||
*
|
||||
* The function will validate the format and return a pointer to
|
||||
* @machine to <machine_name>. For example, when passed "x86_64/pc"
|
||||
* it will return "pc".
|
||||
*
|
||||
* Note that this function *does not* allocate any new string.
|
||||
*/
|
||||
char *qos_get_machine_type(char *name);
|
||||
|
||||
/**
|
||||
* qos_delete_cmd_line(): delete the
|
||||
* command line present in node mapped with key @name.
|
||||
*
|
||||
* This function is called when the QMP query returns a node with
|
||||
* { "abstract" : true } attribute.
|
||||
*/
|
||||
void qos_delete_cmd_line(const char *name);
|
||||
|
||||
/**
|
||||
* qos_graph_node_set_availability(): sets the node identified
|
||||
* by @node with availability @av.
|
||||
*/
|
||||
void qos_graph_node_set_availability(const char *node, bool av);
|
||||
|
||||
#endif
|
||||
@@ -598,6 +598,9 @@ static inline QTestState *qtest_start(const char *args)
|
||||
*/
|
||||
static inline void qtest_end(void)
|
||||
{
|
||||
if (!global_qtest) {
|
||||
return;
|
||||
}
|
||||
qtest_quit(global_qtest);
|
||||
global_qtest = NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/qgraph_internal.h"
|
||||
|
||||
static char *old_path;
|
||||
|
||||
static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
|
||||
{
|
||||
char *machine_name = NULL;
|
||||
if (is_machine) {
|
||||
const char *arch = qtest_get_arch();
|
||||
machine_name = g_strconcat(arch, "/", name, NULL);
|
||||
name = machine_name;
|
||||
}
|
||||
qos_graph_node_set_availability(name, true);
|
||||
if (is_abstract) {
|
||||
qos_delete_cmd_line(name);
|
||||
}
|
||||
g_free(machine_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* apply_to_qlist(): using QMP queries QEMU for a list of
|
||||
* machines and devices available, and sets the respective node
|
||||
* as true. If a node is found, also all its produced and contained
|
||||
* child are marked available.
|
||||
*
|
||||
* See qos_graph_node_set_availability() for more info
|
||||
*/
|
||||
static void apply_to_qlist(QList *list, bool is_machine)
|
||||
{
|
||||
const QListEntry *p;
|
||||
const char *name;
|
||||
bool abstract;
|
||||
QDict *minfo;
|
||||
QObject *qobj;
|
||||
QString *qstr;
|
||||
QBool *qbool;
|
||||
|
||||
for (p = qlist_first(list); p; p = qlist_next(p)) {
|
||||
minfo = qobject_to(QDict, qlist_entry_obj(p));
|
||||
qobj = qdict_get(minfo, "name");
|
||||
qstr = qobject_to(QString, qobj);
|
||||
name = qstring_get_str(qstr);
|
||||
|
||||
qobj = qdict_get(minfo, "abstract");
|
||||
if (qobj) {
|
||||
qbool = qobject_to(QBool, qobj);
|
||||
abstract = qbool_get_bool(qbool);
|
||||
} else {
|
||||
abstract = false;
|
||||
}
|
||||
|
||||
apply_to_node(name, is_machine, abstract);
|
||||
qobj = qdict_get(minfo, "alias");
|
||||
if (qobj) {
|
||||
qstr = qobject_to(QString, qobj);
|
||||
name = qstring_get_str(qstr);
|
||||
apply_to_node(name, is_machine, abstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_set_machines_devices_available(): sets availability of qgraph
|
||||
* machines and devices.
|
||||
*
|
||||
* This function firstly starts QEMU with "-machine none" option,
|
||||
* and then executes the QMP protocol asking for the list of devices
|
||||
* and machines available.
|
||||
*
|
||||
* for each of these items, it looks up the corresponding qgraph node,
|
||||
* setting it as available. The list currently returns all devices that
|
||||
* are either machines or QEDGE_CONSUMED_BY other nodes.
|
||||
* Therefore, in order to mark all other nodes, it recursively sets
|
||||
* all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too.
|
||||
*/
|
||||
static void qos_set_machines_devices_available(void)
|
||||
{
|
||||
QDict *response;
|
||||
QDict *args = qdict_new();
|
||||
QList *list;
|
||||
|
||||
qtest_start("-machine none");
|
||||
response = qmp("{ 'execute': 'query-machines' }");
|
||||
list = qdict_get_qlist(response, "return");
|
||||
|
||||
apply_to_qlist(list, true);
|
||||
|
||||
qobject_unref(response);
|
||||
|
||||
qdict_put_bool(args, "abstract", true);
|
||||
qdict_put_str(args, "implements", "device");
|
||||
|
||||
response = qmp("{'execute': 'qom-list-types',"
|
||||
" 'arguments': %p }", args);
|
||||
g_assert(qdict_haskey(response, "return"));
|
||||
list = qdict_get_qlist(response, "return");
|
||||
|
||||
apply_to_qlist(list, false);
|
||||
|
||||
qtest_end();
|
||||
qobject_unref(response);
|
||||
}
|
||||
|
||||
static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
|
||||
{
|
||||
return obj->get_driver(obj, "memory");
|
||||
}
|
||||
|
||||
static void restart_qemu_or_continue(char *path)
|
||||
{
|
||||
/* compares the current command line with the
|
||||
* one previously executed: if they are the same,
|
||||
* don't restart QEMU, if they differ, stop previous
|
||||
* QEMU subprocess (if active) and start over with
|
||||
* the new command line
|
||||
*/
|
||||
if (g_strcmp0(old_path, path)) {
|
||||
qtest_end();
|
||||
qos_invalidate_command_line();
|
||||
old_path = g_strdup(path);
|
||||
qtest_start(path);
|
||||
} else { /* if cmd line is the same, reset the guest */
|
||||
qobject_unref(qmp("{ 'execute': 'system_reset' }"));
|
||||
qmp_eventwait("RESET");
|
||||
}
|
||||
}
|
||||
|
||||
void qos_invalidate_command_line(void)
|
||||
{
|
||||
g_free(old_path);
|
||||
old_path = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* allocate_objects(): given an array of nodes @arg,
|
||||
* walks the path invoking all constructors and
|
||||
* passing the corresponding parameter in order to
|
||||
* continue the objects allocation.
|
||||
* Once the test is reached, return the object it consumes.
|
||||
*
|
||||
* Since the machine and QEDGE_CONSUMED_BY nodes allocate
|
||||
* memory in the constructor, g_test_queue_destroy is used so
|
||||
* that after execution they can be safely free'd. (The test's
|
||||
* ->before callback is also welcome to use g_test_queue_destroy).
|
||||
*
|
||||
* Note: as specified in walk_path() too, @arg is an array of
|
||||
* char *, where arg[0] is a pointer to the command line
|
||||
* string that will be used to properly start QEMU when executing
|
||||
* the test, and the remaining elements represent the actual objects
|
||||
* that will be allocated.
|
||||
*/
|
||||
static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
|
||||
{
|
||||
int current = 0;
|
||||
QGuestAllocator *alloc;
|
||||
QOSGraphObject *parent = NULL;
|
||||
QOSGraphEdge *edge;
|
||||
QOSGraphNode *node;
|
||||
void *edge_arg;
|
||||
void *obj;
|
||||
|
||||
node = qos_graph_get_node(path[current]);
|
||||
g_assert(node->type == QNODE_MACHINE);
|
||||
|
||||
obj = qos_machine_new(node, qts);
|
||||
qos_object_queue_destroy(obj);
|
||||
|
||||
alloc = get_machine_allocator(obj);
|
||||
if (p_alloc) {
|
||||
*p_alloc = alloc;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (node->type != QNODE_INTERFACE) {
|
||||
qos_object_start_hw(obj);
|
||||
parent = obj;
|
||||
}
|
||||
|
||||
/* follow edge and get object for next node constructor */
|
||||
current++;
|
||||
edge = qos_graph_get_edge(path[current - 1], path[current]);
|
||||
node = qos_graph_get_node(path[current]);
|
||||
|
||||
if (node->type == QNODE_TEST) {
|
||||
g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
|
||||
return obj;
|
||||
}
|
||||
|
||||
switch (qos_graph_edge_get_type(edge)) {
|
||||
case QEDGE_PRODUCES:
|
||||
obj = parent->get_driver(parent, path[current]);
|
||||
break;
|
||||
|
||||
case QEDGE_CONSUMED_BY:
|
||||
edge_arg = qos_graph_edge_get_arg(edge);
|
||||
obj = qos_driver_new(node, obj, alloc, edge_arg);
|
||||
qos_object_queue_destroy(obj);
|
||||
break;
|
||||
|
||||
case QEDGE_CONTAINS:
|
||||
obj = parent->get_device(parent, path[current]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The argument to run_one_test, which is the test function that is registered
|
||||
* with GTest, is a vector of strings. The first item is the initial command
|
||||
* line (before it is modified by the test's "before" function), the remaining
|
||||
* items are node names forming the path to the test node.
|
||||
*/
|
||||
static char **current_path;
|
||||
|
||||
const char *qos_get_current_command_line(void)
|
||||
{
|
||||
return current_path[0];
|
||||
}
|
||||
|
||||
void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
|
||||
{
|
||||
return allocate_objects(qts, current_path + 1, p_alloc);
|
||||
}
|
||||
|
||||
/**
|
||||
* run_one_test(): given an array of nodes @arg,
|
||||
* walks the path invoking all constructors and
|
||||
* passing the corresponding parameter in order to
|
||||
* continue the objects allocation.
|
||||
* Once the test is reached, its function is executed.
|
||||
*
|
||||
* Since the machine and QEDGE_CONSUMED_BY nodes allocate
|
||||
* memory in the constructor, g_test_queue_destroy is used so
|
||||
* that after execution they can be safely free'd. The test's
|
||||
* ->before callback is also welcome to use g_test_queue_destroy.
|
||||
*
|
||||
* Note: as specified in walk_path() too, @arg is an array of
|
||||
* char *, where arg[0] is a pointer to the command line
|
||||
* string that will be used to properly start QEMU when executing
|
||||
* the test, and the remaining elements represent the actual objects
|
||||
* that will be allocated.
|
||||
*
|
||||
* The order of execution is the following:
|
||||
* 1) @before test function as defined in the given QOSGraphTestOptions
|
||||
* 2) start QEMU
|
||||
* 3) call all nodes constructor and get_driver/get_device depending on edge,
|
||||
* start the hardware (*_device_enable functions)
|
||||
* 4) start test
|
||||
*/
|
||||
static void run_one_test(const void *arg)
|
||||
{
|
||||
QOSGraphNode *test_node;
|
||||
QGuestAllocator *alloc = NULL;
|
||||
void *obj;
|
||||
char **path = (char **) arg;
|
||||
GString *cmd_line = g_string_new(path[0]);
|
||||
void *test_arg;
|
||||
|
||||
/* Before test */
|
||||
current_path = path;
|
||||
test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
|
||||
test_arg = test_node->u.test.arg;
|
||||
if (test_node->u.test.before) {
|
||||
test_arg = test_node->u.test.before(cmd_line, test_arg);
|
||||
}
|
||||
|
||||
restart_qemu_or_continue(cmd_line->str);
|
||||
g_string_free(cmd_line, true);
|
||||
|
||||
obj = qos_allocate_objects(global_qtest, &alloc);
|
||||
test_node->u.test.function(obj, test_arg, alloc);
|
||||
}
|
||||
|
||||
static void subprocess_run_one_test(const void *arg)
|
||||
{
|
||||
const gchar *path = arg;
|
||||
g_test_trap_subprocess(path, 0, 0);
|
||||
g_test_trap_assert_passed();
|
||||
}
|
||||
|
||||
/*
|
||||
* in this function, 2 path will be built:
|
||||
* path_str, a one-string path (ex "pc/i440FX-pcihost/...")
|
||||
* path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost").
|
||||
*
|
||||
* path_str will be only used to build the test name, and won't need the
|
||||
* architecture name at beginning, since it will be added by qtest_add_func().
|
||||
*
|
||||
* path_vec is used to allocate all constructors of the path nodes.
|
||||
* Each name in this array except position 0 must correspond to a valid
|
||||
* QOSGraphNode name.
|
||||
* Position 0 is special, initially contains just the <machine> name of
|
||||
* the node, (ex for "x86_64/pc" it will be "pc"), used to build the test
|
||||
* path (see below). After it will contain the command line used to start
|
||||
* qemu with all required devices.
|
||||
*
|
||||
* Note that the machine node name must be with format <arch>/<machine>
|
||||
* (ex "x86_64/pc"), because it will identify the node "x86_64/pc"
|
||||
* and start QEMU with "-M pc". For this reason,
|
||||
* when building path_str, path_vec
|
||||
* initially contains the <machine> at position 0 ("pc"),
|
||||
* and the node name at position 1 (<arch>/<machine>)
|
||||
* ("x86_64/pc"), followed by the rest of the nodes.
|
||||
*/
|
||||
static void walk_path(QOSGraphNode *orig_path, int len)
|
||||
{
|
||||
QOSGraphNode *path;
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
/* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */
|
||||
QOSEdgeType etype = QEDGE_CONSUMED_BY;
|
||||
|
||||
/* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */
|
||||
char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2));
|
||||
int path_vec_size = 0;
|
||||
|
||||
char *after_cmd = NULL, *before_cmd = NULL, *after_device = NULL;
|
||||
char *node_name = orig_path->name, *path_str;
|
||||
|
||||
GString *cmd_line = g_string_new("");
|
||||
GString *cmd_line2 = g_string_new("");
|
||||
|
||||
path = qos_graph_get_node(node_name); /* root */
|
||||
node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */
|
||||
|
||||
path_vec[path_vec_size++] = node_name;
|
||||
path_vec[path_vec_size++] = qos_get_machine_type(node_name);
|
||||
|
||||
for (;;) {
|
||||
path = qos_graph_get_node(node_name);
|
||||
if (!path->path_edge) {
|
||||
break;
|
||||
}
|
||||
|
||||
node_name = qos_graph_edge_get_dest(path->path_edge);
|
||||
|
||||
/* append node command line + previous edge command line */
|
||||
if (path->command_line && etype == QEDGE_CONSUMED_BY) {
|
||||
g_string_append(cmd_line, path->command_line);
|
||||
if (after_device) {
|
||||
g_string_append(cmd_line, after_device);
|
||||
}
|
||||
}
|
||||
|
||||
path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
|
||||
/* detect if edge has command line args */
|
||||
after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
|
||||
after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
|
||||
before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
|
||||
edge = qos_graph_get_edge(path->name, node_name);
|
||||
etype = qos_graph_edge_get_type(edge);
|
||||
|
||||
if (before_cmd) {
|
||||
g_string_append(cmd_line, before_cmd);
|
||||
}
|
||||
if (after_cmd) {
|
||||
g_string_append(cmd_line2, after_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
path_vec[path_vec_size++] = NULL;
|
||||
if (after_device) {
|
||||
g_string_append(cmd_line, after_device);
|
||||
}
|
||||
g_string_append(cmd_line, cmd_line2->str);
|
||||
g_string_free(cmd_line2, true);
|
||||
|
||||
/* here position 0 has <arch>/<machine>, position 1 has <machine>.
|
||||
* The path must not have the <arch>, qtest_add_data_func adds it.
|
||||
*/
|
||||
path_str = g_strjoinv("/", path_vec + 1);
|
||||
|
||||
/* put arch/machine in position 1 so run_one_test can do its work
|
||||
* and add the command line at position 0.
|
||||
*/
|
||||
path_vec[1] = path_vec[0];
|
||||
path_vec[0] = g_string_free(cmd_line, false);
|
||||
|
||||
if (path->u.test.subprocess) {
|
||||
gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess",
|
||||
qtest_get_arch(), path_str);
|
||||
qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test);
|
||||
g_test_add_data_func(subprocess_path, path_vec, run_one_test);
|
||||
} else {
|
||||
qtest_add_data_func(path_str, path_vec, run_one_test);
|
||||
}
|
||||
|
||||
g_free(path_str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* main(): heart of the qgraph framework.
|
||||
*
|
||||
* - Initializes the glib test framework
|
||||
* - Creates the graph by invoking the various _init constructors
|
||||
* - Starts QEMU to mark the available devices
|
||||
* - Walks the graph, and each path is added to
|
||||
* the glib test framework (walk_path)
|
||||
* - Runs the tests, calling allocate_object() and allocating the
|
||||
* machine/drivers/test objects
|
||||
* - Cleans up everything
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qos_graph_init();
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
module_call_init(MODULE_INIT_LIBQOS);
|
||||
qos_set_machines_devices_available();
|
||||
|
||||
qos_graph_foreach_test_path(walk_path);
|
||||
g_test_run();
|
||||
qtest_end();
|
||||
qos_graph_destroy();
|
||||
g_free(old_path);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/qgraph_internal.h"
|
||||
|
||||
#define MACHINE_PC "x86_64/pc"
|
||||
#define MACHINE_RASPI2 "arm/raspi2"
|
||||
#define I440FX "i440FX-pcihost"
|
||||
#define PCIBUS_PC "pcibus-pc"
|
||||
#define SDHCI "sdhci"
|
||||
#define PCIBUS "pci-bus"
|
||||
#define SDHCI_PCI "sdhci-pci"
|
||||
#define SDHCI_MM "generic-sdhci"
|
||||
#define REGISTER_TEST "register-test"
|
||||
|
||||
int npath;
|
||||
|
||||
static void *machinefunct(QTestState *qts)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void testfunct(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void check_interface(const char *interface)
|
||||
{
|
||||
g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(interface));
|
||||
g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE);
|
||||
qos_graph_node_set_availability(interface, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_machine(const char *machine)
|
||||
{
|
||||
qos_node_create_machine(machine, machinefunct);
|
||||
g_assert_nonnull(qos_graph_get_machine(machine));
|
||||
g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE);
|
||||
g_assert_nonnull(qos_graph_get_node(machine));
|
||||
g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE);
|
||||
qos_graph_node_set_availability(machine, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE);
|
||||
}
|
||||
|
||||
static void check_contains(const char *machine, const char *driver)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
qos_node_contains(machine, driver, NULL);
|
||||
|
||||
edge = qos_graph_get_edge(machine, driver);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS);
|
||||
g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_produces(const char *machine, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
qos_node_produces(machine, interface);
|
||||
check_interface(interface);
|
||||
edge = qos_graph_get_edge(machine, interface);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==,
|
||||
QEDGE_PRODUCES);
|
||||
g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_consumes(const char *driver, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
qos_node_consumes(driver, interface, NULL);
|
||||
check_interface(interface);
|
||||
edge = qos_graph_get_edge(interface, driver);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY);
|
||||
g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_driver(const char *driver)
|
||||
{
|
||||
qos_node_create_driver(driver, driverfunct);
|
||||
g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(driver));
|
||||
g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE);
|
||||
qos_graph_node_set_availability(driver, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_test(const char *test, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
const char *full_name = g_strdup_printf("%s-tests/%s", interface, test);
|
||||
|
||||
qos_add_test(test, interface, testfunct, NULL);
|
||||
g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE);
|
||||
g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(full_name));
|
||||
g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST);
|
||||
edge = qos_graph_get_edge(interface, full_name);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==,
|
||||
QEDGE_CONSUMED_BY);
|
||||
g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE);
|
||||
qos_graph_node_set_availability(full_name, FALSE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE);
|
||||
}
|
||||
|
||||
static void count_each_test(QOSGraphNode *path, int len)
|
||||
{
|
||||
npath++;
|
||||
}
|
||||
|
||||
static void check_leaf_discovered(int n)
|
||||
{
|
||||
npath = 0;
|
||||
qos_graph_foreach_test_path(count_each_test);
|
||||
g_assert_cmpint(n, ==, npath);
|
||||
}
|
||||
|
||||
/* G_Test functions */
|
||||
|
||||
static void init_nop(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_contains(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
g_assert_null(qos_graph_get_machine(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_node(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_node(I440FX));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_contains(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_contains(MACHINE_PC, PCIBUS_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_produces(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_produces(MACHINE_PC, I440FX);
|
||||
g_assert_null(qos_graph_get_machine(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_node(MACHINE_PC));
|
||||
g_assert_nonnull(qos_graph_get_node(I440FX));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_produces(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_produces(MACHINE_PC, I440FX);
|
||||
check_produces(MACHINE_PC, PCIBUS_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_consumes(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_consumes(I440FX, SDHCI);
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_machine(SDHCI));
|
||||
g_assert_null(qos_graph_get_node(I440FX));
|
||||
g_assert_nonnull(qos_graph_get_node(SDHCI));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_consumes(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_consumes(I440FX, SDHCI);
|
||||
check_consumes(PCIBUS_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_test(REGISTER_TEST, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_contains_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_driver(I440FX);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_contains_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(PCIBUS_PC);
|
||||
check_driver(I440FX);
|
||||
check_contains(PCIBUS_PC, I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_produces_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_produces(MACHINE_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_produces_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
check_produces(I440FX, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_consumes(MACHINE_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
check_consumes(I440FX, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_test(REGISTER_TEST, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_full_sample(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_driver(I440FX);
|
||||
check_driver(PCIBUS_PC);
|
||||
check_contains(I440FX, PCIBUS_PC);
|
||||
check_produces(PCIBUS_PC, PCIBUS);
|
||||
check_driver(SDHCI_PCI);
|
||||
qos_node_consumes(SDHCI_PCI, PCIBUS, NULL);
|
||||
check_produces(SDHCI_PCI, SDHCI);
|
||||
check_driver(SDHCI_MM);
|
||||
check_produces(SDHCI_MM, SDHCI);
|
||||
qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL);
|
||||
check_leaf_discovered(1);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_full_sample_raspi(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_driver(I440FX);
|
||||
check_driver(PCIBUS_PC);
|
||||
check_contains(I440FX, PCIBUS_PC);
|
||||
check_produces(PCIBUS_PC, PCIBUS);
|
||||
check_driver(SDHCI_PCI);
|
||||
qos_node_consumes(SDHCI_PCI, PCIBUS, NULL);
|
||||
check_produces(SDHCI_PCI, SDHCI);
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_contains(MACHINE_RASPI2, SDHCI_MM);
|
||||
check_driver(SDHCI_MM);
|
||||
check_produces(SDHCI_MM, SDHCI);
|
||||
qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL);
|
||||
qos_print_graph();
|
||||
check_leaf_discovered(2);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_cycle(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_driver("B");
|
||||
check_driver("C");
|
||||
check_driver("D");
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
check_contains("B", "C");
|
||||
check_contains("C", "D");
|
||||
check_contains("D", MACHINE_RASPI2);
|
||||
check_leaf_discovered(0);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_two_test_same_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces(MACHINE_RASPI2, "B");
|
||||
qos_add_test("C", "B", testfunct, NULL);
|
||||
qos_add_test("D", "B", testfunct, NULL);
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
check_leaf_discovered(4);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test_in_path(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces(MACHINE_RASPI2, "B");
|
||||
qos_add_test("C", "B", testfunct, NULL);
|
||||
check_driver("D");
|
||||
check_consumes("D", "B");
|
||||
check_produces("D", "E");
|
||||
qos_add_test("F", "E", testfunct, NULL);
|
||||
check_leaf_discovered(2);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_double_edge(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces("B", "C");
|
||||
qos_node_consumes("C", "B", NULL);
|
||||
qos_add_test("D", "C", testfunct, NULL);
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/qgraph/init_nop", init_nop);
|
||||
g_test_add_func("/qgraph/test_machine", test_machine);
|
||||
g_test_add_func("/qgraph/test_contains", test_contains);
|
||||
g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains);
|
||||
g_test_add_func("/qgraph/test_produces", test_produces);
|
||||
g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces);
|
||||
g_test_add_func("/qgraph/test_consumes", test_consumes);
|
||||
g_test_add_func("/qgraph/test_multiple_consumes",
|
||||
test_multiple_consumes);
|
||||
g_test_add_func("/qgraph/test_driver", test_driver);
|
||||
g_test_add_func("/qgraph/test_test", test_test);
|
||||
g_test_add_func("/qgraph/test_machine_contains_driver",
|
||||
test_machine_contains_driver);
|
||||
g_test_add_func("/qgraph/test_driver_contains_driver",
|
||||
test_driver_contains_driver);
|
||||
g_test_add_func("/qgraph/test_machine_produces_interface",
|
||||
test_machine_produces_interface);
|
||||
g_test_add_func("/qgraph/test_driver_produces_interface",
|
||||
test_driver_produces_interface);
|
||||
g_test_add_func("/qgraph/test_machine_consumes_interface",
|
||||
test_machine_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_driver_consumes_interface",
|
||||
test_driver_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_test_consumes_interface",
|
||||
test_test_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_full_sample", test_full_sample);
|
||||
g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi);
|
||||
g_test_add_func("/qgraph/test_cycle", test_cycle);
|
||||
g_test_add_func("/qgraph/test_two_test_same_interface",
|
||||
test_two_test_same_interface);
|
||||
g_test_add_func("/qgraph/test_test_in_path", test_test_in_path);
|
||||
g_test_add_func("/qgraph/test_double_edge", test_double_edge);
|
||||
|
||||
g_test_run();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user