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::langs::*;
24use crate::macros::{csharp_var_decl_kinds, csharp_var_declarator_kinds, implement_metric_trait};
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
375macro_rules! ts_npa_compute {
410 ($lang:ident) => {
411 fn compute<'a>(
412 node: &Node<'a>,
413 _code: &'a [u8],
414 _ancestors: Ancestors<'a, '_>,
415 stats: &mut Stats,
416 ) {
417 use $lang::*;
418
419 match node.kind_id().into() {
420 ClassBody => {
421 for member in node.children() {
422 match member.kind_id().into() {
423 PublicFieldDefinition
429 if member
430 .first_child(|id| {
431 id == $lang::ArrowFunction
432 || id == $lang::FunctionExpression
433 })
434 .is_none() =>
435 {
436 stats.class_na += 1;
437 if ts_member_is_public!($lang, member) {
438 stats.class_npa += 1;
439 }
440 }
441 MethodDefinition => {
447 let Some(params) =
448 member.first_child(|id| id == $lang::FormalParameters)
449 else {
450 continue;
451 };
452 for param in params.children().filter(|c| {
453 matches!(
454 c.kind_id().into(),
455 RequiredParameter | RequiredParameter2
456 )
457 }) {
458 if param
459 .first_child(|id| {
460 id == $lang::AccessibilityModifier
461 || id == $lang::Readonly
462 })
463 .is_some()
464 {
465 stats.class_na += 1;
466 if ts_member_is_public!($lang, param) {
467 stats.class_npa += 1;
468 }
469 }
470 }
471 }
472 _ => {}
473 }
474 }
475 }
476 InterfaceBody => {
477 let count = node
478 .children()
479 .filter(|c| matches!(c.kind_id().into(), PropertySignature))
480 .count();
481 stats.interface_na += count;
482 stats.interface_npa = stats.interface_na;
483 }
484 _ => {}
485 }
486 }
487 };
488}
489
490macro_rules! ts_member_is_public {
496 ($lang:ident, $member:expr) => {{
497 match $member.first_child(|id| id == $lang::AccessibilityModifier) {
498 None => true,
499 Some(m) => m
500 .first_child(|id| id == $lang::Private || id == $lang::Protected)
501 .is_none(),
502 }
503 }};
504}
505pub(crate) use ts_member_is_public;
506
507macro_rules! js_npa_compute {
528 ($lang:ident) => {
529 fn compute<'a>(
530 node: &Node<'a>,
531 _code: &'a [u8],
532 _ancestors: Ancestors<'a, '_>,
533 stats: &mut Stats,
534 ) {
535 use $lang::*;
536
537 if !matches!(node.kind_id().into(), ClassBody) {
538 return;
539 }
540
541 for member in node.children() {
542 if matches!(member.kind_id().into(), FieldDefinition)
543 && member
544 .first_child(|id| {
545 id == $lang::ArrowFunction || id == $lang::FunctionExpression
546 })
547 .is_none()
548 {
549 stats.class_na += 1;
550 stats.class_npa += 1;
551 }
552 }
553 }
554 };
555}
556
557mod cpp;
562mod csharp;
563mod elixir;
564mod go;
565mod groovy;
566mod java;
567mod javascript;
568mod kotlin;
569mod mozcpp;
570mod mozjs;
571mod objc;
572mod php;
573mod python;
574mod ruby;
575mod rust;
576mod tsx;
577mod typescript;
578
579implement_metric_trait!(
590 Npa,
591 CCode,
592 PreprocCode,
593 CcommentCode,
594 PerlCode,
595 BashCode,
596 LuaCode,
597 TclCode,
598 IrulesCode
599);
600
601#[cfg(test)]
602#[allow(
603 clippy::float_cmp,
604 clippy::cast_precision_loss,
605 clippy::cast_possible_truncation,
606 clippy::cast_sign_loss,
607 clippy::similar_names,
608 clippy::doc_markdown,
609 clippy::needless_raw_string_hashes,
610 clippy::too_many_lines
611)]
612mod tests {
613 use crate::test_support::{
614 assert_child_space_kind, check_func_space_only_shim, check_metrics_only_shim, child_space,
615 };
616
617 use super::*;
618
619 check_metrics_only_shim!(check_metrics, Npa);
620 check_func_space_only_shim!(check_func_space, Npa);
621
622 #[test]
623 fn java_single_attributes() {
624 check_metrics::<JavaParser>(
625 "class X {
626 public byte a; // +1
627 public short b; // +1
628 public int c; // +1
629 public long d; // +1
630 public float e; // +1
631 public double f; // +1
632 public boolean g; // +1
633 public char h; // +1
634 byte i;
635 short j;
636 int k;
637 long l;
638 float m;
639 double n;
640 boolean o;
641 char p;
642 }",
643 "foo.java",
644 |metric| {
645 insta::assert_json_snapshot!(
646 metric.npa,
647 @r#"
648 {
649 "class_npa_sum": 8,
650 "interface_npa_sum": 0,
651 "class_attributes": 16,
652 "interface_attributes": 0,
653 "class_cda": 0.5,
654 "interface_cda": 0.0,
655 "total": 8,
656 "total_attributes": 16,
657 "cda": 0.5
658 }
659 "#
660 );
661 },
662 );
663 }
664
665 #[test]
666 fn java_multiple_attributes() {
667 check_metrics::<JavaParser>(
668 "class X {
669 public byte a1; // +1
670 public short b1, b2; // +2
671 public int c1, c2, c3; // +3
672 public long d1, d2, d3, d4; // +4
673 public float e1, e2, e3, e4; // +4
674 public double f1, f2, f3; // +3
675 public boolean g1, g2; // +2
676 public char h1; // +1
677 byte i1, i2, i3, i4;
678 short j1, j2, j3;
679 int k1, k2;
680 long l1;
681 float m1;
682 double n1, n2;
683 boolean o1, o2, o3;
684 char p1, p2, p3, p4;
685 }",
686 "foo.java",
687 |metric| {
688 insta::assert_json_snapshot!(
689 metric.npa,
690 @r#"
691 {
692 "class_npa_sum": 20,
693 "interface_npa_sum": 0,
694 "class_attributes": 40,
695 "interface_attributes": 0,
696 "class_cda": 0.5,
697 "interface_cda": 0.0,
698 "total": 20,
699 "total_attributes": 40,
700 "cda": 0.5
701 }
702 "#
703 );
704 },
705 );
706 }
707
708 #[test]
709 fn java_initialized_attributes() {
710 check_metrics::<JavaParser>(
711 "class X {
712 public byte a1 = 1; // +1
713 public short b1 = 2, b2; // +2
714 public int c1, c2 = 3, c3; // +3
715 public long d1 = 4, d2, d3, d4 = 5; // +4
716 public float e1, e2 = 6.0f, e3 = 7.0f, e4; // +4
717 public double f1 = 8.0, f2 = 9.0, f3 = 10.0; // +3
718 public boolean g1 = true, g2; // +2
719 public char h1 = 'a'; // +1
720 byte i1 = 1, i2 = 2, i3 = 3, i4 = 4;
721 short j1 = 5, j2, j3 = 6;
722 int k1, k2 = 7;
723 long l1 = 8;
724 float m1 = 9.0f;
725 double n1, n2 = 10.0;
726 boolean o1, o2 = false, o3;
727 char p1 = 'a', p2 = 'b', p3 = 'c', p4 = 'd';
728 }",
729 "foo.java",
730 |metric| {
731 insta::assert_json_snapshot!(
732 metric.npa,
733 @r#"
734 {
735 "class_npa_sum": 20,
736 "interface_npa_sum": 0,
737 "class_attributes": 40,
738 "interface_attributes": 0,
739 "class_cda": 0.5,
740 "interface_cda": 0.0,
741 "total": 20,
742 "total_attributes": 40,
743 "cda": 0.5
744 }
745 "#
746 );
747 },
748 );
749 }
750
751 #[test]
752 fn java_array_attributes() {
753 check_metrics::<JavaParser>(
754 "class X {
755 public byte[] a1, a2, a3, a4; // +4
756 public short b1[], b2[], b3[]; // +3
757 public int[] c1 = { 1 }, c2; // +2
758 public long d1[] = { 1 }; // +1
759 public float[] e1 = { 1.0f, 2.0f, 3.0f }; // +1
760 public double f1[] = { 1.0, 2.0, 3.0 }, f2[]; // +2
761 public boolean[] g1 = new boolean[5], g2, g3; // +3
762 public char[] h1 = new char[5], h2[], h3[], h4[]; // +4
763 byte[] i1;
764 short j1[], j2[];
765 int[] k1, k2, k3 = { 1 };
766 long l1[], l2[] = { 1 }, l3[] = { 2 }, l4[];
767 float[] m1, m2, m3, m4 = { 1.0f, 2.0f, 3.0f };
768 double n1[], n2[] = { 1.0, 2.0, 3.0 }, n3[];
769 boolean[] o1, o2 = new boolean[5];
770 char[] p1 = new char[5];
771 }",
772 "foo.java",
773 |metric| {
774 insta::assert_json_snapshot!(
775 metric.npa,
776 @r#"
777 {
778 "class_npa_sum": 20,
779 "interface_npa_sum": 0,
780 "class_attributes": 40,
781 "interface_attributes": 0,
782 "class_cda": 0.5,
783 "interface_cda": 0.0,
784 "total": 20,
785 "total_attributes": 40,
786 "cda": 0.5
787 }
788 "#
789 );
790 },
791 );
792 }
793
794 #[test]
795 fn java_object_attributes() {
796 check_metrics::<JavaParser>(
797 "class X {
798 public Integer[] a1 = { 1 }; // +1
799 public Integer b1, b2; // +2
800 public String[] c1 = { \"Hello\" }, c2, c3 = { \"World!\" }; // +3
801 public String d1[][] = { { \"Hello\" }, { \"World!\" } }; // +1
802 public Y[] e1, e2[]; // +2
803 public Y f1[], f2[][], f3[][][]; // +3
804 Integer[] g1 = { new Integer(1) };
805 Integer h1 = new Integer(1), h2 = new Integer(2);
806 String[] i1, i2 = { \"Hello World!\" }, i3;
807 String j1 = \"Hello World!\";
808 Y[] k1[], k2;
809 Y l1[][], l2[], l3 = new Y();
810 }",
811 "foo.java",
812 |metric| {
813 insta::assert_json_snapshot!(
814 metric.npa,
815 @r#"
816 {
817 "class_npa_sum": 12,
818 "interface_npa_sum": 0,
819 "class_attributes": 24,
820 "interface_attributes": 0,
821 "class_cda": 0.5,
822 "interface_cda": 0.0,
823 "total": 12,
824 "total_attributes": 24,
825 "cda": 0.5
826 }
827 "#
828 );
829 },
830 );
831 }
832
833 #[test]
834 fn groovy_no_attributes() {
835 check_metrics::<GroovyParser>("class A { void foo() {} }", "foo.groovy", |metric| {
836 assert_eq!(metric.npa.total_na(), 0);
837 assert_eq!(metric.npa.total_npa(), 0);
838 });
839 }
840
841 #[test]
842 fn groovy_public_attributes() {
843 check_metrics::<GroovyParser>(
844 "class A {
845 public int x
846 public String name
847 private int hidden
848 }",
849 "foo.groovy",
850 |metric| {
851 assert_eq!(metric.npa.class_na_sum(), 3);
853 assert_eq!(metric.npa.class_npa_sum(), 2);
854 },
855 );
856 }
857
858 #[test]
859 fn groovy_def_attributes_not_public() {
860 check_metrics::<GroovyParser>(
864 "class A {
865 def field1
866 def field2
867 }",
868 "foo.groovy",
869 |metric| {
870 assert_eq!(metric.npa.class_na_sum(), 2);
872 assert_eq!(metric.npa.class_npa_sum(), 0);
873 },
874 );
875 }
876
877 #[test]
878 fn groovy_interface_attributes() {
879 check_func_space::<GroovyParser, _>(
883 "interface I {
884 public static final int A = 1
885 public static final int B = 2
886 }",
887 "foo.groovy",
888 |func_space| {
889 let metric = &func_space.metrics;
890 assert_eq!(metric.npa.interface_na_sum(), 2);
892 assert_eq!(metric.npa.interface_npa_sum(), 2);
893 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
894 },
895 );
896 }
897
898 #[test]
899 fn groovy_no_attributes_in_unit_scope() {
900 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
901 assert_eq!(metric.npa.total_na(), 0);
902 });
903 }
904
905 #[test]
906 fn groovy_multiple_classes() {
907 check_metrics::<GroovyParser>(
908 "class A { public int a }
909 class B { public int b }",
910 "foo.groovy",
911 |metric| {
912 assert_eq!(metric.npa.class_na_sum(), 2);
913 assert_eq!(metric.npa.class_npa_sum(), 2);
914 },
915 );
916 }
917
918 #[test]
919 fn groovy_initialized_attributes() {
920 check_metrics::<GroovyParser>(
925 "class X {
926 public int a1 = 1, a2
927 public int b1 = 2
928 int c1, c2 = 3
929 }",
930 "foo.groovy",
931 |metric| {
932 assert_eq!(metric.npa.class_na_sum(), 5);
934 assert_eq!(metric.npa.class_npa_sum(), 3);
935 },
936 );
937 }
938
939 #[test]
940 fn groovy_object_attributes() {
941 check_metrics::<GroovyParser>(
944 "class X {
945 public Integer a1
946 public String b1 = 'hello'
947 public Y[] c1
948 }",
949 "foo.groovy",
950 |metric| {
951 assert_eq!(metric.npa.class_na_sum(), 3);
952 assert_eq!(metric.npa.class_npa_sum(), 3);
953 },
954 );
955 }
956
957 #[test]
958 fn groovy_attribute_modifiers() {
959 check_metrics::<GroovyParser>(
963 "class X {
964 public static int a
965 static public int b
966 public final int c = 1
967 final public int d = 2
968 private static int e
969 int f
970 }",
971 "foo.groovy",
972 |metric| {
973 assert_eq!(metric.npa.class_na_sum(), 6);
975 assert_eq!(metric.npa.class_npa_sum(), 4);
976 },
977 );
978 }
979
980 #[test]
981 #[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')"]
982 fn groovy_nested_inner_classes() {
983 check_metrics::<GroovyParser>(
986 "class X {
987 public int a
988 class Y {
989 public boolean b
990 class Z {
991 public char c
992 }
993 }
994 }",
995 "foo.groovy",
996 |metric| {
997 assert_eq!(metric.npa.class_na_sum(), 3);
999 assert_eq!(metric.npa.class_npa_sum(), 3);
1000 },
1001 );
1002 }
1003
1004 #[test]
1005 fn groovy_array_attributes() {
1006 check_metrics::<GroovyParser>(
1008 "class X {
1009 public int[] a
1010 public String[] b
1011 int[] c
1012 }",
1013 "foo.groovy",
1014 |metric| {
1015 assert_eq!(metric.npa.class_na_sum(), 3);
1016 assert_eq!(metric.npa.class_npa_sum(), 2);
1017 },
1018 );
1019 }
1020
1021 #[test]
1022 fn groovy_anonymous_inner_class() {
1023 check_metrics::<GroovyParser>(
1027 "class X {
1028 public Runnable r = new Runnable() {
1029 public int x
1030 void run() {}
1031 }
1032 }",
1033 "foo.groovy",
1034 |metric| {
1035 assert_eq!(metric.npa.class_na_sum(), 2);
1038 assert_eq!(metric.npa.class_npa_sum(), 2);
1039 },
1040 );
1041 }
1042
1043 #[test]
1048 fn groovy_enum_counts_explicit_public_fields() {
1049 check_metrics::<GroovyParser>(
1050 "enum Status {
1051 ACTIVE, INACTIVE;
1052 public int code;
1053 private int hidden;
1054 }",
1055 "foo.groovy",
1056 |metric| {
1057 assert_eq!(metric.npa.class_na_sum(), 2);
1058 assert_eq!(metric.npa.class_npa_sum(), 1);
1059 },
1060 );
1061 }
1062
1063 #[test]
1064 fn groovy_annotation_type_counts_constants_as_implicit_public() {
1065 check_func_space::<GroovyParser, _>(
1073 "public @interface Marker {
1074 int VERSION = 1;
1075 String NAME = \"x\";
1076 }",
1077 "foo.groovy",
1078 |func_space| {
1079 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1080 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1081 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1082 },
1083 );
1084 }
1085
1086 #[test]
1087 fn java_generic_attributes() {
1088 check_metrics::<JavaParser>(
1089 "class X<T, S extends T> {
1090 public T a1; // +1
1091 public Entry<T, S> b1, b2[]; // +2
1092 public ArrayList<T> c1, c2, c3; // +3
1093 public HashMap<Long, Double> d1, d2; // +2
1094 public TreeSet<String> e1; // +1
1095 S f1;
1096 Entry<S, T> g1[], g2;
1097 ArrayList<S> h1, h2, h3;
1098 HashMap<Long, Float> i1, i2;
1099 TreeSet<Entry<S, T>> j1;
1100 }",
1101 "foo.java",
1102 |metric| {
1103 insta::assert_json_snapshot!(
1104 metric.npa,
1105 @r#"
1106 {
1107 "class_npa_sum": 9,
1108 "interface_npa_sum": 0,
1109 "class_attributes": 18,
1110 "interface_attributes": 0,
1111 "class_cda": 0.5,
1112 "interface_cda": 0.0,
1113 "total": 9,
1114 "total_attributes": 18,
1115 "cda": 0.5
1116 }
1117 "#
1118 );
1119 },
1120 );
1121 }
1122
1123 #[test]
1124 fn java_attribute_modifiers() {
1125 check_metrics::<JavaParser>(
1126 "class X {
1127 public transient volatile static int a; // +1
1128 transient public volatile static int b; // +1
1129 transient volatile public static int c; // +1
1130 transient volatile static public int d; // +1
1131 public transient static final int e = 1; // +1
1132 transient public static final int f = 2; // +1
1133 transient static public final int g = 3; // +1
1134 transient static final public int h = 4; // +1
1135 protected transient volatile static int i;
1136 transient volatile static protected int j;
1137 private transient volatile static int k;
1138 transient volatile static private int l;
1139 transient volatile static int m;
1140 transient static final int n = 5;
1141 static public final int o = 6; // +1
1142 final public int p = 7; // +1
1143 }",
1144 "foo.java",
1145 |metric| {
1146 insta::assert_json_snapshot!(
1147 metric.npa,
1148 @r#"
1149 {
1150 "class_npa_sum": 10,
1151 "interface_npa_sum": 0,
1152 "class_attributes": 16,
1153 "interface_attributes": 0,
1154 "class_cda": 0.625,
1155 "interface_cda": 0.0,
1156 "total": 10,
1157 "total_attributes": 16,
1158 "cda": 0.625
1159 }
1160 "#
1161 );
1162 },
1163 );
1164 }
1165
1166 #[test]
1167 fn java_classes() {
1168 check_metrics::<JavaParser>(
1169 "class X {
1170 public int a; // +1
1171 public boolean b; // +1
1172 private char c;
1173 }
1174 class Y {
1175 private double d;
1176 private long e;
1177 public float f; // +1
1178 }",
1179 "foo.java",
1180 |metric| {
1181 insta::assert_json_snapshot!(
1182 metric.npa,
1183 @r#"
1184 {
1185 "class_npa_sum": 3,
1186 "interface_npa_sum": 0,
1187 "class_attributes": 6,
1188 "interface_attributes": 0,
1189 "class_cda": 0.5,
1190 "interface_cda": 0.0,
1191 "total": 3,
1192 "total_attributes": 6,
1193 "cda": 0.5
1194 }
1195 "#
1196 );
1197 },
1198 );
1199 }
1200
1201 #[test]
1202 fn java_nested_inner_classes() {
1203 check_metrics::<JavaParser>(
1204 "class X {
1205 public int a; // +1
1206 class Y {
1207 public boolean b; // +1
1208 class Z {
1209 public char c; // +1
1210 }
1211 }
1212 }",
1213 "foo.java",
1214 |metric| {
1215 insta::assert_json_snapshot!(
1216 metric.npa,
1217 @r#"
1218 {
1219 "class_npa_sum": 3,
1220 "interface_npa_sum": 0,
1221 "class_attributes": 3,
1222 "interface_attributes": 0,
1223 "class_cda": 1.0,
1224 "interface_cda": 0.0,
1225 "total": 3,
1226 "total_attributes": 3,
1227 "cda": 1.0
1228 }
1229 "#
1230 );
1231 },
1232 );
1233 }
1234
1235 #[test]
1236 fn java_local_inner_classes() {
1237 check_metrics::<JavaParser>(
1238 "class X {
1239 public int a; // +1
1240 void x() {
1241 class Y {
1242 public boolean b; // +1
1243 void y() {
1244 class Z {
1245 public char c; // +1
1246 void z() {}
1247 }
1248 }
1249 }
1250 }
1251 }",
1252 "foo.java",
1253 |metric| {
1254 insta::assert_json_snapshot!(
1255 metric.npa,
1256 @r#"
1257 {
1258 "class_npa_sum": 3,
1259 "interface_npa_sum": 0,
1260 "class_attributes": 3,
1261 "interface_attributes": 0,
1262 "class_cda": 1.0,
1263 "interface_cda": 0.0,
1264 "total": 3,
1265 "total_attributes": 3,
1266 "cda": 1.0
1267 }
1268 "#
1269 );
1270 },
1271 );
1272 }
1273
1274 #[test]
1275 fn java_anonymous_inner_classes() {
1276 check_metrics::<JavaParser>(
1277 "abstract class X {
1278 public int a; // +1
1279 }
1280 abstract class Y {
1281 boolean b;
1282 }
1283 class Z {
1284 public char c; // +1
1285 public void z(){
1286 X x1 = new X() {
1287 public double d; // +1
1288 };
1289 Y y1 = new Y() {
1290 long e;
1291 };
1292 }
1293 }",
1294 "foo.java",
1295 |metric| {
1296 insta::assert_json_snapshot!(
1297 metric.npa,
1298 @r#"
1299 {
1300 "class_npa_sum": 3,
1301 "interface_npa_sum": 0,
1302 "class_attributes": 5,
1303 "interface_attributes": 0,
1304 "class_cda": 0.6,
1305 "interface_cda": 0.0,
1306 "total": 3,
1307 "total_attributes": 5,
1308 "cda": 0.6
1309 }
1310 "#
1311 );
1312 },
1313 );
1314 }
1315
1316 #[test]
1317 fn java_interface() {
1318 check_metrics::<JavaParser>(
1319 "interface X {
1320 public int a = 0; // +1
1321 static boolean b = false; // +1
1322 final char c = ' '; // +1
1323 }",
1324 "foo.java",
1325 |metric| {
1326 insta::assert_json_snapshot!(
1327 metric.npa,
1328 @r#"
1329 {
1330 "class_npa_sum": 0,
1331 "interface_npa_sum": 3,
1332 "class_attributes": 0,
1333 "interface_attributes": 3,
1334 "class_cda": 0.0,
1335 "interface_cda": 1.0,
1336 "total": 3,
1337 "total_attributes": 3,
1338 "cda": 1.0
1339 }
1340 "#
1341 );
1342 },
1343 );
1344 }
1345
1346 #[test]
1350 fn java_enum_counts_explicit_public_fields() {
1351 check_metrics::<JavaParser>(
1352 "enum Status {
1353 ACTIVE, INACTIVE;
1354 public static final int FLAG = 1; // implicit static final, still public
1355 public int code; // +1 explicit public
1356 private int hidden; // not public
1357 }",
1358 "foo.java",
1359 |metric| {
1360 assert_eq!(metric.npa.class_na_sum(), 3);
1362 assert_eq!(metric.npa.class_npa_sum(), 2);
1363 },
1364 );
1365 }
1366
1367 #[test]
1374 fn java_record_counts_explicit_body_fields() {
1375 check_metrics::<JavaParser>(
1376 "record Point(int x, int y) {
1377 public static int origin = 0; // explicit body, public
1378 private int cached; // explicit body, private
1379 }",
1380 "foo.java",
1381 |metric| {
1382 assert_eq!(metric.npa.class_na_sum(), 2);
1385 assert_eq!(metric.npa.class_npa_sum(), 1);
1386 },
1387 );
1388 }
1389
1390 #[test]
1391 fn java_annotation_type_counts_constants_as_implicit_public() {
1392 check_func_space::<JavaParser, _>(
1401 "@interface Marker {
1402 int VERSION = 1; // implicit public static final
1403 String NAME = \"x\"; // implicit public static final
1404 }",
1405 "foo.java",
1406 |func_space| {
1407 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1408 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1409 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1410 },
1411 );
1412 }
1413
1414 #[test]
1415 fn php_no_class_attributes() {
1416 check_metrics::<PhpParser>(
1417 "<?php class A { public function f(): void {} }",
1418 "foo.php",
1419 |metric| insta::assert_json_snapshot!(metric.npa),
1420 );
1421 }
1422
1423 #[test]
1424 fn csharp_single_attributes() {
1425 check_metrics::<CsharpParser>(
1426 "class X {
1427 public byte a;
1428 public short b;
1429 public int c;
1430 public long d;
1431 public float e;
1432 public double f;
1433 public bool g;
1434 public char h;
1435 byte i;
1436 short j;
1437 int k;
1438 long l;
1439 float m;
1440 double n;
1441 bool o;
1442 char p;
1443 }",
1444 "foo.cs",
1445 |metric| {
1446 assert_eq!(metric.npa.class_npa_sum(), 8);
1447 assert_eq!(metric.npa.class_na_sum(), 16);
1448 assert_eq!(metric.npa.interface_na_sum(), 0);
1449 insta::assert_json_snapshot!(metric.npa);
1450 },
1451 );
1452 }
1453
1454 #[test]
1455 fn csharp_multiple_attributes() {
1456 check_metrics::<CsharpParser>(
1457 "class X {
1458 public byte a1;
1459 public short b1, b2;
1460 public int c1, c2, c3;
1461 public long d1, d2, d3, d4;
1462 public bool g1, g2;
1463 byte i1, i2, i3, i4;
1464 int k1, k2;
1465 }",
1466 "foo.cs",
1467 |metric| {
1468 assert_eq!(metric.npa.class_npa_sum(), 12);
1469 assert_eq!(metric.npa.class_na_sum(), 18);
1470 assert_eq!(metric.npa.interface_na_sum(), 0);
1471 insta::assert_json_snapshot!(metric.npa);
1472 },
1473 );
1474 }
1475
1476 #[test]
1477 fn csharp_initialized_attributes() {
1478 check_metrics::<CsharpParser>(
1479 "class X {
1480 public int a = 1;
1481 public bool b = true;
1482 public string c = \"hello\";
1483 public double d = 3.14;
1484 int e = 0;
1485 }",
1486 "foo.cs",
1487 |metric| {
1488 assert_eq!(metric.npa.class_npa_sum(), 4);
1489 assert_eq!(metric.npa.class_na_sum(), 5);
1490 assert_eq!(metric.npa.interface_na_sum(), 0);
1491 insta::assert_json_snapshot!(metric.npa);
1492 },
1493 );
1494 }
1495
1496 #[test]
1497 fn csharp_array_attributes() {
1498 check_metrics::<CsharpParser>(
1499 "class X {
1500 public int[] a;
1501 public string[] b = new string[5];
1502 int[] c;
1503 }",
1504 "foo.cs",
1505 |metric| {
1506 assert_eq!(metric.npa.class_npa_sum(), 2);
1507 assert_eq!(metric.npa.class_na_sum(), 3);
1508 assert_eq!(metric.npa.interface_na_sum(), 0);
1509 insta::assert_json_snapshot!(metric.npa);
1510 },
1511 );
1512 }
1513
1514 #[test]
1515 fn csharp_object_attributes() {
1516 check_metrics::<CsharpParser>(
1517 "class Point { public int X, Y; }
1518 class Shape {
1519 public Point origin;
1520 public Point endpoint = new Point();
1521 Point hidden;
1522 }",
1523 "foo.cs",
1524 |metric| {
1525 assert_eq!(metric.npa.class_npa_sum(), 4);
1526 assert_eq!(metric.npa.class_na_sum(), 5);
1527 assert_eq!(metric.npa.interface_na_sum(), 0);
1528 insta::assert_json_snapshot!(metric.npa);
1529 },
1530 );
1531 }
1532
1533 #[test]
1534 fn csharp_generic_attributes() {
1535 check_metrics::<CsharpParser>(
1536 "class X {
1537 public System.Collections.Generic.List<int> a;
1538 public System.Collections.Generic.Dictionary<string, int> b;
1539 System.Collections.Generic.List<string> c;
1540 }",
1541 "foo.cs",
1542 |metric| {
1543 assert_eq!(metric.npa.class_npa_sum(), 2);
1544 assert_eq!(metric.npa.class_na_sum(), 3);
1545 assert_eq!(metric.npa.interface_na_sum(), 0);
1546 insta::assert_json_snapshot!(metric.npa);
1547 },
1548 );
1549 }
1550
1551 #[test]
1552 fn csharp_attribute_modifiers() {
1553 check_metrics::<CsharpParser>(
1554 "class X {
1555 public int a;
1556 private int b;
1557 protected int c;
1558 internal int d;
1559 public static int e;
1560 public readonly int f;
1561 public const int g = 1;
1562 }",
1563 "foo.cs",
1564 |metric| {
1565 assert_eq!(metric.npa.class_npa_sum(), 4);
1568 assert_eq!(metric.npa.class_na_sum(), 7);
1569 assert_eq!(metric.npa.interface_na_sum(), 0);
1570 insta::assert_json_snapshot!(metric.npa);
1571 },
1572 );
1573 }
1574
1575 #[test]
1576 fn csharp_classes() {
1577 check_metrics::<CsharpParser>(
1578 "class A {
1579 public int a;
1580 public int b;
1581 int c;
1582 }
1583 class B {
1584 public string s;
1585 int n;
1586 }",
1587 "foo.cs",
1588 |metric| {
1589 assert_eq!(metric.npa.class_npa_sum(), 3);
1590 assert_eq!(metric.npa.class_na_sum(), 5);
1591 assert_eq!(metric.npa.interface_na_sum(), 0);
1592 insta::assert_json_snapshot!(metric.npa);
1593 },
1594 );
1595 }
1596
1597 #[test]
1598 fn csharp_nested_inner_classes() {
1599 check_metrics::<CsharpParser>(
1600 "class Outer {
1601 public int a;
1602 int b;
1603 public class Inner {
1604 public string s;
1605 int n;
1606 }
1607 }",
1608 "foo.cs",
1609 |metric| {
1610 assert_eq!(metric.npa.class_npa_sum(), 2);
1611 assert_eq!(metric.npa.class_na_sum(), 4);
1612 assert_eq!(metric.npa.interface_na_sum(), 0);
1613 insta::assert_json_snapshot!(metric.npa);
1614 },
1615 );
1616 }
1617
1618 #[test]
1619 fn csharp_struct_attributes() {
1620 check_metrics::<CsharpParser>(
1623 "struct Point {
1624 public int X;
1625 public int Y;
1626 int Hidden;
1627 }",
1628 "foo.cs",
1629 |metric| {
1630 assert_eq!(metric.npa.class_npa_sum(), 2);
1631 assert_eq!(metric.npa.class_na_sum(), 3);
1632 assert_eq!(metric.npa.interface_na_sum(), 0);
1633 insta::assert_json_snapshot!(metric.npa);
1634 },
1635 );
1636 }
1637
1638 #[test]
1639 fn csharp_record_attributes() {
1640 check_metrics::<CsharpParser>(
1643 "record Person {
1644 public string Name;
1645 int Age;
1646 }",
1647 "foo.cs",
1648 |metric| {
1649 assert_eq!(metric.npa.class_npa_sum(), 1);
1650 assert_eq!(metric.npa.class_na_sum(), 2);
1651 assert_eq!(metric.npa.interface_na_sum(), 0);
1652 insta::assert_json_snapshot!(metric.npa);
1653 },
1654 );
1655 }
1656
1657 #[test]
1658 fn csharp_interface() {
1659 check_func_space::<CsharpParser, _>(
1664 "interface I {
1665 static int A = 1;
1666 static string B = \"hello\";
1667 }",
1668 "foo.cs",
1669 |func_space| {
1670 let metric = &func_space.metrics;
1671 assert_eq!(metric.npa.class_na_sum(), 0);
1672 assert_eq!(metric.npa.interface_na_sum(), 2);
1673 assert_eq!(metric.npa.interface_npa_sum(), 2);
1676 insta::assert_json_snapshot!(metric.npa);
1677 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1678 },
1679 );
1680 }
1681
1682 #[test]
1683 fn csharp_interface_explicit_modifiers() {
1684 check_metrics::<CsharpParser>(
1689 "interface I {
1690 private static int Hidden = 1;
1691 protected static int AlsoHidden = 2;
1692 public static int Shown = 3;
1693 static int Implicit = 4;
1694 }",
1695 "foo.cs",
1696 |metric| {
1697 assert_eq!(metric.npa.interface_na_sum(), 4);
1698 assert_eq!(metric.npa.interface_npa_sum(), 2);
1701 assert_eq!(metric.npa.class_na_sum(), 0);
1702 insta::assert_json_snapshot!(metric.npa);
1703 },
1704 );
1705 }
1706
1707 #[test]
1708 fn csharp_interface_multi_declarator_modifier() {
1709 check_metrics::<CsharpParser>(
1714 "interface I {
1715 private static int a = 1, b = 2;
1716 static int c = 3, d = 4;
1717 }",
1718 "foo.cs",
1719 |metric| {
1720 assert_eq!(metric.npa.interface_na_sum(), 4);
1721 assert_eq!(metric.npa.interface_npa_sum(), 2);
1722 assert_eq!(metric.npa.class_na_sum(), 0);
1723 insta::assert_json_snapshot!(metric.npa);
1724 },
1725 );
1726 }
1727
1728 #[test]
1729 fn php_one_public_attribute() {
1730 check_metrics::<PhpParser>(
1731 "<?php class A { public int $x = 0; }",
1732 "foo.php",
1733 |metric| insta::assert_json_snapshot!(metric.npa),
1734 );
1735 }
1736
1737 #[test]
1738 fn php_one_private_attribute() {
1739 check_metrics::<PhpParser>(
1740 "<?php class A { private int $x = 0; }",
1741 "foo.php",
1742 |metric| insta::assert_json_snapshot!(metric.npa),
1743 );
1744 }
1745
1746 #[test]
1747 fn php_one_protected_attribute() {
1748 check_metrics::<PhpParser>(
1749 "<?php class A { protected int $x = 0; }",
1750 "foo.php",
1751 |metric| insta::assert_json_snapshot!(metric.npa),
1752 );
1753 }
1754
1755 #[test]
1756 fn php_mixed_visibility_attributes() {
1757 check_metrics::<PhpParser>(
1758 "<?php
1759 class A {
1760 public int $a = 0;
1761 public int $b = 0;
1762 private int $c = 0;
1763 protected int $d = 0;
1764 }",
1765 "foo.php",
1766 |metric| insta::assert_json_snapshot!(metric.npa),
1767 );
1768 }
1769
1770 #[test]
1771 fn php_static_public_attribute() {
1772 check_metrics::<PhpParser>(
1773 "<?php class A { public static int $x = 0; }",
1774 "foo.php",
1775 |metric| insta::assert_json_snapshot!(metric.npa),
1776 );
1777 }
1778
1779 #[test]
1780 fn php_readonly_public_attribute() {
1781 check_metrics::<PhpParser>(
1782 "<?php class A { public readonly int $x; }",
1783 "foo.php",
1784 |metric| insta::assert_json_snapshot!(metric.npa),
1785 );
1786 }
1787
1788 #[test]
1789 fn php_multiple_attributes_per_declaration() {
1790 check_metrics::<PhpParser>(
1793 "<?php class A { public int $a = 0, $b = 0, $c = 0; }",
1794 "foo.php",
1795 |metric| insta::assert_json_snapshot!(metric.npa),
1796 );
1797 }
1798
1799 #[test]
1800 fn php_interface_constants() {
1801 check_metrics::<PhpParser>(
1803 "<?php
1804 interface I {
1805 const A = 1;
1806 const B = 2;
1807 }",
1808 "foo.php",
1809 |metric| insta::assert_json_snapshot!(metric.npa),
1810 );
1811 }
1812
1813 #[test]
1814 fn php_enum_cases_not_counted() {
1815 check_metrics::<PhpParser>(
1820 "<?php
1821 enum Color {
1822 case Red;
1823 case Green;
1824 case Blue;
1825 }",
1826 "foo.php",
1827 |metric| {
1828 assert_eq!(metric.npa.class_na_sum(), 0);
1829 assert_eq!(metric.npa.class_npa_sum(), 0);
1830 assert_eq!(metric.npa.interface_na_sum(), 0);
1831 insta::assert_json_snapshot!(metric.npa);
1832 },
1833 );
1834 }
1835
1836 #[test]
1837 fn php_enum_const_not_counted() {
1838 check_metrics::<PhpParser>(
1843 "<?php
1844 enum Suit: string {
1845 case Hearts = 'H';
1846 case Diamonds = 'D';
1847 const Wild = 'W';
1848 public function label(): string { return $this->name; }
1849 }",
1850 "foo.php",
1851 |metric| {
1852 assert_eq!(metric.npa.class_na_sum(), 0);
1853 assert_eq!(metric.npa.class_npa_sum(), 0);
1854 },
1855 );
1856 }
1857
1858 #[test]
1859 fn php_enum_npa_matches_java_enum_npa() {
1860 check_metrics::<PhpParser>(
1866 "<?php
1867 enum Color {
1868 case Red;
1869 case Green;
1870 case Blue;
1871 }",
1872 "foo.php",
1873 |metric| {
1874 assert_eq!(metric.npa.class_na_sum(), 0);
1875 assert_eq!(metric.npa.class_npa_sum(), 0);
1876 },
1877 );
1878 check_metrics::<JavaParser>(
1879 "enum Color {
1880 RED, GREEN, BLUE;
1881 }",
1882 "foo.java",
1883 |metric| {
1884 assert_eq!(metric.npa.class_na_sum(), 0);
1885 assert_eq!(metric.npa.class_npa_sum(), 0);
1886 },
1887 );
1888 }
1889
1890 #[test]
1891 fn php_trait_attributes() {
1892 check_metrics::<PhpParser>(
1893 "<?php
1894 trait T {
1895 public int $a = 0;
1896 private int $b = 0;
1897 }",
1898 "foo.php",
1899 |metric| insta::assert_json_snapshot!(metric.npa),
1900 );
1901 }
1902
1903 #[test]
1904 fn php_no_explicit_visibility_excluded() {
1905 check_metrics::<PhpParser>("<?php class A { var $x = 0; }", "foo.php", |metric| {
1909 assert_eq!(metric.npa.class_npa_sum(), 0);
1913 assert_eq!(metric.npa.class_na_sum(), 1);
1914 assert_eq!(metric.npa.interface_na_sum(), 0);
1915 insta::assert_json_snapshot!(metric.npa);
1916 });
1917 }
1918
1919 #[test]
1920 fn php_anonymous_class_attributes() {
1921 check_metrics::<PhpParser>(
1926 "<?php
1927 $obj = new class {
1928 public int $a = 0;
1929 private int $b = 0;
1930 };",
1931 "foo.php",
1932 |metric| insta::assert_json_snapshot!(metric.npa),
1933 );
1934 }
1935
1936 #[test]
1937 fn php_property_promotion_excluded() {
1938 check_metrics::<PhpParser>(
1946 "<?php
1947 class A {
1948 public function __construct(public string $x, public int $y) {}
1949 }",
1950 "foo.php",
1951 |metric| insta::assert_json_snapshot!(metric.npa),
1952 );
1953 }
1954
1955 #[test]
1965 fn kotlin_empty_class_no_attributes() {
1966 check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
1967 assert_eq!(metric.npa.class_npa_sum(), 0);
1968 assert_eq!(metric.npa.class_na_sum(), 0);
1969 assert_eq!(metric.npa.interface_na_sum(), 0);
1970 insta::assert_json_snapshot!(metric.npa);
1971 });
1972 }
1973
1974 #[test]
1975 fn kotlin_public_val_var_default() {
1976 check_metrics::<KotlinParser>(
1978 "class C {
1979 val a: Int = 1
1980 var b: Int = 2
1981 val c: String = \"hi\"
1982 }",
1983 "foo.kt",
1984 |metric| {
1985 assert_eq!(metric.npa.class_npa_sum(), 3);
1986 assert_eq!(metric.npa.class_na_sum(), 3);
1987 insta::assert_json_snapshot!(metric.npa);
1988 },
1989 );
1990 }
1991
1992 #[test]
1993 fn kotlin_private_val_var() {
1994 check_metrics::<KotlinParser>(
1996 "class C {
1997 val a: Int = 1 // public
1998 private val b: Int = 2 // not public
1999 var c: Int = 3 // public
2000 private var d: Int = 4 // not public
2001 }",
2002 "foo.kt",
2003 |metric| {
2004 assert_eq!(metric.npa.class_npa_sum(), 2);
2005 assert_eq!(metric.npa.class_na_sum(), 4);
2006 insta::assert_json_snapshot!(metric.npa);
2007 },
2008 );
2009 }
2010
2011 #[test]
2012 fn kotlin_protected_internal_excluded_from_public() {
2013 check_metrics::<KotlinParser>(
2014 "open class C {
2015 protected val a: Int = 1
2016 internal val b: Int = 2
2017 public val c: Int = 3 // explicit public
2018 }",
2019 "foo.kt",
2020 |metric| {
2021 assert_eq!(metric.npa.class_npa_sum(), 1);
2022 assert_eq!(metric.npa.class_na_sum(), 3);
2023 insta::assert_json_snapshot!(metric.npa);
2024 },
2025 );
2026 }
2027
2028 #[test]
2029 fn kotlin_primary_constructor_parameter_property() {
2030 check_metrics::<KotlinParser>(
2034 "class C(val a: Int, var b: Int, c: Int) {
2035 val d: Int = c
2036 }",
2037 "foo.kt",
2038 |metric| {
2039 assert_eq!(metric.npa.class_npa_sum(), 3);
2041 assert_eq!(metric.npa.class_na_sum(), 3);
2042 insta::assert_json_snapshot!(metric.npa);
2043 },
2044 );
2045 }
2046
2047 #[test]
2048 fn kotlin_primary_constructor_private_param_property() {
2049 check_metrics::<KotlinParser>(
2050 "class C(private val a: Int, val b: Int)",
2051 "foo.kt",
2052 |metric| {
2053 assert_eq!(metric.npa.class_npa_sum(), 1);
2054 assert_eq!(metric.npa.class_na_sum(), 2);
2055 insta::assert_json_snapshot!(metric.npa);
2056 },
2057 );
2058 }
2059
2060 #[test]
2061 fn kotlin_secondary_constructor_does_not_add_attrs() {
2062 check_metrics::<KotlinParser>(
2064 "class C {
2065 private var a: Int = 0
2066 constructor(n: Int) { a = n }
2067 }",
2068 "foo.kt",
2069 |metric| {
2070 assert_eq!(metric.npa.class_npa_sum(), 0);
2071 assert_eq!(metric.npa.class_na_sum(), 1);
2072 insta::assert_json_snapshot!(metric.npa);
2073 },
2074 );
2075 }
2076
2077 #[test]
2078 fn kotlin_companion_object_attributes() {
2079 check_metrics::<KotlinParser>(
2082 "class Holder {
2083 val instance: Int = 1
2084 companion object {
2085 val SCALE: Int = 10
2086 private val SECRET: Int = 7
2087 }
2088 }",
2089 "foo.kt",
2090 |metric| {
2091 assert_eq!(metric.npa.class_npa_sum(), 2);
2094 assert_eq!(metric.npa.class_na_sum(), 3);
2095 insta::assert_json_snapshot!(metric.npa);
2096 },
2097 );
2098 }
2099
2100 #[test]
2101 fn kotlin_data_class_attributes() {
2102 check_metrics::<KotlinParser>(
2104 "data class Point(val x: Int, val y: Int)",
2105 "foo.kt",
2106 |metric| {
2107 assert_eq!(metric.npa.class_npa_sum(), 2);
2108 assert_eq!(metric.npa.class_na_sum(), 2);
2109 insta::assert_json_snapshot!(metric.npa);
2110 },
2111 );
2112 }
2113
2114 #[test]
2115 fn kotlin_object_singleton_attributes() {
2116 check_metrics::<KotlinParser>(
2117 "object Config {
2118 val DEFAULT: Int = 42
2119 private val SEED: Int = 0
2120 var debug: Boolean = false
2121 }",
2122 "foo.kt",
2123 |metric| {
2124 assert_eq!(metric.npa.class_npa_sum(), 2);
2126 assert_eq!(metric.npa.class_na_sum(), 3);
2127 insta::assert_json_snapshot!(metric.npa);
2128 },
2129 );
2130 }
2131
2132 #[test]
2133 fn kotlin_interface_attributes() {
2134 check_func_space::<KotlinParser, _>(
2140 "interface I {
2141 val a: Int
2142 val b: String
2143 }",
2144 "foo.kt",
2145 |func_space| {
2146 let metric = &func_space.metrics;
2147 assert_eq!(metric.npa.interface_npa_sum(), 2);
2148 assert_eq!(metric.npa.interface_na_sum(), 2);
2149 assert_eq!(metric.npa.class_na_sum(), 0);
2150 insta::assert_json_snapshot!(metric.npa);
2151 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2152 },
2153 );
2154 }
2155
2156 #[test]
2157 fn kotlin_nested_class_attributes() {
2158 check_metrics::<KotlinParser>(
2161 "class Outer {
2162 val o1: Int = 1
2163 class Nested {
2164 val n1: Int = 1
2165 val n2: Int = 2
2166 }
2167 }",
2168 "foo.kt",
2169 |metric| {
2170 assert_eq!(metric.npa.class_npa_sum(), 3);
2172 assert_eq!(metric.npa.class_na_sum(), 3);
2173 insta::assert_json_snapshot!(metric.npa);
2174 },
2175 );
2176 }
2177
2178 #[test]
2179 fn kotlin_inner_class_attributes() {
2180 check_metrics::<KotlinParser>(
2181 "class Outer {
2182 val o1: Int = 1
2183 inner class Inner {
2184 val i1: Int = 1
2185 }
2186 }",
2187 "foo.kt",
2188 |metric| {
2189 assert_eq!(metric.npa.class_npa_sum(), 2);
2190 assert_eq!(metric.npa.class_na_sum(), 2);
2191 insta::assert_json_snapshot!(metric.npa);
2192 },
2193 );
2194 }
2195
2196 #[test]
2197 fn kotlin_top_level_properties_excluded() {
2198 check_metrics::<KotlinParser>(
2201 "val topVal: Int = 0
2202 var topVar: Int = 1
2203 class C { val x: Int = 0 }",
2204 "foo.kt",
2205 |metric| {
2206 assert_eq!(metric.npa.class_npa_sum(), 1);
2207 assert_eq!(metric.npa.class_na_sum(), 1);
2208 insta::assert_json_snapshot!(metric.npa);
2209 },
2210 );
2211 }
2212
2213 #[test]
2214 fn kotlin_multiple_classes_attributes() {
2215 check_metrics::<KotlinParser>(
2216 "class A {
2217 val a1: Int = 0
2218 var a2: Int = 0
2219 }
2220 class B {
2221 val b1: Int = 0
2222 private val b2: Int = 0
2223 }",
2224 "foo.kt",
2225 |metric| {
2226 assert_eq!(metric.npa.class_npa_sum(), 3);
2228 assert_eq!(metric.npa.class_na_sum(), 4);
2229 insta::assert_json_snapshot!(metric.npa);
2230 },
2231 );
2232 }
2233
2234 #[test]
2235 fn kotlin_class_with_methods_no_attrs() {
2236 check_metrics::<KotlinParser>(
2238 "class C {
2239 fun m1() {}
2240 fun m2(): Int = 0
2241 }",
2242 "foo.kt",
2243 |metric| {
2244 assert_eq!(metric.npa.class_npa_sum(), 0);
2245 assert_eq!(metric.npa.class_na_sum(), 0);
2246 insta::assert_json_snapshot!(metric.npa);
2247 },
2248 );
2249 }
2250
2251 #[test]
2263 fn typescript_empty_class_no_attributes() {
2264 check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
2265 assert_eq!(metric.npa.class_npa_sum(), 0);
2266 assert_eq!(metric.npa.class_na_sum(), 0);
2267 insta::assert_json_snapshot!(metric.npa);
2268 });
2269 }
2270
2271 #[test]
2272 fn typescript_default_public_fields() {
2273 check_metrics::<TypescriptParser>(
2275 "class C {
2276 a: number = 1;
2277 b: string = \"\";
2278 c: boolean = false;
2279 }",
2280 "foo.ts",
2281 |metric| {
2282 assert_eq!(metric.npa.class_npa_sum(), 3);
2283 assert_eq!(metric.npa.class_na_sum(), 3);
2284 insta::assert_json_snapshot!(metric.npa);
2285 },
2286 );
2287 }
2288
2289 #[test]
2290 fn typescript_visibility_modifiers() {
2291 check_metrics::<TypescriptParser>(
2293 "class C {
2294 public a: number = 1;
2295 private b: number = 2;
2296 protected c: number = 3;
2297 d: number = 4;
2298 }",
2299 "foo.ts",
2300 |metric| {
2301 assert_eq!(metric.npa.class_npa_sum(), 2);
2303 assert_eq!(metric.npa.class_na_sum(), 4);
2304 insta::assert_json_snapshot!(metric.npa);
2305 },
2306 );
2307 }
2308
2309 #[test]
2310 fn typescript_static_fields() {
2311 check_metrics::<TypescriptParser>(
2313 "class C {
2314 static a: number = 0;
2315 public static b: number = 0;
2316 private static c: number = 0;
2317 }",
2318 "foo.ts",
2319 |metric| {
2320 assert_eq!(metric.npa.class_npa_sum(), 2);
2322 assert_eq!(metric.npa.class_na_sum(), 3);
2323 insta::assert_json_snapshot!(metric.npa);
2324 },
2325 );
2326 }
2327
2328 #[test]
2329 fn typescript_parameter_properties() {
2330 check_metrics::<TypescriptParser>(
2332 "class C {
2333 constructor(public a: number, private b: string, c: boolean) {}
2334 }",
2335 "foo.ts",
2336 |metric| {
2337 assert_eq!(metric.npa.class_npa_sum(), 1);
2340 assert_eq!(metric.npa.class_na_sum(), 2);
2341 insta::assert_json_snapshot!(metric.npa);
2342 },
2343 );
2344 }
2345
2346 #[test]
2347 fn typescript_readonly_constructor_param_property() {
2348 check_metrics::<TypescriptParser>(
2355 "class C {
2356 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2357 }",
2358 "foo.ts",
2359 |metric| {
2360 assert_eq!(metric.npa.class_na_sum(), 4);
2364 assert_eq!(metric.npa.class_npa_sum(), 2);
2365 insta::assert_json_snapshot!(metric.npa);
2366 },
2367 );
2368 }
2369
2370 #[test]
2371 fn typescript_readonly_field() {
2372 check_metrics::<TypescriptParser>(
2375 "class C {
2376 readonly a: number = 1;
2377 private readonly b: number = 2;
2378 }",
2379 "foo.ts",
2380 |metric| {
2381 assert_eq!(metric.npa.class_npa_sum(), 1);
2382 assert_eq!(metric.npa.class_na_sum(), 2);
2383 insta::assert_json_snapshot!(metric.npa);
2384 },
2385 );
2386 }
2387
2388 #[test]
2389 fn typescript_abstract_class_attributes() {
2390 check_metrics::<TypescriptParser>(
2393 "abstract class C {
2394 public a: number = 1;
2395 protected b: number = 2;
2396 abstract m(): void;
2397 }",
2398 "foo.ts",
2399 |metric| {
2400 assert_eq!(metric.npa.class_npa_sum(), 1);
2403 assert_eq!(metric.npa.class_na_sum(), 2);
2404 insta::assert_json_snapshot!(metric.npa);
2405 },
2406 );
2407 }
2408
2409 #[test]
2410 fn typescript_arrow_field_is_method_not_attribute() {
2411 check_metrics::<TypescriptParser>(
2414 "class C {
2415 a: number = 0;
2416 arrow = () => this.a;
2417 }",
2418 "foo.ts",
2419 |metric| {
2420 assert_eq!(metric.npa.class_npa_sum(), 1);
2421 assert_eq!(metric.npa.class_na_sum(), 1);
2422 insta::assert_json_snapshot!(metric.npa);
2423 },
2424 );
2425 }
2426
2427 #[test]
2428 fn typescript_interface_property_signatures() {
2429 check_func_space::<TypescriptParser, _>(
2435 "interface I {
2436 a: number;
2437 b: string;
2438 m(): void;
2439 }",
2440 "foo.ts",
2441 |func_space| {
2442 let metric = &func_space.metrics;
2443 assert_eq!(metric.npa.interface_npa_sum(), 2);
2444 assert_eq!(metric.npa.interface_na_sum(), 2);
2445 assert_eq!(metric.npa.class_na_sum(), 0);
2446 insta::assert_json_snapshot!(metric.npa);
2447 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2448 },
2449 );
2450 }
2451
2452 #[test]
2453 fn typescript_generic_class_attributes() {
2454 check_metrics::<TypescriptParser>(
2456 "class Box<T, U> {
2457 value: T;
2458 other: U;
2459 constructor(v: T, o: U) { this.value = v; this.other = o; }
2460 }",
2461 "foo.ts",
2462 |metric| {
2463 assert_eq!(metric.npa.class_npa_sum(), 2);
2464 assert_eq!(metric.npa.class_na_sum(), 2);
2465 insta::assert_json_snapshot!(metric.npa);
2466 },
2467 );
2468 }
2469
2470 #[test]
2471 fn typescript_getters_setters_not_attributes() {
2472 check_metrics::<TypescriptParser>(
2474 "class C {
2475 private _x: number = 0;
2476 get x(): number { return this._x; }
2477 set x(v: number) { this._x = v; }
2478 }",
2479 "foo.ts",
2480 |metric| {
2481 assert_eq!(metric.npa.class_npa_sum(), 0);
2483 assert_eq!(metric.npa.class_na_sum(), 1);
2484 insta::assert_json_snapshot!(metric.npa);
2485 },
2486 );
2487 }
2488
2489 #[test]
2490 fn typescript_multiple_classes_and_interface() {
2491 check_func_space::<TypescriptParser, _>(
2492 "class A { x: number = 0; }
2493 class B { private y: number = 0; }
2494 interface I { z: number; }",
2495 "foo.ts",
2496 |func_space| {
2497 let metric = &func_space.metrics;
2498 assert_eq!(metric.npa.class_npa_sum(), 1);
2501 assert_eq!(metric.npa.class_na_sum(), 2);
2502 assert_eq!(metric.npa.interface_npa_sum(), 1);
2503 assert_eq!(metric.npa.interface_na_sum(), 1);
2504 insta::assert_json_snapshot!(metric.npa);
2505 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2506 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2507 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2508 },
2509 );
2510 }
2511
2512 #[test]
2513 fn typescript_nested_class_attributes_independent() {
2514 check_metrics::<TypescriptParser>(
2522 "class Outer {
2523 a: number = 0;
2524 static Inner = class {
2525 b: number = 0;
2526 c: number = 0;
2527 };
2528 }",
2529 "foo.ts",
2530 |metric| {
2531 assert_eq!(metric.npa.class_npa_sum(), 4);
2532 assert_eq!(metric.npa.class_na_sum(), 4);
2533 insta::assert_json_snapshot!(metric.npa);
2534 },
2535 );
2536 }
2537
2538 #[test]
2542 fn tsx_empty_class_no_attributes() {
2543 check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
2544 assert_eq!(metric.npa.class_npa_sum(), 0);
2545 assert_eq!(metric.npa.class_na_sum(), 0);
2546 insta::assert_json_snapshot!(metric.npa);
2547 });
2548 }
2549
2550 #[test]
2551 fn tsx_default_public_fields() {
2552 check_metrics::<TsxParser>(
2553 "class C {
2554 a: number = 1;
2555 b: string = \"\";
2556 }",
2557 "foo.tsx",
2558 |metric| {
2559 assert_eq!(metric.npa.class_npa_sum(), 2);
2560 assert_eq!(metric.npa.class_na_sum(), 2);
2561 insta::assert_json_snapshot!(metric.npa);
2562 },
2563 );
2564 }
2565
2566 #[test]
2567 fn tsx_visibility_modifiers() {
2568 check_metrics::<TsxParser>(
2569 "class C {
2570 public a: number = 1;
2571 private b: number = 2;
2572 protected c: number = 3;
2573 }",
2574 "foo.tsx",
2575 |metric| {
2576 assert_eq!(metric.npa.class_npa_sum(), 1);
2577 assert_eq!(metric.npa.class_na_sum(), 3);
2578 insta::assert_json_snapshot!(metric.npa);
2579 },
2580 );
2581 }
2582
2583 #[test]
2584 fn tsx_parameter_properties() {
2585 check_metrics::<TsxParser>(
2586 "class C {
2587 constructor(public a: number, private b: string) {}
2588 }",
2589 "foo.tsx",
2590 |metric| {
2591 assert_eq!(metric.npa.class_npa_sum(), 1);
2592 assert_eq!(metric.npa.class_na_sum(), 2);
2593 insta::assert_json_snapshot!(metric.npa);
2594 },
2595 );
2596 }
2597
2598 #[test]
2599 fn tsx_readonly_constructor_param_property() {
2600 check_metrics::<TsxParser>(
2605 "class C {
2606 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2607 }",
2608 "foo.tsx",
2609 |metric| {
2610 assert_eq!(metric.npa.class_na_sum(), 4);
2611 assert_eq!(metric.npa.class_npa_sum(), 2);
2612 insta::assert_json_snapshot!(metric.npa);
2613 },
2614 );
2615 }
2616
2617 #[test]
2618 fn tsx_abstract_class_attributes() {
2619 check_metrics::<TsxParser>(
2620 "abstract class C {
2621 public a: number = 1;
2622 private b: number = 2;
2623 abstract m(): void;
2624 }",
2625 "foo.tsx",
2626 |metric| {
2627 assert_eq!(metric.npa.class_npa_sum(), 1);
2628 assert_eq!(metric.npa.class_na_sum(), 2);
2629 insta::assert_json_snapshot!(metric.npa);
2630 },
2631 );
2632 }
2633
2634 #[test]
2635 fn tsx_interface_property_signatures() {
2636 check_func_space::<TsxParser, _>(
2637 "interface I {
2638 a: number;
2639 b: string;
2640 m(): void;
2641 }",
2642 "foo.tsx",
2643 |func_space| {
2644 let metric = &func_space.metrics;
2645 assert_eq!(metric.npa.interface_npa_sum(), 2);
2646 assert_eq!(metric.npa.interface_na_sum(), 2);
2647 insta::assert_json_snapshot!(metric.npa);
2648 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2649 },
2650 );
2651 }
2652
2653 #[test]
2654 fn tsx_arrow_field_is_method_not_attribute() {
2655 check_metrics::<TsxParser>(
2656 "class C {
2657 a: number = 0;
2658 arrow = () => this.a;
2659 }",
2660 "foo.tsx",
2661 |metric| {
2662 assert_eq!(metric.npa.class_npa_sum(), 1);
2663 assert_eq!(metric.npa.class_na_sum(), 1);
2664 insta::assert_json_snapshot!(metric.npa);
2665 },
2666 );
2667 }
2668
2669 #[test]
2670 fn tsx_static_fields() {
2671 check_metrics::<TsxParser>(
2672 "class C {
2673 static a: number = 0;
2674 private static b: number = 0;
2675 }",
2676 "foo.tsx",
2677 |metric| {
2678 assert_eq!(metric.npa.class_npa_sum(), 1);
2679 assert_eq!(metric.npa.class_na_sum(), 2);
2680 insta::assert_json_snapshot!(metric.npa);
2681 },
2682 );
2683 }
2684
2685 #[test]
2686 fn tsx_readonly_field() {
2687 check_metrics::<TsxParser>(
2688 "class C {
2689 readonly a: number = 1;
2690 private readonly b: number = 2;
2691 }",
2692 "foo.tsx",
2693 |metric| {
2694 assert_eq!(metric.npa.class_npa_sum(), 1);
2695 assert_eq!(metric.npa.class_na_sum(), 2);
2696 insta::assert_json_snapshot!(metric.npa);
2697 },
2698 );
2699 }
2700
2701 #[test]
2702 fn tsx_generic_class_attributes() {
2703 check_metrics::<TsxParser>("class Box<T> { value: T; }", "foo.tsx", |metric| {
2704 assert_eq!(metric.npa.class_npa_sum(), 1);
2705 assert_eq!(metric.npa.class_na_sum(), 1);
2706 insta::assert_json_snapshot!(metric.npa);
2707 });
2708 }
2709
2710 #[test]
2711 fn tsx_getters_setters_not_attributes() {
2712 check_metrics::<TsxParser>(
2713 "class C {
2714 private _x: number = 0;
2715 get x(): number { return this._x; }
2716 set x(v: number) { this._x = v; }
2717 }",
2718 "foo.tsx",
2719 |metric| {
2720 assert_eq!(metric.npa.class_npa_sum(), 0);
2721 assert_eq!(metric.npa.class_na_sum(), 1);
2722 insta::assert_json_snapshot!(metric.npa);
2723 },
2724 );
2725 }
2726
2727 #[test]
2728 fn tsx_multiple_classes_and_interface() {
2729 check_func_space::<TsxParser, _>(
2730 "class A { x: number = 0; }
2731 class B { private y: number = 0; }
2732 interface I { z: number; }",
2733 "foo.tsx",
2734 |func_space| {
2735 let metric = &func_space.metrics;
2736 assert_eq!(metric.npa.class_npa_sum(), 1);
2737 assert_eq!(metric.npa.class_na_sum(), 2);
2738 assert_eq!(metric.npa.interface_npa_sum(), 1);
2739 assert_eq!(metric.npa.interface_na_sum(), 1);
2740 insta::assert_json_snapshot!(metric.npa);
2741 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2742 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2743 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2744 },
2745 );
2746 }
2747
2748 #[test]
2758 fn ruby_no_class_attributes() {
2759 check_metrics::<RubyParser>(
2760 "class A\n def f\n 1\n end\nend\n",
2761 "foo.rb",
2762 |metric| {
2763 assert_eq!(metric.npa.class_npa_sum(), 0);
2764 assert_eq!(metric.npa.class_na_sum(), 0);
2765 insta::assert_json_snapshot!(metric.npa);
2766 },
2767 );
2768 }
2769
2770 #[test]
2771 fn ruby_instance_variable_attribute() {
2772 check_metrics::<RubyParser>("class A\n @x = 1\nend\n", "foo.rb", |metric| {
2774 assert_eq!(metric.npa.class_npa_sum(), 1);
2775 assert_eq!(metric.npa.class_na_sum(), 1);
2776 insta::assert_json_snapshot!(metric.npa);
2777 });
2778 }
2779
2780 #[test]
2781 fn ruby_class_variable_attribute() {
2782 check_metrics::<RubyParser>("class A\n @@y = 1\nend\n", "foo.rb", |metric| {
2784 assert_eq!(metric.npa.class_npa_sum(), 1);
2785 assert_eq!(metric.npa.class_na_sum(), 1);
2786 insta::assert_json_snapshot!(metric.npa);
2787 });
2788 }
2789
2790 #[test]
2791 fn ruby_attr_accessor_counts_symbols() {
2792 check_metrics::<RubyParser>(
2794 "class A\n attr_accessor :x, :y, :z\nend\n",
2795 "foo.rb",
2796 |metric| {
2797 assert_eq!(metric.npa.class_npa_sum(), 3);
2798 assert_eq!(metric.npa.class_na_sum(), 3);
2799 insta::assert_json_snapshot!(metric.npa);
2800 },
2801 );
2802 }
2803
2804 #[test]
2805 fn ruby_attr_reader_and_writer() {
2806 check_metrics::<RubyParser>(
2807 "class A\n attr_reader :r1, :r2\n attr_writer :w\nend\n",
2808 "foo.rb",
2809 |metric| {
2810 assert_eq!(metric.npa.class_npa_sum(), 3);
2811 assert_eq!(metric.npa.class_na_sum(), 3);
2812 insta::assert_json_snapshot!(metric.npa);
2813 },
2814 );
2815 }
2816
2817 #[test]
2818 fn ruby_mixed_attributes_and_assignments() {
2819 check_metrics::<RubyParser>(
2820 "class A\n attr_accessor :x, :y\n @z = 1\n @@w = 2\nend\n",
2821 "foo.rb",
2822 |metric| {
2823 assert_eq!(metric.npa.class_npa_sum(), 4);
2824 assert_eq!(metric.npa.class_na_sum(), 4);
2825 insta::assert_json_snapshot!(metric.npa);
2826 },
2827 );
2828 }
2829
2830 #[test]
2831 fn ruby_private_attributes() {
2832 check_metrics::<RubyParser>(
2834 "class A\n attr_accessor :pub\n private\n attr_accessor :hidden\nend\n",
2835 "foo.rb",
2836 |metric| {
2837 assert_eq!(metric.npa.class_npa_sum(), 1);
2838 assert_eq!(metric.npa.class_na_sum(), 2);
2839 insta::assert_json_snapshot!(metric.npa);
2840 },
2841 );
2842 }
2843
2844 #[test]
2845 fn ruby_visibility_public_resets_private() {
2846 check_metrics::<RubyParser>(
2848 "class A\n attr_reader :a\n private\n attr_reader :b\n public\n attr_reader :c\nend\n",
2849 "foo.rb",
2850 |metric| {
2851 assert_eq!(metric.npa.class_npa_sum(), 2);
2852 assert_eq!(metric.npa.class_na_sum(), 3);
2853 insta::assert_json_snapshot!(metric.npa);
2854 },
2855 );
2856 }
2857
2858 #[test]
2859 fn ruby_method_scope_assignments_excluded() {
2860 check_metrics::<RubyParser>(
2864 "class A\n def init\n @x = 1\n @@y = 2\n end\nend\n",
2865 "foo.rb",
2866 |metric| {
2867 assert_eq!(metric.npa.class_npa_sum(), 0);
2868 assert_eq!(metric.npa.class_na_sum(), 0);
2869 insta::assert_json_snapshot!(metric.npa);
2870 },
2871 );
2872 }
2873
2874 #[test]
2875 fn ruby_module_attributes_not_counted() {
2876 check_metrics::<RubyParser>(
2879 "module M\n attr_accessor :x\n @@m = 1\nend\n",
2880 "foo.rb",
2881 |metric| {
2882 assert_eq!(metric.npa.class_npa_sum(), 0);
2883 assert_eq!(metric.npa.class_na_sum(), 0);
2884 insta::assert_json_snapshot!(metric.npa);
2885 },
2886 );
2887 }
2888
2889 #[test]
2890 fn ruby_inheritance_attributes() {
2891 check_metrics::<RubyParser>(
2893 "class A < B\n attr_accessor :x\n @y = 0\nend\n",
2894 "foo.rb",
2895 |metric| {
2896 assert_eq!(metric.npa.class_npa_sum(), 2);
2897 assert_eq!(metric.npa.class_na_sum(), 2);
2898 insta::assert_json_snapshot!(metric.npa);
2899 },
2900 );
2901 }
2902
2903 #[test]
2904 fn ruby_constant_assignments_excluded() {
2905 check_metrics::<RubyParser>(
2909 "class A\n PI = 3.14\n E = 2.71\n attr_reader :x\nend\n",
2910 "foo.rb",
2911 |metric| {
2912 assert_eq!(metric.npa.class_npa_sum(), 1);
2914 assert_eq!(metric.npa.class_na_sum(), 1);
2915 insta::assert_json_snapshot!(metric.npa);
2916 },
2917 );
2918 }
2919
2920 #[test]
2921 fn ruby_multiple_classes_attribute_rollup() {
2922 check_metrics::<RubyParser>(
2923 "class A\n attr_accessor :x\nend\nclass B\n private\n attr_accessor :y\nend\n",
2924 "foo.rb",
2925 |metric| {
2926 assert_eq!(metric.npa.class_npa_sum(), 1);
2928 assert_eq!(metric.npa.class_na_sum(), 2);
2929 insta::assert_json_snapshot!(metric.npa);
2930 },
2931 );
2932 }
2933
2934 #[test]
2947 fn python_empty_class_no_attributes() {
2948 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
2949 assert_eq!(metric.npa.class_na_sum(), 0);
2950 assert_eq!(metric.npa.class_npa_sum(), 0);
2951 assert_eq!(metric.npa.interface_na_sum(), 0);
2952 insta::assert_json_snapshot!(metric.npa);
2953 });
2954 }
2955
2956 #[test]
2957 fn python_class_level_assignments_are_attributes() {
2958 check_metrics::<PythonParser>("class C:\n x = 1\n y = 2\n", "foo.py", |metric| {
2961 assert_eq!(metric.npa.class_na_sum(), 2);
2962 assert_eq!(metric.npa.class_npa_sum(), 2);
2963 insta::assert_json_snapshot!(metric.npa);
2964 });
2965 }
2966
2967 #[test]
2968 fn python_bare_type_annotation_not_attribute() {
2969 check_metrics::<PythonParser>(
2972 "class C:\n x: int\n y: int = 2\n",
2973 "foo.py",
2974 |metric| {
2975 assert_eq!(metric.npa.class_na_sum(), 1);
2976 insta::assert_json_snapshot!(metric.npa);
2977 },
2978 );
2979 }
2980
2981 #[test]
2982 fn python_self_attributes_in_init() {
2983 check_metrics::<PythonParser>(
2986 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n",
2987 "foo.py",
2988 |metric| {
2989 assert_eq!(metric.npa.class_na_sum(), 2);
2990 assert_eq!(metric.npa.class_npa_sum(), 2);
2991 insta::assert_json_snapshot!(metric.npa);
2992 },
2993 );
2994 }
2995
2996 #[test]
2997 fn python_self_attributes_in_nested_control_flow() {
2998 check_metrics::<PythonParser>(
3002 "class C:\n def __init__(self, flag):\n if flag:\n self.z = 1\n else:\n self.z = 2\n",
3003 "foo.py",
3004 |metric| {
3005 assert_eq!(metric.npa.class_na_sum(), 1);
3006 insta::assert_json_snapshot!(metric.npa);
3007 },
3008 );
3009 }
3010
3011 #[test]
3022 fn python_defensive_reinit_self_attribute_counts_once() {
3023 check_metrics::<PythonParser>(
3024 "class C:\n def __init__(self):\n self.value = None\n def reset(self):\n self.value = 0\n",
3025 "foo.py",
3026 |metric| {
3027 assert_eq!(metric.npa.class_na_sum(), 1);
3028 assert_eq!(metric.npa.class_npa_sum(), 1);
3029 insta::assert_json_snapshot!(metric.npa);
3030 },
3031 );
3032 }
3033
3034 #[test]
3037 fn python_distinct_self_attributes_count_independently() {
3038 check_metrics::<PythonParser>(
3039 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n self.z = 3\n",
3040 "foo.py",
3041 |metric| {
3042 assert_eq!(metric.npa.class_na_sum(), 3);
3043 insta::assert_json_snapshot!(metric.npa);
3044 },
3045 );
3046 }
3047
3048 #[test]
3055 fn python_self_attribute_annotated_assignment_dedupes() {
3056 check_metrics::<PythonParser>(
3057 "class C:\n def __init__(self):\n self.value: int = 1\n def reset(self):\n self.value = 0\n",
3058 "foo.py",
3059 |metric| {
3060 assert_eq!(metric.npa.class_na_sum(), 1);
3061 insta::assert_json_snapshot!(metric.npa);
3062 },
3063 );
3064 }
3065
3066 #[test]
3067 fn python_class_level_and_self_attrs_combine() {
3068 check_metrics::<PythonParser>(
3070 "class C:\n counter = 0\n def __init__(self):\n self.name = 'a'\n self.value = 1\n",
3071 "foo.py",
3072 |metric| {
3073 assert_eq!(metric.npa.class_na_sum(), 3);
3074 insta::assert_json_snapshot!(metric.npa);
3075 },
3076 );
3077 }
3078
3079 #[test]
3080 fn python_self_attrs_isolated_per_class() {
3081 check_metrics::<PythonParser>(
3087 "class Outer:\n\
3088 \x20 def __init__(self):\n\
3089 \x20 self.x = 1\n\
3090 \x20 class Inner:\n\
3091 \x20 def __init__(self):\n\
3092 \x20 self.z = 2\n",
3093 "foo.py",
3094 |metric| {
3095 assert_eq!(metric.npa.class_na_sum(), 2);
3096 insta::assert_json_snapshot!(metric.npa);
3097 },
3098 );
3099 }
3100
3101 #[test]
3102 fn python_decorated_methods_do_not_inflate_attrs() {
3103 check_metrics::<PythonParser>(
3107 "class C:\n\
3108 \x20 @property\n\
3109 \x20 def p(self):\n\
3110 \x20 return 1\n\
3111 \x20 @staticmethod\n\
3112 \x20 def s():\n\
3113 \x20 return 2\n",
3114 "foo.py",
3115 |metric| {
3116 assert_eq!(metric.npa.class_na_sum(), 0);
3117 insta::assert_json_snapshot!(metric.npa);
3118 },
3119 );
3120 }
3121
3122 #[test]
3123 fn python_module_level_assignments_not_attributes() {
3124 check_metrics::<PythonParser>("x = 1\ny = 2\nclass C:\n a = 3\n", "foo.py", |metric| {
3126 assert_eq!(metric.npa.class_na_sum(), 1);
3128 insta::assert_json_snapshot!(metric.npa);
3129 });
3130 }
3131
3132 #[test]
3138 fn python_foreign_object_writes_not_attributes() {
3139 check_metrics::<PythonParser>(
3140 "class Service:\n\
3141 \x20 def __init__(self, db, logger):\n\
3142 \x20 self.name = \"svc\"\n\
3143 \x20 db.connection = None\n\
3144 \x20 logger.level = \"INFO\"\n",
3145 "foo.py",
3146 |metric| {
3147 assert_eq!(metric.npa.class_na_sum(), 1);
3149 assert_eq!(metric.npa.class_npa_sum(), 1);
3150 insta::assert_json_snapshot!(metric.npa);
3151 },
3152 );
3153 }
3154
3155 #[test]
3160 fn python_self_attribute_unpacking_counts_each() {
3161 check_metrics::<PythonParser>(
3162 "class C:\n\
3163 \x20 def __init__(self):\n\
3164 \x20 self.a, self.b = 1, 2\n\
3165 \x20 self.c = 3\n",
3166 "foo.py",
3167 |metric| {
3168 assert_eq!(metric.npa.class_na_sum(), 3);
3170 assert_eq!(metric.npa.class_npa_sum(), 3);
3171 insta::assert_json_snapshot!(metric.npa);
3172 },
3173 );
3174 }
3175
3176 #[test]
3182 fn python_self_attribute_nested_unpacking_counts_each() {
3183 check_metrics::<PythonParser>(
3184 "class C:\n\
3185 \x20 def __init__(self):\n\
3186 \x20 self.a, (self.b, self.c) = 1, (2, 3)\n",
3187 "foo.py",
3188 |metric| {
3189 assert_eq!(metric.npa.class_na_sum(), 3);
3191 assert_eq!(metric.npa.class_npa_sum(), 3);
3192 },
3193 );
3194 }
3195
3196 #[test]
3201 fn python_class_level_nested_unpacking_counts_each() {
3202 check_metrics::<PythonParser>(
3203 "class C:\n\
3204 \x20 (a, (b, c)) = 1, (2, 3)\n",
3205 "foo.py",
3206 |metric| {
3207 assert_eq!(metric.npa.class_na_sum(), 3);
3209 assert_eq!(metric.npa.class_npa_sum(), 3);
3210 },
3211 );
3212 }
3213
3214 #[test]
3222 fn python_class_level_parenthesized_unpacking_counts_each() {
3223 check_metrics::<PythonParser>(
3224 "class C:\n\
3225 \x20 (p, q) = 1, 2\n\
3226 \x20 [m, n] = 3, 4\n",
3227 "foo.py",
3228 |metric| {
3229 assert_eq!(metric.npa.class_na_sum(), 4);
3231 assert_eq!(metric.npa.class_npa_sum(), 4);
3232 },
3233 );
3234 }
3235
3236 #[test]
3240 fn python_self_attribute_unpacking_skips_non_self_targets() {
3241 check_metrics::<PythonParser>(
3242 "class C:\n\
3243 \x20 def __init__(self):\n\
3244 \x20 self.a, x = 1, 2\n",
3245 "foo.py",
3246 |metric| {
3247 assert_eq!(metric.npa.class_na_sum(), 1);
3249 insta::assert_json_snapshot!(metric.npa);
3250 },
3251 );
3252 }
3253
3254 #[test]
3259 fn python_class_level_multi_target_counts_each_name() {
3260 check_metrics::<PythonParser>(
3261 "class C:\n x = 1\n a = b = 3\n p, q = 1, 2\n",
3262 "foo.py",
3263 |metric| {
3264 assert_eq!(metric.npa.class_na_sum(), 5);
3266 assert_eq!(metric.npa.class_npa_sum(), 5);
3267 insta::assert_json_snapshot!(metric.npa);
3268 },
3269 );
3270 }
3271
3272 #[test]
3276 fn python_chained_self_assignment_counts_each() {
3277 check_metrics::<PythonParser>(
3278 "class C:\n def __init__(self):\n self.a = self.b = 1\n",
3279 "foo.py",
3280 |metric| {
3281 assert_eq!(metric.npa.class_na_sum(), 2);
3282 assert_eq!(metric.npa.class_npa_sum(), 2);
3283 insta::assert_json_snapshot!(metric.npa);
3284 },
3285 );
3286 }
3287
3288 #[test]
3292 fn python_classmethod_cls_attribute_counts() {
3293 check_metrics::<PythonParser>(
3294 "class C:\n\
3295 \x20 @classmethod\n\
3296 \x20 def make(cls, other):\n\
3297 \x20 cls.registry = {}\n\
3298 \x20 other.thing = 1\n",
3299 "foo.py",
3300 |metric| {
3301 assert_eq!(metric.npa.class_na_sum(), 1);
3303 assert_eq!(metric.npa.class_npa_sum(), 1);
3304 insta::assert_json_snapshot!(metric.npa);
3305 },
3306 );
3307 }
3308
3309 #[test]
3314 fn python_nested_self_attribute_not_counted() {
3315 check_metrics::<PythonParser>(
3316 "class C:\n def __init__(self):\n self.f.g = 1\n",
3317 "foo.py",
3318 |metric| {
3319 assert_eq!(metric.npa.class_na_sum(), 0);
3320 insta::assert_json_snapshot!(metric.npa);
3321 },
3322 );
3323 }
3324
3325 #[test]
3330 fn python_class_default_and_self_attr_dedupe() {
3331 check_metrics::<PythonParser>(
3332 "class C:\n x = 1\n def __init__(self):\n self.x = 2\n",
3333 "foo.py",
3334 |metric| {
3335 assert_eq!(metric.npa.class_na_sum(), 1);
3336 assert_eq!(metric.npa.class_npa_sum(), 1);
3337 insta::assert_json_snapshot!(metric.npa);
3338 },
3339 );
3340 }
3341
3342 #[test]
3343 fn rust_empty_unit_no_attributes() {
3344 check_metrics::<RustParser>("", "empty.rs", |metric| {
3345 assert_eq!(metric.npa.class_na_sum(), 0);
3346 assert_eq!(metric.npa.class_npa_sum(), 0);
3347 assert_eq!(metric.npa.interface_na_sum(), 0);
3348 assert_eq!(metric.npa.interface_npa_sum(), 0);
3349 insta::assert_json_snapshot!(metric.npa);
3350 });
3351 }
3352
3353 #[test]
3354 fn rust_struct_fields_are_attributes() {
3355 check_metrics::<RustParser>(
3358 "struct Foo { pub a: i32, b: String, pub c: bool }",
3359 "foo.rs",
3360 |metric| {
3361 assert_eq!(metric.npa.class_na_sum(), 3);
3362 assert_eq!(metric.npa.class_npa_sum(), 2);
3363 insta::assert_json_snapshot!(metric.npa);
3364 },
3365 );
3366 }
3367
3368 #[test]
3369 fn rust_pub_self_field_is_private() {
3370 check_metrics::<RustParser>(
3378 "struct S {\n\
3379 \x20 pub(self) a: i32,\n\
3380 \x20 pub(in self) a2: i32,\n\
3381 \x20 pub(crate) b: i32,\n\
3382 \x20 pub(super) d: i32,\n\
3383 \x20 pub(in crate::x) e: i32,\n\
3384 \x20 pub f: i32,\n\
3385 \x20 c: i32,\n\
3386 }",
3387 "foo.rs",
3388 |metric| {
3389 assert_eq!(metric.npa.class_na_sum(), 7);
3390 assert_eq!(metric.npa.class_npa_sum(), 4);
3391 },
3392 );
3393 }
3394
3395 #[test]
3396 fn rust_pub_self_assoc_const_is_private() {
3397 check_metrics::<RustParser>(
3402 "struct Foo;\n\
3403 impl Foo {\n\
3404 \x20 pub(self) const A: i32 = 1;\n\
3405 \x20 pub(in self) const A2: i32 = 2;\n\
3406 \x20 pub(crate) const B: i32 = 3;\n\
3407 \x20 pub const D: i32 = 4;\n\
3408 }\n",
3409 "foo.rs",
3410 |metric| {
3411 assert_eq!(metric.npa.class_na_sum(), 4);
3412 assert_eq!(metric.npa.class_npa_sum(), 2);
3413 },
3414 );
3415 }
3416
3417 #[test]
3418 fn rust_pub_self_tuple_field_is_private() {
3419 check_metrics::<RustParser>(
3427 "struct Bar(pub(self) i32, pub(in self) i32, pub(crate) i32, pub u8, String);",
3428 "foo.rs",
3429 |metric| {
3430 assert_eq!(metric.npa.class_na_sum(), 5);
3431 assert_eq!(metric.npa.class_npa_sum(), 2);
3432 },
3433 );
3434 }
3435
3436 #[test]
3437 fn rust_tuple_struct_fields_are_attributes() {
3438 check_metrics::<RustParser>("struct Bar(pub i32, String);", "foo.rs", |metric| {
3441 assert_eq!(metric.npa.class_na_sum(), 2);
3442 assert_eq!(metric.npa.class_npa_sum(), 1);
3443 insta::assert_json_snapshot!(metric.npa);
3444 });
3445 }
3446
3447 #[test]
3448 fn rust_unit_struct_has_no_attributes() {
3449 check_metrics::<RustParser>("struct Empty;", "foo.rs", |metric| {
3451 assert_eq!(metric.npa.class_na_sum(), 0);
3452 insta::assert_json_snapshot!(metric.npa);
3453 });
3454 }
3455
3456 #[test]
3457 fn rust_empty_struct_body_has_no_attributes() {
3458 check_metrics::<RustParser>("struct Empty { }", "foo.rs", |metric| {
3460 assert_eq!(metric.npa.class_na_sum(), 0);
3461 insta::assert_json_snapshot!(metric.npa);
3462 });
3463 }
3464
3465 #[test]
3466 fn rust_impl_associated_consts_are_attributes() {
3467 check_metrics::<RustParser>(
3470 "struct Foo;\n\
3471 impl Foo {\n\
3472 \x20 const X: i32 = 1;\n\
3473 \x20 pub const Y: i32 = 2;\n\
3474 \x20 static Z: i32 = 3;\n\
3475 \x20 pub static W: i32 = 4;\n\
3476 }\n",
3477 "foo.rs",
3478 |metric| {
3479 assert_eq!(metric.npa.class_na_sum(), 4);
3482 assert_eq!(metric.npa.class_npa_sum(), 2);
3483 insta::assert_json_snapshot!(metric.npa);
3484 },
3485 );
3486 }
3487
3488 #[test]
3489 fn rust_trait_consts_and_associated_types_are_attributes() {
3490 check_func_space::<RustParser, _>(
3495 "trait Drawable { const DEFAULT_COLOR: u32; type Item; }",
3496 "foo.rs",
3497 |func_space| {
3498 let metric = &func_space.metrics;
3499 assert_eq!(metric.npa.interface_na_sum(), 2);
3500 assert_eq!(metric.npa.interface_npa_sum(), 2);
3501 assert_eq!(metric.npa.class_na_sum(), 0);
3502 insta::assert_json_snapshot!(metric.npa);
3503 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
3504 },
3505 );
3506 }
3507
3508 #[test]
3509 fn rust_multiple_impls_aggregate() {
3510 check_metrics::<RustParser>(
3513 "struct Foo;\n\
3514 impl Foo { const X: i32 = 1; }\n\
3515 impl Foo { pub const Y: i32 = 2; }\n",
3516 "foo.rs",
3517 |metric| {
3518 assert_eq!(metric.npa.class_na_sum(), 2);
3519 assert_eq!(metric.npa.class_npa_sum(), 1);
3520 insta::assert_json_snapshot!(metric.npa);
3521 },
3522 );
3523 }
3524
3525 #[test]
3526 fn rust_module_level_consts_not_attributes() {
3527 check_metrics::<RustParser>(
3531 "const PI: f64 = 3.14;\nstatic Q: i32 = 0;\n",
3532 "foo.rs",
3533 |metric| {
3534 assert_eq!(metric.npa.class_na_sum(), 0);
3535 assert_eq!(metric.npa.interface_na_sum(), 0);
3536 insta::assert_json_snapshot!(metric.npa);
3537 },
3538 );
3539 }
3540
3541 #[test]
3544 fn go_empty_unit_no_attributes() {
3545 check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
3548 assert_eq!(metric.npa.class_na_sum(), 0);
3549 insta::assert_json_snapshot!(metric.npa);
3550 });
3551 }
3552
3553 #[test]
3554 fn go_empty_struct_has_no_attributes() {
3555 check_metrics::<GoParser>("package main\ntype Empty struct{}\n", "foo.go", |metric| {
3558 assert_eq!(metric.npa.class_na_sum(), 0);
3559 insta::assert_json_snapshot!(metric.npa);
3560 });
3561 }
3562
3563 #[test]
3564 fn go_struct_fields_are_attributes() {
3565 check_metrics::<GoParser>(
3569 "package main\ntype Foo struct { X int; y string; Z float64 }\n",
3570 "foo.go",
3571 |metric| {
3572 assert_eq!(metric.npa.class_na_sum(), 3);
3573 assert_eq!(metric.npa.class_npa_sum(), 2);
3574 insta::assert_json_snapshot!(metric.npa);
3575 },
3576 );
3577 }
3578
3579 #[test]
3580 fn go_grouped_struct_fields_each_count() {
3581 check_metrics::<GoParser>(
3586 "package main\ntype Point struct { X, Y int; Z float64 }\n",
3587 "foo.go",
3588 |metric| {
3589 assert_eq!(metric.npa.class_na_sum(), 3);
3590 assert_eq!(metric.npa.class_npa_sum(), 3);
3591 insta::assert_json_snapshot!(metric.npa);
3592 },
3593 );
3594 }
3595
3596 #[test]
3597 fn go_embedded_type_counts_as_attribute() {
3598 check_metrics::<GoParser>(
3605 "package main\nimport \"io\"\ntype Bar struct { io.Reader; *Foo; n int }\ntype Foo struct {}\n",
3606 "foo.go",
3607 |metric| {
3608 assert_eq!(metric.npa.class_na_sum(), 3);
3609 assert_eq!(metric.npa.class_npa_sum(), 2);
3610 insta::assert_json_snapshot!(metric.npa);
3611 },
3612 );
3613 }
3614
3615 #[test]
3616 fn go_multiple_structs_aggregate_at_unit() {
3617 check_metrics::<GoParser>(
3623 "package main\ntype Foo struct { x int }\ntype Bar struct { a int; b string }\n",
3624 "foo.go",
3625 |metric| {
3626 assert_eq!(metric.npa.class_na_sum(), 3);
3627 assert_eq!(metric.npa.class_npa_sum(), 0);
3628 insta::assert_json_snapshot!(metric.npa);
3629 },
3630 );
3631 }
3632
3633 #[test]
3634 fn go_top_level_var_const_not_attributes() {
3635 check_metrics::<GoParser>(
3639 "package main\nvar Counter int\nconst Pi = 3.14\n",
3640 "foo.go",
3641 |metric| {
3642 assert_eq!(metric.npa.class_na_sum(), 0);
3643 insta::assert_json_snapshot!(metric.npa);
3644 },
3645 );
3646 }
3647
3648 #[test]
3649 fn go_npa_excludes_unexported() {
3650 check_metrics::<GoParser>(
3661 "package main\nimport \"io\"\n\
3662 type T struct { Name string; secret int; A, b int; io.Reader; _ int; Ärger bool }\n",
3663 "foo.go",
3664 |metric| {
3665 assert_eq!(metric.npa.class_na_sum(), 7);
3666 assert_eq!(metric.npa.class_npa_sum(), 4);
3667 insta::assert_json_snapshot!(metric.npa);
3668 },
3669 );
3670 }
3671
3672 #[test]
3678 fn elixir_npa_defstruct_keyword_list() {
3679 check_metrics::<ElixirParser>(
3680 "defmodule User do\n defstruct name: nil, age: 0, email: nil\nend\n",
3681 "foo.ex",
3682 |metric| {
3683 assert_eq!(metric.npa.class_na_sum(), 3);
3685 assert_eq!(metric.npa.class_npa_sum(), 3);
3686 },
3687 );
3688 }
3689
3690 #[test]
3691 fn elixir_npa_defstruct_atom_list() {
3692 check_metrics::<ElixirParser>(
3693 "defmodule User do\n defstruct [:name, :age, :email]\nend\n",
3694 "foo.ex",
3695 |metric| {
3696 assert_eq!(metric.npa.class_na_sum(), 3);
3697 assert_eq!(metric.npa.class_npa_sum(), 3);
3698 },
3699 );
3700 }
3701
3702 #[test]
3703 fn elixir_npa_defstruct_bracketed_keyword_list() {
3704 check_metrics::<ElixirParser>(
3705 "defmodule User do\n defstruct [name: nil, age: 0]\nend\n",
3706 "foo.ex",
3707 |metric| {
3708 assert_eq!(metric.npa.class_na_sum(), 2);
3709 assert_eq!(metric.npa.class_npa_sum(), 2);
3710 },
3711 );
3712 }
3713
3714 #[test]
3715 fn elixir_npa_defstruct_single_field() {
3716 check_metrics::<ElixirParser>(
3717 "defmodule Box do\n defstruct value: nil\nend\n",
3718 "foo.ex",
3719 |metric| {
3720 assert_eq!(metric.npa.class_na_sum(), 1);
3721 assert_eq!(metric.npa.class_npa_sum(), 1);
3722 },
3723 );
3724 }
3725
3726 #[test]
3727 fn elixir_npa_no_defstruct_is_zero() {
3728 check_metrics::<ElixirParser>(
3729 "defmodule Foo do\n def m, do: :ok\nend\n",
3730 "foo.ex",
3731 |metric| {
3732 assert_eq!(metric.npa.class_na_sum(), 0);
3733 assert_eq!(metric.npa.class_npa_sum(), 0);
3734 },
3735 );
3736 }
3737
3738 #[test]
3748 fn elixir_npa_counts_a_quoted_defmodule_as_a_class() {
3749 check_metrics::<ElixirParser>(
3750 "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",
3751 "outer.ex",
3752 |metric| {
3753 assert_eq!(metric.npa.class_na_sum(), 3);
3756 assert_eq!(metric.npa.class_npa_sum(), 3);
3757 },
3758 );
3759 }
3760
3761 #[test]
3764 fn objc_npa() {
3765 check_metrics::<ObjcParser>(
3778 "@interface Foo : NSObject {\n\
3779 int _prot;\n\
3780 @private\n\
3781 int _priv;\n\
3782 @public\n\
3783 int _pub1, _pub2;\n\
3784 @protected\n\
3785 int _prot2;\n\
3786 }\n\
3787 @property (nonatomic) int count;\n\
3788 @property (copy) NSString *name;\n\
3789 @end\n",
3790 "foo.m",
3791 |metric| {
3792 assert_eq!(metric.npa.interface_na_sum(), 7);
3793 assert_eq!(metric.npa.interface_npa_sum(), 4);
3794 },
3795 );
3796 }
3797
3798 #[test]
3799 fn objc_npa_protocol() {
3800 check_metrics::<ObjcParser>(
3805 "@protocol Drawable <NSObject>\n\
3806 @optional\n\
3807 @property (readonly) int z;\n\
3808 @end\n",
3809 "foo.m",
3810 |metric| {
3811 assert_eq!(metric.npa.interface_na_sum(), 1);
3812 assert_eq!(metric.npa.interface_npa_sum(), 1);
3813 },
3814 );
3815 }
3816
3817 #[test]
3820 fn cpp_empty_unit_no_attributes() {
3821 check_metrics::<CppParser>("", "empty.cpp", |metric| {
3824 assert_eq!(metric.npa.class_na_sum(), 0);
3825 assert_eq!(metric.npa.class_npa_sum(), 0);
3826 insta::assert_json_snapshot!(metric.npa);
3827 });
3828 }
3829
3830 #[test]
3831 fn cpp_empty_class_no_attributes() {
3832 check_metrics::<CppParser>("class Foo {};", "foo.cpp", |metric| {
3835 assert_eq!(metric.npa.class_na_sum(), 0);
3836 assert_eq!(metric.npa.class_npa_sum(), 0);
3837 insta::assert_json_snapshot!(metric.npa);
3838 });
3839 }
3840
3841 #[test]
3842 fn cpp_class_public_attributes() {
3843 check_metrics::<CppParser>(
3848 "class Foo { public: int a; int b, c; };",
3849 "foo.cpp",
3850 |metric| {
3851 assert_eq!(metric.npa.class_na_sum(), 3);
3852 assert_eq!(metric.npa.class_npa_sum(), 3);
3853 insta::assert_json_snapshot!(metric.npa);
3854 },
3855 );
3856 }
3857
3858 #[test]
3859 fn cpp_class_private_default_visibility() {
3860 check_metrics::<CppParser>("class Foo { int value_; };", "foo.cpp", |metric| {
3864 assert_eq!(metric.npa.class_na_sum(), 1);
3865 assert_eq!(metric.npa.class_npa_sum(), 0);
3866 insta::assert_json_snapshot!(metric.npa);
3867 });
3868 }
3869
3870 #[test]
3871 fn cpp_struct_default_public_visibility() {
3872 check_metrics::<CppParser>("struct Bar { int value_; };", "foo.cpp", |metric| {
3875 assert_eq!(metric.npa.class_na_sum(), 1);
3876 assert_eq!(metric.npa.class_npa_sum(), 1);
3877 insta::assert_json_snapshot!(metric.npa);
3878 });
3879 }
3880
3881 #[test]
3882 fn cpp_mixed_visibility_sections() {
3883 check_metrics::<CppParser>(
3887 "class Foo {\n\
3888 public: int a;\n\
3889 protected: int b;\n\
3890 private: int c;\n\
3891 };",
3892 "foo.cpp",
3893 |metric| {
3894 assert_eq!(metric.npa.class_na_sum(), 3);
3895 assert_eq!(metric.npa.class_npa_sum(), 1);
3896 insta::assert_json_snapshot!(metric.npa);
3897 },
3898 );
3899 }
3900
3901 #[test]
3902 fn cpp_methods_not_counted_as_attributes() {
3903 check_metrics::<CppParser>(
3908 "class Foo {\n\
3909 public:\n\
3910 void method1() {}\n\
3911 void method2();\n\
3912 private:\n\
3913 int value_;\n\
3914 };",
3915 "foo.cpp",
3916 |metric| {
3917 assert_eq!(metric.npa.class_na_sum(), 1);
3918 assert_eq!(metric.npa.class_npa_sum(), 0);
3919 insta::assert_json_snapshot!(metric.npa);
3920 },
3921 );
3922 }
3923
3924 #[test]
3925 fn cpp_pointer_array_fields_count() {
3926 check_metrics::<CppParser>(
3931 "struct S {\n\
3932 int* p;\n\
3933 int a[10];\n\
3934 int x;\n\
3935 };",
3936 "foo.cpp",
3937 |metric| {
3938 assert_eq!(metric.npa.class_na_sum(), 3);
3939 assert_eq!(metric.npa.class_npa_sum(), 3);
3941 insta::assert_json_snapshot!(metric.npa);
3942 },
3943 );
3944 }
3945
3946 #[test]
3947 fn cpp_multiple_classes_aggregate_at_unit() {
3948 check_metrics::<CppParser>(
3953 "class Foo { public: int a; private: int b; };\nstruct Bar { int c; };",
3954 "foo.cpp",
3955 |metric| {
3956 assert_eq!(metric.npa.class_na_sum(), 3);
3957 assert_eq!(metric.npa.class_npa_sum(), 2);
3959 insta::assert_json_snapshot!(metric.npa);
3960 },
3961 );
3962 }
3963
3964 #[test]
3965 fn javascript_empty_unit_no_attributes() {
3966 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
3969 assert_eq!(metric.npa.class_na_sum(), 0);
3970 assert_eq!(metric.npa.class_npa_sum(), 0);
3971 insta::assert_json_snapshot!(metric.npa);
3972 });
3973 }
3974
3975 #[test]
3976 fn javascript_empty_class_no_attributes() {
3977 check_metrics::<JavascriptParser>("class Foo {}", "foo.js", |metric| {
3979 assert_eq!(metric.npa.class_na_sum(), 0);
3980 assert_eq!(metric.npa.class_npa_sum(), 0);
3981 insta::assert_json_snapshot!(metric.npa);
3982 });
3983 }
3984
3985 #[test]
3986 fn javascript_class_fields_count() {
3987 check_metrics::<JavascriptParser>(
3992 "class Foo { x = 1; y; static z = 2; }",
3993 "foo.js",
3994 |metric| {
3995 assert_eq!(metric.npa.class_na_sum(), 3);
3996 assert_eq!(metric.npa.class_npa_sum(), 3);
3997 insta::assert_json_snapshot!(metric.npa);
3998 },
3999 );
4000 }
4001
4002 #[test]
4003 fn javascript_arrow_field_is_method_not_attribute() {
4004 check_metrics::<JavascriptParser>(
4008 "class Foo { x = () => {}; y = function() {}; z = 1; }",
4009 "foo.js",
4010 |metric| {
4011 assert_eq!(metric.npa.class_na_sum(), 1);
4013 assert_eq!(metric.npa.class_npa_sum(), 1);
4014 insta::assert_json_snapshot!(metric.npa);
4015 },
4016 );
4017 }
4018
4019 #[test]
4020 fn javascript_methods_not_counted_as_attributes() {
4021 check_metrics::<JavascriptParser>(
4024 "class Foo { constructor() {} bar() {} get baz() { return 1; } x = 1; }",
4025 "foo.js",
4026 |metric| {
4027 assert_eq!(metric.npa.class_na_sum(), 1);
4029 assert_eq!(metric.npa.class_npa_sum(), 1);
4030 insta::assert_json_snapshot!(metric.npa);
4031 },
4032 );
4033 }
4034
4035 #[test]
4036 fn javascript_multiple_classes_aggregate_at_unit() {
4037 check_metrics::<JavascriptParser>(
4041 "class Foo { a = 1; b = 2; }\nclass Bar { c = 3; }",
4042 "foo.js",
4043 |metric| {
4044 assert_eq!(metric.npa.class_na_sum(), 3);
4045 assert_eq!(metric.npa.class_npa_sum(), 3);
4046 insta::assert_json_snapshot!(metric.npa);
4047 },
4048 );
4049 }
4050
4051 #[test]
4052 fn mozjs_class_fields_count() {
4053 check_metrics::<MozjsParser>(
4056 "class Foo { x = 1; y; static z = 2; }",
4057 "foo.js",
4058 |metric| {
4059 assert_eq!(metric.npa.class_na_sum(), 3);
4060 assert_eq!(metric.npa.class_npa_sum(), 3);
4061 insta::assert_json_snapshot!(metric.npa);
4062 },
4063 );
4064 }
4065
4066 #[test]
4074 fn empty_class_cda_is_zero_not_nan() {
4075 let assert_zero = |metric: crate::CodeMetrics| {
4076 assert_eq!(metric.npa.class_na_sum(), 0);
4077 assert!(!metric.npa.class_cda().is_nan());
4078 assert!(!metric.npa.total_cda().is_nan());
4079 assert_eq!(metric.npa.class_cda(), 0.0);
4080 assert_eq!(metric.npa.total_cda(), 0.0);
4081 };
4082 check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
4083 check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
4084 check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
4085 check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
4086 }
4087
4088 #[test]
4093 fn empty_interface_cda_is_zero_not_nan() {
4094 let assert_zero = |metric: crate::CodeMetrics| {
4095 assert_eq!(metric.npa.interface_na_sum(), 0);
4096 assert!(!metric.npa.interface_cda().is_nan());
4097 assert_eq!(metric.npa.interface_cda(), 0.0);
4098 };
4099 check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
4100 check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
4101 }
4102
4103 #[test]
4107 fn stats_display_and_per_space_accessors() {
4108 check_func_space::<JavaParser, _>(
4109 "public interface I {\n int K = 1;\n}\n\
4110 public class C {\n public int a;\n private int b;\n}\n",
4111 "X.java",
4112 |unit| {
4113 assert_eq!(unit.metrics.npa.class_npa_sum(), 1);
4116 assert_eq!(unit.metrics.npa.class_na_sum(), 2);
4117 let rendered = unit.metrics.npa.to_string();
4118 for fragment in [
4119 "classes: 1, interfaces: 1",
4120 "class_attributes: 2",
4121 "interface_attributes: 1",
4122 "total: 2, total_attributes: 3",
4123 ] {
4124 assert!(
4125 rendered.contains(fragment),
4126 "missing {fragment:?} in {rendered}"
4127 );
4128 }
4129 let class = child_space(&unit, "C");
4134 assert_eq!(class.kind, SpaceKind::Class);
4135 assert_eq!(class.metrics.npa.class_npa(), 1);
4136 assert_eq!(class.metrics.npa.class_na(), 2);
4137 let iface = child_space(&unit, "I");
4138 assert_eq!(iface.kind, SpaceKind::Interface);
4139 assert_eq!(iface.metrics.npa.interface_npa(), 1);
4140 assert_eq!(iface.metrics.npa.interface_na(), 1);
4141 },
4142 );
4143 }
4144}