use std::process::Command;
use anyhow::{Context, Result, bail};
use crate::{Replay, ReplayOutput, replay_command};
pub struct AngleTrace {
file: String,
test: String,
name: String,
}
impl AngleTrace {
pub fn new(file: &str, test: &str) -> AngleTrace {
AngleTrace {
file: file.to_owned(),
test: test.to_owned(),
name: format!("angle_trace_tests/{}", test),
}
}
pub fn enumerate(bin: &str) -> Result<Vec<AngleTrace>> {
let mut command = Command::new(bin);
command.arg("--list-tests");
let output = command
.output()
.with_context(|| format!("Executing {:?}", &command))?;
let stdout = String::from_utf8_lossy(&output.stdout);
if !output.status.success() {
println!("Enumerating angle tests failed: {}", &stdout);
bail!("Enumerating angle tests");
}
let tests = parse_gtest_test_list(&stdout);
Ok(tests
.iter()
.map(|test| AngleTrace::new(bin, test))
.collect())
}
}
fn parse_gtest_test_list(output: &str) -> Vec<String> {
output
.lines()
.skip_while(|line| *line != "Tests list:")
.skip(1)
.filter(|x| *x != "End tests list.")
.map(|x| x.to_string())
.collect()
}
fn angle_result_field<'a>(stdout: &'a str, result: &str, suffix: &str) -> Result<&'a str> {
stdout
.lines()
.find(|x| x.contains(result))
.with_context(|| format!("finding {} in {}", result, stdout))?
.split_once("= ")
.with_context(|| format!("finding delimiter for {} in {}", result, stdout))?
.1
.strip_suffix(suffix)
.with_context(|| format!("stripping suffix for {} in {}", suffix, stdout))
}
fn angle_output_fps(stdout: &str) -> Result<f64> {
let wall_time = angle_result_field(stdout, "wall_time", " ms")?;
let wall_time =
str::parse::<f64>(wall_time).with_context(|| format!("parsing wall time {}", wall_time))?;
Ok(1000.0 / wall_time)
}
impl Replay for AngleTrace {
fn replay(&self, wrapper: Option<&str>, envs: &[(String, String)]) -> Result<ReplayOutput> {
let angle_command = [
&self.file,
"--no-warmup",
"--trials=1",
"--fixed-test-time=1",
"--use-gl=native",
&format!("--gtest_filter={}", &self.test),
];
let mut command = replay_command(&angle_command, wrapper, envs);
let output = command.output().expect("failed to `trace");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr).unwrap();
println!("Failed to start angle_trace_tests:");
let stdout = std::str::from_utf8(&output.stdout).unwrap();
println!("{}", if stderr.len() > 2 { stderr } else { stdout });
println!("command: {:?}", command);
if stdout.contains("Could not load trace.") {
println!("Is your build synced with the trace sources you have downloaded?");
}
bail!("Failed to start angle_trace_tests");
}
Ok(ReplayOutput::from(output))
}
fn fps(&self, output: &ReplayOutput) -> Result<f64> {
angle_output_fps(&output.stdout)
}
fn name(&self) -> &str {
&self.name
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_parse_gtest_test_list() {
let list = "
1 GPUs:
0 - AMD device id: 0x150E, revision id: 0xD3, system device id: 0x0
Active GPU: 0
Optimus: false
AMD Switchable: false
Mac Switchable: false
Needs EAGL on Mac: false
Tests list:
TraceTest.1945_air_force
TraceTest.20_minutes_till_dawn
TraceTest.age_of_origins_z
End tests list.
";
assert_eq!(
parse_gtest_test_list(list),
vec![
"TraceTest.1945_air_force".to_string(),
"TraceTest.20_minutes_till_dawn".to_string(),
"TraceTest.age_of_origins_z".to_string(),
]
);
}
#[test]
fn test_fps() {
let stdout = r#"
1 GPUs:
0 - AMD device id: 0x150E, revision id: 0xD3, system device id: 0x0
Active GPU: 0
Optimus: false
AMD Switchable: false
Mac Switchable: false
Needs EAGL on Mac: false
Note: Google Test filter = TraceTest.1945_air_force
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TraceTest
[ RUN ] TraceTest.1945_air_force
Warning: setpriority failed in StabilizeCPUForBenchmarking. Process will retain default priority: Permission denied
running test name: "TracePerf", backend: "_native", story: "1945_air_force"
*RESULT TracePerf_native.cpu_time: 1945_air_force= 1.3413200000 ms
*RESULT TracePerf_native.wall_time: 1945_air_force= 1.2631850701 ms
RESULT TracePerf_native.trial_steps: 1945_air_force= 200 count
RESULT TracePerf_native.total_steps: 1945_air_force= 200 count
*RESULT TracePerf_native.memory_median: 1945_air_force= 379936000 sizeInBytes
*RESULT TracePerf_native.memory_max: 1945_air_force= 382492000 sizeInBytes
[ OK ] TraceTest.1945_air_force (1059 ms)
[----------] 1 test from TraceTest (1059 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1059 ms total)
[ PASSED ] 1 test.
"#;
assert_approx_eq!(angle_output_fps(stdout).unwrap(), 1000.0 / 1.2631850701);
}
}