Skip to content

Commit

Permalink
Merge pull request #62 from codecov/joseph/compute-name
Browse files Browse the repository at this point in the history
feat: compute name takes in network
  • Loading branch information
joseph-sentry authored Dec 20, 2024
2 parents a1636b3 + d430923 commit 996ecb2
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 45 deletions.
139 changes: 133 additions & 6 deletions src/compute_name.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::testrun::Framework;
use pyo3::prelude::*;
use quick_xml::escape::unescape;
use std::borrow::Cow;
use std::{borrow::Cow, collections::HashSet};

fn compute_pytest(classname: &str, name: &str, filename: &str) -> String {
fn compute_pytest_using_filename(classname: &str, name: &str, filename: &str) -> String {
let path_components = filename.split('/').count();

let classname_components = classname.split(".");
Expand All @@ -13,19 +12,53 @@ fn compute_pytest(classname: &str, name: &str, filename: &str) -> String {
.collect::<Vec<_>>()
.join("::");

format!("{}::{}::{}", filename, actual_classname, name)
if actual_classname.is_empty() {
format!("{}::{}", filename, name)
} else {
format!("{}::{}::{}", filename, actual_classname, name)
}
}

fn path_from_classname(classname: &[&str]) -> String {
format!("{}.py", classname.join("/"))
}

fn compute_pytest_using_network(classname: &str, name: &str, network: &HashSet<String>) -> String {
let classname_components = classname.split(".").collect::<Vec<_>>();
let mut path_component_count = 0;
let start = classname_components.len();

while path_component_count < start {
let path = path_from_classname(&classname_components[..start - path_component_count]);
if network.contains(&path) {
if path_component_count > 0 {
let actual_classname = classname_components
.into_iter()
.skip(start - path_component_count)
.collect::<Vec<_>>()
.join("::");
return format!("{}::{}::{}", path, actual_classname, name);
} else {
return format!("{}::{}", path, name);
}
}

path_component_count += 1;
}

format!("{}::{}", classname, name)
}

pub fn unescape_str(s: &str) -> Cow<'_, str> {
unescape(s).unwrap_or(Cow::Borrowed(s))
}

#[pyfunction(signature = (classname, name, framework, filename=None))]
pub fn compute_name(
classname: &str,
name: &str,
framework: Framework,
filename: Option<&str>,
network: Option<&HashSet<String>>,
) -> String {
let name = unescape_str(name);
let classname = unescape_str(classname);
Expand All @@ -35,7 +68,9 @@ pub fn compute_name(
Framework::Jest => name.to_string(),
Framework::Pytest => {
if let Some(filename) = filename {
compute_pytest(&classname, &name, &filename)
compute_pytest_using_filename(&classname, &name, &filename)
} else if let Some(network) = network {
compute_pytest_using_network(&classname, &name, network)
} else {
format!("{}::{}", classname, name)
}
Expand All @@ -48,3 +83,95 @@ pub fn compute_name(
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_compute_name() {
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, None, None),
"a.b.c::d"
);
}

#[test]
fn test_compute_name_with_filename() {
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, Some("a/b/c.py"), None),
"a/b/c.py::d"
);
}

#[test]
fn test_compute_name_with_filename_classname() {
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, Some("a/b.py"), None),
"a/b.py::c::d"
);
}

#[test]
fn test_compute_name_with_network() {
let network = ["a/b/c.py"].iter().map(|e| e.to_string()).collect();
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, None, Some(&network)),
"a/b/c.py::d"
);
}

#[test]
fn test_compute_name_with_network_actual_classname() {
let network = ["a/b.py"].iter().map(|e| e.to_string()).collect();
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, None, Some(&network)),
"a/b.py::c::d"
);
}

#[test]
fn test_compute_name_with_network_actual_classname_no_match() {
let network = ["d.py"].iter().map(|e| e.to_string()).collect();
assert_eq!(
compute_name("a.b.c", "d", Framework::Pytest, None, Some(&network)),
"a.b.c::d"
);
}

#[test]
fn test_compute_name_jest() {
assert_eq!(
compute_name(
"it does the thing &gt; it does the thing",
"it does the thing &gt; it does the thing",
Framework::Jest,
None,
None
),
"it does the thing > it does the thing"
);
}

#[test]
fn test_compute_name_vitest() {
assert_eq!(
compute_name(
"tests/thing.js",
"it does the thing &gt; it does the thing",
Framework::Vitest,
None,
None
),
"tests/thing.js > it does the thing > it does the thing"
);
}

#[test]
fn test_compute_name_phpunit() {
assert_eq!(
compute_name("class.className", "test1", Framework::PHPUnit, None, None),
"class.className::test1"
);
}
}
15 changes: 11 additions & 4 deletions src/junit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use pyo3::prelude::*;
use std::collections::HashSet;

use quick_xml::events::attributes::Attributes;
use quick_xml::events::{BytesStart, Event};
Expand Down Expand Up @@ -52,6 +53,7 @@ fn populate(
testsuite: String,
testsuite_time: Option<&str>,
framework: Option<Framework>,
network: Option<&HashSet<String>>,
) -> PyResult<(Testrun, Option<Framework>)> {
let classname = rel_attrs.classname.unwrap_or_default();

Expand Down Expand Up @@ -79,7 +81,7 @@ fn populate(

let framework = framework.or_else(|| t.framework());
if let Some(f) = framework {
let computed_name = compute_name(&t.classname, &t.name, f, t.filename.as_deref());
let computed_name = compute_name(&t.classname, &t.name, f, t.filename.as_deref(), network);
t.computed_name = Some(computed_name);
};

Expand All @@ -90,12 +92,12 @@ fn populate(
pub fn parse_junit_xml(file_bytes: &[u8]) -> PyResult<ParsingInfo> {
let mut reader = Reader::from_reader(file_bytes);
reader.config_mut().trim_text(true);
let thing = use_reader(&mut reader).map_err(|e| {
let reader_result = use_reader(&mut reader, None).map_err(|e| {
let pos = reader.buffer_position();
let (line, col) = get_position_info(file_bytes, pos.try_into().unwrap());
ParserError::new_err(format!("Error at {}:{}: {}", line, col, e))
})?;
Ok(thing)
Ok(reader_result)
}

pub fn get_position_info(input: &[u8], byte_offset: usize) -> (usize, usize) {
Expand All @@ -114,7 +116,10 @@ pub fn get_position_info(input: &[u8], byte_offset: usize) -> (usize, usize) {
(line, column)
}

fn use_reader(reader: &mut Reader<&[u8]>) -> PyResult<ParsingInfo> {
pub fn use_reader(
reader: &mut Reader<&[u8]>,
network: Option<&HashSet<String>>,
) -> PyResult<ParsingInfo> {
let mut testruns: Vec<Testrun> = Vec::new();
let mut saved_testrun: Option<Testrun> = None;

Expand Down Expand Up @@ -154,6 +159,7 @@ fn use_reader(reader: &mut Reader<&[u8]>) -> PyResult<ParsingInfo> {
.unwrap_or_default(),
testsuite_times.iter().rev().find_map(|e| e.as_deref()),
framework,
network,
)?;
saved_testrun = Some(testrun);
framework = parsed_framework;
Expand Down Expand Up @@ -225,6 +231,7 @@ fn use_reader(reader: &mut Reader<&[u8]>) -> PyResult<ParsingInfo> {
.unwrap_or_default(),
testsuite_times.iter().rev().find_map(|e| e.as_deref()),
framework,
network,
)?;
testruns.push(testrun);
framework = parsed_framework;
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@ fn test_results_parser(py: Python, m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(failure_message::build_message, m)?)?;
m.add_function(wrap_pyfunction!(failure_message::escape_message, m)?)?;
m.add_function(wrap_pyfunction!(failure_message::shorten_file_paths, m)?)?;
m.add_function(wrap_pyfunction!(compute_name::compute_name, m)?)?;
Ok(())
}
34 changes: 0 additions & 34 deletions tests/test_compute_name.py

This file was deleted.

0 comments on commit 996ecb2

Please sign in to comment.