1 // Copyright (C) 2024 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Representation of a TEST_MAPPING JSON file. 16 17 use std::collections::BTreeSet; 18 19 use serde::{Deserialize, Serialize}; 20 21 use crate::Error; 22 23 #[derive(Serialize, Deserialize, Default, Debug)] 24 pub(crate) struct TestMappingJson { 25 #[serde(default, skip_serializing_if = "Vec::is_empty")] 26 pub imports: Vec<TestMappingPath>, 27 #[serde(default, skip_serializing_if = "Vec::is_empty")] 28 presubmit: Vec<TestMappingName>, 29 #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "presubmit-rust")] 30 presubmit_rust: Vec<TestMappingName>, 31 #[serde(default, skip_serializing_if = "Vec::is_empty")] 32 postsubmit: Vec<TestMappingName>, 33 } 34 35 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 36 struct TestMappingName { 37 name: String, 38 } 39 40 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 41 pub(crate) struct TestMappingPath { 42 pub path: String, 43 } 44 45 impl TestMappingJson { parse(mut contents: String) -> Result<TestMappingJson, Error>46 pub fn parse(mut contents: String) -> Result<TestMappingJson, Error> { 47 let contents = contents.as_mut_str(); 48 // Comments are not part of the JSON spec (although they are often used), and Serde won't parse them. 49 json_strip_comments::strip(contents).map_err(Error::StripJsonCommentsError)?; 50 let parsed: TestMappingJson = serde_json::from_str(contents)?; 51 Ok(parsed) 52 } is_empty(&self) -> bool53 pub fn is_empty(&self) -> bool { 54 self.imports.is_empty() 55 && self.presubmit.is_empty() 56 && self.presubmit_rust.is_empty() 57 && self.postsubmit.is_empty() 58 } remove_unknown_tests(&mut self, tests: &BTreeSet<String>) -> bool59 pub fn remove_unknown_tests(&mut self, tests: &BTreeSet<String>) -> bool { 60 let mut changed = false; 61 if self.presubmit.iter().any(|t| !tests.contains(&t.name)) { 62 self.presubmit.retain(|t| tests.contains(&t.name)); 63 changed = true; 64 } 65 if self.presubmit_rust.iter().any(|t| !tests.contains(&t.name)) { 66 self.presubmit_rust.retain(|t| tests.contains(&t.name)); 67 changed = true; 68 } 69 if self.postsubmit.iter().any(|t| !tests.contains(&t.name)) { 70 self.postsubmit.retain(|t| tests.contains(&t.name)); 71 changed = true; 72 } 73 changed 74 } set_presubmits(&mut self, tests: &BTreeSet<String>)75 pub fn set_presubmits(&mut self, tests: &BTreeSet<String>) { 76 self.presubmit = tests.iter().map(|t| TestMappingName { name: t.to_string() }).collect(); 77 self.presubmit_rust = self.presubmit.clone(); 78 } convert_postsubmit_tests(&mut self) -> bool79 pub fn convert_postsubmit_tests(&mut self) -> bool { 80 let changed = !self.postsubmit.is_empty(); 81 self.presubmit.append(&mut self.postsubmit.clone()); 82 self.presubmit_rust.append(&mut self.postsubmit); 83 self.presubmit.sort(); 84 self.presubmit.dedup(); 85 self.presubmit_rust.sort(); 86 self.presubmit_rust.dedup(); 87 changed 88 } add_new_tests_to_postsubmit(&mut self, tests: &BTreeSet<String>) -> bool89 pub fn add_new_tests_to_postsubmit(&mut self, tests: &BTreeSet<String>) -> bool { 90 let mut changed = false; 91 self.postsubmit.extend(tests.difference(&self.all_test_names()).map(|test_name| { 92 changed = true; 93 TestMappingName { name: test_name.to_string() } 94 })); 95 self.postsubmit.sort(); 96 self.postsubmit.dedup(); 97 changed 98 } all_test_names(&self) -> BTreeSet<String>99 fn all_test_names(&self) -> BTreeSet<String> { 100 let mut tests = BTreeSet::new(); 101 tests.extend(self.presubmit.iter().map(|t| t.name.clone())); 102 tests.extend(self.presubmit_rust.iter().map(|t| t.name.clone())); 103 tests.extend(self.postsubmit.iter().map(|t| t.name.clone())); 104 tests 105 } 106 } 107 108 #[cfg(test)] 109 mod tests { 110 use super::*; 111 112 static TEST_JSON: &str = r###"{ 113 "imports": [ 114 { 115 "path": "foo" 116 } 117 ], 118 "presubmit": [ 119 { 120 "name": "bar" 121 } 122 ], 123 "presubmit-rust": [ 124 { 125 "name": "baz" 126 } 127 ], 128 "postsubmit": [ 129 { 130 "name": "qux" 131 } 132 ] 133 } 134 "###; 135 136 #[test] parse() -> Result<(), Error>137 fn parse() -> Result<(), Error> { 138 TestMappingJson::parse("{}".to_string())?; 139 TestMappingJson::parse("//comment\n{}".to_string())?; 140 TestMappingJson::parse(TEST_JSON.to_string())?; 141 assert!(TestMappingJson::parse("foo".to_string()).is_err()); 142 Ok(()) 143 } 144 145 #[test] all_test_names() -> Result<(), Error>146 fn all_test_names() -> Result<(), Error> { 147 let json = TestMappingJson::parse(TEST_JSON.to_string())?; 148 assert_eq!( 149 json.all_test_names(), 150 BTreeSet::from(["bar".to_string(), "baz".to_string(), "qux".to_string()]) 151 ); 152 Ok(()) 153 } 154 155 #[test] set_presubmits() -> Result<(), Error>156 fn set_presubmits() -> Result<(), Error> { 157 let mut json = TestMappingJson::parse(TEST_JSON.to_string())?; 158 json.set_presubmits(&BTreeSet::from(["asdf".to_string()])); 159 assert_eq!(json.presubmit, vec![TestMappingName { name: "asdf".to_string() }]); 160 assert_eq!(json.presubmit, json.presubmit_rust); 161 Ok(()) 162 } 163 164 #[test] add_new_tests_to_postsubmit() -> Result<(), Error>165 fn add_new_tests_to_postsubmit() -> Result<(), Error> { 166 let mut json = TestMappingJson::parse(TEST_JSON.to_string())?; 167 assert!(!json.add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string()]))); 168 assert!(json 169 .add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string(), "asdf".to_string()]))); 170 assert_eq!( 171 json.postsubmit, 172 vec![ 173 TestMappingName { name: "asdf".to_string() }, 174 TestMappingName { name: "qux".to_string() } 175 ] 176 ); 177 Ok(()) 178 } 179 180 #[test] is_empty() -> Result<(), Error>181 fn is_empty() -> Result<(), Error> { 182 let json = TestMappingJson::parse(TEST_JSON.to_string())?; 183 assert!(!json.is_empty()); 184 assert!(TestMappingJson::default().is_empty()); 185 Ok(()) 186 } 187 } 188