use crate::test_parser::{ParsedTest, TestStatus};
use crate::{ParsedTestSuite, TestParser};
use serde;
use serde::{Deserialize, Serialize};
use serde_xml_rs::Error;
use std::time::Duration;
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "valgrindoutput")]
pub(crate) struct ValgrindOutput {
pub(crate) preamble: Vec<ValgrindPreamble>,
pub(crate) pid: usize,
#[serde(rename = "ppid")]
pub(crate) parent_pid: usize,
pub(crate) tool: String, #[serde(rename = "protocolversion")]
pub(crate) protocol_version: usize,
#[serde(rename = "protocoltool")]
pub(crate) protocol_tool: String,
#[serde(rename = "args")]
pub(crate) args: Option<ValgrindArgs>,
#[serde(rename = "status")]
pub(crate) status: Option<Vec<ValgrindStatus>>,
#[serde(rename = "error")]
pub(crate) errors: Option<Vec<ValgrindError>>,
}
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "preamble")]
pub(crate) struct ValgrindPreamble {
#[serde(rename = "line")]
pub(crate) lines: Vec<String>,
}
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "args")]
pub(crate) struct ValgrindArgs {
#[serde(rename = "vargv")]
pub(crate) vargv: Argv,
#[serde(rename = "argv")]
pub(crate) argv: Argv,
}
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "status")]
pub(crate) struct ValgrindStatus {
pub(crate) state: String,
pub(crate) time: String,
}
#[derive(Deserialize, Debug, PartialEq)]
pub(crate) struct Argv {
pub(crate) exe: String,
pub(crate) arg: Option<Vec<String>>,
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename = "error")]
pub(crate) struct ValgrindError {
pub(crate) unique: String,
pub(crate) tid: String,
pub(crate) kind: String,
#[serde(rename = "what")]
pub(crate) what: Option<String>,
#[serde(rename = "xwhat")]
pub(crate) x_what: Option<XWhat>,
#[serde(rename = "stack")]
pub(crate) stack: Option<ValgrindStack>,
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename = "xwhat")]
pub(crate) struct XWhat {
pub(crate) text: String,
#[serde(rename = "leakedbytes")]
pub(crate) leaked_bytes: usize,
#[serde(rename = "leakedblocks")]
pub(crate) leaked_blocks: usize,
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename = "stack")]
pub(crate) struct ValgrindStack {
#[serde(rename = "frame")]
pub(crate) frames: Vec<ValgrindStackFrame>,
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
pub(crate) struct ValgrindStackFrame {
pub(crate) ip: String,
pub(crate) obj: Option<String>,
#[serde(rename = "fn")]
pub(crate) function: Option<String>,
pub(crate) dir: Option<String>,
pub(crate) file: Option<String>,
pub(crate) line: Option<usize>,
}
pub(crate) struct ValgrindTestParser {}
impl ValgrindTestParser {
pub(crate) fn new() -> Self {
Self {}
}
}
impl TestParser for ValgrindTestParser {
type Error = Error;
fn multi_line(&self) -> bool {
false
}
fn parse(&mut self, text: &str) -> Result<Option<ParsedTestSuite>, Error> {
let valgrind: ValgrindOutput = serde_xml_rs::from_str(text)?;
let finish = valgrind
.status
.map(|v| {
v.iter()
.find(|s| s.state.to_lowercase() == "finished")
.map(|s| parse_duration(&s.time))
})
.flatten()
.flatten();
let tests = valgrind
.errors
.map(|v| {
v.iter()
.map(|e| {
let pretty_out = serde_json::to_string_pretty(e).ok();
let name = e
.what
.as_ref()
.map(|w| w.to_string())
.or(e.x_what.as_ref().map(|x| x.text.clone()))
.unwrap_or_default();
ParsedTest {
full_name: format!("{} ({})", name, e.kind),
name,
module: Some(format!("{}", e.kind)),
exec_time: None,
status: TestStatus::Failed,
std_out: pretty_out,
}
})
.collect::<Vec<_>>()
})
.unwrap_or(vec![]);
Ok(Some(ParsedTestSuite {
suite_name: valgrind.tool.clone(),
test_count: tests.len(),
passed: 0,
failed: tests.len(),
errors: 0,
allowed_fail: 0,
ignored: 0,
measured: 0,
filtered_out: 0,
exec_time: finish.map(|d| d.as_secs_f64()).unwrap_or(0.0f64),
tests,
}))
}
fn reset(&mut self) {}
}
fn parse_duration(duration: &str) -> Option<Duration> {
let parts = duration.split(":").take(4).collect::<Vec<&str>>();
if parts.len() < 4 {
None
} else {
let days = parts[0].parse::<u64>().ok()?;
let hours = parts[1].parse::<u64>().ok()?;
let mins = parts[2].parse::<u64>().ok()?;
let (s, ms) = parts[3].split_once(".")?;
let seconds = s.parse::<u64>().ok()?;
let ms = ms.parse::<u32>().ok()?;
Some(Duration::new(
(days * 24 * 60 * 60) + (hours * 60 * 60) + (mins * 60) + seconds,
ms * 1000000,
))
}
}