gtest_runner_lib/
parser.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use fidl_fuchsia_io as fio;
7use serde::{Deserialize, Serialize};
8use test_runners_lib::cases::TestCaseInfo;
9use test_runners_lib::errors::*;
10
11/// In `gtest_list_test` output, provides info about individual test cases.
12/// Example: For test FOO.Bar, this contains info about Bar.
13/// Please refer to documentation of `ListTestResult` for details.
14#[derive(Serialize, Deserialize, Debug)]
15struct IndividualTestInfo {
16    pub name: String,
17    pub file: String,
18    pub line: u64,
19}
20
21/// In `gtest_list_test` output, provides info about individual test suites.
22/// Example: For test FOO.Bar, this contains info about FOO.
23/// Please refer to documentation of `ListTestResult` for details.
24#[derive(Serialize, Deserialize, Debug)]
25struct TestSuiteResult {
26    pub tests: usize,
27    pub name: String,
28    pub testsuite: Vec<IndividualTestInfo>,
29}
30
31/// Structure of the output of `<test binary> --gtest_list_test`.
32///
33/// Sample json will look like
34/// ```
35/// {
36/// "tests": 6,
37/// "name": "AllTests",
38/// "testsuites": [
39///    {
40///      "name": "SampleTest1",
41///      "tests": 2,
42///      "testsuite": [
43///        {
44///          "name": "Test1",
45///          "file": "../../src/sys/test_runners/gtest/test_data/sample_tests.cc",
46///          "line": 7
47///        },
48///        {
49///          "name": "Test2",
50///          "file": "../../src/sys/test_runners/gtest/test_data/sample_tests.cc",
51///          "line": 9
52///        }
53///      ]
54///    },
55///  ]
56///}
57///```
58#[derive(Serialize, Deserialize, Debug)]
59struct ListTestResult {
60    pub tests: usize,
61    pub name: String,
62    pub testsuites: Vec<TestSuiteResult>,
63}
64
65/// Provides info about test case failure if any.
66#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
67pub struct Failure {
68    pub failure: String,
69}
70
71/// Provides info about individual test executions.
72/// Example: For test FOO.Bar, this contains info about Bar.
73/// Please refer to documentation of `TestOutput` for details.
74#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
75pub struct IndividualTestOutput {
76    pub name: String,
77    pub status: IndividualTestOutputStatus,
78    pub time: String,
79    pub failures: Option<Vec<Failure>>,
80    /// This field is not documented, so using String. We can use serde_enum_str to convert it to
81    /// enum, but that is not in our third party crates.
82    /// Most common values seen in the output are COMPLETED, SKIPPED, SUPPRESSED
83    pub result: String,
84}
85
86/// Describes whether a test was run or skipped.
87///
88/// Refer to [`TestSuiteOutput`] documentation for schema details.
89#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
90#[serde(rename_all = "UPPERCASE")]
91pub enum IndividualTestOutputStatus {
92    #[default]
93    Run,
94    NotRun,
95}
96
97/// Provides info about individual test suites.
98/// Refer to [gtest documentation] for output structure.
99/// [gtest documentation]: https://github.com/google/googletest/blob/2002f267f05be6f41a3d458954414ba2bfa3ff1d/googletest/docs/advanced.md#generating-a-json-report
100#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
101pub struct TestSuiteOutput {
102    pub name: String,
103    pub tests: usize,
104    pub failures: usize,
105    pub disabled: usize,
106    pub time: String,
107    pub testsuite: Vec<IndividualTestOutput>,
108}
109
110/// Provides info test and the its run result.
111/// Example: For test FOO.Bar, this contains info about FOO.
112/// Please refer to documentation of `TestSuiteOutput` for details.
113#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
114pub struct TestOutput {
115    pub testsuites: Vec<TestSuiteOutput>,
116}
117
118/// Opens and reads file defined by `path` in `dir`.
119pub async fn read_file(dir: &fio::DirectoryProxy, path: &str) -> Result<String, Error> {
120    // Open the file in read-only mode.
121    let result_file_proxy = fuchsia_fs::directory::open_file(dir, path, fio::PERM_READABLE).await?;
122    return fuchsia_fs::file::read_to_string(&result_file_proxy).await.map_err(Into::into);
123}
124
125pub fn parse_test_cases(test_string: String) -> Result<Vec<TestCaseInfo>, EnumerationError> {
126    let test_list: ListTestResult =
127        serde_json::from_str(&test_string).map_err(EnumerationError::from)?;
128
129    let mut tests = Vec::<TestCaseInfo>::with_capacity(test_list.tests);
130
131    for suite in &test_list.testsuites {
132        for test in &suite.testsuite {
133            let name = format!("{}.{}", suite.name, test.name);
134            let enabled = is_test_case_enabled(&name);
135            tests.push(TestCaseInfo { name, enabled })
136        }
137    }
138
139    Ok(tests)
140}
141
142/// Returns `true` if the test case is disabled, based on its name. (This is apparently the only
143/// way that gtest tests can be disabled.)
144/// See
145/// https://github.com/google/googletest/blob/HEAD/googletest/docs/advanced.md#temporarily-disabling-tests
146fn is_test_case_enabled(case_name: &str) -> bool {
147    !case_name.contains("DISABLED_")
148}