@samitouri / QOSamiQemu / commits / c4d8038813

tests/qtest: add tests for dynamic monitor add/remove

Test the object-add/object-del QMP commands with the monitor-qmp object type. - Basic lifecycle: chardev-add -> object-add -> object-del -> chardev-remove - Error: object-add with nonexistent chardev - Error: second monitor on same chardev (chardev already in use) - Removal of CLI-created QMP monitor succeeds - Error: object-remove on HMP monitor - Re-add after remove: same id and chardev reusable after removal Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> [DB: modified to use object-add/object-del, removing redundant scenarios already handled by object-add/del code] Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Tested-by: Peter Krempa <pkrempa@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260706135824.2623960-29-berrange@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>

Christian Brauner committed Jul 6, 2026 at 14:58 UTC c4d803881307de4164fc3732939375d55c00070a
1 file changed +174
tests/qtest/qmp-test.c
+174
@@ -337,6 +337,174 @@ static void test_qmp_missing_any_arg(void)
337 qtest_quit(qts);
338 }
339
340 +static void test_qmp_monitor_add_remove(void)
341 +{
342 + QTestState *qts;
343 + QDict *resp;
344 +
345 + qts = qtest_init(common_args);
346 +
347 + /* Create a null chardev for the dynamic monitor */
348 + resp = qtest_qmp(qts,
349 + "{'execute': 'chardev-add',"
350 + " 'arguments': {'id': 'monitor-chardev',"
351 + " 'backend': {'type': 'null',"
352 + " 'data': {}}}}");
353 + g_assert(qdict_haskey(resp, "return"));
354 + qobject_unref(resp);
355 +
356 + /* Add a dynamic monitor */
357 + resp = qtest_qmp(qts,
358 + "{'execute': 'object-add',"
359 + " 'arguments': {'qom-type': 'monitor-qmp',"
360 + " 'id': 'dyn-mon',"
361 + " 'chardev': 'monitor-chardev'}}");
362 + g_assert(qdict_haskey(resp, "return"));
363 + qobject_unref(resp);
364 +
365 + /* Remove the dynamic monitor */
366 + resp = qtest_qmp(qts,
367 + "{'execute': 'object-del',"
368 + " 'arguments': {'id': 'dyn-mon'}}");
369 + g_assert(qdict_haskey(resp, "return"));
370 + qobject_unref(resp);
371 +
372 + /* Add again after remove -- same id and chardev should work */
373 + resp = qtest_qmp(qts,
374 + "{'execute': 'object-add',"
375 + " 'arguments': {'qom-type': 'monitor-qmp',"
376 + " 'id': 'dyn-mon',"
377 + " 'chardev': 'monitor-chardev'}}");
378 + g_assert(qdict_haskey(resp, "return"));
379 + qobject_unref(resp);
380 +
381 + /* Clean up */
382 + resp = qtest_qmp(qts,
383 + "{'execute': 'object-del',"
384 + " 'arguments': {'id': 'dyn-mon'}}");
385 + g_assert(qdict_haskey(resp, "return"));
386 + qobject_unref(resp);
387 +
388 + resp = qtest_qmp(qts,
389 + "{'execute': 'chardev-remove',"
390 + " 'arguments': {'id': 'monitor-chardev'}}");
391 + g_assert(qdict_haskey(resp, "return"));
392 + qobject_unref(resp);
393 +
394 + qtest_quit(qts);
395 +}
396 +
397 +static void test_qmp_monitor_error_paths(void)
398 +{
399 + QTestState *qts;
400 + QDict *resp;
401 +
402 + qts = qtest_init(common_args);
403 +
404 + /* Error: chardev does not exist */
405 + resp = qtest_qmp(qts,
406 + "{'execute': 'object-add',"
407 + " 'arguments': {'qom-type': 'monitor-qmp',"
408 + " 'id': 'bad-mon',"
409 + " 'chardev': 'nonexistent'}}");
410 + qmp_expect_error_and_unref(resp, "GenericError");
411 +
412 + qtest_quit(qts);
413 +}
414 +
415 +static void test_qmp_monitor_chardev_in_use(void)
416 +{
417 + QTestState *qts;
418 + QDict *resp;
419 +
420 + qts = qtest_init(common_args);
421 +
422 + /* Create a null chardev */
423 + resp = qtest_qmp(qts,
424 + "{'execute': 'chardev-add',"
425 + " 'arguments': {'id': 'shared-chr',"
426 + " 'backend': {'type': 'null',"
427 + " 'data': {}}}}");
428 + g_assert(qdict_haskey(resp, "return"));
429 + qobject_unref(resp);
430 +
431 + /* Attach first monitor */
432 + resp = qtest_qmp(qts,
433 + "{'execute': 'object-add',"
434 + " 'arguments': {'qom-type': 'monitor-qmp',"
435 + " 'id': 'mon-1',"
436 + " 'chardev': 'shared-chr'}}");
437 + g_assert(qdict_haskey(resp, "return"));
438 + qobject_unref(resp);
439 +
440 + /* Error: second monitor on the same chardev */
441 + resp = qtest_qmp(qts,
442 + "{'execute': 'object-add',"
443 + " 'arguments': {'qom-type': 'monitor-qmp',"
444 + " 'id': 'mon-2',"
445 + " 'chardev': 'shared-chr'}}");
446 + qmp_expect_error_and_unref(resp, "GenericError");
447 +
448 + /* Clean up */
449 + resp = qtest_qmp(qts,
450 + "{'execute': 'object-del',"
451 + " 'arguments': {'id': 'mon-1'}}");
452 + g_assert(qdict_haskey(resp, "return"));
453 + qobject_unref(resp);
454 +
455 + resp = qtest_qmp(qts,
456 + "{'execute': 'chardev-remove',"
457 + " 'arguments': {'id': 'shared-chr'}}");
458 + g_assert(qdict_haskey(resp, "return"));
459 + qobject_unref(resp);
460 +
461 + qtest_quit(qts);
462 +}
463 +
464 +static void test_qmp_monitor_remove_cli(void)
465 +{
466 + QTestState *qts;
467 + QDict *resp;
468 +
469 + /* Launch with a named CLI monitor on a null chardev */
470 + qts = qtest_initf("%s -chardev null,id=cli-chr"
471 + " -object monitor-qmp,id=cli-mon,chardev=cli-chr",
472 + common_args);
473 +
474 + /* CLI-created QMP monitors can be removed */
475 + resp = qtest_qmp(qts,
476 + "{'execute': 'object-del',"
477 + " 'arguments': {'id': 'cli-mon'}}");
478 + g_assert(qdict_haskey(resp, "return"));
479 + qobject_unref(resp);
480 +
481 + resp = qtest_qmp(qts,
482 + "{'execute': 'chardev-remove',"
483 + " 'arguments': {'id': 'cli-chr'}}");
484 + g_assert(qdict_haskey(resp, "return"));
485 + qobject_unref(resp);
486 +
487 + qtest_quit(qts);
488 +}
489 +
490 +static void test_qmp_monitor_remove_hmp(void)
491 +{
492 + QTestState *qts;
493 + QDict *resp;
494 +
495 + qts = qtest_initf("%s -chardev null,id=hmp-chr"
496 + " -object monitor-hmp,id=hmp-mon,chardev=hmp-chr",
497 + common_args);
498 +
499 + /* Error: object_del must reject HMP monitors */
500 + resp = qtest_qmp(qts,
501 + "{'execute': 'object-del',"
502 + " 'arguments': {'id': 'hmp-mon'}}");
503 + qmp_expect_error_and_unref(resp, "GenericError");
504 +
505 + qtest_quit(qts);
506 +}
507 +
508 int main(int argc, char *argv[])
509 {
510 g_test_init(&argc, &argv, NULL);
@@ -348,6 +516,12 @@ int main(int argc, char *argv[])
516 #endif
517 qtest_add_func("qmp/preconfig", test_qmp_preconfig);
518 qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg);
519 + qtest_add_func("qmp/monitor-add-remove", test_qmp_monitor_add_remove);
520 + qtest_add_func("qmp/monitor-error-paths", test_qmp_monitor_error_paths);
521 + qtest_add_func("qmp/monitor-chardev-in-use",
522 + test_qmp_monitor_chardev_in_use);
523 + qtest_add_func("qmp/monitor-remove-cli", test_qmp_monitor_remove_cli);
524 + qtest_add_func("qmp/monitor-remove-hmp", test_qmp_monitor_remove_hmp);
525
526 return g_test_run();
527 }