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