1/* This file is part of the KDE libraries
2 Copyright (C) 2008 Niko Sams <niko.sams\gmail.com>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "completion_test.h"
21
22#include "codecompletiontestmodels.h"
23#include "codecompletiontestmodels.moc"
24
25#include <qtest_kde.h>
26#include <ksycoca.h>
27
28#include <ktexteditor/document.h>
29#include <ktexteditor/editorchooser.h>
30
31#include <kateview.h>
32#include <katecompletionwidget.h>
33#include <katecompletionmodel.h>
34#include <katecompletiontree.h>
35#include <katerenderer.h>
36#include <kateconfig.h>
37
38QTEST_KDEMAIN(CompletionTest, GUI)
39
40
41using namespace KTextEditor;
42
43int countItems(KateCompletionModel *model)
44{
45 int ret = 0;
46 for (int i=0; i < model->rowCount(QModelIndex()); ++i) {
47 ret += model->rowCount(model->index(i, 0));
48 }
49 return ret;
50}
51
52static void verifyCompletionStarted(KateView* view)
53{
54 const QDateTime startTime = QDateTime::currentDateTime();
55 while (startTime.msecsTo(QDateTime::currentDateTime()) < 1000)
56 {
57 QApplication::processEvents();
58 if (view->completionWidget()->isCompletionActive())
59 {
60 break;
61 }
62 }
63 QVERIFY(view->completionWidget()->isCompletionActive());
64}
65
66static void verifyCompletionAborted(KateView* view)
67{
68 const QDateTime startTime = QDateTime::currentDateTime();
69 while (startTime.msecsTo(QDateTime::currentDateTime()) < 1000)
70 {
71 QApplication::processEvents();
72 if (!view->completionWidget()->isCompletionActive())
73 {
74 break;
75 }
76 }
77 QVERIFY(!view->completionWidget()->isCompletionActive());
78}
79
80static void invokeCompletionBox(KateView* view)
81{
82 view->userInvokedCompletion();
83 verifyCompletionStarted(view);
84}
85
86void CompletionTest::init()
87{
88 if ( !KSycoca::isAvailable() )
89 QSKIP( "ksycoca not available", SkipAll );
90
91 Editor* editor = EditorChooser::editor();
92 QVERIFY(editor);
93
94 m_doc = editor->createDocument(this);
95 QVERIFY(m_doc);
96 m_doc->setText("aa bb cc\ndd");
97
98 KTextEditor::View *v = m_doc->createView(0);
99 QApplication::setActiveWindow(v);
100 m_view = static_cast<KateView*>(v);
101 Q_ASSERT(m_view);
102
103 //view needs to be shown as completion won't work if the cursor is off screen
104 m_view->show();
105}
106
107void CompletionTest::cleanup()
108{
109 delete m_view;
110 delete m_doc;
111}
112
113void CompletionTest::testFilterEmptyRange()
114{
115 KateCompletionModel *model = m_view->completionWidget()->model();
116
117 new CodeCompletionTestModel(m_view, "a");
118 m_view->setCursorPosition(Cursor(0, 0));
119 invokeCompletionBox(m_view);
120
121 QCOMPARE(countItems(model), 40);
122 m_view->insertText("aa");
123 QTest::qWait(1000); // process events
124 QCOMPARE(countItems(model), 14);
125}
126
127void CompletionTest::testFilterWithRange()
128{
129 KateCompletionModel *model = m_view->completionWidget()->model();
130
131 CodeCompletionTestModel* testModel = new CodeCompletionTestModel(m_view, "a");
132 m_view->setCursorPosition(Cursor(0, 2));
133 invokeCompletionBox(m_view);
134
135 Range complRange = *m_view->completionWidget()->completionRange(testModel);
136 QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 2)));
137 QCOMPARE(countItems(model), 14);
138
139 m_view->insertText("a");
140 QTest::qWait(1000); // process events
141 QCOMPARE(countItems(model), 1);
142}
143
144
145void CompletionTest::testAbortCursorMovedOutOfRange()
146{
147 KateCompletionModel *model = m_view->completionWidget()->model();
148
149 new CodeCompletionTestModel(m_view, "a");
150 m_view->setCursorPosition(Cursor(0, 2));
151 invokeCompletionBox(m_view);
152
153 QCOMPARE(countItems(model), 14);
154 QVERIFY(m_view->completionWidget()->isCompletionActive());
155
156 m_view->setCursorPosition(Cursor(0, 4));
157 QTest::qWait(1000); // process events
158 QVERIFY(!m_view->completionWidget()->isCompletionActive());
159}
160
161void CompletionTest::testAbortInvalidText()
162{
163 KateCompletionModel *model = m_view->completionWidget()->model();
164
165 new CodeCompletionTestModel(m_view, "a");
166 m_view->setCursorPosition(Cursor(0, 2));
167 invokeCompletionBox(m_view);
168
169 QCOMPARE(countItems(model), 14);
170 QVERIFY(m_view->completionWidget()->isCompletionActive());
171
172 m_view->insertText(".");
173 verifyCompletionAborted(m_view);
174}
175
176void CompletionTest::testCustomRange1()
177{
178 m_doc->setText("$aa bb cc\ndd");
179 KateCompletionModel *model = m_view->completionWidget()->model();
180
181 CodeCompletionTestModel* testModel = new CustomRangeModel(m_view, "$a");
182 m_view->setCursorPosition(Cursor(0, 3));
183 invokeCompletionBox(m_view);
184
185 Range complRange = *m_view->completionWidget()->completionRange(testModel);
186 kDebug() << complRange;
187 QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 3)));
188 QCOMPARE(countItems(model), 14);
189
190 m_view->insertText("a");
191 QTest::qWait(1000); // process events
192 QCOMPARE(countItems(model), 1);
193}
194
195void CompletionTest::testCustomRange2()
196{
197 m_doc->setText("$ bb cc\ndd");
198 KateCompletionModel *model = m_view->completionWidget()->model();
199
200 CodeCompletionTestModel* testModel = new CustomRangeModel(m_view, "$a");
201 m_view->setCursorPosition(Cursor(0, 1));
202 invokeCompletionBox(m_view);
203
204 Range complRange = *m_view->completionWidget()->completionRange(testModel);
205 QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 1)));
206 QCOMPARE(countItems(model), 40);
207
208 m_view->insertText("aa");
209 QTest::qWait(1000); // process events
210 QCOMPARE(countItems(model), 14);
211}
212
213void CompletionTest::testCustomRangeMultipleModels()
214{
215 m_doc->setText("$a bb cc\ndd");
216 KateCompletionModel *model = m_view->completionWidget()->model();
217
218 CodeCompletionTestModel* testModel1 = new CustomRangeModel(m_view, "$a");
219 CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "a");
220 m_view->setCursorPosition(Cursor(0, 1));
221 invokeCompletionBox(m_view);
222
223 QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel1)), Range(Cursor(0, 0), Cursor(0, 2)));
224 QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel2)), Range(Cursor(0, 1), Cursor(0, 2)));
225 QCOMPARE(model->currentCompletion(testModel1), QString("$"));
226 QCOMPARE(model->currentCompletion(testModel2), QString(""));
227 QCOMPARE(countItems(model), 80);
228
229
230 m_view->insertText("aa");
231 QTest::qWait(1000); // process events
232 QCOMPARE(model->currentCompletion(testModel1), QString("$aa"));
233 QCOMPARE(model->currentCompletion(testModel2), QString("aa"));
234 QCOMPARE(countItems(model), 14*2);
235}
236
237void CompletionTest::testAbortController()
238{
239 KateCompletionModel *model = m_view->completionWidget()->model();
240
241 new CustomRangeModel(m_view, "$a");
242 m_view->setCursorPosition(Cursor(0, 0));
243 invokeCompletionBox(m_view);
244
245 QCOMPARE(countItems(model), 40);
246 QVERIFY(m_view->completionWidget()->isCompletionActive());
247
248 m_view->insertText("$a");
249 QTest::qWait(1000); // process events
250 QVERIFY(m_view->completionWidget()->isCompletionActive());
251
252 m_view->insertText(".");
253 verifyCompletionAborted(m_view);
254}
255
256void CompletionTest::testAbortControllerMultipleModels()
257{
258 KateCompletionModel *model = m_view->completionWidget()->model();
259
260 CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "aa");
261 CodeCompletionTestModel* testModel2 = new CustomAbortModel(m_view, "a-");
262 m_view->setCursorPosition(Cursor(0, 0));
263 invokeCompletionBox(m_view);
264
265 QCOMPARE(countItems(model), 80);
266 QVERIFY(m_view->completionWidget()->isCompletionActive());
267
268 m_view->insertText("a");
269 QTest::qWait(1000); // process events
270 QVERIFY(m_view->completionWidget()->isCompletionActive());
271 QCOMPARE(countItems(model), 80);
272
273 m_view->insertText("-");
274 QTest::qWait(1000); // process events
275 QVERIFY(m_view->completionWidget()->isCompletionActive());
276 QVERIFY(!m_view->completionWidget()->completionRanges().contains(testModel1));
277 QVERIFY(m_view->completionWidget()->completionRanges().contains(testModel2));
278
279 QCOMPARE(countItems(model), 40);
280
281 m_view->insertText(" ");
282 QTest::qWait(1000); // process events
283 QVERIFY(!m_view->completionWidget()->isCompletionActive());
284}
285
286void CompletionTest::testEmptyFilterString()
287{
288 KateCompletionModel *model = m_view->completionWidget()->model();
289
290 new EmptyFilterStringModel(m_view, "aa");
291 m_view->setCursorPosition(Cursor(0, 0));
292 invokeCompletionBox(m_view);
293
294 QCOMPARE(countItems(model), 40);
295
296 m_view->insertText("a");
297 QTest::qWait(1000); // process events
298 QCOMPARE(countItems(model), 40);
299
300 m_view->insertText("bam");
301 QTest::qWait(1000); // process events
302 QCOMPARE(countItems(model), 40);
303}
304
305void CompletionTest::testUpdateCompletionRange()
306{
307 m_doc->setText("ab bb cc\ndd");
308 KateCompletionModel *model = m_view->completionWidget()->model();
309
310 CodeCompletionTestModel* testModel = new UpdateCompletionRangeModel(m_view, "ab ab");
311 m_view->setCursorPosition(Cursor(0, 3));
312 invokeCompletionBox(m_view);
313
314 QCOMPARE(countItems(model), 40);
315 QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel)), Range(Cursor(0, 3), Cursor(0, 3)));
316
317 m_view->insertText("ab");
318 QTest::qWait(1000); // process events
319 QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel)), Range(Cursor(0, 0), Cursor(0, 5)));
320 QCOMPARE(countItems(model), 40);
321}
322
323void CompletionTest::testCustomStartCompl()
324{
325 KateCompletionModel *model = m_view->completionWidget()->model();
326
327 m_view->completionWidget()->setAutomaticInvocationDelay(1);
328
329 new StartCompletionModel(m_view, "aa");
330
331 m_view->setCursorPosition(Cursor(0, 0));
332 m_view->insertText("%");
333 QTest::qWait(1000);
334
335 QVERIFY(m_view->completionWidget()->isCompletionActive());
336 QCOMPARE(countItems(model), 40);
337}
338
339void CompletionTest::testKateCompletionModel()
340{
341 KateCompletionModel *model = m_view->completionWidget()->model();
342 CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "aa");
343 CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "bb");
344
345 model->setCompletionModel(testModel1);
346 QCOMPARE(countItems(model), 40);
347
348 model->addCompletionModel(testModel2);
349 QCOMPARE(countItems(model), 80);
350
351 model->removeCompletionModel(testModel2);
352 QCOMPARE(countItems(model), 40);
353}
354
355void CompletionTest::testAbortImmideatelyAfterStart()
356{
357 new ImmideatelyAbortCompletionModel(m_view);
358 m_view->setCursorPosition(Cursor(0, 3));
359 QVERIFY(!m_view->completionWidget()->isCompletionActive());
360 emit m_view->userInvokedCompletion();
361 QVERIFY(!m_view->completionWidget()->isCompletionActive());
362}
363
364void CompletionTest::testJumpToListBottomAfterCursorUpWhileAtTop()
365{
366 new CodeCompletionTestModel(m_view, "aa");
367 invokeCompletionBox(m_view);
368
369 m_view->completionWidget()->cursorUp();
370 m_view->completionWidget()->bottom();
371 // TODO - better way of finding the index?
372 QCOMPARE(m_view->completionWidget()->treeView()->selectionModel()->currentIndex().row(), 39);
373}
374
375void CompletionTest::testAbbreviationEngine()
376{
377 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fb"));
378 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "foob"));
379 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fbar"));
380 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fba"));
381 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "foba"));
382 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarBazBang", "fbbb"));
383 QVERIFY(KateCompletionModel::matchesAbbreviation("foo_bar_cat", "fbc"));
384 QVERIFY(KateCompletionModel::matchesAbbreviation("foo_bar_cat", "fb"));
385 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fba"));
386 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fbara"));
387 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fobaar"));
388 QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fb"));
389
390 QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qid"));
391 QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qualid"));
392 QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qualidentifier"));
393 QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qi"));
394 QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kcmodel"));
395 QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kc"));
396 QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kcomplmodel"));
397 QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kacomplmodel"));
398 QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kacom"));
399
400 QVERIFY(! KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "identifier"));
401 QVERIFY(! KateCompletionModel::matchesAbbreviation("FooBarArr", "fobaara"));
402 QVERIFY(! KateCompletionModel::matchesAbbreviation("FooBarArr", "fbac"));
403 QVERIFY(! KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kamodel"));
404
405 QVERIFY(KateCompletionModel::matchesAbbreviation("AbcdefBcdefCdefDefEfFzZ", "AbcdefBcdefCdefDefEfFzZ"));
406 QVERIFY(! KateCompletionModel::matchesAbbreviation("AbcdefBcdefCdefDefEfFzZ", "ABCDEFX"));
407 QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "XZYBFA"));
408}
409
410void CompletionTest::benchAbbreviationEngineGoodCase()
411{
412 QBENCHMARK {
413 for ( int i = 0; i < 10000; i++ ) {
414 QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "XZYBFA"));
415 }
416 }
417}
418
419void CompletionTest::benchAbbreviationEngineNormalCase()
420{
421 QBENCHMARK {
422 for ( int i = 0; i < 10000; i++ ) {
423 QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "ABCDEFX"));
424 }
425 }
426}
427
428void CompletionTest::benchAbbreviationEngineWorstCase()
429{
430 QBENCHMARK {
431 for ( int i = 0; i < 10000; i++ ) {
432 // This case is quite horrible, because it requires a branch at every letter.
433 // The current code will at some point drop out and just return false.
434 KateCompletionModel::matchesAbbreviation("XxBbbbbbBbbbbbBbbbbBbbbBbbbbbbBbbbbbBbbbbbBbbbFox", "XbbbbbbbbbbbbbbbbbbbbFx");
435 }
436 }
437}
438
439void CompletionTest::testAbbrevAndContainsMatching()
440{
441 KateCompletionModel *model = m_view->completionWidget()->model();
442
443 new AbbreviationCodeCompletionTestModel(m_view, QString());
444
445 m_view->document()->setText("SCA");
446 invokeCompletionBox(m_view);
447 QCOMPARE(model->filteredItemCount(), (uint) 6);
448
449 m_view->document()->setText("SC");
450 invokeCompletionBox(m_view);
451 QCOMPARE(model->filteredItemCount(), (uint) 6);
452
453 m_view->document()->setText("sca");
454 invokeCompletionBox(m_view);
455 QCOMPARE(model->filteredItemCount(), (uint) 6);
456
457 m_view->document()->setText("contains");
458 invokeCompletionBox(m_view);
459 QCOMPARE(model->filteredItemCount(), (uint) 2);
460
461 m_view->document()->setText("CONTAINS");
462 invokeCompletionBox(m_view);
463 QCOMPARE(model->filteredItemCount(), (uint) 2);
464
465 m_view->document()->setText("containssome");
466 invokeCompletionBox(m_view);
467 QCOMPARE(model->filteredItemCount(), (uint) 1);
468
469 m_view->document()->setText("matched");
470 m_view->userInvokedCompletion();
471 QApplication::processEvents();
472 QCOMPARE(model->filteredItemCount(), (uint) 0);
473}
474
475void CompletionTest::benchCompletionModel()
476{
477 const QString text("abcdefg abcdef");
478 m_doc->setText(text);
479 CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "abcdefg");
480 testModel1->setRowCount(500);
481 CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "abcdef");
482 testModel2->setRowCount(500);
483 CodeCompletionTestModel* testModel3 = new CodeCompletionTestModel(m_view, "abcde");
484 testModel3->setRowCount(500);
485 CodeCompletionTestModel* testModel4 = new CodeCompletionTestModel(m_view, "abcd");
486 testModel4->setRowCount(5000);
487 QBENCHMARK_ONCE {
488 for(int i = 0; i < text.size(); ++i) {
489 m_view->setCursorPosition(Cursor(0, i));
490 invokeCompletionBox(m_view);
491 }
492 }
493}
494
495#include "completion_test.moc"
496