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)]
33#[non_exhaustive]
34pub struct Stats {
35 class_npa: usize,
36 interface_npa: usize,
37 class_na: usize,
38 interface_na: usize,
39 class_npa_sum: usize,
40 interface_npa_sum: usize,
41 class_na_sum: usize,
42 interface_na_sum: usize,
43 is_class_space: bool,
44}
45
46impl fmt::Display for Stats {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(
49 f,
50 "classes: {}, interfaces: {}, class_attributes: {}, interface_attributes: {}, class_cda: {}, interface_cda: {}, total: {}, total_attributes: {}, cda: {}",
51 self.class_npa_sum(),
52 self.interface_npa_sum(),
53 self.class_na_sum(),
54 self.interface_na_sum(),
55 self.class_cda(),
56 self.interface_cda(),
57 self.total_npa(),
58 self.total_na(),
59 self.total_cda()
60 )
61 }
62}
63
64impl Stats {
65 pub fn merge(&mut self, other: &Stats) {
67 self.class_npa_sum += other.class_npa_sum;
68 self.interface_npa_sum += other.interface_npa_sum;
69 self.class_na_sum += other.class_na_sum;
70 self.interface_na_sum += other.interface_na_sum;
71 }
72
73 #[inline]
75 #[must_use]
76 pub fn class_npa(&self) -> u64 {
77 self.class_npa as u64
78 }
79
80 #[inline]
82 #[must_use]
83 pub fn interface_npa(&self) -> u64 {
84 self.interface_npa as u64
85 }
86
87 #[inline]
89 #[must_use]
90 pub fn class_na(&self) -> u64 {
91 self.class_na as u64
92 }
93
94 #[inline]
96 #[must_use]
97 pub fn interface_na(&self) -> u64 {
98 self.interface_na as u64
99 }
100
101 #[inline]
103 #[must_use]
104 pub fn class_npa_sum(&self) -> u64 {
105 self.class_npa_sum as u64
106 }
107
108 #[inline]
110 #[must_use]
111 pub fn interface_npa_sum(&self) -> u64 {
112 self.interface_npa_sum as u64
113 }
114
115 #[inline]
117 #[must_use]
118 pub fn class_na_sum(&self) -> u64 {
119 self.class_na_sum as u64
120 }
121
122 #[inline]
124 #[must_use]
125 pub fn interface_na_sum(&self) -> u64 {
126 self.interface_na_sum as u64
127 }
128
129 #[inline]
139 #[must_use]
140 pub fn class_cda(&self) -> f64 {
141 accessibility_ratio(self.class_npa_sum() as f64, self.class_na_sum() as f64)
142 }
143
144 #[inline]
154 #[must_use]
155 pub fn interface_cda(&self) -> f64 {
156 if self.interface_npa_sum == self.interface_na_sum && self.interface_npa_sum != 0 {
162 1.0
163 } else {
164 accessibility_ratio(
165 self.interface_npa_sum() as f64,
166 self.interface_na_sum() as f64,
167 )
168 }
169 }
170
171 #[inline]
181 #[must_use]
182 pub fn total_cda(&self) -> f64 {
183 accessibility_ratio(self.total_npa() as f64, self.total_na() as f64)
184 }
185
186 #[inline]
188 #[must_use]
189 pub fn total_npa(&self) -> u64 {
190 self.class_npa_sum() + self.interface_npa_sum()
191 }
192
193 #[inline]
195 #[must_use]
196 pub fn total_na(&self) -> u64 {
197 self.class_na_sum() + self.interface_na_sum()
198 }
199
200 #[inline]
203 pub(crate) fn compute_sum(&mut self) {
204 self.class_npa_sum += self.class_npa;
205 self.interface_npa_sum += self.interface_npa;
206 self.class_na_sum += self.class_na;
207 self.interface_na_sum += self.interface_na;
208 }
209
210 #[inline]
212 pub(crate) fn is_disabled(&self) -> bool {
213 !self.is_class_space
214 }
215}
216
217#[inline]
223pub(crate) fn accessibility_ratio(public: f64, total: f64) -> f64 {
224 if total == 0.0 { 0.0 } else { public / total }
225}
226
227#[doc(hidden)]
228pub(crate) trait Npa
230where
231 Self: Checker,
232{
233 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
243}
244
245macro_rules! impl_npa_java_like {
267 ($code:ty, $lang:ident) => {
268 impl Npa for $code {
269 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
270 use $lang::*;
271
272 if Self::is_func_space(node) && stats.is_disabled() {
273 stats.is_class_space = true;
274 }
275
276 match node.kind_id().into() {
277 ClassBody | EnumBodyDeclarations => {
278 for declaration in node
279 .children()
280 .filter(|n| matches!(n.kind_id().into(), FieldDeclaration))
281 {
282 let attributes = declaration
283 .children()
284 .filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
285 .count();
286 stats.class_na += attributes;
287 if declaration.child(0).is_some_and(|modifiers| {
291 matches!(modifiers.kind_id().into(), Modifiers)
292 && modifiers.first_child(|id| id == Public).is_some()
293 }) {
294 stats.class_npa += attributes;
295 }
296 }
297 }
298 InterfaceBody | AnnotationTypeBody => {
299 stats.interface_na += node
300 .children()
301 .filter(|n| matches!(n.kind_id().into(), ConstantDeclaration))
302 .flat_map(|n| n.children())
303 .filter(|n| matches!(n.kind_id().into(), VariableDeclarator))
304 .count();
305 stats.interface_npa = stats.interface_na;
306 }
307 _ => {}
308 }
309 }
310 }
311 };
312}
313
314mod shared;
315pub(crate) use shared::*;
316
317macro_rules! ts_npa_compute {
352 ($lang:ident) => {
353 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
354 use $lang::*;
355
356 if Self::is_func_space(node) && stats.is_disabled() {
357 stats.is_class_space = true;
358 }
359
360 match node.kind_id().into() {
361 ClassBody => {
362 for member in node.children() {
363 match member.kind_id().into() {
364 PublicFieldDefinition
370 if member
371 .first_child(|id| {
372 id == $lang::ArrowFunction
373 || id == $lang::FunctionExpression
374 })
375 .is_none() =>
376 {
377 stats.class_na += 1;
378 if ts_member_is_public!($lang, member) {
379 stats.class_npa += 1;
380 }
381 }
382 MethodDefinition => {
388 let Some(params) =
389 member.first_child(|id| id == $lang::FormalParameters)
390 else {
391 continue;
392 };
393 for param in params.children().filter(|c| {
394 matches!(
395 c.kind_id().into(),
396 RequiredParameter | RequiredParameter2
397 )
398 }) {
399 if param
400 .first_child(|id| {
401 id == $lang::AccessibilityModifier
402 || id == $lang::Readonly
403 })
404 .is_some()
405 {
406 stats.class_na += 1;
407 if ts_member_is_public!($lang, param) {
408 stats.class_npa += 1;
409 }
410 }
411 }
412 }
413 _ => {}
414 }
415 }
416 }
417 InterfaceBody => {
418 let count = node
419 .children()
420 .filter(|c| matches!(c.kind_id().into(), PropertySignature))
421 .count();
422 stats.interface_na += count;
423 stats.interface_npa = stats.interface_na;
424 }
425 _ => {}
426 }
427 }
428 };
429}
430
431macro_rules! ts_member_is_public {
437 ($lang:ident, $member:expr) => {{
438 match $member.first_child(|id| id == $lang::AccessibilityModifier) {
439 None => true,
440 Some(m) => m
441 .first_child(|id| id == $lang::Private || id == $lang::Protected)
442 .is_none(),
443 }
444 }};
445}
446pub(crate) use ts_member_is_public;
447
448macro_rules! js_npa_compute {
469 ($lang:ident) => {
470 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
471 use $lang::*;
472
473 if Self::is_func_space(node) && stats.is_disabled() {
474 stats.is_class_space = true;
475 }
476
477 if !matches!(node.kind_id().into(), ClassBody) {
478 return;
479 }
480
481 for member in node.children() {
482 if matches!(member.kind_id().into(), FieldDefinition)
483 && member
484 .first_child(|id| {
485 id == $lang::ArrowFunction || id == $lang::FunctionExpression
486 })
487 .is_none()
488 {
489 stats.class_na += 1;
490 stats.class_npa += 1;
491 }
492 }
493 }
494 };
495}
496
497mod cpp;
502mod csharp;
503mod elixir;
504mod go;
505mod groovy;
506mod java;
507mod javascript;
508mod kotlin;
509mod mozcpp;
510mod mozjs;
511mod objc;
512mod php;
513mod python;
514mod ruby;
515mod rust;
516mod tsx;
517mod typescript;
518
519implement_metric_trait!(
530 Npa,
531 CCode,
532 PreprocCode,
533 CcommentCode,
534 PerlCode,
535 BashCode,
536 LuaCode,
537 TclCode,
538 IrulesCode
539);
540
541#[cfg(test)]
542#[allow(
543 clippy::float_cmp,
544 clippy::cast_precision_loss,
545 clippy::cast_possible_truncation,
546 clippy::cast_sign_loss,
547 clippy::similar_names,
548 clippy::doc_markdown,
549 clippy::needless_raw_string_hashes,
550 clippy::too_many_lines
551)]
552mod tests {
553 use crate::tools::{assert_child_space_kind, check_func_space, check_metrics};
554
555 use super::*;
556
557 #[test]
558 fn java_single_attributes() {
559 check_metrics::<JavaParser>(
560 "class X {
561 public byte a; // +1
562 public short b; // +1
563 public int c; // +1
564 public long d; // +1
565 public float e; // +1
566 public double f; // +1
567 public boolean g; // +1
568 public char h; // +1
569 byte i;
570 short j;
571 int k;
572 long l;
573 float m;
574 double n;
575 boolean o;
576 char p;
577 }",
578 "foo.java",
579 |metric| {
580 insta::assert_json_snapshot!(
581 metric.npa,
582 @r#"
583 {
584 "class_npa_sum": 8,
585 "interface_npa_sum": 0,
586 "class_attributes": 16,
587 "interface_attributes": 0,
588 "class_cda": 0.5,
589 "interface_cda": 0.0,
590 "total": 8,
591 "total_attributes": 16,
592 "cda": 0.5
593 }
594 "#
595 );
596 },
597 );
598 }
599
600 #[test]
601 fn java_multiple_attributes() {
602 check_metrics::<JavaParser>(
603 "class X {
604 public byte a1; // +1
605 public short b1, b2; // +2
606 public int c1, c2, c3; // +3
607 public long d1, d2, d3, d4; // +4
608 public float e1, e2, e3, e4; // +4
609 public double f1, f2, f3; // +3
610 public boolean g1, g2; // +2
611 public char h1; // +1
612 byte i1, i2, i3, i4;
613 short j1, j2, j3;
614 int k1, k2;
615 long l1;
616 float m1;
617 double n1, n2;
618 boolean o1, o2, o3;
619 char p1, p2, p3, p4;
620 }",
621 "foo.java",
622 |metric| {
623 insta::assert_json_snapshot!(
624 metric.npa,
625 @r#"
626 {
627 "class_npa_sum": 20,
628 "interface_npa_sum": 0,
629 "class_attributes": 40,
630 "interface_attributes": 0,
631 "class_cda": 0.5,
632 "interface_cda": 0.0,
633 "total": 20,
634 "total_attributes": 40,
635 "cda": 0.5
636 }
637 "#
638 );
639 },
640 );
641 }
642
643 #[test]
644 fn java_initialized_attributes() {
645 check_metrics::<JavaParser>(
646 "class X {
647 public byte a1 = 1; // +1
648 public short b1 = 2, b2; // +2
649 public int c1, c2 = 3, c3; // +3
650 public long d1 = 4, d2, d3, d4 = 5; // +4
651 public float e1, e2 = 6.0f, e3 = 7.0f, e4; // +4
652 public double f1 = 8.0, f2 = 9.0, f3 = 10.0; // +3
653 public boolean g1 = true, g2; // +2
654 public char h1 = 'a'; // +1
655 byte i1 = 1, i2 = 2, i3 = 3, i4 = 4;
656 short j1 = 5, j2, j3 = 6;
657 int k1, k2 = 7;
658 long l1 = 8;
659 float m1 = 9.0f;
660 double n1, n2 = 10.0;
661 boolean o1, o2 = false, o3;
662 char p1 = 'a', p2 = 'b', p3 = 'c', p4 = 'd';
663 }",
664 "foo.java",
665 |metric| {
666 insta::assert_json_snapshot!(
667 metric.npa,
668 @r#"
669 {
670 "class_npa_sum": 20,
671 "interface_npa_sum": 0,
672 "class_attributes": 40,
673 "interface_attributes": 0,
674 "class_cda": 0.5,
675 "interface_cda": 0.0,
676 "total": 20,
677 "total_attributes": 40,
678 "cda": 0.5
679 }
680 "#
681 );
682 },
683 );
684 }
685
686 #[test]
687 fn java_array_attributes() {
688 check_metrics::<JavaParser>(
689 "class X {
690 public byte[] a1, a2, a3, a4; // +4
691 public short b1[], b2[], b3[]; // +3
692 public int[] c1 = { 1 }, c2; // +2
693 public long d1[] = { 1 }; // +1
694 public float[] e1 = { 1.0f, 2.0f, 3.0f }; // +1
695 public double f1[] = { 1.0, 2.0, 3.0 }, f2[]; // +2
696 public boolean[] g1 = new boolean[5], g2, g3; // +3
697 public char[] h1 = new char[5], h2[], h3[], h4[]; // +4
698 byte[] i1;
699 short j1[], j2[];
700 int[] k1, k2, k3 = { 1 };
701 long l1[], l2[] = { 1 }, l3[] = { 2 }, l4[];
702 float[] m1, m2, m3, m4 = { 1.0f, 2.0f, 3.0f };
703 double n1[], n2[] = { 1.0, 2.0, 3.0 }, n3[];
704 boolean[] o1, o2 = new boolean[5];
705 char[] p1 = new char[5];
706 }",
707 "foo.java",
708 |metric| {
709 insta::assert_json_snapshot!(
710 metric.npa,
711 @r#"
712 {
713 "class_npa_sum": 20,
714 "interface_npa_sum": 0,
715 "class_attributes": 40,
716 "interface_attributes": 0,
717 "class_cda": 0.5,
718 "interface_cda": 0.0,
719 "total": 20,
720 "total_attributes": 40,
721 "cda": 0.5
722 }
723 "#
724 );
725 },
726 );
727 }
728
729 #[test]
730 fn java_object_attributes() {
731 check_metrics::<JavaParser>(
732 "class X {
733 public Integer[] a1 = { 1 }; // +1
734 public Integer b1, b2; // +2
735 public String[] c1 = { \"Hello\" }, c2, c3 = { \"World!\" }; // +3
736 public String d1[][] = { { \"Hello\" }, { \"World!\" } }; // +1
737 public Y[] e1, e2[]; // +2
738 public Y f1[], f2[][], f3[][][]; // +3
739 Integer[] g1 = { new Integer(1) };
740 Integer h1 = new Integer(1), h2 = new Integer(2);
741 String[] i1, i2 = { \"Hello World!\" }, i3;
742 String j1 = \"Hello World!\";
743 Y[] k1[], k2;
744 Y l1[][], l2[], l3 = new Y();
745 }",
746 "foo.java",
747 |metric| {
748 insta::assert_json_snapshot!(
749 metric.npa,
750 @r#"
751 {
752 "class_npa_sum": 12,
753 "interface_npa_sum": 0,
754 "class_attributes": 24,
755 "interface_attributes": 0,
756 "class_cda": 0.5,
757 "interface_cda": 0.0,
758 "total": 12,
759 "total_attributes": 24,
760 "cda": 0.5
761 }
762 "#
763 );
764 },
765 );
766 }
767
768 #[test]
769 fn groovy_no_attributes() {
770 check_metrics::<GroovyParser>("class A { void foo() {} }", "foo.groovy", |metric| {
771 assert_eq!(metric.npa.total_na(), 0);
772 assert_eq!(metric.npa.total_npa(), 0);
773 });
774 }
775
776 #[test]
777 fn groovy_public_attributes() {
778 check_metrics::<GroovyParser>(
779 "class A {
780 public int x
781 public String name
782 private int hidden
783 }",
784 "foo.groovy",
785 |metric| {
786 assert_eq!(metric.npa.class_na_sum(), 3);
788 assert_eq!(metric.npa.class_npa_sum(), 2);
789 },
790 );
791 }
792
793 #[test]
794 fn groovy_def_attributes_not_public() {
795 check_metrics::<GroovyParser>(
799 "class A {
800 def field1
801 def field2
802 }",
803 "foo.groovy",
804 |metric| {
805 assert_eq!(metric.npa.class_na_sum(), 2);
807 assert_eq!(metric.npa.class_npa_sum(), 0);
808 },
809 );
810 }
811
812 #[test]
813 fn groovy_interface_attributes() {
814 check_func_space::<GroovyParser, _>(
818 "interface I {
819 public static final int A = 1
820 public static final int B = 2
821 }",
822 "foo.groovy",
823 |func_space| {
824 let metric = &func_space.metrics;
825 assert_eq!(metric.npa.interface_na_sum(), 2);
827 assert_eq!(metric.npa.interface_npa_sum(), 2);
828 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
829 },
830 );
831 }
832
833 #[test]
834 fn groovy_no_attributes_in_unit_scope() {
835 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
836 assert_eq!(metric.npa.total_na(), 0);
837 });
838 }
839
840 #[test]
841 fn groovy_multiple_classes() {
842 check_metrics::<GroovyParser>(
843 "class A { public int a }
844 class B { public int b }",
845 "foo.groovy",
846 |metric| {
847 assert_eq!(metric.npa.class_na_sum(), 2);
848 assert_eq!(metric.npa.class_npa_sum(), 2);
849 },
850 );
851 }
852
853 #[test]
854 fn groovy_initialized_attributes() {
855 check_metrics::<GroovyParser>(
860 "class X {
861 public int a1 = 1, a2
862 public int b1 = 2
863 int c1, c2 = 3
864 }",
865 "foo.groovy",
866 |metric| {
867 assert_eq!(metric.npa.class_na_sum(), 5);
869 assert_eq!(metric.npa.class_npa_sum(), 3);
870 },
871 );
872 }
873
874 #[test]
875 fn groovy_object_attributes() {
876 check_metrics::<GroovyParser>(
879 "class X {
880 public Integer a1
881 public String b1 = 'hello'
882 public Y[] c1
883 }",
884 "foo.groovy",
885 |metric| {
886 assert_eq!(metric.npa.class_na_sum(), 3);
887 assert_eq!(metric.npa.class_npa_sum(), 3);
888 },
889 );
890 }
891
892 #[test]
893 fn groovy_attribute_modifiers() {
894 check_metrics::<GroovyParser>(
898 "class X {
899 public static int a
900 static public int b
901 public final int c = 1
902 final public int d = 2
903 private static int e
904 int f
905 }",
906 "foo.groovy",
907 |metric| {
908 assert_eq!(metric.npa.class_na_sum(), 6);
910 assert_eq!(metric.npa.class_npa_sum(), 4);
911 },
912 );
913 }
914
915 #[test]
916 #[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')"]
917 fn groovy_nested_inner_classes() {
918 check_metrics::<GroovyParser>(
921 "class X {
922 public int a
923 class Y {
924 public boolean b
925 class Z {
926 public char c
927 }
928 }
929 }",
930 "foo.groovy",
931 |metric| {
932 assert_eq!(metric.npa.class_na_sum(), 3);
934 assert_eq!(metric.npa.class_npa_sum(), 3);
935 },
936 );
937 }
938
939 #[test]
940 fn groovy_array_attributes() {
941 check_metrics::<GroovyParser>(
943 "class X {
944 public int[] a
945 public String[] b
946 int[] c
947 }",
948 "foo.groovy",
949 |metric| {
950 assert_eq!(metric.npa.class_na_sum(), 3);
951 assert_eq!(metric.npa.class_npa_sum(), 2);
952 },
953 );
954 }
955
956 #[test]
957 fn groovy_anonymous_inner_class() {
958 check_metrics::<GroovyParser>(
962 "class X {
963 public Runnable r = new Runnable() {
964 public int x
965 void run() {}
966 }
967 }",
968 "foo.groovy",
969 |metric| {
970 assert_eq!(metric.npa.class_na_sum(), 2);
973 assert_eq!(metric.npa.class_npa_sum(), 2);
974 },
975 );
976 }
977
978 #[test]
983 fn groovy_enum_counts_explicit_public_fields() {
984 check_metrics::<GroovyParser>(
985 "enum Status {
986 ACTIVE, INACTIVE;
987 public int code;
988 private int hidden;
989 }",
990 "foo.groovy",
991 |metric| {
992 assert_eq!(metric.npa.class_na_sum(), 2);
993 assert_eq!(metric.npa.class_npa_sum(), 1);
994 },
995 );
996 }
997
998 #[test]
999 fn groovy_annotation_type_counts_constants_as_implicit_public() {
1000 check_func_space::<GroovyParser, _>(
1008 "public @interface Marker {
1009 int VERSION = 1;
1010 String NAME = \"x\";
1011 }",
1012 "foo.groovy",
1013 |func_space| {
1014 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1015 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1016 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1017 },
1018 );
1019 }
1020
1021 #[test]
1022 fn java_generic_attributes() {
1023 check_metrics::<JavaParser>(
1024 "class X<T, S extends T> {
1025 public T a1; // +1
1026 public Entry<T, S> b1, b2[]; // +2
1027 public ArrayList<T> c1, c2, c3; // +3
1028 public HashMap<Long, Double> d1, d2; // +2
1029 public TreeSet<String> e1; // +1
1030 S f1;
1031 Entry<S, T> g1[], g2;
1032 ArrayList<S> h1, h2, h3;
1033 HashMap<Long, Float> i1, i2;
1034 TreeSet<Entry<S, T>> j1;
1035 }",
1036 "foo.java",
1037 |metric| {
1038 insta::assert_json_snapshot!(
1039 metric.npa,
1040 @r#"
1041 {
1042 "class_npa_sum": 9,
1043 "interface_npa_sum": 0,
1044 "class_attributes": 18,
1045 "interface_attributes": 0,
1046 "class_cda": 0.5,
1047 "interface_cda": 0.0,
1048 "total": 9,
1049 "total_attributes": 18,
1050 "cda": 0.5
1051 }
1052 "#
1053 );
1054 },
1055 );
1056 }
1057
1058 #[test]
1059 fn java_attribute_modifiers() {
1060 check_metrics::<JavaParser>(
1061 "class X {
1062 public transient volatile static int a; // +1
1063 transient public volatile static int b; // +1
1064 transient volatile public static int c; // +1
1065 transient volatile static public int d; // +1
1066 public transient static final int e = 1; // +1
1067 transient public static final int f = 2; // +1
1068 transient static public final int g = 3; // +1
1069 transient static final public int h = 4; // +1
1070 protected transient volatile static int i;
1071 transient volatile static protected int j;
1072 private transient volatile static int k;
1073 transient volatile static private int l;
1074 transient volatile static int m;
1075 transient static final int n = 5;
1076 static public final int o = 6; // +1
1077 final public int p = 7; // +1
1078 }",
1079 "foo.java",
1080 |metric| {
1081 insta::assert_json_snapshot!(
1082 metric.npa,
1083 @r#"
1084 {
1085 "class_npa_sum": 10,
1086 "interface_npa_sum": 0,
1087 "class_attributes": 16,
1088 "interface_attributes": 0,
1089 "class_cda": 0.625,
1090 "interface_cda": 0.0,
1091 "total": 10,
1092 "total_attributes": 16,
1093 "cda": 0.625
1094 }
1095 "#
1096 );
1097 },
1098 );
1099 }
1100
1101 #[test]
1102 fn java_classes() {
1103 check_metrics::<JavaParser>(
1104 "class X {
1105 public int a; // +1
1106 public boolean b; // +1
1107 private char c;
1108 }
1109 class Y {
1110 private double d;
1111 private long e;
1112 public float f; // +1
1113 }",
1114 "foo.java",
1115 |metric| {
1116 insta::assert_json_snapshot!(
1117 metric.npa,
1118 @r#"
1119 {
1120 "class_npa_sum": 3,
1121 "interface_npa_sum": 0,
1122 "class_attributes": 6,
1123 "interface_attributes": 0,
1124 "class_cda": 0.5,
1125 "interface_cda": 0.0,
1126 "total": 3,
1127 "total_attributes": 6,
1128 "cda": 0.5
1129 }
1130 "#
1131 );
1132 },
1133 );
1134 }
1135
1136 #[test]
1137 fn java_nested_inner_classes() {
1138 check_metrics::<JavaParser>(
1139 "class X {
1140 public int a; // +1
1141 class Y {
1142 public boolean b; // +1
1143 class Z {
1144 public char c; // +1
1145 }
1146 }
1147 }",
1148 "foo.java",
1149 |metric| {
1150 insta::assert_json_snapshot!(
1151 metric.npa,
1152 @r#"
1153 {
1154 "class_npa_sum": 3,
1155 "interface_npa_sum": 0,
1156 "class_attributes": 3,
1157 "interface_attributes": 0,
1158 "class_cda": 1.0,
1159 "interface_cda": 0.0,
1160 "total": 3,
1161 "total_attributes": 3,
1162 "cda": 1.0
1163 }
1164 "#
1165 );
1166 },
1167 );
1168 }
1169
1170 #[test]
1171 fn java_local_inner_classes() {
1172 check_metrics::<JavaParser>(
1173 "class X {
1174 public int a; // +1
1175 void x() {
1176 class Y {
1177 public boolean b; // +1
1178 void y() {
1179 class Z {
1180 public char c; // +1
1181 void z() {}
1182 }
1183 }
1184 }
1185 }
1186 }",
1187 "foo.java",
1188 |metric| {
1189 insta::assert_json_snapshot!(
1190 metric.npa,
1191 @r#"
1192 {
1193 "class_npa_sum": 3,
1194 "interface_npa_sum": 0,
1195 "class_attributes": 3,
1196 "interface_attributes": 0,
1197 "class_cda": 1.0,
1198 "interface_cda": 0.0,
1199 "total": 3,
1200 "total_attributes": 3,
1201 "cda": 1.0
1202 }
1203 "#
1204 );
1205 },
1206 );
1207 }
1208
1209 #[test]
1210 fn java_anonymous_inner_classes() {
1211 check_metrics::<JavaParser>(
1212 "abstract class X {
1213 public int a; // +1
1214 }
1215 abstract class Y {
1216 boolean b;
1217 }
1218 class Z {
1219 public char c; // +1
1220 public void z(){
1221 X x1 = new X() {
1222 public double d; // +1
1223 };
1224 Y y1 = new Y() {
1225 long e;
1226 };
1227 }
1228 }",
1229 "foo.java",
1230 |metric| {
1231 insta::assert_json_snapshot!(
1232 metric.npa,
1233 @r#"
1234 {
1235 "class_npa_sum": 3,
1236 "interface_npa_sum": 0,
1237 "class_attributes": 5,
1238 "interface_attributes": 0,
1239 "class_cda": 0.6,
1240 "interface_cda": 0.0,
1241 "total": 3,
1242 "total_attributes": 5,
1243 "cda": 0.6
1244 }
1245 "#
1246 );
1247 },
1248 );
1249 }
1250
1251 #[test]
1252 fn java_interface() {
1253 check_metrics::<JavaParser>(
1254 "interface X {
1255 public int a = 0; // +1
1256 static boolean b = false; // +1
1257 final char c = ' '; // +1
1258 }",
1259 "foo.java",
1260 |metric| {
1261 insta::assert_json_snapshot!(
1262 metric.npa,
1263 @r#"
1264 {
1265 "class_npa_sum": 0,
1266 "interface_npa_sum": 3,
1267 "class_attributes": 0,
1268 "interface_attributes": 3,
1269 "class_cda": 0.0,
1270 "interface_cda": 1.0,
1271 "total": 3,
1272 "total_attributes": 3,
1273 "cda": 1.0
1274 }
1275 "#
1276 );
1277 },
1278 );
1279 }
1280
1281 #[test]
1285 fn java_enum_counts_explicit_public_fields() {
1286 check_metrics::<JavaParser>(
1287 "enum Status {
1288 ACTIVE, INACTIVE;
1289 public static final int FLAG = 1; // implicit static final, still public
1290 public int code; // +1 explicit public
1291 private int hidden; // not public
1292 }",
1293 "foo.java",
1294 |metric| {
1295 assert_eq!(metric.npa.class_na_sum(), 3);
1297 assert_eq!(metric.npa.class_npa_sum(), 2);
1298 },
1299 );
1300 }
1301
1302 #[test]
1309 fn java_record_counts_explicit_body_fields() {
1310 check_metrics::<JavaParser>(
1311 "record Point(int x, int y) {
1312 public static int origin = 0; // explicit body, public
1313 private int cached; // explicit body, private
1314 }",
1315 "foo.java",
1316 |metric| {
1317 assert_eq!(metric.npa.class_na_sum(), 2);
1320 assert_eq!(metric.npa.class_npa_sum(), 1);
1321 },
1322 );
1323 }
1324
1325 #[test]
1326 fn java_annotation_type_counts_constants_as_implicit_public() {
1327 check_func_space::<JavaParser, _>(
1336 "@interface Marker {
1337 int VERSION = 1; // implicit public static final
1338 String NAME = \"x\"; // implicit public static final
1339 }",
1340 "foo.java",
1341 |func_space| {
1342 assert_eq!(func_space.metrics.npa.interface_na_sum(), 2);
1343 assert_eq!(func_space.metrics.npa.interface_npa_sum(), 2);
1344 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1345 },
1346 );
1347 }
1348
1349 #[test]
1350 fn php_no_class_attributes() {
1351 check_metrics::<PhpParser>(
1352 "<?php class A { public function f(): void {} }",
1353 "foo.php",
1354 |metric| insta::assert_json_snapshot!(metric.npa),
1355 );
1356 }
1357
1358 #[test]
1359 fn csharp_single_attributes() {
1360 check_metrics::<CsharpParser>(
1361 "class X {
1362 public byte a;
1363 public short b;
1364 public int c;
1365 public long d;
1366 public float e;
1367 public double f;
1368 public bool g;
1369 public char h;
1370 byte i;
1371 short j;
1372 int k;
1373 long l;
1374 float m;
1375 double n;
1376 bool o;
1377 char p;
1378 }",
1379 "foo.cs",
1380 |metric| {
1381 assert_eq!(metric.npa.class_npa_sum(), 8);
1382 assert_eq!(metric.npa.class_na_sum(), 16);
1383 assert_eq!(metric.npa.interface_na_sum(), 0);
1384 insta::assert_json_snapshot!(metric.npa);
1385 },
1386 );
1387 }
1388
1389 #[test]
1390 fn csharp_multiple_attributes() {
1391 check_metrics::<CsharpParser>(
1392 "class X {
1393 public byte a1;
1394 public short b1, b2;
1395 public int c1, c2, c3;
1396 public long d1, d2, d3, d4;
1397 public bool g1, g2;
1398 byte i1, i2, i3, i4;
1399 int k1, k2;
1400 }",
1401 "foo.cs",
1402 |metric| {
1403 assert_eq!(metric.npa.class_npa_sum(), 12);
1404 assert_eq!(metric.npa.class_na_sum(), 18);
1405 assert_eq!(metric.npa.interface_na_sum(), 0);
1406 insta::assert_json_snapshot!(metric.npa);
1407 },
1408 );
1409 }
1410
1411 #[test]
1412 fn csharp_initialized_attributes() {
1413 check_metrics::<CsharpParser>(
1414 "class X {
1415 public int a = 1;
1416 public bool b = true;
1417 public string c = \"hello\";
1418 public double d = 3.14;
1419 int e = 0;
1420 }",
1421 "foo.cs",
1422 |metric| {
1423 assert_eq!(metric.npa.class_npa_sum(), 4);
1424 assert_eq!(metric.npa.class_na_sum(), 5);
1425 assert_eq!(metric.npa.interface_na_sum(), 0);
1426 insta::assert_json_snapshot!(metric.npa);
1427 },
1428 );
1429 }
1430
1431 #[test]
1432 fn csharp_array_attributes() {
1433 check_metrics::<CsharpParser>(
1434 "class X {
1435 public int[] a;
1436 public string[] b = new string[5];
1437 int[] c;
1438 }",
1439 "foo.cs",
1440 |metric| {
1441 assert_eq!(metric.npa.class_npa_sum(), 2);
1442 assert_eq!(metric.npa.class_na_sum(), 3);
1443 assert_eq!(metric.npa.interface_na_sum(), 0);
1444 insta::assert_json_snapshot!(metric.npa);
1445 },
1446 );
1447 }
1448
1449 #[test]
1450 fn csharp_object_attributes() {
1451 check_metrics::<CsharpParser>(
1452 "class Point { public int X, Y; }
1453 class Shape {
1454 public Point origin;
1455 public Point endpoint = new Point();
1456 Point hidden;
1457 }",
1458 "foo.cs",
1459 |metric| {
1460 assert_eq!(metric.npa.class_npa_sum(), 4);
1461 assert_eq!(metric.npa.class_na_sum(), 5);
1462 assert_eq!(metric.npa.interface_na_sum(), 0);
1463 insta::assert_json_snapshot!(metric.npa);
1464 },
1465 );
1466 }
1467
1468 #[test]
1469 fn csharp_generic_attributes() {
1470 check_metrics::<CsharpParser>(
1471 "class X {
1472 public System.Collections.Generic.List<int> a;
1473 public System.Collections.Generic.Dictionary<string, int> b;
1474 System.Collections.Generic.List<string> c;
1475 }",
1476 "foo.cs",
1477 |metric| {
1478 assert_eq!(metric.npa.class_npa_sum(), 2);
1479 assert_eq!(metric.npa.class_na_sum(), 3);
1480 assert_eq!(metric.npa.interface_na_sum(), 0);
1481 insta::assert_json_snapshot!(metric.npa);
1482 },
1483 );
1484 }
1485
1486 #[test]
1487 fn csharp_attribute_modifiers() {
1488 check_metrics::<CsharpParser>(
1489 "class X {
1490 public int a;
1491 private int b;
1492 protected int c;
1493 internal int d;
1494 public static int e;
1495 public readonly int f;
1496 public const int g = 1;
1497 }",
1498 "foo.cs",
1499 |metric| {
1500 assert_eq!(metric.npa.class_npa_sum(), 4);
1503 assert_eq!(metric.npa.class_na_sum(), 7);
1504 assert_eq!(metric.npa.interface_na_sum(), 0);
1505 insta::assert_json_snapshot!(metric.npa);
1506 },
1507 );
1508 }
1509
1510 #[test]
1511 fn csharp_classes() {
1512 check_metrics::<CsharpParser>(
1513 "class A {
1514 public int a;
1515 public int b;
1516 int c;
1517 }
1518 class B {
1519 public string s;
1520 int n;
1521 }",
1522 "foo.cs",
1523 |metric| {
1524 assert_eq!(metric.npa.class_npa_sum(), 3);
1525 assert_eq!(metric.npa.class_na_sum(), 5);
1526 assert_eq!(metric.npa.interface_na_sum(), 0);
1527 insta::assert_json_snapshot!(metric.npa);
1528 },
1529 );
1530 }
1531
1532 #[test]
1533 fn csharp_nested_inner_classes() {
1534 check_metrics::<CsharpParser>(
1535 "class Outer {
1536 public int a;
1537 int b;
1538 public class Inner {
1539 public string s;
1540 int n;
1541 }
1542 }",
1543 "foo.cs",
1544 |metric| {
1545 assert_eq!(metric.npa.class_npa_sum(), 2);
1546 assert_eq!(metric.npa.class_na_sum(), 4);
1547 assert_eq!(metric.npa.interface_na_sum(), 0);
1548 insta::assert_json_snapshot!(metric.npa);
1549 },
1550 );
1551 }
1552
1553 #[test]
1554 fn csharp_struct_attributes() {
1555 check_metrics::<CsharpParser>(
1558 "struct Point {
1559 public int X;
1560 public int Y;
1561 int Hidden;
1562 }",
1563 "foo.cs",
1564 |metric| {
1565 assert_eq!(metric.npa.class_npa_sum(), 2);
1566 assert_eq!(metric.npa.class_na_sum(), 3);
1567 assert_eq!(metric.npa.interface_na_sum(), 0);
1568 insta::assert_json_snapshot!(metric.npa);
1569 },
1570 );
1571 }
1572
1573 #[test]
1574 fn csharp_record_attributes() {
1575 check_metrics::<CsharpParser>(
1578 "record Person {
1579 public string Name;
1580 int Age;
1581 }",
1582 "foo.cs",
1583 |metric| {
1584 assert_eq!(metric.npa.class_npa_sum(), 1);
1585 assert_eq!(metric.npa.class_na_sum(), 2);
1586 assert_eq!(metric.npa.interface_na_sum(), 0);
1587 insta::assert_json_snapshot!(metric.npa);
1588 },
1589 );
1590 }
1591
1592 #[test]
1593 fn csharp_interface() {
1594 check_func_space::<CsharpParser, _>(
1599 "interface I {
1600 static int A = 1;
1601 static string B = \"hello\";
1602 }",
1603 "foo.cs",
1604 |func_space| {
1605 let metric = &func_space.metrics;
1606 assert_eq!(metric.npa.class_na_sum(), 0);
1607 assert_eq!(metric.npa.interface_na_sum(), 2);
1608 assert_eq!(metric.npa.interface_npa_sum(), 2);
1611 insta::assert_json_snapshot!(metric.npa);
1612 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1613 },
1614 );
1615 }
1616
1617 #[test]
1618 fn csharp_interface_explicit_modifiers() {
1619 check_metrics::<CsharpParser>(
1624 "interface I {
1625 private static int Hidden = 1;
1626 protected static int AlsoHidden = 2;
1627 public static int Shown = 3;
1628 static int Implicit = 4;
1629 }",
1630 "foo.cs",
1631 |metric| {
1632 assert_eq!(metric.npa.interface_na_sum(), 4);
1633 assert_eq!(metric.npa.interface_npa_sum(), 2);
1636 assert_eq!(metric.npa.class_na_sum(), 0);
1637 insta::assert_json_snapshot!(metric.npa);
1638 },
1639 );
1640 }
1641
1642 #[test]
1643 fn csharp_interface_multi_declarator_modifier() {
1644 check_metrics::<CsharpParser>(
1649 "interface I {
1650 private static int a = 1, b = 2;
1651 static int c = 3, d = 4;
1652 }",
1653 "foo.cs",
1654 |metric| {
1655 assert_eq!(metric.npa.interface_na_sum(), 4);
1656 assert_eq!(metric.npa.interface_npa_sum(), 2);
1657 assert_eq!(metric.npa.class_na_sum(), 0);
1658 insta::assert_json_snapshot!(metric.npa);
1659 },
1660 );
1661 }
1662
1663 #[test]
1664 fn php_one_public_attribute() {
1665 check_metrics::<PhpParser>(
1666 "<?php class A { public int $x = 0; }",
1667 "foo.php",
1668 |metric| insta::assert_json_snapshot!(metric.npa),
1669 );
1670 }
1671
1672 #[test]
1673 fn php_one_private_attribute() {
1674 check_metrics::<PhpParser>(
1675 "<?php class A { private int $x = 0; }",
1676 "foo.php",
1677 |metric| insta::assert_json_snapshot!(metric.npa),
1678 );
1679 }
1680
1681 #[test]
1682 fn php_one_protected_attribute() {
1683 check_metrics::<PhpParser>(
1684 "<?php class A { protected int $x = 0; }",
1685 "foo.php",
1686 |metric| insta::assert_json_snapshot!(metric.npa),
1687 );
1688 }
1689
1690 #[test]
1691 fn php_mixed_visibility_attributes() {
1692 check_metrics::<PhpParser>(
1693 "<?php
1694 class A {
1695 public int $a = 0;
1696 public int $b = 0;
1697 private int $c = 0;
1698 protected int $d = 0;
1699 }",
1700 "foo.php",
1701 |metric| insta::assert_json_snapshot!(metric.npa),
1702 );
1703 }
1704
1705 #[test]
1706 fn php_static_public_attribute() {
1707 check_metrics::<PhpParser>(
1708 "<?php class A { public static int $x = 0; }",
1709 "foo.php",
1710 |metric| insta::assert_json_snapshot!(metric.npa),
1711 );
1712 }
1713
1714 #[test]
1715 fn php_readonly_public_attribute() {
1716 check_metrics::<PhpParser>(
1717 "<?php class A { public readonly int $x; }",
1718 "foo.php",
1719 |metric| insta::assert_json_snapshot!(metric.npa),
1720 );
1721 }
1722
1723 #[test]
1724 fn php_multiple_attributes_per_declaration() {
1725 check_metrics::<PhpParser>(
1728 "<?php class A { public int $a = 0, $b = 0, $c = 0; }",
1729 "foo.php",
1730 |metric| insta::assert_json_snapshot!(metric.npa),
1731 );
1732 }
1733
1734 #[test]
1735 fn php_interface_constants() {
1736 check_metrics::<PhpParser>(
1738 "<?php
1739 interface I {
1740 const A = 1;
1741 const B = 2;
1742 }",
1743 "foo.php",
1744 |metric| insta::assert_json_snapshot!(metric.npa),
1745 );
1746 }
1747
1748 #[test]
1749 fn php_enum_cases_not_counted() {
1750 check_metrics::<PhpParser>(
1755 "<?php
1756 enum Color {
1757 case Red;
1758 case Green;
1759 case Blue;
1760 }",
1761 "foo.php",
1762 |metric| {
1763 assert_eq!(metric.npa.class_na_sum(), 0);
1764 assert_eq!(metric.npa.class_npa_sum(), 0);
1765 assert_eq!(metric.npa.interface_na_sum(), 0);
1766 insta::assert_json_snapshot!(metric.npa);
1767 },
1768 );
1769 }
1770
1771 #[test]
1772 fn php_enum_const_not_counted() {
1773 check_metrics::<PhpParser>(
1778 "<?php
1779 enum Suit: string {
1780 case Hearts = 'H';
1781 case Diamonds = 'D';
1782 const Wild = 'W';
1783 public function label(): string { return $this->name; }
1784 }",
1785 "foo.php",
1786 |metric| {
1787 assert_eq!(metric.npa.class_na_sum(), 0);
1788 assert_eq!(metric.npa.class_npa_sum(), 0);
1789 },
1790 );
1791 }
1792
1793 #[test]
1794 fn php_enum_npa_matches_java_enum_npa() {
1795 check_metrics::<PhpParser>(
1801 "<?php
1802 enum Color {
1803 case Red;
1804 case Green;
1805 case Blue;
1806 }",
1807 "foo.php",
1808 |metric| {
1809 assert_eq!(metric.npa.class_na_sum(), 0);
1810 assert_eq!(metric.npa.class_npa_sum(), 0);
1811 },
1812 );
1813 check_metrics::<JavaParser>(
1814 "enum Color {
1815 RED, GREEN, BLUE;
1816 }",
1817 "foo.java",
1818 |metric| {
1819 assert_eq!(metric.npa.class_na_sum(), 0);
1820 assert_eq!(metric.npa.class_npa_sum(), 0);
1821 },
1822 );
1823 }
1824
1825 #[test]
1826 fn php_trait_attributes() {
1827 check_metrics::<PhpParser>(
1828 "<?php
1829 trait T {
1830 public int $a = 0;
1831 private int $b = 0;
1832 }",
1833 "foo.php",
1834 |metric| insta::assert_json_snapshot!(metric.npa),
1835 );
1836 }
1837
1838 #[test]
1839 fn php_no_explicit_visibility_excluded() {
1840 check_metrics::<PhpParser>("<?php class A { var $x = 0; }", "foo.php", |metric| {
1844 assert_eq!(metric.npa.class_npa_sum(), 0);
1848 assert_eq!(metric.npa.class_na_sum(), 1);
1849 assert_eq!(metric.npa.interface_na_sum(), 0);
1850 insta::assert_json_snapshot!(metric.npa);
1851 });
1852 }
1853
1854 #[test]
1855 fn php_anonymous_class_attributes() {
1856 check_metrics::<PhpParser>(
1861 "<?php
1862 $obj = new class {
1863 public int $a = 0;
1864 private int $b = 0;
1865 };",
1866 "foo.php",
1867 |metric| insta::assert_json_snapshot!(metric.npa),
1868 );
1869 }
1870
1871 #[test]
1872 fn php_property_promotion_excluded() {
1873 check_metrics::<PhpParser>(
1881 "<?php
1882 class A {
1883 public function __construct(public string $x, public int $y) {}
1884 }",
1885 "foo.php",
1886 |metric| insta::assert_json_snapshot!(metric.npa),
1887 );
1888 }
1889
1890 #[test]
1900 fn kotlin_empty_class_no_attributes() {
1901 check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
1902 assert_eq!(metric.npa.class_npa_sum(), 0);
1903 assert_eq!(metric.npa.class_na_sum(), 0);
1904 assert_eq!(metric.npa.interface_na_sum(), 0);
1905 insta::assert_json_snapshot!(metric.npa);
1906 });
1907 }
1908
1909 #[test]
1910 fn kotlin_public_val_var_default() {
1911 check_metrics::<KotlinParser>(
1913 "class C {
1914 val a: Int = 1
1915 var b: Int = 2
1916 val c: String = \"hi\"
1917 }",
1918 "foo.kt",
1919 |metric| {
1920 assert_eq!(metric.npa.class_npa_sum(), 3);
1921 assert_eq!(metric.npa.class_na_sum(), 3);
1922 insta::assert_json_snapshot!(metric.npa);
1923 },
1924 );
1925 }
1926
1927 #[test]
1928 fn kotlin_private_val_var() {
1929 check_metrics::<KotlinParser>(
1931 "class C {
1932 val a: Int = 1 // public
1933 private val b: Int = 2 // not public
1934 var c: Int = 3 // public
1935 private var d: Int = 4 // not public
1936 }",
1937 "foo.kt",
1938 |metric| {
1939 assert_eq!(metric.npa.class_npa_sum(), 2);
1940 assert_eq!(metric.npa.class_na_sum(), 4);
1941 insta::assert_json_snapshot!(metric.npa);
1942 },
1943 );
1944 }
1945
1946 #[test]
1947 fn kotlin_protected_internal_excluded_from_public() {
1948 check_metrics::<KotlinParser>(
1949 "open class C {
1950 protected val a: Int = 1
1951 internal val b: Int = 2
1952 public val c: Int = 3 // explicit public
1953 }",
1954 "foo.kt",
1955 |metric| {
1956 assert_eq!(metric.npa.class_npa_sum(), 1);
1957 assert_eq!(metric.npa.class_na_sum(), 3);
1958 insta::assert_json_snapshot!(metric.npa);
1959 },
1960 );
1961 }
1962
1963 #[test]
1964 fn kotlin_primary_constructor_parameter_property() {
1965 check_metrics::<KotlinParser>(
1969 "class C(val a: Int, var b: Int, c: Int) {
1970 val d: Int = c
1971 }",
1972 "foo.kt",
1973 |metric| {
1974 assert_eq!(metric.npa.class_npa_sum(), 3);
1976 assert_eq!(metric.npa.class_na_sum(), 3);
1977 insta::assert_json_snapshot!(metric.npa);
1978 },
1979 );
1980 }
1981
1982 #[test]
1983 fn kotlin_primary_constructor_private_param_property() {
1984 check_metrics::<KotlinParser>(
1985 "class C(private val a: Int, val b: Int)",
1986 "foo.kt",
1987 |metric| {
1988 assert_eq!(metric.npa.class_npa_sum(), 1);
1989 assert_eq!(metric.npa.class_na_sum(), 2);
1990 insta::assert_json_snapshot!(metric.npa);
1991 },
1992 );
1993 }
1994
1995 #[test]
1996 fn kotlin_secondary_constructor_does_not_add_attrs() {
1997 check_metrics::<KotlinParser>(
1999 "class C {
2000 private var a: Int = 0
2001 constructor(n: Int) { a = n }
2002 }",
2003 "foo.kt",
2004 |metric| {
2005 assert_eq!(metric.npa.class_npa_sum(), 0);
2006 assert_eq!(metric.npa.class_na_sum(), 1);
2007 insta::assert_json_snapshot!(metric.npa);
2008 },
2009 );
2010 }
2011
2012 #[test]
2013 fn kotlin_companion_object_attributes() {
2014 check_metrics::<KotlinParser>(
2017 "class Holder {
2018 val instance: Int = 1
2019 companion object {
2020 val SCALE: Int = 10
2021 private val SECRET: Int = 7
2022 }
2023 }",
2024 "foo.kt",
2025 |metric| {
2026 assert_eq!(metric.npa.class_npa_sum(), 2);
2029 assert_eq!(metric.npa.class_na_sum(), 3);
2030 insta::assert_json_snapshot!(metric.npa);
2031 },
2032 );
2033 }
2034
2035 #[test]
2036 fn kotlin_data_class_attributes() {
2037 check_metrics::<KotlinParser>(
2039 "data class Point(val x: Int, val y: Int)",
2040 "foo.kt",
2041 |metric| {
2042 assert_eq!(metric.npa.class_npa_sum(), 2);
2043 assert_eq!(metric.npa.class_na_sum(), 2);
2044 insta::assert_json_snapshot!(metric.npa);
2045 },
2046 );
2047 }
2048
2049 #[test]
2050 fn kotlin_object_singleton_attributes() {
2051 check_metrics::<KotlinParser>(
2052 "object Config {
2053 val DEFAULT: Int = 42
2054 private val SEED: Int = 0
2055 var debug: Boolean = false
2056 }",
2057 "foo.kt",
2058 |metric| {
2059 assert_eq!(metric.npa.class_npa_sum(), 2);
2061 assert_eq!(metric.npa.class_na_sum(), 3);
2062 insta::assert_json_snapshot!(metric.npa);
2063 },
2064 );
2065 }
2066
2067 #[test]
2068 fn kotlin_interface_attributes() {
2069 check_func_space::<KotlinParser, _>(
2075 "interface I {
2076 val a: Int
2077 val b: String
2078 }",
2079 "foo.kt",
2080 |func_space| {
2081 let metric = &func_space.metrics;
2082 assert_eq!(metric.npa.interface_npa_sum(), 2);
2083 assert_eq!(metric.npa.interface_na_sum(), 2);
2084 assert_eq!(metric.npa.class_na_sum(), 0);
2085 insta::assert_json_snapshot!(metric.npa);
2086 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2087 },
2088 );
2089 }
2090
2091 #[test]
2092 fn kotlin_nested_class_attributes() {
2093 check_metrics::<KotlinParser>(
2096 "class Outer {
2097 val o1: Int = 1
2098 class Nested {
2099 val n1: Int = 1
2100 val n2: Int = 2
2101 }
2102 }",
2103 "foo.kt",
2104 |metric| {
2105 assert_eq!(metric.npa.class_npa_sum(), 3);
2107 assert_eq!(metric.npa.class_na_sum(), 3);
2108 insta::assert_json_snapshot!(metric.npa);
2109 },
2110 );
2111 }
2112
2113 #[test]
2114 fn kotlin_inner_class_attributes() {
2115 check_metrics::<KotlinParser>(
2116 "class Outer {
2117 val o1: Int = 1
2118 inner class Inner {
2119 val i1: Int = 1
2120 }
2121 }",
2122 "foo.kt",
2123 |metric| {
2124 assert_eq!(metric.npa.class_npa_sum(), 2);
2125 assert_eq!(metric.npa.class_na_sum(), 2);
2126 insta::assert_json_snapshot!(metric.npa);
2127 },
2128 );
2129 }
2130
2131 #[test]
2132 fn kotlin_top_level_properties_excluded() {
2133 check_metrics::<KotlinParser>(
2136 "val topVal: Int = 0
2137 var topVar: Int = 1
2138 class C { val x: Int = 0 }",
2139 "foo.kt",
2140 |metric| {
2141 assert_eq!(metric.npa.class_npa_sum(), 1);
2142 assert_eq!(metric.npa.class_na_sum(), 1);
2143 insta::assert_json_snapshot!(metric.npa);
2144 },
2145 );
2146 }
2147
2148 #[test]
2149 fn kotlin_multiple_classes_attributes() {
2150 check_metrics::<KotlinParser>(
2151 "class A {
2152 val a1: Int = 0
2153 var a2: Int = 0
2154 }
2155 class B {
2156 val b1: Int = 0
2157 private val b2: Int = 0
2158 }",
2159 "foo.kt",
2160 |metric| {
2161 assert_eq!(metric.npa.class_npa_sum(), 3);
2163 assert_eq!(metric.npa.class_na_sum(), 4);
2164 insta::assert_json_snapshot!(metric.npa);
2165 },
2166 );
2167 }
2168
2169 #[test]
2170 fn kotlin_class_with_methods_no_attrs() {
2171 check_metrics::<KotlinParser>(
2173 "class C {
2174 fun m1() {}
2175 fun m2(): Int = 0
2176 }",
2177 "foo.kt",
2178 |metric| {
2179 assert_eq!(metric.npa.class_npa_sum(), 0);
2180 assert_eq!(metric.npa.class_na_sum(), 0);
2181 insta::assert_json_snapshot!(metric.npa);
2182 },
2183 );
2184 }
2185
2186 #[test]
2198 fn typescript_empty_class_no_attributes() {
2199 check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
2200 assert_eq!(metric.npa.class_npa_sum(), 0);
2201 assert_eq!(metric.npa.class_na_sum(), 0);
2202 insta::assert_json_snapshot!(metric.npa);
2203 });
2204 }
2205
2206 #[test]
2207 fn typescript_default_public_fields() {
2208 check_metrics::<TypescriptParser>(
2210 "class C {
2211 a: number = 1;
2212 b: string = \"\";
2213 c: boolean = false;
2214 }",
2215 "foo.ts",
2216 |metric| {
2217 assert_eq!(metric.npa.class_npa_sum(), 3);
2218 assert_eq!(metric.npa.class_na_sum(), 3);
2219 insta::assert_json_snapshot!(metric.npa);
2220 },
2221 );
2222 }
2223
2224 #[test]
2225 fn typescript_visibility_modifiers() {
2226 check_metrics::<TypescriptParser>(
2228 "class C {
2229 public a: number = 1;
2230 private b: number = 2;
2231 protected c: number = 3;
2232 d: number = 4;
2233 }",
2234 "foo.ts",
2235 |metric| {
2236 assert_eq!(metric.npa.class_npa_sum(), 2);
2238 assert_eq!(metric.npa.class_na_sum(), 4);
2239 insta::assert_json_snapshot!(metric.npa);
2240 },
2241 );
2242 }
2243
2244 #[test]
2245 fn typescript_static_fields() {
2246 check_metrics::<TypescriptParser>(
2248 "class C {
2249 static a: number = 0;
2250 public static b: number = 0;
2251 private static c: number = 0;
2252 }",
2253 "foo.ts",
2254 |metric| {
2255 assert_eq!(metric.npa.class_npa_sum(), 2);
2257 assert_eq!(metric.npa.class_na_sum(), 3);
2258 insta::assert_json_snapshot!(metric.npa);
2259 },
2260 );
2261 }
2262
2263 #[test]
2264 fn typescript_parameter_properties() {
2265 check_metrics::<TypescriptParser>(
2267 "class C {
2268 constructor(public a: number, private b: string, c: boolean) {}
2269 }",
2270 "foo.ts",
2271 |metric| {
2272 assert_eq!(metric.npa.class_npa_sum(), 1);
2275 assert_eq!(metric.npa.class_na_sum(), 2);
2276 insta::assert_json_snapshot!(metric.npa);
2277 },
2278 );
2279 }
2280
2281 #[test]
2282 fn typescript_readonly_constructor_param_property() {
2283 check_metrics::<TypescriptParser>(
2290 "class C {
2291 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2292 }",
2293 "foo.ts",
2294 |metric| {
2295 assert_eq!(metric.npa.class_na_sum(), 4);
2299 assert_eq!(metric.npa.class_npa_sum(), 2);
2300 insta::assert_json_snapshot!(metric.npa);
2301 },
2302 );
2303 }
2304
2305 #[test]
2306 fn typescript_readonly_field() {
2307 check_metrics::<TypescriptParser>(
2310 "class C {
2311 readonly a: number = 1;
2312 private readonly b: number = 2;
2313 }",
2314 "foo.ts",
2315 |metric| {
2316 assert_eq!(metric.npa.class_npa_sum(), 1);
2317 assert_eq!(metric.npa.class_na_sum(), 2);
2318 insta::assert_json_snapshot!(metric.npa);
2319 },
2320 );
2321 }
2322
2323 #[test]
2324 fn typescript_abstract_class_attributes() {
2325 check_metrics::<TypescriptParser>(
2328 "abstract class C {
2329 public a: number = 1;
2330 protected b: number = 2;
2331 abstract m(): void;
2332 }",
2333 "foo.ts",
2334 |metric| {
2335 assert_eq!(metric.npa.class_npa_sum(), 1);
2338 assert_eq!(metric.npa.class_na_sum(), 2);
2339 insta::assert_json_snapshot!(metric.npa);
2340 },
2341 );
2342 }
2343
2344 #[test]
2345 fn typescript_arrow_field_is_method_not_attribute() {
2346 check_metrics::<TypescriptParser>(
2349 "class C {
2350 a: number = 0;
2351 arrow = () => this.a;
2352 }",
2353 "foo.ts",
2354 |metric| {
2355 assert_eq!(metric.npa.class_npa_sum(), 1);
2356 assert_eq!(metric.npa.class_na_sum(), 1);
2357 insta::assert_json_snapshot!(metric.npa);
2358 },
2359 );
2360 }
2361
2362 #[test]
2363 fn typescript_interface_property_signatures() {
2364 check_func_space::<TypescriptParser, _>(
2370 "interface I {
2371 a: number;
2372 b: string;
2373 m(): void;
2374 }",
2375 "foo.ts",
2376 |func_space| {
2377 let metric = &func_space.metrics;
2378 assert_eq!(metric.npa.interface_npa_sum(), 2);
2379 assert_eq!(metric.npa.interface_na_sum(), 2);
2380 assert_eq!(metric.npa.class_na_sum(), 0);
2381 insta::assert_json_snapshot!(metric.npa);
2382 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2383 },
2384 );
2385 }
2386
2387 #[test]
2388 fn typescript_generic_class_attributes() {
2389 check_metrics::<TypescriptParser>(
2391 "class Box<T, U> {
2392 value: T;
2393 other: U;
2394 constructor(v: T, o: U) { this.value = v; this.other = o; }
2395 }",
2396 "foo.ts",
2397 |metric| {
2398 assert_eq!(metric.npa.class_npa_sum(), 2);
2399 assert_eq!(metric.npa.class_na_sum(), 2);
2400 insta::assert_json_snapshot!(metric.npa);
2401 },
2402 );
2403 }
2404
2405 #[test]
2406 fn typescript_getters_setters_not_attributes() {
2407 check_metrics::<TypescriptParser>(
2409 "class C {
2410 private _x: number = 0;
2411 get x(): number { return this._x; }
2412 set x(v: number) { this._x = v; }
2413 }",
2414 "foo.ts",
2415 |metric| {
2416 assert_eq!(metric.npa.class_npa_sum(), 0);
2418 assert_eq!(metric.npa.class_na_sum(), 1);
2419 insta::assert_json_snapshot!(metric.npa);
2420 },
2421 );
2422 }
2423
2424 #[test]
2425 fn typescript_multiple_classes_and_interface() {
2426 check_func_space::<TypescriptParser, _>(
2427 "class A { x: number = 0; }
2428 class B { private y: number = 0; }
2429 interface I { z: number; }",
2430 "foo.ts",
2431 |func_space| {
2432 let metric = &func_space.metrics;
2433 assert_eq!(metric.npa.class_npa_sum(), 1);
2436 assert_eq!(metric.npa.class_na_sum(), 2);
2437 assert_eq!(metric.npa.interface_npa_sum(), 1);
2438 assert_eq!(metric.npa.interface_na_sum(), 1);
2439 insta::assert_json_snapshot!(metric.npa);
2440 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2441 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2442 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2443 },
2444 );
2445 }
2446
2447 #[test]
2448 fn typescript_nested_class_attributes_independent() {
2449 check_metrics::<TypescriptParser>(
2457 "class Outer {
2458 a: number = 0;
2459 static Inner = class {
2460 b: number = 0;
2461 c: number = 0;
2462 };
2463 }",
2464 "foo.ts",
2465 |metric| {
2466 assert_eq!(metric.npa.class_npa_sum(), 4);
2467 assert_eq!(metric.npa.class_na_sum(), 4);
2468 insta::assert_json_snapshot!(metric.npa);
2469 },
2470 );
2471 }
2472
2473 #[test]
2477 fn tsx_empty_class_no_attributes() {
2478 check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
2479 assert_eq!(metric.npa.class_npa_sum(), 0);
2480 assert_eq!(metric.npa.class_na_sum(), 0);
2481 insta::assert_json_snapshot!(metric.npa);
2482 });
2483 }
2484
2485 #[test]
2486 fn tsx_default_public_fields() {
2487 check_metrics::<TsxParser>(
2488 "class C {
2489 a: number = 1;
2490 b: string = \"\";
2491 }",
2492 "foo.tsx",
2493 |metric| {
2494 assert_eq!(metric.npa.class_npa_sum(), 2);
2495 assert_eq!(metric.npa.class_na_sum(), 2);
2496 insta::assert_json_snapshot!(metric.npa);
2497 },
2498 );
2499 }
2500
2501 #[test]
2502 fn tsx_visibility_modifiers() {
2503 check_metrics::<TsxParser>(
2504 "class C {
2505 public a: number = 1;
2506 private b: number = 2;
2507 protected c: number = 3;
2508 }",
2509 "foo.tsx",
2510 |metric| {
2511 assert_eq!(metric.npa.class_npa_sum(), 1);
2512 assert_eq!(metric.npa.class_na_sum(), 3);
2513 insta::assert_json_snapshot!(metric.npa);
2514 },
2515 );
2516 }
2517
2518 #[test]
2519 fn tsx_parameter_properties() {
2520 check_metrics::<TsxParser>(
2521 "class C {
2522 constructor(public a: number, private b: string) {}
2523 }",
2524 "foo.tsx",
2525 |metric| {
2526 assert_eq!(metric.npa.class_npa_sum(), 1);
2527 assert_eq!(metric.npa.class_na_sum(), 2);
2528 insta::assert_json_snapshot!(metric.npa);
2529 },
2530 );
2531 }
2532
2533 #[test]
2534 fn tsx_readonly_constructor_param_property() {
2535 check_metrics::<TsxParser>(
2540 "class C {
2541 constructor(private a: number, readonly b: number, public c: number, e: number, private readonly d: number) {}
2542 }",
2543 "foo.tsx",
2544 |metric| {
2545 assert_eq!(metric.npa.class_na_sum(), 4);
2546 assert_eq!(metric.npa.class_npa_sum(), 2);
2547 insta::assert_json_snapshot!(metric.npa);
2548 },
2549 );
2550 }
2551
2552 #[test]
2553 fn tsx_abstract_class_attributes() {
2554 check_metrics::<TsxParser>(
2555 "abstract class C {
2556 public a: number = 1;
2557 private b: number = 2;
2558 abstract m(): void;
2559 }",
2560 "foo.tsx",
2561 |metric| {
2562 assert_eq!(metric.npa.class_npa_sum(), 1);
2563 assert_eq!(metric.npa.class_na_sum(), 2);
2564 insta::assert_json_snapshot!(metric.npa);
2565 },
2566 );
2567 }
2568
2569 #[test]
2570 fn tsx_interface_property_signatures() {
2571 check_func_space::<TsxParser, _>(
2572 "interface I {
2573 a: number;
2574 b: string;
2575 m(): void;
2576 }",
2577 "foo.tsx",
2578 |func_space| {
2579 let metric = &func_space.metrics;
2580 assert_eq!(metric.npa.interface_npa_sum(), 2);
2581 assert_eq!(metric.npa.interface_na_sum(), 2);
2582 insta::assert_json_snapshot!(metric.npa);
2583 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2584 },
2585 );
2586 }
2587
2588 #[test]
2589 fn tsx_arrow_field_is_method_not_attribute() {
2590 check_metrics::<TsxParser>(
2591 "class C {
2592 a: number = 0;
2593 arrow = () => this.a;
2594 }",
2595 "foo.tsx",
2596 |metric| {
2597 assert_eq!(metric.npa.class_npa_sum(), 1);
2598 assert_eq!(metric.npa.class_na_sum(), 1);
2599 insta::assert_json_snapshot!(metric.npa);
2600 },
2601 );
2602 }
2603
2604 #[test]
2605 fn tsx_static_fields() {
2606 check_metrics::<TsxParser>(
2607 "class C {
2608 static a: number = 0;
2609 private static b: number = 0;
2610 }",
2611 "foo.tsx",
2612 |metric| {
2613 assert_eq!(metric.npa.class_npa_sum(), 1);
2614 assert_eq!(metric.npa.class_na_sum(), 2);
2615 insta::assert_json_snapshot!(metric.npa);
2616 },
2617 );
2618 }
2619
2620 #[test]
2621 fn tsx_readonly_field() {
2622 check_metrics::<TsxParser>(
2623 "class C {
2624 readonly a: number = 1;
2625 private readonly b: number = 2;
2626 }",
2627 "foo.tsx",
2628 |metric| {
2629 assert_eq!(metric.npa.class_npa_sum(), 1);
2630 assert_eq!(metric.npa.class_na_sum(), 2);
2631 insta::assert_json_snapshot!(metric.npa);
2632 },
2633 );
2634 }
2635
2636 #[test]
2637 fn tsx_generic_class_attributes() {
2638 check_metrics::<TsxParser>("class Box<T> { value: T; }", "foo.tsx", |metric| {
2639 assert_eq!(metric.npa.class_npa_sum(), 1);
2640 assert_eq!(metric.npa.class_na_sum(), 1);
2641 insta::assert_json_snapshot!(metric.npa);
2642 });
2643 }
2644
2645 #[test]
2646 fn tsx_getters_setters_not_attributes() {
2647 check_metrics::<TsxParser>(
2648 "class C {
2649 private _x: number = 0;
2650 get x(): number { return this._x; }
2651 set x(v: number) { this._x = v; }
2652 }",
2653 "foo.tsx",
2654 |metric| {
2655 assert_eq!(metric.npa.class_npa_sum(), 0);
2656 assert_eq!(metric.npa.class_na_sum(), 1);
2657 insta::assert_json_snapshot!(metric.npa);
2658 },
2659 );
2660 }
2661
2662 #[test]
2663 fn tsx_multiple_classes_and_interface() {
2664 check_func_space::<TsxParser, _>(
2665 "class A { x: number = 0; }
2666 class B { private y: number = 0; }
2667 interface I { z: number; }",
2668 "foo.tsx",
2669 |func_space| {
2670 let metric = &func_space.metrics;
2671 assert_eq!(metric.npa.class_npa_sum(), 1);
2672 assert_eq!(metric.npa.class_na_sum(), 2);
2673 assert_eq!(metric.npa.interface_npa_sum(), 1);
2674 assert_eq!(metric.npa.interface_na_sum(), 1);
2675 insta::assert_json_snapshot!(metric.npa);
2676 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2677 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2678 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2679 },
2680 );
2681 }
2682
2683 #[test]
2693 fn ruby_no_class_attributes() {
2694 check_metrics::<RubyParser>(
2695 "class A\n def f\n 1\n end\nend\n",
2696 "foo.rb",
2697 |metric| {
2698 assert_eq!(metric.npa.class_npa_sum(), 0);
2699 assert_eq!(metric.npa.class_na_sum(), 0);
2700 insta::assert_json_snapshot!(metric.npa);
2701 },
2702 );
2703 }
2704
2705 #[test]
2706 fn ruby_instance_variable_attribute() {
2707 check_metrics::<RubyParser>("class A\n @x = 1\nend\n", "foo.rb", |metric| {
2709 assert_eq!(metric.npa.class_npa_sum(), 1);
2710 assert_eq!(metric.npa.class_na_sum(), 1);
2711 insta::assert_json_snapshot!(metric.npa);
2712 });
2713 }
2714
2715 #[test]
2716 fn ruby_class_variable_attribute() {
2717 check_metrics::<RubyParser>("class A\n @@y = 1\nend\n", "foo.rb", |metric| {
2719 assert_eq!(metric.npa.class_npa_sum(), 1);
2720 assert_eq!(metric.npa.class_na_sum(), 1);
2721 insta::assert_json_snapshot!(metric.npa);
2722 });
2723 }
2724
2725 #[test]
2726 fn ruby_attr_accessor_counts_symbols() {
2727 check_metrics::<RubyParser>(
2729 "class A\n attr_accessor :x, :y, :z\nend\n",
2730 "foo.rb",
2731 |metric| {
2732 assert_eq!(metric.npa.class_npa_sum(), 3);
2733 assert_eq!(metric.npa.class_na_sum(), 3);
2734 insta::assert_json_snapshot!(metric.npa);
2735 },
2736 );
2737 }
2738
2739 #[test]
2740 fn ruby_attr_reader_and_writer() {
2741 check_metrics::<RubyParser>(
2742 "class A\n attr_reader :r1, :r2\n attr_writer :w\nend\n",
2743 "foo.rb",
2744 |metric| {
2745 assert_eq!(metric.npa.class_npa_sum(), 3);
2746 assert_eq!(metric.npa.class_na_sum(), 3);
2747 insta::assert_json_snapshot!(metric.npa);
2748 },
2749 );
2750 }
2751
2752 #[test]
2753 fn ruby_mixed_attributes_and_assignments() {
2754 check_metrics::<RubyParser>(
2755 "class A\n attr_accessor :x, :y\n @z = 1\n @@w = 2\nend\n",
2756 "foo.rb",
2757 |metric| {
2758 assert_eq!(metric.npa.class_npa_sum(), 4);
2759 assert_eq!(metric.npa.class_na_sum(), 4);
2760 insta::assert_json_snapshot!(metric.npa);
2761 },
2762 );
2763 }
2764
2765 #[test]
2766 fn ruby_private_attributes() {
2767 check_metrics::<RubyParser>(
2769 "class A\n attr_accessor :pub\n private\n attr_accessor :hidden\nend\n",
2770 "foo.rb",
2771 |metric| {
2772 assert_eq!(metric.npa.class_npa_sum(), 1);
2773 assert_eq!(metric.npa.class_na_sum(), 2);
2774 insta::assert_json_snapshot!(metric.npa);
2775 },
2776 );
2777 }
2778
2779 #[test]
2780 fn ruby_visibility_public_resets_private() {
2781 check_metrics::<RubyParser>(
2783 "class A\n attr_reader :a\n private\n attr_reader :b\n public\n attr_reader :c\nend\n",
2784 "foo.rb",
2785 |metric| {
2786 assert_eq!(metric.npa.class_npa_sum(), 2);
2787 assert_eq!(metric.npa.class_na_sum(), 3);
2788 insta::assert_json_snapshot!(metric.npa);
2789 },
2790 );
2791 }
2792
2793 #[test]
2794 fn ruby_method_scope_assignments_excluded() {
2795 check_metrics::<RubyParser>(
2799 "class A\n def init\n @x = 1\n @@y = 2\n end\nend\n",
2800 "foo.rb",
2801 |metric| {
2802 assert_eq!(metric.npa.class_npa_sum(), 0);
2803 assert_eq!(metric.npa.class_na_sum(), 0);
2804 insta::assert_json_snapshot!(metric.npa);
2805 },
2806 );
2807 }
2808
2809 #[test]
2810 fn ruby_module_attributes_not_counted() {
2811 check_metrics::<RubyParser>(
2814 "module M\n attr_accessor :x\n @@m = 1\nend\n",
2815 "foo.rb",
2816 |metric| {
2817 assert_eq!(metric.npa.class_npa_sum(), 0);
2818 assert_eq!(metric.npa.class_na_sum(), 0);
2819 insta::assert_json_snapshot!(metric.npa);
2820 },
2821 );
2822 }
2823
2824 #[test]
2825 fn ruby_inheritance_attributes() {
2826 check_metrics::<RubyParser>(
2828 "class A < B\n attr_accessor :x\n @y = 0\nend\n",
2829 "foo.rb",
2830 |metric| {
2831 assert_eq!(metric.npa.class_npa_sum(), 2);
2832 assert_eq!(metric.npa.class_na_sum(), 2);
2833 insta::assert_json_snapshot!(metric.npa);
2834 },
2835 );
2836 }
2837
2838 #[test]
2839 fn ruby_constant_assignments_excluded() {
2840 check_metrics::<RubyParser>(
2844 "class A\n PI = 3.14\n E = 2.71\n attr_reader :x\nend\n",
2845 "foo.rb",
2846 |metric| {
2847 assert_eq!(metric.npa.class_npa_sum(), 1);
2849 assert_eq!(metric.npa.class_na_sum(), 1);
2850 insta::assert_json_snapshot!(metric.npa);
2851 },
2852 );
2853 }
2854
2855 #[test]
2856 fn ruby_multiple_classes_attribute_rollup() {
2857 check_metrics::<RubyParser>(
2858 "class A\n attr_accessor :x\nend\nclass B\n private\n attr_accessor :y\nend\n",
2859 "foo.rb",
2860 |metric| {
2861 assert_eq!(metric.npa.class_npa_sum(), 1);
2863 assert_eq!(metric.npa.class_na_sum(), 2);
2864 insta::assert_json_snapshot!(metric.npa);
2865 },
2866 );
2867 }
2868
2869 #[test]
2882 fn python_empty_class_no_attributes() {
2883 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
2884 assert_eq!(metric.npa.class_na_sum(), 0);
2885 assert_eq!(metric.npa.class_npa_sum(), 0);
2886 assert_eq!(metric.npa.interface_na_sum(), 0);
2887 insta::assert_json_snapshot!(metric.npa);
2888 });
2889 }
2890
2891 #[test]
2892 fn python_class_level_assignments_are_attributes() {
2893 check_metrics::<PythonParser>("class C:\n x = 1\n y = 2\n", "foo.py", |metric| {
2896 assert_eq!(metric.npa.class_na_sum(), 2);
2897 assert_eq!(metric.npa.class_npa_sum(), 2);
2898 insta::assert_json_snapshot!(metric.npa);
2899 });
2900 }
2901
2902 #[test]
2903 fn python_bare_type_annotation_not_attribute() {
2904 check_metrics::<PythonParser>(
2907 "class C:\n x: int\n y: int = 2\n",
2908 "foo.py",
2909 |metric| {
2910 assert_eq!(metric.npa.class_na_sum(), 1);
2911 insta::assert_json_snapshot!(metric.npa);
2912 },
2913 );
2914 }
2915
2916 #[test]
2917 fn python_self_attributes_in_init() {
2918 check_metrics::<PythonParser>(
2921 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n",
2922 "foo.py",
2923 |metric| {
2924 assert_eq!(metric.npa.class_na_sum(), 2);
2925 assert_eq!(metric.npa.class_npa_sum(), 2);
2926 insta::assert_json_snapshot!(metric.npa);
2927 },
2928 );
2929 }
2930
2931 #[test]
2932 fn python_self_attributes_in_nested_control_flow() {
2933 check_metrics::<PythonParser>(
2937 "class C:\n def __init__(self, flag):\n if flag:\n self.z = 1\n else:\n self.z = 2\n",
2938 "foo.py",
2939 |metric| {
2940 assert_eq!(metric.npa.class_na_sum(), 1);
2941 insta::assert_json_snapshot!(metric.npa);
2942 },
2943 );
2944 }
2945
2946 #[test]
2957 fn python_defensive_reinit_self_attribute_counts_once() {
2958 check_metrics::<PythonParser>(
2959 "class C:\n def __init__(self):\n self.value = None\n def reset(self):\n self.value = 0\n",
2960 "foo.py",
2961 |metric| {
2962 assert_eq!(metric.npa.class_na_sum(), 1);
2963 assert_eq!(metric.npa.class_npa_sum(), 1);
2964 insta::assert_json_snapshot!(metric.npa);
2965 },
2966 );
2967 }
2968
2969 #[test]
2972 fn python_distinct_self_attributes_count_independently() {
2973 check_metrics::<PythonParser>(
2974 "class C:\n def __init__(self):\n self.x = 1\n self.y = 2\n self.z = 3\n",
2975 "foo.py",
2976 |metric| {
2977 assert_eq!(metric.npa.class_na_sum(), 3);
2978 insta::assert_json_snapshot!(metric.npa);
2979 },
2980 );
2981 }
2982
2983 #[test]
2990 fn python_self_attribute_annotated_assignment_dedupes() {
2991 check_metrics::<PythonParser>(
2992 "class C:\n def __init__(self):\n self.value: int = 1\n def reset(self):\n self.value = 0\n",
2993 "foo.py",
2994 |metric| {
2995 assert_eq!(metric.npa.class_na_sum(), 1);
2996 insta::assert_json_snapshot!(metric.npa);
2997 },
2998 );
2999 }
3000
3001 #[test]
3002 fn python_class_level_and_self_attrs_combine() {
3003 check_metrics::<PythonParser>(
3005 "class C:\n counter = 0\n def __init__(self):\n self.name = 'a'\n self.value = 1\n",
3006 "foo.py",
3007 |metric| {
3008 assert_eq!(metric.npa.class_na_sum(), 3);
3009 insta::assert_json_snapshot!(metric.npa);
3010 },
3011 );
3012 }
3013
3014 #[test]
3015 fn python_self_attrs_isolated_per_class() {
3016 check_metrics::<PythonParser>(
3022 "class Outer:\n\
3023 \x20 def __init__(self):\n\
3024 \x20 self.x = 1\n\
3025 \x20 class Inner:\n\
3026 \x20 def __init__(self):\n\
3027 \x20 self.z = 2\n",
3028 "foo.py",
3029 |metric| {
3030 assert_eq!(metric.npa.class_na_sum(), 2);
3031 insta::assert_json_snapshot!(metric.npa);
3032 },
3033 );
3034 }
3035
3036 #[test]
3037 fn python_decorated_methods_do_not_inflate_attrs() {
3038 check_metrics::<PythonParser>(
3042 "class C:\n\
3043 \x20 @property\n\
3044 \x20 def p(self):\n\
3045 \x20 return 1\n\
3046 \x20 @staticmethod\n\
3047 \x20 def s():\n\
3048 \x20 return 2\n",
3049 "foo.py",
3050 |metric| {
3051 assert_eq!(metric.npa.class_na_sum(), 0);
3052 insta::assert_json_snapshot!(metric.npa);
3053 },
3054 );
3055 }
3056
3057 #[test]
3058 fn python_module_level_assignments_not_attributes() {
3059 check_metrics::<PythonParser>("x = 1\ny = 2\nclass C:\n a = 3\n", "foo.py", |metric| {
3061 assert_eq!(metric.npa.class_na_sum(), 1);
3063 insta::assert_json_snapshot!(metric.npa);
3064 });
3065 }
3066
3067 #[test]
3073 fn python_foreign_object_writes_not_attributes() {
3074 check_metrics::<PythonParser>(
3075 "class Service:\n\
3076 \x20 def __init__(self, db, logger):\n\
3077 \x20 self.name = \"svc\"\n\
3078 \x20 db.connection = None\n\
3079 \x20 logger.level = \"INFO\"\n",
3080 "foo.py",
3081 |metric| {
3082 assert_eq!(metric.npa.class_na_sum(), 1);
3084 assert_eq!(metric.npa.class_npa_sum(), 1);
3085 insta::assert_json_snapshot!(metric.npa);
3086 },
3087 );
3088 }
3089
3090 #[test]
3095 fn python_self_attribute_unpacking_counts_each() {
3096 check_metrics::<PythonParser>(
3097 "class C:\n\
3098 \x20 def __init__(self):\n\
3099 \x20 self.a, self.b = 1, 2\n\
3100 \x20 self.c = 3\n",
3101 "foo.py",
3102 |metric| {
3103 assert_eq!(metric.npa.class_na_sum(), 3);
3105 assert_eq!(metric.npa.class_npa_sum(), 3);
3106 insta::assert_json_snapshot!(metric.npa);
3107 },
3108 );
3109 }
3110
3111 #[test]
3117 fn python_self_attribute_nested_unpacking_counts_each() {
3118 check_metrics::<PythonParser>(
3119 "class C:\n\
3120 \x20 def __init__(self):\n\
3121 \x20 self.a, (self.b, self.c) = 1, (2, 3)\n",
3122 "foo.py",
3123 |metric| {
3124 assert_eq!(metric.npa.class_na_sum(), 3);
3126 assert_eq!(metric.npa.class_npa_sum(), 3);
3127 },
3128 );
3129 }
3130
3131 #[test]
3136 fn python_class_level_nested_unpacking_counts_each() {
3137 check_metrics::<PythonParser>(
3138 "class C:\n\
3139 \x20 (a, (b, c)) = 1, (2, 3)\n",
3140 "foo.py",
3141 |metric| {
3142 assert_eq!(metric.npa.class_na_sum(), 3);
3144 assert_eq!(metric.npa.class_npa_sum(), 3);
3145 },
3146 );
3147 }
3148
3149 #[test]
3157 fn python_class_level_parenthesized_unpacking_counts_each() {
3158 check_metrics::<PythonParser>(
3159 "class C:\n\
3160 \x20 (p, q) = 1, 2\n\
3161 \x20 [m, n] = 3, 4\n",
3162 "foo.py",
3163 |metric| {
3164 assert_eq!(metric.npa.class_na_sum(), 4);
3166 assert_eq!(metric.npa.class_npa_sum(), 4);
3167 },
3168 );
3169 }
3170
3171 #[test]
3175 fn python_self_attribute_unpacking_skips_non_self_targets() {
3176 check_metrics::<PythonParser>(
3177 "class C:\n\
3178 \x20 def __init__(self):\n\
3179 \x20 self.a, x = 1, 2\n",
3180 "foo.py",
3181 |metric| {
3182 assert_eq!(metric.npa.class_na_sum(), 1);
3184 insta::assert_json_snapshot!(metric.npa);
3185 },
3186 );
3187 }
3188
3189 #[test]
3194 fn python_class_level_multi_target_counts_each_name() {
3195 check_metrics::<PythonParser>(
3196 "class C:\n x = 1\n a = b = 3\n p, q = 1, 2\n",
3197 "foo.py",
3198 |metric| {
3199 assert_eq!(metric.npa.class_na_sum(), 5);
3201 assert_eq!(metric.npa.class_npa_sum(), 5);
3202 insta::assert_json_snapshot!(metric.npa);
3203 },
3204 );
3205 }
3206
3207 #[test]
3211 fn python_chained_self_assignment_counts_each() {
3212 check_metrics::<PythonParser>(
3213 "class C:\n def __init__(self):\n self.a = self.b = 1\n",
3214 "foo.py",
3215 |metric| {
3216 assert_eq!(metric.npa.class_na_sum(), 2);
3217 assert_eq!(metric.npa.class_npa_sum(), 2);
3218 insta::assert_json_snapshot!(metric.npa);
3219 },
3220 );
3221 }
3222
3223 #[test]
3227 fn python_classmethod_cls_attribute_counts() {
3228 check_metrics::<PythonParser>(
3229 "class C:\n\
3230 \x20 @classmethod\n\
3231 \x20 def make(cls, other):\n\
3232 \x20 cls.registry = {}\n\
3233 \x20 other.thing = 1\n",
3234 "foo.py",
3235 |metric| {
3236 assert_eq!(metric.npa.class_na_sum(), 1);
3238 assert_eq!(metric.npa.class_npa_sum(), 1);
3239 insta::assert_json_snapshot!(metric.npa);
3240 },
3241 );
3242 }
3243
3244 #[test]
3249 fn python_nested_self_attribute_not_counted() {
3250 check_metrics::<PythonParser>(
3251 "class C:\n def __init__(self):\n self.f.g = 1\n",
3252 "foo.py",
3253 |metric| {
3254 assert_eq!(metric.npa.class_na_sum(), 0);
3255 insta::assert_json_snapshot!(metric.npa);
3256 },
3257 );
3258 }
3259
3260 #[test]
3265 fn python_class_default_and_self_attr_dedupe() {
3266 check_metrics::<PythonParser>(
3267 "class C:\n x = 1\n def __init__(self):\n self.x = 2\n",
3268 "foo.py",
3269 |metric| {
3270 assert_eq!(metric.npa.class_na_sum(), 1);
3271 assert_eq!(metric.npa.class_npa_sum(), 1);
3272 insta::assert_json_snapshot!(metric.npa);
3273 },
3274 );
3275 }
3276
3277 #[test]
3278 fn rust_empty_unit_no_attributes() {
3279 check_metrics::<RustParser>("", "empty.rs", |metric| {
3280 assert_eq!(metric.npa.class_na_sum(), 0);
3281 assert_eq!(metric.npa.class_npa_sum(), 0);
3282 assert_eq!(metric.npa.interface_na_sum(), 0);
3283 assert_eq!(metric.npa.interface_npa_sum(), 0);
3284 insta::assert_json_snapshot!(metric.npa);
3285 });
3286 }
3287
3288 #[test]
3289 fn rust_struct_fields_are_attributes() {
3290 check_metrics::<RustParser>(
3293 "struct Foo { pub a: i32, b: String, pub c: bool }",
3294 "foo.rs",
3295 |metric| {
3296 assert_eq!(metric.npa.class_na_sum(), 3);
3297 assert_eq!(metric.npa.class_npa_sum(), 2);
3298 insta::assert_json_snapshot!(metric.npa);
3299 },
3300 );
3301 }
3302
3303 #[test]
3304 fn rust_pub_self_field_is_private() {
3305 check_metrics::<RustParser>(
3313 "struct S {\n\
3314 \x20 pub(self) a: i32,\n\
3315 \x20 pub(in self) a2: i32,\n\
3316 \x20 pub(crate) b: i32,\n\
3317 \x20 pub(super) d: i32,\n\
3318 \x20 pub(in crate::x) e: i32,\n\
3319 \x20 pub f: i32,\n\
3320 \x20 c: i32,\n\
3321 }",
3322 "foo.rs",
3323 |metric| {
3324 assert_eq!(metric.npa.class_na_sum(), 7);
3325 assert_eq!(metric.npa.class_npa_sum(), 4);
3326 },
3327 );
3328 }
3329
3330 #[test]
3331 fn rust_pub_self_assoc_const_is_private() {
3332 check_metrics::<RustParser>(
3337 "struct Foo;\n\
3338 impl Foo {\n\
3339 \x20 pub(self) const A: i32 = 1;\n\
3340 \x20 pub(in self) const A2: i32 = 2;\n\
3341 \x20 pub(crate) const B: i32 = 3;\n\
3342 \x20 pub const D: i32 = 4;\n\
3343 }\n",
3344 "foo.rs",
3345 |metric| {
3346 assert_eq!(metric.npa.class_na_sum(), 4);
3347 assert_eq!(metric.npa.class_npa_sum(), 2);
3348 },
3349 );
3350 }
3351
3352 #[test]
3353 fn rust_pub_self_tuple_field_is_private() {
3354 check_metrics::<RustParser>(
3362 "struct Bar(pub(self) i32, pub(in self) i32, pub(crate) i32, pub u8, String);",
3363 "foo.rs",
3364 |metric| {
3365 assert_eq!(metric.npa.class_na_sum(), 5);
3366 assert_eq!(metric.npa.class_npa_sum(), 2);
3367 },
3368 );
3369 }
3370
3371 #[test]
3372 fn rust_tuple_struct_fields_are_attributes() {
3373 check_metrics::<RustParser>("struct Bar(pub i32, String);", "foo.rs", |metric| {
3376 assert_eq!(metric.npa.class_na_sum(), 2);
3377 assert_eq!(metric.npa.class_npa_sum(), 1);
3378 insta::assert_json_snapshot!(metric.npa);
3379 });
3380 }
3381
3382 #[test]
3383 fn rust_unit_struct_has_no_attributes() {
3384 check_metrics::<RustParser>("struct Empty;", "foo.rs", |metric| {
3386 assert_eq!(metric.npa.class_na_sum(), 0);
3387 insta::assert_json_snapshot!(metric.npa);
3388 });
3389 }
3390
3391 #[test]
3392 fn rust_empty_struct_body_has_no_attributes() {
3393 check_metrics::<RustParser>("struct Empty { }", "foo.rs", |metric| {
3395 assert_eq!(metric.npa.class_na_sum(), 0);
3396 insta::assert_json_snapshot!(metric.npa);
3397 });
3398 }
3399
3400 #[test]
3401 fn rust_impl_associated_consts_are_attributes() {
3402 check_metrics::<RustParser>(
3405 "struct Foo;\n\
3406 impl Foo {\n\
3407 \x20 const X: i32 = 1;\n\
3408 \x20 pub const Y: i32 = 2;\n\
3409 \x20 static Z: i32 = 3;\n\
3410 \x20 pub static W: i32 = 4;\n\
3411 }\n",
3412 "foo.rs",
3413 |metric| {
3414 assert_eq!(metric.npa.class_na_sum(), 4);
3417 assert_eq!(metric.npa.class_npa_sum(), 2);
3418 insta::assert_json_snapshot!(metric.npa);
3419 },
3420 );
3421 }
3422
3423 #[test]
3424 fn rust_trait_consts_and_associated_types_are_attributes() {
3425 check_func_space::<RustParser, _>(
3430 "trait Drawable { const DEFAULT_COLOR: u32; type Item; }",
3431 "foo.rs",
3432 |func_space| {
3433 let metric = &func_space.metrics;
3434 assert_eq!(metric.npa.interface_na_sum(), 2);
3435 assert_eq!(metric.npa.interface_npa_sum(), 2);
3436 assert_eq!(metric.npa.class_na_sum(), 0);
3437 insta::assert_json_snapshot!(metric.npa);
3438 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
3439 },
3440 );
3441 }
3442
3443 #[test]
3444 fn rust_multiple_impls_aggregate() {
3445 check_metrics::<RustParser>(
3448 "struct Foo;\n\
3449 impl Foo { const X: i32 = 1; }\n\
3450 impl Foo { pub const Y: i32 = 2; }\n",
3451 "foo.rs",
3452 |metric| {
3453 assert_eq!(metric.npa.class_na_sum(), 2);
3454 assert_eq!(metric.npa.class_npa_sum(), 1);
3455 insta::assert_json_snapshot!(metric.npa);
3456 },
3457 );
3458 }
3459
3460 #[test]
3461 fn rust_module_level_consts_not_attributes() {
3462 check_metrics::<RustParser>(
3466 "const PI: f64 = 3.14;\nstatic Q: i32 = 0;\n",
3467 "foo.rs",
3468 |metric| {
3469 assert_eq!(metric.npa.class_na_sum(), 0);
3470 assert_eq!(metric.npa.interface_na_sum(), 0);
3471 insta::assert_json_snapshot!(metric.npa);
3472 },
3473 );
3474 }
3475
3476 #[test]
3479 fn go_empty_unit_no_attributes() {
3480 check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
3483 assert_eq!(metric.npa.class_na_sum(), 0);
3484 insta::assert_json_snapshot!(metric.npa);
3485 });
3486 }
3487
3488 #[test]
3489 fn go_empty_struct_has_no_attributes() {
3490 check_metrics::<GoParser>("package main\ntype Empty struct{}\n", "foo.go", |metric| {
3493 assert_eq!(metric.npa.class_na_sum(), 0);
3494 insta::assert_json_snapshot!(metric.npa);
3495 });
3496 }
3497
3498 #[test]
3499 fn go_struct_fields_are_attributes() {
3500 check_metrics::<GoParser>(
3504 "package main\ntype Foo struct { X int; y string; Z float64 }\n",
3505 "foo.go",
3506 |metric| {
3507 assert_eq!(metric.npa.class_na_sum(), 3);
3508 assert_eq!(metric.npa.class_npa_sum(), 2);
3509 insta::assert_json_snapshot!(metric.npa);
3510 },
3511 );
3512 }
3513
3514 #[test]
3515 fn go_grouped_struct_fields_each_count() {
3516 check_metrics::<GoParser>(
3521 "package main\ntype Point struct { X, Y int; Z float64 }\n",
3522 "foo.go",
3523 |metric| {
3524 assert_eq!(metric.npa.class_na_sum(), 3);
3525 assert_eq!(metric.npa.class_npa_sum(), 3);
3526 insta::assert_json_snapshot!(metric.npa);
3527 },
3528 );
3529 }
3530
3531 #[test]
3532 fn go_embedded_type_counts_as_attribute() {
3533 check_metrics::<GoParser>(
3540 "package main\nimport \"io\"\ntype Bar struct { io.Reader; *Foo; n int }\ntype Foo struct {}\n",
3541 "foo.go",
3542 |metric| {
3543 assert_eq!(metric.npa.class_na_sum(), 3);
3544 assert_eq!(metric.npa.class_npa_sum(), 2);
3545 insta::assert_json_snapshot!(metric.npa);
3546 },
3547 );
3548 }
3549
3550 #[test]
3551 fn go_multiple_structs_aggregate_at_unit() {
3552 check_metrics::<GoParser>(
3558 "package main\ntype Foo struct { x int }\ntype Bar struct { a int; b string }\n",
3559 "foo.go",
3560 |metric| {
3561 assert_eq!(metric.npa.class_na_sum(), 3);
3562 assert_eq!(metric.npa.class_npa_sum(), 0);
3563 insta::assert_json_snapshot!(metric.npa);
3564 },
3565 );
3566 }
3567
3568 #[test]
3569 fn go_top_level_var_const_not_attributes() {
3570 check_metrics::<GoParser>(
3574 "package main\nvar Counter int\nconst Pi = 3.14\n",
3575 "foo.go",
3576 |metric| {
3577 assert_eq!(metric.npa.class_na_sum(), 0);
3578 insta::assert_json_snapshot!(metric.npa);
3579 },
3580 );
3581 }
3582
3583 #[test]
3584 fn go_npa_excludes_unexported() {
3585 check_metrics::<GoParser>(
3596 "package main\nimport \"io\"\n\
3597 type T struct { Name string; secret int; A, b int; io.Reader; _ int; Ärger bool }\n",
3598 "foo.go",
3599 |metric| {
3600 assert_eq!(metric.npa.class_na_sum(), 7);
3601 assert_eq!(metric.npa.class_npa_sum(), 4);
3602 insta::assert_json_snapshot!(metric.npa);
3603 },
3604 );
3605 }
3606
3607 #[test]
3613 fn elixir_npa_defstruct_keyword_list() {
3614 check_metrics::<ElixirParser>(
3615 "defmodule User do\n defstruct name: nil, age: 0, email: nil\nend\n",
3616 "foo.ex",
3617 |metric| {
3618 assert_eq!(metric.npa.class_na_sum(), 3);
3620 assert_eq!(metric.npa.class_npa_sum(), 3);
3621 },
3622 );
3623 }
3624
3625 #[test]
3626 fn elixir_npa_defstruct_atom_list() {
3627 check_metrics::<ElixirParser>(
3628 "defmodule User do\n defstruct [:name, :age, :email]\nend\n",
3629 "foo.ex",
3630 |metric| {
3631 assert_eq!(metric.npa.class_na_sum(), 3);
3632 assert_eq!(metric.npa.class_npa_sum(), 3);
3633 },
3634 );
3635 }
3636
3637 #[test]
3638 fn elixir_npa_defstruct_bracketed_keyword_list() {
3639 check_metrics::<ElixirParser>(
3640 "defmodule User do\n defstruct [name: nil, age: 0]\nend\n",
3641 "foo.ex",
3642 |metric| {
3643 assert_eq!(metric.npa.class_na_sum(), 2);
3644 assert_eq!(metric.npa.class_npa_sum(), 2);
3645 },
3646 );
3647 }
3648
3649 #[test]
3650 fn elixir_npa_defstruct_single_field() {
3651 check_metrics::<ElixirParser>(
3652 "defmodule Box do\n defstruct value: nil\nend\n",
3653 "foo.ex",
3654 |metric| {
3655 assert_eq!(metric.npa.class_na_sum(), 1);
3656 assert_eq!(metric.npa.class_npa_sum(), 1);
3657 },
3658 );
3659 }
3660
3661 #[test]
3662 fn elixir_npa_no_defstruct_is_zero() {
3663 check_metrics::<ElixirParser>(
3664 "defmodule Foo do\n def m, do: :ok\nend\n",
3665 "foo.ex",
3666 |metric| {
3667 assert_eq!(metric.npa.class_na_sum(), 0);
3668 assert_eq!(metric.npa.class_npa_sum(), 0);
3669 },
3670 );
3671 }
3672
3673 #[test]
3676 fn objc_npa() {
3677 check_metrics::<ObjcParser>(
3690 "@interface Foo : NSObject {\n\
3691 int _prot;\n\
3692 @private\n\
3693 int _priv;\n\
3694 @public\n\
3695 int _pub1, _pub2;\n\
3696 @protected\n\
3697 int _prot2;\n\
3698 }\n\
3699 @property (nonatomic) int count;\n\
3700 @property (copy) NSString *name;\n\
3701 @end\n",
3702 "foo.m",
3703 |metric| {
3704 assert_eq!(metric.npa.interface_na_sum(), 7);
3705 assert_eq!(metric.npa.interface_npa_sum(), 4);
3706 },
3707 );
3708 }
3709
3710 #[test]
3711 fn objc_npa_protocol() {
3712 check_metrics::<ObjcParser>(
3717 "@protocol Drawable <NSObject>\n\
3718 @optional\n\
3719 @property (readonly) int z;\n\
3720 @end\n",
3721 "foo.m",
3722 |metric| {
3723 assert_eq!(metric.npa.interface_na_sum(), 1);
3724 assert_eq!(metric.npa.interface_npa_sum(), 1);
3725 },
3726 );
3727 }
3728
3729 #[test]
3732 fn cpp_empty_unit_no_attributes() {
3733 check_metrics::<CppParser>("", "empty.cpp", |metric| {
3736 assert_eq!(metric.npa.class_na_sum(), 0);
3737 assert_eq!(metric.npa.class_npa_sum(), 0);
3738 insta::assert_json_snapshot!(metric.npa);
3739 });
3740 }
3741
3742 #[test]
3743 fn cpp_empty_class_no_attributes() {
3744 check_metrics::<CppParser>("class Foo {};", "foo.cpp", |metric| {
3747 assert_eq!(metric.npa.class_na_sum(), 0);
3748 assert_eq!(metric.npa.class_npa_sum(), 0);
3749 insta::assert_json_snapshot!(metric.npa);
3750 });
3751 }
3752
3753 #[test]
3754 fn cpp_class_public_attributes() {
3755 check_metrics::<CppParser>(
3760 "class Foo { public: int a; int b, c; };",
3761 "foo.cpp",
3762 |metric| {
3763 assert_eq!(metric.npa.class_na_sum(), 3);
3764 assert_eq!(metric.npa.class_npa_sum(), 3);
3765 insta::assert_json_snapshot!(metric.npa);
3766 },
3767 );
3768 }
3769
3770 #[test]
3771 fn cpp_class_private_default_visibility() {
3772 check_metrics::<CppParser>("class Foo { int value_; };", "foo.cpp", |metric| {
3776 assert_eq!(metric.npa.class_na_sum(), 1);
3777 assert_eq!(metric.npa.class_npa_sum(), 0);
3778 insta::assert_json_snapshot!(metric.npa);
3779 });
3780 }
3781
3782 #[test]
3783 fn cpp_struct_default_public_visibility() {
3784 check_metrics::<CppParser>("struct Bar { int value_; };", "foo.cpp", |metric| {
3787 assert_eq!(metric.npa.class_na_sum(), 1);
3788 assert_eq!(metric.npa.class_npa_sum(), 1);
3789 insta::assert_json_snapshot!(metric.npa);
3790 });
3791 }
3792
3793 #[test]
3794 fn cpp_mixed_visibility_sections() {
3795 check_metrics::<CppParser>(
3799 "class Foo {\n\
3800 public: int a;\n\
3801 protected: int b;\n\
3802 private: int c;\n\
3803 };",
3804 "foo.cpp",
3805 |metric| {
3806 assert_eq!(metric.npa.class_na_sum(), 3);
3807 assert_eq!(metric.npa.class_npa_sum(), 1);
3808 insta::assert_json_snapshot!(metric.npa);
3809 },
3810 );
3811 }
3812
3813 #[test]
3814 fn cpp_methods_not_counted_as_attributes() {
3815 check_metrics::<CppParser>(
3820 "class Foo {\n\
3821 public:\n\
3822 void method1() {}\n\
3823 void method2();\n\
3824 private:\n\
3825 int value_;\n\
3826 };",
3827 "foo.cpp",
3828 |metric| {
3829 assert_eq!(metric.npa.class_na_sum(), 1);
3830 assert_eq!(metric.npa.class_npa_sum(), 0);
3831 insta::assert_json_snapshot!(metric.npa);
3832 },
3833 );
3834 }
3835
3836 #[test]
3837 fn cpp_pointer_array_fields_count() {
3838 check_metrics::<CppParser>(
3843 "struct S {\n\
3844 int* p;\n\
3845 int a[10];\n\
3846 int x;\n\
3847 };",
3848 "foo.cpp",
3849 |metric| {
3850 assert_eq!(metric.npa.class_na_sum(), 3);
3851 assert_eq!(metric.npa.class_npa_sum(), 3);
3853 insta::assert_json_snapshot!(metric.npa);
3854 },
3855 );
3856 }
3857
3858 #[test]
3859 fn cpp_multiple_classes_aggregate_at_unit() {
3860 check_metrics::<CppParser>(
3865 "class Foo { public: int a; private: int b; };\nstruct Bar { int c; };",
3866 "foo.cpp",
3867 |metric| {
3868 assert_eq!(metric.npa.class_na_sum(), 3);
3869 assert_eq!(metric.npa.class_npa_sum(), 2);
3871 insta::assert_json_snapshot!(metric.npa);
3872 },
3873 );
3874 }
3875
3876 #[test]
3877 fn javascript_empty_unit_no_attributes() {
3878 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
3881 assert_eq!(metric.npa.class_na_sum(), 0);
3882 assert_eq!(metric.npa.class_npa_sum(), 0);
3883 insta::assert_json_snapshot!(metric.npa);
3884 });
3885 }
3886
3887 #[test]
3888 fn javascript_empty_class_no_attributes() {
3889 check_metrics::<JavascriptParser>("class Foo {}", "foo.js", |metric| {
3891 assert_eq!(metric.npa.class_na_sum(), 0);
3892 assert_eq!(metric.npa.class_npa_sum(), 0);
3893 insta::assert_json_snapshot!(metric.npa);
3894 });
3895 }
3896
3897 #[test]
3898 fn javascript_class_fields_count() {
3899 check_metrics::<JavascriptParser>(
3904 "class Foo { x = 1; y; static z = 2; }",
3905 "foo.js",
3906 |metric| {
3907 assert_eq!(metric.npa.class_na_sum(), 3);
3908 assert_eq!(metric.npa.class_npa_sum(), 3);
3909 insta::assert_json_snapshot!(metric.npa);
3910 },
3911 );
3912 }
3913
3914 #[test]
3915 fn javascript_arrow_field_is_method_not_attribute() {
3916 check_metrics::<JavascriptParser>(
3920 "class Foo { x = () => {}; y = function() {}; z = 1; }",
3921 "foo.js",
3922 |metric| {
3923 assert_eq!(metric.npa.class_na_sum(), 1);
3925 assert_eq!(metric.npa.class_npa_sum(), 1);
3926 insta::assert_json_snapshot!(metric.npa);
3927 },
3928 );
3929 }
3930
3931 #[test]
3932 fn javascript_methods_not_counted_as_attributes() {
3933 check_metrics::<JavascriptParser>(
3936 "class Foo { constructor() {} bar() {} get baz() { return 1; } x = 1; }",
3937 "foo.js",
3938 |metric| {
3939 assert_eq!(metric.npa.class_na_sum(), 1);
3941 assert_eq!(metric.npa.class_npa_sum(), 1);
3942 insta::assert_json_snapshot!(metric.npa);
3943 },
3944 );
3945 }
3946
3947 #[test]
3948 fn javascript_multiple_classes_aggregate_at_unit() {
3949 check_metrics::<JavascriptParser>(
3953 "class Foo { a = 1; b = 2; }\nclass Bar { c = 3; }",
3954 "foo.js",
3955 |metric| {
3956 assert_eq!(metric.npa.class_na_sum(), 3);
3957 assert_eq!(metric.npa.class_npa_sum(), 3);
3958 insta::assert_json_snapshot!(metric.npa);
3959 },
3960 );
3961 }
3962
3963 #[test]
3964 fn mozjs_class_fields_count() {
3965 check_metrics::<MozjsParser>(
3968 "class Foo { x = 1; y; static z = 2; }",
3969 "foo.js",
3970 |metric| {
3971 assert_eq!(metric.npa.class_na_sum(), 3);
3972 assert_eq!(metric.npa.class_npa_sum(), 3);
3973 insta::assert_json_snapshot!(metric.npa);
3974 },
3975 );
3976 }
3977
3978 #[test]
3986 fn empty_class_cda_is_zero_not_nan() {
3987 let assert_zero = |metric: crate::CodeMetrics| {
3988 assert_eq!(metric.npa.class_na_sum(), 0);
3989 assert!(!metric.npa.class_cda().is_nan());
3990 assert!(!metric.npa.total_cda().is_nan());
3991 assert_eq!(metric.npa.class_cda(), 0.0);
3992 assert_eq!(metric.npa.total_cda(), 0.0);
3993 };
3994 check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
3995 check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
3996 check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
3997 check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
3998 }
3999
4000 #[test]
4005 fn empty_interface_cda_is_zero_not_nan() {
4006 let assert_zero = |metric: crate::CodeMetrics| {
4007 assert_eq!(metric.npa.interface_na_sum(), 0);
4008 assert!(!metric.npa.interface_cda().is_nan());
4009 assert_eq!(metric.npa.interface_cda(), 0.0);
4010 };
4011 check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
4012 check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
4013 }
4014}