aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Donchevskii <ivan.donchevskii@qt.io>2017-07-28 09:48:13 +0200
committerIvan Donchevskii <ivan.donchevskii@qt.io>2017-08-18 12:10:02 +0000
commit9c9baaac04f2d5f3fc0c3e429c1a4b7e9ba35824 (patch)
treeb14a5a53096179892f0d276cf89298c5c30bc9f9
parent32d38789f9bb322ef9510cf79c1ce0de017e07b6 (diff)
Clang: add unit-tests for follow symbol
Change-Id: I336fe29052237ede6f5c578daf01557400d9027c Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
-rw-r--r--src/tools/clangbackend/ipcsource/clangfollowsymboljob.h6
-rw-r--r--tests/unit/unittest/clangcodemodelserver-test.cpp44
-rw-r--r--tests/unit/unittest/clangfollowsymbol-test.cpp366
-rw-r--r--tests/unit/unittest/data/followsymbol_header.h59
-rw-r--r--tests/unit/unittest/data/followsymbol_main.cpp67
-rw-r--r--tests/unit/unittest/readandwritemessageblock-test.cpp9
-rw-r--r--tests/unit/unittest/unittest.pro1
7 files changed, 552 insertions, 0 deletions
diff --git a/src/tools/clangbackend/ipcsource/clangfollowsymboljob.h b/src/tools/clangbackend/ipcsource/clangfollowsymboljob.h
index cd96b7158d..2d56e2ca10 100644
--- a/src/tools/clangbackend/ipcsource/clangfollowsymboljob.h
+++ b/src/tools/clangbackend/ipcsource/clangfollowsymboljob.h
@@ -41,6 +41,12 @@ public:
, failedToFollow(failedToFollow)
{}
+ friend bool operator==(const FollowSymbolResult &first, const FollowSymbolResult &second)
+ {
+ return first.range == second.range
+ && first.failedToFollow == second.failedToFollow;
+ }
+
SourceRangeContainer range;
bool failedToFollow = false;
};
diff --git a/tests/unit/unittest/clangcodemodelserver-test.cpp b/tests/unit/unittest/clangcodemodelserver-test.cpp
index 0dad554a0b..eeb500215b 100644
--- a/tests/unit/unittest/clangcodemodelserver-test.cpp
+++ b/tests/unit/unittest/clangcodemodelserver-test.cpp
@@ -126,6 +126,7 @@ protected:
void requestDocumentAnnotations(const Utf8String &filePath);
void requestReferences(quint32 documentRevision = 0);
+ void requestFollowSymbol(quint32 documentRevision = 0);
void completeCode(const Utf8String &filePath, uint line = 1, uint column = 1,
const Utf8String &projectPartId = Utf8String());
@@ -144,6 +145,7 @@ protected:
void expectCompletionFromFileAUnsavedMethodVersion2();
void expectNoCompletionWithUnsavedMethod();
void expectReferences();
+ void expectFollowSymbol();
void expectDocumentAnnotationsChangedForFileBWithSpecificHighlightingMark();
static const Utf8String unsavedContent(const QString &unsavedFilePath);
@@ -206,6 +208,25 @@ TEST_F(ClangCodeModelServerSlowTest, RequestReferencesTakesRevisionFromMessage)
queue.clear(); // Avoid blocking
}
+TEST_F(ClangCodeModelServerSlowTest, RequestFollowSymbolForCurrentDocumentRevision)
+{
+ registerProjectAndFileAndWaitForFinished(filePathC);
+
+ expectFollowSymbol();
+ requestFollowSymbol();
+}
+
+TEST_F(ClangCodeModelServerSlowTest, RequestFollowSymbolTakesRevisionFromMessage)
+{
+ registerProjectAndFileAndWaitForFinished(filePathC);
+
+ requestFollowSymbol(/*documentRevision=*/ 99);
+
+ JobRequests &queue = documentProcessorForFile(filePathC).queue();
+ Utils::anyOf(queue, [](const JobRequest &request) { return request.documentRevision == 99; });
+ queue.clear(); // Avoid blocking
+}
+
TEST_F(ClangCodeModelServerSlowTest, NoInitialDocumentAnnotationsForClosedDocument)
{
const int expectedDocumentAnnotationsChangedCount = 0;
@@ -555,6 +576,20 @@ void ClangCodeModelServer::expectReferences()
.Times(1);
}
+void ClangCodeModelServer::expectFollowSymbol()
+{
+ const ClangBackEnd::SourceRangeContainer classDefinition{
+ {filePathC, 40, 7},
+ {filePathC, 40, 10}
+ };
+
+ EXPECT_CALL(mockClangCodeModelClient,
+ followSymbol(
+ Property(&FollowSymbolMessage::sourceRange,
+ Eq(classDefinition))))
+ .Times(1);
+}
+
void ClangCodeModelServer::expectCompletionFromFileA()
{
const CodeCompletion completion(Utf8StringLiteral("Function"),
@@ -580,6 +615,15 @@ void ClangCodeModelServer::requestReferences(quint32 documentRevision)
clangServer.requestReferences(message);
}
+void ClangCodeModelServer::requestFollowSymbol(quint32 documentRevision)
+{
+ const FileContainer fileContainer{filePathC, projectPartId, Utf8StringVector(),
+ documentRevision};
+ const RequestFollowSymbolMessage message{fileContainer, QVector<Utf8String>(), 43, 9};
+
+ clangServer.requestFollowSymbol(message);
+}
+
void ClangCodeModelServer::expectDocumentAnnotationsChangedForFileBWithSpecificHighlightingMark()
{
HighlightingTypes types;
diff --git a/tests/unit/unittest/clangfollowsymbol-test.cpp b/tests/unit/unittest/clangfollowsymbol-test.cpp
new file mode 100644
index 0000000000..7346e31c25
--- /dev/null
+++ b/tests/unit/unittest/clangfollowsymbol-test.cpp
@@ -0,0 +1,366 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "googletest.h"
+#include "testenvironment.h"
+
+#include <clangbackendipc_global.h>
+#include <clangfollowsymboljob.h>
+#include <clangdocument.h>
+#include <clangdocuments.h>
+#include <clangtranslationunit.h>
+#include <fixitcontainer.h>
+#include <projectpart.h>
+#include <projects.h>
+#include <sourcelocationcontainer.h>
+#include <sourcerangecontainer.h>
+#include <unsavedfiles.h>
+#include <commandlinearguments.h>
+
+#include <utils/qtcassert.h>
+
+#include <clang-c/Index.h>
+
+using ::testing::Contains;
+using ::testing::Not;
+using ::testing::ContainerEq;
+using ::testing::Eq;
+using ::testing::PrintToString;
+
+using ::ClangBackEnd::ProjectPart;
+using ::ClangBackEnd::SourceLocationContainer;
+using ::ClangBackEnd::Document;
+using ::ClangBackEnd::UnsavedFiles;
+using ::ClangBackEnd::ReferencesResult;
+using ::ClangBackEnd::SourceRangeContainer;
+
+using ClangBackEnd::FollowSymbolResult;
+
+namespace {
+const Utf8String sourceFilePath = Utf8StringLiteral(TESTDATA_DIR"/followsymbol_main.cpp");
+const Utf8String headerFilePath = Utf8StringLiteral(TESTDATA_DIR"/followsymbol_header.h");
+const Utf8String cursorPath = Utf8StringLiteral(TESTDATA_DIR"/cursor.cpp");
+
+std::ostream &operator<<(std::ostream &os, const FollowSymbolResult &result)
+{
+ os << result.range;
+
+ return os;
+}
+
+MATCHER_P3(MatchesHeaderSourceRange, line, column, length,
+ std::string(negation ? "isn't " : "is ")
+ + PrintToString(SourceRangeContainer {
+ SourceLocationContainer(headerFilePath, line, column),
+ SourceLocationContainer(headerFilePath, line, column + length)
+ })
+ )
+{
+ const SourceRangeContainer expected = {
+ SourceLocationContainer(headerFilePath, line, column),
+ SourceLocationContainer(headerFilePath, line, column + length)
+ };
+
+ return arg == FollowSymbolResult {expected, false};
+}
+
+MATCHER_P3(MatchesSourceRange, line, column, length,
+ std::string(negation ? "isn't " : "is ")
+ + PrintToString(SourceRangeContainer {
+ SourceLocationContainer(sourceFilePath, line, column),
+ SourceLocationContainer(sourceFilePath, line, column + length)
+ })
+ )
+{
+ const SourceRangeContainer expected = {
+ SourceLocationContainer(sourceFilePath, line, column),
+ SourceLocationContainer(sourceFilePath, line, column + length)
+ };
+
+ return arg == FollowSymbolResult {expected, false};
+}
+
+MATCHER_P4(MatchesFileSourceRange, filename, line, column, length,
+ std::string(negation ? "isn't " : "is ")
+ + PrintToString(SourceRangeContainer {
+ SourceLocationContainer(filename, line, column),
+ SourceLocationContainer(filename, line, column + length)
+ })
+ )
+{
+ const SourceRangeContainer expected = {
+ SourceLocationContainer(filename, line, column),
+ SourceLocationContainer(filename, line, column + length)
+ };
+
+ return arg == FollowSymbolResult {expected, false};
+}
+
+class Data {
+public:
+ Data()
+ {
+ m_document.parse();
+ m_headerDocument.parse();
+ }
+
+ const Document &document() const { return m_document; }
+ const Document &headerDocument() const { return m_headerDocument; }
+ const QVector<Utf8String> &deps() const { return m_deps; }
+private:
+ ProjectPart m_projectPart{
+ Utf8StringLiteral("projectPartId"),
+ TestEnvironment::addPlatformArguments({Utf8StringLiteral("-std=c++14")})};
+ ClangBackEnd::ProjectParts m_projects;
+ ClangBackEnd::UnsavedFiles m_unsavedFiles;
+ ClangBackEnd::Documents m_documents{m_projects, m_unsavedFiles};
+ Document m_document = {sourceFilePath,
+ m_projectPart,
+ Utf8StringVector(),
+ m_documents};
+ Document m_headerDocument = {headerFilePath,
+ m_projectPart,
+ Utf8StringVector(),
+ m_documents};
+ QVector<Utf8String> m_deps {sourceFilePath, cursorPath};
+};
+
+class FollowSymbol : public ::testing::Test
+{
+protected:
+ FollowSymbolResult followSymbol(uint line, uint column)
+ {
+ ClangBackEnd::TranslationUnitUpdateInput updateInput = d->document().createUpdateInput();
+ const ClangBackEnd::CommandLineArguments currentArgs(updateInput.filePath.constData(),
+ updateInput.projectArguments,
+ updateInput.fileArguments,
+ false);
+ return d->document().translationUnit().followSymbol(line, column,
+ d->deps(),
+ currentArgs);
+ }
+
+ FollowSymbolResult followHeaderSymbol(uint line, uint column)
+ {
+ ClangBackEnd::TranslationUnitUpdateInput updateInput
+ = d->headerDocument().createUpdateInput();
+ const ClangBackEnd::CommandLineArguments currentArgs(updateInput.filePath.constData(),
+ updateInput.projectArguments,
+ updateInput.fileArguments,
+ false);
+ return d->headerDocument().translationUnit().followSymbol(line, column,
+ d->deps(),
+ currentArgs);
+ }
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+private:
+ static std::unique_ptr<Data> d;
+};
+
+TEST_F(FollowSymbol, CursorOnNamespace)
+{
+ const auto namespaceDefinition = followSymbol(27, 1);
+
+ ASSERT_THAT(namespaceDefinition, MatchesHeaderSourceRange(28, 11, 6));
+}
+
+TEST_F(FollowSymbol, CursorOnClassReference)
+{
+ const auto classDefinition = followSymbol(27, 9);
+
+ ASSERT_THAT(classDefinition, MatchesHeaderSourceRange(34, 7, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnClassForwardDeclarationFollowToHeader)
+{
+ const auto classDefinition = followHeaderSymbol(32, 7);
+
+ ASSERT_THAT(classDefinition, MatchesHeaderSourceRange(34, 7, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnClassForwardDeclarationFollowToCpp)
+{
+ const auto classDefinition = followHeaderSymbol(48, 9);
+
+ ASSERT_THAT(classDefinition, MatchesSourceRange(54, 7, 8));
+}
+
+TEST_F(FollowSymbol, CursorOnClassDefinition)
+{
+ const auto classForwardDeclaration = followHeaderSymbol(34, 7);
+
+ ASSERT_THAT(classForwardDeclaration, MatchesHeaderSourceRange(32, 7, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnClassDefinitionInCpp)
+{
+ const auto classForwardDeclaration = followSymbol(54, 7);
+
+ ASSERT_THAT(classForwardDeclaration, MatchesHeaderSourceRange(48, 7, 8));
+}
+
+TEST_F(FollowSymbol, CursorOnConstructorDeclaration)
+{
+ const auto constructorDefinition = followHeaderSymbol(36, 5);
+
+ ASSERT_THAT(constructorDefinition, MatchesSourceRange(27, 14, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnConstructorDefinition)
+{
+ const auto constructorDeclaration = followSymbol(27, 14);
+
+ ASSERT_THAT(constructorDeclaration, MatchesHeaderSourceRange(36, 5, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnMemberReference)
+{
+ const auto memberDeclaration = followSymbol(39, 10);
+
+ ASSERT_THAT(memberDeclaration, MatchesHeaderSourceRange(38, 18, 6));
+}
+
+TEST_F(FollowSymbol, CursorOnMemberDeclaration)
+{
+ const auto sameMemberDeclaration = followHeaderSymbol(38, 18);
+
+ ASSERT_THAT(sameMemberDeclaration, MatchesHeaderSourceRange(38, 18, 6));
+}
+
+TEST_F(FollowSymbol, CursorOnFunctionReference)
+{
+ const auto functionDefinition = followSymbol(66, 12);
+
+ ASSERT_THAT(functionDefinition, MatchesSourceRange(35, 5, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnMemberFunctionReference)
+{
+ const auto memberFunctionDefinition = followSymbol(42, 12);
+
+ ASSERT_THAT(memberFunctionDefinition, MatchesSourceRange(49, 21, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnFunctionWithoutDefinitionReference)
+{
+ const auto functionDeclaration = followSymbol(43, 5);
+
+ ASSERT_THAT(functionDeclaration, MatchesHeaderSourceRange(59, 5, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnFunctionDefinition)
+{
+ const auto functionDeclaration = followSymbol(35, 5);
+
+ ASSERT_THAT(functionDeclaration, MatchesHeaderSourceRange(52, 5, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnMemberFunctionDefinition)
+{
+ const auto memberFunctionDeclaration = followSymbol(49, 21);
+
+ ASSERT_THAT(memberFunctionDeclaration, MatchesHeaderSourceRange(43, 9, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnMemberFunctionDeclaration)
+{
+ const auto memberFunctionDefinition = followHeaderSymbol(43, 9);
+
+ ASSERT_THAT(memberFunctionDefinition, MatchesSourceRange(49, 21, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnInclude)
+{
+ const auto startOfIncludeFile = followSymbol(25, 13);
+
+ ASSERT_THAT(startOfIncludeFile, MatchesHeaderSourceRange(1, 1, 0));
+}
+
+TEST_F(FollowSymbol, CursorOnLocalVariable)
+{
+ const auto variableDeclaration = followSymbol(39, 6);
+
+ ASSERT_THAT(variableDeclaration, MatchesSourceRange(36, 9, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnClassAlias)
+{
+ const auto aliasDefinition = followSymbol(36, 5);
+
+ ASSERT_THAT(aliasDefinition, MatchesSourceRange(33, 7, 3));
+}
+
+TEST_F(FollowSymbol, CursorOnStaticVariable)
+{
+ const auto staticVariableDeclaration = followSymbol(40, 27);
+
+ ASSERT_THAT(staticVariableDeclaration, MatchesHeaderSourceRange(30, 7, 7));
+}
+
+TEST_F(FollowSymbol, CursorOnFunctionFromOtherClass)
+{
+ const auto functionDefinition = followSymbol(62, 39);
+
+ ASSERT_THAT(functionDefinition, MatchesFileSourceRange(cursorPath, 104, 22, 8));
+}
+
+TEST_F(FollowSymbol, CursorOnDefineReference)
+{
+ const auto functionDefinition = followSymbol(66, 43);
+
+ ASSERT_THAT(functionDefinition, MatchesHeaderSourceRange(27, 9, 11));
+}
+
+
+TEST_F(FollowSymbol, CursorInTheMiddleOfNamespace)
+{
+ const auto namespaceDefinition = followSymbol(27, 3);
+
+ ASSERT_THAT(namespaceDefinition, MatchesHeaderSourceRange(28, 11, 6));
+}
+
+TEST_F(FollowSymbol, CursorAfterNamespace)
+{
+ const auto namespaceDefinition = followSymbol(27, 7);
+
+ ASSERT_THAT(namespaceDefinition, MatchesFileSourceRange(QString(""), 0, 0, 0));
+}
+
+std::unique_ptr<Data> FollowSymbol::d;
+
+void FollowSymbol::SetUpTestCase()
+{
+ d.reset(new Data);
+}
+
+void FollowSymbol::TearDownTestCase()
+{
+ d.reset();
+}
+
+} // anonymous namespace
diff --git a/tests/unit/unittest/data/followsymbol_header.h b/tests/unit/unittest/data/followsymbol_header.h
new file mode 100644
index 0000000000..8630834023
--- /dev/null
+++ b/tests/unit/unittest/data/followsymbol_header.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#pragma once
+
+#define TEST_DEFINE 1
+namespace Fooish
+{
+float flvalue = 100.f;
+
+class Bar;
+
+class Bar {
+public:
+ Bar();
+
+ volatile int member = 0;
+};
+
+struct Barish
+{
+ int foo(float p, int u);
+ int mem = 10;
+};
+}
+
+class FooClass;
+
+int foo(const float p, int u);
+
+int foo();
+
+int foo(float p, int u)
+{
+ return foo() + p + u;
+}
+
+int foo(int x, float y);
diff --git a/tests/unit/unittest/data/followsymbol_main.cpp b/tests/unit/unittest/data/followsymbol_main.cpp
new file mode 100644
index 0000000000..3727f494cf
--- /dev/null
+++ b/tests/unit/unittest/data/followsymbol_main.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#include "followsymbol_header.h"
+#include "cursor.h"
+Fooish::Bar::Bar() {
+
+}
+
+class X;
+
+using YYY = Fooish::Bar;
+
+int foo() {
+ YYY bar;
+ bar.member = 30;
+ Fooish::Barish barish;
+ bar.member++;
+ barish.mem = Fooish::flvalue;
+
+ barish.foo(1.f, 2);
+ foo(1, 2.f);
+ return 1;
+
+ X* x;
+}
+
+int Fooish::Barish::foo(float p, int u)
+{
+ return ::foo() + p + u;
+}
+
+class FooClass
+{
+public:
+ FooClass();
+ static int mememember;
+};
+
+FooClass::FooClass() {
+ NonFinalStruct nfStruct; nfStruct.function();
+}
+
+int main() {
+ return foo() + FooClass::mememember + TEST_DEFINE;
+}
diff --git a/tests/unit/unittest/readandwritemessageblock-test.cpp b/tests/unit/unittest/readandwritemessageblock-test.cpp
index a95e2866b4..ac57f83398 100644
--- a/tests/unit/unittest/readandwritemessageblock-test.cpp
+++ b/tests/unit/unittest/readandwritemessageblock-test.cpp
@@ -203,6 +203,15 @@ TEST_F(ReadAndWriteMessageBlock, CompareRequestReferences)
CompareMessage(ClangBackEnd::RequestReferencesMessage{fileContainer, 13, 37});
}
+TEST_F(ReadAndWriteMessageBlock, CompareRequestFollowSymbol)
+{
+ QVector<Utf8String> dependentFiles;
+ dependentFiles.push_back(QString("somefile.cpp"));
+ dependentFiles.push_back(QString("otherfile.cpp"));
+ CompareMessage(ClangBackEnd::RequestFollowSymbolMessage{fileContainer, dependentFiles, 13, 37,
+ false});
+}
+
TEST_F(ReadAndWriteMessageBlock, CompareReferences)
{
const QVector<ClangBackEnd::SourceRangeContainer> references{
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index 67226416af..8a29b173aa 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -37,6 +37,7 @@ DEFINES += CPPTOOLS_JSON=\"R\\\"xxx($${cpptoolsjson.output})xxx\\\"\"
SOURCES += \
changedfilepathcompressor-test.cpp \
+ clangfollowsymbol-test.cpp \
clangpathwatcher-test.cpp \
clangqueryexamplehighlightmarker-test.cpp \
clangqueryhighlightmarker-test.cpp \