use crate::test_support::metrics_verbatim;
use crate::*;
#[cfg(feature = "elixir")]
type Scores = [u64; 4];
#[cfg(feature = "elixir")]
fn measure(source: &str) -> Scores {
let m = metrics_verbatim(
LANG::Elixir,
format!("{source}\n").as_bytes(),
MetricsOptions::default().with_only(&[Metric::Cyclomatic, Metric::Abc, Metric::Cognitive]),
);
[
m.cyclomatic.cyclomatic_sum(),
m.cyclomatic.cyclomatic_modified_sum(),
m.abc.conditions_sum(),
m.cognitive.cognitive_sum(),
]
}
#[cfg(feature = "elixir")]
fn assert_group(twin: &str, want: Scores, spellings: &[&str]) {
assert_eq!(
measure(twin),
want,
"twin `{twin}` moved; re-derive the group"
);
for spelling in spellings {
assert_eq!(
measure(spelling),
want,
"`{spelling}` must score like `{twin}`"
);
}
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_kernel_remote_call_scores_like_the_operator() {
assert_group(
"a || b",
[2, 2, 2, 1],
&[
"Kernel.||(a, b)",
"Elixir.Kernel.||(a, b)",
"a |> Kernel.||(b)",
],
);
assert_group("a && b", [2, 2, 2, 1], &["Kernel.&&(a, b)"]);
assert_group(
"a || (b || c)",
[3, 3, 3, 1],
&[
"Kernel.||(a, Kernel.||(b, c))",
"a || Kernel.||(b, c)",
"Kernel.||(a, b || c)",
],
);
assert_group(
"a && (b || c)",
[3, 3, 3, 2],
&["Kernel.&&(a, Kernel.||(b, c))"],
);
assert_group("(a || b) |> f()", [2, 2, 2, 1], &["Kernel.||(a, b) |> f()"]);
assert_group(
"if a || b, do: 1",
[3, 3, 2, 2],
&["if Kernel.||(a, b), do: 1"],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_kernel_word_operator_scores_like_the_operator() {
assert_group(
"a and b",
[2, 2, 2, 1],
&[
"Kernel.and(a, b)",
"Elixir.Kernel.and(a, b)",
"a |> Kernel.and(b)",
],
);
assert_group("a or b", [2, 2, 2, 1], &["Kernel.or(a, b)"]);
assert_group(
"a or (b or c)",
[3, 3, 3, 1],
&["Kernel.or(a, Kernel.or(b, c))", "a or Kernel.||(b, c)"],
);
assert_group(
"a and (b or c)",
[3, 3, 3, 2],
&["Kernel.and(a, Kernel.or(b, c))"],
);
assert_group(
"if a and b, do: 1",
[3, 3, 2, 2],
&["if Kernel.and(a, b), do: 1"],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_kernel_negation_and_foreign_word_operator_decide_nothing() {
assert_group(
"not x",
[1, 1, 0, 0],
&["!x", "Kernel.not(x)", "Kernel.!(x)"],
);
assert_group(
"Foo.foo(a, b)",
[1, 1, 0, 0],
&[
"Foo.and(a, b)",
"Foo.or(a, b)",
"&Kernel.and/2",
"Kernel.and",
],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_non_kernel_remote_call_decides_nothing() {
assert_group(
"Foo.foo(a, b)",
[1, 1, 0, 0],
&["Foo.||(a, b)", "a |> Foo.||(b)", "&Kernel.||/2"],
);
assert_group(
"if foo(a), do: 1",
[2, 2, 1, 1],
&["if Kernel.foo(a), do: 1", "if Foo.||(a, b), do: 1"],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_if_else_scores_alike_in_every_spelling() {
assert_group(
"if c do 1 else 2 end",
[2, 2, 2, 2],
&[
"if(c, do: 1, else: 2)",
"if(c, [do: 1, else: 2])",
"if(c, [do: 1, else: 2,])",
"if(c, \"do\": 1, \"else\": 2)",
"if(c, [\"do\": 1, \"else\": 2])",
],
);
assert_group(
"unless c do 1 else 2 end",
[2, 2, 2, 2],
&["unless(c, do: 1, else: 2)", "unless(c, [do: 1, else: 2])"],
);
assert_group(
"if a do (if b do 1 else 2 end) else 3 end",
[3, 3, 4, 5],
&[
"if(a, do: if(b, do: 1, else: 2), else: 3)",
"if(a, [do: if(b, [do: 1, else: 2]), else: 3])",
],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_list_that_is_not_a_keyword_list_has_no_sections() {
assert_group("[1]", [1, 1, 0, 0], &["[else: 1]", "[do: 1, else: 2]"]);
assert_group(
"if(c, x)",
[2, 2, 1, 1],
&[
"if([do: 1, else: 2], c)",
"if(c, [1, do: 1, else: 2])",
"if(c, [])",
],
);
assert_group(
"f(x, y)",
[1, 1, 0, 0],
&["f(x, else: 1)", "f(x, [else: 1])"],
);
assert_group(
"Foo.bar(x, y)",
[1, 1, 0, 0],
&["Foo.bar(x, else: 1)", "Foo.bar(x, [else: 1])"],
);
assert_group("f(x, y)", [1, 1, 0, 0], &["f(x, \"else#{y}\": 1)"]);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_clause_constructs_score_alike_in_every_spelling() {
assert_group(
"case x do 1 -> :a; 2 -> :b end",
[3, 2, 2, 1],
&[
"case(x, do: (1 -> :a; 2 -> :b))",
"case(x, [do: (1 -> :a; 2 -> :b)])",
],
);
assert_group(
"cond do x > 1 -> :a; true -> :b end",
[2, 2, 1, 1],
&[
"cond(do: (x > 1 -> :a; true -> :b))",
"cond([do: (x > 1 -> :a; true -> :b)])",
],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_try_sections_score_alike_in_every_spelling() {
assert_group(
"try do f() rescue e in RuntimeError -> e catch :x -> 1 end",
[3, 2, 2, 2],
&[
"try(do: f(), rescue: (e in RuntimeError -> e), catch: (:x -> 1))",
"try([do: f(), rescue: (e in RuntimeError -> e), catch: (:x -> 1)])",
],
);
assert_group(
"try do f() rescue _ -> 1 else x -> x end",
[2, 2, 1, 2],
&[
"try(do: f(), rescue: (_ -> 1), else: (x -> x))",
"try([do: f(), rescue: (_ -> 1), else: (x -> x)])",
],
);
assert_group(
"try do f() rescue _ -> (if a do 1 end) end",
[2, 3, 1, 3],
&[
"try(do: f(), rescue: (_ -> if(a, do: 1)))",
"try([do: f(), rescue: (_ -> if(a, do: 1))])",
],
);
assert_group(
"try do f() catch _ -> (if a do 1 end) end",
[2, 3, 1, 3],
&["try(do: f(), catch: (_ -> if(a, do: 1)))"],
);
assert_group(
"def f() do\n g()\nrescue\n e -> e\nend",
[3, 2, 1, 1],
&["def f(), do: g(), rescue: (e -> e)"],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_with_else_scores_like_case() {
assert_eq!(
measure("case g(x) do {:ok, a} -> a; {:error, e} -> e; :z -> 0 end")[3],
1
);
assert_group(
"with({:ok, a} <- g(x), do: a, else: ({:error, e} -> e; :z -> 0))",
[4, 3, 3, 1],
&[
"with {:ok, a} <- g(x) do a else {:error, e} -> e; :z -> 0 end",
"with({:ok, a} <- g(x), [do: a, else: ({:error, e} -> e; :z -> 0)])",
],
);
assert_group(
"with({:ok, a} <- g(x), do: a, else: (_ -> 0))",
[2, 2, 1, 1],
&[
"with {:ok, a} <- g(x) do a else _ -> 0 end",
"with({:ok, a} <- g(x), [do: a, else: (_ -> 0)])",
],
);
assert_group(
"with({:ok, a} <- g(x), do: if(a, do: 1, else: 2), else: (_ -> 0))",
[3, 3, 3, 4],
&["with {:ok, a} <- g(x) do (if a do 1 else 2 end) else _ -> 0 end"],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_for_reduce_head_is_free_in_every_spelling() {
assert_group(
"for x <- xs, reduce: 0 do acc -> acc + x end",
[2, 2, 0, 1],
&[
"for(x <- xs, reduce: 0, do: (acc -> acc + x))",
"for(x <- xs, [reduce: 0, do: (acc -> acc + x)])",
],
);
assert_group(
"for x <- xs, reduce: 0 do 0 -> x; acc -> acc + x end",
[3, 2, 1, 1],
&[
"for(x <- xs, reduce: 0, do: (0 -> x; acc -> acc + x))",
"for(x <- xs, [reduce: 0, do: (0 -> x; acc -> acc + x)])",
],
);
}
#[cfg(feature = "elixir")]
#[test]
fn elixir_receive_scores_like_case() {
assert_group(
"case x do {:a, y} -> y; :b -> 0 end",
[3, 2, 2, 1],
&[
"receive do {:a, y} -> y; :b -> 0 end",
"receive do {:a, y} -> y; :b -> 0 after 10 -> :t end",
"receive(do: ({:a, y} -> y; :b -> 0), after: (10 -> :t))",
"receive([do: ({:a, y} -> y; :b -> 0), after: (10 -> :t)])",
],
);
assert_group(
"case x do {:a, y} -> (if y do 1 end) end",
[3, 3, 2, 3],
&[
"receive do {:a, y} -> (if y do 1 end) after 10 -> :t end",
"receive(do: ({:a, y} -> if(y, do: 1)), after: (10 -> :t))",
],
);
assert_group(
"case x do {:a, y} -> y; :b -> 0; _ -> 1 end",
[3, 2, 2, 1],
&[
"receive do {:a, y} -> y; _ -> 0 after 10 -> :t end",
"receive(do: ({:a, y} -> y; _ -> 0), after: (10 -> :t))",
"receive([do: ({:a, y} -> y; _ -> 0), after: (10 -> :t)])",
],
);
assert_group(
"case x do _ -> 0 end",
[1, 2, 0, 1],
&["receive do _ -> 0 end", "receive(do: (_ -> 0))"],
);
assert_group(
"Process.sleep(10)",
[1, 1, 0, 0],
&[
"receive do after 10 -> :t end",
"receive(after: (10 -> :t))",
"receive([after: (10 -> :t)])",
],
);
}