summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.include5
-rw-r--r--tests/qemu-iotests/049.out14
-rw-r--r--tests/qemu-iotests/178.out.qcow23
-rw-r--r--tests/qemu-iotests/178.out.raw3
-rw-r--r--tests/qemu-iotests/241.out4
-rw-r--r--tests/qtest/libqos/qgraph.h450
-rw-r--r--tests/qtest/meson.build1
-rw-r--r--tests/qtest/sse-timer-test.c240
-rw-r--r--tests/test-cutils.c168
-rw-r--r--tests/test-keyval.c35
-rw-r--r--tests/test-qemu-opts.c33
11 files changed, 563 insertions, 393 deletions
diff --git a/tests/Makefile.include b/tests/Makefile.include
index d34254fb29..799e47169c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -92,7 +92,7 @@ TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
# Any number of command separated loggers are accepted. For more
# information please refer to "avocado --help".
AVOCADO_SHOW=app
-AVOCADO_TAGS=$(patsubst %-softmmu,-t arch:%, $(filter %-softmmu,$(TARGET_DIRS)))
+AVOCADO_TAGS=$(patsubst %-softmmu,-t arch:%, $(filter %-softmmu,$(TARGETS)))
$(TESTS_VENV_DIR): $(TESTS_VENV_REQ)
$(call quiet-command, \
@@ -109,7 +109,8 @@ $(TESTS_RESULTS_DIR):
check-venv: $(TESTS_VENV_DIR)
-FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(TARGETS))
+FEDORA_31_ARCHES_TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGETS)))
+FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(FEDORA_31_ARCHES_TARGETS))
FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x
FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES))
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index b1d8fd9107..01f7b1f240 100644
--- a/tests/qemu-iotests/049.out
+++ b/tests/qemu-iotests/049.out
@@ -92,16 +92,22 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
== 3. Invalid sizes ==
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024
-qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
+qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
+qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
-qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size'
+qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
+Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
+and exabytes, respectively.
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
-qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
+qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
+qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
-qemu-img: TEST_DIR/t.qcow2: Value '-1k' is out of range for parameter 'size'
+qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
+Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
+and exabytes, respectively.
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
index fe193fd5f4..0d51fe401e 100644
--- a/tests/qemu-iotests/178.out.qcow2
+++ b/tests/qemu-iotests/178.out.qcow2
@@ -13,7 +13,8 @@ qemu-img: Invalid option list: ,
qemu-img: Invalid parameter 'snapshot.foo'
qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
qemu-img: --output must be used with human or json as argument.
-qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
+qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
+qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img: Unknown file format 'foo'
== Size calculation for a new file (human) ==
diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw
index 445e460fad..116241ddef 100644
--- a/tests/qemu-iotests/178.out.raw
+++ b/tests/qemu-iotests/178.out.raw
@@ -13,7 +13,8 @@ qemu-img: Invalid option list: ,
qemu-img: Invalid parameter 'snapshot.foo'
qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
qemu-img: --output must be used with human or json as argument.
-qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
+qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
+qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img: Unknown file format 'foo'
== Size calculation for a new file (human) ==
diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out
index 75f9f465e5..3f8c173cc8 100644
--- a/tests/qemu-iotests/241.out
+++ b/tests/qemu-iotests/241.out
@@ -5,7 +5,7 @@ QA output created by 241
size: 1024
min block: 1
[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
-{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}]
+{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
=== Exporting unaligned raw image, forced server sector alignment ===
@@ -23,6 +23,6 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
size: 1024
min block: 1
[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
-{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}]
+{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
*** done
diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h
index 07a32535f1..54672350c8 100644
--- a/tests/qtest/libqos/qgraph.h
+++ b/tests/qtest/libqos/qgraph.h
@@ -29,7 +29,6 @@
typedef struct QOSGraphObject QOSGraphObject;
typedef struct QOSGraphNode QOSGraphNode;
typedef struct QOSGraphEdge QOSGraphEdge;
-typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
typedef struct QOSGraphTestOptions QOSGraphTestOptions;
@@ -49,340 +48,94 @@ typedef void (*QOSStartFunct) (QOSGraphObject *object);
typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
/**
- * SECTION: qgraph.h
- * @title: Qtest Driver Framework
- * @short_description: interfaces to organize drivers and tests
- * as nodes in a graph
- *
- * This Qgraph API provides all basic functions to create a graph
- * and instantiate nodes representing machines, drivers and tests
- * representing their relations with CONSUMES, PRODUCES, and CONTAINS
- * edges.
- *
- * The idea is to have a framework where each test asks for a specific
- * driver, and the framework takes care of allocating the proper devices
- * required and passing the correct command line arguments to QEMU.
- *
- * A node can be of four types:
- * - QNODE_MACHINE: for example "arm/raspi2"
- * - QNODE_DRIVER: for example "generic-sdhci"
- * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers)
- * an interface is not explicitly created, it will be auto-
- * matically instantiated when a node consumes or produces
- * it.
- * - QNODE_TEST: for example "sdhci-test", consumes an interface and tests
- * the functions provided
- *
- * Notes for the nodes:
- * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and
- * implement get_driver to return the allocator passing
- * "memory". The function can also return NULL if the
- * allocator is not set.
- * - QNODE_DRIVER: driver names must be unique, and machines and nodes
- * planned to be "consumed" by other nodes must match QEMU
- * drivers name, otherwise they won't be discovered
- *
- * An edge relation between two nodes (drivers or machines) X and Y can be:
- * - X CONSUMES Y: Y can be plugged into X
- * - X PRODUCES Y: X provides the interface Y
- * - X CONTAINS Y: Y is part of X component
- *
- * Basic framework steps are the following:
- * - All nodes and edges are created in their respective
- * machine/driver/test files
- * - The framework starts QEMU and asks for a list of available devices
- * and machines (note that only machines and "consumed" nodes are mapped
- * 1:1 with QEMU devices)
- * - The framework walks the graph starting from the available machines and
- * performs a Depth First Search for tests
- * - Once a test is found, the path is walked again and all drivers are
- * allocated accordingly and the final interface is passed to the test
- * - The test is executed
- * - Unused objects are cleaned and the path discovery is continued
- *
- * Depending on the QEMU binary used, only some drivers/machines will be
- * available and only test that are reached by them will be executed.
- *
- * <example>
- * <title>Creating new driver an its interface</title>
- * <programlisting>
- #include "qgraph.h"
-
- struct My_driver {
- QOSGraphObject obj;
- Node_produced prod;
- Node_contained cont;
- }
-
- static void my_destructor(QOSGraphObject *obj)
- {
- g_free(obj);
- }
-
- static void my_get_driver(void *object, const char *interface) {
- My_driver *dev = object;
- if (!g_strcmp0(interface, "my_interface")) {
- return &dev->prod;
- }
- abort();
- }
-
- static void my_get_device(void *object, const char *device) {
- My_driver *dev = object;
- if (!g_strcmp0(device, "my_driver_contained")) {
- return &dev->cont;
- }
- abort();
- }
-
- static void *my_driver_constructor(void *node_consumed,
- QOSGraphObject *alloc)
- {
- My_driver dev = g_new(My_driver, 1);
- // get the node pointed by the produce edge
- dev->obj.get_driver = my_get_driver;
- // get the node pointed by the contains
- dev->obj.get_device = my_get_device;
- // free the object
- dev->obj.destructor = my_destructor;
- do_something_with_node_consumed(node_consumed);
- // set all fields of contained device
- init_contained_device(&dev->cont);
- return &dev->obj;
- }
-
- static void register_my_driver(void)
- {
- qos_node_create_driver("my_driver", my_driver_constructor);
- // contained drivers don't need a constructor,
- // they will be init by the parent.
- qos_node_create_driver("my_driver_contained", NULL);
-
- // For the sake of this example, assume machine x86_64/pc contains
- // "other_node".
- // This relation, along with the machine and "other_node" creation,
- // should be defined in the x86_64_pc-machine.c file.
- // "my_driver" will then consume "other_node"
- qos_node_contains("my_driver", "my_driver_contained");
- qos_node_produces("my_driver", "my_interface");
- qos_node_consumes("my_driver", "other_node");
- }
- * </programlisting>
- * </example>
- *
- * In the above example, all possible types of relations are created:
- * node "my_driver" consumes, contains and produces other nodes.
- * more specifically:
- * x86_64/pc -->contains--> other_node <--consumes-- my_driver
- * |
- * my_driver_contained <--contains--+
- * |
- * my_interface <--produces--+
- *
- * or inverting the consumes edge in consumed_by:
- *
- * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
- * |
- * my_driver_contained <--contains--+
- * |
- * my_interface <--produces--+
- *
- * <example>
- * <title>Creating new test</title>
- * <programlisting>
- * #include "qgraph.h"
- *
- * static void my_test_function(void *obj, void *data)
- * {
- * Node_produced *interface_to_test = obj;
- * // test interface_to_test
- * }
- *
- * static void register_my_test(void)
- * {
- * qos_add_test("my_interface", "my_test", my_test_function);
- * }
- *
- * libqos_init(register_my_test);
- *
- * </programlisting>
- * </example>
- *
- * Here a new test is created, consuming "my_interface" node
- * and creating a valid path from a machine to a test.
- * Final graph will be like this:
- * x86_64/pc -->contains--> other_node <--consumes-- my_driver
- * |
- * my_driver_contained <--contains--+
- * |
- * my_test --consumes--> my_interface <--produces--+
- *
- * or inverting the consumes edge in consumed_by:
- *
- * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
- * |
- * my_driver_contained <--contains--+
- * |
- * my_test <--consumed_by-- my_interface <--produces--+
- *
- * Assuming there the binary is
- * QTEST_QEMU_BINARY=./qemu-system-x86_64
- * a valid test path will be:
- * "/x86_64/pc/other_node/my_driver/my_interface/my_test".
- *
- * Additional examples are also in test-qgraph.c
- *
- * Command line:
- * Command line is built by using node names and optional arguments
- * passed by the user when building the edges.
- *
- * There are three types of command line arguments:
- * - in node : created from the node name. For example, machines will
- * have "-M <machine>" to its command line, while devices
- * "-device <device>". It is automatically done by the
- * framework.
- * - after node : added as additional argument to the node name.
- * This argument is added optionally when creating edges,
- * by setting the parameter @after_cmd_line and
- * @extra_edge_opts in #QOSGraphEdgeOptions.
- * The framework automatically adds
- * a comma before @extra_edge_opts,
- * because it is going to add attributes
- * after the destination node pointed by
- * the edge containing these options, and automatically
- * adds a space before @after_cmd_line, because it
- * adds an additional device, not an attribute.
- * - before node : added as additional argument to the node name.
- * This argument is added optionally when creating edges,
- * by setting the parameter @before_cmd_line in
- * #QOSGraphEdgeOptions. This attribute
- * is going to add attributes before the destination node
- * pointed by the edge containing these options. It is
- * helpful to commands that are not node-representable,
- * such as "-fdsev" or "-netdev".
- *
- * While adding command line in edges is always used, not all nodes names are
- * used in every path walk: this is because the contained or produced ones
- * are already added by QEMU, so only nodes that "consumes" will be used to
- * build the command line. Also, nodes that will have { "abstract" : true }
- * as QMP attribute will loose their command line, since they are not proper
- * devices to be added in QEMU.
- *
- * Example:
- *
- QOSGraphEdgeOptions opts = {
- .arg = NULL,
- .size_arg = 0,
- .after_cmd_line = "-device other",
- .before_cmd_line = "-netdev something",
- .extra_edge_opts = "addr=04.0",
- };
- QOSGraphNode * node = qos_node_create_driver("my_node", constructor);
- qos_node_consumes_args("my_node", "interface", &opts);
- *
- * Will produce the following command line:
- * "-netdev something -device my_node,addr=04.0 -device other"
- */
-
-/**
- * Edge options to be passed to the contains/consumes *_args function.
+ * struct QOSGraphEdgeOptions:
+ * Edge options to be passed to the contains/consumes \*_args function.
+ * @arg: optional arg that will be used by dest edge
+ * @size_arg: @arg size that will be used by dest edge
+ * @extra_device_opts: optional additional command line for dest
+ * edge, used to add additional attributes
+ * *after* the node command line, the
+ * framework automatically prepends ","
+ * to this argument.
+ * @before_cmd_line: optional additional command line for dest
+ * edge, used to add additional attributes
+ * *before* the node command line, usually
+ * other non-node represented commands,
+ * like "-fdsev synt"
+ * @after_cmd_line: optional extra command line to be added
+ * after the device command. This option
+ * is used to add other devices
+ * command line that depend on current node.
+ * Automatically prepends " " to this argument
+ * @edge_name: optional edge to differentiate multiple
+ * devices with same node name
*/
struct QOSGraphEdgeOptions {
- void *arg; /*
- * optional arg that will be used by
- * dest edge
- */
- uint32_t size_arg; /*
- * optional arg size that will be used by
- * dest edge
- */
- const char *extra_device_opts;/*
- *optional additional command line for dest
- * edge, used to add additional attributes
- * *after* the node command line, the
- * framework automatically prepends ","
- * to this argument.
- */
- const char *before_cmd_line; /*
- * optional additional command line for dest
- * edge, used to add additional attributes
- * *before* the node command line, usually
- * other non-node represented commands,
- * like "-fdsev synt"
- */
- const char *after_cmd_line; /*
- * optional extra command line to be added
- * after the device command. This option
- * is used to add other devices
- * command line that depend on current node.
- * Automatically prepends " " to this
- * argument
- */
- const char *edge_name; /*
- * optional edge to differentiate multiple
- * devices with same node name
- */
+ void *arg;
+ uint32_t size_arg;
+ const char *extra_device_opts;
+ const char *before_cmd_line;
+ const char *after_cmd_line;
+ const char *edge_name;
};
/**
+ * struct QOSGraphTestOptions:
* Test options to be passed to the test functions.
+ * @edge: edge arguments that will be used by test.
+ * Note that test *does not* use edge_name,
+ * and uses instead arg and size_arg as
+ * data arg for its test function.
+ * @arg: if @before is non-NULL, pass @arg there.
+ * Otherwise pass it to the test function.
+ * @before: executed before the test. Used to add
+ * additional parameters to the command line
+ * and modify the argument to the test function.
+ * @subprocess: run the test in a subprocess.
*/
struct QOSGraphTestOptions {
- QOSGraphEdgeOptions edge; /* edge arguments that will be used by test.
- * Note that test *does not* use edge_name,
- * and uses instead arg and size_arg as
- * data arg for its test function.
- */
- void *arg; /* passed to the .before function, or to the
- * test function if there is no .before
- * function
- */
- QOSBeforeTest before; /* executed before the test. Can add
- * additional parameters to the command line
- * and modify the argument to the test function.
- */
- bool subprocess; /* run the test in a subprocess */
+ QOSGraphEdgeOptions edge;
+ void *arg;
+ QOSBeforeTest before;
+ bool subprocess;
};
/**
+ * struct QOSGraphObject:
* Each driver, test or machine of this framework will have a
* QOSGraphObject as first field.
*
* This set of functions offered by QOSGraphObject are executed
* in different stages of the framework:
- * - get_driver / get_device : Once a machine-to-test path has been
- * found, the framework traverses it again and allocates all the
- * nodes, using the provided constructor. To satisfy their relations,
- * i.e. for produces or contains, where a struct constructor needs
- * an external parameter represented by the previous node,
- * the framework will call get_device (for contains) or
- * get_driver (for produces), depending on the edge type, passing
- * them the name of the next node to be taken and getting from them
- * the corresponding pointer to the actual structure of the next node to
- * be used in the path.
- *
- * - start_hw: This function is executed after all the path objects
- * have been allocated, but before the test is run. It starts the hw, setting
- * the initial configurations (*_device_enable) and making it ready for the
- * test.
- *
- * - destructor: Opposite to the node constructor, destroys the object.
- * This function is called after the test has been executed, and performs
- * a complete cleanup of each node allocated field. In case no constructor
- * is provided, no destructor will be called.
- *
+ * @get_driver: see @get_device
+ * @get_device: Once a machine-to-test path has been
+ * found, the framework traverses it again and allocates all the
+ * nodes, using the provided constructor. To satisfy their
+ * relations, i.e. for produces or contains, where a struct
+ * constructor needs an external parameter represented by the
+ * previous node, the framework will call
+ * @get_device (for contains) or @get_driver (for produces),
+ * depending on the edge type, passing them the name of the next
+ * node to be taken and getting from them the corresponding
+ * pointer to the actual structure of the next node to
+ * be used in the path.
+ * @start_hw: This function is executed after all the path objects
+ * have been allocated, but before the test is run. It starts the
+ * hw, setting the initial configurations (\*_device_enable) and
+ * making it ready for the test.
+ * @destructor: Opposite to the node constructor, destroys the object.
+ * This function is called after the test has been executed, and
+ * performs a complete cleanup of each node allocated field.
+ * In case no constructor is provided, no destructor will be
+ * called.
+ * @free: free the memory associated to the QOSGraphObject and its contained
+ * children
*/
struct QOSGraphObject {
- /* for produces edges, returns void * */
QOSGetDriver get_driver;
- /* for contains edges, returns a QOSGraphObject * */
QOSGetDevice get_device;
- /* start the hw, get ready for the test */
QOSStartFunct start_hw;
- /* destroy this QOSGraphObject */
QOSDestructorFunc destructor;
- /* free the memory associated to the QOSGraphObject and its contained
- * children */
GDestroyNotify free;
};
@@ -399,24 +152,30 @@ void qos_graph_init(void);
void qos_graph_destroy(void);
/**
- * qos_node_destroy(): removes and frees a node from the,
+ * qos_node_destroy(): removes and frees a node from the
* nodes hash table.
+ * @key: Name of the node
*/
void qos_node_destroy(void *key);
/**
- * qos_edge_destroy(): removes and frees an edge from the,
+ * qos_edge_destroy(): removes and frees an edge from the
* edges hash table.
+ * @key: Name of the node
*/
void qos_edge_destroy(void *key);
/**
* qos_add_test(): adds a test node @name to the nodes hash table.
+ * @name: Name of the test
+ * @interface: Name of the interface node it consumes
+ * @test_func: Actual test to perform
+ * @opts: Facultative options (see %QOSGraphTestOptions)
*
* The test will consume a @interface node, and once the
* graph walking algorithm has found it, the @test_func will be
* executed. It also has the possibility to
- * add an optional @opts (see %QOSGraphNodeOptions).
+ * add an optional @opts (see %QOSGraphTestOptions).
*
* For tests, opts->edge.arg and size_arg represent the arg to pass
* to @test_func
@@ -428,6 +187,8 @@ void qos_add_test(const char *name, const char *interface,
/**
* qos_node_create_machine(): creates the machine @name and
* adds it to the node hash table.
+ * @name: Name of the machine
+ * @function: Machine constructor
*
* This node will be of type QNODE_MACHINE and have @function
* as constructor
@@ -438,6 +199,9 @@ void qos_node_create_machine(const char *name, QOSCreateMachineFunc function);
* qos_node_create_machine_args(): same as qos_node_create_machine,
* but with the possibility to add an optional ", @opts" after -M machine
* command line.
+ * @name: Name of the machine
+ * @function: Machine constructor
+ * @opts: Optional additional command line
*/
void qos_node_create_machine_args(const char *name,
QOSCreateMachineFunc function,
@@ -446,6 +210,8 @@ void qos_node_create_machine_args(const char *name,
/**
* qos_node_create_driver(): creates the driver @name and
* adds it to the node hash table.
+ * @name: Name of the driver
+ * @function: Driver constructor
*
* This node will be of type QNODE_DRIVER and have @function
* as constructor
@@ -453,17 +219,17 @@ void qos_node_create_machine_args(const char *name,
void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
/**
- * Behaves as qos_node_create_driver() with the extension of allowing to
- * specify a different node name vs. associated QEMU device name.
+ * qos_node_create_driver_named(): behaves as qos_node_create_driver() with the
+ * extension of allowing to specify a different node name vs. associated QEMU
+ * device name.
+ * @name: Custom, unique name of the node to be created
+ * @qemu_name: Actual (official) QEMU driver name the node shall be
+ * associated with
+ * @function: Driver constructor
*
* Use this function instead of qos_node_create_driver() if you need to create
* several instances of the same QEMU device. You are free to choose a custom
* node name, however the chosen node name must always be unique.
- *
- * @param name: custom, unique name of the node to be created
- * @param qemu_name: actual (official) QEMU driver name the node shall be
- * associated with
- * @param function: driver constructor
*/
void qos_node_create_driver_named(const char *name, const char *qemu_name,
QOSCreateDriverFunc function);
@@ -472,6 +238,9 @@ void qos_node_create_driver_named(const char *name, const char *qemu_name,
* qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS
* and adds them to the edge list mapped to @container in the
* edge hash table.
+ * @container: Source node that "contains"
+ * @contained: Destination node that "is contained"
+ * @opts: Facultative options (see %QOSGraphEdgeOptions)
*
* The edges will have @container as source and @contained as destination.
*
@@ -483,14 +252,17 @@ void qos_node_create_driver_named(const char *name, const char *qemu_name,
* This function can be useful when there are multiple devices
* with the same node name contained in a machine/other node
*
- * For example, if "arm/raspi2" contains 2 "generic-sdhci"
+ * For example, if ``arm/raspi2`` contains 2 ``generic-sdhci``
* devices, the right commands will be:
- * qos_node_create_machine("arm/raspi2");
- * qos_node_create_driver("generic-sdhci", constructor);
- * //assume rest of the fields are set NULL
- * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
- * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
- * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
+ *
+ * .. code::
+ *
+ * qos_node_create_machine("arm/raspi2");
+ * qos_node_create_driver("generic-sdhci", constructor);
+ * // assume rest of the fields are set NULL
+ * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
+ * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
+ * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
*
* Of course this also requires that the @container's get_device function
* should implement a case for "emmc" and "sdcard".
@@ -505,6 +277,8 @@ void qos_node_contains(const char *container, const char *contained,
* qos_node_produces(): creates an edge of type QEDGE_PRODUCES and
* adds it to the edge list mapped to @producer in the
* edge hash table.
+ * @producer: Source node that "produces"
+ * @interface: Interface node that "is produced"
*
* This edge will have @producer as source and @interface as destination.
*/
@@ -514,6 +288,9 @@ void qos_node_produces(const char *producer, const char *interface);
* qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and
* adds it to the edge list mapped to @interface in the
* edge hash table.
+ * @consumer: Node that "consumes"
+ * @interface: Interface node that "is consumed by"
+ * @opts: Facultative options (see %QOSGraphEdgeOptions)
*
* This edge will have @interface as source and @consumer as destination.
* It also has the possibility to add an optional @opts
@@ -539,7 +316,7 @@ const char *qos_get_current_command_line(void);
/**
* qos_allocate_objects():
* @qts: The #QTestState that will be referred to by the machine object.
- * @alloc: Where to store the allocator for the machine object, or %NULL.
+ * @p_alloc: Where to store the allocator for the machine object, or %NULL.
*
* Allocate driver objects for the current test
* path, but relative to the QTestState @qts.
@@ -551,24 +328,27 @@ void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
/**
* qos_object_destroy(): calls the destructor for @obj
+ * @obj: A #QOSGraphObject to destroy
*/
void qos_object_destroy(QOSGraphObject *obj);
/**
* qos_object_queue_destroy(): queue the destructor for @obj so that it is
* called at the end of the test
+ * @obj: A #QOSGraphObject to destroy
*/
void qos_object_queue_destroy(QOSGraphObject *obj);
/**
* qos_object_start_hw(): calls the start_hw function for @obj
+ * @obj: A #QOSGraphObject containing the start_hw function
*/
void qos_object_start_hw(QOSGraphObject *obj);
/**
* qos_machine_new(): instantiate a new machine node
- * @node: A machine node to be instantiated
- * @qts: The #QTestState that will be referred to by the machine object.
+ * @node: Machine node to be instantiated
+ * @qts: A #QTestState that will be referred to by the machine object.
*
* Returns a machine object.
*/
@@ -587,8 +367,8 @@ QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
QGuestAllocator *alloc, void *arg);
/**
- * Just for debugging purpose: prints all currently existing nodes and
- * edges to stdout.
+ * qos_dump_graph(): prints all currently existing nodes and
+ * edges to stdout. Just for debugging purposes.
*
* All qtests add themselves to the overall qos graph by calling qgraph
* functions that add device nodes and edges between the individual graph
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 58efc46144..2688e1bfad 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -157,6 +157,7 @@ qtests_npcm7xx = \
'npcm7xx_watchdog_timer-test'] + \
(slirp.found() ? ['npcm7xx_emc-test'] : [])
qtests_arm = \
+ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \
(config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \
diff --git a/tests/qtest/sse-timer-test.c b/tests/qtest/sse-timer-test.c
new file mode 100644
index 0000000000..a65d7542d5
--- /dev/null
+++ b/tests/qtest/sse-timer-test.c
@@ -0,0 +1,240 @@
+/*
+ * QTest testcase for the SSE timer device
+ *
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+/*
+ * SSE-123/SSE-300 timer in the mps3-an547 board, where it is driven
+ * at 32MHz, so 31.25ns per tick.
+ */
+#define TIMER_BASE 0x48000000
+
+/* PERIPHNSPPC0 register in the SSE-300 Secure Access Configuration block */
+#define PERIPHNSPPC0 (0x50080000 + 0x70)
+
+/* Base of the System Counter control frame */
+#define COUNTER_BASE 0x58100000
+
+/* SSE counter register offsets in the control frame */
+#define CNTCR 0
+#define CNTSR 0x4
+#define CNTCV_LO 0x8
+#define CNTCV_HI 0xc
+#define CNTSCR 0x10
+
+/* SSE timer register offsets */
+#define CNTPCT_LO 0
+#define CNTPCT_HI 4
+#define CNTFRQ 0x10
+#define CNTP_CVAL_LO 0x20
+#define CNTP_CVAL_HI 0x24
+#define CNTP_TVAL 0x28
+#define CNTP_CTL 0x2c
+#define CNTP_AIVAL_LO 0x40
+#define CNTP_AIVAL_HI 0x44
+#define CNTP_AIVAL_RELOAD 0x48
+#define CNTP_AIVAL_CTL 0x4c
+
+/* 4 ticks in nanoseconds (so we can work in integers) */
+#define FOUR_TICKS 125
+
+static void clock_step_ticks(uint64_t ticks)
+{
+ /*
+ * Advance the qtest clock by however many nanoseconds we
+ * need to move the timer forward the specified number of ticks.
+ * ticks must be a multiple of 4, so we get a whole number of ns.
+ */
+ assert(!(ticks & 3));
+ clock_step(FOUR_TICKS * (ticks >> 2));
+}
+
+static void reset_counter_and_timer(void)
+{
+ /*
+ * Reset the system counter and the timer between tests. This
+ * isn't a full reset, but it's sufficient for what the tests check.
+ */
+ writel(COUNTER_BASE + CNTCR, 0);
+ writel(TIMER_BASE + CNTP_CTL, 0);
+ writel(TIMER_BASE + CNTP_AIVAL_CTL, 0);
+ writel(COUNTER_BASE + CNTCV_LO, 0);
+ writel(COUNTER_BASE + CNTCV_HI, 0);
+}
+
+static void test_counter(void)
+{
+ /* Basic counter functionality test */
+
+ reset_counter_and_timer();
+ /* The counter should start disabled: check that it doesn't move */
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 0);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
+ /* Now enable it and check that it does count */
+ writel(COUNTER_BASE + CNTCR, 1);
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 100);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
+ /* Check the counter scaling functionality */
+ writel(COUNTER_BASE + CNTCR, 0);
+ writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */
+ writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */
+ clock_step_ticks(160);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 110);
+ g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0);
+}
+
+static void test_timer(void)
+{
+ /* Basic timer functionality test */
+
+ reset_counter_and_timer();
+ /*
+ * The timer is behind a Peripheral Protection Controller, and
+ * qtest accesses are always non-secure (no memory attributes),
+ * so we must program the PPC to accept NS transactions. TIMER0
+ * is on port 0 of PPC0, controlled by bit 0 of this register.
+ */
+ writel(PERIPHNSPPC0, 1);
+ /* We must enable the System Counter or the timer won't run. */
+ writel(COUNTER_BASE + CNTCR, 1);
+
+ /* Timer starts disabled and with a counter of 0 */
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);
+
+ /* Turn it on */
+ writel(TIMER_BASE + CNTP_CTL, 1);
+
+ /* Is the timer ticking? */
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0);
+
+ /* Set the CompareValue to 4000 ticks */
+ writel(TIMER_BASE + CNTP_CVAL_LO, 4000);
+ writel(TIMER_BASE + CNTP_CVAL_HI, 0);
+
+ /* Check TVAL view of the counter */
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 3900);
+
+ /* Advance to the CompareValue mark and check ISTATUS is set */
+ clock_step_ticks(3900);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
+
+ /* Now exercise the auto-reload part of the timer */
+ writel(TIMER_BASE + CNTP_AIVAL_RELOAD, 200);
+ writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
+
+ /* Check AIVAL was reloaded and that ISTATUS is now clear */
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4200);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+
+ /*
+ * Check that when we advance forward to the reload time the interrupt
+ * fires and the value reloads
+ */
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4400);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
+
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
+ /* Check that writing 0 to CLR clears the interrupt */
+ writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+ /* Check that when we move forward to the reload time it fires again */
+ clock_step_ticks(100);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0);
+
+ /*
+ * Step the clock far enough that we overflow the low half of the
+ * CNTPCT and AIVAL registers, and check that their high halves
+ * give the right values. We do the forward movement in
+ * non-autoinc mode because otherwise it takes forever as the
+ * timer has to emulate all the 'reload at t + N, t + 2N, etc'
+ * steps.
+ */
+ writel(TIMER_BASE + CNTP_AIVAL_CTL, 0);
+ clock_step_ticks(0x42ULL << 32);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42);
+
+ /* Turn on the autoinc again to check AIVAL_HI */
+ writel(TIMER_BASE + CNTP_AIVAL_CTL, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42);
+}
+
+static void test_timer_scale_change(void)
+{
+ /*
+ * Test that the timer responds correctly to counter
+ * scaling changes while it has an active timer.
+ */
+ reset_counter_and_timer();
+ /* Give ourselves access to the timer, and enable the counter and timer */
+ writel(PERIPHNSPPC0, 1);
+ writel(COUNTER_BASE + CNTCR, 1);
+ writel(TIMER_BASE + CNTP_CTL, 1);
+ /* Set the CompareValue to 4000 ticks */
+ writel(TIMER_BASE + CNTP_CVAL_LO, 4000);
+ writel(TIMER_BASE + CNTP_CVAL_HI, 0);
+ /* Advance halfway and check ISTATUS is not set */
+ clock_step_ticks(2000);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+ /* Reprogram the counter to run at 1/16th speed */
+ writel(COUNTER_BASE + CNTCR, 0);
+ writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */
+ writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */
+ /* Advance to where the timer would have fired and check it has not */
+ clock_step_ticks(2000);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+ /* Advance to where the timer must fire at the new clock rate */
+ clock_step_ticks(29996);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1);
+ clock_step_ticks(4);
+ g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5);
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_start("-machine mps3-an547");
+
+ qtest_add_func("/sse-timer/counter", test_counter);
+ qtest_add_func("/sse-timer/timer", test_timer);
+ qtest_add_func("/sse-timer/timer-scale-change", test_timer_scale_change);
+
+ r = g_test_run();
+
+ qtest_end();
+
+ return r;
+}
diff --git a/tests/test-cutils.c b/tests/test-cutils.c
index 1aa8351520..bad3a60993 100644
--- a/tests/test-cutils.c
+++ b/tests/test-cutils.c
@@ -1960,18 +1960,24 @@ static void test_qemu_strtosz_simple(void)
g_assert_cmpint(res, ==, 0);
g_assert(endptr == str + 1);
- str = "12345";
+ /* Leading 0 gives decimal results, not octal */
+ str = "08";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 8);
+ g_assert(endptr == str + 2);
+
+ /* Leading space is ignored */
+ str = " 12345";
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
g_assert_cmpint(res, ==, 12345);
- g_assert(endptr == str + 5);
+ g_assert(endptr == str + 6);
err = qemu_strtosz(str, NULL, &res);
g_assert_cmpint(err, ==, 0);
g_assert_cmpint(res, ==, 12345);
- /* Note: precision is 53 bits since we're parsing with strtod() */
-
str = "9007199254740991"; /* 2^53-1 */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
@@ -1987,7 +1993,7 @@ static void test_qemu_strtosz_simple(void)
str = "9007199254740993"; /* 2^53+1 */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */
+ g_assert_cmpint(res, ==, 0x20000000000001);
g_assert(endptr == str + 16);
str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */
@@ -1999,11 +2005,40 @@ static void test_qemu_strtosz_simple(void)
str = "18446744073709550591"; /* 0xfffffffffffffbff */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */
+ g_assert_cmpint(res, ==, 0xfffffffffffffbff);
g_assert(endptr == str + 20);
- /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to
- * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */
+ str = "18446744073709551615"; /* 0xffffffffffffffff */
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0xffffffffffffffff);
+ g_assert(endptr == str + 20);
+}
+
+static void test_qemu_strtosz_hex(void)
+{
+ const char *str;
+ const char *endptr;
+ int err;
+ uint64_t res = 0xbaadf00d;
+
+ str = "0x0";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0);
+ g_assert(endptr == str + 3);
+
+ str = "0xab";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 171);
+ g_assert(endptr == str + 4);
+
+ str = "0xae";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 174);
+ g_assert(endptr == str + 4);
}
static void test_qemu_strtosz_units(void)
@@ -2064,14 +2099,36 @@ static void test_qemu_strtosz_units(void)
static void test_qemu_strtosz_float(void)
{
- const char *str = "12.345M";
+ const char *str;
int err;
const char *endptr;
uint64_t res = 0xbaadf00d;
+ str = "0.5E";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, EiB / 2);
+ g_assert(endptr == str + 4);
+
+ /* For convenience, a fraction of 0 is tolerated even on bytes */
+ str = "1.0B";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 1);
+ g_assert(endptr == str + 4);
+
+ /* An empty fraction is tolerated */
+ str = "1.k";
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 12.345 * MiB);
+ g_assert_cmpint(res, ==, 1024);
+ g_assert(endptr == str + 3);
+
+ /* For convenience, we permit values that are not byte-exact */
+ str = "12.345M";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB));
g_assert(endptr == str + 7);
}
@@ -2106,6 +2163,45 @@ static void test_qemu_strtosz_invalid(void)
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -EINVAL);
g_assert(endptr == str);
+
+ /* Fractional values require scale larger than bytes */
+ str = "1.1B";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ str = "1.1";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ /* No floating point exponents */
+ str = "1.5e1k";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ str = "1.5E+0k";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ /* No hex fractions */
+ str = "0x1.8k";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ /* No negative values */
+ str = "-0";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+
+ str = "-1";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
}
static void test_qemu_strtosz_trailing(void)
@@ -2131,6 +2227,30 @@ static void test_qemu_strtosz_trailing(void)
err = qemu_strtosz(str, NULL, &res);
g_assert_cmpint(err, ==, -EINVAL);
+
+ str = "0x";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(res, ==, 0);
+ g_assert(endptr == str + 1);
+
+ err = qemu_strtosz(str, NULL, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+
+ str = "0.NaN";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert(endptr == str + 2);
+
+ err = qemu_strtosz(str, NULL, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
+
+ str = "123-45";
+ err = qemu_strtosz(str, &endptr, &res);
+ g_assert_cmpint(res, ==, 123);
+ g_assert(endptr == str + 3);
+
+ err = qemu_strtosz(str, NULL, &res);
+ g_assert_cmpint(err, ==, -EINVAL);
}
static void test_qemu_strtosz_erange(void)
@@ -2140,22 +2260,7 @@ static void test_qemu_strtosz_erange(void)
int err;
uint64_t res = 0xbaadf00d;
- str = "-1";
- err = qemu_strtosz(str, &endptr, &res);
- g_assert_cmpint(err, ==, -ERANGE);
- g_assert(endptr == str + 2);
-
- str = "18446744073709550592"; /* 0xfffffffffffffc00 */
- err = qemu_strtosz(str, &endptr, &res);
- g_assert_cmpint(err, ==, -ERANGE);
- g_assert(endptr == str + 20);
-
- str = "18446744073709551615"; /* 2^64-1 */
- err = qemu_strtosz(str, &endptr, &res);
- g_assert_cmpint(err, ==, -ERANGE);
- g_assert(endptr == str + 20);
-
- str = "18446744073709551616"; /* 2^64 */
+ str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -ERANGE);
g_assert(endptr == str + 20);
@@ -2168,15 +2273,22 @@ static void test_qemu_strtosz_erange(void)
static void test_qemu_strtosz_metric(void)
{
- const char *str = "12345k";
+ const char *str;
int err;
const char *endptr;
uint64_t res = 0xbaadf00d;
+ str = "12345k";
err = qemu_strtosz_metric(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
g_assert_cmpint(res, ==, 12345000);
g_assert(endptr == str + 6);
+
+ str = "12.345M";
+ err = qemu_strtosz_metric(str, &endptr, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 12345000);
+ g_assert(endptr == str + 7);
}
int main(int argc, char **argv)
@@ -2443,6 +2555,8 @@ int main(int argc, char **argv)
g_test_add_func("/cutils/strtosz/simple",
test_qemu_strtosz_simple);
+ g_test_add_func("/cutils/strtosz/hex",
+ test_qemu_strtosz_hex);
g_test_add_func("/cutils/strtosz/units",
test_qemu_strtosz_units);
g_test_add_func("/cutils/strtosz/float",
diff --git a/tests/test-keyval.c b/tests/test-keyval.c
index ee927fe4e4..e20c07cf3e 100644
--- a/tests/test-keyval.c
+++ b/tests/test-keyval.c
@@ -445,9 +445,9 @@ static void test_keyval_visit_size(void)
visit_end_struct(v, NULL);
visit_free(v);
- /* Note: precision is 53 bits since we're parsing with strtod() */
+ /* Note: full 64 bits of precision */
- /* Around limit of precision: 2^53-1, 2^53, 2^53+1 */
+ /* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */
qdict = keyval_parse("sz1=9007199254740991,"
"sz2=9007199254740992,"
"sz3=9007199254740993",
@@ -460,22 +460,25 @@ static void test_keyval_visit_size(void)
visit_type_size(v, "sz2", &sz, &error_abort);
g_assert_cmphex(sz, ==, 0x20000000000000);
visit_type_size(v, "sz3", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x20000000000000);
+ g_assert_cmphex(sz, ==, 0x20000000000001);
visit_check_struct(v, &error_abort);
visit_end_struct(v, NULL);
visit_free(v);
- /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
- qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */
- "sz2=9223372036854775295", /* 7ffffffffffffdff */
+ /* Close to signed integer limit 2^63 */
+ qdict = keyval_parse("sz1=9223372036854775807," /* 7fffffffffffffff */
+ "sz2=9223372036854775808," /* 8000000000000000 */
+ "sz3=9223372036854775809", /* 8000000000000001 */
NULL, NULL, &error_abort);
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
qobject_unref(qdict);
visit_start_struct(v, NULL, NULL, 0, &error_abort);
visit_type_size(v, "sz1", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
+ g_assert_cmphex(sz, ==, 0x7fffffffffffffff);
visit_type_size(v, "sz2", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
+ g_assert_cmphex(sz, ==, 0x8000000000000000);
+ visit_type_size(v, "sz3", &sz, &error_abort);
+ g_assert_cmphex(sz, ==, 0x8000000000000001);
visit_check_struct(v, &error_abort);
visit_end_struct(v, NULL);
visit_free(v);
@@ -490,14 +493,26 @@ static void test_keyval_visit_size(void)
visit_type_size(v, "sz1", &sz, &error_abort);
g_assert_cmphex(sz, ==, 0xfffffffffffff800);
visit_type_size(v, "sz2", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0xfffffffffffff800);
+ g_assert_cmphex(sz, ==, 0xfffffffffffffbff);
+ visit_check_struct(v, &error_abort);
+ visit_end_struct(v, NULL);
+ visit_free(v);
+
+ /* Actual limit 2^64-1*/
+ qdict = keyval_parse("sz1=18446744073709551615", /* ffffffffffffffff */
+ NULL, NULL, &error_abort);
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ qobject_unref(qdict);
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+ visit_type_size(v, "sz1", &sz, &error_abort);
+ g_assert_cmphex(sz, ==, 0xffffffffffffffff);
visit_check_struct(v, &error_abort);
visit_end_struct(v, NULL);
visit_free(v);
/* Beyond limits */
qdict = keyval_parse("sz1=-1,"
- "sz2=18446744073709550592", /* fffffffffffffc00 */
+ "sz2=18446744073709551616", /* 2^64 */
NULL, NULL, &error_abort);
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
qobject_unref(qdict);
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 8bbb17b1c7..6568e31a72 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -654,9 +654,9 @@ static void test_opts_parse_size(void)
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0);
- /* Note: precision is 53 bits since we're parsing with strtod() */
+ /* Note: full 64 bits of precision */
- /* Around limit of precision: 2^53-1, 2^53, 2^54 */
+ /* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */
opts = qemu_opts_parse(&opts_list_02,
"size1=9007199254740991,"
"size2=9007199254740992,"
@@ -668,18 +668,21 @@ static void test_opts_parse_size(void)
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
==, 0x20000000000000);
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
- ==, 0x20000000000000);
+ ==, 0x20000000000001);
- /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
+ /* Close to signed int limit: 2^63-1, 2^63, 2^63+1 */
opts = qemu_opts_parse(&opts_list_02,
- "size1=9223372036854774784," /* 7ffffffffffffc00 */
- "size2=9223372036854775295", /* 7ffffffffffffdff */
+ "size1=9223372036854775807," /* 7fffffffffffffff */
+ "size2=9223372036854775808," /* 8000000000000000 */
+ "size3=9223372036854775809", /* 8000000000000001 */
false, &error_abort);
- g_assert_cmpuint(opts_count(opts), ==, 2);
+ g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
- ==, 0x7ffffffffffffc00);
+ ==, 0x7fffffffffffffff);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
- ==, 0x7ffffffffffffc00);
+ ==, 0x8000000000000000);
+ g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
+ ==, 0x8000000000000001);
/* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
opts = qemu_opts_parse(&opts_list_02,
@@ -690,14 +693,22 @@ static void test_opts_parse_size(void)
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
==, 0xfffffffffffff800);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
- ==, 0xfffffffffffff800);
+ ==, 0xfffffffffffffbff);
+
+ /* Actual limit, 2^64-1 */
+ opts = qemu_opts_parse(&opts_list_02,
+ "size1=18446744073709551615", /* ffffffffffffffff */
+ false, &error_abort);
+ g_assert_cmpuint(opts_count(opts), ==, 1);
+ g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
+ ==, 0xffffffffffffffff);
/* Beyond limits */
opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_02,
- "size1=18446744073709550592", /* fffffffffffffc00 */
+ "size1=18446744073709551616", /* 2^64 */
false, &err);
error_free_or_abort(&err);
g_assert(!opts);