istanbul_oxide/
coverage_map.rs1use indexmap::IndexMap;
2
3use crate::{CoverageSummary, FileCoverage};
4
5#[derive(Clone, PartialEq, Default)]
7pub struct CoverageMap {
8 inner: IndexMap<String, FileCoverage>,
9}
10
11impl CoverageMap {
12 pub fn new() -> CoverageMap {
13 CoverageMap {
14 inner: Default::default(),
15 }
16 }
17
18 pub fn default() -> CoverageMap {
19 CoverageMap {
20 inner: Default::default(),
21 }
22 }
23
24 pub fn from_iter<'a>(value: impl IntoIterator<Item = &'a FileCoverage>) -> CoverageMap {
25 let mut ret = CoverageMap {
26 inner: Default::default(),
27 };
28
29 for coverage in value.into_iter() {
30 ret.add_coverage_for_file(coverage)
31 }
32
33 ret
34 }
35
36 pub fn merge(&mut self, map: &CoverageMap) {
38 for (_, coverage) in map.inner.iter() {
39 self.add_coverage_for_file(coverage);
40 }
41 }
42
43 pub fn filter(&mut self, predicate: impl Fn(&FileCoverage) -> bool) {
46 let mut filtered: IndexMap<String, FileCoverage> = Default::default();
47
48 for (_, coverage) in self.inner.drain(..) {
49 if predicate(&coverage) {
50 filtered.insert(coverage.path.clone(), coverage);
51 }
52 }
53
54 self.inner = filtered;
55 }
56
57 pub fn to_json() {
58 unimplemented!()
59 }
60
61 pub fn get_files(&self) -> Vec<&String> {
62 self.inner.keys().collect()
63 }
64
65 pub fn get_coverage_for_file(&self, file_path: &str) -> Option<&FileCoverage> {
66 self.inner.get(file_path)
67 }
68
69 pub fn add_coverage_for_file(&mut self, coverage: &FileCoverage) {
70 if let Some(value) = self.inner.get_mut(coverage.path.as_str()) {
71 value.merge(coverage);
72 } else {
73 self.inner.insert(coverage.path.clone(), coverage.clone());
74 }
75 }
76
77 pub fn get_coverage_summary(&self) -> CoverageSummary {
78 let mut ret: CoverageSummary = Default::default();
79
80 for coverage in self.inner.values() {
81 ret.merge(&coverage.to_summary());
82 }
83
84 ret
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::{CoverageMap, FileCoverage};
91
92 #[test]
93 fn should_able_to_merge_another_coverage_map() {
94 let mut base = CoverageMap::from_iter(vec![
95 &FileCoverage::from_file_path("foo.js".to_string(), false),
96 &FileCoverage::from_file_path("bar.js".to_string(), false),
97 ]);
98
99 let second = CoverageMap::from_iter(vec![
100 &FileCoverage::from_file_path("foo.js".to_string(), false),
101 &FileCoverage::from_file_path("baz.js".to_string(), false),
102 ]);
103 base.merge(&second);
104 assert_eq!(
105 base.get_files(),
106 vec![
107 &"foo.js".to_string(),
108 &"bar.js".to_string(),
109 &"baz.js".to_string()
110 ]
111 );
112 }
113
114 #[test]
115 fn should_able_to_return_file_coverage() {
116 let base = CoverageMap::from_iter(vec![
117 &FileCoverage::from_file_path("foo.js".to_string(), false),
118 &FileCoverage::from_file_path("bar.js".to_string(), false),
119 ]);
120
121 assert!(base.get_coverage_for_file("foo.js").is_some());
122 assert!(base.get_coverage_for_file("bar.js").is_some());
123
124 assert!(base.get_coverage_for_file("baz.js").is_none());
125 }
126
127 #[test]
128 fn should_able_to_filter_coverage() {
129 let mut base = CoverageMap::from_iter(vec![
130 &FileCoverage::from_file_path("foo.js".to_string(), false),
131 &FileCoverage::from_file_path("bar.js".to_string(), false),
132 ]);
133
134 assert_eq!(
135 base.get_files(),
136 vec![&"foo.js".to_string(), &"bar.js".to_string()]
137 );
138
139 base.filter(|x| x.path == "foo.js");
140 assert_eq!(base.get_files(), vec![&"foo.js".to_string()]);
141 }
142
143 #[test]
144 fn should_return_coverage_summary_for_all_files() {
145 let mut base = CoverageMap::from_iter(vec![
146 &FileCoverage::from_file_path("foo.js".to_string(), false),
147 &FileCoverage::from_file_path("bar.js".to_string(), false),
148 ]);
149
150 base.add_coverage_for_file(&FileCoverage::from_file_path("foo.js".to_string(), false));
151 base.add_coverage_for_file(&FileCoverage::from_file_path("baz.js".to_string(), false));
152
153 let summary = base.get_coverage_summary();
154 assert_eq!(summary.statements.total, 0);
155 }
156}