#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use serde::Serialize;
use serde::ser::{SerializeStruct, Serializer};
use std::fmt;
use crate::checker::Checker;
use crate::langs::*;
use crate::macros::implement_metric_trait;
use crate::metrics::npa::ts_member_is_public;
use crate::node::Node;
use crate::*;
#[derive(Clone, Debug, Default)]
pub struct Stats {
class_npm: usize,
interface_npm: usize,
class_nm: usize,
interface_nm: usize,
class_npm_sum: usize,
interface_npm_sum: usize,
class_nm_sum: usize,
interface_nm_sum: usize,
is_class_space: bool,
}
impl Serialize for Stats {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut st = serializer.serialize_struct("npm", 9)?;
st.serialize_field("classes", &self.class_npm_sum())?;
st.serialize_field("interfaces", &self.interface_npm_sum())?;
st.serialize_field("class_methods", &self.class_nm_sum())?;
st.serialize_field("interface_methods", &self.interface_nm_sum())?;
st.serialize_field("classes_average", &self.class_coa())?;
st.serialize_field("interfaces_average", &self.interface_coa())?;
st.serialize_field("total", &self.total_npm())?;
st.serialize_field("total_methods", &self.total_nm())?;
st.serialize_field("average", &self.total_coa())?;
st.end()
}
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"classes: {}, interfaces: {}, class_methods: {}, interface_methods: {}, classes_average: {}, interfaces_average: {}, total: {}, total_methods: {}, average: {}",
self.class_npm_sum(),
self.interface_npm_sum(),
self.class_nm_sum(),
self.interface_nm_sum(),
self.class_coa(),
self.interface_coa(),
self.total_npm(),
self.total_nm(),
self.total_coa()
)
}
}
impl Stats {
pub fn merge(&mut self, other: &Stats) {
self.class_npm_sum += other.class_npm_sum;
self.interface_npm_sum += other.interface_npm_sum;
self.class_nm_sum += other.class_nm_sum;
self.interface_nm_sum += other.interface_nm_sum;
}
#[inline]
#[must_use]
pub fn class_npm(&self) -> f64 {
self.class_npm as f64
}
#[inline]
#[must_use]
pub fn interface_npm(&self) -> f64 {
self.interface_npm as f64
}
#[inline]
#[must_use]
pub fn class_nm(&self) -> f64 {
self.class_nm as f64
}
#[inline]
#[must_use]
pub fn interface_nm(&self) -> f64 {
self.interface_nm as f64
}
#[inline]
#[must_use]
pub fn class_npm_sum(&self) -> f64 {
self.class_npm_sum as f64
}
#[inline]
#[must_use]
pub fn interface_npm_sum(&self) -> f64 {
self.interface_npm_sum as f64
}
#[inline]
#[must_use]
pub fn class_nm_sum(&self) -> f64 {
self.class_nm_sum as f64
}
#[inline]
#[must_use]
pub fn interface_nm_sum(&self) -> f64 {
self.interface_nm_sum as f64
}
#[inline]
#[must_use]
pub fn class_coa(&self) -> f64 {
self.class_npm_sum() / self.class_nm_sum()
}
#[inline]
#[must_use]
pub fn interface_coa(&self) -> f64 {
if self.interface_npm_sum == self.interface_nm_sum && self.interface_npm_sum != 0 {
1.0
} else {
self.interface_npm_sum() / self.interface_nm_sum()
}
}
#[inline]
#[must_use]
pub fn total_coa(&self) -> f64 {
self.total_npm() / self.total_nm()
}
#[inline]
#[must_use]
pub fn total_npm(&self) -> f64 {
self.class_npm_sum() + self.interface_npm_sum()
}
#[inline]
#[must_use]
pub fn total_nm(&self) -> f64 {
self.class_nm_sum() + self.interface_nm_sum()
}
#[inline]
pub(crate) fn compute_sum(&mut self) {
self.class_npm_sum += self.class_npm;
self.interface_npm_sum += self.interface_npm;
self.class_nm_sum += self.class_nm;
self.interface_nm_sum += self.interface_nm;
}
#[inline]
pub(crate) fn is_disabled(&self) -> bool {
!self.is_class_space
}
}
#[doc(hidden)]
pub trait Npm
where
Self: Checker,
{
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
}
macro_rules! impl_npm_java_like {
($code:ty, $lang:ident) => {
impl Npm for $code {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use $lang::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
match node.kind_id().into() {
ClassBody | EnumBodyDeclarations => {
for method in node.children().filter(|n| Self::is_func(n)) {
stats.class_nm += 1;
if let Some(modifiers) = method.child(0)
&& matches!(modifiers.kind_id().into(), Modifiers)
&& modifiers.first_child(|id| id == Public).is_some()
{
stats.class_npm += 1;
}
}
}
InterfaceBody => {
stats.interface_nm += node.children().filter(|n| Self::is_func(n)).count();
stats.interface_npm = stats.interface_nm;
}
AnnotationTypeBody => {
stats.interface_nm += node
.children()
.filter(|n| {
matches!(n.kind_id().into(), AnnotationTypeElementDeclaration)
})
.count();
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
}
};
}
impl_npm_java_like!(JavaCode, Java);
impl Npm for GroovyCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use crate::metrics::npa::{groovy_body_is_interface_like, groovy_has_explicit_public};
use Groovy::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
match node.kind_id().into() {
ClassBody | EnumBody => {
let is_interface_like = groovy_body_is_interface_like(node);
for method in node.children().filter(|n| Self::is_func(n)) {
if is_interface_like {
stats.interface_nm += 1;
stats.interface_npm += 1;
} else {
stats.class_nm += 1;
if groovy_has_explicit_public(&method) {
stats.class_npm += 1;
}
}
}
}
_ => {}
}
}
}
fn csharp_count_member(member: &Node) -> usize {
use Csharp::*;
match member.kind_id().into() {
MethodDeclaration
| ConstructorDeclaration
| DestructorDeclaration
| OperatorDeclaration
| ConversionOperatorDeclaration => 1,
PropertyDeclaration | IndexerDeclaration => member
.children()
.filter(|c| matches!(c.kind_id().into(), AccessorList))
.flat_map(|c| c.children())
.filter(|c| matches!(c.kind_id().into(), AccessorDeclaration))
.count()
.max(1),
_ => 0,
}
}
impl Npm for CsharpCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Csharp::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), DeclarationList) {
return;
}
let Some(parent_kind) = node.parent().map(|p| p.kind_id().into()) else {
return;
};
match parent_kind {
ClassDeclaration | StructDeclaration | RecordDeclaration => {
for member in node.children() {
let count = csharp_count_member(&member);
stats.class_nm += count;
if super::npa::csharp_is_explicit_public(&member) {
stats.class_npm += count;
}
}
}
InterfaceDeclaration => {
for member in node.children() {
stats.interface_nm += csharp_count_member(&member);
}
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
}
impl Npm for PhpCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Php::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
match node.kind_id().into() {
DeclarationList => {
let Some(parent_kind) = node.parent().map(|p| p.kind_id().into()) else {
return;
};
match parent_kind {
ClassDeclaration | TraitDeclaration | AnonymousClass => {
for method in node.children().filter(|c| Self::is_func(c)) {
stats.class_nm += 1;
if super::npa::php_is_explicit_public(&method) {
stats.class_npm += 1;
}
}
}
InterfaceDeclaration => {
let count = node.children().filter(|c| Self::is_func(c)).count();
stats.interface_nm += count;
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
EnumDeclarationList => {
for method in node.children().filter(|c| Self::is_func(c)) {
stats.class_nm += 1;
if super::npa::php_is_explicit_public(&method) {
stats.class_npm += 1;
}
}
}
_ => {}
}
}
}
impl Npm for PythonCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Python::*;
if !matches!(node.kind_id().into(), ClassDefinition) {
return;
}
if stats.is_disabled() {
stats.is_class_space = true;
}
let Some(body) = node.children().find(|c| c.kind_id() == Block2) else {
return;
};
let count = body
.children()
.filter(|stmt| match stmt.kind_id().into() {
FunctionDefinition => true,
DecoratedDefinition => stmt.children().any(|c| c.kind_id() == FunctionDefinition),
_ => false,
})
.count();
stats.class_nm += count;
stats.class_npm += count;
}
}
impl Npm for RustCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Rust::*;
if matches!(node.kind_id().into(), ImplItem | TraitItem) && stats.is_disabled() {
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), FunctionItem | FunctionSignatureItem) {
return;
}
let Some(parent) = node.parent() else {
return;
};
if !matches!(parent.kind_id().into(), DeclarationList) {
return;
}
let Some(grand) = parent.parent() else {
return;
};
match grand.kind_id().into() {
ImplItem => {
stats.class_nm += 1;
if super::npa::rust_item_is_public(node) {
stats.class_npm += 1;
}
}
TraitItem => {
stats.interface_nm += 1;
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
}
impl Npm for GoCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Go as G;
match node.kind_id().into() {
G::SourceFile
if stats.is_disabled()
&& node
.children()
.any(|c| matches!(c.kind_id().into(), G::MethodDeclaration)) =>
{
stats.is_class_space = true;
}
G::MethodDeclaration => {
stats.class_nm += 1;
stats.class_npm += 1;
}
G::InterfaceType => {
let methods = node
.children()
.filter(|c| matches!(c.kind_id().into(), G::MethodElem))
.count();
if methods == 0 {
return;
}
if stats.is_disabled() {
stats.is_class_space = true;
}
stats.interface_nm += methods;
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
}
impl Npm for CppCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Cpp::*;
if matches!(node.kind_id().into(), ClassSpecifier | StructSpecifier) && stats.is_disabled()
{
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), FieldDeclarationList) {
return;
}
let Some(parent) = node.parent() else {
return;
};
let mut current_is_public = match parent.kind_id().into() {
ClassSpecifier => false,
StructSpecifier => true,
_ => return,
};
for child in node.children() {
match child.kind_id().into() {
AccessSpecifier => {
current_is_public = child
.first_child(|id| {
id == Cpp::Public || id == Cpp::Protected || id == Cpp::Private
})
.is_some_and(|tok| tok.kind_id() == Cpp::Public);
}
FunctionDefinition | FunctionDefinition2 | FunctionDefinition3
| FunctionDefinition4 => {
stats.class_nm += 1;
if current_is_public {
stats.class_npm += 1;
}
}
FieldDeclaration | Declaration | Declaration2 | Declaration3 | Declaration4
| TemplateDeclaration
if super::npa::cpp_has_function_declarator(&child) =>
{
stats.class_nm += 1;
if current_is_public {
stats.class_npm += 1;
}
}
_ => {}
}
}
}
}
impl Npm for KotlinCode {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use Kotlin::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), ClassBody) {
return;
}
let is_interface = super::npa::kotlin_class_body_is_interface(node);
for func in node.children().filter(|c| Self::is_func(c)) {
if is_interface {
stats.interface_nm += 1;
stats.interface_npm += 1;
} else {
stats.class_nm += 1;
if super::npa::kotlin_is_public(&func) {
stats.class_npm += 1;
}
}
}
}
}
macro_rules! ts_npm_compute {
($lang:ident) => {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use $lang::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
match node.kind_id().into() {
ClassBody => {
for member in node.children() {
match member.kind_id().into() {
MethodDefinition | AbstractMethodSignature => {
stats.class_nm += 1;
if ts_member_is_public!($lang, member) {
stats.class_npm += 1;
}
}
PublicFieldDefinition
if member
.first_child(|id| {
id == $lang::ArrowFunction
|| id == $lang::FunctionExpression
})
.is_some() =>
{
stats.class_nm += 1;
if ts_member_is_public!($lang, member) {
stats.class_npm += 1;
}
}
_ => {}
}
}
}
InterfaceBody => {
let count = node
.children()
.filter(|c| {
matches!(
c.kind_id().into(),
MethodSignature | AbstractMethodSignature | ConstructSignature
)
})
.count();
stats.interface_nm += count;
stats.interface_npm = stats.interface_nm;
}
_ => {}
}
}
};
}
impl Npm for TypescriptCode {
ts_npm_compute!(Typescript);
}
impl Npm for TsxCode {
ts_npm_compute!(Tsx);
}
impl Npm for RubyCode {
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
use Ruby::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), BodyStatement | BodyStatement2) {
return;
}
let Some(parent_kind) = node.parent().map(|p| p.kind_id().into()) else {
return;
};
if !matches!(parent_kind, Class | SingletonClass) {
return;
}
let mut visibility = super::npa::RubyVisibility::Public;
for child in node.children() {
if let Some(marker) = super::npa::ruby_visibility_marker(&child, code) {
visibility = marker;
continue;
}
if matches!(child.kind_id().into(), Method | SingletonMethod) {
stats.class_nm += 1;
if visibility == super::npa::RubyVisibility::Public {
stats.class_npm += 1;
}
}
}
}
}
macro_rules! js_npm_compute {
($lang:ident) => {
fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
use $lang::*;
if Self::is_func_space(node) && stats.is_disabled() {
stats.is_class_space = true;
}
if !matches!(node.kind_id().into(), ClassBody) {
return;
}
for member in node.children() {
match member.kind_id().into() {
MethodDefinition => {
stats.class_nm += 1;
stats.class_npm += 1;
}
FieldDefinition
if member
.first_child(|id| {
id == $lang::ArrowFunction || id == $lang::FunctionExpression
})
.is_some() =>
{
stats.class_nm += 1;
stats.class_npm += 1;
}
_ => {}
}
}
}
};
}
impl Npm for JavascriptCode {
js_npm_compute!(Javascript);
}
impl Npm for MozjsCode {
js_npm_compute!(Mozjs);
}
impl Npm for ElixirCode {
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
use crate::metrics::cognitive::{elixir_call_keyword, elixir_do_block_call_children};
if !stats.is_disabled() || !Self::is_func_space_with_code(node, code) {
return;
}
if !matches!(elixir_call_keyword(node, code), Some("defmodule")) {
return;
}
stats.is_class_space = true;
for stmt in elixir_do_block_call_children(node) {
match elixir_call_keyword(&stmt, code) {
Some("def" | "defmacro") => {
stats.class_nm += 1;
stats.class_npm += 1;
}
Some("defp" | "defmacrop") => {
stats.class_nm += 1;
}
_ => {}
}
}
}
}
implement_metric_trait!(
Npm,
PreprocCode,
CcommentCode,
PerlCode,
BashCode,
LuaCode,
TclCode
);
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::similar_names,
clippy::doc_markdown,
clippy::needless_raw_string_hashes,
clippy::too_many_lines
)]
mod tests {
use crate::tools::{assert_child_space_kind, check_func_space, check_metrics};
use super::*;
#[test]
fn java_constructors() {
check_metrics::<JavaParser>(
"class X {
X() {}
private X(int a) {}
protected X(int a, int b) {}
public X(int a, int b, int c) {} // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 1.0,
"interfaces": 0.0,
"class_methods": 4.0,
"interface_methods": 0.0,
"classes_average": 0.25,
"interfaces_average": null,
"total": 1.0,
"total_methods": 4.0,
"average": 0.25
}"###
);
},
);
}
#[test]
fn groovy_no_methods() {
check_metrics::<GroovyParser>("class A { int x = 1 }", "foo.groovy", |metric| {
assert_eq!(metric.npm.total_nm(), 0.0);
});
}
#[test]
fn groovy_public_methods() {
check_metrics::<GroovyParser>(
"class A {
public void m1() {}
public int m2() { return 0 }
private void m3() {}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn groovy_interface_methods_implicitly_public() {
check_func_space::<GroovyParser, _>(
"interface I {
void a()
int b()
}",
"foo.groovy",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn groovy_enum_counts_methods() {
check_metrics::<GroovyParser>(
"enum Status {
ACTIVE, INACTIVE;
public int code() { return 0 }
private void reset() {}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
#[ignore = "dekobon Groovy grammar v1 does not support annotation type elements with `default` values; the trailing `default \"\"`/`default 0` make the body fail to parse"]
fn groovy_annotation_type_counts_elements() {
check_func_space::<GroovyParser, _>(
"public @interface Marker {
String value() default \"\";
int priority() default 0;
}",
"foo.groovy",
|func_space| {
assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2.0);
assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2.0);
assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
},
);
}
#[test]
fn groovy_constructors() {
check_metrics::<GroovyParser>(
"class X {
X() {}
private X(int a) {}
protected X(int a, int b) {}
public X(int a, int b, int c) {}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 4.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn groovy_no_methods_in_unit_scope() {
check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
assert_eq!(metric.npm.total_nm(), 0.0);
});
}
#[test]
fn groovy_multiple_classes_methods() {
check_metrics::<GroovyParser>(
"class A { public void a() {} }
class B { public void b() {} }",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn groovy_methods_returning_primitive_types() {
check_metrics::<GroovyParser>(
"class X {
public byte a() {}
public int b() {}
public double c() {}
public boolean d() {}
byte e() {}
int f() {}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 6.0);
assert_eq!(metric.npm.class_npm_sum(), 4.0);
},
);
}
#[test]
fn groovy_methods_with_generic_types() {
check_metrics::<GroovyParser>(
"class X {
public List<String> a() {}
public Map<String, Integer> b() {}
List<Integer> c() {}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn groovy_method_modifiers() {
check_metrics::<GroovyParser>(
"abstract class X {
public static void a() {}
static public void b() {}
public final void c() {}
final public void d() {}
protected static void e() {}
static protected void f() {}
abstract public void g()
abstract void h()
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 8.0);
assert_eq!(metric.npm.class_npm_sum(), 5.0);
},
);
}
#[test]
#[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies"]
fn groovy_nested_inner_classes() {
check_metrics::<GroovyParser>(
"class X {
public void a() {}
class Y {
public void b() {}
class Z {
public void c() {}
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
},
);
}
#[test]
#[ignore = "dekobon Groovy grammar v1 does not yet support anonymous inner classes (`new T() { … }`)"]
fn groovy_anonymous_inner_class() {
check_metrics::<GroovyParser>(
"class X {
public Runnable r = new Runnable() {
public void run() {}
void helper() {}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn groovy_interfaces_and_class() {
check_func_space::<GroovyParser, _>(
"interface X {
void a()
}
interface Y extends X {
void b()
void c()
}
class Z implements Y {
public void a() {}
public void b() {}
public void c() {}
void d() {}
void e() {}
}",
"foo.groovy",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_nm_sum(), 3.0);
assert_eq!(metric.npm.interface_npm_sum(), 3.0);
assert_eq!(metric.npm.class_nm_sum(), 5.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
assert_child_space_kind(&func_space, "X", SpaceKind::Interface);
assert_child_space_kind(&func_space, "Y", SpaceKind::Interface);
assert_child_space_kind(&func_space, "Z", SpaceKind::Class);
},
);
}
#[test]
fn java_methods_returning_primitive_types() {
check_metrics::<JavaParser>(
"class X {
public byte a() {} // +1
public short b() {} // +1
public int c() {} // +1
public long d() {} // +1
public float e() {} // +1
public double f() {} // +1
public boolean g() {} // +1
public char h() {} // +1
byte i() {}
short j() {}
int k() {}
long l() {}
float m() {}
double n() {}
boolean o() {}
char p() {}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 8.0,
"interfaces": 0.0,
"class_methods": 16.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 8.0,
"total_methods": 16.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_methods_returning_arrays() {
check_metrics::<JavaParser>(
"class X {
public byte[] a() {} // +1
public short[] b() {} // +1
public int[] c() {} // +1
public long[] d() {} // +1
public float[] e() {} // +1
public double[] f() {} // +1
public boolean[] g() {} // +1
public char[] h() {} // +1
byte[] i() {}
short[] j() {}
int[] k() {}
long[] l() {}
float[] m() {}
double[] n() {}
boolean[] o() {}
char[] p() {}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 8.0,
"interfaces": 0.0,
"class_methods": 16.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 8.0,
"total_methods": 16.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_methods_returning_objects() {
check_metrics::<JavaParser>(
"class X {
public Integer[] a() {} // +1
public Integer b() {} // +1
public String[] c() {} // +1
public String d() {} // +1
public Y[] e() {} // +1
public Y f() {} // +1
Integer[] g() {}
Integer h() {}
String[] i() {}
String j() {}
Y[] k() {}
Y l() {}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 6.0,
"interfaces": 0.0,
"class_methods": 12.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 6.0,
"total_methods": 12.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_methods_with_generic_types() {
check_metrics::<JavaParser>(
"class X {
public <T, S extends T> void a(T x, S y) {} // +1
public <T, S> int b(T x, S y) {} // +1
public <T> boolean c(T x) {} // +1
public <T> ArrayList<T> d() {} // +1
public Y<String> e() {} // +1
<T, S extends T> void f(T x, S y) {}
<T, S> int g(T x, S y) {}
<T> boolean h(T x) {}
<T> ArrayList<T> i() {}
Y<String> j() {}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 5.0,
"interfaces": 0.0,
"class_methods": 10.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 5.0,
"total_methods": 10.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_method_modifiers() {
check_metrics::<JavaParser>(
"abstract class X {
public static final synchronized strictfp void a() {} // +1
static public final synchronized strictfp void b() {} // +1
static final public synchronized strictfp void c() {} // +1
static final synchronized public strictfp void d() {} // +1
static final synchronized strictfp public void e() {} // +1
protected static final synchronized native void f();
static protected final synchronized native void g();
static final protected synchronized native void h();
static final synchronized protected native void i();
static final synchronized native protected void j();
abstract public void k(); // +1
abstract void l();
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 6.0,
"interfaces": 0.0,
"class_methods": 12.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 6.0,
"total_methods": 12.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_classes() {
check_metrics::<JavaParser>(
"class X {
public void a() {} // +1
public void b() {} // +1
private void c() {}
}
class Y {
private void d() {}
private void e() {}
public void f() {} // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 3.0,
"interfaces": 0.0,
"class_methods": 6.0,
"interface_methods": 0.0,
"classes_average": 0.5,
"interfaces_average": null,
"total": 3.0,
"total_methods": 6.0,
"average": 0.5
}"###
);
},
);
}
#[test]
fn java_nested_inner_classes() {
check_metrics::<JavaParser>(
"class X {
public void a() {} // +1
class Y {
public void b() {} // +1
class Z {
public void c() {} // +1
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 3.0,
"interfaces": 0.0,
"class_methods": 3.0,
"interface_methods": 0.0,
"classes_average": 1.0,
"interfaces_average": null,
"total": 3.0,
"total_methods": 3.0,
"average": 1.0
}"###
);
},
);
}
#[test]
fn java_local_inner_classes() {
check_metrics::<JavaParser>(
"class X {
public void a() { // +1
class Y {
public void b() { // +1
class Z {
public void c() {} // +1
}
}
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 3.0,
"interfaces": 0.0,
"class_methods": 3.0,
"interface_methods": 0.0,
"classes_average": 1.0,
"interfaces_average": null,
"total": 3.0,
"total_methods": 3.0,
"average": 1.0
}"###
);
},
);
}
#[test]
fn java_anonymous_inner_classes() {
check_metrics::<JavaParser>(
"abstract class X {
public abstract void a(); // +1
}
abstract class Y {
abstract void b();
}
class Z {
public void c(){ // +1
X x = new X() {
@Override
public void a() {} // +1
};
Y y = new Y() {
@Override
void b() {}
};
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 3.0,
"interfaces": 0.0,
"class_methods": 5.0,
"interface_methods": 0.0,
"classes_average": 0.6,
"interfaces_average": null,
"total": 3.0,
"total_methods": 5.0,
"average": 0.6
}"###
);
},
);
}
#[test]
fn java_interface() {
check_metrics::<JavaParser>(
"interface X {
public int a(); // +1
boolean b(); // +1
void c(); // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 0.0,
"interfaces": 3.0,
"class_methods": 0.0,
"interface_methods": 3.0,
"classes_average": null,
"interfaces_average": 1.0,
"total": 3.0,
"total_methods": 3.0,
"average": 1.0
}"###
);
},
);
}
#[test]
fn java_enum_counts_methods() {
check_metrics::<JavaParser>(
"enum Status {
ACTIVE, INACTIVE;
public int code() { return 0; } // +1 public
private void reset() {} // not public
}",
"foo.java",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn java_record_counts_methods() {
check_metrics::<JavaParser>(
"record Point(int x, int y) {
public int sum() { return x + y; }
public Point() { this(0, 0); }
}",
"foo.java",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn java_annotation_type_counts_elements() {
check_func_space::<JavaParser, _>(
"@interface Marker {
String value() default \"\";
int priority() default 0;
}",
"foo.java",
|func_space| {
assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2.0);
assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2.0);
assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
},
);
}
#[test]
fn java_interfaces_and_class() {
check_metrics::<JavaParser>(
"interface X {
void a(); // +1
}
interface Y extends X {
void b(); // +1
void c(); // +1
}
class Z implements Y {
@Override
public void a() {} // +1
@Override
public void b() {} // +1
@Override
public void c() {} // +1
void d() {}
void e() {}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npm,
@r###"
{
"classes": 3.0,
"interfaces": 3.0,
"class_methods": 5.0,
"interface_methods": 3.0,
"classes_average": 0.6,
"interfaces_average": 1.0,
"total": 6.0,
"total_methods": 8.0,
"average": 0.75
}"###
);
},
);
}
#[test]
fn csharp_constructors() {
check_metrics::<CsharpParser>(
"class A {
public A() {}
public A(int x) {}
A(int x, int y) {}
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_methods_returning_primitive_types() {
check_metrics::<CsharpParser>(
"class A {
public int M1() { return 1; }
public bool M2() { return true; }
public double M3() { return 0.0; }
int M4() { return 0; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_methods_returning_arrays() {
check_metrics::<CsharpParser>(
"class A {
public int[] M1() { return new int[0]; }
public string[] M2() { return new string[0]; }
int[] M3() { return new int[0]; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_methods_returning_objects() {
check_metrics::<CsharpParser>(
"class Point { }
class A {
public Point M1() { return new Point(); }
public string M2() { return \"\"; }
Point M3() { return new Point(); }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_methods_with_generic_types() {
check_metrics::<CsharpParser>(
"class A {
public System.Collections.Generic.List<int> M1() { return null; }
public System.Collections.Generic.Dictionary<string, int> M2() { return null; }
System.Collections.Generic.List<string> M3() { return null; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_method_modifiers() {
check_metrics::<CsharpParser>(
"class A {
public void M1() {}
private void M2() {}
protected void M3() {}
internal void M4() {}
public static void M5() {}
public virtual void M6() {}
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_classes() {
check_metrics::<CsharpParser>(
"class A {
public void M1() {}
public void M2() {}
void M3() {}
}
class B {
public int N() { return 0; }
int Hidden() { return 0; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_nested_inner_classes() {
check_metrics::<CsharpParser>(
"class Outer {
public void M() {}
void Hidden() {}
public class Inner {
public void N() {}
void HiddenN() {}
}
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_property_accessors() {
check_metrics::<CsharpParser>(
"class A {
int _w;
public int X { get; set; }
public int Y { get; }
public int Z { get; init; }
public int W => _w;
int Hidden { get; set; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_local_functions() {
check_metrics::<CsharpParser>(
"class A {
public void Outer() {
void Local() {}
Local();
}
private void Hidden() {}
}",
"foo.cs",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0, "Local must not leak");
assert_eq!(metric.npm.class_npm_sum(), 1.0, "only Outer is public");
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn csharp_interface() {
check_metrics::<CsharpParser>(
"interface I {
int M1();
bool M2();
int X { get; set; }
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn csharp_interfaces_and_class() {
check_metrics::<CsharpParser>(
"interface I1 { int M1(); }
interface I2 { bool M2(); float M3(); }
class A {
public void M() {}
void Hidden() {}
}",
"foo.cs",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_no_class_methods() {
check_metrics::<PhpParser>(
"<?php class A { public int $x = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_one_public_method() {
check_metrics::<PhpParser>(
"<?php class A { public function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_one_private_method() {
check_metrics::<PhpParser>(
"<?php class A { private function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_one_protected_method() {
check_metrics::<PhpParser>(
"<?php class A { protected function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_mixed_visibility_methods() {
check_metrics::<PhpParser>(
"<?php
class A {
public function a(): void {}
public function b(): void {}
private function c(): void {}
protected function d(): void {}
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_static_public_method() {
check_metrics::<PhpParser>(
"<?php class A { public static function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_abstract_method() {
check_metrics::<PhpParser>(
"<?php abstract class A { abstract public function f(): void; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_final_public_method() {
check_metrics::<PhpParser>(
"<?php class A { final public function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_interface_methods() {
check_metrics::<PhpParser>(
"<?php
interface I {
public function a(): void;
public function b(): int;
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_enum_methods() {
check_metrics::<PhpParser>(
"<?php
enum Color {
case Red;
case Green;
public function label(): string {
return match ($this) {
Color::Red => 'r',
Color::Green => 'g',
};
}
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_trait_methods() {
check_metrics::<PhpParser>(
"<?php
trait T {
public function a(): void {}
private function b(): void {}
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn php_no_explicit_visibility_method_excluded() {
check_metrics::<PhpParser>(
"<?php class A { function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npm),
);
}
#[test]
fn kotlin_empty_class_no_methods() {
check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.interface_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn kotlin_public_methods_default() {
check_metrics::<KotlinParser>(
"class C {
fun a() {}
fun b(): Int = 0
fun c(x: Int): Int = x
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 3.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_private_method() {
check_metrics::<KotlinParser>(
"class C {
fun a() {} // public
private fun b() {} // private
fun c() {} // public
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_protected_internal_methods() {
check_metrics::<KotlinParser>(
"open class C {
protected fun a() {}
internal fun b() {}
public fun c() {}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_secondary_constructor_counts() {
check_metrics::<KotlinParser>(
"class C {
private var a: Int = 0
constructor(n: Int) { a = n }
constructor(n: Int, m: Int) { a = n + m }
fun get(): Int = a
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 3.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_companion_object_methods() {
check_metrics::<KotlinParser>(
"class Holder {
fun memberFn() {}
companion object {
fun staticFn() {}
private fun secret() {}
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_data_class_methods() {
check_metrics::<KotlinParser>(
"data class Point(val x: Int, val y: Int) {
fun manhattan(): Int = x + y
private fun internal_(): Int = 0
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_object_singleton_methods() {
check_metrics::<KotlinParser>(
"object Util {
fun add(a: Int, b: Int): Int = a + b
private fun helper(): Int = 0
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_interface_methods() {
check_func_space::<KotlinParser, _>(
"interface I {
fun work(): Int
fun describe(): String
}",
"foo.kt",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn kotlin_interface_with_default_method() {
check_func_space::<KotlinParser, _>(
"interface I {
fun abs(n: Int): Int {
return if (n < 0) -n else n
}
fun pure(): Int
}",
"foo.kt",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn kotlin_override_fun_counts() {
check_metrics::<KotlinParser>(
"open class Base {
open fun greet(): String = \"hi\"
}
class Sub : Base() {
override fun greet(): String = \"yo\"
private fun secret() {}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_nested_class_methods() {
check_metrics::<KotlinParser>(
"class Outer {
fun outerM() {}
class Nested {
fun nestedM() {}
private fun nestedSecret() {}
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_inner_class_methods() {
check_metrics::<KotlinParser>(
"class Outer {
fun outerM() {}
inner class Inner {
fun innerM() {}
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_top_level_function_excluded() {
check_metrics::<KotlinParser>(
"fun freeFn() {}
class C {
fun m() {}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_extension_function_excluded() {
check_metrics::<KotlinParser>(
"fun List<Int>.sum2(): Int = this.size
class C {
fun m() {}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn kotlin_class_in_interface() {
check_func_space::<KotlinParser, _>(
"interface Outer {
fun work(): Int
class Helper {
fun help() {}
}
}",
"foo.kt",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_npm_sum(), 1.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "Outer", SpaceKind::Interface);
let outer = func_space
.spaces
.iter()
.find(|s| s.name.as_deref() == Some("Outer"))
.expect("Outer FuncSpace");
assert_child_space_kind(outer, "Helper", SpaceKind::Class);
},
);
}
#[test]
fn kotlin_interface_in_class() {
check_func_space::<KotlinParser, _>(
"class Outer {
fun work() {}
interface Sub {
fun help(): Int
}
}",
"foo.kt",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.interface_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "Outer", SpaceKind::Class);
let outer = func_space
.spaces
.iter()
.find(|s| s.name.as_deref() == Some("Outer"))
.expect("Outer FuncSpace");
assert_child_space_kind(outer, "Sub", SpaceKind::Interface);
},
);
}
#[test]
fn kotlin_init_block_not_a_method() {
check_metrics::<KotlinParser>(
"class C(val n: Int) {
init { require(n >= 0) }
fun get(): Int = n
}",
"foo.kt",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_empty_class_no_methods() {
check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn typescript_default_public_methods() {
check_metrics::<TypescriptParser>(
"class C {
a(): void {}
b(): number { return 0; }
c(x: number): number { return x; }
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 3.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_method_visibility() {
check_metrics::<TypescriptParser>(
"class C {
public a(): void {}
private b(): void {}
protected c(): void {}
d(): void {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 4.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_static_methods() {
check_metrics::<TypescriptParser>(
"class C {
static a(): void {}
public static b(): void {}
private static c(): void {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_constructor_counts_as_method() {
check_metrics::<TypescriptParser>(
"class C {
constructor(public x: number) {}
m(): void {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_getter_setter_each_count_once() {
check_metrics::<TypescriptParser>(
"class C {
private _x: number = 0;
get x(): number { return this._x; }
set x(v: number) { this._x = v; }
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_arrow_field_counts_as_method() {
check_metrics::<TypescriptParser>(
"class C {
a: number = 0;
arrow = () => this.a;
private secret = () => this.a;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_method_overload_counts_once() {
check_metrics::<TypescriptParser>(
"class C {
m(x: number): void;
m(x: string): void;
m(x: any): void {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_abstract_class_methods() {
check_metrics::<TypescriptParser>(
"abstract class C {
abstract a(): void;
public abstract b(): number;
protected abstract c(): void;
public m(): void {}
private n(): void {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 3.0);
assert_eq!(metric.npm.class_nm_sum(), 5.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_interface_methods() {
check_func_space::<TypescriptParser, _>(
"interface I {
a(): void;
b(x: number): number;
c: string;
}",
"foo.ts",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn typescript_generic_class_methods() {
check_metrics::<TypescriptParser>(
"class Box<T> {
value: T;
set(v: T): void { this.value = v; }
get(): T { return this.value; }
}",
"foo.ts",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn typescript_multiple_classes_and_interface() {
check_func_space::<TypescriptParser, _>(
"class A { m(): void {} }
class B { private h(): void {} }
interface I { p(): number; }",
"foo.ts",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.interface_npm_sum(), 1.0);
assert_eq!(metric.npm.interface_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "A", SpaceKind::Class);
assert_child_space_kind(&func_space, "B", SpaceKind::Class);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn tsx_empty_class_no_methods() {
check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn tsx_default_public_methods() {
check_metrics::<TsxParser>(
"class C {
a(): void {}
b(): number { return 0; }
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_method_visibility() {
check_metrics::<TsxParser>(
"class C {
public a(): void {}
private b(): void {}
protected c(): void {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_static_methods() {
check_metrics::<TsxParser>(
"class C {
static a(): void {}
private static b(): void {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_constructor_counts_as_method() {
check_metrics::<TsxParser>(
"class C {
constructor() {}
m(): void {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_getter_setter_each_count_once() {
check_metrics::<TsxParser>(
"class C {
private _x: number = 0;
get x(): number { return this._x; }
set x(v: number) { this._x = v; }
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_arrow_field_counts_as_method() {
check_metrics::<TsxParser>(
"class C {
arrow = () => 1;
private secret = () => 2;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_method_overload_counts_once() {
check_metrics::<TsxParser>(
"class C {
m(x: number): void;
m(x: string): void;
m(x: any): void {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_abstract_class_methods() {
check_metrics::<TsxParser>(
"abstract class C {
abstract a(): void;
public m(): void {}
private n(): void {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_interface_methods() {
check_func_space::<TsxParser, _>(
"interface I {
a(): void;
b(): number;
}",
"foo.tsx",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn tsx_generic_class_methods() {
check_metrics::<TsxParser>(
"class Box<T> { value: T; set(v: T): void { this.value = v; } }",
"foo.tsx",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn tsx_multiple_classes_and_interface() {
check_func_space::<TsxParser, _>(
"class A { m(): void {} }
class B { private h(): void {} }
interface I { p(): number; }",
"foo.tsx",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.interface_npm_sum(), 1.0);
assert_eq!(metric.npm.interface_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "A", SpaceKind::Class);
assert_child_space_kind(&func_space, "B", SpaceKind::Class);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn ruby_no_class_methods() {
check_metrics::<RubyParser>("def foo\n 1\nend\n", "foo.rb", |metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn ruby_one_public_method() {
check_metrics::<RubyParser>(
"class A\n def f\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_one_private_method() {
check_metrics::<RubyParser>(
"class A\n private\n def f\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_one_protected_method() {
check_metrics::<RubyParser>(
"class A\n protected\n def f\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_mixed_visibility_methods() {
check_metrics::<RubyParser>(
"class A\n def a\n 1\n end\n private\n def b\n 1\n end\n public\n def c\n 1\n end\n protected\n def d\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 4.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_singleton_method_is_counted() {
check_metrics::<RubyParser>(
"class A\n def self.f\n 1\n end\n def g\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_singleton_class_methods() {
check_metrics::<RubyParser>(
"class A\n class << self\n def s\n 1\n end\n def t\n 2\n end\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_argument_form_visibility_does_not_flip() {
check_metrics::<RubyParser>(
"class A\n def y\n 1\n end\n private :y\n def z\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_multiple_classes() {
check_metrics::<RubyParser>(
"class A\n def a\n 1\n end\nend\nclass B\n private\n def b\n 1\n end\n def c\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_module_methods_not_counted() {
check_metrics::<RubyParser>(
"module M\n def f\n 1\n end\n def g\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_class_with_inheritance() {
check_metrics::<RubyParser>(
"class A < B\n def f\n 1\n end\n def g\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_visibility_resets_between_classes() {
check_metrics::<RubyParser>(
"class A\n private\n def a\n 1\n end\nend\nclass B\n def b\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npm.class_npm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn ruby_empty_class_no_methods() {
check_metrics::<RubyParser>("class Empty\nend\n", "foo.rb", |metric| {
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn python_empty_class_no_methods() {
check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn python_class_methods_count() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 def __init__(self):\n\
\x20 pass\n\
\x20 def m(self):\n\
\x20 pass\n\
\x20 def n(self):\n\
\x20 pass\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn python_decorated_methods_count() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 @property\n\
\x20 def p(self):\n\
\x20 return 1\n\
\x20 @staticmethod\n\
\x20 def s():\n\
\x20 return 2\n\
\x20 @classmethod\n\
\x20 def c(cls):\n\
\x20 return 3\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn python_async_method_counts() {
check_metrics::<PythonParser>(
"class C:\n async def m(self):\n return 1\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn python_nested_class_methods_independent() {
check_metrics::<PythonParser>(
"class Outer:\n\
\x20 def method(self):\n\
\x20 pass\n\
\x20 class Inner:\n\
\x20 def inner_method(self):\n\
\x20 pass\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn python_module_level_function_is_not_method() {
check_metrics::<PythonParser>(
"def f():\n pass\nclass C:\n def m(self):\n pass\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn python_dunder_methods_count() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 def __init__(self):\n\
\x20 pass\n\
\x20 def __repr__(self):\n\
\x20 return 'C'\n\
\x20 def __eq__(self, other):\n\
\x20 return True\n",
"foo.py",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn rust_empty_unit_no_methods() {
check_metrics::<RustParser>("", "empty.rs", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
assert_eq!(metric.npm.interface_nm_sum(), 0.0);
assert_eq!(metric.npm.interface_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn rust_impl_methods_count() {
check_metrics::<RustParser>(
"struct Foo;\n\
impl Foo {\n\
\x20 pub fn new() -> Self { Foo }\n\
\x20 fn helper(&self) -> i32 { 0 }\n\
\x20 pub fn process(&self) -> i32 { 0 }\n\
}\n",
"foo.rs",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn rust_trait_methods_count() {
check_func_space::<RustParser, _>(
"trait Drawable {\n\
\x20 fn draw(&self);\n\
\x20 fn area(&self) -> f64 { 0.0 }\n\
}\n",
"foo.rs",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
},
);
}
#[test]
fn rust_module_level_function_not_method() {
check_metrics::<RustParser>("fn f() {}\nfn g() {}\n", "foo.rs", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.interface_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn rust_multiple_impls_methods_aggregate() {
check_metrics::<RustParser>(
"struct Foo;\n\
impl Foo { pub fn m1(&self) {} }\n\
impl Foo { fn m2(&self) {} }\n",
"foo.rs",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn rust_trait_impl_block_counts_methods() {
check_func_space::<RustParser, _>(
"struct Foo;\n\
trait Drawable { fn draw(&self); }\n\
impl Drawable for Foo { fn draw(&self) {} }\n",
"foo.rs",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npm.interface_nm_sum(), 1.0);
assert_eq!(metric.npm.class_nm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
},
);
}
#[test]
fn go_empty_unit_no_methods() {
check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn go_method_declarations_count() {
check_metrics::<GoParser>(
"package main\n\
type Foo struct{}\n\
func (f Foo) DoX() {}\n\
func (f Foo) doY() {}\n",
"foo.go",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn go_free_function_is_not_method() {
check_metrics::<GoParser>(
"package main\nfunc g() {}\nfunc h(x int) int { return x }\n",
"foo.go",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn go_methods_on_different_receivers_aggregate_at_unit() {
check_metrics::<GoParser>(
"package main\n\
type Foo struct{}\n\
type Bar struct{}\n\
func (f Foo) M1() {}\n\
func (b Bar) M2() {}\n\
func (b *Bar) M3() {}\n",
"foo.go",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn go_interface_methods_count_as_interface_nm() {
check_metrics::<GoParser>(
"package main\ntype RC interface { Read() error; Close() error }\n",
"foo.go",
|metric| {
assert_eq!(metric.npm.interface_nm_sum(), 2.0);
assert_eq!(metric.npm.interface_npm_sum(), 2.0);
assert_eq!(metric.npm.class_nm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn go_pointer_receiver_methods_count() {
check_metrics::<GoParser>(
"package main\n\
type Foo struct{}\n\
func (f *Foo) Set() {}\n\
func (f *Foo) Get() int { return 0 }\n",
"foo.go",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn elixir_npm_def_is_public_defp_is_private() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def pub_one, do: 1\n defp priv_one, do: 1\n def pub_two(x), do: x\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn elixir_npm_defmacro_counts_as_public() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n defmacro pub_macro(x), do: x\n defmacrop priv_macro(x), do: x\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn elixir_npm_multiple_def_clauses_each_count() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def f(0), do: :zero\n def f(_), do: :other\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn elixir_npm_nested_defmodule_each_class() {
check_metrics::<ElixirParser>(
"defmodule Outer do\n def o, do: 1\n defmodule Inner do\n def i, do: 1\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
},
);
}
#[test]
fn elixir_npm_user_macro_not_classified_as_method() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n defmacro custom_def(name, body) do\n quote do\n def unquote(name), do: unquote(body)\n end\n end\n custom_def foo, do: 1\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn elixir_npm_quoted_defs_do_not_inflate_method_count() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n defmacro multi do\n quote do\n def a, do: 1\n def b, do: 2\n defp c, do: 3\n end\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
},
);
}
#[test]
fn cpp_empty_unit_no_methods() {
check_metrics::<CppParser>("", "empty.cpp", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn cpp_class_methods_count() {
check_metrics::<CppParser>(
"class Foo {\n\
void method1() {}\n\
void method2();\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn cpp_constructors_and_destructors_count() {
check_metrics::<CppParser>(
"class Foo {\n\
public:\n\
Foo();\n\
~Foo();\n\
void method();\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn cpp_template_methods_count() {
check_metrics::<CppParser>(
"class Foo {\n\
public:\n\
template<typename T> T fn(T x);\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn cpp_struct_methods_default_public() {
check_metrics::<CppParser>(
"struct Foo {\n\
void a();\n\
void b() {}\n\
Foo() {}\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn cpp_free_function_is_not_method() {
check_metrics::<CppParser>("void free_fn() {}\n", "foo.cpp", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn cpp_mixed_visibility_methods() {
check_metrics::<CppParser>(
"class Foo {\n\
public: void a();\n\
protected: void b();\n\
private: void c();\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn cpp_multiple_classes_aggregate_at_unit() {
check_metrics::<CppParser>(
"class Foo { public: void a(); void b() {} };\n\
struct Bar { void c(); };",
"foo.cpp",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn javascript_empty_unit_no_methods() {
check_metrics::<JavascriptParser>("", "empty.js", |metric| {
assert_eq!(metric.npm.class_nm_sum(), 0.0);
assert_eq!(metric.npm.class_npm_sum(), 0.0);
insta::assert_json_snapshot!(metric.npm);
});
}
#[test]
fn javascript_class_methods_count() {
check_metrics::<JavascriptParser>(
"class Foo {\n\
constructor() {}\n\
bar() {}\n\
get baz() { return 1; }\n\
set baz(v) {}\n\
}",
"foo.js",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 4.0);
assert_eq!(metric.npm.class_npm_sum(), 4.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn javascript_arrow_field_is_method() {
check_metrics::<JavascriptParser>(
"class Foo { x = () => {}; y = function() {}; z = 1; }",
"foo.js",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 2.0);
assert_eq!(metric.npm.class_npm_sum(), 2.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn javascript_free_function_is_not_method() {
check_metrics::<JavascriptParser>(
"function f() {}\nconst g = () => {};\nclass Foo { h() {} }",
"foo.js",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 1.0);
assert_eq!(metric.npm.class_npm_sum(), 1.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn javascript_multiple_classes_aggregate_at_unit() {
check_metrics::<JavascriptParser>(
"class Foo { a() {} b() {} }\nclass Bar { c() {} }",
"foo.js",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 3.0);
assert_eq!(metric.npm.class_npm_sum(), 3.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
#[test]
fn mozjs_class_methods_count() {
check_metrics::<MozjsParser>(
"class Foo {\n\
constructor() {}\n\
bar() {}\n\
get baz() { return 1; }\n\
set baz(v) {}\n\
}",
"foo.js",
|metric| {
assert_eq!(metric.npm.class_nm_sum(), 4.0);
assert_eq!(metric.npm.class_npm_sum(), 4.0);
insta::assert_json_snapshot!(metric.npm);
},
);
}
}