test: Add a spy type that waits for one or more signals
authorManuel Nickschas <sputnick@quassel-irc.org>
Thu, 4 Oct 2018 19:10:13 +0000 (21:10 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 18 Nov 2018 10:06:43 +0000 (11:06 +0100)
Introducing SignalSpy that allows to wait for one or more signals
to be emitted in test cases. Unlike QSignalSpy, it is not bound to
a particular signal on construction; instead, one has to explicitly
connect one or more signal(s) before calling wait().

src/test/util/invocationspy.cpp
src/test/util/invocationspy.h

index 74bb3bd..da9c4f0 100644 (file)
@@ -43,4 +43,16 @@ bool InvocationSpy::wait(std::chrono::milliseconds timeout)
     return result;
 }
 
     return result;
 }
 
+// -----------------------------------------------------------------------------------------------------------------------------------------
+
+bool SignalSpy::wait(std::chrono::milliseconds timeout)
+{
+    bool result = InvocationSpy::wait(timeout);
+    for (auto&& connection : _connections) {
+        QObject::disconnect(connection);
+    }
+    _connections.clear();
+    return result;
+}
+
 }  // namespace test
 }  // namespace test
index 92dd8ec..903fc38 100644 (file)
@@ -55,7 +55,7 @@ public:
      * @param timeout Timeout for waiting
      * @returns true if the spy was notified, and false if it timed out.
      */
      * @param timeout Timeout for waiting
      * @returns true if the spy was notified, and false if it timed out.
      */
-    bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{60});
+    virtual bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{60});
 
 signals:
     /// Internally used signal
 
 signals:
     /// Internally used signal
@@ -65,6 +65,8 @@ private:
     QSignalSpy _internalSpy;
 };
 
     QSignalSpy _internalSpy;
 };
 
+// -----------------------------------------------------------------------------------------------------------------------------------------
+
 /**
  * Spy that allows to be notified with a value.
  *
 /**
  * Spy that allows to be notified with a value.
  *
@@ -100,4 +102,47 @@ private:
     boost::optional<T> _value;
 };
 
     boost::optional<T> _value;
 };
 
+// -----------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Spy that is notified by one (or multiple) signal(s).
+ *
+ * Unlike QSignalSpy, this class is not bound to a particular signal. Instead, SignalSpy::connect() must be called
+ * to define the signal prior to calling wait(). connect() can also be called more than once, in which case any of
+ * the connected signals will trigger the spy.
+ *
+ * Before wait() returns, it disconnects all existing signals, so the spy can be reused without having to explicitly
+ * reset it.
+ */
+class TEST_UTIL_EXPORT SignalSpy : private InvocationSpy
+{
+public:
+    using InvocationSpy::InvocationSpy;
+
+    /**
+     * Connects a signal to wait for.
+     *
+     * @param sender The signal's sender
+     * @param sig The signal
+     */
+    template<typename TSender, typename TSignal>
+    void connect(const TSender* sender, TSignal sig) {
+        _connections.emplace_back(QObject::connect(sender, sig, this, &SignalSpy::notify));
+    }
+
+    /**
+     * Waits for the spy to be notified with any of the connected signals within the given timeout.
+     *
+     * Any connections will be cleared before this function returns. When reusing the spy, one needs
+     * to connect signals before calling wait() again.
+     *
+     * @param timeout Timeout for waiting
+     * @returns true if a signal was received, and false if it timed out.
+     */
+    bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{60}) override;
+
+private:
+    std::vector<QMetaObject::Connection> _connections;
+};
+
 }  // namespace test
 }  // namespace test