#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use std::fmt;
use crate::checker::Checker;
use crate::langs::*;
use crate::macros::{csharp_var_decl_kinds, csharp_var_declarator_kinds, implement_metric_trait};
use crate::node::Node;
use crate::*;
#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct Stats {
class_npa: usize,
interface_npa: usize,
class_na: usize,
interface_na: usize,
class_npa_sum: usize,
interface_npa_sum: usize,
class_na_sum: usize,
interface_na_sum: usize,
is_class_space: bool,
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"classes: {}, interfaces: {}, class_attributes: {}, interface_attributes: {}, class_cda: {}, interface_cda: {}, total: {}, total_attributes: {}, cda: {}",
self.class_npa_sum(),
self.interface_npa_sum(),
self.class_na_sum(),
self.interface_na_sum(),
self.class_cda(),
self.interface_cda(),
self.total_npa(),
self.total_na(),
self.total_cda()
)
}
}
impl Stats {
pub fn merge(&mut self, other: &Stats) {
self.class_npa_sum += other.class_npa_sum;
self.interface_npa_sum += other.interface_npa_sum;
self.class_na_sum += other.class_na_sum;
self.interface_na_sum += other.interface_na_sum;
}
#[inline]
#[must_use]
pub fn class_npa(&self) -> u64 {
self.class_npa as u64
}
#[inline]
#[must_use]
pub fn interface_npa(&self) -> u64 {
self.interface_npa as u64
}
#[inline]
#[must_use]
pub fn class_na(&self) -> u64 {
self.class_na as u64
}
#[inline]
#[must_use]
pub fn interface_na(&self) -> u64 {
self.interface_na as u64
}
#[inline]
#[must_use]
pub fn class_npa_sum(&self) -> u64 {
self.class_npa_sum as u64
}
#[inline]
#[must_use]
pub fn interface_npa_sum(&self) -> u64 {
self.interface_npa_sum as u64
}
#[inline]
#[must_use]
pub fn class_na_sum(&self) -> u64 {
self.class_na_sum as u64
}
#[inline]
#[must_use]
pub fn interface_na_sum(&self) -> u64 {
self.interface_na_sum as u64
}
#[inline]
#[must_use]
pub fn class_cda(&self) -> f64 {
accessibility_ratio(self.class_npa_sum() as f64, self.class_na_sum() as f64)
}
#[inline]
#[must_use]
pub fn interface_cda(&self) -> f64 {
if self.interface_npa_sum == self.interface_na_sum && self.interface_npa_sum != 0 {
1.0
} else {
accessibility_ratio(
self.interface_npa_sum() as f64,
self.interface_na_sum() as f64,
)
}
}
#[inline]
#[must_use]
pub fn total_cda(&self) -> f64 {
accessibility_ratio(self.total_npa() as f64, self.total_na() as f64)
}
#[inline]
#[must_use]
pub fn total_npa(&self) -> u64 {
self.class_npa_sum() + self.interface_npa_sum()
}
#[inline]
#[must_use]
pub fn total_na(&self) -> u64 {
self.class_na_sum() + self.interface_na_sum()
}
#[inline]
pub(crate) fn compute_sum(&mut self) {
self.class_npa_sum += self.class_npa;
self.interface_npa_sum += self.interface_npa;
self.class_na_sum += self.class_na;
self.interface_na_sum += self.interface_na;
}
#[inline]
pub(crate) fn is_disabled(&self) -> bool {
!self.is_class_space
}
}
#[inline]
pub(crate) fn accessibility_ratio(public: f64, total: f64) -> f64 {
if total == 0.0 { 0.0 } else { public / total }
}
#[doc(hidden)]
pub(crate) trait Npa
where
Self: Checker,
{
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
}
macro_rules! impl_npa_java_like {
($code:ty, $lang:ident) => {
impl Npa 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 declaration in node
.children()
.filter(|n| matches!(n.kind_id().into(), FieldDeclaration))
{
let attributes = declaration
.children()
.filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
.count();
stats.class_na += attributes;
if declaration.child(0).is_some_and(|modifiers| {
matches!(modifiers.kind_id().into(), Modifiers)
&& modifiers.first_child(|id| id == Public).is_some()
}) {
stats.class_npa += attributes;
}
}
}
InterfaceBody | AnnotationTypeBody => {
stats.interface_na += node
.children()
.filter(|n| matches!(n.kind_id().into(), ConstantDeclaration))
.flat_map(|n| n.children())
.filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
.count();
stats.interface_npa = stats.interface_na;
}
_ => {}
}
}
}
};
}
mod shared;
pub(crate) use shared::*;
macro_rules! ts_npa_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() {
PublicFieldDefinition
if member
.first_child(|id| {
id == $lang::ArrowFunction
|| id == $lang::FunctionExpression
})
.is_none() =>
{
stats.class_na += 1;
if ts_member_is_public!($lang, member) {
stats.class_npa += 1;
}
}
MethodDefinition => {
let Some(params) =
member.first_child(|id| id == $lang::FormalParameters)
else {
continue;
};
for param in params.children().filter(|c| {
matches!(
c.kind_id().into(),
RequiredParameter | RequiredParameter2
)
}) {
if param
.first_child(|id| {
id == $lang::AccessibilityModifier
|| id == $lang::Readonly
})
.is_some()
{
stats.class_na += 1;
if ts_member_is_public!($lang, param) {
stats.class_npa += 1;
}
}
}
}
_ => {}
}
}
}
InterfaceBody => {
let count = node
.children()
.filter(|c| matches!(c.kind_id().into(), PropertySignature))
.count();
stats.interface_na += count;
stats.interface_npa = stats.interface_na;
}
_ => {}
}
}
};
}
macro_rules! ts_member_is_public {
($lang:ident, $member:expr) => {{
match $member.first_child(|id| id == $lang::AccessibilityModifier) {
None => true,
Some(m) => m
.first_child(|id| id == $lang::Private || id == $lang::Protected)
.is_none(),
}
}};
}
pub(crate) use ts_member_is_public;
macro_rules! js_npa_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() {
if matches!(member.kind_id().into(), FieldDefinition)
&& member
.first_child(|id| {
id == $lang::ArrowFunction || id == $lang::FunctionExpression
})
.is_none()
{
stats.class_na += 1;
stats.class_npa += 1;
}
}
}
};
}
mod cpp;
mod csharp;
mod elixir;
mod go;
mod groovy;
mod java;
mod javascript;
mod kotlin;
mod mozcpp;
mod mozjs;
mod objc;
mod php;
mod python;
mod ruby;
mod rust;
mod tsx;
mod typescript;
implement_metric_trait!(
Npa,
CCode,
PreprocCode,
CcommentCode,
PerlCode,
BashCode,
LuaCode,
TclCode,
IrulesCode
);
#[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_single_attributes() {
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.npa,
@r#"
{
"class_npa_sum": 8,
"interface_npa_sum": 0,
"class_attributes": 16,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 8,
"total_attributes": 16,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_multiple_attributes() {
check_metrics::<JavaParser>(
"class X {
public byte a1; // +1
public short b1, b2; // +2
public int c1, c2, c3; // +3
public long d1, d2, d3, d4; // +4
public float e1, e2, e3, e4; // +4
public double f1, f2, f3; // +3
public boolean g1, g2; // +2
public char h1; // +1
byte i1, i2, i3, i4;
short j1, j2, j3;
int k1, k2;
long l1;
float m1;
double n1, n2;
boolean o1, o2, o3;
char p1, p2, p3, p4;
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 20,
"interface_npa_sum": 0,
"class_attributes": 40,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 20,
"total_attributes": 40,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_initialized_attributes() {
check_metrics::<JavaParser>(
"class X {
public byte a1 = 1; // +1
public short b1 = 2, b2; // +2
public int c1, c2 = 3, c3; // +3
public long d1 = 4, d2, d3, d4 = 5; // +4
public float e1, e2 = 6.0f, e3 = 7.0f, e4; // +4
public double f1 = 8.0, f2 = 9.0, f3 = 10.0; // +3
public boolean g1 = true, g2; // +2
public char h1 = 'a'; // +1
byte i1 = 1, i2 = 2, i3 = 3, i4 = 4;
short j1 = 5, j2, j3 = 6;
int k1, k2 = 7;
long l1 = 8;
float m1 = 9.0f;
double n1, n2 = 10.0;
boolean o1, o2 = false, o3;
char p1 = 'a', p2 = 'b', p3 = 'c', p4 = 'd';
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 20,
"interface_npa_sum": 0,
"class_attributes": 40,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 20,
"total_attributes": 40,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_array_attributes() {
check_metrics::<JavaParser>(
"class X {
public byte[] a1, a2, a3, a4; // +4
public short b1[], b2[], b3[]; // +3
public int[] c1 = { 1 }, c2; // +2
public long d1[] = { 1 }; // +1
public float[] e1 = { 1.0f, 2.0f, 3.0f }; // +1
public double f1[] = { 1.0, 2.0, 3.0 }, f2[]; // +2
public boolean[] g1 = new boolean[5], g2, g3; // +3
public char[] h1 = new char[5], h2[], h3[], h4[]; // +4
byte[] i1;
short j1[], j2[];
int[] k1, k2, k3 = { 1 };
long l1[], l2[] = { 1 }, l3[] = { 2 }, l4[];
float[] m1, m2, m3, m4 = { 1.0f, 2.0f, 3.0f };
double n1[], n2[] = { 1.0, 2.0, 3.0 }, n3[];
boolean[] o1, o2 = new boolean[5];
char[] p1 = new char[5];
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 20,
"interface_npa_sum": 0,
"class_attributes": 40,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 20,
"total_attributes": 40,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_object_attributes() {
check_metrics::<JavaParser>(
"class X {
public Integer[] a1 = { 1 }; // +1
public Integer b1, b2; // +2
public String[] c1 = { \"Hello\" }, c2, c3 = { \"World!\" }; // +3
public String d1[][] = { { \"Hello\" }, { \"World!\" } }; // +1
public Y[] e1, e2[]; // +2
public Y f1[], f2[][], f3[][][]; // +3
Integer[] g1 = { new Integer(1) };
Integer h1 = new Integer(1), h2 = new Integer(2);
String[] i1, i2 = { \"Hello World!\" }, i3;
String j1 = \"Hello World!\";
Y[] k1[], k2;
Y l1[][], l2[], l3 = new Y();
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 12,
"interface_npa_sum": 0,
"class_attributes": 24,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 12,
"total_attributes": 24,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn groovy_no_attributes() {
check_metrics::<GroovyParser>("class A { void foo() {} }", "foo.groovy", |metric| {
assert_eq!(metric.npa.total_na(), 0);
assert_eq!(metric.npa.total_npa(), 0);
});
}
#[test]
fn groovy_public_attributes() {
check_metrics::<GroovyParser>(
"class A {
public int x
public String name
private int hidden
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn groovy_def_attributes_not_public() {
check_metrics::<GroovyParser>(
"class A {
def field1
def field2
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 0);
},
);
}
#[test]
fn groovy_interface_attributes() {
check_func_space::<GroovyParser, _>(
"interface I {
public static final int A = 1
public static final int B = 2
}",
"foo.groovy",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.interface_na_sum(), 2);
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn groovy_no_attributes_in_unit_scope() {
check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
assert_eq!(metric.npa.total_na(), 0);
});
}
#[test]
fn groovy_multiple_classes() {
check_metrics::<GroovyParser>(
"class A { public int a }
class B { public int b }",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn groovy_initialized_attributes() {
check_metrics::<GroovyParser>(
"class X {
public int a1 = 1, a2
public int b1 = 2
int c1, c2 = 3
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn groovy_object_attributes() {
check_metrics::<GroovyParser>(
"class X {
public Integer a1
public String b1 = 'hello'
public Y[] c1
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn groovy_attribute_modifiers() {
check_metrics::<GroovyParser>(
"class X {
public static int a
static public int b
public final int c = 1
final public int d = 2
private static int e
int f
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 6);
assert_eq!(metric.npa.class_npa_sum(), 4);
},
);
}
#[test]
#[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies (https://github.com/dekobon/tree-sitter-groovy SPECIFICATION.md §4 — 'Field declarations, static initialisers, and inner classes land later')"]
fn groovy_nested_inner_classes() {
check_metrics::<GroovyParser>(
"class X {
public int a
class Y {
public boolean b
class Z {
public char c
}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn groovy_array_attributes() {
check_metrics::<GroovyParser>(
"class X {
public int[] a
public String[] b
int[] c
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn groovy_anonymous_inner_class() {
check_metrics::<GroovyParser>(
"class X {
public Runnable r = new Runnable() {
public int x
void run() {}
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn groovy_enum_counts_explicit_public_fields() {
check_metrics::<GroovyParser>(
"enum Status {
ACTIVE, INACTIVE;
public int code;
private int hidden;
}",
"foo.groovy",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 1);
},
);
}
#[test]
fn groovy_annotation_type_counts_constants_as_implicit_public() {
check_func_space::<GroovyParser, _>(
"public @interface Marker {
int VERSION = 1;
String NAME = \"x\";
}",
"foo.groovy",
|func_space| {
assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
},
);
}
#[test]
fn java_generic_attributes() {
check_metrics::<JavaParser>(
"class X<T, S extends T> {
public T a1; // +1
public Entry<T, S> b1, b2[]; // +2
public ArrayList<T> c1, c2, c3; // +3
public HashMap<Long, Double> d1, d2; // +2
public TreeSet<String> e1; // +1
S f1;
Entry<S, T> g1[], g2;
ArrayList<S> h1, h2, h3;
HashMap<Long, Float> i1, i2;
TreeSet<Entry<S, T>> j1;
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 9,
"interface_npa_sum": 0,
"class_attributes": 18,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 9,
"total_attributes": 18,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_attribute_modifiers() {
check_metrics::<JavaParser>(
"class X {
public transient volatile static int a; // +1
transient public volatile static int b; // +1
transient volatile public static int c; // +1
transient volatile static public int d; // +1
public transient static final int e = 1; // +1
transient public static final int f = 2; // +1
transient static public final int g = 3; // +1
transient static final public int h = 4; // +1
protected transient volatile static int i;
transient volatile static protected int j;
private transient volatile static int k;
transient volatile static private int l;
transient volatile static int m;
transient static final int n = 5;
static public final int o = 6; // +1
final public int p = 7; // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 10,
"interface_npa_sum": 0,
"class_attributes": 16,
"interface_attributes": 0,
"class_cda": 0.625,
"interface_cda": 0.0,
"total": 10,
"total_attributes": 16,
"cda": 0.625
}
"#
);
},
);
}
#[test]
fn java_classes() {
check_metrics::<JavaParser>(
"class X {
public int a; // +1
public boolean b; // +1
private char c;
}
class Y {
private double d;
private long e;
public float f; // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 3,
"interface_npa_sum": 0,
"class_attributes": 6,
"interface_attributes": 0,
"class_cda": 0.5,
"interface_cda": 0.0,
"total": 3,
"total_attributes": 6,
"cda": 0.5
}
"#
);
},
);
}
#[test]
fn java_nested_inner_classes() {
check_metrics::<JavaParser>(
"class X {
public int a; // +1
class Y {
public boolean b; // +1
class Z {
public char c; // +1
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 3,
"interface_npa_sum": 0,
"class_attributes": 3,
"interface_attributes": 0,
"class_cda": 1.0,
"interface_cda": 0.0,
"total": 3,
"total_attributes": 3,
"cda": 1.0
}
"#
);
},
);
}
#[test]
fn java_local_inner_classes() {
check_metrics::<JavaParser>(
"class X {
public int a; // +1
void x() {
class Y {
public boolean b; // +1
void y() {
class Z {
public char c; // +1
void z() {}
}
}
}
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 3,
"interface_npa_sum": 0,
"class_attributes": 3,
"interface_attributes": 0,
"class_cda": 1.0,
"interface_cda": 0.0,
"total": 3,
"total_attributes": 3,
"cda": 1.0
}
"#
);
},
);
}
#[test]
fn java_anonymous_inner_classes() {
check_metrics::<JavaParser>(
"abstract class X {
public int a; // +1
}
abstract class Y {
boolean b;
}
class Z {
public char c; // +1
public void z(){
X x1 = new X() {
public double d; // +1
};
Y y1 = new Y() {
long e;
};
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 3,
"interface_npa_sum": 0,
"class_attributes": 5,
"interface_attributes": 0,
"class_cda": 0.6,
"interface_cda": 0.0,
"total": 3,
"total_attributes": 5,
"cda": 0.6
}
"#
);
},
);
}
#[test]
fn java_interface() {
check_metrics::<JavaParser>(
"interface X {
public int a = 0; // +1
static boolean b = false; // +1
final char c = ' '; // +1
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.npa,
@r#"
{
"class_npa_sum": 0,
"interface_npa_sum": 3,
"class_attributes": 0,
"interface_attributes": 3,
"class_cda": 0.0,
"interface_cda": 1.0,
"total": 3,
"total_attributes": 3,
"cda": 1.0
}
"#
);
},
);
}
#[test]
fn java_enum_counts_explicit_public_fields() {
check_metrics::<JavaParser>(
"enum Status {
ACTIVE, INACTIVE;
public static final int FLAG = 1; // implicit static final, still public
public int code; // +1 explicit public
private int hidden; // not public
}",
"foo.java",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn java_record_counts_explicit_body_fields() {
check_metrics::<JavaParser>(
"record Point(int x, int y) {
public static int origin = 0; // explicit body, public
private int cached; // explicit body, private
}",
"foo.java",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 1);
},
);
}
#[test]
fn java_annotation_type_counts_constants_as_implicit_public() {
check_func_space::<JavaParser, _>(
"@interface Marker {
int VERSION = 1; // implicit public static final
String NAME = \"x\"; // implicit public static final
}",
"foo.java",
|func_space| {
assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
},
);
}
#[test]
fn php_no_class_attributes() {
check_metrics::<PhpParser>(
"<?php class A { public function f(): void {} }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn csharp_single_attributes() {
check_metrics::<CsharpParser>(
"class X {
public byte a;
public short b;
public int c;
public long d;
public float e;
public double f;
public bool g;
public char h;
byte i;
short j;
int k;
long l;
float m;
double n;
bool o;
char p;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 8);
assert_eq!(metric.npa.class_na_sum(), 16);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_multiple_attributes() {
check_metrics::<CsharpParser>(
"class X {
public byte a1;
public short b1, b2;
public int c1, c2, c3;
public long d1, d2, d3, d4;
public bool g1, g2;
byte i1, i2, i3, i4;
int k1, k2;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 12);
assert_eq!(metric.npa.class_na_sum(), 18);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_initialized_attributes() {
check_metrics::<CsharpParser>(
"class X {
public int a = 1;
public bool b = true;
public string c = \"hello\";
public double d = 3.14;
int e = 0;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 4);
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_array_attributes() {
check_metrics::<CsharpParser>(
"class X {
public int[] a;
public string[] b = new string[5];
int[] c;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_object_attributes() {
check_metrics::<CsharpParser>(
"class Point { public int X, Y; }
class Shape {
public Point origin;
public Point endpoint = new Point();
Point hidden;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 4);
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_generic_attributes() {
check_metrics::<CsharpParser>(
"class X {
public System.Collections.Generic.List<int> a;
public System.Collections.Generic.Dictionary<string, int> b;
System.Collections.Generic.List<string> c;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_attribute_modifiers() {
check_metrics::<CsharpParser>(
"class X {
public int a;
private int b;
protected int c;
internal int d;
public static int e;
public readonly int f;
public const int g = 1;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 4);
assert_eq!(metric.npa.class_na_sum(), 7);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_classes() {
check_metrics::<CsharpParser>(
"class A {
public int a;
public int b;
int c;
}
class B {
public string s;
int n;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_nested_inner_classes() {
check_metrics::<CsharpParser>(
"class Outer {
public int a;
int b;
public class Inner {
public string s;
int n;
}
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_struct_attributes() {
check_metrics::<CsharpParser>(
"struct Point {
public int X;
public int Y;
int Hidden;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_record_attributes() {
check_metrics::<CsharpParser>(
"record Person {
public string Name;
int Age;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_interface() {
check_func_space::<CsharpParser, _>(
"interface I {
static int A = 1;
static string B = \"hello\";
}",
"foo.cs",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 2);
assert_eq!(metric.npa.interface_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn csharp_interface_explicit_modifiers() {
check_metrics::<CsharpParser>(
"interface I {
private static int Hidden = 1;
protected static int AlsoHidden = 2;
public static int Shown = 3;
static int Implicit = 4;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.interface_na_sum(), 4);
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn csharp_interface_multi_declarator_modifier() {
check_metrics::<CsharpParser>(
"interface I {
private static int a = 1, b = 2;
static int c = 3, d = 4;
}",
"foo.cs",
|metric| {
assert_eq!(metric.npa.interface_na_sum(), 4);
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn php_one_public_attribute() {
check_metrics::<PhpParser>(
"<?php class A { public int $x = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_one_private_attribute() {
check_metrics::<PhpParser>(
"<?php class A { private int $x = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_one_protected_attribute() {
check_metrics::<PhpParser>(
"<?php class A { protected int $x = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_mixed_visibility_attributes() {
check_metrics::<PhpParser>(
"<?php
class A {
public int $a = 0;
public int $b = 0;
private int $c = 0;
protected int $d = 0;
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_static_public_attribute() {
check_metrics::<PhpParser>(
"<?php class A { public static int $x = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_readonly_public_attribute() {
check_metrics::<PhpParser>(
"<?php class A { public readonly int $x; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_multiple_attributes_per_declaration() {
check_metrics::<PhpParser>(
"<?php class A { public int $a = 0, $b = 0, $c = 0; }",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_interface_constants() {
check_metrics::<PhpParser>(
"<?php
interface I {
const A = 1;
const B = 2;
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_enum_cases_not_counted() {
check_metrics::<PhpParser>(
"<?php
enum Color {
case Red;
case Green;
case Blue;
}",
"foo.php",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn php_enum_const_not_counted() {
check_metrics::<PhpParser>(
"<?php
enum Suit: string {
case Hearts = 'H';
case Diamonds = 'D';
const Wild = 'W';
public function label(): string { return $this->name; }
}",
"foo.php",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
},
);
}
#[test]
fn php_enum_npa_matches_java_enum_npa() {
check_metrics::<PhpParser>(
"<?php
enum Color {
case Red;
case Green;
case Blue;
}",
"foo.php",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
},
);
check_metrics::<JavaParser>(
"enum Color {
RED, GREEN, BLUE;
}",
"foo.java",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
},
);
}
#[test]
fn php_trait_attributes() {
check_metrics::<PhpParser>(
"<?php
trait T {
public int $a = 0;
private int $b = 0;
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_no_explicit_visibility_excluded() {
check_metrics::<PhpParser>("<?php class A { var $x = 0; }", "foo.php", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn php_anonymous_class_attributes() {
check_metrics::<PhpParser>(
"<?php
$obj = new class {
public int $a = 0;
private int $b = 0;
};",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn php_property_promotion_excluded() {
check_metrics::<PhpParser>(
"<?php
class A {
public function __construct(public string $x, public int $y) {}
}",
"foo.php",
|metric| insta::assert_json_snapshot!(metric.npa),
);
}
#[test]
fn kotlin_empty_class_no_attributes() {
check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn kotlin_public_val_var_default() {
check_metrics::<KotlinParser>(
"class C {
val a: Int = 1
var b: Int = 2
val c: String = \"hi\"
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_private_val_var() {
check_metrics::<KotlinParser>(
"class C {
val a: Int = 1 // public
private val b: Int = 2 // not public
var c: Int = 3 // public
private var d: Int = 4 // not public
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_protected_internal_excluded_from_public() {
check_metrics::<KotlinParser>(
"open class C {
protected val a: Int = 1
internal val b: Int = 2
public val c: Int = 3 // explicit public
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_primary_constructor_parameter_property() {
check_metrics::<KotlinParser>(
"class C(val a: Int, var b: Int, c: Int) {
val d: Int = c
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_primary_constructor_private_param_property() {
check_metrics::<KotlinParser>(
"class C(private val a: Int, val b: Int)",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_secondary_constructor_does_not_add_attrs() {
check_metrics::<KotlinParser>(
"class C {
private var a: Int = 0
constructor(n: Int) { a = n }
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_companion_object_attributes() {
check_metrics::<KotlinParser>(
"class Holder {
val instance: Int = 1
companion object {
val SCALE: Int = 10
private val SECRET: Int = 7
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_data_class_attributes() {
check_metrics::<KotlinParser>(
"data class Point(val x: Int, val y: Int)",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_object_singleton_attributes() {
check_metrics::<KotlinParser>(
"object Config {
val DEFAULT: Int = 42
private val SEED: Int = 0
var debug: Boolean = false
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_interface_attributes() {
check_func_space::<KotlinParser, _>(
"interface I {
val a: Int
val b: String
}",
"foo.kt",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.interface_na_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn kotlin_nested_class_attributes() {
check_metrics::<KotlinParser>(
"class Outer {
val o1: Int = 1
class Nested {
val n1: Int = 1
val n2: Int = 2
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_inner_class_attributes() {
check_metrics::<KotlinParser>(
"class Outer {
val o1: Int = 1
inner class Inner {
val i1: Int = 1
}
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_top_level_properties_excluded() {
check_metrics::<KotlinParser>(
"val topVal: Int = 0
var topVar: Int = 1
class C { val x: Int = 0 }",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_multiple_classes_attributes() {
check_metrics::<KotlinParser>(
"class A {
val a1: Int = 0
var a2: Int = 0
}
class B {
val b1: Int = 0
private val b2: Int = 0
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn kotlin_class_with_methods_no_attrs() {
check_metrics::<KotlinParser>(
"class C {
fun m1() {}
fun m2(): Int = 0
}",
"foo.kt",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_empty_class_no_attributes() {
check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn typescript_default_public_fields() {
check_metrics::<TypescriptParser>(
"class C {
a: number = 1;
b: string = \"\";
c: boolean = false;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_visibility_modifiers() {
check_metrics::<TypescriptParser>(
"class C {
public a: number = 1;
private b: number = 2;
protected c: number = 3;
d: number = 4;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_static_fields() {
check_metrics::<TypescriptParser>(
"class C {
static a: number = 0;
public static b: number = 0;
private static c: number = 0;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_parameter_properties() {
check_metrics::<TypescriptParser>(
"class C {
constructor(public a: number, private b: string, c: boolean) {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_readonly_constructor_param_property() {
check_metrics::<TypescriptParser>(
"class C {
constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_readonly_field() {
check_metrics::<TypescriptParser>(
"class C {
readonly a: number = 1;
private readonly b: number = 2;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_abstract_class_attributes() {
check_metrics::<TypescriptParser>(
"abstract class C {
public a: number = 1;
protected b: number = 2;
abstract m(): void;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_arrow_field_is_method_not_attribute() {
check_metrics::<TypescriptParser>(
"class C {
a: number = 0;
arrow = () => this.a;
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_interface_property_signatures() {
check_func_space::<TypescriptParser, _>(
"interface I {
a: number;
b: string;
m(): void;
}",
"foo.ts",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.interface_na_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn typescript_generic_class_attributes() {
check_metrics::<TypescriptParser>(
"class Box<T, U> {
value: T;
other: U;
constructor(v: T, o: U) { this.value = v; this.other = o; }
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_getters_setters_not_attributes() {
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.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn typescript_multiple_classes_and_interface() {
check_func_space::<TypescriptParser, _>(
"class A { x: number = 0; }
class B { private y: number = 0; }
interface I { z: number; }",
"foo.ts",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.interface_npa_sum(), 1);
assert_eq!(metric.npa.interface_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
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 typescript_nested_class_attributes_independent() {
check_metrics::<TypescriptParser>(
"class Outer {
a: number = 0;
static Inner = class {
b: number = 0;
c: number = 0;
};
}",
"foo.ts",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 4);
assert_eq!(metric.npa.class_na_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_empty_class_no_attributes() {
check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn tsx_default_public_fields() {
check_metrics::<TsxParser>(
"class C {
a: number = 1;
b: string = \"\";
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_visibility_modifiers() {
check_metrics::<TsxParser>(
"class C {
public a: number = 1;
private b: number = 2;
protected c: number = 3;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_parameter_properties() {
check_metrics::<TsxParser>(
"class C {
constructor(public a: number, private b: string) {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_readonly_constructor_param_property() {
check_metrics::<TsxParser>(
"class C {
constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_abstract_class_attributes() {
check_metrics::<TsxParser>(
"abstract class C {
public a: number = 1;
private b: number = 2;
abstract m(): void;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_interface_property_signatures() {
check_func_space::<TsxParser, _>(
"interface I {
a: number;
b: string;
m(): void;
}",
"foo.tsx",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.interface_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
},
);
}
#[test]
fn tsx_arrow_field_is_method_not_attribute() {
check_metrics::<TsxParser>(
"class C {
a: number = 0;
arrow = () => this.a;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_static_fields() {
check_metrics::<TsxParser>(
"class C {
static a: number = 0;
private static b: number = 0;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_readonly_field() {
check_metrics::<TsxParser>(
"class C {
readonly a: number = 1;
private readonly b: number = 2;
}",
"foo.tsx",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_generic_class_attributes() {
check_metrics::<TsxParser>("class Box<T> { value: T; }", "foo.tsx", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn tsx_getters_setters_not_attributes() {
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.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn tsx_multiple_classes_and_interface() {
check_func_space::<TsxParser, _>(
"class A { x: number = 0; }
class B { private y: number = 0; }
interface I { z: number; }",
"foo.tsx",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.interface_npa_sum(), 1);
assert_eq!(metric.npa.interface_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
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_attributes() {
check_metrics::<RubyParser>(
"class A\n def f\n 1\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_instance_variable_attribute() {
check_metrics::<RubyParser>("class A\n @x = 1\nend\n", "foo.rb", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn ruby_class_variable_attribute() {
check_metrics::<RubyParser>("class A\n @@y = 1\nend\n", "foo.rb", |metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn ruby_attr_accessor_counts_symbols() {
check_metrics::<RubyParser>(
"class A\n attr_accessor :x, :y, :z\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_attr_reader_and_writer() {
check_metrics::<RubyParser>(
"class A\n attr_reader :r1, :r2\n attr_writer :w\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 3);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_mixed_attributes_and_assignments() {
check_metrics::<RubyParser>(
"class A\n attr_accessor :x, :y\n @z = 1\n @@w = 2\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 4);
assert_eq!(metric.npa.class_na_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_private_attributes() {
check_metrics::<RubyParser>(
"class A\n attr_accessor :pub\n private\n attr_accessor :hidden\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_visibility_public_resets_private() {
check_metrics::<RubyParser>(
"class A\n attr_reader :a\n private\n attr_reader :b\n public\n attr_reader :c\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_method_scope_assignments_excluded() {
check_metrics::<RubyParser>(
"class A\n def init\n @x = 1\n @@y = 2\n end\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_module_attributes_not_counted() {
check_metrics::<RubyParser>(
"module M\n attr_accessor :x\n @@m = 1\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_inheritance_attributes() {
check_metrics::<RubyParser>(
"class A < B\n attr_accessor :x\n @y = 0\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_constant_assignments_excluded() {
check_metrics::<RubyParser>(
"class A\n PI = 3.14\n E = 2.71\n attr_reader :x\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn ruby_multiple_classes_attribute_rollup() {
check_metrics::<RubyParser>(
"class A\n attr_accessor :x\nend\nclass B\n private\n attr_accessor :y\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.npa.class_npa_sum(), 1);
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_empty_class_no_attributes() {
check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn python_class_level_assignments_are_attributes() {
check_metrics::<PythonParser>("class C:\n x = 1\n y = 2\n", "foo.py", |metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn python_bare_type_annotation_not_attribute() {
check_metrics::<PythonParser>(
"class C:\n x: int\n y: int = 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attributes_in_init() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attributes_in_nested_control_flow() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self, flag):\n if flag:\n self.z = 1\n else:\n self.z = 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_defensive_reinit_self_attribute_counts_once() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.value = None\n def reset(self):\n self.value = 0\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_distinct_self_attributes_count_independently() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n self.z = 3\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attribute_annotated_assignment_dedupes() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.value: int = 1\n def reset(self):\n self.value = 0\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_class_level_and_self_attrs_combine() {
check_metrics::<PythonParser>(
"class C:\n counter = 0\n def __init__(self):\n self.name = 'a'\n self.value = 1\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attrs_isolated_per_class() {
check_metrics::<PythonParser>(
"class Outer:\n\
\x20 def __init__(self):\n\
\x20 self.x = 1\n\
\x20 class Inner:\n\
\x20 def __init__(self):\n\
\x20 self.z = 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_decorated_methods_do_not_inflate_attrs() {
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",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_module_level_assignments_not_attributes() {
check_metrics::<PythonParser>("x = 1\ny = 2\nclass C:\n a = 3\n", "foo.py", |metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn python_foreign_object_writes_not_attributes() {
check_metrics::<PythonParser>(
"class Service:\n\
\x20 def __init__(self, db, logger):\n\
\x20 self.name = \"svc\"\n\
\x20 db.connection = None\n\
\x20 logger.level = \"INFO\"\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attribute_unpacking_counts_each() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 def __init__(self):\n\
\x20 self.a, self.b = 1, 2\n\
\x20 self.c = 3\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_self_attribute_nested_unpacking_counts_each() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 def __init__(self):\n\
\x20 self.a, (self.b, self.c) = 1, (2, 3)\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn python_class_level_nested_unpacking_counts_each() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 (a, (b, c)) = 1, (2, 3)\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn python_class_level_parenthesized_unpacking_counts_each() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 (p, q) = 1, 2\n\
\x20 [m, n] = 3, 4\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.class_npa_sum(), 4);
},
);
}
#[test]
fn python_self_attribute_unpacking_skips_non_self_targets() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 def __init__(self):\n\
\x20 self.a, x = 1, 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_class_level_multi_target_counts_each_name() {
check_metrics::<PythonParser>(
"class C:\n x = 1\n a = b = 3\n p, q = 1, 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.class_npa_sum(), 5);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_chained_self_assignment_counts_each() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.a = self.b = 1\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_classmethod_cls_attribute_counts() {
check_metrics::<PythonParser>(
"class C:\n\
\x20 @classmethod\n\
\x20 def make(cls, other):\n\
\x20 cls.registry = {}\n\
\x20 other.thing = 1\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_nested_self_attribute_not_counted() {
check_metrics::<PythonParser>(
"class C:\n def __init__(self):\n self.f.g = 1\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn python_class_default_and_self_attr_dedupe() {
check_metrics::<PythonParser>(
"class C:\n x = 1\n def __init__(self):\n self.x = 2\n",
"foo.py",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn rust_empty_unit_no_attributes() {
check_metrics::<RustParser>("", "empty.rs", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 0);
assert_eq!(metric.npa.interface_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn rust_struct_fields_are_attributes() {
check_metrics::<RustParser>(
"struct Foo { pub a: i32, b: String, pub c: bool }",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn rust_pub_self_field_is_private() {
check_metrics::<RustParser>(
"struct S {\n\
\x20 pub(self) a: i32,\n\
\x20 pub(in self) a2: i32,\n\
\x20 pub(crate) b: i32,\n\
\x20 pub(super) d: i32,\n\
\x20 pub(in crate::x) e: i32,\n\
\x20 pub f: i32,\n\
\x20 c: i32,\n\
}",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 7);
assert_eq!(metric.npa.class_npa_sum(), 4);
},
);
}
#[test]
fn rust_pub_self_assoc_const_is_private() {
check_metrics::<RustParser>(
"struct Foo;\n\
impl Foo {\n\
\x20 pub(self) const A: i32 = 1;\n\
\x20 pub(in self) const A2: i32 = 2;\n\
\x20 pub(crate) const B: i32 = 3;\n\
\x20 pub const D: i32 = 4;\n\
}\n",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn rust_pub_self_tuple_field_is_private() {
check_metrics::<RustParser>(
"struct Bar(pub(self) i32, pub(in self) i32, pub(crate) i32, pub u8, String);",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 5);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn rust_tuple_struct_fields_are_attributes() {
check_metrics::<RustParser>("struct Bar(pub i32, String);", "foo.rs", |metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn rust_unit_struct_has_no_attributes() {
check_metrics::<RustParser>("struct Empty;", "foo.rs", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn rust_empty_struct_body_has_no_attributes() {
check_metrics::<RustParser>("struct Empty { }", "foo.rs", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn rust_impl_associated_consts_are_attributes() {
check_metrics::<RustParser>(
"struct Foo;\n\
impl Foo {\n\
\x20 const X: i32 = 1;\n\
\x20 pub const Y: i32 = 2;\n\
\x20 static Z: i32 = 3;\n\
\x20 pub static W: i32 = 4;\n\
}\n",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 4);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn rust_trait_consts_and_associated_types_are_attributes() {
check_func_space::<RustParser, _>(
"trait Drawable { const DEFAULT_COLOR: u32; type Item; }",
"foo.rs",
|func_space| {
let metric = &func_space.metrics;
assert_eq!(metric.npa.interface_na_sum(), 2);
assert_eq!(metric.npa.interface_npa_sum(), 2);
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
},
);
}
#[test]
fn rust_multiple_impls_aggregate() {
check_metrics::<RustParser>(
"struct Foo;\n\
impl Foo { const X: i32 = 1; }\n\
impl Foo { pub const Y: i32 = 2; }\n",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn rust_module_level_consts_not_attributes() {
check_metrics::<RustParser>(
"const PI: f64 = 3.14;\nstatic Q: i32 = 0;\n",
"foo.rs",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.interface_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_empty_unit_no_attributes() {
check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn go_empty_struct_has_no_attributes() {
check_metrics::<GoParser>("package main\ntype Empty struct{}\n", "foo.go", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn go_struct_fields_are_attributes() {
check_metrics::<GoParser>(
"package main\ntype Foo struct { X int; y string; Z float64 }\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_grouped_struct_fields_each_count() {
check_metrics::<GoParser>(
"package main\ntype Point struct { X, Y int; Z float64 }\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_embedded_type_counts_as_attribute() {
check_metrics::<GoParser>(
"package main\nimport \"io\"\ntype Bar struct { io.Reader; *Foo; n int }\ntype Foo struct {}\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_multiple_structs_aggregate_at_unit() {
check_metrics::<GoParser>(
"package main\ntype Foo struct { x int }\ntype Bar struct { a int; b string }\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_top_level_var_const_not_attributes() {
check_metrics::<GoParser>(
"package main\nvar Counter int\nconst Pi = 3.14\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn go_npa_excludes_unexported() {
check_metrics::<GoParser>(
"package main\nimport \"io\"\n\
type T struct { Name string; secret int; A, b int; io.Reader; _ int; Ärger bool }\n",
"foo.go",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 7);
assert_eq!(metric.npa.class_npa_sum(), 4);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn elixir_npa_defstruct_keyword_list() {
check_metrics::<ElixirParser>(
"defmodule User do\n defstruct name: nil, age: 0, email: nil\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn elixir_npa_defstruct_atom_list() {
check_metrics::<ElixirParser>(
"defmodule User do\n defstruct [:name, :age, :email]\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
},
);
}
#[test]
fn elixir_npa_defstruct_bracketed_keyword_list() {
check_metrics::<ElixirParser>(
"defmodule User do\n defstruct [name: nil, age: 0]\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 2);
assert_eq!(metric.npa.class_npa_sum(), 2);
},
);
}
#[test]
fn elixir_npa_defstruct_single_field() {
check_metrics::<ElixirParser>(
"defmodule Box do\n defstruct value: nil\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
},
);
}
#[test]
fn elixir_npa_no_defstruct_is_zero() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def m, do: :ok\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
},
);
}
#[test]
fn objc_npa() {
check_metrics::<ObjcParser>(
"@interface Foo : NSObject {\n\
int _prot;\n\
@private\n\
int _priv;\n\
@public\n\
int _pub1, _pub2;\n\
@protected\n\
int _prot2;\n\
}\n\
@property (nonatomic) int count;\n\
@property (copy) NSString *name;\n\
@end\n",
"foo.m",
|metric| {
assert_eq!(metric.npa.interface_na_sum(), 7);
assert_eq!(metric.npa.interface_npa_sum(), 4);
},
);
}
#[test]
fn objc_npa_protocol() {
check_metrics::<ObjcParser>(
"@protocol Drawable <NSObject>\n\
@optional\n\
@property (readonly) int z;\n\
@end\n",
"foo.m",
|metric| {
assert_eq!(metric.npa.interface_na_sum(), 1);
assert_eq!(metric.npa.interface_npa_sum(), 1);
},
);
}
#[test]
fn cpp_empty_unit_no_attributes() {
check_metrics::<CppParser>("", "empty.cpp", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn cpp_empty_class_no_attributes() {
check_metrics::<CppParser>("class Foo {};", "foo.cpp", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn cpp_class_public_attributes() {
check_metrics::<CppParser>(
"class Foo { public: int a; int b, c; };",
"foo.cpp",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn cpp_class_private_default_visibility() {
check_metrics::<CppParser>("class Foo { int value_; };", "foo.cpp", |metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn cpp_struct_default_public_visibility() {
check_metrics::<CppParser>("struct Bar { int value_; };", "foo.cpp", |metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn cpp_mixed_visibility_sections() {
check_metrics::<CppParser>(
"class Foo {\n\
public: int a;\n\
protected: int b;\n\
private: int c;\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn cpp_methods_not_counted_as_attributes() {
check_metrics::<CppParser>(
"class Foo {\n\
public:\n\
void method1() {}\n\
void method2();\n\
private:\n\
int value_;\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn cpp_pointer_array_fields_count() {
check_metrics::<CppParser>(
"struct S {\n\
int* p;\n\
int a[10];\n\
int x;\n\
};",
"foo.cpp",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn cpp_multiple_classes_aggregate_at_unit() {
check_metrics::<CppParser>(
"class Foo { public: int a; private: int b; };\nstruct Bar { int c; };",
"foo.cpp",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 2);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn javascript_empty_unit_no_attributes() {
check_metrics::<JavascriptParser>("", "empty.js", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn javascript_empty_class_no_attributes() {
check_metrics::<JavascriptParser>("class Foo {}", "foo.js", |metric| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert_eq!(metric.npa.class_npa_sum(), 0);
insta::assert_json_snapshot!(metric.npa);
});
}
#[test]
fn javascript_class_fields_count() {
check_metrics::<JavascriptParser>(
"class Foo { x = 1; y; static z = 2; }",
"foo.js",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn javascript_arrow_field_is_method_not_attribute() {
check_metrics::<JavascriptParser>(
"class Foo { x = () => {}; y = function() {}; z = 1; }",
"foo.js",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn javascript_methods_not_counted_as_attributes() {
check_metrics::<JavascriptParser>(
"class Foo { constructor() {} bar() {} get baz() { return 1; } x = 1; }",
"foo.js",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 1);
assert_eq!(metric.npa.class_npa_sum(), 1);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn javascript_multiple_classes_aggregate_at_unit() {
check_metrics::<JavascriptParser>(
"class Foo { a = 1; b = 2; }\nclass Bar { c = 3; }",
"foo.js",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn mozjs_class_fields_count() {
check_metrics::<MozjsParser>(
"class Foo { x = 1; y; static z = 2; }",
"foo.js",
|metric| {
assert_eq!(metric.npa.class_na_sum(), 3);
assert_eq!(metric.npa.class_npa_sum(), 3);
insta::assert_json_snapshot!(metric.npa);
},
);
}
#[test]
fn empty_class_cda_is_zero_not_nan() {
let assert_zero = |metric: crate::CodeMetrics| {
assert_eq!(metric.npa.class_na_sum(), 0);
assert!(!metric.npa.class_cda().is_nan());
assert!(!metric.npa.total_cda().is_nan());
assert_eq!(metric.npa.class_cda(), 0.0);
assert_eq!(metric.npa.total_cda(), 0.0);
};
check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
}
#[test]
fn empty_interface_cda_is_zero_not_nan() {
let assert_zero = |metric: crate::CodeMetrics| {
assert_eq!(metric.npa.interface_na_sum(), 0);
assert!(!metric.npa.interface_cda().is_nan());
assert_eq!(metric.npa.interface_cda(), 0.0);
};
check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
}
}