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::TestMappingError; 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, TestMappingError>46 pub fn parse(mut contents: String) -> Result<TestMappingJson, TestMappingError> { 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(TestMappingError::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 } set_presubmits(&mut self, tests: &BTreeSet<String>)59 pub fn set_presubmits(&mut self, tests: &BTreeSet<String>) { 60 self.presubmit = tests.iter().map(|t| TestMappingName { name: t.to_string() }).collect(); 61 self.presubmit_rust = self.presubmit.clone(); 62 } convert_postsubmit_tests(&mut self) -> bool63 pub fn convert_postsubmit_tests(&mut self) -> bool { 64 let changed = !self.postsubmit.is_empty(); 65 self.presubmit.append(&mut self.postsubmit.clone()); 66 self.presubmit_rust.append(&mut self.postsubmit); 67 self.presubmit.sort(); 68 self.presubmit.dedup(); 69 self.presubmit_rust.sort(); 70 self.presubmit_rust.dedup(); 71 changed 72 } add_new_tests_to_postsubmit(&mut self, tests: &BTreeSet<String>) -> bool73 pub fn add_new_tests_to_postsubmit(&mut self, tests: &BTreeSet<String>) -> bool { 74 let mut changed = false; 75 self.postsubmit.extend(tests.difference(&self.all_test_names()).map(|test_name| { 76 changed = true; 77 TestMappingName { name: test_name.to_string() } 78 })); 79 self.postsubmit.sort(); 80 self.postsubmit.dedup(); 81 changed 82 } all_test_names(&self) -> BTreeSet<String>83 fn all_test_names(&self) -> BTreeSet<String> { 84 let mut tests = BTreeSet::new(); 85 tests.extend(self.presubmit.iter().map(|t| t.name.clone())); 86 tests.extend(self.presubmit_rust.iter().map(|t| t.name.clone())); 87 tests.extend(self.postsubmit.iter().map(|t| t.name.clone())); 88 tests 89 } 90 } 91 92 #[cfg(test)] 93 mod tests { 94 use super::*; 95 96 static TEST_JSON: &str = r###"{ 97 "imports": [ 98 { 99 "path": "foo" 100 } 101 ], 102 "presubmit": [ 103 { 104 "name": "bar" 105 } 106 ], 107 "presubmit-rust": [ 108 { 109 "name": "baz" 110 } 111 ], 112 "postsubmit": [ 113 { 114 "name": "qux" 115 } 116 ] 117 } 118 "###; 119 120 #[test] parse() -> Result<(), TestMappingError>121 fn parse() -> Result<(), TestMappingError> { 122 TestMappingJson::parse("{}".to_string())?; 123 TestMappingJson::parse("//comment\n{}".to_string())?; 124 TestMappingJson::parse(TEST_JSON.to_string())?; 125 assert!(TestMappingJson::parse("foo".to_string()).is_err()); 126 Ok(()) 127 } 128 129 #[test] all_test_names() -> Result<(), TestMappingError>130 fn all_test_names() -> Result<(), TestMappingError> { 131 let json = TestMappingJson::parse(TEST_JSON.to_string())?; 132 assert_eq!( 133 json.all_test_names(), 134 BTreeSet::from(["bar".to_string(), "baz".to_string(), "qux".to_string()]) 135 ); 136 Ok(()) 137 } 138 139 #[test] set_presubmits() -> Result<(), TestMappingError>140 fn set_presubmits() -> Result<(), TestMappingError> { 141 let mut json = TestMappingJson::parse(TEST_JSON.to_string())?; 142 json.set_presubmits(&BTreeSet::from(["asdf".to_string()])); 143 assert_eq!(json.presubmit, vec![TestMappingName { name: "asdf".to_string() }]); 144 assert_eq!(json.presubmit, json.presubmit_rust); 145 Ok(()) 146 } 147 148 #[test] add_new_tests_to_postsubmit() -> Result<(), TestMappingError>149 fn add_new_tests_to_postsubmit() -> Result<(), TestMappingError> { 150 let mut json = TestMappingJson::parse(TEST_JSON.to_string())?; 151 assert!(!json.add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string()]))); 152 assert!(json 153 .add_new_tests_to_postsubmit(&BTreeSet::from(["bar".to_string(), "asdf".to_string()]))); 154 assert_eq!( 155 json.postsubmit, 156 vec![ 157 TestMappingName { name: "asdf".to_string() }, 158 TestMappingName { name: "qux".to_string() } 159 ] 160 ); 161 Ok(()) 162 } 163 164 #[test] is_empty() -> Result<(), TestMappingError>165 fn is_empty() -> Result<(), TestMappingError> { 166 let json = TestMappingJson::parse(TEST_JSON.to_string())?; 167 assert!(!json.is_empty()); 168 assert!(TestMappingJson::default().is_empty()); 169 Ok(()) 170 } 171 } 172