diff mbox series

[v3,2/9] tests/qtest: add support for callback to receive QMP events

Message ID 20230531132400.1129576-3-berrange@redhat.com
State New
Headers show
Series tests/qtest: make migration-test massively faster | expand

Commit Message

Daniel P. Berrangé May 31, 2023, 1:23 p.m. UTC
Currently code must call one of the qtest_qmp_event* functions to
fetch events. These are only usable if the immediate caller knows
the particular event they want to capture, and are only interested
in one specific event type. Adding ability to register an event
callback lets the caller capture a range of events over any period
of time.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 tests/qtest/libqtest.c | 20 ++++++++++++++++++--
 tests/qtest/libqtest.h | 37 +++++++++++++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 4 deletions(-)

Comments

Thomas Huth May 31, 2023, 2:57 p.m. UTC | #1
On 31/05/2023 15.23, Daniel P. Berrangé wrote:
> Currently code must call one of the qtest_qmp_event* functions to
> fetch events. These are only usable if the immediate caller knows
> the particular event they want to capture, and are only interested
> in one specific event type. Adding ability to register an event
> callback lets the caller capture a range of events over any period
> of time.
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   tests/qtest/libqtest.c | 20 ++++++++++++++++++--
>   tests/qtest/libqtest.h | 37 +++++++++++++++++++++++++++++++++++--
>   2 files changed, 53 insertions(+), 4 deletions(-)
> 
> diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
> index 603c26d955..1534177fc1 100644
> --- a/tests/qtest/libqtest.c
> +++ b/tests/qtest/libqtest.c
> @@ -82,6 +82,8 @@ struct QTestState
>       GString *rx;
>       QTestTransportOps ops;
>       GList *pending_events;
> +    QTestQMPEventCallback eventCB;
> +    void *eventData;
>   };
>   
>   static GHookList abrt_hooks;
> @@ -703,8 +705,14 @@ QDict *qtest_qmp_receive(QTestState *s)
>           if (!qdict_get_try_str(response, "event")) {
>               return response;
>           }
> -        /* Stash the event for a later consumption */
> -        s->pending_events = g_list_append(s->pending_events, response);
> +
> +        if (s->eventCB) {
> +            s->eventCB(s, qdict_get_str(response, "event"),
> +                       response, s->eventData);
> +        } else {
> +            /* Stash the event for a later consumption */
> +            s->pending_events = g_list_append(s->pending_events, response);
> +        }
>       }
>   }
>   
> @@ -808,6 +816,14 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
>       va_end(ap);
>   }
>   
> +void qtest_qmp_set_event_callback(QTestState *s,
> +                                  QTestQMPEventCallback cb, void *opaque)
> +{
> +    s->eventCB = cb;
> +    s->eventData = opaque;
> +}
> +
> +

Nit: Use one empty line only instead of two.

Apart from that:
Reviewed-by: Thomas Huth <thuth@redhat.com>
Juan Quintela June 1, 2023, 12:14 p.m. UTC | #2
Daniel P. Berrangé <berrange@redhat.com> wrote:
> Currently code must call one of the qtest_qmp_event* functions to
> fetch events. These are only usable if the immediate caller knows
> the particular event they want to capture, and are only interested
> in one specific event type. Adding ability to register an event
> callback lets the caller capture a range of events over any period
> of time.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>

First of all, I *love* the idea of the patch, but ...

>  static GHookList abrt_hooks;
> @@ -703,8 +705,14 @@ QDict *qtest_qmp_receive(QTestState *s)
>          if (!qdict_get_try_str(response, "event")) {
>              return response;
>          }
> -        /* Stash the event for a later consumption */
> -        s->pending_events = g_list_append(s->pending_events, response);
> +
> +        if (s->eventCB) {
> +            s->eventCB(s, qdict_get_str(response, "event"),
> +                       response, s->eventData);
> +        } else {
> +            /* Stash the event for a later consumption */
> +            s->pending_events = g_list_append(s->pending_events, response);
> +        }
>      }

s->eventCB returns a bool that you are not using.  I think this part of
the code would be more usefule if:

        if (!s->eventCB || !s->eventCB(s, qdict_get_str(response, "event"),
                                       response, s->eventData)) {
            /* Stash the event for a later consumption */
            s->pending_events = g_list_append(s->pending_events, response);
        }

So if we are not handling the event, we put it on the pending_events
list.

What do you think?

Later, Juan.
Daniel P. Berrangé June 1, 2023, 12:56 p.m. UTC | #3
On Thu, Jun 01, 2023 at 02:14:08PM +0200, Juan Quintela wrote:
> Daniel P. Berrangé <berrange@redhat.com> wrote:
> > Currently code must call one of the qtest_qmp_event* functions to
> > fetch events. These are only usable if the immediate caller knows
> > the particular event they want to capture, and are only interested
> > in one specific event type. Adding ability to register an event
> > callback lets the caller capture a range of events over any period
> > of time.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> 
> First of all, I *love* the idea of the patch, but ...
> 
> >  static GHookList abrt_hooks;
> > @@ -703,8 +705,14 @@ QDict *qtest_qmp_receive(QTestState *s)
> >          if (!qdict_get_try_str(response, "event")) {
> >              return response;
> >          }
> > -        /* Stash the event for a later consumption */
> > -        s->pending_events = g_list_append(s->pending_events, response);
> > +
> > +        if (s->eventCB) {
> > +            s->eventCB(s, qdict_get_str(response, "event"),
> > +                       response, s->eventData);
> > +        } else {
> > +            /* Stash the event for a later consumption */
> > +            s->pending_events = g_list_append(s->pending_events, response);
> > +        }
> >      }
> 
> s->eventCB returns a bool that you are not using.  I think this part of
> the code would be more usefule if:
> 
>         if (!s->eventCB || !s->eventCB(s, qdict_get_str(response, "event"),
>                                        response, s->eventData)) {
>             /* Stash the event for a later consumption */
>             s->pending_events = g_list_append(s->pending_events, response);
>         }
> 
> So if we are not handling the event, we put it on the pending_events
> list.
> 
> What do you think?

Sure, it is easy enough to do.


With regards,
Daniel
diff mbox series

Patch

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 603c26d955..1534177fc1 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -82,6 +82,8 @@  struct QTestState
     GString *rx;
     QTestTransportOps ops;
     GList *pending_events;
+    QTestQMPEventCallback eventCB;
+    void *eventData;
 };
 
 static GHookList abrt_hooks;
@@ -703,8 +705,14 @@  QDict *qtest_qmp_receive(QTestState *s)
         if (!qdict_get_try_str(response, "event")) {
             return response;
         }
-        /* Stash the event for a later consumption */
-        s->pending_events = g_list_append(s->pending_events, response);
+
+        if (s->eventCB) {
+            s->eventCB(s, qdict_get_str(response, "event"),
+                       response, s->eventData);
+        } else {
+            /* Stash the event for a later consumption */
+            s->pending_events = g_list_append(s->pending_events, response);
+        }
     }
 }
 
@@ -808,6 +816,14 @@  void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
     va_end(ap);
 }
 
+void qtest_qmp_set_event_callback(QTestState *s,
+                                  QTestQMPEventCallback cb, void *opaque)
+{
+    s->eventCB = cb;
+    s->eventData = opaque;
+}
+
+
 QDict *qtest_qmp_event_ref(QTestState *s, const char *event)
 {
     while (s->pending_events) {
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 41bc6633bd..67ec4db43e 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -238,17 +238,46 @@  QDict *qtest_qmp_receive_dict(QTestState *s);
  * @s: #QTestState instance to operate on.
  *
  * Reads a QMP message from QEMU and returns the response.
- * Buffers all the events received meanwhile, until a
- * call to qtest_qmp_eventwait
+ *
+ * If a callback is registered with qtest_qmp_set_event_callback,
+ * it will be invoked for every event seen, otherwise events
+ * will be buffered until a call to one of the qtest_qmp_eventwait
+ * family of functions.
  */
 QDict *qtest_qmp_receive(QTestState *s);
 
+/*
+ * QTestQMPEventCallback:
+ * @s: #QTestState instance event was received on
+ * @name: name of the event type
+ * @event: #QDict for the event details
+ * @opaque: opaque data from time of callback registration
+ *
+ * This callback will be invoked whenever an event is received
+ */
+typedef bool (*QTestQMPEventCallback)(QTestState *s, const char *name,
+                                      QDict *event, void *opaque);
+
+/**
+ * qtest_qmp_set_event_callback:
+ * @s: #QTestSTate instance to operate on
+ * @cb: callback to invoke for events
+ * @opaque: data to pass to @cb
+ *
+ * Register a callback to be invoked whenever an event arrives
+ */
+void qtest_qmp_set_event_callback(QTestState *s,
+                                  QTestQMPEventCallback cb, void *opaque);
+
 /**
  * qtest_qmp_eventwait:
  * @s: #QTestState instance to operate on.
  * @event: event to wait for.
  *
  * Continuously polls for QMP responses until it receives the desired event.
+ *
+ * Any callback registered with qtest_qmp_set_event_callback will
+ * be invoked for every event seen.
  */
 void qtest_qmp_eventwait(QTestState *s, const char *event);
 
@@ -258,6 +287,10 @@  void qtest_qmp_eventwait(QTestState *s, const char *event);
  * @event: event to wait for.
  *
  * Continuously polls for QMP responses until it receives the desired event.
+ *
+ * Any callback registered with qtest_qmp_set_event_callback will
+ * be invoked for every event seen.
+ *
  * Returns a copy of the event for further investigation.
  */
 QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);