Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions plugins/cpp_metrics/model/include/model/cppcohesionmetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ struct CohesionCppRecordView
CppAstNodeId astNodeId;
};

#pragma db view \
object(CppRecord) \
object(CppAstNode : CppRecord::astNodeId == CppAstNode::id) \
object(File : CppAstNode::location.file)
struct CohesionCppRecord_Count
{
#pragma db column("count(" + CppEntity::id + ")")
std::size_t count;
};

#pragma db view \
object(CppMemberType) \
object(CppAstNode : CppMemberType::memberAstNode) \
Expand Down
9 changes: 5 additions & 4 deletions plugins/cpp_metrics/model/include/model/cppfilemetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ struct CppFileMetrics
{
enum Type
{
EFFERENT_MODULE,
AFFERENT_MODULE
EFFERENT_MODULE = 1,
AFFERENT_MODULE = 2,
RELATIONAL_COHESION_MODULE = 3
};

#pragma db id auto
Expand All @@ -27,7 +28,7 @@ struct CppFileMetrics
Type type;

#pragma db not_null
unsigned value;
double value;
};

#pragma db view \
Expand All @@ -45,7 +46,7 @@ struct CppModuleMetricsForPathView
CppFileMetrics::Type type;

#pragma db column(CppFileMetrics::value)
unsigned value;
double value;
};

#pragma db view \
Expand Down
14 changes: 14 additions & 0 deletions plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ struct CppTypeDependencyMetricsPathView
std::string dependencyPath;
};

#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_Count
{
#pragma db column("count(" + CppTypeDependencyMetrics::id + ")")
std::size_t count;
};

#pragma db view \
object(CppTypeDependencyMetrics) \
object(CppAstNode = EntityAstNode : CppTypeDependencyMetrics::entityHash == EntityAstNode::entityHash \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ class CppMetricsParser : public AbstractParser
void efferentModuleLevel();
// Calculate the afferent coupling at module level.
void afferentModuleLevel();
// Calculate relational cohesion at module level.
void relationalCohesionModuleLevel();
// Returns module path query based on parser configuration.
odb::query<model::File> getModulePathsQuery();

Expand Down Expand Up @@ -210,6 +212,7 @@ class CppMetricsParser : public AbstractParser
static const int afferentCouplingTypesPartitionMultiplier = 5;
static const int efferentCouplingModulesPartitionMultiplier = 5;
static const int afferentCouplingModulesPartitionMultiplier = 5;
static const int relationalCohesionPartitionMultiplier = 5;
};

} // parser
Expand Down
51 changes: 49 additions & 2 deletions plugins/cpp_metrics/parser/src/cppmetricsparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,6 @@ void CppMetricsParser::efferentTypeLevel()
{
typedef odb::query<cc::model::CppMemberType> MemTypeQuery;
typedef odb::query<cc::model::CppInheritanceCount> InheritanceQuery;
typedef odb::query<cc::model::CppFunctionParamTypeView> ParamQuery;
typedef odb::query<cc::model::CppFunctionLocalTypeView> LocalQuery;
typedef odb::query<cc::model::CppFunction> FuncQuery;

std::set<std::uint64_t> dependentTypes;
Expand Down Expand Up @@ -605,6 +603,53 @@ void CppMetricsParser::afferentModuleLevel()
});
}

void CppMetricsParser::relationalCohesionModuleLevel()
{
// Compute relational cohesion defined by CppDepend:
// https://www.cppdepend.com/documentation/code-metrics#RelationalCohesion

parallelCalcMetric<model::File>(
"Relational cohesion at module level",
_threadCount * relationalCohesionPartitionMultiplier, // number of jobs; adjust for granularity
getModulePathsQuery(),
[&, this](const MetricsTasks<model::File>& tasks)
{
util::OdbTransaction{_ctx.db}([&, this]
{
typedef odb::query<model::CppTypeDependencyMetrics_Count> TypeDependencyQuery;
typedef model::CppTypeDependencyMetrics_Count TypeDependencyResult;
typedef odb::query<model::CohesionCppRecord_Count> RecordQuery;
typedef model::CohesionCppRecord_Count RecordResult;

for (const model::File& file : tasks)
{
TypeDependencyResult dependencies = _ctx.db->query_value<model::CppTypeDependencyMetrics_Count>(
TypeDependencyQuery::EntityFile::path.like(file.path + '%') &&
TypeDependencyQuery::DependencyFile::path.like(file.path + '%'));

// Let R be the number of type relationships that are internal to a module
// (i.e that do not connect to types outside the module)
const std::size_t r = dependencies.count;

RecordResult types = _ctx.db->query_value<model::CohesionCppRecord_Count>(
RecordQuery::File::path.like(file.path + '%'));

// Let N be the number of types within the module.
const std::size_t n = types.count;

// Relational cohesion
const double h = (n != 0) ? (double)(r + 1) / n : 0;

model::CppFileMetrics metric;
metric.file = file.id;
metric.type = model::CppFileMetrics::Type::RELATIONAL_COHESION_MODULE;
metric.value = h;
_ctx.db->persist(metric);
}
});
});
}

bool CppMetricsParser::parse()
{
LOG(info) << "[cppmetricsparser] Computing function parameter count metric.";
Expand All @@ -625,6 +670,8 @@ bool CppMetricsParser::parse()
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
LOG(info) << "[cppmetricsparser] Computing relational cohesion metric at module level.";
relationalCohesionModuleLevel(); // This metric needs to be calculated after efferentTypeLevel
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion plugins/cpp_metrics/service/cxxmetrics.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ enum CppAstNodeMetricsType

enum CppModuleMetricsType
{
Placeholder = 1
EfferentModule = 1,
AfferentModule = 2,
RelationalCohesionModule = 3
}

struct CppAstNodeMetricsTypeName
Expand Down
12 changes: 10 additions & 2 deletions plugins/cpp_metrics/service/src/cppmetricsservice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,16 @@ void CppMetricsServiceHandler::getCppModuleMetricsTypeNames(
{
CppModuleMetricsTypeName typeName;

typeName.type = CppModuleMetricsType::Placeholder;
typeName.name = "Placeholder";
typeName.type = CppModuleMetricsType::EfferentModule;
typeName.name = "Efferent coupling of module";
_return.push_back(typeName);

typeName.type = CppModuleMetricsType::AfferentModule;
typeName.name = "Afferent coupling of module";
_return.push_back(typeName);

typeName.type = CppModuleMetricsType::RelationalCohesionModule;
typeName.name = "Relational cohesion of module";
_return.push_back(typeName);
}

Expand Down
9 changes: 9 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@
#include "./module_c/c1.h"
#include "./module_c/c2.h"
#include "./module_d/d1.h"

#include "./rc_module_a/a1.h"
#include "./rc_module_a/a2.h"
#include "./rc_module_a/a3.h"
#include "./rc_module_b/b1.h"
#include "./rc_module_c/c1.h"
#include "./rc_module_c/c2.h"
#include "./rc_module_c/c3.h"
#include "./rc_module_c/c4.h"
15 changes: 15 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_a/a1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_A1
#define CC_CPP_RC_MODULE_METRICS_TEST_A1

#include "./a2.h"
#include "./a3.h"

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class A1 {
A2 a2;
A3 a3;
};
}

#endif
13 changes: 13 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_a/a2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_A2
#define CC_CPP_RC_MODULE_METRICS_TEST_A2

#include "./a3.h"

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class A2 {
A3 a3;
};
}

#endif
9 changes: 9 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_a/a3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_A3
#define CC_CPP_RC_MODULE_METRICS_TEST_A3

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class A3 {};
}

#endif
9 changes: 9 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_b/b1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_B1
#define CC_CPP_RC_MODULE_METRICS_TEST_B1

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class B1 {};
}

#endif
17 changes: 17 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_c/c1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_C1
#define CC_CPP_RC_MODULE_METRICS_TEST_C1

#include "../rc_module_a/a1.h"
#include "../rc_module_a/a2.h"
#include "../rc_module_a/a3.h"

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class C1 {
A1 a1;
A2 a2;
A3 a3;
};
}

#endif
13 changes: 13 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_c/c2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_C2
#define CC_CPP_RC_MODULE_METRICS_TEST_C2

#include "./c1.h"

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class C2 {
C1 c1;
};
}

#endif
9 changes: 9 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_c/c3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_C3
#define CC_CPP_RC_MODULE_METRICS_TEST_C3

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class C3 {};
}

#endif
9 changes: 9 additions & 0 deletions plugins/cpp_metrics/test/sources/parser/rc_module_c/c4.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_C4
#define CC_CPP_RC_MODULE_METRICS_TEST_C4

namespace CC_CPP_RC_MODULE_METRICS_TEST
{
class C4 {};
}

#endif
39 changes: 39 additions & 0 deletions plugins/cpp_metrics/test/src/cppmetricsparsertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ using namespace cc;
extern char* dbConnectionString;

typedef std::pair<std::string, unsigned int> StringUintParam;
typedef std::pair<std::string, double> StringDoubleParam;

class CppMetricsParserTest : public ::testing::Test
{
Expand Down Expand Up @@ -387,3 +388,41 @@ INSTANTIATE_TEST_SUITE_P(
ParameterizedAfferentModuleCouplingTest,
::testing::ValuesIn(paramAfferentModule)
);

// Relational cohesion at module level

class ParameterizedRelationalCohesionTest
: public CppMetricsParserTest,
public ::testing::WithParamInterface<StringDoubleParam>
{};

// Relational cohesion formula:
// H = (R + 1)/ N
// where R is the number of type relationships that are internal to a module,
// N is the number of types within a module.

std::vector<StringDoubleParam> paramRelationalCohesion = {
{"%/test/sources/parser/rc_module_a", (3 + 1) / 3.0},
{"%/test/sources/parser/rc_module_b", (0 + 1) / 1.0},
{"%/test/sources/parser/rc_module_c", (1 + 1) / 4.0},
};

TEST_P(ParameterizedRelationalCohesionTest, RelationalCohesionTest) {
_transaction([&, this]() {

typedef odb::query<model::CppModuleMetricsForPathView> CppModuleMetricsQuery;

const auto metric = _db->query_value<model::CppModuleMetricsForPathView>(
CppModuleMetricsQuery::CppFileMetrics::type == model::CppFileMetrics::Type::RELATIONAL_COHESION_MODULE &&
CppModuleMetricsQuery::File::path.like(GetParam().first));

constexpr double tolerance = 1e-8;
EXPECT_NEAR(GetParam().second, metric.value, tolerance);
});
}

INSTANTIATE_TEST_SUITE_P(
ParameterizedRelationalCohesionTestSuite,
ParameterizedRelationalCohesionTest,
::testing::ValuesIn(paramRelationalCohesion)
);