test: Add a spy type that waits for one or more signals
[quassel.git] / src / test / util / invocationspy.h
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.
      */
-    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
@@ -65,6 +65,8 @@ private:
     QSignalSpy _internalSpy;
 };
 
+// -----------------------------------------------------------------------------------------------------------------------------------------
+
 /**
  * Spy that allows to be notified with a value.
  *
@@ -100,4 +102,47 @@ private:
     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