From b32d109551148f8216fb666afeef8c4ed4834532 Mon Sep 17 00:00:00 2001 From: Luni-4 Date: Wed, 5 Aug 2020 15:03:00 +0200 Subject: [PATCH 1/2] Add a new type of cyclomatic complexity --- src/metrics/cyclomatic.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/metrics/cyclomatic.rs b/src/metrics/cyclomatic.rs index 611fbedfa..049cbc7cd 100644 --- a/src/metrics/cyclomatic.rs +++ b/src/metrics/cyclomatic.rs @@ -1,4 +1,4 @@ -use serde::ser::Serializer; +use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::fmt; @@ -26,13 +26,21 @@ impl Serialize for Stats { where S: Serializer, { - serializer.serialize_f64(self.cyclomatic()) + let mut st = serializer.serialize_struct("cyclomatic", 2)?; + st.serialize_field("sum", &self.cyclomatic())?; + st.serialize_field("average", &self.cyclomatic_average())?; + st.end() } } impl fmt::Display for Stats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.cyclomatic) + write!( + f, + "sum: {}, average: {}", + self.cyclomatic(), + self.cyclomatic_average() + ) } } @@ -45,7 +53,15 @@ impl Stats { /// Returns the `Cyclomatic` metric value pub fn cyclomatic(&self) -> f64 { - self.cyclomatic / self.n as f64 + self.cyclomatic + } + + /// Returns the `Cyclomatic` metric average value + /// + /// This value is computed dividing the `Cyclomatic` value for the + /// number of spaces. + pub fn cyclomatic_average(&self) -> f64 { + self.cyclomatic() / self.n as f64 } } From 553b60a3b679c36e01f6d32fa9836dafd26a4ccc Mon Sep 17 00:00:00 2001 From: Luni-4 Date: Wed, 5 Aug 2020 16:54:36 +0200 Subject: [PATCH 2/2] Add cyclomatic tests --- rust-code-analysis-cli/src/web/server.rs | 10 +- src/metrics/cyclomatic.rs | 114 +++++++++++++++++++++++ 2 files changed, 119 insertions(+), 5 deletions(-) diff --git a/rust-code-analysis-cli/src/web/server.rs b/rust-code-analysis-cli/src/web/server.rs index acc4406e5..1e711b07d 100644 --- a/rust-code-analysis-cli/src/web/server.rs +++ b/rust-code-analysis-cli/src/web/server.rs @@ -631,7 +631,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 4, - "metrics": {"cyclomatic": 1.0, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "nargs": 0., "nexits": 0., "halstead": {"bugs": 0.000_942_552_557_372_941_4, @@ -657,7 +657,7 @@ mod tests { "spaces": [{"kind": "function", "start_line": 3, "end_line": 4, - "metrics": {"cyclomatic": 1.0, + "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, "nargs": 0., "nexits": 0., "halstead": {"bugs": 0.000_942_552_557_372_941_4, @@ -709,7 +709,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": 1.0, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "nargs": 0., "nexits": 0., "halstead": {"bugs": 0.000_942_552_557_372_941_4, @@ -757,7 +757,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": 1.0, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "nargs": 0., "nexits": 0., "halstead": {"bugs": 0.000_942_552_557_372_941_4, @@ -783,7 +783,7 @@ mod tests { "spaces": [{"kind": "function", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": 1.0, + "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, "nargs": 0., "nexits": 0., "halstead": {"bugs": 0.000_942_552_557_372_941_4, diff --git a/src/metrics/cyclomatic.rs b/src/metrics/cyclomatic.rs index 049cbc7cd..f0707b415 100644 --- a/src/metrics/cyclomatic.rs +++ b/src/metrics/cyclomatic.rs @@ -176,3 +176,117 @@ impl Cyclomatic for JavaCode {} impl Cyclomatic for GoCode {} impl Cyclomatic for CssCode {} impl Cyclomatic for HtmlCode {} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn test_cyclomatic() { + check_metrics!( + "def f(a, b): # +2 (+1 unit space) + if a and b: # +2 (+1 and) + return 1 + if c and d: # +2 (+1 and) + return 1\n", + "foo.py", + PythonParser, + cyclomatic, + [ + (cyclomatic, 6, usize), + (cyclomatic_average, 3, usize) // nspace = 2 (func and unit) + ] + ); + } + + #[test] + fn test_1_level_nesting_cyclomatic() { + check_metrics!( + "def f(a, b): # +2 (+1 unit space) + if a: # +1 + for i in range(b): # +1 + return 1\n", + "foo.py", + PythonParser, + cyclomatic, + [ + (cyclomatic, 4, usize), + (cyclomatic_average, 2, usize) // nspace = 2 (func and unit) + ] + ); + + check_metrics!( + "fn f() { // +2 (+1 unit space) + if true { // +1 + match true { + true => println!(\"test\"), // +1 + false => println!(\"test\"), // +1 + } + } + }\n", + "foo.rs", + RustParser, + cyclomatic, + [ + (cyclomatic, 5, usize), + (cyclomatic_average, 2, usize) // nspace = 2 (func and unit) + ] + ); + } + + #[test] + fn test_c_switch_cyclomatic() { + check_metrics!( + "void f() { // +2 (+1 unit space) + switch (1) { + case 1: // +1 + printf(\"one\"); + break; + case 2: // +1 + printf(\"two\"); + break; + case 3: // +1 + printf(\"three\"); + break; + default: + printf(\"all\"); + break; + } + }\n", + "foo.c", + CppParser, + cyclomatic, + [ + (cyclomatic, 5, usize), + (cyclomatic_average, 2, usize) // nspace = 2 (func and unit) + ] + ); + } + + #[test] + fn test_real_cyclomatic() { + check_metrics!( + "int sumOfPrimes(int max) { // +2 (+1 unit space) + int total = 0; + OUT: for (int i = 1; i <= max; ++i) { // +1 + for (int j = 2; j < i; ++j) { // +1 + if (i % j == 0) { // +1 + continue OUT; + } + } + total += i; + } + return total; + }\n", + "foo.c", + CppParser, + cyclomatic, + [ + (cyclomatic, 5, usize), + (cyclomatic_average, 2, usize) // nspace = 2 (func and unit) + ] + ); + } +}