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)]
34#[non_exhaustive]
35pub struct Stats {
36 class_npm: usize,
37 interface_npm: usize,
38 class_nm: usize,
39 interface_nm: usize,
40 class_npm_sum: usize,
41 interface_npm_sum: usize,
42 class_nm_sum: usize,
43 interface_nm_sum: usize,
44 is_class_space: bool,
45}
46
47impl fmt::Display for Stats {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 write!(
50 f,
51 "classes: {}, interfaces: {}, class_methods: {}, interface_methods: {}, class_coa: {}, interface_coa: {}, total: {}, total_methods: {}, coa: {}",
52 self.class_npm_sum(),
53 self.interface_npm_sum(),
54 self.class_nm_sum(),
55 self.interface_nm_sum(),
56 self.class_coa(),
57 self.interface_coa(),
58 self.total_npm(),
59 self.total_nm(),
60 self.total_coa()
61 )
62 }
63}
64
65impl Stats {
66 pub fn merge(&mut self, other: &Stats) {
68 self.class_npm_sum += other.class_npm_sum;
69 self.interface_npm_sum += other.interface_npm_sum;
70 self.class_nm_sum += other.class_nm_sum;
71 self.interface_nm_sum += other.interface_nm_sum;
72 }
73
74 #[inline]
76 #[must_use]
77 pub fn class_npm(&self) -> u64 {
78 self.class_npm as u64
79 }
80
81 #[inline]
83 #[must_use]
84 pub fn interface_npm(&self) -> u64 {
85 self.interface_npm as u64
86 }
87
88 #[inline]
90 #[must_use]
91 pub fn class_nm(&self) -> u64 {
92 self.class_nm as u64
93 }
94
95 #[inline]
97 #[must_use]
98 pub fn interface_nm(&self) -> u64 {
99 self.interface_nm as u64
100 }
101
102 #[inline]
104 #[must_use]
105 pub fn class_npm_sum(&self) -> u64 {
106 self.class_npm_sum as u64
107 }
108
109 #[inline]
111 #[must_use]
112 pub fn interface_npm_sum(&self) -> u64 {
113 self.interface_npm_sum as u64
114 }
115
116 #[inline]
118 #[must_use]
119 pub fn class_nm_sum(&self) -> u64 {
120 self.class_nm_sum as u64
121 }
122
123 #[inline]
125 #[must_use]
126 pub fn interface_nm_sum(&self) -> u64 {
127 self.interface_nm_sum as u64
128 }
129
130 #[inline]
140 #[must_use]
141 pub fn class_coa(&self) -> f64 {
142 accessibility_ratio(self.class_npm_sum() as f64, self.class_nm_sum() as f64)
143 }
144
145 #[inline]
155 #[must_use]
156 pub fn interface_coa(&self) -> f64 {
157 if self.interface_npm_sum == self.interface_nm_sum && self.interface_npm_sum != 0 {
163 1.0
164 } else {
165 accessibility_ratio(
166 self.interface_npm_sum() as f64,
167 self.interface_nm_sum() as f64,
168 )
169 }
170 }
171
172 #[inline]
182 #[must_use]
183 pub fn total_coa(&self) -> f64 {
184 accessibility_ratio(self.total_npm() as f64, self.total_nm() as f64)
185 }
186
187 #[inline]
189 #[must_use]
190 pub fn total_npm(&self) -> u64 {
191 self.class_npm_sum() + self.interface_npm_sum()
192 }
193
194 #[inline]
196 #[must_use]
197 pub fn total_nm(&self) -> u64 {
198 self.class_nm_sum() + self.interface_nm_sum()
199 }
200
201 #[inline]
204 pub(crate) fn compute_sum(&mut self) {
205 self.class_npm_sum += self.class_npm;
206 self.interface_npm_sum += self.interface_npm;
207 self.class_nm_sum += self.class_nm;
208 self.interface_nm_sum += self.interface_nm;
209 }
210
211 #[inline]
213 pub(crate) fn is_disabled(&self) -> bool {
214 !self.is_class_space
215 }
216}
217
218#[doc(hidden)]
219pub(crate) trait Npm
221where
222 Self: Checker,
223{
224 fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats);
234}
235
236macro_rules! impl_npm_java_like {
251 ($code:ty, $lang:ident) => {
252 impl Npm for $code {
253 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
254 use $lang::*;
255
256 if Self::is_func_space(node) && stats.is_disabled() {
257 stats.is_class_space = true;
258 }
259
260 match node.kind_id().into() {
261 ClassBody | EnumBodyDeclarations => {
262 for method in node.children().filter(|n| Self::is_func(n)) {
263 stats.class_nm += 1;
264 if let Some(modifiers) = method.child(0)
267 && matches!(modifiers.kind_id().into(), Modifiers)
268 && modifiers.first_child(|id| id == Public).is_some()
269 {
270 stats.class_npm += 1;
271 }
272 }
273 }
274 InterfaceBody => {
275 stats.interface_nm += node.children().filter(|n| Self::is_func(n)).count();
276 stats.interface_npm = stats.interface_nm;
277 }
278 AnnotationTypeBody => {
279 stats.interface_nm += node
280 .children()
281 .filter(|n| {
282 matches!(n.kind_id().into(), AnnotationTypeElementDeclaration)
283 })
284 .count();
285 stats.interface_npm = stats.interface_nm;
286 }
287 _ => {}
288 }
289 }
290 }
291 };
292}
293
294macro_rules! ts_npm_compute {
322 ($lang:ident) => {
323 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
324 use $lang::*;
325
326 if Self::is_func_space(node) && stats.is_disabled() {
327 stats.is_class_space = true;
328 }
329
330 match node.kind_id().into() {
331 ClassBody => {
332 for member in node.children() {
333 match member.kind_id().into() {
334 MethodDefinition | AbstractMethodSignature => {
335 stats.class_nm += 1;
336 if ts_member_is_public!($lang, member) {
337 stats.class_npm += 1;
338 }
339 }
340 PublicFieldDefinition
343 if member
344 .first_child(|id| {
345 id == $lang::ArrowFunction
346 || id == $lang::FunctionExpression
347 })
348 .is_some() =>
349 {
350 stats.class_nm += 1;
351 if ts_member_is_public!($lang, member) {
352 stats.class_npm += 1;
353 }
354 }
355 _ => {}
356 }
357 }
358 }
359 InterfaceBody => {
360 let count = node
361 .children()
362 .filter(|c| {
363 matches!(
364 c.kind_id().into(),
365 MethodSignature | AbstractMethodSignature | ConstructSignature
366 )
367 })
368 .count();
369 stats.interface_nm += count;
370 stats.interface_npm = stats.interface_nm;
371 }
372 _ => {}
373 }
374 }
375 };
376}
377
378macro_rules! js_npm_compute {
395 ($lang:ident) => {
396 fn compute<'a>(node: &Node<'a>, _code: &'a [u8], stats: &mut Stats) {
397 use $lang::*;
398
399 if Self::is_func_space(node) && stats.is_disabled() {
400 stats.is_class_space = true;
401 }
402
403 if !matches!(node.kind_id().into(), ClassBody) {
404 return;
405 }
406
407 for member in node.children() {
408 match member.kind_id().into() {
409 MethodDefinition => {
410 stats.class_nm += 1;
411 stats.class_npm += 1;
412 }
413 FieldDefinition
414 if member
415 .first_child(|id| {
416 id == $lang::ArrowFunction || id == $lang::FunctionExpression
417 })
418 .is_some() =>
419 {
420 stats.class_nm += 1;
421 stats.class_npm += 1;
422 }
423 _ => {}
424 }
425 }
426 }
427 };
428}
429
430mod cpp;
435mod csharp;
436mod elixir;
437mod go;
438mod groovy;
439mod java;
440mod javascript;
441mod kotlin;
442mod mozcpp;
443mod mozjs;
444mod objc;
445mod php;
446mod python;
447mod ruby;
448mod rust;
449mod tsx;
450mod typescript;
451
452implement_metric_trait!(
458 Npm,
459 CCode,
460 PreprocCode,
461 CcommentCode,
462 PerlCode,
463 BashCode,
464 LuaCode,
465 TclCode,
466 IrulesCode
467);
468
469#[cfg(test)]
470#[allow(
471 clippy::float_cmp,
472 clippy::cast_precision_loss,
473 clippy::cast_possible_truncation,
474 clippy::cast_sign_loss,
475 clippy::similar_names,
476 clippy::doc_markdown,
477 clippy::needless_raw_string_hashes,
478 clippy::too_many_lines
479)]
480mod tests {
481 use crate::tools::{assert_child_space_kind, check_func_space, check_metrics};
482
483 use super::*;
484
485 #[test]
486 fn java_constructors() {
487 check_metrics::<JavaParser>(
488 "class X {
489 X() {}
490 private X(int a) {}
491 protected X(int a, int b) {}
492 public X(int a, int b, int c) {} // +1
493 }",
494 "foo.java",
495 |metric| {
496 insta::assert_json_snapshot!(
497 metric.npm,
498 @r#"
499 {
500 "class_npm_sum": 1,
501 "interface_npm_sum": 0,
502 "class_methods": 4,
503 "interface_methods": 0,
504 "class_coa": 0.25,
505 "interface_coa": 0.0,
506 "total": 1,
507 "total_methods": 4,
508 "coa": 0.25
509 }
510 "#
511 );
512 },
513 );
514 }
515
516 #[test]
517 fn groovy_no_methods() {
518 check_metrics::<GroovyParser>("class A { int x = 1 }", "foo.groovy", |metric| {
519 assert_eq!(metric.npm.total_nm(), 0);
520 });
521 }
522
523 #[test]
524 fn groovy_public_methods() {
525 check_metrics::<GroovyParser>(
526 "class A {
527 public void m1() {}
528 public int m2() { return 0 }
529 private void m3() {}
530 }",
531 "foo.groovy",
532 |metric| {
533 assert_eq!(metric.npm.class_nm_sum(), 3);
534 assert_eq!(metric.npm.class_npm_sum(), 2);
535 },
536 );
537 }
538
539 #[test]
540 fn groovy_interface_methods_implicitly_public() {
541 check_func_space::<GroovyParser, _>(
548 "interface I {
549 void a()
550 int b()
551 }",
552 "foo.groovy",
553 |func_space| {
554 let metric = &func_space.metrics;
555 assert_eq!(metric.npm.interface_nm_sum(), 2);
557 assert_eq!(metric.npm.interface_npm_sum(), 2);
558 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
559 },
560 );
561 }
562
563 #[test]
566 fn groovy_enum_counts_methods() {
567 check_metrics::<GroovyParser>(
568 "enum Status {
569 ACTIVE, INACTIVE;
570 public int code() { return 0 }
571 private void reset() {}
572 }",
573 "foo.groovy",
574 |metric| {
575 assert_eq!(metric.npm.class_nm_sum(), 2);
576 assert_eq!(metric.npm.class_npm_sum(), 1);
577 },
578 );
579 }
580
581 #[test]
582 #[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"]
583 fn groovy_annotation_type_counts_elements() {
584 check_func_space::<GroovyParser, _>(
595 "public @interface Marker {
596 String value() default \"\";
597 int priority() default 0;
598 }",
599 "foo.groovy",
600 |func_space| {
601 assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2);
602 assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2);
603 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
604 },
605 );
606 }
607
608 #[test]
609 fn groovy_constructors() {
610 check_metrics::<GroovyParser>(
611 "class X {
612 X() {}
613 private X(int a) {}
614 protected X(int a, int b) {}
615 public X(int a, int b, int c) {}
616 }",
617 "foo.groovy",
618 |metric| {
619 assert_eq!(metric.npm.class_nm_sum(), 4);
621 assert_eq!(metric.npm.class_npm_sum(), 1);
622 },
623 );
624 }
625
626 #[test]
627 fn groovy_no_methods_in_unit_scope() {
628 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
629 assert_eq!(metric.npm.total_nm(), 0);
630 });
631 }
632
633 #[test]
634 fn groovy_multiple_classes_methods() {
635 check_metrics::<GroovyParser>(
636 "class A { public void a() {} }
637 class B { public void b() {} }",
638 "foo.groovy",
639 |metric| {
640 assert_eq!(metric.npm.class_nm_sum(), 2);
641 assert_eq!(metric.npm.class_npm_sum(), 2);
642 },
643 );
644 }
645
646 #[test]
647 fn groovy_methods_returning_primitive_types() {
648 check_metrics::<GroovyParser>(
652 "class X {
653 public byte a() {}
654 public int b() {}
655 public double c() {}
656 public boolean d() {}
657 byte e() {}
658 int f() {}
659 }",
660 "foo.groovy",
661 |metric| {
662 assert_eq!(metric.npm.class_nm_sum(), 6);
664 assert_eq!(metric.npm.class_npm_sum(), 4);
665 },
666 );
667 }
668
669 #[test]
670 fn groovy_methods_with_generic_types() {
671 check_metrics::<GroovyParser>(
673 "class X {
674 public List<String> a() {}
675 public Map<String, Integer> b() {}
676 List<Integer> c() {}
677 }",
678 "foo.groovy",
679 |metric| {
680 assert_eq!(metric.npm.class_nm_sum(), 3);
681 assert_eq!(metric.npm.class_npm_sum(), 2);
682 },
683 );
684 }
685
686 #[test]
687 fn groovy_method_modifiers() {
688 check_metrics::<GroovyParser>(
692 "abstract class X {
693 public static void a() {}
694 static public void b() {}
695 public final void c() {}
696 final public void d() {}
697 protected static void e() {}
698 static protected void f() {}
699 abstract public void g()
700 abstract void h()
701 }",
702 "foo.groovy",
703 |metric| {
704 assert_eq!(metric.npm.class_nm_sum(), 8);
706 assert_eq!(metric.npm.class_npm_sum(), 5);
707 },
708 );
709 }
710
711 #[test]
712 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies"]
713 fn groovy_nested_inner_classes() {
714 check_metrics::<GroovyParser>(
717 "class X {
718 public void a() {}
719 class Y {
720 public void b() {}
721 class Z {
722 public void c() {}
723 }
724 }
725 }",
726 "foo.groovy",
727 |metric| {
728 assert_eq!(metric.npm.class_nm_sum(), 3);
730 assert_eq!(metric.npm.class_npm_sum(), 3);
731 },
732 );
733 }
734
735 #[test]
736 #[ignore = "dekobon Groovy grammar v1 does not yet support anonymous inner classes (`new T() { … }`)"]
737 fn groovy_anonymous_inner_class() {
738 check_metrics::<GroovyParser>(
741 "class X {
742 public Runnable r = new Runnable() {
743 public void run() {}
744 void helper() {}
745 }
746 }",
747 "foo.groovy",
748 |metric| {
749 assert_eq!(metric.npm.class_nm_sum(), 2);
752 assert_eq!(metric.npm.class_npm_sum(), 1);
753 },
754 );
755 }
756
757 #[test]
758 fn groovy_interfaces_and_class() {
759 check_func_space::<GroovyParser, _>(
765 "interface X {
766 void a()
767 }
768 interface Y extends X {
769 void b()
770 void c()
771 }
772 class Z implements Y {
773 public void a() {}
774 public void b() {}
775 public void c() {}
776 void d() {}
777 void e() {}
778 }",
779 "foo.groovy",
780 |func_space| {
781 let metric = &func_space.metrics;
782 assert_eq!(metric.npm.interface_nm_sum(), 3);
784 assert_eq!(metric.npm.interface_npm_sum(), 3);
785 assert_eq!(metric.npm.class_nm_sum(), 5);
788 assert_eq!(metric.npm.class_npm_sum(), 3);
789 assert_child_space_kind(&func_space, "X", SpaceKind::Interface);
790 assert_child_space_kind(&func_space, "Y", SpaceKind::Interface);
791 assert_child_space_kind(&func_space, "Z", SpaceKind::Class);
792 },
793 );
794 }
795
796 #[test]
797 fn java_methods_returning_primitive_types() {
798 check_metrics::<JavaParser>(
799 "class X {
800 public byte a() {} // +1
801 public short b() {} // +1
802 public int c() {} // +1
803 public long d() {} // +1
804 public float e() {} // +1
805 public double f() {} // +1
806 public boolean g() {} // +1
807 public char h() {} // +1
808 byte i() {}
809 short j() {}
810 int k() {}
811 long l() {}
812 float m() {}
813 double n() {}
814 boolean o() {}
815 char p() {}
816 }",
817 "foo.java",
818 |metric| {
819 insta::assert_json_snapshot!(
820 metric.npm,
821 @r#"
822 {
823 "class_npm_sum": 8,
824 "interface_npm_sum": 0,
825 "class_methods": 16,
826 "interface_methods": 0,
827 "class_coa": 0.5,
828 "interface_coa": 0.0,
829 "total": 8,
830 "total_methods": 16,
831 "coa": 0.5
832 }
833 "#
834 );
835 },
836 );
837 }
838
839 #[test]
840 fn java_methods_returning_arrays() {
841 check_metrics::<JavaParser>(
842 "class X {
843 public byte[] a() {} // +1
844 public short[] b() {} // +1
845 public int[] c() {} // +1
846 public long[] d() {} // +1
847 public float[] e() {} // +1
848 public double[] f() {} // +1
849 public boolean[] g() {} // +1
850 public char[] h() {} // +1
851 byte[] i() {}
852 short[] j() {}
853 int[] k() {}
854 long[] l() {}
855 float[] m() {}
856 double[] n() {}
857 boolean[] o() {}
858 char[] p() {}
859 }",
860 "foo.java",
861 |metric| {
862 insta::assert_json_snapshot!(
863 metric.npm,
864 @r#"
865 {
866 "class_npm_sum": 8,
867 "interface_npm_sum": 0,
868 "class_methods": 16,
869 "interface_methods": 0,
870 "class_coa": 0.5,
871 "interface_coa": 0.0,
872 "total": 8,
873 "total_methods": 16,
874 "coa": 0.5
875 }
876 "#
877 );
878 },
879 );
880 }
881
882 #[test]
883 fn java_methods_returning_objects() {
884 check_metrics::<JavaParser>(
885 "class X {
886 public Integer[] a() {} // +1
887 public Integer b() {} // +1
888 public String[] c() {} // +1
889 public String d() {} // +1
890 public Y[] e() {} // +1
891 public Y f() {} // +1
892 Integer[] g() {}
893 Integer h() {}
894 String[] i() {}
895 String j() {}
896 Y[] k() {}
897 Y l() {}
898 }",
899 "foo.java",
900 |metric| {
901 insta::assert_json_snapshot!(
902 metric.npm,
903 @r#"
904 {
905 "class_npm_sum": 6,
906 "interface_npm_sum": 0,
907 "class_methods": 12,
908 "interface_methods": 0,
909 "class_coa": 0.5,
910 "interface_coa": 0.0,
911 "total": 6,
912 "total_methods": 12,
913 "coa": 0.5
914 }
915 "#
916 );
917 },
918 );
919 }
920
921 #[test]
922 fn java_methods_with_generic_types() {
923 check_metrics::<JavaParser>(
924 "class X {
925 public <T, S extends T> void a(T x, S y) {} // +1
926 public <T, S> int b(T x, S y) {} // +1
927 public <T> boolean c(T x) {} // +1
928 public <T> ArrayList<T> d() {} // +1
929 public Y<String> e() {} // +1
930 <T, S extends T> void f(T x, S y) {}
931 <T, S> int g(T x, S y) {}
932 <T> boolean h(T x) {}
933 <T> ArrayList<T> i() {}
934 Y<String> j() {}
935 }",
936 "foo.java",
937 |metric| {
938 insta::assert_json_snapshot!(
939 metric.npm,
940 @r#"
941 {
942 "class_npm_sum": 5,
943 "interface_npm_sum": 0,
944 "class_methods": 10,
945 "interface_methods": 0,
946 "class_coa": 0.5,
947 "interface_coa": 0.0,
948 "total": 5,
949 "total_methods": 10,
950 "coa": 0.5
951 }
952 "#
953 );
954 },
955 );
956 }
957
958 #[test]
959 fn java_method_modifiers() {
960 check_metrics::<JavaParser>(
961 "abstract class X {
962 public static final synchronized strictfp void a() {} // +1
963 static public final synchronized strictfp void b() {} // +1
964 static final public synchronized strictfp void c() {} // +1
965 static final synchronized public strictfp void d() {} // +1
966 static final synchronized strictfp public void e() {} // +1
967 protected static final synchronized native void f();
968 static protected final synchronized native void g();
969 static final protected synchronized native void h();
970 static final synchronized protected native void i();
971 static final synchronized native protected void j();
972 abstract public void k(); // +1
973 abstract void l();
974 }",
975 "foo.java",
976 |metric| {
977 insta::assert_json_snapshot!(
978 metric.npm,
979 @r#"
980 {
981 "class_npm_sum": 6,
982 "interface_npm_sum": 0,
983 "class_methods": 12,
984 "interface_methods": 0,
985 "class_coa": 0.5,
986 "interface_coa": 0.0,
987 "total": 6,
988 "total_methods": 12,
989 "coa": 0.5
990 }
991 "#
992 );
993 },
994 );
995 }
996
997 #[test]
998 fn java_classes() {
999 check_metrics::<JavaParser>(
1000 "class X {
1001 public void a() {} // +1
1002 public void b() {} // +1
1003 private void c() {}
1004 }
1005 class Y {
1006 private void d() {}
1007 private void e() {}
1008 public void f() {} // +1
1009 }",
1010 "foo.java",
1011 |metric| {
1012 insta::assert_json_snapshot!(
1013 metric.npm,
1014 @r#"
1015 {
1016 "class_npm_sum": 3,
1017 "interface_npm_sum": 0,
1018 "class_methods": 6,
1019 "interface_methods": 0,
1020 "class_coa": 0.5,
1021 "interface_coa": 0.0,
1022 "total": 3,
1023 "total_methods": 6,
1024 "coa": 0.5
1025 }
1026 "#
1027 );
1028 },
1029 );
1030 }
1031
1032 #[test]
1033 fn java_nested_inner_classes() {
1034 check_metrics::<JavaParser>(
1035 "class X {
1036 public void a() {} // +1
1037 class Y {
1038 public void b() {} // +1
1039 class Z {
1040 public void c() {} // +1
1041 }
1042 }
1043 }",
1044 "foo.java",
1045 |metric| {
1046 insta::assert_json_snapshot!(
1047 metric.npm,
1048 @r#"
1049 {
1050 "class_npm_sum": 3,
1051 "interface_npm_sum": 0,
1052 "class_methods": 3,
1053 "interface_methods": 0,
1054 "class_coa": 1.0,
1055 "interface_coa": 0.0,
1056 "total": 3,
1057 "total_methods": 3,
1058 "coa": 1.0
1059 }
1060 "#
1061 );
1062 },
1063 );
1064 }
1065
1066 #[test]
1067 fn java_local_inner_classes() {
1068 check_metrics::<JavaParser>(
1069 "class X {
1070 public void a() { // +1
1071 class Y {
1072 public void b() { // +1
1073 class Z {
1074 public void c() {} // +1
1075 }
1076 }
1077 }
1078 }
1079 }",
1080 "foo.java",
1081 |metric| {
1082 insta::assert_json_snapshot!(
1083 metric.npm,
1084 @r#"
1085 {
1086 "class_npm_sum": 3,
1087 "interface_npm_sum": 0,
1088 "class_methods": 3,
1089 "interface_methods": 0,
1090 "class_coa": 1.0,
1091 "interface_coa": 0.0,
1092 "total": 3,
1093 "total_methods": 3,
1094 "coa": 1.0
1095 }
1096 "#
1097 );
1098 },
1099 );
1100 }
1101
1102 #[test]
1103 fn java_anonymous_inner_classes() {
1104 check_metrics::<JavaParser>(
1105 "abstract class X {
1106 public abstract void a(); // +1
1107 }
1108 abstract class Y {
1109 abstract void b();
1110 }
1111 class Z {
1112 public void c(){ // +1
1113 X x = new X() {
1114 @Override
1115 public void a() {} // +1
1116 };
1117 Y y = new Y() {
1118 @Override
1119 void b() {}
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": 5,
1132 "interface_methods": 0,
1133 "class_coa": 0.6,
1134 "interface_coa": 0.0,
1135 "total": 3,
1136 "total_methods": 5,
1137 "coa": 0.6
1138 }
1139 "#
1140 );
1141 },
1142 );
1143 }
1144
1145 #[test]
1146 fn java_interface() {
1147 check_metrics::<JavaParser>(
1148 "interface X {
1149 public int a(); // +1
1150 boolean b(); // +1
1151 void c(); // +1
1152 }",
1153 "foo.java",
1154 |metric| {
1155 insta::assert_json_snapshot!(
1156 metric.npm,
1157 @r#"
1158 {
1159 "class_npm_sum": 0,
1160 "interface_npm_sum": 3,
1161 "class_methods": 0,
1162 "interface_methods": 3,
1163 "class_coa": 0.0,
1164 "interface_coa": 1.0,
1165 "total": 3,
1166 "total_methods": 3,
1167 "coa": 1.0
1168 }
1169 "#
1170 );
1171 },
1172 );
1173 }
1174
1175 #[test]
1179 fn java_enum_counts_methods() {
1180 check_metrics::<JavaParser>(
1181 "enum Status {
1182 ACTIVE, INACTIVE;
1183 public int code() { return 0; } // +1 public
1184 private void reset() {} // not public
1185 }",
1186 "foo.java",
1187 |metric| {
1188 assert_eq!(metric.npm.class_nm_sum(), 2);
1189 assert_eq!(metric.npm.class_npm_sum(), 1);
1190 },
1191 );
1192 }
1193
1194 #[test]
1197 fn java_record_counts_methods() {
1198 check_metrics::<JavaParser>(
1199 "record Point(int x, int y) {
1200 public int sum() { return x + y; }
1201 public Point() { this(0, 0); }
1202 }",
1203 "foo.java",
1204 |metric| {
1205 assert_eq!(metric.npm.class_nm_sum(), 2);
1210 assert_eq!(metric.npm.class_npm_sum(), 2);
1211 },
1212 );
1213 }
1214
1215 #[test]
1216 fn java_annotation_type_counts_elements() {
1217 check_func_space::<JavaParser, _>(
1227 "@interface Marker {
1228 String value() default \"\";
1229 int priority() default 0;
1230 }",
1231 "foo.java",
1232 |func_space| {
1233 assert_eq!(func_space.metrics.npm.interface_nm_sum(), 2);
1234 assert_eq!(func_space.metrics.npm.interface_npm_sum(), 2);
1235 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1236 },
1237 );
1238 }
1239
1240 #[test]
1241 fn java_interfaces_and_class() {
1242 check_metrics::<JavaParser>(
1243 "interface X {
1244 void a(); // +1
1245 }
1246 interface Y extends X {
1247 void b(); // +1
1248 void c(); // +1
1249 }
1250 class Z implements Y {
1251 @Override
1252 public void a() {} // +1
1253 @Override
1254 public void b() {} // +1
1255 @Override
1256 public void c() {} // +1
1257 void d() {}
1258 void e() {}
1259 }",
1260 "foo.java",
1261 |metric| {
1262 insta::assert_json_snapshot!(
1263 metric.npm,
1264 @r#"
1265 {
1266 "class_npm_sum": 3,
1267 "interface_npm_sum": 3,
1268 "class_methods": 5,
1269 "interface_methods": 3,
1270 "class_coa": 0.6,
1271 "interface_coa": 1.0,
1272 "total": 6,
1273 "total_methods": 8,
1274 "coa": 0.75
1275 }
1276 "#
1277 );
1278 },
1279 );
1280 }
1281
1282 #[test]
1283 fn csharp_constructors() {
1284 check_metrics::<CsharpParser>(
1285 "class A {
1286 public A() {}
1287 public A(int x) {}
1288 A(int x, int y) {}
1289 }",
1290 "foo.cs",
1291 |metric| insta::assert_json_snapshot!(metric.npm),
1292 );
1293 }
1294
1295 #[test]
1296 fn csharp_methods_returning_primitive_types() {
1297 check_metrics::<CsharpParser>(
1298 "class A {
1299 public int M1() { return 1; }
1300 public bool M2() { return true; }
1301 public double M3() { return 0.0; }
1302 int M4() { return 0; }
1303 }",
1304 "foo.cs",
1305 |metric| insta::assert_json_snapshot!(metric.npm),
1306 );
1307 }
1308
1309 #[test]
1310 fn csharp_methods_returning_arrays() {
1311 check_metrics::<CsharpParser>(
1312 "class A {
1313 public int[] M1() { return new int[0]; }
1314 public string[] M2() { return new string[0]; }
1315 int[] M3() { return new int[0]; }
1316 }",
1317 "foo.cs",
1318 |metric| insta::assert_json_snapshot!(metric.npm),
1319 );
1320 }
1321
1322 #[test]
1323 fn csharp_methods_returning_objects() {
1324 check_metrics::<CsharpParser>(
1325 "class Point { }
1326 class A {
1327 public Point M1() { return new Point(); }
1328 public string M2() { return \"\"; }
1329 Point M3() { return new Point(); }
1330 }",
1331 "foo.cs",
1332 |metric| insta::assert_json_snapshot!(metric.npm),
1333 );
1334 }
1335
1336 #[test]
1337 fn csharp_methods_with_generic_types() {
1338 check_metrics::<CsharpParser>(
1339 "class A {
1340 public System.Collections.Generic.List<int> M1() { return null; }
1341 public System.Collections.Generic.Dictionary<string, int> M2() { return null; }
1342 System.Collections.Generic.List<string> M3() { return null; }
1343 }",
1344 "foo.cs",
1345 |metric| insta::assert_json_snapshot!(metric.npm),
1346 );
1347 }
1348
1349 #[test]
1350 fn csharp_method_modifiers() {
1351 check_metrics::<CsharpParser>(
1352 "class A {
1353 public void M1() {}
1354 private void M2() {}
1355 protected void M3() {}
1356 internal void M4() {}
1357 public static void M5() {}
1358 public virtual void M6() {}
1359 }",
1360 "foo.cs",
1361 |metric| insta::assert_json_snapshot!(metric.npm),
1362 );
1363 }
1364
1365 #[test]
1366 fn csharp_classes() {
1367 check_metrics::<CsharpParser>(
1368 "class A {
1369 public void M1() {}
1370 public void M2() {}
1371 void M3() {}
1372 }
1373 class B {
1374 public int N() { return 0; }
1375 int Hidden() { return 0; }
1376 }",
1377 "foo.cs",
1378 |metric| insta::assert_json_snapshot!(metric.npm),
1379 );
1380 }
1381
1382 #[test]
1383 fn csharp_nested_inner_classes() {
1384 check_metrics::<CsharpParser>(
1385 "class Outer {
1386 public void M() {}
1387 void Hidden() {}
1388 public class Inner {
1389 public void N() {}
1390 void HiddenN() {}
1391 }
1392 }",
1393 "foo.cs",
1394 |metric| insta::assert_json_snapshot!(metric.npm),
1395 );
1396 }
1397
1398 #[test]
1399 fn csharp_property_accessors() {
1400 check_metrics::<CsharpParser>(
1405 "class A {
1406 int _w;
1407 public int X { get; set; }
1408 public int Y { get; }
1409 public int Z { get; init; }
1410 public int W => _w;
1411 int Hidden { get; set; }
1412 }",
1413 "foo.cs",
1414 |metric| insta::assert_json_snapshot!(metric.npm),
1415 );
1416 }
1417
1418 #[test]
1419 fn csharp_narrowed_accessor_visibility() {
1420 check_metrics::<CsharpParser>(
1433 "class A {
1434 public int X { get; private set; }
1435 public int this[int i] { get; protected set; }
1436 public int Y { get; set; }
1437 public int W { get; }
1438 public int Z => 0;
1439 int P { get; set; }
1440 }",
1441 "foo.cs",
1442 |metric| {
1443 assert_eq!(metric.npm.class_nm_sum(), 10, "all accessors count as nm");
1444 assert_eq!(
1445 metric.npm.class_npm_sum(),
1446 6,
1447 "narrowed private/protected accessors are not public methods"
1448 );
1449 insta::assert_json_snapshot!(metric.npm);
1450 },
1451 );
1452 }
1453
1454 #[test]
1455 fn csharp_local_functions() {
1456 check_metrics::<CsharpParser>(
1463 "class A {
1464 public void Outer() {
1465 void Local() {}
1466 Local();
1467 }
1468 private void Hidden() {}
1469 }",
1470 "foo.cs",
1471 |metric| {
1472 assert_eq!(metric.npm.class_nm_sum(), 2, "Local must not leak");
1473 assert_eq!(metric.npm.class_npm_sum(), 1, "only Outer is public");
1474 insta::assert_json_snapshot!(metric.npm);
1475 },
1476 );
1477 }
1478
1479 #[test]
1480 fn csharp_interface() {
1481 check_metrics::<CsharpParser>(
1483 "interface I {
1484 int M1();
1485 bool M2();
1486 int X { get; set; }
1487 }",
1488 "foo.cs",
1489 |metric| insta::assert_json_snapshot!(metric.npm),
1490 );
1491 }
1492
1493 #[test]
1494 fn csharp_interfaces_and_class() {
1495 check_metrics::<CsharpParser>(
1496 "interface I1 { int M1(); }
1497 interface I2 { bool M2(); float M3(); }
1498 class A {
1499 public void M() {}
1500 void Hidden() {}
1501 }",
1502 "foo.cs",
1503 |metric| insta::assert_json_snapshot!(metric.npm),
1504 );
1505 }
1506
1507 #[test]
1508 fn php_no_class_methods() {
1509 check_metrics::<PhpParser>(
1510 "<?php class A { public int $x = 0; }",
1511 "foo.php",
1512 |metric| insta::assert_json_snapshot!(metric.npm),
1513 );
1514 }
1515
1516 #[test]
1517 fn php_one_public_method() {
1518 check_metrics::<PhpParser>(
1519 "<?php class A { public function f(): void {} }",
1520 "foo.php",
1521 |metric| insta::assert_json_snapshot!(metric.npm),
1522 );
1523 }
1524
1525 #[test]
1526 fn php_one_private_method() {
1527 check_metrics::<PhpParser>(
1528 "<?php class A { private function f(): void {} }",
1529 "foo.php",
1530 |metric| insta::assert_json_snapshot!(metric.npm),
1531 );
1532 }
1533
1534 #[test]
1535 fn php_one_protected_method() {
1536 check_metrics::<PhpParser>(
1537 "<?php class A { protected function f(): void {} }",
1538 "foo.php",
1539 |metric| insta::assert_json_snapshot!(metric.npm),
1540 );
1541 }
1542
1543 #[test]
1544 fn php_mixed_visibility_methods() {
1545 check_metrics::<PhpParser>(
1546 "<?php
1547 class A {
1548 public function a(): void {}
1549 public function b(): void {}
1550 private function c(): void {}
1551 protected function d(): void {}
1552 }",
1553 "foo.php",
1554 |metric| insta::assert_json_snapshot!(metric.npm),
1555 );
1556 }
1557
1558 #[test]
1559 fn php_static_public_method() {
1560 check_metrics::<PhpParser>(
1561 "<?php class A { public static function f(): void {} }",
1562 "foo.php",
1563 |metric| insta::assert_json_snapshot!(metric.npm),
1564 );
1565 }
1566
1567 #[test]
1568 fn php_abstract_method() {
1569 check_metrics::<PhpParser>(
1570 "<?php abstract class A { abstract public function f(): void; }",
1571 "foo.php",
1572 |metric| insta::assert_json_snapshot!(metric.npm),
1573 );
1574 }
1575
1576 #[test]
1577 fn php_final_public_method() {
1578 check_metrics::<PhpParser>(
1579 "<?php class A { final public function f(): void {} }",
1580 "foo.php",
1581 |metric| insta::assert_json_snapshot!(metric.npm),
1582 );
1583 }
1584
1585 #[test]
1586 fn php_interface_methods() {
1587 check_metrics::<PhpParser>(
1589 "<?php
1590 interface I {
1591 public function a(): void;
1592 public function b(): int;
1593 }",
1594 "foo.php",
1595 |metric| insta::assert_json_snapshot!(metric.npm),
1596 );
1597 }
1598
1599 #[test]
1600 fn php_enum_methods() {
1601 check_metrics::<PhpParser>(
1603 "<?php
1604 enum Color {
1605 case Red;
1606 case Green;
1607 public function label(): string {
1608 return match ($this) {
1609 Color::Red => 'r',
1610 Color::Green => 'g',
1611 };
1612 }
1613 }",
1614 "foo.php",
1615 |metric| insta::assert_json_snapshot!(metric.npm),
1616 );
1617 }
1618
1619 #[test]
1620 fn php_trait_methods() {
1621 check_metrics::<PhpParser>(
1622 "<?php
1623 trait T {
1624 public function a(): void {}
1625 private function b(): void {}
1626 }",
1627 "foo.php",
1628 |metric| insta::assert_json_snapshot!(metric.npm),
1629 );
1630 }
1631
1632 #[test]
1633 fn php_no_explicit_visibility_method_excluded() {
1634 check_metrics::<PhpParser>(
1637 "<?php class A { function f(): void {} }",
1638 "foo.php",
1639 |metric| insta::assert_json_snapshot!(metric.npm),
1640 );
1641 }
1642
1643 #[test]
1646 fn kotlin_empty_class_no_methods() {
1647 check_metrics::<KotlinParser>("class C {}", "foo.kt", |metric| {
1648 assert_eq!(metric.npm.class_npm_sum(), 0);
1649 assert_eq!(metric.npm.class_nm_sum(), 0);
1650 assert_eq!(metric.npm.interface_nm_sum(), 0);
1651 insta::assert_json_snapshot!(metric.npm);
1652 });
1653 }
1654
1655 #[test]
1656 fn kotlin_public_methods_default() {
1657 check_metrics::<KotlinParser>(
1659 "class C {
1660 fun a() {}
1661 fun b(): Int = 0
1662 fun c(x: Int): Int = x
1663 }",
1664 "foo.kt",
1665 |metric| {
1666 assert_eq!(metric.npm.class_npm_sum(), 3);
1667 assert_eq!(metric.npm.class_nm_sum(), 3);
1668 insta::assert_json_snapshot!(metric.npm);
1669 },
1670 );
1671 }
1672
1673 #[test]
1674 fn kotlin_private_method() {
1675 check_metrics::<KotlinParser>(
1676 "class C {
1677 fun a() {} // public
1678 private fun b() {} // private
1679 fun c() {} // public
1680 }",
1681 "foo.kt",
1682 |metric| {
1683 assert_eq!(metric.npm.class_npm_sum(), 2);
1684 assert_eq!(metric.npm.class_nm_sum(), 3);
1685 insta::assert_json_snapshot!(metric.npm);
1686 },
1687 );
1688 }
1689
1690 #[test]
1691 fn kotlin_protected_internal_methods() {
1692 check_metrics::<KotlinParser>(
1693 "open class C {
1694 protected fun a() {}
1695 internal fun b() {}
1696 public fun c() {}
1697 }",
1698 "foo.kt",
1699 |metric| {
1700 assert_eq!(metric.npm.class_npm_sum(), 1);
1701 assert_eq!(metric.npm.class_nm_sum(), 3);
1702 insta::assert_json_snapshot!(metric.npm);
1703 },
1704 );
1705 }
1706
1707 #[test]
1708 fn kotlin_secondary_constructor_counts() {
1709 check_metrics::<KotlinParser>(
1712 "class C {
1713 private var a: Int = 0
1714 constructor(n: Int) { a = n }
1715 constructor(n: Int, m: Int) { a = n + m }
1716 fun get(): Int = a
1717 }",
1718 "foo.kt",
1719 |metric| {
1720 assert_eq!(metric.npm.class_npm_sum(), 3);
1721 assert_eq!(metric.npm.class_nm_sum(), 3);
1722 insta::assert_json_snapshot!(metric.npm);
1723 },
1724 );
1725 }
1726
1727 #[test]
1728 fn kotlin_companion_object_methods() {
1729 check_metrics::<KotlinParser>(
1732 "class Holder {
1733 fun memberFn() {}
1734 companion object {
1735 fun staticFn() {}
1736 private fun secret() {}
1737 }
1738 }",
1739 "foo.kt",
1740 |metric| {
1741 assert_eq!(metric.npm.class_npm_sum(), 2);
1742 assert_eq!(metric.npm.class_nm_sum(), 3);
1743 insta::assert_json_snapshot!(metric.npm);
1744 },
1745 );
1746 }
1747
1748 #[test]
1749 fn kotlin_data_class_methods() {
1750 check_metrics::<KotlinParser>(
1753 "data class Point(val x: Int, val y: Int) {
1754 fun manhattan(): Int = x + y
1755 private fun internal_(): Int = 0
1756 }",
1757 "foo.kt",
1758 |metric| {
1759 assert_eq!(metric.npm.class_npm_sum(), 1);
1760 assert_eq!(metric.npm.class_nm_sum(), 2);
1761 insta::assert_json_snapshot!(metric.npm);
1762 },
1763 );
1764 }
1765
1766 #[test]
1767 fn kotlin_object_singleton_methods() {
1768 check_metrics::<KotlinParser>(
1769 "object Util {
1770 fun add(a: Int, b: Int): Int = a + b
1771 private fun helper(): Int = 0
1772 }",
1773 "foo.kt",
1774 |metric| {
1775 assert_eq!(metric.npm.class_npm_sum(), 1);
1776 assert_eq!(metric.npm.class_nm_sum(), 2);
1777 insta::assert_json_snapshot!(metric.npm);
1778 },
1779 );
1780 }
1781
1782 #[test]
1783 fn kotlin_interface_methods() {
1784 check_func_space::<KotlinParser, _>(
1785 "interface I {
1786 fun work(): Int
1787 fun describe(): String
1788 }",
1789 "foo.kt",
1790 |func_space| {
1791 let metric = &func_space.metrics;
1792 assert_eq!(metric.npm.interface_npm_sum(), 2);
1793 assert_eq!(metric.npm.interface_nm_sum(), 2);
1794 assert_eq!(metric.npm.class_nm_sum(), 0);
1795 insta::assert_json_snapshot!(metric.npm);
1796 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1797 },
1798 );
1799 }
1800
1801 #[test]
1802 fn kotlin_interface_with_default_method() {
1803 check_func_space::<KotlinParser, _>(
1804 "interface I {
1805 fun abs(n: Int): Int {
1806 return if (n < 0) -n else n
1807 }
1808 fun pure(): Int
1809 }",
1810 "foo.kt",
1811 |func_space| {
1812 let metric = &func_space.metrics;
1813 assert_eq!(metric.npm.interface_npm_sum(), 2);
1814 assert_eq!(metric.npm.interface_nm_sum(), 2);
1815 insta::assert_json_snapshot!(metric.npm);
1816 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
1817 },
1818 );
1819 }
1820
1821 #[test]
1822 fn kotlin_override_fun_counts() {
1823 check_metrics::<KotlinParser>(
1824 "open class Base {
1825 open fun greet(): String = \"hi\"
1826 }
1827 class Sub : Base() {
1828 override fun greet(): String = \"yo\"
1829 private fun secret() {}
1830 }",
1831 "foo.kt",
1832 |metric| {
1833 assert_eq!(metric.npm.class_npm_sum(), 2);
1837 assert_eq!(metric.npm.class_nm_sum(), 3);
1838 insta::assert_json_snapshot!(metric.npm);
1839 },
1840 );
1841 }
1842
1843 #[test]
1844 fn kotlin_nested_class_methods() {
1845 check_metrics::<KotlinParser>(
1846 "class Outer {
1847 fun outerM() {}
1848 class Nested {
1849 fun nestedM() {}
1850 private fun nestedSecret() {}
1851 }
1852 }",
1853 "foo.kt",
1854 |metric| {
1855 assert_eq!(metric.npm.class_npm_sum(), 2);
1856 assert_eq!(metric.npm.class_nm_sum(), 3);
1857 insta::assert_json_snapshot!(metric.npm);
1858 },
1859 );
1860 }
1861
1862 #[test]
1863 fn kotlin_inner_class_methods() {
1864 check_metrics::<KotlinParser>(
1865 "class Outer {
1866 fun outerM() {}
1867 inner class Inner {
1868 fun innerM() {}
1869 }
1870 }",
1871 "foo.kt",
1872 |metric| {
1873 assert_eq!(metric.npm.class_npm_sum(), 2);
1874 assert_eq!(metric.npm.class_nm_sum(), 2);
1875 insta::assert_json_snapshot!(metric.npm);
1876 },
1877 );
1878 }
1879
1880 #[test]
1881 fn kotlin_top_level_function_excluded() {
1882 check_metrics::<KotlinParser>(
1884 "fun freeFn() {}
1885class C {
1886 fun m() {}
1887}",
1888 "foo.kt",
1889 |metric| {
1890 assert_eq!(metric.npm.class_npm_sum(), 1);
1891 assert_eq!(metric.npm.class_nm_sum(), 1);
1892 insta::assert_json_snapshot!(metric.npm);
1893 },
1894 );
1895 }
1896
1897 #[test]
1898 fn kotlin_extension_function_excluded() {
1899 check_metrics::<KotlinParser>(
1902 "fun List<Int>.sum2(): Int = this.size
1903class C {
1904 fun m() {}
1905}",
1906 "foo.kt",
1907 |metric| {
1908 assert_eq!(metric.npm.class_npm_sum(), 1);
1909 assert_eq!(metric.npm.class_nm_sum(), 1);
1910 insta::assert_json_snapshot!(metric.npm);
1911 },
1912 );
1913 }
1914
1915 #[test]
1916 fn kotlin_class_in_interface() {
1917 check_func_space::<KotlinParser, _>(
1922 "interface Outer {
1923 fun work(): Int
1924 class Helper {
1925 fun help() {}
1926 }
1927 }",
1928 "foo.kt",
1929 |func_space| {
1930 let metric = &func_space.metrics;
1931 assert_eq!(metric.npm.interface_npm_sum(), 1);
1932 assert_eq!(metric.npm.class_npm_sum(), 1);
1933 insta::assert_json_snapshot!(metric.npm);
1934 assert_child_space_kind(&func_space, "Outer", SpaceKind::Interface);
1935 let outer = func_space
1936 .spaces
1937 .iter()
1938 .find(|s| s.name.as_deref() == Some("Outer"))
1939 .expect("Outer FuncSpace");
1940 assert_child_space_kind(outer, "Helper", SpaceKind::Class);
1941 },
1942 );
1943 }
1944
1945 #[test]
1946 fn kotlin_interface_in_class() {
1947 check_func_space::<KotlinParser, _>(
1952 "class Outer {
1953 fun work() {}
1954 interface Sub {
1955 fun help(): Int
1956 }
1957 }",
1958 "foo.kt",
1959 |func_space| {
1960 let metric = &func_space.metrics;
1961 assert_eq!(metric.npm.class_npm_sum(), 1);
1962 assert_eq!(metric.npm.interface_npm_sum(), 1);
1963 insta::assert_json_snapshot!(metric.npm);
1964 assert_child_space_kind(&func_space, "Outer", SpaceKind::Class);
1965 let outer = func_space
1966 .spaces
1967 .iter()
1968 .find(|s| s.name.as_deref() == Some("Outer"))
1969 .expect("Outer FuncSpace");
1970 assert_child_space_kind(outer, "Sub", SpaceKind::Interface);
1971 },
1972 );
1973 }
1974
1975 #[test]
1976 fn kotlin_init_block_not_a_method() {
1977 check_metrics::<KotlinParser>(
1980 "class C(val n: Int) {
1981 init { require(n >= 0) }
1982 fun get(): Int = n
1983 }",
1984 "foo.kt",
1985 |metric| {
1986 assert_eq!(metric.npm.class_npm_sum(), 1);
1987 assert_eq!(metric.npm.class_nm_sum(), 1);
1988 insta::assert_json_snapshot!(metric.npm);
1989 },
1990 );
1991 }
1992
1993 #[test]
2009 fn typescript_empty_class_no_methods() {
2010 check_metrics::<TypescriptParser>("class C {}", "foo.ts", |metric| {
2011 assert_eq!(metric.npm.class_npm_sum(), 0);
2012 assert_eq!(metric.npm.class_nm_sum(), 0);
2013 insta::assert_json_snapshot!(metric.npm);
2014 });
2015 }
2016
2017 #[test]
2018 fn typescript_default_public_methods() {
2019 check_metrics::<TypescriptParser>(
2020 "class C {
2021 a(): void {}
2022 b(): number { return 0; }
2023 c(x: number): number { return x; }
2024 }",
2025 "foo.ts",
2026 |metric| {
2027 assert_eq!(metric.npm.class_npm_sum(), 3);
2028 assert_eq!(metric.npm.class_nm_sum(), 3);
2029 insta::assert_json_snapshot!(metric.npm);
2030 },
2031 );
2032 }
2033
2034 #[test]
2035 fn typescript_method_visibility() {
2036 check_metrics::<TypescriptParser>(
2037 "class C {
2038 public a(): void {}
2039 private b(): void {}
2040 protected c(): void {}
2041 d(): void {}
2042 }",
2043 "foo.ts",
2044 |metric| {
2045 assert_eq!(metric.npm.class_npm_sum(), 2);
2047 assert_eq!(metric.npm.class_nm_sum(), 4);
2048 insta::assert_json_snapshot!(metric.npm);
2049 },
2050 );
2051 }
2052
2053 #[test]
2054 fn typescript_static_methods() {
2055 check_metrics::<TypescriptParser>(
2056 "class C {
2057 static a(): void {}
2058 public static b(): void {}
2059 private static c(): void {}
2060 }",
2061 "foo.ts",
2062 |metric| {
2063 assert_eq!(metric.npm.class_npm_sum(), 2);
2065 assert_eq!(metric.npm.class_nm_sum(), 3);
2066 insta::assert_json_snapshot!(metric.npm);
2067 },
2068 );
2069 }
2070
2071 #[test]
2072 fn typescript_constructor_counts_as_method() {
2073 check_metrics::<TypescriptParser>(
2075 "class C {
2076 constructor(public x: number) {}
2077 m(): void {}
2078 }",
2079 "foo.ts",
2080 |metric| {
2081 assert_eq!(metric.npm.class_npm_sum(), 2);
2082 assert_eq!(metric.npm.class_nm_sum(), 2);
2083 insta::assert_json_snapshot!(metric.npm);
2084 },
2085 );
2086 }
2087
2088 #[test]
2089 fn typescript_getter_setter_each_count_once() {
2090 check_metrics::<TypescriptParser>(
2093 "class C {
2094 private _x: number = 0;
2095 get x(): number { return this._x; }
2096 set x(v: number) { this._x = v; }
2097 }",
2098 "foo.ts",
2099 |metric| {
2100 assert_eq!(metric.npm.class_npm_sum(), 2);
2101 assert_eq!(metric.npm.class_nm_sum(), 2);
2102 insta::assert_json_snapshot!(metric.npm);
2103 },
2104 );
2105 }
2106
2107 #[test]
2108 fn typescript_arrow_field_counts_as_method() {
2109 check_metrics::<TypescriptParser>(
2111 "class C {
2112 a: number = 0;
2113 arrow = () => this.a;
2114 private secret = () => this.a;
2115 }",
2116 "foo.ts",
2117 |metric| {
2118 assert_eq!(metric.npm.class_npm_sum(), 1);
2120 assert_eq!(metric.npm.class_nm_sum(), 2);
2121 insta::assert_json_snapshot!(metric.npm);
2122 },
2123 );
2124 }
2125
2126 #[test]
2127 fn typescript_method_overload_counts_once() {
2128 check_metrics::<TypescriptParser>(
2131 "class C {
2132 m(x: number): void;
2133 m(x: string): void;
2134 m(x: any): void {}
2135 }",
2136 "foo.ts",
2137 |metric| {
2138 assert_eq!(metric.npm.class_npm_sum(), 1);
2139 assert_eq!(metric.npm.class_nm_sum(), 1);
2140 insta::assert_json_snapshot!(metric.npm);
2141 },
2142 );
2143 }
2144
2145 #[test]
2146 fn typescript_abstract_class_methods() {
2147 check_metrics::<TypescriptParser>(
2150 "abstract class C {
2151 abstract a(): void;
2152 public abstract b(): number;
2153 protected abstract c(): void;
2154 public m(): void {}
2155 private n(): void {}
2156 }",
2157 "foo.ts",
2158 |metric| {
2159 assert_eq!(metric.npm.class_npm_sum(), 3);
2162 assert_eq!(metric.npm.class_nm_sum(), 5);
2163 insta::assert_json_snapshot!(metric.npm);
2164 },
2165 );
2166 }
2167
2168 #[test]
2169 fn typescript_interface_methods() {
2170 check_func_space::<TypescriptParser, _>(
2172 "interface I {
2173 a(): void;
2174 b(x: number): number;
2175 c: string;
2176 }",
2177 "foo.ts",
2178 |func_space| {
2179 let metric = &func_space.metrics;
2180 assert_eq!(metric.npm.interface_npm_sum(), 2);
2181 assert_eq!(metric.npm.interface_nm_sum(), 2);
2182 assert_eq!(metric.npm.class_nm_sum(), 0);
2183 insta::assert_json_snapshot!(metric.npm);
2184 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2185 },
2186 );
2187 }
2188
2189 #[test]
2190 fn typescript_generic_class_methods() {
2191 check_metrics::<TypescriptParser>(
2192 "class Box<T> {
2193 value: T;
2194 set(v: T): void { this.value = v; }
2195 get(): T { return this.value; }
2196 }",
2197 "foo.ts",
2198 |metric| {
2199 assert_eq!(metric.npm.class_npm_sum(), 2);
2200 assert_eq!(metric.npm.class_nm_sum(), 2);
2201 insta::assert_json_snapshot!(metric.npm);
2202 },
2203 );
2204 }
2205
2206 #[test]
2207 fn typescript_multiple_classes_and_interface() {
2208 check_func_space::<TypescriptParser, _>(
2209 "class A { m(): void {} }
2210 class B { private h(): void {} }
2211 interface I { p(): number; }",
2212 "foo.ts",
2213 |func_space| {
2214 let metric = &func_space.metrics;
2215 assert_eq!(metric.npm.class_npm_sum(), 1);
2216 assert_eq!(metric.npm.class_nm_sum(), 2);
2217 assert_eq!(metric.npm.interface_npm_sum(), 1);
2218 assert_eq!(metric.npm.interface_nm_sum(), 1);
2219 insta::assert_json_snapshot!(metric.npm);
2220 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2221 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2222 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2223 },
2224 );
2225 }
2226
2227 #[test]
2230 fn tsx_empty_class_no_methods() {
2231 check_metrics::<TsxParser>("class C {}", "foo.tsx", |metric| {
2232 assert_eq!(metric.npm.class_npm_sum(), 0);
2233 assert_eq!(metric.npm.class_nm_sum(), 0);
2234 insta::assert_json_snapshot!(metric.npm);
2235 });
2236 }
2237
2238 #[test]
2239 fn tsx_default_public_methods() {
2240 check_metrics::<TsxParser>(
2241 "class C {
2242 a(): void {}
2243 b(): number { return 0; }
2244 }",
2245 "foo.tsx",
2246 |metric| {
2247 assert_eq!(metric.npm.class_npm_sum(), 2);
2248 assert_eq!(metric.npm.class_nm_sum(), 2);
2249 insta::assert_json_snapshot!(metric.npm);
2250 },
2251 );
2252 }
2253
2254 #[test]
2255 fn tsx_method_visibility() {
2256 check_metrics::<TsxParser>(
2257 "class C {
2258 public a(): void {}
2259 private b(): void {}
2260 protected c(): void {}
2261 }",
2262 "foo.tsx",
2263 |metric| {
2264 assert_eq!(metric.npm.class_npm_sum(), 1);
2265 assert_eq!(metric.npm.class_nm_sum(), 3);
2266 insta::assert_json_snapshot!(metric.npm);
2267 },
2268 );
2269 }
2270
2271 #[test]
2272 fn tsx_static_methods() {
2273 check_metrics::<TsxParser>(
2274 "class C {
2275 static a(): void {}
2276 private static b(): void {}
2277 }",
2278 "foo.tsx",
2279 |metric| {
2280 assert_eq!(metric.npm.class_npm_sum(), 1);
2281 assert_eq!(metric.npm.class_nm_sum(), 2);
2282 insta::assert_json_snapshot!(metric.npm);
2283 },
2284 );
2285 }
2286
2287 #[test]
2288 fn tsx_constructor_counts_as_method() {
2289 check_metrics::<TsxParser>(
2290 "class C {
2291 constructor() {}
2292 m(): void {}
2293 }",
2294 "foo.tsx",
2295 |metric| {
2296 assert_eq!(metric.npm.class_npm_sum(), 2);
2297 assert_eq!(metric.npm.class_nm_sum(), 2);
2298 insta::assert_json_snapshot!(metric.npm);
2299 },
2300 );
2301 }
2302
2303 #[test]
2304 fn tsx_getter_setter_each_count_once() {
2305 check_metrics::<TsxParser>(
2306 "class C {
2307 private _x: number = 0;
2308 get x(): number { return this._x; }
2309 set x(v: number) { this._x = v; }
2310 }",
2311 "foo.tsx",
2312 |metric| {
2313 assert_eq!(metric.npm.class_npm_sum(), 2);
2314 assert_eq!(metric.npm.class_nm_sum(), 2);
2315 insta::assert_json_snapshot!(metric.npm);
2316 },
2317 );
2318 }
2319
2320 #[test]
2321 fn tsx_arrow_field_counts_as_method() {
2322 check_metrics::<TsxParser>(
2323 "class C {
2324 arrow = () => 1;
2325 private secret = () => 2;
2326 }",
2327 "foo.tsx",
2328 |metric| {
2329 assert_eq!(metric.npm.class_npm_sum(), 1);
2330 assert_eq!(metric.npm.class_nm_sum(), 2);
2331 insta::assert_json_snapshot!(metric.npm);
2332 },
2333 );
2334 }
2335
2336 #[test]
2337 fn tsx_method_overload_counts_once() {
2338 check_metrics::<TsxParser>(
2339 "class C {
2340 m(x: number): void;
2341 m(x: string): void;
2342 m(x: any): void {}
2343 }",
2344 "foo.tsx",
2345 |metric| {
2346 assert_eq!(metric.npm.class_npm_sum(), 1);
2347 assert_eq!(metric.npm.class_nm_sum(), 1);
2348 insta::assert_json_snapshot!(metric.npm);
2349 },
2350 );
2351 }
2352
2353 #[test]
2354 fn tsx_abstract_class_methods() {
2355 check_metrics::<TsxParser>(
2356 "abstract class C {
2357 abstract a(): void;
2358 public m(): void {}
2359 private n(): void {}
2360 }",
2361 "foo.tsx",
2362 |metric| {
2363 assert_eq!(metric.npm.class_npm_sum(), 2);
2365 assert_eq!(metric.npm.class_nm_sum(), 3);
2366 insta::assert_json_snapshot!(metric.npm);
2367 },
2368 );
2369 }
2370
2371 #[test]
2372 fn tsx_interface_methods() {
2373 check_func_space::<TsxParser, _>(
2374 "interface I {
2375 a(): void;
2376 b(): number;
2377 }",
2378 "foo.tsx",
2379 |func_space| {
2380 let metric = &func_space.metrics;
2381 assert_eq!(metric.npm.interface_npm_sum(), 2);
2382 assert_eq!(metric.npm.interface_nm_sum(), 2);
2383 insta::assert_json_snapshot!(metric.npm);
2384 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2385 },
2386 );
2387 }
2388
2389 #[test]
2390 fn tsx_generic_class_methods() {
2391 check_metrics::<TsxParser>(
2392 "class Box<T> { value: T; set(v: T): void { this.value = v; } }",
2393 "foo.tsx",
2394 |metric| {
2395 assert_eq!(metric.npm.class_npm_sum(), 1);
2396 assert_eq!(metric.npm.class_nm_sum(), 1);
2397 insta::assert_json_snapshot!(metric.npm);
2398 },
2399 );
2400 }
2401
2402 #[test]
2403 fn tsx_multiple_classes_and_interface() {
2404 check_func_space::<TsxParser, _>(
2405 "class A { m(): void {} }
2406 class B { private h(): void {} }
2407 interface I { p(): number; }",
2408 "foo.tsx",
2409 |func_space| {
2410 let metric = &func_space.metrics;
2411 assert_eq!(metric.npm.class_npm_sum(), 1);
2412 assert_eq!(metric.npm.class_nm_sum(), 2);
2413 assert_eq!(metric.npm.interface_npm_sum(), 1);
2414 assert_eq!(metric.npm.interface_nm_sum(), 1);
2415 insta::assert_json_snapshot!(metric.npm);
2416 assert_child_space_kind(&func_space, "A", SpaceKind::Class);
2417 assert_child_space_kind(&func_space, "B", SpaceKind::Class);
2418 assert_child_space_kind(&func_space, "I", SpaceKind::Interface);
2419 },
2420 );
2421 }
2422
2423 #[test]
2432 fn ruby_no_class_methods() {
2433 check_metrics::<RubyParser>("def foo\n 1\nend\n", "foo.rb", |metric| {
2434 assert_eq!(metric.npm.class_npm_sum(), 0);
2435 assert_eq!(metric.npm.class_nm_sum(), 0);
2436 insta::assert_json_snapshot!(metric.npm);
2437 });
2438 }
2439
2440 #[test]
2441 fn ruby_one_public_method() {
2442 check_metrics::<RubyParser>(
2444 "class A\n def f\n 1\n end\nend\n",
2445 "foo.rb",
2446 |metric| {
2447 assert_eq!(metric.npm.class_npm_sum(), 1);
2448 assert_eq!(metric.npm.class_nm_sum(), 1);
2449 insta::assert_json_snapshot!(metric.npm);
2450 },
2451 );
2452 }
2453
2454 #[test]
2455 fn ruby_one_private_method() {
2456 check_metrics::<RubyParser>(
2458 "class A\n private\n def f\n 1\n end\nend\n",
2459 "foo.rb",
2460 |metric| {
2461 assert_eq!(metric.npm.class_npm_sum(), 0);
2462 assert_eq!(metric.npm.class_nm_sum(), 1);
2463 insta::assert_json_snapshot!(metric.npm);
2464 },
2465 );
2466 }
2467
2468 #[test]
2469 fn ruby_one_protected_method() {
2470 check_metrics::<RubyParser>(
2471 "class A\n protected\n def f\n 1\n end\nend\n",
2472 "foo.rb",
2473 |metric| {
2474 assert_eq!(metric.npm.class_npm_sum(), 0);
2475 assert_eq!(metric.npm.class_nm_sum(), 1);
2476 insta::assert_json_snapshot!(metric.npm);
2477 },
2478 );
2479 }
2480
2481 #[test]
2482 fn ruby_mixed_visibility_methods() {
2483 check_metrics::<RubyParser>(
2487 "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",
2488 "foo.rb",
2489 |metric| {
2490 assert_eq!(metric.npm.class_npm_sum(), 2);
2491 assert_eq!(metric.npm.class_nm_sum(), 4);
2492 insta::assert_json_snapshot!(metric.npm);
2493 },
2494 );
2495 }
2496
2497 #[test]
2498 fn ruby_singleton_method_is_counted() {
2499 check_metrics::<RubyParser>(
2501 "class A\n def self.f\n 1\n end\n def g\n 1\n end\nend\n",
2502 "foo.rb",
2503 |metric| {
2504 assert_eq!(metric.npm.class_npm_sum(), 2);
2505 assert_eq!(metric.npm.class_nm_sum(), 2);
2506 insta::assert_json_snapshot!(metric.npm);
2507 },
2508 );
2509 }
2510
2511 #[test]
2512 fn ruby_singleton_class_methods() {
2513 check_metrics::<RubyParser>(
2516 "class A\n class << self\n def s\n 1\n end\n def t\n 2\n end\n end\nend\n",
2517 "foo.rb",
2518 |metric| {
2519 assert_eq!(metric.npm.class_npm_sum(), 2);
2520 assert_eq!(metric.npm.class_nm_sum(), 2);
2521 insta::assert_json_snapshot!(metric.npm);
2522 },
2523 );
2524 }
2525
2526 #[test]
2527 fn ruby_argument_form_visibility_does_not_flip() {
2528 check_metrics::<RubyParser>(
2532 "class A\n def y\n 1\n end\n private :y\n def z\n 1\n end\nend\n",
2533 "foo.rb",
2534 |metric| {
2535 assert_eq!(metric.npm.class_npm_sum(), 2);
2536 assert_eq!(metric.npm.class_nm_sum(), 2);
2537 insta::assert_json_snapshot!(metric.npm);
2538 },
2539 );
2540 }
2541
2542 #[test]
2543 fn ruby_multiple_classes() {
2544 check_metrics::<RubyParser>(
2545 "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",
2546 "foo.rb",
2547 |metric| {
2548 assert_eq!(metric.npm.class_npm_sum(), 1);
2550 assert_eq!(metric.npm.class_nm_sum(), 3);
2551 insta::assert_json_snapshot!(metric.npm);
2552 },
2553 );
2554 }
2555
2556 #[test]
2557 fn ruby_module_methods_not_counted() {
2558 check_metrics::<RubyParser>(
2561 "module M\n def f\n 1\n end\n def g\n 1\n end\nend\n",
2562 "foo.rb",
2563 |metric| {
2564 assert_eq!(metric.npm.class_npm_sum(), 0);
2565 assert_eq!(metric.npm.class_nm_sum(), 0);
2566 insta::assert_json_snapshot!(metric.npm);
2567 },
2568 );
2569 }
2570
2571 #[test]
2572 fn ruby_class_with_inheritance() {
2573 check_metrics::<RubyParser>(
2575 "class A < B\n def f\n 1\n end\n def g\n 1\n end\nend\n",
2576 "foo.rb",
2577 |metric| {
2578 assert_eq!(metric.npm.class_npm_sum(), 2);
2579 assert_eq!(metric.npm.class_nm_sum(), 2);
2580 insta::assert_json_snapshot!(metric.npm);
2581 },
2582 );
2583 }
2584
2585 #[test]
2586 fn ruby_visibility_resets_between_classes() {
2587 check_metrics::<RubyParser>(
2590 "class A\n private\n def a\n 1\n end\nend\nclass B\n def b\n 1\n end\nend\n",
2591 "foo.rb",
2592 |metric| {
2593 assert_eq!(metric.npm.class_npm_sum(), 1);
2595 assert_eq!(metric.npm.class_nm_sum(), 2);
2596 insta::assert_json_snapshot!(metric.npm);
2597 },
2598 );
2599 }
2600
2601 #[test]
2602 fn ruby_empty_class_no_methods() {
2603 check_metrics::<RubyParser>("class Empty\nend\n", "foo.rb", |metric| {
2604 assert_eq!(metric.npm.class_npm_sum(), 0);
2605 assert_eq!(metric.npm.class_nm_sum(), 0);
2606 insta::assert_json_snapshot!(metric.npm);
2607 });
2608 }
2609
2610 #[test]
2623 fn python_empty_class_no_methods() {
2624 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
2625 assert_eq!(metric.npm.class_nm_sum(), 0);
2626 assert_eq!(metric.npm.class_npm_sum(), 0);
2627 insta::assert_json_snapshot!(metric.npm);
2628 });
2629 }
2630
2631 #[test]
2632 fn python_class_methods_count() {
2633 check_metrics::<PythonParser>(
2635 "class C:\n\
2636 \x20 def __init__(self):\n\
2637 \x20 pass\n\
2638 \x20 def m(self):\n\
2639 \x20 pass\n\
2640 \x20 def n(self):\n\
2641 \x20 pass\n",
2642 "foo.py",
2643 |metric| {
2644 assert_eq!(metric.npm.class_nm_sum(), 3);
2645 assert_eq!(metric.npm.class_npm_sum(), 3);
2646 insta::assert_json_snapshot!(metric.npm);
2647 },
2648 );
2649 }
2650
2651 #[test]
2652 fn python_decorated_methods_count() {
2653 check_metrics::<PythonParser>(
2657 "class C:\n\
2658 \x20 @property\n\
2659 \x20 def p(self):\n\
2660 \x20 return 1\n\
2661 \x20 @staticmethod\n\
2662 \x20 def s():\n\
2663 \x20 return 2\n\
2664 \x20 @classmethod\n\
2665 \x20 def c(cls):\n\
2666 \x20 return 3\n",
2667 "foo.py",
2668 |metric| {
2669 assert_eq!(metric.npm.class_nm_sum(), 3);
2670 insta::assert_json_snapshot!(metric.npm);
2671 },
2672 );
2673 }
2674
2675 #[test]
2676 fn python_async_method_counts() {
2677 check_metrics::<PythonParser>(
2680 "class C:\n async def m(self):\n return 1\n",
2681 "foo.py",
2682 |metric| {
2683 assert_eq!(metric.npm.class_nm_sum(), 1);
2684 insta::assert_json_snapshot!(metric.npm);
2685 },
2686 );
2687 }
2688
2689 #[test]
2690 fn python_nested_class_methods_independent() {
2691 check_metrics::<PythonParser>(
2694 "class Outer:\n\
2695 \x20 def method(self):\n\
2696 \x20 pass\n\
2697 \x20 class Inner:\n\
2698 \x20 def inner_method(self):\n\
2699 \x20 pass\n",
2700 "foo.py",
2701 |metric| {
2702 assert_eq!(metric.npm.class_nm_sum(), 2);
2703 insta::assert_json_snapshot!(metric.npm);
2704 },
2705 );
2706 }
2707
2708 #[test]
2709 fn python_module_level_function_is_not_method() {
2710 check_metrics::<PythonParser>(
2713 "def f():\n pass\nclass C:\n def m(self):\n pass\n",
2714 "foo.py",
2715 |metric| {
2716 assert_eq!(metric.npm.class_nm_sum(), 1);
2718 insta::assert_json_snapshot!(metric.npm);
2719 },
2720 );
2721 }
2722
2723 #[test]
2724 fn python_dunder_methods_count() {
2725 check_metrics::<PythonParser>(
2728 "class C:\n\
2729 \x20 def __init__(self):\n\
2730 \x20 pass\n\
2731 \x20 def __repr__(self):\n\
2732 \x20 return 'C'\n\
2733 \x20 def __eq__(self, other):\n\
2734 \x20 return True\n",
2735 "foo.py",
2736 |metric| {
2737 assert_eq!(metric.npm.class_nm_sum(), 3);
2738 assert_eq!(metric.npm.class_npm_sum(), 3);
2739 insta::assert_json_snapshot!(metric.npm);
2740 },
2741 );
2742 }
2743
2744 #[test]
2745 fn rust_empty_unit_no_methods() {
2746 check_metrics::<RustParser>("", "empty.rs", |metric| {
2747 assert_eq!(metric.npm.class_nm_sum(), 0);
2748 assert_eq!(metric.npm.class_npm_sum(), 0);
2749 assert_eq!(metric.npm.interface_nm_sum(), 0);
2750 assert_eq!(metric.npm.interface_npm_sum(), 0);
2751 insta::assert_json_snapshot!(metric.npm);
2752 });
2753 }
2754
2755 #[test]
2756 fn rust_impl_methods_count() {
2757 check_metrics::<RustParser>(
2760 "struct Foo;\n\
2761 impl Foo {\n\
2762 \x20 pub fn new() -> Self { Foo }\n\
2763 \x20 fn helper(&self) -> i32 { 0 }\n\
2764 \x20 pub fn process(&self) -> i32 { 0 }\n\
2765 }\n",
2766 "foo.rs",
2767 |metric| {
2768 assert_eq!(metric.npm.class_nm_sum(), 3);
2769 assert_eq!(metric.npm.class_npm_sum(), 2);
2770 insta::assert_json_snapshot!(metric.npm);
2771 },
2772 );
2773 }
2774
2775 #[test]
2776 fn rust_pub_self_is_private() {
2777 check_metrics::<RustParser>(
2785 "struct S;\n\
2786 impl S {\n\
2787 \x20 pub(self) fn a(&self) {}\n\
2788 \x20 pub(in self) fn a2(&self) {}\n\
2789 \x20 pub(crate) fn b(&self) {}\n\
2790 \x20 pub(super) fn d(&self) {}\n\
2791 \x20 pub(in crate::x) fn e(&self) {}\n\
2792 \x20 pub fn f(&self) {}\n\
2793 \x20 fn c(&self) {}\n\
2794 }\n",
2795 "foo.rs",
2796 |metric| {
2797 assert_eq!(metric.npm.class_nm_sum(), 7);
2798 assert_eq!(metric.npm.class_npm_sum(), 4);
2799 },
2800 );
2801 }
2802
2803 #[test]
2804 fn rust_trait_methods_count() {
2805 check_func_space::<RustParser, _>(
2812 "trait Drawable {\n\
2813 \x20 fn draw(&self);\n\
2814 \x20 fn area(&self) -> f64 { 0.0 }\n\
2815 }\n",
2816 "foo.rs",
2817 |func_space| {
2818 let metric = &func_space.metrics;
2819 assert_eq!(metric.npm.interface_nm_sum(), 2);
2820 assert_eq!(metric.npm.interface_npm_sum(), 2);
2821 assert_eq!(metric.npm.class_nm_sum(), 0);
2822 insta::assert_json_snapshot!(metric.npm);
2823 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
2824 },
2825 );
2826 }
2827
2828 #[test]
2829 fn rust_module_level_function_not_method() {
2830 check_metrics::<RustParser>("fn f() {}\nfn g() {}\n", "foo.rs", |metric| {
2834 assert_eq!(metric.npm.class_nm_sum(), 0);
2835 assert_eq!(metric.npm.interface_nm_sum(), 0);
2836 insta::assert_json_snapshot!(metric.npm);
2837 });
2838 }
2839
2840 #[test]
2841 fn rust_multiple_impls_methods_aggregate() {
2842 check_metrics::<RustParser>(
2844 "struct Foo;\n\
2845 impl Foo { pub fn m1(&self) {} }\n\
2846 impl Foo { fn m2(&self) {} }\n",
2847 "foo.rs",
2848 |metric| {
2849 assert_eq!(metric.npm.class_nm_sum(), 2);
2850 assert_eq!(metric.npm.class_npm_sum(), 1);
2851 insta::assert_json_snapshot!(metric.npm);
2852 },
2853 );
2854 }
2855
2856 #[test]
2857 fn rust_trait_impl_block_counts_methods() {
2858 check_func_space::<RustParser, _>(
2865 "struct Foo;\n\
2866 trait Drawable { fn draw(&self); }\n\
2867 impl Drawable for Foo { fn draw(&self) {} }\n",
2868 "foo.rs",
2869 |func_space| {
2870 let metric = &func_space.metrics;
2871 assert_eq!(metric.npm.interface_nm_sum(), 1);
2874 assert_eq!(metric.npm.class_nm_sum(), 1);
2875 insta::assert_json_snapshot!(metric.npm);
2876 assert_child_space_kind(&func_space, "Drawable", SpaceKind::Trait);
2877 },
2878 );
2879 }
2880
2881 #[test]
2884 fn go_empty_unit_no_methods() {
2885 check_metrics::<GoParser>("package main\n", "empty.go", |metric| {
2887 assert_eq!(metric.npm.class_nm_sum(), 0);
2888 insta::assert_json_snapshot!(metric.npm);
2889 });
2890 }
2891
2892 #[test]
2893 fn go_method_declarations_count() {
2894 check_metrics::<GoParser>(
2898 "package main\n\
2899 type Foo struct{}\n\
2900 func (f Foo) DoX() {}\n\
2901 func (f Foo) doY() {}\n",
2902 "foo.go",
2903 |metric| {
2904 assert_eq!(metric.npm.class_nm_sum(), 2);
2905 assert_eq!(metric.npm.class_npm_sum(), 1);
2906 insta::assert_json_snapshot!(metric.npm);
2907 },
2908 );
2909 }
2910
2911 #[test]
2912 fn go_free_function_is_not_method() {
2913 check_metrics::<GoParser>(
2917 "package main\nfunc g() {}\nfunc h(x int) int { return x }\n",
2918 "foo.go",
2919 |metric| {
2920 assert_eq!(metric.npm.class_nm_sum(), 0);
2921 insta::assert_json_snapshot!(metric.npm);
2922 },
2923 );
2924 }
2925
2926 #[test]
2927 fn go_methods_on_different_receivers_aggregate_at_unit() {
2928 check_metrics::<GoParser>(
2932 "package main\n\
2933 type Foo struct{}\n\
2934 type Bar struct{}\n\
2935 func (f Foo) M1() {}\n\
2936 func (b Bar) M2() {}\n\
2937 func (b *Bar) M3() {}\n",
2938 "foo.go",
2939 |metric| {
2940 assert_eq!(metric.npm.class_nm_sum(), 3);
2941 insta::assert_json_snapshot!(metric.npm);
2942 },
2943 );
2944 }
2945
2946 #[test]
2947 fn go_interface_methods_count_as_interface_nm() {
2948 check_metrics::<GoParser>(
2963 "package main\ntype RC interface { Read() error; Close() error }\n",
2964 "foo.go",
2965 |metric| {
2966 assert_eq!(metric.npm.interface_nm_sum(), 2);
2967 assert_eq!(metric.npm.interface_npm_sum(), 2);
2968 assert_eq!(metric.npm.class_nm_sum(), 0);
2969 insta::assert_json_snapshot!(metric.npm);
2970 },
2971 );
2972 }
2973
2974 #[test]
2975 fn go_interface_methods_respect_export() {
2976 check_metrics::<GoParser>(
2982 "package main\ntype I interface { Foo(); bar(); Ünic() }\n",
2983 "foo.go",
2984 |metric| {
2985 assert_eq!(metric.npm.interface_nm_sum(), 3);
2986 assert_eq!(metric.npm.interface_npm_sum(), 2);
2987 assert_eq!(metric.npm.class_nm_sum(), 0);
2988 insta::assert_json_snapshot!(metric.npm);
2989 },
2990 );
2991 }
2992
2993 #[test]
2994 fn go_pointer_receiver_methods_count() {
2995 check_metrics::<GoParser>(
2999 "package main\n\
3000 type Foo struct{}\n\
3001 func (f *Foo) Set() {}\n\
3002 func (f *Foo) Get() int { return 0 }\n",
3003 "foo.go",
3004 |metric| {
3005 assert_eq!(metric.npm.class_nm_sum(), 2);
3006 insta::assert_json_snapshot!(metric.npm);
3007 },
3008 );
3009 }
3010
3011 #[test]
3012 fn go_npm_excludes_unexported() {
3013 check_metrics::<GoParser>(
3019 "package main\n\
3020 type T struct{}\n\
3021 func (t *T) Greet() {}\n\
3022 func (t *T) helper() {}\n\
3023 func (t *T) Ärger() {}\n",
3024 "foo.go",
3025 |metric| {
3026 assert_eq!(metric.npm.class_nm_sum(), 3);
3027 assert_eq!(metric.npm.class_npm_sum(), 2);
3028 insta::assert_json_snapshot!(metric.npm);
3029 },
3030 );
3031 }
3032
3033 #[test]
3038 fn elixir_npm_def_is_public_defp_is_private() {
3039 check_metrics::<ElixirParser>(
3040 "defmodule Foo do\n def pub_one, do: 1\n defp priv_one, do: 1\n def pub_two(x), do: x\nend\n",
3041 "foo.ex",
3042 |metric| {
3043 assert_eq!(metric.npm.class_nm_sum(), 3);
3045 assert_eq!(metric.npm.class_npm_sum(), 2);
3046 },
3047 );
3048 }
3049
3050 #[test]
3051 fn elixir_npm_defmacro_counts_as_public() {
3052 check_metrics::<ElixirParser>(
3053 "defmodule Foo do\n defmacro pub_macro(x), do: x\n defmacrop priv_macro(x), do: x\nend\n",
3054 "foo.ex",
3055 |metric| {
3056 assert_eq!(metric.npm.class_nm_sum(), 2);
3058 assert_eq!(metric.npm.class_npm_sum(), 1);
3059 },
3060 );
3061 }
3062
3063 #[test]
3064 fn elixir_npm_multiple_def_clauses_each_count() {
3065 check_metrics::<ElixirParser>(
3067 "defmodule Foo do\n def f(0), do: :zero\n def f(_), do: :other\nend\n",
3068 "foo.ex",
3069 |metric| {
3070 assert_eq!(metric.npm.class_nm_sum(), 2);
3071 assert_eq!(metric.npm.class_npm_sum(), 2);
3072 },
3073 );
3074 }
3075
3076 #[test]
3077 fn elixir_npm_nested_defmodule_each_class() {
3078 check_metrics::<ElixirParser>(
3079 "defmodule Outer do\n def o, do: 1\n defmodule Inner do\n def i, do: 1\n end\nend\n",
3080 "foo.ex",
3081 |metric| {
3082 assert_eq!(metric.npm.class_nm_sum(), 2);
3084 assert_eq!(metric.npm.class_npm_sum(), 2);
3085 },
3086 );
3087 }
3088
3089 #[test]
3090 fn elixir_npm_user_macro_not_classified_as_method() {
3091 check_metrics::<ElixirParser>(
3095 "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",
3096 "foo.ex",
3097 |metric| {
3098 assert_eq!(metric.npm.class_nm_sum(), 1);
3103 assert_eq!(metric.npm.class_npm_sum(), 1);
3104 },
3105 );
3106 }
3107
3108 #[test]
3109 fn elixir_npm_quoted_defs_do_not_inflate_method_count() {
3110 check_metrics::<ElixirParser>(
3118 "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",
3119 "foo.ex",
3120 |metric| {
3121 assert_eq!(metric.npm.class_nm_sum(), 1);
3123 assert_eq!(metric.npm.class_npm_sum(), 1);
3124 },
3125 );
3126 }
3127
3128 #[test]
3131 fn objc_npm() {
3132 check_metrics::<ObjcParser>(
3139 "@interface Foo : NSObject\n\
3140 - (void)pub1;\n\
3141 - (void)pub2;\n\
3142 @end\n\
3143 @implementation Foo\n\
3144 - (void)pub1 { }\n\
3145 - (void)pub2 { }\n\
3146 - (void)privHelper { }\n\
3147 void cFunc(void) { }\n\
3148 @end\n",
3149 "foo.m",
3150 |metric| {
3151 assert_eq!(metric.npm.interface_nm_sum(), 2);
3152 assert_eq!(metric.npm.interface_npm_sum(), 2);
3153 assert_eq!(metric.npm.class_nm_sum(), 3);
3154 assert_eq!(metric.npm.class_npm_sum(), 3);
3155 },
3156 );
3157 }
3158
3159 #[test]
3160 fn objc_npm_protocol() {
3161 check_metrics::<ObjcParser>(
3166 "@protocol Drawable <NSObject>\n\
3167 - (void)draw;\n\
3168 @optional\n\
3169 - (void)optDraw;\n\
3170 @end\n",
3171 "foo.m",
3172 |metric| {
3173 assert_eq!(metric.npm.interface_nm_sum(), 2);
3174 assert_eq!(metric.npm.interface_npm_sum(), 2);
3175 },
3176 );
3177 }
3178
3179 #[test]
3182 fn cpp_empty_unit_no_methods() {
3183 check_metrics::<CppParser>("", "empty.cpp", |metric| {
3185 assert_eq!(metric.npm.class_nm_sum(), 0);
3186 assert_eq!(metric.npm.class_npm_sum(), 0);
3187 insta::assert_json_snapshot!(metric.npm);
3188 });
3189 }
3190
3191 #[test]
3192 fn cpp_class_methods_count() {
3193 check_metrics::<CppParser>(
3196 "class Foo {\n\
3197 void method1() {}\n\
3198 void method2();\n\
3199 };",
3200 "foo.cpp",
3201 |metric| {
3202 assert_eq!(metric.npm.class_nm_sum(), 2);
3203 assert_eq!(metric.npm.class_npm_sum(), 0);
3204 insta::assert_json_snapshot!(metric.npm);
3205 },
3206 );
3207 }
3208
3209 #[test]
3210 fn cpp_constructors_and_destructors_count() {
3211 check_metrics::<CppParser>(
3215 "class Foo {\n\
3216 public:\n\
3217 Foo();\n\
3218 ~Foo();\n\
3219 void method();\n\
3220 };",
3221 "foo.cpp",
3222 |metric| {
3223 assert_eq!(metric.npm.class_nm_sum(), 3);
3224 assert_eq!(metric.npm.class_npm_sum(), 3);
3225 insta::assert_json_snapshot!(metric.npm);
3226 },
3227 );
3228 }
3229
3230 #[test]
3231 fn cpp_template_methods_count() {
3232 check_metrics::<CppParser>(
3236 "class Foo {\n\
3237 public:\n\
3238 template<typename T> T fn(T x);\n\
3239 };",
3240 "foo.cpp",
3241 |metric| {
3242 assert_eq!(metric.npm.class_nm_sum(), 1);
3243 assert_eq!(metric.npm.class_npm_sum(), 1);
3244 insta::assert_json_snapshot!(metric.npm);
3245 },
3246 );
3247 }
3248
3249 #[test]
3250 fn cpp_struct_methods_default_public() {
3251 check_metrics::<CppParser>(
3254 "struct Foo {\n\
3255 void a();\n\
3256 void b() {}\n\
3257 Foo() {}\n\
3258 };",
3259 "foo.cpp",
3260 |metric| {
3261 assert_eq!(metric.npm.class_nm_sum(), 3);
3262 assert_eq!(metric.npm.class_npm_sum(), 3);
3263 insta::assert_json_snapshot!(metric.npm);
3264 },
3265 );
3266 }
3267
3268 #[test]
3269 fn cpp_free_function_is_not_method() {
3270 check_metrics::<CppParser>("void free_fn() {}\n", "foo.cpp", |metric| {
3274 assert_eq!(metric.npm.class_nm_sum(), 0);
3275 assert_eq!(metric.npm.class_npm_sum(), 0);
3276 insta::assert_json_snapshot!(metric.npm);
3277 });
3278 }
3279
3280 #[test]
3281 fn cpp_mixed_visibility_methods() {
3282 check_metrics::<CppParser>(
3286 "class Foo {\n\
3287 public: void a();\n\
3288 protected: void b();\n\
3289 private: void c();\n\
3290 };",
3291 "foo.cpp",
3292 |metric| {
3293 assert_eq!(metric.npm.class_nm_sum(), 3);
3294 assert_eq!(metric.npm.class_npm_sum(), 1);
3295 insta::assert_json_snapshot!(metric.npm);
3296 },
3297 );
3298 }
3299
3300 #[test]
3301 fn cpp_multiple_classes_aggregate_at_unit() {
3302 check_metrics::<CppParser>(
3305 "class Foo { public: void a(); void b() {} };\n\
3306 struct Bar { void c(); };",
3307 "foo.cpp",
3308 |metric| {
3309 assert_eq!(metric.npm.class_nm_sum(), 3);
3310 assert_eq!(metric.npm.class_npm_sum(), 3);
3311 insta::assert_json_snapshot!(metric.npm);
3312 },
3313 );
3314 }
3315
3316 #[test]
3317 fn javascript_empty_unit_no_methods() {
3318 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
3319 assert_eq!(metric.npm.class_nm_sum(), 0);
3320 assert_eq!(metric.npm.class_npm_sum(), 0);
3321 insta::assert_json_snapshot!(metric.npm);
3322 });
3323 }
3324
3325 #[test]
3326 fn javascript_class_methods_count() {
3327 check_metrics::<JavascriptParser>(
3331 "class Foo {\n\
3332 constructor() {}\n\
3333 bar() {}\n\
3334 get baz() { return 1; }\n\
3335 set baz(v) {}\n\
3336 }",
3337 "foo.js",
3338 |metric| {
3339 assert_eq!(metric.npm.class_nm_sum(), 4);
3340 assert_eq!(metric.npm.class_npm_sum(), 4);
3341 insta::assert_json_snapshot!(metric.npm);
3342 },
3343 );
3344 }
3345
3346 #[test]
3347 fn javascript_arrow_field_is_method() {
3348 check_metrics::<JavascriptParser>(
3352 "class Foo { x = () => {}; y = function() {}; z = 1; }",
3353 "foo.js",
3354 |metric| {
3355 assert_eq!(metric.npm.class_nm_sum(), 2);
3357 assert_eq!(metric.npm.class_npm_sum(), 2);
3358 insta::assert_json_snapshot!(metric.npm);
3359 },
3360 );
3361 }
3362
3363 #[test]
3364 fn javascript_free_function_is_not_method() {
3365 check_metrics::<JavascriptParser>(
3368 "function f() {}\nconst g = () => {};\nclass Foo { h() {} }",
3369 "foo.js",
3370 |metric| {
3371 assert_eq!(metric.npm.class_nm_sum(), 1);
3373 assert_eq!(metric.npm.class_npm_sum(), 1);
3374 insta::assert_json_snapshot!(metric.npm);
3375 },
3376 );
3377 }
3378
3379 #[test]
3380 fn javascript_multiple_classes_aggregate_at_unit() {
3381 check_metrics::<JavascriptParser>(
3384 "class Foo { a() {} b() {} }\nclass Bar { c() {} }",
3385 "foo.js",
3386 |metric| {
3387 assert_eq!(metric.npm.class_nm_sum(), 3);
3388 assert_eq!(metric.npm.class_npm_sum(), 3);
3389 insta::assert_json_snapshot!(metric.npm);
3390 },
3391 );
3392 }
3393
3394 #[test]
3395 fn mozjs_class_methods_count() {
3396 check_metrics::<MozjsParser>(
3398 "class Foo {\n\
3399 constructor() {}\n\
3400 bar() {}\n\
3401 get baz() { return 1; }\n\
3402 set baz(v) {}\n\
3403 }",
3404 "foo.js",
3405 |metric| {
3406 assert_eq!(metric.npm.class_nm_sum(), 4);
3407 assert_eq!(metric.npm.class_npm_sum(), 4);
3408 insta::assert_json_snapshot!(metric.npm);
3409 },
3410 );
3411 }
3412
3413 #[test]
3421 fn empty_class_coa_is_zero_not_nan() {
3422 let assert_zero = |metric: crate::CodeMetrics| {
3423 assert_eq!(metric.npm.class_nm_sum(), 0);
3424 assert!(!metric.npm.class_coa().is_nan());
3425 assert!(!metric.npm.total_coa().is_nan());
3426 assert_eq!(metric.npm.class_coa(), 0.0);
3427 assert_eq!(metric.npm.total_coa(), 0.0);
3428 };
3429 check_metrics::<JavaParser>("class Foo {}", "foo.java", assert_zero);
3430 check_metrics::<CsharpParser>("class Foo {}", "foo.cs", assert_zero);
3431 check_metrics::<KotlinParser>("class Foo {}", "foo.kt", assert_zero);
3432 check_metrics::<PhpParser>("<?php class Foo {}", "foo.php", assert_zero);
3433 }
3434
3435 #[test]
3440 fn empty_interface_coa_is_zero_not_nan() {
3441 let assert_zero = |metric: crate::CodeMetrics| {
3442 assert_eq!(metric.npm.interface_nm_sum(), 0);
3443 assert!(!metric.npm.interface_coa().is_nan());
3444 assert_eq!(metric.npm.interface_coa(), 0.0);
3445 };
3446 check_metrics::<JavaParser>("interface Foo {}", "foo.java", assert_zero);
3447 check_metrics::<CsharpParser>("interface Foo {}", "foo.cs", assert_zero);
3448 }
3449}