diff --git a/plugins/cpp_metrics/model/include/model/cppfilemetrics.h b/plugins/cpp_metrics/model/include/model/cppfilemetrics.h index b5f6f24dc..faa977aa0 100644 --- a/plugins/cpp_metrics/model/include/model/cppfilemetrics.h +++ b/plugins/cpp_metrics/model/include/model/cppfilemetrics.h @@ -13,7 +13,8 @@ struct CppFileMetrics { enum Type { - EFFERENT_MODULE + EFFERENT_MODULE, + AFFERENT_MODULE }; #pragma db id auto diff --git a/plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h b/plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h index cca2370e5..76673904c 100644 --- a/plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h +++ b/plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h @@ -57,12 +57,26 @@ struct CppTypeDependencyMetricsPathView object(CppAstNode = DependencyAstNode : CppTypeDependencyMetrics::dependencyHash == DependencyAstNode::entityHash \ && DependencyAstNode::astType == cc::model::CppAstNode::AstType::Definition) \ object(File = DependencyFile : DependencyAstNode::location.file == DependencyFile::id) -struct CppTypeDependencyMetricsPathViewDistinctCount +struct CppTypeDependencyMetrics_Distinct_D_Count { #pragma db column("count(distinct" + CppTypeDependencyMetrics::dependencyHash + ")") std::size_t count; }; +#pragma db view \ + object(CppTypeDependencyMetrics) \ + object(CppAstNode = EntityAstNode : CppTypeDependencyMetrics::entityHash == EntityAstNode::entityHash \ + && EntityAstNode::astType == cc::model::CppAstNode::AstType::Definition) \ + object(File = EntityFile : EntityAstNode::location.file == EntityFile::id) \ + object(CppAstNode = DependencyAstNode : CppTypeDependencyMetrics::dependencyHash == DependencyAstNode::entityHash \ + && DependencyAstNode::astType == cc::model::CppAstNode::AstType::Definition) \ + object(File = DependencyFile : DependencyAstNode::location.file == DependencyFile::id) +struct CppTypeDependencyMetrics_Distinct_E_Count +{ + #pragma db column("count(distinct" + CppTypeDependencyMetrics::entityHash + ")") + std::size_t count; +}; + } // model } // cc diff --git a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h index 4f9c21f9e..35f20440e 100644 --- a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h +++ b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h @@ -82,6 +82,8 @@ class CppMetricsParser : public AbstractParser void afferentTypeLevel(); // Calculate the efferent coupling at module level. void efferentModuleLevel(); + // Calculate the afferent coupling at module level. + void afferentModuleLevel(); // Returns module path query based on parser configuration. odb::query getModulePathsQuery(); @@ -207,6 +209,7 @@ class CppMetricsParser : public AbstractParser static const int efferentCouplingTypesPartitionMultiplier = 5; static const int afferentCouplingTypesPartitionMultiplier = 5; static const int efferentCouplingModulesPartitionMultiplier = 5; + static const int afferentCouplingModulesPartitionMultiplier = 5; }; } // parser diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 55a390147..3edbca7a1 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -557,12 +557,12 @@ void CppMetricsParser::efferentModuleLevel() { util::OdbTransaction{_ctx.db}([&, this] { - typedef odb::query TypeDependencyQuery; - typedef model::CppTypeDependencyMetricsPathViewDistinctCount TypeDependencyResult; + typedef odb::query TypeDependencyQuery; + typedef model::CppTypeDependencyMetrics_Distinct_D_Count TypeDependencyResult; for (const model::File& file : tasks) { - TypeDependencyResult types = _ctx.db->query_value( + TypeDependencyResult types = _ctx.db->query_value( TypeDependencyQuery::EntityFile::path.like(file.path + '%') && !TypeDependencyQuery::DependencyFile::path.like(file.path + '%')); @@ -576,6 +576,35 @@ void CppMetricsParser::efferentModuleLevel() }); } +void CppMetricsParser::afferentModuleLevel() +{ + parallelCalcMetric( + "Afferent coupling at module level", + _threadCount * afferentCouplingModulesPartitionMultiplier,// number of jobs; adjust for granularity + getModulePathsQuery(), + [&, this](const MetricsTasks& tasks) + { + util::OdbTransaction{_ctx.db}([&, this] + { + typedef odb::query TypeDependencyQuery; + typedef model::CppTypeDependencyMetrics_Distinct_E_Count TypeDependencyResult; + + for (const model::File& file : tasks) + { + TypeDependencyResult types = _ctx.db->query_value( + !TypeDependencyQuery::EntityFile::path.like(file.path + '%') && + TypeDependencyQuery::DependencyFile::path.like(file.path + '%')); + + model::CppFileMetrics metric; + metric.file = file.id; + metric.type = model::CppFileMetrics::Type::AFFERENT_MODULE; + metric.value = types.count; + _ctx.db->persist(metric); + } + }); + }); +} + bool CppMetricsParser::parse() { LOG(info) << "[cppmetricsparser] Computing function parameter count metric."; @@ -594,6 +623,8 @@ bool CppMetricsParser::parse() afferentTypeLevel(); LOG(info) << "[cppmetricsparser] Computing efferent coupling metric at module level."; efferentModuleLevel(); // This metric needs to be calculated after efferentTypeLevel + LOG(info) << "[cppmetricsparser] Computing afferent coupling metric at module level."; + afferentModuleLevel(); // This metric needs to be calculated after afferentTypeLevel return true; } diff --git a/plugins/cpp_metrics/test/sources/parser/module_d/d1.h b/plugins/cpp_metrics/test/sources/parser/module_d/d1.h new file mode 100644 index 000000000..4c0e91ea1 --- /dev/null +++ b/plugins/cpp_metrics/test/sources/parser/module_d/d1.h @@ -0,0 +1,15 @@ +#ifndef CC_CPP_MODULE_METRICS_TEST_D1 +#define CC_CPP_MODULE_METRICS_TEST_D1 + +#include "../module_c/c1.h" +#include "../module_c/c2.h" + +namespace CC_CPP_MODULE_METRICS_TEST +{ +class D1 { + C1 c1; + C2 c2; +}; +} + +#endif diff --git a/plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp b/plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp index 9c6e16c34..5080981b8 100644 --- a/plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp +++ b/plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp @@ -4,3 +4,4 @@ #include "./module_b/b2.h" #include "./module_c/c1.h" #include "./module_c/c2.h" +#include "./module_d/d1.h" diff --git a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp index 3ab40e57b..095c40d54 100644 --- a/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp +++ b/plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp @@ -330,9 +330,10 @@ class ParameterizedEfferentModuleCouplingTest {}; std::vector paramEfferentModule = { - {"%/test/sources/parser/module_a", 1}, + {"%/test/sources/parser/module_a", 1}, // B1 {"%/test/sources/parser/module_b", 0}, - {"%/test/sources/parser/module_c", 2}, + {"%/test/sources/parser/module_c", 2}, // A2, B1 + {"%/test/sources/parser/module_d", 2}, // C1, C2 }; TEST_P(ParameterizedEfferentModuleCouplingTest, ModuleEfferentTest) { @@ -353,3 +354,36 @@ INSTANTIATE_TEST_SUITE_P( ParameterizedEfferentModuleCouplingTest, ::testing::ValuesIn(paramEfferentModule) ); + +// Afferent coupling at module level + +class ParameterizedAfferentModuleCouplingTest + : public CppMetricsParserTest, + public ::testing::WithParamInterface +{}; + +std::vector paramAfferentModule = { + {"%/test/sources/parser/module_a", 1}, // C2 + {"%/test/sources/parser/module_b", 3}, // A2, C1, C2 + {"%/test/sources/parser/module_c", 1}, // D1 + {"%/test/sources/parser/module_d", 0}, +}; + +TEST_P(ParameterizedAfferentModuleCouplingTest, ModuleAfferentTest) { + _transaction([&, this]() { + + typedef odb::query CppModuleMetricsQuery; + + const auto metric = _db->query_value( + CppModuleMetricsQuery::CppFileMetrics::type == model::CppFileMetrics::Type::AFFERENT_MODULE && + CppModuleMetricsQuery::File::path.like(GetParam().first)); + + EXPECT_EQ(GetParam().second, metric.value); + }); +} + +INSTANTIATE_TEST_SUITE_P( + ParameterizedAfferentModuleCouplingTestSuite, + ParameterizedAfferentModuleCouplingTest, + ::testing::ValuesIn(paramAfferentModule) +);