1#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
8#![allow(
15 clippy::cast_precision_loss,
16 clippy::cast_possible_truncation,
17 clippy::cast_sign_loss
18)]
19
20use std::fmt;
21
22use crate::checker::Checker;
23use crate::macros::{csharp_var_decl_kinds, csharp_var_declarator_kinds, implement_metric_trait};
24use crate::metrics::MemberScopeExt;
25use crate::node::Node;
26use crate::*;
27
28#[derive(Clone, Debug, Default, PartialEq)]
52#[non_exhaustive]
53pub struct Stats {
54 class_npa: usize,
55 interface_npa: usize,
56 class_na: usize,
57 interface_na: usize,
58 class_npa_sum: usize,
59 interface_npa_sum: usize,
60 class_na_sum: usize,
61 interface_na_sum: usize,
62 space_kind: SpaceKind,
63}
64
65impl fmt::Display for Stats {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 write!(
68 f,
69 "classes: {}, interfaces: {}, class_attributes: {}, interface_attributes: {}, class_cda: {}, interface_cda: {}, total: {}, total_attributes: {}, cda: {}",
70 self.class_npa_sum(),
71 self.interface_npa_sum(),
72 self.class_na_sum(),
73 self.interface_na_sum(),
74 self.class_cda(),
75 self.interface_cda(),
76 self.total_npa(),
77 self.total_na(),
78 self.total_cda()
79 )
80 }
81}
82
83impl Stats {
84 pub fn merge(&mut self, other: &Stats) {
86 self.class_npa_sum += other.class_npa_sum;
87 self.interface_npa_sum += other.interface_npa_sum;
88 self.class_na_sum += other.class_na_sum;
89 self.interface_na_sum += other.interface_na_sum;
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn class_npa(&self) -> u64 {
96 self.class_npa as u64
97 }
98
99 #[inline]
101 #[must_use]
102 pub fn interface_npa(&self) -> u64 {
103 self.interface_npa as u64
104 }
105
106 #[inline]
108 #[must_use]
109 pub fn class_na(&self) -> u64 {
110 self.class_na as u64
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn interface_na(&self) -> u64 {
117 self.interface_na as u64
118 }
119
120 #[inline]
122 #[must_use]
123 pub fn class_npa_sum(&self) -> u64 {
124 self.class_npa_sum as u64
125 }
126
127 #[inline]
129 #[must_use]
130 pub fn interface_npa_sum(&self) -> u64 {
131 self.interface_npa_sum as u64
132 }
133
134 #[inline]
136 #[must_use]
137 pub fn class_na_sum(&self) -> u64 {
138 self.class_na_sum as u64
139 }
140
141 #[inline]
143 #[must_use]
144 pub fn interface_na_sum(&self) -> u64 {
145 self.interface_na_sum as u64
146 }
147
148 #[inline]
158 #[must_use]
159 pub fn class_cda(&self) -> f64 {
160 accessibility_ratio(self.class_npa_sum() as f64, self.class_na_sum() as f64)
161 }
162
163 #[inline]
173 #[must_use]
174 pub fn interface_cda(&self) -> f64 {
175 if self.interface_npa_sum == self.interface_na_sum && self.interface_npa_sum != 0 {
181 1.0
182 } else {
183 accessibility_ratio(
184 self.interface_npa_sum() as f64,
185 self.interface_na_sum() as f64,
186 )
187 }
188 }
189
190 #[inline]
200 #[must_use]
201 pub fn total_cda(&self) -> f64 {
202 accessibility_ratio(self.total_npa() as f64, self.total_na() as f64)
203 }
204
205 #[inline]
207 #[must_use]
208 pub fn total_npa(&self) -> u64 {
209 self.class_npa_sum() + self.interface_npa_sum()
210 }
211
212 #[inline]
214 #[must_use]
215 pub fn total_na(&self) -> u64 {
216 self.class_na_sum() + self.interface_na_sum()
217 }
218
219 #[inline]
222 pub(crate) fn compute_sum(&mut self) {
223 self.class_npa_sum += self.class_npa;
224 self.interface_npa_sum += self.interface_npa;
225 self.class_na_sum += self.class_na;
226 self.interface_na_sum += self.interface_na;
227 }
228
229 #[inline]
236 pub(crate) fn set_space_kind(&mut self, kind: SpaceKind) {
237 self.space_kind = kind;
238 }
239
240 #[inline]
242 pub(crate) fn is_disabled(&self) -> bool {
243 !self.space_kind.is_member_scope()
244 }
245}
246
247#[inline]
253pub(crate) fn accessibility_ratio(public: f64, total: f64) -> f64 {
254 if total == 0.0 { 0.0 } else { public / total }
255}
256
257#[doc(hidden)]
258pub(crate) trait Npa
260where
261 Self: Checker,
262{
263 const HAS_MEMBERS: bool = true;
273
274 fn compute<'a>(
292 node: &Node<'a>,
293 code: &'a [u8],
294 ancestors: Ancestors<'a, '_>,
295 stats: &mut Stats,
296 );
297}
298
299macro_rules! impl_npa_java_like {
324 ($code:ty, $lang:ident) => {
325 impl Npa for $code {
326 fn compute<'a>(
327 node: &Node<'a>,
328 _code: &'a [u8],
329 _ancestors: Ancestors<'a, '_>,
330 stats: &mut Stats,
331 ) {
332 use $lang::*;
333
334 match node.kind_id().into() {
335 ClassBody | EnumBodyDeclarations => {
336 for declaration in node
337 .children()
338 .filter(|n| matches!(n.kind_id().into(), FieldDeclaration))
339 {
340 let attributes = declaration
341 .children()
342 .filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
343 .count();
344 stats.class_na += attributes;
345 if declaration.child(0).is_some_and(|modifiers| {
349 matches!(modifiers.kind_id().into(), Modifiers)
350 && modifiers.first_child(|id| id == Public).is_some()
351 }) {
352 stats.class_npa += attributes;
353 }
354 }
355 }
356 InterfaceBody | AnnotationTypeBody => {
357 stats.interface_na += node
358 .children()
359 .filter(|n| matches!(n.kind_id().into(), ConstantDeclaration))
360 .flat_map(|n| n.children())
361 .filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
362 .count();
363 stats.interface_npa = stats.interface_na;
364 }
365 _ => {}
366 }
367 }
368 }
369 };
370}
371
372mod shared;
373pub(crate) use shared::*;
374
375use crate::lang_helpers::python::python_is_block;
377
378macro_rules! ts_npa_compute {
413 ($lang:ident) => {
414 fn compute<'a>(
415 node: &Node<'a>,
416 _code: &'a [u8],
417 _ancestors: Ancestors<'a, '_>,
418 stats: &mut Stats,
419 ) {
420 use $lang::*;
421
422 match node.kind_id().into() {
423 ClassBody => {
424 for member in node.children() {
425 match member.kind_id().into() {
426 PublicFieldDefinition
432 if member
433 .first_child(|id| {
434 id == $lang::ArrowFunction
435 || id == $lang::FunctionExpression
436 })
437 .is_none() =>
438 {
439 stats.class_na += 1;
440 if ts_member_is_public!($lang, member) {
441 stats.class_npa += 1;
442 }
443 }
444 MethodDefinition => {
450 let Some(params) =
451 member.first_child(|id| id == $lang::FormalParameters)
452 else {
453 continue;
454 };
455 for param in params.children().filter(|c| {
456 matches!(
457 c.kind_id().into(),
458 RequiredParameter | RequiredParameter2
459 )
460 }) {
461 if param
462 .first_child(|id| {
463 id == $lang::AccessibilityModifier
464 || id == $lang::Readonly
465 })
466 .is_some()
467 {
468 stats.class_na += 1;
469 if ts_member_is_public!($lang, param) {
470 stats.class_npa += 1;
471 }
472 }
473 }
474 }
475 _ => {}
476 }
477 }
478 }
479 InterfaceBody => {
480 let count = node
481 .children()
482 .filter(|c| matches!(c.kind_id().into(), PropertySignature))
483 .count();
484 stats.interface_na += count;
485 stats.interface_npa = stats.interface_na;
486 }
487 _ => {}
488 }
489 }
490 };
491}
492
493macro_rules! ts_member_is_public {
499 ($lang:ident, $member:expr) => {{
500 match $member.first_child(|id| id == $lang::AccessibilityModifier) {
501 None => true,
502 Some(m) => m
503 .first_child(|id| id == $lang::Private || id == $lang::Protected)
504 .is_none(),
505 }
506 }};
507}
508pub(crate) use ts_member_is_public;
509
510macro_rules! js_npa_compute {
531 ($lang:ident) => {
532 fn compute<'a>(
533 node: &Node<'a>,
534 _code: &'a [u8],
535 _ancestors: Ancestors<'a, '_>,
536 stats: &mut Stats,
537 ) {
538 use $lang::*;
539
540 if !matches!(node.kind_id().into(), ClassBody) {
541 return;
542 }
543
544 for member in node.children() {
545 if matches!(member.kind_id().into(), FieldDefinition)
546 && member
547 .first_child(|id| {
548 id == $lang::ArrowFunction || id == $lang::FunctionExpression
549 })
550 .is_none()
551 {
552 stats.class_na += 1;
553 stats.class_npa += 1;
554 }
555 }
556 }
557 };
558}
559
560mod cpp;
565mod csharp;
566mod elixir;
567mod go;
568mod groovy;
569mod java;
570mod javascript;
571mod kotlin;
572mod mozcpp;
573mod mozjs;
574mod objc;
575mod php;
576mod python;
577mod ruby;
578mod rust;
579mod tsx;
580mod typescript;
581
582implement_metric_trait!(
593 Npa,
594 CCode,
595 PreprocCode,
596 CcommentCode,
597 PerlCode,
598 BashCode,
599 LuaCode,
600 TclCode,
601 IrulesCode
602);
603
604#[cfg(test)]
605#[allow(
606 clippy::float_cmp,
607 clippy::cast_precision_loss,
608 clippy::cast_possible_truncation,
609 clippy::cast_sign_loss,
610 clippy::similar_names,
611 clippy::doc_markdown,
612 clippy::needless_raw_string_hashes,
613 clippy::too_many_lines
614)]
615mod tests {
616 use std::path::PathBuf;
617
618 use crate::test_support::{
619 assert_child_space_kind, ast_has_kind_id, check_func_space_only_shim,
620 check_metrics_only_shim, child_space,
621 };
622
623 use super::*;
624
625 check_metrics_only_shim!(check_metrics, Npa);
626 check_func_space_only_shim!(check_func_space, Npa);
627
628 #[cfg(feature = "java")]
629 #[test]
630 fn java_single_attributes() {
631 check_metrics::<JavaParser>(
632 "class X {
633 public byte a; // +1
634 public short b; // +1
635 public int c; // +1
636 public long d; // +1
637 public float e; // +1
638 public double f; // +1
639 public boolean g; // +1
640 public char h; // +1
641 byte i;
642 short j;
643 int k;
644 long l;
645 float m;
646 double n;
647 boolean o;
648 char p;
649 }",
650 "foo.java",
651 |metric| {
652 insta::assert_json_snapshot!(
653 metric.npa,
654 @r#"
655 {
656 "class_npa_sum": 8,
657 "interface_npa_sum": 0,
658 "class_attributes": 16,
659 "interface_attributes": 0,
660 "class_cda": 0.5,
661 "interface_cda": 0.0,
662 "total": 8,
663 "total_attributes": 16,
664 "cda": 0.5
665 }
666 "#
667 );
668 },
669 );
670 }
671
672 #[cfg(feature = "java")]
673 #[test]
674 fn java_multiple_attributes() {
675 check_metrics::<JavaParser>(
676 "class X {
677 public byte a1; // +1
678 public short b1, b2; // +2
679 public int c1, c2, c3; // +3
680 public long d1, d2, d3, d4; // +4
681 public float e1, e2, e3, e4; // +4
682 public double f1, f2, f3; // +3
683 public boolean g1, g2; // +2
684 public char h1; // +1
685 byte i1, i2, i3, i4;
686 short j1, j2, j3;
687 int k1, k2;
688 long l1;
689 float m1;
690 double n1, n2;
691 boolean o1, o2, o3;
692 char p1, p2, p3, p4;
693 }",
694 "foo.java",
695 |metric| {
696 insta::assert_json_snapshot!(
697 metric.npa,
698 @r#"
699 {
700 "class_npa_sum": 20,
701 "interface_npa_sum": 0,
702 "class_attributes": 40,
703 "interface_attributes": 0,
704 "class_cda": 0.5,
705 "interface_cda": 0.0,
706 "total": 20,
707 "total_attributes": 40,
708 "cda": 0.5
709 }
710 "#
711 );
712 },
713 );
714 }
715
716 #[cfg(feature = "java")]
717 #[test]
718 fn java_initialized_attributes() {
719 check_metrics::<JavaParser>(
720 "class X {
721 public byte a1 = 1; // +1
722 public short b1 = 2, b2; // +2
723 public int c1, c2 = 3, c3; // +3
724 public long d1 = 4, d2, d3, d4 = 5; // +4
725 public float e1, e2 = 6.0f, e3 = 7.0f, e4; // +4
726 public double f1 = 8.0, f2 = 9.0, f3 = 10.0; // +3
727 public boolean g1 = true, g2; // +2
728 public char h1 = 'a'; // +1
729 byte i1 = 1, i2 = 2, i3 = 3, i4 = 4;
730 short j1 = 5, j2, j3 = 6;
731 int k1, k2 = 7;
732 long l1 = 8;
733 float m1 = 9.0f;
734 double n1, n2 = 10.0;
735 boolean o1, o2 = false, o3;
736 char p1 = 'a', p2 = 'b', p3 = 'c', p4 = 'd';
737 }",
738 "foo.java",
739 |metric| {
740 insta::assert_json_snapshot!(
741 metric.npa,
742 @r#"
743 {
744 "class_npa_sum": 20,
745 "interface_npa_sum": 0,
746 "class_attributes": 40,
747 "interface_attributes": 0,
748 "class_cda": 0.5,
749 "interface_cda": 0.0,
750 "total": 20,
751 "total_attributes": 40,
752 "cda": 0.5
753 }
754 "#
755 );
756 },
757 );
758 }
759
760 #[cfg(feature = "java")]
761 #[test]
762 fn java_array_attributes() {
763 check_metrics::<JavaParser>(
764 "class X {
765 public byte[] a1, a2, a3, a4; // +4
766 public short b1[], b2[], b3[]; // +3
767 public int[] c1 = { 1 }, c2; // +2
768 public long d1[] = { 1 }; // +1
769 public float[] e1 = { 1.0f, 2.0f, 3.0f }; // +1
770 public double f1[] = { 1.0, 2.0, 3.0 }, f2[]; // +2
771 public boolean[] g1 = new boolean[5], g2, g3; // +3
772 public char[] h1 = new char[5], h2[], h3[], h4[]; // +4
773 byte[] i1;
774 short j1[], j2[];
775 int[] k1, k2, k3 = { 1 };
776 long l1[], l2[] = { 1 }, l3[] = { 2 }, l4[];
777 float[] m1, m2, m3, m4 = { 1.0f, 2.0f, 3.0f };
778 double n1[], n2[] = { 1.0, 2.0, 3.0 }, n3[];
779 boolean[] o1, o2 = new boolean[5];
780 char[] p1 = new char[5];
781 }",
782 "foo.java",
783 |metric| {
784 insta::assert_json_snapshot!(
785 metric.npa,
786 @r#"
787 {
788 "class_npa_sum": 20,
789 "interface_npa_sum": 0,
790 "class_attributes": 40,
791 "interface_attributes": 0,
792 "class_cda": 0.5,
793 "interface_cda": 0.0,
794 "total": 20,
795 "total_attributes": 40,
796 "cda": 0.5
797 }
798 "#
799 );
800 },
801 );
802 }
803
804 #[cfg(feature = "java")]
805 #[test]
806 fn java_object_attributes() {
807 check_metrics::<JavaParser>(
808 "class X {
809 public Integer[] a1 = { 1 }; // +1
810 public Integer b1, b2; // +2
811 public String[] c1 = { \"Hello\" }, c2, c3 = { \"World!\" }; // +3
812 public String d1[][] = { { \"Hello\" }, { \"World!\" } }; // +1
813 public Y[] e1, e2[]; // +2
814 public Y f1[], f2[][], f3[][][]; // +3
815 Integer[] g1 = { new Integer(1) };
816 Integer h1 = new Integer(1), h2 = new Integer(2);
817 String[] i1, i2 = { \"Hello World!\" }, i3;
818 String j1 = \"Hello World!\";
819 Y[] k1[], k2;
820 Y l1[][], l2[], l3 = new Y();
821 }",
822 "foo.java",
823 |metric| {
824 insta::assert_json_snapshot!(
825 metric.npa,
826 @r#"
827 {
828 "class_npa_sum": 12,
829 "interface_npa_sum": 0,
830 "class_attributes": 24,
831 "interface_attributes": 0,
832 "class_cda": 0.5,
833 "interface_cda": 0.0,
834 "total": 12,
835 "total_attributes": 24,
836 "cda": 0.5
837 }
838 "#
839 );
840 },
841 );
842 }
843
844 #[cfg(feature = "groovy")]
845 #[test]
846 fn groovy_no_attributes() {
847 check_metrics::<GroovyParser>("class A { void foo() {} }", "foo.groovy", |metric| {
848 assert_eq!(metric.npa.total_na(), 0);
849 assert_eq!(metric.npa.total_npa(), 0);
850 });
851 }
852
853 #[cfg(feature = "groovy")]
854 #[test]
855 fn groovy_public_attributes() {
856 check_metrics::<GroovyParser>(
857 "class A {
858 public int x
859 public String name
860 private int hidden
861 }",
862 "foo.groovy",
863 |metric| {
864 assert_eq!(metric.npa.class_na_sum(), 3);
866 assert_eq!(metric.npa.class_npa_sum(), 2);
867 },
868 );
869 }
870
871 #[cfg(feature = "groovy")]
872 #[test]
873 fn groovy_def_attributes_not_public() {
874 check_metrics::<GroovyParser>(
878 "class A {
879 def field1
880 def field2
881 }",
882 "foo.groovy",
883 |metric| {
884 assert_eq!(metric.npa.class_na_sum(), 2);
886 assert_eq!(metric.npa.class_npa_sum(), 0);
887 },
888 );
889 }
890
891 #[cfg(feature = "groovy")]
892 #[test]
893 fn groovy_interface_attributes() {
894 check_func_space::<GroovyParser, _>(
898 "interface I {
899 public static final int A = 1
900 public static final int B = 2
901 }",
902 "foo.groovy",
903 |func_space| {
904 let metric = &func_space.metrics;
905 assert_eq!(metric.npa.interface_na_sum(), 2);
907 assert_eq!(metric.npa.interface_npa_sum(), 2);
908 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
909 },
910 );
911 }
912
913 #[cfg(feature = "groovy")]
914 #[test]
915 fn groovy_no_attributes_in_unit_scope() {
916 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
917 assert_eq!(metric.npa.total_na(), 0);
918 });
919 }
920
921 #[cfg(feature = "groovy")]
922 #[test]
923 fn groovy_multiple_classes() {
924 check_metrics::<GroovyParser>(
925 "class A { public int a }
926 class B { public int b }",
927 "foo.groovy",
928 |metric| {
929 assert_eq!(metric.npa.class_na_sum(), 2);
930 assert_eq!(metric.npa.class_npa_sum(), 2);
931 },
932 );
933 }
934
935 #[cfg(feature = "groovy")]
936 #[test]
937 fn groovy_initialized_attributes() {
938 check_metrics::<GroovyParser>(
943 "class X {
944 public int a1 = 1, a2
945 public int b1 = 2
946 int c1, c2 = 3
947 }",
948 "foo.groovy",
949 |metric| {
950 assert_eq!(metric.npa.class_na_sum(), 5);
952 assert_eq!(metric.npa.class_npa_sum(), 3);
953 },
954 );
955 }
956
957 #[cfg(feature = "groovy")]
958 #[test]
959 fn groovy_object_attributes() {
960 check_metrics::<GroovyParser>(
963 "class X {
964 public Integer a1
965 public String b1 = 'hello'
966 public Y[] c1
967 }",
968 "foo.groovy",
969 |metric| {
970 assert_eq!(metric.npa.class_na_sum(), 3);
971 assert_eq!(metric.npa.class_npa_sum(), 3);
972 },
973 );
974 }
975
976 #[cfg(feature = "groovy")]
977 #[test]
978 fn groovy_attribute_modifiers() {
979 check_metrics::<GroovyParser>(
983 "class X {
984 public static int a
985 static public int b
986 public final int c = 1
987 final public int d = 2
988 private static int e
989 int f
990 }",
991 "foo.groovy",
992 |metric| {
993 assert_eq!(metric.npa.class_na_sum(), 6);
995 assert_eq!(metric.npa.class_npa_sum(), 4);
996 },
997 );
998 }
999
1000 #[cfg(feature = "groovy")]
1001 #[test]
1002 #[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')"]
1003 fn groovy_nested_inner_classes() {
1004 check_metrics::<GroovyParser>(
1007 "class X {
1008 public int a
1009 class Y {
1010 public boolean b
1011 class Z {
1012 public char c
1013 }
1014 }
1015 }",
1016 "foo.groovy",
1017 |metric| {
1018 assert_eq!(metric.npa.class_na_sum(), 3);
1020 assert_eq!(metric.npa.class_npa_sum(), 3);
1021 },
1022 );
1023 }
1024
1025 #[cfg(feature = "groovy")]
1026 #[test]
1027 fn groovy_array_attributes() {
1028 check_metrics::<GroovyParser>(
1030 "class X {
1031 public int[] a
1032 public String[] b
1033 int[] c
1034 }",
1035 "foo.groovy",
1036 |metric| {
1037 assert_eq!(metric.npa.class_na_sum(), 3);
1038 assert_eq!(metric.npa.class_npa_sum(), 2);
1039 },
1040 );
1041 }
1042
1043 #[cfg(feature = "groovy")]
1044 #[test]
1045 fn groovy_anonymous_inner_class() {
1046 check_metrics::<GroovyParser>(
1050 "class X {
1051 public Runnable r = new Runnable() {
1052 public int x
1053 void run() {}
1054 }
1055 }",
1056 "foo.groovy",
1057 |metric| {
1058 assert_eq!(metric.npa.class_na_sum(), 2);
1061 assert_eq!(metric.npa.class_npa_sum(), 2);
1062 },
1063 );
1064 }
1065
1066 #[cfg(feature = "groovy")]
1071 #[test]
1072 fn groovy_enum_counts_explicit_public_fields() {
1073 check_metrics::<GroovyParser>(
1074 "enum Status {
1075 ACTIVE, INACTIVE;
1076 public int code;
1077 private int hidden;
1078 }",
1079 "foo.groovy",
1080 |metric| {
1081 assert_eq!(metric.npa.class_na_sum(), 2);
1082 assert_eq!(metric.npa.class_npa_sum(), 1);
1083 },
1084 );
1085 }
1086
1087 #[cfg(feature = "groovy")]
1088 #[test]
1089 fn groovy_annotation_type_counts_constants_as_implicit_public() {
1090 check_func_space::<GroovyParser, _>(
1098 "public @interface Marker {
1099 int VERSION = 1;
1100 String NAME = \"x\";
1101 }",
1102 "foo.groovy",
1103 |func_space| {
1104 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1105 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1106 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1107 },
1108 );
1109 }
1110
1111 #[cfg(feature = "java")]
1112 #[test]
1113 fn java_generic_attributes() {
1114 check_metrics::<JavaParser>(
1115 "class X<T, S extends T> {
1116 public T a1; // +1
1117 public Entry<T, S> b1, b2[]; // +2
1118 public ArrayList<T> c1, c2, c3; // +3
1119 public HashMap<Long, Double> d1, d2; // +2
1120 public TreeSet<String> e1; // +1
1121 S f1;
1122 Entry<S, T> g1[], g2;
1123 ArrayList<S> h1, h2, h3;
1124 HashMap<Long, Float> i1, i2;
1125 TreeSet<Entry<S, T>> j1;
1126 }",
1127 "foo.java",
1128 |metric| {
1129 insta::assert_json_snapshot!(
1130 metric.npa,
1131 @r#"
1132 {
1133 "class_npa_sum": 9,
1134 "interface_npa_sum": 0,
1135 "class_attributes": 18,
1136 "interface_attributes": 0,
1137 "class_cda": 0.5,
1138 "interface_cda": 0.0,
1139 "total": 9,
1140 "total_attributes": 18,
1141 "cda": 0.5
1142 }
1143 "#
1144 );
1145 },
1146 );
1147 }
1148
1149 #[cfg(feature = "java")]
1150 #[test]
1151 fn java_attribute_modifiers() {
1152 check_metrics::<JavaParser>(
1153 "class X {
1154 public transient volatile static int a; // +1
1155 transient public volatile static int b; // +1
1156 transient volatile public static int c; // +1
1157 transient volatile static public int d; // +1
1158 public transient static final int e = 1; // +1
1159 transient public static final int f = 2; // +1
1160 transient static public final int g = 3; // +1
1161 transient static final public int h = 4; // +1
1162 protected transient volatile static int i;
1163 transient volatile static protected int j;
1164 private transient volatile static int k;
1165 transient volatile static private int l;
1166 transient volatile static int m;
1167 transient static final int n = 5;
1168 static public final int o = 6; // +1
1169 final public int p = 7; // +1
1170 }",
1171 "foo.java",
1172 |metric| {
1173 insta::assert_json_snapshot!(
1174 metric.npa,
1175 @r#"
1176 {
1177 "class_npa_sum": 10,
1178 "interface_npa_sum": 0,
1179 "class_attributes": 16,
1180 "interface_attributes": 0,
1181 "class_cda": 0.625,
1182 "interface_cda": 0.0,
1183 "total": 10,
1184 "total_attributes": 16,
1185 "cda": 0.625
1186 }
1187 "#
1188 );
1189 },
1190 );
1191 }
1192
1193 #[cfg(feature = "java")]
1194 #[test]
1195 fn java_classes() {
1196 check_metrics::<JavaParser>(
1197 "class X {
1198 public int a; // +1
1199 public boolean b; // +1
1200 private char c;
1201 }
1202 class Y {
1203 private double d;
1204 private long e;
1205 public float f; // +1
1206 }",
1207 "foo.java",
1208 |metric| {
1209 insta::assert_json_snapshot!(
1210 metric.npa,
1211 @r#"
1212 {
1213 "class_npa_sum": 3,
1214 "interface_npa_sum": 0,
1215 "class_attributes": 6,
1216 "interface_attributes": 0,
1217 "class_cda": 0.5,
1218 "interface_cda": 0.0,
1219 "total": 3,
1220 "total_attributes": 6,
1221 "cda": 0.5
1222 }
1223 "#
1224 );
1225 },
1226 );
1227 }
1228
1229 #[cfg(feature = "java")]
1230 #[test]
1231 fn java_nested_inner_classes() {
1232 check_metrics::<JavaParser>(
1233 "class X {
1234 public int a; // +1
1235 class Y {
1236 public boolean b; // +1
1237 class Z {
1238 public char c; // +1
1239 }
1240 }
1241 }",
1242 "foo.java",
1243 |metric| {
1244 insta::assert_json_snapshot!(
1245 metric.npa,
1246 @r#"
1247 {
1248 "class_npa_sum": 3,
1249 "interface_npa_sum": 0,
1250 "class_attributes": 3,
1251 "interface_attributes": 0,
1252 "class_cda": 1.0,
1253 "interface_cda": 0.0,
1254 "total": 3,
1255 "total_attributes": 3,
1256 "cda": 1.0
1257 }
1258 "#
1259 );
1260 },
1261 );
1262 }
1263
1264 #[cfg(feature = "java")]
1265 #[test]
1266 fn java_local_inner_classes() {
1267 check_metrics::<JavaParser>(
1268 "class X {
1269 public int a; // +1
1270 void x() {
1271 class Y {
1272 public boolean b; // +1
1273 void y() {
1274 class Z {
1275 public char c; // +1
1276 void z() {}
1277 }
1278 }
1279 }
1280 }
1281 }",
1282 "foo.java",
1283 |metric| {
1284 insta::assert_json_snapshot!(
1285 metric.npa,
1286 @r#"
1287 {
1288 "class_npa_sum": 3,
1289 "interface_npa_sum": 0,
1290 "class_attributes": 3,
1291 "interface_attributes": 0,
1292 "class_cda": 1.0,
1293 "interface_cda": 0.0,
1294 "total": 3,
1295 "total_attributes": 3,
1296 "cda": 1.0
1297 }
1298 "#
1299 );
1300 },
1301 );
1302 }
1303
1304 #[cfg(feature = "java")]
1305 #[test]
1306 fn java_anonymous_inner_classes() {
1307 check_metrics::<JavaParser>(
1308 "abstract class X {
1309 public int a; // +1
1310 }
1311 abstract class Y {
1312 boolean b;
1313 }
1314 class Z {
1315 public char c; // +1
1316 public void z(){
1317 X x1 = new X() {
1318 public double d; // +1
1319 };
1320 Y y1 = new Y() {
1321 long e;
1322 };
1323 }
1324 }",
1325 "foo.java",
1326 |metric| {
1327 insta::assert_json_snapshot!(
1328 metric.npa,
1329 @r#"
1330 {
1331 "class_npa_sum": 3,
1332 "interface_npa_sum": 0,
1333 "class_attributes": 5,
1334 "interface_attributes": 0,
1335 "class_cda": 0.6,
1336 "interface_cda": 0.0,
1337 "total": 3,
1338 "total_attributes": 5,
1339 "cda": 0.6
1340 }
1341 "#
1342 );
1343 },
1344 );
1345 }
1346
1347 #[cfg(feature = "java")]
1348 #[test]
1349 fn java_interface() {
1350 check_metrics::<JavaParser>(
1351 "interface X {
1352 public int a = 0; // +1
1353 static boolean b = false; // +1
1354 final char c = ' '; // +1
1355 }",
1356 "foo.java",
1357 |metric| {
1358 insta::assert_json_snapshot!(
1359 metric.npa,
1360 @r#"
1361 {
1362 "class_npa_sum": 0,
1363 "interface_npa_sum": 3,
1364 "class_attributes": 0,
1365 "interface_attributes": 3,
1366 "class_cda": 0.0,
1367 "interface_cda": 1.0,
1368 "total": 3,
1369 "total_attributes": 3,
1370 "cda": 1.0
1371 }
1372 "#
1373 );
1374 },
1375 );
1376 }
1377
1378 #[cfg(feature = "java")]
1382 #[test]
1383 fn java_enum_counts_explicit_public_fields() {
1384 check_metrics::<JavaParser>(
1385 "enum Status {
1386 ACTIVE, INACTIVE;
1387 public static final int FLAG = 1; // implicit static final, still public
1388 public int code; // +1 explicit public
1389 private int hidden; // not public
1390 }",
1391 "foo.java",
1392 |metric| {
1393 assert_eq!(metric.npa.class_na_sum(), 3);
1395 assert_eq!(metric.npa.class_npa_sum(), 2);
1396 },
1397 );
1398 }
1399
1400 #[cfg(feature = "java")]
1407 #[test]
1408 fn java_record_counts_explicit_body_fields() {
1409 check_metrics::<JavaParser>(
1410 "record Point(int x, int y) {
1411 public static int origin = 0; // explicit body, public
1412 private int cached; // explicit body, private
1413 }",
1414 "foo.java",
1415 |metric| {
1416 assert_eq!(metric.npa.class_na_sum(), 2);
1419 assert_eq!(metric.npa.class_npa_sum(), 1);
1420 },
1421 );
1422 }
1423
1424 #[cfg(feature = "java")]
1425 #[test]
1426 fn java_annotation_type_counts_constants_as_implicit_public() {
1427 check_func_space::<JavaParser, _>(
1436 "@interface Marker {
1437 int VERSION = 1; // implicit public static final
1438 String NAME = \"x\"; // implicit public static final
1439 }",
1440 "foo.java",
1441 |func_space| {
1442 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1443 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1444 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1445 },
1446 );
1447 }
1448
1449 #[cfg(feature = "php")]
1450 #[test]
1451 fn php_no_class_attributes() {
1452 check_metrics::<PhpParser>(
1453 "<?php class A { public function f(): void {} }",
1454 "foo.php",
1455 |metric| insta::assert_json_snapshot!(metric.npa),
1456 );
1457 }
1458
1459 #[cfg(feature = "csharp")]
1460 #[test]
1461 fn csharp_single_attributes() {
1462 check_metrics::<CsharpParser>(
1463 "class X {
1464 public byte a;
1465 public short b;
1466 public int c;
1467 public long d;
1468 public float e;
1469 public double f;
1470 public bool g;
1471 public char h;
1472 byte i;
1473 short j;
1474 int k;
1475 long l;
1476 float m;
1477 double n;
1478 bool o;
1479 char p;
1480 }",
1481 "foo.cs",
1482 |metric| {
1483 assert_eq!(metric.npa.class_npa_sum(), 8);
1484 assert_eq!(metric.npa.class_na_sum(), 16);
1485 assert_eq!(metric.npa.interface_na_sum(), 0);
1486 insta::assert_json_snapshot!(metric.npa);
1487 },
1488 );
1489 }
1490
1491 #[cfg(feature = "csharp")]
1492 #[test]
1493 fn csharp_multiple_attributes() {
1494 check_metrics::<CsharpParser>(
1495 "class X {
1496 public byte a1;
1497 public short b1, b2;
1498 public int c1, c2, c3;
1499 public long d1, d2, d3, d4;
1500 public bool g1, g2;
1501 byte i1, i2, i3, i4;
1502 int k1, k2;
1503 }",
1504 "foo.cs",
1505 |metric| {
1506 assert_eq!(metric.npa.class_npa_sum(), 12);
1507 assert_eq!(metric.npa.class_na_sum(), 18);
1508 assert_eq!(metric.npa.interface_na_sum(), 0);
1509 insta::assert_json_snapshot!(metric.npa);
1510 },
1511 );
1512 }
1513
1514 #[cfg(feature = "csharp")]
1515 #[test]
1516 fn csharp_initialized_attributes() {
1517 check_metrics::<CsharpParser>(
1518 "class X {
1519 public int a = 1;
1520 public bool b = true;
1521 public string c = \"hello\";
1522 public double d = 3.14;
1523 int e = 0;
1524 }",
1525 "foo.cs",
1526 |metric| {
1527 assert_eq!(metric.npa.class_npa_sum(), 4);
1528 assert_eq!(metric.npa.class_na_sum(), 5);
1529 assert_eq!(metric.npa.interface_na_sum(), 0);
1530 insta::assert_json_snapshot!(metric.npa);
1531 },
1532 );
1533 }
1534
1535 #[cfg(feature = "csharp")]
1536 #[test]
1537 fn csharp_array_attributes() {
1538 check_metrics::<CsharpParser>(
1539 "class X {
1540 public int[] a;
1541 public string[] b = new string[5];
1542 int[] c;
1543 }",
1544 "foo.cs",
1545 |metric| {
1546 assert_eq!(metric.npa.class_npa_sum(), 2);
1547 assert_eq!(metric.npa.class_na_sum(), 3);
1548 assert_eq!(metric.npa.interface_na_sum(), 0);
1549 insta::assert_json_snapshot!(metric.npa);
1550 },
1551 );
1552 }
1553
1554 #[cfg(feature = "csharp")]
1555 #[test]
1556 fn csharp_object_attributes() {
1557 check_metrics::<CsharpParser>(
1558 "class Point { public int X, Y; }
1559 class Shape {
1560 public Point origin;
1561 public Point endpoint = new Point();
1562 Point hidden;
1563 }",
1564 "foo.cs",
1565 |metric| {
1566 assert_eq!(metric.npa.class_npa_sum(), 4);
1567 assert_eq!(metric.npa.class_na_sum(), 5);
1568 assert_eq!(metric.npa.interface_na_sum(), 0);
1569 insta::assert_json_snapshot!(metric.npa);
1570 },
1571 );
1572 }
1573
1574 #[cfg(feature = "csharp")]
1575 #[test]
1576 fn csharp_generic_attributes() {
1577 check_metrics::<CsharpParser>(
1578 "class X {
1579 public System.Collections.Generic.List<int> a;
1580 public System.Collections.Generic.Dictionary<string, int> b;
1581 System.Collections.Generic.List<string> c;
1582 }",
1583 "foo.cs",
1584 |metric| {
1585 assert_eq!(metric.npa.class_npa_sum(), 2);
1586 assert_eq!(metric.npa.class_na_sum(), 3);
1587 assert_eq!(metric.npa.interface_na_sum(), 0);
1588 insta::assert_json_snapshot!(metric.npa);
1589 },
1590 );
1591 }
1592
1593 #[cfg(feature = "csharp")]
1594 #[test]
1595 fn csharp_attribute_modifiers() {
1596 check_metrics::<CsharpParser>(
1597 "class X {
1598 public int a;
1599 private int b;
1600 protected int c;
1601 internal int d;
1602 public static int e;
1603 public readonly int f;
1604 public const int g = 1;
1605 }",
1606 "foo.cs",
1607 |metric| {
1608 assert_eq!(metric.npa.class_npa_sum(), 4);
1611 assert_eq!(metric.npa.class_na_sum(), 7);
1612 assert_eq!(metric.npa.interface_na_sum(), 0);
1613 insta::assert_json_snapshot!(metric.npa);
1614 },
1615 );
1616 }
1617
1618 #[cfg(feature = "csharp")]
1619 #[test]
1620 fn csharp_classes() {
1621 check_metrics::<CsharpParser>(
1622 "class A {
1623 public int a;
1624 public int b;
1625 int c;
1626 }
1627 class B {
1628 public string s;
1629 int n;
1630 }",
1631 "foo.cs",
1632 |metric| {
1633 assert_eq!(metric.npa.class_npa_sum(), 3);
1634 assert_eq!(metric.npa.class_na_sum(), 5);
1635 assert_eq!(metric.npa.interface_na_sum(), 0);
1636 insta::assert_json_snapshot!(metric.npa);
1637 },
1638 );
1639 }
1640
1641 #[cfg(feature = "csharp")]
1642 #[test]
1643 fn csharp_nested_inner_classes() {
1644 check_metrics::<CsharpParser>(
1645 "class Outer {
1646 public int a;
1647 int b;
1648 public class Inner {
1649 public string s;
1650 int n;
1651 }
1652 }",
1653 "foo.cs",
1654 |metric| {
1655 assert_eq!(metric.npa.class_npa_sum(), 2);
1656 assert_eq!(metric.npa.class_na_sum(), 4);
1657 assert_eq!(metric.npa.interface_na_sum(), 0);
1658 insta::assert_json_snapshot!(metric.npa);
1659 },
1660 );
1661 }
1662
1663 #[cfg(feature = "csharp")]
1664 #[test]
1665 fn csharp_struct_attributes() {
1666 check_metrics::<CsharpParser>(
1669 "struct Point {
1670 public int X;
1671 public int Y;
1672 int Hidden;
1673 }",
1674 "foo.cs",
1675 |metric| {
1676 assert_eq!(metric.npa.class_npa_sum(), 2);
1677 assert_eq!(metric.npa.class_na_sum(), 3);
1678 assert_eq!(metric.npa.interface_na_sum(), 0);
1679 insta::assert_json_snapshot!(metric.npa);
1680 },
1681 );
1682 }
1683
1684 #[cfg(feature = "csharp")]
1685 #[test]
1686 fn csharp_record_attributes() {
1687 check_metrics::<CsharpParser>(
1690 "record Person {
1691 public string Name;
1692 int Age;
1693 }",
1694 "foo.cs",
1695 |metric| {
1696 assert_eq!(metric.npa.class_npa_sum(), 1);
1697 assert_eq!(metric.npa.class_na_sum(), 2);
1698 assert_eq!(metric.npa.interface_na_sum(), 0);
1699 insta::assert_json_snapshot!(metric.npa);
1700 },
1701 );
1702 }
1703
1704 #[cfg(feature = "csharp")]
1705 #[test]
1706 fn csharp_interface() {
1707 check_func_space::<CsharpParser, _>(
1712 "interface I {
1713 static int A = 1;
1714 static string B = \"hello\";
1715 }",
1716 "foo.cs",
1717 |func_space| {
1718 let metric = &func_space.metrics;
1719 assert_eq!(metric.npa.class_na_sum(), 0);
1720 assert_eq!(metric.npa.interface_na_sum(), 2);
1721 assert_eq!(metric.npa.interface_npa_sum(), 2);
1724 insta::assert_json_snapshot!(metric.npa);
1725 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1726 },
1727 );
1728 }
1729
1730 #[cfg(feature = "csharp")]
1731 #[test]
1732 fn csharp_interface_explicit_modifiers() {
1733 check_metrics::<CsharpParser>(
1738 "interface I {
1739 private static int Hidden = 1;
1740 protected static int AlsoHidden = 2;
1741 public static int Shown = 3;
1742 static int Implicit = 4;
1743 }",
1744 "foo.cs",
1745 |metric| {
1746 assert_eq!(metric.npa.interface_na_sum(), 4);
1747 assert_eq!(metric.npa.interface_npa_sum(), 2);
1750 assert_eq!(metric.npa.class_na_sum(), 0);
1751 insta::assert_json_snapshot!(metric.npa);
1752 },
1753 );
1754 }
1755
1756 #[cfg(feature = "csharp")]
1757 #[test]
1758 fn csharp_interface_multi_declarator_modifier() {
1759 check_metrics::<CsharpParser>(
1764 "interface I {
1765 private static int a = 1, b = 2;
1766 static int c = 3, d = 4;
1767 }",
1768 "foo.cs",
1769 |metric| {
1770 assert_eq!(metric.npa.interface_na_sum(), 4);
1771 assert_eq!(metric.npa.interface_npa_sum(), 2);
1772 assert_eq!(metric.npa.class_na_sum(), 0);
1773 insta::assert_json_snapshot!(metric.npa);
1774 },
1775 );
1776 }
1777
1778 #[cfg(feature = "php")]
1779 #[test]
1780 fn php_one_public_attribute() {
1781 check_metrics::<PhpParser>(
1782 "<?php class A { public int $x = 0; }",
1783 "foo.php",
1784 |metric| insta::assert_json_snapshot!(metric.npa),
1785 );
1786 }
1787
1788 #[cfg(feature = "php")]
1789 #[test]
1790 fn php_one_private_attribute() {
1791 check_metrics::<PhpParser>(
1792 "<?php class A { private int $x = 0; }",
1793 "foo.php",
1794 |metric| insta::assert_json_snapshot!(metric.npa),
1795 );
1796 }
1797
1798 #[cfg(feature = "php")]
1799 #[test]
1800 fn php_one_protected_attribute() {
1801 check_metrics::<PhpParser>(
1802 "<?php class A { protected int $x = 0; }",
1803 "foo.php",
1804 |metric| insta::assert_json_snapshot!(metric.npa),
1805 );
1806 }
1807
1808 #[cfg(feature = "php")]
1809 #[test]
1810 fn php_mixed_visibility_attributes() {
1811 check_metrics::<PhpParser>(
1812 "<?php
1813 class A {
1814 public int $a = 0;
1815 public int $b = 0;
1816 private int $c = 0;
1817 protected int $d = 0;
1818 }",
1819 "foo.php",
1820 |metric| insta::assert_json_snapshot!(metric.npa),
1821 );
1822 }
1823
1824 #[cfg(feature = "php")]
1825 #[test]
1826 fn php_static_public_attribute() {
1827 check_metrics::<PhpParser>(
1828 "<?php class A { public static int $x = 0; }",
1829 "foo.php",
1830 |metric| insta::assert_json_snapshot!(metric.npa),
1831 );
1832 }
1833
1834 #[cfg(feature = "php")]
1835 #[test]
1836 fn php_readonly_public_attribute() {
1837 check_metrics::<PhpParser>(
1838 "<?php class A { public readonly int $x; }",
1839 "foo.php",
1840 |metric| insta::assert_json_snapshot!(metric.npa),
1841 );
1842 }
1843
1844 #[cfg(feature = "php")]
1845 #[test]
1846 fn php_multiple_attributes_per_declaration() {
1847 check_metrics::<PhpParser>(
1850 "<?php class A { public int $a = 0, $b = 0, $c = 0; }",
1851 "foo.php",
1852 |metric| insta::assert_json_snapshot!(metric.npa),
1853 );
1854 }
1855
1856 #[cfg(feature = "php")]
1857 #[test]
1858 fn php_interface_constants() {
1859 check_metrics::<PhpParser>(
1861 "<?php
1862 interface I {
1863 const A = 1;
1864 const B = 2;
1865 }",
1866 "foo.php",
1867 |metric| insta::assert_json_snapshot!(metric.npa),
1868 );
1869 }
1870
1871 #[cfg(feature = "php")]
1872 #[test]
1873 fn php_enum_cases_not_counted() {
1874 check_metrics::<PhpParser>(
1879 "<?php
1880 enum Color {
1881 case Red;
1882 case Green;
1883 case Blue;
1884 }",
1885 "foo.php",
1886 |metric| {
1887 assert_eq!(metric.npa.class_na_sum(), 0);
1888 assert_eq!(metric.npa.class_npa_sum(), 0);
1889 assert_eq!(metric.npa.interface_na_sum(), 0);
1890 insta::assert_json_snapshot!(metric.npa);
1891 },
1892 );
1893 }
1894
1895 #[cfg(feature = "php")]
1896 #[test]
1897 fn php_enum_const_not_counted() {
1898 check_metrics::<PhpParser>(
1903 "<?php
1904 enum Suit: string {
1905 case Hearts = 'H';
1906 case Diamonds = 'D';
1907 const Wild = 'W';
1908 public function label(): string { return $this->name; }
1909 }",
1910 "foo.php",
1911 |metric| {
1912 assert_eq!(metric.npa.class_na_sum(), 0);
1913 assert_eq!(metric.npa.class_npa_sum(), 0);
1914 },
1915 );
1916 }
1917
1918 #[cfg(all(feature = "java", feature = "php"))]
1919 #[test]
1920 fn php_enum_npa_matches_java_enum_npa() {
1921 check_metrics::<PhpParser>(
1927 "<?php
1928 enum Color {
1929 case Red;
1930 case Green;
1931 case Blue;
1932 }",
1933 "foo.php",
1934 |metric| {
1935 assert_eq!(metric.npa.class_na_sum(), 0);
1936 assert_eq!(metric.npa.class_npa_sum(), 0);
1937 },
1938 );
1939 check_metrics::<JavaParser>(
1940 "enum Color {
1941 RED, GREEN, BLUE;
1942 }",
1943 "foo.java",
1944 |metric| {
1945 assert_eq!(metric.npa.class_na_sum(), 0);
1946 assert_eq!(metric.npa.class_npa_sum(), 0);
1947 },
1948 );
1949 }
1950
1951 #[cfg(feature = "php")]
1952 #[test]
1953 fn php_trait_attributes() {
1954 check_metrics::<PhpParser>(
1955 "<?php
1956 trait T {
1957 public int $a = 0;
1958 private int $b = 0;
1959 }",
1960 "foo.php",
1961 |metric| insta::assert_json_snapshot!(metric.npa),
1962 );
1963 }
1964
1965 #[cfg(feature = "php")]
1966 #[test]
1967 fn php_no_explicit_visibility_excluded() {
1968 check_metrics::<PhpParser>("<?php class A { var $x = 0; }", "foo.php", |metric| {
1972 assert_eq!(metric.npa.class_npa_sum(), 0);
1976 assert_eq!(metric.npa.class_na_sum(), 1);
1977 assert_eq!(metric.npa.interface_na_sum(), 0);
1978 insta::assert_json_snapshot!(metric.npa);
1979 });
1980 }
1981
1982 #[cfg(feature = "php")]
1983 #[test]
1984 fn php_anonymous_class_attributes() {
1985 check_metrics::<PhpParser>(
1990 "<?php
1991 $obj = new class {
1992 public int $a = 0;
1993 private int $b = 0;
1994 };",
1995 "foo.php",
1996 |metric| insta::assert_json_snapshot!(metric.npa),
1997 );
1998 }
1999
2000 #[cfg(feature = "php")]
2001 #[test]
2002 fn php_property_promotion_excluded() {
2003 check_metrics::<PhpParser>(
2011 "<?php
2012 class A {
2013 public function __construct(public string $x, public int $y) {}
2014 }",
2015 "foo.php",
2016 |metric| insta::assert_json_snapshot!(metric.npa),
2017 );
2018 }
2019
2020 #[cfg(feature = "kotlin")]
2030 #[test]
2031 fn kotlin_empty_class_no_attributes() {
2032 check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
2033 assert_eq!(metric.npa.class_npa_sum(), 0);
2034 assert_eq!(metric.npa.class_na_sum(), 0);
2035 assert_eq!(metric.npa.interface_na_sum(), 0);
2036 insta::assert_json_snapshot!(metric.npa);
2037 });
2038 }
2039
2040 #[cfg(feature = "kotlin")]
2041 #[test]
2042 fn kotlin_public_val_var_default() {
2043 check_metrics::<KotlinParser>(
2045 "class C {
2046 val a: Int = 1
2047 var b: Int = 2
2048 val c: String = \"hi\"
2049 }",
2050 "foo.kt",
2051 |metric| {
2052 assert_eq!(metric.npa.class_npa_sum(), 3);
2053 assert_eq!(metric.npa.class_na_sum(), 3);
2054 insta::assert_json_snapshot!(metric.npa);
2055 },
2056 );
2057 }
2058
2059 #[cfg(feature = "kotlin")]
2060 #[test]
2061 fn kotlin_private_val_var() {
2062 check_metrics::<KotlinParser>(
2064 "class C {
2065 val a: Int = 1 // public
2066 private val b: Int = 2 // not public
2067 var c: Int = 3 // public
2068 private var d: Int = 4 // not public
2069 }",
2070 "foo.kt",
2071 |metric| {
2072 assert_eq!(metric.npa.class_npa_sum(), 2);
2073 assert_eq!(metric.npa.class_na_sum(), 4);
2074 insta::assert_json_snapshot!(metric.npa);
2075 },
2076 );
2077 }
2078
2079 #[cfg(feature = "kotlin")]
2080 #[test]
2081 fn kotlin_protected_internal_excluded_from_public() {
2082 check_metrics::<KotlinParser>(
2083 "open class C {
2084 protected val a: Int = 1
2085 internal val b: Int = 2
2086 public val c: Int = 3 // explicit public
2087 }",
2088 "foo.kt",
2089 |metric| {
2090 assert_eq!(metric.npa.class_npa_sum(), 1);
2091 assert_eq!(metric.npa.class_na_sum(), 3);
2092 insta::assert_json_snapshot!(metric.npa);
2093 },
2094 );
2095 }
2096
2097 #[cfg(feature = "kotlin")]
2098 #[test]
2099 fn kotlin_primary_constructor_parameter_property() {
2100 check_metrics::<KotlinParser>(
2104 "class C(val a: Int, var b: Int, c: Int) {
2105 val d: Int = c
2106 }",
2107 "foo.kt",
2108 |metric| {
2109 assert_eq!(metric.npa.class_npa_sum(), 3);
2111 assert_eq!(metric.npa.class_na_sum(), 3);
2112 insta::assert_json_snapshot!(metric.npa);
2113 },
2114 );
2115 }
2116
2117 #[cfg(feature = "kotlin")]
2118 #[test]
2119 fn kotlin_primary_constructor_private_param_property() {
2120 check_metrics::<KotlinParser>(
2121 "class C(private val a: Int, val b: Int)",
2122 "foo.kt",
2123 |metric| {
2124 assert_eq!(metric.npa.class_npa_sum(), 1);
2125 assert_eq!(metric.npa.class_na_sum(), 2);
2126 insta::assert_json_snapshot!(metric.npa);
2127 },
2128 );
2129 }
2130
2131 #[cfg(feature = "kotlin")]
2132 #[test]
2133 fn kotlin_secondary_constructor_does_not_add_attrs() {
2134 check_metrics::<KotlinParser>(
2136 "class C {
2137 private var a: Int = 0
2138 constructor(n: Int) { a = n }
2139 }",
2140 "foo.kt",
2141 |metric| {
2142 assert_eq!(metric.npa.class_npa_sum(), 0);
2143 assert_eq!(metric.npa.class_na_sum(), 1);
2144 insta::assert_json_snapshot!(metric.npa);
2145 },
2146 );
2147 }
2148
2149 #[cfg(feature = "kotlin")]
2150 #[test]
2151 fn kotlin_companion_object_attributes() {
2152 check_metrics::<KotlinParser>(
2155 "class Holder {
2156 val instance: Int = 1
2157 companion object {
2158 val SCALE: Int = 10
2159 private val SECRET: Int = 7
2160 }
2161 }",
2162 "foo.kt",
2163 |metric| {
2164 assert_eq!(metric.npa.class_npa_sum(), 2);
2167 assert_eq!(metric.npa.class_na_sum(), 3);
2168 insta::assert_json_snapshot!(metric.npa);
2169 },
2170 );
2171 }
2172
2173 #[cfg(feature = "kotlin")]
2174 #[test]
2175 fn kotlin_data_class_attributes() {
2176 check_metrics::<KotlinParser>(
2178 "data class Point(val x: Int, val y: Int)",
2179 "foo.kt",
2180 |metric| {
2181 assert_eq!(metric.npa.class_npa_sum(), 2);
2182 assert_eq!(metric.npa.class_na_sum(), 2);
2183 insta::assert_json_snapshot!(metric.npa);
2184 },
2185 );
2186 }
2187
2188 #[cfg(feature = "kotlin")]
2189 #[test]
2190 fn kotlin_object_singleton_attributes() {
2191 check_metrics::<KotlinParser>(
2192 "object Config {
2193 val DEFAULT: Int = 42
2194 private val SEED: Int = 0
2195 var debug: Boolean = false
2196 }",
2197 "foo.kt",
2198 |metric| {
2199 assert_eq!(metric.npa.class_npa_sum(), 2);
2201 assert_eq!(metric.npa.class_na_sum(), 3);
2202 insta::assert_json_snapshot!(metric.npa);
2203 },
2204 );
2205 }
2206
2207 #[cfg(feature = "kotlin")]
2208 #[test]
2209 fn kotlin_interface_attributes() {
2210 check_func_space::<KotlinParser, _>(
2216 "interface I {
2217 val a: Int
2218 val b: String
2219 }",
2220 "foo.kt",
2221 |func_space| {
2222 let metric = &func_space.metrics;
2223 assert_eq!(metric.npa.interface_npa_sum(), 2);
2224 assert_eq!(metric.npa.interface_na_sum(), 2);
2225 assert_eq!(metric.npa.class_na_sum(), 0);
2226 insta::assert_json_snapshot!(metric.npa);
2227 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2228 },
2229 );
2230 }
2231
2232 #[cfg(feature = "kotlin")]
2233 #[test]
2234 fn kotlin_nested_class_attributes() {
2235 check_metrics::<KotlinParser>(
2238 "class Outer {
2239 val o1: Int = 1
2240 class Nested {
2241 val n1: Int = 1
2242 val n2: Int = 2
2243 }
2244 }",
2245 "foo.kt",
2246 |metric| {
2247 assert_eq!(metric.npa.class_npa_sum(), 3);
2249 assert_eq!(metric.npa.class_na_sum(), 3);
2250 insta::assert_json_snapshot!(metric.npa);
2251 },
2252 );
2253 }
2254
2255 #[cfg(feature = "kotlin")]
2256 #[test]
2257 fn kotlin_inner_class_attributes() {
2258 check_metrics::<KotlinParser>(
2259 "class Outer {
2260 val o1: Int = 1
2261 inner class Inner {
2262 val i1: Int = 1
2263 }
2264 }",
2265 "foo.kt",
2266 |metric| {
2267 assert_eq!(metric.npa.class_npa_sum(), 2);
2268 assert_eq!(metric.npa.class_na_sum(), 2);
2269 insta::assert_json_snapshot!(metric.npa);
2270 },
2271 );
2272 }
2273
2274 #[cfg(feature = "kotlin")]
2275 #[test]
2276 fn kotlin_top_level_properties_excluded() {
2277 check_metrics::<KotlinParser>(
2280 "val topVal: Int = 0
2281 var topVar: Int = 1
2282 class C { val x: Int = 0 }",
2283 "foo.kt",
2284 |metric| {
2285 assert_eq!(metric.npa.class_npa_sum(), 1);
2286 assert_eq!(metric.npa.class_na_sum(), 1);
2287 insta::assert_json_snapshot!(metric.npa);
2288 },
2289 );
2290 }
2291
2292 #[cfg(feature = "kotlin")]
2293 #[test]
2294 fn kotlin_multiple_classes_attributes() {
2295 check_metrics::<KotlinParser>(
2296 "class A {
2297 val a1: Int = 0
2298 var a2: Int = 0
2299 }
2300 class B {
2301 val b1: Int = 0
2302 private val b2: Int = 0
2303 }",
2304 "foo.kt",
2305 |metric| {
2306 assert_eq!(metric.npa.class_npa_sum(), 3);
2308 assert_eq!(metric.npa.class_na_sum(), 4);
2309 insta::assert_json_snapshot!(metric.npa);
2310 },
2311 );
2312 }
2313
2314 #[cfg(feature = "kotlin")]
2315 #[test]
2316 fn kotlin_class_with_methods_no_attrs() {
2317 check_metrics::<KotlinParser>(
2319 "class C {
2320 fun m1() {}
2321 fun m2(): Int = 0
2322 }",
2323 "foo.kt",
2324 |metric| {
2325 assert_eq!(metric.npa.class_npa_sum(), 0);
2326 assert_eq!(metric.npa.class_na_sum(), 0);
2327 insta::assert_json_snapshot!(metric.npa);
2328 },
2329 );
2330 }
2331
2332 #[cfg(feature = "typescript")]
2344 #[test]
2345 fn typescript_empty_class_no_attributes() {
2346 check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
2347 assert_eq!(metric.npa.class_npa_sum(), 0);
2348 assert_eq!(metric.npa.class_na_sum(), 0);
2349 insta::assert_json_snapshot!(metric.npa);
2350 });
2351 }
2352
2353 #[cfg(feature = "typescript")]
2354 #[test]
2355 fn typescript_default_public_fields() {
2356 check_metrics::<TypescriptParser>(
2358 "class C {
2359 a: number = 1;
2360 b: string = \"\";
2361 c: boolean = false;
2362 }",
2363 "foo.ts",
2364 |metric| {
2365 assert_eq!(metric.npa.class_npa_sum(), 3);
2366 assert_eq!(metric.npa.class_na_sum(), 3);
2367 insta::assert_json_snapshot!(metric.npa);
2368 },
2369 );
2370 }
2371
2372 #[cfg(feature = "typescript")]
2373 #[test]
2374 fn typescript_visibility_modifiers() {
2375 check_metrics::<TypescriptParser>(
2377 "class C {
2378 public a: number = 1;
2379 private b: number = 2;
2380 protected c: number = 3;
2381 d: number = 4;
2382 }",
2383 "foo.ts",
2384 |metric| {
2385 assert_eq!(metric.npa.class_npa_sum(), 2);
2387 assert_eq!(metric.npa.class_na_sum(), 4);
2388 insta::assert_json_snapshot!(metric.npa);
2389 },
2390 );
2391 }
2392
2393 #[cfg(feature = "typescript")]
2394 #[test]
2395 fn typescript_static_fields() {
2396 check_metrics::<TypescriptParser>(
2398 "class C {
2399 static a: number = 0;
2400 public static b: number = 0;
2401 private static c: number = 0;
2402 }",
2403 "foo.ts",
2404 |metric| {
2405 assert_eq!(metric.npa.class_npa_sum(), 2);
2407 assert_eq!(metric.npa.class_na_sum(), 3);
2408 insta::assert_json_snapshot!(metric.npa);
2409 },
2410 );
2411 }
2412
2413 #[cfg(feature = "typescript")]
2414 #[test]
2415 fn typescript_parameter_properties() {
2416 check_metrics::<TypescriptParser>(
2418 "class C {
2419 constructor(public a: number, private b: string, c: boolean) {}
2420 }",
2421 "foo.ts",
2422 |metric| {
2423 assert_eq!(metric.npa.class_npa_sum(), 1);
2426 assert_eq!(metric.npa.class_na_sum(), 2);
2427 insta::assert_json_snapshot!(metric.npa);
2428 },
2429 );
2430 }
2431
2432 #[cfg(feature = "typescript")]
2433 #[test]
2434 fn typescript_readonly_constructor_param_property() {
2435 check_metrics::<TypescriptParser>(
2442 "class C {
2443 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2444 }",
2445 "foo.ts",
2446 |metric| {
2447 assert_eq!(metric.npa.class_na_sum(), 4);
2451 assert_eq!(metric.npa.class_npa_sum(), 2);
2452 insta::assert_json_snapshot!(metric.npa);
2453 },
2454 );
2455 }
2456
2457 #[cfg(feature = "typescript")]
2458 #[test]
2459 fn typescript_readonly_field() {
2460 check_metrics::<TypescriptParser>(
2463 "class C {
2464 readonly a: number = 1;
2465 private readonly b: number = 2;
2466 }",
2467 "foo.ts",
2468 |metric| {
2469 assert_eq!(metric.npa.class_npa_sum(), 1);
2470 assert_eq!(metric.npa.class_na_sum(), 2);
2471 insta::assert_json_snapshot!(metric.npa);
2472 },
2473 );
2474 }
2475
2476 #[cfg(feature = "typescript")]
2477 #[test]
2478 fn typescript_abstract_class_attributes() {
2479 check_metrics::<TypescriptParser>(
2482 "abstract class C {
2483 public a: number = 1;
2484 protected b: number = 2;
2485 abstract m(): void;
2486 }",
2487 "foo.ts",
2488 |metric| {
2489 assert_eq!(metric.npa.class_npa_sum(), 1);
2492 assert_eq!(metric.npa.class_na_sum(), 2);
2493 insta::assert_json_snapshot!(metric.npa);
2494 },
2495 );
2496 }
2497
2498 #[cfg(feature = "typescript")]
2499 #[test]
2500 fn typescript_arrow_field_is_method_not_attribute() {
2501 check_metrics::<TypescriptParser>(
2504 "class C {
2505 a: number = 0;
2506 arrow = () => this.a;
2507 }",
2508 "foo.ts",
2509 |metric| {
2510 assert_eq!(metric.npa.class_npa_sum(), 1);
2511 assert_eq!(metric.npa.class_na_sum(), 1);
2512 insta::assert_json_snapshot!(metric.npa);
2513 },
2514 );
2515 }
2516
2517 #[cfg(feature = "typescript")]
2518 #[test]
2519 fn typescript_interface_property_signatures() {
2520 check_func_space::<TypescriptParser, _>(
2526 "interface I {
2527 a: number;
2528 b: string;
2529 m(): void;
2530 }",
2531 "foo.ts",
2532 |func_space| {
2533 let metric = &func_space.metrics;
2534 assert_eq!(metric.npa.interface_npa_sum(), 2);
2535 assert_eq!(metric.npa.interface_na_sum(), 2);
2536 assert_eq!(metric.npa.class_na_sum(), 0);
2537 insta::assert_json_snapshot!(metric.npa);
2538 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2539 },
2540 );
2541 }
2542
2543 #[cfg(feature = "typescript")]
2544 #[test]
2545 fn typescript_generic_class_attributes() {
2546 check_metrics::<TypescriptParser>(
2548 "class Box<T, U> {
2549 value: T;
2550 other: U;
2551 constructor(v: T, o: U) { this.value = v; this.other = o; }
2552 }",
2553 "foo.ts",
2554 |metric| {
2555 assert_eq!(metric.npa.class_npa_sum(), 2);
2556 assert_eq!(metric.npa.class_na_sum(), 2);
2557 insta::assert_json_snapshot!(metric.npa);
2558 },
2559 );
2560 }
2561
2562 #[cfg(feature = "typescript")]
2563 #[test]
2564 fn typescript_getters_setters_not_attributes() {
2565 check_metrics::<TypescriptParser>(
2567 "class C {
2568 private _x: number = 0;
2569 get x(): number { return this._x; }
2570 set x(v: number) { this._x = v; }
2571 }",
2572 "foo.ts",
2573 |metric| {
2574 assert_eq!(metric.npa.class_npa_sum(), 0);
2576 assert_eq!(metric.npa.class_na_sum(), 1);
2577 insta::assert_json_snapshot!(metric.npa);
2578 },
2579 );
2580 }
2581
2582 #[cfg(feature = "typescript")]
2583 #[test]
2584 fn typescript_multiple_classes_and_interface() {
2585 check_func_space::<TypescriptParser, _>(
2586 "class A { x: number = 0; }
2587 class B { private y: number = 0; }
2588 interface I { z: number; }",
2589 "foo.ts",
2590 |func_space| {
2591 let metric = &func_space.metrics;
2592 assert_eq!(metric.npa.class_npa_sum(), 1);
2595 assert_eq!(metric.npa.class_na_sum(), 2);
2596 assert_eq!(metric.npa.interface_npa_sum(), 1);
2597 assert_eq!(metric.npa.interface_na_sum(), 1);
2598 insta::assert_json_snapshot!(metric.npa);
2599 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2600 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2601 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2602 },
2603 );
2604 }
2605
2606 #[cfg(feature = "typescript")]
2607 #[test]
2608 fn typescript_nested_class_attributes_independent() {
2609 check_metrics::<TypescriptParser>(
2617 "class Outer {
2618 a: number = 0;
2619 static Inner = class {
2620 b: number = 0;
2621 c: number = 0;
2622 };
2623 }",
2624 "foo.ts",
2625 |metric| {
2626 assert_eq!(metric.npa.class_npa_sum(), 4);
2627 assert_eq!(metric.npa.class_na_sum(), 4);
2628 insta::assert_json_snapshot!(metric.npa);
2629 },
2630 );
2631 }
2632
2633 #[cfg(feature = "typescript")]
2637 #[test]
2638 fn tsx_empty_class_no_attributes() {
2639 check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
2640 assert_eq!(metric.npa.class_npa_sum(), 0);
2641 assert_eq!(metric.npa.class_na_sum(), 0);
2642 insta::assert_json_snapshot!(metric.npa);
2643 });
2644 }
2645
2646 #[cfg(feature = "typescript")]
2647 #[test]
2648 fn tsx_default_public_fields() {
2649 check_metrics::<TsxParser>(
2650 "class C {
2651 a: number = 1;
2652 b: string = \"\";
2653 }",
2654 "foo.tsx",
2655 |metric| {
2656 assert_eq!(metric.npa.class_npa_sum(), 2);
2657 assert_eq!(metric.npa.class_na_sum(), 2);
2658 insta::assert_json_snapshot!(metric.npa);
2659 },
2660 );
2661 }
2662
2663 #[cfg(feature = "typescript")]
2664 #[test]
2665 fn tsx_visibility_modifiers() {
2666 check_metrics::<TsxParser>(
2667 "class C {
2668 public a: number = 1;
2669 private b: number = 2;
2670 protected c: number = 3;
2671 }",
2672 "foo.tsx",
2673 |metric| {
2674 assert_eq!(metric.npa.class_npa_sum(), 1);
2675 assert_eq!(metric.npa.class_na_sum(), 3);
2676 insta::assert_json_snapshot!(metric.npa);
2677 },
2678 );
2679 }
2680
2681 #[cfg(feature = "typescript")]
2682 #[test]
2683 fn tsx_parameter_properties() {
2684 check_metrics::<TsxParser>(
2685 "class C {
2686 constructor(public a: number, private b: string) {}
2687 }",
2688 "foo.tsx",
2689 |metric| {
2690 assert_eq!(metric.npa.class_npa_sum(), 1);
2691 assert_eq!(metric.npa.class_na_sum(), 2);
2692 insta::assert_json_snapshot!(metric.npa);
2693 },
2694 );
2695 }
2696
2697 #[cfg(feature = "typescript")]
2698 #[test]
2699 fn tsx_readonly_constructor_param_property() {
2700 check_metrics::<TsxParser>(
2705 "class C {
2706 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2707 }",
2708 "foo.tsx",
2709 |metric| {
2710 assert_eq!(metric.npa.class_na_sum(), 4);
2711 assert_eq!(metric.npa.class_npa_sum(), 2);
2712 insta::assert_json_snapshot!(metric.npa);
2713 },
2714 );
2715 }
2716
2717 #[cfg(feature = "typescript")]
2718 #[test]
2719 fn tsx_abstract_class_attributes() {
2720 check_metrics::<TsxParser>(
2721 "abstract class C {
2722 public a: number = 1;
2723 private b: number = 2;
2724 abstract m(): void;
2725 }",
2726 "foo.tsx",
2727 |metric| {
2728 assert_eq!(metric.npa.class_npa_sum(), 1);
2729 assert_eq!(metric.npa.class_na_sum(), 2);
2730 insta::assert_json_snapshot!(metric.npa);
2731 },
2732 );
2733 }
2734
2735 #[cfg(feature = "typescript")]
2736 #[test]
2737 fn tsx_interface_property_signatures() {
2738 check_func_space::<TsxParser, _>(
2739 "interface I {
2740 a: number;
2741 b: string;
2742 m(): void;
2743 }",
2744 "foo.tsx",
2745 |func_space| {
2746 let metric = &func_space.metrics;
2747 assert_eq!(metric.npa.interface_npa_sum(), 2);
2748 assert_eq!(metric.npa.interface_na_sum(), 2);
2749 insta::assert_json_snapshot!(metric.npa);
2750 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2751 },
2752 );
2753 }
2754
2755 #[cfg(feature = "typescript")]
2756 #[test]
2757 fn tsx_arrow_field_is_method_not_attribute() {
2758 check_metrics::<TsxParser>(
2759 "class C {
2760 a: number = 0;
2761 arrow = () => this.a;
2762 }",
2763 "foo.tsx",
2764 |metric| {
2765 assert_eq!(metric.npa.class_npa_sum(), 1);
2766 assert_eq!(metric.npa.class_na_sum(), 1);
2767 insta::assert_json_snapshot!(metric.npa);
2768 },
2769 );
2770 }
2771
2772 #[cfg(feature = "typescript")]
2773 #[test]
2774 fn tsx_static_fields() {
2775 check_metrics::<TsxParser>(
2776 "class C {
2777 static a: number = 0;
2778 private static b: number = 0;
2779 }",
2780 "foo.tsx",
2781 |metric| {
2782 assert_eq!(metric.npa.class_npa_sum(), 1);
2783 assert_eq!(metric.npa.class_na_sum(), 2);
2784 insta::assert_json_snapshot!(metric.npa);
2785 },
2786 );
2787 }
2788
2789 #[cfg(feature = "typescript")]
2790 #[test]
2791 fn tsx_readonly_field() {
2792 check_metrics::<TsxParser>(
2793 "class C {
2794 readonly a: number = 1;
2795 private readonly b: number = 2;
2796 }",
2797 "foo.tsx",
2798 |metric| {
2799 assert_eq!(metric.npa.class_npa_sum(), 1);
2800 assert_eq!(metric.npa.class_na_sum(), 2);
2801 insta::assert_json_snapshot!(metric.npa);
2802 },
2803 );
2804 }
2805
2806 #[cfg(feature = "typescript")]
2807 #[test]
2808 fn tsx_generic_class_attributes() {
2809 check_metrics::<TsxParser>("class Box<T> { value: T; }", "foo.tsx", |metric| {
2810 assert_eq!(metric.npa.class_npa_sum(), 1);
2811 assert_eq!(metric.npa.class_na_sum(), 1);
2812 insta::assert_json_snapshot!(metric.npa);
2813 });
2814 }
2815
2816 #[cfg(feature = "typescript")]
2817 #[test]
2818 fn tsx_getters_setters_not_attributes() {
2819 check_metrics::<TsxParser>(
2820 "class C {
2821 private _x: number = 0;
2822 get x(): number { return this._x; }
2823 set x(v: number) { this._x = v; }
2824 }",
2825 "foo.tsx",
2826 |metric| {
2827 assert_eq!(metric.npa.class_npa_sum(), 0);
2828 assert_eq!(metric.npa.class_na_sum(), 1);
2829 insta::assert_json_snapshot!(metric.npa);
2830 },
2831 );
2832 }
2833
2834 #[cfg(feature = "typescript")]
2835 #[test]
2836 fn tsx_multiple_classes_and_interface() {
2837 check_func_space::<TsxParser, _>(
2838 "class A { x: number = 0; }
2839 class B { private y: number = 0; }
2840 interface I { z: number; }",
2841 "foo.tsx",
2842 |func_space| {
2843 let metric = &func_space.metrics;
2844 assert_eq!(metric.npa.class_npa_sum(), 1);
2845 assert_eq!(metric.npa.class_na_sum(), 2);
2846 assert_eq!(metric.npa.interface_npa_sum(), 1);
2847 assert_eq!(metric.npa.interface_na_sum(), 1);
2848 insta::assert_json_snapshot!(metric.npa);
2849 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2850 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2851 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2852 },
2853 );
2854 }
2855
2856 #[cfg(feature = "ruby")]
2866 #[test]
2867 fn ruby_no_class_attributes() {
2868 check_metrics::<RubyParser>(
2869 "class A\n def f\n 1\n end\nend\n",
2870 "foo.rb",
2871 |metric| {
2872 assert_eq!(metric.npa.class_npa_sum(), 0);
2873 assert_eq!(metric.npa.class_na_sum(), 0);
2874 insta::assert_json_snapshot!(metric.npa);
2875 },
2876 );
2877 }
2878
2879 #[cfg(feature = "ruby")]
2880 #[test]
2881 fn ruby_instance_variable_attribute() {
2882 check_metrics::<RubyParser>("class A\n @x = 1\nend\n", "foo.rb", |metric| {
2884 assert_eq!(metric.npa.class_npa_sum(), 1);
2885 assert_eq!(metric.npa.class_na_sum(), 1);
2886 insta::assert_json_snapshot!(metric.npa);
2887 });
2888 }
2889
2890 #[cfg(feature = "ruby")]
2891 #[test]
2892 fn ruby_class_variable_attribute() {
2893 check_metrics::<RubyParser>("class A\n @@y = 1\nend\n", "foo.rb", |metric| {
2895 assert_eq!(metric.npa.class_npa_sum(), 1);
2896 assert_eq!(metric.npa.class_na_sum(), 1);
2897 insta::assert_json_snapshot!(metric.npa);
2898 });
2899 }
2900
2901 #[cfg(feature = "ruby")]
2902 #[test]
2903 fn ruby_attr_accessor_counts_symbols() {
2904 check_metrics::<RubyParser>(
2906 "class A\n attr_accessor :x, :y, :z\nend\n",
2907 "foo.rb",
2908 |metric| {
2909 assert_eq!(metric.npa.class_npa_sum(), 3);
2910 assert_eq!(metric.npa.class_na_sum(), 3);
2911 insta::assert_json_snapshot!(metric.npa);
2912 },
2913 );
2914 }
2915
2916 #[cfg(feature = "ruby")]
2917 #[test]
2918 fn ruby_attr_reader_and_writer() {
2919 check_metrics::<RubyParser>(
2920 "class A\n attr_reader :r1, :r2\n attr_writer :w\nend\n",
2921 "foo.rb",
2922 |metric| {
2923 assert_eq!(metric.npa.class_npa_sum(), 3);
2924 assert_eq!(metric.npa.class_na_sum(), 3);
2925 insta::assert_json_snapshot!(metric.npa);
2926 },
2927 );
2928 }
2929
2930 #[cfg(feature = "ruby")]
2931 #[test]
2932 fn ruby_mixed_attributes_and_assignments() {
2933 check_metrics::<RubyParser>(
2934 "class A\n attr_accessor :x, :y\n @z = 1\n @@w = 2\nend\n",
2935 "foo.rb",
2936 |metric| {
2937 assert_eq!(metric.npa.class_npa_sum(), 4);
2938 assert_eq!(metric.npa.class_na_sum(), 4);
2939 insta::assert_json_snapshot!(metric.npa);
2940 },
2941 );
2942 }
2943
2944 #[cfg(feature = "ruby")]
2945 #[test]
2946 fn ruby_private_attributes() {
2947 check_metrics::<RubyParser>(
2949 "class A\n attr_accessor :pub\n private\n attr_accessor :hidden\nend\n",
2950 "foo.rb",
2951 |metric| {
2952 assert_eq!(metric.npa.class_npa_sum(), 1);
2953 assert_eq!(metric.npa.class_na_sum(), 2);
2954 insta::assert_json_snapshot!(metric.npa);
2955 },
2956 );
2957 }
2958
2959 #[cfg(feature = "ruby")]
2960 #[test]
2961 fn ruby_visibility_public_resets_private() {
2962 check_metrics::<RubyParser>(
2964 "class A\n attr_reader :a\n private\n attr_reader :b\n public\n attr_reader :c\nend\n",
2965 "foo.rb",
2966 |metric| {
2967 assert_eq!(metric.npa.class_npa_sum(), 2);
2968 assert_eq!(metric.npa.class_na_sum(), 3);
2969 insta::assert_json_snapshot!(metric.npa);
2970 },
2971 );
2972 }
2973
2974 #[cfg(feature = "ruby")]
2975 #[test]
2976 fn ruby_method_scope_assignments_excluded() {
2977 check_metrics::<RubyParser>(
2981 "class A\n def init\n @x = 1\n @@y = 2\n end\nend\n",
2982 "foo.rb",
2983 |metric| {
2984 assert_eq!(metric.npa.class_npa_sum(), 0);
2985 assert_eq!(metric.npa.class_na_sum(), 0);
2986 insta::assert_json_snapshot!(metric.npa);
2987 },
2988 );
2989 }
2990
2991 #[cfg(feature = "ruby")]
2992 #[test]
2993 fn ruby_module_attributes_not_counted() {
2994 check_metrics::<RubyParser>(
2997 "module M\n attr_accessor :x\n @@m = 1\nend\n",
2998 "foo.rb",
2999 |metric| {
3000 assert_eq!(metric.npa.class_npa_sum(), 0);
3001 assert_eq!(metric.npa.class_na_sum(), 0);
3002 insta::assert_json_snapshot!(metric.npa);
3003 },
3004 );
3005 }
3006
3007 #[cfg(feature = "ruby")]
3008 #[test]
3009 fn ruby_inheritance_attributes() {
3010 check_metrics::<RubyParser>(
3012 "class A < B\n attr_accessor :x\n @y = 0\nend\n",
3013 "foo.rb",
3014 |metric| {
3015 assert_eq!(metric.npa.class_npa_sum(), 2);
3016 assert_eq!(metric.npa.class_na_sum(), 2);
3017 insta::assert_json_snapshot!(metric.npa);
3018 },
3019 );
3020 }
3021
3022 #[cfg(feature = "ruby")]
3023 #[test]
3024 fn ruby_constant_assignments_excluded() {
3025 check_metrics::<RubyParser>(
3029 "class A\n PI = 3.14\n E = 2.71\n attr_reader :x\nend\n",
3030 "foo.rb",
3031 |metric| {
3032 assert_eq!(metric.npa.class_npa_sum(), 1);
3034 assert_eq!(metric.npa.class_na_sum(), 1);
3035 insta::assert_json_snapshot!(metric.npa);
3036 },
3037 );
3038 }
3039
3040 #[cfg(feature = "ruby")]
3041 #[test]
3042 fn ruby_multiple_classes_attribute_rollup() {
3043 check_metrics::<RubyParser>(
3044 "class A\n attr_accessor :x\nend\nclass B\n private\n attr_accessor :y\nend\n",
3045 "foo.rb",
3046 |metric| {
3047 assert_eq!(metric.npa.class_npa_sum(), 1);
3049 assert_eq!(metric.npa.class_na_sum(), 2);
3050 insta::assert_json_snapshot!(metric.npa);
3051 },
3052 );
3053 }
3054
3055 #[cfg(feature = "ruby")]
3056 #[test]
3057 fn ruby_visibility_call_wrapping_attr_macro_counts_symbols() {
3058 check_metrics::<RubyParser>(
3065 "class D\n attr_accessor :a\n private attr_accessor :b\nend\n",
3066 "foo.rb",
3067 |metric| {
3068 assert_eq!(metric.npa.class_na_sum(), 2);
3069 assert_eq!(metric.npa.class_npa_sum(), 1);
3070 insta::assert_json_snapshot!(metric.npa);
3071 },
3072 );
3073 }
3074
3075 #[cfg(feature = "ruby")]
3076 #[test]
3077 fn ruby_wrapped_attr_macro_reads_the_keyword_not_the_flag() {
3078 check_metrics::<RubyParser>(
3086 "class D\n private\n attr_reader :a\n public attr_reader :b\nend\n",
3087 "foo.rb",
3088 |metric| {
3089 assert_eq!(metric.npa.class_na_sum(), 2);
3090 assert_eq!(metric.npa.class_npa_sum(), 1);
3091 insta::assert_json_snapshot!(metric.npa);
3092 },
3093 );
3094 }
3095
3096 #[cfg(feature = "ruby")]
3097 #[test]
3098 fn ruby_parenthesised_bare_keyword_flips_the_attribute_flag() {
3099 check_metrics::<RubyParser>(
3107 "class D\n attr_accessor :a\n private()\n attr_accessor :b\nend\n",
3108 "foo.rb",
3109 |metric| {
3110 assert_eq!(metric.npa.class_na_sum(), 2);
3111 assert_eq!(metric.npa.class_npa_sum(), 1);
3112 insta::assert_json_snapshot!(metric.npa);
3113 },
3114 );
3115 }
3116
3117 #[cfg(feature = "ruby")]
3118 #[test]
3119 fn ruby_attr_macro_counts_symbol_array_elements() {
3120 check_metrics::<RubyParser>(
3126 "class A\n attr_accessor :a\n attr_writer %i[e f]\nend\n",
3127 "foo.rb",
3128 |metric| {
3129 assert_eq!(metric.npa.class_na_sum(), 3);
3130 assert_eq!(metric.npa.class_npa_sum(), 3);
3131 insta::assert_json_snapshot!(metric.npa);
3132 },
3133 );
3134 }
3135
3136 #[cfg(feature = "ruby")]
3137 #[test]
3138 fn ruby_attr_macro_on_another_object_declares_nothing() {
3139 check_metrics::<RubyParser>(
3150 "class A\n attr_accessor :a\n Other.attr_accessor :b\n self.(:c)\nend\n",
3151 "foo.rb",
3152 |metric| {
3153 assert_eq!(metric.npa.class_na_sum(), 1);
3154 assert_eq!(metric.npa.class_npa_sum(), 1);
3155 insta::assert_json_snapshot!(metric.npa);
3156 },
3157 );
3158 }
3159
3160 #[cfg(feature = "ruby")]
3161 #[test]
3162 fn ruby_hash_key_symbol_declares_no_attribute() {
3163 let source = "class A\n attr_accessor :x, foo: 1\nend\n";
3174 let parser = RubyParser::new(source.as_bytes().to_vec(), &PathBuf::from("foo.rb"), None);
3175 assert!(
3176 ast_has_kind_id(&parser, Ruby::HashKeySymbol as u16),
3177 "the fixture no longer produces a `hash_key_symbol`; the \
3178 claim below is then vacuous"
3179 );
3180 check_metrics::<RubyParser>(source, "foo.rb", |metric| {
3181 assert_eq!(metric.npa.class_na_sum(), 1);
3182 assert_eq!(metric.npa.class_npa_sum(), 1);
3183 });
3184 }
3185
3186 #[cfg(feature = "ruby")]
3187 #[test]
3188 fn ruby_hidden_call_alias_is_not_emitted() {
3189 let source =
3194 "class A\n attr_accessor :a\n private attr_accessor :b\n self.private :a\nend\n";
3195 let parser = RubyParser::new(source.as_bytes().to_vec(), &PathBuf::from("foo.rb"), None);
3196 assert!(
3197 ast_has_kind_id(&parser, Ruby::Call3 as u16),
3198 "the fixture no longer produces the `Call3` alias; the \
3199 negative claim below is then vacuous"
3200 );
3201 assert!(
3202 !ast_has_kind_id(&parser, Ruby::Call5 as u16),
3203 "tree-sitter-ruby now emits the hidden `_call` supertype; \
3204 add `Call5` to every Ruby `Call` dispatch set"
3205 );
3206 }
3207
3208 #[cfg(feature = "ruby")]
3209 #[test]
3210 fn ruby_visibility_call_without_a_wrapped_macro_declares_nothing() {
3211 check_metrics::<RubyParser>(
3220 "class D\n attr_accessor :a\n attr_reader name\n private :a\n private *SYMS\nend\n",
3221 "foo.rb",
3222 |metric| {
3223 assert_eq!(metric.npa.class_na_sum(), 1);
3224 assert_eq!(metric.npa.class_npa_sum(), 1);
3225 insta::assert_json_snapshot!(metric.npa);
3226 },
3227 );
3228 }
3229
3230 #[cfg(feature = "python")]
3243 #[test]
3244 fn python_empty_class_no_attributes() {
3245 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
3246 assert_eq!(metric.npa.class_na_sum(), 0);
3247 assert_eq!(metric.npa.class_npa_sum(), 0);
3248 assert_eq!(metric.npa.interface_na_sum(), 0);
3249 insta::assert_json_snapshot!(metric.npa);
3250 });
3251 }
3252
3253 #[cfg(feature = "python")]
3254 #[test]
3255 fn python_class_level_assignments_are_attributes() {
3256 check_metrics::<PythonParser>("class C:\n x = 1\n y = 2\n", "foo.py", |metric| {
3259 assert_eq!(metric.npa.class_na_sum(), 2);
3260 assert_eq!(metric.npa.class_npa_sum(), 2);
3261 insta::assert_json_snapshot!(metric.npa);
3262 });
3263 }
3264
3265 #[cfg(feature = "python")]
3266 #[test]
3267 fn python_bare_type_annotation_not_attribute() {
3268 check_metrics::<PythonParser>(
3271 "class C:\n x: int\n y: int = 2\n",
3272 "foo.py",
3273 |metric| {
3274 assert_eq!(metric.npa.class_na_sum(), 1);
3275 insta::assert_json_snapshot!(metric.npa);
3276 },
3277 );
3278 }
3279
3280 #[cfg(feature = "python")]
3281 #[test]
3282 fn python_self_attributes_in_init() {
3283 check_metrics::<PythonParser>(
3286 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n",
3287 "foo.py",
3288 |metric| {
3289 assert_eq!(metric.npa.class_na_sum(), 2);
3290 assert_eq!(metric.npa.class_npa_sum(), 2);
3291 insta::assert_json_snapshot!(metric.npa);
3292 },
3293 );
3294 }
3295
3296 #[cfg(feature = "python")]
3297 #[test]
3298 fn python_self_attributes_in_nested_control_flow() {
3299 check_metrics::<PythonParser>(
3303 "class C:\n def __init__(self, flag):\n if flag:\n self.z = 1\n else:\n self.z = 2\n",
3304 "foo.py",
3305 |metric| {
3306 assert_eq!(metric.npa.class_na_sum(), 1);
3307 insta::assert_json_snapshot!(metric.npa);
3308 },
3309 );
3310 }
3311
3312 #[cfg(feature = "python")]
3323 #[test]
3324 fn python_defensive_reinit_self_attribute_counts_once() {
3325 check_metrics::<PythonParser>(
3326 "class C:\n def __init__(self):\n self.value = None\n def reset(self):\n self.value = 0\n",
3327 "foo.py",
3328 |metric| {
3329 assert_eq!(metric.npa.class_na_sum(), 1);
3330 assert_eq!(metric.npa.class_npa_sum(), 1);
3331 insta::assert_json_snapshot!(metric.npa);
3332 },
3333 );
3334 }
3335
3336 #[cfg(feature = "python")]
3339 #[test]
3340 fn python_distinct_self_attributes_count_independently() {
3341 check_metrics::<PythonParser>(
3342 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n self.z = 3\n",
3343 "foo.py",
3344 |metric| {
3345 assert_eq!(metric.npa.class_na_sum(), 3);
3346 insta::assert_json_snapshot!(metric.npa);
3347 },
3348 );
3349 }
3350
3351 #[cfg(feature = "python")]
3358 #[test]
3359 fn python_self_attribute_annotated_assignment_dedupes() {
3360 check_metrics::<PythonParser>(
3361 "class C:\n def __init__(self):\n self.value: int = 1\n def reset(self):\n self.value = 0\n",
3362 "foo.py",
3363 |metric| {
3364 assert_eq!(metric.npa.class_na_sum(), 1);
3365 insta::assert_json_snapshot!(metric.npa);
3366 },
3367 );
3368 }
3369
3370 #[cfg(feature = "python")]
3371 #[test]
3372 fn python_class_level_and_self_attrs_combine() {
3373 check_metrics::<PythonParser>(
3375 "class C:\n counter = 0\n def __init__(self):\n self.name = 'a'\n self.value = 1\n",
3376 "foo.py",
3377 |metric| {
3378 assert_eq!(metric.npa.class_na_sum(), 3);
3379 insta::assert_json_snapshot!(metric.npa);
3380 },
3381 );
3382 }
3383
3384 #[cfg(feature = "python")]
3385 #[test]
3386 fn python_self_attrs_isolated_per_class() {
3387 check_metrics::<PythonParser>(
3393 "class Outer:\n\
3394 \x20 def __init__(self):\n\
3395 \x20 self.x = 1\n\
3396 \x20 class Inner:\n\
3397 \x20 def __init__(self):\n\
3398 \x20 self.z = 2\n",
3399 "foo.py",
3400 |metric| {
3401 assert_eq!(metric.npa.class_na_sum(), 2);
3402 insta::assert_json_snapshot!(metric.npa);
3403 },
3404 );
3405 }
3406
3407 #[cfg(feature = "python")]
3408 #[test]
3409 fn python_decorated_methods_do_not_inflate_attrs() {
3410 check_metrics::<PythonParser>(
3414 "class C:\n\
3415 \x20 @property\n\
3416 \x20 def p(self):\n\
3417 \x20 return 1\n\
3418 \x20 @staticmethod\n\
3419 \x20 def s():\n\
3420 \x20 return 2\n",
3421 "foo.py",
3422 |metric| {
3423 assert_eq!(metric.npa.class_na_sum(), 0);
3424 insta::assert_json_snapshot!(metric.npa);
3425 },
3426 );
3427 }
3428
3429 #[cfg(feature = "python")]
3430 #[test]
3431 fn python_module_level_assignments_not_attributes() {
3432 check_metrics::<PythonParser>("x = 1\ny = 2\nclass C:\n a = 3\n", "foo.py", |metric| {
3434 assert_eq!(metric.npa.class_na_sum(), 1);
3436 insta::assert_json_snapshot!(metric.npa);
3437 });
3438 }
3439
3440 #[cfg(feature = "python")]
3446 #[test]
3447 fn python_foreign_object_writes_not_attributes() {
3448 check_metrics::<PythonParser>(
3449 "class Service:\n\
3450 \x20 def __init__(self, db, logger):\n\
3451 \x20 self.name = \"svc\"\n\
3452 \x20 db.connection = None\n\
3453 \x20 logger.level = \"INFO\"\n",
3454 "foo.py",
3455 |metric| {
3456 assert_eq!(metric.npa.class_na_sum(), 1);
3458 assert_eq!(metric.npa.class_npa_sum(), 1);
3459 insta::assert_json_snapshot!(metric.npa);
3460 },
3461 );
3462 }
3463
3464 #[cfg(feature = "python")]
3469 #[test]
3470 fn python_self_attribute_unpacking_counts_each() {
3471 check_metrics::<PythonParser>(
3472 "class C:\n\
3473 \x20 def __init__(self):\n\
3474 \x20 self.a, self.b = 1, 2\n\
3475 \x20 self.c = 3\n",
3476 "foo.py",
3477 |metric| {
3478 assert_eq!(metric.npa.class_na_sum(), 3);
3480 assert_eq!(metric.npa.class_npa_sum(), 3);
3481 insta::assert_json_snapshot!(metric.npa);
3482 },
3483 );
3484 }
3485
3486 #[cfg(feature = "python")]
3492 #[test]
3493 fn python_self_attribute_nested_unpacking_counts_each() {
3494 check_metrics::<PythonParser>(
3495 "class C:\n\
3496 \x20 def __init__(self):\n\
3497 \x20 self.a, (self.b, self.c) = 1, (2, 3)\n",
3498 "foo.py",
3499 |metric| {
3500 assert_eq!(metric.npa.class_na_sum(), 3);
3502 assert_eq!(metric.npa.class_npa_sum(), 3);
3503 },
3504 );
3505 }
3506
3507 #[cfg(feature = "python")]
3512 #[test]
3513 fn python_class_level_nested_unpacking_counts_each() {
3514 check_metrics::<PythonParser>(
3515 "class C:\n\
3516 \x20 (a, (b, c)) = 1, (2, 3)\n",
3517 "foo.py",
3518 |metric| {
3519 assert_eq!(metric.npa.class_na_sum(), 3);
3521 assert_eq!(metric.npa.class_npa_sum(), 3);
3522 },
3523 );
3524 }
3525
3526 #[cfg(feature = "python")]
3534 #[test]
3535 fn python_class_level_parenthesized_unpacking_counts_each() {
3536 check_metrics::<PythonParser>(
3537 "class C:\n\
3538 \x20 (p, q) = 1, 2\n\
3539 \x20 [m, n] = 3, 4\n",
3540 "foo.py",
3541 |metric| {
3542 assert_eq!(metric.npa.class_na_sum(), 4);
3544 assert_eq!(metric.npa.class_npa_sum(), 4);
3545 },
3546 );
3547 }
3548
3549 #[cfg(feature = "python")]
3553 #[test]
3554 fn python_self_attribute_unpacking_skips_non_self_targets() {
3555 check_metrics::<PythonParser>(
3556 "class C:\n\
3557 \x20 def __init__(self):\n\
3558 \x20 self.a, x = 1, 2\n",
3559 "foo.py",
3560 |metric| {
3561 assert_eq!(metric.npa.class_na_sum(), 1);
3563 insta::assert_json_snapshot!(metric.npa);
3564 },
3565 );
3566 }
3567
3568 #[cfg(feature = "python")]
3573 #[test]
3574 fn python_class_level_multi_target_counts_each_name() {
3575 check_metrics::<PythonParser>(
3576 "class C:\n x = 1\n a = b = 3\n p, q = 1, 2\n",
3577 "foo.py",
3578 |metric| {
3579 assert_eq!(metric.npa.class_na_sum(), 5);
3581 assert_eq!(metric.npa.class_npa_sum(), 5);
3582 insta::assert_json_snapshot!(metric.npa);
3583 },
3584 );
3585 }
3586
3587 #[cfg(feature = "python")]
3591 #[test]
3592 fn python_chained_self_assignment_counts_each() {
3593 check_metrics::<PythonParser>(
3594 "class C:\n def __init__(self):\n self.a = self.b = 1\n",
3595 "foo.py",
3596 |metric| {
3597 assert_eq!(metric.npa.class_na_sum(), 2);
3598 assert_eq!(metric.npa.class_npa_sum(), 2);
3599 insta::assert_json_snapshot!(metric.npa);
3600 },
3601 );
3602 }
3603
3604 #[cfg(feature = "python")]
3608 #[test]
3609 fn python_classmethod_cls_attribute_counts() {
3610 check_metrics::<PythonParser>(
3611 "class C:\n\
3612 \x20 @classmethod\n\
3613 \x20 def make(cls, other):\n\
3614 \x20 cls.registry = {}\n\
3615 \x20 other.thing = 1\n",
3616 "foo.py",
3617 |metric| {
3618 assert_eq!(metric.npa.class_na_sum(), 1);
3620 assert_eq!(metric.npa.class_npa_sum(), 1);
3621 insta::assert_json_snapshot!(metric.npa);
3622 },
3623 );
3624 }
3625
3626 #[cfg(feature = "python")]
3631 #[test]
3632 fn python_nested_self_attribute_not_counted() {
3633 check_metrics::<PythonParser>(
3634 "class C:\n def __init__(self):\n self.f.g = 1\n",
3635 "foo.py",
3636 |metric| {
3637 assert_eq!(metric.npa.class_na_sum(), 0);
3638 insta::assert_json_snapshot!(metric.npa);
3639 },
3640 );
3641 }
3642
3643 #[cfg(feature = "python")]
3648 #[test]
3649 fn python_class_default_and_self_attr_dedupe() {
3650 check_metrics::<PythonParser>(
3651 "class C:\n x = 1\n def __init__(self):\n self.x = 2\n",
3652 "foo.py",
3653 |metric| {
3654 assert_eq!(metric.npa.class_na_sum(), 1);
3655 assert_eq!(metric.npa.class_npa_sum(), 1);
3656 insta::assert_json_snapshot!(metric.npa);
3657 },
3658 );
3659 }
3660
3661 #[cfg(feature = "rust")]
3662 #[test]
3663 fn rust_empty_unit_no_attributes() {
3664 check_metrics::<RustParser>("", "empty.rs", |metric| {
3665 assert_eq!(metric.npa.class_na_sum(), 0);
3666 assert_eq!(metric.npa.class_npa_sum(), 0);
3667 assert_eq!(metric.npa.interface_na_sum(), 0);
3668 assert_eq!(metric.npa.interface_npa_sum(), 0);
3669 insta::assert_json_snapshot!(metric.npa);
3670 });
3671 }
3672
3673 #[cfg(feature = "rust")]
3674 #[test]
3675 fn rust_struct_fields_are_attributes() {
3676 check_metrics::<RustParser>(
3679 "struct Foo { pub a: i32, b: String, pub c: bool }",
3680 "foo.rs",
3681 |metric| {
3682 assert_eq!(metric.npa.class_na_sum(), 3);
3683 assert_eq!(metric.npa.class_npa_sum(), 2);
3684 insta::assert_json_snapshot!(metric.npa);
3685 },
3686 );
3687 }
3688
3689 #[cfg(feature = "rust")]
3690 #[test]
3691 fn rust_pub_self_field_is_private() {
3692 check_metrics::<RustParser>(
3700 "struct S {\n\
3701 \x20 pub(self) a: i32,\n\
3702 \x20 pub(in self) a2: i32,\n\
3703 \x20 pub(crate) b: i32,\n\
3704 \x20 pub(super) d: i32,\n\
3705 \x20 pub(in crate::x) e: i32,\n\
3706 \x20 pub f: i32,\n\
3707 \x20 c: i32,\n\
3708 }",
3709 "foo.rs",
3710 |metric| {
3711 assert_eq!(metric.npa.class_na_sum(), 7);
3712 assert_eq!(metric.npa.class_npa_sum(), 4);
3713 },
3714 );
3715 }
3716
3717 #[cfg(feature = "rust")]
3718 #[test]
3719 fn rust_pub_self_assoc_const_is_private() {
3720 check_metrics::<RustParser>(
3725 "struct Foo;\n\
3726 impl Foo {\n\
3727 \x20 pub(self) const A: i32 = 1;\n\
3728 \x20 pub(in self) const A2: i32 = 2;\n\
3729 \x20 pub(crate) const B: i32 = 3;\n\
3730 \x20 pub const D: i32 = 4;\n\
3731 }\n",
3732 "foo.rs",
3733 |metric| {
3734 assert_eq!(metric.npa.class_na_sum(), 4);
3735 assert_eq!(metric.npa.class_npa_sum(), 2);
3736 },
3737 );
3738 }
3739
3740 #[cfg(feature = "rust")]
3741 #[test]
3742 fn rust_pub_self_tuple_field_is_private() {
3743 check_metrics::<RustParser>(
3751 "struct Bar(pub(self) i32, pub(in self) i32, pub(crate) i32, pub u8, String);",
3752 "foo.rs",
3753 |metric| {
3754 assert_eq!(metric.npa.class_na_sum(), 5);
3755 assert_eq!(metric.npa.class_npa_sum(), 2);
3756 },
3757 );
3758 }
3759
3760 #[cfg(feature = "rust")]
3761 #[test]
3762 fn rust_tuple_struct_fields_are_attributes() {
3763 check_metrics::<RustParser>("struct Bar(pub i32, String);", "foo.rs", |metric| {
3766 assert_eq!(metric.npa.class_na_sum(), 2);
3767 assert_eq!(metric.npa.class_npa_sum(), 1);
3768 insta::assert_json_snapshot!(metric.npa);
3769 });
3770 }
3771
3772 #[cfg(feature = "rust")]
3773 #[test]
3774 fn rust_unit_struct_has_no_attributes() {
3775 check_metrics::<RustParser>("struct Empty;", "foo.rs", |metric| {
3777 assert_eq!(metric.npa.class_na_sum(), 0);
3778 insta::assert_json_snapshot!(metric.npa);
3779 });
3780 }
3781
3782 #[cfg(feature = "rust")]
3783 #[test]
3784 fn rust_empty_struct_body_has_no_attributes() {
3785 check_metrics::<RustParser>("struct Empty { }", "foo.rs", |metric| {
3787 assert_eq!(metric.npa.class_na_sum(), 0);
3788 insta::assert_json_snapshot!(metric.npa);
3789 });
3790 }
3791
3792 #[cfg(feature = "rust")]
3793 #[test]
3794 fn rust_impl_associated_consts_are_attributes() {
3795 check_metrics::<RustParser>(
3798 "struct Foo;\n\
3799 impl Foo {\n\
3800 \x20 const X: i32 = 1;\n\
3801 \x20 pub const Y: i32 = 2;\n\
3802 \x20 static Z: i32 = 3;\n\
3803 \x20 pub static W: i32 = 4;\n\
3804 }\n",
3805 "foo.rs",
3806 |metric| {
3807 assert_eq!(metric.npa.class_na_sum(), 4);
3810 assert_eq!(metric.npa.class_npa_sum(), 2);
3811 insta::assert_json_snapshot!(metric.npa);
3812 },
3813 );
3814 }
3815
3816 #[cfg(feature = "rust")]
3817 #[test]
3818 fn rust_trait_consts_and_associated_types_are_attributes() {
3819 check_func_space::<RustParser, _>(
3824 "trait Drawable { const DEFAULT_COLOR: u32; type Item; }",
3825 "foo.rs",
3826 |func_space| {
3827 let metric = &func_space.metrics;
3828 assert_eq!(metric.npa.interface_na_sum(), 2);
3829 assert_eq!(metric.npa.interface_npa_sum(), 2);
3830 assert_eq!(metric.npa.class_na_sum(), 0);
3831 insta::assert_json_snapshot!(metric.npa);
3832 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
3833 },
3834 );
3835 }
3836
3837 #[cfg(feature = "rust")]
3838 #[test]
3839 fn rust_multiple_impls_aggregate() {
3840 check_metrics::<RustParser>(
3843 "struct Foo;\n\
3844 impl Foo { const X: i32 = 1; }\n\
3845 impl Foo { pub const Y: i32 = 2; }\n",
3846 "foo.rs",
3847 |metric| {
3848 assert_eq!(metric.npa.class_na_sum(), 2);
3849 assert_eq!(metric.npa.class_npa_sum(), 1);
3850 insta::assert_json_snapshot!(metric.npa);
3851 },
3852 );
3853 }
3854
3855 #[cfg(feature = "rust")]
3856 #[test]
3857 fn rust_module_level_consts_not_attributes() {
3858 check_metrics::<RustParser>(
3862 "const PI: f64 = 3.14;\nstatic Q: i32 = 0;\n",
3863 "foo.rs",
3864 |metric| {
3865 assert_eq!(metric.npa.class_na_sum(), 0);
3866 assert_eq!(metric.npa.interface_na_sum(), 0);
3867 insta::assert_json_snapshot!(metric.npa);
3868 },
3869 );
3870 }
3871
3872 #[cfg(feature = "go")]
3875 #[test]
3876 fn go_empty_unit_no_attributes() {
3877 check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
3880 assert_eq!(metric.npa.class_na_sum(), 0);
3881 insta::assert_json_snapshot!(metric.npa);
3882 });
3883 }
3884
3885 #[cfg(feature = "go")]
3886 #[test]
3887 fn go_empty_struct_has_no_attributes() {
3888 check_metrics::<GoParser>("package main\ntype Empty struct{}\n", "foo.go", |metric| {
3891 assert_eq!(metric.npa.class_na_sum(), 0);
3892 insta::assert_json_snapshot!(metric.npa);
3893 });
3894 }
3895
3896 #[cfg(feature = "go")]
3897 #[test]
3898 fn go_struct_fields_are_attributes() {
3899 check_metrics::<GoParser>(
3903 "package main\ntype Foo struct { X int; y string; Z float64 }\n",
3904 "foo.go",
3905 |metric| {
3906 assert_eq!(metric.npa.class_na_sum(), 3);
3907 assert_eq!(metric.npa.class_npa_sum(), 2);
3908 insta::assert_json_snapshot!(metric.npa);
3909 },
3910 );
3911 }
3912
3913 #[cfg(feature = "go")]
3914 #[test]
3915 fn go_grouped_struct_fields_each_count() {
3916 check_metrics::<GoParser>(
3921 "package main\ntype Point struct { X, Y int; Z float64 }\n",
3922 "foo.go",
3923 |metric| {
3924 assert_eq!(metric.npa.class_na_sum(), 3);
3925 assert_eq!(metric.npa.class_npa_sum(), 3);
3926 insta::assert_json_snapshot!(metric.npa);
3927 },
3928 );
3929 }
3930
3931 #[cfg(feature = "go")]
3932 #[test]
3933 fn go_embedded_type_counts_as_attribute() {
3934 check_metrics::<GoParser>(
3941 "package main\nimport \"io\"\ntype Bar struct { io.Reader; *Foo; n int }\ntype Foo struct {}\n",
3942 "foo.go",
3943 |metric| {
3944 assert_eq!(metric.npa.class_na_sum(), 3);
3945 assert_eq!(metric.npa.class_npa_sum(), 2);
3946 insta::assert_json_snapshot!(metric.npa);
3947 },
3948 );
3949 }
3950
3951 #[cfg(feature = "go")]
3952 #[test]
3953 fn go_multiple_structs_aggregate_at_unit() {
3954 check_metrics::<GoParser>(
3960 "package main\ntype Foo struct { x int }\ntype Bar struct { a int; b string }\n",
3961 "foo.go",
3962 |metric| {
3963 assert_eq!(metric.npa.class_na_sum(), 3);
3964 assert_eq!(metric.npa.class_npa_sum(), 0);
3965 insta::assert_json_snapshot!(metric.npa);
3966 },
3967 );
3968 }
3969
3970 #[cfg(feature = "go")]
3971 #[test]
3972 fn go_top_level_var_const_not_attributes() {
3973 check_metrics::<GoParser>(
3977 "package main\nvar Counter int\nconst Pi = 3.14\n",
3978 "foo.go",
3979 |metric| {
3980 assert_eq!(metric.npa.class_na_sum(), 0);
3981 insta::assert_json_snapshot!(metric.npa);
3982 },
3983 );
3984 }
3985
3986 #[cfg(feature = "go")]
3987 #[test]
3988 fn go_npa_excludes_unexported() {
3989 check_metrics::<GoParser>(
4000 "package main\nimport \"io\"\n\
4001 type T struct { Name string; secret int; A, b int; io.Reader; _ int; Ärger bool }\n",
4002 "foo.go",
4003 |metric| {
4004 assert_eq!(metric.npa.class_na_sum(), 7);
4005 assert_eq!(metric.npa.class_npa_sum(), 4);
4006 insta::assert_json_snapshot!(metric.npa);
4007 },
4008 );
4009 }
4010
4011 #[cfg(feature = "elixir")]
4017 #[test]
4018 fn elixir_npa_defstruct_keyword_list() {
4019 check_metrics::<ElixirParser>(
4020 "defmodule User do\n defstruct name: nil, age: 0, email: nil\nend\n",
4021 "foo.ex",
4022 |metric| {
4023 assert_eq!(metric.npa.class_na_sum(), 3);
4025 assert_eq!(metric.npa.class_npa_sum(), 3);
4026 },
4027 );
4028 }
4029
4030 #[cfg(feature = "elixir")]
4031 #[test]
4032 fn elixir_npa_defstruct_atom_list() {
4033 check_metrics::<ElixirParser>(
4034 "defmodule User do\n defstruct [:name, :age, :email]\nend\n",
4035 "foo.ex",
4036 |metric| {
4037 assert_eq!(metric.npa.class_na_sum(), 3);
4038 assert_eq!(metric.npa.class_npa_sum(), 3);
4039 },
4040 );
4041 }
4042
4043 #[cfg(feature = "elixir")]
4044 #[test]
4045 fn elixir_npa_defstruct_bracketed_keyword_list() {
4046 check_metrics::<ElixirParser>(
4047 "defmodule User do\n defstruct [name: nil, age: 0]\nend\n",
4048 "foo.ex",
4049 |metric| {
4050 assert_eq!(metric.npa.class_na_sum(), 2);
4051 assert_eq!(metric.npa.class_npa_sum(), 2);
4052 },
4053 );
4054 }
4055
4056 #[cfg(feature = "elixir")]
4057 #[test]
4058 fn elixir_npa_defstruct_single_field() {
4059 check_metrics::<ElixirParser>(
4060 "defmodule Box do\n defstruct value: nil\nend\n",
4061 "foo.ex",
4062 |metric| {
4063 assert_eq!(metric.npa.class_na_sum(), 1);
4064 assert_eq!(metric.npa.class_npa_sum(), 1);
4065 },
4066 );
4067 }
4068
4069 #[cfg(feature = "elixir")]
4070 #[test]
4071 fn elixir_npa_no_defstruct_is_zero() {
4072 check_metrics::<ElixirParser>(
4073 "defmodule Foo do\n def m, do: :ok\nend\n",
4074 "foo.ex",
4075 |metric| {
4076 assert_eq!(metric.npa.class_na_sum(), 0);
4077 assert_eq!(metric.npa.class_npa_sum(), 0);
4078 },
4079 );
4080 }
4081
4082 #[cfg(feature = "elixir")]
4092 #[test]
4093 fn elixir_npa_counts_a_quoted_defmodule_as_a_class() {
4094 check_metrics::<ElixirParser>(
4095 "defmodule Outer do\n defstruct [:x]\n defmacro gen do\n quote do\n defmodule Inner do\n defstruct [:a, :b]\n end\n end\n end\nend\n",
4096 "outer.ex",
4097 |metric| {
4098 assert_eq!(metric.npa.class_na_sum(), 3);
4101 assert_eq!(metric.npa.class_npa_sum(), 3);
4102 },
4103 );
4104 }
4105
4106 #[cfg(feature = "objc")]
4109 #[test]
4110 fn objc_npa() {
4111 check_metrics::<ObjcParser>(
4124 "@interface Foo : NSObject {\n\
4125 int _prot;\n\
4126 @private\n\
4127 int _priv;\n\
4128 @public\n\
4129 int _pub1, _pub2;\n\
4130 @protected\n\
4131 int _prot2;\n\
4132 }\n\
4133 @property (nonatomic) int count;\n\
4134 @property (copy) NSString *name;\n\
4135 @end\n",
4136 "foo.m",
4137 |metric| {
4138 assert_eq!(metric.npa.interface_na_sum(), 7);
4139 assert_eq!(metric.npa.interface_npa_sum(), 4);
4140 },
4141 );
4142 }
4143
4144 #[cfg(feature = "objc")]
4145 #[test]
4146 fn objc_npa_protocol() {
4147 check_metrics::<ObjcParser>(
4152 "@protocol Drawable <NSObject>\n\
4153 @optional\n\
4154 @property (readonly) int z;\n\
4155 @end\n",
4156 "foo.m",
4157 |metric| {
4158 assert_eq!(metric.npa.interface_na_sum(), 1);
4159 assert_eq!(metric.npa.interface_npa_sum(), 1);
4160 },
4161 );
4162 }
4163
4164 #[cfg(feature = "cpp")]
4167 #[test]
4168 fn cpp_empty_unit_no_attributes() {
4169 check_metrics::<CppParser>("", "empty.cpp", |metric| {
4172 assert_eq!(metric.npa.class_na_sum(), 0);
4173 assert_eq!(metric.npa.class_npa_sum(), 0);
4174 insta::assert_json_snapshot!(metric.npa);
4175 });
4176 }
4177
4178 #[cfg(feature = "cpp")]
4179 #[test]
4180 fn cpp_empty_class_no_attributes() {
4181 check_metrics::<CppParser>("class Foo {};", "foo.cpp", |metric| {
4184 assert_eq!(metric.npa.class_na_sum(), 0);
4185 assert_eq!(metric.npa.class_npa_sum(), 0);
4186 insta::assert_json_snapshot!(metric.npa);
4187 });
4188 }
4189
4190 #[cfg(feature = "cpp")]
4191 #[test]
4192 fn cpp_class_public_attributes() {
4193 check_metrics::<CppParser>(
4198 "class Foo { public: int a; int b, c; };",
4199 "foo.cpp",
4200 |metric| {
4201 assert_eq!(metric.npa.class_na_sum(), 3);
4202 assert_eq!(metric.npa.class_npa_sum(), 3);
4203 insta::assert_json_snapshot!(metric.npa);
4204 },
4205 );
4206 }
4207
4208 #[cfg(feature = "cpp")]
4209 #[test]
4210 fn cpp_class_private_default_visibility() {
4211 check_metrics::<CppParser>("class Foo { int value_; };", "foo.cpp", |metric| {
4215 assert_eq!(metric.npa.class_na_sum(), 1);
4216 assert_eq!(metric.npa.class_npa_sum(), 0);
4217 insta::assert_json_snapshot!(metric.npa);
4218 });
4219 }
4220
4221 #[cfg(feature = "cpp")]
4222 #[test]
4223 fn cpp_struct_default_public_visibility() {
4224 check_metrics::<CppParser>("struct Bar { int value_; };", "foo.cpp", |metric| {
4227 assert_eq!(metric.npa.class_na_sum(), 1);
4228 assert_eq!(metric.npa.class_npa_sum(), 1);
4229 insta::assert_json_snapshot!(metric.npa);
4230 });
4231 }
4232
4233 #[cfg(feature = "cpp")]
4234 #[test]
4235 fn cpp_mixed_visibility_sections() {
4236 check_metrics::<CppParser>(
4240 "class Foo {\n\
4241 public: int a;\n\
4242 protected: int b;\n\
4243 private: int c;\n\
4244 };",
4245 "foo.cpp",
4246 |metric| {
4247 assert_eq!(metric.npa.class_na_sum(), 3);
4248 assert_eq!(metric.npa.class_npa_sum(), 1);
4249 insta::assert_json_snapshot!(metric.npa);
4250 },
4251 );
4252 }
4253
4254 #[cfg(feature = "cpp")]
4255 #[test]
4256 fn cpp_methods_not_counted_as_attributes() {
4257 check_metrics::<CppParser>(
4262 "class Foo {\n\
4263 public:\n\
4264 void method1() {}\n\
4265 void method2();\n\
4266 private:\n\
4267 int value_;\n\
4268 };",
4269 "foo.cpp",
4270 |metric| {
4271 assert_eq!(metric.npa.class_na_sum(), 1);
4272 assert_eq!(metric.npa.class_npa_sum(), 0);
4273 insta::assert_json_snapshot!(metric.npa);
4274 },
4275 );
4276 }
4277
4278 #[cfg(feature = "cpp")]
4279 #[test]
4280 fn cpp_pointer_array_fields_count() {
4281 check_metrics::<CppParser>(
4286 "struct S {\n\
4287 int* p;\n\
4288 int a[10];\n\
4289 int x;\n\
4290 };",
4291 "foo.cpp",
4292 |metric| {
4293 assert_eq!(metric.npa.class_na_sum(), 3);
4294 assert_eq!(metric.npa.class_npa_sum(), 3);
4296 insta::assert_json_snapshot!(metric.npa);
4297 },
4298 );
4299 }
4300
4301 #[cfg(feature = "cpp")]
4302 #[test]
4303 fn cpp_multiple_classes_aggregate_at_unit() {
4304 check_metrics::<CppParser>(
4309 "class Foo { public: int a; private: int b; };\nstruct Bar { int c; };",
4310 "foo.cpp",
4311 |metric| {
4312 assert_eq!(metric.npa.class_na_sum(), 3);
4313 assert_eq!(metric.npa.class_npa_sum(), 2);
4315 insta::assert_json_snapshot!(metric.npa);
4316 },
4317 );
4318 }
4319
4320 #[cfg(feature = "javascript")]
4321 #[test]
4322 fn javascript_empty_unit_no_attributes() {
4323 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
4326 assert_eq!(metric.npa.class_na_sum(), 0);
4327 assert_eq!(metric.npa.class_npa_sum(), 0);
4328 insta::assert_json_snapshot!(metric.npa);
4329 });
4330 }
4331
4332 #[cfg(feature = "javascript")]
4333 #[test]
4334 fn javascript_empty_class_no_attributes() {
4335 check_metrics::<JavascriptParser>("class Foo {}", "foo.js", |metric| {
4337 assert_eq!(metric.npa.class_na_sum(), 0);
4338 assert_eq!(metric.npa.class_npa_sum(), 0);
4339 insta::assert_json_snapshot!(metric.npa);
4340 });
4341 }
4342
4343 #[cfg(feature = "javascript")]
4344 #[test]
4345 fn javascript_class_fields_count() {
4346 check_metrics::<JavascriptParser>(
4351 "class Foo { x = 1; y; static z = 2; }",
4352 "foo.js",
4353 |metric| {
4354 assert_eq!(metric.npa.class_na_sum(), 3);
4355 assert_eq!(metric.npa.class_npa_sum(), 3);
4356 insta::assert_json_snapshot!(metric.npa);
4357 },
4358 );
4359 }
4360
4361 #[cfg(feature = "javascript")]
4362 #[test]
4363 fn javascript_arrow_field_is_method_not_attribute() {
4364 check_metrics::<JavascriptParser>(
4368 "class Foo { x = () => {}; y = function() {}; z = 1; }",
4369 "foo.js",
4370 |metric| {
4371 assert_eq!(metric.npa.class_na_sum(), 1);
4373 assert_eq!(metric.npa.class_npa_sum(), 1);
4374 insta::assert_json_snapshot!(metric.npa);
4375 },
4376 );
4377 }
4378
4379 #[cfg(feature = "javascript")]
4380 #[test]
4381 fn javascript_methods_not_counted_as_attributes() {
4382 check_metrics::<JavascriptParser>(
4385 "class Foo { constructor() {} bar() {} get baz() { return 1; } x = 1; }",
4386 "foo.js",
4387 |metric| {
4388 assert_eq!(metric.npa.class_na_sum(), 1);
4390 assert_eq!(metric.npa.class_npa_sum(), 1);
4391 insta::assert_json_snapshot!(metric.npa);
4392 },
4393 );
4394 }
4395
4396 #[cfg(feature = "javascript")]
4397 #[test]
4398 fn javascript_multiple_classes_aggregate_at_unit() {
4399 check_metrics::<JavascriptParser>(
4403 "class Foo { a = 1; b = 2; }\nclass Bar { c = 3; }",
4404 "foo.js",
4405 |metric| {
4406 assert_eq!(metric.npa.class_na_sum(), 3);
4407 assert_eq!(metric.npa.class_npa_sum(), 3);
4408 insta::assert_json_snapshot!(metric.npa);
4409 },
4410 );
4411 }
4412
4413 #[cfg(feature = "mozjs")]
4414 #[test]
4415 fn mozjs_class_fields_count() {
4416 check_metrics::<MozjsParser>(
4419 "class Foo { x = 1; y; static z = 2; }",
4420 "foo.js",
4421 |metric| {
4422 assert_eq!(metric.npa.class_na_sum(), 3);
4423 assert_eq!(metric.npa.class_npa_sum(), 3);
4424 insta::assert_json_snapshot!(metric.npa);
4425 },
4426 );
4427 }
4428
4429 #[cfg(all(
4437 feature = "csharp",
4438 feature = "java",
4439 feature = "kotlin",
4440 feature = "php"
4441 ))]
4442 #[test]
4443 fn empty_class_cda_is_zero_not_nan() {
4444 let assert_zero = |metric: crate::CodeMetrics| {
4445 assert_eq!(metric.npa.class_na_sum(), 0);
4446 assert!(!metric.npa.class_cda().is_nan());
4447 assert!(!metric.npa.total_cda().is_nan());
4448 assert_eq!(metric.npa.class_cda(), 0.0);
4449 assert_eq!(metric.npa.total_cda(), 0.0);
4450 };
4451 check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
4452 check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
4453 check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
4454 check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
4455 }
4456
4457 #[cfg(all(feature = "csharp", feature = "java"))]
4462 #[test]
4463 fn empty_interface_cda_is_zero_not_nan() {
4464 let assert_zero = |metric: crate::CodeMetrics| {
4465 assert_eq!(metric.npa.interface_na_sum(), 0);
4466 assert!(!metric.npa.interface_cda().is_nan());
4467 assert_eq!(metric.npa.interface_cda(), 0.0);
4468 };
4469 check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
4470 check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
4471 }
4472
4473 #[cfg(feature = "java")]
4477 #[test]
4478 fn stats_display_and_per_space_accessors() {
4479 check_func_space::<JavaParser, _>(
4480 "public interface I {\n int K = 1;\n}\n\
4481 public class C {\n public int a;\n private int b;\n}\n",
4482 "X.java",
4483 |unit| {
4484 assert_eq!(unit.metrics.npa.class_npa_sum(), 1);
4487 assert_eq!(unit.metrics.npa.class_na_sum(), 2);
4488 let rendered = unit.metrics.npa.to_string();
4489 for fragment in [
4490 "classes: 1, interfaces: 1",
4491 "class_attributes: 2",
4492 "interface_attributes: 1",
4493 "total: 2, total_attributes: 3",
4494 ] {
4495 assert!(
4496 rendered.contains(fragment),
4497 "missing {fragment:?} in {rendered}"
4498 );
4499 }
4500 let class = child_space(&unit, "C");
4505 assert_eq!(class.kind, SpaceKind::Class);
4506 assert_eq!(class.metrics.npa.class_npa(), 1);
4507 assert_eq!(class.metrics.npa.class_na(), 2);
4508 let iface = child_space(&unit, "I");
4509 assert_eq!(iface.kind, SpaceKind::Interface);
4510 assert_eq!(iface.metrics.npa.interface_npa(), 1);
4511 assert_eq!(iface.metrics.npa.interface_na(), 1);
4512 },
4513 );
4514 }
4515}