use std::cell::Cell;
use crate::test_support::check_func_space_only;
use crate::*;
#[cfg(feature = "elixir")]
type Halstead = [u64; 4];
#[cfg(feature = "elixir")]
type Cyclomatic = [u64; 2];
#[cfg(feature = "elixir")]
fn measure(source: &str) -> (Halstead, Cyclomatic) {
let out = Cell::new(([0; 4], [0; 2]));
check_func_space_only::<ElixirParser, _>(
&format!("{source}\n"),
"foo.exs",
&[Metric::Halstead, Metric::Cyclomatic],
|space| {
let m = &space.metrics;
out.set((
[
m.halstead.unique_operators(),
m.halstead.total_operators(),
m.halstead.unique_operands(),
m.halstead.total_operands(),
],
[
m.cyclomatic.cyclomatic_sum(),
m.cyclomatic.cyclomatic_modified_sum(),
],
));
},
);
out.get()
}
#[cfg(feature = "elixir")]
fn names_an_operator(source: &str) -> bool {
let parser = ElixirParser::new(
source.as_bytes().to_vec(),
std::path::Path::new("foo.exs"),
None,
);
crate::test_support::ast_has_kind_id(&parser, Elixir::OperatorIdentifier as u16)
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_named_operator_scores_like_a_named_function() {
let rows: [(&str, &str, Halstead); 15] = [
("a = &foo/2", "a = &==/2", [3, 3, 3, 3]),
("a = &foo/2", "a = &in/2", [3, 3, 3, 3]),
("a = &foo/2", "a = ¬ in/2", [3, 3, 3, 3]),
("a = &foo/2", "a = &and/2", [3, 3, 3, 3]),
("a = &foo/2", "a = &or/2", [3, 3, 3, 3]),
("a = &foo/2", "a = &||/2", [3, 3, 3, 3]),
("a = &foo/1", "a = ¬/1", [3, 3, 3, 3]),
("a = &foo/1", "a = &!/1", [3, 3, 3, 3]),
("a = &foo/2", "a = &+/2", [3, 3, 3, 3]),
("a = Kernel.foo(x, y)", "a = Kernel.==(x, y)", [4, 4, 5, 5]),
("a = &Kernel.foo/2", "a = &Kernel.||/2", [4, 4, 4, 4]),
("a = foo", "a = ..", [1, 1, 2, 2]),
("a = &foo/0", "a = &../0", [3, 3, 3, 3]),
("a = foo/2", "a = &&&/2", [2, 2, 3, 3]),
("a = foo/1", "a = &&/1", [2, 2, 3, 3]),
];
for (twin, named, halstead) in rows {
assert!(
names_an_operator(named),
"`{named}` lost its operator_identifier"
);
let want = (halstead, [1, 1]);
assert_eq!(measure(twin), want, "`{twin}` moved; re-derive the row");
assert_eq!(measure(named), want, "`{named}` must score like `{twin}`");
}
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_named_operators_are_distinct_operands() {
let (halstead, _) = measure("a = &==/2\nb = &</2");
assert_eq!(halstead[2..], [5, 6]);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_applied_operator_keeps_its_operator() {
let rows: [(&str, Halstead, Cyclomatic); 11] = [
("a = x == y", [2, 2, 3, 3], [1, 1]),
("a = x in y", [2, 2, 3, 3], [1, 1]),
("a = not x", [2, 2, 2, 2], [1, 1]),
("a = x and y", [2, 2, 3, 3], [2, 2]),
("a = x || y", [2, 2, 3, 3], [2, 2]),
("a = Kernel.||(x, y)", [4, 4, 5, 5], [2, 2]),
("a = Kernel.&&(x, y)", [4, 4, 5, 5], [2, 2]),
("a = x |> Kernel.||(y)", [4, 4, 5, 5], [2, 2]),
("a = Elixir.Kernel.||(x, y)", [4, 4, 5, 5], [2, 2]),
("a = Foo.||(x, y)", [4, 4, 5, 5], [1, 1]),
("a = x |> Foo.||(y)", [4, 4, 5, 5], [1, 1]),
];
for (source, halstead, cyclomatic) in rows {
assert_eq!(measure(source), (halstead, cyclomatic), "`{source}`");
}
}