Mixing QT with Google Test
-
Hey guys,
I'm using QT with google test.
Some of my functions to test involve signals and slots (like Bluetooth in my case).
However teese do no seam to work in GTESTThe basic setup
My class QT_BT_ExampleScan should perform a scan for available bluetooth devices in the background => that's why it's running in it's own thread.
class QT_BT_ExampleScan : public QThread { Q_OBJECT public: QT_BT_ExampleScan (); private: std::unique_ptr<QBluetoothDeviceDiscoveryAgent> m_pDeviceDiscoveryAgent; void run() override; public slots: void addDevice(const QBluetoothDeviceInfo&); }; QT_BT_ExampleScan ::QT_BT_ExampleScan (); { m_pDeviceDiscoveryAgent = std::make_unique<QBluetoothDeviceDiscoveryAgent>(); if (!connect(m_pDeviceDiscoveryAgent.get(), SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this, SLOT(addDevice(QBluetoothDeviceInfo)))) { qWarning() << "Failed to connect"; } } void QT_BT_ExampleScan ::run() { startDeviceDiscovery(); int iRes = exec(); qDebug() << "Thread " << currentThreadId() << " finnished with result " << iRes; } void QT_BT_ExampleScan::addDevice(const QBluetoothDeviceInfo& info) { qDebug() << "Adding device " << info.name(); QBluetoothLocalDevice::Pairing pairingStatus = m_pLocalBluetoothDevice->pairingStatus(info.address()); if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == QBluetoothLocalDevice::AuthorizedPaired) { qDebug() << "Device with address" << info.address() << "already paired"; } else { qDebug() << "Device with address " << info.address() << "not yet paired"; } }
1) In the GTest case it is called via
int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); QGuiApplication app(argc, argv); int res = RUN_ALL_TESTS(); return res; }
with fixture
class TestBluetooth : public ::testing::Test { public: }; TEST_F(TestBluetooth, BT_QT_Example) { auto pThread = std::make_unique<QT_BT_ExampleScan>(); pThread->start(); const auto sleepTime = 20s; std::this_thread::sleep_for(sleepTime); }
However in this case, no QT events are fired/received.
2) Modification of the main()
int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); QGuiApplication app(argc, argv); QT_BT_ExampleScan test; test.start(); return app.exec(); }
This here fires up events nicely. However Gtest is not running any longer.
Problem assumption for 1) case
=>for signals/slots the eventloop must be running. This only seams to be triggers by
QGuiApplication app(argc, argv); app.exec()
=>My Idea how to solve this:
=> Keep calling app.exec() but move GTest to a separate thread.
int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); QGuiApplication app(argc, argv); auto pGTestThread = std::make_unique<GTestThread>(); return app.exec(); } class GTestThread : protected QThread { public: explicit GTestThread(); ~GTestThread(); private: void run() override; }; GTestThread::GTestThread() { start(); } GTestThread::~GTestThread() { if (isRunning()) { exit(0); wait(); } } void GTestThread::run() { RUN_ALL_TESTS(); }
However this does not work either. GTest is running but no signals/slots are working again.
Do you know why? The eventloop should be up by the end of main(). Why are no events fired?could you help me out here?
-
I got that running with the following :
// Test fixture for setting up Qt event loop class QtTest : public ::testing::Test { protected: void SetUp() override { int argc = 0; char **argv = nullptr; app = new QCoreApplication(argc, argv); } void TearDown() override { delete app; } QCoreApplication *app; }; TEST_F(QtTest, SignalSlotTest) { MyClass sender; MyClass receiver; QObject::connect(&sender, &MyClass::mySignal, &receiver, &MyClass::mySlot); int testValue = 42; emit sender.mySignal(testValue); // Process Qt events to ensure signal delivery QCoreApplication::processEvents(); EXPECT_EQ(receiver.lastValue(), testValue); }
with test class MyClass.h :
class MyClass : public QObject { Q_OBJECT public: explicit MyClass(QObject *parent = nullptr); signals: void mySignal(int value); public slots: void mySlot(int value); int lastValue() const; private: int m_lastValue; };
MyClass.cpp :
MyClass::MyClass(QObject *parent) : QObject(parent), m_lastValue(0) { } void MyClass::mySlot(int value) { m_lastValue = value; } int MyClass::lastValue() const { return m_lastValue; }
-
Or simply use qWait/qWaitFor() instead QCoreApplication::processEvents()