File indexing completed on 2024-10-13 12:19:20
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2007-2009 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kxmlgui_unittest.h" 0009 #include "testguiclient.h" 0010 #include "testxmlguiwindow.h" 0011 0012 #include <QAction> 0013 #include <QDebug> 0014 #include <QDialogButtonBox> 0015 #include <QDir> 0016 #include <QHBoxLayout> 0017 #include <QMenuBar> 0018 #include <QPushButton> 0019 #include <QShowEvent> 0020 #include <QTest> 0021 #include <QWidget> 0022 0023 #include <KConfigGroup> 0024 #include <KSharedConfig> 0025 0026 #include <kedittoolbar.h> 0027 #include <kswitchlanguagedialog_p.h> 0028 #include <kxmlguibuilder.h> 0029 #include <kxmlguiclient.h> 0030 #include <kxmlguiversionhandler.cpp> // it's not exported, so we need to include the code here 0031 0032 QTEST_MAIN(KXmlGui_UnitTest) 0033 0034 enum Flags { 0035 NoFlags = 0, 0036 AddToolBars = 1, 0037 AddModifiedToolBars = 2, 0038 AddActionProperties = 4, 0039 AddModifiedMenus = 8, 0040 // next item is 16 0041 }; 0042 0043 static void createXmlFile(QFile &file, int version, int flags, const QByteArray &toplevelTag = "gui") 0044 { 0045 const QByteArray xml = 0046 "<?xml version = '1.0'?>\n" 0047 "<!DOCTYPE " + toplevelTag + " SYSTEM \"kpartgui.dtd\">\n" 0048 "<" + toplevelTag + " version=\"" + QByteArray::number(version) + "\" name=\"foo\" >\n" 0049 "<MenuBar>\n"; 0050 file.write(xml); 0051 if (flags & AddModifiedMenus) { 0052 file.write( 0053 "<Menu noMerge=\"1\" name=\"file\" >\n" 0054 "<text>&File</text>\n" 0055 "<Action name=\"file_open\" />\n" 0056 "</Menu>\n"); 0057 } 0058 file.write("</MenuBar>\n"); 0059 if (flags & AddToolBars) { 0060 file.write( 0061 "<ToolBar name=\"mainToolBar\">\n" 0062 " <text>Main Toolbar</text>\n" 0063 " <Action name=\"print\" />\n" 0064 "</ToolBar>\n" 0065 "<ToolBar name=\"bookmarkToolBar\">\n" 0066 " <text>Bookmark Toolbar</text>\n" 0067 "</ToolBar>\n" 0068 "<ToolBar name=\"newToolBar\">\n" 0069 " <text>New Toolbar</text>\n" 0070 "</ToolBar>\n"); 0071 } 0072 if (flags & AddModifiedToolBars) { 0073 file.write( 0074 "<ToolBar name=\"mainToolBar\">\n" 0075 " <text>Main Toolbar</text>\n" 0076 " <Action name=\"home\" />\n" 0077 "</ToolBar>\n" 0078 "<ToolBar name=\"bookmarkToolBar\">\n" 0079 " <text>Modified toolbars</text>\n" 0080 "</ToolBar>\n"); 0081 } 0082 if (flags & AddActionProperties) { 0083 file.write( 0084 "<ActionProperties>\n" 0085 " <Action shortcut=\"F9\" name=\"konq_sidebartng\" />\n" 0086 "</ActionProperties>\n"); 0087 } 0088 file.write("</" + toplevelTag + ">\n"); 0089 } 0090 0091 static void clickApply(KEditToolBar *dialog) 0092 { 0093 QDialogButtonBox *box = dialog->findChild<QDialogButtonBox *>(); 0094 Q_ASSERT(box != nullptr); 0095 box->button(QDialogButtonBox::Apply)->setEnabled(true); 0096 box->button(QDialogButtonBox::Apply)->click(); 0097 } 0098 0099 void KXmlGui_UnitTest::initTestCase() 0100 { 0101 QStandardPaths::setTestModeEnabled(true); 0102 // Leftover configuration breaks testAutoSaveSettings 0103 const QString configFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, KSharedConfig::openConfig()->name()); 0104 if (!configFile.isEmpty()) { 0105 qDebug() << "Removing old config file"; 0106 QFile::remove(configFile); 0107 KSharedConfig::openConfig()->reparseConfiguration(); 0108 } 0109 } 0110 0111 void KXmlGui_UnitTest::testFindVersionNumber_data() 0112 { 0113 QTest::addColumn<QString>("xml"); 0114 QTest::addColumn<QString>("version"); 0115 0116 QTest::newRow("simple test") << "<?xml version = '1.0'?>\n" 0117 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0118 "<gui version=\"3\" name=\"foo\"/>\n" 0119 << "3"; 0120 QTest::newRow("two digits") << "<?xml version = '1.0'?>\n" 0121 "<gui version=\"42\" name=\"foo\"/>\n" 0122 << "42"; 0123 QTest::newRow("with spaces") << // as found in dirfilterplugin.rc for instance 0124 "<?xml version = '1.0'?>\n" 0125 "<gui version = \"1\" name=\"foo\"/>\n" 0126 << "1"; 0127 QTest::newRow("with a dot") << // as was found in autorefresh.rc 0128 "<?xml version = '1.0'?>\n" 0129 "<gui version = \"0.2\" name=\"foo\"/>\n" 0130 << QString() /*error*/; 0131 QTest::newRow("with a comment") << // as found in kmail.rc 0132 "<!DOCTYPE gui>\n" 0133 "<!-- This file should be synchronized with kmail_part.rc to provide\n" 0134 "the same menu entries at the same place in KMail and Kontact -->\n" 0135 "<gui version=\"452\" name=\"kmmainwin\">\n" 0136 << "452"; 0137 } 0138 0139 void KXmlGui_UnitTest::testFindVersionNumber() 0140 { 0141 QFETCH(QString, xml); 0142 QFETCH(QString, version); 0143 QCOMPARE(KXMLGUIClient::findVersionNumber(xml), version); 0144 } 0145 0146 void KXmlGui_UnitTest::testVersionHandlerSameVersion() 0147 { 0148 // This emulates the case where the user has modified stuff locally 0149 // and the application hasn't changed since, so the version number is unchanged. 0150 QTemporaryFile userFile; 0151 QVERIFY(userFile.open()); 0152 createXmlFile(userFile, 2, AddActionProperties | AddModifiedToolBars); 0153 const QString firstFile = userFile.fileName(); 0154 0155 QTemporaryFile appFile; 0156 QVERIFY(appFile.open()); 0157 createXmlFile(appFile, 2, AddToolBars); 0158 const QString secondFile = appFile.fileName(); 0159 0160 QStringList files; 0161 files << firstFile << secondFile; 0162 0163 userFile.close(); 0164 appFile.close(); 0165 0166 KXmlGuiVersionHandler versionHandler(files); 0167 QCOMPARE(versionHandler.finalFile(), firstFile); 0168 QString finalDoc = versionHandler.finalDocument(); 0169 QVERIFY(finalDoc.startsWith(QLatin1String("<?xml"))); 0170 // Check that the shortcuts defined by the user were kept 0171 QVERIFY(finalDoc.contains(QLatin1String("<ActionProperties>"))); 0172 QVERIFY(finalDoc.contains(QLatin1String("sidebartng"))); 0173 // Check that the toolbars modified by the user were kept 0174 QVERIFY(finalDoc.contains(QLatin1String("<Action name=\"home\""))); 0175 // and that the toolbar that isn't in the local file, isn't there in the GUI 0176 QVERIFY(!finalDoc.contains(QLatin1String("<ToolBar name=\"newToolBar\""))); 0177 0178 QVERIFY(userFile.open()); 0179 const QString userFileContents = QString::fromUtf8(userFile.readAll()); 0180 QCOMPARE(finalDoc, userFileContents); 0181 } 0182 0183 void KXmlGui_UnitTest::testVersionHandlerNewVersionNothingKept() 0184 { 0185 // This emulates the case where the application has been upgraded 0186 // and the user has a local ui.rc file, but without shortcuts or toolbar changes. 0187 // Not sure how this can happen - would be a menu-only app and hand editing !? 0188 // Anyway the point is to test version number comparison :) 0189 0190 QMap<QString, int> fileToVersionMap; // makes QCOMPARE failures more readable than just temp filenames 0191 0192 const QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui_unittest"); 0193 QDir().mkpath(dir); 0194 QFile fileV2(dir + QStringLiteral("/testui.rc")); 0195 QVERIFY2(fileV2.open(QIODevice::WriteOnly), qPrintable(fileV2.fileName())); 0196 createXmlFile(fileV2, 2, NoFlags); 0197 fileToVersionMap.insert(fileV2.fileName(), 2); 0198 0199 QTemporaryFile fileV5; 0200 QVERIFY(fileV5.open()); 0201 createXmlFile(fileV5, 5, NoFlags); 0202 fileToVersionMap.insert(fileV5.fileName(), 5); 0203 0204 // The highest version is neither the first nor last one in the list, 0205 // to make sure the code really selects the highest version, not just by chance :) 0206 // (This is why we add the v1 version at the end of the list) 0207 QTemporaryFile fileV1; 0208 QVERIFY(fileV1.open()); 0209 createXmlFile(fileV1, 1, NoFlags); 0210 fileToVersionMap.insert(fileV1.fileName(), 1); 0211 0212 QStringList files; 0213 files << fileV2.fileName() << fileV5.fileName() << fileV1.fileName(); 0214 0215 fileV2.close(); 0216 fileV5.close(); 0217 fileV1.close(); 0218 0219 KXmlGuiVersionHandler versionHandler(files); 0220 QCOMPARE(fileToVersionMap.value(versionHandler.finalFile()), 5); 0221 QString finalDoc = versionHandler.finalDocument(); 0222 QVERIFY(finalDoc.startsWith(QLatin1String("<?xml"))); 0223 QVERIFY(finalDoc.contains(QLatin1String("version=\"5\""))); 0224 0225 QVERIFY(fileV5.open()); 0226 const QString fileV5Contents = QString::fromUtf8(fileV5.readAll()); 0227 QCOMPARE(finalDoc, fileV5Contents); 0228 } 0229 0230 void KXmlGui_UnitTest::testVersionHandlerNewVersionUserChanges() 0231 { 0232 // This emulates the case where the application has been upgraded 0233 // after the user has changed shortcuts and toolbars 0234 0235 QMap<QString, int> fileToVersionMap; // makes QCOMPARE failures more readable than just temp filenames 0236 0237 // local file 0238 const QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui_unittest"); 0239 QFile fileV2(dir + QLatin1String("/testui.rc")); 0240 QVERIFY(fileV2.open(QIODevice::WriteOnly)); 0241 createXmlFile(fileV2, 2, AddActionProperties | AddModifiedToolBars); 0242 fileToVersionMap.insert(fileV2.fileName(), 2); 0243 0244 // more-global (application) file 0245 QTemporaryFile fileV5; 0246 QVERIFY(fileV5.open()); 0247 createXmlFile(fileV5, 5, AddToolBars | AddModifiedMenus, "kpartgui"); 0248 fileToVersionMap.insert(fileV5.fileName(), 5); 0249 0250 // The highest version is neither the first nor last one in the list, 0251 // to make sure the code really selects the highest version, not just by chance :) 0252 // (This is why we add the v1 version at the end of the list) 0253 QTemporaryFile fileV1; 0254 QVERIFY(fileV1.open()); 0255 createXmlFile(fileV1, 1, AddToolBars); 0256 fileToVersionMap.insert(fileV1.fileName(), 1); 0257 0258 QStringList files; 0259 files << fileV2.fileName() << fileV5.fileName() << fileV1.fileName(); 0260 0261 fileV2.close(); 0262 fileV5.close(); 0263 fileV1.close(); 0264 0265 KXmlGuiVersionHandler versionHandler(files); 0266 // We end up with the local file, so in our map it has version 2. 0267 // But of course by now it says "version=5" in it :) 0268 QCOMPARE(fileToVersionMap.value(versionHandler.finalFile()), 2); 0269 const QString finalDoc = versionHandler.finalDocument(); 0270 // qDebug() << finalDoc; 0271 QVERIFY(finalDoc.startsWith(QLatin1String("<?xml"))); 0272 QVERIFY(finalDoc.contains(QLatin1String("version=\"5\""))); 0273 // Check that the shortcuts defined by the user were kept 0274 QVERIFY(finalDoc.contains(QLatin1String("<ActionProperties>"))); 0275 QVERIFY(finalDoc.contains(QLatin1String("sidebartng"))); 0276 // Check that the menus modified by the app are still there 0277 QVERIFY(finalDoc.contains(QLatin1String("<Action name=\"file_open\""))); 0278 // Check that the toolbars modified by the user were kept 0279 QVERIFY(finalDoc.contains(QLatin1String("<Action name=\"home\""))); 0280 // Check that the toolbars added by the application were kept (https://invent.kde.org/graphics/okular/-/merge_requests/197) 0281 QVERIFY(finalDoc.contains(QLatin1String("<ToolBar name=\"newToolBar\""))); 0282 } 0283 0284 static QStringList collectMenuNames(KXMLGUIFactory &factory) 0285 { 0286 const QList<QWidget *> containers = factory.containers(QStringLiteral("Menu")); 0287 QStringList containerNames; 0288 for (QWidget *w : containers) { 0289 containerNames << w->objectName(); 0290 } 0291 return containerNames; 0292 } 0293 0294 void debugActions(const QList<QAction *> &actions) 0295 { 0296 for (QAction *action : actions) { 0297 qDebug() << (action->isSeparator() ? QString::fromLatin1("separator") : action->objectName()); 0298 } 0299 } 0300 0301 static void checkActions(const QList<QAction *> &actions, const QStringList &expectedActions) 0302 { 0303 for (int i = 0; i < expectedActions.count(); ++i) { 0304 if (i >= actions.count()) { 0305 break; 0306 } 0307 QAction *action = actions.at(i); 0308 if (action->isSeparator()) { 0309 QCOMPARE(QStringLiteral("separator"), expectedActions[i]); 0310 } else { 0311 QCOMPARE(action->objectName(), expectedActions[i]); 0312 } 0313 } 0314 QCOMPARE(actions.count(), expectedActions.count()); 0315 } 0316 0317 void KXmlGui_UnitTest::testPartMerging() 0318 { 0319 const QByteArray hostXml = 0320 "<?xml version = '1.0'?>\n" 0321 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0322 "<gui version=\"1\" name=\"foo\" >\n" 0323 "<MenuBar>\n" 0324 " <Menu name=\"go\"><text>&Go</text>\n" 0325 " <!-- go_up, go_back, go_forward, go_home, separator: coming from ui_standards.rc -->\n" 0326 " <DefineGroup name=\"before_merge\"/>\n" 0327 " <Merge/>\n" 0328 " <Action name=\"host_after_merge\"/>\n" 0329 " <Action name=\"host_after_merge_2\"/>\n" 0330 " <Separator/>\n" 0331 " <DefineGroup name=\"new_merge\"/>\n" 0332 " <Title>Section title</Title>\n" 0333 " <Action name=\"last_from_host\"/>\n" 0334 " </Menu>\n" 0335 " <Menu name=\"file\"><text>&File</text>\n" 0336 " <DefineGroup name=\"placed_merge\" append=\"new_merge\"/>\n" 0337 " </Menu>\n" 0338 "</MenuBar>\n" 0339 "</gui>\n"; 0340 0341 TestGuiClient hostClient; 0342 hostClient.createActions(QStringList() << QStringLiteral("go_up") << QStringLiteral("go_back") << QStringLiteral("go_forward") << QStringLiteral("go_home") 0343 << QStringLiteral("host_after_merge") << QStringLiteral("host_after_merge_2") << QStringLiteral("last_from_host") 0344 << QStringLiteral("file_new") << QStringLiteral("file_open") << QStringLiteral("file_quit")); 0345 hostClient.createGUI(hostXml, true /*ui_standards.rc*/); 0346 KMainWindow mainWindow; 0347 KXMLGUIBuilder builder(&mainWindow); 0348 KXMLGUIFactory factory(&builder); 0349 factory.addClient(&hostClient); 0350 0351 const QString hostDomDoc = hostClient.domDocument().toString(); 0352 0353 QWidget *goMenuW = factory.container(QStringLiteral("go"), &hostClient); 0354 QVERIFY(goMenuW); 0355 QMenu *goMenu = qobject_cast<QMenu *>(goMenuW); 0356 QVERIFY(goMenu); 0357 QMenu *fileMenu = qobject_cast<QMenu *>(factory.container(QStringLiteral("file"), &hostClient)); 0358 0359 // debugActions(goMenu->actions()); 0360 // clang-format off 0361 checkActions(goMenu->actions(), QStringList() 0362 << QStringLiteral("go_up") 0363 << QStringLiteral("go_back") 0364 << QStringLiteral("go_forward") 0365 << QStringLiteral("go_home") 0366 << QStringLiteral("separator") 0367 << QStringLiteral("host_after_merge") 0368 << QStringLiteral("host_after_merge_2") 0369 << QStringLiteral("separator") 0370 << QStringLiteral("separator") // <Title> separator 0371 << QStringLiteral("last_from_host")); 0372 checkActions(fileMenu->actions(), QStringList() 0373 << QStringLiteral("file_new") 0374 << QStringLiteral("file_open") 0375 << QStringLiteral("separator") 0376 << QStringLiteral("file_quit")); 0377 // clang-format on 0378 0379 qDebug() << "Now merging the part"; 0380 0381 const QByteArray partXml = 0382 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0383 "<gui version=\"1\" name=\"part\" >\n" 0384 "<MenuBar>\n" 0385 " <Menu name=\"go\"><text>&Go</text>\n" 0386 " <Action name=\"go_previous\" group=\"before_merge\"/>\n" 0387 " <Action name=\"go_next\" group=\"before_merge\"/>\n" 0388 " <Separator/>\n" 0389 " <Action name=\"first_page\"/>\n" 0390 " <Action name=\"last_page\"/>\n" 0391 " <Title>Part Section</Title>\n" 0392 " <ActionList name=\"action_list\"/>\n" 0393 " <Action name=\"action_in_merge_group\" group=\"new_merge\"/>\n" 0394 " <Action name=\"undefined_group\" group=\"no_such_merge\"/>\n" 0395 " <Action name=\"last_from_part\"/>\n" 0396 " </Menu>\n" 0397 " <Menu name=\"file\"><text>&File</text>\n" 0398 " <Action group=\"placed_merge\" name=\"action_in_placed_merge\"/>\n" 0399 " <Action name=\"other_file_action\"/>\n" 0400 " </Menu>\n" 0401 "</MenuBar>\n" 0402 "</gui>\n"; 0403 0404 TestGuiClient partClient(partXml); 0405 partClient.createActions(QStringList() << QStringLiteral("go_previous") << QStringLiteral("go_next") << QStringLiteral("first_page") 0406 << QStringLiteral("last_page") << QStringLiteral("last_from_part") << QStringLiteral("action_in_merge_group") 0407 << QStringLiteral("undefined_group") << QStringLiteral("action_in_placed_merge") 0408 << QStringLiteral("other_file_action") << QStringLiteral("action1") << QStringLiteral("action2")); 0409 const QList<QAction *> actionList = {partClient.actionCollection()->action(QStringLiteral("action1")), 0410 partClient.actionCollection()->action(QStringLiteral("action2"))}; 0411 0412 for (int i = 0; i < 5; ++i) { 0413 // qDebug() << "addClient, iteration" << i; 0414 factory.addClient(&partClient); 0415 partClient.plugActionList(QStringLiteral("action_list"), actionList); 0416 0417 // clang-format off 0418 //debugActions(goMenu->actions()); 0419 checkActions(goMenu->actions(), QStringList() 0420 << QStringLiteral("go_up") 0421 << QStringLiteral("go_back") 0422 << QStringLiteral("go_forward") 0423 << QStringLiteral("go_home") 0424 << QStringLiteral("separator") 0425 << QStringLiteral("go_previous") 0426 << QStringLiteral("go_next") 0427 // Contents of the <Merge>: 0428 << QStringLiteral("separator") 0429 << QStringLiteral("first_page") 0430 << QStringLiteral("last_page") 0431 << QStringLiteral("separator") // <title> in the part 0432 << QStringLiteral("action1") 0433 << QStringLiteral("action2") 0434 << QStringLiteral("undefined_group") 0435 << QStringLiteral("last_from_part") 0436 // End of <Merge> 0437 << QStringLiteral("host_after_merge") 0438 << QStringLiteral("host_after_merge_2") 0439 << QStringLiteral("separator") 0440 // Contents of <DefineGroup> 0441 << QStringLiteral("action_in_merge_group") 0442 // End of <DefineGroup> 0443 << QStringLiteral("separator") // <title> is a separator qaction with text 0444 << QStringLiteral("last_from_host") 0445 ); 0446 checkActions(fileMenu->actions(), QStringList() 0447 << QStringLiteral("file_new") 0448 << QStringLiteral("action_in_placed_merge") 0449 << QStringLiteral("file_open") 0450 << QStringLiteral("separator") 0451 << QStringLiteral("file_quit") 0452 << QStringLiteral("other_file_action")); 0453 factory.removeClient(&partClient); 0454 QCOMPARE(hostClient.domDocument().toString(), hostDomDoc); 0455 // clang-format on 0456 } 0457 factory.removeClient(&hostClient); 0458 } 0459 0460 void KXmlGui_UnitTest::testPartMergingSettings() // #252911 0461 { 0462 const QByteArray hostXml = 0463 "<?xml version = '1.0'?>\n" 0464 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0465 "<gui version=\"1\" name=\"foo\" >\n" 0466 "<MenuBar>\n" 0467 // The solution was to remove the duplicated definition 0468 // " <Menu name=\"settings\"><text>&Settings</text>\n" 0469 // " <Action name=\"options_configure_keybinding\"/>\n" 0470 // " <Action name=\"options_configure_toolbars\"/>\n" 0471 // " <Merge name=\"configure_merge\"/>\n" 0472 // " <Separator/>\n" 0473 // " <Merge/>\n" 0474 // " </Menu>\n" 0475 "</MenuBar></gui>\n"; 0476 TestGuiClient hostClient; 0477 hostClient.createActions(QStringList() << QStringLiteral("options_configure_keybinding") << QStringLiteral("options_configure_toolbars")); 0478 hostClient.createGUI(hostXml, true /*ui_standards.rc*/); 0479 // qDebug() << hostClient.domDocument().toString(); 0480 KMainWindow mainWindow; 0481 KXMLGUIBuilder builder(&mainWindow); 0482 KXMLGUIFactory factory(&builder); 0483 factory.addClient(&hostClient); 0484 QWidget *settingsMenu = qobject_cast<QMenu *>(factory.container(QStringLiteral("settings"), &hostClient)); 0485 QVERIFY(settingsMenu); 0486 // debugActions(settingsMenu->actions()); 0487 0488 const QByteArray partXml = 0489 "<?xml version = '1.0'?>\n" 0490 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0491 "<gui version=\"1\" name=\"foo\" >\n" 0492 "<MenuBar>\n" 0493 " <Menu name=\"settings\"><text>&Settings</text>\n" 0494 " <Action name=\"configure_klinkstatus\"/>\n" 0495 " </Menu>\n" 0496 "</MenuBar></gui>\n"; 0497 TestGuiClient partClient(partXml); 0498 partClient.createActions(QStringList() << QStringLiteral("configure_klinkstatus")); 0499 factory.addClient(&partClient); 0500 // debugActions(settingsMenu->actions()); 0501 checkActions(settingsMenu->actions(), 0502 QStringList() << QStringLiteral("separator") // that's ok, QMenuPrivate::filterActions won't show it 0503 << QStringLiteral("options_configure_keybinding") << QStringLiteral("options_configure_toolbars") 0504 << QStringLiteral("configure_klinkstatus")); 0505 factory.removeClient(&partClient); 0506 factory.removeClient(&hostClient); 0507 } 0508 0509 void KXmlGui_UnitTest::testUiStandardsMerging_data() 0510 { 0511 QTest::addColumn<QByteArray>("xml"); 0512 QTest::addColumn<QStringList>("actions"); 0513 QTest::addColumn<QStringList>("expectedMenus"); 0514 0515 const QByteArray xmlBegin = 0516 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0517 "<gui version=\"1\" name=\"foo\" >\n" 0518 "<MenuBar>\n"; 0519 const QByteArray xmlEnd = 0520 "</MenuBar>\n" 0521 "</gui>"; 0522 0523 // Merging an empty menu (or a menu with only non-existing actions) would make 0524 // the empty menu appear at the end after all other menus (fixed for KDE-4.2) 0525 /* clang-format off */ 0526 QTest::newRow("empty file menu, implicit settings menu") 0527 << QByteArray(xmlBegin + "<Menu name=\"file\"/>\n" + xmlEnd) 0528 << (QStringList() << QStringLiteral("options_configure_toolbars")) 0529 << (QStringList() << QStringLiteral("settings")); 0530 QTest::newRow("file menu with non existing action, implicit settings menu") 0531 << QByteArray(xmlBegin + "<Menu name=\"file\"><Action name=\"foo\"/></Menu>\n" + xmlEnd) 0532 << (QStringList() << QStringLiteral("options_configure_toolbars")) 0533 << (QStringList() << QStringLiteral("settings")); 0534 QTest::newRow("file menu with existing action, implicit settings menu") 0535 << QByteArray(xmlBegin + "<Menu name=\"file\"><Action name=\"open\"/></Menu>\n" + xmlEnd) 0536 << (QStringList() << QStringLiteral("open") << QStringLiteral("options_configure_toolbars")) 0537 << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); 0538 QTest::newRow("implicit file and settings menu") 0539 << QByteArray(xmlBegin + xmlEnd) 0540 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) 0541 << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); // we could check that file_open is in the mainToolBar, too 0542 0543 // Check that unknown non-empty menus are added at the "MergeLocal" position (before settings). 0544 QTest::newRow("foo menu added at the end") 0545 << QByteArray(xmlBegin + "<Menu name=\"foo\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) 0546 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars") << QStringLiteral("foo_action")) 0547 << (QStringList() << QStringLiteral("file") << QStringLiteral("foo") << QStringLiteral("settings")); 0548 0549 QTest::newRow("Bille's testcase: menu patch + menu edit") 0550 << QByteArray(xmlBegin + "<Menu name=\"patch\"><Action name=\"patch_generate\"/></Menu>\n" 0551 + "<Menu name=\"edit\"><Action name=\"edit_foo\"/></Menu>\n" + xmlEnd) 0552 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("patch_generate") << QStringLiteral("edit_foo")) 0553 << (QStringList() << QStringLiteral("file") << QStringLiteral("edit") << QStringLiteral("patch")); 0554 QTest::newRow("Bille's testcase: menu patch + menu edit, lowercase tag") 0555 << QByteArray(xmlBegin + "<Menu name=\"patch\"><Action name=\"patch_generate\"/></Menu>\n" 0556 + "<menu name=\"edit\"><Action name=\"edit_foo\"/></menu>\n" + xmlEnd) 0557 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("patch_generate") << QStringLiteral("edit_foo")) 0558 << (QStringList() << QStringLiteral("file") << QStringLiteral("edit") << QStringLiteral("patch")); 0559 0560 // Check that <Menu append="..."> allows to insert menus at specific positions 0561 QTest::newRow("Menu append") 0562 << QByteArray(xmlBegin + "<Menu name=\"foo\" append=\"settings_merge\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) 0563 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars") 0564 << QStringLiteral("foo_action") << QStringLiteral("help_contents")) 0565 << (QStringList() << QStringLiteral("file") << QStringLiteral("settings") << QStringLiteral("foo") << QStringLiteral("help")); 0566 QTest::newRow("Custom first menu") 0567 << QByteArray(xmlBegin + "<Menu name=\"foo\" append=\"first_menu\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) 0568 << (QStringList() << QStringLiteral("edit_undo") << QStringLiteral("foo_action") << QStringLiteral("help_contents")) 0569 << (QStringList() << QStringLiteral("foo") << QStringLiteral("edit") << QStringLiteral("help")); 0570 0571 // Tests for noMerge="1" 0572 QTest::newRow("noMerge empty file menu, implicit settings menu") 0573 << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"/>\n" + xmlEnd) 0574 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) 0575 << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); // we keep empty menus, see #186382 0576 QTest::newRow("noMerge empty file menu, file_open moved elsewhere") 0577 << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"/>\n<Menu name=\"foo\"><Action name=\"file_open\"/></Menu>" + xmlEnd) 0578 << (QStringList() << QStringLiteral("file_open")) 0579 << (QStringList() << QStringLiteral("file") << QStringLiteral("foo")); 0580 QTest::newRow("noMerge file menu with open before new") 0581 << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"><Action name=\"file_open\"/><Action name=\"file_new\"/></Menu>" + xmlEnd) 0582 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("file_new")) 0583 << (QStringList() << QStringLiteral("file")); // TODO check the order of the actions in the menu? how? 0584 0585 // Tests for deleted="true" 0586 QTest::newRow("deleted file menu, implicit settings menu") 0587 << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"/>\n" + xmlEnd) 0588 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) 0589 << (QStringList() << QStringLiteral("settings")); 0590 QTest::newRow("deleted file menu, file_open moved elsewhere") 0591 << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"/>\n<Menu name=\"foo\"><Action name=\"file_open\"/></Menu>" + xmlEnd) 0592 << (QStringList() << QStringLiteral("file_open")) 0593 << (QStringList() << QStringLiteral("foo")); 0594 QTest::newRow("deleted file menu with actions (contradiction)") 0595 << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"><Action name=\"file_open\"/><Action name=\"file_new\"/></Menu>" + xmlEnd) 0596 << (QStringList() << QStringLiteral("file_open") << QStringLiteral("file_new")) 0597 << (QStringList()); 0598 /* clang-format on */ 0599 } 0600 0601 void KXmlGui_UnitTest::testUiStandardsMerging() 0602 { 0603 QFETCH(QByteArray, xml); 0604 QFETCH(QStringList, actions); 0605 QFETCH(QStringList, expectedMenus); 0606 0607 TestGuiClient client; 0608 client.createActions(actions); 0609 client.createGUI(xml, true /*ui_standards.rc*/); 0610 0611 const QDomDocument domDocument = client.domDocument(); 0612 const QDomElement docElem = domDocument.documentElement(); 0613 QCOMPARE(docElem.attribute(QStringLiteral("name")), QStringLiteral("foo")); // not standard_containers from ui_standards.rc 0614 QCOMPARE(docElem.attribute(QStringLiteral("version")), QStringLiteral("1")); // not standard_containers from ui_standards.rc 0615 0616 KMainWindow mainWindow; 0617 KXMLGUIBuilder builder(&mainWindow); 0618 KXMLGUIFactory factory(&builder); 0619 factory.addClient(&client); 0620 0621 const QStringList containerNames = collectMenuNames(factory); 0622 // qDebug() << containerNames; 0623 QCOMPARE(containerNames, expectedMenus); 0624 0625 factory.removeClient(&client); 0626 } 0627 0628 void KXmlGui_UnitTest::testActionListAndSeparator() 0629 { 0630 const QByteArray xml = 0631 "<?xml version = '1.0'?>\n" 0632 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0633 "<gui version=\"1\" name=\"foo\" >\n" 0634 "<MenuBar>\n" 0635 " <Menu name=\"groups\"><text>Add to Group</text>\n" 0636 " <ActionList name=\"view_groups_list\"/>\n" 0637 " <Separator />" 0638 " <Action name=\"view_add_to_new_group\" />\n" 0639 " <ActionList name=\"second_list\"/>\n" 0640 " </Menu>\n" 0641 "</MenuBar>\n" 0642 "</gui>"; 0643 0644 TestGuiClient client(xml); 0645 client.createActions(QStringList() << QStringLiteral("view_add_to_new_group")); 0646 KMainWindow mainWindow; 0647 KXMLGUIBuilder builder(&mainWindow); 0648 KXMLGUIFactory factory(&builder); 0649 factory.addClient(&client); 0650 0651 QWidget *menuW = factory.container(QStringLiteral("groups"), &client); 0652 QVERIFY(menuW); 0653 QMenu *menu = qobject_cast<QMenu *>(menuW); 0654 QVERIFY(menu); 0655 0656 // debugActions(menu->actions()); 0657 checkActions(menu->actions(), 0658 QStringList() << QStringLiteral("separator") // that's ok, QMenuPrivate::filterActions won't show it 0659 << QStringLiteral("view_add_to_new_group")); 0660 0661 qDebug() << "Now plugging the actionlist"; 0662 0663 QAction *action1 = new QAction(this); 0664 action1->setObjectName(QStringLiteral("action1")); 0665 client.actionCollection()->setDefaultShortcut(action1, QKeySequence(QStringLiteral("Ctrl+2"))); 0666 QAction *action2 = new QAction(this); 0667 action2->setObjectName(QStringLiteral("action2")); 0668 const QList<QAction *> actionList = {action1, action2}; 0669 client.plugActionList(QStringLiteral("view_groups_list"), actionList); 0670 QCOMPARE(QKeySequence::listToString(action1->shortcuts()), QStringLiteral("Ctrl+2")); 0671 0672 const QStringList expectedActionsOneList = {QStringLiteral("action1"), 0673 QStringLiteral("action2"), 0674 QStringLiteral("separator"), 0675 QStringLiteral("view_add_to_new_group")}; 0676 // debugActions(menu->actions()); 0677 checkActions(menu->actions(), expectedActionsOneList); 0678 0679 QAction *action3 = new QAction(this); 0680 action3->setObjectName(QStringLiteral("action3")); 0681 const QList<QAction *> secondActionList = {action3}; 0682 client.plugActionList(QStringLiteral("second_list"), secondActionList); 0683 QStringList expectedActions = expectedActionsOneList; 0684 expectedActions << QStringLiteral("action3"); 0685 checkActions(menu->actions(), expectedActions); 0686 0687 qDebug() << "Now remove+add gui client"; 0688 0689 // While I'm here, what happens with the action list if I remove+add the guiclient, 0690 // like KXmlGuiWindow::newToolBarConfig does? 0691 factory.removeClient(&client); 0692 factory.addClient(&client); 0693 // We need to get the container widget again, it was re-created. 0694 menuW = factory.container(QStringLiteral("groups"), &client); 0695 QVERIFY(menuW); 0696 menu = qobject_cast<QMenu *>(menuW); 0697 // debugActions(menu->actions()); 0698 checkActions(menu->actions(), 0699 QStringList() << QStringLiteral("separator") // yep, it removed the actionlist thing... 0700 << QStringLiteral("view_add_to_new_group")); 0701 qDebug() << "Now plugging the actionlist again"; 0702 client.plugActionList(QStringLiteral("second_list"), secondActionList); 0703 client.plugActionList(QStringLiteral("view_groups_list"), actionList); 0704 checkActions(menu->actions(), expectedActions); 0705 factory.removeClient(&client); 0706 } 0707 0708 void KXmlGui_UnitTest::testHiddenToolBar() 0709 { 0710 const QByteArray xml = 0711 "<?xml version = '1.0'?>\n" 0712 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0713 "<gui version=\"1\" name=\"foo\" >\n" 0714 "<MenuBar>\n" 0715 "</MenuBar>\n" 0716 "<ToolBar hidden=\"true\" name=\"mainToolBar\">\n" 0717 " <Action name=\"go_up\"/>\n" 0718 "</ToolBar>\n" 0719 "<ToolBar name=\"visibleToolBar\">\n" 0720 " <Action name=\"go_up\"/>\n" 0721 "</ToolBar>\n" 0722 "<ToolBar hidden=\"true\" name=\"hiddenToolBar\">\n" 0723 " <Action name=\"go_up\"/>\n" 0724 "</ToolBar>\n" 0725 "</gui>\n"; 0726 KConfigGroup cg(KSharedConfig::openConfig(), "testHiddenToolBar"); 0727 TestXmlGuiWindow mainWindow(xml, "kxmlgui_unittest.rc"); 0728 mainWindow.setAutoSaveSettings(cg); 0729 mainWindow.createActions(QStringList() << QStringLiteral("go_up")); 0730 mainWindow.createGUI(); 0731 0732 KToolBar *mainToolBar = mainWindow.toolBarByName(QStringLiteral("mainToolBar")); 0733 QVERIFY(mainToolBar->isHidden()); 0734 0735 KXMLGUIFactory *factory = mainWindow.guiFactory(); 0736 QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); 0737 KToolBar *hiddenToolBar = qobject_cast<KToolBar *>(factory->container(QStringLiteral("hiddenToolBar"), &mainWindow)); 0738 qDebug() << hiddenToolBar; 0739 QVERIFY(hiddenToolBar->isHidden()); 0740 0741 // Now open KEditToolBar (#105525) 0742 KEditToolBar editToolBar(factory); 0743 // KEditToolBar loads the stuff in showEvent... 0744 QShowEvent ev; 0745 qApp->sendEvent(&editToolBar, &ev); 0746 clickApply(&editToolBar); 0747 QVERIFY(qobject_cast<KToolBar *>(factory->container(QStringLiteral("hiddenToolBar"), &mainWindow))->isHidden()); 0748 0749 mainWindow.close(); 0750 } 0751 0752 void KXmlGui_UnitTest::testCustomPlaceToolBar() 0753 { 0754 const QByteArray xml = 0755 "<?xml version = '1.0'?>\n" 0756 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0757 "<gui version=\"1\" name=\"foo\" >\n" 0758 "<MenuBar>\n" 0759 "</MenuBar>\n" 0760 "<ToolBar name=\"mainToolBar\" noMerge=\"1\">\n" 0761 " <text>Main Toolbar</text>\n" 0762 " <Action name=\"go_up\"/>\n" 0763 "</ToolBar>\n" 0764 "<ToolBar name=\"customToolBar\" noMerge=\"1\">\n" 0765 " <text>Custom Toolbar</text>\n" 0766 " <Action name=\"go_up\"/>\n" 0767 " <Action name=\"file_open\"/>\n" 0768 " <Action name=\"help_about_kde\"/>\n" 0769 "</ToolBar>\n" 0770 "</gui>\n"; 0771 KConfigGroup cg(KSharedConfig::openConfig(), "testHiddenToolBar"); 0772 TestXmlGuiWindow mw(xml, "kxmlgui_unittest.rc"); 0773 mw.setAutoSaveSettings(cg); 0774 0775 QWidget *centralWidget = new QWidget(&mw); 0776 centralWidget->setGeometry(0, 0, 100, 100); 0777 mw.setCentralWidget(centralWidget); 0778 QHBoxLayout *layout = new QHBoxLayout(centralWidget); 0779 KToolBar *customToolBar = new KToolBar(QStringLiteral("customToolBar"), centralWidget); 0780 layout->addWidget(customToolBar); 0781 0782 mw.createActions(QStringList() << QStringLiteral("go_up")); 0783 mw.createGUI(); 0784 0785 QVERIFY(centralWidget->findChild<KToolBar *>(QStringLiteral("customToolBar")) != nullptr); 0786 0787 QVERIFY2(mw.toolBarArea(customToolBar) == Qt::NoToolBarArea, "The custom toolbar should not be in a ToolBarArea of the main window"); 0788 0789 // Now open KEditToolBar, just to check it doesn't crash on apply 0790 KXMLGUIFactory *factory = mw.guiFactory(); 0791 KEditToolBar editToolBar(factory); 0792 // KEditToolBar loads the stuff in showEvent... 0793 QShowEvent ev; 0794 qApp->sendEvent(&editToolBar, &ev); 0795 clickApply(&editToolBar); 0796 0797 QVERIFY2(centralWidget->findChild<KToolBar *>(QStringLiteral("customToolBar")) != nullptr, 0798 "After a KEditToolBar action the custom toolbar should stay in the widget it was orignally added"); 0799 QVERIFY2(mw.toolBarArea(mw.toolBarByName(QStringLiteral("customToolBar"))) == Qt::NoToolBarArea, 0800 "After a KEditToolBar action the custom toolbar should not be in a ToolBarArea of the main window"); 0801 0802 mw.close(); 0803 } 0804 0805 // taken from KMainWindow_UnitTest::testAutoSaveSettings() 0806 void KXmlGui_UnitTest::testAutoSaveSettings() 0807 { 0808 const QByteArray xml = 0809 "<?xml version = '1.0'?>\n" 0810 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0811 "<gui version=\"1\" name=\"foo\" >\n" 0812 "<MenuBar>\n" 0813 "</MenuBar>\n" 0814 "<ToolBar name=\"mainToolBar\">\n" 0815 " <Action name=\"go_up\"/>\n" 0816 "</ToolBar>\n" 0817 "<ToolBar name=\"secondToolBar\">\n" 0818 " <Action name=\"go_up\"/>\n" 0819 "</ToolBar>\n" 0820 "</gui>\n"; 0821 { 0822 // do not interfere with the "toolbarVisibility" unit test 0823 KConfigGroup cg(KSharedConfig::openConfig(), "testAutoSaveSettings"); 0824 TestXmlGuiWindow mw(xml, "kxmlgui_unittest.rc"); 0825 mw.show(); 0826 mw.setAutoSaveSettings(cg); 0827 0828 // Test resizing first (like show() does). 0829 mw.reallyResize(400, 400); 0830 QTest::qWait(200); 0831 0832 mw.createActions(QStringList() << QStringLiteral("go_up")); 0833 mw.createGUI(); 0834 0835 // Resize again, should be saved 0836 mw.reallyResize(800, 600); 0837 QTest::qWait(200); 0838 0839 KToolBar *mainToolBar = mw.toolBarByName(QStringLiteral("mainToolBar")); 0840 QCOMPARE(mw.toolBarArea(mainToolBar), Qt::TopToolBarArea); 0841 KToolBar *secondToolBar = mw.toolBarByName(QStringLiteral("secondToolBar")); 0842 QCOMPARE((int)mw.toolBarArea(secondToolBar), (int)Qt::TopToolBarArea); // REFERENCE #1 (see below) 0843 0844 // Move second toolbar to bottom 0845 const QPoint oldPos = secondToolBar->pos(); 0846 mw.addToolBar(Qt::BottomToolBarArea, secondToolBar); 0847 const QPoint newPos = secondToolBar->pos(); 0848 QCOMPARE(mw.toolBarArea(secondToolBar), Qt::BottomToolBarArea); 0849 // Calling to addToolBar is not enough to trigger the event filter for move events 0850 // in KMainWindow, because there is no layouting happening in hidden mainwindows. 0851 QMoveEvent moveEvent(newPos, oldPos); 0852 QApplication::sendEvent(secondToolBar, &moveEvent); 0853 0854 mw.close(); 0855 } 0856 0857 { 0858 KConfigGroup cg(KSharedConfig::openConfig(), "testAutoSaveSettings"); 0859 TestXmlGuiWindow mw2(xml, "kxmlgui_unittest.rc"); 0860 mw2.show(); 0861 mw2.setAutoSaveSettings(cg); 0862 QTest::qWait(200); 0863 // Check window size was restored 0864 QCOMPARE(mw2.size(), QSize(800, 600)); 0865 0866 mw2.createActions(QStringList() << QStringLiteral("go_up")); 0867 mw2.createGUI(); 0868 0869 // Force window layout to happen 0870 mw2.reallyResize(800, 600); 0871 QTest::qWait(200); 0872 0873 // Check toolbar positions were restored 0874 KToolBar *mainToolBar = mw2.toolBarByName(QStringLiteral("mainToolBar")); 0875 QCOMPARE(mw2.toolBarArea(mainToolBar), Qt::TopToolBarArea); 0876 KToolBar *secondToolBar = mw2.toolBarByName(QStringLiteral("secondToolBar")); 0877 QCOMPARE(mw2.toolBarArea(secondToolBar), Qt::BottomToolBarArea); 0878 mw2.applyMainWindowSettings(mw2.autoSaveConfigGroup()); 0879 QCOMPARE(mw2.toolBarArea(secondToolBar), Qt::BottomToolBarArea); 0880 } 0881 } 0882 0883 void KXmlGui_UnitTest::testDeletedContainers() // deleted="true" 0884 { 0885 const QByteArray xml = 0886 "<?xml version = '1.0'?>\n" 0887 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0888 "<gui version=\"1\" name=\"foo\" >\n" 0889 "<MenuBar>\n" 0890 " <Menu deleted=\"true\" name=\"game\"/>\n" 0891 "</MenuBar>\n" 0892 "<ToolBar deleted=\"true\" name=\"mainToolBar\">\n" 0893 " <Action name=\"go_up\"/>\n" 0894 "</ToolBar>\n" 0895 "<ToolBar name=\"visibleToolBar\">\n" 0896 " <Action name=\"go_up\"/>\n" 0897 "</ToolBar>\n" 0898 "<ToolBar deleted=\"true\" name=\"deletedToolBar\">\n" 0899 " <Action name=\"go_up\"/>\n" 0900 "</ToolBar>\n" 0901 "</gui>\n"; 0902 KConfigGroup cg(KSharedConfig::openConfig(), "testDeletedToolBar"); 0903 TestXmlGuiWindow mainWindow(xml, "kxmlgui_unittest.rc"); 0904 mainWindow.setAutoSaveSettings(cg); 0905 mainWindow.createActions(QStringList() << QStringLiteral("go_up") << QStringLiteral("file_new") << QStringLiteral("game_new")); 0906 mainWindow.createGUI(); 0907 KXMLGUIFactory *factory = mainWindow.guiFactory(); 0908 0909 // qDebug() << "containers:" << factory->containers("ToolBar"); 0910 QVERIFY(!factory->container(QStringLiteral("mainToolBar"), &mainWindow)); 0911 QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); 0912 QVERIFY(!factory->container(QStringLiteral("deletedToolBar"), &mainWindow)); 0913 QVERIFY(factory->container(QStringLiteral("file"), &mainWindow)); // File menu was created 0914 QVERIFY(!factory->container(QStringLiteral("game"), &mainWindow)); // Game menu was not created 0915 0916 // Now open KEditToolBar, just to check it doesn't crash. 0917 KEditToolBar editToolBar(factory); 0918 // KEditToolBar loads the stuff in showEvent... 0919 QShowEvent ev; 0920 qApp->sendEvent(&editToolBar, &ev); 0921 clickApply(&editToolBar); 0922 QVERIFY(!factory->container(QStringLiteral("mainToolBar"), &mainWindow)); 0923 QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); 0924 QVERIFY(!factory->container(QStringLiteral("deletedToolBar"), &mainWindow)); 0925 QVERIFY(factory->container(QStringLiteral("file"), &mainWindow)); 0926 QVERIFY(!factory->container(QStringLiteral("game"), &mainWindow)); 0927 0928 mainWindow.close(); 0929 } 0930 0931 void KXmlGui_UnitTest::testTopLevelSeparator() 0932 { 0933 const QByteArray xml = 0934 "<?xml version = '1.0'?>\n" 0935 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0936 "<gui version=\"1\" name=\"foo\" >\n" 0937 "<MenuBar>\n" 0938 " <Menu name=\"before_separator\"><text>Before Separator</text></Menu>\n" 0939 " <Separator />\n" 0940 " <Menu name=\"after_separator\"><text>After Separator</text></Menu>\n" 0941 "</MenuBar>\n" 0942 "</gui>"; 0943 0944 TestXmlGuiWindow mainWindow(xml, "kxmlgui_unittest.rc"); 0945 mainWindow.createGUI(); 0946 0947 checkActions(mainWindow.menuBar()->actions(), 0948 QStringList() << QStringLiteral("before_separator") << QStringLiteral("separator") << QStringLiteral("after_separator") 0949 << QStringLiteral("settings") << QStringLiteral("separator") << QStringLiteral("help")); 0950 } 0951 0952 // Check that the objectName() of the menus is set from the name in the XML file 0953 void KXmlGui_UnitTest::testMenuNames() 0954 { 0955 const QByteArray xml = 0956 "<?xml version = '1.0'?>\n" 0957 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 0958 "<gui version=\"1\" name=\"foo\" >\n" 0959 "<MenuBar>\n" 0960 " <Menu name=\"filemenu\"><text>File Menu</text></Menu>\n" 0961 "</MenuBar>\n" 0962 "</gui>"; 0963 0964 TestXmlGuiWindow mainWindow(xml, "kxmlgui_unittest.rc"); 0965 mainWindow.createGUI(); 0966 0967 checkActions(mainWindow.menuBar()->actions(), 0968 QStringList() << QStringLiteral("filemenu") << QStringLiteral("settings") << QStringLiteral("separator") << QStringLiteral("help")); 0969 } 0970 0971 // Test what happens when the application's rc file isn't found 0972 // We want a warning to be printed, but we don't want to see all menus from ui_standards.rc 0973 void KXmlGui_UnitTest::testMenusNoXmlFile() 0974 { 0975 TestXmlGuiWindow mainWindow(QByteArray(), "nolocalfile_either.rc"); 0976 mainWindow.createGUIBad(); 0977 0978 checkActions(mainWindow.menuBar()->actions(), QStringList{QStringLiteral("settings"), QStringLiteral("separator"), QStringLiteral("help")}); 0979 } 0980 0981 void KXmlGui_UnitTest::testXMLFileReplacement() 0982 { 0983 // to differentiate "original" and replacement xml file, one is created with "modified" toolbars 0984 QTemporaryFile fileOrig; 0985 QVERIFY(fileOrig.open()); 0986 createXmlFile(fileOrig, 2, AddToolBars); 0987 const QString filenameOrig = fileOrig.fileName(); 0988 fileOrig.close(); 0989 0990 QTemporaryFile fileReplace; 0991 QVERIFY(fileReplace.open()); 0992 createXmlFile(fileReplace, 2, AddModifiedToolBars); 0993 const QString filenameReplace = fileReplace.fileName(); 0994 fileReplace.close(); 0995 0996 // finally, our local xml file has <ActionProperties/> 0997 const QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui_unittest"); 0998 QFile fileLocal(dir + QLatin1String("/testui.rc")); 0999 QVERIFY2(fileLocal.open(QIODevice::WriteOnly), qPrintable(fileLocal.fileName())); 1000 createXmlFile(fileLocal, 1, AddActionProperties); 1001 const QString filenameLocal = fileLocal.fileName(); 1002 fileLocal.close(); 1003 1004 TestGuiClient client; 1005 // first make sure that the "original" file is loaded, correctly 1006 client.setXMLFilePublic(filenameOrig); 1007 QString xml = client.domDocument().toString(); 1008 // qDebug() << xml; 1009 QVERIFY(xml.contains(QLatin1String("<Action name=\"print\""))); 1010 QVERIFY(!xml.contains(QLatin1String("<Action name=\"home\""))); 1011 QVERIFY(!xml.contains(QLatin1String("<ActionProperties>"))); 1012 1013 // now test the replacement (+ local file) 1014 client.replaceXMLFile(filenameReplace, filenameLocal); 1015 xml = client.domDocument().toString(); 1016 QVERIFY(!xml.contains(QLatin1String("<Action name=\"print\""))); 1017 QVERIFY(xml.contains(QLatin1String("<Action name=\"home\""))); 1018 QVERIFY(xml.contains(QLatin1String("<ActionProperties>"))); 1019 1020 // re-check after a reload 1021 client.reloadXML(); 1022 QString reloadedXml = client.domDocument().toString(); 1023 QVERIFY(!reloadedXml.contains(QLatin1String("<Action name=\"print\""))); 1024 QVERIFY(reloadedXml.contains(QLatin1String("<Action name=\"home\""))); 1025 QVERIFY(reloadedXml.contains(QLatin1String("<ActionProperties>"))); 1026 1027 // Check what happens when the local file doesn't exist 1028 TestGuiClient client2; 1029 QFile::remove(filenameLocal); 1030 client2.replaceXMLFile(filenameReplace, filenameLocal); 1031 xml = client2.domDocument().toString(); 1032 // qDebug() << xml; 1033 QVERIFY(!xml.contains(QLatin1String("<Action name=\"print\""))); 1034 QVERIFY(xml.contains(QLatin1String("<Action name=\"home\""))); // modified toolbars 1035 QVERIFY(!xml.contains(QLatin1String("<ActionProperties>"))); // but no local xml file 1036 } 1037 1038 void KXmlGui_UnitTest::testClientDestruction() // #170806 1039 { 1040 const QByteArray hostXml = 1041 "<?xml version = '1.0'?>\n" 1042 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 1043 "<gui version=\"1\" name=\"foo\" >\n" 1044 "<MenuBar>\n" 1045 " <Menu name=\"file\"><text>&File</text>\n" 1046 " </Menu>\n" 1047 " <Merge/>\n" 1048 "</MenuBar>\n" 1049 "</gui>"; 1050 const QByteArray xml = 1051 "<?xml version = '1.0'?>\n" 1052 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 1053 "<gui version=\"1\" name=\"foo\" >\n" 1054 "<MenuBar>\n" 1055 " <Menu name=\"file\"><text>&File</text>\n" 1056 " <Action name=\"file_open\"/>\n" 1057 " <Action name=\"file_quit\"/>\n" 1058 " </Menu>\n" 1059 "</MenuBar>\n" 1060 "</gui>"; 1061 1062 TestXmlGuiWindow mainWindow(hostXml, "kxmlgui_unittest.rc"); 1063 TestGuiClient *client = new TestGuiClient(xml); 1064 client->createActions(QStringList() << QStringLiteral("file_open") << QStringLiteral("file_quit")); 1065 mainWindow.insertChildClient(client); 1066 mainWindow.createGUI(); 1067 1068 const QStringList menus = {QStringLiteral("file"), QStringLiteral("settings"), QStringLiteral("separator"), QStringLiteral("help")}; 1069 checkActions(mainWindow.menuBar()->actions(), menus); 1070 1071 QVERIFY(mainWindow.factory()->clients().contains(client)); 1072 delete client; 1073 QVERIFY(!mainWindow.factory()->clients().contains(client)); 1074 1075 // No change, because deletion is fast, it doesn't do manual unplugging. 1076 checkActions(mainWindow.menuBar()->actions(), menus); 1077 } 1078 1079 void KXmlGui_UnitTest::testShortcuts() 1080 { 1081 const QByteArray xml = 1082 "<?xml version = '1.0'?>\n" 1083 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 1084 "<gui version=\"1\" name=\"foo\" >\n" 1085 "<MenuBar>\n" 1086 " <Menu name=\"file\"><text>&File</text>\n" 1087 " <Action name=\"file_open\" shortcut=\"Ctrl+O\"/>\n" 1088 " <Action name=\"file_quit\" shortcut=\"Ctrl+Q; Ctrl+D\"/>\n" 1089 " </Menu>\n" 1090 "</MenuBar>\n" 1091 "<ActionProperties scheme=\"Default\">\n" 1092 " <Action shortcut=\"Ctrl+O\" name=\"file_open\"/>\n" 1093 " <Action shortcut=\"Ctrl+Q; Ctrl+D\" name=\"file_quit\"/>\n" 1094 "</ActionProperties>\n" 1095 "</gui>"; 1096 1097 TestGuiClient client; 1098 client.createActions(QStringList() << QStringLiteral("file_open") << QStringLiteral("file_quit")); 1099 client.createGUI(xml, false /*ui_standards.rc*/); 1100 1101 KMainWindow mainWindow; 1102 KXMLGUIBuilder builder(&mainWindow); 1103 KXMLGUIFactory factory(&builder); 1104 factory.addClient(&client); 1105 1106 QAction *actionOpen = client.action("file_open"); 1107 QAction *actionQuit = client.action("file_quit"); 1108 QVERIFY(actionOpen && actionQuit); 1109 QCOMPARE(actionOpen->shortcuts(), QList<QKeySequence>() << QKeySequence(QStringLiteral("Ctrl+O"))); 1110 // #345411 1111 QCOMPARE(actionQuit->shortcuts(), QList<QKeySequence>() << QKeySequence(QStringLiteral("Ctrl+Q")) << QKeySequence(QStringLiteral("Ctrl+D"))); 1112 1113 factory.removeClient(&client); 1114 } 1115 1116 void KXmlGui_UnitTest::testPopupMenuParent() 1117 { 1118 const QByteArray xml = 1119 "<?xml version = '1.0'?>\n" 1120 "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" 1121 "<gui version=\"1\" name=\"foo\" >\n" 1122 "<Menu name=\"foo\"><text>Foo</text></Menu>\n" 1123 "</gui>"; 1124 1125 TestXmlGuiWindow mainWindow(xml, "kxmlgui_unittest.rc"); 1126 mainWindow.createGUI(); 1127 1128 auto popupMenu = mainWindow.menuByName(QStringLiteral("foo")); 1129 QVERIFY(popupMenu); 1130 QCOMPARE(popupMenu->parent(), &mainWindow); 1131 } 1132 1133 void KXmlGui_UnitTest::testSpecificApplicationLanguageQLocale() 1134 { 1135 qunsetenv("LC_ALL"); // In case the user sets the LC_ALL ENV variable this test could fail 1136 const QLocale originalSystemLocale = QLocale::system(); 1137 1138 KDEPrivate::setApplicationSpecificLanguage("ru_RU"); 1139 KDEPrivate::initializeLanguages(); 1140 1141 #ifdef Q_OS_WIN 1142 QEXPECT_FAIL("", 1143 "KDEPrivate::initializeLanguages assumes that setting $LANGUAGE will have an effect, this isn't the case on Windows. Needs to be fixed.", 1144 Abort); 1145 #endif 1146 QCOMPARE(QLocale::system().language(), QLocale::Russian); 1147 1148 KDEPrivate::setApplicationSpecificLanguage("wa"); 1149 KDEPrivate::initializeLanguages(); 1150 1151 QCOMPARE(QLocale::system().language(), QLocale::Walloon); 1152 1153 KDEPrivate::setApplicationSpecificLanguage(QByteArray()); 1154 KDEPrivate::initializeLanguages(); 1155 1156 QCOMPARE(QLocale::system(), originalSystemLocale); 1157 } 1158 1159 void KXmlGui_UnitTest::testSingleModifierQKeySequenceEndsWithPlus() 1160 { 1161 // Check that native texts of the Meta, Alt, Control, Shift, Keypad modifiers end in "+" 1162 // we depend on that in KKeySequenceWidgetPrivate::updateShortcutDisplay() 1163 QVERIFY(QKeySequence(Qt::MetaModifier).toString(QKeySequence::NativeText).endsWith(QLatin1Char('+'))); 1164 QVERIFY(QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText).endsWith(QLatin1Char('+'))); 1165 QVERIFY(QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText).endsWith(QLatin1Char('+'))); 1166 QVERIFY(QKeySequence(Qt::ShiftModifier).toString(QKeySequence::NativeText).endsWith(QLatin1Char('+'))); 1167 QVERIFY(QKeySequence(Qt::KeypadModifier).toString(QKeySequence::NativeText).endsWith(QLatin1Char('+'))); 1168 } 1169 1170 void KXmlGui_UnitTest::testSaveShortcutsAndRefresh() 1171 { 1172 QTemporaryFile xmlFile; 1173 QVERIFY(xmlFile.open()); 1174 createXmlFile(xmlFile, 2, AddModifiedMenus); 1175 const QString filename = xmlFile.fileName(); 1176 xmlFile.close(); 1177 1178 QTemporaryFile localXmlFile; 1179 QVERIFY(localXmlFile.open()); 1180 const QString localFilename = localXmlFile.fileName(); 1181 localXmlFile.close(); 1182 1183 TestGuiClient client; 1184 client.createActions({QStringLiteral("file_open")}); 1185 client.setLocalXMLFilePublic(localFilename); 1186 client.setXMLFilePublic(filename); 1187 1188 QWidget w; 1189 KXMLGUIBuilder builder(&w); 1190 KXMLGUIFactory factory(&builder); 1191 factory.addClient(&client); 1192 factory.removeClient(&client); 1193 factory.addClient(&client); 1194 1195 QAction *a = client.actionCollection()->action(QStringLiteral("file_open")); 1196 QCOMPARE(a->shortcut(), QKeySequence()); 1197 a->setShortcut(Qt::Key_F22); 1198 QCOMPARE(a->shortcut(), QKeySequence(Qt::Key_F22)); 1199 1200 client.actionCollection()->writeSettings(); 1201 1202 factory.refreshActionProperties(); 1203 1204 a = client.actionCollection()->action(QStringLiteral("file_open")); 1205 QCOMPARE(a->shortcut(), QKeySequence(Qt::Key_F22)); 1206 } 1207 1208 #include "moc_kxmlgui_unittest.cpp"