test_codecompletion.cpp 56.9 KB
Newer Older
1 2
/*
 * Copyright 2014  David Stevens <dgedstevens@gmail.com>
3
 * Copyright 2014  Kevin Funk <kfunk@kde.org>
4
 * Copyright 2015 Milian Wolff <mail@milianw.de>
5
 * Copyright 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License or (at your option) version 3 or any later version
 * accepted by the membership of KDE e.V. (or its successor approved
 * by the membership of KDE e.V.), which shall act as a proxy
 * defined in Section 14 of version 3 of the license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

Kevin Funk's avatar
Kevin Funk committed
24
#include "test_codecompletion.h"
25
#include <language/backgroundparser/backgroundparser.h>
26 27 28 29

#include <tests/testcore.h>
#include <tests/autotestshell.h>
#include <tests/testfile.h>
30
#include <tests/testproject.h>
31

Milian Wolff's avatar
Milian Wolff committed
32 33 34
#include "duchain/parsesession.h"
#include "util/clangtypes.h"

35
#include <interfaces/idocumentcontroller.h>
36
#include <interfaces/ilanguagecontroller.h>
37

38
#include <language/codecompletion/codecompletiontesthelper.h>
39
#include <language/duchain/types/functiontype.h>
Milian Wolff's avatar
Milian Wolff committed
40 41 42 43

#include "codecompletion/completionhelper.h"
#include "codecompletion/context.h"
#include "codecompletion/includepathcompletioncontext.h"
44
#include "../clangsettings/clangsettingsmanager.h"
45

46 47 48 49
#include <KTextEditor/Editor>
#include <KTextEditor/Document>
#include <KTextEditor/View>

50 51
#include <KConfigGroup>

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
52
QTEST_MAIN(TestCodeCompletion)
53

54 55 56
static const auto NoMacroOrBuiltin = ClangCodeCompletionContext::ContextFilters(
    ClangCodeCompletionContext::NoBuiltins | ClangCodeCompletionContext::NoMacros);

57 58
using namespace KDevelop;

59 60 61
using ClangCodeCompletionItemTester = CodeCompletionItemTester<ClangCodeCompletionContext>;

struct CompletionItems {
62 63 64 65 66 67
    CompletionItems(){}
    CompletionItems(const KTextEditor::Cursor& position, const QStringList& completions, const QStringList& declarationItems = {})
        : position(position)
        , completions(completions)
        , declarationItems(declarationItems)
    {};
Kevin Funk's avatar
Kevin Funk committed
68
    KTextEditor::Cursor position;
69
    QStringList completions;
70
    QStringList declarationItems; ///< completion items that have associated declarations. Declarations with higher match quality at the top. @sa KTextEditor::CodeCompletionModel::MatchQuality
71 72
};
Q_DECLARE_TYPEINFO(CompletionItems, Q_MOVABLE_TYPE);
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
73
Q_DECLARE_METATYPE(CompletionItems)
74

75 76 77

struct CompletionPriorityItem
{
78
    CompletionPriorityItem(const QString& name, int matchQuality = 0, int inheritanceDepth = 0, const QString& failMessage = {})
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
        : name(name)
        , failMessage(failMessage)
        , matchQuality(matchQuality)
        , inheritanceDepth(inheritanceDepth)
    {}

    QString name;
    QString failMessage;
    int matchQuality;
    int inheritanceDepth;
};

struct CompletionPriorityItems : public CompletionItems
{
    CompletionPriorityItems(){}
    CompletionPriorityItems(const KTextEditor::Cursor& position, const QList<CompletionPriorityItem>& completions)
        : CompletionItems(position, {})
        , completions(completions)
    {};

    QList<CompletionPriorityItem> completions;
};

Q_DECLARE_TYPEINFO(CompletionPriorityItems, Q_MOVABLE_TYPE);
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
103
Q_DECLARE_METATYPE(CompletionPriorityItems)
104

105 106
namespace {

107 108 109 110 111 112 113
struct NoopTestFunction
{
    void operator()(const ClangCodeCompletionItemTester& /*tester*/) const
    {
    }
};

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
QString textForDocument(const QUrl& url, const KTextEditor::Cursor& position)
{
    bool close = false;

    auto* doc = ICore::self()->documentController()->documentForUrl(url);
    if (!doc) {
        doc = ICore::self()->documentController()->openDocument(url);
        close = true;
    }

    auto text = doc->textDocument()->text({{0, 0}, position});

    if (close) {
        doc->close(IDocument::Discard);
    }

    return text;
}

QExplicitlySharedDataPointer<ClangCodeCompletionContext> createContext(const ReferencedTopDUContext& top,
                                                                       const ParseSessionData::Ptr& sessionData,
                                                                       const KTextEditor::Cursor position,
                                                                       const QString& code = {})
{
    const auto url = top->url().toUrl();
    const auto text = code.isEmpty() ? textForDocument(url, position) : code;
    return QExplicitlySharedDataPointer<ClangCodeCompletionContext>{
        new ClangCodeCompletionContext(DUContextPointer(top), sessionData, url, position, text)};
}

144
template<typename CustomTestFunction = NoopTestFunction>
145
void executeCompletionTest(const ReferencedTopDUContext& top, const CompletionItems& expectedCompletionItems,
146 147
                           const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin,
                           CustomTestFunction customTestFunction = {})
148 149
{
    DUChainReadLocker lock;
Kevin Funk's avatar
Kevin Funk committed
150 151
    const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
    QVERIFY(sessionData);
152
    lock.unlock();
153
    // TODO: We should not need to pass 'session' to the context, should just use the base class ctor
154
    auto context = createContext(top, sessionData, expectedCompletionItems.position);
155 156
    context->setFilters(filters);
    lock.lock();
157

158
    auto tester = ClangCodeCompletionItemTester(context);
159

160
    int previousMatchQuality = 10;
161 162 163 164
    for(const auto& declarationName : expectedCompletionItems.declarationItems){
        const auto declarationItem = tester.findItem(declarationName);
        QVERIFY(declarationItem);
        QVERIFY(declarationItem->declaration());
165 166 167 168

        auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt();
        QVERIFY(matchQuality <= previousMatchQuality);
        previousMatchQuality = matchQuality;
169
    }
170 171

    tester.names.sort();
172
    QEXPECT_FAIL("look-ahead function primary type argument", "No API in LibClang to determine expected code completion type", Continue);
173
    QEXPECT_FAIL("look-ahead template parameter substitution", "No parameters substitution so far", Continue);
174
#if CINDEX_VERSION_MINOR < 30
175
    QEXPECT_FAIL("look-ahead auto item", "Auto type, like many other types, is not exposed through LibClang. We assign DelayedType to it instead of IdentifiedType", Continue);
176
#endif
177 178 179
    if (QTest::currentTestFunction() == QByteArrayLiteral("testImplementAfterEdit") && expectedCompletionItems.position.line() == 3) {
        QEXPECT_FAIL("", "TU is not properly updated after edit", Continue);
    }
180 181 182
    if (tester.names.size() != expectedCompletionItems.completions.size()) {
        qDebug() << "different results:\nactual:" << tester.names << "\nexpected:" << expectedCompletionItems.completions;
    }
183
    QCOMPARE(tester.names, expectedCompletionItems.completions);
184 185

    customTestFunction(tester);
186
}
187

188
template<typename CustomTestFunction = NoopTestFunction>
189
void executeCompletionTest(const QString& code, const CompletionItems& expectedCompletionItems,
190 191
                           const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin,
                           CustomTestFunction customTestFunction = {})
192
{
193
    TestFile file(code, QStringLiteral("cpp"));
194
    QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
195
    executeCompletionTest(file.topContext(), expectedCompletionItems, filters, customTestFunction);
196 197
}

198
void executeCompletionPriorityTest(const QString& code, const CompletionPriorityItems& expectedCompletionItems,
199
                           const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin)
200
{
201
    TestFile file(code, QStringLiteral("cpp"));
202 203 204 205 206 207 208 209 210 211
    QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
    DUChainReadLocker lock;
    auto top = file.topContext();
    QVERIFY(top);
    const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
    QVERIFY(sessionData);

    // don't hold DUChain lock when constructing ClangCodeCompletionContext
    lock.unlock();

212
    auto context = createContext(top, sessionData, expectedCompletionItems.position);
213 214 215
    context->setFilters(filters);

    lock.lock();
216
    auto tester = ClangCodeCompletionItemTester(context);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

    for(const auto& declaration : expectedCompletionItems.completions){
        const auto declarationItem = tester.findItem(declaration.name);
        QVERIFY(declarationItem);
        QVERIFY(declarationItem->declaration());

        auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt();
        auto inheritanceDepth = declarationItem->inheritanceDepth();

        if(!declaration.failMessage.isEmpty()){
            QEXPECT_FAIL("", declaration.failMessage.toUtf8().constData(), Continue);
        }
        QVERIFY(matchQuality == declaration.matchQuality && inheritanceDepth == declaration.inheritanceDepth);
    }
}

233
void executeMemberAccessReplacerTest(const QString& code, const CompletionItems& expectedCompletionItems,
234
                                     const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin)
235
{
236
    TestFile file(code, QStringLiteral("cpp"));
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

    auto document = ICore::self()->documentController()->openDocument(file.url().toUrl());
    QVERIFY(document);
    ICore::self()->documentController()->activateDocument(document);
    auto view = ICore::self()->documentController()->activeTextDocumentView();
    Q_ASSERT(view);
    view->setCursorPosition(expectedCompletionItems.position);

    QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
    DUChainReadLocker lock;
    auto top = file.topContext();
    QVERIFY(top);
    const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
    QVERIFY(sessionData);

    lock.unlock();

254
    auto context = createContext(top, sessionData, expectedCompletionItems.position, code);
255 256 257

    QApplication::processEvents();
    document->close(KDevelop::IDocument::Silent);
258

259
    // The previous ClangCodeCompletionContext call should replace member access.
260 261
    // That triggers an update request in the duchain which we are not interested in,
    // so let's stop that request.
262
    ICore::self()->languageController()->backgroundParser()->removeDocument(file.url());
263

264
    context = createContext(top, sessionData, expectedCompletionItems.position);
265 266
    context->setFilters(filters);
    lock.lock();
Kevin Funk's avatar
Kevin Funk committed
267
    auto tester = ClangCodeCompletionItemTester(context);
268 269 270 271 272

    tester.names.sort();
    QCOMPARE(tester.names, expectedCompletionItems.completions);
}

273 274 275
using IncludeTester = CodeCompletionItemTester<IncludePathCompletionContext>;

QExplicitlySharedDataPointer<IncludePathCompletionContext> executeIncludePathCompletion(TestFile* file, const KTextEditor::Cursor& position)
276
{
277 278 279 280
    if (!file->parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)) {
        QTest::qFail("Failed to parse source file.", __FILE__, __LINE__);
        return {};
    }
281 282 283

    DUChainReadLocker lock;
    auto top = file->topContext();
284 285 286 287
    if (!top) {
        QTest::qFail("Failed to parse source file.", __FILE__, __LINE__);
        return {};
    }
288
    const ParseSessionData::Ptr sessionData(dynamic_cast<ParseSessionData*>(top->ast().data()));
289 290 291 292
    if (!sessionData) {
        QTest::qFail("Failed to acquire parse session data.", __FILE__, __LINE__);
        return {};
    }
293 294 295 296 297

    DUContextPointer topPtr(top);

    lock.unlock();

298 299 300 301 302 303 304 305 306 307 308
    auto text = file->fileContents();
    int textLength = -1;
    if (position.isValid()) {
        textLength = 0;
        for (int i = 0; i < position.line(); ++i) {
            textLength = text.indexOf('\n', textLength) + 1;
        }
        textLength += position.column();
    }
    auto context = new IncludePathCompletionContext(topPtr, sessionData, file->url().toUrl(), position, text.mid(0, textLength));
    return QExplicitlySharedDataPointer<IncludePathCompletionContext>{context};
309 310
}

311 312
}

313 314 315
void TestCodeCompletion::testClangCodeCompletion()
{
    QFETCH(QString, code);
316
    QFETCH(CompletionItems, expectedItems);
317 318 319 320 321 322 323

    executeCompletionTest(code, expectedItems);
}

void TestCodeCompletion::testClangCodeCompletion_data()
{
    QTest::addColumn<QString>("code");
324
    QTest::addColumn<CompletionItems>("expectedItems");
325

Kevin Funk's avatar
Kevin Funk committed
326 327
    QTest::newRow("assignment")
        << "int foo = 5; \nint bar = "
328
        << CompletionItems{{1,9}, {
Kevin Funk's avatar
Kevin Funk committed
329 330
            "bar",
            "foo",
331
        }, {"bar","foo"}};
Kevin Funk's avatar
Kevin Funk committed
332
    QTest::newRow("dotmemberaccess")
333
        << "class Foo { public: void foo() {} bool operator=(Foo &&) }; int main() { Foo f; \nf. "
334
        << CompletionItems{{1, 2}, {
335
            "foo",
336 337
            "operator="
        }, {"foo",  "operator="}};
Kevin Funk's avatar
Kevin Funk committed
338 339
    QTest::newRow("arrowmemberaccess")
        << "class Foo { public: void foo() {} }; int main() { Foo* f = new Foo; \nf-> }"
340
        << CompletionItems{{1, 3}, {
341
            "foo"
342
        }, {"foo"}};
Kevin Funk's avatar
Kevin Funk committed
343
    QTest::newRow("enum-case")
Kevin Funk's avatar
Kevin Funk committed
344
        << "enum Foo { foo, bar }; int main() { Foo f; switch (f) {\ncase "
345
        << CompletionItems{{1,4}, {
Kevin Funk's avatar
Kevin Funk committed
346 347
            "bar",
            "foo",
348
        }, {"foo", "bar"}};
349 350 351
    QTest::newRow("only-private")
        << "class SomeStruct { private: void priv() {} };\n"
           "int main() { SomeStruct s;\ns. "
352 353
        << CompletionItems{{2, 2}, {
        }};
354 355 356
    QTest::newRow("private-friend")
        << "class SomeStruct { private: void priv() {} friend int main(); };\n"
           "int main() { SomeStruct s;\ns. "
357
        << CompletionItems{{2, 2}, {
358 359
            "priv",
        }, {"priv"}};
360 361 362
    QTest::newRow("private-public")
        << "class SomeStruct { public: void pub() {} private: void priv() {} };\n"
           "int main() { SomeStruct s;\ns. "
363
        << CompletionItems{{2, 2}, {
364 365
            "pub",
        }, {"pub"}};
366 367 368
    QTest::newRow("protected-public")
        << "class SomeStruct { public: void pub() {} protected: void prot() {} };\n"
           "int main() { SomeStruct s;\ns. "
369
        << CompletionItems{{2, 2}, {
370 371
            "pub",
        }, {"pub"}};
372 373 374
    QTest::newRow("localVariable")
        << "int main() { int localVariable;\nloc "
        << CompletionItems{{1, 3},
375 376
            {"localVariable","main"},
            {"localVariable", "main"}
377 378 379 380
        };
    QTest::newRow("globalVariable")
        << "int globalVariable;\nint main() { \ngl "
        << CompletionItems{{2, 2},
381 382
            {"globalVariable","main"},
            {"globalVariable", "main"}
383 384 385 386 387 388 389 390 391 392
        };
    QTest::newRow("namespaceVariable")
        << "namespace NameSpace{int variable};\nint main() { \nNameSpace:: "
        << CompletionItems{{2, 11},
            {"variable"},
            {"variable"}
        };
    QTest::newRow("parentVariable")
        << "class A{public: int m_variable;};class B : public A{};\nint main() { B b;\nb. "
        << CompletionItems{{2, 2},
393
            {"m_variable"},
394
            {"m_variable"}
395
        };
396 397 398
    QTest::newRow("itemsPriority")
        << "class A; class B; void f(A); int main(){ A c; B b;f(\n} "
        << CompletionItems{{1, 0},
399 400
            {"A", "B", "b", "c", "f",
#if CINDEX_VERSION_MINOR >= 30
401
             "f",
402 403
#endif
                         "main"},
404 405
            {"c", "A", "b", "B"}
    };
406 407 408 409 410 411
    QTest::newRow("function-arguments")
        << "class Abc; int f(Abc){\n "
        << CompletionItems{{1, 0}, {
            "Abc",
            "f",
        }};
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
    QTest::newRow("look-ahead int")
        << "struct LookAhead { int intItem;}; int main() {LookAhead* pInstance; LookAhead instance; int i =\n }"
        << CompletionItems{{1, 0}, {
            "LookAhead", "i", "instance",
            "instance.intItem", "main",
            "pInstance", "pInstance->intItem",
        }};
    QTest::newRow("look-ahead class")
        << "class Class{}; struct LookAhead {Class classItem;}; int main() {LookAhead* pInstance; LookAhead instance; Class cl =\n }"
        << CompletionItems{{1, 0}, {
            "Class", "LookAhead", "cl",
            "instance", "instance.classItem",
            "main", "pInstance", "pInstance->classItem",
        }};
    QTest::newRow("look-ahead function argument")
        << "class Class{}; struct LookAhead {Class classItem;}; void function(Class cl);"
           "int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
        << CompletionItems{{1, 0}, {
            "Class", "LookAhead", "function",
431
#if CINDEX_VERSION_MINOR >= 30
432
            "function",
433
#endif
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
            "instance", "instance.classItem",
            "main", "pInstance", "pInstance->classItem",
        }};
    QTest::newRow("look-ahead function primary type argument")
        << "struct LookAhead {double doubleItem;}; void function(double double);"
           "int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
        << CompletionItems{{1, 0}, {
            "LookAhead", "function", "instance",
            "instance.doubleItem", "main",
            "pInstance", "pInstance->doubleItem",
        }};
    QTest::newRow("look-ahead typedef")
        << "typedef double DOUBLE; struct LookAhead {DOUBLE doubleItem;};"
           "int main() {LookAhead* pInstance; LookAhead instance; double i =\n "
        << CompletionItems{{1, 0}, {
            "DOUBLE", "LookAhead", "i",
            "instance", "instance.doubleItem",
            "main", "pInstance", "pInstance->doubleItem",
        }};
    QTest::newRow("look-ahead pointer")
        << "struct LookAhead {int* pInt;};"
           "int main() {LookAhead* pInstance; LookAhead instance; int* i =\n "
        << CompletionItems{{1, 0}, {
            "LookAhead", "i", "instance",
            "instance.pInt", "main",
            "pInstance", "pInstance->pInt",
        }};
    QTest::newRow("look-ahead template")
        << "template <typename T> struct LookAhead {int intItem;};"
           "int main() {LookAhead<int>* pInstance; LookAhead<int> instance; int i =\n "
        << CompletionItems{{1, 0}, {
            "LookAhead", "i", "instance",
            "instance.intItem", "main",
            "pInstance", "pInstance->intItem",
        }};
469 470 471 472 473 474 475 476
    QTest::newRow("look-ahead template parameter substitution")
        << "template <typename T> struct LookAhead {T itemT;};"
           "int main() {LookAhead<int>* pInstance; LookAhead<int> instance; int i =\n "
        << CompletionItems{{1, 0}, {
            "LookAhead", "i", "instance",
            "instance.itemT", "main",
            "pInstance", "pInstance->itemT",
        }};
477
    QTest::newRow("look-ahead item access")
478 479 480 481 482 483 484 485
        << "class Class { public: int publicInt; protected: int protectedInt; private: int privateInt;};"
           "int main() {Class cl; int i =\n "
        << CompletionItems{{1, 0}, {
            "Class", "cl",
            "cl.publicInt",
            "i", "main",
        }};

486
    QTest::newRow("look-ahead auto item")
487 488 489 490 491 492
        << "struct LookAhead { int intItem; };"
           "int main() {auto instance = LookAhead(); int i = \n "
        << CompletionItems{{1, 0}, {
            "LookAhead",
            "i",
            "instance",
493 494
            "instance.intItem",
            "main"
495
        }};
496 497 498 499 500 501 502 503 504

    QTest::newRow("variadic template recursive class")
        << R"(
template <typename Head, typename ...Tail>
struct my_class : Head, my_class<Tail...>
{
    using base = Head;
};)"
        << CompletionItems{{3, 17}, { "Head", "Tail", "my_class" }};
505 506
}

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
void TestCodeCompletion::testReplaceMemberAccess()
{
    QFETCH(QString, code);
    QFETCH(CompletionItems, expectedItems);

    executeMemberAccessReplacerTest(code, expectedItems);
}

void TestCodeCompletion::testReplaceMemberAccess_data()
{
    QTest::addColumn<QString>("code");
    QTest::addColumn<CompletionItems>("expectedItems");

    QTest::newRow("replace arrow to dot")
    <<  "struct Struct { void function(); };"
        "int main() { Struct s; \ns-> "
    << CompletionItems{{1, 3}, {
        "function"
    }};

    QTest::newRow("replace dot to arrow")
    <<  "struct Struct { void function(); };"
529 530
        "int main() { Struct* s; \ns.  "
    << CompletionItems{{1, 3}, {
531 532
        "function"
    }};
533 534 535 536 537

    QTest::newRow("no replacement needed")
    <<  "int main() { double a = \n0.  "
    << CompletionItems{{1, 2}, {
    }};
538 539
}

540
void TestCodeCompletion::testVirtualOverride()
541
{
542
    QFETCH(QString, code);
543
    QFETCH(CompletionItems, expectedItems);
544

Kevin Funk's avatar
Kevin Funk committed
545
    executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion);
546 547
}

548 549 550
void TestCodeCompletion::testVirtualOverride_data()
{
    QTest::addColumn<QString>("code");
551
    QTest::addColumn<CompletionItems>("expectedItems");
552 553

    QTest::newRow("basic")
554 555
        <<  "class Foo { virtual void foo(); virtual void foo(char c); virtual char foo(char c, int i, double d); };\n"
            "class Bar : Foo \n{void foo(char c) override;\n}"
556
        << CompletionItems{{3, 1}, {"foo()", "foo(char c, int i, double d)"}};
557 558

    QTest::newRow("template")
559 560
        << "template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a); } ;\n"
           "class Bar : Foo<char, double> \n{double overridden(char a) override;\n}"
561
        << CompletionItems{{3, 1}, {"foo(char a, double b, int i)"}};
562 563

    QTest::newRow("nested-template")
564
        << "template<class T1, class T2> class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a, T2 b, int i); } ;\n"
565
           "template<class T1, class T2> class Baz { };\n"
566
           "class Bar : Foo<char, Baz<char, double>> \n{Baz<char, double> overridden(char a, Baz<char, double> b, int i) override;\n}"
567
        << CompletionItems{{4, 1}, {"foo(char a, Baz<char, double> b, int i)"}};
568 569

    QTest::newRow("multi")
570
        << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
571
           "class Baz { virtual char baz(char c); };\n"
572
           "class Bar : Foo, Baz \n{int overridden(int i) override;\n}"
573
        << CompletionItems{{4, 1}, {"baz(char c)", "foo(int i)"}};
574 575

    QTest::newRow("deep")
576
        << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
577
           "class Baz : Foo { };\n"
578
           "class Bar : Baz \n{int overridden(int i) override;\n}"
579
        << CompletionItems{{4, 1}, {"foo(int i)"}};
580

581 582 583 584
    QTest::newRow("repeated")
        << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n"
           "class Baz : Foo { int foo(int i) override; };\n"
           "class Bar : Baz \n{int overridden(int i) override;\n}"
585
        << CompletionItems{{4, 1}, {"foo(int i)"}};
586 587

    QTest::newRow("pure")
588 589 590
        << "class Foo { virtual void foo() = 0; virtual void overridden() = 0;};\n"
           "class Bar : Foo \n{void overridden() override;\n};"
        << CompletionItems{{3, 0}, {"foo() = 0"}};
591

592 593 594 595 596 597
    QTest::newRow("repeated-pure")
        << "class Foo { virtual void foo() = 0; virtual void overridden() = 0; };\n"
           "class Baz : Foo { void foo() override; };\n"
           "class Bar : Baz \n{void overridden() override;\n}"
        << CompletionItems{{4, 1}, {"foo()"}};

598
    QTest::newRow("const")
599 600
        << "class Foo { virtual void foo(const int b) const; virtual void overridden(const int b) const; }\n;"
           "class Bar : Foo \n{void overridden(const int b) const override;\n}"
601
        << CompletionItems{{3, 1}, {"foo(const int b) const"}};
602 603 604 605 606 607 608 609 610 611 612 613

    QTest::newRow("dont-override")
        << R"(class A {
                  virtual void something() = 0;
              };
              class B : public A
              {
              public:
                  void foo();
              };
              void B::foo() {}
        )" << CompletionItems{{8, 14}, {}};
614 615
}

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
void TestCodeCompletion::testOverrideExecute()
{
    QFETCH(QString, code);
    QFETCH(CompletionItems, expectedItems);
    QFETCH(QString, itemToExecute);
    QFETCH(QString, cppStandard);
    QFETCH(QString, expectedCode);

    QTemporaryDir directory;
    TestProject testProject {Path{directory.path()}};
    auto t = testProject.path().toLocalFile();
    auto configGroup = testProject.projectConfiguration()->group("CustomDefinesAndIncludes").group("ProjectPath0");
    configGroup.writeEntry("Path", ".");
    configGroup.writeEntry("parserArguments", cppStandard);
    configGroup.sync();
    m_projectController->addProject(&testProject);

    TestFile file(code, QStringLiteral("cpp"), &testProject, directory.path());
    QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));

    auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) {
        auto item = tester.findItem(itemToExecute);
        QVERIFY(item);
        auto view = createView(tester.completionContext->duContext()->url().toUrl(), this);
        item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position));
        QCOMPARE(view->document()->text(), expectedCode);
    };
    executeCompletionTest(file.topContext(), expectedItems, NoMacroOrBuiltin, executeItem);
    m_projectController->closeProject(&testProject);
}

void TestCodeCompletion::testOverrideExecute_data()
{
    QTest::addColumn<QString>("code");
    QTest::addColumn<CompletionItems>("expectedItems");
    QTest::addColumn<QString>("itemToExecute");
    QTest::addColumn<QString>("cppStandard");
    QTest::addColumn<QString>("expectedCode");

    QTest::newRow("override-modern")
        << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
        << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
        << "bar(char c)"
        << "-std=c++11"
        << "class Foo { virtual int bar(char c); };\n"
           "class Baz : Foo\n{\nint bar(char c) override;};";

    QTest::newRow("override-non-modern")
        << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
        << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
        << "bar(char c)"
        << "-std=c++98"
        << "class Foo { virtual int bar(char c); };\n"
           "class Baz : Foo\n{\nint bar(char c);};";

     QTest::newRow("override-unknown")
        << "class Foo { virtual int bar(char c); };\nclass Baz : Foo\n{\n};"
        << CompletionItems{{3, 0}, {"Baz", "Foo", "bar(char c)"}}
        << "bar(char c)"
        << "-std=c++1z"
        << "class Foo { virtual int bar(char c); };\n"
           "class Baz : Foo\n{\nint bar(char c) override;};";
}


681
void TestCodeCompletion::testImplement()
682
{
683
    QFETCH(QString, code);
684
    QFETCH(CompletionItems, expectedItems);
685

Kevin Funk's avatar
Kevin Funk committed
686
    executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion);
687 688
}

689
void TestCodeCompletion::testImplement_data()
690
{
691
    QTest::addColumn<QString>("code");
692
    QTest::addColumn<CompletionItems>("expectedItems");
693 694 695

    QTest::newRow("basic")
        << "int foo(char c, int i); \n"
696
        << CompletionItems{{1, 0}, {"foo(char c, int i)"}};
697 698 699 700 701

    QTest::newRow("class")
        << "class Foo { \n"
           "int bar(char c, int i); \n\n"
           "}; \n"
702 703 704 705 706 707 708
        << CompletionItems{{2, 0}, {}};

    QTest::newRow("class2")
        << "class Foo { \n"
            "int bar(char c, int i); \n\n"
            "}; \n"
        << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}};
709 710 711 712 713

    QTest::newRow("namespace")
        << "namespace Foo { \n"
           "int bar(char c, int i); \n\n"
           "}; \n"
714 715
        << CompletionItems{{2, 0}, {"bar(char c, int i)"}};

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
    QTest::newRow("anonymous-namespace")
        << R"(
                namespace {
                    int bar(char c, int i);
                };
            )"
        << CompletionItems{{3, 0}, {"bar(char c, int i)"}};

    QTest::newRow("anonymous-namespace2")
        << R"(
                namespace {
                    int bar(char c, int i);
                };
           )"
        << CompletionItems{{4, 0}, {}};

732 733 734 735 736
    QTest::newRow("namespace2")
        << "namespace Foo { \n"
           "int bar(char c, int i); \n\n"
           "}; \n"
        << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}};
737 738 739 740 741

    QTest::newRow("two-namespace")
        << "namespace Foo { int bar(char c, int i); };\n"
           "namespace Foo {\n"
           "};\n"
742
        << CompletionItems{{2, 0}, {"bar(char c, int i)"}};
743 744 745

    QTest::newRow("destructor")
        << "class Foo { ~Foo(); }\n"
746
        << CompletionItems{{1, 0}, {"Foo::~Foo()"}};
747 748 749

    QTest::newRow("constructor")
        << "class Foo { \n"
750 751
                 "Foo(int i); \n"
                 "}; \n"
752
        << CompletionItems{{3, 1}, {"Foo::Foo(int i)"}};
753 754 755

    QTest::newRow("template")
        << "template<typename T> class Foo { T bar(T t); };\n"
756
        << CompletionItems{{1, 1}, {"Foo<T>::bar(T t)"}};
757 758 759 760 761 762 763 764 765

    QTest::newRow("specialized-template")
        << "template<typename T> class Foo { \n"
           "T bar(T t); \n"
           "}; \n"
           "template<typename T> T Foo<T>::bar(T t){} \n"
           "template<> class Foo<int> { \n"
           "int bar(int t); \n"
           "}\n"
766
        << CompletionItems{{6, 1}, {"Foo<int>::bar(int t)"}};
767 768 769 770 771 772 773

    QTest::newRow("nested-class")
        << "class Foo { \n"
           "class Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
        << CompletionItems {{3, 1}, {}};

    QTest::newRow("nested-class2")
        << "class Foo { \n"
           "class Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
        << CompletionItems {{5, 1}, {}};

    QTest::newRow("nested-class3")
        << "class Foo { \n"
           "class Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
        << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}};
791 792 793 794 795 796 797

    QTest::newRow("nested-namespace")
        << "namespace Foo { \n"
           "namespace Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
        << CompletionItems {{3, 1}, {"baz(char c, int i)"}};

    QTest::newRow("nested-namespace2")
        << "namespace Foo { \n"
           "namespace Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
        << CompletionItems {{5, 1}, {"Bar::baz(char c, int i)"}};

    QTest::newRow("nested-namespace3")
        << "namespace Foo { \n"
           "namespace Bar { \n"
           "int baz(char c, int i); \n\n"
           "}; \n\n"
           "}; \n\n"
        << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}};
815 816 817 818 819 820

    QTest::newRow("partial-template")
        << "template<typename T> class Foo { \n"
           "template<typename U> class Bar;\n"
           "template<typename U> class Bar<U*> { void baz(T t, U u); }\n"
           "}\n"
821
        << CompletionItems{{5,1}, {"Foo<T>::Bar<U *>::baz(T t, U u)"}};
822 823 824

    QTest::newRow("variadic")
        << "int foo(...); int bar(int i, ...); \n"
825
        << CompletionItems{{1, 1}, {"bar(int i, ...)", "foo(...)"}};
826 827 828

    QTest::newRow("const")
        << "class Foo { int bar() const; };"
829
        << CompletionItems{{3, 1}, {"Foo::bar() const"}};
830 831 832

    QTest::newRow("multiple-methods")
        << "class Foo { int bar(); void foo(); char asdf() const; };"
833 834
        << CompletionItems{{1, 1}, {"Foo::asdf() const", "Foo::bar()", "Foo::foo()"}};

835
    // explicitly deleted/defaulted functions should not appear in the implements completion
836
    QTest::newRow("deleted-copy-ctor")
837
        << "struct S { S(); S(const S&) = /*some comment*/ delete; };"
838 839 840 841
        << CompletionItems{{1,1}, {"S::S()"}};
    QTest::newRow("deleted-overload-member")
        << "struct Foo {\n"
           "  int x();\n"
842
           "  int x(int) =delete;\n"
843 844 845 846
           "};\n"
        << CompletionItems{{5,1}, {"Foo::x()"}};
    QTest::newRow("deleted-overload-global")
        << "int x();\n"
847
           "int x(int)=  delete;\n"
848
        << CompletionItems{{2,1}, {"x()"}};
849 850 851 852 853 854 855 856 857
    QTest::newRow("defaulted-copy-ctor")
        << "struct S { S(); S(const S&) = default; };"
        << CompletionItems{{1,1}, {"S::S()"}};
    QTest::newRow("defaulted-dtor")
        << "struct Foo {\n"
           "  Foo();\n"
           "  ~Foo() =default;\n"
           "};\n"
        << CompletionItems{{5,1}, {"Foo::Foo()"}};
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872

    QTest::newRow("bug355163")
        << R"(
                #include <type_traits>
                namespace test {

                template<typename T, typename U>
                struct IsSafeConversion : public std::is_same<T, typename std::common_type<T, U>::type>
                {

                };

                } // namespace test
            )"
        << CompletionItems{{7,0}, {}};
873 874 875 876 877 878 879 880 881 882 883 884

    QTest::newRow("bug355954")
        << R"(
                struct Hello {
                    struct Private;
                };

                struct Hello::Private {
                    void test();
                };
            )"
        << CompletionItems{{8,0}, {"Hello::Private::test()"}};
885 886 887 888

    QTest::newRow("lineOfNextFunction")
        << "void foo();\nvoid bar() {}"
        << CompletionItems{{1,0}, {"foo()"}};
889 890 891 892 893 894 895 896 897 898

    QTest::newRow("pure")
        << R"(
                struct Hello {
                    virtual void foo() = 0;
                    virtual void bar();
                };
            )"
        << CompletionItems{{5, 0}, {"Hello::bar()"}};

899 900 901 902 903 904 905 906 907 908
    QTest::newRow("bug368544")
        << R"(
                class Klass {
                public:
                    template <typename T>
                    void func(int a, T x, int b) const;
                };
            )"
        << CompletionItems{{6, 0}, {"Klass::func(int a, T x, int b) const"}};

909 910
}

911 912
void TestCodeCompletion::testImplementOtherFile()
{
913
    TestFile header1(QStringLiteral("void foo();"), QStringLiteral("h"));
914
    QVERIFY(header1.parseAndWait());
915
    TestFile header2(QStringLiteral("void bar();"), QStringLiteral("h"));
916 917 918 919
    QVERIFY(header2.parseAndWait());
    TestFile impl(QString("#include \"%1\"\n"
                          "#include \"%2\"\n"
                          "void asdf();\n\n")
920
                    .arg(header1.url().str(), header2.url().str()),
921
                  QStringLiteral("cpp"), &header1);
922 923 924 925 926 927

    CompletionItems expectedItems{{3,1}, {"asdf()", "foo()"}};
    QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));
    executeCompletionTest(impl.topContext(), expectedItems);
}

928 929
void TestCodeCompletion::testImplementAfterEdit()
{
930
    TestFile header1(QStringLiteral("void foo();"), QStringLiteral("h"));
931 932 933 934
    QVERIFY(header1.parseAndWait());
    TestFile impl(QString("#include \"%1\"\n"
                          "void asdf() {}\nvoid bar() {}")
                    .arg(header1.url().str()),
935
                  QStringLiteral("cpp"), &header1);
936 937 938 939 940 941 942 943

    auto document = ICore::self()->documentController()->openDocument(impl.url().toUrl());

    QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST));

    CompletionItems expectedItems{{2,0}, {"foo()"}};
    executeCompletionTest(impl.topContext(), expectedItems);

944
    document->textDocument()->insertText(expectedItems.position, QStringLiteral("\n"));
945 946 947 948 949 950 951
    expectedItems.position.setLine(3);

    executeCompletionTest(impl.topContext(), expectedItems);

    document->close(IDocument::Discard);
}

952 953 954
void TestCodeCompletion::testInvalidCompletions()
{
    QFETCH(QString, code);
955
    QFETCH(CompletionItems, expectedItems);
956 957

    executeCompletionTest(code, expectedItems);
958 959
}

960 961 962
void TestCodeCompletion::testInvalidCompletions_data()
{
    QTest::addColumn<QString>("code");
963
    QTest::addColumn<CompletionItems>("expectedItems");
964 965 966

    QTest::newRow("invalid-context-incomment")
        << "class Foo { int bar() const; };\n/*\n*/"
967
        << CompletionItems{{2, 0}, {}};
968 969
}

970
void TestCodeCompletion::testIncludePathCompletion_data()
971
{
972 973 974 975 976
    QTest::addColumn<QString>("code");
    QTest::addColumn<KTextEditor::Cursor>("cursor");
    QTest::addColumn<QString>("itemId");
    QTest::addColumn<QString>("result");

977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
    QTest::newRow("global-1") << QStringLiteral("#include ") << KTextEditor::Cursor(0, 9)
                              << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
    QTest::newRow("global-2") << QStringLiteral("#include <") << KTextEditor::Cursor(0, 9)
                              << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
    QTest::newRow("global-3") << QStringLiteral("#include <") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
    QTest::newRow("global-4") << QStringLiteral("#  include <") << KTextEditor::Cursor(0, 12)
                              << QStringLiteral("iostream") << QStringLiteral("#  include <iostream>");
    QTest::newRow("global-5") << QStringLiteral("#  include   <") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("iostream") << QStringLiteral("#  include   <iostream>");
    QTest::newRow("global-6") << QStringLiteral("#  include   <> /* 1 */") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("iostream") << QStringLiteral("#  include   <iostream> /* 1 */");
    QTest::newRow("global-7") << QStringLiteral("#  include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 21)
                              << QStringLiteral("iostream") << QStringLiteral("#  include /* 1 */ <iostream> /* 1 */");
    QTest::newRow("global-8") << QStringLiteral("# /* 1 */ include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 28)
                              << QStringLiteral("iostream") << QStringLiteral("# /* 1 */ include /* 1 */ <iostream> /* 1 */");
    QTest::newRow("global-9") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("iostream") << QStringLiteral("#include <iostream>");
    QTest::newRow("global-10") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("cstdint") << QStringLiteral("#include <cstdint>");
    QTest::newRow("global-11") << QStringLiteral("#include <cstdint>") << KTextEditor::Cursor(0, 17)
                              << QStringLiteral("cstdint") << QStringLiteral("#include <cstdint>");
    QTest::newRow("local-0") << QStringLiteral("#include \"") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
    QTest::newRow("local-1") << QStringLiteral("#include \"foo/\"") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
    QTest::newRow("local-2") << QStringLiteral("#include \"foo/") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
    QTest::newRow("local-3") << QStringLiteral("# /* 1 */ include /* 1 */ \"\" /* 1 */") << KTextEditor::Cursor(0, 28)
                              << QStringLiteral("foo/") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */");
    QTest::newRow("local-4") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */") << KTextEditor::Cursor(0, 31)
                              << QStringLiteral("bar/") << QStringLiteral("# /* 1 */ include /* 1 */ \"foo/bar/\" /* 1 */");
    QTest::newRow("local-5") << QStringLiteral("#include \"foo/\"") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
    QTest::newRow("local-6") << QStringLiteral("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("foo/") << QStringLiteral("#include \"foo/\"");
    QTest::newRow("local-7") << QStringLiteral("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("bar/") << QStringLiteral("#include \"foo/bar/\"");
    QTest::newRow("dash-1") << QStringLiteral("#include \"") << KTextEditor::Cursor(0, 10)
                              << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
    QTest::newRow("dash-2") << QStringLiteral("#include \"dash-") << KTextEditor::Cursor(0, 15)
                              << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
    QTest::newRow("dash-4") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 13)
                              << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
    QTest::newRow("dash-5") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 14)
                              << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
    QTest::newRow("dash-6") << QStringLiteral("#include \"dash-file.h\"") << KTextEditor::Cursor(0, 15)
                              << QStringLiteral("dash-file.h") << QStringLiteral("#include \"dash-file.h\"");
1025
}
1026

1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
void TestCodeCompletion::testIncludePathCompletion()
{
    QFETCH(QString, code);
    QFETCH(KTextEditor::Cursor, cursor);
    QFETCH(QString, itemId);
    QFETCH(QString, result);

    QTemporaryDir tempDir;
    QDir dir(tempDir.path());
    QVERIFY(dir.mkpath("foo/bar/asdf"));
1037
    TestFile file(code, QStringLiteral("cpp"), nullptr, tempDir.path());
1038 1039 1040 1041
    {
        QFile otherFile(tempDir.path() + "/dash-file.h");
        QVERIFY(otherFile.open(QIODevice::WriteOnly));
    }
1042 1043 1044 1045 1046 1047 1048
    IncludeTester tester(executeIncludePathCompletion(&file, cursor));
    QVERIFY(tester.completionContext);
    QVERIFY(tester.completionContext->isValid());

    auto item = tester.findItem(itemId);
    QVERIFY(item);

1049 1050 1051 1052
    auto view = createView(file.url().toUrl(), this);
    QVERIFY(view.get());
    auto doc = view->document();
    item->execute(view.get(), KTextEditor::Range(cursor, cursor));
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
    QCOMPARE(doc->text(), result);

    const auto newCursor = view->cursorPosition();
    QCOMPARE(newCursor.line(), cursor.line());
    if (!itemId.endsWith('/')) {
        // file inserted, cursor should be at end of line
        QCOMPARE(newCursor.column(), doc->lineLength(cursor.line()));
    } else {
        // directory inserted, cursor should be before the " or >
        const auto cursorChar = doc->characterAt(newCursor);
        QVERIFY(cursorChar == '"' || cursorChar == '>');
    }
}
1066

1067 1068
void TestCodeCompletion::testIncludePathCompletionLocal()
{
1069 1070
    TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
    TestFile impl(QStringLiteral("#include \""), QStringLiteral("cpp"), &header);
1071

1072 1073
    IncludeTester tester(executeIncludePathCompletion(&impl, {0, 10}));
    QVERIFY(tester.names.contains(header.url().toUrl().fileName()));
1074
    QVERIFY(tester.names.contains("iostream"));
1075
}
1076 1077 1078

void TestCodeCompletion::testOverloadedFunctions()
{
1079