blob: d741b3f20590b1963119def6a3e2c260c92fabb4 [file] [log] [blame]
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Representation of a TEST_MAPPING JSON file.
use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::TestMappingError;
#[derive(Serialize, Deserialize, Default, Debug)]
pub(crate) struct TestMappingJson {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub imports: Vec<TestMappingPath>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
presubmit: Vec<TestMappingName>,
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "presubmit-rust")]
presubmit_rust: Vec<TestMappingName>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
postsubmit: Vec<TestMappingName>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct TestMappingName {
name: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct TestMappingPath {
pub path: String,
}
impl TestMappingJson {
pub fn parse(mut contents: String) -> Result<TestMappingJson, TestMappingError> {
let contents = contents.as_mut_str();
// Comments are not part of the JSON spec (although they are often used), and Serde won't parse them.
json_strip_comments::strip(contents).map_err(TestMappingError::StripJsonCommentsError)?;
let parsed: TestMappingJson = serde_json::from_str(contents)?;
Ok(parsed)
}
pub fn is_empty(&self) -> bool {
self.imports.is_empty()
&& self.presubmit.is_empty()
&& self.presubmit_rust.is_empty()
&& self.postsubmit.is_empty()
}
pub fn remove_unknown_tests(&mut self, tests: &BTreeSet<String>) -> bool {
let mut changed = false;
if self.presubmit.iter().any(|t| !tests.contains(&t.name)) {
self.presubmit.retain(|t| tests.contains(&t.name));
changed = true;
}
if self.presubmit_rust.iter().any(|t| !tests.contains(&t.name)) {
self.presubmit_rust.retain(|t| tests.contains(&t.name));
changed = true;
}
if self.postsubmit.iter().any(|t| !tests.contains(&t.name)) {
self.postsubmit.retain(|t| tests.contains(&t.name));
changed = true;
}
changed
}
pub fn set_presubmits(&mut self, tests: &BTreeSet<String>) {
self.presubmit = tests.iter().map(|t| TestMappingName { name: t.to_string() }).collect();
self.presubmit_rust = self.presubmit.clone();
}
pub fn convert_postsubmit_tests(&mut self) -> bool {
let changed = !self.postsubmit.is_empty();
self.presubmit.append(&mut self.postsubmit.clone());
self.presubmit_rust.append(&mut self.postsubmit);
self.presubmit.sort();
self.presubmit.dedup();
self.presubmit_rust.sort();
self.presubmit_rust.dedup();
changed
}
pub fn add_new_tests_to_postsubmit(&mut self, tests: &BTreeSet<String>) -> bool {
let mut changed = false;
self.postsubmit.extend(tests.difference(&self.all_test_names()).map(|test_name| {
changed = true;
TestMappingName { name: test_name.to_string() }
}));
self.postsubmit.sort();
self.postsubmit.dedup();
changed
}
fn all_test_names(&self) -> BTreeSet<String> {
let mut tests = BTreeSet::new();
tests.extend(self.presubmit.iter().map(|t| t.name.clone()));
tests.extend(self.presubmit_rust.iter().map(|t| t.name.clone()));
tests.extend(self.postsubmit.iter().map(|t| t.name.clone()));
tests
}
}
#[cfg(test)]
mod tests {
use super::*;
static TEST_JSON: &str = r###"{
"imports": [
{
"path": "foo"
}
],
"presubmit": [
{
"name": "bar"
}
],
"presubmit-rust": [
{
"name": "baz"
}
],
"postsubmit": [
{
"name": "qux"
}
]
}
"###;
#[test]
fn parse() -> Result<(), TestMappingError> {
TestMappingJson::parse("{}".to_string())?;
TestMappingJson::parse("//comment\n{}".to_string())?;
TestMappingJson::parse(TEST_JSON.to_string())?;
assert!(TestMappingJson::parse("foo".to_string()).is_err());
Ok(())
}
#[test]
fn all_test_names() -> Result<(), TestMappingError> {
let json = TestMappingJson::parse(TEST_JSON.to_string())?;
assert_eq!(
json.all_test_names(),
BTreeSet::from(["bar".to_string(), "baz".to_string(), "qux".to_string()])
);
Ok(())
}
#[test]
fn set_presubmits() -> Result<(), TestMappingError> {
let mut json = TestMappingJson::parse(TEST_JSON.to_string())?;
json.set_presubmits(&BTreeSet::from(["asdf".to_string()]));
assert_eq!(json.presubmit, vec![TestMappingName { name: "asdf".to_string() }]);
assert_eq!(json.presubmit, json.presubmit_rust);
Ok(())
}
#[test]
fn add_new_tests_to_postsubmit() -> Result<(), TestMappingError> {
let mut json = TestMappingJson::parse(TEST_JSON.to_string())?;
assert!(!json.add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string()])));
assert!(json
.add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string(), "asdf".to_string()])));
assert_eq!(
json.postsubmit,
vec![
TestMappingName { name: "asdf".to_string() },
TestMappingName { name: "qux".to_string() }
]
);
Ok(())
}
#[test]
fn is_empty() -> Result<(), TestMappingError> {
let json = TestMappingJson::parse(TEST_JSON.to_string())?;
assert!(!json.is_empty());
assert!(TestMappingJson::default().is_empty());
Ok(())
}
}