1#![allow(
8 clippy::enum_glob_use,
9 clippy::match_same_arms,
10 clippy::struct_field_names,
11 clippy::wildcard_imports
12)]
13#![allow(
20 clippy::cast_precision_loss,
21 clippy::cast_possible_truncation,
22 clippy::cast_sign_loss
23)]
24#![warn(clippy::arithmetic_side_effects)]
38
39use crate::checker::Checker;
40use crate::lang_helpers::python::python_is_block;
41use std::fmt;
42
43use crate::macros::implement_metric_trait;
44use crate::*;
45
46#[inline]
51fn min_or_zero(v: usize) -> u64 {
52 if v == usize::MAX { 0 } else { v as u64 }
53}
54
55#[inline]
81fn span_rows(start_row: usize, end_line: usize) -> usize {
82 debug_assert!(
83 end_line >= start_row,
84 "span_rows: end_line {end_line} < start_row {start_row}"
85 );
86 end_line.saturating_sub(start_row)
87}
88
89mod dloc;
90mod line_set;
91
92use dloc::Dloc;
93use line_set::LineSet;
94
95#[derive(Debug, Clone, PartialEq)]
97pub struct Sloc {
98 start: usize,
99 end_line: usize,
103 excluded_rows: LineSet,
119 sloc_min: usize,
120 sloc_max: usize,
121}
122
123impl Default for Sloc {
124 fn default() -> Self {
125 Self {
126 start: 0,
127 end_line: 0,
128 excluded_rows: LineSet::default(),
129 sloc_min: usize::MAX,
130 sloc_max: 0,
131 }
132 }
133}
134
135impl Sloc {
136 #[inline]
138 #[must_use]
139 pub fn sloc(&self) -> u64 {
140 let span = span_rows(self.start, self.end_line);
143 span.saturating_sub(self.excluded_rows.len()) as u64
149 }
150
151 #[inline]
169 pub(crate) fn exclude_span(&mut self, start_row: usize, end_line: usize) {
170 if end_line <= start_row {
177 debug_assert!(
178 end_line == start_row,
179 "exclude_span: end_line {end_line} < start_row {start_row}"
180 );
181 return;
182 }
183 self.excluded_rows
189 .insert_range(start_row, end_line.saturating_sub(1));
190 }
191
192 #[inline]
195 #[must_use]
196 pub fn sloc_min(&self) -> u64 {
197 min_or_zero(self.sloc_min)
198 }
199
200 #[inline]
202 #[must_use]
203 pub fn sloc_max(&self) -> u64 {
204 self.sloc_max as u64
205 }
206
207 #[inline]
210 pub fn merge(&mut self, other: &Sloc) {
211 self.sloc_min = self.sloc_min.min(other.sloc_min);
216 self.sloc_max = self.sloc_max.max(other.sloc_max);
217
218 self.excluded_rows.union_with(&other.excluded_rows);
241 }
242
243 #[inline]
244 pub(crate) fn compute_minmax(&mut self) {
245 self.sloc_min = self.sloc_min.min(self.sloc() as usize);
250 self.sloc_max = self.sloc_max.max(self.sloc() as usize);
251 }
252}
253
254#[derive(Debug, Clone, PartialEq)]
256pub struct Ploc {
257 lines: LineSet,
258 ploc_min: usize,
259 ploc_max: usize,
260}
261
262impl Default for Ploc {
263 fn default() -> Self {
264 Self {
265 lines: LineSet::default(),
266 ploc_min: usize::MAX,
267 ploc_max: 0,
268 }
269 }
270}
271
272impl Ploc {
273 #[inline]
275 #[must_use]
276 pub fn ploc(&self) -> u64 {
277 self.lines.len() as u64
280 }
281
282 #[inline]
285 #[must_use]
286 pub fn ploc_min(&self) -> u64 {
287 min_or_zero(self.ploc_min)
288 }
289
290 #[inline]
292 #[must_use]
293 pub fn ploc_max(&self) -> u64 {
294 self.ploc_max as u64
295 }
296
297 #[inline]
299 pub fn merge(&mut self, other: &Ploc) {
300 self.lines.union_with(&other.lines);
305
306 self.ploc_min = self.ploc_min.min(other.ploc_min);
308 self.ploc_max = self.ploc_max.max(other.ploc_max);
309 }
310
311 #[inline]
312 pub(crate) fn compute_minmax(&mut self) {
313 let ploc = self.ploc() as usize;
318 self.ploc_min = self.ploc_min.min(ploc);
319 self.ploc_max = self.ploc_max.max(ploc);
320 }
321}
322
323#[derive(Debug, Clone, PartialEq)]
325pub struct Cloc {
326 only_comment_line_starts: LineSet,
334 code_comment_line_starts: LineSet,
342 comment_line_end: Option<usize>,
343 cloc_min: usize,
344 cloc_max: usize,
345}
346
347impl Default for Cloc {
348 fn default() -> Self {
349 Self {
350 only_comment_line_starts: LineSet::default(),
351 code_comment_line_starts: LineSet::default(),
352 comment_line_end: Option::default(),
353 cloc_min: usize::MAX,
354 cloc_max: 0,
355 }
356 }
357}
358
359impl Cloc {
360 #[inline]
362 #[must_use]
363 pub fn cloc(&self) -> u64 {
364 self.only_comment_line_starts
374 .union_len(&self.code_comment_line_starts) as u64
375 }
376
377 #[inline]
380 #[must_use]
381 pub fn cloc_min(&self) -> u64 {
382 min_or_zero(self.cloc_min)
383 }
384
385 #[inline]
387 #[must_use]
388 pub fn cloc_max(&self) -> u64 {
389 self.cloc_max as u64
390 }
391
392 #[inline]
394 pub fn merge(&mut self, other: &Cloc) {
395 self.only_comment_line_starts
398 .union_with(&other.only_comment_line_starts);
399 self.code_comment_line_starts
400 .union_with(&other.code_comment_line_starts);
401
402 self.cloc_min = self.cloc_min.min(other.cloc_min);
404 self.cloc_max = self.cloc_max.max(other.cloc_max);
405 }
406
407 #[inline]
408 pub(crate) fn compute_minmax(&mut self) {
409 let cloc = self.cloc() as usize;
414 self.cloc_min = self.cloc_min.min(cloc);
415 self.cloc_max = self.cloc_max.max(cloc);
416 }
417}
418
419#[derive(Debug, Clone, PartialEq)]
421pub struct Lloc {
422 logical_lines: usize,
423 lloc_min: usize,
424 lloc_max: usize,
425}
426
427impl Default for Lloc {
428 fn default() -> Self {
429 Self {
430 logical_lines: 0,
431 lloc_min: usize::MAX,
432 lloc_max: 0,
433 }
434 }
435}
436
437impl Lloc {
438 #[inline]
440 #[must_use]
441 pub fn lloc(&self) -> u64 {
442 self.logical_lines as u64
445 }
446
447 #[inline]
450 #[must_use]
451 pub fn lloc_min(&self) -> u64 {
452 min_or_zero(self.lloc_min)
453 }
454
455 #[inline]
457 #[must_use]
458 pub fn lloc_max(&self) -> u64 {
459 self.lloc_max as u64
460 }
461
462 #[inline]
472 fn count_logical_line(&mut self) {
473 self.logical_lines = self.logical_lines.saturating_add(1);
474 }
475
476 #[inline]
478 pub fn merge(&mut self, other: &Lloc) {
479 self.logical_lines = self.logical_lines.saturating_add(other.logical_lines);
481 self.lloc_min = self.lloc_min.min(other.lloc_min);
483 self.lloc_max = self.lloc_max.max(other.lloc_max);
484 }
485
486 #[inline]
487 pub(crate) fn compute_minmax(&mut self) {
488 self.lloc_min = self.lloc_min.min(self.lloc() as usize);
491 self.lloc_max = self.lloc_max.max(self.lloc() as usize);
492 }
493}
494
495#[derive(Debug, Clone, PartialEq)]
497#[non_exhaustive]
498pub struct Stats {
499 sloc: Sloc,
500 ploc: Ploc,
501 cloc: Cloc,
502 lloc: Lloc,
503 dloc: Dloc,
504 space_count: usize,
505 blank_min: usize,
506 blank_max: usize,
507}
508
509impl Default for Stats {
510 fn default() -> Self {
511 Self {
512 sloc: Sloc::default(),
513 ploc: Ploc::default(),
514 cloc: Cloc::default(),
515 lloc: Lloc::default(),
516 dloc: Dloc::default(),
517 space_count: 1,
518 blank_min: usize::MAX,
519 blank_max: 0,
520 }
521 }
522}
523
524impl fmt::Display for Stats {
525 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
526 write!(
527 f,
528 "sloc: {}, ploc: {}, lloc: {}, cloc: {}, blank: {}, dloc: {}, sloc_average: {}, ploc_average: {}, lloc_average: {}, cloc_average: {}, blank_average: {}, dloc_average: {}, sloc_min: {}, sloc_max: {}, cloc_min: {}, cloc_max: {}, ploc_min: {}, ploc_max: {}, lloc_min: {}, lloc_max: {}, blank_min: {}, blank_max: {}, dloc_min: {}, dloc_max: {}",
529 self.sloc(),
530 self.ploc(),
531 self.lloc(),
532 self.cloc(),
533 self.blank(),
534 self.dloc(),
535 self.sloc_average(),
536 self.ploc_average(),
537 self.lloc_average(),
538 self.cloc_average(),
539 self.blank_average(),
540 self.dloc_average(),
541 self.sloc_min(),
542 self.sloc_max(),
543 self.cloc_min(),
544 self.cloc_max(),
545 self.ploc_min(),
546 self.ploc_max(),
547 self.lloc_min(),
548 self.lloc_max(),
549 self.blank_min(),
550 self.blank_max(),
551 self.dloc_min(),
552 self.dloc_max(),
553 )
554 }
555}
556
557impl Stats {
558 #[cfg(test)]
564 pub(crate) fn with_cloc_sloc(code_comment_lines: usize, sloc_end_row: usize) -> Self {
565 let mut stats = Stats::default();
566 stats.sloc.start = 0;
567 stats.sloc.end_line = sloc_end_row.saturating_add(1);
570 if code_comment_lines > 0 {
575 let synthetic_base = sloc_end_row.saturating_add(1);
576 let synthetic_end = synthetic_base
581 .saturating_add(code_comment_lines)
582 .saturating_sub(1);
583 stats
584 .cloc
585 .code_comment_line_starts
586 .insert_range(synthetic_base, synthetic_end);
587 }
588 stats
589 }
590
591 pub fn merge(&mut self, other: &Stats) {
593 self.sloc.merge(&other.sloc);
594 self.ploc.merge(&other.ploc);
595 self.cloc.merge(&other.cloc);
596 self.lloc.merge(&other.lloc);
597 self.dloc.merge(&other.dloc);
598
599 self.space_count = self.space_count.saturating_add(other.space_count);
601
602 self.blank_min = self.blank_min.min(other.blank_min);
604 self.blank_max = self.blank_max.max(other.blank_max);
605 }
606
607 #[inline]
613 pub(crate) fn exclude_test_span(&mut self, start_row: usize, end_line: usize) {
614 self.sloc.exclude_span(start_row, end_line);
615 }
616
617 #[inline]
621 #[must_use]
622 pub fn sloc(&self) -> u64 {
623 self.sloc.sloc()
624 }
625
626 #[inline]
630 #[must_use]
631 pub fn ploc(&self) -> u64 {
632 self.ploc.ploc()
633 }
634
635 #[inline]
639 #[must_use]
640 pub fn lloc(&self) -> u64 {
641 self.lloc.lloc()
642 }
643
644 #[inline]
648 #[must_use]
649 pub fn cloc(&self) -> u64 {
650 self.cloc.cloc()
651 }
652
653 #[inline]
661 #[must_use]
662 pub fn dloc(&self) -> u64 {
663 self.dloc.dloc()
664 }
665
666 #[inline]
672 #[must_use]
673 pub(crate) fn dloc_without_code(&self) -> u64 {
674 let shared = self.dloc.lines.intersection_len(&self.ploc.lines);
675 self.dloc.lines.len().saturating_sub(shared) as u64
676 }
677
678 #[inline]
682 #[must_use]
683 pub fn blank(&self) -> u64 {
684 self.sloc()
690 .saturating_sub(self.ploc())
691 .saturating_sub(self.cloc.only_comment_line_starts.len() as u64)
692 }
693
694 #[inline]
698 #[must_use]
699 pub fn sloc_average(&self) -> f64 {
700 crate::metrics::average(self.sloc() as f64, self.space_count)
701 }
702
703 #[inline]
707 #[must_use]
708 pub fn ploc_average(&self) -> f64 {
709 crate::metrics::average(self.ploc() as f64, self.space_count)
710 }
711
712 #[inline]
716 #[must_use]
717 pub fn lloc_average(&self) -> f64 {
718 crate::metrics::average(self.lloc() as f64, self.space_count)
719 }
720
721 #[inline]
725 #[must_use]
726 pub fn cloc_average(&self) -> f64 {
727 crate::metrics::average(self.cloc() as f64, self.space_count)
728 }
729
730 #[inline]
734 #[must_use]
735 pub fn dloc_average(&self) -> f64 {
736 crate::metrics::average(self.dloc() as f64, self.space_count)
737 }
738
739 #[inline]
743 #[must_use]
744 pub fn blank_average(&self) -> f64 {
745 crate::metrics::average(self.blank() as f64, self.space_count)
746 }
747
748 #[inline]
750 #[must_use]
751 pub fn sloc_min(&self) -> u64 {
752 self.sloc.sloc_min()
753 }
754
755 #[inline]
757 #[must_use]
758 pub fn sloc_max(&self) -> u64 {
759 self.sloc.sloc_max()
760 }
761
762 #[inline]
764 #[must_use]
765 pub fn cloc_min(&self) -> u64 {
766 self.cloc.cloc_min()
767 }
768
769 #[inline]
771 #[must_use]
772 pub fn cloc_max(&self) -> u64 {
773 self.cloc.cloc_max()
774 }
775
776 #[inline]
778 #[must_use]
779 pub fn dloc_min(&self) -> u64 {
780 self.dloc.dloc_min()
781 }
782
783 #[inline]
785 #[must_use]
786 pub fn dloc_max(&self) -> u64 {
787 self.dloc.dloc_max()
788 }
789
790 #[inline]
792 #[must_use]
793 pub fn ploc_min(&self) -> u64 {
794 self.ploc.ploc_min()
795 }
796
797 #[inline]
799 #[must_use]
800 pub fn ploc_max(&self) -> u64 {
801 self.ploc.ploc_max()
802 }
803
804 #[inline]
806 #[must_use]
807 pub fn lloc_min(&self) -> u64 {
808 self.lloc.lloc_min()
809 }
810
811 #[inline]
813 #[must_use]
814 pub fn lloc_max(&self) -> u64 {
815 self.lloc.lloc_max()
816 }
817
818 #[inline]
821 #[must_use]
822 pub fn blank_min(&self) -> u64 {
823 min_or_zero(self.blank_min)
824 }
825
826 #[inline]
828 #[must_use]
829 pub fn blank_max(&self) -> u64 {
830 self.blank_max as u64
831 }
832
833 #[inline]
834 pub(crate) fn compute_minmax(&mut self) {
835 self.sloc.compute_minmax();
836 self.ploc.compute_minmax();
837 self.cloc.compute_minmax();
838 self.lloc.compute_minmax();
839 self.dloc.compute_minmax();
840
841 let blank = self.blank() as usize;
848 self.blank_min = self.blank_min.min(blank);
849 self.blank_max = self.blank_max.max(blank);
850 }
851
852 pub(crate) fn init_unit_span(&mut self, start: usize, end_line: usize) {
853 self.sloc.start = start;
854 self.sloc.end_line = end_line;
855 }
856
857 pub(crate) fn clamp_line_sets_to_span(&mut self) {
913 let start = self.sloc.start;
914 let span = span_rows(start, self.sloc.end_line);
915 let (first, last) = if span == 0 {
950 debug_assert!(
951 self.ploc() == 0 && self.cloc() == 0,
952 "zero-span space reached the clamp holding {} ploc / {} cloc \
953 row(s); clearing them here would lose them silently",
954 self.ploc(),
955 self.cloc()
956 );
957 (1, 0)
958 } else {
959 (start, self.sloc.end_line.saturating_sub(1))
960 };
961 self.ploc.lines.retain_range(first, last);
962 self.cloc.only_comment_line_starts.retain_range(first, last);
963 self.cloc.code_comment_line_starts.retain_range(first, last);
964 self.dloc.lines.retain_range(first, last);
966 self.sloc.excluded_rows.retain_range(first, last);
976
977 debug_assert!(
995 self.ploc() <= span as u64,
996 "ploc {} exceeds the {span} row span it was clamped to",
997 self.ploc()
998 );
999 debug_assert!(
1000 self.cloc() <= span as u64,
1001 "cloc {} exceeds the {span} row span it was clamped to",
1002 self.cloc()
1003 );
1004 }
1005
1006 pub(crate) fn settle_excluded_rows(&mut self) {
1047 debug_assert_eq!(
1052 self.ploc
1053 .lines
1054 .union_len(&self.cloc.code_comment_line_starts),
1055 self.ploc.lines.len(),
1056 "a row carrying both code and a comment must also be a code row"
1057 );
1058
1059 let excluded = &mut self.sloc.excluded_rows;
1061 excluded.subtract(&self.ploc.lines);
1062 excluded.subtract(&self.cloc.only_comment_line_starts);
1063 excluded.subtract(&self.cloc.code_comment_line_starts);
1064
1065 debug_assert!(
1066 self.ploc() <= self.sloc(),
1067 "ploc {} exceeds sloc {} after settling",
1068 self.ploc(),
1069 self.sloc()
1070 );
1071 let cloc = self.cloc();
1072 debug_assert!(
1073 cloc <= self.sloc(),
1074 "cloc {} exceeds sloc {} after settling",
1075 cloc,
1076 self.sloc()
1077 );
1078 #[cfg(debug_assertions)]
1083 {
1084 let mut comment_rows = self.cloc.only_comment_line_starts.clone();
1085 comment_rows.union_with(&self.cloc.code_comment_line_starts);
1086 debug_assert_eq!(
1087 comment_rows.union_len(&self.dloc.lines),
1088 comment_rows.len(),
1089 "a dloc row outside the cloc sets after settling (dloc {}, cloc {})",
1090 self.dloc(),
1091 cloc
1092 );
1093 }
1094 }
1095}
1096
1097#[doc(hidden)]
1098pub(crate) trait Loc
1100where
1101 Self: Checker,
1102{
1103 fn compute<'a>(
1112 node: &Node<'a>,
1113 code: &[u8],
1114 ancestors: Ancestors<'a, '_>,
1115 stats: &mut Stats,
1116 is_func_space: bool,
1117 );
1118}
1119
1120mod shared;
1121pub(crate) use shared::*;
1122
1123implement_metric_trait!(Loc, PreprocCode, CcommentCode);
1127
1128mod bash;
1129mod c;
1130mod cpp;
1131mod csharp;
1132mod elixir;
1133mod go;
1134mod groovy;
1135mod irules;
1136mod java;
1137mod javascript;
1138mod kotlin;
1139mod lua;
1140mod mozcpp;
1141mod mozjs;
1142mod objc;
1143mod perl;
1144mod php;
1145mod python;
1146mod ruby;
1147mod rust;
1148mod tcl;
1149mod tsx;
1150mod typescript;
1151
1152#[cfg(test)]
1153#[allow(
1154 clippy::float_cmp,
1155 clippy::cast_precision_loss,
1156 clippy::cast_possible_truncation,
1157 clippy::cast_sign_loss,
1158 clippy::similar_names,
1159 clippy::doc_markdown,
1160 clippy::needless_raw_string_hashes,
1161 clippy::too_many_lines
1162)]
1163mod tests {
1164 use crate::test_support::{check_metrics_only_shim, metrics_verbatim, space_verbatim};
1165
1166 use super::*;
1167
1168 check_metrics_only_shim!(check_metrics, Loc);
1169
1170 #[test]
1177 fn loc_empty_file_min_is_zero() {
1178 let stats = Stats::default();
1179 assert_eq!(stats.sloc_min(), 0);
1180 assert_eq!(stats.ploc_min(), 0);
1181 assert_eq!(stats.lloc_min(), 0);
1182 assert_eq!(stats.cloc_min(), 0);
1183 assert_eq!(stats.blank_min(), 0);
1184 assert_eq!(stats.dloc_min(), 0);
1185 }
1186
1187 #[cfg(feature = "perl")]
1193 #[cfg(test)]
1194 fn assert_perl_parses_cleanly(source: &str) {
1195 use crate::traits::ParserTrait;
1196 let path = std::path::PathBuf::from("foo.pl");
1200 let mut bytes = source.trim_end_matches('\n').as_bytes().to_vec();
1201 bytes.push(b'\n');
1202 let parser = PerlParser::new(bytes, &path, None);
1203 assert!(
1204 !parser.root().has_error(),
1205 "tree-sitter-perl returned an error tree for snippet:\n{source}"
1206 );
1207 }
1208
1209 #[cfg(feature = "python")]
1210 #[test]
1211 fn python_sloc() {
1212 check_metrics::<PythonParser>(
1213 "
1214
1215 a = 42
1216
1217 ",
1218 "foo.py",
1219 |metric| {
1220 insta::assert_json_snapshot!(
1222 metric.loc,
1223 @r#"
1224 {
1225 "sloc": 1,
1226 "ploc": 1,
1227 "lloc": 1,
1228 "cloc": 0,
1229 "blank": 0,
1230 "dloc": 0,
1231 "sloc_average": 1.0,
1232 "ploc_average": 1.0,
1233 "lloc_average": 1.0,
1234 "cloc_average": 0.0,
1235 "blank_average": 0.0,
1236 "dloc_average": 0.0,
1237 "sloc_min": 1,
1238 "sloc_max": 1,
1239 "cloc_min": 0,
1240 "cloc_max": 0,
1241 "ploc_min": 1,
1242 "ploc_max": 1,
1243 "lloc_min": 1,
1244 "lloc_max": 1,
1245 "blank_min": 0,
1246 "blank_max": 0,
1247 "dloc_min": 0,
1248 "dloc_max": 0
1249 }
1250 "#
1251 );
1252 },
1253 );
1254 }
1255
1256 #[cfg(feature = "python")]
1257 #[test]
1258 fn python_blank() {
1259 check_metrics::<PythonParser>(
1260 "
1261 a = 42
1262
1263 b = 43
1264
1265 ",
1266 "foo.py",
1267 |metric| {
1268 insta::assert_json_snapshot!(
1270 metric.loc,
1271 @r#"
1272 {
1273 "sloc": 3,
1274 "ploc": 2,
1275 "lloc": 2,
1276 "cloc": 0,
1277 "blank": 1,
1278 "dloc": 0,
1279 "sloc_average": 3.0,
1280 "ploc_average": 2.0,
1281 "lloc_average": 2.0,
1282 "cloc_average": 0.0,
1283 "blank_average": 1.0,
1284 "dloc_average": 0.0,
1285 "sloc_min": 3,
1286 "sloc_max": 3,
1287 "cloc_min": 0,
1288 "cloc_max": 0,
1289 "ploc_min": 2,
1290 "ploc_max": 2,
1291 "lloc_min": 2,
1292 "lloc_max": 2,
1293 "blank_min": 1,
1294 "blank_max": 1,
1295 "dloc_min": 0,
1296 "dloc_max": 0
1297 }
1298 "#
1299 );
1300 },
1301 );
1302 }
1303
1304 #[cfg(feature = "rust")]
1305 #[test]
1306 fn rust_blank() {
1307 check_metrics::<RustParser>(
1308 "
1309
1310 let a = 42;
1311
1312 let b = 43;
1313
1314 ",
1315 "foo.rs",
1316 |metric| {
1317 insta::assert_json_snapshot!(
1319 metric.loc,
1320 @r#"
1321 {
1322 "sloc": 3,
1323 "ploc": 2,
1324 "lloc": 2,
1325 "cloc": 0,
1326 "blank": 1,
1327 "dloc": 0,
1328 "sloc_average": 3.0,
1329 "ploc_average": 2.0,
1330 "lloc_average": 2.0,
1331 "cloc_average": 0.0,
1332 "blank_average": 1.0,
1333 "dloc_average": 0.0,
1334 "sloc_min": 3,
1335 "sloc_max": 3,
1336 "cloc_min": 0,
1337 "cloc_max": 0,
1338 "ploc_min": 2,
1339 "ploc_max": 2,
1340 "lloc_min": 2,
1341 "lloc_max": 2,
1342 "blank_min": 1,
1343 "blank_max": 1,
1344 "dloc_min": 0,
1345 "dloc_max": 0
1346 }
1347 "#
1348 );
1349 },
1350 );
1351
1352 check_metrics::<RustParser>("fn func() { /* comment */ }", "foo.rs", |metric| {
1353 insta::assert_json_snapshot!(
1355 metric.loc,
1356 @r#"
1357 {
1358 "sloc": 1,
1359 "ploc": 1,
1360 "lloc": 0,
1361 "cloc": 1,
1362 "blank": 0,
1363 "dloc": 0,
1364 "sloc_average": 0.5,
1365 "ploc_average": 0.5,
1366 "lloc_average": 0.0,
1367 "cloc_average": 0.5,
1368 "blank_average": 0.0,
1369 "dloc_average": 0.0,
1370 "sloc_min": 1,
1371 "sloc_max": 1,
1372 "cloc_min": 1,
1373 "cloc_max": 1,
1374 "ploc_min": 1,
1375 "ploc_max": 1,
1376 "lloc_min": 0,
1377 "lloc_max": 0,
1378 "blank_min": 0,
1379 "blank_max": 0,
1380 "dloc_min": 0,
1381 "dloc_max": 0
1382 }
1383 "#
1384 );
1385 });
1386 }
1387
1388 #[cfg(feature = "c")]
1389 #[test]
1390 fn c_blank() {
1391 check_metrics::<CParser>(
1392 "
1393
1394 int a = 42;
1395
1396 int b = 43;
1397
1398 ",
1399 "foo.c",
1400 |metric| {
1401 insta::assert_json_snapshot!(
1403 metric.loc,
1404 @r#"
1405 {
1406 "sloc": 3,
1407 "ploc": 2,
1408 "lloc": 2,
1409 "cloc": 0,
1410 "blank": 1,
1411 "dloc": 0,
1412 "sloc_average": 3.0,
1413 "ploc_average": 2.0,
1414 "lloc_average": 2.0,
1415 "cloc_average": 0.0,
1416 "blank_average": 1.0,
1417 "dloc_average": 0.0,
1418 "sloc_min": 3,
1419 "sloc_max": 3,
1420 "cloc_min": 0,
1421 "cloc_max": 0,
1422 "ploc_min": 2,
1423 "ploc_max": 2,
1424 "lloc_min": 2,
1425 "lloc_max": 2,
1426 "blank_min": 1,
1427 "blank_max": 1,
1428 "dloc_min": 0,
1429 "dloc_max": 0
1430 }
1431 "#
1432 );
1433 },
1434 );
1435 }
1436
1437 #[cfg(feature = "python")]
1438 #[test]
1439 fn python_no_zero_blank() {
1440 check_metrics::<PythonParser>(
1443 "def ConnectToUpdateServer():
1444 pool = 4
1445
1446 updateServer = -42
1447 isConnected = False
1448 currTry = 0
1449 numRetries = 10 # Number of IPC connection retries before
1450 # giving up.
1451 numTries = 20 # Number of IPC connection tries before
1452 # giving up.",
1453 "foo.py",
1454 |metric| {
1455 insta::assert_json_snapshot!(
1457 metric.loc,
1458 @r#"
1459 {
1460 "sloc": 10,
1461 "ploc": 7,
1462 "lloc": 6,
1463 "cloc": 4,
1464 "blank": 1,
1465 "dloc": 0,
1466 "sloc_average": 5.0,
1467 "ploc_average": 3.5,
1468 "lloc_average": 3.0,
1469 "cloc_average": 2.0,
1470 "blank_average": 0.5,
1471 "dloc_average": 0.0,
1472 "sloc_min": 10,
1473 "sloc_max": 10,
1474 "cloc_min": 4,
1475 "cloc_max": 4,
1476 "ploc_min": 7,
1477 "ploc_max": 7,
1478 "lloc_min": 6,
1479 "lloc_max": 6,
1480 "blank_min": 1,
1481 "blank_max": 1,
1482 "dloc_min": 0,
1483 "dloc_max": 0
1484 }
1485 "#
1486 );
1487 },
1488 );
1489 }
1490
1491 #[cfg(feature = "python")]
1492 #[test]
1493 fn python_no_blank() {
1494 check_metrics::<PythonParser>(
1497 "def ConnectToUpdateServer():
1498 pool = 4
1499 updateServer = -42
1500 isConnected = False
1501 currTry = 0
1502 numRetries = 10 # Number of IPC connection retries before
1503 # giving up.
1504 numTries = 20 # Number of IPC connection tries before
1505 # giving up.",
1506 "foo.py",
1507 |metric| {
1508 insta::assert_json_snapshot!(
1510 metric.loc,
1511 @r#"
1512 {
1513 "sloc": 9,
1514 "ploc": 7,
1515 "lloc": 6,
1516 "cloc": 4,
1517 "blank": 0,
1518 "dloc": 0,
1519 "sloc_average": 4.5,
1520 "ploc_average": 3.5,
1521 "lloc_average": 3.0,
1522 "cloc_average": 2.0,
1523 "blank_average": 0.0,
1524 "dloc_average": 0.0,
1525 "sloc_min": 9,
1526 "sloc_max": 9,
1527 "cloc_min": 4,
1528 "cloc_max": 4,
1529 "ploc_min": 7,
1530 "ploc_max": 7,
1531 "lloc_min": 6,
1532 "lloc_max": 6,
1533 "blank_min": 0,
1534 "blank_max": 0,
1535 "dloc_min": 0,
1536 "dloc_max": 0
1537 }
1538 "#
1539 );
1540 },
1541 );
1542 }
1543
1544 #[cfg(feature = "python")]
1545 #[test]
1546 fn python_no_zero_blank_more_comments() {
1547 check_metrics::<PythonParser>(
1550 "def ConnectToUpdateServer():
1551 pool = 4
1552
1553 updateServer = -42
1554 isConnected = False
1555 currTry = 0 # Set this variable to 0
1556 numRetries = 10 # Number of IPC connection retries before
1557 # giving up.
1558 numTries = 20 # Number of IPC connection tries before
1559 # giving up.",
1560 "foo.py",
1561 |metric| {
1562 insta::assert_json_snapshot!(
1564 metric.loc,
1565 @r#"
1566 {
1567 "sloc": 10,
1568 "ploc": 7,
1569 "lloc": 6,
1570 "cloc": 5,
1571 "blank": 1,
1572 "dloc": 0,
1573 "sloc_average": 5.0,
1574 "ploc_average": 3.5,
1575 "lloc_average": 3.0,
1576 "cloc_average": 2.5,
1577 "blank_average": 0.5,
1578 "dloc_average": 0.0,
1579 "sloc_min": 10,
1580 "sloc_max": 10,
1581 "cloc_min": 5,
1582 "cloc_max": 5,
1583 "ploc_min": 7,
1584 "ploc_max": 7,
1585 "lloc_min": 6,
1586 "lloc_max": 6,
1587 "blank_min": 1,
1588 "blank_max": 1,
1589 "dloc_min": 0,
1590 "dloc_max": 0
1591 }
1592 "#
1593 );
1594 },
1595 );
1596 }
1597
1598 #[cfg(feature = "rust")]
1599 #[test]
1600 fn rust_no_zero_blank() {
1601 check_metrics::<RustParser>(
1604 "fn ConnectToUpdateServer() {
1605 let pool = 0;
1606
1607 let updateServer = -42;
1608 let isConnected = false;
1609 let currTry = 0;
1610 let numRetries = 10; // Number of IPC connection retries before
1611 // giving up.
1612 let numTries = 20; // Number of IPC connection tries before
1613 // giving up.
1614 }",
1615 "foo.rs",
1616 |metric| {
1617 insta::assert_json_snapshot!(
1619 metric.loc,
1620 @r#"
1621 {
1622 "sloc": 11,
1623 "ploc": 8,
1624 "lloc": 6,
1625 "cloc": 4,
1626 "blank": 1,
1627 "dloc": 0,
1628 "sloc_average": 5.5,
1629 "ploc_average": 4.0,
1630 "lloc_average": 3.0,
1631 "cloc_average": 2.0,
1632 "blank_average": 0.5,
1633 "dloc_average": 0.0,
1634 "sloc_min": 11,
1635 "sloc_max": 11,
1636 "cloc_min": 4,
1637 "cloc_max": 4,
1638 "ploc_min": 8,
1639 "ploc_max": 8,
1640 "lloc_min": 6,
1641 "lloc_max": 6,
1642 "blank_min": 1,
1643 "blank_max": 1,
1644 "dloc_min": 0,
1645 "dloc_max": 0
1646 }
1647 "#
1648 );
1649 },
1650 );
1651 }
1652
1653 #[cfg(feature = "javascript")]
1654 #[test]
1655 fn javascript_no_zero_blank() {
1656 check_metrics::<JavascriptParser>(
1659 "function ConnectToUpdateServer() {
1660 var pool = 0;
1661
1662 var updateServer = -42;
1663 var isConnected = false;
1664 var currTry = 0;
1665 var numRetries = 10; // Number of IPC connection retries before
1666 // giving up.
1667 var numTries = 20; // Number of IPC connection tries before
1668 // giving up.
1669 }",
1670 "foo.js",
1671 |metric| {
1672 insta::assert_json_snapshot!(
1674 metric.loc,
1675 @r#"
1676 {
1677 "sloc": 11,
1678 "ploc": 8,
1679 "lloc": 6,
1680 "cloc": 4,
1681 "blank": 1,
1682 "dloc": 0,
1683 "sloc_average": 5.5,
1684 "ploc_average": 4.0,
1685 "lloc_average": 3.0,
1686 "cloc_average": 2.0,
1687 "blank_average": 0.5,
1688 "dloc_average": 0.0,
1689 "sloc_min": 11,
1690 "sloc_max": 11,
1691 "cloc_min": 4,
1692 "cloc_max": 4,
1693 "ploc_min": 8,
1694 "ploc_max": 8,
1695 "lloc_min": 6,
1696 "lloc_max": 6,
1697 "blank_min": 1,
1698 "blank_max": 1,
1699 "dloc_min": 0,
1700 "dloc_max": 0
1701 }
1702 "#
1703 );
1704 },
1705 );
1706 }
1707
1708 #[cfg(feature = "cpp")]
1709 #[test]
1710 fn cpp_no_zero_blank() {
1711 check_metrics::<CppParser>(
1714 "void ConnectToUpdateServer() {
1715 int pool;
1716
1717 int updateServer = -42;
1718 bool isConnected = false;
1719 int currTry = 0;
1720 const int numRetries = 10; // Number of IPC connection retries before
1721 // giving up.
1722 const int numTries = 20; // Number of IPC connection tries before
1723 // giving up.
1724 }",
1725 "foo.cpp",
1726 |metric| {
1727 insta::assert_json_snapshot!(
1729 metric.loc,
1730 @r#"
1731 {
1732 "sloc": 11,
1733 "ploc": 8,
1734 "lloc": 6,
1735 "cloc": 4,
1736 "blank": 1,
1737 "dloc": 0,
1738 "sloc_average": 5.5,
1739 "ploc_average": 4.0,
1740 "lloc_average": 3.0,
1741 "cloc_average": 2.0,
1742 "blank_average": 0.5,
1743 "dloc_average": 0.0,
1744 "sloc_min": 11,
1745 "sloc_max": 11,
1746 "cloc_min": 4,
1747 "cloc_max": 4,
1748 "ploc_min": 8,
1749 "ploc_max": 8,
1750 "lloc_min": 6,
1751 "lloc_max": 6,
1752 "blank_min": 1,
1753 "blank_max": 1,
1754 "dloc_min": 0,
1755 "dloc_max": 0
1756 }
1757 "#
1758 );
1759 },
1760 );
1761 }
1762
1763 #[cfg(feature = "cpp")]
1764 #[test]
1765 fn cpp_code_line_start_block_blank() {
1766 check_metrics::<CppParser>(
1769 "void ConnectToUpdateServer() {
1770 int pool;
1771
1772 int updateServer = -42;
1773 bool isConnected = false;
1774 int currTry = 0;
1775 const int numRetries = 10; /* Number of IPC connection retries
1776 before
1777 giving up. */
1778 const int numTries = 20; // Number of IPC connection tries before
1779 // giving up.
1780 }",
1781 "foo.cpp",
1782 |metric| {
1783 insta::assert_json_snapshot!(
1785 metric.loc,
1786 @r#"
1787 {
1788 "sloc": 12,
1789 "ploc": 8,
1790 "lloc": 6,
1791 "cloc": 5,
1792 "blank": 1,
1793 "dloc": 0,
1794 "sloc_average": 6.0,
1795 "ploc_average": 4.0,
1796 "lloc_average": 3.0,
1797 "cloc_average": 2.5,
1798 "blank_average": 0.5,
1799 "dloc_average": 0.0,
1800 "sloc_min": 12,
1801 "sloc_max": 12,
1802 "cloc_min": 5,
1803 "cloc_max": 5,
1804 "ploc_min": 8,
1805 "ploc_max": 8,
1806 "lloc_min": 6,
1807 "lloc_max": 6,
1808 "blank_min": 1,
1809 "blank_max": 1,
1810 "dloc_min": 0,
1811 "dloc_max": 0
1812 }
1813 "#
1814 );
1815 },
1816 );
1817 }
1818
1819 #[cfg(feature = "cpp")]
1820 #[test]
1821 fn cpp_block_comment_blank() {
1822 check_metrics::<CppParser>(
1825 "void ConnectToUpdateServer() {
1826 int pool;
1827
1828 int updateServer = -42;
1829 bool isConnected = false;
1830 int currTry = 0;
1831 /* Number of IPC connection retries
1832 before
1833 giving up. */
1834 const int numRetries = 10;
1835 const int numTries = 20; // Number of IPC connection tries before
1836 // giving up.
1837 }",
1838 "foo.cpp",
1839 |metric| {
1840 insta::assert_json_snapshot!(
1842 metric.loc,
1843 @r#"
1844 {
1845 "sloc": 13,
1846 "ploc": 8,
1847 "lloc": 6,
1848 "cloc": 5,
1849 "blank": 1,
1850 "dloc": 0,
1851 "sloc_average": 6.5,
1852 "ploc_average": 4.0,
1853 "lloc_average": 3.0,
1854 "cloc_average": 2.5,
1855 "blank_average": 0.5,
1856 "dloc_average": 0.0,
1857 "sloc_min": 13,
1858 "sloc_max": 13,
1859 "cloc_min": 5,
1860 "cloc_max": 5,
1861 "ploc_min": 8,
1862 "ploc_max": 8,
1863 "lloc_min": 6,
1864 "lloc_max": 6,
1865 "blank_min": 1,
1866 "blank_max": 1,
1867 "dloc_min": 0,
1868 "dloc_max": 0
1869 }
1870 "#
1871 );
1872 },
1873 );
1874 }
1875
1876 #[cfg(feature = "cpp")]
1877 #[test]
1878 fn cpp_code_line_block_one_line_blank() {
1879 check_metrics::<CppParser>(
1882 "void ConnectToUpdateServer() {
1883 int pool;
1884
1885 int updateServer = -42;
1886 bool isConnected = false;
1887 int currTry = 0;
1888 /* Number of IPC connection retries before giving up. */ const int numRetries = 10;
1889 const int numTries = 20; // Number of IPC connection tries before
1890 // giving up.
1891 }",
1892 "foo.cpp",
1893 |metric| {
1894 insta::assert_json_snapshot!(
1896 metric.loc,
1897 @r#"
1898 {
1899 "sloc": 10,
1900 "ploc": 8,
1901 "lloc": 6,
1902 "cloc": 3,
1903 "blank": 1,
1904 "dloc": 0,
1905 "sloc_average": 5.0,
1906 "ploc_average": 4.0,
1907 "lloc_average": 3.0,
1908 "cloc_average": 1.5,
1909 "blank_average": 0.5,
1910 "dloc_average": 0.0,
1911 "sloc_min": 10,
1912 "sloc_max": 10,
1913 "cloc_min": 3,
1914 "cloc_max": 3,
1915 "ploc_min": 8,
1916 "ploc_max": 8,
1917 "lloc_min": 6,
1918 "lloc_max": 6,
1919 "blank_min": 1,
1920 "blank_max": 1,
1921 "dloc_min": 0,
1922 "dloc_max": 0
1923 }
1924 "#
1925 );
1926 },
1927 );
1928 }
1929
1930 #[cfg(feature = "cpp")]
1931 #[test]
1932 fn cpp_code_line_end_block_blank() {
1933 check_metrics::<CppParser>(
1936 "void ConnectToUpdateServer() {
1937 int pool;
1938
1939 int updateServer = -42;
1940 bool isConnected = false;
1941 int currTry = 0;
1942 /* Number of IPC connection retries
1943 before
1944 giving up. */ const int numRetries = 10;
1945 const int numTries = 20; // Number of IPC connection tries before
1946 // giving up.
1947 }",
1948 "foo.cpp",
1949 |metric| {
1950 insta::assert_json_snapshot!(
1952 metric.loc,
1953 @r#"
1954 {
1955 "sloc": 12,
1956 "ploc": 8,
1957 "lloc": 6,
1958 "cloc": 5,
1959 "blank": 1,
1960 "dloc": 0,
1961 "sloc_average": 6.0,
1962 "ploc_average": 4.0,
1963 "lloc_average": 3.0,
1964 "cloc_average": 2.5,
1965 "blank_average": 0.5,
1966 "dloc_average": 0.0,
1967 "sloc_min": 12,
1968 "sloc_max": 12,
1969 "cloc_min": 5,
1970 "cloc_max": 5,
1971 "ploc_min": 8,
1972 "ploc_max": 8,
1973 "lloc_min": 6,
1974 "lloc_max": 6,
1975 "blank_min": 1,
1976 "blank_max": 1,
1977 "dloc_min": 0,
1978 "dloc_max": 0
1979 }
1980 "#
1981 );
1982 },
1983 );
1984 }
1985
1986 #[cfg(feature = "python")]
1987 #[test]
1988 fn python_cloc() {
1989 check_metrics::<PythonParser>(
1990 "\"\"\"Block comment
1991 Block comment
1992 \"\"\"
1993 # Line Comment
1994 a = 42 # Line Comment",
1995 "foo.py",
1996 |metric| {
1997 insta::assert_json_snapshot!(
1999 metric.loc,
2000 @r#"
2001 {
2002 "sloc": 5,
2003 "ploc": 1,
2004 "lloc": 2,
2005 "cloc": 5,
2006 "blank": 0,
2007 "dloc": 3,
2008 "sloc_average": 5.0,
2009 "ploc_average": 1.0,
2010 "lloc_average": 2.0,
2011 "cloc_average": 5.0,
2012 "blank_average": 0.0,
2013 "dloc_average": 3.0,
2014 "sloc_min": 5,
2015 "sloc_max": 5,
2016 "cloc_min": 5,
2017 "cloc_max": 5,
2018 "ploc_min": 1,
2019 "ploc_max": 1,
2020 "lloc_min": 2,
2021 "lloc_max": 2,
2022 "blank_min": 0,
2023 "blank_max": 0,
2024 "dloc_min": 3,
2025 "dloc_max": 3
2026 }
2027 "#
2028 );
2029 },
2030 );
2031 }
2032
2033 #[cfg(feature = "rust")]
2034 #[test]
2035 fn rust_cloc() {
2036 check_metrics::<RustParser>(
2037 "/*Block comment
2038 Block Comment*/
2039 //Line Comment
2040 /*Block Comment*/ let a = 42; // Line Comment",
2041 "foo.rs",
2042 |metric| {
2043 insta::assert_json_snapshot!(
2052 metric.loc,
2053 @r#"
2054 {
2055 "sloc": 4,
2056 "ploc": 1,
2057 "lloc": 1,
2058 "cloc": 4,
2059 "blank": 0,
2060 "dloc": 0,
2061 "sloc_average": 4.0,
2062 "ploc_average": 1.0,
2063 "lloc_average": 1.0,
2064 "cloc_average": 4.0,
2065 "blank_average": 0.0,
2066 "dloc_average": 0.0,
2067 "sloc_min": 4,
2068 "sloc_max": 4,
2069 "cloc_min": 4,
2070 "cloc_max": 4,
2071 "ploc_min": 1,
2072 "ploc_max": 1,
2073 "lloc_min": 1,
2074 "lloc_max": 1,
2075 "blank_min": 0,
2076 "blank_max": 0,
2077 "dloc_min": 0,
2078 "dloc_max": 0
2079 }
2080 "#
2081 );
2082 },
2083 );
2084 }
2085
2086 #[cfg(feature = "c")]
2087 #[test]
2088 fn c_cloc() {
2089 check_metrics::<CParser>(
2090 "/*Block comment
2091 Block Comment*/
2092 //Line Comment
2093 /*Block Comment*/ int a = 42; // Line Comment",
2094 "foo.c",
2095 |metric| {
2096 insta::assert_json_snapshot!(
2102 metric.loc,
2103 @r#"
2104 {
2105 "sloc": 4,
2106 "ploc": 1,
2107 "lloc": 1,
2108 "cloc": 4,
2109 "blank": 0,
2110 "dloc": 0,
2111 "sloc_average": 4.0,
2112 "ploc_average": 1.0,
2113 "lloc_average": 1.0,
2114 "cloc_average": 4.0,
2115 "blank_average": 0.0,
2116 "dloc_average": 0.0,
2117 "sloc_min": 4,
2118 "sloc_max": 4,
2119 "cloc_min": 4,
2120 "cloc_max": 4,
2121 "ploc_min": 1,
2122 "ploc_max": 1,
2123 "lloc_min": 1,
2124 "lloc_max": 1,
2125 "blank_min": 0,
2126 "blank_max": 0,
2127 "dloc_min": 0,
2128 "dloc_max": 0
2129 }
2130 "#
2131 );
2132 },
2133 );
2134 }
2135
2136 #[cfg(feature = "python")]
2137 #[test]
2138 fn python_lloc() {
2139 check_metrics::<PythonParser>(
2140 "for x in range(0,42):
2141 if x % 2 == 0:
2142 print(x)",
2143 "foo.py",
2144 |metric| {
2145 insta::assert_json_snapshot!(
2147 metric.loc,
2148 @r#"
2149 {
2150 "sloc": 3,
2151 "ploc": 3,
2152 "lloc": 3,
2153 "cloc": 0,
2154 "blank": 0,
2155 "dloc": 0,
2156 "sloc_average": 3.0,
2157 "ploc_average": 3.0,
2158 "lloc_average": 3.0,
2159 "cloc_average": 0.0,
2160 "blank_average": 0.0,
2161 "dloc_average": 0.0,
2162 "sloc_min": 3,
2163 "sloc_max": 3,
2164 "cloc_min": 0,
2165 "cloc_max": 0,
2166 "ploc_min": 3,
2167 "ploc_max": 3,
2168 "lloc_min": 3,
2169 "lloc_max": 3,
2170 "blank_min": 0,
2171 "blank_max": 0,
2172 "dloc_min": 0,
2173 "dloc_max": 0
2174 }
2175 "#
2176 );
2177 },
2178 );
2179 }
2180
2181 #[cfg(feature = "rust")]
2182 #[test]
2183 fn rust_lloc() {
2184 check_metrics::<RustParser>(
2185 "for x in 0..42 {
2186 if x % 2 == 0 {
2187 println!(\"{}\", x);
2188 }
2189 }",
2190 "foo.rs",
2191 |metric| {
2192 insta::assert_json_snapshot!(
2194 metric.loc,
2195 @r#"
2196 {
2197 "sloc": 5,
2198 "ploc": 5,
2199 "lloc": 3,
2200 "cloc": 0,
2201 "blank": 0,
2202 "dloc": 0,
2203 "sloc_average": 5.0,
2204 "ploc_average": 5.0,
2205 "lloc_average": 3.0,
2206 "cloc_average": 0.0,
2207 "blank_average": 0.0,
2208 "dloc_average": 0.0,
2209 "sloc_min": 5,
2210 "sloc_max": 5,
2211 "cloc_min": 0,
2212 "cloc_max": 0,
2213 "ploc_min": 5,
2214 "ploc_max": 5,
2215 "lloc_min": 3,
2216 "lloc_max": 3,
2217 "blank_min": 0,
2218 "blank_max": 0,
2219 "dloc_min": 0,
2220 "dloc_max": 0
2221 }
2222 "#
2223 );
2224 },
2225 );
2226
2227 check_metrics::<RustParser>(
2229 "let a = 42;
2230 if true {
2231 42
2232 } else {
2233 43
2234 };",
2235 "foo.rs",
2236 |metric| {
2237 insta::assert_json_snapshot!(
2239 metric.loc,
2240 @r#"
2241 {
2242 "sloc": 6,
2243 "ploc": 6,
2244 "lloc": 3,
2245 "cloc": 0,
2246 "blank": 0,
2247 "dloc": 0,
2248 "sloc_average": 6.0,
2249 "ploc_average": 6.0,
2250 "lloc_average": 3.0,
2251 "cloc_average": 0.0,
2252 "blank_average": 0.0,
2253 "dloc_average": 0.0,
2254 "sloc_min": 6,
2255 "sloc_max": 6,
2256 "cloc_min": 0,
2257 "cloc_max": 0,
2258 "ploc_min": 6,
2259 "ploc_max": 6,
2260 "lloc_min": 3,
2261 "lloc_max": 3,
2262 "blank_min": 0,
2263 "blank_max": 0,
2264 "dloc_min": 0,
2265 "dloc_max": 0
2266 }
2267 "#
2268 );
2269 },
2270 );
2271 }
2272
2273 #[cfg(feature = "c")]
2274 #[test]
2275 fn c_lloc() {
2276 check_metrics::<CParser>(
2277 "for (;;)
2278 break;",
2279 "foo.c",
2280 |metric| {
2281 insta::assert_json_snapshot!(
2283 metric.loc,
2284 @r#"
2285 {
2286 "sloc": 2,
2287 "ploc": 2,
2288 "lloc": 2,
2289 "cloc": 0,
2290 "blank": 0,
2291 "dloc": 0,
2292 "sloc_average": 2.0,
2293 "ploc_average": 2.0,
2294 "lloc_average": 2.0,
2295 "cloc_average": 0.0,
2296 "blank_average": 0.0,
2297 "dloc_average": 0.0,
2298 "sloc_min": 2,
2299 "sloc_max": 2,
2300 "cloc_min": 0,
2301 "cloc_max": 0,
2302 "ploc_min": 2,
2303 "ploc_max": 2,
2304 "lloc_min": 2,
2305 "lloc_max": 2,
2306 "blank_min": 0,
2307 "blank_max": 0,
2308 "dloc_min": 0,
2309 "dloc_max": 0
2310 }
2311 "#
2312 );
2313 },
2314 );
2315 }
2316
2317 #[cfg(feature = "cpp")]
2318 #[test]
2319 fn cpp_lloc() {
2320 check_metrics::<CppParser>(
2321 "nsTArray<xpcGCCallback> callbacks(extraGCCallbacks.Clone());
2322 for (uint32_t i = 0; i < callbacks.Length(); ++i) {
2323 callbacks[i](status);
2324 }",
2325 "foo.cpp",
2326 |metric| {
2327 insta::assert_json_snapshot!(
2330 metric.loc,
2331 @r#"
2332 {
2333 "sloc": 4,
2334 "ploc": 4,
2335 "lloc": 3,
2336 "cloc": 0,
2337 "blank": 0,
2338 "dloc": 0,
2339 "sloc_average": 4.0,
2340 "ploc_average": 4.0,
2341 "lloc_average": 3.0,
2342 "cloc_average": 0.0,
2343 "blank_average": 0.0,
2344 "dloc_average": 0.0,
2345 "sloc_min": 4,
2346 "sloc_max": 4,
2347 "cloc_min": 0,
2348 "cloc_max": 0,
2349 "ploc_min": 4,
2350 "ploc_max": 4,
2351 "lloc_min": 3,
2352 "lloc_max": 3,
2353 "blank_min": 0,
2354 "blank_max": 0,
2355 "dloc_min": 0,
2356 "dloc_max": 0
2357 }
2358 "#
2359 );
2360 },
2361 );
2362 }
2363
2364 #[cfg(feature = "cpp")]
2365 #[test]
2366 fn cpp_return_lloc() {
2367 check_metrics::<CppParser>(
2368 "uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
2369 return RgbaColor(pixel_data) == blank_pixel_;",
2370 "foo.cpp",
2371 |metric| {
2372 insta::assert_json_snapshot!(
2375 metric.loc,
2376 @r#"
2377 {
2378 "sloc": 2,
2379 "ploc": 2,
2380 "lloc": 2,
2381 "cloc": 0,
2382 "blank": 0,
2383 "dloc": 0,
2384 "sloc_average": 2.0,
2385 "ploc_average": 2.0,
2386 "lloc_average": 2.0,
2387 "cloc_average": 0.0,
2388 "blank_average": 0.0,
2389 "dloc_average": 0.0,
2390 "sloc_min": 2,
2391 "sloc_max": 2,
2392 "cloc_min": 0,
2393 "cloc_max": 0,
2394 "ploc_min": 2,
2395 "ploc_max": 2,
2396 "lloc_min": 2,
2397 "lloc_max": 2,
2398 "blank_min": 0,
2399 "blank_max": 0,
2400 "dloc_min": 0,
2401 "dloc_max": 0
2402 }
2403 "#
2404 );
2405 },
2406 );
2407 }
2408
2409 #[cfg(feature = "cpp")]
2410 #[test]
2411 fn cpp_for_lloc() {
2412 check_metrics::<CppParser>(
2413 "for (; start != end; ++start) {
2414 const unsigned char idx = *start;
2415 if (idx > 127 || !kValidTokenMap[idx]) return false;
2416 }",
2417 "foo.cpp",
2418 |metric| {
2419 insta::assert_json_snapshot!(
2422 metric.loc,
2423 @r#"
2424 {
2425 "sloc": 4,
2426 "ploc": 4,
2427 "lloc": 4,
2428 "cloc": 0,
2429 "blank": 0,
2430 "dloc": 0,
2431 "sloc_average": 4.0,
2432 "ploc_average": 4.0,
2433 "lloc_average": 4.0,
2434 "cloc_average": 0.0,
2435 "blank_average": 0.0,
2436 "dloc_average": 0.0,
2437 "sloc_min": 4,
2438 "sloc_max": 4,
2439 "cloc_min": 0,
2440 "cloc_max": 0,
2441 "ploc_min": 4,
2442 "ploc_max": 4,
2443 "lloc_min": 4,
2444 "lloc_max": 4,
2445 "blank_min": 0,
2446 "blank_max": 0,
2447 "dloc_min": 0,
2448 "dloc_max": 0
2449 }
2450 "#
2451 );
2452 },
2453 );
2454 }
2455
2456 #[cfg(feature = "cpp")]
2457 #[test]
2458 fn cpp_while_lloc() {
2459 check_metrics::<CppParser>(
2460 "while (sHeapAtoms) {
2461 HttpHeapAtom* next = sHeapAtoms->next;
2462 free(sHeapAtoms);
2463 }",
2464 "foo.cpp",
2465 |metric| {
2466 insta::assert_json_snapshot!(
2469 metric.loc,
2470 @r#"
2471 {
2472 "sloc": 4,
2473 "ploc": 4,
2474 "lloc": 3,
2475 "cloc": 0,
2476 "blank": 0,
2477 "dloc": 0,
2478 "sloc_average": 4.0,
2479 "ploc_average": 4.0,
2480 "lloc_average": 3.0,
2481 "cloc_average": 0.0,
2482 "blank_average": 0.0,
2483 "dloc_average": 0.0,
2484 "sloc_min": 4,
2485 "sloc_max": 4,
2486 "cloc_min": 0,
2487 "cloc_max": 0,
2488 "ploc_min": 4,
2489 "ploc_max": 4,
2490 "lloc_min": 3,
2491 "lloc_max": 3,
2492 "blank_min": 0,
2493 "blank_max": 0,
2494 "dloc_min": 0,
2495 "dloc_max": 0
2496 }
2497 "#
2498 );
2499 },
2500 );
2501 }
2502
2503 #[cfg(feature = "python")]
2504 #[test]
2505 fn python_string_on_new_line() {
2506 check_metrics::<PythonParser>(
2508 "capabilities[\"goog:chromeOptions\"][\"androidPackage\"] = \\
2509 \"org.chromium.weblayer.shell\"",
2510 "foo.py",
2511 |metric| {
2512 insta::assert_json_snapshot!(
2514 metric.loc,
2515 @r#"
2516 {
2517 "sloc": 2,
2518 "ploc": 2,
2519 "lloc": 1,
2520 "cloc": 0,
2521 "blank": 0,
2522 "dloc": 0,
2523 "sloc_average": 2.0,
2524 "ploc_average": 2.0,
2525 "lloc_average": 1.0,
2526 "cloc_average": 0.0,
2527 "blank_average": 0.0,
2528 "dloc_average": 0.0,
2529 "sloc_min": 2,
2530 "sloc_max": 2,
2531 "cloc_min": 0,
2532 "cloc_max": 0,
2533 "ploc_min": 2,
2534 "ploc_max": 2,
2535 "lloc_min": 1,
2536 "lloc_max": 1,
2537 "blank_min": 0,
2538 "blank_max": 0,
2539 "dloc_min": 0,
2540 "dloc_max": 0
2541 }
2542 "#
2543 );
2544 },
2545 );
2546 }
2547
2548 #[cfg(feature = "python")]
2549 #[test]
2550 fn python_multiline_string_assignment_ploc() {
2551 check_metrics::<PythonParser>(
2554 "QUERY = \"\"\"
2555SELECT id, name
2556FROM users
2557WHERE active = 1
2558ORDER BY name
2559\"\"\"",
2560 "foo.py",
2561 |metric| {
2562 assert_eq!(metric.loc.sloc(), 6);
2566 assert_eq!(metric.loc.ploc(), 6);
2567 assert_eq!(metric.loc.cloc(), 0);
2568 assert_eq!(metric.loc.dloc(), 0);
2569 assert_eq!(metric.loc.blank(), 0);
2570 insta::assert_json_snapshot!(
2571 metric.loc,
2572 @r#"
2573 {
2574 "sloc": 6,
2575 "ploc": 6,
2576 "lloc": 1,
2577 "cloc": 0,
2578 "blank": 0,
2579 "dloc": 0,
2580 "sloc_average": 6.0,
2581 "ploc_average": 6.0,
2582 "lloc_average": 1.0,
2583 "cloc_average": 0.0,
2584 "blank_average": 0.0,
2585 "dloc_average": 0.0,
2586 "sloc_min": 6,
2587 "sloc_max": 6,
2588 "cloc_min": 0,
2589 "cloc_max": 0,
2590 "ploc_min": 6,
2591 "ploc_max": 6,
2592 "lloc_min": 1,
2593 "lloc_max": 1,
2594 "blank_min": 0,
2595 "blank_max": 0,
2596 "dloc_min": 0,
2597 "dloc_max": 0
2598 }
2599 "#
2600 );
2601 },
2602 );
2603 }
2604
2605 #[cfg(feature = "python")]
2606 #[test]
2607 fn python_multiline_string_argument_ploc() {
2608 check_metrics::<PythonParser>(
2611 "print(\"\"\"
2612line one
2613line two
2614\"\"\")",
2615 "foo.py",
2616 |metric| {
2617 assert_eq!(metric.loc.sloc(), 4);
2619 assert_eq!(metric.loc.ploc(), 4);
2620 assert_eq!(metric.loc.cloc(), 0);
2621 assert_eq!(metric.loc.blank(), 0);
2622 },
2623 );
2624 }
2625
2626 #[cfg(feature = "python")]
2627 #[test]
2628 fn python_single_line_string_assignment_ploc() {
2629 check_metrics::<PythonParser>("QUERY = \"SELECT 1\"", "foo.py", |metric| {
2632 assert_eq!(metric.loc.sloc(), 1);
2634 assert_eq!(metric.loc.ploc(), 1);
2635 assert_eq!(metric.loc.cloc(), 0);
2636 assert_eq!(metric.loc.blank(), 0);
2637 });
2638 }
2639
2640 #[cfg(feature = "python")]
2641 #[test]
2642 fn python_multiline_docstring_still_cloc() {
2643 check_metrics::<PythonParser>(
2646 "def f():
2647 \"\"\"Docstring line one
2648 Docstring line two
2649 \"\"\"
2650 return 1",
2651 "foo.py",
2652 |metric| {
2653 assert_eq!(metric.loc.cloc(), 3);
2658 assert_eq!(metric.loc.dloc(), 3);
2659 assert_eq!(metric.loc.blank(), 0);
2660 },
2661 );
2662 }
2663
2664 #[cfg(feature = "python")]
2665 #[test]
2666 fn python_single_line_docstring_is_one_docstring_row() {
2667 check_metrics::<PythonParser>(
2670 "def f():\n \"\"\"One line.\"\"\"\n return 1",
2671 "foo.py",
2672 |metric| {
2673 assert_eq!(metric.loc.sloc(), 3);
2674 assert_eq!(metric.loc.ploc(), 2);
2675 assert_eq!(metric.loc.cloc(), 1);
2676 assert_eq!(metric.loc.dloc(), 1);
2677 assert_eq!(metric.loc.blank(), 0);
2678 },
2679 );
2680 }
2681
2682 #[cfg(feature = "python")]
2683 #[test]
2684 fn python_docstring_sharing_the_def_row_counts_that_row() {
2685 check_metrics::<PythonParser>("def f(): \"\"\"x\"\"\"", "foo.py", |metric| {
2690 assert_eq!(metric.loc.sloc(), 1);
2691 assert_eq!(metric.loc.ploc(), 1);
2692 assert_eq!(metric.loc.cloc(), 1);
2693 assert_eq!(metric.loc.dloc(), 1);
2694 assert_eq!(metric.loc.blank(), 0);
2695 });
2696 }
2697
2698 #[cfg(feature = "python")]
2699 #[test]
2700 fn python_docstring_is_a_subset_of_cloc() {
2701 check_metrics::<PythonParser>(
2707 "def f():\n \"\"\"Doc one.\n Doc two.\"\"\"\n # a comment\n return 1",
2708 "foo.py",
2709 |metric| {
2710 assert_eq!(metric.loc.sloc(), 5);
2711 assert_eq!(metric.loc.ploc(), 2);
2712 assert_eq!(metric.loc.cloc(), 3);
2713 assert_eq!(metric.loc.dloc(), 2);
2714 assert_eq!(metric.loc.blank(), 0);
2715 },
2716 );
2717 }
2718
2719 #[cfg(feature = "python")]
2720 #[test]
2721 fn python_bare_string_out_of_docstring_position_is_a_comment_not_documentation() {
2722 check_metrics::<PythonParser>(
2729 "def f():\n x = 1\n \"\"\"Not first, still bare.\"\"\"\n return x",
2730 "foo.py",
2731 |metric| {
2732 assert_eq!(metric.loc.sloc(), 4);
2733 assert_eq!(metric.loc.ploc(), 3);
2734 assert_eq!(metric.loc.cloc(), 1);
2735 assert_eq!(metric.loc.dloc(), 0);
2736 },
2737 );
2738 }
2739
2740 #[cfg(feature = "python")]
2744 #[test]
2745 fn python_documentation_positions_follow_pep_257() {
2746 let cases: &[(&str, &[u8], u64, u64)] = &[
2747 ("module docstring", b"\"\"\"M.\"\"\"\nx = 1\n", 1, 1),
2749 ("module docstring after a comment", b"# c\n\"\"\"M.\"\"\"\nx = 1\n", 2, 1),
2750 ("class docstring", b"class C:\n \"\"\"C.\"\"\"\n x = 1\n", 1, 1),
2751 ("function docstring", b"def f():\n \"\"\"F.\"\"\"\n return 1\n", 1, 1),
2752 ("additional docstring", b"def f():\n \"\"\"F.\"\"\"\n \"\"\"More.\"\"\"\n return 1\n", 2, 2),
2753 ("successive additional docstrings", b"def f():\n \"\"\"F.\"\"\"\n \"\"\"More.\"\"\"\n \"\"\"Still more.\"\"\"\n return 1\n", 3, 3),
2754 ("additional attribute docstring", b"x = 1\n\"\"\"X.\"\"\"\n\"\"\"More.\"\"\"\n", 2, 2),
2755 ("additional class attribute docstring", b"class C:\n x = 1\n \"\"\"X.\"\"\"\n # comment\n \"\"\"More.\"\"\"\n", 3, 2),
2756 ("successive strings after a call", b"print(1)\n\"\"\"No.\"\"\"\n\"\"\"Still no.\"\"\"\n", 2, 0),
2757 ("attribute docstring run after comments", b"x = 1\n# a\n\"\"\"X.\"\"\"\n# b\n\"\"\"More.\"\"\"\n", 4, 2),
2760 ("string run after comments after a call", b"print(1)\n# a\n\"\"\"No.\"\"\"\n# b\n\"\"\"Still no.\"\"\"\n", 4, 0),
2761 ("module attribute docstring", b"x = 1\n\"\"\"X.\"\"\"\n", 1, 1),
2762 ("annotated attribute docstring", b"x: int = 1\n\"\"\"X.\"\"\"\n", 1, 1),
2763 ("class attribute docstring", b"class C:\n x = 1\n \"\"\"X.\"\"\"\n", 1, 1),
2764 ("after an assignment in a function", b"def f():\n x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2765 ("after a call at module level", b"print(1)\n\"\"\"No.\"\"\"\n", 1, 0),
2766 ("after the imports", b"import os\n\"\"\"No.\"\"\"\n", 1, 0),
2767 ("inside an if body", b"if True:\n \"\"\"No.\"\"\"\n", 1, 0),
2768 ("concatenated docstring", b"def f():\n \"\"\"F.\"\"\" \"more\"\n return 1\n", 1, 1),
2769 ("attribute docstring in __init__", b"class C:\n def __init__(self):\n self.x = 1\n \"\"\"X.\"\"\"\n", 1, 1),
2773 ("additional attribute docstring in __init__", b"class C:\n def __init__(self):\n self.x = 1\n \"\"\"X.\"\"\"\n \"\"\"More.\"\"\"\n", 2, 2),
2774 ("annotated attribute in __init__", b"class C:\n def __init__(self):\n self.x: int = 1\n \"\"\"X.\"\"\"\n", 1, 1),
2775 ("decorated __init__", b"class C:\n @wraps\n def __init__(self):\n self.x = 1\n \"\"\"X.\"\"\"\n", 1, 1),
2776 ("receiver named otherwise", b"class C:\n def __init__(this):\n this.x = 1\n \"\"\"X.\"\"\"\n", 1, 1),
2777 ("local in __init__", b"class C:\n def __init__(self):\n x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2778 ("other object in __init__", b"class C:\n def __init__(self, o):\n o.x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2779 ("self attribute in another method", b"class C:\n def m(self):\n self.x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2780 ("__init__ outside a class", b"def __init__(self):\n self.x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2781 ("tuple target", b"a, b = 1, 2\n\"\"\"Pair.\"\"\"\n", 1, 1),
2785 ("subscript target", b"x = {}\nx[\"k\"] = 1\n\"\"\"No.\"\"\"\n", 1, 0),
2786 ("attribute target", b"import os\nos.sep = \"/\"\n\"\"\"No.\"\"\"\n", 1, 0),
2787 ("class-level attribute target", b"class C:\n Foo.x = 1\n \"\"\"No.\"\"\"\n", 1, 0),
2788 ("f-string docstring", b"def f():\n f\"\"\"F.\"\"\"\n return 1\n", 1, 0),
2790 ("bytes docstring", b"def f():\n b\"\"\"B.\"\"\"\n return 1\n", 1, 0),
2791 ("raw docstring", b"def f():\n r\"\"\"R.\"\"\"\n return 1\n", 1, 1),
2792 ("f-string fragment", b"def f():\n \"\"\"a\"\"\" f\"b\"\n return 1\n", 1, 0),
2793 ("string after an f-string", b"def f():\n f\"\"\"x\"\"\"\n \"\"\"No.\"\"\"\n return 1\n", 2, 0),
2794 ];
2795 for (name, source, cloc, dloc) in cases {
2796 let loc = metrics_verbatim(crate::LANG::Python, source, MetricsOptions::default()).loc;
2797 assert_eq!((loc.cloc(), loc.dloc()), (*cloc, *dloc), "Python: {name}");
2798 }
2799 }
2800
2801 #[cfg(feature = "python")]
2802 #[test]
2803 fn python_docstring_with_an_escape_sequence_is_not_a_code_row() {
2804 check_metrics::<PythonParser>(
2810 "def f():\n \"\"\"Has a \\n escape.\"\"\"\n return 1",
2811 "foo.py",
2812 |metric| {
2813 assert_eq!(metric.loc.sloc(), 3);
2814 assert_eq!(metric.loc.ploc(), 2);
2815 assert_eq!(metric.loc.cloc(), 1);
2816 assert_eq!(metric.loc.dloc(), 1);
2817 assert_eq!(metric.loc.dloc_without_code(), 1);
2818 },
2819 );
2820 }
2821
2822 #[cfg(feature = "python")]
2823 #[test]
2824 fn python_concatenated_docstring_counts_its_rows() {
2825 check_metrics::<PythonParser>(
2831 "def f():\n \"\"\"Doc one.\n Two.\"\"\" \"and more.\"\n return 1",
2832 "foo.py",
2833 |metric| {
2834 assert_eq!(metric.loc.sloc(), 4);
2835 assert_eq!(metric.loc.ploc(), 2);
2836 assert_eq!(metric.loc.cloc(), 2);
2837 assert_eq!(metric.loc.dloc(), 2);
2838 },
2839 );
2840 }
2841
2842 #[cfg(feature = "python")]
2843 #[test]
2844 fn python_concatenated_string_value_stays_code() {
2845 check_metrics::<PythonParser>(
2849 "def f():\n x = \"a\" \"b\"\n return x",
2850 "foo.py",
2851 |metric| {
2852 assert_eq!(metric.loc.sloc(), 3);
2853 assert_eq!(metric.loc.ploc(), 3);
2854 assert_eq!(metric.loc.cloc(), 0);
2855 assert_eq!(metric.loc.dloc(), 0);
2856 },
2857 );
2858 }
2859
2860 #[cfg(feature = "python")]
2861 #[test]
2862 fn python_bare_tuple_of_strings_is_not_a_docstring() {
2863 check_metrics::<PythonParser>(
2868 "def f():\n \"a\", \"b\"\n return 1",
2869 "foo.py",
2870 |metric| {
2871 assert_eq!(metric.loc.sloc(), 3);
2872 assert_eq!(metric.loc.ploc(), 3);
2873 assert_eq!(metric.loc.cloc(), 0);
2874 assert_eq!(metric.loc.dloc(), 0);
2875 },
2876 );
2877 check_metrics::<PythonParser>(
2881 "def f():\n \"\"\"first\n second\"\"\", None\n return 1",
2882 "foo.py",
2883 |metric| {
2884 assert_eq!(metric.loc.sloc(), 4);
2885 assert_eq!(metric.loc.ploc(), 4);
2886 assert_eq!(metric.loc.cloc(), 0);
2887 assert_eq!(metric.loc.blank(), 0);
2888 },
2889 );
2890 }
2891
2892 #[cfg(feature = "python")]
2897 #[test]
2898 fn python_parenthesized_docstring_counts_as_the_bare_one() {
2899 let cases: &[(&str, &[u8], [u64; 5])] = &[
2900 (
2902 "function docstring",
2903 b"def f():\n (\"\"\"F doc.\"\"\")\n return 1\n",
2904 [3, 2, 1, 1, 0],
2905 ),
2906 (
2907 "module docstring",
2908 b"(\"\"\"Module doc.\"\"\")\nx = 1\n",
2909 [2, 1, 1, 1, 0],
2910 ),
2911 (
2912 "nested parentheses",
2913 b"def f():\n ((\"doc\"))\n return 1\n",
2914 [3, 2, 1, 1, 0],
2915 ),
2916 (
2917 "concatenation",
2918 b"def f():\n (\"a\" \"b\")\n return 1\n",
2919 [3, 2, 1, 1, 0],
2920 ),
2921 (
2923 "tuple of strings",
2924 b"def f():\n (\"a\", \"b\")\n return 1\n",
2925 [3, 3, 0, 0, 0],
2926 ),
2927 (
2929 "tuple led by a parenthesized string",
2930 b"def f():\n (\n \"a\"\n ), \"b\"\n return 1\n",
2931 [5, 5, 0, 0, 0],
2932 ),
2933 (
2936 "two-row docstring",
2937 b"def f():\n (\"\"\"doc\n more\"\"\")\n return 1\n",
2938 [4, 2, 2, 2, 0],
2939 ),
2940 (
2943 "parentheses on their own rows",
2944 b"def f():\n (\n \"\"\"doc\n more\"\"\"\n )\n return 1\n",
2945 [6, 2, 4, 4, 0],
2946 ),
2947 (
2949 "comment inside the parentheses",
2950 b"def f():\n (\n # c\n \"\"\"doc\"\"\"\n )\n return 1\n",
2951 [6, 2, 4, 3, 0],
2952 ),
2953 (
2956 "parenthesized f-string",
2957 b"def f():\n (f\"x\")\n return 1\n",
2958 [3, 2, 1, 0, 0],
2959 ),
2960 (
2961 "parenthesized additional docstring",
2962 b"def f():\n \"\"\"F.\"\"\"\n (\"\"\"More.\"\"\")\n return 1\n",
2963 [4, 2, 2, 2, 0],
2964 ),
2965 (
2966 "attribute docstring",
2967 b"x = 1\n(\"\"\"X.\"\"\")\n",
2968 [2, 1, 1, 1, 0],
2969 ),
2970 (
2972 "parenthesized name",
2973 b"def f(x):\n (\n x\n )\n return 1\n",
2974 [5, 5, 0, 0, 0],
2975 ),
2976 (
2978 "parenthesized assigned string",
2979 b"x = (\"\"\"a\nb\"\"\")\n",
2980 [2, 2, 0, 0, 0],
2981 ),
2982 ];
2983 let row = |source: &[u8]| {
2984 let loc = metrics_verbatim(crate::LANG::Python, source, MetricsOptions::default()).loc;
2985 [loc.sloc(), loc.ploc(), loc.cloc(), loc.dloc(), loc.blank()]
2986 };
2987 for (name, source, expected) in cases {
2988 assert_eq!(row(source), *expected, "Python: {name}");
2989 }
2990 for (depth, expected) in [(200, [3, 2, 1, 1, 0]), (201, [3, 3, 0, 0, 0])] {
2993 let source = format!(
2994 "def f():\n {}\"doc\"{}\n return 1\n",
2995 "(".repeat(depth),
2996 ")".repeat(depth)
2997 );
2998 assert_eq!(
2999 row(source.as_bytes()),
3000 expected,
3001 "Python: {depth} parentheses"
3002 );
3003 }
3004 }
3005
3006 #[cfg(feature = "python")]
3007 #[test]
3008 fn python_docstring_sharing_a_code_row_is_not_without_code() {
3009 check_metrics::<PythonParser>("def f(): \"\"\"x\"\"\"", "foo.py", |metric| {
3015 assert_eq!((metric.loc.dloc(), metric.loc.dloc_without_code()), (1, 0));
3016 });
3017 check_metrics::<PythonParser>(
3018 "def f():\n \"\"\"Doc.\n More.\"\"\"; y = 1\n return y",
3019 "foo.py",
3020 |metric| {
3021 assert_eq!(metric.loc.ploc(), 3);
3022 assert_eq!((metric.loc.dloc(), metric.loc.dloc_without_code()), (2, 1));
3023 },
3024 );
3025 }
3026
3027 #[cfg(feature = "python")]
3031 #[test]
3032 fn python_docstring_min_max_propagate_through_nested_spaces() {
3033 check_metrics::<PythonParser>(
3040 "\"\"\"Module doc.\nMore.\"\"\"\nclass C:\n \"\"\"Class doc.\"\"\"\n def small(self):\n x = 1\n def big(self):\n \"\"\"Big doc.\n Second.\n Third.\"\"\"\n return 1\n",
3041 "c.py",
3042 |metric| {
3043 let loc = &metric.loc;
3044 assert_eq!(loc.dloc(), 6);
3045 assert_eq!(loc.cloc(), 6);
3046 assert_eq!(loc.dloc_min(), 0, "smallest leaf method has no docstring");
3047 assert_eq!(loc.dloc_max(), 6, "the unit's merged total is the maximum");
3048 assert_eq!(loc.dloc_average(), 1.5);
3049 },
3050 );
3051 }
3052
3053 #[cfg(feature = "rust")]
3054 #[test]
3055 fn rust_no_field_expression_lloc() {
3056 check_metrics::<RustParser>(
3057 "struct Foo {
3058 field: usize,
3059 }
3060 let foo = Foo { 42 };
3061 foo.field;",
3062 "foo.rs",
3063 |metric| {
3064 insta::assert_json_snapshot!(
3066 metric.loc,
3067 @r#"
3068 {
3069 "sloc": 5,
3070 "ploc": 5,
3071 "lloc": 2,
3072 "cloc": 0,
3073 "blank": 0,
3074 "dloc": 0,
3075 "sloc_average": 5.0,
3076 "ploc_average": 5.0,
3077 "lloc_average": 2.0,
3078 "cloc_average": 0.0,
3079 "blank_average": 0.0,
3080 "dloc_average": 0.0,
3081 "sloc_min": 5,
3082 "sloc_max": 5,
3083 "cloc_min": 0,
3084 "cloc_max": 0,
3085 "ploc_min": 5,
3086 "ploc_max": 5,
3087 "lloc_min": 2,
3088 "lloc_max": 2,
3089 "blank_min": 0,
3090 "blank_max": 0,
3091 "dloc_min": 0,
3092 "dloc_max": 0
3093 }
3094 "#
3095 );
3096 },
3097 );
3098 }
3099
3100 #[cfg(feature = "rust")]
3101 #[test]
3102 fn rust_no_parenthesized_expression_lloc() {
3103 check_metrics::<RustParser>("let a = (42 + 0);", "foo.rs", |metric| {
3104 insta::assert_json_snapshot!(
3106 metric.loc,
3107 @r#"
3108 {
3109 "sloc": 1,
3110 "ploc": 1,
3111 "lloc": 1,
3112 "cloc": 0,
3113 "blank": 0,
3114 "dloc": 0,
3115 "sloc_average": 1.0,
3116 "ploc_average": 1.0,
3117 "lloc_average": 1.0,
3118 "cloc_average": 0.0,
3119 "blank_average": 0.0,
3120 "dloc_average": 0.0,
3121 "sloc_min": 1,
3122 "sloc_max": 1,
3123 "cloc_min": 0,
3124 "cloc_max": 0,
3125 "ploc_min": 1,
3126 "ploc_max": 1,
3127 "lloc_min": 1,
3128 "lloc_max": 1,
3129 "blank_min": 0,
3130 "blank_max": 0,
3131 "dloc_min": 0,
3132 "dloc_max": 0
3133 }
3134 "#
3135 );
3136 });
3137 }
3138
3139 #[cfg(feature = "rust")]
3140 #[test]
3141 fn rust_no_array_expression_lloc() {
3142 check_metrics::<RustParser>("let a = [0; 42];", "foo.rs", |metric| {
3143 insta::assert_json_snapshot!(
3145 metric.loc,
3146 @r#"
3147 {
3148 "sloc": 1,
3149 "ploc": 1,
3150 "lloc": 1,
3151 "cloc": 0,
3152 "blank": 0,
3153 "dloc": 0,
3154 "sloc_average": 1.0,
3155 "ploc_average": 1.0,
3156 "lloc_average": 1.0,
3157 "cloc_average": 0.0,
3158 "blank_average": 0.0,
3159 "dloc_average": 0.0,
3160 "sloc_min": 1,
3161 "sloc_max": 1,
3162 "cloc_min": 0,
3163 "cloc_max": 0,
3164 "ploc_min": 1,
3165 "ploc_max": 1,
3166 "lloc_min": 1,
3167 "lloc_max": 1,
3168 "blank_min": 0,
3169 "blank_max": 0,
3170 "dloc_min": 0,
3171 "dloc_max": 0
3172 }
3173 "#
3174 );
3175 });
3176 }
3177
3178 #[cfg(feature = "rust")]
3179 #[test]
3180 fn rust_no_tuple_expression_lloc() {
3181 check_metrics::<RustParser>("let a = (0, 42);", "foo.rs", |metric| {
3182 insta::assert_json_snapshot!(
3184 metric.loc,
3185 @r#"
3186 {
3187 "sloc": 1,
3188 "ploc": 1,
3189 "lloc": 1,
3190 "cloc": 0,
3191 "blank": 0,
3192 "dloc": 0,
3193 "sloc_average": 1.0,
3194 "ploc_average": 1.0,
3195 "lloc_average": 1.0,
3196 "cloc_average": 0.0,
3197 "blank_average": 0.0,
3198 "dloc_average": 0.0,
3199 "sloc_min": 1,
3200 "sloc_max": 1,
3201 "cloc_min": 0,
3202 "cloc_max": 0,
3203 "ploc_min": 1,
3204 "ploc_max": 1,
3205 "lloc_min": 1,
3206 "lloc_max": 1,
3207 "blank_min": 0,
3208 "blank_max": 0,
3209 "dloc_min": 0,
3210 "dloc_max": 0
3211 }
3212 "#
3213 );
3214 });
3215 }
3216
3217 #[cfg(feature = "rust")]
3218 #[test]
3219 fn rust_no_unit_expression_lloc() {
3220 check_metrics::<RustParser>("let a = ();", "foo.rs", |metric| {
3221 insta::assert_json_snapshot!(
3223 metric.loc,
3224 @r#"
3225 {
3226 "sloc": 1,
3227 "ploc": 1,
3228 "lloc": 1,
3229 "cloc": 0,
3230 "blank": 0,
3231 "dloc": 0,
3232 "sloc_average": 1.0,
3233 "ploc_average": 1.0,
3234 "lloc_average": 1.0,
3235 "cloc_average": 0.0,
3236 "blank_average": 0.0,
3237 "dloc_average": 0.0,
3238 "sloc_min": 1,
3239 "sloc_max": 1,
3240 "cloc_min": 0,
3241 "cloc_max": 0,
3242 "ploc_min": 1,
3243 "ploc_max": 1,
3244 "lloc_min": 1,
3245 "lloc_max": 1,
3246 "blank_min": 0,
3247 "blank_max": 0,
3248 "dloc_min": 0,
3249 "dloc_max": 0
3250 }
3251 "#
3252 );
3253 });
3254 }
3255
3256 #[cfg(feature = "rust")]
3257 #[test]
3258 fn rust_call_function_lloc() {
3259 check_metrics::<RustParser>(
3260 "let a = foo(); // +1
3261 foo(); // +1
3262 k!(foo()); // +1",
3263 "foo.rs",
3264 |metric| {
3265 insta::assert_json_snapshot!(
3267 metric.loc,
3268 @r#"
3269 {
3270 "sloc": 3,
3271 "ploc": 3,
3272 "lloc": 3,
3273 "cloc": 3,
3274 "blank": 0,
3275 "dloc": 0,
3276 "sloc_average": 3.0,
3277 "ploc_average": 3.0,
3278 "lloc_average": 3.0,
3279 "cloc_average": 3.0,
3280 "blank_average": 0.0,
3281 "dloc_average": 0.0,
3282 "sloc_min": 3,
3283 "sloc_max": 3,
3284 "cloc_min": 3,
3285 "cloc_max": 3,
3286 "ploc_min": 3,
3287 "ploc_max": 3,
3288 "lloc_min": 3,
3289 "lloc_max": 3,
3290 "blank_min": 0,
3291 "blank_max": 0,
3292 "dloc_min": 0,
3293 "dloc_max": 0
3294 }
3295 "#
3296 );
3297 },
3298 );
3299 }
3300
3301 #[cfg(feature = "rust")]
3302 #[test]
3303 fn rust_macro_invocation_lloc() {
3304 check_metrics::<RustParser>(
3305 "let a = foo!(); // +1
3306 foo!(); // +1
3307 k(foo!()); // +1",
3308 "foo.rs",
3309 |metric| {
3310 insta::assert_json_snapshot!(
3312 metric.loc,
3313 @r#"
3314 {
3315 "sloc": 3,
3316 "ploc": 3,
3317 "lloc": 3,
3318 "cloc": 3,
3319 "blank": 0,
3320 "dloc": 0,
3321 "sloc_average": 3.0,
3322 "ploc_average": 3.0,
3323 "lloc_average": 3.0,
3324 "cloc_average": 3.0,
3325 "blank_average": 0.0,
3326 "dloc_average": 0.0,
3327 "sloc_min": 3,
3328 "sloc_max": 3,
3329 "cloc_min": 3,
3330 "cloc_max": 3,
3331 "ploc_min": 3,
3332 "ploc_max": 3,
3333 "lloc_min": 3,
3334 "lloc_max": 3,
3335 "blank_min": 0,
3336 "blank_max": 0,
3337 "dloc_min": 0,
3338 "dloc_max": 0
3339 }
3340 "#
3341 );
3342 },
3343 );
3344 }
3345
3346 #[cfg(feature = "rust")]
3347 #[test]
3348 fn rust_function_in_loop_lloc() {
3349 check_metrics::<RustParser>(
3350 "for (a, b) in c.iter().enumerate() {} // +1
3351 while (a, b) in c.iter().enumerate() {} // +1
3352 while let Some(a) = c.strip_prefix(\"hi\") {} // +1",
3353 "foo.rs",
3354 |metric| {
3355 insta::assert_json_snapshot!(
3357 metric.loc,
3358 @r#"
3359 {
3360 "sloc": 3,
3361 "ploc": 3,
3362 "lloc": 3,
3363 "cloc": 3,
3364 "blank": 0,
3365 "dloc": 0,
3366 "sloc_average": 3.0,
3367 "ploc_average": 3.0,
3368 "lloc_average": 3.0,
3369 "cloc_average": 3.0,
3370 "blank_average": 0.0,
3371 "dloc_average": 0.0,
3372 "sloc_min": 3,
3373 "sloc_max": 3,
3374 "cloc_min": 3,
3375 "cloc_max": 3,
3376 "ploc_min": 3,
3377 "ploc_max": 3,
3378 "lloc_min": 3,
3379 "lloc_max": 3,
3380 "blank_min": 0,
3381 "blank_max": 0,
3382 "dloc_min": 0,
3383 "dloc_max": 0
3384 }
3385 "#
3386 );
3387 },
3388 );
3389 }
3390
3391 #[cfg(feature = "rust")]
3392 #[test]
3393 fn rust_function_in_if_lloc() {
3394 check_metrics::<RustParser>(
3395 "if foo() {} // +1
3396 if let Some(a) = foo() {} // +1",
3397 "foo.rs",
3398 |metric| {
3399 insta::assert_json_snapshot!(
3401 metric.loc,
3402 @r#"
3403 {
3404 "sloc": 2,
3405 "ploc": 2,
3406 "lloc": 2,
3407 "cloc": 2,
3408 "blank": 0,
3409 "dloc": 0,
3410 "sloc_average": 2.0,
3411 "ploc_average": 2.0,
3412 "lloc_average": 2.0,
3413 "cloc_average": 2.0,
3414 "blank_average": 0.0,
3415 "dloc_average": 0.0,
3416 "sloc_min": 2,
3417 "sloc_max": 2,
3418 "cloc_min": 2,
3419 "cloc_max": 2,
3420 "ploc_min": 2,
3421 "ploc_max": 2,
3422 "lloc_min": 2,
3423 "lloc_max": 2,
3424 "blank_min": 0,
3425 "blank_max": 0,
3426 "dloc_min": 0,
3427 "dloc_max": 0
3428 }
3429 "#
3430 );
3431 },
3432 );
3433 }
3434
3435 #[cfg(feature = "rust")]
3436 #[test]
3437 fn rust_function_in_return_lloc() {
3438 check_metrics::<RustParser>(
3439 "return foo();
3440 await foo();",
3441 "foo.rs",
3442 |metric| {
3443 insta::assert_json_snapshot!(
3445 metric.loc,
3446 @r#"
3447 {
3448 "sloc": 2,
3449 "ploc": 2,
3450 "lloc": 2,
3451 "cloc": 0,
3452 "blank": 0,
3453 "dloc": 0,
3454 "sloc_average": 2.0,
3455 "ploc_average": 2.0,
3456 "lloc_average": 2.0,
3457 "cloc_average": 0.0,
3458 "blank_average": 0.0,
3459 "dloc_average": 0.0,
3460 "sloc_min": 2,
3461 "sloc_max": 2,
3462 "cloc_min": 0,
3463 "cloc_max": 0,
3464 "ploc_min": 2,
3465 "ploc_max": 2,
3466 "lloc_min": 2,
3467 "lloc_max": 2,
3468 "blank_min": 0,
3469 "blank_max": 0,
3470 "dloc_min": 0,
3471 "dloc_max": 0
3472 }
3473 "#
3474 );
3475 },
3476 );
3477 }
3478
3479 #[cfg(feature = "rust")]
3480 #[test]
3481 fn rust_closure_expression_lloc() {
3482 check_metrics::<RustParser>(
3483 "let a = |i: i32| -> i32 { i + 1 }; // +1
3484 a(42); // +1
3485 k(b.iter().map(|n| n.parse.ok().unwrap_or(42))); // +1",
3486 "foo.rs",
3487 |metric| {
3488 insta::assert_json_snapshot!(
3490 metric.loc,
3491 @r#"
3492 {
3493 "sloc": 3,
3494 "ploc": 3,
3495 "lloc": 3,
3496 "cloc": 3,
3497 "blank": 0,
3498 "dloc": 0,
3499 "sloc_average": 1.0,
3500 "ploc_average": 1.0,
3501 "lloc_average": 1.0,
3502 "cloc_average": 1.0,
3503 "blank_average": 0.0,
3504 "dloc_average": 0.0,
3505 "sloc_min": 1,
3506 "sloc_max": 3,
3507 "cloc_min": 0,
3508 "cloc_max": 3,
3509 "ploc_min": 1,
3510 "ploc_max": 3,
3511 "lloc_min": 0,
3512 "lloc_max": 3,
3513 "blank_min": 0,
3514 "blank_max": 0,
3515 "dloc_min": 0,
3516 "dloc_max": 0
3517 }
3518 "#
3519 );
3520 },
3521 );
3522 }
3523
3524 #[cfg(feature = "python")]
3525 #[test]
3526 fn python_general_loc() {
3527 check_metrics::<PythonParser>(
3528 "def func(a,
3529 b,
3530 c):
3531 print(a)
3532 print(b)
3533 print(c)",
3534 "foo.py",
3535 |metric| {
3536 insta::assert_json_snapshot!(
3538 metric.loc,
3539 @r#"
3540 {
3541 "sloc": 6,
3542 "ploc": 6,
3543 "lloc": 3,
3544 "cloc": 0,
3545 "blank": 0,
3546 "dloc": 0,
3547 "sloc_average": 3.0,
3548 "ploc_average": 3.0,
3549 "lloc_average": 1.5,
3550 "cloc_average": 0.0,
3551 "blank_average": 0.0,
3552 "dloc_average": 0.0,
3553 "sloc_min": 6,
3554 "sloc_max": 6,
3555 "cloc_min": 0,
3556 "cloc_max": 0,
3557 "ploc_min": 6,
3558 "ploc_max": 6,
3559 "lloc_min": 3,
3560 "lloc_max": 3,
3561 "blank_min": 0,
3562 "blank_max": 0,
3563 "dloc_min": 0,
3564 "dloc_max": 0
3565 }
3566 "#
3567 );
3568 },
3569 );
3570 }
3571
3572 #[cfg(feature = "python")]
3573 #[test]
3574 fn python_real_loc() {
3575 check_metrics::<PythonParser>(
3576 "def web_socket_transfer_data(request):
3577 while True:
3578 line = request.ws_stream.receive_message()
3579 if line is None:
3580 return
3581 code, reason = line.split(' ', 1)
3582 if code is None or reason is None:
3583 return
3584 request.ws_stream.close_connection(int(code), reason)
3585 # close_connection() initiates closing handshake. It validates code
3586 # and reason. If you want to send a broken close frame for a test,
3587 # following code will be useful.
3588 # > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
3589 # > request.connection.write(stream.create_close_frame(data))
3590 # > # Suppress to re-respond client responding close frame.
3591 # > raise Exception(\"customized server initiated closing handshake\")",
3592 "foo.py",
3593 |metric| {
3594 insta::assert_json_snapshot!(
3596 metric.loc,
3597 @r#"
3598 {
3599 "sloc": 16,
3600 "ploc": 9,
3601 "lloc": 8,
3602 "cloc": 7,
3603 "blank": 0,
3604 "dloc": 0,
3605 "sloc_average": 8.0,
3606 "ploc_average": 4.5,
3607 "lloc_average": 4.0,
3608 "cloc_average": 3.5,
3609 "blank_average": 0.0,
3610 "dloc_average": 0.0,
3611 "sloc_min": 16,
3612 "sloc_max": 16,
3613 "cloc_min": 7,
3614 "cloc_max": 7,
3615 "ploc_min": 9,
3616 "ploc_max": 9,
3617 "lloc_min": 8,
3618 "lloc_max": 8,
3619 "blank_min": 0,
3620 "blank_max": 0,
3621 "dloc_min": 0,
3622 "dloc_max": 0
3623 }
3624 "#
3625 );
3626 },
3627 );
3628 }
3629
3630 #[cfg(feature = "javascript")]
3631 #[test]
3632 fn javascript_real_loc() {
3633 check_metrics::<JavascriptParser>(
3634 "assert.throws(Test262Error, function() {
3635 for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
3636 return;
3637 }
3638 });",
3639 "foo.js",
3640 |metric| {
3641 insta::assert_json_snapshot!(
3643 metric.loc,
3644 @r#"
3645 {
3646 "sloc": 5,
3647 "ploc": 5,
3648 "lloc": 4,
3649 "cloc": 0,
3650 "blank": 0,
3651 "dloc": 0,
3652 "sloc_average": 2.5,
3653 "ploc_average": 2.5,
3654 "lloc_average": 2.0,
3655 "cloc_average": 0.0,
3656 "blank_average": 0.0,
3657 "dloc_average": 0.0,
3658 "sloc_min": 5,
3659 "sloc_max": 5,
3660 "cloc_min": 0,
3661 "cloc_max": 0,
3662 "ploc_min": 5,
3663 "ploc_max": 5,
3664 "lloc_min": 3,
3665 "lloc_max": 4,
3666 "blank_min": 0,
3667 "blank_max": 0,
3668 "dloc_min": 0,
3669 "dloc_max": 0
3670 }
3671 "#
3672 );
3673 },
3674 );
3675 }
3676
3677 #[cfg(feature = "mozjs")]
3678 #[test]
3679 fn mozjs_real_loc() {
3680 check_metrics::<MozjsParser>(
3681 "assert.throws(Test262Error, function() {
3682 for (let { poisoned: x = ++initEvalCount } = poisonedProperty; ; ) {
3683 return;
3684 }
3685 });",
3686 "foo.js",
3687 |metric| {
3688 insta::assert_json_snapshot!(
3690 metric.loc,
3691 @r#"
3692 {
3693 "sloc": 5,
3694 "ploc": 5,
3695 "lloc": 4,
3696 "cloc": 0,
3697 "blank": 0,
3698 "dloc": 0,
3699 "sloc_average": 2.5,
3700 "ploc_average": 2.5,
3701 "lloc_average": 2.0,
3702 "cloc_average": 0.0,
3703 "blank_average": 0.0,
3704 "dloc_average": 0.0,
3705 "sloc_min": 5,
3706 "sloc_max": 5,
3707 "cloc_min": 0,
3708 "cloc_max": 0,
3709 "ploc_min": 5,
3710 "ploc_max": 5,
3711 "lloc_min": 3,
3712 "lloc_max": 4,
3713 "blank_min": 0,
3714 "blank_max": 0,
3715 "dloc_min": 0,
3716 "dloc_max": 0
3717 }
3718 "#
3719 );
3720 },
3721 );
3722 }
3723
3724 #[cfg(feature = "mozjs")]
3725 #[test]
3726 fn mozjs_blank_and_comment_loc() {
3727 check_metrics::<MozjsParser>(
3728 "// a comment
3729 function f() {
3730
3731 var x = 1;
3732
3733 }",
3734 "foo.js",
3735 |metric| {
3736 insta::assert_json_snapshot!(
3737 metric.loc,
3738 @r#"
3739 {
3740 "sloc": 6,
3741 "ploc": 3,
3742 "lloc": 1,
3743 "cloc": 1,
3744 "blank": 2,
3745 "dloc": 0,
3746 "sloc_average": 3.0,
3747 "ploc_average": 1.5,
3748 "lloc_average": 0.5,
3749 "cloc_average": 0.5,
3750 "blank_average": 1.0,
3751 "dloc_average": 0.0,
3752 "sloc_min": 5,
3753 "sloc_max": 6,
3754 "cloc_min": 0,
3755 "cloc_max": 1,
3756 "ploc_min": 3,
3757 "ploc_max": 3,
3758 "lloc_min": 1,
3759 "lloc_max": 1,
3760 "blank_min": 2,
3761 "blank_max": 2,
3762 "dloc_min": 0,
3763 "dloc_max": 0
3764 }
3765 "#
3766 );
3767 },
3768 );
3769 }
3770
3771 #[cfg(feature = "cpp")]
3772 #[test]
3773 fn cpp_namespace_loc() {
3774 check_metrics::<CppParser>(
3775 "namespace mozilla::dom::quota {} // namespace mozilla::dom::quota",
3776 "foo.cpp",
3777 |metric| {
3778 insta::assert_json_snapshot!(
3780 metric.loc,
3781 @r#"
3782 {
3783 "sloc": 1,
3784 "ploc": 1,
3785 "lloc": 0,
3786 "cloc": 1,
3787 "blank": 0,
3788 "dloc": 0,
3789 "sloc_average": 0.5,
3790 "ploc_average": 0.5,
3791 "lloc_average": 0.0,
3792 "cloc_average": 0.5,
3793 "blank_average": 0.0,
3794 "dloc_average": 0.0,
3795 "sloc_min": 1,
3796 "sloc_max": 1,
3797 "cloc_min": 0,
3798 "cloc_max": 1,
3799 "ploc_min": 1,
3800 "ploc_max": 1,
3801 "lloc_min": 0,
3802 "lloc_max": 0,
3803 "blank_min": 0,
3804 "blank_max": 0,
3805 "dloc_min": 0,
3806 "dloc_max": 0
3807 }
3808 "#
3809 );
3810 },
3811 );
3812 }
3813
3814 #[cfg(feature = "java")]
3815 #[test]
3816 fn java_comments() {
3817 check_metrics::<JavaParser>(
3818 "for (int i = 0; i < 100; i++) { \
3819 // Print hello
3820 System.out.println(\"hello\"); \
3821 // Print world
3822 System.out.println(\"hello\"); \
3823 }",
3824 "foo.java",
3825 |metric| {
3826 insta::assert_json_snapshot!(
3828 metric.loc,
3829 @r#"
3830 {
3831 "sloc": 3,
3832 "ploc": 3,
3833 "lloc": 3,
3834 "cloc": 2,
3835 "blank": 0,
3836 "dloc": 0,
3837 "sloc_average": 3.0,
3838 "ploc_average": 3.0,
3839 "lloc_average": 3.0,
3840 "cloc_average": 2.0,
3841 "blank_average": 0.0,
3842 "dloc_average": 0.0,
3843 "sloc_min": 3,
3844 "sloc_max": 3,
3845 "cloc_min": 2,
3846 "cloc_max": 2,
3847 "ploc_min": 3,
3848 "ploc_max": 3,
3849 "lloc_min": 3,
3850 "lloc_max": 3,
3851 "blank_min": 0,
3852 "blank_max": 0,
3853 "dloc_min": 0,
3854 "dloc_max": 0
3855 }
3856 "#
3857 );
3858 },
3859 );
3860 }
3861
3862 #[cfg(feature = "java")]
3863 #[test]
3864 fn java_blank() {
3865 check_metrics::<JavaParser>(
3866 "int x = 1;
3867
3868
3869 int y = 2;",
3870 "foo.java",
3871 |metric| {
3872 insta::assert_json_snapshot!(
3874 metric.loc,
3875 @r#"
3876 {
3877 "sloc": 4,
3878 "ploc": 2,
3879 "lloc": 2,
3880 "cloc": 0,
3881 "blank": 2,
3882 "dloc": 0,
3883 "sloc_average": 4.0,
3884 "ploc_average": 2.0,
3885 "lloc_average": 2.0,
3886 "cloc_average": 0.0,
3887 "blank_average": 2.0,
3888 "dloc_average": 0.0,
3889 "sloc_min": 4,
3890 "sloc_max": 4,
3891 "cloc_min": 0,
3892 "cloc_max": 0,
3893 "ploc_min": 2,
3894 "ploc_max": 2,
3895 "lloc_min": 2,
3896 "lloc_max": 2,
3897 "blank_min": 2,
3898 "blank_max": 2,
3899 "dloc_min": 0,
3900 "dloc_max": 0
3901 }
3902 "#
3903 );
3904 },
3905 );
3906 }
3907
3908 #[cfg(feature = "java")]
3909 #[test]
3910 fn java_sloc() {
3911 check_metrics::<JavaParser>(
3912 "for (int i = 0; i < 100; i++) {
3913 System.out.println(i);
3914 }",
3915 "foo.java",
3916 |metric| {
3917 insta::assert_json_snapshot!(
3919 metric.loc,
3920 @r#"
3921 {
3922 "sloc": 3,
3923 "ploc": 3,
3924 "lloc": 2,
3925 "cloc": 0,
3926 "blank": 0,
3927 "dloc": 0,
3928 "sloc_average": 3.0,
3929 "ploc_average": 3.0,
3930 "lloc_average": 2.0,
3931 "cloc_average": 0.0,
3932 "blank_average": 0.0,
3933 "dloc_average": 0.0,
3934 "sloc_min": 3,
3935 "sloc_max": 3,
3936 "cloc_min": 0,
3937 "cloc_max": 0,
3938 "ploc_min": 3,
3939 "ploc_max": 3,
3940 "lloc_min": 2,
3941 "lloc_max": 2,
3942 "blank_min": 0,
3943 "blank_max": 0,
3944 "dloc_min": 0,
3945 "dloc_max": 0
3946 }
3947 "#
3948 );
3949 },
3950 );
3951 }
3952
3953 #[cfg(feature = "java")]
3954 #[test]
3955 fn java_module_sloc() {
3956 check_metrics::<JavaParser>(
3957 "module helloworld{
3958 exports com.test;
3959 }",
3960 "foo.java",
3961 |metric| {
3962 insta::assert_json_snapshot!(
3964 metric.loc,
3965 @r#"
3966 {
3967 "sloc": 3,
3968 "ploc": 3,
3969 "lloc": 0,
3970 "cloc": 0,
3971 "blank": 0,
3972 "dloc": 0,
3973 "sloc_average": 3.0,
3974 "ploc_average": 3.0,
3975 "lloc_average": 0.0,
3976 "cloc_average": 0.0,
3977 "blank_average": 0.0,
3978 "dloc_average": 0.0,
3979 "sloc_min": 3,
3980 "sloc_max": 3,
3981 "cloc_min": 0,
3982 "cloc_max": 0,
3983 "ploc_min": 3,
3984 "ploc_max": 3,
3985 "lloc_min": 0,
3986 "lloc_max": 0,
3987 "blank_min": 0,
3988 "blank_max": 0,
3989 "dloc_min": 0,
3990 "dloc_max": 0
3991 }
3992 "#
3993 );
3994 },
3995 );
3996 }
3997
3998 #[cfg(feature = "java")]
3999 #[test]
4000 fn java_single_ploc() {
4001 check_metrics::<JavaParser>("int x = 1;", "foo.java", |metric| {
4002 insta::assert_json_snapshot!(
4004 metric.loc,
4005 @r#"
4006 {
4007 "sloc": 1,
4008 "ploc": 1,
4009 "lloc": 1,
4010 "cloc": 0,
4011 "blank": 0,
4012 "dloc": 0,
4013 "sloc_average": 1.0,
4014 "ploc_average": 1.0,
4015 "lloc_average": 1.0,
4016 "cloc_average": 0.0,
4017 "blank_average": 0.0,
4018 "dloc_average": 0.0,
4019 "sloc_min": 1,
4020 "sloc_max": 1,
4021 "cloc_min": 0,
4022 "cloc_max": 0,
4023 "ploc_min": 1,
4024 "ploc_max": 1,
4025 "lloc_min": 1,
4026 "lloc_max": 1,
4027 "blank_min": 0,
4028 "blank_max": 0,
4029 "dloc_min": 0,
4030 "dloc_max": 0
4031 }
4032 "#
4033 );
4034 });
4035 }
4036
4037 #[cfg(feature = "java")]
4038 #[test]
4039 fn java_simple_ploc() {
4040 check_metrics::<JavaParser>(
4041 "for (int i = 0; i < 100; i = i++) {
4042 System.out.println(i);
4043 }",
4044 "foo.java",
4045 |metric| {
4046 insta::assert_json_snapshot!(
4048 metric.loc,
4049 @r#"
4050 {
4051 "sloc": 3,
4052 "ploc": 3,
4053 "lloc": 2,
4054 "cloc": 0,
4055 "blank": 0,
4056 "dloc": 0,
4057 "sloc_average": 3.0,
4058 "ploc_average": 3.0,
4059 "lloc_average": 2.0,
4060 "cloc_average": 0.0,
4061 "blank_average": 0.0,
4062 "dloc_average": 0.0,
4063 "sloc_min": 3,
4064 "sloc_max": 3,
4065 "cloc_min": 0,
4066 "cloc_max": 0,
4067 "ploc_min": 3,
4068 "ploc_max": 3,
4069 "lloc_min": 2,
4070 "lloc_max": 2,
4071 "blank_min": 0,
4072 "blank_max": 0,
4073 "dloc_min": 0,
4074 "dloc_max": 0
4075 }
4076 "#
4077 );
4078 },
4079 );
4080 }
4081
4082 #[cfg(feature = "java")]
4083 #[test]
4084 fn java_multi_ploc() {
4085 check_metrics::<JavaParser>(
4086 "int x = 1;
4087 for (int i = 0; i < 100; i++) {
4088 System.out.println(i);
4089 }",
4090 "foo.java",
4091 |metric| {
4092 insta::assert_json_snapshot!(
4094 metric.loc,
4095 @r#"
4096 {
4097 "sloc": 4,
4098 "ploc": 4,
4099 "lloc": 3,
4100 "cloc": 0,
4101 "blank": 0,
4102 "dloc": 0,
4103 "sloc_average": 4.0,
4104 "ploc_average": 4.0,
4105 "lloc_average": 3.0,
4106 "cloc_average": 0.0,
4107 "blank_average": 0.0,
4108 "dloc_average": 0.0,
4109 "sloc_min": 4,
4110 "sloc_max": 4,
4111 "cloc_min": 0,
4112 "cloc_max": 0,
4113 "ploc_min": 4,
4114 "ploc_max": 4,
4115 "lloc_min": 3,
4116 "lloc_max": 3,
4117 "blank_min": 0,
4118 "blank_max": 0,
4119 "dloc_min": 0,
4120 "dloc_max": 0
4121 }
4122 "#
4123 );
4124 },
4125 );
4126 }
4127
4128 #[cfg(feature = "java")]
4129 #[test]
4130 fn java_single_statement_lloc() {
4131 check_metrics::<JavaParser>("int max = 10;", "foo.java", |metric| {
4132 insta::assert_json_snapshot!(
4134 metric.loc,
4135 @r#"
4136 {
4137 "sloc": 1,
4138 "ploc": 1,
4139 "lloc": 1,
4140 "cloc": 0,
4141 "blank": 0,
4142 "dloc": 0,
4143 "sloc_average": 1.0,
4144 "ploc_average": 1.0,
4145 "lloc_average": 1.0,
4146 "cloc_average": 0.0,
4147 "blank_average": 0.0,
4148 "dloc_average": 0.0,
4149 "sloc_min": 1,
4150 "sloc_max": 1,
4151 "cloc_min": 0,
4152 "cloc_max": 0,
4153 "ploc_min": 1,
4154 "ploc_max": 1,
4155 "lloc_min": 1,
4156 "lloc_max": 1,
4157 "blank_min": 0,
4158 "blank_max": 0,
4159 "dloc_min": 0,
4160 "dloc_max": 0
4161 }
4162 "#
4163 );
4164 });
4165 }
4166
4167 #[cfg(feature = "java")]
4168 #[test]
4169 fn java_for_lloc() {
4170 check_metrics::<JavaParser>(
4171 "for (int i = 0; i < 100; i++) { // + 1
4172 System.out.println(i); // + 1
4173 }",
4174 "foo.java",
4175 |metric| {
4176 insta::assert_json_snapshot!(
4178 metric.loc,
4179 @r#"
4180 {
4181 "sloc": 3,
4182 "ploc": 3,
4183 "lloc": 2,
4184 "cloc": 2,
4185 "blank": 0,
4186 "dloc": 0,
4187 "sloc_average": 3.0,
4188 "ploc_average": 3.0,
4189 "lloc_average": 2.0,
4190 "cloc_average": 2.0,
4191 "blank_average": 0.0,
4192 "dloc_average": 0.0,
4193 "sloc_min": 3,
4194 "sloc_max": 3,
4195 "cloc_min": 2,
4196 "cloc_max": 2,
4197 "ploc_min": 3,
4198 "ploc_max": 3,
4199 "lloc_min": 2,
4200 "lloc_max": 2,
4201 "blank_min": 0,
4202 "blank_max": 0,
4203 "dloc_min": 0,
4204 "dloc_max": 0
4205 }
4206 "#
4207 );
4208 },
4209 );
4210 }
4211
4212 #[cfg(feature = "java")]
4213 #[test]
4214 fn java_foreach_lloc() {
4215 check_metrics::<JavaParser>(
4216 "
4217 int arr[]={12,13,14,44}; // +1
4218 for (int i:arr) { // +1
4219 System.out.println(i); // +1
4220 }",
4221 "foo.java",
4222 |metric| {
4223 insta::assert_json_snapshot!(
4225 metric.loc,
4226 @r#"
4227 {
4228 "sloc": 4,
4229 "ploc": 4,
4230 "lloc": 3,
4231 "cloc": 3,
4232 "blank": 0,
4233 "dloc": 0,
4234 "sloc_average": 4.0,
4235 "ploc_average": 4.0,
4236 "lloc_average": 3.0,
4237 "cloc_average": 3.0,
4238 "blank_average": 0.0,
4239 "dloc_average": 0.0,
4240 "sloc_min": 4,
4241 "sloc_max": 4,
4242 "cloc_min": 3,
4243 "cloc_max": 3,
4244 "ploc_min": 4,
4245 "ploc_max": 4,
4246 "lloc_min": 3,
4247 "lloc_max": 3,
4248 "blank_min": 0,
4249 "blank_max": 0,
4250 "dloc_min": 0,
4251 "dloc_max": 0
4252 }
4253 "#
4254 );
4255 },
4256 );
4257 }
4258
4259 #[cfg(feature = "java")]
4260 #[test]
4261 fn java_while_lloc() {
4262 check_metrics::<JavaParser>(
4263 "
4264 int i=0; // +1
4265 while(i < 10) { // +1
4266 i++; // +1
4267 System.out.println(i); // +1
4268 }",
4269 "foo.java",
4270 |metric| {
4271 insta::assert_json_snapshot!(
4273 metric.loc,
4274 @r#"
4275 {
4276 "sloc": 5,
4277 "ploc": 5,
4278 "lloc": 4,
4279 "cloc": 4,
4280 "blank": 0,
4281 "dloc": 0,
4282 "sloc_average": 5.0,
4283 "ploc_average": 5.0,
4284 "lloc_average": 4.0,
4285 "cloc_average": 4.0,
4286 "blank_average": 0.0,
4287 "dloc_average": 0.0,
4288 "sloc_min": 5,
4289 "sloc_max": 5,
4290 "cloc_min": 4,
4291 "cloc_max": 4,
4292 "ploc_min": 5,
4293 "ploc_max": 5,
4294 "lloc_min": 4,
4295 "lloc_max": 4,
4296 "blank_min": 0,
4297 "blank_max": 0,
4298 "dloc_min": 0,
4299 "dloc_max": 0
4300 }
4301 "#
4302 );
4303 },
4304 );
4305 }
4306
4307 #[cfg(feature = "java")]
4308 #[test]
4309 fn java_do_while_lloc() {
4310 check_metrics::<JavaParser>(
4311 "
4312 int i=0; // +1
4313 do { // +1
4314 i++; // +1
4315 System.out.println(i); // +1
4316 } while(i < 10)",
4317 "foo.java",
4318 |metric| {
4319 insta::assert_json_snapshot!(
4321 metric.loc,
4322 @r#"
4323 {
4324 "sloc": 5,
4325 "ploc": 5,
4326 "lloc": 4,
4327 "cloc": 4,
4328 "blank": 0,
4329 "dloc": 0,
4330 "sloc_average": 5.0,
4331 "ploc_average": 5.0,
4332 "lloc_average": 4.0,
4333 "cloc_average": 4.0,
4334 "blank_average": 0.0,
4335 "dloc_average": 0.0,
4336 "sloc_min": 5,
4337 "sloc_max": 5,
4338 "cloc_min": 4,
4339 "cloc_max": 4,
4340 "ploc_min": 5,
4341 "ploc_max": 5,
4342 "lloc_min": 4,
4343 "lloc_max": 4,
4344 "blank_min": 0,
4345 "blank_max": 0,
4346 "dloc_min": 0,
4347 "dloc_max": 0
4348 }
4349 "#
4350 );
4351 },
4352 );
4353 }
4354
4355 #[cfg(feature = "java")]
4356 #[test]
4357 fn java_switch_lloc() {
4358 check_metrics::<JavaParser>(
4359 "switch(grade) { // +1
4360 case 'A' :
4361 System.out.println(\"Pass with distinction\"); // +1
4362 break; // +1
4363 case 'B' :
4364 case 'C' :
4365 System.out.println(\"Pass\"); // +1
4366 break; // +1
4367 case 'D' :
4368 System.out.println(\"At risk\"); // +1
4369 case 'F' :
4370 System.out.println(\"Fail\"); // +1
4371 break; // +1
4372 default :
4373 System.out.println(\"Invalid grade\"); // +1
4374 }",
4375 "foo.java",
4376 |metric| {
4377 insta::assert_json_snapshot!(
4379 metric.loc,
4380 @r#"
4381 {
4382 "sloc": 16,
4383 "ploc": 16,
4384 "lloc": 9,
4385 "cloc": 9,
4386 "blank": 0,
4387 "dloc": 0,
4388 "sloc_average": 16.0,
4389 "ploc_average": 16.0,
4390 "lloc_average": 9.0,
4391 "cloc_average": 9.0,
4392 "blank_average": 0.0,
4393 "dloc_average": 0.0,
4394 "sloc_min": 16,
4395 "sloc_max": 16,
4396 "cloc_min": 9,
4397 "cloc_max": 9,
4398 "ploc_min": 16,
4399 "ploc_max": 16,
4400 "lloc_min": 9,
4401 "lloc_max": 9,
4402 "blank_min": 0,
4403 "blank_max": 0,
4404 "dloc_min": 0,
4405 "dloc_max": 0
4406 }
4407 "#
4408 );
4409 },
4410 );
4411 }
4412
4413 #[cfg(feature = "java")]
4414 #[test]
4415 fn java_continue_lloc() {
4416 check_metrics::<JavaParser>(
4417 "int max = 10; // +1
4418
4419 for (int i = 0; i < max; i++) { // +1
4420 if(i % 2 == 0) { continue;} + 2
4421 System.out.println(i); // +1
4422 }",
4423 "foo.java",
4424 |metric| {
4425 insta::assert_json_snapshot!(
4427 metric.loc,
4428 @r#"
4429 {
4430 "sloc": 6,
4431 "ploc": 5,
4432 "lloc": 5,
4433 "cloc": 3,
4434 "blank": 1,
4435 "dloc": 0,
4436 "sloc_average": 6.0,
4437 "ploc_average": 5.0,
4438 "lloc_average": 5.0,
4439 "cloc_average": 3.0,
4440 "blank_average": 1.0,
4441 "dloc_average": 0.0,
4442 "sloc_min": 6,
4443 "sloc_max": 6,
4444 "cloc_min": 3,
4445 "cloc_max": 3,
4446 "ploc_min": 5,
4447 "ploc_max": 5,
4448 "lloc_min": 5,
4449 "lloc_max": 5,
4450 "blank_min": 1,
4451 "blank_max": 1,
4452 "dloc_min": 0,
4453 "dloc_max": 0
4454 }
4455 "#
4456 );
4457 },
4458 );
4459 }
4460
4461 #[cfg(feature = "java")]
4462 #[test]
4463 fn java_try_lloc() {
4464 check_metrics::<JavaParser>(
4465 "try { // +1
4466 int[] myNumbers = {1, 2, 3}; // +1
4467 System.out.println(myNumbers[10]); // +1
4468 } catch (Exception e) {
4469 System.out.println(e.getMessage()); // +1
4470 throw e; // +1
4471 }",
4472 "foo.java",
4473 |metric| {
4474 insta::assert_json_snapshot!(
4476 metric.loc,
4477 @r#"
4478 {
4479 "sloc": 7,
4480 "ploc": 7,
4481 "lloc": 5,
4482 "cloc": 5,
4483 "blank": 0,
4484 "dloc": 0,
4485 "sloc_average": 7.0,
4486 "ploc_average": 7.0,
4487 "lloc_average": 5.0,
4488 "cloc_average": 5.0,
4489 "blank_average": 0.0,
4490 "dloc_average": 0.0,
4491 "sloc_min": 7,
4492 "sloc_max": 7,
4493 "cloc_min": 5,
4494 "cloc_max": 5,
4495 "ploc_min": 7,
4496 "ploc_max": 7,
4497 "lloc_min": 5,
4498 "lloc_max": 5,
4499 "blank_min": 0,
4500 "blank_max": 0,
4501 "dloc_min": 0,
4502 "dloc_max": 0
4503 }
4504 "#
4505 );
4506 },
4507 );
4508 }
4509
4510 #[cfg(feature = "java")]
4511 #[test]
4512 fn java_class_loc() {
4513 check_metrics::<JavaParser>(
4514 "
4515 public class Person {
4516 private String name;
4517 public Person(String name){
4518 this.name = name; // +1
4519 }
4520 public String getName() {
4521 return name; // +1
4522 }
4523 }",
4524 "foo.java",
4525 |metric| {
4526 insta::assert_json_snapshot!(
4528 metric.loc,
4529 @r#"
4530 {
4531 "sloc": 9,
4532 "ploc": 9,
4533 "lloc": 2,
4534 "cloc": 2,
4535 "blank": 0,
4536 "dloc": 0,
4537 "sloc_average": 2.25,
4538 "ploc_average": 2.25,
4539 "lloc_average": 0.5,
4540 "cloc_average": 0.5,
4541 "blank_average": 0.0,
4542 "dloc_average": 0.0,
4543 "sloc_min": 3,
4544 "sloc_max": 9,
4545 "cloc_min": 1,
4546 "cloc_max": 2,
4547 "ploc_min": 3,
4548 "ploc_max": 9,
4549 "lloc_min": 1,
4550 "lloc_max": 2,
4551 "blank_min": 0,
4552 "blank_max": 0,
4553 "dloc_min": 0,
4554 "dloc_max": 0
4555 }
4556 "#
4557 );
4558 },
4559 );
4560 }
4561
4562 #[cfg(feature = "java")]
4563 #[test]
4564 fn java_expressions_lloc() {
4565 check_metrics::<JavaParser>(
4566 "int x = 10; // +1 local var declaration
4567 x=+89; // +1 expression statement
4568 int y = x * 2; // +1 local var declaration
4569 IntFunction double = (n) -> n*2; // +1 local var declaration
4570 int y2 = double(x); // +1 local var declaration
4571 System.out.println(\"double \" + x + \" = \" + y2); // +1 expression statement
4572 String message = (x % 2) == 0 ? \"Evenly done.\" : \"Oddly done.\"; // +1 local var declaration
4573 Object done = (Runnable) () -> { System.out.println(\"Done!\"); }; // +2 local var declaration + expression statement
4574 String s = \"string\"; // +1 local var declaration
4575 boolean isS = (s instanceof String); // +1 local var declaration
4576 done.run(); // +1 expression statement
4577 ",
4578 "foo.java",
4579 |metric| {
4580 insta::assert_json_snapshot!(
4582 metric.loc,
4583 @r#"
4584 {
4585 "sloc": 11,
4586 "ploc": 11,
4587 "lloc": 12,
4588 "cloc": 11,
4589 "blank": 0,
4590 "dloc": 0,
4591 "sloc_average": 11.0,
4592 "ploc_average": 11.0,
4593 "lloc_average": 12.0,
4594 "cloc_average": 11.0,
4595 "blank_average": 0.0,
4596 "dloc_average": 0.0,
4597 "sloc_min": 11,
4598 "sloc_max": 11,
4599 "cloc_min": 11,
4600 "cloc_max": 11,
4601 "ploc_min": 11,
4602 "ploc_max": 11,
4603 "lloc_min": 12,
4604 "lloc_max": 12,
4605 "blank_min": 0,
4606 "blank_max": 0,
4607 "dloc_min": 0,
4608 "dloc_max": 0
4609 }
4610 "#
4611 );
4612 },
4613 );
4614 }
4615
4616 #[cfg(feature = "java")]
4617 #[test]
4618 fn java_statement_inline_loc() {
4619 check_metrics::<JavaParser>(
4620 "for (int i = 0; i < 100; i++) { System.out.println(\"hello\"); }",
4621 "foo.java",
4622 |metric| {
4623 insta::assert_json_snapshot!(
4625 metric.loc,
4626 @r#"
4627 {
4628 "sloc": 1,
4629 "ploc": 1,
4630 "lloc": 2,
4631 "cloc": 0,
4632 "blank": 0,
4633 "dloc": 0,
4634 "sloc_average": 1.0,
4635 "ploc_average": 1.0,
4636 "lloc_average": 2.0,
4637 "cloc_average": 0.0,
4638 "blank_average": 0.0,
4639 "dloc_average": 0.0,
4640 "sloc_min": 1,
4641 "sloc_max": 1,
4642 "cloc_min": 0,
4643 "cloc_max": 0,
4644 "ploc_min": 1,
4645 "ploc_max": 1,
4646 "lloc_min": 2,
4647 "lloc_max": 2,
4648 "blank_min": 0,
4649 "blank_max": 0,
4650 "dloc_min": 0,
4651 "dloc_max": 0
4652 }
4653 "#
4654 );
4655 },
4656 );
4657 }
4658
4659 #[cfg(feature = "java")]
4660 #[test]
4661 fn java_general_loc() {
4662 check_metrics::<JavaParser>(
4663 "int max = 100;
4664
4665 /*
4666 Loop through and print
4667 from: 0
4668 to: max
4669 */
4670 for (int i = 0; i < max; i++) {
4671 // Print the value
4672 System.out.println(i);
4673 }",
4674 "foo.java",
4675 |metric| {
4676 insta::assert_json_snapshot!(
4678 metric.loc,
4679 @r#"
4680 {
4681 "sloc": 11,
4682 "ploc": 4,
4683 "lloc": 3,
4684 "cloc": 6,
4685 "blank": 1,
4686 "dloc": 0,
4687 "sloc_average": 11.0,
4688 "ploc_average": 4.0,
4689 "lloc_average": 3.0,
4690 "cloc_average": 6.0,
4691 "blank_average": 1.0,
4692 "dloc_average": 0.0,
4693 "sloc_min": 11,
4694 "sloc_max": 11,
4695 "cloc_min": 6,
4696 "cloc_max": 6,
4697 "ploc_min": 4,
4698 "ploc_max": 4,
4699 "lloc_min": 3,
4700 "lloc_max": 3,
4701 "blank_min": 1,
4702 "blank_max": 1,
4703 "dloc_min": 0,
4704 "dloc_max": 0
4705 }
4706 "#
4707 );
4708 },
4709 );
4710 }
4711
4712 #[cfg(feature = "java")]
4713 #[test]
4714 fn java_main_class_loc() {
4715 check_metrics::<JavaParser>(
4716 "package com.company;
4717 /**
4718 * The HelloWorldApp class implements an application that
4719 * simply prints \"Hello World!\" to standard output.
4720 */
4721
4722 class HelloWorldApp {
4723 public void main(String[] args) {
4724 String message = args.length == 0 ? \"Hello empty world\" : \"Hello world\"; // +1 lloc : 1 var assignment
4725 System.out.println(message); // Display the string. +1 lloc
4726 }
4727 }",
4728 "foo.java",
4729 |metric| {
4730 insta::assert_json_snapshot!(
4732 metric.loc,
4733 @r#"
4734 {
4735 "sloc": 12,
4736 "ploc": 7,
4737 "lloc": 2,
4738 "cloc": 6,
4739 "blank": 1,
4740 "dloc": 4,
4741 "sloc_average": 4.0,
4742 "ploc_average": 2.3333333333333335,
4743 "lloc_average": 0.6666666666666666,
4744 "cloc_average": 2.0,
4745 "blank_average": 0.3333333333333333,
4746 "dloc_average": 1.3333333333333333,
4747 "sloc_min": 4,
4748 "sloc_max": 12,
4749 "cloc_min": 2,
4750 "cloc_max": 6,
4751 "ploc_min": 4,
4752 "ploc_max": 7,
4753 "lloc_min": 2,
4754 "lloc_max": 2,
4755 "blank_min": 0,
4756 "blank_max": 1,
4757 "dloc_min": 0,
4758 "dloc_max": 4
4759 }
4760 "#
4761 );
4762 },
4763 );
4764 }
4765
4766 #[cfg(feature = "go")]
4767 #[test]
4768 fn go_general_loc() {
4769 check_metrics::<GoParser>(
4770 "package main
4771
4772 // entrypoint
4773 func main() {
4774 /* loop body */
4775 for i := 0; i < 10; i++ {
4776 fmt.Println(i)
4777 }
4778 }",
4779 "foo.go",
4780 |metric| {
4781 insta::assert_json_snapshot!(
4786 metric.loc,
4787 @r#"
4788 {
4789 "sloc": 9,
4790 "ploc": 6,
4791 "lloc": 2,
4792 "cloc": 2,
4793 "blank": 1,
4794 "dloc": 1,
4795 "sloc_average": 4.5,
4796 "ploc_average": 3.0,
4797 "lloc_average": 1.0,
4798 "cloc_average": 1.0,
4799 "blank_average": 0.5,
4800 "dloc_average": 0.5,
4801 "sloc_min": 6,
4802 "sloc_max": 9,
4803 "cloc_min": 1,
4804 "cloc_max": 2,
4805 "ploc_min": 5,
4806 "ploc_max": 6,
4807 "lloc_min": 2,
4808 "lloc_max": 2,
4809 "blank_min": 0,
4810 "blank_max": 1,
4811 "dloc_min": 0,
4812 "dloc_max": 1
4813 }
4814 "#
4815 );
4816 },
4817 );
4818 }
4819
4820 #[cfg(feature = "go")]
4821 #[test]
4822 fn go_for_clause_does_not_double_count_lloc() {
4823 check_metrics::<GoParser>(
4827 "package main
4828 func f(n int) int {
4829 for i := 0; i < n; i++ {
4830 return i
4831 }
4832 return 0
4833 }",
4834 "foo.go",
4835 |metric| {
4836 assert_eq!(metric.loc.lloc(), 3);
4839 },
4840 );
4841 }
4842
4843 #[cfg(feature = "go")]
4844 #[test]
4845 fn go_blank() {
4846 check_metrics::<GoParser>(
4847 "package main
4848
4849 func foo() {
4850 x := 1
4851
4852 y := 2
4853 }",
4854 "foo.go",
4855 |metric| {
4856 insta::assert_json_snapshot!(
4859 metric.loc,
4860 @r#"
4861 {
4862 "sloc": 7,
4863 "ploc": 5,
4864 "lloc": 2,
4865 "cloc": 0,
4866 "blank": 2,
4867 "dloc": 0,
4868 "sloc_average": 3.5,
4869 "ploc_average": 2.5,
4870 "lloc_average": 1.0,
4871 "cloc_average": 0.0,
4872 "blank_average": 1.0,
4873 "dloc_average": 0.0,
4874 "sloc_min": 5,
4875 "sloc_max": 7,
4876 "cloc_min": 0,
4877 "cloc_max": 0,
4878 "ploc_min": 4,
4879 "ploc_max": 5,
4880 "lloc_min": 2,
4881 "lloc_max": 2,
4882 "blank_min": 1,
4883 "blank_max": 2,
4884 "dloc_min": 0,
4885 "dloc_max": 0
4886 }
4887 "#
4888 );
4889 },
4890 );
4891 }
4892
4893 #[cfg(feature = "go")]
4894 #[test]
4895 fn go_cloc_line_comments() {
4896 check_metrics::<GoParser>(
4897 "package main
4898
4899 // helper adds two numbers.
4900 // It returns their sum.
4901 func add(a, b int) int {
4902 // compute the result
4903 return a + b
4904 }",
4905 "foo.go",
4906 |metric| {
4907 insta::assert_json_snapshot!(
4910 metric.loc,
4911 @r#"
4912 {
4913 "sloc": 8,
4914 "ploc": 4,
4915 "lloc": 1,
4916 "cloc": 3,
4917 "blank": 1,
4918 "dloc": 2,
4919 "sloc_average": 4.0,
4920 "ploc_average": 2.0,
4921 "lloc_average": 0.5,
4922 "cloc_average": 1.5,
4923 "blank_average": 0.5,
4924 "dloc_average": 1.0,
4925 "sloc_min": 4,
4926 "sloc_max": 8,
4927 "cloc_min": 1,
4928 "cloc_max": 3,
4929 "ploc_min": 3,
4930 "ploc_max": 4,
4931 "lloc_min": 1,
4932 "lloc_max": 1,
4933 "blank_min": 0,
4934 "blank_max": 1,
4935 "dloc_min": 0,
4936 "dloc_max": 2
4937 }
4938 "#
4939 );
4940 },
4941 );
4942 }
4943
4944 #[cfg(feature = "go")]
4945 #[test]
4946 fn go_cloc_block_comments() {
4947 check_metrics::<GoParser>(
4948 "package main
4949
4950 /* block comment
4951 spanning two lines */
4952 func foo() {
4953 x := 1 /* inline block */
4954 }",
4955 "foo.go",
4956 |metric| {
4957 insta::assert_json_snapshot!(
4960 metric.loc,
4961 @r#"
4962 {
4963 "sloc": 7,
4964 "ploc": 4,
4965 "lloc": 1,
4966 "cloc": 3,
4967 "blank": 1,
4968 "dloc": 2,
4969 "sloc_average": 3.5,
4970 "ploc_average": 2.0,
4971 "lloc_average": 0.5,
4972 "cloc_average": 1.5,
4973 "blank_average": 0.5,
4974 "dloc_average": 1.0,
4975 "sloc_min": 3,
4976 "sloc_max": 7,
4977 "cloc_min": 1,
4978 "cloc_max": 3,
4979 "ploc_min": 3,
4980 "ploc_max": 4,
4981 "lloc_min": 1,
4982 "lloc_max": 1,
4983 "blank_min": 0,
4984 "blank_max": 1,
4985 "dloc_min": 0,
4986 "dloc_max": 2
4987 }
4988 "#
4989 );
4990 },
4991 );
4992 }
4993
4994 #[cfg(feature = "go")]
4995 #[test]
4996 fn go_lloc_if_for_switch() {
4997 check_metrics::<GoParser>(
4998 "package main
4999
5000 func foo(n int) int {
5001 if n > 0 {
5002 for i := 0; i < n; i++ {
5003 switch i {
5004 }
5005 }
5006 }
5007 return n
5008 }",
5009 "foo.go",
5010 |metric| {
5011 insta::assert_json_snapshot!(
5014 metric.loc,
5015 @r#"
5016 {
5017 "sloc": 11,
5018 "ploc": 10,
5019 "lloc": 4,
5020 "cloc": 0,
5021 "blank": 1,
5022 "dloc": 0,
5023 "sloc_average": 5.5,
5024 "ploc_average": 5.0,
5025 "lloc_average": 2.0,
5026 "cloc_average": 0.0,
5027 "blank_average": 0.5,
5028 "dloc_average": 0.0,
5029 "sloc_min": 9,
5030 "sloc_max": 11,
5031 "cloc_min": 0,
5032 "cloc_max": 0,
5033 "ploc_min": 9,
5034 "ploc_max": 10,
5035 "lloc_min": 4,
5036 "lloc_max": 4,
5037 "blank_min": 0,
5038 "blank_max": 1,
5039 "dloc_min": 0,
5040 "dloc_max": 0
5041 }
5042 "#
5043 );
5044 },
5045 );
5046 }
5047
5048 #[cfg(feature = "go")]
5049 #[test]
5050 fn go_lloc_go_defer() {
5051 check_metrics::<GoParser>(
5052 "package main
5053
5054 func foo() {
5055 go run()
5056 defer cleanup()
5057 }",
5058 "foo.go",
5059 |metric| {
5060 insta::assert_json_snapshot!(
5063 metric.loc,
5064 @r#"
5065 {
5066 "sloc": 6,
5067 "ploc": 5,
5068 "lloc": 2,
5069 "cloc": 0,
5070 "blank": 1,
5071 "dloc": 0,
5072 "sloc_average": 3.0,
5073 "ploc_average": 2.5,
5074 "lloc_average": 1.0,
5075 "cloc_average": 0.0,
5076 "blank_average": 0.5,
5077 "dloc_average": 0.0,
5078 "sloc_min": 4,
5079 "sloc_max": 6,
5080 "cloc_min": 0,
5081 "cloc_max": 0,
5082 "ploc_min": 4,
5083 "ploc_max": 5,
5084 "lloc_min": 2,
5085 "lloc_max": 2,
5086 "blank_min": 0,
5087 "blank_max": 1,
5088 "dloc_min": 0,
5089 "dloc_max": 0
5090 }
5091 "#
5092 );
5093 },
5094 );
5095 }
5096
5097 #[cfg(feature = "go")]
5098 #[test]
5099 fn go_lloc_var_const_declarations() {
5100 check_metrics::<GoParser>(
5101 "package main
5102
5103 func foo() {
5104 var x int
5105 var y = 10
5106 const z = 42
5107 a := 3
5108 a = 4
5109 }",
5110 "foo.go",
5111 |metric| {
5112 insta::assert_json_snapshot!(
5116 metric.loc,
5117 @r#"
5118 {
5119 "sloc": 9,
5120 "ploc": 8,
5121 "lloc": 5,
5122 "cloc": 0,
5123 "blank": 1,
5124 "dloc": 0,
5125 "sloc_average": 4.5,
5126 "ploc_average": 4.0,
5127 "lloc_average": 2.5,
5128 "cloc_average": 0.0,
5129 "blank_average": 0.5,
5130 "dloc_average": 0.0,
5131 "sloc_min": 7,
5132 "sloc_max": 9,
5133 "cloc_min": 0,
5134 "cloc_max": 0,
5135 "ploc_min": 7,
5136 "ploc_max": 8,
5137 "lloc_min": 5,
5138 "lloc_max": 5,
5139 "blank_min": 0,
5140 "blank_max": 1,
5141 "dloc_min": 0,
5142 "dloc_max": 0
5143 }
5144 "#
5145 );
5146 },
5147 );
5148 }
5149
5150 #[cfg(feature = "go")]
5151 #[test]
5152 fn go_lloc_select() {
5153 check_metrics::<GoParser>(
5154 "package main
5155
5156 func foo(ch chan int) {
5157 select {
5158 case v := <-ch:
5159 _ = v
5160 }
5161 }",
5162 "foo.go",
5163 |metric| {
5164 insta::assert_json_snapshot!(
5169 metric.loc,
5170 @r#"
5171 {
5172 "sloc": 8,
5173 "ploc": 7,
5174 "lloc": 2,
5175 "cloc": 0,
5176 "blank": 1,
5177 "dloc": 0,
5178 "sloc_average": 4.0,
5179 "ploc_average": 3.5,
5180 "lloc_average": 1.0,
5181 "cloc_average": 0.0,
5182 "blank_average": 0.5,
5183 "dloc_average": 0.0,
5184 "sloc_min": 6,
5185 "sloc_max": 8,
5186 "cloc_min": 0,
5187 "cloc_max": 0,
5188 "ploc_min": 6,
5189 "ploc_max": 7,
5190 "lloc_min": 2,
5191 "lloc_max": 2,
5192 "blank_min": 0,
5193 "blank_max": 1,
5194 "dloc_min": 0,
5195 "dloc_max": 0
5196 }
5197 "#
5198 );
5199 },
5200 );
5201 }
5202
5203 #[cfg(feature = "go")]
5204 #[test]
5205 fn go_sloc_multiline_function() {
5206 check_metrics::<GoParser>(
5207 "package main
5208
5209 func add(
5210 a int,
5211 b int,
5212 ) int {
5213 return a + b
5214 }",
5215 "foo.go",
5216 |metric| {
5217 insta::assert_json_snapshot!(
5220 metric.loc,
5221 @r#"
5222 {
5223 "sloc": 8,
5224 "ploc": 7,
5225 "lloc": 1,
5226 "cloc": 0,
5227 "blank": 1,
5228 "dloc": 0,
5229 "sloc_average": 4.0,
5230 "ploc_average": 3.5,
5231 "lloc_average": 0.5,
5232 "cloc_average": 0.0,
5233 "blank_average": 0.5,
5234 "dloc_average": 0.0,
5235 "sloc_min": 6,
5236 "sloc_max": 8,
5237 "cloc_min": 0,
5238 "cloc_max": 0,
5239 "ploc_min": 6,
5240 "ploc_max": 7,
5241 "lloc_min": 1,
5242 "lloc_max": 1,
5243 "blank_min": 0,
5244 "blank_max": 1,
5245 "dloc_min": 0,
5246 "dloc_max": 0
5247 }
5248 "#
5249 );
5250 },
5251 );
5252 }
5253
5254 #[cfg(feature = "go")]
5255 #[test]
5256 fn go_code_comment_same_line() {
5257 check_metrics::<GoParser>(
5258 "package main
5259
5260 func foo() {
5261 x := 1 // initialize x
5262 y := 2 // initialize y
5263 }",
5264 "foo.go",
5265 |metric| {
5266 insta::assert_json_snapshot!(
5271 metric.loc,
5272 @r#"
5273 {
5274 "sloc": 6,
5275 "ploc": 5,
5276 "lloc": 2,
5277 "cloc": 2,
5278 "blank": 1,
5279 "dloc": 0,
5280 "sloc_average": 3.0,
5281 "ploc_average": 2.5,
5282 "lloc_average": 1.0,
5283 "cloc_average": 1.0,
5284 "blank_average": 0.5,
5285 "dloc_average": 0.0,
5286 "sloc_min": 4,
5287 "sloc_max": 6,
5288 "cloc_min": 2,
5289 "cloc_max": 2,
5290 "ploc_min": 4,
5291 "ploc_max": 5,
5292 "lloc_min": 2,
5293 "lloc_max": 2,
5294 "blank_min": 0,
5295 "blank_max": 1,
5296 "dloc_min": 0,
5297 "dloc_max": 0
5298 }
5299 "#
5300 );
5301 },
5302 );
5303 }
5304
5305 #[cfg(feature = "perl")]
5306 #[test]
5307 fn perl_grammar_smoke() {
5308 assert_perl_parses_cleanly(
5314 "use strict;
5315use warnings;
5316
5317# line comment
5318
5319=pod
5320multi-line POD
5321=cut
5322
5323sub factorial {
5324 my ($n) = @_;
5325 return 1 if $n <= 1;
5326 return $n * factorial($n - 1);
5327}
5328
5329my @arr = (1, 2, 3);
5330my %hash = (a => 1, b => 2);
5331my $closure = sub { return $_[0] + 1; };
5332
5333for my $i (1..3) {
5334 if ($i % 2 == 0) {
5335 print \"even\\n\";
5336 } elsif ($i == 1) {
5337 print \"one\\n\";
5338 } else {
5339 print \"odd\\n\";
5340 }
5341}
5342
5343while ($x > 0) {
5344 last if $x == 0;
5345 $x--;
5346}
5347
5348unless ($done) {
5349 next;
5350}
5351
5352my $heredoc = <<END;
5353hello
5354END
5355",
5356 );
5357 }
5358
5359 #[cfg(feature = "perl")]
5360 #[test]
5361 fn perl_blank() {
5362 check_metrics::<PerlParser>(
5363 "
5364
5365my $a = 42;
5366
5367my $b = 43;
5368
5369",
5370 "foo.pl",
5371 |metric| {
5372 insta::assert_json_snapshot!(metric.loc, @r#"
5373 {
5374 "sloc": 3,
5375 "ploc": 2,
5376 "lloc": 2,
5377 "cloc": 0,
5378 "blank": 1,
5379 "dloc": 0,
5380 "sloc_average": 3.0,
5381 "ploc_average": 2.0,
5382 "lloc_average": 2.0,
5383 "cloc_average": 0.0,
5384 "blank_average": 1.0,
5385 "dloc_average": 0.0,
5386 "sloc_min": 3,
5387 "sloc_max": 3,
5388 "cloc_min": 0,
5389 "cloc_max": 0,
5390 "ploc_min": 2,
5391 "ploc_max": 2,
5392 "lloc_min": 2,
5393 "lloc_max": 2,
5394 "blank_min": 1,
5395 "blank_max": 1,
5396 "dloc_min": 0,
5397 "dloc_max": 0
5398 }
5399 "#);
5400 },
5401 );
5402 }
5403
5404 #[cfg(feature = "perl")]
5405 #[test]
5406 fn perl_no_zero_blank() {
5407 check_metrics::<PerlParser>(
5410 "my $a = 1;
5411my $b = 2;
5412
5413my $c = 3; # trailing
5414my $d = 4; # trailing
5415my $e = 5;",
5416 "foo.pl",
5417 |metric| {
5418 assert_eq!(metric.loc.sloc(), 6);
5419 assert_eq!(metric.loc.ploc(), 5);
5420 assert_eq!(metric.loc.cloc(), 2);
5421 assert_eq!(metric.loc.blank(), 1);
5422 insta::assert_json_snapshot!(metric.loc);
5423 },
5424 );
5425 }
5426
5427 #[cfg(feature = "perl")]
5428 #[test]
5429 fn perl_blank_zero_sanity() {
5430 check_metrics::<PerlParser>(
5433 "my $a = 1;
5434my $b = 2;",
5435 "foo.pl",
5436 |metric| {
5437 assert_eq!(metric.loc.sloc(), 2);
5438 assert_eq!(metric.loc.ploc(), 2);
5439 assert_eq!(metric.loc.lloc(), 2);
5440 assert_eq!(metric.loc.cloc(), 0);
5441 assert_eq!(metric.loc.blank(), 0);
5442 },
5443 );
5444 }
5445
5446 #[cfg(feature = "perl")]
5452 #[test]
5453 fn perl_cloc_line_comments() {
5454 check_metrics::<PerlParser>(
5455 "# top comment
5456my $a = 1; # trailing
5457my $b = 2;",
5458 "foo.pl",
5459 |metric| {
5460 insta::assert_json_snapshot!(metric.loc, @r#"
5461 {
5462 "sloc": 3,
5463 "ploc": 2,
5464 "lloc": 2,
5465 "cloc": 2,
5466 "blank": 0,
5467 "dloc": 0,
5468 "sloc_average": 3.0,
5469 "ploc_average": 2.0,
5470 "lloc_average": 2.0,
5471 "cloc_average": 2.0,
5472 "blank_average": 0.0,
5473 "dloc_average": 0.0,
5474 "sloc_min": 3,
5475 "sloc_max": 3,
5476 "cloc_min": 2,
5477 "cloc_max": 2,
5478 "ploc_min": 2,
5479 "ploc_max": 2,
5480 "lloc_min": 2,
5481 "lloc_max": 2,
5482 "blank_min": 0,
5483 "blank_max": 0,
5484 "dloc_min": 0,
5485 "dloc_max": 0
5486 }
5487 "#);
5488 },
5489 );
5490 }
5491
5492 #[cfg(feature = "perl")]
5493 #[test]
5494 fn perl_cloc_pod_block() {
5495 check_metrics::<PerlParser>(
5496 "my $x = 1;
5497=pod
5498multi-line
5499pod block
5500=cut
5501my $y = 2;",
5502 "foo.pl",
5503 |metric| {
5504 insta::assert_json_snapshot!(metric.loc, @r#"
5505 {
5506 "sloc": 6,
5507 "ploc": 2,
5508 "lloc": 2,
5509 "cloc": 4,
5510 "blank": 0,
5511 "dloc": 4,
5512 "sloc_average": 6.0,
5513 "ploc_average": 2.0,
5514 "lloc_average": 2.0,
5515 "cloc_average": 4.0,
5516 "blank_average": 0.0,
5517 "dloc_average": 4.0,
5518 "sloc_min": 6,
5519 "sloc_max": 6,
5520 "cloc_min": 4,
5521 "cloc_max": 4,
5522 "ploc_min": 2,
5523 "ploc_max": 2,
5524 "lloc_min": 2,
5525 "lloc_max": 2,
5526 "blank_min": 0,
5527 "blank_max": 0,
5528 "dloc_min": 4,
5529 "dloc_max": 4
5530 }
5531 "#);
5532 },
5533 );
5534 }
5535
5536 #[cfg(feature = "perl")]
5537 #[test]
5538 fn perl_lloc_simple_statements() {
5539 check_metrics::<PerlParser>(
5540 "my $a = 1;
5541my $b = 2;
5542my $c = 3;",
5543 "foo.pl",
5544 |metric| {
5545 insta::assert_json_snapshot!(metric.loc, @r#"
5546 {
5547 "sloc": 3,
5548 "ploc": 3,
5549 "lloc": 3,
5550 "cloc": 0,
5551 "blank": 0,
5552 "dloc": 0,
5553 "sloc_average": 3.0,
5554 "ploc_average": 3.0,
5555 "lloc_average": 3.0,
5556 "cloc_average": 0.0,
5557 "blank_average": 0.0,
5558 "dloc_average": 0.0,
5559 "sloc_min": 3,
5560 "sloc_max": 3,
5561 "cloc_min": 0,
5562 "cloc_max": 0,
5563 "ploc_min": 3,
5564 "ploc_max": 3,
5565 "lloc_min": 3,
5566 "lloc_max": 3,
5567 "blank_min": 0,
5568 "blank_max": 0,
5569 "dloc_min": 0,
5570 "dloc_max": 0
5571 }
5572 "#);
5573 },
5574 );
5575 }
5576
5577 #[cfg(feature = "perl")]
5578 #[test]
5579 fn perl_lloc_compound_statements() {
5580 check_metrics::<PerlParser>(
5581 "if ($x) {
5582 print 'a';
5583}
5584while ($n > 0) {
5585 $n--;
5586}",
5587 "foo.pl",
5588 |metric| {
5589 insta::assert_json_snapshot!(metric.loc, @r#"
5590 {
5591 "sloc": 6,
5592 "ploc": 6,
5593 "lloc": 4,
5594 "cloc": 0,
5595 "blank": 0,
5596 "dloc": 0,
5597 "sloc_average": 6.0,
5598 "ploc_average": 6.0,
5599 "lloc_average": 4.0,
5600 "cloc_average": 0.0,
5601 "blank_average": 0.0,
5602 "dloc_average": 0.0,
5603 "sloc_min": 6,
5604 "sloc_max": 6,
5605 "cloc_min": 0,
5606 "cloc_max": 0,
5607 "ploc_min": 6,
5608 "ploc_max": 6,
5609 "lloc_min": 4,
5610 "lloc_max": 4,
5611 "blank_min": 0,
5612 "blank_max": 0,
5613 "dloc_min": 0,
5614 "dloc_max": 0
5615 }
5616 "#);
5617 },
5618 );
5619 }
5620
5621 #[cfg(feature = "perl")]
5622 #[test]
5623 fn perl_lloc_postfix_form_counts_once() {
5624 check_metrics::<PerlParser>(
5628 "sub f {
5629 return 1 if $_[0];
5630}",
5631 "foo.pl",
5632 |metric| {
5633 assert_eq!(metric.loc.lloc(), 1);
5634 },
5635 );
5636 }
5637
5638 #[cfg(feature = "perl")]
5639 #[test]
5640 fn perl_lloc_use_statement() {
5641 check_metrics::<PerlParser>(
5642 "use strict;
5643use warnings;
5644my $x = 1;",
5645 "foo.pl",
5646 |metric| {
5647 insta::assert_json_snapshot!(metric.loc, @r#"
5648 {
5649 "sloc": 3,
5650 "ploc": 3,
5651 "lloc": 3,
5652 "cloc": 0,
5653 "blank": 0,
5654 "dloc": 0,
5655 "sloc_average": 3.0,
5656 "ploc_average": 3.0,
5657 "lloc_average": 3.0,
5658 "cloc_average": 0.0,
5659 "blank_average": 0.0,
5660 "dloc_average": 0.0,
5661 "sloc_min": 3,
5662 "sloc_max": 3,
5663 "cloc_min": 0,
5664 "cloc_max": 0,
5665 "ploc_min": 3,
5666 "ploc_max": 3,
5667 "lloc_min": 3,
5668 "lloc_max": 3,
5669 "blank_min": 0,
5670 "blank_max": 0,
5671 "dloc_min": 0,
5672 "dloc_max": 0
5673 }
5674 "#);
5675 },
5676 );
5677 }
5678
5679 #[cfg(feature = "perl")]
5680 #[test]
5681 fn perl_lloc_for_loop() {
5682 check_metrics::<PerlParser>(
5683 "for my $i (1..3) {
5684 print $i;
5685}",
5686 "foo.pl",
5687 |metric| {
5688 assert_eq!(metric.loc.lloc(), 2);
5690 },
5691 );
5692 }
5693
5694 #[cfg(feature = "perl")]
5695 #[test]
5696 fn perl_lloc_loop_control_statement() {
5697 check_metrics::<PerlParser>(
5698 "while (1) {
5699 last if $done;
5700}",
5701 "foo.pl",
5702 |metric| {
5703 assert_eq!(metric.loc.lloc(), 2);
5705 },
5706 );
5707 }
5708
5709 #[cfg(feature = "perl")]
5710 #[test]
5711 fn perl_lloc_no_double_count_inside_single_line_statement() {
5712 check_metrics::<PerlParser>(
5715 "sub f {
5716 print 'a' unless $_[0];
5717}",
5718 "foo.pl",
5719 |metric| {
5720 assert_eq!(metric.loc.lloc(), 1);
5721 },
5722 );
5723 }
5724
5725 #[cfg(feature = "perl")]
5726 #[test]
5727 fn perl_lloc_function_definition_not_counted() {
5728 check_metrics::<PerlParser>(
5731 "sub f {
5732 my $x = 1;
5733}",
5734 "foo.pl",
5735 |metric| {
5736 assert_eq!(metric.loc.lloc(), 1);
5737 },
5738 );
5739 }
5740
5741 #[cfg(feature = "perl")]
5742 #[test]
5743 fn perl_lloc_anonymous_function() {
5744 check_metrics::<PerlParser>("my $f = sub { return 1; };", "foo.pl", |metric| {
5748 assert_eq!(metric.loc.lloc(), 2);
5749 });
5750 }
5751
5752 #[cfg(feature = "perl")]
5753 #[test]
5754 fn perl_multiline_string_assignment_ploc() {
5755 check_metrics::<PerlParser>(
5762 "my $s = \"line1
5763line2
5764line3\";",
5765 "foo.pl",
5766 |metric| {
5767 assert_eq!(metric.loc.sloc(), 3);
5769 assert_eq!(metric.loc.ploc(), 3);
5770 assert_eq!(metric.loc.cloc(), 0);
5771 assert_eq!(metric.loc.blank(), 0);
5772 },
5773 );
5774 }
5775
5776 #[cfg(any(
5783 feature = "bash",
5784 feature = "c",
5785 feature = "elixir",
5786 feature = "go",
5787 feature = "irules",
5788 feature = "kotlin",
5789 feature = "mozcpp",
5790 feature = "mozjs",
5791 feature = "perl",
5792 feature = "php",
5793 feature = "python",
5794 feature = "ruby",
5795 feature = "tcl",
5796 ))]
5797 #[test]
5798 fn multiline_string_ploc_consistent_across_languages() {
5799 #[cfg(any(
5820 feature = "bash",
5821 feature = "c",
5822 feature = "elixir",
5823 feature = "irules",
5824 feature = "kotlin",
5825 feature = "mozcpp",
5826 feature = "mozjs",
5827 feature = "perl",
5828 feature = "php",
5829 feature = "python",
5830 feature = "ruby",
5831 feature = "tcl",
5832 ))]
5833 #[allow(clippy::needless_pass_by_value)]
5834 fn assert_three_code_rows(metric: crate::CodeMetrics) {
5835 assert_eq!(metric.loc.sloc(), 3);
5836 assert_eq!(metric.loc.ploc(), 3);
5837 assert_eq!(metric.loc.cloc(), 0);
5838 assert_eq!(metric.loc.blank(), 0);
5839 }
5840 #[cfg(any(feature = "bash", feature = "php"))]
5843 #[allow(clippy::needless_pass_by_value)]
5844 fn assert_four_code_rows(metric: crate::CodeMetrics) {
5845 assert_eq!(metric.loc.sloc(), 4);
5846 assert_eq!(metric.loc.ploc(), 4);
5847 assert_eq!(metric.loc.cloc(), 0);
5848 assert_eq!(metric.loc.blank(), 0);
5849 }
5850 #[cfg(feature = "python")]
5851 check_metrics::<PythonParser>(
5852 "s = \"\"\"line1\n\nline3\"\"\"",
5853 "foo.py",
5854 assert_three_code_rows,
5855 );
5856 #[cfg(feature = "perl")]
5857 check_metrics::<PerlParser>(
5858 "my $s = \"line1\n\nline3\";",
5859 "foo.pl",
5860 assert_three_code_rows,
5861 );
5862 #[cfg(feature = "ruby")]
5863 check_metrics::<RubyParser>("s = \"line1\n\nline3\"", "foo.rb", assert_three_code_rows);
5864 #[cfg(feature = "go")]
5871 check_metrics::<GoParser>(
5872 "package p\n\nvar s = `line1\n\nline3`",
5873 "foo.go",
5874 |metric| {
5875 assert_eq!(metric.loc.sloc(), 5);
5878 assert_eq!(metric.loc.ploc(), 4);
5879 assert_eq!(metric.loc.cloc(), 0);
5880 assert_eq!(metric.loc.blank(), 1);
5881 },
5882 );
5883 #[cfg(feature = "kotlin")]
5884 check_metrics::<KotlinParser>(
5885 "val s = \"\"\"line1\n\nline3\"\"\"",
5886 "foo.kt",
5887 assert_three_code_rows,
5888 );
5889 #[cfg(feature = "mozcpp")]
5890 check_metrics::<MozcppParser>(
5891 "const char* s = R\"(line1\n\nline3)\";",
5892 "foo.cpp",
5893 assert_three_code_rows,
5894 );
5895 #[cfg(feature = "c")]
5903 check_metrics::<CParser>(
5904 "const char* s = \"line1\\\nline2\\\nline3\";",
5905 "foo.c",
5906 assert_three_code_rows,
5907 );
5908 #[cfg(feature = "mozjs")]
5909 check_metrics::<MozjsParser>(
5910 "const s = `line1\n\nline3`;",
5911 "foo.jsm",
5912 assert_three_code_rows,
5913 );
5914 #[cfg(feature = "bash")]
5919 check_metrics::<BashParser>("s='line1\n\nline3'", "foo.sh", assert_three_code_rows);
5920 #[cfg(feature = "tcl")]
5921 check_metrics::<TclParser>(
5922 "set s \"line1\n\nline3\"",
5923 "foo.tcl",
5924 assert_three_code_rows,
5925 );
5926 #[cfg(feature = "irules")]
5927 check_metrics::<IrulesParser>(
5928 "set s \"line1\n\nline3\"",
5929 "foo.irule",
5930 assert_three_code_rows,
5931 );
5932 #[cfg(feature = "elixir")]
5933 check_metrics::<ElixirParser>("s = \"line1\n\nline3\"", "foo.ex", assert_three_code_rows);
5934 #[cfg(feature = "php")]
5938 check_metrics::<PhpParser>(
5939 "<?php $s = \"line1\n\nline3\";",
5940 "foo.php",
5941 assert_three_code_rows,
5942 );
5943 #[cfg(feature = "php")]
5949 check_metrics::<PhpParser>(
5950 "<?php $s = <<<EOT\n\nline3\nEOT;",
5951 "foo.php",
5952 assert_four_code_rows,
5953 );
5954 #[cfg(feature = "bash")]
5955 check_metrics::<BashParser>("cat <<EOT\n\nline3\nEOT", "foo.sh", assert_four_code_rows);
5956 }
5957
5958 #[cfg(any(
5959 feature = "bash",
5960 feature = "elixir",
5961 feature = "irules",
5962 feature = "php",
5963 feature = "tcl",
5964 ))]
5965 #[test]
5966 fn multiline_literal_ending_at_eof_credits_every_row() {
5967 const FIXTURES: &[(crate::LANG, &[u8], u64)] = &[
5984 #[cfg(feature = "bash")]
5985 (crate::LANG::Bash, b"s='line1\n\nline3'", 3),
5986 #[cfg(feature = "tcl")]
5987 (crate::LANG::Tcl, b"set s \"line1\n\nline3\"", 3),
5988 #[cfg(feature = "irules")]
5989 (crate::LANG::Irules, b"set s \"line1\n\nline3\"", 3),
5990 #[cfg(feature = "elixir")]
5991 (crate::LANG::Elixir, b"s = \"line1\n\nline3\"", 3),
5992 #[cfg(feature = "php")]
5993 (crate::LANG::Php, b"<?php $s = \"line1\n\nline3\";", 3),
5994 #[cfg(feature = "php")]
5997 (crate::LANG::Php, b"<?php $s = <<<EOT\n\nline3\nEOT;", 4),
5998 #[cfg(feature = "bash")]
5999 (crate::LANG::Bash, b"cat <<EOT\n\nline3\nEOT", 4),
6000 #[cfg(feature = "bash")]
6003 (crate::LANG::Bash, b"cat <<EOT\n\nEOT", 3),
6004 ];
6005
6006 crate::test_support::assert_fixtures_present(FIXTURES);
6007 for &(lang, source, rows) in FIXTURES {
6008 let text = String::from_utf8_lossy(source);
6009 let loc = metrics_verbatim(lang, source, MetricsOptions::default()).loc;
6010 assert_eq!(loc.sloc(), rows, "{lang:?} sloc for {text:?}");
6011 assert_eq!(loc.ploc(), rows, "{lang:?} ploc for {text:?}");
6012 assert_eq!(loc.cloc(), 0, "{lang:?} cloc for {text:?}");
6013 assert_eq!(loc.blank(), 0, "{lang:?} blank for {text:?}");
6014 }
6015 }
6016
6017 #[cfg(feature = "perl")]
6018 #[test]
6019 fn perl_lloc_unless_until() {
6020 check_metrics::<PerlParser>(
6021 "unless ($x) {
6022 print 'a';
6023}
6024until ($n == 0) {
6025 $n--;
6026}",
6027 "foo.pl",
6028 |metric| {
6029 assert_eq!(metric.loc.lloc(), 4);
6032 },
6033 );
6034 }
6035
6036 #[cfg(feature = "perl")]
6037 #[test]
6038 fn perl_lloc_heredoc_body_not_counted() {
6039 check_metrics::<PerlParser>(
6042 "my $s = <<END;
6043line1
6044line2
6045END
6046my $x = 1;",
6047 "foo.pl",
6048 |metric| {
6049 assert_eq!(metric.loc.lloc(), 2);
6052 },
6053 );
6054 assert_perl_parses_cleanly(
6058 "my $s = <<END;
6059line1
6060line2
6061END
6062my $x = 1;",
6063 );
6064 }
6065
6066 #[cfg(feature = "perl")]
6067 #[test]
6068 fn perl_lloc_package_and_require() {
6069 check_metrics::<PerlParser>(
6070 "package Foo;
6071require 5.010;
6072my $x = 1;",
6073 "foo.pl",
6074 |metric| {
6075 insta::assert_json_snapshot!(metric.loc, @r#"
6076 {
6077 "sloc": 3,
6078 "ploc": 3,
6079 "lloc": 3,
6080 "cloc": 0,
6081 "blank": 0,
6082 "dloc": 0,
6083 "sloc_average": 3.0,
6084 "ploc_average": 3.0,
6085 "lloc_average": 3.0,
6086 "cloc_average": 0.0,
6087 "blank_average": 0.0,
6088 "dloc_average": 0.0,
6089 "sloc_min": 3,
6090 "sloc_max": 3,
6091 "cloc_min": 0,
6092 "cloc_max": 0,
6093 "ploc_min": 3,
6094 "ploc_max": 3,
6095 "lloc_min": 3,
6096 "lloc_max": 3,
6097 "blank_min": 0,
6098 "blank_max": 0,
6099 "dloc_min": 0,
6100 "dloc_max": 0
6101 }
6102 "#);
6103 },
6104 );
6105 }
6106
6107 #[cfg(feature = "lua")]
6108 #[test]
6109 fn lua_blank() {
6110 check_metrics::<LuaParser>(
6111 "local x = 1
6112
6113local y = 2",
6114 "foo.lua",
6115 |metric| {
6116 assert_eq!(metric.loc.sloc(), 3);
6117 assert_eq!(metric.loc.ploc(), 2);
6118 assert_eq!(metric.loc.lloc(), 2);
6119 assert_eq!(metric.loc.cloc(), 0);
6120 assert_eq!(metric.loc.blank(), 1);
6121 insta::assert_json_snapshot!(metric.loc);
6122 },
6123 );
6124 }
6125
6126 #[cfg(feature = "lua")]
6127 #[test]
6128 fn lua_no_zero_blank() {
6129 check_metrics::<LuaParser>(
6132 "local a = 1
6133local b = 2
6134
6135local c = 3 -- trailing
6136local d = 4 -- trailing
6137local e = 5",
6138 "foo.lua",
6139 |metric| {
6140 assert_eq!(metric.loc.sloc(), 6);
6141 assert_eq!(metric.loc.ploc(), 5);
6142 assert_eq!(metric.loc.cloc(), 2);
6143 assert_eq!(metric.loc.blank(), 1);
6144 insta::assert_json_snapshot!(metric.loc);
6145 },
6146 );
6147 }
6148
6149 #[cfg(feature = "lua")]
6150 #[test]
6151 fn lua_blank_zero_sanity() {
6152 check_metrics::<LuaParser>(
6155 "local x = 1
6156local y = 2",
6157 "foo.lua",
6158 |metric| {
6159 assert_eq!(metric.loc.sloc(), 2);
6160 assert_eq!(metric.loc.ploc(), 2);
6161 assert_eq!(metric.loc.lloc(), 2);
6162 assert_eq!(metric.loc.cloc(), 0);
6163 assert_eq!(metric.loc.blank(), 0);
6164 },
6165 );
6166 }
6167
6168 #[cfg(feature = "lua")]
6169 #[test]
6170 fn lua_cloc() {
6171 check_metrics::<LuaParser>(
6172 "-- single line comment
6173local x = 1
6174--[[
6175 block comment
6176 second line
6177]]",
6178 "foo.lua",
6179 |metric| {
6180 assert_eq!(metric.loc.sloc(), 6);
6181 assert_eq!(metric.loc.ploc(), 1);
6182 assert_eq!(metric.loc.lloc(), 1);
6183 assert_eq!(metric.loc.cloc(), 5);
6184 assert_eq!(metric.loc.blank(), 0);
6185 insta::assert_json_snapshot!(metric.loc);
6186 },
6187 );
6188 }
6189
6190 #[cfg(feature = "lua")]
6191 #[test]
6192 fn lua_lloc() {
6193 check_metrics::<LuaParser>(
6194 "local function f(x)
6195 if x > 0 then
6196 local y = x + 1
6197 return y
6198 end
6199 return 0
6200end",
6201 "foo.lua",
6202 |metric| {
6203 assert_eq!(metric.loc.sloc(), 7);
6204 assert_eq!(metric.loc.ploc(), 7);
6205 assert_eq!(metric.loc.lloc(), 5);
6206 assert_eq!(metric.loc.cloc(), 0);
6207 assert_eq!(metric.loc.blank(), 0);
6208 insta::assert_json_snapshot!(metric.loc);
6209 },
6210 );
6211 }
6212
6213 #[cfg(feature = "lua")]
6214 #[test]
6215 fn lua_no_string_lloc() {
6216 check_metrics::<LuaParser>(
6218 "local s = [[
6219 line one
6220 line two
6221]]",
6222 "foo.lua",
6223 |metric| {
6224 assert_eq!(metric.loc.sloc(), 4);
6229 assert_eq!(metric.loc.ploc(), 4);
6230 assert_eq!(metric.loc.lloc(), 1);
6231 assert_eq!(metric.loc.cloc(), 0);
6232 assert_eq!(metric.loc.blank(), 0);
6233 insta::assert_json_snapshot!(metric.loc);
6234 },
6235 );
6236 }
6237
6238 #[cfg(feature = "lua")]
6239 #[test]
6240 fn lua_no_functiondefinition_lloc() {
6241 check_metrics::<LuaParser>(
6244 "local f = function(x)
6245 return x + 1
6246end",
6247 "foo.lua",
6248 |metric| {
6249 assert_eq!(metric.loc.sloc(), 3);
6250 assert_eq!(metric.loc.ploc(), 3);
6251 assert_eq!(metric.loc.lloc(), 2);
6252 assert_eq!(metric.loc.cloc(), 0);
6253 assert_eq!(metric.loc.blank(), 0);
6254 insta::assert_json_snapshot!(metric.loc);
6255 },
6256 );
6257 }
6258
6259 #[cfg(feature = "lua")]
6260 #[test]
6261 fn lua_no_elseif_lloc() {
6262 check_metrics::<LuaParser>(
6264 "local function f(x)
6265 if x > 0 then
6266 return 1
6267 elseif x < 0 then
6268 return -1
6269 else
6270 return 0
6271 end
6272end",
6273 "foo.lua",
6274 |metric| {
6275 assert_eq!(metric.loc.sloc(), 9);
6276 assert_eq!(metric.loc.ploc(), 9);
6277 assert_eq!(metric.loc.lloc(), 5);
6278 assert_eq!(metric.loc.cloc(), 0);
6279 assert_eq!(metric.loc.blank(), 0);
6280 insta::assert_json_snapshot!(metric.loc);
6281 },
6282 );
6283 }
6284
6285 #[cfg(feature = "lua")]
6286 #[test]
6287 fn lua_no_else_lloc() {
6288 check_metrics::<LuaParser>(
6290 "local function f(x)
6291 if x > 0 then
6292 return 1
6293 else
6294 return 0
6295 end
6296end",
6297 "foo.lua",
6298 |metric| {
6299 assert_eq!(metric.loc.sloc(), 7);
6300 assert_eq!(metric.loc.ploc(), 7);
6301 assert_eq!(metric.loc.lloc(), 4);
6302 assert_eq!(metric.loc.cloc(), 0);
6303 assert_eq!(metric.loc.blank(), 0);
6304 insta::assert_json_snapshot!(metric.loc);
6305 },
6306 );
6307 }
6308
6309 #[cfg(feature = "lua")]
6310 #[test]
6311 fn lua_functiondeclaration_lloc() {
6312 check_metrics::<LuaParser>(
6314 "function f()
6315 return 1
6316end",
6317 "foo.lua",
6318 |metric| {
6319 assert_eq!(metric.loc.sloc(), 3);
6320 assert_eq!(metric.loc.ploc(), 3);
6321 assert_eq!(metric.loc.lloc(), 2);
6322 assert_eq!(metric.loc.cloc(), 0);
6323 assert_eq!(metric.loc.blank(), 0);
6324 insta::assert_json_snapshot!(metric.loc);
6325 },
6326 );
6327 }
6328
6329 #[cfg(feature = "lua")]
6330 #[test]
6331 fn lua_local_function_lloc() {
6332 check_metrics::<LuaParser>(
6334 "local function g()
6335 return 2
6336end",
6337 "foo.lua",
6338 |metric| {
6339 assert_eq!(metric.loc.sloc(), 3);
6340 assert_eq!(metric.loc.ploc(), 3);
6341 assert_eq!(metric.loc.lloc(), 2);
6342 assert_eq!(metric.loc.cloc(), 0);
6343 assert_eq!(metric.loc.blank(), 0);
6344 insta::assert_json_snapshot!(metric.loc);
6345 },
6346 );
6347 }
6348
6349 #[cfg(feature = "lua")]
6350 #[test]
6351 fn lua_for_numeric_lloc() {
6352 check_metrics::<LuaParser>(
6353 "for i = 1, 10 do
6354 print(i)
6355end",
6356 "foo.lua",
6357 |metric| {
6358 assert_eq!(metric.loc.sloc(), 3);
6359 assert_eq!(metric.loc.ploc(), 3);
6360 assert_eq!(metric.loc.lloc(), 1);
6361 assert_eq!(metric.loc.cloc(), 0);
6362 assert_eq!(metric.loc.blank(), 0);
6363 insta::assert_json_snapshot!(metric.loc);
6364 },
6365 );
6366 }
6367
6368 #[cfg(feature = "lua")]
6369 #[test]
6370 fn lua_for_generic_lloc() {
6371 check_metrics::<LuaParser>(
6372 "for k, v in pairs(t) do
6373 print(k, v)
6374end",
6375 "foo.lua",
6376 |metric| {
6377 assert_eq!(metric.loc.sloc(), 3);
6378 assert_eq!(metric.loc.ploc(), 3);
6379 assert_eq!(metric.loc.lloc(), 1);
6380 assert_eq!(metric.loc.cloc(), 0);
6381 assert_eq!(metric.loc.blank(), 0);
6382 insta::assert_json_snapshot!(metric.loc);
6383 },
6384 );
6385 }
6386
6387 #[cfg(feature = "lua")]
6388 #[test]
6389 fn lua_repeat_lloc() {
6390 check_metrics::<LuaParser>(
6391 "local i = 0
6392repeat
6393 i = i + 1
6394until i >= 10",
6395 "foo.lua",
6396 |metric| {
6397 assert_eq!(metric.loc.sloc(), 4);
6398 assert_eq!(metric.loc.ploc(), 4);
6399 assert_eq!(metric.loc.lloc(), 3);
6400 assert_eq!(metric.loc.cloc(), 0);
6401 assert_eq!(metric.loc.blank(), 0);
6402 insta::assert_json_snapshot!(metric.loc);
6403 },
6404 );
6405 }
6406
6407 #[cfg(feature = "lua")]
6408 #[test]
6409 fn lua_local_decl_lloc() {
6410 check_metrics::<LuaParser>(
6411 "local x = 1
6412local y, z = 2, 3",
6413 "foo.lua",
6414 |metric| {
6415 assert_eq!(metric.loc.sloc(), 2);
6416 assert_eq!(metric.loc.ploc(), 2);
6417 assert_eq!(metric.loc.lloc(), 2);
6418 assert_eq!(metric.loc.cloc(), 0);
6419 assert_eq!(metric.loc.blank(), 0);
6420 insta::assert_json_snapshot!(metric.loc);
6421 },
6422 );
6423 }
6424
6425 #[cfg(feature = "lua")]
6426 #[test]
6427 fn lua_function_call_lloc() {
6428 check_metrics::<LuaParser>(
6431 "print(\"hello\")
6432local x = 1",
6433 "foo.lua",
6434 |metric| {
6435 assert_eq!(metric.loc.sloc(), 2);
6436 assert_eq!(metric.loc.ploc(), 2);
6437 assert_eq!(metric.loc.lloc(), 1);
6438 assert_eq!(metric.loc.cloc(), 0);
6439 assert_eq!(metric.loc.blank(), 0);
6440 insta::assert_json_snapshot!(metric.loc);
6441 },
6442 );
6443 }
6444
6445 #[cfg(feature = "lua")]
6446 #[test]
6447 fn lua_toplevel_assignment_lloc() {
6448 check_metrics::<LuaParser>(
6451 "x = 1
6452y, z = 2, 3",
6453 "foo.lua",
6454 |metric| {
6455 assert_eq!(metric.loc.sloc(), 2);
6456 assert_eq!(metric.loc.ploc(), 2);
6457 assert_eq!(metric.loc.lloc(), 2);
6458 assert_eq!(metric.loc.cloc(), 0);
6459 assert_eq!(metric.loc.blank(), 0);
6460 insta::assert_json_snapshot!(metric.loc);
6461 },
6462 );
6463 }
6464
6465 #[cfg(feature = "typescript")]
6466 #[test]
6467 fn tsx_basic_loc() {
6468 check_metrics::<TsxParser>(
6469 "// A simple utility function
6470 function add(a: number, b: number): number {
6471 /* multi-line
6472 comment */
6473 return a + b;
6474 }
6475
6476 const greet = (name: string) => {
6477 return `Hello, ${name}`;
6478 };",
6479 "foo.tsx",
6480 |metric| {
6481 insta::assert_json_snapshot!(
6482 metric.loc,
6483 @r#"
6484 {
6485 "sloc": 10,
6486 "ploc": 6,
6487 "lloc": 3,
6488 "cloc": 3,
6489 "blank": 1,
6490 "dloc": 0,
6491 "sloc_average": 3.3333333333333335,
6492 "ploc_average": 2.0,
6493 "lloc_average": 1.0,
6494 "cloc_average": 1.0,
6495 "blank_average": 0.3333333333333333,
6496 "dloc_average": 0.0,
6497 "sloc_min": 3,
6498 "sloc_max": 10,
6499 "cloc_min": 0,
6500 "cloc_max": 3,
6501 "ploc_min": 3,
6502 "ploc_max": 6,
6503 "lloc_min": 1,
6504 "lloc_max": 3,
6505 "blank_min": 0,
6506 "blank_max": 1,
6507 "dloc_min": 0,
6508 "dloc_max": 0
6509 }
6510 "#
6511 );
6512 },
6513 );
6514 }
6515
6516 #[cfg(feature = "typescript")]
6517 #[test]
6518 fn typescript_basic_loc() {
6519 check_metrics::<TypescriptParser>(
6520 "// Line comment
6521 /* Block
6522 comment */
6523 function greet(name: string): string {
6524 return `Hello, ${name}`;
6525 }
6526
6527 const add = (a: number, b: number): number => a + b;",
6528 "foo.ts",
6529 |metric| {
6530 insta::assert_json_snapshot!(
6531 metric.loc,
6532 @r#"
6533 {
6534 "sloc": 8,
6535 "ploc": 4,
6536 "lloc": 2,
6537 "cloc": 3,
6538 "blank": 1,
6539 "dloc": 0,
6540 "sloc_average": 2.6666666666666665,
6541 "ploc_average": 1.3333333333333333,
6542 "lloc_average": 0.6666666666666666,
6543 "cloc_average": 1.0,
6544 "blank_average": 0.3333333333333333,
6545 "dloc_average": 0.0,
6546 "sloc_min": 1,
6547 "sloc_max": 8,
6548 "cloc_min": 0,
6549 "cloc_max": 3,
6550 "ploc_min": 1,
6551 "ploc_max": 4,
6552 "lloc_min": 0,
6553 "lloc_max": 2,
6554 "blank_min": 0,
6555 "blank_max": 1,
6556 "dloc_min": 0,
6557 "dloc_max": 0
6558 }
6559 "#
6560 );
6561 },
6562 );
6563 }
6564
6565 #[cfg(feature = "csharp")]
6566 #[test]
6567 fn csharp_comments() {
6568 check_metrics::<CsharpParser>(
6569 "for (int i = 0; i < 100; i++) {
6570 // Print hello
6571 System.Console.WriteLine(\"hello\");
6572 /// XML doc comment
6573 System.Console.WriteLine(\"hello\");
6574 }",
6575 "foo.cs",
6576 |metric| {
6577 assert_eq!(metric.loc.sloc(), 6);
6578 assert_eq!(metric.loc.ploc(), 4);
6579 assert_eq!(metric.loc.lloc(), 3);
6580 assert_eq!(metric.loc.cloc(), 2);
6581 assert_eq!(metric.loc.blank(), 0);
6582 assert_eq!(metric.loc.dloc(), 0);
6584 insta::assert_json_snapshot!(metric.loc);
6585 },
6586 );
6587 }
6588
6589 #[cfg(feature = "csharp")]
6590 #[test]
6591 fn csharp_blank() {
6592 check_metrics::<CsharpParser>(
6593 "int x = 1;
6594
6595
6596 int y = 2;",
6597 "foo.cs",
6598 |metric| {
6599 assert_eq!(metric.loc.sloc(), 4);
6600 assert_eq!(metric.loc.ploc(), 2);
6601 assert_eq!(metric.loc.lloc(), 2);
6602 assert_eq!(metric.loc.cloc(), 0);
6603 assert_eq!(metric.loc.blank(), 2);
6604 insta::assert_json_snapshot!(metric.loc);
6605 },
6606 );
6607 }
6608
6609 #[cfg(feature = "csharp")]
6610 #[test]
6611 fn csharp_sloc() {
6612 check_metrics::<CsharpParser>(
6613 "for (int i = 0; i < 100; i++) {
6614 System.Console.WriteLine(i);
6615 }",
6616 "foo.cs",
6617 |metric| {
6618 assert_eq!(metric.loc.sloc(), 3);
6619 assert_eq!(metric.loc.ploc(), 3);
6620 assert_eq!(metric.loc.lloc(), 2);
6621 assert_eq!(metric.loc.cloc(), 0);
6622 assert_eq!(metric.loc.blank(), 0);
6623 insta::assert_json_snapshot!(metric.loc);
6624 },
6625 );
6626 }
6627
6628 #[cfg(feature = "csharp")]
6629 #[test]
6630 fn csharp_module_sloc() {
6631 check_metrics::<CsharpParser>(
6632 "namespace HelloWorld {
6633 class Program { }
6634 }",
6635 "foo.cs",
6636 |metric| {
6637 assert_eq!(metric.loc.sloc(), 3);
6638 assert_eq!(metric.loc.ploc(), 3);
6639 assert_eq!(metric.loc.lloc(), 0);
6640 assert_eq!(metric.loc.cloc(), 0);
6641 assert_eq!(metric.loc.blank(), 0);
6642 insta::assert_json_snapshot!(metric.loc);
6643 },
6644 );
6645 }
6646
6647 #[cfg(feature = "csharp")]
6648 #[test]
6649 fn csharp_single_ploc() {
6650 check_metrics::<CsharpParser>("int x = 1;", "foo.cs", |metric| {
6651 assert_eq!(metric.loc.sloc(), 1);
6652 assert_eq!(metric.loc.ploc(), 1);
6653 assert_eq!(metric.loc.lloc(), 1);
6654 assert_eq!(metric.loc.cloc(), 0);
6655 assert_eq!(metric.loc.blank(), 0);
6656 insta::assert_json_snapshot!(metric.loc);
6657 });
6658 }
6659
6660 #[cfg(feature = "csharp")]
6661 #[test]
6662 fn csharp_simple_ploc() {
6663 check_metrics::<CsharpParser>(
6664 "for (int i = 0; i < 100; i++) {
6665 System.Console.WriteLine(i);
6666 }",
6667 "foo.cs",
6668 |metric| {
6669 assert_eq!(metric.loc.sloc(), 3);
6670 assert_eq!(metric.loc.ploc(), 3);
6671 assert_eq!(metric.loc.lloc(), 2);
6672 assert_eq!(metric.loc.cloc(), 0);
6673 assert_eq!(metric.loc.blank(), 0);
6674 insta::assert_json_snapshot!(metric.loc);
6675 },
6676 );
6677 }
6678
6679 #[cfg(feature = "csharp")]
6680 #[test]
6681 fn csharp_multi_ploc() {
6682 check_metrics::<CsharpParser>(
6683 "int x = 1;
6684 for (int i = 0; i < 100; i++) {
6685 System.Console.WriteLine(i);
6686 }",
6687 "foo.cs",
6688 |metric| {
6689 assert_eq!(metric.loc.sloc(), 4);
6690 assert_eq!(metric.loc.ploc(), 4);
6691 assert_eq!(metric.loc.lloc(), 3);
6692 assert_eq!(metric.loc.cloc(), 0);
6693 assert_eq!(metric.loc.blank(), 0);
6694 insta::assert_json_snapshot!(metric.loc);
6695 },
6696 );
6697 }
6698
6699 #[cfg(feature = "csharp")]
6700 #[test]
6701 fn csharp_single_statement_lloc() {
6702 check_metrics::<CsharpParser>("int max = 10;", "foo.cs", |metric| {
6703 assert_eq!(metric.loc.sloc(), 1);
6704 assert_eq!(metric.loc.ploc(), 1);
6705 assert_eq!(metric.loc.lloc(), 1);
6706 assert_eq!(metric.loc.cloc(), 0);
6707 assert_eq!(metric.loc.blank(), 0);
6708 insta::assert_json_snapshot!(metric.loc);
6709 });
6710 }
6711
6712 #[cfg(feature = "csharp")]
6713 #[test]
6714 fn csharp_for_lloc() {
6715 check_metrics::<CsharpParser>(
6716 "for (int i = 0; i < 10; i++) {
6717 System.Console.WriteLine(i);
6718 }",
6719 "foo.cs",
6720 |metric| {
6721 assert_eq!(metric.loc.sloc(), 3);
6722 assert_eq!(metric.loc.ploc(), 3);
6723 assert_eq!(metric.loc.lloc(), 2);
6724 assert_eq!(metric.loc.cloc(), 0);
6725 assert_eq!(metric.loc.blank(), 0);
6726 insta::assert_json_snapshot!(metric.loc);
6727 },
6728 );
6729 }
6730
6731 #[cfg(feature = "csharp")]
6732 #[test]
6733 fn csharp_foreach_lloc() {
6734 check_metrics::<CsharpParser>(
6735 "foreach (var item in items) {
6736 System.Console.WriteLine(item);
6737 }",
6738 "foo.cs",
6739 |metric| {
6740 assert_eq!(metric.loc.sloc(), 3);
6741 assert_eq!(metric.loc.ploc(), 3);
6742 assert_eq!(metric.loc.lloc(), 2);
6743 assert_eq!(metric.loc.cloc(), 0);
6744 assert_eq!(metric.loc.blank(), 0);
6745 insta::assert_json_snapshot!(metric.loc);
6746 },
6747 );
6748 }
6749
6750 #[cfg(feature = "csharp")]
6751 #[test]
6752 fn csharp_while_lloc() {
6753 check_metrics::<CsharpParser>(
6754 "int i = 0;
6755 while (i < 10) {
6756 i++;
6757 }",
6758 "foo.cs",
6759 |metric| {
6760 assert_eq!(metric.loc.sloc(), 4);
6761 assert_eq!(metric.loc.ploc(), 4);
6762 assert_eq!(metric.loc.lloc(), 3);
6763 assert_eq!(metric.loc.cloc(), 0);
6764 assert_eq!(metric.loc.blank(), 0);
6765 insta::assert_json_snapshot!(metric.loc);
6766 },
6767 );
6768 }
6769
6770 #[cfg(feature = "csharp")]
6771 #[test]
6772 fn csharp_do_while_lloc() {
6773 check_metrics::<CsharpParser>(
6774 "int i = 0;
6775 do {
6776 i++;
6777 } while (i < 10);",
6778 "foo.cs",
6779 |metric| {
6780 assert_eq!(metric.loc.sloc(), 4);
6781 assert_eq!(metric.loc.ploc(), 4);
6782 assert_eq!(metric.loc.lloc(), 3);
6783 assert_eq!(metric.loc.cloc(), 0);
6784 assert_eq!(metric.loc.blank(), 0);
6785 insta::assert_json_snapshot!(metric.loc);
6786 },
6787 );
6788 }
6789
6790 #[cfg(feature = "csharp")]
6791 #[test]
6792 fn csharp_switch_lloc() {
6793 check_metrics::<CsharpParser>(
6794 "switch (x) {
6795 case 1: System.Console.WriteLine(1); break;
6796 case 2: System.Console.WriteLine(2); break;
6797 default: System.Console.WriteLine(0); break;
6798 }
6799 string s = x switch { 1 => \"one\", _ => \"other\" };",
6800 "foo.cs",
6801 |metric| {
6802 assert_eq!(metric.loc.sloc(), 6);
6803 assert_eq!(metric.loc.ploc(), 6);
6804 assert_eq!(metric.loc.lloc(), 8);
6805 assert_eq!(metric.loc.cloc(), 0);
6806 assert_eq!(metric.loc.blank(), 0);
6807 insta::assert_json_snapshot!(metric.loc);
6808 },
6809 );
6810 }
6811
6812 #[cfg(feature = "csharp")]
6813 #[test]
6814 fn csharp_continue_lloc() {
6815 check_metrics::<CsharpParser>(
6816 "for (int i = 0; i < 10; i++) {
6817 if (i == 5) continue;
6818 System.Console.WriteLine(i);
6819 }",
6820 "foo.cs",
6821 |metric| {
6822 assert_eq!(metric.loc.sloc(), 4);
6823 assert_eq!(metric.loc.ploc(), 4);
6824 assert_eq!(metric.loc.lloc(), 4);
6825 assert_eq!(metric.loc.cloc(), 0);
6826 assert_eq!(metric.loc.blank(), 0);
6827 insta::assert_json_snapshot!(metric.loc);
6828 },
6829 );
6830 }
6831
6832 #[cfg(feature = "csharp")]
6833 #[test]
6834 fn csharp_try_lloc() {
6835 check_metrics::<CsharpParser>(
6836 "try {
6837 System.Console.WriteLine(\"try\");
6838 } catch (System.Exception e) {
6839 throw new System.Exception(\"caught\");
6840 } finally {
6841 System.Console.WriteLine(\"done\");
6842 }",
6843 "foo.cs",
6844 |metric| {
6845 assert_eq!(metric.loc.sloc(), 7);
6846 assert_eq!(metric.loc.ploc(), 7);
6847 assert_eq!(metric.loc.lloc(), 4);
6848 assert_eq!(metric.loc.cloc(), 0);
6849 assert_eq!(metric.loc.blank(), 0);
6850 insta::assert_json_snapshot!(metric.loc);
6851 },
6852 );
6853 }
6854
6855 #[cfg(feature = "csharp")]
6856 #[test]
6857 fn csharp_class_loc() {
6858 check_metrics::<CsharpParser>(
6859 "class A {
6860 int x;
6861 public void M() {
6862 System.Console.WriteLine(x);
6863 }
6864 }",
6865 "foo.cs",
6866 |metric| {
6867 assert_eq!(metric.loc.sloc(), 6);
6868 assert_eq!(metric.loc.ploc(), 6);
6869 assert_eq!(metric.loc.lloc(), 1);
6870 assert_eq!(metric.loc.cloc(), 0);
6871 assert_eq!(metric.loc.blank(), 0);
6872 insta::assert_json_snapshot!(metric.loc);
6873 },
6874 );
6875 }
6876
6877 #[cfg(feature = "csharp")]
6878 #[test]
6879 fn csharp_expressions_lloc() {
6880 check_metrics::<CsharpParser>(
6881 "int a = 1;
6882 int b = 2;
6883 int c = a + b;
6884 System.Console.WriteLine(c);",
6885 "foo.cs",
6886 |metric| {
6887 assert_eq!(metric.loc.sloc(), 4);
6888 assert_eq!(metric.loc.ploc(), 4);
6889 assert_eq!(metric.loc.lloc(), 4);
6890 assert_eq!(metric.loc.cloc(), 0);
6891 assert_eq!(metric.loc.blank(), 0);
6892 insta::assert_json_snapshot!(metric.loc);
6893 },
6894 );
6895 }
6896
6897 #[cfg(feature = "csharp")]
6898 #[test]
6899 fn csharp_statement_inline_loc() {
6900 check_metrics::<CsharpParser>(
6901 "if (x > 0) System.Console.WriteLine(x);",
6902 "foo.cs",
6903 |metric| {
6904 assert_eq!(metric.loc.sloc(), 1);
6905 assert_eq!(metric.loc.ploc(), 1);
6906 assert_eq!(metric.loc.lloc(), 2);
6907 assert_eq!(metric.loc.cloc(), 0);
6908 assert_eq!(metric.loc.blank(), 0);
6909 insta::assert_json_snapshot!(metric.loc);
6910 },
6911 );
6912 }
6913
6914 #[cfg(feature = "csharp")]
6915 #[test]
6916 fn csharp_general_loc() {
6917 check_metrics::<CsharpParser>(
6918 "using System;
6919 namespace Demo {
6920 class A {
6921 public void M() {
6922 Console.WriteLine(\"hi\");
6923 }
6924 }
6925 class B {
6926 public int N() { return 0; }
6927 }
6928 }",
6929 "foo.cs",
6930 |metric| {
6931 assert_eq!(metric.loc.sloc(), 11);
6932 assert_eq!(metric.loc.ploc(), 11);
6933 assert_eq!(metric.loc.lloc(), 2);
6934 assert_eq!(metric.loc.cloc(), 0);
6935 assert_eq!(metric.loc.blank(), 0);
6936 insta::assert_json_snapshot!(metric.loc);
6937 },
6938 );
6939 }
6940
6941 #[cfg(feature = "csharp")]
6942 #[test]
6943 fn csharp_using_lloc() {
6944 check_metrics::<CsharpParser>(
6949 "using System;
6950 using System.IO;
6951 class A {
6952 public void M() {
6953 using (var s = File.OpenRead(\"x\")) {
6954 Console.WriteLine(s);
6955 }
6956 using var t = File.OpenRead(\"y\");
6957 Console.WriteLine(t);
6958 }
6959 }",
6960 "foo.cs",
6961 |metric| {
6962 assert_eq!(metric.loc.sloc(), 11);
6963 assert_eq!(metric.loc.ploc(), 11);
6964 assert_eq!(metric.loc.lloc(), 4);
6965 assert_eq!(metric.loc.cloc(), 0);
6966 assert_eq!(metric.loc.blank(), 0);
6967 insta::assert_json_snapshot!(metric.loc);
6968 },
6969 );
6970 }
6971
6972 #[cfg(feature = "kotlin")]
6973 #[test]
6974 fn kotlin_loc_basic() {
6975 check_metrics::<KotlinParser>(
6976 "// A simple function
6977 fun greet(name: String): String {
6978 val greeting = \"Hello, \" + name
6979 if (name.isEmpty()) {
6980 return \"Hello, World!\"
6981 }
6982 return greeting
6983 }",
6984 "foo.kt",
6985 |metric| {
6986 insta::assert_json_snapshot!(
6987 metric.loc,
6988 @r#"
6989 {
6990 "sloc": 8,
6991 "ploc": 7,
6992 "lloc": 4,
6993 "cloc": 1,
6994 "blank": 0,
6995 "dloc": 0,
6996 "sloc_average": 4.0,
6997 "ploc_average": 3.5,
6998 "lloc_average": 2.0,
6999 "cloc_average": 0.5,
7000 "blank_average": 0.0,
7001 "dloc_average": 0.0,
7002 "sloc_min": 7,
7003 "sloc_max": 8,
7004 "cloc_min": 0,
7005 "cloc_max": 1,
7006 "ploc_min": 7,
7007 "ploc_max": 7,
7008 "lloc_min": 4,
7009 "lloc_max": 4,
7010 "blank_min": 0,
7011 "blank_max": 0,
7012 "dloc_min": 0,
7013 "dloc_max": 0
7014 }
7015 "#
7016 );
7017 },
7018 );
7019 }
7020
7021 #[cfg(feature = "kotlin")]
7022 #[test]
7023 fn kotlin_loc_bare_expression() {
7024 check_metrics::<KotlinParser>(
7025 "fun main() {
7026 val x = 42
7027 println(x)
7028 listOf(1, 2, 3).forEach { println(it) }
7029 }",
7030 "foo.kt",
7031 |metric| {
7032 insta::assert_json_snapshot!(
7036 metric.loc,
7037 @r#"
7038 {
7039 "sloc": 5,
7040 "ploc": 5,
7041 "lloc": 3,
7042 "cloc": 0,
7043 "blank": 0,
7044 "dloc": 0,
7045 "sloc_average": 2.5,
7046 "ploc_average": 2.5,
7047 "lloc_average": 1.5,
7048 "cloc_average": 0.0,
7049 "blank_average": 0.0,
7050 "dloc_average": 0.0,
7051 "sloc_min": 5,
7052 "sloc_max": 5,
7053 "cloc_min": 0,
7054 "cloc_max": 0,
7055 "ploc_min": 5,
7056 "ploc_max": 5,
7057 "lloc_min": 3,
7058 "lloc_max": 3,
7059 "blank_min": 0,
7060 "blank_max": 0,
7061 "dloc_min": 0,
7062 "dloc_max": 0
7063 }
7064 "#
7065 );
7066 },
7067 );
7068 }
7069
7070 #[cfg(feature = "bash")]
7071 #[test]
7072 fn bash_loc() {
7073 check_metrics::<BashParser>(
7074 "#!/bin/bash
7075# This is a comment
7076f() {
7077 echo 'hello'
7078}
7079
7080# Another comment
7081f",
7082 "foo.sh",
7083 |metric| {
7084 assert_eq!(metric.loc.sloc(), 8);
7085 assert_eq!(metric.loc.ploc(), 4);
7086 assert_eq!(metric.loc.lloc(), 3);
7087 assert_eq!(metric.loc.cloc(), 3);
7088 assert_eq!(metric.loc.blank(), 1);
7089 insta::assert_json_snapshot!(metric.loc);
7090 },
7091 );
7092 }
7093
7094 #[cfg(feature = "python")]
7098 #[test]
7099 fn python_cloc_crlf_matches_lf() {
7100 check_metrics::<PythonParser>("# comment\nx = 1", "foo.py", |m| {
7101 assert_eq!(m.loc.cloc(), 1);
7102 assert_eq!(m.loc.ploc(), 1);
7103 assert_eq!(m.loc.sloc(), 2);
7104 assert_eq!(m.loc.blank(), 0);
7105 });
7106 check_metrics::<PythonParser>("# comment\r\nx = 1", "foo.py", |m| {
7107 assert_eq!(m.loc.cloc(), 1);
7108 assert_eq!(m.loc.ploc(), 1);
7109 assert_eq!(m.loc.sloc(), 2);
7110 assert_eq!(m.loc.blank(), 0);
7111 });
7112 check_metrics::<PythonParser>("# comment\rx = 1", "foo.py", |m| {
7116 assert_eq!(m.loc.cloc(), 1);
7117 assert_eq!(m.loc.ploc(), 1);
7118 assert_eq!(m.loc.sloc(), 2);
7119 assert_eq!(m.loc.blank(), 0);
7120 });
7121 }
7122
7123 #[cfg(feature = "python")]
7124 #[test]
7125 fn python_blank_crlf_matches_lf() {
7126 check_metrics::<PythonParser>("# comment\n\nx = 1", "foo.py", |m| {
7127 assert_eq!(m.loc.blank(), 1);
7128 });
7129 check_metrics::<PythonParser>("# comment\r\n\r\nx = 1", "foo.py", |m| {
7130 assert_eq!(m.loc.blank(), 1);
7131 });
7132 check_metrics::<PythonParser>("# comment\r\rx = 1", "foo.py", |m| {
7134 assert_eq!(m.loc.blank(), 1);
7135 });
7136 }
7137
7138 #[cfg(feature = "rust")]
7139 #[test]
7140 fn rust_cloc_crlf_matches_lf() {
7141 check_metrics::<RustParser>(
7142 "fn f() {\n // comment\n let x = 1;\n}",
7143 "foo.rs",
7144 |m| {
7145 assert_eq!(m.loc.cloc(), 1);
7146 assert_eq!(m.loc.sloc(), 4);
7147 },
7148 );
7149 check_metrics::<RustParser>(
7150 "fn f() {\r\n // comment\r\n let x = 1;\r\n}",
7151 "foo.rs",
7152 |m| {
7153 assert_eq!(m.loc.cloc(), 1);
7154 assert_eq!(m.loc.sloc(), 4);
7155 },
7156 );
7157 check_metrics::<RustParser>(
7160 "fn f() {\r // comment\r let x = 1;\r}",
7161 "foo.rs",
7162 |m| {
7163 assert_eq!(m.loc.cloc(), 1);
7164 assert_eq!(m.loc.sloc(), 4);
7165 },
7166 );
7167 }
7168
7169 #[cfg(feature = "tcl")]
7170 #[test]
7171 fn tcl_blank() {
7172 check_metrics::<TclParser>("set x 1\n\nset y 2", "foo.tcl", |metric| {
7173 assert_eq!(metric.loc.sloc(), 3);
7174 assert_eq!(metric.loc.ploc(), 2);
7175 assert_eq!(metric.loc.lloc(), 2);
7176 assert_eq!(metric.loc.cloc(), 0);
7177 assert_eq!(metric.loc.blank(), 1);
7178 insta::assert_json_snapshot!(metric.loc);
7179 });
7180 }
7181
7182 #[cfg(feature = "tcl")]
7183 #[test]
7184 fn tcl_no_zero_blank() {
7185 check_metrics::<TclParser>(
7189 "set a 1\nset b 2\n\nset c 3 ;# trailing\nset d 4 ;# trailing\nset e 5",
7190 "foo.tcl",
7191 |metric| {
7192 assert_eq!(metric.loc.sloc(), 6);
7193 assert_eq!(metric.loc.ploc(), 5);
7194 assert_eq!(metric.loc.cloc(), 2);
7195 assert_eq!(metric.loc.blank(), 1);
7196 },
7197 );
7198 }
7199
7200 #[cfg(feature = "tcl")]
7206 #[test]
7207 fn tcl_cloc() {
7208 check_metrics::<TclParser>("# This is a comment\nset x 1", "foo.tcl", |metric| {
7209 assert_eq!(metric.loc.sloc(), 2);
7210 assert_eq!(metric.loc.ploc(), 1);
7211 assert_eq!(metric.loc.lloc(), 1);
7212 assert_eq!(metric.loc.cloc(), 1);
7213 assert_eq!(metric.loc.blank(), 0);
7214 insta::assert_json_snapshot!(metric.loc);
7215 });
7216 }
7217
7218 #[cfg(feature = "tcl")]
7219 #[test]
7220 fn tcl_lloc() {
7221 check_metrics::<TclParser>(
7222 "proc f {x} {
7223 while {$x > 0} {
7224 if {$x > 10} {
7225 set x [expr {$x - 1}]
7226 }
7227 }
7228}",
7229 "foo.tcl",
7230 |metric| {
7231 assert_eq!(metric.loc.sloc(), 7);
7232 assert_eq!(metric.loc.ploc(), 7);
7233 assert_eq!(metric.loc.lloc(), 4);
7234 assert_eq!(metric.loc.cloc(), 0);
7235 assert_eq!(metric.loc.blank(), 0);
7236 insta::assert_json_snapshot!(metric.loc);
7237 },
7238 );
7239 }
7240
7241 #[cfg(feature = "tcl")]
7242 #[test]
7243 fn tcl_no_command_substitution_lloc() {
7244 check_metrics::<TclParser>("puts [string toupper x]", "foo.tcl", |metric| {
7246 assert_eq!(metric.loc.sloc(), 1);
7247 assert_eq!(metric.loc.ploc(), 1);
7248 assert_eq!(metric.loc.lloc(), 1);
7249 assert_eq!(metric.loc.cloc(), 0);
7250 assert_eq!(metric.loc.blank(), 0);
7251 insta::assert_json_snapshot!(metric.loc);
7252 });
7253 }
7254
7255 #[cfg(feature = "tcl")]
7256 #[test]
7257 fn tcl_procedure_lloc() {
7258 check_metrics::<TclParser>("proc foo {} {\n puts hello\n}", "foo.tcl", |metric| {
7259 assert_eq!(metric.loc.sloc(), 3);
7260 assert_eq!(metric.loc.ploc(), 3);
7261 assert_eq!(metric.loc.lloc(), 2);
7262 assert_eq!(metric.loc.cloc(), 0);
7263 assert_eq!(metric.loc.blank(), 0);
7264 insta::assert_json_snapshot!(metric.loc);
7265 });
7266 }
7267
7268 #[cfg(feature = "tcl")]
7269 #[test]
7270 fn tcl_if_lloc() {
7271 check_metrics::<TclParser>("if {1} {\n puts hello\n}", "foo.tcl", |metric| {
7272 assert_eq!(metric.loc.sloc(), 3);
7273 assert_eq!(metric.loc.ploc(), 3);
7274 assert_eq!(metric.loc.lloc(), 2);
7275 assert_eq!(metric.loc.cloc(), 0);
7276 assert_eq!(metric.loc.blank(), 0);
7277 insta::assert_json_snapshot!(metric.loc);
7278 });
7279 }
7280
7281 #[cfg(feature = "tcl")]
7282 #[test]
7283 fn tcl_elseif_lloc() {
7284 check_metrics::<TclParser>(
7286 "if {$x > 10} {
7287 puts big
7288} elseif {$x > 5} {
7289 puts medium
7290} else {
7291 puts small
7292}",
7293 "foo.tcl",
7294 |metric| {
7295 assert_eq!(metric.loc.sloc(), 7);
7296 assert_eq!(metric.loc.ploc(), 7);
7297 assert_eq!(metric.loc.lloc(), 5);
7298 assert_eq!(metric.loc.cloc(), 0);
7299 assert_eq!(metric.loc.blank(), 0);
7300 insta::assert_json_snapshot!(metric.loc);
7301 },
7302 );
7303 }
7304
7305 #[cfg(feature = "tcl")]
7306 #[test]
7307 fn tcl_while_lloc() {
7308 check_metrics::<TclParser>(
7309 "while {$x > 0} {\n set x [expr {$x - 1}]\n}",
7310 "foo.tcl",
7311 |metric| {
7312 assert_eq!(metric.loc.sloc(), 3);
7313 assert_eq!(metric.loc.ploc(), 3);
7314 assert_eq!(metric.loc.lloc(), 2);
7315 assert_eq!(metric.loc.cloc(), 0);
7316 assert_eq!(metric.loc.blank(), 0);
7317 insta::assert_json_snapshot!(metric.loc);
7318 },
7319 );
7320 }
7321
7322 #[cfg(feature = "tcl")]
7323 #[test]
7324 fn tcl_foreach_lloc() {
7325 check_metrics::<TclParser>(
7326 "foreach item {a b c} {\n puts $item\n}",
7327 "foo.tcl",
7328 |metric| {
7329 assert_eq!(metric.loc.sloc(), 3);
7330 assert_eq!(metric.loc.ploc(), 3);
7331 assert_eq!(metric.loc.lloc(), 2);
7332 assert_eq!(metric.loc.cloc(), 0);
7333 assert_eq!(metric.loc.blank(), 0);
7334 insta::assert_json_snapshot!(metric.loc);
7335 },
7336 );
7337 }
7338
7339 #[cfg(feature = "tcl")]
7340 #[test]
7341 fn tcl_set_lloc() {
7342 check_metrics::<TclParser>("set x 42", "foo.tcl", |metric| {
7343 assert_eq!(metric.loc.sloc(), 1);
7344 assert_eq!(metric.loc.ploc(), 1);
7345 assert_eq!(metric.loc.lloc(), 1);
7346 assert_eq!(metric.loc.cloc(), 0);
7347 assert_eq!(metric.loc.blank(), 0);
7348 insta::assert_json_snapshot!(metric.loc);
7349 });
7350 }
7351
7352 #[cfg(feature = "tcl")]
7353 #[test]
7354 fn tcl_global_lloc() {
7355 check_metrics::<TclParser>("global x", "foo.tcl", |metric| {
7356 assert_eq!(metric.loc.sloc(), 1);
7357 assert_eq!(metric.loc.ploc(), 1);
7358 assert_eq!(metric.loc.lloc(), 1);
7359 assert_eq!(metric.loc.cloc(), 0);
7360 assert_eq!(metric.loc.blank(), 0);
7361 insta::assert_json_snapshot!(metric.loc);
7362 });
7363 }
7364
7365 #[cfg(feature = "tcl")]
7366 #[test]
7367 fn tcl_try_catch_lloc() {
7368 check_metrics::<TclParser>(
7370 "catch {
7371 set x 1
7372} result
7373try {
7374 set y 2
7375} on error {msg} {
7376 puts $msg
7377}",
7378 "foo.tcl",
7379 |metric| {
7380 assert_eq!(metric.loc.sloc(), 8);
7381 assert_eq!(metric.loc.ploc(), 8);
7382 assert_eq!(metric.loc.lloc(), 5);
7383 assert_eq!(metric.loc.cloc(), 0);
7384 assert_eq!(metric.loc.blank(), 0);
7385 insta::assert_json_snapshot!(metric.loc);
7386 },
7387 );
7388 }
7389
7390 #[cfg(feature = "tcl")]
7391 #[test]
7392 fn tcl_namespace_lloc() {
7393 check_metrics::<TclParser>(
7394 "namespace eval myns {\n set x 1\n}",
7395 "foo.tcl",
7396 |metric| {
7397 assert_eq!(metric.loc.sloc(), 3);
7398 assert_eq!(metric.loc.ploc(), 3);
7399 assert_eq!(metric.loc.lloc(), 2);
7400 assert_eq!(metric.loc.cloc(), 0);
7401 assert_eq!(metric.loc.blank(), 0);
7402 insta::assert_json_snapshot!(metric.loc);
7403 },
7404 );
7405 }
7406
7407 #[cfg(feature = "tcl")]
7408 #[test]
7409 fn tcl_regexp_lloc() {
7410 check_metrics::<TclParser>("regexp {^[0-9]+$} $x", "foo.tcl", |metric| {
7411 assert_eq!(metric.loc.sloc(), 1);
7412 assert_eq!(metric.loc.ploc(), 1);
7413 assert_eq!(metric.loc.lloc(), 1);
7414 assert_eq!(metric.loc.cloc(), 0);
7415 assert_eq!(metric.loc.blank(), 0);
7416 insta::assert_json_snapshot!(metric.loc);
7417 });
7418 }
7419
7420 #[cfg(feature = "tcl")]
7421 #[test]
7422 fn tcl_expr_cmd_lloc() {
7423 check_metrics::<TclParser>("expr {1 + 2}", "foo.tcl", |metric| {
7424 assert_eq!(metric.loc.sloc(), 1);
7425 assert_eq!(metric.loc.ploc(), 1);
7426 assert_eq!(metric.loc.lloc(), 1);
7427 assert_eq!(metric.loc.cloc(), 0);
7428 assert_eq!(metric.loc.blank(), 0);
7429 insta::assert_json_snapshot!(metric.loc);
7430 });
7431 }
7432
7433 #[cfg(feature = "tcl")]
7434 #[test]
7435 fn tcl_no_expr_cmd_substitution_lloc() {
7436 check_metrics::<TclParser>("set x [expr {1 + 2}]", "foo.tcl", |metric| {
7438 assert_eq!(metric.loc.sloc(), 1);
7439 assert_eq!(metric.loc.ploc(), 1);
7440 assert_eq!(metric.loc.lloc(), 1);
7441 assert_eq!(metric.loc.cloc(), 0);
7442 assert_eq!(metric.loc.blank(), 0);
7443 insta::assert_json_snapshot!(metric.loc);
7444 });
7445 }
7446
7447 #[cfg(feature = "tcl")]
7448 #[test]
7449 fn tcl_nested_commands_lloc() {
7450 check_metrics::<TclParser>(
7452 "proc f {x} {
7453 set y [expr {$x * 2}]
7454 puts $y
7455}",
7456 "foo.tcl",
7457 |metric| {
7458 assert_eq!(metric.loc.sloc(), 4);
7459 assert_eq!(metric.loc.ploc(), 4);
7460 assert_eq!(metric.loc.lloc(), 3);
7461 assert_eq!(metric.loc.cloc(), 0);
7462 assert_eq!(metric.loc.blank(), 0);
7463 insta::assert_json_snapshot!(metric.loc);
7464 },
7465 );
7466 }
7467
7468 #[cfg(feature = "tcl")]
7469 #[test]
7470 fn tcl_command_lloc() {
7471 check_metrics::<TclParser>("puts hello", "foo.tcl", |metric| {
7472 assert_eq!(metric.loc.sloc(), 1);
7473 assert_eq!(metric.loc.ploc(), 1);
7474 assert_eq!(metric.loc.lloc(), 1);
7475 assert_eq!(metric.loc.cloc(), 0);
7476 assert_eq!(metric.loc.blank(), 0);
7477 insta::assert_json_snapshot!(metric.loc);
7478 });
7479 }
7480
7481 #[cfg(feature = "tcl")]
7482 #[test]
7483 fn tcl_no_else_lloc() {
7484 check_metrics::<TclParser>(
7486 "if {1} {\n puts yes\n} else {\n puts no\n}",
7487 "foo.tcl",
7488 |metric| {
7489 assert_eq!(metric.loc.sloc(), 5);
7490 assert_eq!(metric.loc.ploc(), 5);
7491 assert_eq!(metric.loc.lloc(), 3);
7492 assert_eq!(metric.loc.cloc(), 0);
7493 assert_eq!(metric.loc.blank(), 0);
7494 insta::assert_json_snapshot!(metric.loc);
7495 },
7496 );
7497 }
7498
7499 #[cfg(feature = "tcl")]
7500 #[test]
7501 fn tcl_no_finally_lloc() {
7502 check_metrics::<TclParser>(
7505 "proc f {} {\n try {\n puts hi\n } finally {\n puts done\n }\n}",
7506 "foo.tcl",
7507 |metric| {
7508 assert_eq!(
7509 metric.loc.lloc(),
7510 4,
7511 "finally adds 0 lloc; would be 5 if finally counted"
7512 );
7513 },
7514 );
7515 }
7516
7517 #[cfg(feature = "tcl")]
7518 #[test]
7519 fn tcl_multiline_block() {
7520 check_metrics::<TclParser>(
7521 "proc f {x} {
7522 set a 1
7523
7524 set b 2
7525 return [expr {$a + $b}]
7526}",
7527 "foo.tcl",
7528 |metric| {
7529 assert_eq!(metric.loc.sloc(), 6);
7530 assert_eq!(metric.loc.ploc(), 5);
7531 assert_eq!(metric.loc.lloc(), 4);
7532 assert_eq!(metric.loc.cloc(), 0);
7533 assert_eq!(metric.loc.blank(), 1);
7534 insta::assert_json_snapshot!(metric.loc);
7535 },
7536 );
7537 }
7538
7539 #[cfg(feature = "tcl")]
7540 #[test]
7541 fn tcl_no_string_lloc() {
7542 check_metrics::<TclParser>(
7555 "set s \"line one\nline two\nline three\"",
7556 "foo.tcl",
7557 |metric| {
7558 assert_eq!(metric.loc.sloc(), 3);
7559 assert_eq!(metric.loc.ploc(), 3);
7560 assert_eq!(metric.loc.lloc(), 1);
7561 assert_eq!(metric.loc.cloc(), 0);
7562 assert_eq!(metric.loc.blank(), 0);
7563 insta::assert_json_snapshot!(metric.loc);
7564 },
7565 );
7566 }
7567
7568 #[cfg(feature = "tcl")]
7569 #[test]
7570 fn tcl_multiline_quoted_word_credits_every_row_to_ploc() {
7571 check_metrics::<TclParser>(
7580 "set msg \"line one\nline two\nline three\"\nputs $msg",
7581 "foo.tcl",
7582 |metric| {
7583 assert_eq!(metric.loc.sloc(), 4);
7584 assert_eq!(metric.loc.ploc(), 4);
7585 assert_eq!(metric.loc.lloc(), 2);
7586 assert_eq!(metric.loc.cloc(), 0);
7587 assert_eq!(metric.loc.blank(), 0);
7588 },
7589 );
7590
7591 check_metrics::<TclParser>("set msg \"one\n\nthree\"", "foo.tcl", |metric| {
7595 assert_eq!(metric.loc.sloc(), 3);
7596 assert_eq!(metric.loc.ploc(), 3);
7597 assert_eq!(metric.loc.cloc(), 0);
7598 assert_eq!(metric.loc.blank(), 0);
7599 });
7600 }
7601
7602 #[cfg(feature = "tcl")]
7603 #[test]
7604 fn tcl_braced_word_body_blank_rows_stay_blank() {
7605 check_metrics::<TclParser>(
7616 "proc p {} {\n set x 1\n\n set y 2\n}",
7617 "foo.tcl",
7618 |metric| {
7619 assert_eq!(metric.loc.sloc(), 5);
7620 assert_eq!(metric.loc.ploc(), 4);
7621 assert_eq!(metric.loc.lloc(), 3);
7622 assert_eq!(metric.loc.cloc(), 0);
7623 assert_eq!(metric.loc.blank(), 1);
7624 },
7625 );
7626 }
7627
7628 #[cfg(feature = "javascript")]
7629 #[test]
7630 fn javascript_blank() {
7631 check_metrics::<JavascriptParser>(
7632 "// header comment
7633 function f() {
7634
7635 var x = 1;
7636
7637 var y = 2;
7638 }",
7639 "foo.js",
7640 |metric| {
7641 assert_eq!(metric.loc.sloc(), 7);
7642 assert_eq!(metric.loc.ploc(), 4);
7643 assert_eq!(metric.loc.lloc(), 2);
7644 assert_eq!(metric.loc.cloc(), 1);
7645 assert_eq!(metric.loc.blank(), 2);
7646 insta::assert_json_snapshot!(metric.loc);
7647 },
7648 );
7649 }
7650
7651 #[cfg(feature = "javascript")]
7652 #[test]
7653 fn javascript_cloc() {
7654 check_metrics::<JavascriptParser>(
7655 "// line comment
7656 /* block
7657 comment */
7658 function f() {
7659 return 1; // inline
7660 }",
7661 "foo.js",
7662 |metric| {
7663 assert_eq!(metric.loc.sloc(), 6);
7664 assert_eq!(metric.loc.ploc(), 3);
7665 assert_eq!(metric.loc.lloc(), 1);
7666 assert_eq!(metric.loc.cloc(), 4);
7667 assert_eq!(metric.loc.blank(), 0);
7668 insta::assert_json_snapshot!(metric.loc);
7669 },
7670 );
7671 }
7672
7673 #[cfg(feature = "javascript")]
7674 #[test]
7675 fn javascript_cloc_html_comment() {
7676 check_metrics::<JavascriptParser>(
7680 "<!-- header comment -->
7681function f() {
7682 return 1;
7683}",
7684 "foo.js",
7685 |metric| {
7686 assert_eq!(metric.loc.sloc(), 4);
7687 assert_eq!(metric.loc.ploc(), 3);
7688 assert_eq!(metric.loc.lloc(), 1);
7689 assert_eq!(metric.loc.cloc(), 1);
7690 assert_eq!(metric.loc.blank(), 0);
7691 },
7692 );
7693 }
7694
7695 #[cfg(feature = "mozjs")]
7696 #[test]
7697 fn mozjs_blank() {
7698 check_metrics::<MozjsParser>(
7699 "function f() {
7700
7701 var x = 1;
7702
7703 }",
7704 "foo.js",
7705 |metric| {
7706 assert_eq!(metric.loc.sloc(), 5);
7707 assert_eq!(metric.loc.ploc(), 3);
7708 assert_eq!(metric.loc.lloc(), 1);
7709 assert_eq!(metric.loc.cloc(), 0);
7710 assert_eq!(metric.loc.blank(), 2);
7711 insta::assert_json_snapshot!(metric.loc);
7712 },
7713 );
7714 }
7715
7716 #[cfg(feature = "mozjs")]
7717 #[test]
7718 fn mozjs_cloc() {
7719 check_metrics::<MozjsParser>(
7720 "// header
7721 /* block comment */
7722 function f() {
7723 return 42;
7724 }",
7725 "foo.js",
7726 |metric| {
7727 assert_eq!(metric.loc.sloc(), 5);
7728 assert_eq!(metric.loc.ploc(), 3);
7729 assert_eq!(metric.loc.lloc(), 1);
7730 assert_eq!(metric.loc.cloc(), 2);
7731 assert_eq!(metric.loc.blank(), 0);
7732 insta::assert_json_snapshot!(metric.loc);
7733 },
7734 );
7735 }
7736
7737 #[cfg(feature = "mozjs")]
7738 #[test]
7739 fn mozjs_no_zero_blank() {
7740 check_metrics::<MozjsParser>(
7743 "function f() {
7744 var a = 1;
7745
7746 var b = 2; // trailing
7747 var c = 3; // trailing
7748}",
7749 "foo.js",
7750 |metric| {
7751 assert_eq!(metric.loc.sloc(), 6);
7752 assert_eq!(metric.loc.ploc(), 5);
7753 assert_eq!(metric.loc.cloc(), 2);
7754 assert_eq!(metric.loc.blank(), 1);
7755 insta::assert_json_snapshot!(metric.loc);
7756 },
7757 );
7758 }
7759
7760 #[cfg(feature = "mozjs")]
7761 #[test]
7762 fn mozjs_arrow_function_loc() {
7763 check_metrics::<MozjsParser>(
7764 "const add = (a, b) => a + b;
7765 const greet = name => {
7766 return 'Hello ' + name;
7767 };",
7768 "foo.js",
7769 |metric| {
7770 assert_eq!(metric.loc.sloc(), 4);
7771 assert_eq!(metric.loc.ploc(), 4);
7772 assert_eq!(metric.loc.lloc(), 3);
7773 assert_eq!(metric.loc.cloc(), 0);
7774 assert_eq!(metric.loc.blank(), 0);
7775 insta::assert_json_snapshot!(metric.loc);
7776 },
7777 );
7778 }
7779
7780 #[cfg(feature = "mozjs")]
7781 #[test]
7782 fn mozjs_multiple_functions_loc() {
7783 check_metrics::<MozjsParser>(
7784 "function f() {
7785 return 1;
7786 }
7787 function g() {
7788 return 2;
7789 }",
7790 "foo.js",
7791 |metric| {
7792 assert_eq!(metric.loc.sloc(), 6);
7793 assert_eq!(metric.loc.ploc(), 6);
7794 assert_eq!(metric.loc.lloc(), 2);
7795 assert_eq!(metric.loc.cloc(), 0);
7796 assert_eq!(metric.loc.blank(), 0);
7797 insta::assert_json_snapshot!(metric.loc);
7798 },
7799 );
7800 }
7801
7802 #[cfg(feature = "mozjs")]
7803 #[test]
7804 fn mozjs_nested_function_loc() {
7805 check_metrics::<MozjsParser>(
7806 "function outer() {
7807 function inner() {
7808 return 1;
7809 }
7810 return inner();
7811 }",
7812 "foo.js",
7813 |metric| {
7814 assert_eq!(metric.loc.sloc(), 6);
7815 assert_eq!(metric.loc.ploc(), 6);
7816 assert_eq!(metric.loc.lloc(), 2);
7817 assert_eq!(metric.loc.cloc(), 0);
7818 assert_eq!(metric.loc.blank(), 0);
7819 insta::assert_json_snapshot!(metric.loc);
7820 },
7821 );
7822 }
7823
7824 #[cfg(feature = "mozjs")]
7825 #[test]
7826 fn mozjs_if_lloc() {
7827 check_metrics::<MozjsParser>(
7828 "function f(x) {
7829 if (x > 0) {
7830 return 1;
7831 } else {
7832 return -1;
7833 }
7834 }",
7835 "foo.js",
7836 |metric| {
7837 assert_eq!(metric.loc.sloc(), 7);
7838 assert_eq!(metric.loc.ploc(), 7);
7839 assert_eq!(metric.loc.lloc(), 3);
7846 assert_eq!(metric.loc.cloc(), 0);
7847 assert_eq!(metric.loc.blank(), 0);
7848 insta::assert_json_snapshot!(metric.loc);
7849 },
7850 );
7851 }
7852
7853 #[cfg(all(
7860 feature = "cpp",
7861 feature = "javascript",
7862 feature = "mozjs",
7863 feature = "rust",
7864 feature = "typescript",
7865 ))]
7866 #[test]
7867 fn js_family_if_lloc_matches_c_and_rust() {
7868 #[cfg(any(
7869 feature = "cpp",
7870 feature = "javascript",
7871 feature = "mozjs",
7872 feature = "rust",
7873 feature = "typescript",
7874 ))]
7875 const JS_SRC: &str = "function f(x) {
7876 if (x > 0) {
7877 return 1;
7878 } else {
7879 return -1;
7880 }
7881 }";
7882 #[cfg(any(
7883 feature = "cpp",
7884 feature = "javascript",
7885 feature = "mozjs",
7886 feature = "rust",
7887 feature = "typescript",
7888 ))]
7889 const C_SRC: &str = "int f(int x) {
7890 if (x > 0) {
7891 return 1;
7892 } else {
7893 return -1;
7894 }
7895 }";
7896 #[cfg(any(
7897 feature = "cpp",
7898 feature = "javascript",
7899 feature = "mozjs",
7900 feature = "rust",
7901 feature = "typescript",
7902 ))]
7903 const RUST_SRC: &str = "fn f(x: i32) -> i32 {
7904 if x > 0 {
7905 return 1;
7906 } else {
7907 return -1;
7908 }
7909 }";
7910
7911 #[cfg(any(
7914 feature = "cpp",
7915 feature = "javascript",
7916 feature = "mozjs",
7917 feature = "rust",
7918 feature = "typescript",
7919 ))]
7920 const EXPECTED_LLOC: usize = 3;
7921
7922 check_metrics::<CppParser>(C_SRC, "f.c", |m| {
7923 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7924 });
7925 check_metrics::<RustParser>(RUST_SRC, "f.rs", |m| {
7926 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7927 });
7928 check_metrics::<MozjsParser>(JS_SRC, "f.js", |m| {
7929 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7930 });
7931 check_metrics::<JavascriptParser>(JS_SRC, "f.js", |m| {
7932 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7933 });
7934 check_metrics::<TypescriptParser>(JS_SRC, "f.ts", |m| {
7935 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7936 });
7937 check_metrics::<TsxParser>(JS_SRC, "f.tsx", |m| {
7938 assert_eq!(m.loc.lloc() as usize, EXPECTED_LLOC);
7939 });
7940 }
7941
7942 #[cfg(feature = "mozjs")]
7943 #[test]
7944 fn mozjs_for_lloc() {
7945 check_metrics::<MozjsParser>(
7946 "function f(n) {
7947 var s = 0;
7948 for (var i = 0; i < n; i++) {
7949 s += i;
7950 }
7951 return s;
7952 }",
7953 "foo.js",
7954 |metric| {
7955 assert_eq!(metric.loc.sloc(), 7);
7956 assert_eq!(metric.loc.ploc(), 7);
7957 assert_eq!(metric.loc.lloc(), 4);
7958 assert_eq!(metric.loc.cloc(), 0);
7959 assert_eq!(metric.loc.blank(), 0);
7960 insta::assert_json_snapshot!(metric.loc);
7961 },
7962 );
7963 }
7964
7965 #[cfg(feature = "bash")]
7966 #[test]
7967 fn bash_blank() {
7968 check_metrics::<BashParser>(
7969 "#!/bin/bash
7970
7971 f() {
7972
7973 echo hello
7974
7975 }",
7976 "foo.sh",
7977 |metric| {
7978 assert_eq!(metric.loc.sloc(), 7);
7979 assert_eq!(metric.loc.ploc(), 3);
7980 assert_eq!(metric.loc.lloc(), 2);
7981 assert_eq!(metric.loc.cloc(), 1);
7982 assert_eq!(metric.loc.blank(), 3);
7983 insta::assert_json_snapshot!(metric.loc);
7984 },
7985 );
7986 }
7987
7988 #[cfg(feature = "bash")]
7989 #[test]
7990 fn bash_cloc() {
7991 check_metrics::<BashParser>(
7992 "# header comment
7993 f() {
7994 # body comment
7995 echo hello
7996 }",
7997 "foo.sh",
7998 |metric| {
7999 assert_eq!(metric.loc.sloc(), 5);
8000 assert_eq!(metric.loc.ploc(), 3);
8001 assert_eq!(metric.loc.lloc(), 2);
8002 assert_eq!(metric.loc.cloc(), 2);
8003 assert_eq!(metric.loc.blank(), 0);
8004 insta::assert_json_snapshot!(metric.loc);
8005 },
8006 );
8007 }
8008
8009 #[cfg(feature = "bash")]
8010 #[test]
8011 fn bash_no_zero_blank() {
8012 check_metrics::<BashParser>(
8015 "f() {
8016 echo a
8017
8018 echo b # trailing
8019 echo c # trailing
8020}",
8021 "foo.sh",
8022 |metric| {
8023 assert_eq!(metric.loc.sloc(), 6);
8024 assert_eq!(metric.loc.ploc(), 5);
8025 assert_eq!(metric.loc.cloc(), 2);
8026 assert_eq!(metric.loc.blank(), 1);
8027 insta::assert_json_snapshot!(metric.loc);
8028 },
8029 );
8030 }
8031
8032 #[cfg(feature = "bash")]
8033 #[test]
8034 fn bash_comment_before_code_line_reclassified() {
8035 check_metrics::<BashParser>("echo a\n\necho \"$(\n # c\n)\"\n", "foo.sh", |metric| {
8053 assert_eq!(metric.loc.sloc(), 5);
8054 assert_eq!(metric.loc.ploc(), 4);
8055 assert_eq!(metric.loc.lloc(), 3);
8056 assert_eq!(metric.loc.cloc(), 1);
8057 assert_eq!(metric.loc.blank(), 1);
8058 insta::assert_json_snapshot!(
8059 metric.loc,
8060 @r#"
8061 {
8062 "sloc": 5,
8063 "ploc": 4,
8064 "lloc": 3,
8065 "cloc": 1,
8066 "blank": 1,
8067 "dloc": 0,
8068 "sloc_average": 5.0,
8069 "ploc_average": 4.0,
8070 "lloc_average": 3.0,
8071 "cloc_average": 1.0,
8072 "blank_average": 1.0,
8073 "dloc_average": 0.0,
8074 "sloc_min": 5,
8075 "sloc_max": 5,
8076 "cloc_min": 1,
8077 "cloc_max": 1,
8078 "ploc_min": 4,
8079 "ploc_max": 4,
8080 "lloc_min": 3,
8081 "lloc_max": 3,
8082 "blank_min": 1,
8083 "blank_max": 1,
8084 "dloc_min": 0,
8085 "dloc_max": 0
8086 }
8087 "#
8088 );
8089 });
8090 }
8091
8092 #[cfg(feature = "bash")]
8093 #[test]
8094 fn bash_if_lloc() {
8095 check_metrics::<BashParser>(
8096 "f() {
8097 if [ $1 -gt 0 ]; then
8098 echo positive
8099 else
8100 echo negative
8101 fi
8102 }",
8103 "foo.sh",
8104 |metric| {
8105 assert_eq!(metric.loc.sloc(), 7);
8106 assert_eq!(metric.loc.ploc(), 7);
8107 assert_eq!(metric.loc.lloc(), 4);
8108 assert_eq!(metric.loc.cloc(), 0);
8109 assert_eq!(metric.loc.blank(), 0);
8110 insta::assert_json_snapshot!(metric.loc);
8111 },
8112 );
8113 }
8114
8115 #[cfg(feature = "bash")]
8116 #[test]
8117 fn bash_for_lloc() {
8118 check_metrics::<BashParser>(
8119 "f() {
8120 for i in 1 2 3; do
8121 echo $i
8122 done
8123 }",
8124 "foo.sh",
8125 |metric| {
8126 assert_eq!(metric.loc.sloc(), 5);
8127 assert_eq!(metric.loc.ploc(), 5);
8128 assert_eq!(metric.loc.lloc(), 3);
8129 assert_eq!(metric.loc.cloc(), 0);
8130 assert_eq!(metric.loc.blank(), 0);
8131 insta::assert_json_snapshot!(metric.loc);
8132 },
8133 );
8134 }
8135
8136 #[cfg(feature = "bash")]
8137 #[test]
8138 fn bash_while_lloc() {
8139 check_metrics::<BashParser>(
8140 "f() {
8141 local n=5
8142 while [ $n -gt 0 ]; do
8143 echo $n
8144 n=$((n - 1))
8145 done
8146 }",
8147 "foo.sh",
8148 |metric| {
8149 assert_eq!(metric.loc.sloc(), 7);
8150 assert_eq!(metric.loc.ploc(), 7);
8151 assert_eq!(metric.loc.lloc(), 5);
8156 assert_eq!(metric.loc.cloc(), 0);
8157 assert_eq!(metric.loc.blank(), 0);
8158 insta::assert_json_snapshot!(metric.loc);
8159 },
8160 );
8161 }
8162
8163 #[cfg(feature = "bash")]
8178 #[test]
8179 fn bash_assignment_counts_one_logical_line_per_statement() {
8180 for (source, lloc) in [
8181 (&b"a=1\nb=2\nc=3\n"[..], 3),
8182 (&b"local d=4\n"[..], 1),
8183 (&b"export e=5\n"[..], 1),
8184 (&b"declare -a g=(1 2)\n"[..], 1),
8185 (&b"X=1 cmd arg\n"[..], 1),
8186 (&b"(h=6)\n"[..], 1),
8187 (&b"for i in 1; do j=7; done\n"[..], 2),
8188 ] {
8189 let measured = metrics_verbatim(crate::LANG::Bash, source, MetricsOptions::default())
8190 .loc
8191 .lloc();
8192 assert_eq!(measured, lloc, "{:?}", String::from_utf8_lossy(source));
8193 }
8194 }
8195
8196 #[cfg(feature = "bash")]
8197 #[test]
8198 fn bash_case_lloc() {
8199 check_metrics::<BashParser>(
8200 "f() {
8201 case $1 in
8202 start) echo starting ;;
8203 stop) echo stopping ;;
8204 *) echo unknown ;;
8205 esac
8206 }",
8207 "foo.sh",
8208 |metric| {
8209 assert_eq!(metric.loc.sloc(), 7);
8210 assert_eq!(metric.loc.ploc(), 7);
8211 assert_eq!(metric.loc.lloc(), 5);
8212 assert_eq!(metric.loc.cloc(), 0);
8213 assert_eq!(metric.loc.blank(), 0);
8214 insta::assert_json_snapshot!(metric.loc);
8215 },
8216 );
8217 }
8218
8219 #[cfg(feature = "bash")]
8220 #[test]
8221 fn bash_multiple_functions_loc() {
8222 check_metrics::<BashParser>(
8223 "f() {
8224 echo hello
8225 }
8226 g() {
8227 echo world
8228 }",
8229 "foo.sh",
8230 |metric| {
8231 assert_eq!(metric.loc.sloc(), 6);
8232 assert_eq!(metric.loc.ploc(), 6);
8233 assert_eq!(metric.loc.lloc(), 4);
8234 assert_eq!(metric.loc.cloc(), 0);
8235 assert_eq!(metric.loc.blank(), 0);
8236 insta::assert_json_snapshot!(metric.loc);
8237 },
8238 );
8239 }
8240
8241 #[cfg(feature = "bash")]
8242 #[test]
8243 fn bash_nested_function_loc() {
8244 check_metrics::<BashParser>(
8245 "outer() {
8246 inner() {
8247 echo inner
8248 }
8249 inner
8250 echo outer
8251 }",
8252 "foo.sh",
8253 |metric| {
8254 assert_eq!(metric.loc.sloc(), 7);
8255 assert_eq!(metric.loc.ploc(), 7);
8256 assert_eq!(metric.loc.lloc(), 5);
8257 assert_eq!(metric.loc.cloc(), 0);
8258 assert_eq!(metric.loc.blank(), 0);
8259 insta::assert_json_snapshot!(metric.loc);
8260 },
8261 );
8262 }
8263
8264 #[cfg(feature = "bash")]
8265 #[test]
8266 fn bash_heredoc_loc() {
8267 check_metrics::<BashParser>(
8274 "f() {
8275 cat <<EOF
8276line1
8277line2
8278EOF
8279 }",
8280 "foo.sh",
8281 |metric| {
8282 assert_eq!(metric.loc.sloc(), 6);
8283 assert_eq!(metric.loc.ploc(), 6);
8284 assert_eq!(metric.loc.lloc(), 2);
8285 assert_eq!(metric.loc.cloc(), 0);
8286 assert_eq!(metric.loc.blank(), 0);
8287 insta::assert_json_snapshot!(metric.loc);
8288 },
8289 );
8290 }
8291
8292 #[cfg(feature = "bash")]
8293 #[test]
8294 fn bash_multiline_literals_credit_every_row_to_ploc() {
8295 check_metrics::<BashParser>(
8310 "cat <<DOC\nline one\nline two\nDOC\necho done",
8311 "foo.sh",
8312 |metric| {
8313 assert_eq!(metric.loc.sloc(), 5);
8314 assert_eq!(metric.loc.ploc(), 5);
8315 assert_eq!(metric.loc.lloc(), 2);
8316 assert_eq!(metric.loc.cloc(), 0);
8317 assert_eq!(metric.loc.blank(), 0);
8318 },
8319 );
8320
8321 check_metrics::<BashParser>("s='one\ntwo\nthree'", "foo.sh", |metric| {
8324 assert_eq!(metric.loc.sloc(), 3);
8325 assert_eq!(metric.loc.ploc(), 3);
8326 assert_eq!(metric.loc.cloc(), 0);
8327 assert_eq!(metric.loc.blank(), 0);
8328 });
8329
8330 check_metrics::<BashParser>("s=$'one\ntwo'", "foo.sh", |metric| {
8333 assert_eq!(metric.loc.sloc(), 2);
8334 assert_eq!(metric.loc.ploc(), 2);
8335 assert_eq!(metric.loc.cloc(), 0);
8336 assert_eq!(metric.loc.blank(), 0);
8337 });
8338
8339 check_metrics::<BashParser>("s=\"one\n\nthree\"", "foo.sh", |metric| {
8345 assert_eq!(metric.loc.sloc(), 3);
8346 assert_eq!(metric.loc.ploc(), 3);
8347 assert_eq!(metric.loc.cloc(), 0);
8348 assert_eq!(metric.loc.blank(), 0);
8349 });
8350
8351 check_metrics::<BashParser>("cat <<-'DOC'\n\tline one\n\nDOC", "foo.sh", |metric| {
8357 assert_eq!(metric.loc.sloc(), 4);
8358 assert_eq!(metric.loc.ploc(), 4);
8359 assert_eq!(metric.loc.lloc(), 1);
8360 assert_eq!(metric.loc.cloc(), 0);
8361 assert_eq!(metric.loc.blank(), 0);
8362 });
8363
8364 check_metrics::<BashParser>("cat <<DOC\nDOC", "foo.sh", |metric| {
8368 assert_eq!(metric.loc.sloc(), 2);
8369 assert_eq!(metric.loc.ploc(), 2);
8370 assert_eq!(metric.loc.cloc(), 0);
8371 assert_eq!(metric.loc.blank(), 0);
8372 });
8373 }
8374
8375 #[cfg(feature = "bash")]
8380 fn bash_loc_verbatim(source: &[u8]) -> Stats {
8381 metrics_verbatim(
8382 crate::LANG::Bash,
8383 source,
8384 crate::MetricsOptions::default().with_only(&[crate::Metric::Loc]),
8385 )
8386 .loc
8387 }
8388
8389 #[cfg(feature = "bash")]
8401 #[test]
8402 fn bash_heredoc_bodies_that_open_or_stay_empty_are_not_blank() {
8403 const FIXTURES: &[(&[u8], u64, u64)] = &[
8413 (b"cat <<EOT\n\nEOT\n", 3, 1),
8418 (b"cat <<EOT\n\n\nEOT\n", 4, 2),
8419 (b"cat <<EOT\n\na\nEOT\n", 4, 1),
8420 (b"cat <<EOT\na\n\nEOT\n", 4, 0),
8421 (b"cat <<EOT\n\nEOT", 3, 1),
8428 (b"cat <<-EOT\n\n\tx\n\tEOT\n", 4, 1),
8429 (b"cat <<'EOT'\n\nx\nEOT\n", 4, 1),
8430 (b"f() {\n cat <<EOT\n\n x\nEOT\n}\n", 6, 1),
8431 ];
8432
8433 for &(source, rows, blank_before) in FIXTURES {
8434 let text = String::from_utf8_lossy(source);
8435 let loc = bash_loc_verbatim(source);
8436 assert_eq!(loc.sloc(), rows, "sloc for {text:?}");
8437 assert_eq!(loc.ploc(), rows, "ploc for {text:?}");
8438 assert_eq!(loc.cloc(), 0, "cloc for {text:?}");
8439 assert_eq!(
8440 loc.blank(),
8441 0,
8442 "blank for {text:?}, which reported {blank_before} before #1412"
8443 );
8444 }
8445 }
8446
8447 #[cfg(feature = "bash")]
8454 #[test]
8455 fn bash_heredoc_wrapper_credits_no_row_past_its_terminator() {
8456 let loc = bash_loc_verbatim(b"cat <<EOT\n\nEOT\n\n# note\necho after\n");
8460 assert_eq!(loc.sloc(), 6);
8461 assert_eq!(loc.ploc(), 4);
8462 assert_eq!(loc.cloc(), 1);
8463 assert_eq!(loc.blank(), 1);
8464 }
8465
8466 #[cfg(feature = "bash")]
8484 #[test]
8485 fn bash_heredoc_continuation_row_is_code() {
8486 let continuation = bash_loc_verbatim(b"cat <<EOT | \\\n\\\n grep x\nbody\nEOT\n");
8489 assert_eq!(continuation.sloc(), 5);
8490 assert_eq!(
8491 continuation.ploc(),
8492 5,
8493 "the `\\`-only continuation row is part of the command"
8494 );
8495 assert_eq!(continuation.cloc(), 0);
8496 assert_eq!(continuation.blank(), 0);
8497
8498 let with_content = bash_loc_verbatim(b"cat <<EOT | \\\n grep x\nbody\nEOT\n");
8502 assert_eq!(
8503 (
8504 with_content.sloc(),
8505 with_content.ploc(),
8506 with_content.blank()
8507 ),
8508 (4, 4, 0)
8509 );
8510 }
8511
8512 #[cfg(feature = "bash")]
8528 #[test]
8529 fn bash_heredoc_body_without_a_wrapper_still_credits_every_row() {
8530 for source in [
8534 &b"f() { cat <<EOT; }\nbody\nEOT\n"[..],
8535 &b"if true; then cat <<EOT; fi\nx\nEOT\n"[..],
8536 &b"for i in 1; do cat <<EOT; done\nx\nEOT\n"[..],
8537 ] {
8538 let loc = bash_loc_verbatim(source);
8539 let text = String::from_utf8_lossy(source);
8540 assert_eq!(loc.sloc(), 3, "sloc for {text:?}");
8541 assert_eq!(loc.ploc(), 3, "ploc for {text:?}");
8542 assert_eq!(loc.cloc(), 0, "cloc for {text:?}");
8543 assert_eq!(
8544 loc.blank(),
8545 0,
8546 "the terminator row of a wrapper-less heredoc body is code, not blank, for {text:?}"
8547 );
8548 }
8549 }
8550
8551 #[cfg(feature = "bash")]
8552 #[test]
8553 fn bash_literal_that_owns_its_opening_row_still_credits_it() {
8554 check_metrics::<BashParser>("'ls'", "foo.sh", |metric| {
8572 assert_eq!(metric.loc.sloc(), 1);
8573 assert_eq!(metric.loc.ploc(), 1);
8574 assert_eq!(metric.loc.cloc(), 0);
8575 assert_eq!(metric.loc.blank(), 0);
8576 });
8577
8578 check_metrics::<BashParser>("$'ls'", "foo.sh", |metric| {
8581 assert_eq!(metric.loc.sloc(), 1);
8582 assert_eq!(metric.loc.ploc(), 1);
8583 assert_eq!(metric.loc.cloc(), 0);
8584 assert_eq!(metric.loc.blank(), 0);
8585 });
8586
8587 check_metrics::<BashParser>("f() {\n 'ls'\n}", "foo.sh", |metric| {
8591 assert_eq!(metric.loc.sloc(), 3);
8592 assert_eq!(metric.loc.ploc(), 3);
8593 assert_eq!(metric.loc.cloc(), 0);
8594 assert_eq!(metric.loc.blank(), 0);
8595 });
8596
8597 check_metrics::<BashParser>("# c\n'ls'", "foo.sh", |metric| {
8601 assert_eq!(metric.loc.sloc(), 2);
8602 assert_eq!(metric.loc.ploc(), 1);
8603 assert_eq!(metric.loc.cloc(), 1);
8604 assert_eq!(metric.loc.blank(), 0);
8605 });
8606
8607 check_metrics::<BashParser>("'a\nb'", "foo.sh", |metric| {
8613 assert_eq!(metric.loc.sloc(), 2);
8614 assert_eq!(metric.loc.ploc(), 2);
8615 assert_eq!(metric.loc.cloc(), 0);
8616 assert_eq!(metric.loc.blank(), 0);
8617 });
8618 }
8619
8620 #[cfg(all(feature = "bash", feature = "tcl"))]
8621 #[test]
8622 fn a_multiline_literal_credits_no_row_past_its_own_span() {
8623 check_metrics::<BashParser>(
8635 "cat <<A\na1\nA\n# note\ncat <<B\nb1\nB",
8636 "foo.sh",
8637 |metric| {
8638 assert_eq!(metric.loc.sloc(), 7);
8639 assert_eq!(metric.loc.ploc(), 6);
8640 assert_eq!(metric.loc.cloc(), 1);
8641 assert_eq!(metric.loc.blank(), 0);
8642 },
8643 );
8644
8645 check_metrics::<TclParser>("set a \"x\ny\"\n# note\nset b 1", "foo.tcl", |metric| {
8648 assert_eq!(metric.loc.sloc(), 4);
8649 assert_eq!(metric.loc.ploc(), 3);
8650 assert_eq!(metric.loc.cloc(), 1);
8651 assert_eq!(metric.loc.blank(), 0);
8652 });
8653 }
8654
8655 #[cfg(feature = "kotlin")]
8656 #[test]
8657 fn kotlin_loc_blank() {
8658 check_metrics::<KotlinParser>(
8659 "fun f(): Int {
8660
8661 val x = 1
8662
8663 return x
8664 }",
8665 "foo.kt",
8666 |metric| {
8667 assert_eq!(metric.loc.sloc(), 6);
8668 assert_eq!(metric.loc.ploc(), 4);
8669 assert_eq!(metric.loc.lloc(), 2);
8670 assert_eq!(metric.loc.cloc(), 0);
8671 assert_eq!(metric.loc.blank(), 2);
8672 insta::assert_json_snapshot!(metric.loc);
8673 },
8674 );
8675 }
8676
8677 #[cfg(feature = "kotlin")]
8678 #[test]
8679 fn kotlin_loc_cloc() {
8680 check_metrics::<KotlinParser>(
8681 "// header comment
8682 /* block
8683 comment */
8684 fun f(): Int {
8685 return 42 // inline
8686 }",
8687 "foo.kt",
8688 |metric| {
8689 assert_eq!(metric.loc.sloc(), 6);
8690 assert_eq!(metric.loc.ploc(), 3);
8691 assert_eq!(metric.loc.lloc(), 1);
8692 assert_eq!(metric.loc.cloc(), 4);
8693 assert_eq!(metric.loc.blank(), 0);
8694 insta::assert_json_snapshot!(metric.loc);
8695 },
8696 );
8697 }
8698
8699 #[cfg(feature = "kotlin")]
8700 #[test]
8701 fn kotlin_loc_no_zero_blank() {
8702 check_metrics::<KotlinParser>(
8705 "fun connectToUpdateServer() {
8706 val pool = 0
8707
8708 val updateServer = -42
8709 val isConnected = false
8710 val currTry = 0
8711 val numRetries = 10 // Number of IPC connection retries before
8712 // giving up.
8713 val numTries = 20 // Number of IPC connection tries before
8714 // giving up.
8715 }",
8716 "foo.kt",
8717 |metric| {
8718 assert_eq!(metric.loc.sloc(), 11);
8721 assert_eq!(metric.loc.ploc(), 8);
8722 assert_eq!(metric.loc.cloc(), 4);
8723 assert_eq!(metric.loc.blank(), 1);
8724 insta::assert_json_snapshot!(
8725 metric.loc,
8726 @r#"
8727 {
8728 "sloc": 11,
8729 "ploc": 8,
8730 "lloc": 6,
8731 "cloc": 4,
8732 "blank": 1,
8733 "dloc": 0,
8734 "sloc_average": 5.5,
8735 "ploc_average": 4.0,
8736 "lloc_average": 3.0,
8737 "cloc_average": 2.0,
8738 "blank_average": 0.5,
8739 "dloc_average": 0.0,
8740 "sloc_min": 11,
8741 "sloc_max": 11,
8742 "cloc_min": 4,
8743 "cloc_max": 4,
8744 "ploc_min": 8,
8745 "ploc_max": 8,
8746 "lloc_min": 6,
8747 "lloc_max": 6,
8748 "blank_min": 1,
8749 "blank_max": 1,
8750 "dloc_min": 0,
8751 "dloc_max": 0
8752 }
8753 "#
8754 );
8755 },
8756 );
8757 }
8758
8759 #[cfg(feature = "kotlin")]
8760 #[test]
8761 fn kotlin_loc_blank_zero_sanity() {
8762 check_metrics::<KotlinParser>(
8767 "fun f(): Int {
8768 val x = 1 // x
8769 val y = 2 // y
8770 return x + y
8771 }",
8772 "foo.kt",
8773 |metric| {
8774 assert_eq!(metric.loc.sloc(), 5);
8775 assert_eq!(metric.loc.ploc(), 5);
8776 assert_eq!(metric.loc.lloc(), 3);
8777 assert_eq!(metric.loc.cloc(), 2);
8778 assert_eq!(metric.loc.blank(), 0);
8779 },
8780 );
8781 }
8782
8783 #[cfg(feature = "kotlin")]
8784 #[test]
8785 fn kotlin_loc_if_lloc() {
8786 check_metrics::<KotlinParser>(
8787 "fun classify(n: Int): String {
8788 if (n > 0) {
8789 return \"positive\"
8790 } else if (n < 0) {
8791 return \"negative\"
8792 }
8793 return \"zero\"
8794 }",
8795 "foo.kt",
8796 |metric| {
8797 assert_eq!(metric.loc.sloc(), 8);
8798 assert_eq!(metric.loc.ploc(), 8);
8799 assert_eq!(metric.loc.lloc(), 5);
8800 assert_eq!(metric.loc.cloc(), 0);
8801 assert_eq!(metric.loc.blank(), 0);
8802 insta::assert_json_snapshot!(metric.loc);
8803 },
8804 );
8805 }
8806
8807 #[cfg(feature = "kotlin")]
8808 #[test]
8809 fn kotlin_loc_for_lloc() {
8810 check_metrics::<KotlinParser>(
8811 "fun sum(n: Int): Int {
8812 var s = 0
8813 for (i in 1..n) {
8814 s += i
8815 }
8816 return s
8817 }",
8818 "foo.kt",
8819 |metric| {
8820 assert_eq!(metric.loc.sloc(), 7);
8821 assert_eq!(metric.loc.ploc(), 7);
8822 assert_eq!(metric.loc.lloc(), 4);
8823 assert_eq!(metric.loc.cloc(), 0);
8824 assert_eq!(metric.loc.blank(), 0);
8825 insta::assert_json_snapshot!(metric.loc);
8826 },
8827 );
8828 }
8829
8830 #[cfg(feature = "kotlin")]
8831 #[test]
8832 fn kotlin_loc_when_lloc() {
8833 check_metrics::<KotlinParser>(
8834 "fun describe(x: Int): String {
8835 return when (x) {
8836 1 -> \"one\"
8837 2 -> \"two\"
8838 else -> \"other\"
8839 }
8840 }",
8841 "foo.kt",
8842 |metric| {
8843 assert_eq!(metric.loc.sloc(), 7);
8844 assert_eq!(metric.loc.ploc(), 7);
8845 assert_eq!(metric.loc.lloc(), 2);
8846 assert_eq!(metric.loc.cloc(), 0);
8847 assert_eq!(metric.loc.blank(), 0);
8848 insta::assert_json_snapshot!(metric.loc);
8849 },
8850 );
8851 }
8852
8853 #[cfg(feature = "kotlin")]
8854 #[test]
8855 fn kotlin_loc_lambda_lloc() {
8856 check_metrics::<KotlinParser>(
8857 "fun f(list: List<Int>): List<Int> {
8858 return list.filter { it > 0 }
8859 .map { it * 2 }
8860 }",
8861 "foo.kt",
8862 |metric| {
8863 assert_eq!(metric.loc.sloc(), 4);
8864 assert_eq!(metric.loc.ploc(), 4);
8865 assert_eq!(metric.loc.lloc(), 1);
8866 assert_eq!(metric.loc.cloc(), 0);
8867 assert_eq!(metric.loc.blank(), 0);
8868 insta::assert_json_snapshot!(metric.loc);
8869 },
8870 );
8871 }
8872
8873 #[cfg(feature = "kotlin")]
8874 #[test]
8875 fn kotlin_loc_class_loc() {
8876 check_metrics::<KotlinParser>(
8877 "class Counter {
8878 private var count = 0
8879 fun increment() { count++ }
8880 fun get(): Int = count
8881 }",
8882 "foo.kt",
8883 |metric| {
8884 assert_eq!(metric.loc.sloc(), 5);
8885 assert_eq!(metric.loc.ploc(), 5);
8886 assert_eq!(metric.loc.lloc(), 1);
8887 assert_eq!(metric.loc.cloc(), 0);
8888 assert_eq!(metric.loc.blank(), 0);
8889 insta::assert_json_snapshot!(metric.loc);
8890 },
8891 );
8892 }
8893
8894 #[cfg(feature = "kotlin")]
8895 #[test]
8896 fn kotlin_loc_multiple_functions_loc() {
8897 check_metrics::<KotlinParser>(
8898 "fun f(): Int {
8899 return 1
8900 }
8901 fun g(): Int {
8902 return 2
8903 }",
8904 "foo.kt",
8905 |metric| {
8906 assert_eq!(metric.loc.sloc(), 6);
8907 assert_eq!(metric.loc.ploc(), 6);
8908 assert_eq!(metric.loc.lloc(), 2);
8909 assert_eq!(metric.loc.cloc(), 0);
8910 assert_eq!(metric.loc.blank(), 0);
8911 insta::assert_json_snapshot!(metric.loc);
8912 },
8913 );
8914 }
8915
8916 #[cfg(feature = "kotlin")]
8917 #[test]
8918 fn kotlin_loc_while_lloc() {
8919 check_metrics::<KotlinParser>(
8920 "fun countdown(n: Int) {
8921 var i = n
8922 while (i > 0) {
8923 println(i)
8924 i--
8925 }
8926 }",
8927 "foo.kt",
8928 |metric| {
8929 assert_eq!(metric.loc.sloc(), 7);
8930 assert_eq!(metric.loc.ploc(), 7);
8931 assert_eq!(metric.loc.lloc(), 3);
8932 assert_eq!(metric.loc.cloc(), 0);
8933 assert_eq!(metric.loc.blank(), 0);
8934 insta::assert_json_snapshot!(metric.loc);
8935 },
8936 );
8937 }
8938
8939 #[cfg(feature = "typescript")]
8940 #[test]
8941 fn typescript_blank() {
8942 check_metrics::<TypescriptParser>(
8943 "function f(): void {
8944
8945 const x = 1;
8946
8947 }",
8948 "foo.ts",
8949 |metric| {
8950 assert_eq!(metric.loc.sloc(), 5);
8951 assert_eq!(metric.loc.ploc(), 3);
8952 assert_eq!(metric.loc.lloc(), 1);
8953 assert_eq!(metric.loc.cloc(), 0);
8954 assert_eq!(metric.loc.blank(), 2);
8955 insta::assert_json_snapshot!(metric.loc);
8956 },
8957 );
8958 }
8959
8960 #[cfg(feature = "typescript")]
8961 #[test]
8962 fn typescript_cloc() {
8963 check_metrics::<TypescriptParser>(
8964 "// header
8965 /* block
8966 comment */
8967 function f(): number {
8968 return 42; // inline
8969 }",
8970 "foo.ts",
8971 |metric| {
8972 assert_eq!(metric.loc.sloc(), 6);
8973 assert_eq!(metric.loc.ploc(), 3);
8974 assert_eq!(metric.loc.lloc(), 1);
8975 assert_eq!(metric.loc.cloc(), 4);
8976 assert_eq!(metric.loc.blank(), 0);
8977 insta::assert_json_snapshot!(metric.loc);
8978 },
8979 );
8980 }
8981
8982 #[cfg(feature = "typescript")]
8983 #[test]
8984 fn typescript_no_zero_blank() {
8985 check_metrics::<TypescriptParser>(
8988 "function f(): void {
8989 const a = 1;
8990
8991 const b = 2; // trailing
8992 const c = 3; // trailing
8993}",
8994 "foo.ts",
8995 |metric| {
8996 assert_eq!(metric.loc.sloc(), 6);
8997 assert_eq!(metric.loc.ploc(), 5);
8998 assert_eq!(metric.loc.cloc(), 2);
8999 assert_eq!(metric.loc.blank(), 1);
9000 insta::assert_json_snapshot!(metric.loc);
9001 },
9002 );
9003 }
9004
9005 #[cfg(feature = "typescript")]
9006 #[test]
9007 fn typescript_if_lloc() {
9008 check_metrics::<TypescriptParser>(
9009 "function classify(n: number): string {
9010 if (n > 0) {
9011 return 'positive';
9012 } else {
9013 return 'non-positive';
9014 }
9015 }",
9016 "foo.ts",
9017 |metric| {
9018 assert_eq!(metric.loc.sloc(), 7);
9019 assert_eq!(metric.loc.ploc(), 7);
9020 assert_eq!(metric.loc.lloc(), 3);
9021 assert_eq!(metric.loc.cloc(), 0);
9022 assert_eq!(metric.loc.blank(), 0);
9023 insta::assert_json_snapshot!(metric.loc);
9024 },
9025 );
9026 }
9027
9028 #[cfg(feature = "typescript")]
9029 #[test]
9030 fn typescript_for_lloc() {
9031 check_metrics::<TypescriptParser>(
9032 "function sum(n: number): number {
9033 let s = 0;
9034 for (let i = 0; i < n; i++) {
9035 s += i;
9036 }
9037 return s;
9038 }",
9039 "foo.ts",
9040 |metric| {
9041 assert_eq!(metric.loc.sloc(), 7);
9042 assert_eq!(metric.loc.ploc(), 7);
9043 assert_eq!(metric.loc.lloc(), 4);
9044 assert_eq!(metric.loc.cloc(), 0);
9045 assert_eq!(metric.loc.blank(), 0);
9046 insta::assert_json_snapshot!(metric.loc);
9047 },
9048 );
9049 }
9050
9051 #[cfg(feature = "typescript")]
9052 #[test]
9053 fn typescript_while_lloc() {
9054 check_metrics::<TypescriptParser>(
9055 "function countdown(n: number): void {
9056 let i = n;
9057 while (i > 0) {
9058 console.log(i);
9059 i--;
9060 }
9061 }",
9062 "foo.ts",
9063 |metric| {
9064 assert_eq!(metric.loc.sloc(), 7);
9065 assert_eq!(metric.loc.ploc(), 7);
9066 assert_eq!(metric.loc.lloc(), 4);
9067 assert_eq!(metric.loc.cloc(), 0);
9068 assert_eq!(metric.loc.blank(), 0);
9069 insta::assert_json_snapshot!(metric.loc);
9070 },
9071 );
9072 }
9073
9074 #[cfg(feature = "typescript")]
9075 #[test]
9076 fn typescript_switch_lloc() {
9077 check_metrics::<TypescriptParser>(
9078 "function describe(x: number): string {
9079 switch (x) {
9080 case 1: return 'one';
9081 case 2: return 'two';
9082 default: return 'other';
9083 }
9084 }",
9085 "foo.ts",
9086 |metric| {
9087 assert_eq!(metric.loc.sloc(), 7);
9088 assert_eq!(metric.loc.ploc(), 7);
9089 assert_eq!(metric.loc.lloc(), 4);
9090 assert_eq!(metric.loc.cloc(), 0);
9091 assert_eq!(metric.loc.blank(), 0);
9092 insta::assert_json_snapshot!(metric.loc);
9093 },
9094 );
9095 }
9096
9097 #[cfg(feature = "typescript")]
9098 #[test]
9099 fn typescript_class_loc() {
9100 check_metrics::<TypescriptParser>(
9101 "class Counter {
9102 private count: number = 0;
9103 increment(): void { this.count++; }
9104 get(): number { return this.count; }
9105 }",
9106 "foo.ts",
9107 |metric| {
9108 assert_eq!(metric.loc.sloc(), 5);
9109 assert_eq!(metric.loc.ploc(), 5);
9110 assert_eq!(metric.loc.lloc(), 2);
9111 assert_eq!(metric.loc.cloc(), 0);
9112 assert_eq!(metric.loc.blank(), 0);
9113 insta::assert_json_snapshot!(metric.loc);
9114 },
9115 );
9116 }
9117
9118 #[cfg(feature = "typescript")]
9119 #[test]
9120 fn typescript_arrow_function_loc() {
9121 check_metrics::<TypescriptParser>(
9122 "const add = (a: number, b: number): number => a + b;
9123 const greet = (name: string): string => {
9124 return `Hello, ${name}`;
9125 };",
9126 "foo.ts",
9127 |metric| {
9128 assert_eq!(metric.loc.sloc(), 4);
9129 assert_eq!(metric.loc.ploc(), 4);
9130 assert_eq!(metric.loc.lloc(), 3);
9131 assert_eq!(metric.loc.cloc(), 0);
9132 assert_eq!(metric.loc.blank(), 0);
9133 insta::assert_json_snapshot!(metric.loc);
9134 },
9135 );
9136 }
9137
9138 #[cfg(feature = "typescript")]
9139 #[test]
9140 fn typescript_interface_loc() {
9141 check_metrics::<TypescriptParser>(
9142 "interface Shape {
9143 area(): number;
9144 perimeter(): number;
9145 }
9146 function describe(s: Shape): string {
9147 return `area=${s.area()}`;
9148 }",
9149 "foo.ts",
9150 |metric| {
9151 assert_eq!(metric.loc.sloc(), 7);
9152 assert_eq!(metric.loc.ploc(), 7);
9153 assert_eq!(metric.loc.lloc(), 1);
9154 assert_eq!(metric.loc.cloc(), 0);
9155 assert_eq!(metric.loc.blank(), 0);
9156 insta::assert_json_snapshot!(metric.loc);
9157 },
9158 );
9159 }
9160
9161 #[cfg(feature = "typescript")]
9162 #[test]
9163 fn typescript_multiple_functions_loc() {
9164 check_metrics::<TypescriptParser>(
9165 "function f(): number {
9166 return 1;
9167 }
9168 function g(): number {
9169 return 2;
9170 }
9171 function h(): number {
9172 return 3;
9173 }",
9174 "foo.ts",
9175 |metric| {
9176 assert_eq!(metric.loc.sloc(), 9);
9177 assert_eq!(metric.loc.ploc(), 9);
9178 assert_eq!(metric.loc.lloc(), 3);
9179 assert_eq!(metric.loc.cloc(), 0);
9180 assert_eq!(metric.loc.blank(), 0);
9181 insta::assert_json_snapshot!(metric.loc);
9182 },
9183 );
9184 }
9185
9186 #[cfg(feature = "typescript")]
9187 #[test]
9188 fn typescript_try_catch_lloc() {
9189 check_metrics::<TypescriptParser>(
9190 "function safe(x: number): number {
9191 try {
9192 return 1 / x;
9193 } catch (e) {
9194 return 0;
9195 }
9196 }",
9197 "foo.ts",
9198 |metric| {
9199 assert_eq!(metric.loc.sloc(), 7);
9200 assert_eq!(metric.loc.ploc(), 7);
9201 assert_eq!(metric.loc.lloc(), 3);
9202 assert_eq!(metric.loc.cloc(), 0);
9203 assert_eq!(metric.loc.blank(), 0);
9204 insta::assert_json_snapshot!(metric.loc);
9205 },
9206 );
9207 }
9208
9209 #[cfg(feature = "typescript")]
9210 #[test]
9211 fn typescript_nested_functions_loc() {
9212 check_metrics::<TypescriptParser>(
9213 "function outer(x: number): number {
9214 function inner(y: number): number {
9215 return y * 2;
9216 }
9217 return inner(x) + 1;
9218 }",
9219 "foo.ts",
9220 |metric| {
9221 assert_eq!(metric.loc.sloc(), 6);
9222 assert_eq!(metric.loc.ploc(), 6);
9223 assert_eq!(metric.loc.lloc(), 2);
9224 assert_eq!(metric.loc.cloc(), 0);
9225 assert_eq!(metric.loc.blank(), 0);
9226 insta::assert_json_snapshot!(metric.loc);
9227 },
9228 );
9229 }
9230
9231 #[cfg(feature = "typescript")]
9232 #[test]
9233 fn typescript_generic_function_loc() {
9234 check_metrics::<TypescriptParser>(
9235 "function identity<T>(value: T): T {
9236 return value;
9237 }
9238 function first<T>(arr: T[]): T | undefined {
9239 return arr[0];
9240 }",
9241 "foo.ts",
9242 |metric| {
9243 assert_eq!(metric.loc.sloc(), 6);
9244 assert_eq!(metric.loc.ploc(), 6);
9245 assert_eq!(metric.loc.lloc(), 2);
9246 assert_eq!(metric.loc.cloc(), 0);
9247 assert_eq!(metric.loc.blank(), 0);
9248 insta::assert_json_snapshot!(metric.loc);
9249 },
9250 );
9251 }
9252
9253 #[cfg(feature = "typescript")]
9254 #[test]
9255 fn tsx_blank() {
9256 check_metrics::<TsxParser>(
9257 "function f(): void {
9258
9259 const x = 1;
9260
9261 }",
9262 "foo.tsx",
9263 |metric| {
9264 assert_eq!(metric.loc.sloc(), 5);
9265 assert_eq!(metric.loc.ploc(), 3);
9266 assert_eq!(metric.loc.lloc(), 1);
9267 assert_eq!(metric.loc.cloc(), 0);
9268 assert_eq!(metric.loc.blank(), 2);
9269 insta::assert_json_snapshot!(metric.loc);
9270 },
9271 );
9272 }
9273
9274 #[cfg(feature = "typescript")]
9275 #[test]
9276 fn tsx_cloc() {
9277 check_metrics::<TsxParser>(
9278 "// header
9279 /* block
9280 comment */
9281 function f(): number {
9282 return 42; // inline
9283 }",
9284 "foo.tsx",
9285 |metric| {
9286 assert_eq!(metric.loc.sloc(), 6);
9287 assert_eq!(metric.loc.ploc(), 3);
9288 assert_eq!(metric.loc.lloc(), 1);
9289 assert_eq!(metric.loc.cloc(), 4);
9290 assert_eq!(metric.loc.blank(), 0);
9291 insta::assert_json_snapshot!(metric.loc);
9292 },
9293 );
9294 }
9295
9296 #[cfg(feature = "typescript")]
9297 #[test]
9298 fn tsx_no_zero_blank() {
9299 check_metrics::<TsxParser>(
9302 "function f(): void {
9303 const a = 1;
9304
9305 const b = 2; // trailing
9306 const c = 3; // trailing
9307}",
9308 "foo.tsx",
9309 |metric| {
9310 assert_eq!(metric.loc.sloc(), 6);
9311 assert_eq!(metric.loc.ploc(), 5);
9312 assert_eq!(metric.loc.cloc(), 2);
9313 assert_eq!(metric.loc.blank(), 1);
9314 insta::assert_json_snapshot!(metric.loc);
9315 },
9316 );
9317 }
9318
9319 #[cfg(feature = "typescript")]
9320 #[test]
9321 fn tsx_if_lloc() {
9322 check_metrics::<TsxParser>(
9323 "function classify(n: number): string {
9324 if (n > 0) {
9325 return 'positive';
9326 } else {
9327 return 'non-positive';
9328 }
9329 }",
9330 "foo.tsx",
9331 |metric| {
9332 assert_eq!(metric.loc.sloc(), 7);
9333 assert_eq!(metric.loc.ploc(), 7);
9334 assert_eq!(metric.loc.lloc(), 3);
9335 assert_eq!(metric.loc.cloc(), 0);
9336 assert_eq!(metric.loc.blank(), 0);
9337 insta::assert_json_snapshot!(metric.loc);
9338 },
9339 );
9340 }
9341
9342 #[cfg(feature = "typescript")]
9343 #[test]
9344 fn tsx_for_lloc() {
9345 check_metrics::<TsxParser>(
9346 "function sum(n: number): number {
9347 let s = 0;
9348 for (let i = 0; i < n; i++) {
9349 s += i;
9350 }
9351 return s;
9352 }",
9353 "foo.tsx",
9354 |metric| {
9355 assert_eq!(metric.loc.sloc(), 7);
9356 assert_eq!(metric.loc.ploc(), 7);
9357 assert_eq!(metric.loc.lloc(), 4);
9358 assert_eq!(metric.loc.cloc(), 0);
9359 assert_eq!(metric.loc.blank(), 0);
9360 insta::assert_json_snapshot!(metric.loc);
9361 },
9362 );
9363 }
9364
9365 #[cfg(feature = "typescript")]
9366 #[test]
9367 fn tsx_while_lloc() {
9368 check_metrics::<TsxParser>(
9369 "function countdown(n: number): void {
9370 let i = n;
9371 while (i > 0) {
9372 console.log(i);
9373 i--;
9374 }
9375 }",
9376 "foo.tsx",
9377 |metric| {
9378 assert_eq!(metric.loc.sloc(), 7);
9379 assert_eq!(metric.loc.ploc(), 7);
9380 assert_eq!(metric.loc.lloc(), 4);
9381 assert_eq!(metric.loc.cloc(), 0);
9382 assert_eq!(metric.loc.blank(), 0);
9383 insta::assert_json_snapshot!(metric.loc);
9384 },
9385 );
9386 }
9387
9388 #[cfg(feature = "typescript")]
9389 #[test]
9390 fn tsx_switch_lloc() {
9391 check_metrics::<TsxParser>(
9392 "function describe(x: number): string {
9393 switch (x) {
9394 case 1: return 'one';
9395 case 2: return 'two';
9396 default: return 'other';
9397 }
9398 }",
9399 "foo.tsx",
9400 |metric| {
9401 assert_eq!(metric.loc.sloc(), 7);
9402 assert_eq!(metric.loc.ploc(), 7);
9403 assert_eq!(metric.loc.lloc(), 4);
9404 assert_eq!(metric.loc.cloc(), 0);
9405 assert_eq!(metric.loc.blank(), 0);
9406 insta::assert_json_snapshot!(metric.loc);
9407 },
9408 );
9409 }
9410
9411 #[cfg(feature = "typescript")]
9412 #[test]
9413 fn tsx_class_loc() {
9414 check_metrics::<TsxParser>(
9415 "class Counter {
9416 private count: number = 0;
9417 increment(): void { this.count++; }
9418 get(): number { return this.count; }
9419 }",
9420 "foo.tsx",
9421 |metric| {
9422 assert_eq!(metric.loc.sloc(), 5);
9423 assert_eq!(metric.loc.ploc(), 5);
9424 assert_eq!(metric.loc.lloc(), 2);
9425 assert_eq!(metric.loc.cloc(), 0);
9426 assert_eq!(metric.loc.blank(), 0);
9427 insta::assert_json_snapshot!(metric.loc);
9428 },
9429 );
9430 }
9431
9432 #[cfg(feature = "typescript")]
9433 #[test]
9434 fn tsx_arrow_function_loc() {
9435 check_metrics::<TsxParser>(
9436 "const add = (a: number, b: number): number => a + b;
9437 const greet = (name: string): string => {
9438 return `Hello, ${name}`;
9439 };",
9440 "foo.tsx",
9441 |metric| {
9442 assert_eq!(metric.loc.sloc(), 4);
9443 assert_eq!(metric.loc.ploc(), 4);
9444 assert_eq!(metric.loc.lloc(), 3);
9445 assert_eq!(metric.loc.cloc(), 0);
9446 assert_eq!(metric.loc.blank(), 0);
9447 insta::assert_json_snapshot!(metric.loc);
9448 },
9449 );
9450 }
9451
9452 #[cfg(feature = "typescript")]
9453 #[test]
9454 fn tsx_multiple_functions_loc() {
9455 check_metrics::<TsxParser>(
9456 "function f(): number {
9457 return 1;
9458 }
9459 function g(): number {
9460 return 2;
9461 }
9462 function h(): number {
9463 return 3;
9464 }",
9465 "foo.tsx",
9466 |metric| {
9467 assert_eq!(metric.loc.sloc(), 9);
9468 assert_eq!(metric.loc.ploc(), 9);
9469 assert_eq!(metric.loc.lloc(), 3);
9470 assert_eq!(metric.loc.cloc(), 0);
9471 assert_eq!(metric.loc.blank(), 0);
9472 insta::assert_json_snapshot!(metric.loc);
9473 },
9474 );
9475 }
9476
9477 #[cfg(feature = "typescript")]
9478 #[test]
9479 fn tsx_try_catch_lloc() {
9480 check_metrics::<TsxParser>(
9481 "function safe(x: number): number {
9482 try {
9483 return 1 / x;
9484 } catch (e) {
9485 return 0;
9486 }
9487 }",
9488 "foo.tsx",
9489 |metric| {
9490 assert_eq!(metric.loc.sloc(), 7);
9491 assert_eq!(metric.loc.ploc(), 7);
9492 assert_eq!(metric.loc.lloc(), 3);
9493 assert_eq!(metric.loc.cloc(), 0);
9494 assert_eq!(metric.loc.blank(), 0);
9495 insta::assert_json_snapshot!(metric.loc);
9496 },
9497 );
9498 }
9499
9500 #[cfg(feature = "typescript")]
9501 #[test]
9502 fn tsx_nested_functions_loc() {
9503 check_metrics::<TsxParser>(
9504 "function outer(x: number): number {
9505 function inner(y: number): number {
9506 return y * 2;
9507 }
9508 return inner(x) + 1;
9509 }",
9510 "foo.tsx",
9511 |metric| {
9512 assert_eq!(metric.loc.sloc(), 6);
9513 assert_eq!(metric.loc.ploc(), 6);
9514 assert_eq!(metric.loc.lloc(), 2);
9515 assert_eq!(metric.loc.cloc(), 0);
9516 assert_eq!(metric.loc.blank(), 0);
9517 insta::assert_json_snapshot!(metric.loc);
9518 },
9519 );
9520 }
9521
9522 #[cfg(feature = "typescript")]
9523 #[test]
9524 fn tsx_interface_loc() {
9525 check_metrics::<TsxParser>(
9526 "interface Shape {
9527 area(): number;
9528 perimeter(): number;
9529 }
9530 function describe(s: Shape): string {
9531 return `area=${s.area()}`;
9532 }",
9533 "foo.tsx",
9534 |metric| {
9535 assert_eq!(metric.loc.sloc(), 7);
9536 assert_eq!(metric.loc.ploc(), 7);
9537 assert_eq!(metric.loc.lloc(), 1);
9538 assert_eq!(metric.loc.cloc(), 0);
9539 assert_eq!(metric.loc.blank(), 0);
9540 insta::assert_json_snapshot!(metric.loc);
9541 },
9542 );
9543 }
9544
9545 #[cfg(feature = "typescript")]
9546 #[test]
9547 fn tsx_generic_function_loc() {
9548 check_metrics::<TsxParser>(
9549 "function identity<T>(value: T): T {
9550 return value;
9551 }
9552 function first<T>(arr: T[]): T | undefined {
9553 return arr[0];
9554 }",
9555 "foo.tsx",
9556 |metric| {
9557 assert_eq!(metric.loc.sloc(), 6);
9558 assert_eq!(metric.loc.ploc(), 6);
9559 assert_eq!(metric.loc.lloc(), 2);
9560 assert_eq!(metric.loc.cloc(), 0);
9561 assert_eq!(metric.loc.blank(), 0);
9562 insta::assert_json_snapshot!(metric.loc);
9563 },
9564 );
9565 }
9566
9567 #[cfg(feature = "php")]
9568 #[test]
9569 fn php_blank() {
9570 check_metrics::<PhpParser>(
9571 "<?php
9572
9573$a = 1;
9574
9575$b = 2;
9576
9577",
9578 "foo.php",
9579 |metric| {
9580 assert_eq!(metric.loc.sloc(), 5);
9581 assert_eq!(metric.loc.ploc(), 3);
9582 assert_eq!(metric.loc.lloc(), 2);
9583 assert_eq!(metric.loc.cloc(), 0);
9584 assert_eq!(metric.loc.blank(), 2);
9585 insta::assert_json_snapshot!(metric.loc);
9586 },
9587 );
9588 }
9589
9590 #[cfg(feature = "php")]
9591 #[test]
9592 fn php_no_zero_blank() {
9593 check_metrics::<PhpParser>(
9596 "<?php
9597$a = 1;
9598
9599$b = 2; // trailing
9600$c = 3; // trailing
9601",
9602 "foo.php",
9603 |metric| {
9604 assert_eq!(metric.loc.sloc(), 5);
9605 assert_eq!(metric.loc.ploc(), 4);
9606 assert_eq!(metric.loc.cloc(), 2);
9607 assert_eq!(metric.loc.blank(), 1);
9608 insta::assert_json_snapshot!(metric.loc);
9609 },
9610 );
9611 }
9612
9613 #[cfg(feature = "php")]
9614 #[test]
9615 fn php_cloc_double_slash() {
9616 check_metrics::<PhpParser>(
9617 "<?php
9618// first
9619// second
9620$a = 1; // trailing",
9621 "foo.php",
9622 |metric| {
9623 assert_eq!(metric.loc.sloc(), 4);
9624 assert_eq!(metric.loc.ploc(), 2);
9625 assert_eq!(metric.loc.lloc(), 1);
9626 assert_eq!(metric.loc.cloc(), 3);
9627 assert_eq!(metric.loc.blank(), 0);
9628 insta::assert_json_snapshot!(metric.loc);
9629 },
9630 );
9631 }
9632
9633 #[cfg(feature = "php")]
9634 #[test]
9635 fn php_cloc_hash() {
9636 check_metrics::<PhpParser>(
9637 "<?php
9638# first
9639# second
9640$a = 1;",
9641 "foo.php",
9642 |metric| {
9643 assert_eq!(metric.loc.sloc(), 4);
9644 assert_eq!(metric.loc.ploc(), 2);
9645 assert_eq!(metric.loc.lloc(), 1);
9646 assert_eq!(metric.loc.cloc(), 2);
9647 assert_eq!(metric.loc.blank(), 0);
9648 insta::assert_json_snapshot!(metric.loc);
9649 },
9650 );
9651 }
9652
9653 #[cfg(feature = "php")]
9654 #[test]
9655 fn php_cloc_block() {
9656 check_metrics::<PhpParser>(
9657 "<?php
9658/*
9659 * block
9660 * comment
9661 */
9662$a = 1;",
9663 "foo.php",
9664 |metric| {
9665 assert_eq!(metric.loc.sloc(), 6);
9666 assert_eq!(metric.loc.ploc(), 2);
9667 assert_eq!(metric.loc.lloc(), 1);
9668 assert_eq!(metric.loc.cloc(), 4);
9669 assert_eq!(metric.loc.blank(), 0);
9670 insta::assert_json_snapshot!(metric.loc);
9671 },
9672 );
9673 }
9674
9675 #[cfg(feature = "php")]
9676 #[test]
9677 fn php_lloc() {
9678 check_metrics::<PhpParser>(
9680 "<?php
9681$a = 1;
9682if ($a > 0) {
9683 echo $a;
9684}",
9685 "foo.php",
9686 |metric| {
9687 assert_eq!(metric.loc.sloc(), 5);
9688 assert_eq!(metric.loc.ploc(), 5);
9689 assert_eq!(metric.loc.lloc(), 3);
9690 assert_eq!(metric.loc.cloc(), 0);
9691 assert_eq!(metric.loc.blank(), 0);
9692 insta::assert_json_snapshot!(metric.loc);
9693 },
9694 );
9695 }
9696
9697 #[cfg(feature = "php")]
9698 #[test]
9699 fn php_no_parenthesized_expression_lloc() {
9700 check_metrics::<PhpParser>(
9703 "<?php
9704$a = (1 + 2);",
9705 "foo.php",
9706 |metric| {
9707 assert_eq!(metric.loc.sloc(), 2);
9708 assert_eq!(metric.loc.ploc(), 2);
9709 assert_eq!(metric.loc.lloc(), 1);
9710 assert_eq!(metric.loc.cloc(), 0);
9711 assert_eq!(metric.loc.blank(), 0);
9712 insta::assert_json_snapshot!(metric.loc);
9713 },
9714 );
9715 }
9716
9717 #[cfg(feature = "php")]
9718 #[test]
9719 fn php_no_compound_statement_lloc() {
9720 check_metrics::<PhpParser>(
9722 "<?php
9723function f(): void {
9724 $a = 1;
9725}",
9726 "foo.php",
9727 |metric| {
9728 assert_eq!(metric.loc.sloc(), 4);
9729 assert_eq!(metric.loc.ploc(), 4);
9730 assert_eq!(metric.loc.lloc(), 1);
9731 assert_eq!(metric.loc.cloc(), 0);
9732 assert_eq!(metric.loc.blank(), 0);
9733 insta::assert_json_snapshot!(metric.loc);
9734 },
9735 );
9736 }
9737
9738 #[cfg(feature = "php")]
9739 #[test]
9740 fn php_no_colon_block_lloc() {
9741 check_metrics::<PhpParser>(
9744 "<?php
9745if (true):
9746 $a = 1;
9747endif;",
9748 "foo.php",
9749 |metric| {
9750 assert_eq!(metric.loc.sloc(), 4);
9751 assert_eq!(metric.loc.ploc(), 4);
9752 assert_eq!(metric.loc.lloc(), 2);
9753 assert_eq!(metric.loc.cloc(), 0);
9754 assert_eq!(metric.loc.blank(), 0);
9755 insta::assert_json_snapshot!(metric.loc);
9756 },
9757 );
9758 }
9759
9760 #[cfg(feature = "php")]
9761 #[test]
9762 fn php_no_else_clause_lloc() {
9763 check_metrics::<PhpParser>(
9765 "<?php
9766if ($x) {
9767 $a = 1;
9768} elseif ($y) {
9769 $a = 2;
9770} else {
9771 $a = 3;
9772}",
9773 "foo.php",
9774 |metric| {
9775 assert_eq!(metric.loc.sloc(), 8);
9776 assert_eq!(metric.loc.ploc(), 8);
9777 assert_eq!(metric.loc.lloc(), 4);
9778 assert_eq!(metric.loc.cloc(), 0);
9779 assert_eq!(metric.loc.blank(), 0);
9780 insta::assert_json_snapshot!(metric.loc);
9781 },
9782 );
9783 }
9784
9785 #[cfg(feature = "php")]
9786 #[test]
9787 fn php_no_case_statement_lloc() {
9788 check_metrics::<PhpParser>(
9791 "<?php
9792switch ($x) {
9793 case 1:
9794 $a = 1;
9795 break;
9796 case 2:
9797 $a = 2;
9798 break;
9799 default:
9800 $a = 0;
9801}",
9802 "foo.php",
9803 |metric| {
9804 assert_eq!(metric.loc.sloc(), 11);
9805 assert_eq!(metric.loc.ploc(), 11);
9806 assert_eq!(metric.loc.lloc(), 6);
9807 assert_eq!(metric.loc.cloc(), 0);
9808 assert_eq!(metric.loc.blank(), 0);
9809 insta::assert_json_snapshot!(metric.loc);
9810 },
9811 );
9812 }
9813
9814 #[cfg(feature = "php")]
9815 #[test]
9816 fn php_no_match_arm_lloc() {
9817 check_metrics::<PhpParser>(
9820 "<?php
9821$a = match ($x) {
9822 1 => 'one',
9823 2 => 'two',
9824 default => 'other',
9825};",
9826 "foo.php",
9827 |metric| {
9828 assert_eq!(metric.loc.sloc(), 6);
9829 assert_eq!(metric.loc.ploc(), 6);
9830 assert_eq!(metric.loc.lloc(), 1);
9831 assert_eq!(metric.loc.cloc(), 0);
9832 assert_eq!(metric.loc.blank(), 0);
9833 insta::assert_json_snapshot!(metric.loc);
9834 },
9835 );
9836 }
9837
9838 #[cfg(feature = "php")]
9839 #[test]
9840 fn php_no_throw_in_expression_lloc() {
9841 check_metrics::<PhpParser>(
9844 "<?php
9845$x = $y ?? throw new \\Exception('nope');",
9846 "foo.php",
9847 |metric| {
9848 assert_eq!(metric.loc.sloc(), 2);
9849 assert_eq!(metric.loc.ploc(), 2);
9850 assert_eq!(metric.loc.lloc(), 1);
9851 assert_eq!(metric.loc.cloc(), 0);
9852 assert_eq!(metric.loc.blank(), 0);
9853 insta::assert_json_snapshot!(metric.loc);
9854 },
9855 );
9856 }
9857
9858 #[cfg(feature = "php")]
9859 #[test]
9860 fn php_no_closure_in_assignment_lloc() {
9861 check_metrics::<PhpParser>(
9865 "<?php
9866$f = function (): int {
9867 return 42;
9868};",
9869 "foo.php",
9870 |metric| {
9871 assert_eq!(metric.loc.sloc(), 4);
9872 assert_eq!(metric.loc.ploc(), 4);
9873 assert_eq!(metric.loc.lloc(), 2);
9874 assert_eq!(metric.loc.cloc(), 0);
9875 assert_eq!(metric.loc.blank(), 0);
9876 insta::assert_json_snapshot!(metric.loc);
9877 },
9878 );
9879 }
9880
9881 #[cfg(feature = "php")]
9882 #[test]
9883 fn php_for_lloc() {
9884 check_metrics::<PhpParser>(
9887 "<?php
9888for ($i = 0; $i < 10; $i++) {
9889 echo $i;
9890}",
9891 "foo.php",
9892 |metric| {
9893 assert_eq!(metric.loc.sloc(), 4);
9894 assert_eq!(metric.loc.ploc(), 4);
9895 assert_eq!(metric.loc.lloc(), 2);
9896 assert_eq!(metric.loc.cloc(), 0);
9897 assert_eq!(metric.loc.blank(), 0);
9898 insta::assert_json_snapshot!(metric.loc);
9899 },
9900 );
9901 }
9902
9903 #[cfg(feature = "php")]
9904 #[test]
9905 fn php_foreach_lloc() {
9906 check_metrics::<PhpParser>(
9907 "<?php
9908foreach ($items as $k => $v) {
9909 echo $v;
9910}",
9911 "foo.php",
9912 |metric| {
9913 assert_eq!(metric.loc.sloc(), 4);
9914 assert_eq!(metric.loc.ploc(), 4);
9915 assert_eq!(metric.loc.lloc(), 2);
9916 assert_eq!(metric.loc.cloc(), 0);
9917 assert_eq!(metric.loc.blank(), 0);
9918 insta::assert_json_snapshot!(metric.loc);
9919 },
9920 );
9921 }
9922
9923 #[cfg(feature = "php")]
9924 #[test]
9925 fn php_try_lloc() {
9926 check_metrics::<PhpParser>(
9927 "<?php
9928try {
9929 $a = 1;
9930} catch (\\Exception $e) {
9931 $a = 0;
9932} finally {
9933 $b = 2;
9934}",
9935 "foo.php",
9936 |metric| {
9937 assert_eq!(metric.loc.sloc(), 8);
9938 assert_eq!(metric.loc.ploc(), 8);
9939 assert_eq!(metric.loc.lloc(), 4);
9940 assert_eq!(metric.loc.cloc(), 0);
9941 assert_eq!(metric.loc.blank(), 0);
9942 insta::assert_json_snapshot!(metric.loc);
9943 },
9944 );
9945 }
9946
9947 #[cfg(feature = "php")]
9948 #[test]
9949 fn php_class_loc() {
9950 check_metrics::<PhpParser>(
9951 "<?php
9952class A {
9953 public int $x = 0;
9954 private const Y = 1;
9955 public function f(): int {
9956 return $this->x;
9957 }
9958}",
9959 "foo.php",
9960 |metric| {
9961 assert_eq!(metric.loc.sloc(), 8);
9962 assert_eq!(metric.loc.ploc(), 8);
9963 assert_eq!(metric.loc.lloc(), 3);
9964 assert_eq!(metric.loc.cloc(), 0);
9965 assert_eq!(metric.loc.blank(), 0);
9966 insta::assert_json_snapshot!(metric.loc);
9967 },
9968 );
9969 }
9970
9971 #[cfg(feature = "php")]
9972 #[test]
9973 fn php_namespace_use_lloc() {
9974 check_metrics::<PhpParser>(
9975 "<?php
9976namespace App;
9977use App\\Foo;
9978use App\\Bar;
9979$a = 1;",
9980 "foo.php",
9981 |metric| {
9982 assert_eq!(metric.loc.sloc(), 5);
9983 assert_eq!(metric.loc.ploc(), 5);
9984 assert_eq!(metric.loc.lloc(), 3);
9985 assert_eq!(metric.loc.cloc(), 0);
9986 assert_eq!(metric.loc.blank(), 0);
9987 insta::assert_json_snapshot!(metric.loc);
9988 },
9989 );
9990 }
9991
9992 #[cfg(feature = "php")]
9993 #[test]
9994 fn php_general_loc() {
9995 check_metrics::<PhpParser>(
9996 "<?php
9997// header
9998namespace App;
9999use App\\Foo;
10000
10001class Bar {
10002 public int $n = 0;
10003
10004 public function add(int $x): int {
10005 if ($x > 0) {
10006 return $this->n + $x;
10007 }
10008 return $this->n;
10009 }
10010}",
10011 "foo.php",
10012 |metric| {
10013 assert_eq!(metric.loc.sloc(), 15);
10014 assert_eq!(metric.loc.ploc(), 12);
10015 assert_eq!(metric.loc.lloc(), 5);
10016 assert_eq!(metric.loc.cloc(), 1);
10017 assert_eq!(metric.loc.blank(), 2);
10018 insta::assert_json_snapshot!(metric.loc);
10019 },
10020 );
10021 }
10022
10023 #[cfg(feature = "php")]
10024 #[test]
10025 fn php_match_in_expression_lloc() {
10026 check_metrics::<PhpParser>(
10029 "<?php
10030$y = 10 + match ($x) { 1 => 2, default => 0 };",
10031 "foo.php",
10032 |metric| {
10033 assert_eq!(metric.loc.sloc(), 2);
10034 assert_eq!(metric.loc.ploc(), 2);
10035 assert_eq!(metric.loc.lloc(), 1);
10036 assert_eq!(metric.loc.cloc(), 0);
10037 assert_eq!(metric.loc.blank(), 0);
10038 insta::assert_json_snapshot!(metric.loc);
10039 },
10040 );
10041 }
10042
10043 #[cfg(feature = "php")]
10044 #[test]
10045 fn php_html_island_ploc() {
10046 check_metrics::<PhpParser>(
10052 "<?php if ($cond): ?>
10053<div>hello</div>
10054<p>world</p>
10055<?php endif; ?>",
10056 "foo.php",
10057 |metric| {
10058 assert_eq!(metric.loc.sloc(), 4);
10059 assert_eq!(metric.loc.ploc(), 3);
10060 assert_eq!(metric.loc.lloc(), 1);
10061 assert_eq!(metric.loc.cloc(), 0);
10062 assert_eq!(metric.loc.blank(), 1);
10063 insta::assert_json_snapshot!(metric.loc);
10064 },
10065 );
10066 }
10067
10068 #[cfg(feature = "php")]
10069 #[test]
10070 fn php_short_echo_tag_ploc() {
10071 check_metrics::<PhpParser>("<p><?= $name ?></p>", "foo.php", |metric| {
10075 assert_eq!(metric.loc.sloc(), 1);
10076 assert_eq!(metric.loc.ploc(), 1);
10077 assert_eq!(metric.loc.lloc(), 1);
10078 assert_eq!(metric.loc.cloc(), 0);
10079 assert_eq!(metric.loc.blank(), 0);
10080 insta::assert_json_snapshot!(metric.loc);
10081 });
10082 }
10083
10084 #[cfg(feature = "php")]
10090 fn php_loc(source: &[u8]) -> Stats {
10091 metrics_verbatim(
10092 crate::LANG::Php,
10093 source,
10094 crate::MetricsOptions::default().with_only(&[crate::Metric::Loc]),
10095 )
10096 .loc
10097 }
10098
10099 #[cfg(feature = "php")]
10112 #[test]
10113 fn php_heredoc_empty_interior_row_is_code_not_blank() {
10114 check_metrics::<PhpParser>("<?php\n$s = <<<EOT\na\n\nb\nEOT;\n", "foo.php", |metric| {
10117 assert_eq!(metric.loc.sloc(), 6);
10118 assert_eq!(metric.loc.ploc(), 6);
10119 assert_eq!(metric.loc.cloc(), 0);
10120 assert_eq!(metric.loc.lloc(), 1);
10121 assert_eq!(metric.loc.blank(), 0);
10122 });
10123 }
10124
10125 #[cfg(feature = "php")]
10136 #[test]
10137 fn php_nowdoc_empty_interior_row_is_code_not_blank() {
10138 check_metrics::<PhpParser>(
10140 "<?php\n$s = <<<'EOT'\na\n\nb\nEOT;\n",
10141 "foo.php",
10142 |metric| {
10143 assert_eq!(metric.loc.sloc(), 6);
10144 assert_eq!(metric.loc.ploc(), 6);
10145 assert_eq!(metric.loc.cloc(), 0);
10146 assert_eq!(metric.loc.lloc(), 1);
10147 assert_eq!(metric.loc.blank(), 0);
10148 },
10149 );
10150 }
10151
10152 #[cfg(feature = "php")]
10178 #[test]
10179 fn php_heredoc_spellings_credit_every_row_to_ploc() {
10180 let cases = [
10184 ("all-empty heredoc body", "<?php\n$s = <<<EOT\n\nEOT;\n", 4),
10185 ("all-empty nowdoc body", "<?php\n$s = <<<'EOT'\n\nEOT;\n", 4),
10186 (
10187 "interpolated heredoc",
10188 "<?php\n$v = 1;\n$s = <<<EOT\na\n\n{$v}\nEOT;\n",
10189 7,
10190 ),
10191 (
10192 "indented closing marker",
10193 "<?php\n$s = <<<EOT\n a\n\n b\n EOT;\n",
10194 6,
10195 ),
10196 ("backtick shell command", "<?php\n$s = `ls\n\n-l`;\n", 4),
10197 ];
10198 for (label, source, sloc) in cases {
10199 let loc = php_loc(source.as_bytes());
10200 assert_eq!(loc.sloc(), sloc, "{label} sloc");
10201 assert_eq!(loc.ploc(), sloc, "{label} ploc");
10202 assert_eq!(loc.cloc(), 0, "{label} cloc");
10203 assert_eq!(loc.blank(), 0, "{label} blank");
10204 }
10205 }
10206
10207 #[cfg(feature = "php")]
10221 #[test]
10222 fn php_heredoc_credits_only_the_literals_rows() {
10223 let loc = php_loc(b"<?php\n// note\n\n$s = <<<EOT\na\n\nb\nEOT;\n");
10227 assert_eq!(loc.sloc(), 8);
10228 assert_eq!(loc.ploc(), 6);
10229 assert_eq!(loc.cloc(), 1);
10230 assert_eq!(loc.blank(), 1);
10231 }
10232
10233 #[cfg(feature = "php")]
10245 #[test]
10246 fn php_heredoc_ending_at_eof_credits_every_row() {
10247 let loc = php_loc(b"<?php\n$s = <<<EOT\na\n\nb\nEOT;");
10250 assert_eq!(loc.sloc(), 6);
10251 assert_eq!(loc.ploc(), 6);
10252 assert_eq!(loc.cloc(), 0);
10253 assert_eq!(loc.blank(), 0);
10254 }
10255
10256 #[cfg(feature = "rust")]
10264 fn multiline_string_gate_fixture() -> crate::Ast {
10265 crate::test_support::parse_named(
10266 LANG::Rust,
10267 "lib.rs",
10268 "const A: [&str; 1] = [\n \"a\nb\",\n];\nconst B: &str = \"c\nd\";\n",
10269 )
10270 }
10271
10272 #[cfg(feature = "rust")]
10280 fn multiline_string_gate_literal<'a>(
10281 root: &Node<'a>,
10282 index: usize,
10283 rows: (usize, usize),
10284 ) -> Node<'a> {
10285 let literals = root.descendants_by_kind(&["string_literal"]);
10286 assert_eq!(
10287 literals.len(),
10288 2,
10289 "fixture drift: expected exactly two string literals"
10290 );
10291 let literal = literals[index];
10292 assert_eq!(
10293 (literal.start_row(), literal.end_row()),
10294 rows,
10295 "fixture drift: literal {index} no longer spans the expected rows"
10296 );
10297 literal
10298 }
10299
10300 #[cfg(feature = "rust")]
10315 #[test]
10316 fn multiline_string_credits_an_opening_row_its_parent_did_not_start() {
10317 let ast = multiline_string_gate_fixture();
10318 let root = ast.root_node();
10319 let literal = multiline_string_gate_literal(&root, 0, (1, 2));
10320
10321 let mut stats = Stats::default();
10322 stats.ploc.lines.insert(0);
10325
10326 add_multiline_string_ploc(
10327 &literal,
10328 Ancestors::unknown(),
10329 &mut stats,
10330 literal.start_row(),
10331 );
10332
10333 assert!(stats.ploc.lines.contains(0), "the seeded row must survive");
10334 assert!(
10335 stats.ploc.lines.contains(1),
10336 "the `array_expression` parent starts on row 0, so nothing but \
10337 this helper covers the literal's opening row"
10338 );
10339 assert!(
10340 stats.ploc.lines.contains(2),
10341 "the literal's closing row is interior to it"
10342 );
10343 assert!(
10344 !stats.ploc.lines.contains(3),
10345 "row 3 is `];`, past the literal"
10346 );
10347 }
10348
10349 #[cfg(feature = "rust")]
10354 #[test]
10355 fn multiline_string_leaves_an_opening_row_its_parent_started() {
10356 let ast = multiline_string_gate_fixture();
10357 let root = ast.root_node();
10358 let literal = multiline_string_gate_literal(&root, 1, (4, 5));
10359
10360 let mut stats = Stats::default();
10361 stats.ploc.lines.insert(0);
10362
10363 add_multiline_string_ploc(
10364 &literal,
10365 Ancestors::unknown(),
10366 &mut stats,
10367 literal.start_row(),
10368 );
10369
10370 assert!(stats.ploc.lines.contains(0), "the seeded row must survive");
10371 assert!(
10372 !stats.ploc.lines.contains(4),
10373 "the `const_item` parent starts on row 4 and already covers it, \
10374 so the gate must skip it"
10375 );
10376 assert!(
10377 stats.ploc.lines.contains(5),
10378 "the interior insertion runs whichever way the gate goes"
10379 );
10380 }
10381
10382 #[cfg(feature = "bash")]
10402 #[test]
10403 fn string_interior_stops_at_the_last_row_the_literal_occupies() {
10404 let ast = crate::test_support::parse_named(LANG::Bash, "f.sh", "cat <<EOF\na\nb\nEOF\n");
10406 let root = ast.root_node();
10407 let bodies = root.descendants_by_kind(&["heredoc_body"]);
10408 let body = bodies.first().expect("the fixture holds one heredoc body");
10409 assert_eq!(
10410 (body.start_position(), body.end_position()),
10411 ((1, 0), (3, 0)),
10412 "fixture drift: the end column must be 0 for this test to mean anything"
10413 );
10414
10415 let mut stats = Stats::default();
10416 stats.ploc.lines.insert(0);
10420
10421 add_string_interior_ploc(body, &mut stats, body.start_row());
10422
10423 assert!(stats.ploc.lines.contains(0), "the seeded row must survive");
10424 assert!(
10425 !stats.ploc.lines.contains(1),
10426 "this helper leaves its `start` row to the caller"
10427 );
10428 assert!(
10429 stats.ploc.lines.contains(2),
10430 "row 2 is the body's last content row"
10431 );
10432 assert!(
10433 !stats.ploc.lines.contains(3),
10434 "row 3 holds the `EOF` terminator: the body ends at its column 0 \
10435 and so does not occupy it"
10436 );
10437 }
10438
10439 #[cfg(feature = "elixir")]
10440 #[test]
10441 fn elixir_blank() {
10442 check_metrics::<ElixirParser>(
10444 "defmodule Foo do\n\n def a, do: :a\n\n def b, do: :b\nend\n",
10445 "foo.ex",
10446 |metric| {
10447 assert_eq!(metric.loc.sloc(), 6);
10448 assert_eq!(metric.loc.ploc(), 4);
10449 assert_eq!(metric.loc.lloc(), 3);
10450 assert_eq!(metric.loc.cloc(), 0);
10451 assert_eq!(metric.loc.blank(), 2);
10452 insta::assert_json_snapshot!(
10453 metric.loc,
10454 @r#"
10455 {
10456 "sloc": 6,
10457 "ploc": 4,
10458 "lloc": 3,
10459 "cloc": 0,
10460 "blank": 2,
10461 "dloc": 0,
10462 "sloc_average": 1.5,
10463 "ploc_average": 1.0,
10464 "lloc_average": 0.75,
10465 "cloc_average": 0.0,
10466 "blank_average": 0.5,
10467 "dloc_average": 0.0,
10468 "sloc_min": 1,
10469 "sloc_max": 6,
10470 "cloc_min": 0,
10471 "cloc_max": 0,
10472 "ploc_min": 1,
10473 "ploc_max": 4,
10474 "lloc_min": 1,
10475 "lloc_max": 3,
10476 "blank_min": 0,
10477 "blank_max": 2,
10478 "dloc_min": 0,
10479 "dloc_max": 0
10480 }
10481 "#
10482 );
10483 },
10484 );
10485 }
10486
10487 #[cfg(feature = "elixir")]
10488 #[test]
10489 fn elixir_no_zero_blank() {
10490 check_metrics::<ElixirParser>(
10493 "defmodule Foo do\n def f, do: :ok\n\n def g, do: :ok # trailing\n def h, do: :ok # trailing\nend\n",
10494 "foo.ex",
10495 |metric| {
10496 assert_eq!(metric.loc.sloc(), 6);
10497 assert_eq!(metric.loc.ploc(), 5);
10498 assert_eq!(metric.loc.cloc(), 2);
10499 assert_eq!(metric.loc.blank(), 1);
10500 },
10501 );
10502 }
10503
10504 #[cfg(feature = "elixir")]
10505 #[test]
10506 fn elixir_blank_zero_sanity() {
10507 check_metrics::<ElixirParser>(
10510 "defmodule Foo do\n def f, do: :ok\nend\n",
10511 "foo.ex",
10512 |metric| {
10513 assert_eq!(metric.loc.blank(), 0);
10514 },
10515 );
10516 }
10517
10518 #[cfg(feature = "elixir")]
10519 #[test]
10520 fn elixir_cloc() {
10521 check_metrics::<ElixirParser>(
10524 "# top\ndefmodule Foo do\n # body\n def f, do: :ok # trailing\nend\n",
10525 "foo.ex",
10526 |metric| {
10527 assert_eq!(metric.loc.cloc(), 3);
10528 },
10529 );
10530 }
10531
10532 #[cfg(feature = "elixir")]
10533 #[test]
10534 fn elixir_lloc() {
10535 check_metrics::<ElixirParser>(
10540 "defmodule Foo do\n def a, do: 1\n def b, do: 2\nend\n",
10541 "foo.ex",
10542 |metric| {
10543 assert_eq!(metric.loc.lloc(), 3);
10544 },
10545 );
10546 }
10547
10548 #[cfg(feature = "elixir")]
10549 #[test]
10550 fn elixir_no_nested_call_lloc() {
10551 check_metrics::<ElixirParser>(
10555 "defmodule Foo do\n def f do\n IO.puts(Enum.join([1, 2, 3], \", \"))\n end\nend\n",
10556 "foo.ex",
10557 |metric| {
10558 assert_eq!(metric.loc.lloc(), 3);
10559 },
10560 );
10561 }
10562
10563 #[cfg(feature = "elixir")]
10564 #[test]
10565 fn elixir_no_binary_operator_inside_call_lloc() {
10566 check_metrics::<ElixirParser>(
10572 "defmodule Foo do\n def f(a, b) do\n IO.puts(a + b)\n end\nend\n",
10573 "foo.ex",
10574 |metric| {
10575 assert_eq!(metric.loc.lloc(), 3);
10576 },
10577 );
10578 }
10579
10580 #[cfg(feature = "elixir")]
10581 #[test]
10582 fn elixir_stab_clause_counts_lloc() {
10583 check_metrics::<ElixirParser>(
10587 "defmodule Foo do\n def f(x) do\n case x do\n 1 -> :a\n 2 -> :b\n _ -> :c\n end\n end\nend\n",
10588 "foo.ex",
10589 |metric| {
10590 assert_eq!(metric.loc.lloc(), 6);
10591 },
10592 );
10593 }
10594
10595 #[cfg(feature = "elixir")]
10596 #[test]
10597 fn elixir_no_comment_lloc() {
10598 check_metrics::<ElixirParser>(
10603 "# leading\ndefmodule Foo do\n # inside\n def f, do: :ok\n # trailing\nend\n",
10604 "foo.ex",
10605 |metric| {
10606 assert_eq!(metric.loc.lloc(), 2);
10607 },
10608 );
10609 }
10610
10611 #[cfg(feature = "elixir")]
10612 #[test]
10613 fn elixir_no_do_token_lloc() {
10614 check_metrics::<ElixirParser>("defmodule Foo do\n :ok\nend\n", "foo.ex", |metric| {
10619 assert_eq!(metric.loc.lloc(), 2);
10622 });
10623 }
10624
10625 #[cfg(feature = "elixir")]
10626 #[test]
10627 fn elixir_no_keyword_pair_lloc() {
10628 check_metrics::<ElixirParser>(
10632 "defmodule Foo do\n def add(a, b), do: a + b\nend\n",
10633 "foo.ex",
10634 |metric| {
10635 assert_eq!(metric.loc.lloc(), 2);
10637 },
10638 );
10639 }
10640
10641 #[cfg(feature = "elixir")]
10642 #[test]
10643 fn elixir_no_string_content_lloc() {
10644 check_metrics::<ElixirParser>(
10649 "defmodule Foo do\n @moduledoc \"\"\"\n line one\n line two\n \"\"\"\n def f, do: :ok\nend\n",
10650 "foo.ex",
10651 |metric| {
10652 assert_eq!(metric.loc.lloc(), 3);
10654 },
10655 );
10656 }
10657
10658 #[cfg(feature = "elixir")]
10659 #[test]
10660 fn elixir_multiline_string_forms_credit_every_row_to_ploc() {
10661 check_metrics::<ElixirParser>(
10669 "x = \"line one\nline two\nline three\"",
10670 "foo.ex",
10671 |metric| {
10672 assert_eq!(metric.loc.sloc(), 3);
10673 assert_eq!(metric.loc.ploc(), 3);
10674 assert_eq!(metric.loc.cloc(), 0);
10675 assert_eq!(metric.loc.blank(), 0);
10676 },
10677 );
10678
10679 check_metrics::<ElixirParser>("c = '''\nc1\n\n'''", "foo.ex", |metric| {
10682 assert_eq!(metric.loc.sloc(), 4);
10683 assert_eq!(metric.loc.ploc(), 4);
10684 assert_eq!(metric.loc.cloc(), 0);
10685 assert_eq!(metric.loc.blank(), 0);
10686 });
10687
10688 check_metrics::<ElixirParser>("s = ~s\"\"\"\ns1\ns2\n\"\"\"", "foo.ex", |metric| {
10693 assert_eq!(metric.loc.sloc(), 4);
10694 assert_eq!(metric.loc.ploc(), 4);
10695 assert_eq!(metric.loc.cloc(), 0);
10696 assert_eq!(metric.loc.blank(), 0);
10697 });
10698 }
10699
10700 #[cfg(feature = "elixir")]
10701 #[test]
10702 fn elixir_module_attribute_docstring_rows_are_ploc_not_cloc() {
10703 check_metrics::<ElixirParser>(
10717 "defmodule M do\n @doc \"\"\"\n docs one\n docs two\n \"\"\"\n def f, do: :ok\nend",
10718 "foo.ex",
10719 |metric| {
10720 assert_eq!(metric.loc.sloc(), 7);
10721 assert_eq!(metric.loc.ploc(), 7);
10722 assert_eq!(metric.loc.lloc(), 3);
10723 assert_eq!(metric.loc.cloc(), 0);
10724 assert_eq!(metric.loc.dloc(), 0);
10729 assert_eq!(metric.loc.blank(), 0);
10730 },
10731 );
10732 }
10733
10734 #[cfg(feature = "elixir")]
10735 #[test]
10736 fn elixir_rescue_arm_counts_lloc() {
10737 check_metrics::<ElixirParser>(
10744 "defmodule Foo do\n def safe do\n try do\n do_it()\n rescue\n ArgumentError -> :bad\n RuntimeError -> :worse\n end\n end\nend\n",
10745 "foo.ex",
10746 |metric| {
10747 assert_eq!(metric.loc.lloc(), 7);
10748 },
10749 );
10750 }
10751
10752 #[cfg(feature = "elixir")]
10753 #[test]
10754 fn elixir_no_arg_punctuation_lloc() {
10755 check_metrics::<ElixirParser>(
10759 "defmodule Foo do\n def add(a, b, c, d) do\n a + b + c + d\n end\nend\n",
10760 "foo.ex",
10761 |metric| {
10762 assert_eq!(metric.loc.lloc(), 3);
10764 },
10765 );
10766 }
10767
10768 #[cfg(feature = "elixir")]
10769 #[test]
10770 fn elixir_no_list_element_lloc() {
10771 check_metrics::<ElixirParser>(
10774 "defmodule Foo do\n def f do\n [:a, :b, :c, :d]\n end\nend\n",
10775 "foo.ex",
10776 |metric| {
10777 assert_eq!(metric.loc.lloc(), 3);
10779 },
10780 );
10781 }
10782
10783 #[cfg(feature = "elixir")]
10784 #[test]
10785 fn elixir_no_map_field_lloc() {
10786 check_metrics::<ElixirParser>(
10788 "defmodule Foo do\n def f do\n %{a: 1, b: 2, c: 3}\n end\nend\n",
10789 "foo.ex",
10790 |metric| {
10791 assert_eq!(metric.loc.lloc(), 3);
10792 },
10793 );
10794 }
10795
10796 #[cfg(feature = "elixir")]
10797 #[test]
10798 fn elixir_anonymous_fn_body_lloc() {
10799 check_metrics::<ElixirParser>(
10807 "defmodule Foo do\n def f do\n add = fn x -> x + 1 end\n add\n end\nend\n",
10808 "foo.ex",
10809 |metric| {
10810 assert_eq!(metric.loc.lloc(), 5);
10811 },
10812 );
10813 }
10814
10815 #[cfg(feature = "ruby")]
10816 #[test]
10817 fn ruby_blank() {
10818 check_metrics::<RubyParser>("def foo\n a = 1\n\n a + 1\nend\n", "foo.rb", |metric| {
10822 assert_eq!(metric.loc.blank(), 1);
10823 });
10824 }
10825
10826 #[cfg(feature = "ruby")]
10827 #[test]
10828 fn ruby_no_zero_blank() {
10829 check_metrics::<RubyParser>(
10835 "def foo # entry\n pool = 0\n\n server = -42 # negative\n\n ok = false\nend\n",
10836 "foo.rb",
10837 |metric| {
10838 assert_eq!(metric.loc.blank(), 2);
10839 },
10840 );
10841 }
10842
10843 #[cfg(feature = "ruby")]
10844 #[test]
10845 fn ruby_cloc() {
10846 check_metrics::<RubyParser>(
10848 "# one\n# two\n# three\ndef foo\nend\n",
10849 "foo.rb",
10850 |metric| {
10851 assert_eq!(metric.loc.cloc(), 3);
10852 },
10853 );
10854 }
10855
10856 #[cfg(feature = "ruby")]
10857 #[test]
10858 fn ruby_lloc() {
10859 check_metrics::<RubyParser>(
10863 "def foo(a)\n if a\n a += 1\n end\n while a > 0\n a -= 1\n end\nend\n",
10864 "foo.rb",
10865 |metric| {
10866 assert_eq!(metric.loc.lloc(), 3);
10867 },
10868 );
10869 }
10870
10871 #[cfg(feature = "ruby")]
10872 #[test]
10873 fn ruby_no_call_lloc() {
10874 check_metrics::<RubyParser>(
10879 "def foo\n puts 'hello'\n puts 'world'\nend\n",
10880 "foo.rb",
10881 |metric| {
10882 assert_eq!(metric.loc.lloc(), 1);
10883 },
10884 );
10885 }
10886
10887 #[cfg(feature = "ruby")]
10888 #[test]
10889 fn ruby_no_assignment_lloc() {
10890 check_metrics::<RubyParser>(
10893 "def foo\n a = 1\n b = 2\n c = a + b\nend\n",
10894 "foo.rb",
10895 |metric| {
10896 assert_eq!(metric.loc.lloc(), 1);
10897 },
10898 );
10899 }
10900
10901 #[cfg(feature = "ruby")]
10902 #[test]
10903 fn ruby_modifier_lloc() {
10904 check_metrics::<RubyParser>(
10910 "def foo(a)\n return a if a.nil?\n a -= 1 while a > 0\n parse(a) rescue nil\nend\n",
10911 "foo.rb",
10912 |metric| {
10913 assert_eq!(metric.loc.lloc(), 5);
10914 },
10915 );
10916 }
10917
10918 #[cfg(feature = "ruby")]
10919 #[test]
10920 fn ruby_class_lloc() {
10921 check_metrics::<RubyParser>(
10923 "module M\n class C\n def foo\n end\n def bar\n end\n end\nend\n",
10924 "foo.rb",
10925 |metric| {
10926 assert_eq!(metric.loc.lloc(), 4);
10927 },
10928 );
10929 }
10930
10931 #[cfg(feature = "ruby")]
10932 #[test]
10933 fn ruby_begin_rescue_lloc() {
10934 check_metrics::<RubyParser>(
10938 "def foo\n begin\n risky\n rescue StandardError\n nil\n end\nend\n",
10939 "foo.rb",
10940 |metric| {
10941 assert_eq!(metric.loc.lloc(), 2);
10942 },
10943 );
10944 }
10945
10946 #[cfg(feature = "ruby")]
10947 #[test]
10948 fn ruby_nested_defs_lloc() {
10949 check_metrics::<RubyParser>(
10952 "def outer\n def inner\n 1\n end\nend\n",
10953 "foo.rb",
10954 |metric| {
10955 assert_eq!(metric.loc.lloc(), 2);
10956 },
10957 );
10958 }
10959
10960 #[cfg(feature = "ruby")]
10961 #[test]
10962 fn ruby_no_block_body_lloc() {
10963 check_metrics::<RubyParser>(
10969 "[1, 2, 3].each do |x|\n puts x\nend\n",
10970 "foo.rb",
10971 |metric| {
10972 assert_eq!(metric.loc.lloc(), 0);
10973 },
10974 );
10975 }
10976
10977 #[cfg(feature = "ruby")]
10978 #[test]
10979 fn ruby_no_lambda_body_lloc() {
10980 check_metrics::<RubyParser>("add = ->(a, b) {\n a + b\n}\n", "foo.rb", |metric| {
10985 assert_eq!(metric.loc.lloc(), 0);
10986 });
10987 }
10988
10989 #[cfg(feature = "ruby")]
10990 #[test]
10991 fn ruby_heredoc_lloc_and_blank() {
10992 check_metrics::<RubyParser>(
10999 "def foo\n msg = <<~TXT\n one\n two\n TXT\n msg\nend\n",
11000 "foo.rb",
11001 |metric| {
11002 assert_eq!(metric.loc.sloc(), 7);
11003 assert_eq!(metric.loc.ploc(), 7);
11004 assert_eq!(metric.loc.lloc(), 1);
11005 assert_eq!(metric.loc.blank(), 0);
11006 },
11007 );
11008 }
11009
11010 #[cfg(feature = "ruby")]
11011 #[test]
11012 fn ruby_semicolon_multistatement_lloc_undercount() {
11013 check_metrics::<RubyParser>(
11020 "def foo\n a = 1; b = 2; a + b\nend\n",
11021 "foo.rb",
11022 |metric| {
11023 assert_eq!(metric.loc.lloc(), 1);
11024 },
11025 );
11026 }
11027
11028 #[cfg(feature = "ruby")]
11029 #[test]
11030 fn ruby_ploc_skips_comments_and_blanks() {
11031 check_metrics::<RubyParser>("# header\n\ndef foo\n a = 1\nend\n", "foo.rb", |metric| {
11034 assert_eq!(metric.loc.ploc(), 3);
11035 assert_eq!(metric.loc.cloc(), 1);
11036 assert_eq!(metric.loc.blank(), 1);
11037 });
11038 }
11039
11040 #[cfg(feature = "python")]
11049 #[test]
11050 fn python_nested_def_lloc() {
11051 check_metrics::<PythonParser>(
11055 "def outer():\n def inner():\n return 1\n return inner()\n",
11056 "foo.py",
11057 |metric| {
11058 assert_eq!(metric.loc.sloc(), 4);
11059 assert_eq!(metric.loc.ploc(), 4);
11060 assert_eq!(metric.loc.lloc(), 2);
11061 assert_eq!(metric.loc.cloc(), 0);
11062 assert_eq!(metric.loc.blank(), 0);
11063 insta::assert_json_snapshot!(metric.loc);
11064 },
11065 );
11066 }
11067
11068 #[cfg(feature = "python")]
11069 #[test]
11070 fn python_lambda_in_def_lloc() {
11071 check_metrics::<PythonParser>(
11075 "def outer():\n f = lambda x: x + 1\n return f(2)\n",
11076 "foo.py",
11077 |metric| {
11078 assert_eq!(metric.loc.sloc(), 3);
11079 assert_eq!(metric.loc.ploc(), 3);
11080 assert_eq!(metric.loc.lloc(), 2);
11081 assert_eq!(metric.loc.cloc(), 0);
11082 assert_eq!(metric.loc.blank(), 0);
11083 insta::assert_json_snapshot!(metric.loc);
11084 },
11085 );
11086 }
11087
11088 #[cfg(feature = "python")]
11089 #[test]
11090 fn python_match_statement_lloc() {
11091 check_metrics::<PythonParser>(
11098 "def f(x):\n match x:\n case 1: return 1\n case _: return 0\n",
11099 "foo.py",
11100 |metric| {
11101 assert_eq!(metric.loc.lloc(), 3);
11102 insta::assert_json_snapshot!(metric.loc);
11103 },
11104 );
11105 }
11106
11107 #[cfg(feature = "python")]
11108 #[test]
11109 fn python_match_lloc_matches_if_else() {
11110 check_metrics::<PythonParser>(
11118 "def f(x):\n if x == 1: return 1\n else: return 0\n",
11119 "foo.py",
11120 |metric| assert_eq!(metric.loc.lloc(), 3),
11121 );
11122 check_metrics::<PythonParser>(
11123 "def f(x):\n match x:\n case 1: return 1\n case _: return 0\n",
11124 "foo.py",
11125 |metric| assert_eq!(metric.loc.lloc(), 3),
11126 );
11127 }
11128
11129 #[cfg(feature = "python")]
11130 #[test]
11131 fn python_type_alias_lloc() {
11132 check_metrics::<PythonParser>("type Alias = int\nx = 1\n", "foo.py", |metric| {
11137 assert_eq!(metric.loc.lloc(), 2);
11138 insta::assert_json_snapshot!(metric.loc);
11139 });
11140 }
11141
11142 #[cfg(feature = "java")]
11143 #[test]
11144 fn java_local_class_in_method_lloc() {
11145 check_metrics::<JavaParser>(
11149 "class Foo {\n int bar() {\n class Local {\n int v() { return 1; }\n }\n return new Local().v();\n }\n}\n",
11150 "foo.java",
11151 |metric| {
11152 assert_eq!(metric.loc.sloc(), 8);
11153 assert_eq!(metric.loc.ploc(), 8);
11154 assert_eq!(metric.loc.lloc(), 2);
11155 assert_eq!(metric.loc.cloc(), 0);
11156 assert_eq!(metric.loc.blank(), 0);
11157 insta::assert_json_snapshot!(metric.loc);
11158 },
11159 );
11160 }
11161
11162 #[cfg(feature = "java")]
11163 #[test]
11164 fn java_lambda_in_method_lloc() {
11165 check_metrics::<JavaParser>(
11169 "class Foo {\n void bar() {\n java.util.function.IntUnaryOperator f = x -> x + 1;\n f.applyAsInt(3);\n }\n}\n",
11170 "foo.java",
11171 |metric| {
11172 assert_eq!(metric.loc.sloc(), 6);
11173 assert_eq!(metric.loc.ploc(), 6);
11174 assert_eq!(metric.loc.lloc(), 2);
11175 assert_eq!(metric.loc.cloc(), 0);
11176 assert_eq!(metric.loc.blank(), 0);
11177 insta::assert_json_snapshot!(metric.loc);
11178 },
11179 );
11180 }
11181
11182 #[cfg(feature = "groovy")]
11183 #[test]
11184 fn groovy_blank() {
11185 check_metrics::<GroovyParser>("int x = 1\n\n\nint y = 2", "foo.groovy", |metric| {
11188 assert_eq!(metric.loc.sloc(), 4);
11189 assert_eq!(metric.loc.ploc(), 2);
11190 assert_eq!(metric.loc.lloc(), 2);
11191 assert_eq!(metric.loc.blank(), 2);
11192 });
11193 }
11194
11195 #[cfg(feature = "groovy")]
11196 #[test]
11197 fn groovy_no_zero_blank() {
11198 check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
11200 assert_eq!(metric.loc.sloc(), 1);
11201 assert_eq!(metric.loc.blank(), 0);
11202 });
11203 }
11204
11205 #[cfg(feature = "groovy")]
11206 #[test]
11207 fn groovy_cloc_line_comments() {
11208 check_metrics::<GroovyParser>(
11209 "// first comment
11210 int x = 1
11211 // second comment
11212 int y = 2",
11213 "foo.groovy",
11214 |metric| {
11215 assert_eq!(metric.loc.cloc(), 2);
11216 assert_eq!(metric.loc.ploc(), 2);
11217 },
11218 );
11219 }
11220
11221 #[cfg(feature = "groovy")]
11222 #[test]
11223 fn groovy_cloc_block_comment() {
11224 check_metrics::<GroovyParser>(
11225 "/* multi
11226 line
11227 comment */
11228 int x = 1",
11229 "foo.groovy",
11230 |metric| {
11231 assert_eq!(metric.loc.cloc(), 3);
11233 },
11234 );
11235 }
11236
11237 #[cfg(feature = "groovy")]
11238 #[test]
11239 fn groovy_cloc_groovydoc_comment() {
11240 check_metrics::<GroovyParser>(
11244 "/** groovydoc */
11245class A {
11246 int x = 1
11247}",
11248 "foo.groovy",
11249 |metric| {
11250 assert_eq!(metric.loc.cloc(), 1);
11251 assert_eq!(metric.loc.ploc(), 3);
11252 },
11253 );
11254 }
11255
11256 #[cfg(feature = "groovy")]
11257 #[test]
11258 fn groovy_simple_lloc() {
11259 check_metrics::<GroovyParser>(
11261 "int a = 1
11262 int b = 2
11263 int c = 3",
11264 "foo.groovy",
11265 |metric| {
11266 assert_eq!(metric.loc.lloc(), 3);
11267 },
11268 );
11269 }
11270
11271 #[cfg(feature = "groovy")]
11272 #[test]
11273 fn groovy_no_local_variable_declaration_in_for_lloc() {
11274 check_metrics::<GroovyParser>(
11278 "for (int i = 0; i < 10; i++) {
11279 println(i)
11280 }",
11281 "foo.groovy",
11282 |metric| {
11283 assert_eq!(metric.loc.lloc(), 2);
11285 },
11286 );
11287 }
11288
11289 #[cfg(feature = "groovy")]
11290 #[test]
11291 fn groovy_lambda_in_method_lloc() {
11292 check_metrics::<GroovyParser>(
11299 "class Foo {
11300 void bar() {
11301 def f = { x -> x + 1 }
11302 f(3)
11303 }
11304 }",
11305 "foo.groovy",
11306 |metric| {
11307 assert_eq!(metric.loc.lloc(), 3);
11308 },
11309 );
11310 }
11311
11312 #[cfg(feature = "groovy")]
11313 #[test]
11314 fn groovy_try_lloc() {
11315 check_metrics::<GroovyParser>(
11318 "void f() {
11319 try {
11320 risky()
11321 } catch (Exception e) {
11322 handle(e)
11323 }
11324 }",
11325 "foo.groovy",
11326 |metric| {
11327 assert_eq!(metric.loc.lloc(), 3);
11329 },
11330 );
11331 }
11332
11333 #[cfg(feature = "groovy")]
11334 #[test]
11335 fn groovy_class_loc() {
11336 check_metrics::<GroovyParser>(
11338 "class A {
11339 void f() {
11340 int x = 1
11341 }
11342 void g() {
11343 int y = 2
11344 }
11345 }",
11346 "foo.groovy",
11347 |metric| {
11348 assert_eq!(metric.loc.ploc(), 8);
11352 assert_eq!(metric.loc.cloc(), 0);
11353 assert_eq!(metric.loc.lloc(), 2);
11356 },
11357 );
11358 }
11359
11360 #[cfg(feature = "groovy")]
11361 #[test]
11362 fn groovy_partial_parse_recovers_unit() {
11363 check_metrics::<GroovyParser>("def x = (((", "foo.groovy", |metric| {
11368 assert_eq!(metric.loc.sloc(), 1);
11369 assert_eq!(metric.loc.blank(), 0);
11370 });
11371 }
11372
11373 #[cfg(feature = "groovy")]
11374 #[test]
11375 fn groovy_sloc() {
11376 check_metrics::<GroovyParser>(
11379 "int a = 1
11380 int b = 2
11381
11382 int c = 3",
11383 "foo.groovy",
11384 |metric| {
11385 assert_eq!(metric.loc.sloc(), 4);
11386 assert_eq!(metric.loc.ploc(), 3);
11387 assert_eq!(metric.loc.blank(), 1);
11388 },
11389 );
11390 }
11391
11392 #[cfg(feature = "groovy")]
11393 #[test]
11394 fn groovy_single_ploc() {
11395 check_metrics::<GroovyParser>("int x = 42", "foo.groovy", |metric| {
11398 assert_eq!(metric.loc.ploc(), 1);
11399 assert_eq!(metric.loc.cloc(), 0);
11400 });
11401 }
11402
11403 #[cfg(feature = "groovy")]
11404 #[test]
11405 fn groovy_multi_ploc() {
11406 check_metrics::<GroovyParser>(
11409 "int a = 1
11410 int b = 2
11411 int c = 3
11412 int d = 4",
11413 "foo.groovy",
11414 |metric| {
11415 assert_eq!(metric.loc.ploc(), 4);
11416 assert_eq!(metric.loc.lloc(), 4);
11417 },
11418 );
11419 }
11420
11421 #[cfg(feature = "groovy")]
11422 #[test]
11423 fn groovy_single_statement_lloc() {
11424 check_metrics::<GroovyParser>("println 'hi'", "foo.groovy", |metric| {
11427 assert_eq!(metric.loc.lloc(), 1);
11428 });
11429 }
11430
11431 #[cfg(feature = "groovy")]
11432 #[test]
11433 fn groovy_for_lloc() {
11434 check_metrics::<GroovyParser>(
11439 "for (int i = 0; i < 100; i++) {
11440 println(i)
11441 }",
11442 "foo.groovy",
11443 |metric| {
11444 assert_eq!(metric.loc.lloc(), 2);
11446 },
11447 );
11448 }
11449
11450 #[cfg(feature = "groovy")]
11451 #[test]
11452 fn groovy_foreach_lloc() {
11453 check_metrics::<GroovyParser>(
11456 "for (item in items) {
11457 println(item)
11458 }",
11459 "foo.groovy",
11460 |metric| {
11461 assert_eq!(metric.loc.lloc(), 2);
11463 },
11464 );
11465 }
11466
11467 #[cfg(feature = "groovy")]
11468 #[test]
11469 fn groovy_while_lloc() {
11470 check_metrics::<GroovyParser>(
11473 "int i = 0
11474 while (i < 10) {
11475 i++
11476 println(i)
11477 }",
11478 "foo.groovy",
11479 |metric| {
11480 assert_eq!(metric.loc.lloc(), 4);
11482 },
11483 );
11484 }
11485
11486 #[cfg(feature = "groovy")]
11487 #[test]
11488 fn groovy_do_while_lloc() {
11489 check_metrics::<GroovyParser>(
11492 "int i = 0
11493 do {
11494 i++
11495 } while (i < 5)",
11496 "foo.groovy",
11497 |metric| {
11498 assert_eq!(metric.loc.lloc(), 3);
11500 },
11501 );
11502 }
11503
11504 #[cfg(feature = "groovy")]
11505 #[test]
11506 fn groovy_continue_lloc() {
11507 check_metrics::<GroovyParser>(
11509 "for (int i = 0; i < 10; i++) {
11510 if (i == 5) {
11511 continue
11512 }
11513 println(i)
11514 }",
11515 "foo.groovy",
11516 |metric| {
11517 assert_eq!(metric.loc.lloc(), 4);
11519 },
11520 );
11521 }
11522
11523 #[cfg(feature = "groovy")]
11524 #[test]
11525 fn groovy_expressions_lloc() {
11526 check_metrics::<GroovyParser>(
11529 "int a = 1
11530 a = 2
11531 a += 3
11532 println(a)
11533 doSomething()",
11534 "foo.groovy",
11535 |metric| {
11536 assert_eq!(metric.loc.lloc(), 5);
11538 },
11539 );
11540 }
11541
11542 #[cfg(feature = "groovy")]
11543 #[test]
11544 fn groovy_throw_lloc() {
11545 check_metrics::<GroovyParser>(
11547 "throw new RuntimeException('bad')",
11548 "foo.groovy",
11549 |metric| {
11550 assert_eq!(metric.loc.lloc(), 1);
11551 },
11552 );
11553 }
11554
11555 #[cfg(feature = "groovy")]
11556 #[test]
11557 fn groovy_general_loc() {
11558 check_metrics::<GroovyParser>(
11570 "class A {
11571 void f(int x) {
11572 if (x > 0) {
11573 println(x)
11574 } else {
11575 println 'neg'
11576 }
11577 return
11578 }
11579 }",
11580 "foo.groovy",
11581 |metric| {
11582 assert_eq!(metric.loc.lloc(), 4);
11583 assert_eq!(metric.loc.cloc(), 0);
11584 },
11585 );
11586 }
11587
11588 #[cfg(feature = "csharp")]
11589 #[test]
11590 fn csharp_local_function_in_method_lloc() {
11591 check_metrics::<CsharpParser>(
11595 "class Foo {\n int Bar() {\n int Inner(int x) { return x + 1; }\n return Inner(2);\n }\n}\n",
11596 "foo.cs",
11597 |metric| {
11598 assert_eq!(metric.loc.sloc(), 6);
11599 assert_eq!(metric.loc.ploc(), 6);
11600 assert_eq!(metric.loc.lloc(), 2);
11601 assert_eq!(metric.loc.cloc(), 0);
11602 assert_eq!(metric.loc.blank(), 0);
11603 insta::assert_json_snapshot!(metric.loc);
11604 },
11605 );
11606 }
11607
11608 #[cfg(feature = "csharp")]
11609 #[test]
11610 fn csharp_lambda_in_method_lloc() {
11611 check_metrics::<CsharpParser>(
11614 "class Foo {\n void Bar() {\n System.Func<int, int> f = x => x + 1;\n f(3);\n }\n}\n",
11615 "foo.cs",
11616 |metric| {
11617 assert_eq!(metric.loc.sloc(), 6);
11618 assert_eq!(metric.loc.ploc(), 6);
11619 assert_eq!(metric.loc.lloc(), 2);
11620 assert_eq!(metric.loc.cloc(), 0);
11621 assert_eq!(metric.loc.blank(), 0);
11622 insta::assert_json_snapshot!(metric.loc);
11623 },
11624 );
11625 }
11626
11627 #[cfg(feature = "cpp")]
11628 #[test]
11629 fn cpp_lambda_in_function_lloc() {
11630 check_metrics::<CppParser>(
11638 "int bar() {\n auto f = [](int x) { return x + 1; };\n return f(2);\n}\n",
11639 "foo.cpp",
11640 |metric| {
11641 assert_eq!(metric.loc.sloc(), 4);
11642 assert_eq!(metric.loc.ploc(), 4);
11643 assert_eq!(metric.loc.lloc(), 3);
11644 assert_eq!(metric.loc.cloc(), 0);
11645 assert_eq!(metric.loc.blank(), 0);
11646 insta::assert_json_snapshot!(metric.loc);
11647 },
11648 );
11649 }
11650
11651 #[cfg(feature = "javascript")]
11652 #[test]
11653 fn javascript_nested_function_lloc() {
11654 check_metrics::<JavascriptParser>(
11658 "function outer() {\n function inner() {\n return 1;\n }\n return inner();\n}\n",
11659 "foo.js",
11660 |metric| {
11661 assert_eq!(metric.loc.sloc(), 6);
11662 assert_eq!(metric.loc.ploc(), 6);
11663 assert_eq!(metric.loc.lloc(), 2);
11664 assert_eq!(metric.loc.cloc(), 0);
11665 assert_eq!(metric.loc.blank(), 0);
11666 insta::assert_json_snapshot!(metric.loc);
11667 },
11668 );
11669 }
11670
11671 #[cfg(feature = "javascript")]
11672 #[test]
11673 fn javascript_arrow_function_lloc() {
11674 check_metrics::<JavascriptParser>(
11677 "function outer() {\n const inner = (x) => x + 1;\n return inner(2);\n}\n",
11678 "foo.js",
11679 |metric| {
11680 assert_eq!(metric.loc.sloc(), 4);
11681 assert_eq!(metric.loc.ploc(), 4);
11682 assert_eq!(metric.loc.lloc(), 2);
11683 assert_eq!(metric.loc.cloc(), 0);
11684 assert_eq!(metric.loc.blank(), 0);
11685 insta::assert_json_snapshot!(metric.loc);
11686 },
11687 );
11688 }
11689
11690 #[cfg(feature = "kotlin")]
11691 #[test]
11692 fn kotlin_lambda_literal_in_fun_lloc() {
11693 check_metrics::<KotlinParser>(
11696 "fun outer() {\n val f: (Int) -> Int = { x -> x + 1 }\n f(3)\n}\n",
11697 "foo.kt",
11698 |metric| {
11699 assert_eq!(metric.loc.sloc(), 4);
11700 assert_eq!(metric.loc.ploc(), 4);
11701 assert_eq!(metric.loc.lloc(), 2);
11702 assert_eq!(metric.loc.cloc(), 0);
11703 assert_eq!(metric.loc.blank(), 0);
11704 insta::assert_json_snapshot!(metric.loc);
11705 },
11706 );
11707 }
11708
11709 #[cfg(feature = "kotlin")]
11710 #[test]
11711 fn kotlin_local_fun_in_fun_lloc() {
11712 check_metrics::<KotlinParser>(
11716 "fun outer() {\n fun inner(x: Int): Int { return x + 1 }\n inner(3)\n}\n",
11717 "foo.kt",
11718 |metric| {
11719 assert_eq!(metric.loc.sloc(), 4);
11720 assert_eq!(metric.loc.ploc(), 4);
11721 assert_eq!(metric.loc.lloc(), 2);
11722 assert_eq!(metric.loc.cloc(), 0);
11723 assert_eq!(metric.loc.blank(), 0);
11724 insta::assert_json_snapshot!(metric.loc);
11725 },
11726 );
11727 }
11728
11729 #[cfg(feature = "kotlin")]
11730 #[test]
11731 fn kotlin_object_expression_in_fun_lloc() {
11732 check_metrics::<KotlinParser>(
11736 "fun outer() {\n val r = object : Runnable { override fun run() { println(\"hi\") } }\n r.run()\n}\n",
11737 "foo.kt",
11738 |metric| {
11739 assert_eq!(metric.loc.sloc(), 4);
11740 assert_eq!(metric.loc.ploc(), 4);
11741 assert_eq!(metric.loc.lloc(), 3);
11742 assert_eq!(metric.loc.cloc(), 0);
11743 assert_eq!(metric.loc.blank(), 0);
11744 insta::assert_json_snapshot!(metric.loc);
11745 },
11746 );
11747 }
11748
11749 #[cfg(feature = "go")]
11750 #[test]
11751 fn go_function_literal_initializer_lloc() {
11752 check_metrics::<GoParser>(
11757 "package main\nfunc outer() int {\n inner := func(x int) int { return x + 1 }\n return inner(2)\n}\n",
11758 "foo.go",
11759 |metric| {
11760 assert_eq!(metric.loc.sloc(), 5);
11761 assert_eq!(metric.loc.ploc(), 5);
11762 assert_eq!(metric.loc.lloc(), 3);
11763 assert_eq!(metric.loc.cloc(), 0);
11764 assert_eq!(metric.loc.blank(), 0);
11765 insta::assert_json_snapshot!(metric.loc);
11766 },
11767 );
11768 }
11769
11770 #[cfg(feature = "php")]
11771 #[test]
11772 fn php_anonymous_function_in_function_lloc() {
11773 check_metrics::<PhpParser>(
11777 "<?php\nfunction outer() {\n $f = function ($x) { return $x + 1; };\n return $f(2);\n}\n",
11778 "foo.php",
11779 |metric| {
11780 assert_eq!(metric.loc.sloc(), 5);
11781 assert_eq!(metric.loc.ploc(), 5);
11782 assert_eq!(metric.loc.lloc(), 3);
11783 assert_eq!(metric.loc.cloc(), 0);
11784 assert_eq!(metric.loc.blank(), 0);
11785 insta::assert_json_snapshot!(metric.loc);
11786 },
11787 );
11788 }
11789
11790 #[cfg(feature = "php")]
11791 #[test]
11792 fn php_arrow_function_in_function_lloc() {
11793 check_metrics::<PhpParser>(
11796 "<?php\nfunction outer() {\n $f = fn ($x) => $x + 1;\n return $f(2);\n}\n",
11797 "foo.php",
11798 |metric| {
11799 assert_eq!(metric.loc.sloc(), 5);
11800 assert_eq!(metric.loc.ploc(), 5);
11801 assert_eq!(metric.loc.lloc(), 2);
11802 assert_eq!(metric.loc.cloc(), 0);
11803 assert_eq!(metric.loc.blank(), 0);
11804 insta::assert_json_snapshot!(metric.loc);
11805 },
11806 );
11807 }
11808
11809 #[cfg(feature = "lua")]
11810 #[test]
11811 fn lua_nested_local_function_lloc() {
11812 check_metrics::<LuaParser>(
11816 "local function outer()\n local function inner()\n return 1\n end\n return inner()\nend\n",
11817 "foo.lua",
11818 |metric| {
11819 assert_eq!(metric.loc.sloc(), 6);
11820 assert_eq!(metric.loc.ploc(), 6);
11821 assert_eq!(metric.loc.lloc(), 4);
11822 assert_eq!(metric.loc.cloc(), 0);
11823 assert_eq!(metric.loc.blank(), 0);
11824 insta::assert_json_snapshot!(metric.loc);
11825 },
11826 );
11827 }
11828
11829 #[cfg(feature = "lua")]
11830 #[test]
11831 fn lua_function_expression_in_local_decl_lloc() {
11832 check_metrics::<LuaParser>(
11837 "local function outer()\n local f = function (x) return x + 1 end\n return f(2)\nend\n",
11838 "foo.lua",
11839 |metric| {
11840 assert_eq!(metric.loc.sloc(), 4);
11841 assert_eq!(metric.loc.ploc(), 4);
11842 assert_eq!(metric.loc.lloc(), 4);
11843 assert_eq!(metric.loc.cloc(), 0);
11844 assert_eq!(metric.loc.blank(), 0);
11845 insta::assert_json_snapshot!(metric.loc);
11846 },
11847 );
11848 }
11849
11850 #[cfg(feature = "tcl")]
11851 #[test]
11852 fn tcl_apply_closure_lloc() {
11853 check_metrics::<TclParser>(
11860 "proc outer {} {\n set f [list x {return [expr {$x + 1}]}]\n apply $f 2\n}\n",
11861 "foo.tcl",
11862 |metric| {
11863 assert_eq!(metric.loc.sloc(), 4);
11864 assert_eq!(metric.loc.ploc(), 4);
11865 assert_eq!(metric.loc.lloc(), 4);
11866 assert_eq!(metric.loc.cloc(), 0);
11867 assert_eq!(metric.loc.blank(), 0);
11868 insta::assert_json_snapshot!(metric.loc);
11869 },
11870 );
11871 }
11872
11873 #[cfg(feature = "perl")]
11874 #[test]
11875 fn perl_anonymous_sub_in_sub_lloc() {
11876 check_metrics::<PerlParser>(
11886 "sub outer {\n my $f = sub { return $_[0] + 1 };\n return $f->(2);\n}\n",
11887 "foo.pl",
11888 |metric| {
11889 assert_eq!(metric.loc.sloc(), 4);
11890 assert_eq!(metric.loc.ploc(), 4);
11891 assert_eq!(metric.loc.lloc(), 2);
11892 assert_eq!(metric.loc.cloc(), 0);
11893 assert_eq!(metric.loc.blank(), 0);
11894 insta::assert_json_snapshot!(metric.loc);
11895 },
11896 );
11897 }
11898
11899 #[cfg(feature = "perl")]
11900 #[test]
11901 fn perl_named_sub_in_sub_lloc() {
11902 check_metrics::<PerlParser>(
11917 "sub outer {\n sub inner { return 1 }\n return inner();\n}\n",
11918 "foo.pl",
11919 |metric| {
11920 assert_eq!(metric.loc.sloc(), 4);
11921 assert_eq!(metric.loc.ploc(), 4);
11922 assert_eq!(metric.loc.lloc(), 1);
11923 assert_eq!(metric.loc.cloc(), 0);
11924 assert_eq!(metric.loc.blank(), 0);
11925 insta::assert_json_snapshot!(metric.loc);
11926 },
11927 );
11928 }
11929
11930 #[cfg(feature = "elixir")]
11931 #[test]
11932 fn elixir_fn_inside_def_lloc() {
11933 check_metrics::<ElixirParser>(
11937 "defmodule Foo do\n def outer do\n f = fn x -> x + 1 end\n f.(2)\n end\nend\n",
11938 "foo.ex",
11939 |metric| {
11940 assert_eq!(metric.loc.sloc(), 6);
11941 assert_eq!(metric.loc.ploc(), 6);
11942 assert_eq!(metric.loc.lloc(), 5);
11943 assert_eq!(metric.loc.cloc(), 0);
11944 assert_eq!(metric.loc.blank(), 0);
11945 insta::assert_json_snapshot!(metric.loc);
11946 },
11947 );
11948 }
11949
11950 #[cfg(feature = "rust")]
11962 #[test]
11963 fn rust_nested_min_max_propagates() {
11964 check_metrics::<RustParser>(
11965 "struct C;\nimpl C {\n fn small(&self) {\n let _ = 1;\n }\n fn big(&self) {\n let _ = 1;\n let _ = 2;\n let _ = 3;\n }\n}\n",
11966 "c.rs",
11967 |metric| {
11968 let loc = &metric.loc;
11974 assert!(
11975 loc.sloc_min() <= loc.sloc(),
11976 "sloc_min {} must not exceed unit sloc {}",
11977 loc.sloc_min(),
11978 loc.sloc()
11979 );
11980 assert_eq!(loc.sloc_min(), 3, "smallest leaf method span");
11981 assert_eq!(loc.sloc_max(), loc.sloc(), "largest space is the unit");
11982 assert_eq!(loc.lloc_min(), 1);
11984 },
11985 );
11986 }
11987
11988 #[cfg(feature = "java")]
11993 #[test]
11994 fn java_nested_min_max_propagates() {
11995 check_metrics::<JavaParser>(
11996 "public class C {\n void small() {\n int x = 1;\n }\n void big() {\n int a = 1;\n int b = 2;\n int c = 3;\n }\n}\n",
11997 "C.java",
11998 |metric| {
11999 let loc = &metric.loc;
12001 assert!(loc.sloc_min() <= loc.sloc());
12002 assert_eq!(loc.sloc_min(), 3, "smallest leaf method span");
12003 assert_eq!(loc.sloc_max(), loc.sloc());
12004 assert_eq!(loc.lloc_min(), 1);
12005 },
12006 );
12007 }
12008
12009 #[cfg(feature = "python")]
12013 #[test]
12014 fn python_nested_min_max_propagates() {
12015 check_metrics::<PythonParser>(
12016 "class C:\n def small(self):\n x = 1\n def big(self):\n a = 1\n b = 2\n c = 3\n",
12017 "c.py",
12018 |metric| {
12019 let loc = &metric.loc;
12021 assert!(loc.sloc_min() <= loc.sloc());
12022 assert_eq!(loc.sloc_min(), 2, "smallest leaf method span");
12023 assert_eq!(loc.sloc_max(), loc.sloc());
12024 assert_eq!(loc.lloc_min(), 1);
12025 },
12026 );
12027 }
12028
12029 #[test]
12042 fn blank_clamps_negative_to_zero() {
12043 let mut stats = Stats::default();
12044 stats.sloc.start = 0;
12046 stats.sloc.end_line = 1;
12048 stats.ploc.lines.insert(0);
12050 stats.cloc.only_comment_line_starts.insert(0);
12052
12053 assert_eq!(stats.sloc(), 1);
12055 assert_eq!(stats.ploc(), 1);
12056 assert_eq!(
12057 stats.blank(),
12058 0,
12059 "blank() must clamp the negative subtraction to 0"
12060 );
12061 }
12062
12063 #[cfg(all(
12078 feature = "cpp",
12079 feature = "javascript",
12080 feature = "python",
12081 feature = "rust"
12082 ))]
12083 #[test]
12084 fn sibling_spaces_sharing_a_line_count_it_once() {
12085 check_metrics::<RustParser>(
12086 "fn a() { let x = 1; } fn b() { let y = 2; }",
12087 "foo.rs",
12088 |metric| {
12089 assert_eq!(metric.loc.sloc(), 1);
12090 assert_eq!(metric.loc.ploc(), 1, "one physical row, two spaces");
12091 assert_eq!(metric.loc.blank(), 0);
12092 },
12093 );
12094
12095 check_metrics::<CppParser>(
12096 "int a() { return 1; } int b() { return 2; }",
12097 "foo.cpp",
12098 |metric| {
12099 assert_eq!(metric.loc.sloc(), 1);
12100 assert_eq!(metric.loc.ploc(), 1, "one physical row, two spaces");
12101 assert_eq!(metric.loc.blank(), 0);
12102 },
12103 );
12104
12105 check_metrics::<JavascriptParser>(
12106 "function a() { let x = 1; } function b() { let y = 2; }",
12107 "foo.js",
12108 |metric| {
12109 assert_eq!(metric.loc.sloc(), 1);
12110 assert_eq!(metric.loc.ploc(), 1, "one physical row, two spaces");
12111 assert_eq!(metric.loc.blank(), 0);
12112 },
12113 );
12114
12115 check_metrics::<PythonParser>(
12119 "def a():
12120 def b(): return 1",
12121 "foo.py",
12122 |metric| {
12123 assert_eq!(metric.loc.sloc(), 2);
12124 assert_eq!(metric.loc.ploc(), 2, "two physical rows, three spaces");
12125 assert_eq!(metric.loc.blank(), 0);
12126 },
12127 );
12128 }
12129
12130 #[cfg(all(feature = "cpp", feature = "rust"))]
12138 #[test]
12139 fn sibling_spaces_sharing_a_comment_line_count_it_once() {
12140 check_metrics::<CppParser>(
12141 "int a() { /*x*/ return 1; } int b() { /*y*/ return 2; }",
12142 "foo.cpp",
12143 |metric| {
12144 assert_eq!(metric.loc.sloc(), 1);
12145 assert_eq!(metric.loc.cloc(), 1, "one comment row, two spaces");
12146 assert!(
12147 metric.loc.cloc() <= metric.loc.sloc(),
12148 "cloc must never exceed sloc"
12149 );
12150 },
12151 );
12152
12153 check_metrics::<RustParser>(
12154 "fn a() { /*x*/ let p = 1; } fn b() { /*y*/ let q = 2; }",
12155 "foo.rs",
12156 |metric| {
12157 assert_eq!(metric.loc.sloc(), 1);
12158 assert_eq!(metric.loc.cloc(), 1, "one comment row, two spaces");
12159 assert!(
12160 metric.loc.cloc() <= metric.loc.sloc(),
12161 "cloc must never exceed sloc"
12162 );
12163 },
12164 );
12165 }
12166
12167 #[cfg(feature = "rust")]
12175 #[test]
12176 fn a_row_folded_through_nested_spaces_counts_once() {
12177 const DEPTH: usize = 15;
12178 let source = format!(
12179 "{}let x = 1;{}",
12180 "fn f() { ".repeat(DEPTH),
12181 "} ".repeat(DEPTH)
12182 );
12183
12184 check_metrics::<RustParser>(&source, "foo.rs", |metric| {
12185 assert_eq!(metric.loc.sloc(), 1);
12186 assert_eq!(metric.loc.ploc(), 1, "one physical row, {DEPTH} spaces");
12187 assert_eq!(metric.loc.ploc_max(), 1);
12188 assert_eq!(metric.loc.blank(), 0);
12189 });
12190 }
12191
12192 #[cfg(feature = "cpp")]
12200 #[test]
12201 fn cloc_multiple_block_comments_one_line_cpp() {
12202 check_metrics::<CppParser>(
12203 "int f(int /*a*/, int /*b*/) { return 1; }",
12204 "foo.cpp",
12205 |metric| {
12206 assert_eq!(metric.loc.cloc(), 1, "two inline comments => 1 cloc");
12207 assert!(
12208 metric.loc.cloc() <= metric.loc.sloc(),
12209 "cloc must not exceed sloc"
12210 );
12211 assert!(
12212 metric.loc.cloc() <= metric.loc.ploc(),
12213 "cloc must not exceed ploc for a single-line construct"
12214 );
12215 },
12216 );
12217 }
12218
12219 #[cfg(feature = "rust")]
12223 #[test]
12224 fn cloc_multiple_block_comments_one_line_rust() {
12225 check_metrics::<RustParser>(
12226 "fn f(/*a*/ x: i32, /*b*/ y: i32) -> i32 { 1 }",
12227 "foo.rs",
12228 |metric| {
12229 assert_eq!(metric.loc.cloc(), 1, "two inline comments => 1 cloc");
12230 assert!(
12231 metric.loc.cloc() <= metric.loc.sloc(),
12232 "cloc must not exceed sloc"
12233 );
12234 },
12235 );
12236 }
12237
12238 #[cfg(feature = "cpp")]
12243 #[test]
12244 fn cloc_multiline_block_comment_counts_each_line() {
12245 check_metrics::<CppParser>(
12246 "int g() {\n /* l1\n l2\n l3 */\n return 0;\n}",
12247 "foo.cpp",
12248 |metric| {
12249 assert_eq!(
12250 metric.loc.cloc(),
12251 3,
12252 "a 3-line block comment counts 3 comment lines"
12253 );
12254 },
12255 );
12256 }
12257
12258 #[cfg(feature = "cpp")]
12267 #[test]
12268 fn cloc_multiple_standalone_block_comments_one_line_cpp() {
12269 check_metrics::<CppParser>("/*a*/ /*b*/", "foo.cpp", |metric| {
12270 assert_eq!(
12271 metric.loc.cloc(),
12272 1,
12273 "two standalone comments on one line => 1 cloc"
12274 );
12275 assert_eq!(metric.loc.sloc(), 1);
12276 assert!(
12277 metric.loc.cloc() <= metric.loc.sloc(),
12278 "cloc must not exceed sloc"
12279 );
12280 });
12281 }
12282
12283 #[cfg(feature = "rust")]
12287 #[test]
12288 fn cloc_multiple_standalone_block_comments_one_line_rust() {
12289 check_metrics::<RustParser>("/*a*/ /*b*/", "foo.rs", |metric| {
12290 assert_eq!(
12291 metric.loc.cloc(),
12292 1,
12293 "two standalone comments on one line => 1 cloc"
12294 );
12295 assert_eq!(metric.loc.sloc(), 1);
12296 assert!(
12297 metric.loc.cloc() <= metric.loc.sloc(),
12298 "cloc must not exceed sloc"
12299 );
12300 });
12301 }
12302
12303 #[cfg(feature = "cpp")]
12310 #[test]
12311 fn cloc_standalone_comments_then_code_no_double_count() {
12312 check_metrics::<CppParser>("/*a*/ /*b*/ /*c*/\nint x = 1;\n", "foo.cpp", |metric| {
12313 assert_eq!(metric.loc.sloc(), 2);
12314 assert_eq!(
12315 metric.loc.cloc(),
12316 1,
12317 "only line 1 carries comments => 1 cloc"
12318 );
12319 assert!(
12320 metric.loc.cloc() <= metric.loc.sloc(),
12321 "cloc must not exceed sloc"
12322 );
12323 });
12324 }
12325
12326 #[cfg(feature = "irules")]
12327 #[test]
12328 fn irules_multiline_quoted_word_credits_every_row_to_ploc() {
12329 check_metrics::<IrulesParser>(
12339 "when HTTP_REQUEST {\n set msg \"line one\nline two\nline three\"\n log local0. $msg\n}",
12340 "foo.irule",
12341 |metric| {
12342 assert_eq!(metric.loc.sloc(), 6);
12343 assert_eq!(metric.loc.ploc(), 6);
12344 assert_eq!(metric.loc.lloc(), 3);
12345 assert_eq!(metric.loc.cloc(), 0);
12346 assert_eq!(metric.loc.blank(), 0);
12347 },
12348 );
12349
12350 check_metrics::<IrulesParser>("set msg \"one\n\nthree\"", "foo.irule", |metric| {
12353 assert_eq!(metric.loc.sloc(), 3);
12354 assert_eq!(metric.loc.ploc(), 3);
12355 assert_eq!(metric.loc.cloc(), 0);
12356 assert_eq!(metric.loc.blank(), 0);
12357 });
12358 }
12359
12360 #[cfg(feature = "irules")]
12368 #[test]
12369 fn irules_blank() {
12370 check_metrics::<IrulesParser>(
12371 "when X {\n\n set x 1\n\n set y 2\n}\n",
12372 "foo.irule",
12373 |metric| {
12374 assert_eq!(metric.loc.sloc(), 6);
12375 assert_eq!(metric.loc.ploc(), 4);
12376 assert_eq!(metric.loc.lloc(), 3);
12377 assert_eq!(metric.loc.cloc(), 0);
12378 assert_eq!(metric.loc.blank(), 2);
12379 },
12380 );
12381 }
12382
12383 #[cfg(feature = "irules")]
12386 #[test]
12387 fn irules_no_zero_blank() {
12388 check_metrics::<IrulesParser>(
12389 "when HTTP_REQUEST {\n set x 1\n log local0. $x\n}\n",
12390 "foo.irule",
12391 |metric| {
12392 assert_eq!(metric.loc.sloc(), 4);
12393 assert_eq!(metric.loc.ploc(), 4);
12394 assert_eq!(metric.loc.lloc(), 3);
12395 assert_eq!(metric.loc.blank(), 0);
12396 },
12397 );
12398 }
12399
12400 #[cfg(feature = "irules")]
12409 #[test]
12410 fn irules_cloc() {
12411 check_metrics::<IrulesParser>(
12412 "# a\n# b\n# c\nwhen X { set x 1 }\n",
12413 "foo.irule",
12414 |metric| {
12415 assert_eq!(metric.loc.sloc(), 4);
12416 assert_eq!(metric.loc.ploc(), 1);
12417 assert_eq!(metric.loc.cloc(), 3);
12418 assert_eq!(metric.loc.blank(), 0);
12419 },
12420 );
12421 }
12422
12423 #[cfg(feature = "irules")]
12427 #[test]
12428 fn irules_lloc() {
12429 check_metrics::<IrulesParser>(
12430 "when X {\n if { $a } {\n set x 1\n }\n log local0. done\n}\n",
12431 "foo.irule",
12432 |metric| {
12433 assert_eq!(metric.loc.lloc(), 4);
12434 },
12435 );
12436 }
12437
12438 #[cfg(feature = "irules")]
12444 #[test]
12445 fn irules_no_command_substitution_lloc() {
12446 check_metrics::<IrulesParser>(
12447 "when X {\n set y [expr { 1 + 2 }]\n}\n",
12448 "foo.irule",
12449 |metric| {
12450 assert_eq!(metric.loc.lloc(), 2);
12451 },
12452 );
12453 }
12454
12455 #[cfg(feature = "irules")]
12459 #[test]
12460 fn irules_switch_lloc() {
12461 check_metrics::<IrulesParser>(
12462 "when X {\n switch $h {\n a { set r 1 }\n b { set r 2 }\n }\n}\n",
12463 "foo.irule",
12464 |metric| {
12465 assert_eq!(metric.loc.lloc(), 4);
12466 },
12467 );
12468 }
12469
12470 #[cfg(feature = "irules")]
12473 #[test]
12474 fn irules_proc_lloc() {
12475 check_metrics::<IrulesParser>(
12476 "proc f { a } {\n return $a\n}\n",
12477 "foo.irule",
12478 |metric| {
12479 assert_eq!(metric.loc.lloc(), 2);
12480 },
12481 );
12482 }
12483
12484 #[cfg(feature = "objc")]
12487 #[test]
12488 fn objc_blank() {
12489 check_metrics::<ObjcParser>(
12490 "
12491
12492 int a = 42;
12493
12494 int b = 43;
12495
12496 ",
12497 "foo.m",
12498 |metric| {
12499 assert_eq!(metric.loc.blank(), 1);
12500 insta::assert_json_snapshot!(metric.loc, @r#"
12501 {
12502 "sloc": 3,
12503 "ploc": 2,
12504 "lloc": 2,
12505 "cloc": 0,
12506 "blank": 1,
12507 "dloc": 0,
12508 "sloc_average": 3.0,
12509 "ploc_average": 2.0,
12510 "lloc_average": 2.0,
12511 "cloc_average": 0.0,
12512 "blank_average": 1.0,
12513 "dloc_average": 0.0,
12514 "sloc_min": 3,
12515 "sloc_max": 3,
12516 "cloc_min": 0,
12517 "cloc_max": 0,
12518 "ploc_min": 2,
12519 "ploc_max": 2,
12520 "lloc_min": 2,
12521 "lloc_max": 2,
12522 "blank_min": 1,
12523 "blank_max": 1,
12524 "dloc_min": 0,
12525 "dloc_max": 0
12526 }
12527 "#);
12528 },
12529 );
12530 }
12531
12532 #[cfg(feature = "objc")]
12535 #[test]
12536 fn objc_cloc() {
12537 check_metrics::<ObjcParser>(
12538 "/* Block comment
12539 still the block */
12540 // Line comment
12541 int a = 42; // trailing",
12542 "foo.m",
12543 |metric| {
12544 insta::assert_json_snapshot!(metric.loc, @r#"
12545 {
12546 "sloc": 4,
12547 "ploc": 1,
12548 "lloc": 1,
12549 "cloc": 4,
12550 "blank": 0,
12551 "dloc": 0,
12552 "sloc_average": 4.0,
12553 "ploc_average": 1.0,
12554 "lloc_average": 1.0,
12555 "cloc_average": 4.0,
12556 "blank_average": 0.0,
12557 "dloc_average": 0.0,
12558 "sloc_min": 4,
12559 "sloc_max": 4,
12560 "cloc_min": 4,
12561 "cloc_max": 4,
12562 "ploc_min": 1,
12563 "ploc_max": 1,
12564 "lloc_min": 1,
12565 "lloc_max": 1,
12566 "blank_min": 0,
12567 "blank_max": 0,
12568 "dloc_min": 0,
12569 "dloc_max": 0
12570 }
12571 "#);
12572 },
12573 );
12574 }
12575
12576 #[cfg(feature = "objc")]
12580 #[test]
12581 fn objc_lloc() {
12582 check_metrics::<ObjcParser>(
12583 "@implementation Foo
12584- (int)bar {
12585 int a = 1;
12586 int b = 2;
12587 return a + b;
12588}
12589@end
12590",
12591 "foo.m",
12592 |metric| {
12593 assert_eq!(metric.loc.lloc(), 3);
12596 insta::assert_json_snapshot!(metric.loc, @r#"
12597 {
12598 "sloc": 7,
12599 "ploc": 7,
12600 "lloc": 3,
12601 "cloc": 0,
12602 "blank": 0,
12603 "dloc": 0,
12604 "sloc_average": 2.3333333333333335,
12605 "ploc_average": 2.3333333333333335,
12606 "lloc_average": 1.0,
12607 "cloc_average": 0.0,
12608 "blank_average": 0.0,
12609 "dloc_average": 0.0,
12610 "sloc_min": 5,
12611 "sloc_max": 7,
12612 "cloc_min": 0,
12613 "cloc_max": 0,
12614 "ploc_min": 5,
12615 "ploc_max": 7,
12616 "lloc_min": 3,
12617 "lloc_max": 3,
12618 "blank_min": 0,
12619 "blank_max": 0,
12620 "dloc_min": 0,
12621 "dloc_max": 0
12622 }
12623 "#);
12624 },
12625 );
12626 }
12627
12628 #[cfg(feature = "objc")]
12634 #[test]
12635 fn objc_no_declaration_in_for_header_lloc() {
12636 check_metrics::<ObjcParser>(
12637 "@implementation Foo
12638- (void)bar {
12639 for (int i = 0; i < 10; ++i) {
12640 [self use:i];
12641 }
12642}
12643@end
12644",
12645 "foo.m",
12646 |metric| {
12647 assert_eq!(metric.loc.lloc(), 2);
12651 insta::assert_json_snapshot!(metric.loc, @r#"
12652 {
12653 "sloc": 7,
12654 "ploc": 7,
12655 "lloc": 2,
12656 "cloc": 0,
12657 "blank": 0,
12658 "dloc": 0,
12659 "sloc_average": 2.3333333333333335,
12660 "ploc_average": 2.3333333333333335,
12661 "lloc_average": 0.6666666666666666,
12662 "cloc_average": 0.0,
12663 "blank_average": 0.0,
12664 "dloc_average": 0.0,
12665 "sloc_min": 5,
12666 "sloc_max": 7,
12667 "cloc_min": 0,
12668 "cloc_max": 0,
12669 "ploc_min": 5,
12670 "ploc_max": 7,
12671 "lloc_min": 2,
12672 "lloc_max": 2,
12673 "blank_min": 0,
12674 "blank_max": 0,
12675 "dloc_min": 0,
12676 "dloc_max": 0
12677 }
12678 "#);
12679 },
12680 );
12681 }
12682
12683 #[cfg(feature = "objc")]
12684 #[test]
12685 fn objc_at_directives_lloc() {
12686 check_metrics::<ObjcParser>(
12692 "@implementation Foo
12693- (void)bar {
12694 @synchronized (self) {
12695 [self use];
12696 }
12697 @autoreleasepool {
12698 [self use];
12699 }
12700}
12701@end
12702",
12703 "foo.m",
12704 |metric| {
12705 assert_eq!(metric.loc.lloc(), 3);
12710 insta::assert_json_snapshot!(metric.loc, @r#"
12711 {
12712 "sloc": 10,
12713 "ploc": 10,
12714 "lloc": 3,
12715 "cloc": 0,
12716 "blank": 0,
12717 "dloc": 0,
12718 "sloc_average": 3.3333333333333335,
12719 "ploc_average": 3.3333333333333335,
12720 "lloc_average": 1.0,
12721 "cloc_average": 0.0,
12722 "blank_average": 0.0,
12723 "dloc_average": 0.0,
12724 "sloc_min": 8,
12725 "sloc_max": 10,
12726 "cloc_min": 0,
12727 "cloc_max": 0,
12728 "ploc_min": 8,
12729 "ploc_max": 10,
12730 "lloc_min": 3,
12731 "lloc_max": 3,
12732 "blank_min": 0,
12733 "blank_max": 0,
12734 "dloc_min": 0,
12735 "dloc_max": 0
12736 }
12737 "#);
12738 },
12739 );
12740 }
12741
12742 #[cfg(feature = "rust")]
12747 fn rust_loc(source: &[u8]) -> Stats {
12748 metrics_verbatim(crate::LANG::Rust, source, crate::MetricsOptions::default()).loc
12749 }
12750
12751 #[cfg(feature = "rust")]
12758 #[test]
12759 fn rust_doc_comment_at_eof_does_not_underflow() {
12760 let outer = rust_loc(b"/// x");
12763 assert_eq!(outer.cloc(), 1);
12764 assert_eq!(outer.ploc(), 0);
12765
12766 let inner = rust_loc(b"//! x");
12767 assert_eq!(inner.cloc(), 1);
12768 assert_eq!(inner.ploc(), 0);
12769
12770 let after_code = rust_loc(b"fn f(){}\n/// x");
12774 assert_eq!(after_code.cloc(), 1);
12775 assert_eq!(after_code.ploc(), 1);
12776
12777 assert_eq!(rust_loc(b"/// a\n/// b").cloc(), 2);
12780
12781 let trailing = rust_loc(b"let x = 1; /// d");
12786 assert_eq!(trailing.cloc(), 1);
12787 assert_eq!(trailing.ploc(), 1);
12788 }
12789
12790 #[cfg(feature = "rust")]
12795 #[test]
12796 fn rust_doc_comment_at_eof_matches_plain_comment() {
12797 let plain = rust_loc(b"// x");
12798 assert_eq!(plain.cloc(), 1);
12802 assert_eq!(plain.ploc(), 0);
12803
12804 for doc in [&b"/// x"[..], &b"//! x"[..]] {
12805 let doc = rust_loc(doc);
12806 assert_eq!(doc.cloc(), plain.cloc());
12807 assert_eq!(doc.ploc(), plain.ploc());
12808 assert_eq!(doc.sloc(), plain.sloc());
12809 assert_eq!(doc.blank(), plain.blank());
12810 }
12811 }
12812
12813 #[cfg(feature = "rust")]
12819 #[test]
12820 fn rust_doc_comment_with_trailing_newline_still_discounts_the_row() {
12821 assert_eq!(rust_loc(b"/// x\n").cloc(), 1);
12823 assert_eq!(rust_loc(b"/// a\n/// b\n").cloc(), 2);
12825
12826 let documented = rust_loc(b"/// doc\nfn f() {}\n");
12829 assert_eq!(documented.cloc(), 1);
12830 assert_eq!(documented.ploc(), 1);
12831 }
12832
12833 #[cfg(feature = "rust")]
12840 #[test]
12841 fn rust_doc_comment_crlf_still_discounts_the_row() {
12842 assert_eq!(rust_loc(b"/// x\r\n").cloc(), 1);
12844 let after_code = rust_loc(b"fn f(){}\r\n/// x");
12846 assert_eq!(after_code.cloc(), 1);
12847 assert_eq!(after_code.ploc(), 1);
12848 assert_eq!(rust_loc(b"/// x\r").cloc(), 1);
12850 }
12851
12852 #[cfg(any(
12875 feature = "bash",
12876 feature = "c",
12877 feature = "c-family-helpers",
12878 feature = "cpp",
12879 feature = "csharp",
12880 feature = "elixir",
12881 feature = "go",
12882 feature = "groovy",
12883 feature = "irules",
12884 feature = "java",
12885 feature = "javascript",
12886 feature = "kotlin",
12887 feature = "lua",
12888 feature = "mozcpp",
12889 feature = "mozjs",
12890 feature = "objc",
12891 feature = "perl",
12892 feature = "php",
12893 feature = "python",
12894 feature = "ruby",
12895 feature = "rust",
12896 feature = "tcl",
12897 feature = "typescript",
12898 ))]
12899 const UNTERMINATED_ONE_LINERS: &[(crate::LANG, &[u8])] = &[
12900 (crate::LANG::Rust, b"fn main() {}"),
12901 (crate::LANG::C, b"int main(void) { return 0; }"),
12902 (crate::LANG::Cpp, b"int main() { return 0; }"),
12903 (crate::LANG::Mozcpp, b"int main() { return 0; }"),
12904 (crate::LANG::Objc, b"int main(void) { return 0; }"),
12905 (crate::LANG::Csharp, b"class C { void M() {} }"),
12906 (crate::LANG::Java, b"class C { void m() {} }"),
12907 (crate::LANG::Kotlin, b"fun main() {}"),
12908 (crate::LANG::Groovy, b"def f() {}"),
12909 (crate::LANG::Go, b"package main"),
12910 (crate::LANG::Javascript, b"function f() {}"),
12911 (crate::LANG::Mozjs, b"function f() {}"),
12912 (crate::LANG::Typescript, b"function f(): void {}"),
12913 (crate::LANG::Tsx, b"function f() {}"),
12914 (crate::LANG::Python, b"def f(): pass"),
12915 (crate::LANG::Ruby, b"def f; end"),
12916 (crate::LANG::Php, b"<?php function f() {}"),
12917 (crate::LANG::Perl, b"sub f { return 1; }"),
12918 (crate::LANG::Bash, b"f() { echo hi; }"),
12919 (crate::LANG::Lua, b"function f() end"),
12920 (crate::LANG::Tcl, b"proc f {} {}"),
12921 (crate::LANG::Irules, b"proc f {} {}"),
12922 (crate::LANG::Elixir, b"defmodule M do end"),
12923 ];
12924
12925 #[cfg(all(
12936 feature = "bash",
12937 feature = "c",
12938 feature = "cpp",
12939 feature = "csharp",
12940 feature = "elixir",
12941 feature = "go",
12942 feature = "groovy",
12943 feature = "irules",
12944 feature = "java",
12945 feature = "javascript",
12946 feature = "kotlin",
12947 feature = "lua",
12948 feature = "mozcpp",
12949 feature = "mozjs",
12950 feature = "objc",
12951 feature = "perl",
12952 feature = "php",
12953 feature = "python",
12954 feature = "ruby",
12955 feature = "rust",
12956 feature = "tcl",
12957 feature = "typescript",
12958 ))]
12959 #[test]
12960 fn unterminated_one_line_file_reports_one_source_line() {
12961 for (lang, source) in UNTERMINATED_ONE_LINERS {
12962 let text = String::from_utf8_lossy(source);
12963 let loc = metrics_verbatim(*lang, source, MetricsOptions::default()).loc;
12964 assert_eq!(loc.sloc(), 1, "{lang:?} sloc for {text:?}");
12965 assert_eq!(loc.sloc_max(), 1, "{lang:?} sloc_max for {text:?}");
12967 assert!(
12973 loc.cloc() + loc.ploc() <= loc.sloc(),
12974 "{lang:?}: cloc {} + ploc {} exceeds sloc {} for {text:?}",
12975 loc.cloc(),
12976 loc.ploc(),
12977 loc.sloc(),
12978 );
12979 }
12980 }
12981
12982 #[cfg(any(
12993 feature = "c",
12994 feature = "c-family-helpers",
12995 feature = "cpp",
12996 feature = "mozcpp"
12997 ))]
12998 #[test]
12999 fn no_op_loc_grammars_still_count_their_unterminated_row() {
13000 for (lang, source) in [
13001 (crate::LANG::Preproc, &b"#define A 1"[..]),
13002 (crate::LANG::Ccomment, &b"/* c */"[..]),
13003 ] {
13004 let bare = metrics_verbatim(lang, source, MetricsOptions::default()).loc;
13005 assert_eq!(bare.sloc(), 1, "{lang:?} unterminated sloc");
13006 let mut terminated = source.to_vec();
13007 terminated.push(b'\n');
13008 let terminated = metrics_verbatim(lang, &terminated, MetricsOptions::default()).loc;
13009 assert_eq!(
13010 bare.sloc(),
13011 terminated.sloc(),
13012 "{lang:?} sloc must not depend on the trailing newline"
13013 );
13014 assert_eq!((bare.ploc(), bare.cloc(), bare.lloc()), (0, 0, 0));
13016 }
13017 }
13018
13019 #[cfg(any(feature = "c", feature = "cpp", feature = "mozcpp", feature = "objc"))]
13042 #[test]
13043 fn a_continued_macro_body_counts_every_row_it_spans() {
13044 const CONTINUED_MACRO: &[u8] =
13046 b"#define SUM(a, b) \\\n ((a) + \\\n (b))\n\nint main(void) { return SUM(1, 2); }\n";
13047
13048 const LANGS: &[crate::LANG] = &[
13054 #[cfg(feature = "c")]
13055 crate::LANG::C,
13056 #[cfg(feature = "cpp")]
13057 crate::LANG::Cpp,
13058 #[cfg(feature = "mozcpp")]
13059 crate::LANG::Mozcpp,
13060 #[cfg(feature = "objc")]
13061 crate::LANG::Objc,
13062 ];
13063 crate::test_support::assert_fixtures_present(LANGS);
13064
13065 for &lang in LANGS {
13066 let loc = metrics_verbatim(lang, CONTINUED_MACRO, MetricsOptions::default()).loc;
13067 assert_eq!(
13068 loc.ploc(),
13069 4,
13070 "{lang:?}: every continuation row of the macro body is code"
13071 );
13072 assert_eq!(loc.sloc(), 5, "{lang:?} sloc");
13073 assert_eq!(loc.lloc(), 1, "{lang:?} lloc");
13074 assert_eq!(loc.cloc(), 0, "{lang:?} cloc");
13075 assert_eq!(loc.blank(), 1, "{lang:?} blank");
13076 }
13077 }
13078
13079 #[cfg(any(feature = "c", feature = "cpp", feature = "mozcpp", feature = "objc"))]
13111 #[test]
13112 fn a_dangling_macro_continuation_does_not_credit_the_row_below() {
13113 const DANGLING_CONTINUATION: &[u8] = b"#define A 1 \\\n 2 \\\n\nint x;\n";
13116
13117 const LANGS: &[crate::LANG] = &[
13120 #[cfg(feature = "c")]
13121 crate::LANG::C,
13122 #[cfg(feature = "cpp")]
13123 crate::LANG::Cpp,
13124 #[cfg(feature = "mozcpp")]
13125 crate::LANG::Mozcpp,
13126 #[cfg(feature = "objc")]
13127 crate::LANG::Objc,
13128 ];
13129 crate::test_support::assert_fixtures_present(LANGS);
13130
13131 for &lang in LANGS {
13132 let loc = metrics_verbatim(lang, DANGLING_CONTINUATION, MetricsOptions::default()).loc;
13133 assert_eq!(
13134 loc.ploc(),
13135 3,
13136 "{lang:?}: the row a dangling continuation ends *at* is not code"
13137 );
13138 assert_eq!(
13139 loc.blank(),
13140 1,
13141 "{lang:?}: that row is blank, not part of the macro body"
13142 );
13143 assert_eq!(loc.sloc(), 4, "{lang:?} sloc");
13144 assert_eq!(loc.lloc(), 1, "{lang:?} lloc");
13145 assert_eq!(loc.cloc(), 0, "{lang:?} cloc");
13146 }
13147 }
13148
13149 #[cfg(all(
13166 feature = "bash",
13167 feature = "c",
13168 feature = "cpp",
13169 feature = "csharp",
13170 feature = "elixir",
13171 feature = "go",
13172 feature = "groovy",
13173 feature = "irules",
13174 feature = "java",
13175 feature = "javascript",
13176 feature = "kotlin",
13177 feature = "lua",
13178 feature = "mozcpp",
13179 feature = "mozjs",
13180 feature = "objc",
13181 feature = "perl",
13182 feature = "php",
13183 feature = "python",
13184 feature = "ruby",
13185 feature = "rust",
13186 feature = "tcl",
13187 feature = "typescript",
13188 ))]
13189 #[test]
13190 fn trailing_newline_does_not_change_loc_or_mi() {
13191 for (lang, source) in UNTERMINATED_ONE_LINERS {
13192 let text = String::from_utf8_lossy(source);
13193 let bare = metrics_verbatim(*lang, source, MetricsOptions::default());
13194 let mut newline_terminated = source.to_vec();
13195 newline_terminated.push(b'\n');
13196 let terminated =
13197 metrics_verbatim(*lang, &newline_terminated, MetricsOptions::default());
13198
13199 assert_eq!(
13200 bare.loc.sloc(),
13201 terminated.loc.sloc(),
13202 "{lang:?} sloc {text:?}"
13203 );
13204 assert_eq!(
13205 bare.loc.ploc(),
13206 terminated.loc.ploc(),
13207 "{lang:?} ploc {text:?}"
13208 );
13209 assert_eq!(
13210 bare.loc.cloc(),
13211 terminated.loc.cloc(),
13212 "{lang:?} cloc {text:?}"
13213 );
13214 assert_eq!(
13215 bare.loc.lloc(),
13216 terminated.loc.lloc(),
13217 "{lang:?} lloc {text:?}"
13218 );
13219 assert_eq!(
13220 bare.loc.blank(),
13221 terminated.loc.blank(),
13222 "{lang:?} blank {text:?}"
13223 );
13224 assert_eq!(
13230 bare.mi.original(),
13231 terminated.mi.original(),
13232 "{lang:?} mi {text:?}"
13233 );
13234 assert_eq!(
13235 bare.mi.sei(),
13236 terminated.mi.sei(),
13237 "{lang:?} mi.sei {text:?}"
13238 );
13239 assert_eq!(
13240 bare.mi.visual_studio(),
13241 terminated.mi.visual_studio(),
13242 "{lang:?} mi.visual_studio {text:?}",
13243 );
13244 assert_ne!(bare.mi.original(), 0.0, "{lang:?} mi must not be zeroed");
13245 }
13246 }
13247
13248 #[cfg(feature = "rust")]
13252 #[test]
13253 fn unterminated_trailing_comment_upholds_the_cloc_ploc_invariant() {
13254 let loc = rust_loc(b"fn f(){}\n/// x");
13256 assert_eq!(loc.sloc(), 2);
13257 assert_eq!(loc.ploc(), 1);
13258 assert_eq!(loc.cloc(), 1);
13259 assert_eq!(loc.blank(), 0);
13260 }
13261
13262 #[cfg(feature = "rust")]
13266 #[test]
13267 fn degenerate_inputs_report_their_real_row_count() {
13268 assert_eq!(rust_loc(b"").sloc(), 0);
13270 let spaces = rust_loc(b" ");
13273 assert_eq!(spaces.sloc(), 1);
13274 assert_eq!(spaces.blank(), 1);
13275 let one = rust_loc(b"\n");
13283 assert_eq!((one.sloc(), one.ploc(), one.blank()), (1, 0, 1));
13284 let two = rust_loc(b"\n\n");
13285 assert_eq!((two.sloc(), two.ploc(), two.blank()), (2, 0, 2));
13286 }
13287
13288 #[cfg(any(
13297 feature = "bash",
13298 feature = "c",
13299 feature = "c-family-helpers",
13300 feature = "cpp",
13301 feature = "csharp",
13302 feature = "elixir",
13303 feature = "go",
13304 feature = "groovy",
13305 feature = "irules",
13306 feature = "java",
13307 feature = "javascript",
13308 feature = "kotlin",
13309 feature = "lua",
13310 feature = "mozcpp",
13311 feature = "mozjs",
13312 feature = "objc",
13313 feature = "perl",
13314 feature = "php",
13315 feature = "python",
13316 feature = "ruby",
13317 feature = "rust",
13318 feature = "tcl",
13319 feature = "typescript",
13320 ))]
13321 fn all_loc_grammars() -> impl Iterator<Item = crate::LANG> {
13322 UNTERMINATED_ONE_LINERS
13323 .iter()
13324 .map(|(lang, _)| *lang)
13325 .chain([crate::LANG::Preproc, crate::LANG::Ccomment])
13326 }
13327
13328 #[cfg(all(
13347 feature = "bash",
13348 feature = "c",
13349 any(
13350 feature = "c",
13351 feature = "c-family-helpers",
13352 feature = "cpp",
13353 feature = "mozcpp"
13354 ),
13355 feature = "cpp",
13356 feature = "csharp",
13357 feature = "elixir",
13358 feature = "go",
13359 feature = "groovy",
13360 feature = "irules",
13361 feature = "java",
13362 feature = "javascript",
13363 feature = "kotlin",
13364 feature = "lua",
13365 feature = "mozcpp",
13366 feature = "mozjs",
13367 feature = "objc",
13368 feature = "perl",
13369 feature = "php",
13370 feature = "python",
13371 feature = "ruby",
13372 feature = "rust",
13373 feature = "tcl",
13374 feature = "typescript",
13375 ))]
13376 #[test]
13377 fn whitespace_only_input_is_uniform_across_grammars() {
13378 for bare in [&b" "[..], b"\t\t"] {
13381 let mut newline_terminated = bare.to_vec();
13382 newline_terminated.push(b'\n');
13383 for lang in all_loc_grammars() {
13384 let text = String::from_utf8_lossy(bare);
13385 let unterminated = metrics_verbatim(lang, bare, MetricsOptions::default()).loc;
13386 let terminated =
13387 metrics_verbatim(lang, &newline_terminated, MetricsOptions::default()).loc;
13388
13389 assert_eq!(
13390 (unterminated.sloc(), unterminated.blank()),
13391 (1, 1),
13392 "{lang:?}: unterminated {text:?} is one blank row for every grammar"
13393 );
13394
13395 assert_eq!(
13396 (terminated.sloc(), terminated.blank()),
13397 (1, 1),
13398 "{lang:?}: newline-terminated {text:?} is the same one blank \
13399 row — whether the grammar collapses its root at \
13400 end-of-input is no longer observable in loc (#1247)"
13401 );
13402
13403 for loc in [&unterminated, &terminated] {
13408 assert_eq!(
13409 (loc.ploc(), loc.cloc(), loc.lloc()),
13410 (0, 0, 0),
13411 "{lang:?}: whitespace is neither code nor comment"
13412 );
13413 }
13414 }
13415 }
13416 }
13417
13418 #[cfg(all(feature = "c", feature = "python", feature = "rust", feature = "tcl"))]
13434 #[test]
13435 fn leading_blank_rows_count_in_the_units_sloc_and_blank() {
13436 #[cfg(any(feature = "c", feature = "python", feature = "rust", feature = "tcl"))]
13437 const LEADING_BLANKS: u64 = 3;
13438 for (lang, body, ploc) in [
13439 (crate::LANG::Rust, &b"fn a() {}\n"[..], 1),
13440 (crate::LANG::Python, b"def a():\n pass\n", 2),
13441 (crate::LANG::C, b"int f() { return 0; }\n", 1),
13442 (crate::LANG::Tcl, b"puts hi\n", 1),
13443 ] {
13444 let mut source = vec![b'\n'; LEADING_BLANKS as usize];
13445 source.extend_from_slice(body);
13446 let space = space_verbatim(lang, &source, MetricsOptions::default());
13447 let loc = &space.metrics.loc;
13448 assert_eq!(
13449 (loc.sloc(), loc.ploc(), loc.cloc(), loc.blank()),
13450 (LEADING_BLANKS + ploc, ploc, 0, LEADING_BLANKS),
13451 "{lang:?}: the leading rows are blank, not absent"
13452 );
13453 assert_eq!(
13457 loc.sloc() as usize,
13458 space.end_line - space.start_line + 1,
13459 "{lang:?}: sloc equals the rows of the unit's reported span"
13460 );
13461 }
13462 }
13463
13464 #[cfg(feature = "rust")]
13470 #[test]
13471 fn interior_blanks_and_leading_comments_are_unmoved_by_the_anchor() {
13472 let interior = rust_loc(b"fn a() {}\n\nfn b() {}\n");
13474 assert_eq!(
13475 (
13476 interior.sloc(),
13477 interior.ploc(),
13478 interior.cloc(),
13479 interior.blank()
13480 ),
13481 (3, 2, 0, 1)
13482 );
13483 let leading_comment = rust_loc(b"// c\n\n\nfn a() {}\n");
13485 assert_eq!(
13486 (
13487 leading_comment.sloc(),
13488 leading_comment.ploc(),
13489 leading_comment.cloc(),
13490 leading_comment.blank()
13491 ),
13492 (4, 1, 1, 2)
13493 );
13494 }
13495
13496 #[cfg(feature = "rust")]
13501 #[test]
13502 fn the_unit_anchor_does_not_reach_nested_spaces() {
13503 let space = space_verbatim(
13504 crate::LANG::Rust,
13505 b"\n\n\nfn a() {\n let x = 1;\n}\n",
13506 MetricsOptions::default(),
13507 );
13508 assert_eq!((space.start_line, space.end_line), (1, 6));
13509 assert_eq!(space.metrics.loc.sloc(), 6, "the file has six rows");
13510 let nested = &space.spaces[0];
13511 assert_eq!((nested.start_line, nested.end_line), (4, 6));
13512 assert_eq!(
13513 nested.metrics.loc.sloc(),
13514 3,
13515 "the function's own three rows, not the file's six"
13516 );
13517 assert_eq!(nested.metrics.loc.blank(), 0, "the function has no blanks");
13518 }
13519
13520 #[cfg(feature = "rust")]
13528 #[test]
13529 fn exclude_tests_pruning_composes_with_the_unit_anchor() {
13530 let source = b"\n\n\nfn a() {}\n\n#[test]\nfn t() {\n assert!(true);\n}\n";
13532 let kept = metrics_verbatim(
13533 crate::LANG::Rust,
13534 source,
13535 MetricsOptions::default().with_exclude_tests(true),
13536 )
13537 .loc;
13538 assert_eq!(
13543 (kept.sloc(), kept.ploc(), kept.cloc(), kept.blank()),
13544 (5, 1, 0, 4),
13545 "the four pruned rows leave; the three leading blanks stay"
13546 );
13547
13548 let unpruned = rust_loc(source);
13549 assert_eq!(
13550 (unpruned.sloc(), unpruned.ploc(), unpruned.blank()),
13551 (9, 5, 4),
13552 "the same file unpruned — the anchor is what makes both blank counts 4"
13553 );
13554 }
13555
13556 #[cfg(feature = "rust")]
13564 fn rust_loc_pruned(source: &[u8]) -> Stats {
13565 metrics_verbatim(
13566 crate::LANG::Rust,
13567 source,
13568 crate::MetricsOptions::default().with_exclude_tests(true),
13569 )
13570 .loc
13571 }
13572
13573 #[cfg(feature = "rust")]
13595 #[test]
13596 fn a_pruned_item_sharing_a_row_with_retained_code_keeps_the_row() {
13597 for source in [
13598 &b"fn a() {} #[cfg(test)] mod t { #[test] fn x() {} }\n"[..],
13599 &b"#[cfg(test)] mod t { #[test] fn x() {} } fn a() {}\n"[..],
13600 ] {
13601 let text = String::from_utf8_lossy(source);
13602 let pruned = rust_loc_pruned(source);
13603 assert_eq!(
13604 (pruned.sloc(), pruned.ploc(), pruned.cloc(), pruned.blank()),
13605 (1, 1, 0, 0),
13606 "pruned {text:?}"
13607 );
13608
13609 let kept = rust_loc(source);
13610 assert_eq!(
13611 (kept.sloc(), kept.ploc(), kept.cloc(), kept.blank()),
13612 (1, 1, 0, 0),
13613 "unpruned {text:?} — the prune can remove no row here"
13614 );
13615 }
13616 }
13617
13618 #[cfg(feature = "rust")]
13655 #[test]
13656 fn a_blank_row_does_not_absorb_a_phantom_exclusion() {
13657 let shared_row = rust_loc_pruned(b"fn a() {} #[cfg(test)] mod t {}\n\nfn b() {}\n");
13659 assert_eq!(
13660 (
13661 shared_row.sloc(),
13662 shared_row.ploc(),
13663 shared_row.cloc(),
13664 shared_row.blank()
13665 ),
13666 (3, 2, 0, 1),
13667 "nothing is excluded: row 0 carries `fn a`"
13668 );
13669
13670 let own_rows = rust_loc_pruned(b"fn a() {}\n\n#[cfg(test)]\nmod t { #[test] fn x() {} }\n");
13674 assert_eq!(
13675 (
13676 own_rows.sloc(),
13677 own_rows.ploc(),
13678 own_rows.cloc(),
13679 own_rows.blank()
13680 ),
13681 (2, 1, 0, 1),
13682 "two of four rows excluded; only `fn a` and the blank stay"
13683 );
13684 }
13685
13686 #[cfg(feature = "rust")]
13696 #[test]
13697 fn a_comment_sharing_a_pruned_items_row_keeps_that_row() {
13698 let loc = rust_loc_pruned(b"#[cfg(test)]\nmod t {} // note\n");
13705 assert_eq!(loc.sloc(), 1, "the comment row survives the prune");
13706 assert_eq!(loc.cloc(), 1, "the trailing comment is still counted");
13707 assert_eq!(loc.ploc(), 0, "no production code is left on either row");
13708 assert!(
13709 loc.ploc() <= loc.sloc(),
13710 "ploc {} must not exceed sloc {}",
13711 loc.ploc(),
13712 loc.sloc()
13713 );
13714 }
13715
13716 #[cfg(feature = "perl")]
13723 #[test]
13724 fn perl_last_sub_does_not_absorb_the_trailing_newline() {
13725 let space = space_verbatim(
13727 crate::LANG::Perl,
13728 b"sub f {\n return 1;\n}\nsub g {\n return 2;\n}\n",
13729 MetricsOptions::default(),
13730 );
13731 assert_eq!(space.metrics.loc.sloc(), 6, "the file has six rows");
13732 let subs: Vec<u64> = space
13733 .spaces
13734 .iter()
13735 .map(|child| child.metrics.loc.sloc())
13736 .collect();
13737 assert_eq!(subs, vec![3, 3], "both subs occupy three rows");
13738 }
13739
13740 #[cfg(any(
13775 feature = "bash",
13776 feature = "c",
13777 feature = "cpp",
13778 feature = "elixir",
13779 feature = "groovy",
13780 feature = "lua",
13781 feature = "mozcpp",
13782 feature = "objc",
13783 feature = "perl",
13784 feature = "ruby",
13785 ))]
13786 #[test]
13787 fn a_recovery_token_past_the_span_is_not_a_code_row() {
13788 const FIXTURES: &[(crate::LANG, &[u8], u64, u64)] = &[
13803 #[cfg(feature = "bash")]
13804 (crate::LANG::Bash, b"cat <<DOC\na\n", 2, 3),
13805 #[cfg(feature = "c")]
13806 (crate::LANG::C, b"int x = \\\n", 1, 2),
13807 #[cfg(feature = "cpp")]
13808 (crate::LANG::Cpp, b"int x = \\\n", 1, 2),
13809 #[cfg(feature = "mozcpp")]
13810 (crate::LANG::Mozcpp, b"int x = \\\n", 1, 2),
13811 #[cfg(feature = "objc")]
13812 (crate::LANG::Objc, b"int x = \\\n", 1, 2),
13813 #[cfg(feature = "elixir")]
13818 (crate::LANG::Elixir, b"x = \"abc\n", 1, 2),
13819 #[cfg(feature = "elixir")]
13820 (crate::LANG::Elixir, b"x = \"\"\"\nabc\n", 2, 3),
13821 #[cfg(feature = "elixir")]
13822 (crate::LANG::Elixir, b"x = ~s(\n", 1, 2),
13823 #[cfg(feature = "elixir")]
13829 (crate::LANG::Elixir, b"/* block */\n", 1, 2),
13830 #[cfg(feature = "groovy")]
13831 (crate::LANG::Groovy, b"def x = \"\"\"abc\n", 1, 2),
13832 #[cfg(feature = "lua")]
13833 (crate::LANG::Lua, b"x = \"abc\n", 1, 2),
13834 #[cfg(feature = "lua")]
13835 (crate::LANG::Lua, b"x = 'abc\n", 1, 2),
13836 #[cfg(feature = "perl")]
13837 (crate::LANG::Perl, b"my $x = <<DOC;\na\n", 2, 3),
13838 #[cfg(feature = "ruby")]
13839 (crate::LANG::Ruby, b"x = <<~DOC\na\n", 2, 3),
13840 #[cfg(feature = "ruby")]
13841 (crate::LANG::Ruby, b"x = %w[\n", 1, 2),
13842 ];
13843
13844 crate::test_support::assert_fixtures_present(FIXTURES);
13845 for &(lang, source, rows, ploc_before_1398) in FIXTURES {
13846 let text = String::from_utf8_lossy(source);
13847 let ast = crate::Ast::parse(crate::Source::new(lang, source))
13860 .expect("an enabled language parses, error tree or not");
13861 let mut stack = vec![ast.root_node()];
13862 let mut phantom = None;
13863 while let Some(node) = stack.pop() {
13864 if node.start_row() as u64 >= rows {
13865 phantom = Some(node.start_row());
13866 break;
13867 }
13868 stack.extend(node.children());
13869 }
13870 assert!(
13871 phantom.is_some(),
13872 "{lang:?}: {text:?} no longer produces a node past row {rows}, \
13873 so it no longer exercises #1398"
13874 );
13875
13876 let loc = metrics_verbatim(lang, source, MetricsOptions::default()).loc;
13877 assert_eq!(loc.sloc(), rows, "{lang:?}: {text:?} occupies {rows} rows");
13878 assert_eq!(
13879 loc.ploc(),
13880 rows,
13881 "{lang:?}: every row of {text:?} is code and no row past it is \
13882 (was {ploc_before_1398} before #1398)"
13883 );
13884 assert_eq!((loc.cloc(), loc.blank()), (0, 0), "{lang:?}: {text:?}");
13893 }
13894 }
13895
13896 #[test]
13917 #[cfg(debug_assertions)]
13918 #[should_panic(expected = "zero-span space reached the clamp holding")]
13919 fn a_zero_span_holding_rows_trips_the_guard() {
13920 let mut stats = Stats::default();
13921 stats.init_unit_span(0, 0);
13923 stats.ploc.lines.insert(0);
13924 assert_eq!((stats.sloc(), stats.ploc()), (0, 1));
13925
13926 stats.clamp_line_sets_to_span();
13927 }
13928
13929 #[test]
13933 fn a_zero_span_without_rows_clamps_cleanly() {
13934 let mut stats = Stats::default();
13935 stats.init_unit_span(0, 0);
13936
13937 stats.clamp_line_sets_to_span();
13938
13939 assert_eq!(
13940 (stats.sloc(), stats.ploc(), stats.cloc()),
13941 (0, 0, 0),
13942 "a span of no rows retains no row"
13943 );
13944 }
13945
13946 #[cfg(feature = "elixir")]
13957 #[test]
13958 fn the_clamp_reaches_a_nested_space_not_only_the_unit() {
13959 let space = space_verbatim(
13960 crate::LANG::Elixir,
13961 b"defmodule M do\n",
13962 MetricsOptions::default(),
13963 );
13964 assert_eq!(
13965 (space.metrics.loc.sloc(), space.metrics.loc.ploc()),
13966 (1, 1),
13967 "the unit is one row, all code"
13968 );
13969 let module = space
13970 .spaces
13971 .first()
13972 .expect("the `defmodule` opens a space even unterminated");
13973 assert_eq!(module.name.as_deref(), Some("M"));
13974 assert_eq!(
13975 (module.metrics.loc.sloc(), module.metrics.loc.ploc()),
13976 (1, 1),
13977 "the module's own space is one row too — `ploc` was 2 before #1398"
13978 );
13979 }
13980
13981 #[cfg(feature = "perl")]
13992 #[test]
13993 fn an_unterminated_pod_block_does_not_comment_a_row_past_eof() {
13994 let loc =
13995 metrics_verbatim(crate::LANG::Perl, b"=pod\nabc\n", MetricsOptions::default()).loc;
13996 assert_eq!((loc.sloc(), loc.cloc()), (2, 2));
13999 assert_eq!((loc.ploc(), loc.blank()), (0, 0));
14000 }
14001
14002 #[cfg(all(feature = "irules", feature = "tcl"))]
14017 #[test]
14018 fn tcl_family_does_not_count_terminator_rows_as_code() {
14019 for lang in [crate::LANG::Tcl, crate::LANG::Irules] {
14020 let padded = metrics_verbatim(
14022 lang,
14023 b"proc f {} {}\n \nproc g {} {}\n",
14024 MetricsOptions::default(),
14025 )
14026 .loc;
14027 assert_eq!(padded.sloc(), 3, "{lang:?} sloc");
14028 assert_eq!(padded.ploc(), 2, "{lang:?} ploc");
14029 assert_eq!(padded.blank(), 1, "{lang:?} blank");
14030
14031 let empty = metrics_verbatim(
14036 lang,
14037 b"proc f {} {}\n\nproc g {} {}\n",
14038 MetricsOptions::default(),
14039 )
14040 .loc;
14041 assert_eq!(
14042 (padded.sloc(), padded.ploc(), padded.blank()),
14043 (empty.sloc(), empty.ploc(), empty.blank()),
14044 "{lang:?}: trailing whitespace on a blank row must not make it code"
14045 );
14046
14047 let commented = metrics_verbatim(
14054 lang,
14055 b"# lead-in\nproc f {} {}\n",
14056 MetricsOptions::default(),
14057 )
14058 .loc;
14059 assert_eq!(commented.sloc(), 2, "{lang:?} commented sloc");
14060 assert_eq!(commented.ploc(), 1, "{lang:?} commented ploc");
14061 assert_eq!(commented.cloc(), 1, "{lang:?} commented cloc");
14062 assert_eq!(commented.blank(), 0, "{lang:?} commented blank");
14063 }
14064 }
14065
14066 #[cfg(any(
14075 feature = "bash",
14076 feature = "c",
14077 feature = "cpp",
14078 feature = "csharp",
14079 feature = "elixir",
14080 feature = "go",
14081 feature = "groovy",
14082 feature = "irules",
14083 feature = "java",
14084 feature = "javascript",
14085 feature = "kotlin",
14086 feature = "lua",
14087 feature = "mozcpp",
14088 feature = "mozjs",
14089 feature = "objc",
14090 feature = "perl",
14091 feature = "php",
14092 feature = "python",
14093 feature = "ruby",
14094 feature = "rust",
14095 feature = "tcl",
14096 feature = "typescript",
14097 ))]
14098 fn comment_spellings(lang: crate::LANG) -> &'static [&'static str] {
14099 use crate::LANG::*;
14100 match lang {
14101 Python | Ruby | Bash | Elixir | Tcl | Irules | Perl => &["# c"],
14102 Lua => &["-- c", "--[[ c ]]"],
14103 Php => &["# c", "/* c */", "/** c */"],
14104 Rust => &["// c", "/* c */", "/// c"],
14105 _ => &["// c", "/* c */"],
14106 }
14107 }
14108
14109 #[cfg(any(
14114 feature = "bash",
14115 feature = "c",
14116 feature = "cpp",
14117 feature = "csharp",
14118 feature = "elixir",
14119 feature = "go",
14120 feature = "groovy",
14121 feature = "irules",
14122 feature = "java",
14123 feature = "javascript",
14124 feature = "kotlin",
14125 feature = "lua",
14126 feature = "mozcpp",
14127 feature = "mozjs",
14128 feature = "objc",
14129 feature = "perl",
14130 feature = "php",
14131 feature = "python",
14132 feature = "ruby",
14133 feature = "rust",
14134 feature = "tcl",
14135 feature = "typescript",
14136 ))]
14137 fn doc_comment_spellings(lang: crate::LANG) -> &'static [&'static str] {
14138 use crate::LANG::*;
14139 match lang {
14140 Php => &["/** c */"],
14141 Rust => &["/// c"],
14142 _ => &[],
14143 }
14144 }
14145
14146 #[cfg(all(
14164 feature = "bash",
14165 feature = "c",
14166 feature = "cpp",
14167 feature = "csharp",
14168 feature = "elixir",
14169 feature = "go",
14170 feature = "groovy",
14171 feature = "irules",
14172 feature = "java",
14173 feature = "javascript",
14174 feature = "kotlin",
14175 feature = "lua",
14176 feature = "mozcpp",
14177 feature = "mozjs",
14178 feature = "objc",
14179 feature = "perl",
14180 feature = "php",
14181 feature = "python",
14182 feature = "ruby",
14183 feature = "rust",
14184 feature = "tcl",
14185 feature = "typescript",
14186 ))]
14187 #[test]
14188 fn a_comment_row_is_never_counted_as_code() {
14189 for (lang, code) in UNTERMINATED_ONE_LINERS {
14190 let must_lead = *lang == crate::LANG::Php;
14194 for &comment in comment_spellings(*lang) {
14195 let orders: &[bool] = if must_lead { &[false] } else { &[true, false] };
14196 for &comment_first in orders {
14197 let mut src = Vec::new();
14198 let (first, second): (&[u8], &[u8]) = if comment_first {
14199 (comment.as_bytes(), code)
14200 } else {
14201 (code, comment.as_bytes())
14202 };
14203 src.extend_from_slice(first);
14204 src.push(b'\n');
14205 src.extend_from_slice(second);
14206 src.push(b'\n');
14207
14208 let loc = metrics_verbatim(*lang, &src, MetricsOptions::default()).loc;
14209 let text = String::from_utf8_lossy(&src);
14210 assert_eq!(loc.sloc(), 2, "{lang:?} sloc for {text:?}");
14211 assert_eq!(
14212 loc.ploc(),
14213 1,
14214 "{lang:?} ploc for {text:?} — the comment row is not code"
14215 );
14216 assert_eq!(loc.cloc(), 1, "{lang:?} cloc for {text:?}");
14217 assert_eq!(loc.blank(), 0, "{lang:?} blank for {text:?}");
14218 let positional =
14232 comment_first && matches!(lang, crate::LANG::Go | crate::LANG::Ruby);
14233 let bound_dangling =
14234 !comment_first && matches!(lang, crate::LANG::Rust | crate::LANG::Php);
14235 let expected = u64::from(
14236 (doc_comment_spellings(*lang).contains(&comment) && !bound_dangling)
14237 || positional,
14238 );
14239 assert_eq!(
14240 loc.dloc(),
14241 expected,
14242 "{lang:?} dloc for {text:?} — a doc marker, or a Go / Ruby lead comment"
14243 );
14244 }
14245 }
14246 }
14247 }
14248
14249 #[cfg(feature = "javascript")]
14257 #[test]
14258 fn javascript_declaration_lloc() {
14259 check_metrics::<JavascriptParser>(
14260 "var a = 1;\nlet b = 2;\nconst c = 3;\nvar d = 4, e = 5;\nusing r = open();\n",
14261 "foo.js",
14262 |metric| {
14263 assert_eq!(metric.loc.sloc(), 5);
14264 assert_eq!(metric.loc.ploc(), 5);
14265 assert_eq!(metric.loc.lloc(), 5);
14266 assert_eq!(metric.loc.cloc(), 0);
14267 assert_eq!(metric.loc.blank(), 0);
14268 },
14269 );
14270 }
14271
14272 #[cfg(feature = "javascript")]
14283 #[test]
14284 fn javascript_for_header_declaration_not_double_counted() {
14285 check_metrics::<JavascriptParser>(
14286 "function f(arr, obj) {\n for (var i = 0; i < 3; i++) {\n var s = i;\n }\n for (const x of arr) {}\n for (var k in obj) {}\n}\n",
14287 "foo.js",
14288 |metric| {
14289 assert_eq!(metric.loc.sloc(), 7);
14290 assert_eq!(metric.loc.ploc(), 7);
14291 assert_eq!(metric.loc.lloc(), 4);
14292 assert_eq!(metric.loc.cloc(), 0);
14293 assert_eq!(metric.loc.blank(), 0);
14294 },
14295 );
14296 }
14297
14298 #[cfg(feature = "javascript")]
14306 #[test]
14307 fn javascript_exported_declaration_counts_once() {
14308 check_metrics::<JavascriptParser>(
14309 "export const a = 1;\nexport let b = 2;\nexport default 3;\nexport function f() { const c = 4; }\n",
14310 "foo.js",
14311 |metric| {
14312 assert_eq!(metric.loc.sloc(), 4);
14313 assert_eq!(metric.loc.ploc(), 4);
14314 assert_eq!(metric.loc.lloc(), 5);
14315 assert_eq!(metric.loc.cloc(), 0);
14316 assert_eq!(metric.loc.blank(), 0);
14317 },
14318 );
14319 }
14320
14321 #[cfg(feature = "mozjs")]
14329 #[test]
14330 fn mozjs_declaration_lloc() {
14331 check_metrics::<MozjsParser>(
14332 "var a = 1;\nlet b = 2;\nconst c = 3;\nvar d = 4, e = 5;\nusing r = open();\n",
14333 "foo.js",
14334 |metric| {
14335 assert_eq!(metric.loc.sloc(), 5);
14336 assert_eq!(metric.loc.ploc(), 5);
14337 assert_eq!(metric.loc.lloc(), 5);
14338 assert_eq!(metric.loc.cloc(), 0);
14339 assert_eq!(metric.loc.blank(), 0);
14340 },
14341 );
14342 }
14343
14344 #[cfg(feature = "mozjs")]
14355 #[test]
14356 fn mozjs_for_header_declaration_not_double_counted() {
14357 check_metrics::<MozjsParser>(
14358 "function f(arr, obj) {\n for (var i = 0; i < 3; i++) {\n var s = i;\n }\n for (const x of arr) {}\n for (var k in obj) {}\n}\n",
14359 "foo.js",
14360 |metric| {
14361 assert_eq!(metric.loc.sloc(), 7);
14362 assert_eq!(metric.loc.ploc(), 7);
14363 assert_eq!(metric.loc.lloc(), 4);
14364 assert_eq!(metric.loc.cloc(), 0);
14365 assert_eq!(metric.loc.blank(), 0);
14366 },
14367 );
14368 }
14369
14370 #[cfg(feature = "mozjs")]
14378 #[test]
14379 fn mozjs_exported_declaration_counts_once() {
14380 check_metrics::<MozjsParser>(
14381 "export const a = 1;\nexport let b = 2;\nexport default 3;\nexport function f() { const c = 4; }\n",
14382 "foo.js",
14383 |metric| {
14384 assert_eq!(metric.loc.sloc(), 4);
14385 assert_eq!(metric.loc.ploc(), 4);
14386 assert_eq!(metric.loc.lloc(), 5);
14387 assert_eq!(metric.loc.cloc(), 0);
14388 assert_eq!(metric.loc.blank(), 0);
14389 },
14390 );
14391 }
14392
14393 #[cfg(feature = "typescript")]
14399 #[test]
14400 fn typescript_declaration_lloc() {
14401 check_metrics::<TypescriptParser>(
14402 "var a: number = 1;\nlet b = 2;\nconst c = 3;\nvar d = 4, e = 5;\n",
14403 "foo.ts",
14404 |metric| {
14405 assert_eq!(metric.loc.sloc(), 4);
14406 assert_eq!(metric.loc.ploc(), 4);
14407 assert_eq!(metric.loc.lloc(), 4);
14408 assert_eq!(metric.loc.cloc(), 0);
14409 assert_eq!(metric.loc.blank(), 0);
14410 },
14411 );
14412 }
14413
14414 #[cfg(feature = "typescript")]
14425 #[test]
14426 fn typescript_for_header_declaration_not_double_counted() {
14427 check_metrics::<TypescriptParser>(
14428 "function f(arr, obj) {\n for (var i = 0; i < 3; i++) {\n var s = i;\n }\n for (const x of arr) {}\n for (var k in obj) {}\n}\n",
14429 "foo.ts",
14430 |metric| {
14431 assert_eq!(metric.loc.sloc(), 7);
14432 assert_eq!(metric.loc.ploc(), 7);
14433 assert_eq!(metric.loc.lloc(), 4);
14434 assert_eq!(metric.loc.cloc(), 0);
14435 assert_eq!(metric.loc.blank(), 0);
14436 },
14437 );
14438 }
14439
14440 #[cfg(feature = "typescript")]
14453 #[test]
14454 fn typescript_exported_declaration_counts_once() {
14455 check_metrics::<TypescriptParser>(
14456 "export const a: number = 1;\nexport declare const b: string;\nexport default 3;\nexport function f(): void { const c = 4; }\n",
14457 "foo.ts",
14458 |metric| {
14459 assert_eq!(metric.loc.sloc(), 4);
14460 assert_eq!(metric.loc.ploc(), 4);
14461 assert_eq!(metric.loc.lloc(), 5);
14462 assert_eq!(metric.loc.cloc(), 0);
14463 assert_eq!(metric.loc.blank(), 0);
14464 },
14465 );
14466 }
14467
14468 #[cfg(feature = "typescript")]
14474 #[test]
14475 fn tsx_declaration_lloc() {
14476 check_metrics::<TsxParser>(
14477 "var a: number = 1;\nlet b = 2;\nconst c = 3;\nvar d = 4, e = 5;\n",
14478 "foo.tsx",
14479 |metric| {
14480 assert_eq!(metric.loc.sloc(), 4);
14481 assert_eq!(metric.loc.ploc(), 4);
14482 assert_eq!(metric.loc.lloc(), 4);
14483 assert_eq!(metric.loc.cloc(), 0);
14484 assert_eq!(metric.loc.blank(), 0);
14485 },
14486 );
14487 }
14488
14489 #[cfg(feature = "typescript")]
14500 #[test]
14501 fn tsx_for_header_declaration_not_double_counted() {
14502 check_metrics::<TsxParser>(
14503 "function f(arr, obj) {\n for (var i = 0; i < 3; i++) {\n var s = i;\n }\n for (const x of arr) {}\n for (var k in obj) {}\n}\n",
14504 "foo.tsx",
14505 |metric| {
14506 assert_eq!(metric.loc.sloc(), 7);
14507 assert_eq!(metric.loc.ploc(), 7);
14508 assert_eq!(metric.loc.lloc(), 4);
14509 assert_eq!(metric.loc.cloc(), 0);
14510 assert_eq!(metric.loc.blank(), 0);
14511 },
14512 );
14513 }
14514
14515 #[cfg(feature = "typescript")]
14528 #[test]
14529 fn tsx_exported_declaration_counts_once() {
14530 check_metrics::<TsxParser>(
14531 "export const a: number = 1;\nexport declare const b: string;\nexport default 3;\nexport function f(): void { const c = 4; }\n",
14532 "foo.tsx",
14533 |metric| {
14534 assert_eq!(metric.loc.sloc(), 4);
14535 assert_eq!(metric.loc.ploc(), 4);
14536 assert_eq!(metric.loc.lloc(), 5);
14537 assert_eq!(metric.loc.cloc(), 0);
14538 assert_eq!(metric.loc.blank(), 0);
14539 },
14540 );
14541 }
14542
14543 #[cfg(all(feature = "javascript", feature = "mozjs", feature = "typescript"))]
14554 #[test]
14555 fn js_family_braceless_for_body_declaration_counts() {
14556 #[cfg(any(feature = "javascript", feature = "mozjs", feature = "typescript"))]
14557 const BODY: &str = "for (var i = 0; i < 3; i++) var s = i;\n";
14558 #[cfg(any(feature = "javascript", feature = "mozjs", feature = "typescript"))]
14559 const NESTED: &str = "for (let i = 0; i < 2; i++) switch (i) { case 0: let y = 1; }\n";
14560 check_metrics::<JavascriptParser>(BODY, "foo.js", |m| assert_eq!(m.loc.lloc(), 2));
14561 check_metrics::<MozjsParser>(BODY, "foo.js", |m| assert_eq!(m.loc.lloc(), 2));
14562 check_metrics::<TypescriptParser>(BODY, "foo.ts", |m| assert_eq!(m.loc.lloc(), 2));
14563 check_metrics::<TsxParser>(BODY, "foo.tsx", |m| assert_eq!(m.loc.lloc(), 2));
14564 check_metrics::<JavascriptParser>(NESTED, "foo.js", |m| assert_eq!(m.loc.lloc(), 3));
14565 check_metrics::<MozjsParser>(NESTED, "foo.js", |m| assert_eq!(m.loc.lloc(), 3));
14566 check_metrics::<TypescriptParser>(NESTED, "foo.ts", |m| assert_eq!(m.loc.lloc(), 3));
14567 check_metrics::<TsxParser>(NESTED, "foo.tsx", |m| assert_eq!(m.loc.lloc(), 3));
14568 }
14569
14570 #[cfg(feature = "typescript")]
14579 #[test]
14580 fn typescript_ambient_declarations_are_not_logical_lines() {
14581 const SRC: &str = "declare const VERSION: string;\ndeclare let mutable: number;\ndeclare namespace NS { const inner: number; }\ndeclare module \"m\" { let y: string; }\n";
14582 check_metrics::<TypescriptParser>(SRC, "foo.ts", |m| assert_eq!(m.loc.lloc(), 0));
14583 check_metrics::<TsxParser>(SRC, "foo.tsx", |m| assert_eq!(m.loc.lloc(), 0));
14584 }
14585}