diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a798f352e..5522ca08a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,7 +19,9 @@ else() endif() set(SOURCES_COMMON common.cpp) -set(SOURCES_COMMON_GUI common_gui.cpp) +set(SOURCES_COMMON_GUI + common_gui.cpp + mock_qsettings.cpp) function(add_xtest SOURCE_NAME) add_executable(${SOURCE_NAME} @@ -39,6 +41,7 @@ function(add_xtest_gui SOURCE_NAME) ${SOURCE_NAME}.cpp ${SOURCES_COMMON} ${SOURCES_COMMON_GUI} + ${SRC_SHELL_PLATFORM} ${ARGV1} ${ARGV2}) target_link_libraries(${SOURCE_NAME} ${QTLIBS} ${MSGPACK_LIBRARIES} neovim-qt Qt5::Widgets neovim-qt-gui) add_test(NAME ${SOURCE_NAME} COMMAND ${SOURCE_NAME} ${CTEST_EXE_ARGS} @@ -59,11 +62,9 @@ add_xtest(tst_neovimconnector) add_xtest(tst_callallmethods) add_xtest(tst_encoding) add_xtest(tst_msgpackiodevice) -add_xtest_gui(tst_shell ${SRC_SHELL_PLATFORM}) +add_xtest_gui(tst_shell) add_xtest_gui(tst_main) -add_xtest_gui(tst_qsettings - ${SRC_SHELL_PLATFORM} - mock_qsettings.cpp) +add_xtest_gui(tst_qsettings) # Platform Specific Input Tests add_xtest(tst_input_mac diff --git a/test/common_gui.cpp b/test/common_gui.cpp index a85718c3b..2f60a4a5f 100644 --- a/test/common_gui.cpp +++ b/test/common_gui.cpp @@ -1,12 +1,30 @@ #include "common_gui.h" #include +#include #include "common.h" namespace NeovimQt { -static const QStringList cs_argsNone{ "-u", "NONE" }; // clazy:exclude=non-pod-global-static +static QStringList GetNoneArguments() noexcept +{ + static const QStringList cs_argsNone{ "-u", "NONE" }; + return cs_argsNone; +} + +static QStringList GetNoneArgumentsWithRuntime() noexcept +{ + static const QStringList cs_argsNoneRuntime{ + "-u", "NONE", "--cmd", "set rtp+=" + GetRuntimeAbsolutePath() + }; + return cs_argsNoneRuntime; +} + +static void BypassLocalAndLoadTestGviminit() noexcept +{ + qputenv("GVIMINIT", "let g:loaded_test_gviminit = 1"); +} static void DisableLocalGInitVim() noexcept { @@ -37,8 +55,9 @@ template static void ValidateNeovimConnection(T* obj) noexcept std::pair CreateShellWidget() noexcept { - DisableLocalGInitVim(); - NeovimConnector* c{ NeovimConnector::spawn(cs_argsNone) }; + BypassLocalAndLoadTestGviminit(); + + NeovimConnector* c{ NeovimConnector::spawn(GetNoneArguments()) }; Shell* s{ new Shell{ c } }; s->show(); @@ -50,7 +69,9 @@ std::pair CreateShellWidget() noexcept std::pair CreateMainWindow() noexcept { - NeovimConnector* c{ NeovimConnector::spawn(cs_argsNone) }; + BypassLocalAndLoadTestGviminit(); + + NeovimConnector* c{ NeovimConnector::spawn(GetNoneArguments()) }; MainWindow* w{ new MainWindow{ c } }; w->show(); @@ -62,12 +83,10 @@ std::pair CreateMainWindow() noexcept std::pair CreateMainWindowWithRuntime() noexcept { - static const QStringList cs_argsNoneRuntime{ - "-u", "NONE", "--cmd", "set rtp+=" + GetRuntimeAbsolutePath() - }; + BypassLocalAndLoadTestGviminit(); DisableLocalGInitVim(); - NeovimConnector* c{ NeovimConnector::spawn(cs_argsNoneRuntime) }; + NeovimConnector* c{ NeovimConnector::spawn(GetNoneArgumentsWithRuntime()) }; MainWindow* w{ new MainWindow{ c } }; w->show(); @@ -77,4 +96,17 @@ std::pair CreateMainWindowWithRuntime() noexcept return { c, w }; } +void LoadLocalDejaVuTestFonts() noexcept +{ + const QStringList fonts{ QStringLiteral("third-party/DejaVuSansMono.ttf"), + QStringLiteral("third-party/DejaVuSansMono-Bold.ttf"), + QStringLiteral("third-party/DejaVuSansMono-BoldOblique.ttf") }; + + for (const auto& path : fonts) { + QString abs_path_to_font(CMAKE_SOURCE_DIR); + abs_path_to_font.append("/").append(path); + QFontDatabase::addApplicationFont(abs_path_to_font); + } +} + } // namespace NeovimQt diff --git a/test/common_gui.h b/test/common_gui.h index 4c981a013..6e4b5b0ae 100644 --- a/test/common_gui.h +++ b/test/common_gui.h @@ -13,4 +13,6 @@ std::pair CreateMainWindow() noexcept; std::pair CreateMainWindowWithRuntime() noexcept; +void LoadLocalDejaVuTestFonts() noexcept; + } // namespace NeovimQt diff --git a/test/mock_qsettings.cpp b/test/mock_qsettings.cpp index 4d8880b45..3dcd0f4de 100644 --- a/test/mock_qsettings.cpp +++ b/test/mock_qsettings.cpp @@ -30,12 +30,8 @@ void EnableByDefault() noexcept void ClearAllContents() noexcept { - s_mockSettingsMap = {}; -} - -void OverwriteContents(QSettings::SettingsMap newValue) noexcept -{ - s_mockSettingsMap = std::move(newValue); + QSettings settings; + settings.clear(); } }} // namespace NeovimQt::MockQSettings diff --git a/test/mock_qsettings.h b/test/mock_qsettings.h index 909d70b63..64e86c797 100644 --- a/test/mock_qsettings.h +++ b/test/mock_qsettings.h @@ -10,7 +10,4 @@ void EnableByDefault() noexcept; /// Clears all contents store in MockQSettings void ClearAllContents() noexcept; -/// Overwrites call contents stored in MockQSettings with newValue -void OverwriteContents(QSettings::SettingsMap newValue) noexcept; - }} // namespace NeovimQt::MockQSettings diff --git a/test/tst_qsettings.cpp b/test/tst_qsettings.cpp index a9912a198..4ed6372c9 100644 --- a/test/tst_qsettings.cpp +++ b/test/tst_qsettings.cpp @@ -37,6 +37,7 @@ static void SendNeovimCommand(NeovimConnector& connector, const QString& command void TestQSettings::initTestCase() noexcept { NeovimQt::MockQSettings::EnableByDefault(); + LoadLocalDejaVuTestFonts(); } void TestQSettings::cleanup() noexcept diff --git a/test/tst_shell.cpp b/test/tst_shell.cpp index 83a6d651a..5ff2bdf39 100644 --- a/test/tst_shell.cpp +++ b/test/tst_shell.cpp @@ -3,13 +3,13 @@ #include #include #include -#include #include #include #include #include "common.h" #include "common_gui.h" +#include "mock_qsettings.h" #if defined(Q_OS_WIN) && defined(USE_STATIC_QT) #include @@ -28,7 +28,8 @@ private slots: void startVarsShellWidget() noexcept; void startVarsMainWindow() noexcept; void gviminit() noexcept; - void guiShimCommands() noexcept; + void GuiLinespaceCommand() noexcept; + void GuiFontCommand() noexcept; void CloseEvent_data() noexcept; void CloseEvent() noexcept; void GetClipboard_data() noexcept; @@ -48,16 +49,9 @@ static void SignalPrintError(QString msg, const QVariant& err) noexcept void TestShell::initTestCase() noexcept { - const QStringList fonts{ - QStringLiteral("third-party/DejaVuSansMono.ttf"), - QStringLiteral("third-party/DejaVuSansMono-Bold.ttf"), - QStringLiteral("third-party/DejaVuSansMono-BoldOblique.ttf") }; - - for (const auto& path : fonts) { - QString abs_path_to_font(CMAKE_SOURCE_DIR); - abs_path_to_font.append("/").append(path); - QFontDatabase::addApplicationFont(abs_path_to_font); - } + NeovimQt::MockQSettings::EnableByDefault(); + + LoadLocalDejaVuTestFonts(); } void TestShell::benchStart() noexcept @@ -91,17 +85,19 @@ void TestShell::startVarsMainWindow() noexcept void TestShell::gviminit() noexcept { - qputenv("GVIMINIT", "let g:test_gviminit = 1"); + // NOTE: inside CreateShellWidget GVIMINIT is set and g:loaded_test_gviminit is set to "1". NeovimConnector* c{ CreateShellWidget().first }; - MsgpackRequest* req{ c->api0()->vim_command_output(c->encode("echo g:test_gviminit")) }; + MsgpackRequest* req{ c->api0()->vim_command_output(c->encode("echo g:loaded_test_gviminit")) }; QSignalSpy cmd{ req, &MsgpackRequest::finished }; QVERIFY(cmd.isValid()); QVERIFY(SPYWAIT(cmd)); - QCOMPARE(cmd.at(0).at(2).toByteArray(), QByteArray("1")); + + const QByteArray varObserved{ cmd.at(0).at(2).toByteArray() }; + QCOMPARE(varObserved, QByteArray{ "1" }); } -void TestShell::guiShimCommands() noexcept +void TestShell::GuiLinespaceCommand() noexcept { auto cw{ CreateMainWindowWithRuntime() }; NeovimConnector* c{ cw.first }; @@ -109,49 +105,78 @@ void TestShell::guiShimCommands() noexcept QObject::connect(c->neovimObject(), &NeovimApi1::err_vim_command_output, SignalPrintError); - QSignalSpy cmd_font( - c->neovimObject()->vim_command_output(c->encode("GuiFont!")), &MsgpackRequest::finished); - QVERIFY(cmd_font.isValid()); - QVERIFY2(SPYWAIT(cmd_font), "Waiting for GuiFont"); + QSignalSpy cmd_ls{ c->neovimObject()->vim_command_output(c->encode("GuiLinespace")), + &MsgpackRequest::finished }; - QSignalSpy cmd_ls( - c->neovimObject()->vim_command_output(c->encode("GuiLinespace")), - &MsgpackRequest::finished); QVERIFY(cmd_ls.isValid()); QVERIFY2(SPYWAIT(cmd_ls), "Waiting for GuiLinespace"); + QSignalSpy spyFontChanged{ w->shell(), &ShellWidget::shellFontChanged }; + QSignalSpy spyLineSpace2{ c->neovimObject()->vim_command_output(c->encode("GuiLinespace 2")), + &MsgpackRequest::finished }; + + QVERIFY(spyFontChanged.isValid()); + QVERIFY(SPYWAIT(spyFontChanged)); + + QSignalSpy spyLineSpaceValue{ c->neovimObject()->vim_command_output(c->encode("GuiLinespace")), + &MsgpackRequest::finished }; + + QVERIFY(spyLineSpaceValue.isValid()); + QVERIFY(SPYWAIT(spyLineSpaceValue)); + + QByteArray varObserved{ spyLineSpaceValue.at(0).at(2).toByteArray() }; + QCOMPARE(varObserved, QByteArray{ "2" }); +} + +void TestShell::GuiFontCommand() noexcept +{ + auto cw{ CreateMainWindowWithRuntime() }; + NeovimConnector* c{ cw.first }; + MainWindow* w{ cw.second }; + + QObject::connect(c->neovimObject(), &NeovimApi1::err_vim_command_output, SignalPrintError); + + QSignalSpy spyGuiFontNoArgs{ c->neovimObject()->vim_command_output(c->encode("GuiFont")), + &MsgpackRequest::finished }; + QVERIFY(spyGuiFontNoArgs.isValid()); + QVERIFY2(SPYWAIT(spyGuiFontNoArgs), "Waiting for GuiFont"); + // Test font attributes const QString cmdFontSize14{ QStringLiteral("GuiFont! %1:h14").arg(GetPlatformTestFont()) }; const QString expectedFontSize14{ QStringLiteral("%1:h14").arg(GetPlatformTestFont()) }; - QSignalSpy cmd_gf{ c->neovimObject()->vim_command_output(c->encode(cmdFontSize14)), - &MsgpackRequest::finished }; - QVERIFY(cmd_gf.isValid()); - QVERIFY(SPYWAIT(cmd_gf)); - QSignalSpy spy_fontchange(w->shell(), &ShellWidget::shellFontChanged); + // Issue#977: concurrency issue inside of Shell::updateGuiFontRegisters() for guifont. + // A call to :GuiFont triggers a call update guifont=. If a second call to :GuiFont is made + // while the guifont= callback is still outstanding, the guifont value is mangled. This can be + // observed if the test delay below is removed. + QTest::qWait(500); - // Test Performance: timeout occurs often, set value carefully. - SPYWAIT(spy_fontchange, 2500 /*msec*/); + QSignalSpy spyGuiFontSize14{ c->neovimObject()->vim_command_output(c->encode(cmdFontSize14)), + &MsgpackRequest::finished }; + QSignalSpy spyFontChangedSize14{ w->shell(), &ShellWidget::shellFontChanged }; + QVERIFY(spyGuiFontSize14.isValid()); + QVERIFY(SPYWAIT(spyGuiFontSize14)); + QVERIFY(SPYWAIT(spyFontChangedSize14)); QCOMPARE(w->shell()->fontDesc(), expectedFontSize14); // Normalization removes the :b attribute const QString cmdFontBoldRemoved{ QStringLiteral("GuiFont! %1:h16:b:l").arg(GetPlatformTestFont()) }; - const QString expectedFontBoldRemoved{ QStringLiteral("%1:h16:l").arg(GetPlatformTestFont()) }; - QSignalSpy spy_fontchange2(w->shell(), &ShellWidget::shellFontChanged); - QSignalSpy cmd_gf2{ c->neovimObject()->vim_command_output(c->encode(cmdFontBoldRemoved)), - &MsgpackRequest::finished }; - QVERIFY(cmd_gf2.isValid()); - QVERIFY(SPYWAIT(cmd_gf2, 5000)); - // Test Performance: timeout occurs often, set value carefully. - SPYWAIT(spy_fontchange2, 5000 /*msec*/); + const QString expectedFontBoldRemoved{ QStringLiteral("%1:h14:l").arg(GetPlatformTestFont()) }; + QSignalSpy spyFontConflictingArgs{ c->neovimObject()->vim_command_output( + c->encode(cmdFontBoldRemoved)), + &MsgpackRequest::finished }; + QSignalSpy spyFontChangedBoldRemoved(w->shell(), &ShellWidget::shellFontChanged); + + QVERIFY(spyFontConflictingArgs.isValid()); + QVERIFY(SPYWAIT(spyFontConflictingArgs)); + QVERIFY(SPYWAIT(spyFontChangedBoldRemoved)); QCOMPARE(w->shell()->fontDesc(), expectedFontBoldRemoved); } - void TestShell::CloseEvent_data() noexcept { QTest::addColumn("msgpack_status");