diff --git a/cmake/modules/FindClang.cmake b/cmake/modules/FindClang.cmake index 05b3e7717c..bb72250363 100644 --- a/cmake/modules/FindClang.cmake +++ b/cmake/modules/FindClang.cmake @@ -1,110 +1,118 @@ # Detect Clang libraries # # Defines the following variables: # CLANG_FOUND - True if Clang was found # CLANG_INCLUDE_DIRS - Where to find Clang includes # CLANG_LIBRARY_DIRS - Where to find Clang libraries # # CLANG_LIBCLANG_LIB - Libclang C library # # CLANG_CLANGFRONTEND_LIB - Clang Frontend (C++) Library # CLANG_CLANGDRIVER_LIB - Clang Driver (C++) Library # ... # # CLANG_LIBS - All the Clang C++ libraries # # Uses the same include and library paths detected by FindLLVM.cmake # # See http://clang.llvm.org/docs/InternalsManual.html for full list of libraries #============================================================================= # Copyright 2014-2015 Kevin Funk # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= +set(KNOWN_VERSIONS 3.9 3.8 3.7 3.6 3.5) + +foreach(version ${KNOWN_VERSIONS}) + if (LLVM_DIR OR (DEFINED Clang_FIND_VERSION AND Clang_FIND_VERSION VERSION_GREATER version)) + break() + endif () + if (${Clang_FIND_REQUIRED}) - find_package(LLVM ${Clang_FIND_VERSION} REQUIRED) + find_package(LLVM ${version} REQUIRED) else () - find_package(LLVM ${Clang_FIND_VERSION}) + find_package(LLVM ${version}) endif () +endforeach() set(CLANG_FOUND FALSE) if (LLVM_FOUND AND LLVM_LIBRARY_DIRS) macro(FIND_AND_ADD_CLANG_LIB _libname_) string(TOUPPER ${_libname_} _prettylibname_) find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_} HINTS ${LLVM_LIBRARY_DIRS}) if(CLANG_${_prettylibname_}_LIB) set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_prettylibname_}_LIB}) endif() endmacro(FIND_AND_ADD_CLANG_LIB) # note: On Windows there's 'libclang.dll' instead of 'clang.dll' -> search for 'libclang', too find_library(CLANG_LIBCLANG_LIB NAMES clang libclang HINTS ${LLVM_LIBRARY_DIRS}) # LibClang: high-level C interface FIND_AND_ADD_CLANG_LIB(clangFrontend) FIND_AND_ADD_CLANG_LIB(clangDriver) FIND_AND_ADD_CLANG_LIB(clangCodeGen) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangChecker) FIND_AND_ADD_CLANG_LIB(clangAnalysis) FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) FIND_AND_ADD_CLANG_LIB(clangRewrite) FIND_AND_ADD_CLANG_LIB(clangAST) FIND_AND_ADD_CLANG_LIB(clangParse) FIND_AND_ADD_CLANG_LIB(clangLex) FIND_AND_ADD_CLANG_LIB(clangBasic) FIND_AND_ADD_CLANG_LIB(clangARCMigrate) FIND_AND_ADD_CLANG_LIB(clangEdit) FIND_AND_ADD_CLANG_LIB(clangFrontendTool) FIND_AND_ADD_CLANG_LIB(clangRewrite) FIND_AND_ADD_CLANG_LIB(clangSerialization) FIND_AND_ADD_CLANG_LIB(clangTooling) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangRewriteCore) endif() if(CLANG_LIBS OR CLANG_LIBCLANG_LIB) set(CLANG_FOUND TRUE) else() message(STATUS "Could not find any Clang libraries in ${LLVM_LIBRARY_DIRS}") endif() if(CLANG_FOUND) set(CLANG_LIBRARY_DIRS ${LLVM_LIBRARY_DIRS}) set(CLANG_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS}) # check whether llvm-config comes from an install prefix execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --src-root OUTPUT_VARIABLE _llvmSourceRoot OUTPUT_STRIP_TRAILING_WHITESPACE ) string(FIND "${LLVM_INCLUDE_DIRS}" "${_llvmSourceRoot}" _llvmIsInstalled) if (NOT _llvmIsInstalled) message(STATUS "Detected that llvm-config comes from a build-tree, adding more include directories for Clang") list(APPEND CLANG_INCLUDE_DIRS "${LLVM_INSTALL_PREFIX}/tools/clang/include" # build dir "${_llvmSourceRoot}/tools/clang/include" # source dir ) endif() message(STATUS "Found Clang (LLVM version: ${LLVM_VERSION})") message(STATUS " Include dirs: ${CLANG_INCLUDE_DIRS}") message(STATUS " Clang libraries: ${CLANG_LIBS}") message(STATUS " Libclang C library: ${CLANG_LIBCLANG_LIB}") else() if(Clang_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find Clang") endif() endif() diff --git a/documentation/manpage/tests/test_manpagemodel.cpp b/documentation/manpage/tests/test_manpagemodel.cpp index 018ec02335..f428812090 100644 --- a/documentation/manpage/tests/test_manpagemodel.cpp +++ b/documentation/manpage/tests/test_manpagemodel.cpp @@ -1,70 +1,73 @@ /* This file is part of KDevelop Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "../manpagedocumentation.h" #include "../manpagemodel.h" #include #include #include #include class TestManPageModel : public QObject { Q_OBJECT private Q_SLOTS: void testModel(); void testDocumentation(); }; void TestManPageModel::testModel() { ManPageModel model; QSignalSpy spy(&model, SIGNAL(manPagesLoaded())); spy.wait(); if (model.isLoaded()) { QVERIFY(model.rowCount() > 0); new ModelTest(&model); } else { QCOMPARE(model.rowCount(), 0); } } void TestManPageModel::testDocumentation() { ManPageDocumentation documentation("dlopen", QUrl("man: (3)/dlmopen")); QSignalSpy spy(&documentation, SIGNAL(descriptionChanged())); QVERIFY(spy.wait()); const QString description = documentation.description(); - if (!description.isEmpty()) { - qDebug() << description; - // check that we've found the correct page by checking some references - QVERIFY(description.contains("dlclose")); - QVERIFY(description.contains("dlerror")); - QVERIFY(description.contains("dlopen")); + QVERIFY(!description.isEmpty()); + qDebug() << "Description:" << description; + if (description.isEmpty() || description.contains("No man page matching to dlmopen found")) { + QSKIP("This test requires installed man pages for dlmopen & friends"); } + + // check that we've found the correct page by checking some references + QVERIFY(description.contains("dlclose")); + QVERIFY(description.contains("dlerror")); + QVERIFY(description.contains("dlopen")); } QTEST_MAIN(TestManPageModel) #include "test_manpagemodel.moc" diff --git a/languages/clang/tests/test_assistants.cpp b/languages/clang/tests/test_assistants.cpp index 9cdd4ccc74..2505b426da 100644 --- a/languages/clang/tests/test_assistants.cpp +++ b/languages/clang/tests/test_assistants.cpp @@ -1,785 +1,785 @@ /* This file is part of KDevelop Copyright 2012 Olivier de Gaalon 2014 David Stevens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "test_assistants.h" #include "codegen/clangrefactoring.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; using namespace KTextEditor; QTEST_MAIN(TestAssistants) ForegroundLock *globalTestLock = 0; StaticAssistantsManager *staticAssistantsManager() { return Core::self()->languageController()->staticAssistantsManager(); } void TestAssistants::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral( "*.debug=false\n" "default.debug=true\n" "kdevelop.plugins.clang.debug=true\n" )); QVERIFY(qputenv("KDEV_DISABLE_PLUGINS", "kdevcppsupport")); QVERIFY(qputenv("KDEV_CLANG_DISPLAY_DIAGS", "1")); AutoTestShell::init({QStringLiteral("kdevclangsupport"), QStringLiteral("kdevproblemreporter")}); TestCore::initialize(); DUChain::self()->disablePersistentStorage(); Core::self()->languageController()->backgroundParser()->setDelay(0); Core::self()->sourceFormatterController()->disableSourceFormatting(true); CodeRepresentation::setDiskChangesForbidden(true); globalTestLock = new ForegroundLock; } void TestAssistants::cleanupTestCase() { Core::self()->cleanup(); delete globalTestLock; globalTestLock = 0; } static QUrl createFile(const QString& fileContents, QString extension, int id) { static QTemporaryDir tempDirA; Q_ASSERT(tempDirA.isValid()); static QDir dirA(tempDirA.path()); QFile file(dirA.filePath(QString::number(id) + extension)); file.open(QIODevice::WriteOnly | QIODevice::Text); file.write(fileContents.toUtf8()); file.close(); return QUrl::fromLocalFile(file.fileName()); } class Testbed { public: enum TestDoc { HeaderDoc, CppDoc }; enum IncludeBehavior { NoAutoInclude, AutoInclude, }; Testbed(const QString& headerContents, const QString& cppContents, IncludeBehavior include = AutoInclude) : m_includeBehavior(include) { static int i = 0; int id = i; ++i; m_headerDocument.url = createFile(headerContents,".h",id); m_headerDocument.textDoc = openDocument(m_headerDocument.url); QString preamble; if (include == AutoInclude) preamble = QStringLiteral("#include \"%1\"\n").arg(m_headerDocument.url.toLocalFile()); m_cppDocument.url = createFile(preamble + cppContents,".cpp",id); m_cppDocument.textDoc = openDocument(m_cppDocument.url); } ~Testbed() { Core::self()->documentController()->documentForUrl(m_cppDocument.url)->textDocument(); Core::self()->documentController()->documentForUrl(m_cppDocument.url)->close(KDevelop::IDocument::Discard); Core::self()->documentController()->documentForUrl(m_headerDocument.url)->close(KDevelop::IDocument::Discard); } void changeDocument(TestDoc which, Range where, const QString& what, bool waitForUpdate = false) { TestDocument document; if (which == CppDoc) { document = m_cppDocument; if (m_includeBehavior == AutoInclude) { where = Range(where.start().line() + 1, where.start().column(), where.end().line() + 1, where.end().column()); //The include adds a line } } else { document = m_headerDocument; } // we must activate the document, otherwise we cannot find the correct active view auto kdevdoc = ICore::self()->documentController()->documentForUrl(document.url); QVERIFY(kdevdoc); ICore::self()->documentController()->activateDocument(kdevdoc); auto view = ICore::self()->documentController()->activeTextDocumentView(); QCOMPARE(view->document(), document.textDoc); view->setSelection(where); view->removeSelectionText(); view->setCursorPosition(where.start()); view->insertText(what); QCoreApplication::processEvents(); if (waitForUpdate) { DUChain::self()->waitForUpdate(IndexedString(document.url), KDevelop::TopDUContext::AllDeclarationsAndContexts); } } QString documentText(TestDoc which) { if (which == CppDoc) { //The CPP document text shouldn't include the autogenerated include line QString text = m_cppDocument.textDoc->text(); return m_includeBehavior == AutoInclude ? text.mid(text.indexOf("\n") + 1) : text; } else return m_headerDocument.textDoc->text(); } QString includeFileName() const { return m_headerDocument.url.toLocalFile(); } KTextEditor::Document *document(TestDoc which) const { return Core::self()->documentController()->documentForUrl( which == CppDoc ? m_cppDocument.url : m_headerDocument.url)->textDocument(); } private: struct TestDocument { QUrl url; Document *textDoc; }; Document* openDocument(const QUrl& url) { Core::self()->documentController()->openDocument(url); DUChain::self()->waitForUpdate(IndexedString(url), KDevelop::TopDUContext::AllDeclarationsAndContexts); return Core::self()->documentController()->documentForUrl(url)->textDocument(); } IncludeBehavior m_includeBehavior; TestDocument m_headerDocument; TestDocument m_cppDocument; }; /** * A StateChange describes an insertion/deletion/replacement and the expected result **/ struct StateChange { StateChange(){}; StateChange(Testbed::TestDoc document, const Range& range, const QString& newText, const QString& result) : document(document) , range(range) , newText(newText) , result(result) { } Testbed::TestDoc document; Range range; QString newText; QString result; }; Q_DECLARE_METATYPE(StateChange) Q_DECLARE_METATYPE(QList) void TestAssistants::testRenameAssistant_data() { QTest::addColumn("fileContents"); QTest::addColumn("oldDeclarationName"); QTest::addColumn >("stateChanges"); QTest::addColumn("finalFileContents"); QTest::newRow("Prepend Text") << "int foo(int i)\n { i = 0; return i; }" << "i" << QList{ StateChange(Testbed::CppDoc, Range(0,12,0,12), "u", "ui"), StateChange(Testbed::CppDoc, Range(0,13,0,13), "z", "uzi"), } << "int foo(int uzi)\n { uzi = 0; return uzi; }"; QTest::newRow("Append Text") << "int foo(int i)\n { i = 0; return i; }" << "i" << (QList() << StateChange(Testbed::CppDoc, Range(0,13,0,13), "d", "id")) << "int foo(int id)\n { id = 0; return id; }"; QTest::newRow("Replace Text") << "int foo(int i)\n { i = 0; return i; }" << "i" << (QList() << StateChange(Testbed::CppDoc, Range(0,12,0,13), "u", "u")) << "int foo(int u)\n { u = 0; return u; }"; QTest::newRow("Paste Replace") << "int foo(int abg)\n { abg = 0; return abg; }" << "abg" << (QList() << StateChange(Testbed::CppDoc, Range(0,12,0,15), "abcdefg", "abcdefg")) << "int foo(int abcdefg)\n { abcdefg = 0; return abcdefg; }"; QTest::newRow("Paste Insert") << "int foo(int abg)\n { abg = 0; return abg; }" << "abg" << (QList() << StateChange(Testbed::CppDoc, Range(0,14,0,14), "cdef", "abcdefg")) << "int foo(int abcdefg)\n { abcdefg = 0; return abcdefg; }"; QTest::newRow("Letter-by-Letter Prepend") << "int foo(int i)\n { i = 0; return i; }" << "i" << (QList() << StateChange(Testbed::CppDoc, Range(0,12,0,12), "a", "ai") << StateChange(Testbed::CppDoc, Range(0,13,0,13), "b", "abi") << StateChange(Testbed::CppDoc, Range(0,14,0,14), "c", "abci") ) - << "int foo(int abc)\n { abc = 0; return abc; }"; + << "int foo(int abci)\n { abci = 0; return abci; }"; QTest::newRow("Letter-by-Letter Insert") << "int foo(int abg)\n { abg = 0; return abg; }" << "abg" << (QList() << StateChange(Testbed::CppDoc, Range(0,14,0,14), "c", "abcg") << StateChange(Testbed::CppDoc, Range(0,15,0,15), "d", "abcdg") << StateChange(Testbed::CppDoc, Range(0,16,0,16), "e", "abcdeg") << StateChange(Testbed::CppDoc, Range(0,17,0,17), "f", "abcdefg") ) << "int foo(int abcdefg)\n { abcdefg = 0; return abcdefg; }"; } ProblemPointer findStaticAssistantProblem(const QList& problems) { const auto renameProblemIt = std::find_if(problems.cbegin(), problems.cend(), [](const ProblemPointer& p) { return dynamic_cast(p.constData()); }); if (renameProblemIt != problems.cend()) return *renameProblemIt; return {}; } void TestAssistants::testRenameAssistant() { QFETCH(QString, fileContents); Testbed testbed("", fileContents); const auto document = testbed.document(Testbed::CppDoc); QVERIFY(document); QExplicitlySharedDataPointer assistant; QFETCH(QString, oldDeclarationName); QFETCH(QList, stateChanges); foreach(StateChange stateChange, stateChanges) { testbed.changeDocument(Testbed::CppDoc, stateChange.range, stateChange.newText, true); DUChainReadLocker lock; auto topCtx = DUChain::self()->chainForDocument(document->url()); QVERIFY(topCtx); const auto problem = findStaticAssistantProblem(topCtx->problems()); if (problem) assistant = problem->solutionAssistant(); if (stateChange.result.isEmpty()) { QVERIFY(!assistant || !assistant->actions().size()); } else { qWarning() << assistant.data() << stateChange.result; QVERIFY(assistant && assistant->actions().size()); RenameAction *r = qobject_cast(assistant->actions().first().data()); QCOMPARE(r->oldDeclarationName(), oldDeclarationName); QCOMPARE(r->newDeclarationName(), stateChange.result); } } if (assistant && assistant->actions().size()) { assistant->actions().first()->execute(); } QFETCH(QString, finalFileContents); QCOMPARE(testbed.documentText(Testbed::CppDoc), finalFileContents); } void TestAssistants::testRenameAssistantUndoRename() { Testbed testbed("", "int foo(int i)\n { i = 0; return i; }"); testbed.changeDocument(Testbed::CppDoc, Range(0,13,0,13), "d", true); const auto document = testbed.document(Testbed::CppDoc); QVERIFY(document); DUChainReadLocker lock; auto topCtx = DUChain::self()->chainForDocument(document->url()); QVERIFY(topCtx); auto firstProblem = findStaticAssistantProblem(topCtx->problems()); auto assistant = firstProblem->solutionAssistant(); QVERIFY(assistant); QVERIFY(assistant->actions().size() > 0); RenameAction *r = qobject_cast(assistant->actions().first().data()); qWarning() << topCtx->problems() << assistant->actions().first().data() << assistant->actions().size(); QVERIFY(r); // now rename the variable back to its original identifier testbed.changeDocument(Testbed::CppDoc, Range(0,13,0,14), ""); // there should be no assistant anymore QVERIFY(!assistant || assistant->actions().isEmpty()); } const QString SHOULD_ASSIST = "SHOULD_ASSIST"; //An assistant will be visible const QString NO_ASSIST = "NO_ASSIST"; //No assistant visible void TestAssistants::testSignatureAssistant_data() { QTest::addColumn("headerContents"); QTest::addColumn("cppContents"); QTest::addColumn >("stateChanges"); QTest::addColumn("finalHeaderContents"); QTest::addColumn("finalCppContents"); QTest::newRow("change_argument_type") << "class Foo {\nint bar(int a, char* b, int c = 10); \n};" << "int Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }" << (QList() << StateChange(Testbed::HeaderDoc, Range(1,8,1,11), "char", SHOULD_ASSIST)) << "class Foo {\nint bar(char a, char* b, int c = 10); \n};" << "int Foo::bar(char a, char* b, int c)\n{ a = c; b = new char; return a + *b; }"; QTest::newRow("prepend_arg_header") << "class Foo { void bar(int i); };" << "void Foo::bar(int i)\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 21, 0, 21), "char c, ", SHOULD_ASSIST)) << "class Foo { void bar(char c, int i); };" << "void Foo::bar(char c, int i)\n{}"; QTest::newRow("prepend_arg_cpp") << "class Foo { void bar(int i); };" << "void Foo::bar(int i)\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0, 14, 0, 14), "char c, ", SHOULD_ASSIST)) << "class Foo { void bar(char c, int i); };" << "void Foo::bar(char c, int i)\n{}"; QTest::newRow("change_default_parameter") << "class Foo {\nint bar(int a, char* b, int c = 10); \n};" << "int Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }" << (QList() << StateChange(Testbed::HeaderDoc, Range(1,29,1,34), "", NO_ASSIST)) << "class Foo {\nint bar(int a, char* b, int c); \n};" << "int Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }"; QTest::newRow("change_function_type") << "class Foo {\nint bar(int a, char* b, int c = 10); \n};" << "int Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }" << (QList() << StateChange(Testbed::CppDoc, Range(0,0,0,3), "char", SHOULD_ASSIST)) << "class Foo {\nchar bar(int a, char* b, int c = 10); \n};" << "char Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }"; QTest::newRow("swap_args_definition_side") << "class Foo {\nint bar(int a, char* b, int c = 10); \n};" << "int Foo::bar(int a, char* b, int c)\n{ a = c; b = new char; return a + *b; }" << (QList() << StateChange(Testbed::CppDoc, Range(0,13,0,28), "char* b, int a,", SHOULD_ASSIST)) << "class Foo {\nint bar(char* b, int a, int c = 10); \n};" << "int Foo::bar(char* b, int a, int c)\n{ a = c; b = new char; return a + *b; }"; // see https://bugs.kde.org/show_bug.cgi?id=299393 // actually related to the whitespaces in the header... QTest::newRow("change_function_constness") << "class Foo {\nvoid bar(const Foo&) const;\n};" << "void Foo::bar(const Foo&) const\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0,25,0,31), "", SHOULD_ASSIST)) << "class Foo {\nvoid bar(const Foo&);\n};" << "void Foo::bar(const Foo&)\n{}"; // see https://bugs.kde.org/show_bug.cgi?id=356179 QTest::newRow("keep_static_cpp") << "class Foo { static void bar(int i); };" << "void Foo::bar(int i)\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0, 19, 0, 19), ", char c", SHOULD_ASSIST)) << "class Foo { static void bar(int i, char c); };" << "void Foo::bar(int i, char c)\n{}"; QTest::newRow("keep_static_header") << "class Foo { static void bar(int i); };" << "void Foo::bar(int i)\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 33, 0, 33), ", char c", SHOULD_ASSIST)) << "class Foo { static void bar(int i, char c); };" << "void Foo::bar(int i, char c)\n{}"; // see https://bugs.kde.org/show_bug.cgi?id=356178 QTest::newRow("keep_default_args_cpp_before") << "class Foo { void bar(bool b, int i = 0); };" << "void Foo::bar(bool b, int i)\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0, 14, 0, 14), "char c, ", SHOULD_ASSIST)) << "class Foo { void bar(char c, bool b, int i = 0); };" << "void Foo::bar(char c, bool b, int i)\n{}"; QTest::newRow("keep_default_args_cpp_after") << "class Foo { void bar(bool b, int i = 0); };" << "void Foo::bar(bool b, int i)\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0, 27, 0, 27), ", char c", SHOULD_ASSIST)) << "class Foo { void bar(bool b, int i = 0, char c = {} /* TODO */); };" << "void Foo::bar(bool b, int i, char c)\n{}"; QTest::newRow("keep_default_args_header_before") << "class Foo { void bar(bool b, int i = 0); };" << "void Foo::bar(bool b, int i)\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 29, 0, 29), "char c = 'A', ", SHOULD_ASSIST)) << "class Foo { void bar(bool b, char c = 'A', int i = 0); };" << "void Foo::bar(bool b, char c, int i)\n{}"; QTest::newRow("keep_default_args_header_after") << "class Foo { void bar(bool b, int i = 0); };" << "void Foo::bar(bool b, int i)\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 38, 0, 38), ", char c = 'A'", SHOULD_ASSIST)) << "class Foo { void bar(bool b, int i = 0, char c = 'A'); };" << "void Foo::bar(bool b, int i, char c)\n{}"; // see https://bugs.kde.org/show_bug.cgi?id=355356 QTest::newRow("no_retval_on_ctor") << "class Foo { Foo(); };" << "Foo::Foo()\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 16, 0, 16), "char c", SHOULD_ASSIST)) << "class Foo { Foo(char c); };" << "Foo::Foo(char c)\n{}"; // see https://bugs.kde.org/show_bug.cgi?id=298511 QTest::newRow("change_return_type_header") << "struct Foo { int bar(); };" << "int Foo::bar()\n{}" << (QList() << StateChange(Testbed::HeaderDoc, Range(0, 13, 0, 16), "char", SHOULD_ASSIST)) << "struct Foo { char bar(); };" << "char Foo::bar()\n{}"; QTest::newRow("change_return_type_impl") << "struct Foo { int bar(); };" << "int Foo::bar()\n{}" << (QList() << StateChange(Testbed::CppDoc, Range(0, 0, 0, 3), "char", SHOULD_ASSIST)) << "struct Foo { char bar(); };" << "char Foo::bar()\n{}"; } void TestAssistants::testSignatureAssistant() { QFETCH(QString, headerContents); QFETCH(QString, cppContents); Testbed testbed(headerContents, cppContents); QExplicitlySharedDataPointer assistant; QFETCH(QList, stateChanges); foreach (StateChange stateChange, stateChanges) { testbed.changeDocument(stateChange.document, stateChange.range, stateChange.newText, true); const auto document = testbed.document(stateChange.document); QVERIFY(document); DUChainReadLocker lock; auto topCtx = DUChain::self()->chainForDocument(document->url()); QVERIFY(topCtx); const auto problem = findStaticAssistantProblem(topCtx->problems()); if (problem) { assistant = problem->solutionAssistant(); } if (stateChange.result == SHOULD_ASSIST) { QEXPECT_FAIL("change_function_type", "Clang sees that return type of out-of-line definition differs from that in the declaration and won't parse the code...", Abort); QEXPECT_FAIL("change_return_type_impl", "Clang sees that return type of out-of-line definition differs from that in the declaration and won't include the function's AST and thus we never get updated about the new return type...", Abort); QVERIFY(assistant && !assistant->actions().isEmpty()); } else { QVERIFY(!assistant || assistant->actions().isEmpty()); } } DUChainReadLocker lock; if (assistant && !assistant->actions().isEmpty()) assistant->actions().first()->execute(); QFETCH(QString, finalHeaderContents); QFETCH(QString, finalCppContents); QCOMPARE(testbed.documentText(Testbed::HeaderDoc), finalHeaderContents); QCOMPARE(testbed.documentText(Testbed::CppDoc), finalCppContents); } enum UnknownDeclarationAction { NoUnknownDeclarationAction = 0x0, ForwardDecls = 0x1, MissingInclude = 0x2 }; Q_DECLARE_FLAGS(UnknownDeclarationActions, UnknownDeclarationAction) Q_DECLARE_METATYPE(UnknownDeclarationActions) void TestAssistants::testUnknownDeclarationAssistant_data() { QTest::addColumn("headerContents"); QTest::addColumn("globalText"); QTest::addColumn("functionText"); QTest::addColumn("actions"); QTest::newRow("unincluded_struct") << "struct test{};" << "" << "test" << UnknownDeclarationActions(ForwardDecls | MissingInclude); QTest::newRow("forward_declared_struct") << "struct test{};" << "struct test;" << "test *f; f->" << UnknownDeclarationActions(MissingInclude); QTest::newRow("unknown_struct") << "" << "" << "test" << UnknownDeclarationActions(); } void TestAssistants::testUnknownDeclarationAssistant() { QFETCH(QString, headerContents); QFETCH(QString, globalText); QFETCH(QString, functionText); QFETCH(UnknownDeclarationActions, actions); static const auto cppContents = QStringLiteral("%1\nvoid f_u_n_c_t_i_o_n() {\n}"); Testbed testbed(headerContents, cppContents.arg(globalText), Testbed::NoAutoInclude); const auto document = testbed.document(Testbed::CppDoc); QVERIFY(document); const int line = document->lines() - 1; testbed.changeDocument(Testbed::CppDoc, Range(line, 0, line, 0), functionText, true); DUChainReadLocker lock; auto topCtx = DUChain::self()->chainForDocument(document->url()); QVERIFY(topCtx); const auto problems = topCtx->problems(); if (actions == NoUnknownDeclarationAction) { QVERIFY(!problems.isEmpty()); return; } auto firstProblem = problems.first(); auto assistant = firstProblem->solutionAssistant(); QVERIFY(assistant); const auto assistantActions = assistant->actions(); QStringList actionDescriptions; for (auto action: assistantActions) { actionDescriptions << action->description(); } { const bool hasForwardDecls = actionDescriptions.contains(QObject::tr("Forward declare as 'struct'")) && actionDescriptions.contains(QObject::tr("Forward declare as 'class'")); QCOMPARE(hasForwardDecls, static_cast(actions & ForwardDecls)); } { auto fileName = testbed.includeFileName(); fileName = fileName.mid(fileName.lastIndexOf('/') + 1); const auto description = QObject::tr("Insert \'%1\'") .arg(QStringLiteral("#include \"%1\"").arg(fileName)); const bool hasMissingInclude = actionDescriptions.contains(description); QCOMPARE(hasMissingInclude, static_cast(actions & MissingInclude)); } } void TestAssistants::testMoveIntoSource() { QFETCH(QString, origHeader); QFETCH(QString, origImpl); QFETCH(QString, newHeader); QFETCH(QString, newImpl); QFETCH(QualifiedIdentifier, id); TestFile header(origHeader, "h"); TestFile impl("#include \"" + header.url().byteArray() + "\"\n" + origImpl, "cpp", &header); { TopDUContext* headerCtx = nullptr; { DUChainReadLocker lock; headerCtx = DUChain::self()->chainForDocument(header.url()); } // Here is a problem: when launching tests one by one, we can reuse the same tmp file for headers. // But because of document chain for header wasn't unloaded properly in previous run we reuse it here // Therefore when using headerCtx->findDeclarations below we find declarations from the previous launch -> tests fail if (headerCtx) { // TODO: Investigate why this chain doesn't get updated when parsing source file DUChainWriteLocker lock; DUChain::self()->removeDocumentChain(headerCtx); } } impl.parse(KDevelop::TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(impl.waitForParsed()); IndexedDeclaration declaration; { DUChainReadLocker lock; auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); auto decls = headerCtx->findDeclarations(id); Q_ASSERT(!decls.isEmpty()); declaration = IndexedDeclaration(decls.first()); QVERIFY(declaration.isValid()); } CodeRepresentation::setDiskChangesForbidden(false); ClangRefactoring refactoring; QCOMPARE(refactoring.moveIntoSource(declaration), QString()); CodeRepresentation::setDiskChangesForbidden(true); QCOMPARE(header.fileContents(), newHeader); QVERIFY(impl.fileContents().endsWith(newImpl)); } void TestAssistants::testMoveIntoSource_data() { QTest::addColumn("origHeader"); QTest::addColumn("origImpl"); QTest::addColumn("newHeader"); QTest::addColumn("newImpl"); QTest::addColumn("id"); const QualifiedIdentifier fooId("foo"); QTest::newRow("globalfunction") << QString("int foo()\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo();\n") << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}\n") << fooId; QTest::newRow("staticfunction") << QString("static int foo()\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("static int foo();\n") << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}\n") << fooId; QTest::newRow("funcsameline") << QString("int foo() {\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo();\n") << QString("\nint foo() {\n int i = 0;\n return 0;\n}\n") << fooId; QTest::newRow("func-comment") << QString("int foo()\n/* foobar */ {\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo()\n/* foobar */;\n") << QString("\nint foo() {\n int i = 0;\n return 0;\n}\n") << fooId; QTest::newRow("func-comment2") << QString("int foo()\n/*asdf*/\n{\n int i = 0;\n return 0;\n}\n") << QString() << QString("int foo()\n/*asdf*/;\n") << QString("\nint foo()\n{\n int i = 0;\n return 0;\n}\n") << fooId; const QualifiedIdentifier aFooId("a::foo"); QTest::newRow("class-method") << QString("class a {\n int foo(){\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo();\n};\n") << QString("\nint a::foo() {\n return 0;\n }\n") << aFooId; QTest::newRow("class-method-const") << QString("class a {\n int foo() const\n {\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo() const;\n};\n") << QString("\nint a::foo() const\n {\n return 0;\n }\n") << aFooId; QTest::newRow("class-method-const-sameline") << QString("class a {\n int foo() const{\n return 0;\n }\n};\n") << QString() << QString("class a {\n int foo() const;\n};\n") << QString("\nint a::foo() const {\n return 0;\n }\n") << aFooId; QTest::newRow("elaborated-type") << QString("namespace NS{class C{};} class a {\nint foo(const NS::C c) const{\nreturn 0;\n}\n};\n") << QString() << QString("namespace NS{class C{};} class a {\nint foo(const NS::C c) const;\n};\n") << QString("\nint a::foo(const NS::C c) const {\nreturn 0;\n}\n") << aFooId; QTest::newRow("add-into-namespace") << QString("namespace NS{class a {\nint foo() const {\nreturn 0;\n}\n};\n}") << QString("namespace NS{\n}") << QString("namespace NS{class a {\nint foo() const;\n};\n}") << QString("namespace NS{\n\nint a::foo() const {\nreturn 0;\n}\n}") << QualifiedIdentifier("NS::a::foo"); QTest::newRow("class-template-parameter") << QString(R"( namespace first { template class Test{}; namespace second { template class List; } class MoveIntoSource { public: void f(const second::List>*>& param){} };} )") << QString("") << QString(R"( namespace first { template class Test{}; namespace second { template class List; } class MoveIntoSource { public: void f(const second::List>*>& param); };} )") << QString("namespace first {\nvoid MoveIntoSource::f(const first::second::List< const volatile first::Test< first::second::List< int* > >* >& param) {}}\n\n") << QualifiedIdentifier("first::MoveIntoSource::f"); QTest::newRow("move-unexposed-type") << QString("namespace std { template class basic_string; \ntypedef basic_string string;}\n void move(std::string i){}") << QString("") << QString("namespace std { template class basic_string; \ntypedef basic_string string;}\n void move(std::string i);") << QString("void move(std::string i) {}\n") << QualifiedIdentifier("move"); QTest::newRow("move-constructor") << QString("class Class{Class(){}\n};") << QString("") << QString("class Class{Class();\n};") << QString("Class::Class() {}\n") << QualifiedIdentifier("Class::Class"); } diff --git a/languages/qmljs/duchain/tests/test_qmljsdeclarations.cpp b/languages/qmljs/duchain/tests/test_qmljsdeclarations.cpp index 3ee2c1db42..dc90925387 100644 --- a/languages/qmljs/duchain/tests/test_qmljsdeclarations.cpp +++ b/languages/qmljs/duchain/tests/test_qmljsdeclarations.cpp @@ -1,297 +1,288 @@ /* * This file is part of KDevelop * Copyright 2013 Milian Wolff * * 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 . */ #include "test_qmljsdeclarations.h" #include "../helper.h" #include "../parsesession.h" #include "../declarationbuilder.h" #include "../cache.h" #include #include #include #include #include #include #include #include QTEST_GUILESS_MAIN(TestDeclarations); using namespace KDevelop; void TestDeclarations::initTestCase() { - AutoTestShell::init(); + AutoTestShell::init({"kdevqmljslanguagesupport"}); TestCore::initialize(Core::NoUi); QmlJS::registerDUChainItems(); } void TestDeclarations::cleanupTestCase() { TestCore::shutdown(); } void TestDeclarations::testJSProblems() { const IndexedString file(QUrl("file:///internal/jsproblems.js")); ParseSession session(file, "function f(a) {}\n" "f(2);\n" "f(true);\n", 0); QVERIFY(session.ast()); DeclarationBuilder builder(&session); builder.build(file, session.ast()); auto problems = session.problems(); QCOMPARE(problems.count(), 1); QCOMPARE((KTextEditor::Range)problems.at(0)->finalLocation(), KTextEditor::Range(2, 2, 2, 6)); } void TestDeclarations::testFunction() { const IndexedString file(QUrl("file:///internal/functionArgs.js")); // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/**\n * some comment\n */\n" "function foo(arg1, arg2, arg3) { var i = 0; }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::JavaScript); DeclarationBuilder builder(&session); ReferencedTopDUContext top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 3); // module, exports and foo FunctionDeclaration* fooDec = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(fooDec); QCOMPARE(fooDec->range(), RangeInRevision(3, 9, 3, 12)); QCOMPARE(QString::fromUtf8(fooDec->comment()), QString("some comment")); QVERIFY(fooDec->internalContext()); DUContext* argCtx = fooDec->internalContext(); QCOMPARE(argCtx->localDeclarations().size(), 3); Declaration* arg1 = argCtx->localDeclarations().at(0); QCOMPARE(arg1->identifier().toString(), QString("arg1")); QCOMPARE(arg1->range(), RangeInRevision(3, 13, 3, 17)); Declaration* arg2 = argCtx->localDeclarations().at(1); QCOMPARE(arg2->identifier().toString(), QString("arg2")); QCOMPARE(arg2->range(), RangeInRevision(3, 19, 3, 23)); Declaration* arg3 = argCtx->localDeclarations().at(2); QCOMPARE(arg3->identifier().toString(), QString("arg3")); QCOMPARE(arg3->range(), RangeInRevision(3, 25, 3, 29)); FunctionType::Ptr funType = fooDec->type(); QVERIFY(funType); QVERIFY(funType->returnType().cast()); QCOMPARE(funType->returnType().cast()->dataType(), (uint) IntegralType::TypeVoid); QCOMPARE(argCtx->childContexts().size(), 2); DUContext* bodyCtx = argCtx->childContexts().at(1); QVERIFY(bodyCtx); QVERIFY(bodyCtx->findDeclarations(arg1->identifier()).contains(arg1)); QVERIFY(bodyCtx->findDeclarations(arg2->identifier()).contains(arg2)); QVERIFY(bodyCtx->findDeclarations(arg3->identifier()).contains(arg3)); QCOMPARE(bodyCtx->localDeclarations().count(), 1); } void TestDeclarations::testQMLId() { const IndexedString file(QUrl("file:///internal/qmlId.qml")); ReferencedTopDUContext top; DeclarationPointer oldDec; { // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/** file comment **/\n" "import QtQuick 1.0\n" "/**\n * some comment\n */\n" "Text { id: test; Text { id: child; } }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 5); // module, exports, Text, test and child are all in the global scope // First declaration, the anonymous class ClassDeclaration* classDecl = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(classDecl); QCOMPARE(classDecl->abstractType()->toString(), QString("qmlId")); QCOMPARE(classDecl->range(), RangeInRevision(5, 0, 5, 0)); QVERIFY(classDecl->internalContext()); QCOMPARE(classDecl->internalContext()->range(), RangeInRevision(5, 6, 5, 37)); // Second declaration: test Declaration* dec = top->localDeclarations().at(3); QVERIFY(dec); QCOMPARE(dec->identifier().toString(), QString("test")); QCOMPARE(dec->abstractType()->toString(), QString("qmlId")); oldDec = dec; } // test recompile { // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/** file comment **/\n" "import QtQuick 1.0\n" "/**\n * some comment\n */\n" "Text { id: test; Text { id: child;}\n" " Text {id: foo;} }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); ReferencedTopDUContext top2 = builder.build(file, session.ast(), top); QVERIFY(top2); QCOMPARE(top2.data(), top.data()); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 6); // module, exports, Text, test, child and foo // First declaration, the anonymous class ClassDeclaration* classDecl = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(classDecl); QCOMPARE(classDecl->abstractType()->toString(), QString("qmlId")); QCOMPARE(classDecl->range(), RangeInRevision(5, 0, 5, 0)); QVERIFY(classDecl->internalContext()); QCOMPARE(classDecl->internalContext()->range(), RangeInRevision(5, 6, 6, 17)); // Second declaration: test Declaration* dec = top->localDeclarations().at(3); QVERIFY(dec); QCOMPARE(dec->identifier().toString(), QString("test")); QCOMPARE(dec->abstractType()->toString(), QString("qmlId")); } } void TestDeclarations::testProperty() { const IndexedString file(QUrl("file:///internal/qmlProperty.qml")); // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "Text {\n" " /// some comment\n" " property int foo;\n" "}", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); ReferencedTopDUContext top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 3); // module, exports, Text ClassDeclaration* text = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(text); QVERIFY(text->internalContext()); QCOMPARE(text->internalContext()->type(), DUContext::Class); QCOMPARE(text->internalContext()->localDeclarations().size(), 1); ClassMemberDeclaration* foo = dynamic_cast(text->internalContext()->localDeclarations().first()); QVERIFY(foo); QCOMPARE(foo->identifier().toString(), QString("foo")); QVERIFY(foo->abstractType()); QCOMPARE(foo->abstractType()->toString(), QString("int")); QCOMPARE(QString::fromUtf8(foo->comment()), QString("some comment")); } /** * Test that all qmltypes files for built-in QtQuick modules are found on the system. * These files are also available on CI machines, since an installed Qt5 is assumed */ void TestDeclarations::testQMLtypesImportPaths() { KDevelop::IndexedString stubPath; QString path; // QtQuick QML modules path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick", "2.0"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtTest", "1.1"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Layouts", "1.1"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Controls", "1.1"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Dialogs", "1.1"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); - path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Extras", "1.1"); - QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); - path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.LocalStorage", "2.0"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Particles", "2.0"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.Window", "2.2"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, "QtQuick.XmlListModel", "2.0"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); // QtQml QML modules - path = QmlJS::Cache::instance().modulePath(stubPath, "QtQml", "2.2"); - QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); - path = QmlJS::Cache::instance().modulePath(stubPath, "QtQml.Models", "2.3"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); - path = QmlJS::Cache::instance().modulePath(stubPath, "QtQml.StateMachine", "1.0"); - QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); - // QtMultimedia QML modules path = QmlJS::Cache::instance().modulePath(stubPath, "QtMultimedia", "5.6"); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); } diff --git a/languages/qmljs/tests/files/directory_import.qml b/languages/qmljs/tests/files/directory_import.qml index babf392e59..4e308189a1 100644 --- a/languages/qmljs/tests/files/directory_import.qml +++ b/languages/qmljs/tests/files/directory_import.qml @@ -1,22 +1,16 @@ import ".././files/directory_to_import/" as MyDir import "types.js" as MyTypes -import org.kde.kdevplatform 1.0 as KDevPlatform /** * "internalContext" : { "importedParents" : { "0" : {"type" : "Class" }}} */ MyDir.MyComponent { id: component onTest: { /** * "type" : { "toString" : "bool" } */ var from_types_js = MyTypes.simple_compare; - - /** - * "EXPECT_FAIL" : {"type" : { "toString": "ICore" }} - */ - var icore = KDevPlatform.ICore; } } diff --git a/languages/qmljs/tests/test_files.cpp b/languages/qmljs/tests/test_files.cpp index 3348ac2a59..66bd48532f 100644 --- a/languages/qmljs/tests/test_files.cpp +++ b/languages/qmljs/tests/test_files.cpp @@ -1,140 +1,141 @@ /************************************************************************************* * Copyright (C) 2013 by Milian Wolff * * Copyright (C) 2013 Olivier de Gaalon * * * * 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) any later version. * * * * 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, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "test_files.h" #include #include #include #include #include #include #include #include "testfilepaths.h" //Include all used json tests, otherwise "Test not found" #include #include #include #include #include // #include "cppjsontests.h" using namespace KDevelop; QTEST_MAIN(TestFiles) void TestFiles::initTestCase() { - AutoTestShell::init(); + AutoTestShell::init({"kdevqmljslanguagesupport"}); TestCore::initialize(KDevelop::Core::NoUi); DUChain::self()->disablePersistentStorage(); Core::self()->languageController()->backgroundParser()->setDelay(0); CodeRepresentation::setDiskChangesForbidden(true); } void TestFiles::cleanupTestCase() { TestCore::shutdown(); } void TestFiles::testQMLCustomComponent() { // First parse CustomComponent, so that it is visible and can be used // by CustomComponentUser. Then re-parse CustomComponent and assert that // it has been used. parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml", false); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponentUser.qml"); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml"); } void TestFiles::testQMLTypes() { parseAndCheck(TEST_FILES_DIR "/qmltypes/AnItem.qml", true); } void TestFiles::testTypeMismatchFalsePositives() { parseAndCheck(TEST_FILES_DIR "/type_mismatch_false_positives/code.js", true); } void TestFiles::testJSUsesBetweenFiles() { parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js", false); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_use.js"); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js"); } void TestFiles::testNodeJS() { parseAndCheck(TEST_FILES_DIR "/node_modules/module.js", false); // Ensure that module.js is in the DUChain parseAndCheck(TEST_FILES_DIR "/node.js/module2.js", false); parseAndCheck(TEST_FILES_DIR "/node.js/main.js"); } void TestFiles::testFiles_data() { QTest::addColumn("fileName"); const QString testDirPath = TEST_FILES_DIR; QStringList files = QDir(testDirPath).entryList(QStringList() << "*.js" << "*.qml", QDir::Files); foreach (QString file, files) { QTest::newRow(file.toUtf8()) << QString(testDirPath + "/" + file); } } void TestFiles::testFiles() { QFETCH(QString, fileName); parseAndCheck(fileName); } void TestFiles::parseAndCheck(const QString& fileName, bool check) { const IndexedString indexedFileName(fileName); ReferencedTopDUContext top = DUChain::self()->waitForUpdate(indexedFileName, KDevelop::TopDUContext::AllDeclarationsContextsAndUses); while (!ICore::self()->languageController()->backgroundParser()->isIdle()) { QTest::qWait(500); } QVERIFY(top); if (check) { DUChainReadLocker lock; DeclarationValidator validator; top->visit(validator); QVERIFY(validator.testsPassed()); if (!top->problems().isEmpty()) { foreach(auto p, top->problems()) { qDebug() << p; } } if (!QTest::currentDataTag() || strcmp("failparse.js", QTest::currentDataTag()) != 0) { QEXPECT_FAIL("plugins.qml", "not working properly yet", Continue); QEXPECT_FAIL("qrc_import.qml", "just making sure it does not crash", Continue); + QEXPECT_FAIL("dynamicObjectProperties.2.qml", "just making sure it does not crash", Continue); QVERIFY(top->problems().isEmpty()); } } } diff --git a/projectmanagers/custom-buildsystem/tests/test_custombuildsystemplugin.cpp b/projectmanagers/custom-buildsystem/tests/test_custombuildsystemplugin.cpp index a77350cafc..a3f80a6fa7 100644 --- a/projectmanagers/custom-buildsystem/tests/test_custombuildsystemplugin.cpp +++ b/projectmanagers/custom-buildsystem/tests/test_custombuildsystemplugin.cpp @@ -1,119 +1,113 @@ /************************************************************************ * KDevelop4 Custom Buildsystem Support * * * * Copyright 2010 Andreas Pakulat * * * * 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 or version 3 of the License, or * * (at your option) any later version. * * * * 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 . * ************************************************************************/ #include "test_custombuildsystemplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "testconfig.h" using KDevelop::Core; using KDevelop::ICore; using KDevelop::IProject; using KDevelop::TestCore; using KDevelop::AutoTestShell; using KDevelop::KDevSignalSpy; using KDevelop::Path; QTEST_MAIN(TestCustomBuildSystemPlugin) void TestCustomBuildSystemPlugin::cleanupTestCase() { TestCore::shutdown(); } void TestCustomBuildSystemPlugin::initTestCase() { AutoTestShell::init(); TestCore::initialize(); } void TestCustomBuildSystemPlugin::loadSimpleProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/simpleproject/simpleproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL( projectOpened( KDevelop::IProject* ) ) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened - if( !projectSpy->wait( 20000 ) ) { - qFatal("Expected project to be loaded within 20 seconds, but this didn't happen"); - } + QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( "SimpleProject" ); QVERIFY( project ); QCOMPARE( project->buildSystemManager()->buildDirectory( project->projectItem() ), Path( "file:///home/andreas/projects/testcustom/build/" ) ); } void TestCustomBuildSystemPlugin::buildDirProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/builddirproject/builddirproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL( projectOpened( KDevelop::IProject* ) ) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened - if( !projectSpy->wait( 20000 ) ) { - qFatal("Expected project to be loaded within 20 seconds, but this didn't happen"); - } + QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( "BuilddirProject" ); QVERIFY( project ); Path currentBuilddir = project->buildSystemManager()->buildDirectory( project->projectItem() ); QCOMPARE( currentBuilddir, Path( projecturl ).parent() ); } void TestCustomBuildSystemPlugin::loadMultiPathProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/multipathproject/multipathproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL( projectOpened( KDevelop::IProject* ) ) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened - if( !projectSpy->wait( 20000 ) ) { - qFatal("Expected project to be loaded within 20 seconds, but this didn't happen"); - } + QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( "MultiPathProject" ); QVERIFY( project ); KDevelop::ProjectBaseItem* mainfile = 0; for (const auto& file: project->fileSet() ) { for (auto i: project->filesForPath(file)) { if( i->text() == "main.cpp" ) { mainfile = i; break; } } } QVERIFY(mainfile); QCOMPARE( project->buildSystemManager()->buildDirectory( mainfile ), Path( "file:///home/andreas/projects/testcustom/build2/src" ) ); }