Skip to main content

deqp_runner/
mock_deqp.rs

1use anyhow::{Context, Result};
2use rand::Rng;
3use std::io::{BufRead, BufReader, BufWriter, Write};
4use std::{fs::File, path::PathBuf};
5use structopt::StructOpt;
6
7/// Mock deqp that uses conventions in the test name to control behavior of the
8/// test.  We use this for integration testing of deqp-runner.
9
10#[derive(Debug, StructOpt)]
11pub struct MockDeqp {
12    #[structopt(long, help = "Path to the caselist file")]
13    deqp_caselist_file: Option<PathBuf>,
14
15    #[structopt(long, default_value = "", help = "individual deqp case")]
16    deqp_case: String,
17
18    #[structopt(long, help = "Path to the QPA log output")]
19    deqp_log_filename: PathBuf,
20
21    #[structopt(long)]
22    #[allow(dead_code)]
23    deqp_log_flush: bool,
24
25    #[structopt(long, default_value = "", help = "Path to the .shader_cache output")]
26    #[allow(dead_code)]
27    deqp_shadercache_filename: PathBuf,
28
29    #[structopt(long)]
30    #[allow(dead_code)]
31    deqp_shadercache_truncate: bool,
32
33    #[structopt(long)]
34    all_pass: bool,
35}
36
37pub fn mock_deqp(mock: &MockDeqp) -> Result<()> {
38    let tests = if let Some(path) = &mock.deqp_caselist_file {
39        let file =
40            File::open(path).with_context(|| format!("Opening {:?}", &mock.deqp_caselist_file))?;
41
42        BufReader::new(file)
43            .lines()
44            .collect::<std::result::Result<Vec<String>, std::io::Error>>()
45            .context("reading test caselist")?
46    } else {
47        mock.deqp_case.split(',').map(|x| x.to_string()).collect()
48    };
49
50    let qpa = File::create(&mock.deqp_log_filename).context("Creating QPA file")?;
51    let mut qpa_writer = BufWriter::new(&qpa);
52
53    // Set up our QPA file before starting to write tests.  We use a snapshot of
54    let qpa_header = if tests[0].contains("dEQP-GLES2") {
55        r#"
56#sessionInfo releaseName git-39e5966401d69eba352d71404827230b90d3063b
57#sessionInfo releaseId 0x39e59664
58#sessionInfo targetName "Surfaceless"
59#sessionInfo vendor "Mesa/X.org"
60#sessionInfo renderer "llvmpipe (LLVM 11.0.1, 256 bits)"
61#sessionInfo commandLineParameters "--deqp-log-images=enable --deqp-log-filename=/home/anholt/TestResults.qpa --deqp-surface-type=pbuffer --deqp-surface-width=256 --deqp-surface-height=256 --deqp-gl-config-name=rgba8888d24s8ms0 --deqp-case=dEQP-GLES2.info.version"
62
63#beginSession
64"#
65    } else {
66        r#"
67#sessionInfo releaseName git-39e5966401d69eba352d71404827230b90d3063b
68#sessionInfo releaseId 0x39e59664
69#sessionInfo targetName "Surfaceless"
70#sessionInfo vendorID 0x1002
71#sessionInfo deviceID 0x687f
72#sessionInfo commandLineParameters "--deqp-log-images=enable --deqp-log-filename=/home/anholt/TestResults.qpa --deqp-surface-type=pbuffer --deqp-surface-width=256 --deqp-surface-height=256 --deqp-gl-config-name=rgba8888d24s8ms0 --deqp-case=dEQP-VK.info.device"
73
74#beginSession
75"#
76    };
77    qpa_writer
78        .write(qpa_header.as_bytes())
79        .context("writing QPA header")?;
80
81    for test_name in tests {
82        // Missing tests won't appear in the output at all.
83        if test_name.contains("dEQP-GLES2.test.m.")
84            || test_name.contains("KHR-Single-GLES31.info.renderer")
85            || test_name.contains("KHR-Single-GLES31.info.version")
86            || test_name.contains("KHR-Single-GLES31.info.extensions")
87        {
88            continue;
89        }
90
91        println!("Test case '{}'..", test_name);
92        writeln!(qpa_writer, "#beginTestCaseResult {}", test_name)
93            .context("writing QPA test start")?;
94
95        if test_name.contains(".timeout.") {
96            // Simulate a testcase that doesn't return in time by infinite
97            // looping.
98            #[allow(clippy::empty_loop)]
99            loop {}
100        }
101
102        if test_name.contains(".p.") || mock.all_pass {
103            println!("  Pass (success case)");
104        } else if test_name.contains(".f.") {
105            println!("  Fail (failure case)");
106        } else if test_name.contains(".flaky") {
107            if rand::thread_rng().gen::<bool>() {
108                println!("  Fail (failure case)");
109            } else {
110                println!("  Pass (success)");
111            }
112        } else if test_name.contains(".s.") {
113            println!("  NotSupported (skip case)");
114        } else if test_name.contains(".c.") {
115            // In a crash, the output just stops before we get a result and
116            // the process returns an error code.  parse_deqp_results() just
117            // handles the deqp output unexpectedly ending as a crash.
118            break;
119        } else if test_name == "dEQP-GLES2.info.renderer" {
120            qpa_writer
121                .write(include_bytes!("test_data/deqp-gles2-renderer.xml"))
122                .context("writing QPA XML")?;
123            println!("  Pass (success case)");
124        } else if test_name == "dEQP-GLES2.info.version" {
125            qpa_writer
126                .write(include_bytes!("test_data/deqp-gles2-version.xml"))
127                .context("writing QPA XML")?;
128            println!("  Pass (success case)");
129        } else if test_name == "dEQP-GLES2.info.extensions" {
130            qpa_writer
131                .write(include_bytes!("test_data/deqp-gles2-extensions.xml"))
132                .context("writing QPA XML")?;
133            println!("  Pass (success case)");
134        } else if test_name == "dEQP-VK.info.device" {
135            qpa_writer
136                .write(include_bytes!("test_data/deqp-vk-info-device.xml"))
137                .context("writing QPA XML")?;
138            println!("  Pass (success case)");
139        } else {
140            unimplemented!("unknown mock test name {}", test_name)
141        }
142
143        writeln!(qpa_writer, "\n#endTestCaseResult").context("writing QPA test end")?;
144    }
145
146    writeln!(
147        qpa_writer,
148        r#"
149#endTestsCasesTime
150
151#endSession
152"#
153    )
154    .context("writing QPA footer")?;
155
156    Ok(())
157}