From c7da7ba27bbd3a3934e7674c210938b30724c37e Mon Sep 17 00:00:00 2001 From: mozesl Date: Mon, 27 Nov 2023 00:16:12 +0100 Subject: [PATCH 1/6] initial implementation --- plugins/cpp/model/include/model/cppfunction.h | 1 + plugins/cpp/parser/src/clangastvisitor.h | 65 +++++++++++++++++++ .../model/include/model/cppastnodemetrics.h | 3 +- .../cppmetricsparser/cppmetricsparser.h | 1 + .../parser/src/cppmetricsparser.cpp | 18 +++++ 5 files changed, 87 insertions(+), 1 deletion(-) diff --git a/plugins/cpp/model/include/model/cppfunction.h b/plugins/cpp/model/include/model/cppfunction.h index 32b1dde6a..3867a9433 100644 --- a/plugins/cpp/model/include/model/cppfunction.h +++ b/plugins/cpp/model/include/model/cppfunction.h @@ -18,6 +18,7 @@ struct CppFunction : CppTypedEntity std::vector> parameters; #pragma db on_delete(cascade) std::vector> locals; + unsigned int mccabe; std::string toString() const { diff --git a/plugins/cpp/parser/src/clangastvisitor.h b/plugins/cpp/parser/src/clangastvisitor.h index f50af517c..0b122015e 100644 --- a/plugins/cpp/parser/src/clangastvisitor.h +++ b/plugins/cpp/parser/src/clangastvisitor.h @@ -682,6 +682,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor cppFunction->qualifiedName = fn_->getQualifiedNameAsString(); cppFunction->typeHash = util::fnvHash(getUSR(qualType, _astContext)); cppFunction->qualifiedType = qualType.getAsString(); + cppFunction->mccabe = getStatementMcCabe(fn_->getBody()); clang::CXXMethodDecl* md = llvm::dyn_cast(fn_); @@ -1563,6 +1564,70 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return false; } + static unsigned int getStatementMcCabe(clang::Stmt* const stmt_) + { + if (!stmt_) + return 0; + switch (stmt_->getStmtClass()) + { + case clang::Stmt::IfStmtClass: + { + clang::IfStmt* ifStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(ifStmt->getThen()) + getStatementMcCabe(ifStmt->getElse()); + } + case clang::Stmt::WhileStmtClass: + { + clang::WhileStmt* whileStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(whileStmt->getBody()); + } + case clang::Stmt::DoStmtClass: + { + clang::DoStmt* doStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(doStmt->getBody()); + } + case clang::Stmt::ForStmtClass: + { + clang::ForStmt* forStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(forStmt->getBody()); + } + case clang::Stmt::CaseStmtClass: + { + clang::CaseStmt* caseStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(caseStmt->getSubStmt()); + } + case clang::Stmt::DefaultStmtClass: + { + clang::DefaultStmt* defaultStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(defaultStmt->getSubStmt()); + } + case clang::Stmt::ContinueStmtClass: + return 1; + case clang::Stmt::GotoStmtClass: + return 1; + case clang::Stmt::BinaryConditionalOperatorClass: + return 1; + case clang::Stmt::CXXCatchStmtClass: + { + clang::CXXCatchStmt* catchStmt = clang::dyn_cast(stmt_); + return 1 + getStatementMcCabe(catchStmt->getHandlerBlock()); + } + case clang::Stmt::ConditionalOperatorClass: + return 1; + case clang::Stmt::CompoundStmtClass: + { + clang::CompoundStmt* compoundStmt = clang::dyn_cast(stmt_); + unsigned bodySum = 0; + for (clang::Stmt* child: compoundStmt->body()) + { + bodySum += getStatementMcCabe(child); + } + return bodySum; + } + default: + return 0; + } + } + std::vector _astNodes; std::vector _enumConstants; std::vector _enums; diff --git a/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h b/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h index 32f5aeb06..3334a9bd7 100644 --- a/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h +++ b/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h @@ -13,7 +13,8 @@ struct CppAstNodeMetrics { enum Type { - PARAMETER_COUNT + PARAMETER_COUNT, + MCCABE }; #pragma db id auto diff --git a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h index f4d508ef3..518361a25 100644 --- a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h +++ b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h @@ -28,6 +28,7 @@ class CppMetricsParser : public AbstractParser private: void functionParameters(); + void functionMcCabe(); std::unordered_set _fileIdCache; std::unordered_map _astNodeIdCache; diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 2730cdb96..36c81a705 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -107,10 +107,28 @@ void CppMetricsParser::functionParameters() }); } +void CppMetricsParser::functionMcCabe() +{ + util::OdbTransaction {_ctx.db} ([&, this] + { + for (const model::CppFunction& function + : _ctx.db->query()) + { + model::CppAstNodeMetrics funcParams; + funcParams.astNodeId = function.id; + funcParams.type = model::CppAstNodeMetrics::Type::MCCABE; + funcParams.value = function.mccabe; + _ctx.db->persist(funcParams); + } + }); +} + bool CppMetricsParser::parse() { // Function parameter number metric. functionParameters(); + // Function McCabe metric + functionMcCabe(); return true; } From d86283f21512ce30a62e76aeb1f94495ae06969c Mon Sep 17 00:00:00 2001 From: mozesl Date: Sun, 3 Dec 2023 15:11:26 +0000 Subject: [PATCH 2/6] traverse implementation of mccabe metric with basic testing --- plugins/cpp/model/include/model/cppfunction.h | 7 + plugins/cpp/parser/src/clangastvisitor.h | 312 ++++++++++++++---- plugins/cpp_metrics/CMakeLists.txt | 1 + .../parser/src/cppmetricsparser.cpp | 4 +- plugins/cpp_metrics/test/CMakeLists.txt | 85 +++++ .../test/sources/parser/CMakeLists.txt | 8 + .../test/sources/parser/mccabe.cpp | 148 +++++++++ .../test/sources/service/CMakeLists.txt | 8 + .../test/sources/service/mccabe.cpp | 148 +++++++++ .../test/src/cppmetricsparsertest.cpp | 57 ++++ .../test/src/cppmetricsservicetest.cpp | 31 ++ .../cpp_metrics/test/src/cppmetricstest.cpp | 29 ++ 12 files changed, 770 insertions(+), 68 deletions(-) create mode 100644 plugins/cpp_metrics/test/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/sources/parser/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/sources/parser/mccabe.cpp create mode 100644 plugins/cpp_metrics/test/sources/service/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/sources/service/mccabe.cpp create mode 100644 plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp create mode 100644 plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp create mode 100644 plugins/cpp_metrics/test/src/cppmetricstest.cpp diff --git a/plugins/cpp/model/include/model/cppfunction.h b/plugins/cpp/model/include/model/cppfunction.h index 3867a9433..e1a46c449 100644 --- a/plugins/cpp/model/include/model/cppfunction.h +++ b/plugins/cpp/model/include/model/cppfunction.h @@ -60,6 +60,13 @@ struct CppFunctionLocalCount std::size_t count; }; +#pragma db view object(CppFunction) +struct CppFunctionMcCabeWithId +{ + CppAstNodeId id; + unsigned int mccabe; +}; + } } diff --git a/plugins/cpp/parser/src/clangastvisitor.h b/plugins/cpp/parser/src/clangastvisitor.h index 0b122015e..13aba8ec2 100644 --- a/plugins/cpp/parser/src/clangastvisitor.h +++ b/plugins/cpp/parser/src/clangastvisitor.h @@ -147,6 +147,9 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool TraverseFunctionDecl(clang::FunctionDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseFunctionDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseFunctionDecl(fd_); @@ -158,8 +161,30 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseFunctionDeclWithBody(clang::FunctionDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseFunctionDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseCXXDeductionGuideDecl(clang::CXXDeductionGuideDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseCXXDeductionGuideDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseCXXDeductionGuideDecl(fd_); @@ -171,8 +196,30 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseCXXDeductionGuideDeclWithBody(clang::CXXDeductionGuideDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseCXXDeductionGuideDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseCXXMethodDecl(clang::CXXMethodDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseCXXMethodDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseCXXMethodDecl(fd_); @@ -184,8 +231,30 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseCXXMethodDeclWithBody(clang::CXXMethodDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseCXXMethodDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseCXXConstructorDecl(clang::CXXConstructorDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseCXXConstructorDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseCXXConstructorDecl(fd_); @@ -197,8 +266,30 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseCXXConstructorDeclWithBody(clang::CXXConstructorDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseCXXConstructorDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseCXXDestructorDecl(clang::CXXDestructorDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseCXXDestructorDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseCXXDestructorDecl(fd_); @@ -210,8 +301,30 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseCXXDestructorDeclWithBody(clang::CXXDestructorDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseCXXDestructorDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseCXXConversionDecl(clang::CXXConversionDecl* fd_) { + if (fd_->doesThisDeclarationHaveABody()) + return TraverseCXXConversionDeclWithBody(fd_); + _functionStack.push(std::make_shared()); bool b = Base::TraverseCXXConversionDecl(fd_); @@ -223,6 +336,25 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } + bool TraverseCXXConversionDeclWithBody(clang::CXXConversionDecl* fd_) { + LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + _functionStack.push(std::make_shared()); + _mcCabeStack.push(1); + + bool b = Base::TraverseCXXConversionDecl(fd_); + + model::CppFunctionPtr top = _functionStack.top(); + if (top->astNodeId) + { + top->mccabe = _mcCabeStack.top(); + _functions.push_back(top); + } + _mcCabeStack.pop(); + _functionStack.pop(); + + return b; + } + bool TraverseRecordDecl(clang::RecordDecl* rd_) { _typeStack.push(std::make_shared()); @@ -316,6 +448,10 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool TraverseBinaryOperator(clang::BinaryOperator* bo_) { + if (bo_->isLogicalOp() && !_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing BinaryOperator"; + ++_mcCabeStack.top(); + } _contextStatementStack.push(bo_); bool b = Base::TraverseBinaryOperator(bo_); _contextStatementStack.pop(); @@ -337,6 +473,113 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor _contextStatementStack.pop(); return b; } + + bool TraverseIfStmt(clang::IfStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing IfStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseIfStmt(stmt_); + } + + bool TraverseWhileStmt(clang::WhileStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing WhileStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseWhileStmt(stmt_); + } + + bool TraverseDoStmt(clang::DoStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing DoStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseDoStmt(stmt_); + } + + bool TraverseForStmt(clang::ForStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing ForStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseForStmt(stmt_); + } + + bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt* stmt_) { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing CXXForRangeStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseCXXForRangeStmt(stmt_); + } + + bool TraverseCaseStmt(clang::CaseStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing CaseStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseCaseStmt(stmt_); + } + + bool TraverseDefaultStmt(clang::DefaultStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing DefaultStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseDefaultStmt(stmt_); + } + + bool TraverseContinueStmt(clang::ContinueStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing ContinueStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseContinueStmt(stmt_); + } + + bool TraverseGotoStmt(clang::GotoStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing GotoStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseGotoStmt(stmt_); + } + + bool TraverseCXXCatchStmt(clang::CXXCatchStmt* stmt_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing CXXCatchStmt"; + ++_mcCabeStack.top(); + } + return Base::TraverseCXXCatchStmt(stmt_); + } + + bool TraverseConditionalOperator(clang::ConditionalOperator* op_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing ConditionalOperator"; + ++_mcCabeStack.top(); + } + return Base::TraverseConditionalOperator(op_); + } + + bool TraverseBinaryConditionalOperator(clang::BinaryConditionalOperator* op_) + { + if(!_mcCabeStack.empty()) { + LOG(info) << "[McCabe] Traversing BinaryConditionalOperator"; + ++_mcCabeStack.top(); + } + return Base::TraverseBinaryConditionalOperator(op_); + } bool VisitTypedefTypeLoc(clang::TypedefTypeLoc tl_) { @@ -682,7 +925,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor cppFunction->qualifiedName = fn_->getQualifiedNameAsString(); cppFunction->typeHash = util::fnvHash(getUSR(qualType, _astContext)); cppFunction->qualifiedType = qualType.getAsString(); - cppFunction->mccabe = getStatementMcCabe(fn_->getBody()); + cppFunction->mccabe = 1; clang::CXXMethodDecl* md = llvm::dyn_cast(fn_); @@ -1564,70 +1807,6 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return false; } - static unsigned int getStatementMcCabe(clang::Stmt* const stmt_) - { - if (!stmt_) - return 0; - switch (stmt_->getStmtClass()) - { - case clang::Stmt::IfStmtClass: - { - clang::IfStmt* ifStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(ifStmt->getThen()) + getStatementMcCabe(ifStmt->getElse()); - } - case clang::Stmt::WhileStmtClass: - { - clang::WhileStmt* whileStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(whileStmt->getBody()); - } - case clang::Stmt::DoStmtClass: - { - clang::DoStmt* doStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(doStmt->getBody()); - } - case clang::Stmt::ForStmtClass: - { - clang::ForStmt* forStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(forStmt->getBody()); - } - case clang::Stmt::CaseStmtClass: - { - clang::CaseStmt* caseStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(caseStmt->getSubStmt()); - } - case clang::Stmt::DefaultStmtClass: - { - clang::DefaultStmt* defaultStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(defaultStmt->getSubStmt()); - } - case clang::Stmt::ContinueStmtClass: - return 1; - case clang::Stmt::GotoStmtClass: - return 1; - case clang::Stmt::BinaryConditionalOperatorClass: - return 1; - case clang::Stmt::CXXCatchStmtClass: - { - clang::CXXCatchStmt* catchStmt = clang::dyn_cast(stmt_); - return 1 + getStatementMcCabe(catchStmt->getHandlerBlock()); - } - case clang::Stmt::ConditionalOperatorClass: - return 1; - case clang::Stmt::CompoundStmtClass: - { - clang::CompoundStmt* compoundStmt = clang::dyn_cast(stmt_); - unsigned bodySum = 0; - for (clang::Stmt* child: compoundStmt->body()) - { - bodySum += getStatementMcCabe(child); - } - return bodySum; - } - default: - return 0; - } - } - std::vector _astNodes; std::vector _enumConstants; std::vector _enums; @@ -1646,8 +1825,9 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor // Check lambda. // TODO: _enumStack also doesn't have to be a stack. std::stack _functionStack; - std::stack _typeStack; + std::stack _typeStack; std::stack _enumStack; + std::stack _mcCabeStack; bool _isImplicit; ParserContext& _ctx; diff --git a/plugins/cpp_metrics/CMakeLists.txt b/plugins/cpp_metrics/CMakeLists.txt index 8cf4f308f..360413c8f 100644 --- a/plugins/cpp_metrics/CMakeLists.txt +++ b/plugins/cpp_metrics/CMakeLists.txt @@ -11,5 +11,6 @@ endif() add_subdirectory(model) add_subdirectory(parser) add_subdirectory(service) +add_subdirectory(test) #install_webplugin(webgui) diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 36c81a705..ffd75a145 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -111,8 +111,8 @@ void CppMetricsParser::functionMcCabe() { util::OdbTransaction {_ctx.db} ([&, this] { - for (const model::CppFunction& function - : _ctx.db->query()) + for (const model::CppFunctionMcCabeWithId& function + : _ctx.db->query()) { model::CppAstNodeMetrics funcParams; funcParams.astNodeId = function.id; diff --git a/plugins/cpp_metrics/test/CMakeLists.txt b/plugins/cpp_metrics/test/CMakeLists.txt new file mode 100644 index 000000000..302fca563 --- /dev/null +++ b/plugins/cpp_metrics/test/CMakeLists.txt @@ -0,0 +1,85 @@ +include_directories( + ${PLUGIN_DIR}/model/include + ${PLUGIN_DIR}/service/include + ${PROJECT_BINARY_DIR}/service/language/gen-cpp + ${PROJECT_BINARY_DIR}/service/project/gen-cpp + ${PROJECT_SOURCE_DIR}/model/include) + +include_directories(SYSTEM + ${THRIFT_LIBTHRIFT_INCLUDE_DIRS}) + +add_executable(cppmetricsservicetest + src/cppmetricstest.cpp + src/cppmetricsservicetest.cpp) + +add_executable(cppmetricsparsertest + src/cppmetricstest.cpp + src/cppmetricsparsertest.cpp) + +target_compile_options(cppmetricsservicetest PUBLIC -Wno-unknown-pragmas) +target_compile_options(cppmetricsparsertest PUBLIC -Wno-unknown-pragmas) + +target_link_libraries(cppmetricsservicetest + model + cppmodel + cppservice + ${Boost_LIBRARIES} + ${GTEST_BOTH_LIBRARIES} + pthread) + +target_link_libraries(cppmetricsparsertest + util + model + cppmodel + ${Boost_LIBRARIES} + ${GTEST_BOTH_LIBRARIES} + pthread) + +if (NOT FUNCTIONAL_TESTING_ENABLED) + fancy_message("Skipping generation of test project cppmetricstest." "yellow" TRUE) +else() + # Clean up the build folder and the output of the test in a `make clean`. + set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${CMAKE_CURRENT_BINARY_DIR}/build" + "${CMAKE_CURRENT_BINARY_DIR}/workdir") + + # Add test to the project to run by ctest. + # + # There are three arguments that are passed to the `cppmetricsservicetest` binary + # here. + # The first sets up the build environment and builds the project. + # The second executes the parsing. + # Then we put the database string which will be used by the tests. + add_test(NAME cppmetricsservice COMMAND cppmetricsservicetest + "echo \"Test database used: ${TEST_DB}\" && \ + rm -rf ${CMAKE_CURRENT_BINARY_DIR}/build && \ + mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cd ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cmake ${CMAKE_CURRENT_SOURCE_DIR}/sources/service \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=on" + "${CMAKE_INSTALL_PREFIX}/bin/CodeCompass_parser \ + --database \"${TEST_DB}\" \ + --name cppmetricsservicetest \ + --input ${CMAKE_CURRENT_BINARY_DIR}/build/compile_commands.json \ + --workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \ + --force" + "${TEST_DB}") + + add_test(NAME cppmetricsparser COMMAND cppmetricsparsertest + "echo \"Test database used: ${TEST_DB}\" && \ + rm -rf ${CMAKE_CURRENT_BINARY_DIR}/build && \ + mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cd ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cmake ${CMAKE_CURRENT_SOURCE_DIR}/sources/parser \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=on" + "${CMAKE_INSTALL_PREFIX}/bin/CodeCompass_parser \ + --database \"${TEST_DB}\" \ + --name cppparsertest \ + --input ${CMAKE_CURRENT_BINARY_DIR}/build/compile_commands.json \ + --workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \ + --force" + "${TEST_DB}") + + fancy_message("Generating test project for cppmetricstest." "blue" TRUE) +endif() \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt new file mode 100644 index 000000000..287da2e1f --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +# This is a dummy CMakeList that can be used to generate a build for the +# C++ test input files. + +add_library(CppTestProject STATIC + mccabe.cpp) diff --git a/plugins/cpp_metrics/test/sources/parser/mccabe.cpp b/plugins/cpp_metrics/test/sources/parser/mccabe.cpp new file mode 100644 index 000000000..00cac9c6d --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/mccabe.cpp @@ -0,0 +1,148 @@ +void conditionals(int arg1, bool arg2) { // +1 + if (arg2) { } // +1 + else { } + + if ((arg1 > 5 && arg2) || (arg1 < 13 && !arg2)) { } // +4 + + int local1 = arg2 ? arg1 / 2 : arg1 * 2; // + 1 + int local2 = local1 ?: arg1 + 5; // +1 +} // 8 + +void loops1() { // +1 + for (int i = 0; i < 5; ++i) {} // +1 + + int j = 0; + while(j < 5) { ++j; } // +1 + + do + { + ++j; + } while (j < 10); // +1 + + char str[] = "hello"; + + for(char c : str) // +1 + { + if (c == 'h') {} // +1 + } +} // 6 + +void loops2(int arg1, bool arg2) // +1 +{ + while(arg2) // +1 + { + if (arg1 < 5) // +1 + { + arg1 *= 2; + continue; // +1 + } + else if (arg1 > 30) // +1 + { + arg1 /= 2; + continue; // +1 + } + break; + } +} // 6 + +int loops3(int arg1) // +1 +{ + const int LIMIT = 42; + int result = 0; + for (int i = 0; i < arg1 * 2; ++i) // +1 + { + ++result; + for (int j = i; j < arg1 * 3; ++j) // +1 + { + result -= j/5; + if (result >= LIMIT) // +1 + goto endfor; // +1 + } + } + endfor: + return result; +} // 5 + +void switchcase(int arg1) // +1 +{ + switch(arg1) + { + case 1: // +1 + break; + case 2: // +1 + break; + case 3: // +1 + break; + default: // +1 + switch(arg1 * 5) { + case 85: // +1 + break; + case 90: // +1 + break; + } + break; + } +} // 7 + +class Err1 {}; +class Err2 {}; + +void fragile(int arg1) // +1 +{} // 1 + +void trycatch(int arg1) // +1 +{ + try + { + fragile(arg1); + } + catch (Err1& err) // +1 + { + + } + catch (Err2& err) // +1 + { + + } +} // 3 + +class MyClass +{ +public: + MyClass(unsigned int arg1) // +1 + { + if (arg1 < LIMIT) // +1 + bad_practice = new char[LIMIT]; + else + bad_practice = new char[arg1]; + } // 2 + + ~MyClass() // +1 + { + for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 + delete[] bad_practice; + } // 2 + + void method1(int arg1) // +1 + { + for (unsigned int i = arg1; i < LIMIT; ++i) // +1 + { + switch(arg1) + { + case -1: // +1 + goto endfor; // +1 + case 0: // +1 + break; + case 1: // +1 + break; + default: // +1 + continue; // +1 + } + arg1 *= 2; + } + endfor:; + } // 8 +private: + const unsigned int LIMIT = 42; + char* bad_practice; +}; diff --git a/plugins/cpp_metrics/test/sources/service/CMakeLists.txt b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt new file mode 100644 index 000000000..287da2e1f --- /dev/null +++ b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +# This is a dummy CMakeList that can be used to generate a build for the +# C++ test input files. + +add_library(CppTestProject STATIC + mccabe.cpp) diff --git a/plugins/cpp_metrics/test/sources/service/mccabe.cpp b/plugins/cpp_metrics/test/sources/service/mccabe.cpp new file mode 100644 index 000000000..00cac9c6d --- /dev/null +++ b/plugins/cpp_metrics/test/sources/service/mccabe.cpp @@ -0,0 +1,148 @@ +void conditionals(int arg1, bool arg2) { // +1 + if (arg2) { } // +1 + else { } + + if ((arg1 > 5 && arg2) || (arg1 < 13 && !arg2)) { } // +4 + + int local1 = arg2 ? arg1 / 2 : arg1 * 2; // + 1 + int local2 = local1 ?: arg1 + 5; // +1 +} // 8 + +void loops1() { // +1 + for (int i = 0; i < 5; ++i) {} // +1 + + int j = 0; + while(j < 5) { ++j; } // +1 + + do + { + ++j; + } while (j < 10); // +1 + + char str[] = "hello"; + + for(char c : str) // +1 + { + if (c == 'h') {} // +1 + } +} // 6 + +void loops2(int arg1, bool arg2) // +1 +{ + while(arg2) // +1 + { + if (arg1 < 5) // +1 + { + arg1 *= 2; + continue; // +1 + } + else if (arg1 > 30) // +1 + { + arg1 /= 2; + continue; // +1 + } + break; + } +} // 6 + +int loops3(int arg1) // +1 +{ + const int LIMIT = 42; + int result = 0; + for (int i = 0; i < arg1 * 2; ++i) // +1 + { + ++result; + for (int j = i; j < arg1 * 3; ++j) // +1 + { + result -= j/5; + if (result >= LIMIT) // +1 + goto endfor; // +1 + } + } + endfor: + return result; +} // 5 + +void switchcase(int arg1) // +1 +{ + switch(arg1) + { + case 1: // +1 + break; + case 2: // +1 + break; + case 3: // +1 + break; + default: // +1 + switch(arg1 * 5) { + case 85: // +1 + break; + case 90: // +1 + break; + } + break; + } +} // 7 + +class Err1 {}; +class Err2 {}; + +void fragile(int arg1) // +1 +{} // 1 + +void trycatch(int arg1) // +1 +{ + try + { + fragile(arg1); + } + catch (Err1& err) // +1 + { + + } + catch (Err2& err) // +1 + { + + } +} // 3 + +class MyClass +{ +public: + MyClass(unsigned int arg1) // +1 + { + if (arg1 < LIMIT) // +1 + bad_practice = new char[LIMIT]; + else + bad_practice = new char[arg1]; + } // 2 + + ~MyClass() // +1 + { + for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 + delete[] bad_practice; + } // 2 + + void method1(int arg1) // +1 + { + for (unsigned int i = arg1; i < LIMIT; ++i) // +1 + { + switch(arg1) + { + case -1: // +1 + goto endfor; // +1 + case 0: // +1 + break; + case 1: // +1 + break; + default: // +1 + continue; // +1 + } + arg1 *= 2; + } + endfor:; + } // 8 +private: + const unsigned int LIMIT = 42; + char* bad_practice; +}; diff --git a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp new file mode 100644 index 000000000..c958ff44a --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp @@ -0,0 +1,57 @@ +#include + +#include +#include + +#include +#include + +using namespace cc; + +extern char* dbConnectionString; + +class CppMetricsParserTest : public ::testing::Test +{ +public: + CppMetricsParserTest() : + _db(util::connectDatabase(dbConnectionString)), + _transaction(_db) + {} + +protected: + std::shared_ptr _db; + util::OdbTransaction _transaction; +}; + +class ParameterizedMcCabeTest + : public CppMetricsParserTest, + public ::testing::WithParamInterface> +{}; + +std::vector> parameters = { + {"conditionals", 8}, + {"loops1", 6}, + {"loops2", 6}, + {"loops3", 5}, + {"switchcase", 7}, + {"fragile", 1}, + {"trycatch", 3}, + {"MyClass::MyClass", 2}, + {"MyClass::~MyClass", 2}, + {"MyClass::method1", 8} +}; + +TEST_P(ParameterizedMcCabeTest, McCabeTest) { + _transaction([&, this]() { + model::CppFunction func = _db->query_value( + odb::query::qualifiedName == GetParam().first); + + EXPECT_EQ(GetParam().second, func.mccabe); + }); +} + +INSTANTIATE_TEST_SUITE_P( + ParameterizedMcCabeTestSuite, + ParameterizedMcCabeTest, + ::testing::ValuesIn(parameters) + ); \ No newline at end of file diff --git a/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp b/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp new file mode 100644 index 000000000..095867874 --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp @@ -0,0 +1,31 @@ +#include + +#include + +#include + +using namespace cc; +using namespace service::cppmetrics; + +extern char* dbConnectionString; + +class CppMetricsServiceTest : public ::testing::Test +{ +public: + CppMetricsServiceTest() : + _db(util::connectDatabase(dbConnectionString)), + _transaction(_db), + _cppservice(new CppMetricsServiceHandler( + _db, + std::make_shared(""), + webserver::ServerContext(std::string(), + boost::program_options::variables_map()))) + { + + } + +protected: + std::shared_ptr _db; + util::OdbTransaction _transaction; + std::shared_ptr _cppservice; +}; \ No newline at end of file diff --git a/plugins/cpp_metrics/test/src/cppmetricstest.cpp b/plugins/cpp_metrics/test/src/cppmetricstest.cpp new file mode 100644 index 000000000..d89a4bf8d --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricstest.cpp @@ -0,0 +1,29 @@ +#define GTEST_HAS_TR1_TUPLE 1 +#define GTEST_USE_OWN_TR1_TUPLE 0 + +#include + +#include + +const char* dbConnectionString; + +int main(int argc, char** argv) +{ + dbConnectionString = argv[3]; + if (strcmp(dbConnectionString, "") == 0) + { + GTEST_LOG_(FATAL) << "No test database connection given."; + return 1; + } + + GTEST_LOG_(INFO) << "Testing C++ started..."; + GTEST_LOG_(INFO) << "Executing build command: " << argv[1]; + system(argv[1]); + + GTEST_LOG_(INFO) << "Executing parser command: " << argv[2]; + system(argv[2]); + + GTEST_LOG_(INFO) << "Using database for tests: " << dbConnectionString; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From eb0d79db862628cf8150a84eb0877d4dd8038db7 Mon Sep 17 00:00:00 2001 From: mozesl Date: Sun, 3 Dec 2023 17:25:42 +0100 Subject: [PATCH 3/6] fix build error --- plugins/cpp_metrics/test/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/cpp_metrics/test/CMakeLists.txt b/plugins/cpp_metrics/test/CMakeLists.txt index 302fca563..37cf39ba6 100644 --- a/plugins/cpp_metrics/test/CMakeLists.txt +++ b/plugins/cpp_metrics/test/CMakeLists.txt @@ -1,8 +1,7 @@ include_directories( ${PLUGIN_DIR}/model/include ${PLUGIN_DIR}/service/include - ${PROJECT_BINARY_DIR}/service/language/gen-cpp - ${PROJECT_BINARY_DIR}/service/project/gen-cpp + ${CMAKE_CURRENT_BINARY_DIR}/../service/gen-cpp ${PROJECT_SOURCE_DIR}/model/include) include_directories(SYSTEM From 09d6a8a1c60e750f40318a047cd7f769c3ed263f Mon Sep 17 00:00:00 2001 From: mozesl Date: Fri, 8 Dec 2023 13:45:46 +0100 Subject: [PATCH 4/6] remove debug logging, generalize traversing branching statements --- plugins/cpp/parser/src/clangastvisitor.h | 114 +++++++---------------- 1 file changed, 36 insertions(+), 78 deletions(-) diff --git a/plugins/cpp/parser/src/clangastvisitor.h b/plugins/cpp/parser/src/clangastvisitor.h index 13aba8ec2..a11bc0e6f 100644 --- a/plugins/cpp/parser/src/clangastvisitor.h +++ b/plugins/cpp/parser/src/clangastvisitor.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,6 @@ #include #include #include -#include #include @@ -161,8 +161,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseFunctionDeclWithBody(clang::FunctionDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseFunctionDeclWithBody(clang::FunctionDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -196,8 +196,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseCXXDeductionGuideDeclWithBody(clang::CXXDeductionGuideDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseCXXDeductionGuideDeclWithBody(clang::CXXDeductionGuideDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -231,8 +231,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseCXXMethodDeclWithBody(clang::CXXMethodDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseCXXMethodDeclWithBody(clang::CXXMethodDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -266,8 +266,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseCXXConstructorDeclWithBody(clang::CXXConstructorDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseCXXConstructorDeclWithBody(clang::CXXConstructorDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -301,8 +301,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseCXXDestructorDeclWithBody(clang::CXXDestructorDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseCXXDestructorDeclWithBody(clang::CXXDestructorDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -336,8 +336,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseCXXConversionDeclWithBody(clang::CXXConversionDecl* fd_) { - LOG(info) << "[McCabe] Traversing function " << fd_->getQualifiedNameAsString(); + bool TraverseCXXConversionDeclWithBody(clang::CXXConversionDecl* fd_) + { _functionStack.push(std::make_shared()); _mcCabeStack.push(1); @@ -448,10 +448,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool TraverseBinaryOperator(clang::BinaryOperator* bo_) { - if (bo_->isLogicalOp() && !_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing BinaryOperator"; + if (bo_->isLogicalOp() && !_mcCabeStack.empty()) ++_mcCabeStack.top(); - } _contextStatementStack.push(bo_); bool b = Base::TraverseBinaryOperator(bo_); _contextStatementStack.pop(); @@ -473,112 +471,72 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor _contextStatementStack.pop(); return b; } - - bool TraverseIfStmt(clang::IfStmt* stmt_) + + bool TraverseWithMcCabeAdd(const std::function& func_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing IfStmt"; + if(!_mcCabeStack.empty()) ++_mcCabeStack.top(); - } - return Base::TraverseIfStmt(stmt_); + return func_(); + } + + bool TraverseIfStmt(clang::IfStmt* stmt_) + { + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseIfStmt(stmt_); }); } bool TraverseWhileStmt(clang::WhileStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing WhileStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseWhileStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseWhileStmt(stmt_); }); } bool TraverseDoStmt(clang::DoStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing DoStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseDoStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseDoStmt(stmt_); }); } bool TraverseForStmt(clang::ForStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing ForStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseForStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseForStmt(stmt_); }); } - bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing CXXForRangeStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseCXXForRangeStmt(stmt_); + bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt* stmt_) + { + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseCXXForRangeStmt(stmt_); }); } bool TraverseCaseStmt(clang::CaseStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing CaseStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseCaseStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseCaseStmt(stmt_); }); } bool TraverseDefaultStmt(clang::DefaultStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing DefaultStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseDefaultStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseDefaultStmt(stmt_); }); } bool TraverseContinueStmt(clang::ContinueStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing ContinueStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseContinueStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseContinueStmt(stmt_); }); } bool TraverseGotoStmt(clang::GotoStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing GotoStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseGotoStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseGotoStmt(stmt_); }); } bool TraverseCXXCatchStmt(clang::CXXCatchStmt* stmt_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing CXXCatchStmt"; - ++_mcCabeStack.top(); - } - return Base::TraverseCXXCatchStmt(stmt_); + return TraverseWithMcCabeAdd([stmt_, this] { return Base::TraverseCXXCatchStmt(stmt_); }); } bool TraverseConditionalOperator(clang::ConditionalOperator* op_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing ConditionalOperator"; - ++_mcCabeStack.top(); - } - return Base::TraverseConditionalOperator(op_); + return TraverseWithMcCabeAdd([op_, this] { return Base::TraverseConditionalOperator(op_); }); } bool TraverseBinaryConditionalOperator(clang::BinaryConditionalOperator* op_) { - if(!_mcCabeStack.empty()) { - LOG(info) << "[McCabe] Traversing BinaryConditionalOperator"; - ++_mcCabeStack.top(); - } - return Base::TraverseBinaryConditionalOperator(op_); + return TraverseWithMcCabeAdd([op_, this] { return Base::TraverseBinaryConditionalOperator(op_); }); } bool VisitTypedefTypeLoc(clang::TypedefTypeLoc tl_) From 2817825bfebcfeced472a7656a72cf3606f004d5 Mon Sep 17 00:00:00 2001 From: mozesl Date: Tue, 19 Dec 2023 19:37:05 +0100 Subject: [PATCH 5/6] implement fixes from review --- plugins/cpp/model/include/model/cppfunction.h | 2 +- plugins/cpp_metrics/parser/src/cppmetricsparser.cpp | 10 +++++----- plugins/cpp_metrics/test/sources/parser/CMakeLists.txt | 3 --- plugins/cpp_metrics/test/sources/parser/mccabe.cpp | 8 ++++---- .../cpp_metrics/test/sources/service/CMakeLists.txt | 3 --- plugins/cpp_metrics/test/sources/service/mccabe.cpp | 8 ++++---- plugins/cpp_metrics/test/src/cppmetricstest.cpp | 2 +- 7 files changed, 15 insertions(+), 21 deletions(-) diff --git a/plugins/cpp/model/include/model/cppfunction.h b/plugins/cpp/model/include/model/cppfunction.h index e1a46c449..00109db98 100644 --- a/plugins/cpp/model/include/model/cppfunction.h +++ b/plugins/cpp/model/include/model/cppfunction.h @@ -63,7 +63,7 @@ struct CppFunctionLocalCount #pragma db view object(CppFunction) struct CppFunctionMcCabeWithId { - CppAstNodeId id; + CppAstNodeId astNodeId; unsigned int mccabe; }; diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index ffd75a145..bbe3cfdf4 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -114,11 +114,11 @@ void CppMetricsParser::functionMcCabe() for (const model::CppFunctionMcCabeWithId& function : _ctx.db->query()) { - model::CppAstNodeMetrics funcParams; - funcParams.astNodeId = function.id; - funcParams.type = model::CppAstNodeMetrics::Type::MCCABE; - funcParams.value = function.mccabe; - _ctx.db->persist(funcParams); + model::CppAstNodeMetrics funcMcCabe; + funcMcCabe.astNodeId = function.astNodeId; + funcMcCabe.type = model::CppAstNodeMetrics::Type::MCCABE; + funcMcCabe.value = function.mccabe; + _ctx.db->persist(funcMcCabe); } }); } diff --git a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt index 287da2e1f..305294e31 100644 --- a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt +++ b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt @@ -1,8 +1,5 @@ cmake_minimum_required(VERSION 2.6) project(CppTestProject) -# This is a dummy CMakeList that can be used to generate a build for the -# C++ test input files. - add_library(CppTestProject STATIC mccabe.cpp) diff --git a/plugins/cpp_metrics/test/sources/parser/mccabe.cpp b/plugins/cpp_metrics/test/sources/parser/mccabe.cpp index 00cac9c6d..058c22039 100644 --- a/plugins/cpp_metrics/test/sources/parser/mccabe.cpp +++ b/plugins/cpp_metrics/test/sources/parser/mccabe.cpp @@ -112,15 +112,15 @@ class MyClass MyClass(unsigned int arg1) // +1 { if (arg1 < LIMIT) // +1 - bad_practice = new char[LIMIT]; + badPractice = new char[LIMIT]; else - bad_practice = new char[arg1]; + badPractice = new char[arg1]; } // 2 ~MyClass() // +1 { for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 - delete[] bad_practice; + delete[] badPractice; } // 2 void method1(int arg1) // +1 @@ -144,5 +144,5 @@ class MyClass } // 8 private: const unsigned int LIMIT = 42; - char* bad_practice; + char* badPractice; }; diff --git a/plugins/cpp_metrics/test/sources/service/CMakeLists.txt b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt index 287da2e1f..305294e31 100644 --- a/plugins/cpp_metrics/test/sources/service/CMakeLists.txt +++ b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt @@ -1,8 +1,5 @@ cmake_minimum_required(VERSION 2.6) project(CppTestProject) -# This is a dummy CMakeList that can be used to generate a build for the -# C++ test input files. - add_library(CppTestProject STATIC mccabe.cpp) diff --git a/plugins/cpp_metrics/test/sources/service/mccabe.cpp b/plugins/cpp_metrics/test/sources/service/mccabe.cpp index 00cac9c6d..058c22039 100644 --- a/plugins/cpp_metrics/test/sources/service/mccabe.cpp +++ b/plugins/cpp_metrics/test/sources/service/mccabe.cpp @@ -112,15 +112,15 @@ class MyClass MyClass(unsigned int arg1) // +1 { if (arg1 < LIMIT) // +1 - bad_practice = new char[LIMIT]; + badPractice = new char[LIMIT]; else - bad_practice = new char[arg1]; + badPractice = new char[arg1]; } // 2 ~MyClass() // +1 { for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 - delete[] bad_practice; + delete[] badPractice; } // 2 void method1(int arg1) // +1 @@ -144,5 +144,5 @@ class MyClass } // 8 private: const unsigned int LIMIT = 42; - char* bad_practice; + char* badPractice; }; diff --git a/plugins/cpp_metrics/test/src/cppmetricstest.cpp b/plugins/cpp_metrics/test/src/cppmetricstest.cpp index d89a4bf8d..25078f570 100644 --- a/plugins/cpp_metrics/test/src/cppmetricstest.cpp +++ b/plugins/cpp_metrics/test/src/cppmetricstest.cpp @@ -16,7 +16,7 @@ int main(int argc, char** argv) return 1; } - GTEST_LOG_(INFO) << "Testing C++ started..."; + GTEST_LOG_(INFO) << "Testing C++ metrics plugin started..."; GTEST_LOG_(INFO) << "Executing build command: " << argv[1]; system(argv[1]); From 38d5b8959013c46519747e3ad640e3b8439b81bb Mon Sep 17 00:00:00 2001 From: mozesl Date: Thu, 4 Jan 2024 13:45:02 +0100 Subject: [PATCH 6/6] create skeleton for cpp_metrics testing --- plugins/cpp_metrics/CMakeLists.txt | 1 + plugins/cpp_metrics/test/CMakeLists.txt | 76 +++++++++++++++++++ .../test/sources/parser/CMakeLists.txt | 4 + .../test/sources/service/CMakeLists.txt | 4 + .../test/src/cppmetricsparsertest.cpp | 21 +++++ .../test/src/cppmetricsservicetest.cpp | 31 ++++++++ .../cpp_metrics/test/src/cppmetricstest.cpp | 29 +++++++ 7 files changed, 166 insertions(+) create mode 100644 plugins/cpp_metrics/test/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/sources/parser/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/sources/service/CMakeLists.txt create mode 100644 plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp create mode 100644 plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp create mode 100644 plugins/cpp_metrics/test/src/cppmetricstest.cpp diff --git a/plugins/cpp_metrics/CMakeLists.txt b/plugins/cpp_metrics/CMakeLists.txt index 8cf4f308f..360413c8f 100644 --- a/plugins/cpp_metrics/CMakeLists.txt +++ b/plugins/cpp_metrics/CMakeLists.txt @@ -11,5 +11,6 @@ endif() add_subdirectory(model) add_subdirectory(parser) add_subdirectory(service) +add_subdirectory(test) #install_webplugin(webgui) diff --git a/plugins/cpp_metrics/test/CMakeLists.txt b/plugins/cpp_metrics/test/CMakeLists.txt new file mode 100644 index 000000000..34b7cf60e --- /dev/null +++ b/plugins/cpp_metrics/test/CMakeLists.txt @@ -0,0 +1,76 @@ +include_directories( + ${PLUGIN_DIR}/model/include + ${PLUGIN_DIR}/service/include + ${CMAKE_CURRENT_BINARY_DIR}/../service/gen-cpp + ${PROJECT_SOURCE_DIR}/model/include) + +include_directories(SYSTEM + ${THRIFT_LIBTHRIFT_INCLUDE_DIRS}) + +add_executable(cppmetricsservicetest + src/cppmetricstest.cpp + src/cppmetricsservicetest.cpp) + +add_executable(cppmetricsparsertest + src/cppmetricstest.cpp + src/cppmetricsparsertest.cpp) + +target_compile_options(cppmetricsservicetest PUBLIC -Wno-unknown-pragmas) +target_compile_options(cppmetricsparsertest PUBLIC -Wno-unknown-pragmas) + +target_link_libraries(cppmetricsservicetest + model + cppmodel + cppservice + ${Boost_LIBRARIES} + ${GTEST_BOTH_LIBRARIES} + pthread) + +target_link_libraries(cppmetricsparsertest + util + model + cppmodel + ${Boost_LIBRARIES} + ${GTEST_BOTH_LIBRARIES} + pthread) + +if (NOT FUNCTIONAL_TESTING_ENABLED) + fancy_message("Skipping generation of test project cppmetricstest." "yellow" TRUE) +else() + set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${CMAKE_CURRENT_BINARY_DIR}/build" + "${CMAKE_CURRENT_BINARY_DIR}/workdir") + + add_test(NAME cppmetricsservice COMMAND cppmetricsservicetest + "echo \"Test database used: ${TEST_DB}\" && \ + rm -rf ${CMAKE_CURRENT_BINARY_DIR}/build && \ + mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cd ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cmake ${CMAKE_CURRENT_SOURCE_DIR}/sources/service \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=on" + "${CMAKE_INSTALL_PREFIX}/bin/CodeCompass_parser \ + --database \"${TEST_DB}\" \ + --name cppmetricsservicetest \ + --input ${CMAKE_CURRENT_BINARY_DIR}/build/compile_commands.json \ + --workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \ + --force" + "${TEST_DB}") + + add_test(NAME cppmetricsparser COMMAND cppmetricsparsertest + "echo \"Test database used: ${TEST_DB}\" && \ + rm -rf ${CMAKE_CURRENT_BINARY_DIR}/build && \ + mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cd ${CMAKE_CURRENT_BINARY_DIR}/build && \ + cmake ${CMAKE_CURRENT_SOURCE_DIR}/sources/parser \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=on" + "${CMAKE_INSTALL_PREFIX}/bin/CodeCompass_parser \ + --database \"${TEST_DB}\" \ + --name cppparsertest \ + --input ${CMAKE_CURRENT_BINARY_DIR}/build/compile_commands.json \ + --workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \ + --force" + "${TEST_DB}") + + fancy_message("Generating test project for cppmetricstest." "blue" TRUE) +endif() \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt new file mode 100644 index 000000000..e004ca4f5 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +add_library(CppTestProject STATIC) diff --git a/plugins/cpp_metrics/test/sources/service/CMakeLists.txt b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt new file mode 100644 index 000000000..e004ca4f5 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +add_library(CppTestProject STATIC) diff --git a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp new file mode 100644 index 000000000..5fd03f6c9 --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp @@ -0,0 +1,21 @@ +#include + +#include +#include + +using namespace cc; + +extern char* dbConnectionString; + +class CppMetricsParserTest : public ::testing::Test +{ +public: + CppMetricsParserTest() : + _db(util::connectDatabase(dbConnectionString)), + _transaction(_db) + {} + +protected: + std::shared_ptr _db; + util::OdbTransaction _transaction; +}; diff --git a/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp b/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp new file mode 100644 index 000000000..095867874 --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricsservicetest.cpp @@ -0,0 +1,31 @@ +#include + +#include + +#include + +using namespace cc; +using namespace service::cppmetrics; + +extern char* dbConnectionString; + +class CppMetricsServiceTest : public ::testing::Test +{ +public: + CppMetricsServiceTest() : + _db(util::connectDatabase(dbConnectionString)), + _transaction(_db), + _cppservice(new CppMetricsServiceHandler( + _db, + std::make_shared(""), + webserver::ServerContext(std::string(), + boost::program_options::variables_map()))) + { + + } + +protected: + std::shared_ptr _db; + util::OdbTransaction _transaction; + std::shared_ptr _cppservice; +}; \ No newline at end of file diff --git a/plugins/cpp_metrics/test/src/cppmetricstest.cpp b/plugins/cpp_metrics/test/src/cppmetricstest.cpp new file mode 100644 index 000000000..25078f570 --- /dev/null +++ b/plugins/cpp_metrics/test/src/cppmetricstest.cpp @@ -0,0 +1,29 @@ +#define GTEST_HAS_TR1_TUPLE 1 +#define GTEST_USE_OWN_TR1_TUPLE 0 + +#include + +#include + +const char* dbConnectionString; + +int main(int argc, char** argv) +{ + dbConnectionString = argv[3]; + if (strcmp(dbConnectionString, "") == 0) + { + GTEST_LOG_(FATAL) << "No test database connection given."; + return 1; + } + + GTEST_LOG_(INFO) << "Testing C++ metrics plugin started..."; + GTEST_LOG_(INFO) << "Executing build command: " << argv[1]; + system(argv[1]); + + GTEST_LOG_(INFO) << "Executing parser command: " << argv[2]; + system(argv[2]); + + GTEST_LOG_(INFO) << "Using database for tests: " << dbConnectionString; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}