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