/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ /** * @file clangcompletion_test.cpp * @brief Performs test for C/C++ code completion * * All test cases given as strings with @ character that points to completion * location. */ #ifdef WITH_TESTS // Disabled because there still no tool to detect system Objective-C headers #define ENABLE_OBJC_TESTS 0 #include #include #undef interface // Canceling "#DEFINE interface struct" on Windows #include "completiontesthelper.h" #include "../clangcodemodelplugin.h" using namespace ClangCodeModel; using namespace ClangCodeModel::Internal; //////////////////////////////////////////////////////////////////////////////// // Test cases /** * \defgroup Regression tests * * This group tests possible regressions in non-standard completion chunks * handling: for example, macro arguments and clang's code snippets. * * @{ */ void ClangCodeModelPlugin::test_CXX_regressions() { QFETCH(QString, file); QFETCH(QStringList, unexpected); QFETCH(QStringList, mustHave); CompletionTestHelper helper; helper << file; QStringList proposals = helper.codeCompleteTexts(); foreach (const QString &p, unexpected) QTEST_ASSERT(false == proposals.contains(p)); foreach (const QString &p, mustHave) QTEST_ASSERT(true == proposals.contains(p)); } void ClangCodeModelPlugin::test_CXX_regressions_data() { QTest::addColumn("file"); QTest::addColumn("unexpected"); QTest::addColumn("mustHave"); QString file; QStringList unexpected; QStringList mustHave; file = QLatin1String("cxx_regression_1.cpp"); mustHave << QLatin1String("sqr"); mustHave << QLatin1String("~Math"); unexpected << QLatin1String("operator="); QTest::newRow("case 1: method call completion") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_2.cpp"); unexpected << QLatin1String("i_second"); unexpected << QLatin1String("c_second"); unexpected << QLatin1String("f_second"); mustHave << QLatin1String("i_first"); mustHave << QLatin1String("c_first"); QTest::newRow("case 2: multiple anonymous structs") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_3.cpp"); mustHave << QLatin1String("i8"); mustHave << QLatin1String("i64"); mustHave << QLatin1String("~Priv"); unexpected << QLatin1String("operator="); QTest::newRow("case 3: nested class resolution") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_4.cpp"); mustHave << QLatin1String("action"); QTest::newRow("case 4: local (in function) class resolution") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_5.cpp"); mustHave << QLatin1String("doB"); unexpected << QLatin1String("doA"); QTest::newRow("case 5: nested template class resolution") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_6.cpp"); mustHave << QLatin1String("OwningPtr"); QTest::newRow("case 6: using particular symbol from namespace") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_7.cpp"); mustHave << QLatin1String("dataMember"); mustHave << QLatin1String("anotherMember"); QTest::newRow("case 7: template class inherited from template parameter") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_8.cpp"); mustHave << QLatin1String("utils::"); unexpected << QLatin1String("utils"); QTest::newRow("case 8: namespace completion in function body") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); file = QLatin1String("cxx_regression_9.cpp"); mustHave << QLatin1String("EnumScoped::Value1"); mustHave << QLatin1String("EnumScoped::Value2"); mustHave << QLatin1String("EnumScoped::Value3"); unexpected << QLatin1String("Value1"); unexpected << QLatin1String("EnumScoped"); QTest::newRow("case 9: c++11 enum class, value used in switch and 'case' completed") << file << unexpected << mustHave; mustHave.clear(); unexpected.clear(); } void ClangCodeModelPlugin::test_CXX_snippets() { QFETCH(QString, file); QFETCH(QStringList, texts); QFETCH(QStringList, snippets); Q_ASSERT(texts.size() == snippets.size()); CompletionTestHelper helper; helper << file; QList proposals = helper.codeComplete(); for (int i = 0, n = texts.size(); i < n; ++i) { const QString &text = texts[i]; const QString &snippet = snippets[i]; const QString snippetError = QLatin1String("Text and snippet mismatch: text '") + text + QLatin1String("', snippet '") + snippet + QLatin1String("', got snippet \"%1\""); bool hasText = false; foreach (const CodeCompletionResult &ccr, proposals) { if (ccr.text() != text) continue; hasText = true; QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii()); } const QString textError(QLatin1String("Text not found:") + text); QVERIFY2(hasText, textError.toAscii()); } } void ClangCodeModelPlugin::test_CXX_snippets_data() { QTest::addColumn("file"); QTest::addColumn("texts"); QTest::addColumn("snippets"); QString file; QStringList texts; QStringList snippets; file = QLatin1String("cxx_snippets_1.cpp"); texts << QLatin1String("reinterpret_cast(expression)"); snippets << QLatin1String("reinterpret_cast<$type$>($expression$)"); texts << QLatin1String("static_cast(expression)"); snippets << QLatin1String("static_cast<$type$>($expression$)"); texts << QLatin1String("new type(expressions)"); snippets << QLatin1String("new $type$($expressions$)"); QTest::newRow("case: snippets for var declaration") << file << texts << snippets; texts.clear(); snippets.clear(); file = QLatin1String("cxx_snippets_2.cpp"); texts << QLatin1String("private"); snippets << QLatin1String(""); texts << QLatin1String("protected"); snippets << QLatin1String(""); texts << QLatin1String("public"); snippets << QLatin1String(""); texts << QLatin1String("friend"); snippets << QLatin1String(""); texts << QLatin1String("virtual"); snippets << QLatin1String(""); texts << QLatin1String("typedef type name"); snippets << QLatin1String("typedef $type$ $name$"); QTest::newRow("case: snippets inside class declaration") << file << texts << snippets; texts.clear(); snippets.clear(); file = QLatin1String("cxx_snippets_3.cpp"); texts << QLatin1String("List"); snippets << QLatin1String("List<$class Item$>"); texts << QLatin1String("Tuple"); snippets << QLatin1String("Tuple<$class First$, $class Second$, $typename Third$>"); QTest::newRow("case: template class insertion as snippet") << file << texts << snippets; texts.clear(); snippets.clear(); file = QLatin1String("cxx_snippets_4.cpp"); texts << QLatin1String("clamp"); snippets << QLatin1String(""); texts << QLatin1String("perform"); snippets << QLatin1String("perform<$class T$>"); QTest::newRow("case: template function insertion as snippet") << file << texts << snippets; texts.clear(); snippets.clear(); } void ClangCodeModelPlugin::test_ObjC_hints() { QFETCH(QString, file); QFETCH(QStringList, texts); QFETCH(QStringList, snippets); QFETCH(QStringList, hints); Q_ASSERT(texts.size() == snippets.size()); Q_ASSERT(texts.size() == hints.size()); CompletionTestHelper helper; helper << file; QList proposals = helper.codeComplete(); for (int i = 0, n = texts.size(); i < n; ++i) { const QString &text = texts[i]; const QString &snippet = snippets[i]; const QString &hint = hints[i]; const QString snippetError = QLatin1String("Text and snippet mismatch: text '") + text + QLatin1String("', snippet '") + snippet + QLatin1String("', got snippet \"%1\""); const QString hintError = QLatin1String("Text and hint mismatch: text '") + text + QLatin1String("', hint\n'") + hint + QLatin1String(", got hint\n\"%1\""); bool hasText = false; QStringList texts; foreach (const CodeCompletionResult &ccr, proposals) { texts << ccr.text(); if (ccr.text() != text) continue; hasText = true; QVERIFY2(snippet == ccr.snippet(), snippetError.arg(ccr.snippet()).toAscii()); QVERIFY2(hint == ccr.hint(), hintError.arg(ccr.hint()).toAscii()); } const QString textError(QString::fromLatin1("Text \"%1\" not found in set %2") .arg(text).arg(texts.join(QLatin1Char(',')))); QVERIFY2(hasText, textError.toAscii()); } } static QString makeObjCHint(const char *cHintPattern) { QString hintPattern(QString::fromUtf8(cHintPattern)); QStringList lines = hintPattern.split(QLatin1Char('\n')); QString hint = QLatin1String("

"); bool prependNewline = false; foreach (const QString &line, lines) { if (prependNewline) hint += QLatin1String("
"); prependNewline = true; int i = 0; while (i < line.size() && line[i] == QLatin1Char(' ')) { ++i; hint += QLatin1String(" "); } hint += line.mid(i); } hint += QLatin1String("

"); return hint; } void ClangCodeModelPlugin::test_ObjC_hints_data() { QTest::addColumn("file"); QTest::addColumn("texts"); QTest::addColumn("snippets"); QTest::addColumn("hints"); QString file; QStringList texts; QStringList snippets; QStringList hints; file = QLatin1String("objc_messages_1.mm"); texts << QLatin1String("spectacleQuality:"); snippets << QLatin1String("spectacleQuality:$(bool)$"); hints << makeObjCHint("-(int) spectacleQuality:(bool)"); texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); snippets << QLatin1String("desiredAmountForDramaDose:$(int)$ andPersonsCount:$(int)$"); hints << makeObjCHint("-(int) desiredAmountForDramaDose:(int) \n" " andPersonsCount:(int)"); QTest::newRow("case: objective-c instance messages call") << file << texts << snippets << hints; texts.clear(); snippets.clear(); hints.clear(); file = QLatin1String("objc_messages_2.mm"); texts << QLatin1String("eatenAmount"); snippets << QLatin1String("(int) eatenAmount"); hints << makeObjCHint("+(int) eatenAmount"); texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count"); hints << makeObjCHint("+(int) desiredAmountForDramaDose:(int)dose \n" " andPersonsCount:(int)count"); QTest::newRow("case: objective-c class messages in @implementation") << file << texts << snippets << hints; texts.clear(); snippets.clear(); hints.clear(); file = QLatin1String("objc_messages_3.mm"); texts << QLatin1String("eatenAmount"); snippets << QLatin1String("(int) eatenAmount"); hints << makeObjCHint("-(int) eatenAmount"); texts << QLatin1String("spectacleQuality"); snippets << QLatin1String("(int) spectacleQuality"); hints << makeObjCHint("-(int) spectacleQuality"); texts << QLatin1String("desiredAmountForDramaDose:andPersonsCount:"); snippets << QLatin1String("(int) desiredAmountForDramaDose:(int)dose andPersonsCount:(int)count"); hints << makeObjCHint("-(int) desiredAmountForDramaDose:(int)dose \n" " andPersonsCount:(int)count"); texts << QLatin1String("initWithOldTracker:"); snippets << QLatin1String("(id) initWithOldTracker:(Bbbb *)aabb"); hints << makeObjCHint("-(id) initWithOldTracker:(Bbbb<Aaaa> *)aabb"); QTest::newRow("case: objective-c class messages from base class") << file << texts << snippets << hints; texts.clear(); snippets.clear(); hints.clear(); } #endif