diff --git a/plugins/cpp/model/include/model/cppfunction.h b/plugins/cpp/model/include/model/cppfunction.h index 32b1dde6a..00109db98 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 { @@ -59,6 +60,13 @@ struct CppFunctionLocalCount std::size_t count; }; +#pragma db view object(CppFunction) +struct CppFunctionMcCabeWithId +{ + CppAstNodeId astNodeId; + unsigned int mccabe; +}; + } } diff --git a/plugins/cpp/parser/src/clangastvisitor.h b/plugins/cpp/parser/src/clangastvisitor.h index 7fa4c9fc4..47bd608f0 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 @@ -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_) + { + _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_) + { + _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_) + { + _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_) + { + _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_) + { + _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_) + { + _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,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool TraverseBinaryOperator(clang::BinaryOperator* bo_) { + if (bo_->isLogicalOp() && !_mcCabeStack.empty()) + ++_mcCabeStack.top(); _contextStatementStack.push(bo_); bool b = Base::TraverseBinaryOperator(bo_); _contextStatementStack.pop(); @@ -338,14 +472,88 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return b; } - bool TraverseStmt(clang::Stmt* s_) + template + bool StatementStackDecorator(T* stmt_, const std::function& func_) { - _statements.push(s_); - bool b = Base::TraverseStmt(s_); + _statements.push(stmt_); + bool b = func_(stmt_); _statements.pop(); return b; } + template + bool McCabeDecorator(T* stmt_, const std::function& func_) + { + if(!_mcCabeStack.empty()) + ++_mcCabeStack.top(); + return StatementStackDecorator(stmt_, func_); + } + + bool TraverseIfStmt(clang::IfStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::IfStmt* s) { return Base::TraverseIfStmt(s); }); + } + + bool TraverseWhileStmt(clang::WhileStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::WhileStmt* s) { return Base::TraverseWhileStmt(s); }); + } + + bool TraverseDoStmt(clang::DoStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::DoStmt* s) { return Base::TraverseDoStmt(s); }); + } + + bool TraverseForStmt(clang::ForStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::ForStmt* s) { return Base::TraverseForStmt(s); }); + } + + bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::CXXForRangeStmt* s) { return Base::TraverseCXXForRangeStmt(s); }); + } + + bool TraverseCaseStmt(clang::CaseStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::CaseStmt* s) { return Base::TraverseCaseStmt(s); }); + } + + bool TraverseDefaultStmt(clang::DefaultStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::DefaultStmt* s) { return Base::TraverseDefaultStmt(s); }); + } + + bool TraverseContinueStmt(clang::ContinueStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::ContinueStmt* s) { return Base::TraverseContinueStmt(s); }); + } + + bool TraverseGotoStmt(clang::GotoStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::GotoStmt* s) { return Base::TraverseGotoStmt(s); }); + } + + bool TraverseCXXCatchStmt(clang::CXXCatchStmt* stmt_) + { + return McCabeDecorator(stmt_, [this] (clang::CXXCatchStmt* s) { return Base::TraverseCXXCatchStmt(s); }); + } + + bool TraverseConditionalOperator(clang::ConditionalOperator* op_) + { + return McCabeDecorator(op_, [this] (clang::ConditionalOperator* s) { return Base::TraverseConditionalOperator(s); }); + } + + bool TraverseBinaryConditionalOperator(clang::BinaryConditionalOperator* op_) + { + return McCabeDecorator(op_, [this] (clang::BinaryConditionalOperator* s) { return Base::TraverseBinaryConditionalOperator(s); }); + } + + bool TraverseStmt(clang::Stmt* stmt_) + { + return StatementStackDecorator(stmt_, [this] (clang::Stmt* s) { return Base::TraverseStmt(s); }); + } + bool VisitTypedefTypeLoc(clang::TypedefTypeLoc tl_) { const clang::TypedefType* type = tl_.getTypePtr(); @@ -710,6 +918,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor cppFunction->qualifiedName = fn_->getQualifiedNameAsString(); cppFunction->typeHash = util::fnvHash(getUSR(qualType, _astContext)); cppFunction->qualifiedType = qualType.getAsString(); + cppFunction->mccabe = 1; clang::CXXMethodDecl* md = llvm::dyn_cast(fn_); @@ -1653,8 +1862,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/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..bbe3cfdf4 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::CppFunctionMcCabeWithId& function + : _ctx.db->query()) + { + model::CppAstNodeMetrics funcMcCabe; + funcMcCabe.astNodeId = function.astNodeId; + funcMcCabe.type = model::CppAstNodeMetrics::Type::MCCABE; + funcMcCabe.value = function.mccabe; + _ctx.db->persist(funcMcCabe); + } + }); +} + bool CppMetricsParser::parse() { // Function parameter number metric. functionParameters(); + // Function McCabe metric + functionMcCabe(); return true; } 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..305294e31 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +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..058c22039 --- /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 + badPractice = new char[LIMIT]; + else + badPractice = new char[arg1]; + } // 2 + + ~MyClass() // +1 + { + for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 + delete[] badPractice; + } // 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* badPractice; +}; 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..305294e31 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/service/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.6) +project(CppTestProject) + +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..058c22039 --- /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 + badPractice = new char[LIMIT]; + else + badPractice = new char[arg1]; + } // 2 + + ~MyClass() // +1 + { + for (unsigned int i = 0; i < LIMIT; ++i) {} // +1 + delete[] badPractice; + } // 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* badPractice; +}; 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..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(); +}