1#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
8#![allow(
12 clippy::cast_precision_loss,
13 clippy::cast_possible_truncation,
14 clippy::cast_sign_loss
15)]
16
17use std::fmt;
18
19use crate::checker::Checker;
20use crate::macros::implement_metric_trait;
21use crate::*;
22
23#[derive(Debug, Clone, Default, PartialEq)]
31#[non_exhaustive]
32pub struct Stats {
33 cyclomatic: f64,
34 nested_class_cyclomatic: f64,
42 class_wmc: f64,
43 interface_wmc: f64,
44 class_wmc_sum: f64,
45 interface_wmc_sum: f64,
46 space_kind: SpaceKind,
47}
48
49impl fmt::Display for Stats {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 write!(
52 f,
53 "classes: {}, interfaces: {}, total: {}",
54 self.class_wmc_sum(),
55 self.interface_wmc_sum(),
56 self.total_wmc()
57 )
58 }
59}
60
61impl Stats {
62 pub fn merge(&mut self, other: &Stats) {
64 use SpaceKind::*;
65
66 match other.space_kind {
70 Function => {
78 let own_cyclomatic = other.cyclomatic - other.nested_class_cyclomatic;
79 match self.space_kind {
80 Class => self.class_wmc += own_cyclomatic,
81 Interface => self.interface_wmc += own_cyclomatic,
82 _ => {}
83 }
84 self.nested_class_cyclomatic += other.nested_class_cyclomatic;
85 }
86 Class | Interface => self.nested_class_cyclomatic += other.cyclomatic,
93 _ => {}
94 }
95
96 self.class_wmc_sum += other.class_wmc_sum;
97 self.interface_wmc_sum += other.interface_wmc_sum;
98 }
99
100 #[inline]
102 #[must_use]
103 pub fn class_wmc(&self) -> u64 {
104 self.class_wmc as u64
105 }
106
107 #[inline]
109 #[must_use]
110 pub fn interface_wmc(&self) -> u64 {
111 self.interface_wmc as u64
112 }
113
114 #[inline]
116 #[must_use]
117 pub fn class_wmc_sum(&self) -> u64 {
118 self.class_wmc_sum as u64
119 }
120
121 #[inline]
123 #[must_use]
124 pub fn interface_wmc_sum(&self) -> u64 {
125 self.interface_wmc_sum as u64
126 }
127
128 #[inline]
130 #[must_use]
131 pub fn total_wmc(&self) -> u64 {
132 self.class_wmc_sum() + self.interface_wmc_sum()
133 }
134
135 #[inline]
138 pub(crate) fn compute_sum(&mut self) {
139 self.class_wmc_sum += self.class_wmc;
140 self.interface_wmc_sum += self.interface_wmc;
141 }
142
143 #[inline]
145 pub(crate) fn is_disabled(&self) -> bool {
146 matches!(self.space_kind, SpaceKind::Function | SpaceKind::Unknown)
147 }
148}
149
150#[doc(hidden)]
151pub(crate) trait Wmc
153where
154 Self: Checker,
155{
156 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats);
159}
160
161fn class_interface_compute(
166 space_kind: SpaceKind,
167 cyclomatic: &cyclomatic::Stats,
168 stats: &mut Stats,
169) {
170 use SpaceKind::*;
171
172 if let Unit | Class | Interface | Function = space_kind {
173 if stats.space_kind == Unknown {
174 stats.space_kind = space_kind;
175 }
176 if let Function | Class | Interface = space_kind {
181 stats.cyclomatic = cyclomatic.cyclomatic_sum() as f64;
182 }
183 }
184}
185
186impl Wmc for JavaCode {
187 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
188 class_interface_compute(space_kind, cyclomatic, stats);
189 }
190}
191
192impl Wmc for GroovyCode {
193 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
194 class_interface_compute(space_kind, cyclomatic, stats);
195 }
196}
197
198impl Wmc for CsharpCode {
199 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
200 class_interface_compute(space_kind, cyclomatic, stats);
201 }
202}
203
204impl Wmc for KotlinCode {
215 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
216 class_interface_compute(space_kind, cyclomatic, stats);
217 }
218}
219
220impl Wmc for PhpCode {
221 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
222 use SpaceKind::*;
223
224 if let Unit | Class | Interface | Function = space_kind {
227 if stats.space_kind == Unknown {
228 stats.space_kind = space_kind;
229 }
230 if let Function | Class | Interface = space_kind {
236 stats.cyclomatic = cyclomatic.cyclomatic_sum() as f64;
237 }
238 }
239 }
240}
241
242impl Wmc for PythonCode {
256 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
257 class_interface_compute(space_kind, cyclomatic, stats);
258 }
259}
260
261impl Wmc for RustCode {
280 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
281 let mapped = match space_kind {
282 SpaceKind::Impl => SpaceKind::Class,
283 SpaceKind::Trait => SpaceKind::Interface,
284 other => other,
285 };
286 class_interface_compute(mapped, cyclomatic, stats);
287 }
288}
289
290impl Wmc for CppCode {
302 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
303 let mapped = match space_kind {
304 SpaceKind::Struct => SpaceKind::Class,
305 other => other,
306 };
307 class_interface_compute(mapped, cyclomatic, stats);
308 }
309}
310
311impl Wmc for MozcppCode {
312 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
313 let mapped = match space_kind {
314 SpaceKind::Struct => SpaceKind::Class,
315 other => other,
316 };
317 class_interface_compute(mapped, cyclomatic, stats);
318 }
319}
320
321impl Wmc for ObjcCode {
328 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
329 class_interface_compute(space_kind, cyclomatic, stats);
330 }
331}
332
333impl Wmc for TypescriptCode {
342 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
343 class_interface_compute(space_kind, cyclomatic, stats);
344 }
345}
346
347impl Wmc for TsxCode {
348 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
349 class_interface_compute(space_kind, cyclomatic, stats);
350 }
351}
352
353impl Wmc for RubyCode {
361 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
362 class_interface_compute(space_kind, cyclomatic, stats);
363 }
364}
365
366impl Wmc for JavascriptCode {
372 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
373 class_interface_compute(space_kind, cyclomatic, stats);
374 }
375}
376
377impl Wmc for MozjsCode {
378 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
379 class_interface_compute(space_kind, cyclomatic, stats);
380 }
381}
382
383impl Wmc for ElixirCode {
407 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
408 class_interface_compute(space_kind, cyclomatic, stats);
409 }
410}
411
412implement_metric_trait!(
419 Wmc,
420 CCode,
421 PreprocCode,
422 CcommentCode,
423 GoCode,
424 PerlCode,
425 BashCode,
426 LuaCode,
427 TclCode,
428 IrulesCode
429);
430
431#[cfg(test)]
432#[allow(
433 clippy::float_cmp,
434 clippy::cast_precision_loss,
435 clippy::cast_possible_truncation,
436 clippy::cast_sign_loss,
437 clippy::similar_names,
438 clippy::doc_markdown,
439 clippy::needless_raw_string_hashes,
440 clippy::too_many_lines
441)]
442mod tests {
443 use crate::tools::{assert_child_space_kind, check_func_space, check_metrics};
444
445 use super::*;
446
447 #[test]
448 fn java_single_class() {
449 check_metrics::<JavaParser>(
450 "public class Example { // wmc = 13
451
452 public boolean m1(boolean a, boolean b) { // +1
453 boolean r = false;
454 if (a && b == a || b) { // +3
455 r = true;
456 }
457 return r;
458 }
459
460 public boolean m2(int n) { // +1
461 for (int i = 0; i < n; i++) { // +1
462 int j = n;
463 while (j > i) { // +1
464 j--;
465 }
466 }
467 return (n % 2 == 0) ? true : false; // +1
468 }
469
470 public int m3(int x, int y, int z) { // +1
471 int ret;
472 try {
473 z = x/y + y/x;
474 } catch (ArithmeticException e) { // +1
475 z = (x == 0) ? -1 : -2; // +1
476 }
477 switch (z) {
478 case -1: // +1
479 ret = y * y;
480 break;
481 case -2: // +1
482 ret = x * x;
483 break;
484 default:
485 ret = x + y;
486 }
487 return ret;
488 }
489 }",
490 "foo.java",
491 |metric| {
492 insta::assert_json_snapshot!(
494 metric.wmc,
495 @r#"
496 {
497 "class_wmc_sum": 13,
498 "interface_wmc_sum": 0,
499 "total": 13
500 }
501 "#
502 );
503 },
504 );
505 }
506
507 #[test]
508 fn groovy_single_class() {
509 check_metrics::<GroovyParser>(
511 "class Example {
512 boolean m1(boolean a, boolean b) {
513 boolean r = false
514 if (a && b == a || b) {
515 r = true
516 }
517 return r
518 }
519 boolean m2(int n) {
520 for (int i = 0; i < n; i++) {
521 int j = n
522 while (j > i) {
523 j--
524 }
525 }
526 return (n % 2 == 0) ? true : false
527 }
528 }",
529 "foo.groovy",
530 |metric| {
531 assert_eq!(metric.wmc.class_wmc_sum(), 8);
535 },
536 );
537 }
538
539 #[test]
540 fn groovy_empty_class() {
541 check_metrics::<GroovyParser>("class Empty {}", "foo.groovy", |metric| {
542 assert_eq!(metric.wmc.class_wmc_sum(), 0);
543 });
544 }
545
546 #[test]
547 fn groovy_class_with_single_method() {
548 check_metrics::<GroovyParser>(
549 "class A {
550 void foo() {
551 println 'hi'
552 }
553 }",
554 "foo.groovy",
555 |metric| {
556 assert_eq!(metric.wmc.class_wmc_sum(), 1);
558 },
559 );
560 }
561
562 #[test]
563 fn groovy_multiple_classes() {
564 check_metrics::<GroovyParser>(
565 "class A {
566 void f() { if (true) {} }
567 }
568 class B {
569 void g() {}
570 }",
571 "foo.groovy",
572 |metric| {
573 assert_eq!(metric.wmc.class_wmc_sum(), 3);
575 },
576 );
577 }
578
579 #[test]
580 fn groovy_class_with_branching_methods() {
581 check_metrics::<GroovyParser>(
582 "class Calc {
583 int abs(int x) {
584 if (x < 0) {
585 return -x
586 }
587 return x
588 }
589 int sign(int x) {
590 if (x > 0) return 1
591 if (x < 0) return -1
592 return 0
593 }
594 }",
595 "foo.groovy",
596 |metric| {
597 assert_eq!(metric.wmc.class_wmc_sum(), 5);
599 },
600 );
601 }
602
603 #[test]
604 fn groovy_interface_wmc_is_zero() {
605 check_metrics::<GroovyParser>(
607 "interface I {
608 void a()
609 void b()
610 }",
611 "foo.groovy",
612 |metric| {
613 assert_eq!(metric.wmc.class_wmc_sum(), 0);
614 },
615 );
616 }
617
618 #[test]
619 fn groovy_static_nested_class() {
620 check_metrics::<GroovyParser>(
623 "class TopLevelClass {
624 static class StaticNestedClass {
625 private void m() {
626 println 'Test'
627 }
628 }
629 }",
630 "foo.groovy",
631 |metric| {
632 assert_eq!(metric.wmc.class_wmc_sum(), 1);
634 },
635 );
636 }
637
638 #[test]
639 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies"]
640 fn groovy_nested_inner_classes_wmc() {
641 check_metrics::<GroovyParser>(
644 "class X {
645 void a() {}
646 class Y {
647 void b() {}
648 class Z {
649 void c() {}
650 }
651 }
652 }",
653 "foo.groovy",
654 |metric| {
655 assert_eq!(metric.wmc.class_wmc_sum(), 3);
657 },
658 );
659 }
660
661 #[test]
662 fn groovy_local_inner_class() {
663 check_metrics::<GroovyParser>(
672 "class Outer {
673 void m() {
674 class Local {
675 void l() {
676 if (true) {}
677 }
678 }
679 }
680 }",
681 "foo.groovy",
682 |metric| {
683 assert_eq!(metric.wmc.class_wmc_sum(), 3);
684 },
685 );
686 }
687
688 #[test]
689 #[ignore = "dekobon Groovy grammar v1 does not yet support anonymous inner classes (`new T() { … }`)"]
690 fn groovy_anonymous_inner_class_wmc() {
691 check_metrics::<GroovyParser>(
694 "abstract class Base {
695 abstract void m1()
696 }
697 class Top {
698 void m() {
699 def b = new Base() {
700 void m1() {
701 for (int i = 0; i < 5; i++) {
702 println(i)
703 }
704 }
705 }
706 }
707 }",
708 "foo.groovy",
709 |metric| {
710 assert_eq!(metric.wmc.class_wmc_sum(), 4);
712 },
713 );
714 }
715
716 #[test]
717 fn groovy_lambda_expression_wmc() {
718 check_metrics::<GroovyParser>(
721 "class Top {
722 void m1() {
723 def list = [1, 2, 3]
724 list.each { n -> println(n) }
725 }
726 void m2() {
727 if (true) {}
728 }
729 }",
730 "foo.groovy",
731 |metric| {
732 assert_eq!(metric.wmc.class_wmc_sum(), 3);
734 },
735 );
736 }
737
738 #[test]
739 fn groovy_single_interface_wmc() {
740 check_metrics::<GroovyParser>(
743 "interface Example {
744 default boolean m1(boolean a, boolean b) {
745 return (a && b == a || b)
746 }
747 default int m2(int n) {
748 return (n != 0) ? 1/n : n
749 }
750 void m3()
751 }",
752 "foo.groovy",
753 |metric| {
754 assert_eq!(metric.wmc.interface_wmc_sum(), 6);
756 assert_eq!(metric.wmc.class_wmc_sum(), 0);
757 },
758 );
759 }
760
761 #[test]
762 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside interface bodies"]
763 fn groovy_class_in_interface() {
764 check_metrics::<GroovyParser>(
767 "interface Outer {
768 void api()
769 class Inner {
770 void f() {
771 if (true) {}
772 }
773 }
774 }",
775 "foo.groovy",
776 |metric| {
777 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
779 assert_eq!(metric.wmc.class_wmc_sum(), 2);
780 },
781 );
782 }
783
784 #[test]
787 fn groovy_enum_wmc_aggregates_method_complexity() {
788 check_metrics::<GroovyParser>(
789 "enum Status {
790 ACTIVE, INACTIVE;
791 public int code(int n) {
792 if (n > 0) { return n }
793 return 0
794 }
795 }",
796 "foo.groovy",
797 |metric| {
798 assert_eq!(metric.wmc.class_wmc_sum(), 2);
799 },
800 );
801 }
802
803 #[test]
811 #[ignore = "dekobon Groovy grammar v1 does not support annotation type elements with `default` values"]
812 fn groovy_annotation_type_opens_interface_space_with_zero_wmc() {
813 check_func_space::<GroovyParser, _>(
814 "public @interface Marker {
815 String value() default \"\";
816 int priority() default 0;
817 }",
818 "foo.groovy",
819 |func_space| {
820 assert_eq!(func_space.metrics.wmc.interface_wmc_sum(), 0);
821 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
822 },
823 );
824 }
825
826 #[test]
829 fn java_multiple_classes() {
830 check_metrics::<JavaParser>(
831 "public class MainClass { // wmc = 3
832 private int a;
833 public MainClass() { // +1
834 a = 0;
835 }
836 public void setA(int n) { // +1
837 a = n;
838 }
839 public int getA() { // +1
840 return a;
841 }
842 }
843
844 class TopLevelClass { // wmc = 2
845 private int b;
846 public TopLevelClass() { // +1
847 b = 0;
848 }
849 public int getB() { // +1
850 return b;
851 }
852 }",
853 "foo.java",
854 |metric| {
855 insta::assert_json_snapshot!(
857 metric.wmc,
858 @r#"
859 {
860 "class_wmc_sum": 5,
861 "interface_wmc_sum": 0,
862 "total": 5
863 }
864 "#
865 );
866 },
867 );
868 }
869
870 #[test]
871 fn java_static_nested_class() {
872 check_metrics::<JavaParser>(
873 "public class TopLevelClass { // wmc = 0
874 public static class StaticNestedClass { // wmc = 1
875 private void m() { // +1
876 System.out.println(\"Test\");
877 }
878 }
879 }",
880 "foo.java",
881 |metric| {
882 insta::assert_json_snapshot!(
884 metric.wmc,
885 @r#"
886 {
887 "class_wmc_sum": 1,
888 "interface_wmc_sum": 0,
889 "total": 1
890 }
891 "#
892 );
893 },
894 );
895 }
896
897 #[test]
898 fn java_nested_inner_classes() {
899 check_metrics::<JavaParser>(
900 "public class TopLevelClass { // wmc = 2
901 private int a;
902
903 class InnerClassBefore { // wmc = 1
904 private boolean b = (a % 2 == 0) ? true : false;
905 public boolean getB() { // +1
906 return b;
907 }
908 }
909
910 public TopLevelClass(int n) { // +1
911 if (a != n) { // +1
912 a = n;
913 }
914 }
915
916 class InnerClassAfter { // wmc = 2
917 private int c = a;
918
919 public int getC() { // +1
920 return c;
921 }
922 public void setC(int n) { // +1
923 c = n;
924 }
925
926 class InnerClass1 { // wmc = 1
927 private int p1;
928 class InnerClass2 { // wmc = 1
929 private int p2;
930 public int getP2() { // +1
931 return p2;
932 }
933 class InnerClass3 { // wmc = 2
934 private int p3;
935 public int getP3() { // +1
936 return p3;
937 }
938 public void setP3(int n) { // +1
939 p3 = n;
940 }
941 }
942 }
943 public void setP1(int n) { // +1
944 p1 = n;
945 }
946 }
947 }
948 }",
949 "foo.java",
950 |metric| {
951 insta::assert_json_snapshot!(
953 metric.wmc,
954 @r#"
955 {
956 "class_wmc_sum": 9,
957 "interface_wmc_sum": 0,
958 "total": 9
959 }
960 "#
961 );
962 },
963 );
964 }
965
966 #[test]
967 fn java_local_inner_class() {
968 check_metrics::<JavaParser>(
969 "import java.util.LinkedList;
970 import java.util.List;
971
972 public final class FinalClass { // wmc = 1 (test only)
973 private int a = 1;
974 public void test() { // +1
975 final List<String> localList = new LinkedList<String>();
976
977 class LocalInnerClass { // wmc = 2 (print only)
978 private int b = (a == 1) ? 1 : 0; // field init, not a method
979 public void print() { // +1
980 for ( String s : localList ) { // +1
981 System.out.println(s);
982 }
983 }
984 }
985 }
986 }",
987 "foo.java",
988 |metric| {
989 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1000 insta::assert_json_snapshot!(
1001 metric.wmc,
1002 @r#"
1003 {
1004 "class_wmc_sum": 3,
1005 "interface_wmc_sum": 0,
1006 "total": 3
1007 }
1008 "#
1009 );
1010 },
1011 );
1012 }
1013
1014 #[test]
1015 fn java_anonymous_inner_class() {
1016 check_metrics::<JavaParser>(
1017 "abstract class AbstractClass { // wmc = 1
1018 abstract void m1(); // +1
1019 }
1020 public class TopLevelClass{ // wmc = 3
1021 public void m(){ // +1
1022 AbstractClass ac1 = new AbstractClass() {
1023 void m1() { // +1
1024 for (int i = 0; i < 5; i++) { // +1
1025 System.out.println(\"Test 1: \" + i);
1026 }
1027 }
1028 };
1029 ac1.m1();
1030 }
1031 }",
1032 "foo.java",
1033 |metric| {
1034 insta::assert_json_snapshot!(
1036 metric.wmc,
1037 @r#"
1038 {
1039 "class_wmc_sum": 4,
1040 "interface_wmc_sum": 0,
1041 "total": 4
1042 }
1043 "#
1044 );
1045 },
1046 );
1047 }
1048
1049 #[test]
1050 fn java_nested_anonymous_inner_classes() {
1051 check_metrics::<JavaParser>(
1052 "abstract class AbstractClass{ // wmc = 2
1053 abstract void m1(); // +1
1054 abstract void m2(); // +1
1055 }
1056 public class TopLevelClass{ // wmc = 6
1057 public void m(){ // +1
1058
1059 AbstractClass ac1 = new AbstractClass() {
1060 void m1() { // +1
1061 for (int i = 0; i < 5; i++) { // +1
1062 System.out.println(\"Test 1: \" + i);
1063 }
1064 }
1065 void m2() { // +1
1066 AbstractClass ac2 = new AbstractClass() {
1067 void m1() { // +1
1068 System.out.println(\"Test A\");
1069 }
1070 void m2() { // +1
1071 System.out.println(\"Test B\");
1072 }
1073 };
1074 ac2.m2();
1075 System.out.println(\"Test 2\");
1076 }
1077 };
1078 ac1.m1();
1079 }
1080 }",
1081 "foo.java",
1082 |metric| {
1083 insta::assert_json_snapshot!(
1085 metric.wmc,
1086 @r#"
1087 {
1088 "class_wmc_sum": 8,
1089 "interface_wmc_sum": 0,
1090 "total": 8
1091 }
1092 "#
1093 );
1094 },
1095 );
1096 }
1097
1098 #[test]
1099 fn java_lambda_expression() {
1100 check_metrics::<JavaParser>(
1101 "import java.util.ArrayList;
1102
1103 public class TopLevelClass { // wmc = 2
1104 private ArrayList<Integer> numbers;
1105
1106 public void m1() { // +1
1107 numbers = new ArrayList<Integer>();
1108 numbers.add(1);
1109 numbers.add(2);
1110 numbers.add(3);
1111 }
1112
1113 public void m2() { // +1
1114 numbers.forEach( (n) -> { System.out.println(n); } );
1115 }
1116 }",
1117 "foo.java",
1118 |metric| {
1119 insta::assert_json_snapshot!(
1121 metric.wmc,
1122 @r#"
1123 {
1124 "class_wmc_sum": 2,
1125 "interface_wmc_sum": 0,
1126 "total": 2
1127 }
1128 "#
1129 );
1130 },
1131 );
1132 }
1133
1134 #[test]
1135 fn java_single_interface() {
1136 check_metrics::<JavaParser>(
1137 "interface Example { // wmc = 6
1138 default boolean m1(boolean a, boolean b) { // +1
1139 return (a && b == a || b); // +2
1140 }
1141 default int m2(int n) { // +1
1142 return (n != 0) ? 1/n : n; // +1
1143 };
1144 void m3(); // +1
1145 }",
1146 "foo.java",
1147 |metric| {
1148 insta::assert_json_snapshot!(
1150 metric.wmc,
1151 @r#"
1152 {
1153 "class_wmc_sum": 0,
1154 "interface_wmc_sum": 6,
1155 "total": 6
1156 }
1157 "#
1158 );
1159 },
1160 );
1161 }
1162
1163 #[test]
1164 fn java_multiple_interfaces() {
1165 check_metrics::<JavaParser>(
1166 "interface FirstInterface { // wmc = 1
1167 int a = 0;
1168 default int getA() { // +1
1169 return a;
1170 }
1171 }
1172
1173 interface SecondInterface { // wmc = 2
1174 void setB(int n); // +1
1175 int getB(); // +1
1176 }",
1177 "foo.java",
1178 |metric| {
1179 insta::assert_json_snapshot!(
1181 metric.wmc,
1182 @r#"
1183 {
1184 "class_wmc_sum": 0,
1185 "interface_wmc_sum": 3,
1186 "total": 3
1187 }
1188 "#
1189 );
1190 },
1191 );
1192 }
1193
1194 #[test]
1195 fn java_nested_inner_interfaces() {
1196 check_metrics::<JavaParser>(
1197 "interface TopLevelInterface { // wmc = 1
1198 interface InnerInterfaceBefore { // wmc = 1
1199 void m1(); // +1
1200 }
1201
1202 void m2(); // +1
1203
1204 interface InnerInterfaceAfter { // wmc = 2
1205 void m3(); // +1
1206 interface InnerInterface { // wmc = 1
1207 void m4(); // +1
1208 }
1209 void m5(); // +1
1210 }
1211 }",
1212 "foo.java",
1213 |metric| {
1214 insta::assert_json_snapshot!(
1216 metric.wmc,
1217 @r#"
1218 {
1219 "class_wmc_sum": 0,
1220 "interface_wmc_sum": 5,
1221 "total": 5
1222 }
1223 "#
1224 );
1225 },
1226 );
1227 }
1228
1229 #[test]
1230 fn java_class_in_interface() {
1231 check_metrics::<JavaParser>(
1232 "interface TopLevelInterface { // wmc = 2
1233 int getA(); // +1
1234 boolean getB(); // +1
1235
1236 class InnerClass { // wmc = 2
1237 float c;
1238 double d;
1239 float getC() { // +1
1240 return c;
1241 }
1242 double getD() { // +1
1243 return d;
1244 }
1245 }
1246 }",
1247 "foo.java",
1248 |metric| {
1249 insta::assert_json_snapshot!(
1251 metric.wmc,
1252 @r#"
1253 {
1254 "class_wmc_sum": 2,
1255 "interface_wmc_sum": 2,
1256 "total": 4
1257 }
1258 "#
1259 );
1260 },
1261 );
1262 }
1263
1264 #[test]
1265 fn java_interface_in_class() {
1266 check_metrics::<JavaParser>(
1267 "class TopLevelClass { // wmc = 2
1268 int a;
1269 boolean b;
1270 int getA() { // +1
1271 return a;
1272 }
1273 boolean getB() { // +1
1274 return b;
1275 }
1276
1277 interface InnerInterface { // wmc = 2
1278 float getC(); // +1
1279 double getD(); // +1
1280 }
1281 }",
1282 "foo.java",
1283 |metric| {
1284 insta::assert_json_snapshot!(
1286 metric.wmc,
1287 @r#"
1288 {
1289 "class_wmc_sum": 2,
1290 "interface_wmc_sum": 2,
1291 "total": 4
1292 }
1293 "#
1294 );
1295 },
1296 );
1297 }
1298
1299 #[test]
1303 fn java_enum_wmc_aggregates_method_complexity() {
1304 check_metrics::<JavaParser>(
1305 "enum Status {
1306 ACTIVE, INACTIVE;
1307 public int code(int n) { // entry +1
1308 if (n > 0) { // if +1
1309 return n;
1310 }
1311 return 0;
1312 }
1313 }",
1314 "foo.java",
1315 |metric| {
1316 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1318 },
1319 );
1320 }
1321
1322 #[test]
1326 fn java_record_wmc_aggregates_method_complexity() {
1327 check_metrics::<JavaParser>(
1328 "record Point(int x, int y) {
1329 public int describe() { // entry +1
1330 return (x == 0) ? 0 : 1; // ternary +1
1331 }
1332 }",
1333 "foo.java",
1334 |metric| {
1335 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1336 },
1337 );
1338 }
1339
1340 #[test]
1353 fn java_annotation_type_opens_interface_space_with_zero_wmc() {
1354 check_func_space::<JavaParser, _>(
1355 "@interface Marker {
1356 String value() default \"\";
1357 int priority() default 0;
1358 }",
1359 "foo.java",
1360 |func_space| {
1361 assert_eq!(func_space.metrics.wmc.interface_wmc_sum(), 0);
1362 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1365 },
1366 );
1367 }
1368
1369 #[test]
1370 fn csharp_single_class() {
1371 check_metrics::<CsharpParser>(
1372 "public class Example {
1373 public bool M1(bool a, bool b) {
1374 bool r = false;
1375 if (a && b == a || b) {
1376 r = true;
1377 }
1378 return r;
1379 }
1380 public int M2(int n) {
1381 for (int i = 0; i < n; i++) {
1382 int j = n;
1383 while (j > i) {
1384 j--;
1385 }
1386 }
1387 return (n % 2 == 0) ? 1 : 0;
1388 }
1389 }",
1390 "foo.cs",
1391 |metric| {
1392 assert_eq!(metric.wmc.class_wmc_sum(), 8);
1393 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1394 insta::assert_json_snapshot!(metric.wmc);
1395 },
1396 );
1397 }
1398
1399 #[test]
1400 fn csharp_multiple_classes() {
1401 check_metrics::<CsharpParser>(
1402 "public class A {
1403 private int a;
1404 public A() { a = 0; }
1405 public void SetA(int n) { a = n; }
1406 public int GetA() { return a; }
1407 }
1408 class B {
1409 private int b;
1410 public B() { b = 0; }
1411 public int GetB() { return b; }
1412 }",
1413 "foo.cs",
1414 |metric| {
1415 assert_eq!(metric.wmc.class_wmc_sum(), 5);
1416 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1417 insta::assert_json_snapshot!(metric.wmc);
1418 },
1419 );
1420 }
1421
1422 #[test]
1423 fn csharp_static_nested_class() {
1424 check_metrics::<CsharpParser>(
1425 "public class Outer {
1426 public static class Nested {
1427 private void M() {
1428 System.Console.WriteLine(\"Test\");
1429 }
1430 }
1431 }",
1432 "foo.cs",
1433 |metric| {
1434 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1435 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1436 insta::assert_json_snapshot!(metric.wmc);
1437 },
1438 );
1439 }
1440
1441 #[test]
1442 fn csharp_nested_inner_classes() {
1443 check_metrics::<CsharpParser>(
1444 "public class Outer {
1445 private int a;
1446 public class Inner {
1447 public int GetX() { return 0; }
1448 public class Innermost {
1449 public int GetY() { return 1; }
1450 }
1451 }
1452 public int GetA() { return a; }
1453 }",
1454 "foo.cs",
1455 |metric| {
1456 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1457 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1458 insta::assert_json_snapshot!(metric.wmc);
1459 },
1460 );
1461 }
1462
1463 #[test]
1464 fn csharp_local_inner_class() {
1465 check_metrics::<CsharpParser>(
1467 "public class A {
1468 public int M(int x) {
1469 int Local(int y) {
1470 if (y > 0) return y;
1471 return -y;
1472 }
1473 return Local(x);
1474 }
1475 }",
1476 "foo.cs",
1477 |metric| {
1478 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1479 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1480 insta::assert_json_snapshot!(metric.wmc);
1481 },
1482 );
1483 }
1484
1485 #[test]
1486 fn csharp_anonymous_inner_class() {
1487 check_metrics::<CsharpParser>(
1488 "public class A {
1489 public void Run() {
1490 System.Action f = delegate(int x) {
1491 if (x > 0) System.Console.WriteLine(x);
1492 };
1493 }
1494 }",
1495 "foo.cs",
1496 |metric| {
1497 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1498 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1499 insta::assert_json_snapshot!(metric.wmc);
1500 },
1501 );
1502 }
1503
1504 #[test]
1505 fn csharp_nested_anonymous_inner_classes() {
1506 check_metrics::<CsharpParser>(
1507 "public class A {
1508 public void Run() {
1509 System.Action f = delegate(int x) {
1510 System.Action g = delegate(int y) {
1511 if (y > 0) System.Console.WriteLine(y);
1512 };
1513 };
1514 }
1515 }",
1516 "foo.cs",
1517 |metric| {
1518 assert_eq!(metric.wmc.class_wmc_sum(), 4);
1519 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1520 insta::assert_json_snapshot!(metric.wmc);
1521 },
1522 );
1523 }
1524
1525 #[test]
1526 fn csharp_lambda_expression() {
1527 check_metrics::<CsharpParser>(
1528 "public class A {
1529 public void Run() {
1530 System.Func<int, int> f = x => x > 0 ? x : -x;
1531 }
1532 }",
1533 "foo.cs",
1534 |metric| {
1535 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1536 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1537 insta::assert_json_snapshot!(metric.wmc);
1538 },
1539 );
1540 }
1541
1542 #[test]
1543 fn csharp_indexer_wmc() {
1544 check_metrics::<CsharpParser>(
1550 "class A {
1551 private int[] _d;
1552 public int this[int i] { get => _d[i]; set => _d[i] = value; }
1553 }",
1554 "foo.cs",
1555 |metric| {
1556 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1559 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1560 assert_eq!(metric.npm.class_nm_sum(), 2);
1561 insta::assert_json_snapshot!(metric.wmc);
1562 },
1563 );
1564 }
1565
1566 #[test]
1567 fn csharp_expression_bodied_indexer_wmc() {
1568 check_metrics::<CsharpParser>(
1573 "class A {
1574 private int[] _d;
1575 public int this[int i] => _d[i];
1576 }",
1577 "foo.cs",
1578 |metric| {
1579 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1581 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1582 assert_eq!(metric.npm.class_nm_sum(), 1);
1583 insta::assert_json_snapshot!(metric.wmc);
1584 },
1585 );
1586 }
1587
1588 #[test]
1589 fn csharp_property_wmc() {
1590 check_metrics::<CsharpParser>(
1595 "class A {
1596 private int _w;
1597 public int W { get => _w; set => _w = value; }
1598 }",
1599 "foo.cs",
1600 |metric| {
1601 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1604 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1605 assert_eq!(metric.npm.class_nm_sum(), 2);
1606 },
1607 );
1608 }
1609
1610 #[test]
1611 fn csharp_expression_bodied_property_wmc() {
1612 check_metrics::<CsharpParser>(
1617 "class A {
1618 private int _w;
1619 public int W => _w;
1620 }",
1621 "foo.cs",
1622 |metric| {
1623 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1625 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1626 assert_eq!(metric.npm.class_nm_sum(), 1);
1627 },
1628 );
1629 }
1630
1631 #[test]
1632 fn csharp_single_interface() {
1633 check_metrics::<CsharpParser>(
1634 "public interface I {
1635 int GetA();
1636 int GetB();
1637 }",
1638 "foo.cs",
1639 |metric| {
1640 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1641 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1642 insta::assert_json_snapshot!(metric.wmc);
1643 },
1644 );
1645 }
1646
1647 #[test]
1648 fn csharp_multiple_interfaces() {
1649 check_metrics::<CsharpParser>(
1650 "public interface I1 { int GetA(); }
1651 public interface I2 { bool GetB(); float GetC(); }",
1652 "foo.cs",
1653 |metric| {
1654 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1655 assert_eq!(metric.wmc.interface_wmc_sum(), 3);
1656 insta::assert_json_snapshot!(metric.wmc);
1657 },
1658 );
1659 }
1660
1661 #[test]
1662 fn csharp_nested_inner_interfaces() {
1663 check_metrics::<CsharpParser>(
1664 "public interface I1 {
1665 int GetA();
1666 public interface I2 {
1667 bool GetB();
1668 }
1669 }",
1670 "foo.cs",
1671 |metric| {
1672 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1673 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1674 insta::assert_json_snapshot!(metric.wmc);
1675 },
1676 );
1677 }
1678
1679 #[test]
1680 fn csharp_class_in_interface() {
1681 check_metrics::<CsharpParser>(
1682 "public interface I {
1683 int GetA();
1684 public class Helper {
1685 public int M() { return 0; }
1686 }
1687 }",
1688 "foo.cs",
1689 |metric| {
1690 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1691 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
1692 insta::assert_json_snapshot!(metric.wmc);
1693 },
1694 );
1695 }
1696
1697 #[test]
1698 fn csharp_interface_in_class() {
1699 check_metrics::<CsharpParser>(
1700 "class Outer {
1701 int a;
1702 bool b;
1703 public int GetA() { return a; }
1704 public bool GetB() { return b; }
1705 public interface InnerI {
1706 float GetC();
1707 double GetD();
1708 }
1709 }",
1710 "foo.cs",
1711 |metric| {
1712 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1713 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1714 insta::assert_json_snapshot!(metric.wmc);
1715 },
1716 );
1717 }
1718
1719 #[test]
1720 fn php_no_classes() {
1721 check_metrics::<PhpParser>(
1722 "<?php function f(): int { return 1; }",
1723 "foo.php",
1724 |metric| insta::assert_json_snapshot!(metric.wmc),
1725 );
1726 }
1727
1728 #[test]
1729 fn php_one_class_simple() {
1730 check_metrics::<PhpParser>(
1731 "<?php
1732 class A {
1733 public function a(): int { return 1; }
1734 public function b(): int { return 2; }
1735 }",
1736 "foo.php",
1737 |metric| insta::assert_json_snapshot!(metric.wmc),
1738 );
1739 }
1740
1741 #[test]
1742 fn php_one_class_with_loops() {
1743 check_metrics::<PhpParser>(
1744 "<?php
1745 class A {
1746 public function f(int $n): int {
1747 $sum = 0;
1748 for ($i = 0; $i < $n; $i++) {
1749 $sum += $i;
1750 }
1751 return $sum;
1752 }
1753 }",
1754 "foo.php",
1755 |metric| insta::assert_json_snapshot!(metric.wmc),
1756 );
1757 }
1758
1759 #[test]
1760 fn php_one_class_with_branches() {
1761 check_metrics::<PhpParser>(
1762 "<?php
1763 class A {
1764 public function f(int $x): int {
1765 if ($x > 0) {
1766 return 1;
1767 }
1768 if ($x < 0) {
1769 return -1;
1770 }
1771 return 0;
1772 }
1773 }",
1774 "foo.php",
1775 |metric| insta::assert_json_snapshot!(metric.wmc),
1776 );
1777 }
1778
1779 #[test]
1780 fn php_class_with_methods_only() {
1781 check_metrics::<PhpParser>(
1782 "<?php
1783 class A {
1784 public function a(): void {}
1785 public function b(): void {}
1786 public function c(): void {}
1787 }",
1788 "foo.php",
1789 |metric| insta::assert_json_snapshot!(metric.wmc),
1790 );
1791 }
1792
1793 #[test]
1794 fn php_multiple_classes() {
1795 check_metrics::<PhpParser>(
1796 "<?php
1797 class A {
1798 public function f(int $x): int {
1799 if ($x > 0) { return 1; }
1800 return 0;
1801 }
1802 }
1803 class B {
1804 public function g(int $x): int {
1805 return $x;
1806 }
1807 }",
1808 "foo.php",
1809 |metric| insta::assert_json_snapshot!(metric.wmc),
1810 );
1811 }
1812
1813 #[test]
1814 fn php_anonymous_class() {
1815 check_metrics::<PhpParser>(
1816 "<?php
1817 $obj = new class {
1818 public function f(int $x): int {
1819 if ($x > 0) { return 1; }
1820 return 0;
1821 }
1822 };",
1823 "foo.php",
1824 |metric| insta::assert_json_snapshot!(metric.wmc),
1825 );
1826 }
1827
1828 #[test]
1829 fn php_class_with_static_methods() {
1830 check_metrics::<PhpParser>(
1831 "<?php
1832 class A {
1833 public static function f(int $x): int {
1834 if ($x > 0) { return 1; }
1835 return 0;
1836 }
1837 public static function g(): int { return 1; }
1838 }",
1839 "foo.php",
1840 |metric| insta::assert_json_snapshot!(metric.wmc),
1841 );
1842 }
1843
1844 #[test]
1845 fn php_interface_wmc() {
1846 check_metrics::<PhpParser>(
1847 "<?php
1848 interface I {
1849 public function a(): void;
1850 public function b(): int;
1851 }",
1852 "foo.php",
1853 |metric| insta::assert_json_snapshot!(metric.wmc),
1854 );
1855 }
1856
1857 #[test]
1858 fn php_trait_wmc() {
1859 check_metrics::<PhpParser>(
1860 "<?php
1861 trait T {
1862 public function f(int $x): int {
1863 if ($x > 0) { return 1; }
1864 return 0;
1865 }
1866 }",
1867 "foo.php",
1868 |metric| insta::assert_json_snapshot!(metric.wmc),
1869 );
1870 }
1871
1872 #[test]
1873 fn php_enum_with_methods() {
1874 check_metrics::<PhpParser>(
1875 "<?php
1876 enum Color {
1877 case Red;
1878 case Green;
1879 public function label(): string {
1880 return match ($this) {
1881 Color::Red => 'r',
1882 Color::Green => 'g',
1883 };
1884 }
1885 }",
1886 "foo.php",
1887 |metric| insta::assert_json_snapshot!(metric.wmc),
1888 );
1889 }
1890
1891 #[test]
1892 fn php_class_inside_namespace() {
1893 check_metrics::<PhpParser>(
1894 "<?php
1895 namespace App;
1896 class A {
1897 public function f(int $x): int {
1898 if ($x > 0) { return 1; }
1899 return 0;
1900 }
1901 }",
1902 "foo.php",
1903 |metric| insta::assert_json_snapshot!(metric.wmc),
1904 );
1905 }
1906
1907 #[test]
1908 fn php_class_complex() {
1909 check_metrics::<PhpParser>(
1910 "<?php
1911 class Calc {
1912 public function add(int $a, int $b): int {
1913 if ($a > 0 && $b > 0) {
1914 return $a + $b;
1915 }
1916 return 0;
1917 }
1918 public function loop(int $n): int {
1919 $s = 0;
1920 for ($i = 0; $i < $n; $i++) {
1921 if ($i % 2 === 0) { $s += $i; }
1922 }
1923 return $s;
1924 }
1925 }",
1926 "foo.php",
1927 |metric| insta::assert_json_snapshot!(metric.wmc),
1928 );
1929 }
1930
1931 #[test]
1940 fn kotlin_empty_class() {
1941 check_metrics::<KotlinParser>("class Empty {}", "foo.kt", |metric| {
1943 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1944 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1945 insta::assert_json_snapshot!(metric.wmc);
1946 });
1947 }
1948
1949 #[test]
1950 fn kotlin_single_class() {
1951 check_metrics::<KotlinParser>(
1954 "class C {
1955 fun m(x: Int): Int { // +1
1956 if (x > 0) { // +1
1957 return x
1958 }
1959 return when (x) {
1960 0 -> 0 // +1 (WhenEntry)
1961 else -> -x // skipped (else is default)
1962 }
1963 }
1964 }",
1965 "foo.kt",
1966 |metric| {
1967 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1968 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1969 insta::assert_json_snapshot!(metric.wmc);
1970 },
1971 );
1972 }
1973
1974 #[test]
1975 fn kotlin_multiple_classes() {
1976 check_metrics::<KotlinParser>(
1979 "class A {
1980 private var a: Int = 0
1981 constructor(n: Int) { a = n } // +1
1982 fun setA(n: Int) { a = n } // +1
1983 fun getA(): Int = a // +1
1984 }
1985 class B {
1986 private var b: Int = 0
1987 constructor(n: Int) { b = n } // +1
1988 fun getB(): Int = b // +1
1989 }",
1990 "foo.kt",
1991 |metric| {
1992 assert_eq!(metric.wmc.class_wmc_sum(), 5);
1993 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1994 insta::assert_json_snapshot!(metric.wmc);
1995 },
1996 );
1997 }
1998
1999 #[test]
2000 fn kotlin_nested_class() {
2001 check_metrics::<KotlinParser>(
2003 "class Outer {
2004 class Nested {
2005 fun m() { println(\"hi\") } // +1
2006 }
2007 }",
2008 "foo.kt",
2009 |metric| {
2010 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2011 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2012 insta::assert_json_snapshot!(metric.wmc);
2013 },
2014 );
2015 }
2016
2017 #[test]
2018 fn kotlin_inner_class() {
2019 check_metrics::<KotlinParser>(
2022 "class Outer {
2023 fun outerM() {} // +1
2024 inner class Inner {
2025 fun innerM() {} // +1
2026 }
2027 }",
2028 "foo.kt",
2029 |metric| {
2030 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2031 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2032 insta::assert_json_snapshot!(metric.wmc);
2033 },
2034 );
2035 }
2036
2037 #[test]
2038 fn kotlin_data_class() {
2039 check_metrics::<KotlinParser>(
2043 "data class Point(val x: Int, val y: Int) {
2044 fun manhattan(): Int = kotlin.math.abs(x) + kotlin.math.abs(y) // +1
2045 }",
2046 "foo.kt",
2047 |metric| {
2048 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2049 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2050 insta::assert_json_snapshot!(metric.wmc);
2051 },
2052 );
2053 }
2054
2055 #[test]
2056 fn kotlin_object_singleton() {
2057 check_metrics::<KotlinParser>(
2060 "object Util {
2061 fun add(a: Int, b: Int): Int = a + b // +1
2062 fun gtZero(n: Int): Boolean { // +1
2063 return n > 0
2064 }
2065 }",
2066 "foo.kt",
2067 |metric| {
2068 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2069 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2070 insta::assert_json_snapshot!(metric.wmc);
2071 },
2072 );
2073 }
2074
2075 #[test]
2076 fn kotlin_companion_object() {
2077 check_metrics::<KotlinParser>(
2083 "class Holder {
2084 val instance: Int = 1
2085 fun get(): Int = instance // +1
2086 companion object {
2087 val SCALE: Int = 10
2088 fun mk(): Holder = Holder() // +1
2089 }
2090 }",
2091 "foo.kt",
2092 |metric| {
2093 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2094 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2095 insta::assert_json_snapshot!(metric.wmc);
2096 },
2097 );
2098 }
2099
2100 #[test]
2101 fn kotlin_companion_object_opens_class_space() {
2102 check_func_space::<KotlinParser, _>(
2108 "class Holder {
2109 fun get(): Int = 1
2110 companion object Companion {
2111 fun mk(): Holder = Holder()
2112 }
2113 }",
2114 "foo.kt",
2115 |func_space| {
2116 let holder = func_space
2117 .spaces
2118 .iter()
2119 .find(|s| s.name.as_deref() == Some("Holder"))
2120 .expect("expected a child FuncSpace named \"Holder\"");
2121 assert_child_space_kind(holder, "Companion", crate::SpaceKind::Class);
2124 let companion = holder
2125 .spaces
2126 .iter()
2127 .find(|s| s.name.as_deref() == Some("Companion"))
2128 .expect("expected a child FuncSpace named \"Companion\"");
2129 assert_eq!(companion.metrics.wmc.class_wmc_sum(), 1);
2133 assert_eq!(holder.metrics.wmc.class_wmc_sum(), 2);
2134 },
2135 );
2136 }
2137
2138 #[test]
2139 fn kotlin_object_literal_opens_class_space() {
2140 check_func_space::<KotlinParser, _>(
2148 "class Holder {
2149 fun get(): Int {
2150 val r = object : Runnable {
2151 override fun run() {}
2152 fun helper(): Int = 42
2153 }
2154 return 1
2155 }
2156 }",
2157 "foo.kt",
2158 |func_space| {
2159 let holder = func_space
2160 .spaces
2161 .iter()
2162 .find(|s| s.name.as_deref() == Some("Holder"))
2163 .expect("expected a child FuncSpace named \"Holder\"");
2164 let get = holder
2165 .spaces
2166 .iter()
2167 .find(|s| s.name.as_deref() == Some("get"))
2168 .expect("expected a child FuncSpace named \"get\"");
2169 assert_child_space_kind(get, "<anonymous>", crate::SpaceKind::Class);
2172 let anon = get
2173 .spaces
2174 .iter()
2175 .find(|s| s.name.as_deref() == Some("<anonymous>"))
2176 .expect("expected a child FuncSpace named \"<anonymous>\"");
2177 assert_eq!(
2182 get.spaces.len(),
2183 1,
2184 "get's only child is the anonymous class"
2185 );
2186 let anon_methods = anon
2187 .spaces
2188 .iter()
2189 .filter(|s| s.kind == crate::SpaceKind::Function)
2190 .count();
2191 assert_eq!(
2192 anon_methods, 2,
2193 "run + helper attributed to the anonymous class"
2194 );
2195 assert_eq!(
2200 get.metrics.nom.functions(),
2201 1,
2202 "get owns only itself; run/helper are not folded in"
2203 );
2204 assert_eq!(anon.metrics.wmc.class_wmc_sum(), 2);
2208 },
2209 );
2210 }
2211
2212 #[test]
2213 fn java_anonymous_class_opens_space() {
2214 check_func_space::<JavaParser, _>(
2222 "class C {
2223 void m() {
2224 Runnable r = new Runnable() {
2225 public void run() {}
2226 };
2227 Object o = new Object();
2228 Runnable l = () -> {};
2229 }
2230 }",
2231 "C.java",
2232 |func_space| {
2233 let c = func_space
2234 .spaces
2235 .iter()
2236 .find(|s| s.name.as_deref() == Some("C"))
2237 .expect("expected a child FuncSpace named \"C\"");
2238 let m = c
2239 .spaces
2240 .iter()
2241 .find(|s| s.name.as_deref() == Some("m"))
2242 .expect("expected a child FuncSpace named \"m\"");
2243 let anon_classes: Vec<_> = m
2248 .spaces
2249 .iter()
2250 .filter(|s| s.kind == crate::SpaceKind::Class)
2251 .collect();
2252 assert_eq!(anon_classes.len(), 1, "exactly one anonymous Class space");
2253 assert_eq!(anon_classes[0].name.as_deref(), Some("<anonymous>"));
2254 let anon_methods = anon_classes[0]
2257 .spaces
2258 .iter()
2259 .filter(|s| s.kind == crate::SpaceKind::Function)
2260 .count();
2261 assert_eq!(anon_methods, 1, "run attributed to the anonymous class");
2262 assert_eq!(anon_classes[0].metrics.wmc.class_wmc_sum(), 1);
2263 },
2264 );
2265 }
2266
2267 #[test]
2268 fn java_lambda_opens_no_class_space() {
2269 check_func_space::<JavaParser, _>(
2274 "class C {
2275 void m() {
2276 Runnable l = () -> { int x = 1; };
2277 }
2278 }",
2279 "C.java",
2280 |func_space| {
2281 let c = func_space
2282 .spaces
2283 .iter()
2284 .find(|s| s.name.as_deref() == Some("C"))
2285 .expect("expected a child FuncSpace named \"C\"");
2286 let m = c
2287 .spaces
2288 .iter()
2289 .find(|s| s.name.as_deref() == Some("m"))
2290 .expect("expected a child FuncSpace named \"m\"");
2291 let class_children = m
2292 .spaces
2293 .iter()
2294 .filter(|s| s.kind == crate::SpaceKind::Class)
2295 .count();
2296 assert_eq!(class_children, 0, "a lambda must not open a Class space");
2297 },
2298 );
2299 }
2300
2301 #[test]
2302 fn groovy_anonymous_class_models_body_as_closure() {
2303 check_func_space::<GroovyParser, _>(
2315 "class C {
2316 void m() {
2317 def r = new Runnable() {
2318 void run() {}
2319 }
2320 }
2321 }",
2322 "C.groovy",
2323 |func_space| {
2324 let c = func_space
2325 .spaces
2326 .iter()
2327 .find(|s| s.name.as_deref() == Some("C"))
2328 .expect("expected a child FuncSpace named \"C\"");
2329 let m = c
2330 .spaces
2331 .iter()
2332 .find(|s| s.name.as_deref() == Some("m"))
2333 .expect("expected a child FuncSpace named \"m\"");
2334 let class_children = m
2337 .spaces
2338 .iter()
2339 .filter(|s| s.kind == crate::SpaceKind::Class)
2340 .count();
2341 assert_eq!(
2342 class_children, 0,
2343 "Groovy grammar models the body as a closure, not a class"
2344 );
2345 assert_eq!(m.metrics.nom.functions(), 1, "`m` itself, not `run`");
2346 let nested_funcs: u64 = m.spaces.iter().map(|s| s.metrics.nom.functions()).sum();
2347 assert_eq!(
2348 nested_funcs, 1,
2349 "`run` is attributed to the nested closure space"
2350 );
2351 },
2352 );
2353 }
2354
2355 #[test]
2356 fn kotlin_interface_simple() {
2357 check_metrics::<KotlinParser>(
2359 "interface I {
2360 fun work(): Int // +1
2361 fun describe(): String // +1
2362 }",
2363 "foo.kt",
2364 |metric| {
2365 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2366 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
2367 insta::assert_json_snapshot!(metric.wmc);
2368 },
2369 );
2370 }
2371
2372 #[test]
2373 fn kotlin_interface_with_default_method() {
2374 check_metrics::<KotlinParser>(
2376 "interface I {
2377 fun abs(n: Int): Int { // +1
2378 return if (n < 0) -n else n // +1 if
2379 }
2380 fun pure(): Int // +1
2381 }",
2382 "foo.kt",
2383 |metric| {
2384 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2385 assert_eq!(metric.wmc.interface_wmc_sum(), 3);
2386 insta::assert_json_snapshot!(metric.wmc);
2387 },
2388 );
2389 }
2390
2391 #[test]
2392 fn kotlin_override_function() {
2393 check_metrics::<KotlinParser>(
2396 "open class Base {
2397 open fun greet(): String = \"hi\" // +1
2398 }
2399 class Sub : Base() {
2400 override fun greet(): String = \"yo\" // +1
2401 }",
2402 "foo.kt",
2403 |metric| {
2404 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2405 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2406 insta::assert_json_snapshot!(metric.wmc);
2407 },
2408 );
2409 }
2410
2411 #[test]
2412 fn kotlin_secondary_constructor() {
2413 check_metrics::<KotlinParser>(
2416 "class C {
2417 private var a: Int = 0
2418 constructor(n: Int) { // +1
2419 a = n
2420 }
2421 constructor(n: Int, m: Int) { // +1
2422 a = n + m
2423 }
2424 fun get(): Int = a // +1
2425 }",
2426 "foo.kt",
2427 |metric| {
2428 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2429 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2430 insta::assert_json_snapshot!(metric.wmc);
2431 },
2432 );
2433 }
2434
2435 #[test]
2436 fn kotlin_init_block() {
2437 check_metrics::<KotlinParser>(
2441 "class C(val n: Int) {
2442 init { // not counted
2443 require(n >= 0) { \"n must be non-negative\" }
2444 }
2445 fun get(): Int = n // +1
2446 }",
2447 "foo.kt",
2448 |metric| {
2449 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2450 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2451 insta::assert_json_snapshot!(metric.wmc);
2452 },
2453 );
2454 }
2455
2456 #[test]
2457 fn kotlin_top_level_function_excluded() {
2458 check_metrics::<KotlinParser>(
2461 "fun freeFunction(): Int = 42
2462 val freeVal: Int = 0
2463 class C { fun m(): Int = 1 } // +1
2464 ",
2465 "foo.kt",
2466 |metric| {
2467 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2468 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2469 insta::assert_json_snapshot!(metric.wmc);
2470 },
2471 );
2472 }
2473
2474 #[test]
2475 fn kotlin_extension_function_excluded() {
2476 check_metrics::<KotlinParser>(
2481 "fun List<Int>.sum2(): Int = this.size // top-level
2482 class C { fun m(): Int = 1 } // +1
2483 ",
2484 "foo.kt",
2485 |metric| {
2486 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2487 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2488 insta::assert_json_snapshot!(metric.wmc);
2489 },
2490 );
2491 }
2492
2493 #[test]
2494 fn kotlin_generic_class() {
2495 check_metrics::<KotlinParser>(
2497 "class Box<T>(val value: T) {
2498 fun get(): T = value // +1
2499 fun mapTo(f: (T) -> T): T = f(value) // +1
2500 }",
2501 "foo.kt",
2502 |metric| {
2503 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2504 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2505 insta::assert_json_snapshot!(metric.wmc);
2506 },
2507 );
2508 }
2509
2510 #[test]
2511 fn kotlin_class_in_interface() {
2512 check_metrics::<KotlinParser>(
2516 "interface Outer {
2517 fun work(): Int // +1 (interface)
2518 class Helper {
2519 fun help(): Int = 0 // +1 (class)
2520 }
2521 }",
2522 "foo.kt",
2523 |metric| {
2524 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2525 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
2526 insta::assert_json_snapshot!(metric.wmc);
2527 },
2528 );
2529 }
2530
2531 #[test]
2532 fn kotlin_interface_in_class() {
2533 check_metrics::<KotlinParser>(
2535 "class Outer {
2536 fun work(): Int = 1 // +1 (class)
2537 interface Sub {
2538 fun help(): Int // +1 (interface)
2539 }
2540 }",
2541 "foo.kt",
2542 |metric| {
2543 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2544 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
2545 insta::assert_json_snapshot!(metric.wmc);
2546 },
2547 );
2548 }
2549
2550 #[test]
2558 fn typescript_class_wmc_single_method() {
2559 check_metrics::<TypescriptParser>(
2560 "class C {
2561 m(): number { return 1; } // cyclomatic 1
2562 }",
2563 "foo.ts",
2564 |metric| {
2565 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2566 insta::assert_json_snapshot!(metric.wmc);
2567 },
2568 );
2569 }
2570
2571 #[test]
2572 fn typescript_class_wmc_two_methods() {
2573 check_metrics::<TypescriptParser>(
2574 "class C {
2575 a(): number { return 1; } // +1
2576 b(x: number): number { // +2 (if branch)
2577 if (x > 0) return x;
2578 return 0;
2579 }
2580 }",
2581 "foo.ts",
2582 |metric| {
2583 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2584 insta::assert_json_snapshot!(metric.wmc);
2585 },
2586 );
2587 }
2588
2589 #[test]
2590 fn typescript_class_wmc_with_branches() {
2591 check_metrics::<TypescriptParser>(
2592 "class C {
2593 m(x: number): number {
2594 if (x > 0) { // +1
2595 return 1;
2596 } else if (x < 0) { // +1
2597 return -1;
2598 }
2599 return 0;
2600 } // base 1
2601 }",
2602 "foo.ts",
2603 |metric| {
2604 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2605 insta::assert_json_snapshot!(metric.wmc);
2606 },
2607 );
2608 }
2609
2610 #[test]
2611 fn typescript_class_wmc_arrow_field() {
2612 check_metrics::<TypescriptParser>(
2615 "class C {
2616 arrow = (x: number) => {
2617 if (x > 0) return x; // +1
2618 return 0;
2619 }; // base 1
2620 }",
2621 "foo.ts",
2622 |metric| {
2623 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2624 insta::assert_json_snapshot!(metric.wmc);
2625 },
2626 );
2627 }
2628
2629 #[test]
2630 fn typescript_class_wmc_with_loops() {
2631 check_metrics::<TypescriptParser>(
2632 "class C {
2633 m(xs: number[]): number {
2634 let total = 0;
2635 for (const x of xs) { // +1
2636 total += x;
2637 }
2638 return total;
2639 } // base 1
2640 }",
2641 "foo.ts",
2642 |metric| {
2643 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2644 insta::assert_json_snapshot!(metric.wmc);
2645 },
2646 );
2647 }
2648
2649 #[test]
2650 fn typescript_abstract_class_wmc() {
2651 check_metrics::<TypescriptParser>(
2653 "abstract class C {
2654 abstract a(): void; // signature only, 0
2655 m(): number { return 1; } // +1
2656 }",
2657 "foo.ts",
2658 |metric| {
2659 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2660 insta::assert_json_snapshot!(metric.wmc);
2661 },
2662 );
2663 }
2664
2665 #[test]
2666 fn typescript_interface_wmc_zero() {
2667 check_metrics::<TypescriptParser>(
2669 "interface I {
2670 a(): void;
2671 b(): number;
2672 }",
2673 "foo.ts",
2674 |metric| {
2675 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2676 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2677 insta::assert_json_snapshot!(metric.wmc);
2678 },
2679 );
2680 }
2681
2682 #[test]
2683 fn typescript_constructor_wmc() {
2684 check_metrics::<TypescriptParser>(
2687 "class C {
2688 x: number;
2689 constructor(n: number) {
2690 if (n > 0) { // +1
2691 this.x = n;
2692 } else {
2693 this.x = 0;
2694 }
2695 } // base 1
2696 }",
2697 "foo.ts",
2698 |metric| {
2699 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2700 insta::assert_json_snapshot!(metric.wmc);
2701 },
2702 );
2703 }
2704
2705 #[test]
2706 fn typescript_getter_setter_wmc() {
2707 check_metrics::<TypescriptParser>(
2709 "class C {
2710 _x: number = 0;
2711 get x(): number { return this._x; }
2712 set x(v: number) { this._x = v; }
2713 }",
2714 "foo.ts",
2715 |metric| {
2716 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2717 insta::assert_json_snapshot!(metric.wmc);
2718 },
2719 );
2720 }
2721
2722 #[test]
2723 fn typescript_multiple_classes_wmc_independent() {
2724 check_metrics::<TypescriptParser>(
2725 "class A { m(): number { return 1; } }
2726 class B {
2727 m(x: number): number {
2728 if (x > 0) return x; // +1
2729 return 0;
2730 } // base 1
2731 }",
2732 "foo.ts",
2733 |metric| {
2734 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2736 insta::assert_json_snapshot!(metric.wmc);
2737 },
2738 );
2739 }
2740
2741 #[test]
2742 fn typescript_class_wmc_with_ternary_and_logical() {
2743 check_metrics::<TypescriptParser>(
2744 "class C {
2745 m(x: number, y: number): number {
2746 return x > 0 && y > 0 // +1 (ternary) +1 (&&)
2747 ? x + y
2748 : 0;
2749 } // base 1
2750 }",
2751 "foo.ts",
2752 |metric| {
2753 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2754 insta::assert_json_snapshot!(metric.wmc);
2755 },
2756 );
2757 }
2758
2759 #[test]
2760 fn typescript_generic_class_wmc() {
2761 check_metrics::<TypescriptParser>(
2762 "class Box<T> {
2763 value: T;
2764 set(v: T): void { this.value = v; }
2765 get(): T { return this.value; }
2766 }",
2767 "foo.ts",
2768 |metric| {
2769 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2770 insta::assert_json_snapshot!(metric.wmc);
2771 },
2772 );
2773 }
2774
2775 #[test]
2778 fn tsx_class_wmc_single_method() {
2779 check_metrics::<TsxParser>(
2780 "class C { m(): number { return 1; } }",
2781 "foo.tsx",
2782 |metric| {
2783 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2784 insta::assert_json_snapshot!(metric.wmc);
2785 },
2786 );
2787 }
2788
2789 #[test]
2790 fn tsx_class_wmc_two_methods() {
2791 check_metrics::<TsxParser>(
2792 "class C {
2793 a(): number { return 1; }
2794 b(x: number): number {
2795 if (x > 0) return x;
2796 return 0;
2797 }
2798 }",
2799 "foo.tsx",
2800 |metric| {
2801 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2802 insta::assert_json_snapshot!(metric.wmc);
2803 },
2804 );
2805 }
2806
2807 #[test]
2808 fn tsx_class_wmc_with_branches() {
2809 check_metrics::<TsxParser>(
2810 "class C {
2811 m(x: number): number {
2812 if (x > 0) return 1;
2813 else if (x < 0) return -1;
2814 return 0;
2815 }
2816 }",
2817 "foo.tsx",
2818 |metric| {
2819 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2820 insta::assert_json_snapshot!(metric.wmc);
2821 },
2822 );
2823 }
2824
2825 #[test]
2826 fn tsx_class_wmc_arrow_field() {
2827 check_metrics::<TsxParser>(
2828 "class C {
2829 arrow = (x: number) => {
2830 if (x > 0) return x;
2831 return 0;
2832 };
2833 }",
2834 "foo.tsx",
2835 |metric| {
2836 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2837 insta::assert_json_snapshot!(metric.wmc);
2838 },
2839 );
2840 }
2841
2842 #[test]
2843 fn tsx_class_wmc_with_loops() {
2844 check_metrics::<TsxParser>(
2845 "class C {
2846 m(xs: number[]): number {
2847 let total = 0;
2848 for (const x of xs) { total += x; }
2849 return total;
2850 }
2851 }",
2852 "foo.tsx",
2853 |metric| {
2854 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2855 insta::assert_json_snapshot!(metric.wmc);
2856 },
2857 );
2858 }
2859
2860 #[test]
2861 fn tsx_abstract_class_wmc() {
2862 check_metrics::<TsxParser>(
2863 "abstract class C {
2864 abstract a(): void;
2865 m(): number { return 1; }
2866 }",
2867 "foo.tsx",
2868 |metric| {
2869 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2870 insta::assert_json_snapshot!(metric.wmc);
2871 },
2872 );
2873 }
2874
2875 #[test]
2876 fn tsx_interface_wmc_zero() {
2877 check_metrics::<TsxParser>(
2878 "interface I { a(): void; b(): number; }",
2879 "foo.tsx",
2880 |metric| {
2881 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2882 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2883 insta::assert_json_snapshot!(metric.wmc);
2884 },
2885 );
2886 }
2887
2888 #[test]
2889 fn tsx_constructor_wmc() {
2890 check_metrics::<TsxParser>(
2891 "class C {
2892 x: number;
2893 constructor(n: number) {
2894 if (n > 0) this.x = n;
2895 else this.x = 0;
2896 }
2897 }",
2898 "foo.tsx",
2899 |metric| {
2900 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2901 insta::assert_json_snapshot!(metric.wmc);
2902 },
2903 );
2904 }
2905
2906 #[test]
2907 fn tsx_getter_setter_wmc() {
2908 check_metrics::<TsxParser>(
2909 "class C {
2910 _x: number = 0;
2911 get x(): number { return this._x; }
2912 set x(v: number) { this._x = v; }
2913 }",
2914 "foo.tsx",
2915 |metric| {
2916 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2917 insta::assert_json_snapshot!(metric.wmc);
2918 },
2919 );
2920 }
2921
2922 #[test]
2923 fn tsx_multiple_classes_wmc_independent() {
2924 check_metrics::<TsxParser>(
2925 "class A { m(): number { return 1; } }
2926 class B {
2927 m(x: number): number {
2928 if (x > 0) return x;
2929 return 0;
2930 }
2931 }",
2932 "foo.tsx",
2933 |metric| {
2934 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2935 insta::assert_json_snapshot!(metric.wmc);
2936 },
2937 );
2938 }
2939
2940 #[test]
2941 fn tsx_class_wmc_with_ternary_and_logical() {
2942 check_metrics::<TsxParser>(
2943 "class C {
2944 m(x: number, y: number): number {
2945 return x > 0 && y > 0 ? x + y : 0;
2946 }
2947 }",
2948 "foo.tsx",
2949 |metric| {
2950 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2951 insta::assert_json_snapshot!(metric.wmc);
2952 },
2953 );
2954 }
2955
2956 #[test]
2957 fn tsx_generic_class_wmc() {
2958 check_metrics::<TsxParser>(
2959 "class Box<T> {
2960 value: T;
2961 set(v: T): void { this.value = v; }
2962 get(): T { return this.value; }
2963 }",
2964 "foo.tsx",
2965 |metric| {
2966 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2967 insta::assert_json_snapshot!(metric.wmc);
2968 },
2969 );
2970 }
2971
2972 #[test]
2980 fn ruby_no_classes() {
2981 check_metrics::<RubyParser>("def foo\n 1\nend\n", "foo.rb", |metric| {
2983 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2984 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2985 insta::assert_json_snapshot!(metric.wmc);
2986 });
2987 }
2988
2989 #[test]
2990 fn ruby_empty_class() {
2991 check_metrics::<RubyParser>("class Foo\nend\n", "foo.rb", |metric| {
2993 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2994 insta::assert_json_snapshot!(metric.wmc);
2995 });
2996 }
2997
2998 #[test]
2999 fn ruby_one_class_simple() {
3000 check_metrics::<RubyParser>(
3002 "class A\n def a\n 1\n end\n def b\n 2\n end\nend\n",
3003 "foo.rb",
3004 |metric| {
3005 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3006 insta::assert_json_snapshot!(metric.wmc);
3007 },
3008 );
3009 }
3010
3011 #[test]
3012 fn ruby_one_class_with_branch() {
3013 check_metrics::<RubyParser>(
3015 "class A\n def f(x)\n if x > 0\n 1\n else\n 0\n end\n end\nend\n",
3016 "foo.rb",
3017 |metric| {
3018 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3019 insta::assert_json_snapshot!(metric.wmc);
3020 },
3021 );
3022 }
3023
3024 #[test]
3025 fn ruby_one_class_with_loop() {
3026 check_metrics::<RubyParser>(
3028 "class A\n def f(n)\n while n > 0\n n -= 1\n end\n end\nend\n",
3029 "foo.rb",
3030 |metric| {
3031 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3032 insta::assert_json_snapshot!(metric.wmc);
3033 },
3034 );
3035 }
3036
3037 #[test]
3038 fn ruby_singleton_method_included() {
3039 check_metrics::<RubyParser>(
3042 "class A\n def f\n 1\n end\n def self.g\n 2\n end\nend\n",
3043 "foo.rb",
3044 |metric| {
3045 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3046 insta::assert_json_snapshot!(metric.wmc);
3047 },
3048 );
3049 }
3050
3051 #[test]
3052 fn ruby_singleton_class_methods_included() {
3053 check_metrics::<RubyParser>(
3056 "class A\n class << self\n def s\n 1\n end\n end\nend\n",
3057 "foo.rb",
3058 |metric| {
3059 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3062 insta::assert_json_snapshot!(metric.wmc);
3063 },
3064 );
3065 }
3066
3067 #[test]
3068 fn ruby_multiple_classes() {
3069 check_metrics::<RubyParser>(
3071 "class A\n def f(x)\n if x > 0\n 1\n end\n end\nend\nclass B\n def g\n 1\n end\nend\n",
3072 "foo.rb",
3073 |metric| {
3074 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3076 insta::assert_json_snapshot!(metric.wmc);
3077 },
3078 );
3079 }
3080
3081 #[test]
3082 fn ruby_module_only() {
3083 check_metrics::<RubyParser>(
3086 "module M\n def f\n 1\n end\nend\n",
3087 "foo.rb",
3088 |metric| {
3089 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3090 insta::assert_json_snapshot!(metric.wmc);
3091 },
3092 );
3093 }
3094
3095 #[test]
3096 fn ruby_class_with_inheritance() {
3097 check_metrics::<RubyParser>(
3100 "class A < B\n def f\n 1\n end\n def g\n 2\n end\nend\n",
3101 "foo.rb",
3102 |metric| {
3103 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3104 insta::assert_json_snapshot!(metric.wmc);
3105 },
3106 );
3107 }
3108
3109 #[test]
3110 fn ruby_class_with_visibility_keywords() {
3111 check_metrics::<RubyParser>(
3114 "class A\n def a\n 1\n end\n private\n def b\n 1\n end\n protected\n def c\n 1\n end\nend\n",
3115 "foo.rb",
3116 |metric| {
3117 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3118 insta::assert_json_snapshot!(metric.wmc);
3119 },
3120 );
3121 }
3122
3123 #[test]
3124 fn ruby_class_complex() {
3125 check_metrics::<RubyParser>(
3130 "class Calc\n def add(a, b)\n if a > 0 && b > 0\n a + b\n end\n end\n def loop(n)\n s = 0\n while n > 0\n if n.even?\n s += n\n end\n n -= 1\n end\n s\n end\nend\n",
3131 "foo.rb",
3132 |metric| {
3133 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3134 insta::assert_json_snapshot!(metric.wmc);
3135 },
3136 );
3137 }
3138
3139 #[test]
3151 fn python_empty_class_zero_wmc() {
3152 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
3153 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3154 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3155 insta::assert_json_snapshot!(metric.wmc);
3156 });
3157 }
3158
3159 #[test]
3160 fn python_single_method_wmc_one() {
3161 check_metrics::<PythonParser>(
3163 "class C:\n def m(self):\n return 1\n",
3164 "foo.py",
3165 |metric| {
3166 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3167 insta::assert_json_snapshot!(metric.wmc);
3168 },
3169 );
3170 }
3171
3172 #[test]
3173 fn python_method_with_if_adds_to_wmc() {
3174 check_metrics::<PythonParser>(
3176 "class C:\n def m(self, x):\n if x > 0:\n return 1\n return 0\n",
3177 "foo.py",
3178 |metric| {
3179 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3180 insta::assert_json_snapshot!(metric.wmc);
3181 },
3182 );
3183 }
3184
3185 #[test]
3186 fn python_multiple_methods_wmc_sums() {
3187 check_metrics::<PythonParser>(
3190 "class C:\n\
3191 \x20 def m1(self):\n\
3192 \x20 return 1\n\
3193 \x20 def m2(self, x):\n\
3194 \x20 if x:\n\
3195 \x20 return 1\n\
3196 \x20 return 0\n\
3197 \x20 def m3(self, xs):\n\
3198 \x20 for x in xs:\n\
3199 \x20 if x:\n\
3200 \x20 return x\n\
3201 \x20 return None\n",
3202 "foo.py",
3203 |metric| {
3204 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3205 insta::assert_json_snapshot!(metric.wmc);
3206 },
3207 );
3208 }
3209
3210 #[test]
3211 fn python_top_level_function_does_not_contribute_to_class_wmc() {
3212 check_metrics::<PythonParser>(
3215 "def f(x):\n if x:\n return 1\n return 0\n",
3216 "foo.py",
3217 |metric| {
3218 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3219 insta::assert_json_snapshot!(metric.wmc);
3220 },
3221 );
3222 }
3223
3224 #[test]
3225 fn python_multiple_classes_wmc_independent() {
3226 check_metrics::<PythonParser>(
3230 "class A:\n\
3231 \x20 def m1(self):\n\
3232 \x20 return 1\n\
3233 class B:\n\
3234 \x20 def m2(self, x):\n\
3235 \x20 if x:\n\
3236 \x20 return 1\n\
3237 \x20 return 0\n",
3238 "foo.py",
3239 |metric| {
3240 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3241 insta::assert_json_snapshot!(metric.wmc);
3242 },
3243 );
3244 }
3245
3246 #[test]
3247 fn rust_empty_unit_zero_wmc() {
3248 check_metrics::<RustParser>("", "empty.rs", |metric| {
3249 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3250 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3251 insta::assert_json_snapshot!(metric.wmc);
3252 });
3253 }
3254
3255 #[test]
3256 fn rust_single_impl_method_wmc_one() {
3257 check_metrics::<RustParser>(
3259 "struct Foo;\nimpl Foo { fn m(&self) -> i32 { 1 } }\n",
3260 "foo.rs",
3261 |metric| {
3262 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3263 insta::assert_json_snapshot!(metric.wmc);
3264 },
3265 );
3266 }
3267
3268 #[test]
3269 fn rust_method_with_if_adds_to_wmc() {
3270 check_metrics::<RustParser>(
3272 "struct Foo;\n\
3273 impl Foo {\n\
3274 \x20 fn m(&self, x: i32) -> i32 {\n\
3275 \x20 if x > 0 { 1 } else { 0 }\n\
3276 \x20 }\n\
3277 }\n",
3278 "foo.rs",
3279 |metric| {
3280 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3281 insta::assert_json_snapshot!(metric.wmc);
3282 },
3283 );
3284 }
3285
3286 #[test]
3287 fn rust_multiple_methods_wmc_sums() {
3288 check_metrics::<RustParser>(
3291 "struct Foo;\n\
3292 impl Foo {\n\
3293 \x20 fn m1(&self) -> i32 { 1 }\n\
3294 \x20 fn m2(&self, x: i32) -> i32 { if x > 0 { 1 } else { 0 } }\n\
3295 \x20 fn m3(&self, xs: &[i32]) -> i32 {\n\
3296 \x20 for x in xs { if *x > 0 { return *x; } }\n\
3297 \x20 0\n\
3298 \x20 }\n\
3299 }\n",
3300 "foo.rs",
3301 |metric| {
3302 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3303 insta::assert_json_snapshot!(metric.wmc);
3304 },
3305 );
3306 }
3307
3308 #[test]
3309 fn rust_multiple_impls_wmc_aggregate() {
3310 check_metrics::<RustParser>(
3313 "struct Foo;\n\
3314 impl Foo { fn m1(&self) {} }\n\
3315 impl Foo { fn m2(&self) {} }\n",
3316 "foo.rs",
3317 |metric| {
3318 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3319 insta::assert_json_snapshot!(metric.wmc);
3320 },
3321 );
3322 }
3323
3324 #[test]
3325 fn rust_trait_default_method_contributes_to_interface_wmc() {
3326 check_metrics::<RustParser>(
3331 "trait T { fn draw(&self); fn area(&self) -> f64 { 0.0 } }",
3332 "foo.rs",
3333 |metric| {
3334 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
3335 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3336 insta::assert_json_snapshot!(metric.wmc);
3337 },
3338 );
3339 }
3340
3341 #[test]
3342 fn rust_top_level_function_does_not_contribute_to_class_wmc() {
3343 check_metrics::<RustParser>(
3347 "fn f(x: i32) -> i32 { if x > 0 { 1 } else { 0 } }",
3348 "foo.rs",
3349 |metric| {
3350 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3351 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3352 insta::assert_json_snapshot!(metric.wmc);
3353 },
3354 );
3355 }
3356
3357 #[test]
3360 fn go_wmc_is_zero_documented_limitation() {
3361 check_metrics::<GoParser>(
3371 "package main\n\
3372 type Foo struct{}\n\
3373 func (f Foo) M(x int) int { if x > 0 { return 1 } else { return 0 } }\n\
3374 func (f Foo) N() {}\n",
3375 "foo.go",
3376 |metric| {
3377 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3378 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3379 insta::assert_json_snapshot!(metric.wmc);
3380 },
3381 );
3382 }
3383
3384 #[test]
3393 fn elixir_wmc_aggregates_def_methods() {
3394 check_metrics::<ElixirParser>(
3395 "defmodule Foo do\n def m(x) do\n if x > 0 do\n 1\n else\n 0\n end\n end\n def n, do: :ok\nend\n",
3396 "foo.ex",
3397 |metric| {
3398 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3400 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3401 insta::assert_json_snapshot!(
3402 metric.wmc,
3403 @r#"
3404 {
3405 "class_wmc_sum": 3,
3406 "interface_wmc_sum": 0,
3407 "total": 3
3408 }
3409 "#
3410 );
3411 },
3412 );
3413 }
3414
3415 #[test]
3416 fn elixir_wmc_def_plus_defp_counts_both() {
3417 check_metrics::<ElixirParser>(
3418 "defmodule Foo do\n def pub_one, do: 1\n defp priv_one, do: 1\nend\n",
3419 "foo.ex",
3420 |metric| {
3421 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3424 },
3425 );
3426 }
3427
3428 #[test]
3429 fn elixir_wmc_defmacro_counts() {
3430 check_metrics::<ElixirParser>(
3431 "defmodule Foo do\n defmacro stuff(x) do\n if x > 0, do: :pos, else: :neg\n end\nend\n",
3432 "foo.ex",
3433 |metric| {
3434 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3436 },
3437 );
3438 }
3439
3440 #[test]
3441 fn elixir_wmc_multiple_clauses_each_a_method() {
3442 check_metrics::<ElixirParser>(
3445 "defmodule Foo do\n def f(0), do: :zero\n def f(_), do: :other\nend\n",
3446 "foo.ex",
3447 |metric| {
3448 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3450 },
3451 );
3452 }
3453
3454 #[test]
3455 fn elixir_wmc_nested_defmodule_isolates() {
3456 check_metrics::<ElixirParser>(
3457 "defmodule Outer do\n def o, do: 1\n defmodule Inner do\n def i, do: 1\n end\nend\n",
3458 "foo.ex",
3459 |metric| {
3460 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3462 },
3463 );
3464 }
3465
3466 #[test]
3467 fn elixir_wmc_user_macro_not_classified_as_method() {
3468 check_metrics::<ElixirParser>(
3478 "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",
3479 "foo.ex",
3480 |metric| {
3481 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3484 },
3485 );
3486 }
3487
3488 #[test]
3489 fn elixir_wmc_quoted_defs_do_not_inflate_method_count() {
3490 check_metrics::<ElixirParser>(
3501 "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",
3502 "foo.ex",
3503 |metric| {
3504 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3507 },
3508 );
3509 }
3510
3511 #[test]
3514 fn objc_wmc() {
3515 check_metrics::<ObjcParser>(
3522 "@interface Foo : NSObject\n\
3523 - (int)a:(int)x;\n\
3524 @end\n\
3525 @implementation Foo\n\
3526 - (int)a:(int)x { if (x > 0) { return 1; } return 0; }\n\
3527 - (void)b { }\n\
3528 @end\n",
3529 "foo.m",
3530 |metric| {
3531 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3532 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3533 },
3534 );
3535 }
3536
3537 #[test]
3540 fn cpp_empty_unit_zero_wmc() {
3541 check_metrics::<CppParser>("", "empty.cpp", |metric| {
3543 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3544 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3545 insta::assert_json_snapshot!(metric.wmc);
3546 });
3547 }
3548
3549 #[test]
3550 fn cpp_single_method_wmc_one() {
3551 check_metrics::<CppParser>("class Foo { public: void m() {} };", "foo.cpp", |metric| {
3553 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3554 insta::assert_json_snapshot!(metric.wmc);
3555 });
3556 }
3557
3558 #[test]
3559 fn cpp_method_with_if_adds_to_wmc() {
3560 check_metrics::<CppParser>(
3562 "class Foo {\n\
3563 public:\n\
3564 int m(int x) {\n\
3565 if (x > 0) { return 1; }\n\
3566 return 0;\n\
3567 }\n\
3568 };",
3569 "foo.cpp",
3570 |metric| {
3571 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3572 insta::assert_json_snapshot!(metric.wmc);
3573 },
3574 );
3575 }
3576
3577 #[test]
3578 fn cpp_struct_wmc_maps_to_class() {
3579 check_metrics::<CppParser>(
3583 "struct Foo {\n\
3584 int m(int x) {\n\
3585 if (x > 0) { return 1; }\n\
3586 return 0;\n\
3587 }\n\
3588 };",
3589 "foo.cpp",
3590 |metric| {
3591 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3592 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3593 insta::assert_json_snapshot!(metric.wmc);
3594 },
3595 );
3596 }
3597
3598 #[test]
3599 fn cpp_free_function_does_not_contribute_to_class_wmc() {
3600 check_metrics::<CppParser>(
3606 "int free_fn(int x) { if (x > 0) { return 1; } return 0; }",
3607 "foo.cpp",
3608 |metric| {
3609 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3610 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3611 insta::assert_json_snapshot!(metric.wmc);
3612 },
3613 );
3614 }
3615
3616 #[test]
3617 fn cpp_multiple_methods_wmc_sums() {
3618 check_metrics::<CppParser>(
3621 "class Foo {\n\
3622 public:\n\
3623 int a(int x) { if (x > 0) { return 1; } return 0; }\n\
3624 int b() { return 42; }\n\
3625 };",
3626 "foo.cpp",
3627 |metric| {
3628 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3629 insta::assert_json_snapshot!(metric.wmc);
3630 },
3631 );
3632 }
3633
3634 #[test]
3635 fn cpp_multiple_classes_wmc_aggregate() {
3636 check_metrics::<CppParser>(
3639 "class Foo { public: void a() {} };\nstruct Bar { void b() {} };",
3640 "foo.cpp",
3641 |metric| {
3642 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3643 insta::assert_json_snapshot!(metric.wmc);
3644 },
3645 );
3646 }
3647
3648 #[test]
3649 fn javascript_empty_unit_zero_wmc() {
3650 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
3651 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3652 insta::assert_json_snapshot!(metric.wmc);
3653 });
3654 }
3655
3656 #[test]
3657 fn javascript_single_method_wmc_one() {
3658 check_metrics::<JavascriptParser>("class Foo { a() { return 1; } }", "foo.js", |metric| {
3661 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3662 insta::assert_json_snapshot!(metric.wmc);
3663 });
3664 }
3665
3666 #[test]
3667 fn javascript_method_with_if_adds_to_wmc() {
3668 check_metrics::<JavascriptParser>(
3670 "class Foo { a(x) { if (x > 0) return 1; return 0; } }",
3671 "foo.js",
3672 |metric| {
3673 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3674 insta::assert_json_snapshot!(metric.wmc);
3675 },
3676 );
3677 }
3678
3679 #[test]
3680 fn javascript_free_function_does_not_contribute_to_class_wmc() {
3681 check_metrics::<JavascriptParser>(
3684 "function f(x) { if (x > 0) return 1; return 0; }\nclass Foo { a() { return 1; } }",
3685 "foo.js",
3686 |metric| {
3687 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3689 insta::assert_json_snapshot!(metric.wmc);
3690 },
3691 );
3692 }
3693
3694 #[test]
3695 fn javascript_multiple_classes_wmc_aggregate() {
3696 check_metrics::<JavascriptParser>(
3699 "class Foo { a() { return 1; } }\nclass Bar { b() { return 1; } }",
3700 "foo.js",
3701 |metric| {
3702 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3703 insta::assert_json_snapshot!(metric.wmc);
3704 },
3705 );
3706 }
3707
3708 #[test]
3709 fn mozjs_single_method_wmc_one() {
3710 check_metrics::<MozjsParser>("class Foo { a() { return 1; } }", "foo.js", |metric| {
3711 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3712 insta::assert_json_snapshot!(metric.wmc);
3713 });
3714 }
3715
3716 #[test]
3723 fn java_method_with_nested_class_wmc_is_non_negative_integer() {
3724 check_metrics::<JavaParser>(
3725 "public class Outer {
3726 public void m() {
3727 class Inner {
3728 public void n() {}
3729 }
3730 }
3731 }",
3732 "Outer.java",
3733 |metric| {
3734 let total = metric.wmc.class_wmc_sum();
3735 assert_eq!(total, 2, "Outer.m (1) + Inner.n (1) with no double-count");
3736 },
3737 );
3738 }
3739}