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}