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, csharp_accessor_count};
23use crate::langs::*;
24use crate::macros::implement_metric_trait;
25use crate::metrics::npa::{accessibility_ratio, python_is_block, ts_member_is_public};
26use crate::node::Node;
27use crate::*;
28
29#[derive(Clone, Debug, Default, PartialEq)]
53#[non_exhaustive]
54pub struct Stats {
55 class_npm: usize,
56 interface_npm: usize,
57 class_nm: usize,
58 interface_nm: usize,
59 class_npm_sum: usize,
60 interface_npm_sum: usize,
61 class_nm_sum: usize,
62 interface_nm_sum: usize,
63 space_kind: SpaceKind,
64}
65
66impl fmt::Display for Stats {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 write!(
69 f,
70 "classes: {}, interfaces: {}, class_methods: {}, interface_methods: {}, class_coa: {}, interface_coa: {}, total: {}, total_methods: {}, coa: {}",
71 self.class_npm_sum(),
72 self.interface_npm_sum(),
73 self.class_nm_sum(),
74 self.interface_nm_sum(),
75 self.class_coa(),
76 self.interface_coa(),
77 self.total_npm(),
78 self.total_nm(),
79 self.total_coa()
80 )
81 }
82}
83
84impl Stats {
85 pub fn merge(&mut self, other: &Stats) {
87 self.class_npm_sum += other.class_npm_sum;
88 self.interface_npm_sum += other.interface_npm_sum;
89 self.class_nm_sum += other.class_nm_sum;
90 self.interface_nm_sum += other.interface_nm_sum;
91 }
92
93 #[inline]
95 #[must_use]
96 pub fn class_npm(&self) -> u64 {
97 self.class_npm as u64
98 }
99
100 #[inline]
102 #[must_use]
103 pub fn interface_npm(&self) -> u64 {
104 self.interface_npm as u64
105 }
106
107 #[inline]
109 #[must_use]
110 pub fn class_nm(&self) -> u64 {
111 self.class_nm as u64
112 }
113
114 #[inline]
116 #[must_use]
117 pub fn interface_nm(&self) -> u64 {
118 self.interface_nm as u64
119 }
120
121 #[inline]
123 #[must_use]
124 pub fn class_npm_sum(&self) -> u64 {
125 self.class_npm_sum as u64
126 }
127
128 #[inline]
130 #[must_use]
131 pub fn interface_npm_sum(&self) -> u64 {
132 self.interface_npm_sum as u64
133 }
134
135 #[inline]
137 #[must_use]
138 pub fn class_nm_sum(&self) -> u64 {
139 self.class_nm_sum as u64
140 }
141
142 #[inline]
144 #[must_use]
145 pub fn interface_nm_sum(&self) -> u64 {
146 self.interface_nm_sum as u64
147 }
148
149 #[inline]
159 #[must_use]
160 pub fn class_coa(&self) -> f64 {
161 accessibility_ratio(self.class_npm_sum() as f64, self.class_nm_sum() as f64)
162 }
163
164 #[inline]
174 #[must_use]
175 pub fn interface_coa(&self) -> f64 {
176 if self.interface_npm_sum == self.interface_nm_sum && self.interface_npm_sum != 0 {
182 1.0
183 } else {
184 accessibility_ratio(
185 self.interface_npm_sum() as f64,
186 self.interface_nm_sum() as f64,
187 )
188 }
189 }
190
191 #[inline]
201 #[must_use]
202 pub fn total_coa(&self) -> f64 {
203 accessibility_ratio(self.total_npm() as f64, self.total_nm() as f64)
204 }
205
206 #[inline]
208 #[must_use]
209 pub fn total_npm(&self) -> u64 {
210 self.class_npm_sum() + self.interface_npm_sum()
211 }
212
213 #[inline]
215 #[must_use]
216 pub fn total_nm(&self) -> u64 {
217 self.class_nm_sum() + self.interface_nm_sum()
218 }
219
220 #[inline]
223 pub(crate) fn compute_sum(&mut self) {
224 self.class_npm_sum += self.class_npm;
225 self.interface_npm_sum += self.interface_npm;
226 self.class_nm_sum += self.class_nm;
227 self.interface_nm_sum += self.interface_nm;
228 }
229
230 #[inline]
237 pub(crate) fn set_space_kind(&mut self, kind: SpaceKind) {
238 self.space_kind = kind;
239 }
240
241 #[inline]
243 pub(crate) fn is_disabled(&self) -> bool {
244 !self.space_kind.is_member_scope()
245 }
246}
247
248fn direct_child_funcs<'a, C: Checker>(node: &Node<'a>) -> impl Iterator<Item = Node<'a>> {
258 node.children()
259 .filter(|child| C::is_func(child, Ancestors::unknown()))
260}
261
262#[doc(hidden)]
263pub(crate) trait Npm
265where
266 Self: Checker,
267{
268 const HAS_MEMBERS: bool = true;
278
279 fn compute<'a>(
297 node: &Node<'a>,
298 code: &'a [u8],
299 ancestors: Ancestors<'a, '_>,
300 stats: &mut Stats,
301 );
302}
303
304macro_rules! impl_npm_java_like {
322 ($code:ty, $lang:ident) => {
323 impl Npm for $code {
324 fn compute<'a>(
325 node: &Node<'a>,
326 _code: &'a [u8],
327 _ancestors: Ancestors<'a, '_>,
328 stats: &mut Stats,
329 ) {
330 use $lang::*;
331
332 match node.kind_id().into() {
333 ClassBody | EnumBodyDeclarations => {
334 for method in direct_child_funcs::<Self>(node) {
335 stats.class_nm += 1;
336 if let Some(modifiers) = method.child(0)
339 && matches!(modifiers.kind_id().into(), Modifiers)
340 && modifiers.first_child(|id| id == Public).is_some()
341 {
342 stats.class_npm += 1;
343 }
344 }
345 }
346 InterfaceBody => {
347 stats.interface_nm += direct_child_funcs::<Self>(node).count();
348 stats.interface_npm = stats.interface_nm;
349 }
350 AnnotationTypeBody => {
351 stats.interface_nm += node
352 .children()
353 .filter(|n| {
354 matches!(n.kind_id().into(), AnnotationTypeElementDeclaration)
355 })
356 .count();
357 stats.interface_npm = stats.interface_nm;
358 }
359 _ => {}
360 }
361 }
362 }
363 };
364}
365
366macro_rules! ts_npm_compute {
394 ($lang:ident) => {
395 fn compute<'a>(
396 node: &Node<'a>,
397 _code: &'a [u8],
398 _ancestors: Ancestors<'a, '_>,
399 stats: &mut Stats,
400 ) {
401 use $lang::*;
402
403 match node.kind_id().into() {
404 ClassBody => {
405 for member in node.children() {
406 match member.kind_id().into() {
407 MethodDefinition | AbstractMethodSignature => {
408 stats.class_nm += 1;
409 if ts_member_is_public!($lang, member) {
410 stats.class_npm += 1;
411 }
412 }
413 PublicFieldDefinition
416 if member
417 .first_child(|id| {
418 id == $lang::ArrowFunction
419 || id == $lang::FunctionExpression
420 })
421 .is_some() =>
422 {
423 stats.class_nm += 1;
424 if ts_member_is_public!($lang, member) {
425 stats.class_npm += 1;
426 }
427 }
428 _ => {}
429 }
430 }
431 }
432 InterfaceBody => {
433 let count = node
434 .children()
435 .filter(|c| {
436 matches!(
437 c.kind_id().into(),
438 MethodSignature | AbstractMethodSignature | ConstructSignature
439 )
440 })
441 .count();
442 stats.interface_nm += count;
443 stats.interface_npm = stats.interface_nm;
444 }
445 _ => {}
446 }
447 }
448 };
449}
450
451macro_rules! js_npm_compute {
468 ($lang:ident) => {
469 fn compute<'a>(
470 node: &Node<'a>,
471 _code: &'a [u8],
472 _ancestors: Ancestors<'a, '_>,
473 stats: &mut Stats,
474 ) {
475 use $lang::*;
476
477 if !matches!(node.kind_id().into(), ClassBody) {
478 return;
479 }
480
481 for member in node.children() {
482 match member.kind_id().into() {
483 MethodDefinition => {
484 stats.class_nm += 1;
485 stats.class_npm += 1;
486 }
487 FieldDefinition
488 if member
489 .first_child(|id| {
490 id == $lang::ArrowFunction || id == $lang::FunctionExpression
491 })
492 .is_some() =>
493 {
494 stats.class_nm += 1;
495 stats.class_npm += 1;
496 }
497 _ => {}
498 }
499 }
500 }
501 };
502}
503
504mod cpp;
509mod csharp;
510mod elixir;
511mod go;
512mod groovy;
513mod java;
514mod javascript;
515mod kotlin;
516mod mozcpp;
517mod mozjs;
518mod objc;
519mod php;
520mod python;
521mod ruby;
522mod rust;
523mod tsx;
524mod typescript;
525
526implement_metric_trait!(
532 Npm,
533 CCode,
534 PreprocCode,
535 CcommentCode,
536 PerlCode,
537 BashCode,
538 LuaCode,
539 TclCode,
540 IrulesCode
541);
542
543#[cfg(test)]
544#[allow(
545 clippy::float_cmp,
546 clippy::cast_precision_loss,
547 clippy::cast_possible_truncation,
548 clippy::cast_sign_loss,
549 clippy::similar_names,
550 clippy::doc_markdown,
551 clippy::needless_raw_string_hashes,
552 clippy::too_many_lines
553)]
554mod tests {
555 use crate::test_support::{
556 assert_child_space_kind, check_func_space_only_shim, check_metrics_only_shim, child_space,
557 };
558
559 use super::*;
560
561 check_metrics_only_shim!(check_metrics, Npm);
562 check_func_space_only_shim!(check_func_space, Npm);
563
564 #[test]
565 fn java_constructors() {
566 check_metrics::<JavaParser>(
567 "class X {
568 X() {}
569 private X(int a) {}
570 protected X(int a, int b) {}
571 public X(int a, int b, int c) {} // +1
572 }",
573 "foo.java",
574 |metric| {
575 insta::assert_json_snapshot!(
576 metric.npm,
577 @r#"
578 {
579 "class_npm_sum": 1,
580 "interface_npm_sum": 0,
581 "class_methods": 4,
582 "interface_methods": 0,
583 "class_coa": 0.25,
584 "interface_coa": 0.0,
585 "total": 1,
586 "total_methods": 4,
587 "coa": 0.25
588 }
589 "#
590 );
591 },
592 );
593 }
594
595 #[test]
596 fn groovy_no_methods() {
597 check_metrics::<GroovyParser>("class A { int x = 1 }", "foo.groovy", |metric| {
598 assert_eq!(metric.npm.total_nm(), 0);
599 });
600 }
601
602 #[test]
603 fn groovy_public_methods() {
604 check_metrics::<GroovyParser>(
605 "class A {
606 public void m1() {}
607 public int m2() { return 0 }
608 private void m3() {}
609 }",
610 "foo.groovy",
611 |metric| {
612 assert_eq!(metric.npm.class_nm_sum(), 3);
613 assert_eq!(metric.npm.class_npm_sum(), 2);
614 },
615 );
616 }
617
618 #[test]
619 fn groovy_interface_methods_implicitly_public() {
620 check_func_space::<GroovyParser, _>(
627 "interface I {
628 void a()
629 int b()
630 }",
631 "foo.groovy",
632 |func_space| {
633 let metric = &func_space.metrics;
634 assert_eq!(metric.npm.interface_nm_sum(), 2);
636 assert_eq!(metric.npm.interface_npm_sum(), 2);
637 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
638 },
639 );
640 }
641
642 #[test]
645 fn groovy_enum_counts_methods() {
646 check_metrics::<GroovyParser>(
647 "enum Status {
648 ACTIVE, INACTIVE;
649 public int code() { return 0 }
650 private void reset() {}
651 }",
652 "foo.groovy",
653 |metric| {
654 assert_eq!(metric.npm.class_nm_sum(), 2);
655 assert_eq!(metric.npm.class_npm_sum(), 1);
656 },
657 );
658 }
659
660 #[test]
661 #[ignore = "dekobon Groovy grammar v1 does not support annotation type elements with `default` values; the trailing `default \"\"`/`default 0` make the body fail to parse"]
662 fn groovy_annotation_type_counts_elements() {
663 check_func_space::<GroovyParser, _>(
674 "public @interface Marker {
675 String value() default \"\";
676 int priority() default 0;
677 }",
678 "foo.groovy",
679 |func_space| {
680 assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2);
681 assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2);
682 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
683 },
684 );
685 }
686
687 #[test]
688 fn groovy_constructors() {
689 check_metrics::<GroovyParser>(
690 "class X {
691 X() {}
692 private X(int a) {}
693 protected X(int a, int b) {}
694 public X(int a, int b, int c) {}
695 }",
696 "foo.groovy",
697 |metric| {
698 assert_eq!(metric.npm.class_nm_sum(), 4);
700 assert_eq!(metric.npm.class_npm_sum(), 1);
701 },
702 );
703 }
704
705 #[test]
706 fn groovy_no_methods_in_unit_scope() {
707 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
708 assert_eq!(metric.npm.total_nm(), 0);
709 });
710 }
711
712 #[test]
713 fn groovy_multiple_classes_methods() {
714 check_metrics::<GroovyParser>(
715 "class A { public void a() {} }
716 class B { public void b() {} }",
717 "foo.groovy",
718 |metric| {
719 assert_eq!(metric.npm.class_nm_sum(), 2);
720 assert_eq!(metric.npm.class_npm_sum(), 2);
721 },
722 );
723 }
724
725 #[test]
726 fn groovy_methods_returning_primitive_types() {
727 check_metrics::<GroovyParser>(
731 "class X {
732 public byte a() {}
733 public int b() {}
734 public double c() {}
735 public boolean d() {}
736 byte e() {}
737 int f() {}
738 }",
739 "foo.groovy",
740 |metric| {
741 assert_eq!(metric.npm.class_nm_sum(), 6);
743 assert_eq!(metric.npm.class_npm_sum(), 4);
744 },
745 );
746 }
747
748 #[test]
749 fn groovy_methods_with_generic_types() {
750 check_metrics::<GroovyParser>(
752 "class X {
753 public List<String> a() {}
754 public Map<String, Integer> b() {}
755 List<Integer> c() {}
756 }",
757 "foo.groovy",
758 |metric| {
759 assert_eq!(metric.npm.class_nm_sum(), 3);
760 assert_eq!(metric.npm.class_npm_sum(), 2);
761 },
762 );
763 }
764
765 #[test]
766 fn groovy_method_modifiers() {
767 check_metrics::<GroovyParser>(
771 "abstract class X {
772 public static void a() {}
773 static public void b() {}
774 public final void c() {}
775 final public void d() {}
776 protected static void e() {}
777 static protected void f() {}
778 abstract public void g()
779 abstract void h()
780 }",
781 "foo.groovy",
782 |metric| {
783 assert_eq!(metric.npm.class_nm_sum(), 8);
785 assert_eq!(metric.npm.class_npm_sum(), 5);
786 },
787 );
788 }
789
790 #[test]
791 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies"]
792 fn groovy_nested_inner_classes() {
793 check_metrics::<GroovyParser>(
796 "class X {
797 public void a() {}
798 class Y {
799 public void b() {}
800 class Z {
801 public void c() {}
802 }
803 }
804 }",
805 "foo.groovy",
806 |metric| {
807 assert_eq!(metric.npm.class_nm_sum(), 3);
809 assert_eq!(metric.npm.class_npm_sum(), 3);
810 },
811 );
812 }
813
814 #[test]
815 #[ignore = "dekobon Groovy grammar v1 does not yet support anonymous inner classes (`new T() { … }`)"]
816 fn groovy_anonymous_inner_class() {
817 check_metrics::<GroovyParser>(
820 "class X {
821 public Runnable r = new Runnable() {
822 public void run() {}
823 void helper() {}
824 }
825 }",
826 "foo.groovy",
827 |metric| {
828 assert_eq!(metric.npm.class_nm_sum(), 2);
831 assert_eq!(metric.npm.class_npm_sum(), 1);
832 },
833 );
834 }
835
836 #[test]
837 fn groovy_interfaces_and_class() {
838 check_func_space::<GroovyParser, _>(
844 "interface X {
845 void a()
846 }
847 interface Y extends X {
848 void b()
849 void c()
850 }
851 class Z implements Y {
852 public void a() {}
853 public void b() {}
854 public void c() {}
855 void d() {}
856 void e() {}
857 }",
858 "foo.groovy",
859 |func_space| {
860 let metric = &func_space.metrics;
861 assert_eq!(metric.npm.interface_nm_sum(), 3);
863 assert_eq!(metric.npm.interface_npm_sum(), 3);
864 assert_eq!(metric.npm.class_nm_sum(), 5);
867 assert_eq!(metric.npm.class_npm_sum(), 3);
868 assert_child_space_kind(&func_space, "X", SpaceKind::Interface);
869 assert_child_space_kind(&func_space, "Y", SpaceKind::Interface);
870 assert_child_space_kind(&func_space, "Z", SpaceKind::Class);
871 },
872 );
873 }
874
875 #[test]
876 fn java_methods_returning_primitive_types() {
877 check_metrics::<JavaParser>(
878 "class X {
879 public byte a() {} // +1
880 public short b() {} // +1
881 public int c() {} // +1
882 public long d() {} // +1
883 public float e() {} // +1
884 public double f() {} // +1
885 public boolean g() {} // +1
886 public char h() {} // +1
887 byte i() {}
888 short j() {}
889 int k() {}
890 long l() {}
891 float m() {}
892 double n() {}
893 boolean o() {}
894 char p() {}
895 }",
896 "foo.java",
897 |metric| {
898 insta::assert_json_snapshot!(
899 metric.npm,
900 @r#"
901 {
902 "class_npm_sum": 8,
903 "interface_npm_sum": 0,
904 "class_methods": 16,
905 "interface_methods": 0,
906 "class_coa": 0.5,
907 "interface_coa": 0.0,
908 "total": 8,
909 "total_methods": 16,
910 "coa": 0.5
911 }
912 "#
913 );
914 },
915 );
916 }
917
918 #[test]
919 fn java_methods_returning_arrays() {
920 check_metrics::<JavaParser>(
921 "class X {
922 public byte[] a() {} // +1
923 public short[] b() {} // +1
924 public int[] c() {} // +1
925 public long[] d() {} // +1
926 public float[] e() {} // +1
927 public double[] f() {} // +1
928 public boolean[] g() {} // +1
929 public char[] h() {} // +1
930 byte[] i() {}
931 short[] j() {}
932 int[] k() {}
933 long[] l() {}
934 float[] m() {}
935 double[] n() {}
936 boolean[] o() {}
937 char[] p() {}
938 }",
939 "foo.java",
940 |metric| {
941 insta::assert_json_snapshot!(
942 metric.npm,
943 @r#"
944 {
945 "class_npm_sum": 8,
946 "interface_npm_sum": 0,
947 "class_methods": 16,
948 "interface_methods": 0,
949 "class_coa": 0.5,
950 "interface_coa": 0.0,
951 "total": 8,
952 "total_methods": 16,
953 "coa": 0.5
954 }
955 "#
956 );
957 },
958 );
959 }
960
961 #[test]
962 fn java_methods_returning_objects() {
963 check_metrics::<JavaParser>(
964 "class X {
965 public Integer[] a() {} // +1
966 public Integer b() {} // +1
967 public String[] c() {} // +1
968 public String d() {} // +1
969 public Y[] e() {} // +1
970 public Y f() {} // +1
971 Integer[] g() {}
972 Integer h() {}
973 String[] i() {}
974 String j() {}
975 Y[] k() {}
976 Y l() {}
977 }",
978 "foo.java",
979 |metric| {
980 insta::assert_json_snapshot!(
981 metric.npm,
982 @r#"
983 {
984 "class_npm_sum": 6,
985 "interface_npm_sum": 0,
986 "class_methods": 12,
987 "interface_methods": 0,
988 "class_coa": 0.5,
989 "interface_coa": 0.0,
990 "total": 6,
991 "total_methods": 12,
992 "coa": 0.5
993 }
994 "#
995 );
996 },
997 );
998 }
999
1000 #[test]
1001 fn java_methods_with_generic_types() {
1002 check_metrics::<JavaParser>(
1003 "class X {
1004 public <T, S extends T> void a(T x, S y) {} // +1
1005 public <T, S> int b(T x, S y) {} // +1
1006 public <T> boolean c(T x) {} // +1
1007 public <T> ArrayList<T> d() {} // +1
1008 public Y<String> e() {} // +1
1009 <T, S extends T> void f(T x, S y) {}
1010 <T, S> int g(T x, S y) {}
1011 <T> boolean h(T x) {}
1012 <T> ArrayList<T> i() {}
1013 Y<String> j() {}
1014 }",
1015 "foo.java",
1016 |metric| {
1017 insta::assert_json_snapshot!(
1018 metric.npm,
1019 @r#"
1020 {
1021 "class_npm_sum": 5,
1022 "interface_npm_sum": 0,
1023 "class_methods": 10,
1024 "interface_methods": 0,
1025 "class_coa": 0.5,
1026 "interface_coa": 0.0,
1027 "total": 5,
1028 "total_methods": 10,
1029 "coa": 0.5
1030 }
1031 "#
1032 );
1033 },
1034 );
1035 }
1036
1037 #[test]
1038 fn java_method_modifiers() {
1039 check_metrics::<JavaParser>(
1040 "abstract class X {
1041 public static final synchronized strictfp void a() {} // +1
1042 static public final synchronized strictfp void b() {} // +1
1043 static final public synchronized strictfp void c() {} // +1
1044 static final synchronized public strictfp void d() {} // +1
1045 static final synchronized strictfp public void e() {} // +1
1046 protected static final synchronized native void f();
1047 static protected final synchronized native void g();
1048 static final protected synchronized native void h();
1049 static final synchronized protected native void i();
1050 static final synchronized native protected void j();
1051 abstract public void k(); // +1
1052 abstract void l();
1053 }",
1054 "foo.java",
1055 |metric| {
1056 insta::assert_json_snapshot!(
1057 metric.npm,
1058 @r#"
1059 {
1060 "class_npm_sum": 6,
1061 "interface_npm_sum": 0,
1062 "class_methods": 12,
1063 "interface_methods": 0,
1064 "class_coa": 0.5,
1065 "interface_coa": 0.0,
1066 "total": 6,
1067 "total_methods": 12,
1068 "coa": 0.5
1069 }
1070 "#
1071 );
1072 },
1073 );
1074 }
1075
1076 #[test]
1077 fn java_classes() {
1078 check_metrics::<JavaParser>(
1079 "class X {
1080 public void a() {} // +1
1081 public void b() {} // +1
1082 private void c() {}
1083 }
1084 class Y {
1085 private void d() {}
1086 private void e() {}
1087 public void f() {} // +1
1088 }",
1089 "foo.java",
1090 |metric| {
1091 insta::assert_json_snapshot!(
1092 metric.npm,
1093 @r#"
1094 {
1095 "class_npm_sum": 3,
1096 "interface_npm_sum": 0,
1097 "class_methods": 6,
1098 "interface_methods": 0,
1099 "class_coa": 0.5,
1100 "interface_coa": 0.0,
1101 "total": 3,
1102 "total_methods": 6,
1103 "coa": 0.5
1104 }
1105 "#
1106 );
1107 },
1108 );
1109 }
1110
1111 #[test]
1112 fn java_nested_inner_classes() {
1113 check_metrics::<JavaParser>(
1114 "class X {
1115 public void a() {} // +1
1116 class Y {
1117 public void b() {} // +1
1118 class Z {
1119 public void c() {} // +1
1120 }
1121 }
1122 }",
1123 "foo.java",
1124 |metric| {
1125 insta::assert_json_snapshot!(
1126 metric.npm,
1127 @r#"
1128 {
1129 "class_npm_sum": 3,
1130 "interface_npm_sum": 0,
1131 "class_methods": 3,
1132 "interface_methods": 0,
1133 "class_coa": 1.0,
1134 "interface_coa": 0.0,
1135 "total": 3,
1136 "total_methods": 3,
1137 "coa": 1.0
1138 }
1139 "#
1140 );
1141 },
1142 );
1143 }
1144
1145 #[test]
1146 fn java_local_inner_classes() {
1147 check_metrics::<JavaParser>(
1148 "class X {
1149 public void a() { // +1
1150 class Y {
1151 public void b() { // +1
1152 class Z {
1153 public void c() {} // +1
1154 }
1155 }
1156 }
1157 }
1158 }",
1159 "foo.java",
1160 |metric| {
1161 insta::assert_json_snapshot!(
1162 metric.npm,
1163 @r#"
1164 {
1165 "class_npm_sum": 3,
1166 "interface_npm_sum": 0,
1167 "class_methods": 3,
1168 "interface_methods": 0,
1169 "class_coa": 1.0,
1170 "interface_coa": 0.0,
1171 "total": 3,
1172 "total_methods": 3,
1173 "coa": 1.0
1174 }
1175 "#
1176 );
1177 },
1178 );
1179 }
1180
1181 #[test]
1182 fn java_anonymous_inner_classes() {
1183 check_metrics::<JavaParser>(
1184 "abstract class X {
1185 public abstract void a(); // +1
1186 }
1187 abstract class Y {
1188 abstract void b();
1189 }
1190 class Z {
1191 public void c(){ // +1
1192 X x = new X() {
1193 @Override
1194 public void a() {} // +1
1195 };
1196 Y y = new Y() {
1197 @Override
1198 void b() {}
1199 };
1200 }
1201 }",
1202 "foo.java",
1203 |metric| {
1204 insta::assert_json_snapshot!(
1205 metric.npm,
1206 @r#"
1207 {
1208 "class_npm_sum": 3,
1209 "interface_npm_sum": 0,
1210 "class_methods": 5,
1211 "interface_methods": 0,
1212 "class_coa": 0.6,
1213 "interface_coa": 0.0,
1214 "total": 3,
1215 "total_methods": 5,
1216 "coa": 0.6
1217 }
1218 "#
1219 );
1220 },
1221 );
1222 }
1223
1224 #[test]
1225 fn java_interface() {
1226 check_metrics::<JavaParser>(
1227 "interface X {
1228 public int a(); // +1
1229 boolean b(); // +1
1230 void c(); // +1
1231 }",
1232 "foo.java",
1233 |metric| {
1234 insta::assert_json_snapshot!(
1235 metric.npm,
1236 @r#"
1237 {
1238 "class_npm_sum": 0,
1239 "interface_npm_sum": 3,
1240 "class_methods": 0,
1241 "interface_methods": 3,
1242 "class_coa": 0.0,
1243 "interface_coa": 1.0,
1244 "total": 3,
1245 "total_methods": 3,
1246 "coa": 1.0
1247 }
1248 "#
1249 );
1250 },
1251 );
1252 }
1253
1254 #[test]
1258 fn java_enum_counts_methods() {
1259 check_metrics::<JavaParser>(
1260 "enum Status {
1261 ACTIVE, INACTIVE;
1262 public int code() { return 0; } // +1 public
1263 private void reset() {} // not public
1264 }",
1265 "foo.java",
1266 |metric| {
1267 assert_eq!(metric.npm.class_nm_sum(), 2);
1268 assert_eq!(metric.npm.class_npm_sum(), 1);
1269 },
1270 );
1271 }
1272
1273 #[test]
1276 fn java_record_counts_methods() {
1277 check_metrics::<JavaParser>(
1278 "record Point(int x, int y) {
1279 public int sum() { return x + y; }
1280 public Point() { this(0, 0); }
1281 }",
1282 "foo.java",
1283 |metric| {
1284 assert_eq!(metric.npm.class_nm_sum(), 2);
1289 assert_eq!(metric.npm.class_npm_sum(), 2);
1290 },
1291 );
1292 }
1293
1294 #[test]
1305 fn java_record_counts_a_compact_constructor_as_a_method() {
1306 check_metrics::<JavaParser>(
1307 "record R(int a, int b) {
1308 public R { }
1309 private int half() { return a / 2; }
1310 public int sum() { return a + b; }
1311 }",
1312 "foo.java",
1313 |metric| {
1314 assert_eq!(metric.npm.class_nm_sum(), 3);
1319 assert_eq!(metric.npm.class_npm_sum(), 2);
1320 },
1321 );
1322 }
1323
1324 #[test]
1325 fn java_annotation_type_counts_elements() {
1326 check_func_space::<JavaParser, _>(
1336 "@interface Marker {
1337 String value() default \"\";
1338 int priority() default 0;
1339 }",
1340 "foo.java",
1341 |func_space| {
1342 assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2);
1343 assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2);
1344 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1345 },
1346 );
1347 }
1348
1349 #[test]
1350 fn java_interfaces_and_class() {
1351 check_metrics::<JavaParser>(
1352 "interface X {
1353 void a(); // +1
1354 }
1355 interface Y extends X {
1356 void b(); // +1
1357 void c(); // +1
1358 }
1359 class Z implements Y {
1360 @Override
1361 public void a() {} // +1
1362 @Override
1363 public void b() {} // +1
1364 @Override
1365 public void c() {} // +1
1366 void d() {}
1367 void e() {}
1368 }",
1369 "foo.java",
1370 |metric| {
1371 insta::assert_json_snapshot!(
1372 metric.npm,
1373 @r#"
1374 {
1375 "class_npm_sum": 3,
1376 "interface_npm_sum": 3,
1377 "class_methods": 5,
1378 "interface_methods": 3,
1379 "class_coa": 0.6,
1380 "interface_coa": 1.0,
1381 "total": 6,
1382 "total_methods": 8,
1383 "coa": 0.75
1384 }
1385 "#
1386 );
1387 },
1388 );
1389 }
1390
1391 #[test]
1392 fn csharp_constructors() {
1393 check_metrics::<CsharpParser>(
1394 "class A {
1395 public A() {}
1396 public A(int x) {}
1397 A(int x, int y) {}
1398 }",
1399 "foo.cs",
1400 |metric| insta::assert_json_snapshot!(metric.npm),
1401 );
1402 }
1403
1404 #[test]
1405 fn csharp_methods_returning_primitive_types() {
1406 check_metrics::<CsharpParser>(
1407 "class A {
1408 public int M1() { return 1; }
1409 public bool M2() { return true; }
1410 public double M3() { return 0.0; }
1411 int M4() { return 0; }
1412 }",
1413 "foo.cs",
1414 |metric| insta::assert_json_snapshot!(metric.npm),
1415 );
1416 }
1417
1418 #[test]
1419 fn csharp_methods_returning_arrays() {
1420 check_metrics::<CsharpParser>(
1421 "class A {
1422 public int[] M1() { return new int[0]; }
1423 public string[] M2() { return new string[0]; }
1424 int[] M3() { return new int[0]; }
1425 }",
1426 "foo.cs",
1427 |metric| insta::assert_json_snapshot!(metric.npm),
1428 );
1429 }
1430
1431 #[test]
1432 fn csharp_methods_returning_objects() {
1433 check_metrics::<CsharpParser>(
1434 "class Point { }
1435 class A {
1436 public Point M1() { return new Point(); }
1437 public string M2() { return \"\"; }
1438 Point M3() { return new Point(); }
1439 }",
1440 "foo.cs",
1441 |metric| insta::assert_json_snapshot!(metric.npm),
1442 );
1443 }
1444
1445 #[test]
1446 fn csharp_methods_with_generic_types() {
1447 check_metrics::<CsharpParser>(
1448 "class A {
1449 public System.Collections.Generic.List<int> M1() { return null; }
1450 public System.Collections.Generic.Dictionary<string, int> M2() { return null; }
1451 System.Collections.Generic.List<string> M3() { return null; }
1452 }",
1453 "foo.cs",
1454 |metric| insta::assert_json_snapshot!(metric.npm),
1455 );
1456 }
1457
1458 #[test]
1459 fn csharp_method_modifiers() {
1460 check_metrics::<CsharpParser>(
1461 "class A {
1462 public void M1() {}
1463 private void M2() {}
1464 protected void M3() {}
1465 internal void M4() {}
1466 public static void M5() {}
1467 public virtual void M6() {}
1468 }",
1469 "foo.cs",
1470 |metric| insta::assert_json_snapshot!(metric.npm),
1471 );
1472 }
1473
1474 #[test]
1475 fn csharp_classes() {
1476 check_metrics::<CsharpParser>(
1477 "class A {
1478 public void M1() {}
1479 public void M2() {}
1480 void M3() {}
1481 }
1482 class B {
1483 public int N() { return 0; }
1484 int Hidden() { return 0; }
1485 }",
1486 "foo.cs",
1487 |metric| insta::assert_json_snapshot!(metric.npm),
1488 );
1489 }
1490
1491 #[test]
1492 fn csharp_nested_inner_classes() {
1493 check_metrics::<CsharpParser>(
1494 "class Outer {
1495 public void M() {}
1496 void Hidden() {}
1497 public class Inner {
1498 public void N() {}
1499 void HiddenN() {}
1500 }
1501 }",
1502 "foo.cs",
1503 |metric| insta::assert_json_snapshot!(metric.npm),
1504 );
1505 }
1506
1507 #[test]
1508 fn csharp_property_accessors() {
1509 check_metrics::<CsharpParser>(
1514 "class A {
1515 int _w;
1516 public int X { get; set; }
1517 public int Y { get; }
1518 public int Z { get; init; }
1519 public int W => _w;
1520 int Hidden { get; set; }
1521 }",
1522 "foo.cs",
1523 |metric| insta::assert_json_snapshot!(metric.npm),
1524 );
1525 }
1526
1527 #[test]
1528 fn csharp_narrowed_accessor_visibility() {
1529 check_metrics::<CsharpParser>(
1542 "class A {
1543 public int X { get; private set; }
1544 public int this[int i] { get; protected set; }
1545 public int Y { get; set; }
1546 public int W { get; }
1547 public int Z => 0;
1548 int P { get; set; }
1549 }",
1550 "foo.cs",
1551 |metric| {
1552 assert_eq!(metric.npm.class_nm_sum(), 10, "all accessors count as nm");
1553 assert_eq!(
1554 metric.npm.class_npm_sum(),
1555 6,
1556 "narrowed private/protected accessors are not public methods"
1557 );
1558 insta::assert_json_snapshot!(metric.npm);
1559 },
1560 );
1561 }
1562
1563 #[test]
1564 fn csharp_local_functions() {
1565 check_metrics::<CsharpParser>(
1572 "class A {
1573 public void Outer() {
1574 void Local() {}
1575 Local();
1576 }
1577 private void Hidden() {}
1578 }",
1579 "foo.cs",
1580 |metric| {
1581 assert_eq!(metric.npm.class_nm_sum(), 2, "Local must not leak");
1582 assert_eq!(metric.npm.class_npm_sum(), 1, "only Outer is public");
1583 insta::assert_json_snapshot!(metric.npm);
1584 },
1585 );
1586 }
1587
1588 #[test]
1589 fn csharp_interface() {
1590 check_metrics::<CsharpParser>(
1592 "interface I {
1593 int M1();
1594 bool M2();
1595 int X { get; set; }
1596 }",
1597 "foo.cs",
1598 |metric| insta::assert_json_snapshot!(metric.npm),
1599 );
1600 }
1601
1602 #[test]
1603 fn csharp_interfaces_and_class() {
1604 check_metrics::<CsharpParser>(
1605 "interface I1 { int M1(); }
1606 interface I2 { bool M2(); float M3(); }
1607 class A {
1608 public void M() {}
1609 void Hidden() {}
1610 }",
1611 "foo.cs",
1612 |metric| insta::assert_json_snapshot!(metric.npm),
1613 );
1614 }
1615
1616 #[test]
1617 fn php_no_class_methods() {
1618 check_metrics::<PhpParser>(
1619 "<?php class A { public int $x = 0; }",
1620 "foo.php",
1621 |metric| insta::assert_json_snapshot!(metric.npm),
1622 );
1623 }
1624
1625 #[test]
1626 fn php_one_public_method() {
1627 check_metrics::<PhpParser>(
1628 "<?php class A { public function f(): void {} }",
1629 "foo.php",
1630 |metric| insta::assert_json_snapshot!(metric.npm),
1631 );
1632 }
1633
1634 #[test]
1635 fn php_one_private_method() {
1636 check_metrics::<PhpParser>(
1637 "<?php class A { private function f(): void {} }",
1638 "foo.php",
1639 |metric| insta::assert_json_snapshot!(metric.npm),
1640 );
1641 }
1642
1643 #[test]
1644 fn php_one_protected_method() {
1645 check_metrics::<PhpParser>(
1646 "<?php class A { protected function f(): void {} }",
1647 "foo.php",
1648 |metric| insta::assert_json_snapshot!(metric.npm),
1649 );
1650 }
1651
1652 #[test]
1653 fn php_mixed_visibility_methods() {
1654 check_metrics::<PhpParser>(
1655 "<?php
1656 class A {
1657 public function a(): void {}
1658 public function b(): void {}
1659 private function c(): void {}
1660 protected function d(): void {}
1661 }",
1662 "foo.php",
1663 |metric| insta::assert_json_snapshot!(metric.npm),
1664 );
1665 }
1666
1667 #[test]
1668 fn php_static_public_method() {
1669 check_metrics::<PhpParser>(
1670 "<?php class A { public static function f(): void {} }",
1671 "foo.php",
1672 |metric| insta::assert_json_snapshot!(metric.npm),
1673 );
1674 }
1675
1676 #[test]
1677 fn php_abstract_method() {
1678 check_metrics::<PhpParser>(
1679 "<?php abstract class A { abstract public function f(): void; }",
1680 "foo.php",
1681 |metric| insta::assert_json_snapshot!(metric.npm),
1682 );
1683 }
1684
1685 #[test]
1686 fn php_final_public_method() {
1687 check_metrics::<PhpParser>(
1688 "<?php class A { final public function f(): void {} }",
1689 "foo.php",
1690 |metric| insta::assert_json_snapshot!(metric.npm),
1691 );
1692 }
1693
1694 #[test]
1695 fn php_interface_methods() {
1696 check_metrics::<PhpParser>(
1698 "<?php
1699 interface I {
1700 public function a(): void;
1701 public function b(): int;
1702 }",
1703 "foo.php",
1704 |metric| insta::assert_json_snapshot!(metric.npm),
1705 );
1706 }
1707
1708 #[test]
1709 fn php_enum_methods() {
1710 check_metrics::<PhpParser>(
1712 "<?php
1713 enum Color {
1714 case Red;
1715 case Green;
1716 public function label(): string {
1717 return match ($this) {
1718 Color::Red => 'r',
1719 Color::Green => 'g',
1720 };
1721 }
1722 }",
1723 "foo.php",
1724 |metric| insta::assert_json_snapshot!(metric.npm),
1725 );
1726 }
1727
1728 #[test]
1729 fn php_trait_methods() {
1730 check_metrics::<PhpParser>(
1731 "<?php
1732 trait T {
1733 public function a(): void {}
1734 private function b(): void {}
1735 }",
1736 "foo.php",
1737 |metric| insta::assert_json_snapshot!(metric.npm),
1738 );
1739 }
1740
1741 #[test]
1742 fn php_no_explicit_visibility_method_excluded() {
1743 check_metrics::<PhpParser>(
1746 "<?php class A { function f(): void {} }",
1747 "foo.php",
1748 |metric| insta::assert_json_snapshot!(metric.npm),
1749 );
1750 }
1751
1752 #[test]
1755 fn kotlin_empty_class_no_methods() {
1756 check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
1757 assert_eq!(metric.npm.class_npm_sum(), 0);
1758 assert_eq!(metric.npm.class_nm_sum(), 0);
1759 assert_eq!(metric.npm.interface_nm_sum(), 0);
1760 insta::assert_json_snapshot!(metric.npm);
1761 });
1762 }
1763
1764 #[test]
1765 fn kotlin_public_methods_default() {
1766 check_metrics::<KotlinParser>(
1768 "class C {
1769 fun a() {}
1770 fun b(): Int = 0
1771 fun c(x: Int): Int = x
1772 }",
1773 "foo.kt",
1774 |metric| {
1775 assert_eq!(metric.npm.class_npm_sum(), 3);
1776 assert_eq!(metric.npm.class_nm_sum(), 3);
1777 insta::assert_json_snapshot!(metric.npm);
1778 },
1779 );
1780 }
1781
1782 #[test]
1783 fn kotlin_private_method() {
1784 check_metrics::<KotlinParser>(
1785 "class C {
1786 fun a() {} // public
1787 private fun b() {} // private
1788 fun c() {} // public
1789 }",
1790 "foo.kt",
1791 |metric| {
1792 assert_eq!(metric.npm.class_npm_sum(), 2);
1793 assert_eq!(metric.npm.class_nm_sum(), 3);
1794 insta::assert_json_snapshot!(metric.npm);
1795 },
1796 );
1797 }
1798
1799 #[test]
1800 fn kotlin_protected_internal_methods() {
1801 check_metrics::<KotlinParser>(
1802 "open class C {
1803 protected fun a() {}
1804 internal fun b() {}
1805 public fun c() {}
1806 }",
1807 "foo.kt",
1808 |metric| {
1809 assert_eq!(metric.npm.class_npm_sum(), 1);
1810 assert_eq!(metric.npm.class_nm_sum(), 3);
1811 insta::assert_json_snapshot!(metric.npm);
1812 },
1813 );
1814 }
1815
1816 #[test]
1817 fn kotlin_secondary_constructor_counts() {
1818 check_metrics::<KotlinParser>(
1821 "class C {
1822 private var a: Int = 0
1823 constructor(n: Int) { a = n }
1824 constructor(n: Int, m: Int) { a = n + m }
1825 fun get(): Int = a
1826 }",
1827 "foo.kt",
1828 |metric| {
1829 assert_eq!(metric.npm.class_npm_sum(), 3);
1830 assert_eq!(metric.npm.class_nm_sum(), 3);
1831 insta::assert_json_snapshot!(metric.npm);
1832 },
1833 );
1834 }
1835
1836 #[test]
1837 fn kotlin_companion_object_methods() {
1838 check_metrics::<KotlinParser>(
1841 "class Holder {
1842 fun memberFn() {}
1843 companion object {
1844 fun staticFn() {}
1845 private fun secret() {}
1846 }
1847 }",
1848 "foo.kt",
1849 |metric| {
1850 assert_eq!(metric.npm.class_npm_sum(), 2);
1851 assert_eq!(metric.npm.class_nm_sum(), 3);
1852 insta::assert_json_snapshot!(metric.npm);
1853 },
1854 );
1855 }
1856
1857 #[test]
1858 fn kotlin_data_class_methods() {
1859 check_metrics::<KotlinParser>(
1862 "data class Point(val x: Int, val y: Int) {
1863 fun manhattan(): Int = x + y
1864 private fun internal_(): Int = 0
1865 }",
1866 "foo.kt",
1867 |metric| {
1868 assert_eq!(metric.npm.class_npm_sum(), 1);
1869 assert_eq!(metric.npm.class_nm_sum(), 2);
1870 insta::assert_json_snapshot!(metric.npm);
1871 },
1872 );
1873 }
1874
1875 #[test]
1876 fn kotlin_object_singleton_methods() {
1877 check_metrics::<KotlinParser>(
1878 "object Util {
1879 fun add(a: Int, b: Int): Int = a + b
1880 private fun helper(): Int = 0
1881 }",
1882 "foo.kt",
1883 |metric| {
1884 assert_eq!(metric.npm.class_npm_sum(), 1);
1885 assert_eq!(metric.npm.class_nm_sum(), 2);
1886 insta::assert_json_snapshot!(metric.npm);
1887 },
1888 );
1889 }
1890
1891 #[test]
1892 fn kotlin_interface_methods() {
1893 check_func_space::<KotlinParser, _>(
1894 "interface I {
1895 fun work(): Int
1896 fun describe(): String
1897 }",
1898 "foo.kt",
1899 |func_space| {
1900 let metric = &func_space.metrics;
1901 assert_eq!(metric.npm.interface_npm_sum(), 2);
1902 assert_eq!(metric.npm.interface_nm_sum(), 2);
1903 assert_eq!(metric.npm.class_nm_sum(), 0);
1904 insta::assert_json_snapshot!(metric.npm);
1905 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1906 },
1907 );
1908 }
1909
1910 #[test]
1911 fn kotlin_interface_with_default_method() {
1912 check_func_space::<KotlinParser, _>(
1913 "interface I {
1914 fun abs(n: Int): Int {
1915 return if (n < 0) -n else n
1916 }
1917 fun pure(): Int
1918 }",
1919 "foo.kt",
1920 |func_space| {
1921 let metric = &func_space.metrics;
1922 assert_eq!(metric.npm.interface_npm_sum(), 2);
1923 assert_eq!(metric.npm.interface_nm_sum(), 2);
1924 insta::assert_json_snapshot!(metric.npm);
1925 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1926 },
1927 );
1928 }
1929
1930 #[test]
1931 fn kotlin_override_fun_counts() {
1932 check_metrics::<KotlinParser>(
1933 "open class Base {
1934 open fun greet(): String = \"hi\"
1935 }
1936 class Sub : Base() {
1937 override fun greet(): String = \"yo\"
1938 private fun secret() {}
1939 }",
1940 "foo.kt",
1941 |metric| {
1942 assert_eq!(metric.npm.class_npm_sum(), 2);
1946 assert_eq!(metric.npm.class_nm_sum(), 3);
1947 insta::assert_json_snapshot!(metric.npm);
1948 },
1949 );
1950 }
1951
1952 #[test]
1953 fn kotlin_nested_class_methods() {
1954 check_metrics::<KotlinParser>(
1955 "class Outer {
1956 fun outerM() {}
1957 class Nested {
1958 fun nestedM() {}
1959 private fun nestedSecret() {}
1960 }
1961 }",
1962 "foo.kt",
1963 |metric| {
1964 assert_eq!(metric.npm.class_npm_sum(), 2);
1965 assert_eq!(metric.npm.class_nm_sum(), 3);
1966 insta::assert_json_snapshot!(metric.npm);
1967 },
1968 );
1969 }
1970
1971 #[test]
1972 fn kotlin_inner_class_methods() {
1973 check_metrics::<KotlinParser>(
1974 "class Outer {
1975 fun outerM() {}
1976 inner class Inner {
1977 fun innerM() {}
1978 }
1979 }",
1980 "foo.kt",
1981 |metric| {
1982 assert_eq!(metric.npm.class_npm_sum(), 2);
1983 assert_eq!(metric.npm.class_nm_sum(), 2);
1984 insta::assert_json_snapshot!(metric.npm);
1985 },
1986 );
1987 }
1988
1989 #[test]
1990 fn kotlin_top_level_function_excluded() {
1991 check_metrics::<KotlinParser>(
1993 "fun freeFn() {}
1994class C {
1995 fun m() {}
1996}",
1997 "foo.kt",
1998 |metric| {
1999 assert_eq!(metric.npm.class_npm_sum(), 1);
2000 assert_eq!(metric.npm.class_nm_sum(), 1);
2001 insta::assert_json_snapshot!(metric.npm);
2002 },
2003 );
2004 }
2005
2006 #[test]
2007 fn kotlin_extension_function_excluded() {
2008 check_metrics::<KotlinParser>(
2011 "fun List<Int>.sum2(): Int = this.size
2012class C {
2013 fun m() {}
2014}",
2015 "foo.kt",
2016 |metric| {
2017 assert_eq!(metric.npm.class_npm_sum(), 1);
2018 assert_eq!(metric.npm.class_nm_sum(), 1);
2019 insta::assert_json_snapshot!(metric.npm);
2020 },
2021 );
2022 }
2023
2024 #[test]
2025 fn kotlin_class_in_interface() {
2026 check_func_space::<KotlinParser, _>(
2031 "interface Outer {
2032 fun work(): Int
2033 class Helper {
2034 fun help() {}
2035 }
2036 }",
2037 "foo.kt",
2038 |func_space| {
2039 let metric = &func_space.metrics;
2040 assert_eq!(metric.npm.interface_npm_sum(), 1);
2041 assert_eq!(metric.npm.class_npm_sum(), 1);
2042 insta::assert_json_snapshot!(metric.npm);
2043 assert_child_space_kind(&func_space, "Outer", SpaceKind::Interface);
2044 let outer = func_space
2045 .spaces
2046 .iter()
2047 .find(|s| s.name.as_deref() == Some("Outer"))
2048 .expect("Outer FuncSpace");
2049 assert_child_space_kind(outer, "Helper", SpaceKind::Class);
2050 },
2051 );
2052 }
2053
2054 #[test]
2055 fn kotlin_interface_in_class() {
2056 check_func_space::<KotlinParser, _>(
2061 "class Outer {
2062 fun work() {}
2063 interface Sub {
2064 fun help(): Int
2065 }
2066 }",
2067 "foo.kt",
2068 |func_space| {
2069 let metric = &func_space.metrics;
2070 assert_eq!(metric.npm.class_npm_sum(), 1);
2071 assert_eq!(metric.npm.interface_npm_sum(), 1);
2072 insta::assert_json_snapshot!(metric.npm);
2073 assert_child_space_kind(&func_space, "Outer", SpaceKind::Class);
2074 let outer = func_space
2075 .spaces
2076 .iter()
2077 .find(|s| s.name.as_deref() == Some("Outer"))
2078 .expect("Outer FuncSpace");
2079 assert_child_space_kind(outer, "Sub", SpaceKind::Interface);
2080 },
2081 );
2082 }
2083
2084 #[test]
2085 fn kotlin_init_block_not_a_method() {
2086 check_metrics::<KotlinParser>(
2089 "class C(val n: Int) {
2090 init { require(n >= 0) }
2091 fun get(): Int = n
2092 }",
2093 "foo.kt",
2094 |metric| {
2095 assert_eq!(metric.npm.class_npm_sum(), 1);
2096 assert_eq!(metric.npm.class_nm_sum(), 1);
2097 insta::assert_json_snapshot!(metric.npm);
2098 },
2099 );
2100 }
2101
2102 #[test]
2118 fn typescript_empty_class_no_methods() {
2119 check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
2120 assert_eq!(metric.npm.class_npm_sum(), 0);
2121 assert_eq!(metric.npm.class_nm_sum(), 0);
2122 insta::assert_json_snapshot!(metric.npm);
2123 });
2124 }
2125
2126 #[test]
2127 fn typescript_default_public_methods() {
2128 check_metrics::<TypescriptParser>(
2129 "class C {
2130 a(): void {}
2131 b(): number { return 0; }
2132 c(x: number): number { return x; }
2133 }",
2134 "foo.ts",
2135 |metric| {
2136 assert_eq!(metric.npm.class_npm_sum(), 3);
2137 assert_eq!(metric.npm.class_nm_sum(), 3);
2138 insta::assert_json_snapshot!(metric.npm);
2139 },
2140 );
2141 }
2142
2143 #[test]
2144 fn typescript_method_visibility() {
2145 check_metrics::<TypescriptParser>(
2146 "class C {
2147 public a(): void {}
2148 private b(): void {}
2149 protected c(): void {}
2150 d(): void {}
2151 }",
2152 "foo.ts",
2153 |metric| {
2154 assert_eq!(metric.npm.class_npm_sum(), 2);
2156 assert_eq!(metric.npm.class_nm_sum(), 4);
2157 insta::assert_json_snapshot!(metric.npm);
2158 },
2159 );
2160 }
2161
2162 #[test]
2163 fn typescript_static_methods() {
2164 check_metrics::<TypescriptParser>(
2165 "class C {
2166 static a(): void {}
2167 public static b(): void {}
2168 private static c(): void {}
2169 }",
2170 "foo.ts",
2171 |metric| {
2172 assert_eq!(metric.npm.class_npm_sum(), 2);
2174 assert_eq!(metric.npm.class_nm_sum(), 3);
2175 insta::assert_json_snapshot!(metric.npm);
2176 },
2177 );
2178 }
2179
2180 #[test]
2181 fn typescript_constructor_counts_as_method() {
2182 check_metrics::<TypescriptParser>(
2184 "class C {
2185 constructor(public x: number) {}
2186 m(): void {}
2187 }",
2188 "foo.ts",
2189 |metric| {
2190 assert_eq!(metric.npm.class_npm_sum(), 2);
2191 assert_eq!(metric.npm.class_nm_sum(), 2);
2192 insta::assert_json_snapshot!(metric.npm);
2193 },
2194 );
2195 }
2196
2197 #[test]
2198 fn typescript_getter_setter_each_count_once() {
2199 check_metrics::<TypescriptParser>(
2202 "class C {
2203 private _x: number = 0;
2204 get x(): number { return this._x; }
2205 set x(v: number) { this._x = v; }
2206 }",
2207 "foo.ts",
2208 |metric| {
2209 assert_eq!(metric.npm.class_npm_sum(), 2);
2210 assert_eq!(metric.npm.class_nm_sum(), 2);
2211 insta::assert_json_snapshot!(metric.npm);
2212 },
2213 );
2214 }
2215
2216 #[test]
2217 fn typescript_arrow_field_counts_as_method() {
2218 check_metrics::<TypescriptParser>(
2220 "class C {
2221 a: number = 0;
2222 arrow = () => this.a;
2223 private secret = () => this.a;
2224 }",
2225 "foo.ts",
2226 |metric| {
2227 assert_eq!(metric.npm.class_npm_sum(), 1);
2229 assert_eq!(metric.npm.class_nm_sum(), 2);
2230 insta::assert_json_snapshot!(metric.npm);
2231 },
2232 );
2233 }
2234
2235 #[test]
2236 fn typescript_method_overload_counts_once() {
2237 check_metrics::<TypescriptParser>(
2240 "class C {
2241 m(x: number): void;
2242 m(x: string): void;
2243 m(x: any): void {}
2244 }",
2245 "foo.ts",
2246 |metric| {
2247 assert_eq!(metric.npm.class_npm_sum(), 1);
2248 assert_eq!(metric.npm.class_nm_sum(), 1);
2249 insta::assert_json_snapshot!(metric.npm);
2250 },
2251 );
2252 }
2253
2254 #[test]
2255 fn typescript_abstract_class_methods() {
2256 check_metrics::<TypescriptParser>(
2259 "abstract class C {
2260 abstract a(): void;
2261 public abstract b(): number;
2262 protected abstract c(): void;
2263 public m(): void {}
2264 private n(): void {}
2265 }",
2266 "foo.ts",
2267 |metric| {
2268 assert_eq!(metric.npm.class_npm_sum(), 3);
2271 assert_eq!(metric.npm.class_nm_sum(), 5);
2272 insta::assert_json_snapshot!(metric.npm);
2273 },
2274 );
2275 }
2276
2277 #[test]
2278 fn typescript_interface_methods() {
2279 check_func_space::<TypescriptParser, _>(
2281 "interface I {
2282 a(): void;
2283 b(x: number): number;
2284 c: string;
2285 }",
2286 "foo.ts",
2287 |func_space| {
2288 let metric = &func_space.metrics;
2289 assert_eq!(metric.npm.interface_npm_sum(), 2);
2290 assert_eq!(metric.npm.interface_nm_sum(), 2);
2291 assert_eq!(metric.npm.class_nm_sum(), 0);
2292 insta::assert_json_snapshot!(metric.npm);
2293 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2294 },
2295 );
2296 }
2297
2298 #[test]
2299 fn typescript_generic_class_methods() {
2300 check_metrics::<TypescriptParser>(
2301 "class Box<T> {
2302 value: T;
2303 set(v: T): void { this.value = v; }
2304 get(): T { return this.value; }
2305 }",
2306 "foo.ts",
2307 |metric| {
2308 assert_eq!(metric.npm.class_npm_sum(), 2);
2309 assert_eq!(metric.npm.class_nm_sum(), 2);
2310 insta::assert_json_snapshot!(metric.npm);
2311 },
2312 );
2313 }
2314
2315 #[test]
2316 fn typescript_multiple_classes_and_interface() {
2317 check_func_space::<TypescriptParser, _>(
2318 "class A { m(): void {} }
2319 class B { private h(): void {} }
2320 interface I { p(): number; }",
2321 "foo.ts",
2322 |func_space| {
2323 let metric = &func_space.metrics;
2324 assert_eq!(metric.npm.class_npm_sum(), 1);
2325 assert_eq!(metric.npm.class_nm_sum(), 2);
2326 assert_eq!(metric.npm.interface_npm_sum(), 1);
2327 assert_eq!(metric.npm.interface_nm_sum(), 1);
2328 insta::assert_json_snapshot!(metric.npm);
2329 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2330 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2331 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2332 },
2333 );
2334 }
2335
2336 #[test]
2339 fn tsx_empty_class_no_methods() {
2340 check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
2341 assert_eq!(metric.npm.class_npm_sum(), 0);
2342 assert_eq!(metric.npm.class_nm_sum(), 0);
2343 insta::assert_json_snapshot!(metric.npm);
2344 });
2345 }
2346
2347 #[test]
2348 fn tsx_default_public_methods() {
2349 check_metrics::<TsxParser>(
2350 "class C {
2351 a(): void {}
2352 b(): number { return 0; }
2353 }",
2354 "foo.tsx",
2355 |metric| {
2356 assert_eq!(metric.npm.class_npm_sum(), 2);
2357 assert_eq!(metric.npm.class_nm_sum(), 2);
2358 insta::assert_json_snapshot!(metric.npm);
2359 },
2360 );
2361 }
2362
2363 #[test]
2364 fn tsx_method_visibility() {
2365 check_metrics::<TsxParser>(
2366 "class C {
2367 public a(): void {}
2368 private b(): void {}
2369 protected c(): void {}
2370 }",
2371 "foo.tsx",
2372 |metric| {
2373 assert_eq!(metric.npm.class_npm_sum(), 1);
2374 assert_eq!(metric.npm.class_nm_sum(), 3);
2375 insta::assert_json_snapshot!(metric.npm);
2376 },
2377 );
2378 }
2379
2380 #[test]
2381 fn tsx_static_methods() {
2382 check_metrics::<TsxParser>(
2383 "class C {
2384 static a(): void {}
2385 private static b(): void {}
2386 }",
2387 "foo.tsx",
2388 |metric| {
2389 assert_eq!(metric.npm.class_npm_sum(), 1);
2390 assert_eq!(metric.npm.class_nm_sum(), 2);
2391 insta::assert_json_snapshot!(metric.npm);
2392 },
2393 );
2394 }
2395
2396 #[test]
2397 fn tsx_constructor_counts_as_method() {
2398 check_metrics::<TsxParser>(
2399 "class C {
2400 constructor() {}
2401 m(): void {}
2402 }",
2403 "foo.tsx",
2404 |metric| {
2405 assert_eq!(metric.npm.class_npm_sum(), 2);
2406 assert_eq!(metric.npm.class_nm_sum(), 2);
2407 insta::assert_json_snapshot!(metric.npm);
2408 },
2409 );
2410 }
2411
2412 #[test]
2413 fn tsx_getter_setter_each_count_once() {
2414 check_metrics::<TsxParser>(
2415 "class C {
2416 private _x: number = 0;
2417 get x(): number { return this._x; }
2418 set x(v: number) { this._x = v; }
2419 }",
2420 "foo.tsx",
2421 |metric| {
2422 assert_eq!(metric.npm.class_npm_sum(), 2);
2423 assert_eq!(metric.npm.class_nm_sum(), 2);
2424 insta::assert_json_snapshot!(metric.npm);
2425 },
2426 );
2427 }
2428
2429 #[test]
2430 fn tsx_arrow_field_counts_as_method() {
2431 check_metrics::<TsxParser>(
2432 "class C {
2433 arrow = () => 1;
2434 private secret = () => 2;
2435 }",
2436 "foo.tsx",
2437 |metric| {
2438 assert_eq!(metric.npm.class_npm_sum(), 1);
2439 assert_eq!(metric.npm.class_nm_sum(), 2);
2440 insta::assert_json_snapshot!(metric.npm);
2441 },
2442 );
2443 }
2444
2445 #[test]
2446 fn tsx_method_overload_counts_once() {
2447 check_metrics::<TsxParser>(
2448 "class C {
2449 m(x: number): void;
2450 m(x: string): void;
2451 m(x: any): void {}
2452 }",
2453 "foo.tsx",
2454 |metric| {
2455 assert_eq!(metric.npm.class_npm_sum(), 1);
2456 assert_eq!(metric.npm.class_nm_sum(), 1);
2457 insta::assert_json_snapshot!(metric.npm);
2458 },
2459 );
2460 }
2461
2462 #[test]
2463 fn tsx_abstract_class_methods() {
2464 check_metrics::<TsxParser>(
2465 "abstract class C {
2466 abstract a(): void;
2467 public m(): void {}
2468 private n(): void {}
2469 }",
2470 "foo.tsx",
2471 |metric| {
2472 assert_eq!(metric.npm.class_npm_sum(), 2);
2474 assert_eq!(metric.npm.class_nm_sum(), 3);
2475 insta::assert_json_snapshot!(metric.npm);
2476 },
2477 );
2478 }
2479
2480 #[test]
2481 fn tsx_interface_methods() {
2482 check_func_space::<TsxParser, _>(
2483 "interface I {
2484 a(): void;
2485 b(): number;
2486 }",
2487 "foo.tsx",
2488 |func_space| {
2489 let metric = &func_space.metrics;
2490 assert_eq!(metric.npm.interface_npm_sum(), 2);
2491 assert_eq!(metric.npm.interface_nm_sum(), 2);
2492 insta::assert_json_snapshot!(metric.npm);
2493 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2494 },
2495 );
2496 }
2497
2498 #[test]
2499 fn tsx_generic_class_methods() {
2500 check_metrics::<TsxParser>(
2501 "class Box<T> { value: T; set(v: T): void { this.value = v; } }",
2502 "foo.tsx",
2503 |metric| {
2504 assert_eq!(metric.npm.class_npm_sum(), 1);
2505 assert_eq!(metric.npm.class_nm_sum(), 1);
2506 insta::assert_json_snapshot!(metric.npm);
2507 },
2508 );
2509 }
2510
2511 #[test]
2512 fn tsx_multiple_classes_and_interface() {
2513 check_func_space::<TsxParser, _>(
2514 "class A { m(): void {} }
2515 class B { private h(): void {} }
2516 interface I { p(): number; }",
2517 "foo.tsx",
2518 |func_space| {
2519 let metric = &func_space.metrics;
2520 assert_eq!(metric.npm.class_npm_sum(), 1);
2521 assert_eq!(metric.npm.class_nm_sum(), 2);
2522 assert_eq!(metric.npm.interface_npm_sum(), 1);
2523 assert_eq!(metric.npm.interface_nm_sum(), 1);
2524 insta::assert_json_snapshot!(metric.npm);
2525 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2526 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2527 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2528 },
2529 );
2530 }
2531
2532 #[test]
2541 fn ruby_no_class_methods() {
2542 check_metrics::<RubyParser>("def foo\n 1\nend\n", "foo.rb", |metric| {
2543 assert_eq!(metric.npm.class_npm_sum(), 0);
2544 assert_eq!(metric.npm.class_nm_sum(), 0);
2545 insta::assert_json_snapshot!(metric.npm);
2546 });
2547 }
2548
2549 #[test]
2550 fn ruby_one_public_method() {
2551 check_metrics::<RubyParser>(
2553 "class A\n def f\n 1\n end\nend\n",
2554 "foo.rb",
2555 |metric| {
2556 assert_eq!(metric.npm.class_npm_sum(), 1);
2557 assert_eq!(metric.npm.class_nm_sum(), 1);
2558 insta::assert_json_snapshot!(metric.npm);
2559 },
2560 );
2561 }
2562
2563 #[test]
2564 fn ruby_one_private_method() {
2565 check_metrics::<RubyParser>(
2567 "class A\n private\n def f\n 1\n end\nend\n",
2568 "foo.rb",
2569 |metric| {
2570 assert_eq!(metric.npm.class_npm_sum(), 0);
2571 assert_eq!(metric.npm.class_nm_sum(), 1);
2572 insta::assert_json_snapshot!(metric.npm);
2573 },
2574 );
2575 }
2576
2577 #[test]
2578 fn ruby_one_protected_method() {
2579 check_metrics::<RubyParser>(
2580 "class A\n protected\n def f\n 1\n end\nend\n",
2581 "foo.rb",
2582 |metric| {
2583 assert_eq!(metric.npm.class_npm_sum(), 0);
2584 assert_eq!(metric.npm.class_nm_sum(), 1);
2585 insta::assert_json_snapshot!(metric.npm);
2586 },
2587 );
2588 }
2589
2590 #[test]
2591 fn ruby_mixed_visibility_methods() {
2592 check_metrics::<RubyParser>(
2596 "class A\n def a\n 1\n end\n private\n def b\n 1\n end\n public\n def c\n 1\n end\n protected\n def d\n 1\n end\nend\n",
2597 "foo.rb",
2598 |metric| {
2599 assert_eq!(metric.npm.class_npm_sum(), 2);
2600 assert_eq!(metric.npm.class_nm_sum(), 4);
2601 insta::assert_json_snapshot!(metric.npm);
2602 },
2603 );
2604 }
2605
2606 #[test]
2607 fn ruby_singleton_method_is_counted() {
2608 check_metrics::<RubyParser>(
2610 "class A\n def self.f\n 1\n end\n def g\n 1\n end\nend\n",
2611 "foo.rb",
2612 |metric| {
2613 assert_eq!(metric.npm.class_npm_sum(), 2);
2614 assert_eq!(metric.npm.class_nm_sum(), 2);
2615 insta::assert_json_snapshot!(metric.npm);
2616 },
2617 );
2618 }
2619
2620 #[test]
2621 fn ruby_singleton_class_methods() {
2622 check_metrics::<RubyParser>(
2625 "class A\n class << self\n def s\n 1\n end\n def t\n 2\n end\n end\nend\n",
2626 "foo.rb",
2627 |metric| {
2628 assert_eq!(metric.npm.class_npm_sum(), 2);
2629 assert_eq!(metric.npm.class_nm_sum(), 2);
2630 insta::assert_json_snapshot!(metric.npm);
2631 },
2632 );
2633 }
2634
2635 #[test]
2636 fn ruby_argument_form_visibility_does_not_flip() {
2637 check_metrics::<RubyParser>(
2641 "class A\n def y\n 1\n end\n private :y\n def z\n 1\n end\nend\n",
2642 "foo.rb",
2643 |metric| {
2644 assert_eq!(metric.npm.class_npm_sum(), 2);
2645 assert_eq!(metric.npm.class_nm_sum(), 2);
2646 insta::assert_json_snapshot!(metric.npm);
2647 },
2648 );
2649 }
2650
2651 #[test]
2652 fn ruby_multiple_classes() {
2653 check_metrics::<RubyParser>(
2654 "class A\n def a\n 1\n end\nend\nclass B\n private\n def b\n 1\n end\n def c\n 1\n end\nend\n",
2655 "foo.rb",
2656 |metric| {
2657 assert_eq!(metric.npm.class_npm_sum(), 1);
2659 assert_eq!(metric.npm.class_nm_sum(), 3);
2660 insta::assert_json_snapshot!(metric.npm);
2661 },
2662 );
2663 }
2664
2665 #[test]
2666 fn ruby_module_methods_not_counted() {
2667 check_metrics::<RubyParser>(
2670 "module M\n def f\n 1\n end\n def g\n 1\n end\nend\n",
2671 "foo.rb",
2672 |metric| {
2673 assert_eq!(metric.npm.class_npm_sum(), 0);
2674 assert_eq!(metric.npm.class_nm_sum(), 0);
2675 insta::assert_json_snapshot!(metric.npm);
2676 },
2677 );
2678 }
2679
2680 #[test]
2681 fn ruby_class_with_inheritance() {
2682 check_metrics::<RubyParser>(
2684 "class A < B\n def f\n 1\n end\n def g\n 1\n end\nend\n",
2685 "foo.rb",
2686 |metric| {
2687 assert_eq!(metric.npm.class_npm_sum(), 2);
2688 assert_eq!(metric.npm.class_nm_sum(), 2);
2689 insta::assert_json_snapshot!(metric.npm);
2690 },
2691 );
2692 }
2693
2694 #[test]
2695 fn ruby_visibility_resets_between_classes() {
2696 check_metrics::<RubyParser>(
2699 "class A\n private\n def a\n 1\n end\nend\nclass B\n def b\n 1\n end\nend\n",
2700 "foo.rb",
2701 |metric| {
2702 assert_eq!(metric.npm.class_npm_sum(), 1);
2704 assert_eq!(metric.npm.class_nm_sum(), 2);
2705 insta::assert_json_snapshot!(metric.npm);
2706 },
2707 );
2708 }
2709
2710 #[test]
2711 fn ruby_empty_class_no_methods() {
2712 check_metrics::<RubyParser>("class Empty\nend\n", "foo.rb", |metric| {
2713 assert_eq!(metric.npm.class_npm_sum(), 0);
2714 assert_eq!(metric.npm.class_nm_sum(), 0);
2715 insta::assert_json_snapshot!(metric.npm);
2716 });
2717 }
2718
2719 #[test]
2732 fn python_empty_class_no_methods() {
2733 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
2734 assert_eq!(metric.npm.class_nm_sum(), 0);
2735 assert_eq!(metric.npm.class_npm_sum(), 0);
2736 insta::assert_json_snapshot!(metric.npm);
2737 });
2738 }
2739
2740 #[test]
2741 fn python_class_methods_count() {
2742 check_metrics::<PythonParser>(
2744 "class C:\n\
2745 \x20 def __init__(self):\n\
2746 \x20 pass\n\
2747 \x20 def m(self):\n\
2748 \x20 pass\n\
2749 \x20 def n(self):\n\
2750 \x20 pass\n",
2751 "foo.py",
2752 |metric| {
2753 assert_eq!(metric.npm.class_nm_sum(), 3);
2754 assert_eq!(metric.npm.class_npm_sum(), 3);
2755 insta::assert_json_snapshot!(metric.npm);
2756 },
2757 );
2758 }
2759
2760 #[test]
2761 fn python_decorated_methods_count() {
2762 check_metrics::<PythonParser>(
2766 "class C:\n\
2767 \x20 @property\n\
2768 \x20 def p(self):\n\
2769 \x20 return 1\n\
2770 \x20 @staticmethod\n\
2771 \x20 def s():\n\
2772 \x20 return 2\n\
2773 \x20 @classmethod\n\
2774 \x20 def c(cls):\n\
2775 \x20 return 3\n",
2776 "foo.py",
2777 |metric| {
2778 assert_eq!(metric.npm.class_nm_sum(), 3);
2779 insta::assert_json_snapshot!(metric.npm);
2780 },
2781 );
2782 }
2783
2784 #[test]
2785 fn python_async_method_counts() {
2786 check_metrics::<PythonParser>(
2789 "class C:\n async def m(self):\n return 1\n",
2790 "foo.py",
2791 |metric| {
2792 assert_eq!(metric.npm.class_nm_sum(), 1);
2793 insta::assert_json_snapshot!(metric.npm);
2794 },
2795 );
2796 }
2797
2798 #[test]
2799 fn python_nested_class_methods_independent() {
2800 check_metrics::<PythonParser>(
2803 "class Outer:\n\
2804 \x20 def method(self):\n\
2805 \x20 pass\n\
2806 \x20 class Inner:\n\
2807 \x20 def inner_method(self):\n\
2808 \x20 pass\n",
2809 "foo.py",
2810 |metric| {
2811 assert_eq!(metric.npm.class_nm_sum(), 2);
2812 insta::assert_json_snapshot!(metric.npm);
2813 },
2814 );
2815 }
2816
2817 #[test]
2818 fn python_module_level_function_is_not_method() {
2819 check_metrics::<PythonParser>(
2822 "def f():\n pass\nclass C:\n def m(self):\n pass\n",
2823 "foo.py",
2824 |metric| {
2825 assert_eq!(metric.npm.class_nm_sum(), 1);
2827 insta::assert_json_snapshot!(metric.npm);
2828 },
2829 );
2830 }
2831
2832 #[test]
2833 fn python_dunder_methods_count() {
2834 check_metrics::<PythonParser>(
2837 "class C:\n\
2838 \x20 def __init__(self):\n\
2839 \x20 pass\n\
2840 \x20 def __repr__(self):\n\
2841 \x20 return 'C'\n\
2842 \x20 def __eq__(self, other):\n\
2843 \x20 return True\n",
2844 "foo.py",
2845 |metric| {
2846 assert_eq!(metric.npm.class_nm_sum(), 3);
2847 assert_eq!(metric.npm.class_npm_sum(), 3);
2848 insta::assert_json_snapshot!(metric.npm);
2849 },
2850 );
2851 }
2852
2853 #[test]
2854 fn rust_empty_unit_no_methods() {
2855 check_metrics::<RustParser>("", "empty.rs", |metric| {
2856 assert_eq!(metric.npm.class_nm_sum(), 0);
2857 assert_eq!(metric.npm.class_npm_sum(), 0);
2858 assert_eq!(metric.npm.interface_nm_sum(), 0);
2859 assert_eq!(metric.npm.interface_npm_sum(), 0);
2860 insta::assert_json_snapshot!(metric.npm);
2861 });
2862 }
2863
2864 #[test]
2865 fn rust_impl_methods_count() {
2866 check_metrics::<RustParser>(
2869 "struct Foo;\n\
2870 impl Foo {\n\
2871 \x20 pub fn new() -> Self { Foo }\n\
2872 \x20 fn helper(&self) -> i32 { 0 }\n\
2873 \x20 pub fn process(&self) -> i32 { 0 }\n\
2874 }\n",
2875 "foo.rs",
2876 |metric| {
2877 assert_eq!(metric.npm.class_nm_sum(), 3);
2878 assert_eq!(metric.npm.class_npm_sum(), 2);
2879 insta::assert_json_snapshot!(metric.npm);
2880 },
2881 );
2882 }
2883
2884 #[test]
2885 fn rust_pub_self_is_private() {
2886 check_metrics::<RustParser>(
2894 "struct S;\n\
2895 impl S {\n\
2896 \x20 pub(self) fn a(&self) {}\n\
2897 \x20 pub(in self) fn a2(&self) {}\n\
2898 \x20 pub(crate) fn b(&self) {}\n\
2899 \x20 pub(super) fn d(&self) {}\n\
2900 \x20 pub(in crate::x) fn e(&self) {}\n\
2901 \x20 pub fn f(&self) {}\n\
2902 \x20 fn c(&self) {}\n\
2903 }\n",
2904 "foo.rs",
2905 |metric| {
2906 assert_eq!(metric.npm.class_nm_sum(), 7);
2907 assert_eq!(metric.npm.class_npm_sum(), 4);
2908 },
2909 );
2910 }
2911
2912 #[test]
2913 fn rust_trait_methods_count() {
2914 check_func_space::<RustParser, _>(
2921 "trait Drawable {\n\
2922 \x20 fn draw(&self);\n\
2923 \x20 fn area(&self) -> f64 { 0.0 }\n\
2924 }\n",
2925 "foo.rs",
2926 |func_space| {
2927 let metric = &func_space.metrics;
2928 assert_eq!(metric.npm.interface_nm_sum(), 2);
2929 assert_eq!(metric.npm.interface_npm_sum(), 2);
2930 assert_eq!(metric.npm.class_nm_sum(), 0);
2931 insta::assert_json_snapshot!(metric.npm);
2932 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
2933 },
2934 );
2935 }
2936
2937 #[test]
2938 fn rust_module_level_function_not_method() {
2939 check_metrics::<RustParser>("fn f() {}\nfn g() {}\n", "foo.rs", |metric| {
2943 assert_eq!(metric.npm.class_nm_sum(), 0);
2944 assert_eq!(metric.npm.interface_nm_sum(), 0);
2945 insta::assert_json_snapshot!(metric.npm);
2946 });
2947 }
2948
2949 #[test]
2950 fn rust_multiple_impls_methods_aggregate() {
2951 check_metrics::<RustParser>(
2953 "struct Foo;\n\
2954 impl Foo { pub fn m1(&self) {} }\n\
2955 impl Foo { fn m2(&self) {} }\n",
2956 "foo.rs",
2957 |metric| {
2958 assert_eq!(metric.npm.class_nm_sum(), 2);
2959 assert_eq!(metric.npm.class_npm_sum(), 1);
2960 insta::assert_json_snapshot!(metric.npm);
2961 },
2962 );
2963 }
2964
2965 #[test]
2966 fn rust_trait_impl_block_counts_methods() {
2967 check_func_space::<RustParser, _>(
2974 "struct Foo;\n\
2975 trait Drawable { fn draw(&self); }\n\
2976 impl Drawable for Foo { fn draw(&self) {} }\n",
2977 "foo.rs",
2978 |func_space| {
2979 let metric = &func_space.metrics;
2980 assert_eq!(metric.npm.interface_nm_sum(), 1);
2983 assert_eq!(metric.npm.class_nm_sum(), 1);
2984 insta::assert_json_snapshot!(metric.npm);
2985 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
2986 },
2987 );
2988 }
2989
2990 #[test]
2993 fn go_empty_unit_no_methods() {
2994 check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
2996 assert_eq!(metric.npm.class_nm_sum(), 0);
2997 insta::assert_json_snapshot!(metric.npm);
2998 });
2999 }
3000
3001 #[test]
3002 fn go_method_declarations_count() {
3003 check_metrics::<GoParser>(
3007 "package main\n\
3008 type Foo struct{}\n\
3009 func (f Foo) DoX() {}\n\
3010 func (f Foo) doY() {}\n",
3011 "foo.go",
3012 |metric| {
3013 assert_eq!(metric.npm.class_nm_sum(), 2);
3014 assert_eq!(metric.npm.class_npm_sum(), 1);
3015 insta::assert_json_snapshot!(metric.npm);
3016 },
3017 );
3018 }
3019
3020 #[test]
3021 fn go_free_function_is_not_method() {
3022 check_metrics::<GoParser>(
3026 "package main\nfunc g() {}\nfunc h(x int) int { return x }\n",
3027 "foo.go",
3028 |metric| {
3029 assert_eq!(metric.npm.class_nm_sum(), 0);
3030 insta::assert_json_snapshot!(metric.npm);
3031 },
3032 );
3033 }
3034
3035 #[test]
3036 fn go_methods_on_different_receivers_aggregate_at_unit() {
3037 check_metrics::<GoParser>(
3041 "package main\n\
3042 type Foo struct{}\n\
3043 type Bar struct{}\n\
3044 func (f Foo) M1() {}\n\
3045 func (b Bar) M2() {}\n\
3046 func (b *Bar) M3() {}\n",
3047 "foo.go",
3048 |metric| {
3049 assert_eq!(metric.npm.class_nm_sum(), 3);
3050 insta::assert_json_snapshot!(metric.npm);
3051 },
3052 );
3053 }
3054
3055 #[test]
3056 fn go_interface_methods_count_as_interface_nm() {
3057 check_metrics::<GoParser>(
3072 "package main\ntype RC interface { Read() error; Close() error }\n",
3073 "foo.go",
3074 |metric| {
3075 assert_eq!(metric.npm.interface_nm_sum(), 2);
3076 assert_eq!(metric.npm.interface_npm_sum(), 2);
3077 assert_eq!(metric.npm.class_nm_sum(), 0);
3078 insta::assert_json_snapshot!(metric.npm);
3079 },
3080 );
3081 }
3082
3083 #[test]
3084 fn go_interface_methods_respect_export() {
3085 check_metrics::<GoParser>(
3091 "package main\ntype I interface { Foo(); bar(); Ünic() }\n",
3092 "foo.go",
3093 |metric| {
3094 assert_eq!(metric.npm.interface_nm_sum(), 3);
3095 assert_eq!(metric.npm.interface_npm_sum(), 2);
3096 assert_eq!(metric.npm.class_nm_sum(), 0);
3097 insta::assert_json_snapshot!(metric.npm);
3098 },
3099 );
3100 }
3101
3102 #[test]
3103 fn go_pointer_receiver_methods_count() {
3104 check_metrics::<GoParser>(
3108 "package main\n\
3109 type Foo struct{}\n\
3110 func (f *Foo) Set() {}\n\
3111 func (f *Foo) Get() int { return 0 }\n",
3112 "foo.go",
3113 |metric| {
3114 assert_eq!(metric.npm.class_nm_sum(), 2);
3115 insta::assert_json_snapshot!(metric.npm);
3116 },
3117 );
3118 }
3119
3120 #[test]
3121 fn go_npm_excludes_unexported() {
3122 check_metrics::<GoParser>(
3128 "package main\n\
3129 type T struct{}\n\
3130 func (t *T) Greet() {}\n\
3131 func (t *T) helper() {}\n\
3132 func (t *T) Ärger() {}\n",
3133 "foo.go",
3134 |metric| {
3135 assert_eq!(metric.npm.class_nm_sum(), 3);
3136 assert_eq!(metric.npm.class_npm_sum(), 2);
3137 insta::assert_json_snapshot!(metric.npm);
3138 },
3139 );
3140 }
3141
3142 #[test]
3147 fn elixir_npm_def_is_public_defp_is_private() {
3148 check_metrics::<ElixirParser>(
3149 "defmodule Foo do\n def pub_one, do: 1\n defp priv_one, do: 1\n def pub_two(x), do: x\nend\n",
3150 "foo.ex",
3151 |metric| {
3152 assert_eq!(metric.npm.class_nm_sum(), 3);
3154 assert_eq!(metric.npm.class_npm_sum(), 2);
3155 },
3156 );
3157 }
3158
3159 #[test]
3160 fn elixir_npm_defmacro_counts_as_public() {
3161 check_metrics::<ElixirParser>(
3162 "defmodule Foo do\n defmacro pub_macro(x), do: x\n defmacrop priv_macro(x), do: x\nend\n",
3163 "foo.ex",
3164 |metric| {
3165 assert_eq!(metric.npm.class_nm_sum(), 2);
3167 assert_eq!(metric.npm.class_npm_sum(), 1);
3168 },
3169 );
3170 }
3171
3172 #[test]
3173 fn elixir_npm_multiple_def_clauses_each_count() {
3174 check_metrics::<ElixirParser>(
3176 "defmodule Foo do\n def f(0), do: :zero\n def f(_), do: :other\nend\n",
3177 "foo.ex",
3178 |metric| {
3179 assert_eq!(metric.npm.class_nm_sum(), 2);
3180 assert_eq!(metric.npm.class_npm_sum(), 2);
3181 },
3182 );
3183 }
3184
3185 #[test]
3186 fn elixir_npm_nested_defmodule_each_class() {
3187 check_metrics::<ElixirParser>(
3188 "defmodule Outer do\n def o, do: 1\n defmodule Inner do\n def i, do: 1\n end\nend\n",
3189 "foo.ex",
3190 |metric| {
3191 assert_eq!(metric.npm.class_nm_sum(), 2);
3193 assert_eq!(metric.npm.class_npm_sum(), 2);
3194 },
3195 );
3196 }
3197
3198 #[test]
3199 fn elixir_npm_user_macro_not_classified_as_method() {
3200 check_metrics::<ElixirParser>(
3204 "defmodule Foo do\n defmacro custom_def(name, body) do\n quote do\n def unquote(name), do: unquote(body)\n end\n end\n custom_def foo, do: 1\nend\n",
3205 "foo.ex",
3206 |metric| {
3207 assert_eq!(metric.npm.class_nm_sum(), 1);
3212 assert_eq!(metric.npm.class_npm_sum(), 1);
3213 },
3214 );
3215 }
3216
3217 #[test]
3218 fn elixir_npm_quoted_defs_do_not_inflate_method_count() {
3219 check_metrics::<ElixirParser>(
3227 "defmodule Foo do\n defmacro multi do\n quote do\n def a, do: 1\n def b, do: 2\n defp c, do: 3\n end\n end\nend\n",
3228 "foo.ex",
3229 |metric| {
3230 assert_eq!(metric.npm.class_nm_sum(), 1);
3232 assert_eq!(metric.npm.class_npm_sum(), 1);
3233 },
3234 );
3235 }
3236
3237 #[test]
3255 fn elixir_npm_counts_a_quoted_defmodule_as_a_class() {
3256 check_metrics::<ElixirParser>(
3257 "defmodule Outer do\n defmacro gen do\n quote do\n defmodule Inner do\n def a, do: 1\n defp b, do: 2\n end\n end\n end\nend\n",
3258 "outer.ex",
3259 |metric| {
3260 assert_eq!(metric.npm.class_nm_sum(), 3);
3263 assert_eq!(metric.npm.class_npm_sum(), 2);
3264 },
3265 );
3266 }
3267
3268 #[test]
3271 fn objc_npm() {
3272 check_metrics::<ObjcParser>(
3279 "@interface Foo : NSObject\n\
3280 - (void)pub1;\n\
3281 - (void)pub2;\n\
3282 @end\n\
3283 @implementation Foo\n\
3284 - (void)pub1 { }\n\
3285 - (void)pub2 { }\n\
3286 - (void)privHelper { }\n\
3287 void cFunc(void) { }\n\
3288 @end\n",
3289 "foo.m",
3290 |metric| {
3291 assert_eq!(metric.npm.interface_nm_sum(), 2);
3292 assert_eq!(metric.npm.interface_npm_sum(), 2);
3293 assert_eq!(metric.npm.class_nm_sum(), 3);
3294 assert_eq!(metric.npm.class_npm_sum(), 3);
3295 },
3296 );
3297 }
3298
3299 #[test]
3300 fn objc_npm_protocol() {
3301 check_metrics::<ObjcParser>(
3306 "@protocol Drawable <NSObject>\n\
3307 - (void)draw;\n\
3308 @optional\n\
3309 - (void)optDraw;\n\
3310 @end\n",
3311 "foo.m",
3312 |metric| {
3313 assert_eq!(metric.npm.interface_nm_sum(), 2);
3314 assert_eq!(metric.npm.interface_npm_sum(), 2);
3315 },
3316 );
3317 }
3318
3319 #[test]
3322 fn cpp_empty_unit_no_methods() {
3323 check_metrics::<CppParser>("", "empty.cpp", |metric| {
3325 assert_eq!(metric.npm.class_nm_sum(), 0);
3326 assert_eq!(metric.npm.class_npm_sum(), 0);
3327 insta::assert_json_snapshot!(metric.npm);
3328 });
3329 }
3330
3331 #[test]
3332 fn cpp_class_methods_count() {
3333 check_metrics::<CppParser>(
3336 "class Foo {\n\
3337 void method1() {}\n\
3338 void method2();\n\
3339 };",
3340 "foo.cpp",
3341 |metric| {
3342 assert_eq!(metric.npm.class_nm_sum(), 2);
3343 assert_eq!(metric.npm.class_npm_sum(), 0);
3344 insta::assert_json_snapshot!(metric.npm);
3345 },
3346 );
3347 }
3348
3349 #[test]
3350 fn cpp_constructors_and_destructors_count() {
3351 check_metrics::<CppParser>(
3355 "class Foo {\n\
3356 public:\n\
3357 Foo();\n\
3358 ~Foo();\n\
3359 void method();\n\
3360 };",
3361 "foo.cpp",
3362 |metric| {
3363 assert_eq!(metric.npm.class_nm_sum(), 3);
3364 assert_eq!(metric.npm.class_npm_sum(), 3);
3365 insta::assert_json_snapshot!(metric.npm);
3366 },
3367 );
3368 }
3369
3370 #[test]
3371 fn cpp_template_methods_count() {
3372 check_metrics::<CppParser>(
3376 "class Foo {\n\
3377 public:\n\
3378 template<typename T> T fn(T x);\n\
3379 };",
3380 "foo.cpp",
3381 |metric| {
3382 assert_eq!(metric.npm.class_nm_sum(), 1);
3383 assert_eq!(metric.npm.class_npm_sum(), 1);
3384 insta::assert_json_snapshot!(metric.npm);
3385 },
3386 );
3387 }
3388
3389 #[test]
3390 fn cpp_struct_methods_default_public() {
3391 check_metrics::<CppParser>(
3394 "struct Foo {\n\
3395 void a();\n\
3396 void b() {}\n\
3397 Foo() {}\n\
3398 };",
3399 "foo.cpp",
3400 |metric| {
3401 assert_eq!(metric.npm.class_nm_sum(), 3);
3402 assert_eq!(metric.npm.class_npm_sum(), 3);
3403 insta::assert_json_snapshot!(metric.npm);
3404 },
3405 );
3406 }
3407
3408 #[test]
3409 fn cpp_free_function_is_not_method() {
3410 check_metrics::<CppParser>("void free_fn() {}\n", "foo.cpp", |metric| {
3414 assert_eq!(metric.npm.class_nm_sum(), 0);
3415 assert_eq!(metric.npm.class_npm_sum(), 0);
3416 insta::assert_json_snapshot!(metric.npm);
3417 });
3418 }
3419
3420 #[test]
3421 fn cpp_mixed_visibility_methods() {
3422 check_metrics::<CppParser>(
3426 "class Foo {\n\
3427 public: void a();\n\
3428 protected: void b();\n\
3429 private: void c();\n\
3430 };",
3431 "foo.cpp",
3432 |metric| {
3433 assert_eq!(metric.npm.class_nm_sum(), 3);
3434 assert_eq!(metric.npm.class_npm_sum(), 1);
3435 insta::assert_json_snapshot!(metric.npm);
3436 },
3437 );
3438 }
3439
3440 #[test]
3441 fn cpp_multiple_classes_aggregate_at_unit() {
3442 check_metrics::<CppParser>(
3445 "class Foo { public: void a(); void b() {} };\n\
3446 struct Bar { void c(); };",
3447 "foo.cpp",
3448 |metric| {
3449 assert_eq!(metric.npm.class_nm_sum(), 3);
3450 assert_eq!(metric.npm.class_npm_sum(), 3);
3451 insta::assert_json_snapshot!(metric.npm);
3452 },
3453 );
3454 }
3455
3456 #[test]
3457 fn javascript_empty_unit_no_methods() {
3458 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
3459 assert_eq!(metric.npm.class_nm_sum(), 0);
3460 assert_eq!(metric.npm.class_npm_sum(), 0);
3461 insta::assert_json_snapshot!(metric.npm);
3462 });
3463 }
3464
3465 #[test]
3466 fn javascript_class_methods_count() {
3467 check_metrics::<JavascriptParser>(
3471 "class Foo {\n\
3472 constructor() {}\n\
3473 bar() {}\n\
3474 get baz() { return 1; }\n\
3475 set baz(v) {}\n\
3476 }",
3477 "foo.js",
3478 |metric| {
3479 assert_eq!(metric.npm.class_nm_sum(), 4);
3480 assert_eq!(metric.npm.class_npm_sum(), 4);
3481 insta::assert_json_snapshot!(metric.npm);
3482 },
3483 );
3484 }
3485
3486 #[test]
3487 fn javascript_arrow_field_is_method() {
3488 check_metrics::<JavascriptParser>(
3492 "class Foo { x = () => {}; y = function() {}; z = 1; }",
3493 "foo.js",
3494 |metric| {
3495 assert_eq!(metric.npm.class_nm_sum(), 2);
3497 assert_eq!(metric.npm.class_npm_sum(), 2);
3498 insta::assert_json_snapshot!(metric.npm);
3499 },
3500 );
3501 }
3502
3503 #[test]
3504 fn javascript_free_function_is_not_method() {
3505 check_metrics::<JavascriptParser>(
3508 "function f() {}\nconst g = () => {};\nclass Foo { h() {} }",
3509 "foo.js",
3510 |metric| {
3511 assert_eq!(metric.npm.class_nm_sum(), 1);
3513 assert_eq!(metric.npm.class_npm_sum(), 1);
3514 insta::assert_json_snapshot!(metric.npm);
3515 },
3516 );
3517 }
3518
3519 #[test]
3520 fn javascript_multiple_classes_aggregate_at_unit() {
3521 check_metrics::<JavascriptParser>(
3524 "class Foo { a() {} b() {} }\nclass Bar { c() {} }",
3525 "foo.js",
3526 |metric| {
3527 assert_eq!(metric.npm.class_nm_sum(), 3);
3528 assert_eq!(metric.npm.class_npm_sum(), 3);
3529 insta::assert_json_snapshot!(metric.npm);
3530 },
3531 );
3532 }
3533
3534 #[test]
3535 fn mozjs_class_methods_count() {
3536 check_metrics::<MozjsParser>(
3538 "class Foo {\n\
3539 constructor() {}\n\
3540 bar() {}\n\
3541 get baz() { return 1; }\n\
3542 set baz(v) {}\n\
3543 }",
3544 "foo.js",
3545 |metric| {
3546 assert_eq!(metric.npm.class_nm_sum(), 4);
3547 assert_eq!(metric.npm.class_npm_sum(), 4);
3548 insta::assert_json_snapshot!(metric.npm);
3549 },
3550 );
3551 }
3552
3553 #[test]
3561 fn empty_class_coa_is_zero_not_nan() {
3562 let assert_zero = |metric: crate::CodeMetrics| {
3563 assert_eq!(metric.npm.class_nm_sum(), 0);
3564 assert!(!metric.npm.class_coa().is_nan());
3565 assert!(!metric.npm.total_coa().is_nan());
3566 assert_eq!(metric.npm.class_coa(), 0.0);
3567 assert_eq!(metric.npm.total_coa(), 0.0);
3568 };
3569 check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
3570 check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
3571 check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
3572 check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
3573 }
3574
3575 #[test]
3580 fn empty_interface_coa_is_zero_not_nan() {
3581 let assert_zero = |metric: crate::CodeMetrics| {
3582 assert_eq!(metric.npm.interface_nm_sum(), 0);
3583 assert!(!metric.npm.interface_coa().is_nan());
3584 assert_eq!(metric.npm.interface_coa(), 0.0);
3585 };
3586 check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
3587 check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
3588 }
3589
3590 #[test]
3594 fn stats_display_and_per_space_accessors() {
3595 check_func_space::<JavaParser, _>(
3596 "public interface I {\n void p();\n}\n\
3597 public class C {\n public void m() {}\n private void n() {}\n}\n",
3598 "X.java",
3599 |unit| {
3600 assert_eq!(unit.metrics.npm.class_npm_sum(), 1);
3603 assert_eq!(unit.metrics.npm.class_nm_sum(), 2);
3604 let rendered = unit.metrics.npm.to_string();
3605 for fragment in [
3606 "classes: 1, interfaces: 1",
3607 "class_methods: 2",
3608 "interface_methods: 1",
3609 "total: 2, total_methods: 3",
3610 ] {
3611 assert!(
3612 rendered.contains(fragment),
3613 "missing {fragment:?} in {rendered}"
3614 );
3615 }
3616 let class = child_space(&unit, "C");
3621 assert_eq!(class.kind, SpaceKind::Class);
3622 assert_eq!(class.metrics.npm.class_npm(), 1);
3623 assert_eq!(class.metrics.npm.class_nm(), 2);
3624 let iface = child_space(&unit, "I");
3625 assert_eq!(iface.kind, SpaceKind::Interface);
3626 assert_eq!(iface.metrics.npm.interface_npm(), 1);
3627 assert_eq!(iface.metrics.npm.interface_nm(), 1);
3628 },
3629 );
3630 }
3631}