diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7989a3090..346b2d262 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: Build project on: [push, pull_request, workflow_dispatch] env: - BUILD_TYPE: Debug + BUILD_TYPE: RelWithDebInfo ## For locally compiled dependencies INSTALL_PATH: ${{github.workspace}}/dependencies/install ## Temp directory for installers of the downloaded dependencies diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 85a08bc94..ece93bf67 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: analyze-cpp: name: Analyze C-C++ env: - BUILD_TYPE: Debug + BUILD_TYPE: RelWithDebInfo INSTALL_PATH: ${{github.workspace}}/dependencies/install DOWNLOAD_PATH: ${{github.workspace}}/dependencies/download runs-on: ubuntu-22.04 diff --git a/.gitlab/build-codecompass.sh b/.gitlab/build-codecompass.sh index 512b292a6..87ca961ad 100644 --- a/.gitlab/build-codecompass.sh +++ b/.gitlab/build-codecompass.sh @@ -63,7 +63,7 @@ cd $CC_BUILD_DIR cmake $CC_SRC_DIR \ -DCMAKE_INSTALL_PREFIX=$CC_INSTALL_DIR \ -DDATABASE=pgsql \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_BUILD_TYPE=Release \ -DWITH_AUTH="plain;ldap" \ -DLLVM_DIR=$DEPS_INSTALL_RUNTIME_DIR/llvm/lib/cmake/llvm/ \ -DClang_DIR=$DEPS_INSTALL_RUNTIME_DIR/llvm/lib/cmake/clang/ diff --git a/.gitlab/build-deps.sh b/.gitlab/build-deps.sh index 1701860d4..09db12e0d 100644 --- a/.gitlab/build-deps.sh +++ b/.gitlab/build-deps.sh @@ -373,12 +373,12 @@ rm -f $PACKAGES_DIR/release-1.10.0.tar.gz ####### cd $PACKAGES_DIR -wget --no-verbose --no-clobber https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.xz -tar -xf node-v16.20.2-linux-x64.tar.xz -C $DEPS_INSTALL_RUNTIME_DIR -rm -f node-v16.20.2-linux-x64.tar.xz +wget --no-verbose --no-clobber https://nodejs.org/dist/v18.20.2/node-v18.20.2-linux-x64.tar.xz +tar -xf node-v18.20.2-linux-x64.tar.xz -C $DEPS_INSTALL_RUNTIME_DIR +rm -f node-v18.20.2-linux-x64.tar.xz cd $DEPS_INSTALL_RUNTIME_DIR -mv node-v16.20.2-linux-x64 node-install +mv node-v18.20.2-linux-x64 node-install export PATH=$DEPS_INSTALL_RUNTIME_DIR/node-install/bin:$PATH ############ diff --git a/.gitlab/ci.yml b/.gitlab/ci.yml index acb3e8f45..625890def 100644 --- a/.gitlab/ci.yml +++ b/.gitlab/ci.yml @@ -61,65 +61,16 @@ variables: echo "GLIBC version: $GLIBC_VERSION" echo "GLIBCXX versions: $GLIBCXX_VERSIONS" -tarball suse-15: +tarball ubuntu-20.04: extends: .tarball - image: opensuse/leap:15 + image: ubuntu:20.04 cache: - key: "leap" + key: "focal" variables: GCC_VERSION: 9.3.0 ODB_VERSION: 2.5.0 - before_script: - - zypper refresh - - zypper update -y - # download tools - - zypper install -y curl wget gzip bzip2 unzip ca-certificates - # build tools for CodeCompass - - zypper install -y -t pattern devel_basis - - zypper install -y binutils gcc-c++ gmp-devel - # build tools for OpenLDAP - - zypper install -y groff - # build tools for ccdb-tools - - zypper install -y libffi-devel - # show GLIBC verison - - *show-glibc-version - -# Permanently disabled job -# (Distribution package repository metadata not available anymore.) -.tarball suse-42.1: - extends: .tarball - image: opensuse/archive:42.1 - cache: - key: "malachite" - variables: - GCC_VERSION: 5.5.0 - ODB_VERSION: 2.4.0 - before_script: - - zypper refresh - - zypper update -y - # download tools - - zypper install -y curl wget gzip bzip2 unzip ca-certificates - # build tools for CodeCompass - - zypper install -y -t pattern devel_basis - - zypper install -y binutils gcc-c++ gmp-devel - # build tools for OpenLDAP - - zypper install -y groff - # build tools for ccdb-tools - - zypper install -y libffi-devel - # disable SSL certificate check (distribution's cacert is outdated) - - echo "check_certificate = off" >> /etc/wgetrc - - echo insecure >> ~/.curlrc - # show GLIBC verison - - *show-glibc-version - -tarball ubuntu-16.04: - extends: .tarball - image: ubuntu:16.04 - cache: - key: "xenial" - variables: - GCC_VERSION: 5.5.0 - ODB_VERSION: 2.4.0 + # No interactive timezone dialog for tzdata + DEBIAN_FRONTEND: noninteractive before_script: - apt-get update -yqq # download tools @@ -154,22 +105,8 @@ tarball ubuntu-16.04: - scp -P22 build/codecompass.tar.gz gitlab-deployer@codecompass.net:/var/www/codecompass/$FILENAME - ssh -p22 gitlab-deployer@codecompass.net "mv -f /var/www/codecompass/$FILENAME /var/www/codecompass/live/wwwroot/tarball/$FILENAME" -upload suse-15: - extends: .upload - variables: - ARCH_SUFFIX: suse-15 - needs: ["tarball suse-15"] - -# Permanently disabled job -# (Distribution package repository metadata not available anymore.) -.upload suse-42.1: - extends: .upload - variables: - ARCH_SUFFIX: suse-42.1 - needs: ["tarball suse-42.1"] - -upload ubuntu-16.04: +upload ubuntu-20.04: extends: .upload variables: - ARCH_SUFFIX: ubuntu-16.04 - needs: ["tarball ubuntu-16.04"] + ARCH_SUFFIX: ubuntu-20.04 + needs: ["tarball ubuntu-20.04"] diff --git a/parser/src/sourcemanager.cpp b/parser/src/sourcemanager.cpp index 114db9661..73868e859 100644 --- a/parser/src/sourcemanager.cpp +++ b/parser/src/sourcemanager.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -238,9 +239,9 @@ void SourceManager::removeFile(const model::File& file_) _transaction([&]() { if(file_.content) { - auto relFiles = _db->query( + odb::result relFiles = _db->query( odb::query::content == file_.content.object_id()); - if (relFiles.size() == 1) + if (util::isSingleResult(relFiles)) { removeContent = true; _db->erase(file_.content.object_id()); diff --git a/plugins/cpp/parser/src/cppparser.cpp b/plugins/cpp/parser/src/cppparser.cpp index 16ae1d6c0..58620801c 100644 --- a/plugins/cpp/parser/src/cppparser.cpp +++ b/plugins/cpp/parser/src/cppparser.cpp @@ -779,6 +779,10 @@ bool CppParser::parseByJson( LOG(warning) << '(' << job_.index << '/' << numCompileCommands << ')' << " Parsing " << command.Filename << " has been failed."; + else + LOG(debug) + << '(' << job_.index << '/' << numCompileCommands << ')' + << " Parsing " << command.Filename << " finished successfully."; }); //--- Push all commands into the thread pool's queue ---// diff --git a/plugins/cpp_metrics/model/CMakeLists.txt b/plugins/cpp_metrics/model/CMakeLists.txt index 38d8ca343..580be0d66 100644 --- a/plugins/cpp_metrics/model/CMakeLists.txt +++ b/plugins/cpp_metrics/model/CMakeLists.txt @@ -5,11 +5,12 @@ include_directories( set(ODB_SOURCES include/model/cppastnodemetrics.h include/model/cppcohesionmetrics.h - include/model/cppfilemetrics.h) + include/model/cppfilemetrics.h + include/model/cpprelationalcohesion.h) generate_odb_files("${ODB_SOURCES}" "cpp") add_odb_library(cppmetricsmodel ${ODB_CXX_SOURCES}) target_link_libraries(cppmetricsmodel cppmodel) -install_sql() \ No newline at end of file +install_sql() diff --git a/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h b/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h index 5c2f5b452..6b53bfd0b 100644 --- a/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h +++ b/plugins/cpp_metrics/model/include/model/cppastnodemetrics.h @@ -45,6 +45,19 @@ struct CppRecordMetricsView double value; }; +#pragma db view \ + object(CppAstNodeMetrics) \ + object(CppAstNode : CppAstNodeMetrics::astNodeId == CppAstNode::id) \ + object(File : CppAstNode::location.file) +struct CppAstNodeMetricsFileView +{ + #pragma db column(CppAstNode::id) + CppAstNodeId astNodeId; + + #pragma db column(File::id) + FileId fileId; +}; + #pragma db view \ object(CppAstNodeMetrics) \ object(CppAstNode : CppAstNodeMetrics::astNodeId == CppAstNode::id) \ diff --git a/plugins/cpp_metrics/model/include/model/cppfilemetrics.h b/plugins/cpp_metrics/model/include/model/cppfilemetrics.h index eebedbc46..95c393e1a 100644 --- a/plugins/cpp_metrics/model/include/model/cppfilemetrics.h +++ b/plugins/cpp_metrics/model/include/model/cppfilemetrics.h @@ -13,7 +13,7 @@ struct CppFileMetrics { enum Type { - PLACEHOLDER + RELATIONAL_COHESION = 1 }; #pragma db id auto @@ -26,7 +26,7 @@ struct CppFileMetrics Type type; #pragma db not_null - unsigned value; + double value; }; #pragma db view \ diff --git a/plugins/cpp_metrics/model/include/model/cpprelationalcohesion.h b/plugins/cpp_metrics/model/include/model/cpprelationalcohesion.h new file mode 100644 index 000000000..216afb91a --- /dev/null +++ b/plugins/cpp_metrics/model/include/model/cpprelationalcohesion.h @@ -0,0 +1,116 @@ +#ifndef CC_MODEL_CPPRELATIONALCOHESION_H +#define CC_MODEL_CPPRELATIONALCOHESION_H + +#include +#include +#include +#include +#include + +namespace cc +{ + namespace model + { + + #pragma db view \ + object(File) + struct RelationalCohesionFileView + { + #pragma db column(File::path) + std::string filePath; + + #pragma db column(File::type) + std::string fileType; + + #pragma db column(File::id) + std::size_t fileId; + + }; + + #pragma db view \ + object(CppRecord) \ + object(CppAstNode : CppRecord::astNodeId == CppAstNode::id) \ + object(File : CppAstNode::location.file) \ + object(CppMemberType : CppMemberType::memberAstNode) + struct RelationalCohesionRecordView + { + #pragma db column(CppEntity::entityHash) + std::size_t entityHash; + + #pragma db column(CppMemberType::typeHash) + std::size_t typeHash; + + #pragma db column(CppEntity::qualifiedName) + std::string qualifiedName; + + #pragma db column(CppEntity::astNodeId) + CppAstNodeId astNodeId; + + #pragma db column(File::path) + std::string filePath; + }; + + #pragma db view \ + object(CppFunction) \ + object(CppAstNode : CppFunction::astNodeId == CppAstNode::id) \ + object(File : CppAstNode::location.file) + struct RelationalCohesionFunctionView + { + #pragma db column(CppEntity::entityHash) + std::size_t entityHash; + + #pragma db column(CppTypedEntity::typeHash) + std::size_t typeHash; + + #pragma db column(File::path) + std::string filePath; + }; + + + #pragma db view \ + object(CppFunction) \ + object(CppVariable = Parameters : CppFunction::parameters) \ + object(CppAstNode : CppAstNode::id == CppFunction::astNodeId) \ + object(File : CppAstNode::location.file) + struct RelationalCohessionFunctionParameterView + { + #pragma db column(Parameters::typeHash) + std::size_t typeHash; + + #pragma db column(File::path) + std::string filePath; + }; + + + #pragma db view \ + object(CppFunction) \ + object(CppVariable = Locals : CppFunction::locals) \ + object(CppAstNode : CppAstNode::id == CppFunction::astNodeId) \ + object(File : CppAstNode::location.file) + struct RelationalCohessionFunctionLocalView + { + #pragma db column(Locals::typeHash) + std::size_t typeHash; + + #pragma db column(File::path) + std::string filePath; + }; + + #pragma db view \ + object(CppVariable) \ + object(CppAstNode : CppAstNode::id == CppEntity::astNodeId) \ + object(File : CppAstNode::location.file) + struct RelationalCohesionVariableView + { + #pragma db column(CppVariable::typeHash) + std::size_t typeHash; + + #pragma db column(File::path) + std::string filePath; + + }; + + } +} + +#endif \ No newline at end of file diff --git a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h index 5abe1af83..798d2e362 100644 --- a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h +++ b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h @@ -40,8 +40,21 @@ class CppMetricsParser : public AbstractParser // Calculate the lack of cohesion between member variables // and member functions for every type. void lackOfCohesion(); + // Calculate the cohesion within modules + void relationalCohesion(); + // Check type relations in template parameter view. + // Used in relational cohesion metric. + template + void checkTypes( + const std::string& path, + const std::unordered_set& typesFound, + const std::unordered_map& typeDefinitionPaths, + std::unordered_multimap& relationsFoundInFile, + int& relationsInModule + ); std::vector _inputPaths; + std::string _modulesPath; std::unordered_set _fileIdCache; std::unordered_map _astNodeIdCache; std::unique_ptr> _pool; diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 12ebd1cb7..ae1e7ec97 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -18,6 +20,7 @@ #include #include +#include namespace cc { @@ -32,6 +35,8 @@ CppMetricsParser::CppMetricsParser(ParserContext& ctx_): AbstractParser(ctx_) _ctx.options["input"].as>()) _inputPaths.push_back(fs::canonical(path).string()); + _modulesPath = _ctx.options["modules"].as(); + util::OdbTransaction {_ctx.db} ([&, this] { for (const model::CppFileMetrics& fm : _ctx.db->query()) @@ -39,12 +44,10 @@ CppMetricsParser::CppMetricsParser(ParserContext& ctx_): AbstractParser(ctx_) _fileIdCache.insert(fm.file); } - for (const model::CppAstNodeMetrics& anm - : _ctx.db->query()) + for (const model::CppAstNodeMetricsFileView& anm + : _ctx.db->query()) { - auto node = _ctx.db->query_one( - odb::query::id == anm.astNodeId); - _astNodeIdCache.insert({anm.astNodeId, node->location.file->id}); + _astNodeIdCache.emplace(anm.astNodeId, anm.fileId); } }); } @@ -165,6 +168,164 @@ void CppMetricsParser::functionBumpyRoad() }); } +template +void CppMetricsParser::checkTypes( + const std::string& path, + const std::unordered_set& typesFound, + const std::unordered_map& typeDefinitionPaths, + std::unordered_multimap& relationsFoundInFile, + int& relationsInModule +) +{ + for (const T& item + : _ctx.db->query( + odb::query::query_columns::File::path.like(path + "%") + )) + { + if ( + typesFound.find(item.typeHash) != typesFound.end() && + // Check if return type is defined within the current module + typeDefinitionPaths.at(item.typeHash) != item.filePath && + // Check for self relation + relationsFoundInFile.find(item.filePath) == relationsFoundInFile.end() && + // Check if any relations were found in the current file + std::none_of( + relationsFoundInFile.equal_range(item.filePath).first, + relationsFoundInFile.equal_range(item.filePath).second, + [&item](const auto& pair) { return pair.second == item.typeHash; } + ) + ) + { + ++relationsInModule; + relationsFoundInFile.emplace(item.filePath, item.typeHash); + } + } +} + +void CppMetricsParser::relationalCohesion() +{ + std::unordered_set modulePaths; + std::unordered_map typeDefinitionPaths; + std::unordered_set typesFound; + std::unordered_map moduleIds; + + util::OdbTransaction{_ctx.db}([&, this] { + std::ifstream file(_modulesPath); + + // Read the specified module list if given + if (file.is_open()) + { + std::string line; + while (std::getline(file, line)) + { + modulePaths.insert(line); + } + } + // If no modules list is specified every directory under the input paths is considered one + else + { + for (const auto& inputPath : _inputPaths) + { + for (const model::RelationalCohesionFileView& file + : _ctx.db->query( + odb::query::query_columns::path.like(inputPath + "%") && + odb::query::query_columns::type.equal(model::File::DIRECTORY_TYPE) + )) + { + modulePaths.insert(file.filePath); + } + } + } + + // Get the file IDs for each module + for (auto& path : modulePaths) + { + for (const model::RelationalCohesionFileView& file + : _ctx.db->query( + odb::query::query_columns::path.equal(path) + )) + { + moduleIds[file.filePath] = file.fileId; + } + } + + for (const auto& path : modulePaths) + { + typesFound.clear(); + typeDefinitionPaths.clear(); + // Find the types defined in the module + for (const model::RelationalCohesionRecordView& record + : _ctx.db->query( + odb::query::query_columns::File::path.like(path + "%") + )) + { + // Save types defined inside the module + typesFound.insert(record.typeHash); + // Save where the type is defined to avoid self relation + typeDefinitionPaths.insert(std::make_pair(record.typeHash, record.filePath)); + } + + // Store the type relations already found for each file + std::unordered_multimap relationsFoundInFile; + int relationsInModule = 0; + + // Check function return types + CppMetricsParser::checkTypes( + path, + typesFound, + typeDefinitionPaths, + relationsFoundInFile, + relationsInModule + ); + + // Check function parameters + CppMetricsParser::checkTypes( + path, + typesFound, + typeDefinitionPaths, + relationsFoundInFile, + relationsInModule + ); + + // Check function locals + CppMetricsParser::checkTypes( + path, + typesFound, + typeDefinitionPaths, + relationsFoundInFile, + relationsInModule + ); + + // Check variables + CppMetricsParser::checkTypes( + path, + typesFound, + typeDefinitionPaths, + relationsFoundInFile, + relationsInModule + ); + + // Calculate relational cohesion for module + // Formula: H = (R + 1)/ N + // Where H is the relational cohesion value, + // R is the number of relationships internal to the module, + // N is the number of types in the module + + uint numberOfTypesInModule = typesFound.size(); //N + + double relationalCohesion = + (static_cast(relationsInModule) + 1.0) / static_cast(numberOfTypesInModule); + + model::CppFileMetrics relationalCohesionMetrics; + relationalCohesionMetrics.file = moduleIds[path]; + relationalCohesionMetrics.value = relationalCohesion; + relationalCohesionMetrics.type = model::CppFileMetrics::Type::RELATIONAL_COHESION; + _ctx.db->persist(relationalCohesionMetrics); + } + }); +} + + void CppMetricsParser::lackOfCohesion() { util::OdbTransaction {_ctx.db} ([&, this] @@ -272,10 +433,16 @@ void CppMetricsParser::lackOfCohesion() bool CppMetricsParser::parse() { + LOG(info) << "[cppmetricsparser] Computing function parameter count metric."; functionParameters(); + LOG(info) << "[cppmetricsparser] Computing McCabe metric for functions."; functionMcCabe(); + LOG(info) << "[cppmetricsparser] Computing Bumpy Road metric for functions."; functionBumpyRoad(); + LOG(info) << "[cppmetricsparser] Computing Lack of Cohesion metric for types."; lackOfCohesion(); + LOG(info) << "[cppmetricsparser] Computing Relational Cohesion metric for modules."; + relationalCohesion(); return true; } @@ -301,7 +468,13 @@ extern "C" { boost::program_options::options_description description("C++ Metrics Plugin"); + description.add_options() + ("modules,m", po::value()->default_value("Unspecified"), + "The user specifies the path to the modules list here."); + return description; + + } std::shared_ptr make(ParserContext& ctx_) diff --git a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt index 83409bc33..79a9771b6 100644 --- a/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt +++ b/plugins/cpp_metrics/test/sources/parser/CMakeLists.txt @@ -1,7 +1,10 @@ cmake_minimum_required(VERSION 2.6) project(CppMetricsTestProject) +file(GLOB_RECURSE RELATIONAL_COHESION_SOURCES "RelationalCohesion/**/*.cpp") + add_library(CppMetricsTestProject STATIC mccabe.cpp lackofcohesion.cpp - bumpyroad.cpp) + bumpyroad.cpp + ${RELATIONAL_COHESION_SOURCES}) diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedA.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedA.cpp new file mode 100644 index 000000000..d1f190669 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedA.cpp @@ -0,0 +1 @@ +class unrelatedA {}; diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedB.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedB.cpp new file mode 100644 index 000000000..27b374dfc --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedB.cpp @@ -0,0 +1 @@ +class unrelatedB {}; diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedC.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedC.cpp new file mode 100644 index 000000000..17350c663 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedC.cpp @@ -0,0 +1 @@ +class unrelatedC {}; diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedD.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedD.cpp new file mode 100644 index 000000000..b5a3d8f55 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleNoRelation/unrelatedD.cpp @@ -0,0 +1 @@ +class unrelatedD {}; diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.cpp new file mode 100644 index 000000000..ddd6130ae --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.cpp @@ -0,0 +1,6 @@ +#include "relatedByFPA.h" +#include "relatedByFPB.h" +#include "relatedByFPC.h" +#include "relatedByFPD.h" + +void relatedByFPA::f(relatedByFPB par_B, relatedByFPC par_C, relatedByFPD par_D) {} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.h new file mode 100644 index 000000000..260c8b9c7 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPA.h @@ -0,0 +1,14 @@ +#ifndef relatedByFPA_H +#define relatedByFPA_H + +// Forward declarations +class relatedByFPB; +class relatedByFPC; +class relatedByFPD; + +class relatedByFPA { +public: + void f(relatedByFPB par_B, relatedByFPC par_C, relatedByFPD par_D); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.cpp new file mode 100644 index 000000000..7cc6703c9 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.cpp @@ -0,0 +1,6 @@ +#include "relatedByFPB.h" +#include "relatedByFPA.h" +#include "relatedByFPC.h" +#include "relatedByFPD.h" + +void relatedByFPB::f(relatedByFPA par_A, relatedByFPC par_C, relatedByFPD par_D) {} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.h new file mode 100644 index 000000000..bc7a22e89 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPB.h @@ -0,0 +1,14 @@ +#ifndef relatedByFPB_H +#define relatedByFPB_H + +// Forward declarations +class relatedByFPA; +class relatedByFPC; +class relatedByFPD; + +class relatedByFPB { +public: + void f(relatedByFPA par_A, relatedByFPC par_C, relatedByFPD par_D); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.cpp new file mode 100644 index 000000000..f5c50b6b7 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.cpp @@ -0,0 +1,8 @@ +#include "relatedByFPC.h" +#include "relatedByFPA.h" +#include "relatedByFPB.h" +#include "relatedByFPD.h" + +int relatedByFPC::f(relatedByFPD par_D) { + return 0; +} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.h new file mode 100644 index 000000000..d19487181 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPC.h @@ -0,0 +1,14 @@ +#ifndef relatedByFPC_H +#define relatedByFPC_H + +// Forward declarations +class relatedByFPA; +class relatedByFPB; +class relatedByFPD; + +class relatedByFPC { +public: + int f(relatedByFPD par_D); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.cpp new file mode 100644 index 000000000..415e1f5e0 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.cpp @@ -0,0 +1 @@ +#include "relatedByFPD.h" diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.h new file mode 100644 index 000000000..cd38e8c61 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionParameter/relatedByFPD.h @@ -0,0 +1,6 @@ +#ifndef relatedByFPD_H +#define relatedByFPD_H + +class relatedByFPD {}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.cpp new file mode 100644 index 000000000..31e4049b8 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.cpp @@ -0,0 +1,8 @@ +#include "relatedByFRA.h" +#include "relatedByFRB.h" +#include "relatedByFRC.h" +#include "relatedByFRD.h" + +relatedByFRB relatedByFRA::fB() {return relatedByFRB();}; +relatedByFRC relatedByFRA::fC() {return relatedByFRC();}; +relatedByFRD relatedByFRA::fD() {return relatedByFRD();}; diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.h new file mode 100644 index 000000000..d19833746 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRA.h @@ -0,0 +1,16 @@ +#ifndef relatedByFRA_H +#define relatedByFRA_H + +class relatedByFRB; +class relatedByFRC; +class relatedByFRD; + +class relatedByFRA { +public: + relatedByFRA() {} + relatedByFRB fB(); + relatedByFRC fC(); + relatedByFRD fD(); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.cpp new file mode 100644 index 000000000..c040a2cf0 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.cpp @@ -0,0 +1,8 @@ +#include "relatedByFRA.h" +#include "relatedByFRB.h" +#include "relatedByFRC.h" +#include "relatedByFRD.h" + +relatedByFRA relatedByFRB::fA() {return relatedByFRA();} +relatedByFRC relatedByFRB::fC() {return relatedByFRC();} +relatedByFRD relatedByFRB::fD() {return relatedByFRD();} \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.h new file mode 100644 index 000000000..d5b8f802e --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRB.h @@ -0,0 +1,16 @@ +#ifndef relatedByFRB_H +#define relatedByFRB_H + +class relatedByFRA; +class relatedByFRC; +class relatedByFRD; + +class relatedByFRB { +public: + relatedByFRB() {} + relatedByFRA fA(); + relatedByFRC fC(); + relatedByFRD fD(); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.cpp new file mode 100644 index 000000000..83c32d48e --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.cpp @@ -0,0 +1,6 @@ +#include "relatedByFRC.h" +#include "relatedByFRA.h" +#include "relatedByFRB.h" +#include "relatedByFRD.h" + +relatedByFRC::relatedByFRC() {} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.h new file mode 100644 index 000000000..dc6eb5907 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRC.h @@ -0,0 +1,13 @@ +#ifndef relatedByFRC_H +#define relatedByFRC_H + +class relatedByFRA; +class relatedByFRB; +class relatedByFRD; + +class relatedByFRC { +public: + relatedByFRC (); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.cpp new file mode 100644 index 000000000..f625a9446 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.cpp @@ -0,0 +1,3 @@ +#include "relatedByFRD.h" + +relatedByFRD::relatedByFRD() {} \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.h new file mode 100644 index 000000000..7d669e475 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByFunctionReturnValue/relatedByFRD.h @@ -0,0 +1,9 @@ +#ifndef relatedByFRD_H +#define relatedByFRD_H + +class relatedByFRD { + public: + relatedByFRD (); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.cpp new file mode 100644 index 000000000..c97c98532 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.cpp @@ -0,0 +1,6 @@ +#include "relatedByVarA.h" +#include "relatedByVarB.h" +#include "relatedByVarC.h" +#include "relatedByVarD.h" + +void relatedByVarA::f() {relatedByVarD* varD;} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.h new file mode 100644 index 000000000..25e367fb3 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarA.h @@ -0,0 +1,16 @@ +#ifndef relatedByVarA_H +#define relatedByVarA_H + +class relatedByVarB; +class relatedByVarC; + +class relatedByVarA { +private: + relatedByVarB* varB; + relatedByVarC* varC; +public: + relatedByVarA() : varB(nullptr), varC(nullptr) {} + void f(); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.cpp new file mode 100644 index 000000000..9a1f9ebb2 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.cpp @@ -0,0 +1,5 @@ +#include "relatedByVarA.h" +#include "relatedByVarB.h" +#include "relatedByVarC.h" + +void relatedByVarB::f() {relatedByVarC* varC;} \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.h new file mode 100644 index 000000000..b5c6c87f1 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarB.h @@ -0,0 +1,14 @@ +#ifndef relatedByVarB_H +#define relatedByVarB_H + +class relatedByVarA; + +class relatedByVarB { +private: + relatedByVarA* varA; +public: + relatedByVarB() : varA(nullptr) {} + void f(); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.cpp new file mode 100644 index 000000000..571187a6b --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.cpp @@ -0,0 +1,4 @@ +#include "relatedByVarA.h" +#include "relatedByVarC.h" + +void relatedByVarC::f() {relatedByVarA* varA;} diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.h new file mode 100644 index 000000000..1e826a12f --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarC.h @@ -0,0 +1,10 @@ +#ifndef relatedByVarC_H +#define relatedByVarC_H + +class relatedByVarC { +public: + relatedByVarC() {} + void f(); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.cpp new file mode 100644 index 000000000..56b4e884f --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.cpp @@ -0,0 +1,3 @@ +#include "relatedByVarD.h" + +relatedByVarD::relatedByVarD() {} \ No newline at end of file diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.h b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.h new file mode 100644 index 000000000..4aef34ae6 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/MultipleTypesInModuleWithRelations/RelationByVariable/relatedByVarD.h @@ -0,0 +1,9 @@ +#ifndef relatedByVarD_H +#define relatedByVarD_H + +class relatedByVarD { + public: + relatedByVarD (); +}; + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/OneTypeInModule/singleTypeInModule.cpp b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/OneTypeInModule/singleTypeInModule.cpp new file mode 100644 index 000000000..a650fd681 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/RelationalCohesion/OneTypeInModule/singleTypeInModule.cpp @@ -0,0 +1 @@ +class A {}; \ No newline at end of file diff --git a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp index e22f26d68..67d7bbf2e 100644 --- a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp +++ b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -231,3 +235,46 @@ INSTANTIATE_TEST_SUITE_P( ParameterizedLackOfCohesionTest, ::testing::ValuesIn(paramLackOfCohesion) ); + + +// Relational cohesion +struct RelationalCohesionParam +{ + std::string moduleName; + double rc; +}; + +class ParameterizedRelationalCohesionTest + : public CppMetricsParserTest, + public ::testing::WithParamInterface +{}; + +std::vector paramRelationalCohesion = { + {"OneTypeInModule", 1.0} + /*{"MultipleTypesInModuleNoRelation", 0.25}, + {"RelationByFunctionParameter", 7.0/4.0}, + {"RelationByFunctionReturnValue", 6.0/4.0}, + {"RelationByVariable", 6.0/4.0}*/ +}; + +TEST_P(ParameterizedRelationalCohesionTest, RelationalCohesionTest) { + _transaction([&, this](){ + const std::string& moduleName = GetParam().moduleName; + std::uint64_t moduleId = _db->query_value( + !odb::query::path.like("%/CMakeFiles/%") && + odb::query::filename == moduleName).id; + double storedValue = _db->query_value( + odb::query::file == moduleId && + odb::query::type == model::CppFileMetrics::Type::RELATIONAL_COHESION + ).value; + + constexpr double tolerance = 1e-8; + EXPECT_EQF(GetParam().rc, storedValue, tolerance); + }); +} + +INSTANTIATE_TEST_SUITE_P( + ParameterizedRelationalCohesionTestSuite, + ParameterizedRelationalCohesionTest, + ::testing::ValuesIn(paramRelationalCohesion) +); \ No newline at end of file diff --git a/util/include/util/dbutil.h b/util/include/util/dbutil.h index 4a3d27514..5d1c331d5 100644 --- a/util/include/util/dbutil.h +++ b/util/include/util/dbutil.h @@ -97,6 +97,40 @@ inline std::string getDbDriver() #endif } +/// @brief Determines if the specified ODB query result only contains +/// a single entity. That single entity is then stored in 'single_'. +/// @tparam TEntity The type of entities in the query result. +/// @param result_ The ODB query result in question. +/// @param single_ The variable that receives the first entity (if any). +/// @return Returns true if 'result_' only contained 'single_'; +/// otherwise false. +template +bool isSingleResult(odb::result& result_, TEntity& single_) +{ + auto it_b = result_.begin(); + const auto it_e = result_.end(); + if (it_b != it_e) + { + single_ = *it_b; + return ++it_b == it_e; + } + else return false; +} + +/// @brief Determines if the specified ODB query result only contains +/// a single entity. +/// @tparam TEntity The type of entities in the query result. +/// @param result_ The ODB query result in question. +/// @return Returns true if 'result_' only contained a single entity; +/// otherwise false. +template +bool isSingleResult(odb::result& result_) +{ + auto it_b = result_.begin(); + const auto it_e = result_.end(); + return (it_b != it_e) && (++it_b == it_e); +} + } // util } // cc