Skip to main content

speccoder_runtime/collection/
junit_map.rs

1//! Test ids (a JUnit report's `classname::name`, a listing's lines) mapped
2//! back to the file they come from, per toolchain.
3
4use std::collections::BTreeMap;
5
6use speccoder_core::collection::{go_id_matches, python_id_matches, rust_tests_by_file};
7
8/// Where a judged file sits, for matching ids to it.
9#[derive(Debug, Clone)]
10pub enum Place {
11    /// A Rust file: the reachability map of its package.
12    Rust(BTreeMap<String, Vec<(String, String)>>),
13    /// A pytest file.
14    Python,
15    /// A JS/TS file, relative to its package root.
16    Js(String),
17    /// A Go file: its package's import path and the tests it declares.
18    Go(String, Vec<String>),
19}
20
21/// The ids of `ids` that come from `path`.
22#[must_use]
23pub fn tests_of(place: &Place, path: &str, ids: &[String]) -> Vec<String> {
24    match place {
25        Place::Rust(reach) => rust_tests_by_file(reach, ids)
26            .remove(path)
27            .unwrap_or_default(),
28        Place::Python => ids
29            .iter()
30            .filter(|id| python_id_matches(path, id))
31            .cloned()
32            .collect(),
33        Place::Js(rel) => ids
34            .iter()
35            .filter(|id| {
36                [path, rel.as_str()].iter().any(|p| {
37                    *id == p
38                        || id.starts_with(&format!("{p}::"))
39                        || id.starts_with(&format!("{p} "))
40                })
41            })
42            .cloned()
43            .collect(),
44        Place::Go(import, names) => ids
45            .iter()
46            .filter(|id| go_id_matches(import, names, id))
47            .cloned()
48            .collect(),
49    }
50}
51
52/// A configured listing's lines that name `path` (as a file, an id
53/// prefixed by it, or by the toolchain's own id form).
54#[must_use]
55pub fn listed(place: &Place, path: &str, lines: &[String]) -> Vec<String> {
56    let mut out: Vec<String> = lines
57        .iter()
58        .filter(|l| *l == path || l.starts_with(&format!("{path}::")))
59        .cloned()
60        .collect();
61    if out.is_empty() {
62        out = tests_of(place, path, lines);
63    }
64    out
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn ids_map_per_toolchain() {
73        let ids = |v: &[&str]| v.iter().map(|s| (*s).to_owned()).collect::<Vec<_>>();
74        assert_eq!(
75            tests_of(
76                &Place::Js("src/a.test.ts".into()),
77                "web/src/a.test.ts",
78                &ids(&["src/a.test.ts::greets", "src/b.test.ts::x"])
79            ),
80            ["src/a.test.ts::greets"]
81        );
82        assert_eq!(
83            tests_of(
84                &Place::Go("ex.com/m/g".into(), vec!["TestA".into()]),
85                "g/a_test.go",
86                &ids(&["ex.com/m/g::TestA", "ex.com/m/g::TestB"])
87            ),
88            ["ex.com/m/g::TestA"]
89        );
90        assert_eq!(
91            listed(
92                &Place::Python,
93                "tests/test_a.py",
94                &ids(&["tests/test_a.py", "tests/test_b.py"])
95            ),
96            ["tests/test_a.py"]
97        );
98    }
99}