Test the monitor-add, monitor-remove, and query-monitors QMP commands:
- Basic lifecycle: chardev-add -> monitor-add -> query-monitors ->
monitor-remove -> chardev-remove
- Error: duplicate monitor id
- Error: monitor-remove on nonexistent id
- Error: monitor-add with nonexistent chardev
- Re-add after remove: same id and chardev reusable after removal
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
tests/qtest/qmp-test.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c
index edf0886787..9dbd3a6103 100644
--- a/tests/qtest/qmp-test.c
+++ b/tests/qtest/qmp-test.c
@@ -337,6 +337,105 @@ static void test_qmp_missing_any_arg(void)
qtest_quit(qts);
}
+static void test_qmp_monitor_add_remove(void)
+{
+ QTestState *qts;
+ QDict *resp;
+
+ qts = qtest_init(common_args);
+
+ /* Create a null chardev for the dynamic monitor */
+ resp = qtest_qmp(qts,
+ "{'execute': 'chardev-add',"
+ " 'arguments': {'id': 'monitor-chardev',"
+ " 'backend': {'type': 'null',"
+ " 'data': {}}}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* Add a dynamic monitor */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-add',"
+ " 'arguments': {'id': 'dyn-mon',"
+ " 'chardev': 'monitor-chardev'}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* Verify it shows up in query-monitors */
+ resp = qtest_qmp(qts, "{'execute': 'query-monitors'}");
+ g_assert(!qdict_haskey(resp, "error"));
+ qobject_unref(resp);
+
+ /* Error: duplicate id */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-add',"
+ " 'arguments': {'id': 'dyn-mon',"
+ " 'chardev': 'monitor-chardev'}}");
+ qmp_expect_error_and_unref(resp, "GenericError");
+
+ /* Remove the dynamic monitor */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-remove',"
+ " 'arguments': {'id': 'dyn-mon'}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* Error: remove nonexistent */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-remove',"
+ " 'arguments': {'id': 'dyn-mon'}}");
+ qmp_expect_error_and_unref(resp, "GenericError");
+
+ /* Error: remove CLI monitor (the default one) -- find its id first.
+ * CLI monitors typically have no id, so use a bogus id to test. */
+
+ /* Add again after remove -- same id and chardev should work */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-add',"
+ " 'arguments': {'id': 'dyn-mon',"
+ " 'chardev': 'monitor-chardev'}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* Clean up */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-remove',"
+ " 'arguments': {'id': 'dyn-mon'}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts,
+ "{'execute': 'chardev-remove',"
+ " 'arguments': {'id': 'monitor-chardev'}}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+}
+
+static void test_qmp_monitor_error_paths(void)
+{
+ QTestState *qts;
+ QDict *resp;
+
+ qts = qtest_init(common_args);
+
+ /* Error: chardev does not exist */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-add',"
+ " 'arguments': {'id': 'bad-mon',"
+ " 'chardev': 'nonexistent'}}");
+ qmp_expect_error_and_unref(resp, "GenericError");
+
+ /* Error: remove nonexistent monitor */
+ resp = qtest_qmp(qts,
+ "{'execute': 'monitor-remove',"
+ " 'arguments': {'id': 'bogus'}}");
+ qmp_expect_error_and_unref(resp, "GenericError");
+
+ qtest_quit(qts);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
@@ -348,6 +447,8 @@ int main(int argc, char *argv[])
#endif
qtest_add_func("qmp/preconfig", test_qmp_preconfig);
qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg);
+ qtest_add_func("qmp/monitor-add-remove", test_qmp_monitor_add_remove);
+ qtest_add_func("qmp/monitor-error-paths", test_qmp_monitor_error_paths);
return g_test_run();
}
--
2.47.3
Am Thu, 02 Apr 2026 23:19:19 +0200
schrieb Christian Brauner <brauner@kernel.org>:
> Test the monitor-add, monitor-remove, and query-monitors QMP commands:
>
> - Basic lifecycle: chardev-add -> monitor-add -> query-monitors ->
> monitor-remove -> chardev-remove
> - Error: duplicate monitor id
> - Error: monitor-remove on nonexistent id
> - Error: monitor-add with nonexistent chardev
> - Re-add after remove: same id and chardev reusable after removal
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> ---
> tests/qtest/qmp-test.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 101 insertions(+)
>
> diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c
> index edf0886787..9dbd3a6103 100644
> --- a/tests/qtest/qmp-test.c
> +++ b/tests/qtest/qmp-test.c
> @@ -337,6 +337,105 @@ static void test_qmp_missing_any_arg(void)
> qtest_quit(qts);
> }
>
> +static void test_qmp_monitor_add_remove(void)
> +{
> + QTestState *qts;
> + QDict *resp;
> +
> + qts = qtest_init(common_args);
> +
> + /* Create a null chardev for the dynamic monitor */
> + resp = qtest_qmp(qts,
> + "{'execute': 'chardev-add',"
> + " 'arguments': {'id': 'monitor-chardev',"
> + " 'backend': {'type': 'null',"
> + " 'data': {}}}}");
> + g_assert(qdict_haskey(resp, "return"));
> + qobject_unref(resp);
> +
> + /* Add a dynamic monitor */
> + resp = qtest_qmp(qts,
> + "{'execute': 'monitor-add',"
> + " 'arguments': {'id': 'dyn-mon',"
> + " 'chardev': 'monitor-chardev'}}");
> + g_assert(qdict_haskey(resp, "return"));
> + qobject_unref(resp);
> +
> + /* Verify it shows up in query-monitors */
> + resp = qtest_qmp(qts, "{'execute': 'query-monitors'}");
> + g_assert(!qdict_haskey(resp, "error"));
> + qobject_unref(resp);
> +
> + /* Error: duplicate id */
> + resp = qtest_qmp(qts,
> + "{'execute': 'monitor-add',"
> + " 'arguments': {'id': 'dyn-mon',"
> + " 'chardev': 'monitor-chardev'}}");
> + qmp_expect_error_and_unref(resp, "GenericError");
> +
> + /* Remove the dynamic monitor */
> + resp = qtest_qmp(qts,
> + "{'execute': 'monitor-remove',"
> + " 'arguments': {'id': 'dyn-mon'}}");
> + g_assert(qdict_haskey(resp, "return"));
> + qobject_unref(resp);
> +
> + /* Error: remove nonexistent */
> + resp = qtest_qmp(qts,
> + "{'execute': 'monitor-remove',"
> + " 'arguments': {'id': 'dyn-mon'}}");
> + qmp_expect_error_and_unref(resp, "GenericError");
> +
> + /* Error: remove CLI monitor (the default one) -- find its id first.
> + * CLI monitors typically have no id, so use a bogus id to test. */
Please check coding style with scripts/checkpatch.pl - I guess it will
complain about the multiline comment here.
Apart from that, the patch looks fine to me. But I think either a qtest or
a functional test should be enough, you don't need both. I'd maybe
recommend to just keep the qtest, since these tests are often run more
frequently than the functional tests.
Thomas
On Sun, Apr 05, 2026 at 06:11:35PM +0000, Thomas Huth wrote:
> Am Thu, 02 Apr 2026 23:19:19 +0200
> schrieb Christian Brauner <brauner@kernel.org>:
>
> > Test the monitor-add, monitor-remove, and query-monitors QMP commands:
> >
> > - Basic lifecycle: chardev-add -> monitor-add -> query-monitors ->
> > monitor-remove -> chardev-remove
> > - Error: duplicate monitor id
> > - Error: monitor-remove on nonexistent id
> > - Error: monitor-add with nonexistent chardev
> > - Re-add after remove: same id and chardev reusable after removal
> >
> > Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> > ---
> > tests/qtest/qmp-test.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 101 insertions(+)
> >
> > diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c
> > index edf0886787..9dbd3a6103 100644
> > --- a/tests/qtest/qmp-test.c
> > +++ b/tests/qtest/qmp-test.c
> > @@ -337,6 +337,105 @@ static void test_qmp_missing_any_arg(void)
> > qtest_quit(qts);
> > }
> >
> > +static void test_qmp_monitor_add_remove(void)
> > +{
> > + QTestState *qts;
> > + QDict *resp;
> > +
> > + qts = qtest_init(common_args);
> > +
> > + /* Create a null chardev for the dynamic monitor */
> > + resp = qtest_qmp(qts,
> > + "{'execute': 'chardev-add',"
> > + " 'arguments': {'id': 'monitor-chardev',"
> > + " 'backend': {'type': 'null',"
> > + " 'data': {}}}}");
> > + g_assert(qdict_haskey(resp, "return"));
> > + qobject_unref(resp);
> > +
> > + /* Add a dynamic monitor */
> > + resp = qtest_qmp(qts,
> > + "{'execute': 'monitor-add',"
> > + " 'arguments': {'id': 'dyn-mon',"
> > + " 'chardev': 'monitor-chardev'}}");
> > + g_assert(qdict_haskey(resp, "return"));
> > + qobject_unref(resp);
> > +
> > + /* Verify it shows up in query-monitors */
> > + resp = qtest_qmp(qts, "{'execute': 'query-monitors'}");
> > + g_assert(!qdict_haskey(resp, "error"));
> > + qobject_unref(resp);
> > +
> > + /* Error: duplicate id */
> > + resp = qtest_qmp(qts,
> > + "{'execute': 'monitor-add',"
> > + " 'arguments': {'id': 'dyn-mon',"
> > + " 'chardev': 'monitor-chardev'}}");
> > + qmp_expect_error_and_unref(resp, "GenericError");
> > +
> > + /* Remove the dynamic monitor */
> > + resp = qtest_qmp(qts,
> > + "{'execute': 'monitor-remove',"
> > + " 'arguments': {'id': 'dyn-mon'}}");
> > + g_assert(qdict_haskey(resp, "return"));
> > + qobject_unref(resp);
> > +
> > + /* Error: remove nonexistent */
> > + resp = qtest_qmp(qts,
> > + "{'execute': 'monitor-remove',"
> > + " 'arguments': {'id': 'dyn-mon'}}");
> > + qmp_expect_error_and_unref(resp, "GenericError");
> > +
> > + /* Error: remove CLI monitor (the default one) -- find its id first.
> > + * CLI monitors typically have no id, so use a bogus id to test. */
>
> Please check coding style with scripts/checkpatch.pl - I guess it will
> complain about the multiline comment here.
Thanks, whatever checkpatch.pl raised should now be fixed.
> Apart from that, the patch looks fine to me. But I think either a qtest or
> a functional test should be enough, you don't need both. I'd maybe
> recommend to just keep the qtest, since these tests are often run more
> frequently than the functional tests.
Hm, I did keep both for v2 and I think whoever applies the changes (if
accepted) can just decide which patch to drop so I'm inclined to just
keep it.
© 2016 - 2026 Red Hat, Inc.