gpt4 book ai didi

c++ - Qt插槽未由QTest环境中std::async发出的信号触发

转载 作者:行者123 更新时间:2023-12-02 10:19:28 24 4
gpt4 key购买 nike

我正在研究使用Qt进行异步编程的主题,得出的结论是,从任何类型的线程(尽管QT文档仅提及QThread)发出信号都是安全的,正如here所描述的那样。现在我面临测试我的应用程序的问题。为了尽可能简化:我有一个异步操作,它可以通过发出SIGNAL来通知MainWindow。它在生产中可以正常工作,但不能在QTest的单元测试环境中使用。完整示例(项目结构平坦,无子目录):
CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(QtFailure)

enable_testing()
set(CMAKE_CXX_STANDARD 14)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
if(CMAKE_VERSION VERSION_LESS "3.7.0")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
endif()
find_package(Qt5 COMPONENTS Core Widgets Test REQUIRED)

add_library(QtFailure source.cpp header.hpp)
target_include_directories(QtFailure PUBLIC .)
target_link_libraries(QtFailure
pthread
Qt5::Core
Qt5::Widgets
)

add_executable(main main.cpp)
target_link_libraries(main QtFailure)

add_executable(QtFailureTest test.cpp)
target_link_libraries(QtFailureTest
QtFailure
Qt5::Test
)
header.hpp
#pragma once

#include <QMainWindow>
#include <QWidget>

#include <future>

class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow();
void start();
int counter_value();

signals:
void sendSignal();

private slots:
bool triggerSlot();

private:
bool stop_;
std::future<void> async_oper_;
};
source.cpp

#include "header.hpp"

#include <QMainWindow>
#include <QWidget>
#include <QObject>
#include <QDebug>

#include <future>
#include <chrono>
#include <thread>

static int counter = 0;

MainWindow::MainWindow(QWidget* parent):
QMainWindow(parent),
stop_(false),
async_oper_()
{
QObject::connect(this, SIGNAL(sendSignal()), this, SLOT(triggerSlot()));
}

MainWindow::~MainWindow()
{
stop_ = true;
}

int MainWindow::counter_value()
{
return counter;
}

void MainWindow::start()
{
if (async_oper_.valid()) return;
emit sendSignal(); // this one works
async_oper_ = std::async(std::launch::async, [this]()
{
while (!stop_)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
emit sendSignal(); // this one doesn't work in tests
}
});
}

bool MainWindow::triggerSlot()
{
qDebug() << "triggerSlot: " << counter;
counter++;
}
test.cpp
#include "header.hpp"

#include <QSignalSpy>
#include <QDebug>
#include <QtTest/QtTest>

#include <memory>
#include <chrono>
#include <thread>

class MyFixture: public QObject
{
Q_OBJECT
private:
std::unique_ptr<MainWindow> sut_;

private slots:
void init()
{
qDebug("MyFixture init");
sut_.reset(new MainWindow);
}
void cleanup()
{
qDebug("MyFixture cleanup");
sut_.reset();
}
void example_test()
{
QSignalSpy spy(sut_.get(), SIGNAL(sendSignal()));
sut_->start();
std::this_thread::sleep_for(std::chrono::seconds(1));
qDebug() << "num signals: " << spy.count();
qDebug() << "counter value: " << sut_->counter_value();
}
};

QTEST_MAIN(MyFixture)
#include "test.moc"
main.cpp

#include <QApplication>

#include "header.hpp"

int main(int argc, char** argv)
{
QApplication a(argc, argv);
MainWindow w;
w.start();
w.show();
return a.exec();
}

我测试的输出是
PASS   : MyFixture::initTestCase()
QDEBUG : MyFixture::example_test() MyFixture init
QDEBUG : MyFixture::example_test() triggerSlot: 0
QDEBUG : MyFixture::example_test() num signals: 10
QDEBUG : MyFixture::example_test() counter value: 1
QDEBUG : MyFixture::example_test() MyFixture cleanup
PASS : MyFixture::example_test()
PASS : MyFixture::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 1003ms

这意味着该插槽仅由主线程发出的信号触发一次。

main的输出是:
...
triggerSlot: 25
triggerSlot: 26
triggerSlot: 27
etc ...

这是预期的行为。为什么QTest环境和main中的普通QApplication有区别?我该怎么做才能纠正测试行为?

我必须使用标准的C++线程,因为我的GUI只是真正的非Qt相关系统的外观,该系统具有不同种类的异步操作,回调等。

很抱歉,代码量很大,但是使用QT时,我无法真正将其压缩成几行。

===编辑===

指定 Qt::DirectConnection连接属性将“修复” QTest环境中的问题。但这是我在大多数情况下无法做的事情,因为GUI操作必须在主线程中进行(例如scync oper发出信号来触发QPixmap刷新);

最佳答案

信号发射代码正确。排队的信号插槽连接需要event loop to be running in the receiver thread for signal delivery:

Events to that object are dispatched by that (receiver) thread's event loop.



接收器线程的事件循环无法正常工作,因为您将其阻塞在行中
// just suspends current thread without running any event loop
std::this_thread::sleep_for(std::chrono::seconds(1));

也就是说,接收器线程的事件循环在整个 example_test()方法主体中都处于阻塞状态。没有一行在其内部运行事件循环。 不是Qt测试或QTEST_MAIN()问题。

解决方法如下:
void example_test()
{
QSignalSpy spy(sut_.get(), SIGNAL(sendSignal()));
sut_->start();

QElapsedTimer timer;
timer.start();

while(timer.elapsed() < 1000)
{
spy.wait(10); // event loop runs here
}

qDebug() << "num signals: " << spy.count();
qDebug() << "counter value: " << sut_->counter_value();
}

QSignalSpy::wait():

Starts an event loop that runs until the given signal is received. Optionally the event loop can return earlier on a timeout (in milliseconds).

Returns true if the signal was emitted at least once in timeout milliseconds, otherwise returns false.

关于c++ - Qt插槽未由QTest环境中std::async发出的信号触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60865413/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com