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::metrics::MemberScopeExt;
22use crate::*;
23
24#[derive(Debug, Clone, Default, PartialEq)]
32#[non_exhaustive]
33pub struct Stats {
34 cyclomatic: f64,
35 nested_class_cyclomatic: f64,
43 non_member: bool,
51 class_wmc: f64,
52 interface_wmc: f64,
53 class_wmc_sum: f64,
54 interface_wmc_sum: f64,
55 space_kind: SpaceKind,
56}
57
58impl fmt::Display for Stats {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 write!(
61 f,
62 "classes: {}, interfaces: {}, total: {}",
63 self.class_wmc_sum(),
64 self.interface_wmc_sum(),
65 self.total_wmc()
66 )
67 }
68}
69
70impl Stats {
71 pub fn merge(&mut self, other: &Stats) {
73 use SpaceKind::*;
74
75 match other.space_kind {
79 Function => {
87 let own_cyclomatic = other.cyclomatic - other.nested_class_cyclomatic;
88 if !other.non_member {
95 match self.space_kind {
96 Class => self.class_wmc += own_cyclomatic,
97 Interface => self.interface_wmc += own_cyclomatic,
98 _ => {}
99 }
100 }
101 self.nested_class_cyclomatic += other.nested_class_cyclomatic;
102 }
103 Class | Interface => self.nested_class_cyclomatic += other.cyclomatic,
110 _ => {}
111 }
112
113 self.class_wmc_sum += other.class_wmc_sum;
114 self.interface_wmc_sum += other.interface_wmc_sum;
115 }
116
117 #[inline]
119 #[must_use]
120 pub fn class_wmc(&self) -> u64 {
121 self.class_wmc as u64
122 }
123
124 #[inline]
126 #[must_use]
127 pub fn interface_wmc(&self) -> u64 {
128 self.interface_wmc as u64
129 }
130
131 #[inline]
133 #[must_use]
134 pub fn class_wmc_sum(&self) -> u64 {
135 self.class_wmc_sum as u64
136 }
137
138 #[inline]
140 #[must_use]
141 pub fn interface_wmc_sum(&self) -> u64 {
142 self.interface_wmc_sum as u64
143 }
144
145 #[inline]
147 #[must_use]
148 pub fn total_wmc(&self) -> u64 {
149 self.class_wmc_sum() + self.interface_wmc_sum()
150 }
151
152 #[inline]
155 pub(crate) fn compute_sum(&mut self) {
156 self.class_wmc_sum += self.class_wmc;
157 self.interface_wmc_sum += self.interface_wmc;
158 }
159
160 #[inline]
165 pub(crate) fn mark_non_member(&mut self) {
166 self.non_member = true;
167 }
168
169 #[inline]
171 pub(crate) fn is_disabled(&self) -> bool {
172 !self.space_kind.is_member_scope()
173 }
174}
175
176#[doc(hidden)]
177pub(crate) trait Wmc
179where
180 Self: Checker,
181{
182 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats);
185}
186
187fn class_interface_compute(
192 space_kind: SpaceKind,
193 cyclomatic: &cyclomatic::Stats,
194 stats: &mut Stats,
195) {
196 use SpaceKind::*;
197
198 if let Unit | Class | Interface | Function = space_kind {
199 if stats.space_kind == Unknown {
200 stats.space_kind = space_kind;
201 }
202 if let Function | Class | Interface = space_kind {
207 stats.cyclomatic = cyclomatic.cyclomatic_sum() as f64;
208 }
209 }
210}
211
212impl Wmc for JavaCode {
213 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
214 class_interface_compute(space_kind, cyclomatic, stats);
215 }
216}
217
218impl Wmc for GroovyCode {
219 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
220 class_interface_compute(space_kind, cyclomatic, stats);
221 }
222}
223
224impl Wmc for CsharpCode {
225 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
226 class_interface_compute(space_kind, cyclomatic, stats);
227 }
228}
229
230impl Wmc for KotlinCode {
241 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
242 class_interface_compute(space_kind, cyclomatic, stats);
243 }
244}
245
246impl Wmc for PhpCode {
247 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
248 use SpaceKind::*;
249
250 if let Unit | Class | Interface | Function = space_kind {
253 if stats.space_kind == Unknown {
254 stats.space_kind = space_kind;
255 }
256 if let Function | Class | Interface = space_kind {
262 stats.cyclomatic = cyclomatic.cyclomatic_sum() as f64;
263 }
264 }
265 }
266}
267
268impl Wmc for PythonCode {
282 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
283 class_interface_compute(space_kind, cyclomatic, stats);
284 }
285}
286
287impl Wmc for RustCode {
306 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
307 let mapped = match space_kind {
308 SpaceKind::Impl => SpaceKind::Class,
309 SpaceKind::Trait => SpaceKind::Interface,
310 other => other,
311 };
312 class_interface_compute(mapped, cyclomatic, stats);
313 }
314}
315
316impl Wmc for CppCode {
328 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
329 let mapped = match space_kind {
330 SpaceKind::Struct => SpaceKind::Class,
331 other => other,
332 };
333 class_interface_compute(mapped, cyclomatic, stats);
334 }
335}
336
337impl Wmc for MozcppCode {
338 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
339 let mapped = match space_kind {
340 SpaceKind::Struct => SpaceKind::Class,
341 other => other,
342 };
343 class_interface_compute(mapped, cyclomatic, stats);
344 }
345}
346
347impl Wmc for ObjcCode {
359 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
360 class_interface_compute(space_kind, cyclomatic, stats);
361 }
362}
363
364impl Wmc for TypescriptCode {
373 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
374 class_interface_compute(space_kind, cyclomatic, stats);
375 }
376}
377
378impl Wmc for TsxCode {
379 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
380 class_interface_compute(space_kind, cyclomatic, stats);
381 }
382}
383
384impl Wmc for RubyCode {
392 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
393 class_interface_compute(space_kind, cyclomatic, stats);
394 }
395}
396
397impl Wmc for JavascriptCode {
403 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
404 class_interface_compute(space_kind, cyclomatic, stats);
405 }
406}
407
408impl Wmc for MozjsCode {
409 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
410 class_interface_compute(space_kind, cyclomatic, stats);
411 }
412}
413
414impl Wmc for ElixirCode {
438 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
439 class_interface_compute(space_kind, cyclomatic, stats);
440 }
441}
442
443implement_metric_trait!(
450 Wmc,
451 CCode,
452 PreprocCode,
453 CcommentCode,
454 GoCode,
455 PerlCode,
456 BashCode,
457 LuaCode,
458 TclCode,
459 IrulesCode
460);
461
462#[cfg(test)]
463#[allow(
464 clippy::float_cmp,
465 clippy::cast_precision_loss,
466 clippy::cast_possible_truncation,
467 clippy::cast_sign_loss,
468 clippy::similar_names,
469 clippy::doc_markdown,
470 clippy::needless_raw_string_hashes,
471 clippy::too_many_lines
472)]
473mod tests {
474 use crate::test_support::{
475 assert_child_space_kind, check_func_space_only_shim, check_metrics_only_shim, child_space,
476 };
477
478 use super::*;
479
480 check_metrics_only_shim!(check_metrics, Wmc);
483 check_func_space_only_shim!(check_func_space, Wmc);
484 check_metrics_only_shim!(check_wmc_and_npm, Wmc, Npm);
488
489 #[cfg(feature = "java")]
490 #[test]
491 fn java_single_class() {
492 check_metrics::<JavaParser>(
493 "public class Example { // wmc = 13
494
495 public boolean m1(boolean a, boolean b) { // +1
496 boolean r = false;
497 if (a && b == a || b) { // +3
498 r = true;
499 }
500 return r;
501 }
502
503 public boolean m2(int n) { // +1
504 for (int i = 0; i < n; i++) { // +1
505 int j = n;
506 while (j > i) { // +1
507 j--;
508 }
509 }
510 return (n % 2 == 0) ? true : false; // +1
511 }
512
513 public int m3(int x, int y, int z) { // +1
514 int ret;
515 try {
516 z = x/y + y/x;
517 } catch (ArithmeticException e) { // +1
518 z = (x == 0) ? -1 : -2; // +1
519 }
520 switch (z) {
521 case -1: // +1
522 ret = y * y;
523 break;
524 case -2: // +1
525 ret = x * x;
526 break;
527 default:
528 ret = x + y;
529 }
530 return ret;
531 }
532 }",
533 "foo.java",
534 |metric| {
535 insta::assert_json_snapshot!(
537 metric.wmc,
538 @r#"
539 {
540 "class_wmc_sum": 13,
541 "interface_wmc_sum": 0,
542 "total": 13
543 }
544 "#
545 );
546 },
547 );
548 }
549
550 #[cfg(feature = "groovy")]
551 #[test]
552 fn groovy_single_class() {
553 check_metrics::<GroovyParser>(
555 "class Example {
556 boolean m1(boolean a, boolean b) {
557 boolean r = false
558 if (a && b == a || b) {
559 r = true
560 }
561 return r
562 }
563 boolean m2(int n) {
564 for (int i = 0; i < n; i++) {
565 int j = n
566 while (j > i) {
567 j--
568 }
569 }
570 return (n % 2 == 0) ? true : false
571 }
572 }",
573 "foo.groovy",
574 |metric| {
575 assert_eq!(metric.wmc.class_wmc_sum(), 8);
579 },
580 );
581 }
582
583 #[cfg(feature = "groovy")]
584 #[test]
585 fn groovy_empty_class() {
586 check_metrics::<GroovyParser>("class Empty {}", "foo.groovy", |metric| {
587 assert_eq!(metric.wmc.class_wmc_sum(), 0);
588 });
589 }
590
591 #[cfg(feature = "groovy")]
592 #[test]
593 fn groovy_class_with_single_method() {
594 check_metrics::<GroovyParser>(
595 "class A {
596 void foo() {
597 println 'hi'
598 }
599 }",
600 "foo.groovy",
601 |metric| {
602 assert_eq!(metric.wmc.class_wmc_sum(), 1);
604 },
605 );
606 }
607
608 #[cfg(feature = "groovy")]
609 #[test]
610 fn groovy_multiple_classes() {
611 check_metrics::<GroovyParser>(
612 "class A {
613 void f() { if (true) {} }
614 }
615 class B {
616 void g() {}
617 }",
618 "foo.groovy",
619 |metric| {
620 assert_eq!(metric.wmc.class_wmc_sum(), 3);
622 },
623 );
624 }
625
626 #[cfg(feature = "groovy")]
627 #[test]
628 fn groovy_class_with_branching_methods() {
629 check_metrics::<GroovyParser>(
630 "class Calc {
631 int abs(int x) {
632 if (x < 0) {
633 return -x
634 }
635 return x
636 }
637 int sign(int x) {
638 if (x > 0) return 1
639 if (x < 0) return -1
640 return 0
641 }
642 }",
643 "foo.groovy",
644 |metric| {
645 assert_eq!(metric.wmc.class_wmc_sum(), 5);
647 },
648 );
649 }
650
651 #[cfg(feature = "groovy")]
652 #[test]
653 fn groovy_interface_wmc_is_zero() {
654 check_metrics::<GroovyParser>(
656 "interface I {
657 void a()
658 void b()
659 }",
660 "foo.groovy",
661 |metric| {
662 assert_eq!(metric.wmc.class_wmc_sum(), 0);
663 },
664 );
665 }
666
667 #[cfg(feature = "groovy")]
668 #[test]
669 fn groovy_static_nested_class() {
670 check_metrics::<GroovyParser>(
673 "class TopLevelClass {
674 static class StaticNestedClass {
675 private void m() {
676 println 'Test'
677 }
678 }
679 }",
680 "foo.groovy",
681 |metric| {
682 assert_eq!(metric.wmc.class_wmc_sum(), 1);
684 },
685 );
686 }
687
688 #[cfg(feature = "groovy")]
689 #[test]
690 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside class bodies"]
691 fn groovy_nested_inner_classes_wmc() {
692 check_metrics::<GroovyParser>(
695 "class X {
696 void a() {}
697 class Y {
698 void b() {}
699 class Z {
700 void c() {}
701 }
702 }
703 }",
704 "foo.groovy",
705 |metric| {
706 assert_eq!(metric.wmc.class_wmc_sum(), 3);
708 },
709 );
710 }
711
712 #[cfg(feature = "groovy")]
713 #[test]
714 fn groovy_local_inner_class() {
715 check_metrics::<GroovyParser>(
724 "class Outer {
725 void m() {
726 class Local {
727 void l() {
728 if (true) {}
729 }
730 }
731 }
732 }",
733 "foo.groovy",
734 |metric| {
735 assert_eq!(metric.wmc.class_wmc_sum(), 3);
736 },
737 );
738 }
739
740 #[cfg(feature = "groovy")]
741 #[test]
742 #[ignore = "dekobon Groovy grammar v1 does not yet support anonymous inner classes (`new T() { … }`)"]
743 fn groovy_anonymous_inner_class_wmc() {
744 check_metrics::<GroovyParser>(
747 "abstract class Base {
748 abstract void m1()
749 }
750 class Top {
751 void m() {
752 def b = new Base() {
753 void m1() {
754 for (int i = 0; i < 5; i++) {
755 println(i)
756 }
757 }
758 }
759 }
760 }",
761 "foo.groovy",
762 |metric| {
763 assert_eq!(metric.wmc.class_wmc_sum(), 4);
765 },
766 );
767 }
768
769 #[cfg(feature = "groovy")]
770 #[test]
771 fn groovy_lambda_expression_wmc() {
772 check_metrics::<GroovyParser>(
775 "class Top {
776 void m1() {
777 def list = [1, 2, 3]
778 list.each { n -> println(n) }
779 }
780 void m2() {
781 if (true) {}
782 }
783 }",
784 "foo.groovy",
785 |metric| {
786 assert_eq!(metric.wmc.class_wmc_sum(), 3);
788 },
789 );
790 }
791
792 #[cfg(feature = "groovy")]
793 #[test]
794 fn groovy_single_interface_wmc() {
795 check_metrics::<GroovyParser>(
798 "interface Example {
799 default boolean m1(boolean a, boolean b) {
800 return (a && b == a || b)
801 }
802 default int m2(int n) {
803 return (n != 0) ? 1/n : n
804 }
805 void m3()
806 }",
807 "foo.groovy",
808 |metric| {
809 assert_eq!(metric.wmc.interface_wmc_sum(), 6);
811 assert_eq!(metric.wmc.class_wmc_sum(), 0);
812 },
813 );
814 }
815
816 #[cfg(feature = "groovy")]
817 #[test]
818 #[ignore = "dekobon Groovy grammar v1 does not yet support inner classes inside interface bodies"]
819 fn groovy_class_in_interface() {
820 check_metrics::<GroovyParser>(
823 "interface Outer {
824 void api()
825 class Inner {
826 void f() {
827 if (true) {}
828 }
829 }
830 }",
831 "foo.groovy",
832 |metric| {
833 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
835 assert_eq!(metric.wmc.class_wmc_sum(), 2);
836 },
837 );
838 }
839
840 #[cfg(feature = "groovy")]
843 #[test]
844 fn groovy_enum_wmc_aggregates_method_complexity() {
845 check_metrics::<GroovyParser>(
846 "enum Status {
847 ACTIVE, INACTIVE;
848 public int code(int n) {
849 if (n > 0) { return n }
850 return 0
851 }
852 }",
853 "foo.groovy",
854 |metric| {
855 assert_eq!(metric.wmc.class_wmc_sum(), 2);
856 },
857 );
858 }
859
860 #[cfg(feature = "groovy")]
868 #[test]
869 #[ignore = "dekobon Groovy grammar v1 does not support annotation type elements with `default` values"]
870 fn groovy_annotation_type_opens_interface_space_with_zero_wmc() {
871 check_func_space::<GroovyParser, _>(
872 "public @interface Marker {
873 String value() default \"\";
874 int priority() default 0;
875 }",
876 "foo.groovy",
877 |func_space| {
878 assert_eq!(func_space.metrics.wmc.interface_wmc_sum(), 0);
879 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
880 },
881 );
882 }
883
884 #[cfg(feature = "java")]
887 #[test]
888 fn java_multiple_classes() {
889 check_metrics::<JavaParser>(
890 "public class MainClass { // wmc = 3
891 private int a;
892 public MainClass() { // +1
893 a = 0;
894 }
895 public void setA(int n) { // +1
896 a = n;
897 }
898 public int getA() { // +1
899 return a;
900 }
901 }
902
903 class TopLevelClass { // wmc = 2
904 private int b;
905 public TopLevelClass() { // +1
906 b = 0;
907 }
908 public int getB() { // +1
909 return b;
910 }
911 }",
912 "foo.java",
913 |metric| {
914 insta::assert_json_snapshot!(
916 metric.wmc,
917 @r#"
918 {
919 "class_wmc_sum": 5,
920 "interface_wmc_sum": 0,
921 "total": 5
922 }
923 "#
924 );
925 },
926 );
927 }
928
929 #[cfg(feature = "java")]
930 #[test]
931 fn java_static_nested_class() {
932 check_metrics::<JavaParser>(
933 "public class TopLevelClass { // wmc = 0
934 public static class StaticNestedClass { // wmc = 1
935 private void m() { // +1
936 System.out.println(\"Test\");
937 }
938 }
939 }",
940 "foo.java",
941 |metric| {
942 insta::assert_json_snapshot!(
944 metric.wmc,
945 @r#"
946 {
947 "class_wmc_sum": 1,
948 "interface_wmc_sum": 0,
949 "total": 1
950 }
951 "#
952 );
953 },
954 );
955 }
956
957 #[cfg(feature = "java")]
958 #[test]
959 fn java_nested_inner_classes() {
960 check_metrics::<JavaParser>(
961 "public class TopLevelClass { // wmc = 2
962 private int a;
963
964 class InnerClassBefore { // wmc = 1
965 private boolean b = (a % 2 == 0) ? true : false;
966 public boolean getB() { // +1
967 return b;
968 }
969 }
970
971 public TopLevelClass(int n) { // +1
972 if (a != n) { // +1
973 a = n;
974 }
975 }
976
977 class InnerClassAfter { // wmc = 2
978 private int c = a;
979
980 public int getC() { // +1
981 return c;
982 }
983 public void setC(int n) { // +1
984 c = n;
985 }
986
987 class InnerClass1 { // wmc = 1
988 private int p1;
989 class InnerClass2 { // wmc = 1
990 private int p2;
991 public int getP2() { // +1
992 return p2;
993 }
994 class InnerClass3 { // wmc = 2
995 private int p3;
996 public int getP3() { // +1
997 return p3;
998 }
999 public void setP3(int n) { // +1
1000 p3 = n;
1001 }
1002 }
1003 }
1004 public void setP1(int n) { // +1
1005 p1 = n;
1006 }
1007 }
1008 }
1009 }",
1010 "foo.java",
1011 |metric| {
1012 insta::assert_json_snapshot!(
1014 metric.wmc,
1015 @r#"
1016 {
1017 "class_wmc_sum": 9,
1018 "interface_wmc_sum": 0,
1019 "total": 9
1020 }
1021 "#
1022 );
1023 },
1024 );
1025 }
1026
1027 #[cfg(feature = "java")]
1028 #[test]
1029 fn java_local_inner_class() {
1030 check_metrics::<JavaParser>(
1031 "import java.util.LinkedList;
1032 import java.util.List;
1033
1034 public final class FinalClass { // wmc = 1 (test only)
1035 private int a = 1;
1036 public void test() { // +1
1037 final List<String> localList = new LinkedList<String>();
1038
1039 class LocalInnerClass { // wmc = 2 (print only)
1040 private int b = (a == 1) ? 1 : 0; // field init, not a method
1041 public void print() { // +1
1042 for ( String s : localList ) { // +1
1043 System.out.println(s);
1044 }
1045 }
1046 }
1047 }
1048 }",
1049 "foo.java",
1050 |metric| {
1051 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1062 insta::assert_json_snapshot!(
1063 metric.wmc,
1064 @r#"
1065 {
1066 "class_wmc_sum": 3,
1067 "interface_wmc_sum": 0,
1068 "total": 3
1069 }
1070 "#
1071 );
1072 },
1073 );
1074 }
1075
1076 #[cfg(feature = "java")]
1077 #[test]
1078 fn java_anonymous_inner_class() {
1079 check_metrics::<JavaParser>(
1080 "abstract class AbstractClass { // wmc = 1
1081 abstract void m1(); // +1
1082 }
1083 public class TopLevelClass{ // wmc = 3
1084 public void m(){ // +1
1085 AbstractClass ac1 = new AbstractClass() {
1086 void m1() { // +1
1087 for (int i = 0; i < 5; i++) { // +1
1088 System.out.println(\"Test 1: \" + i);
1089 }
1090 }
1091 };
1092 ac1.m1();
1093 }
1094 }",
1095 "foo.java",
1096 |metric| {
1097 insta::assert_json_snapshot!(
1099 metric.wmc,
1100 @r#"
1101 {
1102 "class_wmc_sum": 4,
1103 "interface_wmc_sum": 0,
1104 "total": 4
1105 }
1106 "#
1107 );
1108 },
1109 );
1110 }
1111
1112 #[cfg(feature = "java")]
1113 #[test]
1114 fn java_nested_anonymous_inner_classes() {
1115 check_metrics::<JavaParser>(
1116 "abstract class AbstractClass{ // wmc = 2
1117 abstract void m1(); // +1
1118 abstract void m2(); // +1
1119 }
1120 public class TopLevelClass{ // wmc = 6
1121 public void m(){ // +1
1122
1123 AbstractClass ac1 = new AbstractClass() {
1124 void m1() { // +1
1125 for (int i = 0; i < 5; i++) { // +1
1126 System.out.println(\"Test 1: \" + i);
1127 }
1128 }
1129 void m2() { // +1
1130 AbstractClass ac2 = new AbstractClass() {
1131 void m1() { // +1
1132 System.out.println(\"Test A\");
1133 }
1134 void m2() { // +1
1135 System.out.println(\"Test B\");
1136 }
1137 };
1138 ac2.m2();
1139 System.out.println(\"Test 2\");
1140 }
1141 };
1142 ac1.m1();
1143 }
1144 }",
1145 "foo.java",
1146 |metric| {
1147 insta::assert_json_snapshot!(
1149 metric.wmc,
1150 @r#"
1151 {
1152 "class_wmc_sum": 8,
1153 "interface_wmc_sum": 0,
1154 "total": 8
1155 }
1156 "#
1157 );
1158 },
1159 );
1160 }
1161
1162 #[cfg(feature = "java")]
1163 #[test]
1164 fn java_lambda_expression() {
1165 check_metrics::<JavaParser>(
1166 "import java.util.ArrayList;
1167
1168 public class TopLevelClass { // wmc = 2
1169 private ArrayList<Integer> numbers;
1170
1171 public void m1() { // +1
1172 numbers = new ArrayList<Integer>();
1173 numbers.add(1);
1174 numbers.add(2);
1175 numbers.add(3);
1176 }
1177
1178 public void m2() { // +1
1179 numbers.forEach( (n) -> { System.out.println(n); } );
1180 }
1181 }",
1182 "foo.java",
1183 |metric| {
1184 insta::assert_json_snapshot!(
1186 metric.wmc,
1187 @r#"
1188 {
1189 "class_wmc_sum": 2,
1190 "interface_wmc_sum": 0,
1191 "total": 2
1192 }
1193 "#
1194 );
1195 },
1196 );
1197 }
1198
1199 #[cfg(feature = "java")]
1200 #[test]
1201 fn java_single_interface() {
1202 check_metrics::<JavaParser>(
1203 "interface Example { // wmc = 6
1204 default boolean m1(boolean a, boolean b) { // +1
1205 return (a && b == a || b); // +2
1206 }
1207 default int m2(int n) { // +1
1208 return (n != 0) ? 1/n : n; // +1
1209 };
1210 void m3(); // +1
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": 6,
1221 "total": 6
1222 }
1223 "#
1224 );
1225 },
1226 );
1227 }
1228
1229 #[cfg(feature = "java")]
1230 #[test]
1231 fn java_multiple_interfaces() {
1232 check_metrics::<JavaParser>(
1233 "interface FirstInterface { // wmc = 1
1234 int a = 0;
1235 default int getA() { // +1
1236 return a;
1237 }
1238 }
1239
1240 interface SecondInterface { // wmc = 2
1241 void setB(int n); // +1
1242 int getB(); // +1
1243 }",
1244 "foo.java",
1245 |metric| {
1246 insta::assert_json_snapshot!(
1248 metric.wmc,
1249 @r#"
1250 {
1251 "class_wmc_sum": 0,
1252 "interface_wmc_sum": 3,
1253 "total": 3
1254 }
1255 "#
1256 );
1257 },
1258 );
1259 }
1260
1261 #[cfg(feature = "java")]
1262 #[test]
1263 fn java_nested_inner_interfaces() {
1264 check_metrics::<JavaParser>(
1265 "interface TopLevelInterface { // wmc = 1
1266 interface InnerInterfaceBefore { // wmc = 1
1267 void m1(); // +1
1268 }
1269
1270 void m2(); // +1
1271
1272 interface InnerInterfaceAfter { // wmc = 2
1273 void m3(); // +1
1274 interface InnerInterface { // wmc = 1
1275 void m4(); // +1
1276 }
1277 void m5(); // +1
1278 }
1279 }",
1280 "foo.java",
1281 |metric| {
1282 insta::assert_json_snapshot!(
1284 metric.wmc,
1285 @r#"
1286 {
1287 "class_wmc_sum": 0,
1288 "interface_wmc_sum": 5,
1289 "total": 5
1290 }
1291 "#
1292 );
1293 },
1294 );
1295 }
1296
1297 #[cfg(feature = "java")]
1298 #[test]
1299 fn java_class_in_interface() {
1300 check_metrics::<JavaParser>(
1301 "interface TopLevelInterface { // wmc = 2
1302 int getA(); // +1
1303 boolean getB(); // +1
1304
1305 class InnerClass { // wmc = 2
1306 float c;
1307 double d;
1308 float getC() { // +1
1309 return c;
1310 }
1311 double getD() { // +1
1312 return d;
1313 }
1314 }
1315 }",
1316 "foo.java",
1317 |metric| {
1318 insta::assert_json_snapshot!(
1320 metric.wmc,
1321 @r#"
1322 {
1323 "class_wmc_sum": 2,
1324 "interface_wmc_sum": 2,
1325 "total": 4
1326 }
1327 "#
1328 );
1329 },
1330 );
1331 }
1332
1333 #[cfg(feature = "java")]
1334 #[test]
1335 fn java_interface_in_class() {
1336 check_metrics::<JavaParser>(
1337 "class TopLevelClass { // wmc = 2
1338 int a;
1339 boolean b;
1340 int getA() { // +1
1341 return a;
1342 }
1343 boolean getB() { // +1
1344 return b;
1345 }
1346
1347 interface InnerInterface { // wmc = 2
1348 float getC(); // +1
1349 double getD(); // +1
1350 }
1351 }",
1352 "foo.java",
1353 |metric| {
1354 insta::assert_json_snapshot!(
1356 metric.wmc,
1357 @r#"
1358 {
1359 "class_wmc_sum": 2,
1360 "interface_wmc_sum": 2,
1361 "total": 4
1362 }
1363 "#
1364 );
1365 },
1366 );
1367 }
1368
1369 #[cfg(feature = "java")]
1373 #[test]
1374 fn java_enum_wmc_aggregates_method_complexity() {
1375 check_metrics::<JavaParser>(
1376 "enum Status {
1377 ACTIVE, INACTIVE;
1378 public int code(int n) { // entry +1
1379 if (n > 0) { // if +1
1380 return n;
1381 }
1382 return 0;
1383 }
1384 }",
1385 "foo.java",
1386 |metric| {
1387 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1389 },
1390 );
1391 }
1392
1393 #[cfg(feature = "java")]
1397 #[test]
1398 fn java_record_wmc_aggregates_method_complexity() {
1399 check_metrics::<JavaParser>(
1400 "record Point(int x, int y) {
1401 public int describe() { // entry +1
1402 return (x == 0) ? 0 : 1; // ternary +1
1403 }
1404 }",
1405 "foo.java",
1406 |metric| {
1407 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1408 },
1409 );
1410 }
1411
1412 #[cfg(feature = "java")]
1423 #[test]
1424 fn java_record_compact_and_alternative_constructors_open_two_spaces() {
1425 check_func_space::<JavaParser, _>(
1426 "record R(int a, int b) {
1427 R { // entry +1, if +1
1428 if (a < 0) { throw new IllegalArgumentException(); }
1429 }
1430 R(int a) { this(a, 0); } // entry +1
1431 }",
1432 "R.java",
1433 |space| {
1434 assert_child_space_kind(&space, "R", SpaceKind::Class);
1435 let record = child_space(&space, "R");
1436 let constructors = record
1437 .spaces
1438 .iter()
1439 .filter(|child| child.kind == SpaceKind::Function)
1440 .count();
1441 assert_eq!(constructors, 2, "compact + delegating constructor spaces");
1442 assert_eq!(record.metrics.wmc.class_wmc(), 3);
1446 },
1447 );
1448 }
1449
1450 #[cfg(feature = "java")]
1462 #[test]
1463 fn java_record_without_constructor_opens_only_its_methods() {
1464 check_func_space::<JavaParser, _>(
1465 "record R(int a, int b) {
1466 int sum() { return a + b; } // entry +1
1467 }",
1468 "R.java",
1469 |space| {
1470 assert_child_space_kind(&space, "R", SpaceKind::Class);
1471 let record = child_space(&space, "R");
1472 let functions: Vec<_> = record
1473 .spaces
1474 .iter()
1475 .filter(|child| child.kind == SpaceKind::Function)
1476 .map(|child| child.name.as_deref())
1477 .collect();
1478 assert_eq!(functions, vec![Some("sum")]);
1479 assert_eq!(record.metrics.wmc.class_wmc(), 1);
1480 },
1481 );
1482 }
1483
1484 #[cfg(feature = "java")]
1497 #[test]
1498 fn java_annotation_type_opens_interface_space_with_zero_wmc() {
1499 check_func_space::<JavaParser, _>(
1500 "@interface Marker {
1501 String value() default \"\";
1502 int priority() default 0;
1503 }",
1504 "foo.java",
1505 |func_space| {
1506 assert_eq!(func_space.metrics.wmc.interface_wmc_sum(), 0);
1507 assert_child_space_kind(&func_space, "Marker", SpaceKind::Interface);
1510 },
1511 );
1512 }
1513
1514 #[cfg(feature = "csharp")]
1515 #[test]
1516 fn csharp_single_class() {
1517 check_metrics::<CsharpParser>(
1518 "public class Example {
1519 public bool M1(bool a, bool b) {
1520 bool r = false;
1521 if (a && b == a || b) {
1522 r = true;
1523 }
1524 return r;
1525 }
1526 public int M2(int n) {
1527 for (int i = 0; i < n; i++) {
1528 int j = n;
1529 while (j > i) {
1530 j--;
1531 }
1532 }
1533 return (n % 2 == 0) ? 1 : 0;
1534 }
1535 }",
1536 "foo.cs",
1537 |metric| {
1538 assert_eq!(metric.wmc.class_wmc_sum(), 8);
1539 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1540 insta::assert_json_snapshot!(metric.wmc);
1541 },
1542 );
1543 }
1544
1545 #[cfg(feature = "csharp")]
1546 #[test]
1547 fn csharp_multiple_classes() {
1548 check_metrics::<CsharpParser>(
1549 "public class A {
1550 private int a;
1551 public A() { a = 0; }
1552 public void SetA(int n) { a = n; }
1553 public int GetA() { return a; }
1554 }
1555 class B {
1556 private int b;
1557 public B() { b = 0; }
1558 public int GetB() { return b; }
1559 }",
1560 "foo.cs",
1561 |metric| {
1562 assert_eq!(metric.wmc.class_wmc_sum(), 5);
1563 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1564 insta::assert_json_snapshot!(metric.wmc);
1565 },
1566 );
1567 }
1568
1569 #[cfg(feature = "csharp")]
1570 #[test]
1571 fn csharp_static_nested_class() {
1572 check_metrics::<CsharpParser>(
1573 "public class Outer {
1574 public static class Nested {
1575 private void M() {
1576 System.Console.WriteLine(\"Test\");
1577 }
1578 }
1579 }",
1580 "foo.cs",
1581 |metric| {
1582 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1583 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1584 insta::assert_json_snapshot!(metric.wmc);
1585 },
1586 );
1587 }
1588
1589 #[cfg(feature = "csharp")]
1590 #[test]
1591 fn csharp_nested_inner_classes() {
1592 check_metrics::<CsharpParser>(
1593 "public class Outer {
1594 private int a;
1595 public class Inner {
1596 public int GetX() { return 0; }
1597 public class Innermost {
1598 public int GetY() { return 1; }
1599 }
1600 }
1601 public int GetA() { return a; }
1602 }",
1603 "foo.cs",
1604 |metric| {
1605 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1606 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1607 insta::assert_json_snapshot!(metric.wmc);
1608 },
1609 );
1610 }
1611
1612 #[cfg(feature = "csharp")]
1613 #[test]
1614 fn csharp_local_inner_class() {
1615 check_metrics::<CsharpParser>(
1617 "public class A {
1618 public int M(int x) {
1619 int Local(int y) {
1620 if (y > 0) return y;
1621 return -y;
1622 }
1623 return Local(x);
1624 }
1625 }",
1626 "foo.cs",
1627 |metric| {
1628 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1629 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1630 insta::assert_json_snapshot!(metric.wmc);
1631 },
1632 );
1633 }
1634
1635 #[cfg(feature = "csharp")]
1636 #[test]
1637 fn csharp_anonymous_inner_class() {
1638 check_metrics::<CsharpParser>(
1639 "public class A {
1640 public void Run() {
1641 System.Action f = delegate(int x) {
1642 if (x > 0) System.Console.WriteLine(x);
1643 };
1644 }
1645 }",
1646 "foo.cs",
1647 |metric| {
1648 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1649 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1650 insta::assert_json_snapshot!(metric.wmc);
1651 },
1652 );
1653 }
1654
1655 #[cfg(feature = "csharp")]
1656 #[test]
1657 fn csharp_nested_anonymous_inner_classes() {
1658 check_metrics::<CsharpParser>(
1659 "public class A {
1660 public void Run() {
1661 System.Action f = delegate(int x) {
1662 System.Action g = delegate(int y) {
1663 if (y > 0) System.Console.WriteLine(y);
1664 };
1665 };
1666 }
1667 }",
1668 "foo.cs",
1669 |metric| {
1670 assert_eq!(metric.wmc.class_wmc_sum(), 4);
1671 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1672 insta::assert_json_snapshot!(metric.wmc);
1673 },
1674 );
1675 }
1676
1677 #[cfg(feature = "csharp")]
1678 #[test]
1679 fn csharp_lambda_expression() {
1680 check_metrics::<CsharpParser>(
1681 "public class A {
1682 public void Run() {
1683 System.Func<int, int> f = x => x > 0 ? x : -x;
1684 }
1685 }",
1686 "foo.cs",
1687 |metric| {
1688 assert_eq!(metric.wmc.class_wmc_sum(), 3);
1689 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1690 insta::assert_json_snapshot!(metric.wmc);
1691 },
1692 );
1693 }
1694
1695 #[cfg(feature = "csharp")]
1696 #[test]
1697 fn csharp_indexer_wmc() {
1698 check_wmc_and_npm::<CsharpParser>(
1704 "class A {
1705 private int[] _d;
1706 public int this[int i] { get => _d[i]; set => _d[i] = value; }
1707 }",
1708 "foo.cs",
1709 |metric| {
1710 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1713 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1714 assert_eq!(metric.npm.class_nm_sum(), 2);
1715 insta::assert_json_snapshot!(metric.wmc);
1716 },
1717 );
1718 }
1719
1720 #[cfg(feature = "csharp")]
1721 #[test]
1722 fn csharp_expression_bodied_indexer_wmc() {
1723 check_wmc_and_npm::<CsharpParser>(
1728 "class A {
1729 private int[] _d;
1730 public int this[int i] => _d[i];
1731 }",
1732 "foo.cs",
1733 |metric| {
1734 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1736 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1737 assert_eq!(metric.npm.class_nm_sum(), 1);
1738 insta::assert_json_snapshot!(metric.wmc);
1739 },
1740 );
1741 }
1742
1743 #[cfg(feature = "csharp")]
1744 #[test]
1745 fn csharp_property_wmc() {
1746 check_wmc_and_npm::<CsharpParser>(
1751 "class A {
1752 private int _w;
1753 public int W { get => _w; set => _w = value; }
1754 }",
1755 "foo.cs",
1756 |metric| {
1757 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1760 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1761 assert_eq!(metric.npm.class_nm_sum(), 2);
1762 },
1763 );
1764 }
1765
1766 #[cfg(feature = "csharp")]
1767 #[test]
1768 fn csharp_expression_bodied_property_wmc() {
1769 check_wmc_and_npm::<CsharpParser>(
1774 "class A {
1775 private int _w;
1776 public int W => _w;
1777 }",
1778 "foo.cs",
1779 |metric| {
1780 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1782 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
1783 assert_eq!(metric.npm.class_nm_sum(), 1);
1784 },
1785 );
1786 }
1787
1788 #[cfg(feature = "csharp")]
1789 #[test]
1790 fn csharp_single_interface() {
1791 check_metrics::<CsharpParser>(
1792 "public interface I {
1793 int GetA();
1794 int GetB();
1795 }",
1796 "foo.cs",
1797 |metric| {
1798 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1799 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1800 insta::assert_json_snapshot!(metric.wmc);
1801 },
1802 );
1803 }
1804
1805 #[cfg(feature = "csharp")]
1806 #[test]
1807 fn csharp_multiple_interfaces() {
1808 check_metrics::<CsharpParser>(
1809 "public interface I1 { int GetA(); }
1810 public interface I2 { bool GetB(); float GetC(); }",
1811 "foo.cs",
1812 |metric| {
1813 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1814 assert_eq!(metric.wmc.interface_wmc_sum(), 3);
1815 insta::assert_json_snapshot!(metric.wmc);
1816 },
1817 );
1818 }
1819
1820 #[cfg(feature = "csharp")]
1821 #[test]
1822 fn csharp_nested_inner_interfaces() {
1823 check_metrics::<CsharpParser>(
1824 "public interface I1 {
1825 int GetA();
1826 public interface I2 {
1827 bool GetB();
1828 }
1829 }",
1830 "foo.cs",
1831 |metric| {
1832 assert_eq!(metric.wmc.class_wmc_sum(), 0);
1833 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1834 insta::assert_json_snapshot!(metric.wmc);
1835 },
1836 );
1837 }
1838
1839 #[cfg(feature = "csharp")]
1840 #[test]
1841 fn csharp_class_in_interface() {
1842 check_metrics::<CsharpParser>(
1843 "public interface I {
1844 int GetA();
1845 public class Helper {
1846 public int M() { return 0; }
1847 }
1848 }",
1849 "foo.cs",
1850 |metric| {
1851 assert_eq!(metric.wmc.class_wmc_sum(), 1);
1852 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
1853 insta::assert_json_snapshot!(metric.wmc);
1854 },
1855 );
1856 }
1857
1858 #[cfg(feature = "csharp")]
1859 #[test]
1860 fn csharp_interface_in_class() {
1861 check_metrics::<CsharpParser>(
1862 "class Outer {
1863 int a;
1864 bool b;
1865 public int GetA() { return a; }
1866 public bool GetB() { return b; }
1867 public interface InnerI {
1868 float GetC();
1869 double GetD();
1870 }
1871 }",
1872 "foo.cs",
1873 |metric| {
1874 assert_eq!(metric.wmc.class_wmc_sum(), 2);
1875 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
1876 insta::assert_json_snapshot!(metric.wmc);
1877 },
1878 );
1879 }
1880
1881 #[cfg(feature = "php")]
1882 #[test]
1883 fn php_no_classes() {
1884 check_metrics::<PhpParser>(
1885 "<?php function f(): int { return 1; }",
1886 "foo.php",
1887 |metric| insta::assert_json_snapshot!(metric.wmc),
1888 );
1889 }
1890
1891 #[cfg(feature = "php")]
1892 #[test]
1893 fn php_one_class_simple() {
1894 check_metrics::<PhpParser>(
1895 "<?php
1896 class A {
1897 public function a(): int { return 1; }
1898 public function b(): int { return 2; }
1899 }",
1900 "foo.php",
1901 |metric| insta::assert_json_snapshot!(metric.wmc),
1902 );
1903 }
1904
1905 #[cfg(feature = "php")]
1906 #[test]
1907 fn php_one_class_with_loops() {
1908 check_metrics::<PhpParser>(
1909 "<?php
1910 class A {
1911 public function f(int $n): int {
1912 $sum = 0;
1913 for ($i = 0; $i < $n; $i++) {
1914 $sum += $i;
1915 }
1916 return $sum;
1917 }
1918 }",
1919 "foo.php",
1920 |metric| insta::assert_json_snapshot!(metric.wmc),
1921 );
1922 }
1923
1924 #[cfg(feature = "php")]
1925 #[test]
1926 fn php_one_class_with_branches() {
1927 check_metrics::<PhpParser>(
1928 "<?php
1929 class A {
1930 public function f(int $x): int {
1931 if ($x > 0) {
1932 return 1;
1933 }
1934 if ($x < 0) {
1935 return -1;
1936 }
1937 return 0;
1938 }
1939 }",
1940 "foo.php",
1941 |metric| insta::assert_json_snapshot!(metric.wmc),
1942 );
1943 }
1944
1945 #[cfg(feature = "php")]
1946 #[test]
1947 fn php_class_with_methods_only() {
1948 check_metrics::<PhpParser>(
1949 "<?php
1950 class A {
1951 public function a(): void {}
1952 public function b(): void {}
1953 public function c(): void {}
1954 }",
1955 "foo.php",
1956 |metric| insta::assert_json_snapshot!(metric.wmc),
1957 );
1958 }
1959
1960 #[cfg(feature = "php")]
1961 #[test]
1962 fn php_multiple_classes() {
1963 check_metrics::<PhpParser>(
1964 "<?php
1965 class A {
1966 public function f(int $x): int {
1967 if ($x > 0) { return 1; }
1968 return 0;
1969 }
1970 }
1971 class B {
1972 public function g(int $x): int {
1973 return $x;
1974 }
1975 }",
1976 "foo.php",
1977 |metric| insta::assert_json_snapshot!(metric.wmc),
1978 );
1979 }
1980
1981 #[cfg(feature = "php")]
1982 #[test]
1983 fn php_anonymous_class() {
1984 check_metrics::<PhpParser>(
1985 "<?php
1986 $obj = new class {
1987 public function f(int $x): int {
1988 if ($x > 0) { return 1; }
1989 return 0;
1990 }
1991 };",
1992 "foo.php",
1993 |metric| insta::assert_json_snapshot!(metric.wmc),
1994 );
1995 }
1996
1997 #[cfg(feature = "php")]
1998 #[test]
1999 fn php_class_with_static_methods() {
2000 check_metrics::<PhpParser>(
2001 "<?php
2002 class A {
2003 public static function f(int $x): int {
2004 if ($x > 0) { return 1; }
2005 return 0;
2006 }
2007 public static function g(): int { return 1; }
2008 }",
2009 "foo.php",
2010 |metric| insta::assert_json_snapshot!(metric.wmc),
2011 );
2012 }
2013
2014 #[cfg(feature = "php")]
2015 #[test]
2016 fn php_interface_wmc() {
2017 check_metrics::<PhpParser>(
2018 "<?php
2019 interface I {
2020 public function a(): void;
2021 public function b(): int;
2022 }",
2023 "foo.php",
2024 |metric| insta::assert_json_snapshot!(metric.wmc),
2025 );
2026 }
2027
2028 #[cfg(feature = "php")]
2029 #[test]
2030 fn php_trait_wmc() {
2031 check_metrics::<PhpParser>(
2032 "<?php
2033 trait T {
2034 public function f(int $x): int {
2035 if ($x > 0) { return 1; }
2036 return 0;
2037 }
2038 }",
2039 "foo.php",
2040 |metric| insta::assert_json_snapshot!(metric.wmc),
2041 );
2042 }
2043
2044 #[cfg(feature = "php")]
2045 #[test]
2046 fn php_enum_with_methods() {
2047 check_metrics::<PhpParser>(
2048 "<?php
2049 enum Color {
2050 case Red;
2051 case Green;
2052 public function label(): string {
2053 return match ($this) {
2054 Color::Red => 'r',
2055 Color::Green => 'g',
2056 };
2057 }
2058 }",
2059 "foo.php",
2060 |metric| insta::assert_json_snapshot!(metric.wmc),
2061 );
2062 }
2063
2064 #[cfg(feature = "php")]
2065 #[test]
2066 fn php_class_inside_namespace() {
2067 check_metrics::<PhpParser>(
2068 "<?php
2069 namespace App;
2070 class A {
2071 public function f(int $x): int {
2072 if ($x > 0) { return 1; }
2073 return 0;
2074 }
2075 }",
2076 "foo.php",
2077 |metric| insta::assert_json_snapshot!(metric.wmc),
2078 );
2079 }
2080
2081 #[cfg(feature = "php")]
2082 #[test]
2083 fn php_class_complex() {
2084 check_metrics::<PhpParser>(
2085 "<?php
2086 class Calc {
2087 public function add(int $a, int $b): int {
2088 if ($a > 0 && $b > 0) {
2089 return $a + $b;
2090 }
2091 return 0;
2092 }
2093 public function loop(int $n): int {
2094 $s = 0;
2095 for ($i = 0; $i < $n; $i++) {
2096 if ($i % 2 === 0) { $s += $i; }
2097 }
2098 return $s;
2099 }
2100 }",
2101 "foo.php",
2102 |metric| insta::assert_json_snapshot!(metric.wmc),
2103 );
2104 }
2105
2106 #[cfg(feature = "kotlin")]
2115 #[test]
2116 fn kotlin_empty_class() {
2117 check_metrics::<KotlinParser>("class Empty {}", "foo.kt", |metric| {
2119 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2120 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2121 insta::assert_json_snapshot!(metric.wmc);
2122 });
2123 }
2124
2125 #[cfg(feature = "kotlin")]
2126 #[test]
2127 fn kotlin_single_class() {
2128 check_metrics::<KotlinParser>(
2131 "class C {
2132 fun m(x: Int): Int { // +1
2133 if (x > 0) { // +1
2134 return x
2135 }
2136 return when (x) {
2137 0 -> 0 // +1 (WhenEntry)
2138 else -> -x // skipped (else is default)
2139 }
2140 }
2141 }",
2142 "foo.kt",
2143 |metric| {
2144 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2145 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2146 insta::assert_json_snapshot!(metric.wmc);
2147 },
2148 );
2149 }
2150
2151 #[cfg(feature = "kotlin")]
2152 #[test]
2153 fn kotlin_multiple_classes() {
2154 check_metrics::<KotlinParser>(
2157 "class A {
2158 private var a: Int = 0
2159 constructor(n: Int) { a = n } // +1
2160 fun setA(n: Int) { a = n } // +1
2161 fun getA(): Int = a // +1
2162 }
2163 class B {
2164 private var b: Int = 0
2165 constructor(n: Int) { b = n } // +1
2166 fun getB(): Int = b // +1
2167 }",
2168 "foo.kt",
2169 |metric| {
2170 assert_eq!(metric.wmc.class_wmc_sum(), 5);
2171 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2172 insta::assert_json_snapshot!(metric.wmc);
2173 },
2174 );
2175 }
2176
2177 #[cfg(feature = "kotlin")]
2178 #[test]
2179 fn kotlin_nested_class() {
2180 check_metrics::<KotlinParser>(
2182 "class Outer {
2183 class Nested {
2184 fun m() { println(\"hi\") } // +1
2185 }
2186 }",
2187 "foo.kt",
2188 |metric| {
2189 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2190 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2191 insta::assert_json_snapshot!(metric.wmc);
2192 },
2193 );
2194 }
2195
2196 #[cfg(feature = "kotlin")]
2197 #[test]
2198 fn kotlin_inner_class() {
2199 check_metrics::<KotlinParser>(
2202 "class Outer {
2203 fun outerM() {} // +1
2204 inner class Inner {
2205 fun innerM() {} // +1
2206 }
2207 }",
2208 "foo.kt",
2209 |metric| {
2210 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2211 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2212 insta::assert_json_snapshot!(metric.wmc);
2213 },
2214 );
2215 }
2216
2217 #[cfg(feature = "kotlin")]
2218 #[test]
2219 fn kotlin_data_class() {
2220 check_metrics::<KotlinParser>(
2224 "data class Point(val x: Int, val y: Int) {
2225 fun manhattan(): Int = kotlin.math.abs(x) + kotlin.math.abs(y) // +1
2226 }",
2227 "foo.kt",
2228 |metric| {
2229 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2230 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2231 insta::assert_json_snapshot!(metric.wmc);
2232 },
2233 );
2234 }
2235
2236 #[cfg(feature = "kotlin")]
2237 #[test]
2238 fn kotlin_object_singleton() {
2239 check_metrics::<KotlinParser>(
2242 "object Util {
2243 fun add(a: Int, b: Int): Int = a + b // +1
2244 fun gtZero(n: Int): Boolean { // +1
2245 return n > 0
2246 }
2247 }",
2248 "foo.kt",
2249 |metric| {
2250 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2251 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2252 insta::assert_json_snapshot!(metric.wmc);
2253 },
2254 );
2255 }
2256
2257 #[cfg(feature = "kotlin")]
2258 #[test]
2259 fn kotlin_companion_object() {
2260 check_metrics::<KotlinParser>(
2266 "class Holder {
2267 val instance: Int = 1
2268 fun get(): Int = instance // +1
2269 companion object {
2270 val SCALE: Int = 10
2271 fun mk(): Holder = Holder() // +1
2272 }
2273 }",
2274 "foo.kt",
2275 |metric| {
2276 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2277 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2278 insta::assert_json_snapshot!(metric.wmc);
2279 },
2280 );
2281 }
2282
2283 #[cfg(feature = "kotlin")]
2284 #[test]
2285 fn kotlin_companion_object_opens_class_space() {
2286 check_func_space::<KotlinParser, _>(
2292 "class Holder {
2293 fun get(): Int = 1
2294 companion object Companion {
2295 fun mk(): Holder = Holder()
2296 }
2297 }",
2298 "foo.kt",
2299 |func_space| {
2300 let holder = func_space
2301 .spaces
2302 .iter()
2303 .find(|s| s.name.as_deref() == Some("Holder"))
2304 .expect("expected a child FuncSpace named \"Holder\"");
2305 assert_child_space_kind(holder, "Companion", crate::SpaceKind::Class);
2308 let companion = holder
2309 .spaces
2310 .iter()
2311 .find(|s| s.name.as_deref() == Some("Companion"))
2312 .expect("expected a child FuncSpace named \"Companion\"");
2313 assert_eq!(companion.metrics.wmc.class_wmc_sum(), 1);
2317 assert_eq!(holder.metrics.wmc.class_wmc_sum(), 2);
2318 },
2319 );
2320 }
2321
2322 #[cfg(feature = "kotlin")]
2323 #[test]
2324 fn kotlin_object_literal_opens_class_space() {
2325 check_func_space::<KotlinParser, _>(
2333 "class Holder {
2334 fun get(): Int {
2335 val r = object : Runnable {
2336 override fun run() {}
2337 fun helper(): Int = 42
2338 }
2339 return 1
2340 }
2341 }",
2342 "foo.kt",
2343 |func_space| {
2344 let holder = func_space
2345 .spaces
2346 .iter()
2347 .find(|s| s.name.as_deref() == Some("Holder"))
2348 .expect("expected a child FuncSpace named \"Holder\"");
2349 let get = holder
2350 .spaces
2351 .iter()
2352 .find(|s| s.name.as_deref() == Some("get"))
2353 .expect("expected a child FuncSpace named \"get\"");
2354 assert_child_space_kind(get, "<anonymous>", crate::SpaceKind::Class);
2357 let anon = get
2358 .spaces
2359 .iter()
2360 .find(|s| s.name.as_deref() == Some("<anonymous>"))
2361 .expect("expected a child FuncSpace named \"<anonymous>\"");
2362 assert_eq!(
2367 get.spaces.len(),
2368 1,
2369 "get's only child is the anonymous class"
2370 );
2371 let anon_methods = anon
2372 .spaces
2373 .iter()
2374 .filter(|s| s.kind == crate::SpaceKind::Function)
2375 .count();
2376 assert_eq!(
2377 anon_methods, 2,
2378 "run + helper attributed to the anonymous class"
2379 );
2380 assert_eq!(
2385 get.metrics.nom.functions(),
2386 1,
2387 "get owns only itself; run/helper are not folded in"
2388 );
2389 assert_eq!(anon.metrics.wmc.class_wmc_sum(), 2);
2393 },
2394 );
2395 }
2396
2397 #[cfg(feature = "java")]
2398 #[test]
2399 fn java_anonymous_class_opens_space() {
2400 check_func_space::<JavaParser, _>(
2408 "class C {
2409 void m() {
2410 Runnable r = new Runnable() {
2411 public void run() {}
2412 };
2413 Object o = new Object();
2414 Runnable l = () -> {};
2415 }
2416 }",
2417 "C.java",
2418 |func_space| {
2419 let c = func_space
2420 .spaces
2421 .iter()
2422 .find(|s| s.name.as_deref() == Some("C"))
2423 .expect("expected a child FuncSpace named \"C\"");
2424 let m = c
2425 .spaces
2426 .iter()
2427 .find(|s| s.name.as_deref() == Some("m"))
2428 .expect("expected a child FuncSpace named \"m\"");
2429 let anon_classes: Vec<_> = m
2434 .spaces
2435 .iter()
2436 .filter(|s| s.kind == crate::SpaceKind::Class)
2437 .collect();
2438 assert_eq!(anon_classes.len(), 1, "exactly one anonymous Class space");
2439 assert_eq!(anon_classes[0].name.as_deref(), Some("<anonymous>"));
2440 let anon_methods = anon_classes[0]
2443 .spaces
2444 .iter()
2445 .filter(|s| s.kind == crate::SpaceKind::Function)
2446 .count();
2447 assert_eq!(anon_methods, 1, "run attributed to the anonymous class");
2448 assert_eq!(anon_classes[0].metrics.wmc.class_wmc_sum(), 1);
2449 },
2450 );
2451 }
2452
2453 #[cfg(feature = "java")]
2454 #[test]
2455 fn java_lambda_opens_no_class_space() {
2456 check_func_space::<JavaParser, _>(
2461 "class C {
2462 void m() {
2463 Runnable l = () -> { int x = 1; };
2464 }
2465 }",
2466 "C.java",
2467 |func_space| {
2468 let c = func_space
2469 .spaces
2470 .iter()
2471 .find(|s| s.name.as_deref() == Some("C"))
2472 .expect("expected a child FuncSpace named \"C\"");
2473 let m = c
2474 .spaces
2475 .iter()
2476 .find(|s| s.name.as_deref() == Some("m"))
2477 .expect("expected a child FuncSpace named \"m\"");
2478 let class_children = m
2479 .spaces
2480 .iter()
2481 .filter(|s| s.kind == crate::SpaceKind::Class)
2482 .count();
2483 assert_eq!(class_children, 0, "a lambda must not open a Class space");
2484 },
2485 );
2486 }
2487
2488 #[cfg(feature = "groovy")]
2489 #[test]
2490 fn groovy_anonymous_class_models_body_as_closure() {
2491 check_func_space::<GroovyParser, _>(
2503 "class C {
2504 void m() {
2505 def r = new Runnable() {
2506 void run() {}
2507 }
2508 }
2509 }",
2510 "C.groovy",
2511 |func_space| {
2512 let c = func_space
2513 .spaces
2514 .iter()
2515 .find(|s| s.name.as_deref() == Some("C"))
2516 .expect("expected a child FuncSpace named \"C\"");
2517 let m = c
2518 .spaces
2519 .iter()
2520 .find(|s| s.name.as_deref() == Some("m"))
2521 .expect("expected a child FuncSpace named \"m\"");
2522 let class_children = m
2525 .spaces
2526 .iter()
2527 .filter(|s| s.kind == crate::SpaceKind::Class)
2528 .count();
2529 assert_eq!(
2530 class_children, 0,
2531 "Groovy grammar models the body as a closure, not a class"
2532 );
2533 assert_eq!(m.metrics.nom.functions(), 1, "`m` itself, not `run`");
2534 let nested_funcs: u64 = m.spaces.iter().map(|s| s.metrics.nom.functions()).sum();
2535 assert_eq!(
2536 nested_funcs, 1,
2537 "`run` is attributed to the nested closure space"
2538 );
2539 },
2540 );
2541 }
2542
2543 #[cfg(feature = "kotlin")]
2544 #[test]
2545 fn kotlin_interface_simple() {
2546 check_metrics::<KotlinParser>(
2548 "interface I {
2549 fun work(): Int // +1
2550 fun describe(): String // +1
2551 }",
2552 "foo.kt",
2553 |metric| {
2554 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2555 assert_eq!(metric.wmc.interface_wmc_sum(), 2);
2556 insta::assert_json_snapshot!(metric.wmc);
2557 },
2558 );
2559 }
2560
2561 #[cfg(feature = "kotlin")]
2562 #[test]
2563 fn kotlin_interface_with_default_method() {
2564 check_metrics::<KotlinParser>(
2566 "interface I {
2567 fun abs(n: Int): Int { // +1
2568 return if (n < 0) -n else n // +1 if
2569 }
2570 fun pure(): Int // +1
2571 }",
2572 "foo.kt",
2573 |metric| {
2574 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2575 assert_eq!(metric.wmc.interface_wmc_sum(), 3);
2576 insta::assert_json_snapshot!(metric.wmc);
2577 },
2578 );
2579 }
2580
2581 #[cfg(feature = "kotlin")]
2582 #[test]
2583 fn kotlin_override_function() {
2584 check_metrics::<KotlinParser>(
2587 "open class Base {
2588 open fun greet(): String = \"hi\" // +1
2589 }
2590 class Sub : Base() {
2591 override fun greet(): String = \"yo\" // +1
2592 }",
2593 "foo.kt",
2594 |metric| {
2595 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2596 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2597 insta::assert_json_snapshot!(metric.wmc);
2598 },
2599 );
2600 }
2601
2602 #[cfg(feature = "kotlin")]
2603 #[test]
2604 fn kotlin_secondary_constructor() {
2605 check_metrics::<KotlinParser>(
2608 "class C {
2609 private var a: Int = 0
2610 constructor(n: Int) { // +1
2611 a = n
2612 }
2613 constructor(n: Int, m: Int) { // +1
2614 a = n + m
2615 }
2616 fun get(): Int = a // +1
2617 }",
2618 "foo.kt",
2619 |metric| {
2620 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2621 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2622 insta::assert_json_snapshot!(metric.wmc);
2623 },
2624 );
2625 }
2626
2627 #[cfg(feature = "kotlin")]
2628 #[test]
2629 fn kotlin_init_block() {
2630 check_metrics::<KotlinParser>(
2642 "class C(val n: Int) {
2643 init { // +1 since #1184
2644 require(n >= 0) { \"n must be non-negative\" }
2645 }
2646 fun get(): Int = n // +1
2647 }",
2648 "foo.kt",
2649 |metric| {
2650 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2651 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2652 insta::assert_json_snapshot!(metric.wmc);
2653 },
2654 );
2655 }
2656
2657 #[cfg(feature = "kotlin")]
2658 #[test]
2659 fn kotlin_top_level_function_excluded() {
2660 check_metrics::<KotlinParser>(
2663 "fun freeFunction(): Int = 42
2664 val freeVal: Int = 0
2665 class C { fun m(): Int = 1 } // +1
2666 ",
2667 "foo.kt",
2668 |metric| {
2669 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2670 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2671 insta::assert_json_snapshot!(metric.wmc);
2672 },
2673 );
2674 }
2675
2676 #[cfg(feature = "kotlin")]
2677 #[test]
2678 fn kotlin_extension_function_excluded() {
2679 check_metrics::<KotlinParser>(
2684 "fun List<Int>.sum2(): Int = this.size // top-level
2685 class C { fun m(): Int = 1 } // +1
2686 ",
2687 "foo.kt",
2688 |metric| {
2689 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2690 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2691 insta::assert_json_snapshot!(metric.wmc);
2692 },
2693 );
2694 }
2695
2696 #[cfg(feature = "kotlin")]
2697 #[test]
2698 fn kotlin_generic_class() {
2699 check_metrics::<KotlinParser>(
2701 "class Box<T>(val value: T) {
2702 fun get(): T = value // +1
2703 fun mapTo(f: (T) -> T): T = f(value) // +1
2704 }",
2705 "foo.kt",
2706 |metric| {
2707 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2708 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2709 insta::assert_json_snapshot!(metric.wmc);
2710 },
2711 );
2712 }
2713
2714 #[cfg(feature = "kotlin")]
2715 #[test]
2716 fn kotlin_class_in_interface() {
2717 check_metrics::<KotlinParser>(
2721 "interface Outer {
2722 fun work(): Int // +1 (interface)
2723 class Helper {
2724 fun help(): Int = 0 // +1 (class)
2725 }
2726 }",
2727 "foo.kt",
2728 |metric| {
2729 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2730 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
2731 insta::assert_json_snapshot!(metric.wmc);
2732 },
2733 );
2734 }
2735
2736 #[cfg(feature = "kotlin")]
2737 #[test]
2738 fn kotlin_interface_in_class() {
2739 check_metrics::<KotlinParser>(
2741 "class Outer {
2742 fun work(): Int = 1 // +1 (class)
2743 interface Sub {
2744 fun help(): Int // +1 (interface)
2745 }
2746 }",
2747 "foo.kt",
2748 |metric| {
2749 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2750 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
2751 insta::assert_json_snapshot!(metric.wmc);
2752 },
2753 );
2754 }
2755
2756 #[cfg(feature = "typescript")]
2764 #[test]
2765 fn typescript_class_wmc_single_method() {
2766 check_metrics::<TypescriptParser>(
2767 "class C {
2768 m(): number { return 1; } // cyclomatic 1
2769 }",
2770 "foo.ts",
2771 |metric| {
2772 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2773 insta::assert_json_snapshot!(metric.wmc);
2774 },
2775 );
2776 }
2777
2778 #[cfg(feature = "typescript")]
2779 #[test]
2780 fn typescript_class_wmc_two_methods() {
2781 check_metrics::<TypescriptParser>(
2782 "class C {
2783 a(): number { return 1; } // +1
2784 b(x: number): number { // +2 (if branch)
2785 if (x > 0) return x;
2786 return 0;
2787 }
2788 }",
2789 "foo.ts",
2790 |metric| {
2791 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2792 insta::assert_json_snapshot!(metric.wmc);
2793 },
2794 );
2795 }
2796
2797 #[cfg(feature = "typescript")]
2798 #[test]
2799 fn typescript_class_wmc_with_branches() {
2800 check_metrics::<TypescriptParser>(
2801 "class C {
2802 m(x: number): number {
2803 if (x > 0) { // +1
2804 return 1;
2805 } else if (x < 0) { // +1
2806 return -1;
2807 }
2808 return 0;
2809 } // base 1
2810 }",
2811 "foo.ts",
2812 |metric| {
2813 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2814 insta::assert_json_snapshot!(metric.wmc);
2815 },
2816 );
2817 }
2818
2819 #[cfg(feature = "typescript")]
2820 #[test]
2821 fn typescript_class_wmc_arrow_field() {
2822 check_metrics::<TypescriptParser>(
2825 "class C {
2826 arrow = (x: number) => {
2827 if (x > 0) return x; // +1
2828 return 0;
2829 }; // base 1
2830 }",
2831 "foo.ts",
2832 |metric| {
2833 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2834 insta::assert_json_snapshot!(metric.wmc);
2835 },
2836 );
2837 }
2838
2839 #[cfg(feature = "typescript")]
2840 #[test]
2841 fn typescript_class_wmc_with_loops() {
2842 check_metrics::<TypescriptParser>(
2843 "class C {
2844 m(xs: number[]): number {
2845 let total = 0;
2846 for (const x of xs) { // +1
2847 total += x;
2848 }
2849 return total;
2850 } // base 1
2851 }",
2852 "foo.ts",
2853 |metric| {
2854 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2855 insta::assert_json_snapshot!(metric.wmc);
2856 },
2857 );
2858 }
2859
2860 #[cfg(feature = "typescript")]
2861 #[test]
2862 fn typescript_abstract_class_wmc() {
2863 check_metrics::<TypescriptParser>(
2865 "abstract class C {
2866 abstract a(): void; // signature only, 0
2867 m(): number { return 1; } // +1
2868 }",
2869 "foo.ts",
2870 |metric| {
2871 assert_eq!(metric.wmc.class_wmc_sum(), 1);
2872 insta::assert_json_snapshot!(metric.wmc);
2873 },
2874 );
2875 }
2876
2877 #[cfg(feature = "typescript")]
2878 #[test]
2879 fn typescript_interface_wmc_zero() {
2880 check_metrics::<TypescriptParser>(
2882 "interface I {
2883 a(): void;
2884 b(): number;
2885 }",
2886 "foo.ts",
2887 |metric| {
2888 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
2889 assert_eq!(metric.wmc.class_wmc_sum(), 0);
2890 insta::assert_json_snapshot!(metric.wmc);
2891 },
2892 );
2893 }
2894
2895 #[cfg(feature = "typescript")]
2896 #[test]
2897 fn typescript_constructor_wmc() {
2898 check_metrics::<TypescriptParser>(
2901 "class C {
2902 x: number;
2903 constructor(n: number) {
2904 if (n > 0) { // +1
2905 this.x = n;
2906 } else {
2907 this.x = 0;
2908 }
2909 } // base 1
2910 }",
2911 "foo.ts",
2912 |metric| {
2913 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2914 insta::assert_json_snapshot!(metric.wmc);
2915 },
2916 );
2917 }
2918
2919 #[cfg(feature = "typescript")]
2920 #[test]
2921 fn typescript_getter_setter_wmc() {
2922 check_metrics::<TypescriptParser>(
2924 "class C {
2925 _x: number = 0;
2926 get x(): number { return this._x; }
2927 set x(v: number) { this._x = v; }
2928 }",
2929 "foo.ts",
2930 |metric| {
2931 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2932 insta::assert_json_snapshot!(metric.wmc);
2933 },
2934 );
2935 }
2936
2937 #[cfg(feature = "typescript")]
2938 #[test]
2939 fn typescript_multiple_classes_wmc_independent() {
2940 check_metrics::<TypescriptParser>(
2941 "class A { m(): number { return 1; } }
2942 class B {
2943 m(x: number): number {
2944 if (x > 0) return x; // +1
2945 return 0;
2946 } // base 1
2947 }",
2948 "foo.ts",
2949 |metric| {
2950 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2952 insta::assert_json_snapshot!(metric.wmc);
2953 },
2954 );
2955 }
2956
2957 #[cfg(feature = "typescript")]
2958 #[test]
2959 fn typescript_class_wmc_with_ternary_and_logical() {
2960 check_metrics::<TypescriptParser>(
2961 "class C {
2962 m(x: number, y: number): number {
2963 return x > 0 && y > 0 // +1 (ternary) +1 (&&)
2964 ? x + y
2965 : 0;
2966 } // base 1
2967 }",
2968 "foo.ts",
2969 |metric| {
2970 assert_eq!(metric.wmc.class_wmc_sum(), 3);
2971 insta::assert_json_snapshot!(metric.wmc);
2972 },
2973 );
2974 }
2975
2976 #[cfg(feature = "typescript")]
2977 #[test]
2978 fn typescript_generic_class_wmc() {
2979 check_metrics::<TypescriptParser>(
2980 "class Box<T> {
2981 value: T;
2982 set(v: T): void { this.value = v; }
2983 get(): T { return this.value; }
2984 }",
2985 "foo.ts",
2986 |metric| {
2987 assert_eq!(metric.wmc.class_wmc_sum(), 2);
2988 insta::assert_json_snapshot!(metric.wmc);
2989 },
2990 );
2991 }
2992
2993 #[cfg(feature = "typescript")]
2996 #[test]
2997 fn tsx_class_wmc_single_method() {
2998 check_metrics::<TsxParser>(
2999 "class C { m(): number { return 1; } }",
3000 "foo.tsx",
3001 |metric| {
3002 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3003 insta::assert_json_snapshot!(metric.wmc);
3004 },
3005 );
3006 }
3007
3008 #[cfg(feature = "typescript")]
3009 #[test]
3010 fn tsx_class_wmc_two_methods() {
3011 check_metrics::<TsxParser>(
3012 "class C {
3013 a(): number { return 1; }
3014 b(x: number): number {
3015 if (x > 0) return x;
3016 return 0;
3017 }
3018 }",
3019 "foo.tsx",
3020 |metric| {
3021 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3022 insta::assert_json_snapshot!(metric.wmc);
3023 },
3024 );
3025 }
3026
3027 #[cfg(feature = "typescript")]
3028 #[test]
3029 fn tsx_class_wmc_with_branches() {
3030 check_metrics::<TsxParser>(
3031 "class C {
3032 m(x: number): number {
3033 if (x > 0) return 1;
3034 else if (x < 0) return -1;
3035 return 0;
3036 }
3037 }",
3038 "foo.tsx",
3039 |metric| {
3040 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3041 insta::assert_json_snapshot!(metric.wmc);
3042 },
3043 );
3044 }
3045
3046 #[cfg(feature = "typescript")]
3047 #[test]
3048 fn tsx_class_wmc_arrow_field() {
3049 check_metrics::<TsxParser>(
3050 "class C {
3051 arrow = (x: number) => {
3052 if (x > 0) return x;
3053 return 0;
3054 };
3055 }",
3056 "foo.tsx",
3057 |metric| {
3058 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3059 insta::assert_json_snapshot!(metric.wmc);
3060 },
3061 );
3062 }
3063
3064 #[cfg(feature = "typescript")]
3065 #[test]
3066 fn tsx_class_wmc_with_loops() {
3067 check_metrics::<TsxParser>(
3068 "class C {
3069 m(xs: number[]): number {
3070 let total = 0;
3071 for (const x of xs) { total += x; }
3072 return total;
3073 }
3074 }",
3075 "foo.tsx",
3076 |metric| {
3077 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3078 insta::assert_json_snapshot!(metric.wmc);
3079 },
3080 );
3081 }
3082
3083 #[cfg(feature = "typescript")]
3084 #[test]
3085 fn tsx_abstract_class_wmc() {
3086 check_metrics::<TsxParser>(
3087 "abstract class C {
3088 abstract a(): void;
3089 m(): number { return 1; }
3090 }",
3091 "foo.tsx",
3092 |metric| {
3093 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3094 insta::assert_json_snapshot!(metric.wmc);
3095 },
3096 );
3097 }
3098
3099 #[cfg(feature = "typescript")]
3100 #[test]
3101 fn tsx_interface_wmc_zero() {
3102 check_metrics::<TsxParser>(
3103 "interface I { a(): void; b(): number; }",
3104 "foo.tsx",
3105 |metric| {
3106 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3107 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3108 insta::assert_json_snapshot!(metric.wmc);
3109 },
3110 );
3111 }
3112
3113 #[cfg(feature = "typescript")]
3114 #[test]
3115 fn tsx_constructor_wmc() {
3116 check_metrics::<TsxParser>(
3117 "class C {
3118 x: number;
3119 constructor(n: number) {
3120 if (n > 0) this.x = n;
3121 else this.x = 0;
3122 }
3123 }",
3124 "foo.tsx",
3125 |metric| {
3126 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3127 insta::assert_json_snapshot!(metric.wmc);
3128 },
3129 );
3130 }
3131
3132 #[cfg(feature = "typescript")]
3133 #[test]
3134 fn tsx_getter_setter_wmc() {
3135 check_metrics::<TsxParser>(
3136 "class C {
3137 _x: number = 0;
3138 get x(): number { return this._x; }
3139 set x(v: number) { this._x = v; }
3140 }",
3141 "foo.tsx",
3142 |metric| {
3143 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3144 insta::assert_json_snapshot!(metric.wmc);
3145 },
3146 );
3147 }
3148
3149 #[cfg(feature = "typescript")]
3150 #[test]
3151 fn tsx_multiple_classes_wmc_independent() {
3152 check_metrics::<TsxParser>(
3153 "class A { m(): number { return 1; } }
3154 class B {
3155 m(x: number): number {
3156 if (x > 0) return x;
3157 return 0;
3158 }
3159 }",
3160 "foo.tsx",
3161 |metric| {
3162 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3163 insta::assert_json_snapshot!(metric.wmc);
3164 },
3165 );
3166 }
3167
3168 #[cfg(feature = "typescript")]
3169 #[test]
3170 fn tsx_class_wmc_with_ternary_and_logical() {
3171 check_metrics::<TsxParser>(
3172 "class C {
3173 m(x: number, y: number): number {
3174 return x > 0 && y > 0 ? x + y : 0;
3175 }
3176 }",
3177 "foo.tsx",
3178 |metric| {
3179 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3180 insta::assert_json_snapshot!(metric.wmc);
3181 },
3182 );
3183 }
3184
3185 #[cfg(feature = "typescript")]
3186 #[test]
3187 fn tsx_generic_class_wmc() {
3188 check_metrics::<TsxParser>(
3189 "class Box<T> {
3190 value: T;
3191 set(v: T): void { this.value = v; }
3192 get(): T { return this.value; }
3193 }",
3194 "foo.tsx",
3195 |metric| {
3196 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3197 insta::assert_json_snapshot!(metric.wmc);
3198 },
3199 );
3200 }
3201
3202 #[cfg(feature = "ruby")]
3210 #[test]
3211 fn ruby_no_classes() {
3212 check_metrics::<RubyParser>("def foo\n 1\nend\n", "foo.rb", |metric| {
3214 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3215 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3216 insta::assert_json_snapshot!(metric.wmc);
3217 });
3218 }
3219
3220 #[cfg(feature = "ruby")]
3221 #[test]
3222 fn ruby_empty_class() {
3223 check_metrics::<RubyParser>("class Foo\nend\n", "foo.rb", |metric| {
3225 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3226 insta::assert_json_snapshot!(metric.wmc);
3227 });
3228 }
3229
3230 #[cfg(feature = "ruby")]
3231 #[test]
3232 fn ruby_one_class_simple() {
3233 check_metrics::<RubyParser>(
3235 "class A\n def a\n 1\n end\n def b\n 2\n end\nend\n",
3236 "foo.rb",
3237 |metric| {
3238 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3239 insta::assert_json_snapshot!(metric.wmc);
3240 },
3241 );
3242 }
3243
3244 #[cfg(feature = "ruby")]
3245 #[test]
3246 fn ruby_one_class_with_branch() {
3247 check_metrics::<RubyParser>(
3249 "class A\n def f(x)\n if x > 0\n 1\n else\n 0\n end\n end\nend\n",
3250 "foo.rb",
3251 |metric| {
3252 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3253 insta::assert_json_snapshot!(metric.wmc);
3254 },
3255 );
3256 }
3257
3258 #[cfg(feature = "ruby")]
3259 #[test]
3260 fn ruby_one_class_with_loop() {
3261 check_metrics::<RubyParser>(
3263 "class A\n def f(n)\n while n > 0\n n -= 1\n end\n end\nend\n",
3264 "foo.rb",
3265 |metric| {
3266 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3267 insta::assert_json_snapshot!(metric.wmc);
3268 },
3269 );
3270 }
3271
3272 #[cfg(feature = "ruby")]
3273 #[test]
3274 fn ruby_singleton_method_included() {
3275 check_metrics::<RubyParser>(
3278 "class A\n def f\n 1\n end\n def self.g\n 2\n end\nend\n",
3279 "foo.rb",
3280 |metric| {
3281 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3282 insta::assert_json_snapshot!(metric.wmc);
3283 },
3284 );
3285 }
3286
3287 #[cfg(feature = "ruby")]
3288 #[test]
3289 fn ruby_singleton_class_methods_included() {
3290 check_metrics::<RubyParser>(
3293 "class A\n class << self\n def s\n 1\n end\n end\nend\n",
3294 "foo.rb",
3295 |metric| {
3296 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3299 insta::assert_json_snapshot!(metric.wmc);
3300 },
3301 );
3302 }
3303
3304 #[cfg(feature = "ruby")]
3305 #[test]
3306 fn ruby_multiple_classes() {
3307 check_metrics::<RubyParser>(
3309 "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",
3310 "foo.rb",
3311 |metric| {
3312 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3314 insta::assert_json_snapshot!(metric.wmc);
3315 },
3316 );
3317 }
3318
3319 #[cfg(feature = "ruby")]
3320 #[test]
3321 fn ruby_module_only() {
3322 check_metrics::<RubyParser>(
3325 "module M\n def f\n 1\n end\nend\n",
3326 "foo.rb",
3327 |metric| {
3328 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3329 insta::assert_json_snapshot!(metric.wmc);
3330 },
3331 );
3332 }
3333
3334 #[cfg(feature = "ruby")]
3335 #[test]
3336 fn ruby_class_with_inheritance() {
3337 check_metrics::<RubyParser>(
3340 "class A < B\n def f\n 1\n end\n def g\n 2\n end\nend\n",
3341 "foo.rb",
3342 |metric| {
3343 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3344 insta::assert_json_snapshot!(metric.wmc);
3345 },
3346 );
3347 }
3348
3349 #[cfg(feature = "ruby")]
3350 #[test]
3351 fn ruby_class_with_visibility_keywords() {
3352 check_metrics::<RubyParser>(
3355 "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",
3356 "foo.rb",
3357 |metric| {
3358 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3359 insta::assert_json_snapshot!(metric.wmc);
3360 },
3361 );
3362 }
3363
3364 #[cfg(feature = "ruby")]
3365 #[test]
3366 fn ruby_class_complex() {
3367 check_metrics::<RubyParser>(
3372 "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",
3373 "foo.rb",
3374 |metric| {
3375 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3376 insta::assert_json_snapshot!(metric.wmc);
3377 },
3378 );
3379 }
3380
3381 #[cfg(feature = "python")]
3393 #[test]
3394 fn python_empty_class_zero_wmc() {
3395 check_metrics::<PythonParser>("class C:\n pass\n", "foo.py", |metric| {
3396 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3397 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3398 insta::assert_json_snapshot!(metric.wmc);
3399 });
3400 }
3401
3402 #[cfg(feature = "python")]
3403 #[test]
3404 fn python_single_method_wmc_one() {
3405 check_metrics::<PythonParser>(
3407 "class C:\n def m(self):\n return 1\n",
3408 "foo.py",
3409 |metric| {
3410 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3411 insta::assert_json_snapshot!(metric.wmc);
3412 },
3413 );
3414 }
3415
3416 #[cfg(feature = "python")]
3417 #[test]
3418 fn python_method_with_if_adds_to_wmc() {
3419 check_metrics::<PythonParser>(
3421 "class C:\n def m(self, x):\n if x > 0:\n return 1\n return 0\n",
3422 "foo.py",
3423 |metric| {
3424 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3425 insta::assert_json_snapshot!(metric.wmc);
3426 },
3427 );
3428 }
3429
3430 #[cfg(feature = "python")]
3431 #[test]
3432 fn python_multiple_methods_wmc_sums() {
3433 check_metrics::<PythonParser>(
3436 "class C:\n\
3437 \x20 def m1(self):\n\
3438 \x20 return 1\n\
3439 \x20 def m2(self, x):\n\
3440 \x20 if x:\n\
3441 \x20 return 1\n\
3442 \x20 return 0\n\
3443 \x20 def m3(self, xs):\n\
3444 \x20 for x in xs:\n\
3445 \x20 if x:\n\
3446 \x20 return x\n\
3447 \x20 return None\n",
3448 "foo.py",
3449 |metric| {
3450 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3451 insta::assert_json_snapshot!(metric.wmc);
3452 },
3453 );
3454 }
3455
3456 #[cfg(feature = "python")]
3457 #[test]
3458 fn python_top_level_function_does_not_contribute_to_class_wmc() {
3459 check_metrics::<PythonParser>(
3462 "def f(x):\n if x:\n return 1\n return 0\n",
3463 "foo.py",
3464 |metric| {
3465 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3466 insta::assert_json_snapshot!(metric.wmc);
3467 },
3468 );
3469 }
3470
3471 #[cfg(feature = "python")]
3472 #[test]
3473 fn python_multiple_classes_wmc_independent() {
3474 check_metrics::<PythonParser>(
3478 "class A:\n\
3479 \x20 def m1(self):\n\
3480 \x20 return 1\n\
3481 class B:\n\
3482 \x20 def m2(self, x):\n\
3483 \x20 if x:\n\
3484 \x20 return 1\n\
3485 \x20 return 0\n",
3486 "foo.py",
3487 |metric| {
3488 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3489 insta::assert_json_snapshot!(metric.wmc);
3490 },
3491 );
3492 }
3493
3494 #[cfg(feature = "rust")]
3495 #[test]
3496 fn rust_empty_unit_zero_wmc() {
3497 check_metrics::<RustParser>("", "empty.rs", |metric| {
3498 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3499 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3500 insta::assert_json_snapshot!(metric.wmc);
3501 });
3502 }
3503
3504 #[cfg(feature = "rust")]
3505 #[test]
3506 fn rust_single_impl_method_wmc_one() {
3507 check_metrics::<RustParser>(
3509 "struct Foo;\nimpl Foo { fn m(&self) -> i32 { 1 } }\n",
3510 "foo.rs",
3511 |metric| {
3512 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3513 insta::assert_json_snapshot!(metric.wmc);
3514 },
3515 );
3516 }
3517
3518 #[cfg(feature = "rust")]
3519 #[test]
3520 fn rust_method_with_if_adds_to_wmc() {
3521 check_metrics::<RustParser>(
3523 "struct Foo;\n\
3524 impl Foo {\n\
3525 \x20 fn m(&self, x: i32) -> i32 {\n\
3526 \x20 if x > 0 { 1 } else { 0 }\n\
3527 \x20 }\n\
3528 }\n",
3529 "foo.rs",
3530 |metric| {
3531 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3532 insta::assert_json_snapshot!(metric.wmc);
3533 },
3534 );
3535 }
3536
3537 #[cfg(feature = "rust")]
3538 #[test]
3539 fn rust_multiple_methods_wmc_sums() {
3540 check_metrics::<RustParser>(
3543 "struct Foo;\n\
3544 impl Foo {\n\
3545 \x20 fn m1(&self) -> i32 { 1 }\n\
3546 \x20 fn m2(&self, x: i32) -> i32 { if x > 0 { 1 } else { 0 } }\n\
3547 \x20 fn m3(&self, xs: &[i32]) -> i32 {\n\
3548 \x20 for x in xs { if *x > 0 { return *x; } }\n\
3549 \x20 0\n\
3550 \x20 }\n\
3551 }\n",
3552 "foo.rs",
3553 |metric| {
3554 assert_eq!(metric.wmc.class_wmc_sum(), 6);
3555 insta::assert_json_snapshot!(metric.wmc);
3556 },
3557 );
3558 }
3559
3560 #[cfg(feature = "rust")]
3561 #[test]
3562 fn rust_multiple_impls_wmc_aggregate() {
3563 check_metrics::<RustParser>(
3566 "struct Foo;\n\
3567 impl Foo { fn m1(&self) {} }\n\
3568 impl Foo { fn m2(&self) {} }\n",
3569 "foo.rs",
3570 |metric| {
3571 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3572 insta::assert_json_snapshot!(metric.wmc);
3573 },
3574 );
3575 }
3576
3577 #[cfg(feature = "rust")]
3578 #[test]
3579 fn rust_trait_default_method_contributes_to_interface_wmc() {
3580 check_metrics::<RustParser>(
3585 "trait T { fn draw(&self); fn area(&self) -> f64 { 0.0 } }",
3586 "foo.rs",
3587 |metric| {
3588 assert_eq!(metric.wmc.interface_wmc_sum(), 1);
3589 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3590 insta::assert_json_snapshot!(metric.wmc);
3591 },
3592 );
3593 }
3594
3595 #[cfg(feature = "rust")]
3596 #[test]
3597 fn rust_top_level_function_does_not_contribute_to_class_wmc() {
3598 check_metrics::<RustParser>(
3602 "fn f(x: i32) -> i32 { if x > 0 { 1 } else { 0 } }",
3603 "foo.rs",
3604 |metric| {
3605 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3606 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3607 insta::assert_json_snapshot!(metric.wmc);
3608 },
3609 );
3610 }
3611
3612 #[cfg(feature = "go")]
3615 #[test]
3616 fn go_wmc_is_zero_documented_limitation() {
3617 check_metrics::<GoParser>(
3627 "package main\n\
3628 type Foo struct{}\n\
3629 func (f Foo) M(x int) int { if x > 0 { return 1 } else { return 0 } }\n\
3630 func (f Foo) N() {}\n",
3631 "foo.go",
3632 |metric| {
3633 assert_eq!(metric.wmc.class_wmc_sum(), 0);
3634 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3635 insta::assert_json_snapshot!(metric.wmc);
3636 },
3637 );
3638 }
3639
3640 #[cfg(feature = "elixir")]
3649 #[test]
3650 fn elixir_wmc_aggregates_def_methods() {
3651 check_metrics::<ElixirParser>(
3652 "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",
3653 "foo.ex",
3654 |metric| {
3655 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3657 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3658 insta::assert_json_snapshot!(
3659 metric.wmc,
3660 @r#"
3661 {
3662 "class_wmc_sum": 3,
3663 "interface_wmc_sum": 0,
3664 "total": 3
3665 }
3666 "#
3667 );
3668 },
3669 );
3670 }
3671
3672 #[cfg(feature = "elixir")]
3673 #[test]
3674 fn elixir_wmc_def_plus_defp_counts_both() {
3675 check_metrics::<ElixirParser>(
3676 "defmodule Foo do\n def pub_one, do: 1\n defp priv_one, do: 1\nend\n",
3677 "foo.ex",
3678 |metric| {
3679 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3682 },
3683 );
3684 }
3685
3686 #[cfg(feature = "elixir")]
3687 #[test]
3688 fn elixir_wmc_defmacro_counts() {
3689 check_metrics::<ElixirParser>(
3690 "defmodule Foo do\n defmacro stuff(x) do\n if x > 0, do: :pos, else: :neg\n end\nend\n",
3691 "foo.ex",
3692 |metric| {
3693 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3695 },
3696 );
3697 }
3698
3699 #[cfg(feature = "elixir")]
3700 #[test]
3701 fn elixir_wmc_multiple_clauses_each_a_method() {
3702 check_metrics::<ElixirParser>(
3705 "defmodule Foo do\n def f(0), do: :zero\n def f(_), do: :other\nend\n",
3706 "foo.ex",
3707 |metric| {
3708 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3710 },
3711 );
3712 }
3713
3714 #[cfg(feature = "elixir")]
3715 #[test]
3716 fn elixir_wmc_nested_defmodule_isolates() {
3717 check_metrics::<ElixirParser>(
3718 "defmodule Outer do\n def o, do: 1\n defmodule Inner do\n def i, do: 1\n end\nend\n",
3719 "foo.ex",
3720 |metric| {
3721 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3723 },
3724 );
3725 }
3726
3727 #[cfg(feature = "elixir")]
3728 #[test]
3729 fn elixir_wmc_user_macro_not_classified_as_method() {
3730 check_metrics::<ElixirParser>(
3740 "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",
3741 "foo.ex",
3742 |metric| {
3743 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3746 },
3747 );
3748 }
3749
3750 #[cfg(feature = "elixir")]
3751 #[test]
3752 fn elixir_wmc_quoted_defs_do_not_inflate_method_count() {
3753 check_metrics::<ElixirParser>(
3764 "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",
3765 "foo.ex",
3766 |metric| {
3767 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3770 },
3771 );
3772 }
3773
3774 #[cfg(feature = "objc")]
3777 #[test]
3778 fn objc_wmc() {
3779 check_metrics::<ObjcParser>(
3786 "@interface Foo : NSObject\n\
3787 - (int)a:(int)x;\n\
3788 @end\n\
3789 @implementation Foo\n\
3790 - (int)a:(int)x { if (x > 0) { return 1; } return 0; }\n\
3791 - (void)b { }\n\
3792 @end\n",
3793 "foo.m",
3794 |metric| {
3795 assert_eq!(metric.wmc.class_wmc_sum(), 3);
3800 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3801 },
3802 );
3803 }
3804
3805 #[cfg(feature = "objc")]
3821 const OBJC_HELPER_IN_IMPLEMENTATION: &str = "@implementation Foo\n\
3822 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3823 - (void)m { }\n\
3824 @end\n";
3825
3826 #[cfg(feature = "objc")]
3829 const OBJC_HELPER_AND_BRANCHING_METHOD: &str = "@implementation Foo\n\
3830 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3831 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3832 @end\n";
3833
3834 #[cfg(feature = "objc")]
3838 const OBJC_HELPER_IN_CATEGORY: &str = "@implementation Foo (Cat)\n\
3839 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3840 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3841 @end\n";
3842
3843 #[cfg(feature = "objc")]
3850 const OBJC_HELPER_IN_INTERFACE: &str = "@interface Foo : NSObject\n\
3851 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3852 - (int)m:(int)x;\n\
3853 @end\n\
3854 @implementation Foo\n\
3855 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3856 @end\n";
3857
3858 #[cfg(feature = "objc")]
3859 const OBJC_HELPER_IN_PROTOCOL: &str = "@protocol Proto\n\
3860 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3861 - (int)m:(int)x;\n\
3862 @end\n\
3863 @implementation Foo\n\
3864 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3865 @end\n";
3866
3867 #[cfg(feature = "objc")]
3873 const OBJC_HELPER_BEHIND_PREPROC: &str = "@interface Foo : NSObject\n\
3874 #if FOO\n\
3875 static int declared(int x) { if (x) { return 1; } return 0; }\n\
3876 #endif\n\
3877 - (int)m:(int)x;\n\
3878 @end\n\
3879 @implementation Foo\n\
3880 #if FOO\n\
3881 static int helper(int x) { if (x) { return 1; } return 0; }\n\
3882 #endif\n\
3883 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3884 @end\n";
3885
3886 #[cfg(feature = "objc")]
3890 const OBJC_FILE_SCOPE_FUNCTION: &str = "\
3891 static int loose(int x) { if (x) { return 1; } return 0; }\n\
3892 @implementation Foo\n\
3893 - (int)m:(int)x { if (x) { return 1; } return 0; }\n\
3894 @end\n";
3895
3896 #[cfg(feature = "objc")]
3897 #[test]
3898 fn objc_static_helper_in_implementation_is_not_weighted_into_the_class() {
3899 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_IN_IMPLEMENTATION, "foo.m", |metric| {
3900 assert_eq!(metric.wmc.class_wmc_sum(), 1);
3903 assert_eq!(metric.npm.class_nm_sum(), 1);
3904 assert_eq!(metric.npm.class_npm_sum(), 1);
3905 assert_eq!(metric.nom.functions_sum(), 2);
3908 assert_eq!(metric.cyclomatic.cyclomatic_sum(), 5);
3911 });
3912 }
3913
3914 #[cfg(feature = "objc")]
3915 #[test]
3916 fn objc_static_helper_keeps_its_own_function_space() {
3917 check_func_space::<ObjcParser, _>(OBJC_HELPER_IN_IMPLEMENTATION, "foo.m", |space| {
3925 let class = child_space(&space, "Foo");
3926 assert_eq!(class.kind, SpaceKind::Class);
3927 assert_eq!(class.metrics.wmc.class_wmc(), 1);
3928 let helper = child_space(class, "helper");
3929 assert_eq!(helper.kind, SpaceKind::Function);
3930 assert_eq!(helper.metrics.cyclomatic.cyclomatic(), 2);
3931 });
3932 }
3933
3934 #[cfg(feature = "objc")]
3935 #[test]
3936 fn objc_static_helper_leaves_a_branching_method_its_weight() {
3937 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_AND_BRANCHING_METHOD, "foo.m", |metric| {
3938 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3939 assert_eq!(metric.npm.class_nm_sum(), 1);
3940 assert_eq!(metric.nom.functions_sum(), 2);
3941 });
3942 }
3943
3944 #[cfg(feature = "objc")]
3945 #[test]
3946 fn objc_static_helper_in_a_category_is_not_weighted_into_the_class() {
3947 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_IN_CATEGORY, "foo.m", |metric| {
3948 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3949 assert_eq!(metric.npm.class_nm_sum(), 1);
3950 assert_eq!(metric.nom.functions_sum(), 2);
3951 });
3952 }
3953
3954 #[cfg(feature = "objc")]
3955 #[test]
3956 fn objc_static_helper_in_an_interface_is_not_weighted_into_the_interface() {
3957 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_IN_INTERFACE, "foo.m", |metric| {
3958 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3961 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3962 assert_eq!(metric.npm.interface_nm_sum(), 1);
3963 assert_eq!(metric.nom.functions_sum(), 2);
3964 });
3965 }
3966
3967 #[cfg(feature = "objc")]
3968 #[test]
3969 fn objc_static_helper_in_a_protocol_is_not_weighted_into_the_protocol() {
3970 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_IN_PROTOCOL, "foo.m", |metric| {
3971 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3972 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3973 assert_eq!(metric.npm.interface_nm_sum(), 1);
3974 assert_eq!(metric.nom.functions_sum(), 2);
3975 });
3976 }
3977
3978 #[cfg(feature = "objc")]
3979 #[test]
3980 fn objc_static_helper_behind_a_preprocessor_conditional_is_not_weighted() {
3981 check_wmc_and_npm::<ObjcParser>(OBJC_HELPER_BEHIND_PREPROC, "foo.m", |metric| {
3982 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
3987 assert_eq!(metric.wmc.class_wmc_sum(), 2);
3988 assert_eq!(metric.nom.functions_sum(), 3);
3989 });
3990 }
3991
3992 #[cfg(feature = "objc")]
3993 #[test]
3994 fn objc_file_scope_function_is_unaffected() {
3995 check_wmc_and_npm::<ObjcParser>(OBJC_FILE_SCOPE_FUNCTION, "foo.m", |metric| {
3996 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4005 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
4006 assert_eq!(metric.nom.functions_sum(), 2);
4007 assert_eq!(metric.cyclomatic.cyclomatic_sum(), 6);
4011 });
4012 }
4013
4014 #[cfg(feature = "cpp")]
4017 #[test]
4018 fn cpp_empty_unit_zero_wmc() {
4019 check_metrics::<CppParser>("", "empty.cpp", |metric| {
4021 assert_eq!(metric.wmc.class_wmc_sum(), 0);
4022 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
4023 insta::assert_json_snapshot!(metric.wmc);
4024 });
4025 }
4026
4027 #[cfg(feature = "cpp")]
4028 #[test]
4029 fn cpp_single_method_wmc_one() {
4030 check_metrics::<CppParser>("class Foo { public: void m() {} };", "foo.cpp", |metric| {
4032 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4033 insta::assert_json_snapshot!(metric.wmc);
4034 });
4035 }
4036
4037 #[cfg(feature = "cpp")]
4038 #[test]
4039 fn cpp_method_with_if_adds_to_wmc() {
4040 check_metrics::<CppParser>(
4042 "class Foo {\n\
4043 public:\n\
4044 int m(int x) {\n\
4045 if (x > 0) { return 1; }\n\
4046 return 0;\n\
4047 }\n\
4048 };",
4049 "foo.cpp",
4050 |metric| {
4051 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4052 insta::assert_json_snapshot!(metric.wmc);
4053 },
4054 );
4055 }
4056
4057 #[cfg(feature = "cpp")]
4058 #[test]
4059 fn cpp_struct_wmc_maps_to_class() {
4060 check_metrics::<CppParser>(
4064 "struct Foo {\n\
4065 int m(int x) {\n\
4066 if (x > 0) { return 1; }\n\
4067 return 0;\n\
4068 }\n\
4069 };",
4070 "foo.cpp",
4071 |metric| {
4072 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4073 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
4074 insta::assert_json_snapshot!(metric.wmc);
4075 },
4076 );
4077 }
4078
4079 #[cfg(feature = "cpp")]
4080 #[test]
4081 fn cpp_free_function_does_not_contribute_to_class_wmc() {
4082 check_metrics::<CppParser>(
4088 "int free_fn(int x) { if (x > 0) { return 1; } return 0; }",
4089 "foo.cpp",
4090 |metric| {
4091 assert_eq!(metric.wmc.class_wmc_sum(), 0);
4092 assert_eq!(metric.wmc.interface_wmc_sum(), 0);
4093 insta::assert_json_snapshot!(metric.wmc);
4094 },
4095 );
4096 }
4097
4098 #[cfg(feature = "cpp")]
4099 #[test]
4100 fn cpp_multiple_methods_wmc_sums() {
4101 check_metrics::<CppParser>(
4104 "class Foo {\n\
4105 public:\n\
4106 int a(int x) { if (x > 0) { return 1; } return 0; }\n\
4107 int b() { return 42; }\n\
4108 };",
4109 "foo.cpp",
4110 |metric| {
4111 assert_eq!(metric.wmc.class_wmc_sum(), 3);
4112 insta::assert_json_snapshot!(metric.wmc);
4113 },
4114 );
4115 }
4116
4117 #[cfg(feature = "cpp")]
4118 #[test]
4119 fn cpp_multiple_classes_wmc_aggregate() {
4120 check_metrics::<CppParser>(
4123 "class Foo { public: void a() {} };\nstruct Bar { void b() {} };",
4124 "foo.cpp",
4125 |metric| {
4126 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4127 insta::assert_json_snapshot!(metric.wmc);
4128 },
4129 );
4130 }
4131
4132 #[cfg(any(feature = "cpp", feature = "mozcpp"))]
4147 const INLINE_FRIEND: &str = "class R {\n\
4148 public:\n\
4149 friend void amigo() { if (1) { } }\n\
4150 void mine() { }\n\
4151 };";
4152
4153 #[cfg(any(feature = "cpp", feature = "mozcpp"))]
4159 const INLINE_FRIEND_OPERATOR: &str = "class R {\n\
4160 public:\n\
4161 friend std::ostream& operator<<(std::ostream& o, const R& r) {\n\
4162 if (r.n) { return o; }\n\
4163 return o;\n\
4164 }\n\
4165 int dump(int x) { if (x) { return 1; } return 0; }\n\
4166 int n;\n\
4167 };";
4168
4169 #[cfg(any(feature = "cpp", feature = "mozcpp"))]
4176 const TEMPLATED_INLINE_FRIEND: &str = "class R {\n\
4177 public:\n\
4178 template<typename T> friend void amigo(T t) { if (t) { } }\n\
4179 int mine(int x) { if (x) { return 1; } return 0; }\n\
4180 };";
4181
4182 #[cfg(any(feature = "cpp", feature = "mozcpp"))]
4187 const FRIEND_WITHOUT_BODY: &str = "class R {\n\
4188 public:\n\
4189 friend void amigo();\n\
4190 friend R operator+(const R& l, const R& r);\n\
4191 friend class Other;\n\
4192 int mine(int x) { if (x) { return 1; } return 0; }\n\
4193 };";
4194
4195 #[cfg(any(feature = "cpp", feature = "mozcpp"))]
4200 const NESTED_CLASS_FRIEND: &str = "class Outer {\n\
4201 public:\n\
4202 class Inner {\n\
4203 public:\n\
4204 friend void amigo() { if (1) { } }\n\
4205 int hidden(int x) { if (x) { return 1; } return 0; }\n\
4206 };\n\
4207 void outer_m() { }\n\
4208 };";
4209
4210 #[cfg(feature = "cpp")]
4211 #[test]
4212 fn cpp_inline_friend_is_not_weighted_into_the_class() {
4213 check_wmc_and_npm::<CppParser>(INLINE_FRIEND, "foo.cpp", |metric| {
4214 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4218 assert_eq!(metric.npm.class_nm_sum(), 1);
4219 assert_eq!(metric.npm.class_npm_sum(), 1);
4220 assert_eq!(metric.nom.functions_sum(), 2);
4224 assert_eq!(metric.cyclomatic.cyclomatic_sum(), 5);
4227 });
4228 }
4229
4230 #[cfg(feature = "cpp")]
4231 #[test]
4232 fn cpp_inline_friend_keeps_its_own_function_space() {
4233 check_func_space::<CppParser, _>(INLINE_FRIEND, "foo.cpp", |space| {
4241 let class = child_space(&space, "R");
4242 assert_eq!(class.kind, SpaceKind::Class);
4243 assert_eq!(class.metrics.wmc.class_wmc(), 1);
4244 let amigo = child_space(class, "amigo");
4245 assert_eq!(amigo.kind, SpaceKind::Function);
4246 assert_eq!(amigo.metrics.cyclomatic.cyclomatic(), 2);
4247 });
4248 }
4249
4250 #[cfg(feature = "cpp")]
4251 #[test]
4252 fn cpp_inline_friend_operator_is_not_weighted_into_the_class() {
4253 check_wmc_and_npm::<CppParser>(INLINE_FRIEND_OPERATOR, "foo.cpp", |metric| {
4254 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4255 assert_eq!(metric.npm.class_nm_sum(), 1);
4256 assert_eq!(metric.nom.functions_sum(), 2);
4257 });
4258 }
4259
4260 #[cfg(feature = "cpp")]
4261 #[test]
4262 fn cpp_templated_inline_friend_is_not_weighted_into_the_class() {
4263 check_wmc_and_npm::<CppParser>(TEMPLATED_INLINE_FRIEND, "foo.cpp", |metric| {
4264 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4265 assert_eq!(metric.npm.class_nm_sum(), 1);
4266 assert_eq!(metric.nom.functions_sum(), 2);
4267 });
4268 }
4269
4270 #[cfg(feature = "cpp")]
4271 #[test]
4272 fn cpp_friend_declared_without_a_body_leaves_wmc_alone() {
4273 check_wmc_and_npm::<CppParser>(FRIEND_WITHOUT_BODY, "foo.cpp", |metric| {
4274 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4278 assert_eq!(metric.npm.class_nm_sum(), 1);
4279 assert_eq!(metric.nom.functions_sum(), 1);
4280 });
4281 }
4282
4283 #[cfg(feature = "cpp")]
4284 #[test]
4285 fn cpp_friend_of_a_nested_class_is_weighted_into_neither() {
4286 check_wmc_and_npm::<CppParser>(NESTED_CLASS_FRIEND, "foo.cpp", |metric| {
4287 assert_eq!(metric.wmc.class_wmc_sum(), 3);
4288 assert_eq!(metric.npm.class_nm_sum(), 2);
4289 assert_eq!(metric.nom.functions_sum(), 3);
4290 });
4291 }
4292
4293 #[cfg(feature = "mozcpp")]
4300 #[test]
4301 fn mozcpp_inline_friend_is_not_weighted_into_the_class() {
4302 check_wmc_and_npm::<MozcppParser>(INLINE_FRIEND, "foo.cpp", |metric| {
4303 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4304 assert_eq!(metric.npm.class_nm_sum(), 1);
4305 assert_eq!(metric.npm.class_npm_sum(), 1);
4306 assert_eq!(metric.nom.functions_sum(), 2);
4307 assert_eq!(metric.cyclomatic.cyclomatic_sum(), 5);
4308 });
4309 }
4310
4311 #[cfg(feature = "mozcpp")]
4312 #[test]
4313 fn mozcpp_inline_friend_operator_is_not_weighted_into_the_class() {
4314 check_wmc_and_npm::<MozcppParser>(INLINE_FRIEND_OPERATOR, "foo.cpp", |metric| {
4315 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4316 assert_eq!(metric.npm.class_nm_sum(), 1);
4317 assert_eq!(metric.nom.functions_sum(), 2);
4318 });
4319 }
4320
4321 #[cfg(feature = "mozcpp")]
4322 #[test]
4323 fn mozcpp_templated_inline_friend_is_not_weighted_into_the_class() {
4324 check_wmc_and_npm::<MozcppParser>(TEMPLATED_INLINE_FRIEND, "foo.cpp", |metric| {
4325 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4326 assert_eq!(metric.npm.class_nm_sum(), 1);
4327 assert_eq!(metric.nom.functions_sum(), 2);
4328 });
4329 }
4330
4331 #[cfg(feature = "mozcpp")]
4332 #[test]
4333 fn mozcpp_friend_declared_without_a_body_leaves_wmc_alone() {
4334 check_wmc_and_npm::<MozcppParser>(FRIEND_WITHOUT_BODY, "foo.cpp", |metric| {
4335 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4336 assert_eq!(metric.npm.class_nm_sum(), 1);
4337 assert_eq!(metric.nom.functions_sum(), 1);
4338 });
4339 }
4340
4341 #[cfg(feature = "mozcpp")]
4342 #[test]
4343 fn mozcpp_friend_of_a_nested_class_is_weighted_into_neither() {
4344 check_wmc_and_npm::<MozcppParser>(NESTED_CLASS_FRIEND, "foo.cpp", |metric| {
4345 assert_eq!(metric.wmc.class_wmc_sum(), 3);
4346 assert_eq!(metric.npm.class_nm_sum(), 2);
4347 assert_eq!(metric.nom.functions_sum(), 3);
4348 });
4349 }
4350
4351 #[cfg(feature = "javascript")]
4352 #[test]
4353 fn javascript_empty_unit_zero_wmc() {
4354 check_metrics::<JavascriptParser>("", "empty.js", |metric| {
4355 assert_eq!(metric.wmc.class_wmc_sum(), 0);
4356 insta::assert_json_snapshot!(metric.wmc);
4357 });
4358 }
4359
4360 #[cfg(feature = "javascript")]
4361 #[test]
4362 fn javascript_single_method_wmc_one() {
4363 check_metrics::<JavascriptParser>("class Foo { a() { return 1; } }", "foo.js", |metric| {
4366 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4367 insta::assert_json_snapshot!(metric.wmc);
4368 });
4369 }
4370
4371 #[cfg(feature = "javascript")]
4372 #[test]
4373 fn javascript_method_with_if_adds_to_wmc() {
4374 check_metrics::<JavascriptParser>(
4376 "class Foo { a(x) { if (x > 0) return 1; return 0; } }",
4377 "foo.js",
4378 |metric| {
4379 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4380 insta::assert_json_snapshot!(metric.wmc);
4381 },
4382 );
4383 }
4384
4385 #[cfg(feature = "javascript")]
4386 #[test]
4387 fn javascript_free_function_does_not_contribute_to_class_wmc() {
4388 check_metrics::<JavascriptParser>(
4391 "function f(x) { if (x > 0) return 1; return 0; }\nclass Foo { a() { return 1; } }",
4392 "foo.js",
4393 |metric| {
4394 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4396 insta::assert_json_snapshot!(metric.wmc);
4397 },
4398 );
4399 }
4400
4401 #[cfg(feature = "javascript")]
4402 #[test]
4403 fn javascript_multiple_classes_wmc_aggregate() {
4404 check_metrics::<JavascriptParser>(
4407 "class Foo { a() { return 1; } }\nclass Bar { b() { return 1; } }",
4408 "foo.js",
4409 |metric| {
4410 assert_eq!(metric.wmc.class_wmc_sum(), 2);
4411 insta::assert_json_snapshot!(metric.wmc);
4412 },
4413 );
4414 }
4415
4416 #[cfg(feature = "mozjs")]
4417 #[test]
4418 fn mozjs_single_method_wmc_one() {
4419 check_metrics::<MozjsParser>("class Foo { a() { return 1; } }", "foo.js", |metric| {
4420 assert_eq!(metric.wmc.class_wmc_sum(), 1);
4421 insta::assert_json_snapshot!(metric.wmc);
4422 });
4423 }
4424
4425 #[cfg(feature = "java")]
4432 #[test]
4433 fn java_method_with_nested_class_wmc_is_non_negative_integer() {
4434 check_metrics::<JavaParser>(
4435 "public class Outer {
4436 public void m() {
4437 class Inner {
4438 public void n() {}
4439 }
4440 }
4441 }",
4442 "Outer.java",
4443 |metric| {
4444 let total = metric.wmc.class_wmc_sum();
4445 assert_eq!(total, 2, "Outer.m (1) + Inner.n (1) with no double-count");
4446 },
4447 );
4448 }
4449
4450 #[cfg(feature = "java")]
4454 #[test]
4455 fn stats_display_and_per_space_accessors() {
4456 check_func_space::<JavaParser, _>(
4457 "public interface I {\n void p();\n}\n\
4458 public class C {\n public void m() {}\n private void n() {}\n}\n",
4459 "X.java",
4460 |unit| {
4461 assert_eq!(unit.metrics.wmc.class_wmc_sum(), 2);
4463 assert_eq!(unit.metrics.wmc.interface_wmc_sum(), 1);
4464 assert_eq!(
4465 unit.metrics.wmc.to_string(),
4466 "classes: 2, interfaces: 1, total: 3"
4467 );
4468 let class = child_space(&unit, "C");
4473 assert_eq!(class.kind, SpaceKind::Class);
4474 assert_eq!(class.metrics.wmc.class_wmc(), 2);
4475 let iface = child_space(&unit, "I");
4476 assert_eq!(iface.kind, SpaceKind::Interface);
4477 assert_eq!(iface.metrics.wmc.interface_wmc(), 1);
4478 },
4479 );
4480 }
4481}