1 /*
2 SPDX-FileCopyrightText: 2015 Milian Wolff <mail@milianw.de>
3
4 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #include "test_ktexteditorpluginintegration.h"
8
9 #include <QTest>
10 #include <QLoggingCategory>
11 #include <QSignalSpy>
12
13 #include <tests/autotestshell.h>
14 #include <tests/testcore.h>
15
16 #include <shell/plugincontroller.h>
17 #include <shell/uicontroller.h>
18
19 #include <KTextEditor/Application>
20 #include <KTextEditor/Editor>
21 #include <KTextEditor/MainWindow>
22 #include <KTextEditor/Plugin>
23 #include <KTextEditor/View>
24 #include <KTextEditor/Document>
25
26 using namespace KDevelop;
27
28 namespace {
29 template<typename T>
makeQPointer(T * ptr)30 QPointer<T> makeQPointer(T *ptr)
31 {
32 return {ptr};
33 }
34
findToolView(const QString & id)35 IToolViewFactory *findToolView(const QString &id)
36 {
37 const auto uiController = Core::self()->uiControllerInternal();
38 const auto map = uiController->factoryDocuments();
39 for (auto it = map.begin(); it != map.end(); ++it) {
40 if (it.key()->id() == id) {
41 return it.key();
42 }
43 }
44 return nullptr;
45 }
46
47 class TestPlugin : public KTextEditor::Plugin
48 {
49 Q_OBJECT
50 public:
TestPlugin(QObject * parent)51 explicit TestPlugin(QObject *parent)
52 : Plugin(parent)
53 {
54 }
55
createView(KTextEditor::MainWindow * mainWindow)56 QObject *createView(KTextEditor::MainWindow * mainWindow) override
57 {
58 return new QObject(mainWindow);
59 }
60 };
61 }
62
initTestCase()63 void TestKTextEditorPluginIntegration::initTestCase()
64 {
65 QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\n"));
66 AutoTestShell::init({QStringLiteral("katesnippetsplugin")});
67 TestCore::initialize();
68 QVERIFY(KTextEditor::Editor::instance());
69 }
70
cleanupTestCase()71 void TestKTextEditorPluginIntegration::cleanupTestCase()
72 {
73 auto controller = Core::self()->pluginController();
74 const auto id = QStringLiteral("katesnippetsplugin");
75 auto plugin = makeQPointer(controller->loadPlugin(id));
76
77 const auto editor = makeQPointer(KTextEditor::Editor::instance());
78 const auto application = makeQPointer(editor->application());
79 const auto window = makeQPointer(application->activeMainWindow());
80
81 TestCore::shutdown();
82
83 QVERIFY(!plugin);
84 QVERIFY(!window);
85 QVERIFY(!application);
86
87 // editor lives by design until QCoreApplication terminates, then autodeletes
88 }
89
testApplication()90 void TestKTextEditorPluginIntegration::testApplication()
91 {
92 auto app = KTextEditor::Editor::instance()->application();
93 QVERIFY(app);
94 QVERIFY(app->parent());
95 QCOMPARE(app->parent()->metaObject()->className(), "KTextEditorIntegration::Application");
96 QVERIFY(app->activeMainWindow());
97 QCOMPARE(app->mainWindows().size(), 1);
98 QVERIFY(app->mainWindows().contains(app->activeMainWindow()));
99 }
100
testMainWindow()101 void TestKTextEditorPluginIntegration::testMainWindow()
102 {
103 auto window = KTextEditor::Editor::instance()->application()->activeMainWindow();
104 QVERIFY(window);
105 QVERIFY(window->parent());
106 QCOMPARE(window->parent()->metaObject()->className(), "KTextEditorIntegration::MainWindow");
107
108 const auto id = QStringLiteral("kte_integration_toolview");
109 const auto icon = QIcon::fromTheme(QStringLiteral("kdevelop"));
110 const auto text = QStringLiteral("some text");
111 QVERIFY(!findToolView(id));
112
113 auto plugin = new TestPlugin(this);
114 auto toolView = makeQPointer(window->createToolView(plugin, id, KTextEditor::MainWindow::Bottom, icon, text));
115 QVERIFY(toolView);
116
117 auto factory = findToolView(id);
118 QVERIFY(factory);
119
120 // we reuse the same view
121 QWidget parent;
122 auto kdevToolView = makeQPointer(factory->create(&parent));
123 QCOMPARE(kdevToolView->parentWidget(), &parent);
124 QCOMPARE(toolView->parentWidget(), kdevToolView.data());
125
126 // the children are kept alive when the tool view gets destroyed
127 delete kdevToolView;
128 QVERIFY(toolView);
129 kdevToolView = factory->create(&parent);
130 // and we reuse the ktexteditor tool view for the new kdevelop tool view
131 QCOMPARE(toolView->parentWidget(), kdevToolView.data());
132
133 delete toolView;
134 delete kdevToolView;
135
136 delete plugin;
137 QVERIFY(!findToolView(id));
138 }
139
testPlugin()140 void TestKTextEditorPluginIntegration::testPlugin()
141 {
142 auto controller = Core::self()->pluginController();
143 const auto id = QStringLiteral("katesnippetsplugin");
144 auto plugin = makeQPointer(controller->loadPlugin(id));
145 if (!plugin) {
146 QSKIP("Cannot continue without katesnippetsplugin, install Kate");
147 }
148
149 auto app = KTextEditor::Editor::instance()->application();
150 auto ktePlugin = makeQPointer(app->plugin(id));
151 QVERIFY(ktePlugin);
152
153 auto view = makeQPointer(app->activeMainWindow()->pluginView(id));
154 QVERIFY(view);
155 const auto rawView = view.data();
156
157 QSignalSpy spy(app->activeMainWindow(), &KTextEditor::MainWindow::pluginViewDeleted);
158 QVERIFY(controller->unloadPlugin(id));
159 QVERIFY(!ktePlugin);
160 QCOMPARE(spy.count(), 1);
161 QCOMPARE(spy.first().count(), 2);
162 QCOMPARE(spy.first().at(0), QVariant::fromValue(id));
163 QCOMPARE(spy.first().at(1), QVariant::fromValue(rawView));
164 QVERIFY(!view);
165 }
166
testPluginUnload()167 void TestKTextEditorPluginIntegration::testPluginUnload()
168 {
169 auto controller = Core::self()->pluginController();
170 const auto id = QStringLiteral("katesnippetsplugin");
171 auto plugin = makeQPointer(controller->loadPlugin(id));
172 if (!plugin) {
173 QSKIP("Cannot continue without katesnippetsplugin, install Kate");
174 }
175
176 auto app = KTextEditor::Editor::instance()->application();
177 auto ktePlugin = makeQPointer(app->plugin(id));
178 QVERIFY(ktePlugin);
179 delete ktePlugin;
180 // don't crash
181 plugin->unload();
182 }
183
184 QTEST_MAIN(TestKTextEditorPluginIntegration)
185
186 #include <test_ktexteditorpluginintegration.moc>
187