1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #[cfg(test)] 18 pub use test_utils::*; 19 20 #[cfg(test)] 21 pub mod test_utils { 22 use crate::commands::Input; 23 use aconfig_protos::ProtoParsedFlags; 24 use itertools; 25 26 pub const TEST_PACKAGE: &str = "com.android.aconfig.test"; 27 28 pub const TEST_FLAGS_TEXTPROTO: &str = r#" 29 parsed_flag { 30 package: "com.android.aconfig.test" 31 name: "disabled_ro" 32 namespace: "aconfig_test" 33 description: "This flag is DISABLED + READ_ONLY" 34 bug: "123" 35 state: DISABLED 36 permission: READ_ONLY 37 trace { 38 source: "tests/test.aconfig" 39 state: DISABLED 40 permission: READ_WRITE 41 } 42 trace { 43 source: "tests/first.values" 44 state: DISABLED 45 permission: READ_ONLY 46 } 47 is_fixed_read_only: false 48 is_exported: false 49 container: "system" 50 metadata { 51 purpose: PURPOSE_UNSPECIFIED 52 } 53 } 54 parsed_flag { 55 package: "com.android.aconfig.test" 56 name: "disabled_rw" 57 namespace: "aconfig_test" 58 description: "This flag is DISABLED + READ_WRITE" 59 bug: "456" 60 state: DISABLED 61 permission: READ_WRITE 62 trace { 63 source: "tests/test.aconfig" 64 state: DISABLED 65 permission: READ_WRITE 66 } 67 is_fixed_read_only: false 68 is_exported: false 69 container: "system" 70 metadata { 71 purpose: PURPOSE_UNSPECIFIED 72 } 73 } 74 parsed_flag { 75 package: "com.android.aconfig.test" 76 name: "disabled_rw_exported" 77 namespace: "aconfig_test" 78 description: "This flag is DISABLED + READ_WRITE and exported" 79 bug: "111" 80 state: DISABLED 81 permission: READ_WRITE 82 trace { 83 source: "tests/test.aconfig" 84 state: DISABLED 85 permission: READ_WRITE 86 } 87 trace { 88 source: "tests/first.values" 89 state: DISABLED 90 permission: READ_WRITE 91 } 92 is_fixed_read_only: false 93 is_exported: true 94 container: "system" 95 metadata { 96 purpose: PURPOSE_UNSPECIFIED 97 } 98 } 99 parsed_flag { 100 package: "com.android.aconfig.test" 101 name: "disabled_rw_in_other_namespace" 102 namespace: "other_namespace" 103 description: "This flag is DISABLED + READ_WRITE, and is defined in another namespace" 104 bug: "999" 105 state: DISABLED 106 permission: READ_WRITE 107 trace { 108 source: "tests/test.aconfig" 109 state: DISABLED 110 permission: READ_WRITE 111 } 112 trace { 113 source: "tests/first.values" 114 state: DISABLED 115 permission: READ_WRITE 116 } 117 is_fixed_read_only: false 118 is_exported: false 119 container: "system" 120 metadata { 121 purpose: PURPOSE_UNSPECIFIED 122 } 123 } 124 parsed_flag { 125 package: "com.android.aconfig.test" 126 name: "enabled_fixed_ro" 127 namespace: "aconfig_test" 128 description: "This flag is fixed READ_ONLY + ENABLED" 129 bug: "" 130 state: ENABLED 131 permission: READ_ONLY 132 trace { 133 source: "tests/test.aconfig" 134 state: DISABLED 135 permission: READ_ONLY 136 } 137 trace { 138 source: "tests/first.values" 139 state: ENABLED 140 permission: READ_ONLY 141 } 142 is_fixed_read_only: true 143 is_exported: false 144 container: "system" 145 metadata { 146 purpose: PURPOSE_UNSPECIFIED 147 } 148 } 149 parsed_flag { 150 package: "com.android.aconfig.test" 151 name: "enabled_fixed_ro_exported" 152 namespace: "aconfig_test" 153 description: "This flag is fixed ENABLED + READ_ONLY and exported" 154 bug: "111" 155 state: ENABLED 156 permission: READ_ONLY 157 trace { 158 source: "tests/test.aconfig" 159 state: DISABLED 160 permission: READ_ONLY 161 } 162 trace { 163 source: "tests/first.values" 164 state: ENABLED 165 permission: READ_ONLY 166 } 167 is_fixed_read_only: true 168 is_exported: true 169 container: "system" 170 metadata { 171 purpose: PURPOSE_UNSPECIFIED 172 } 173 } 174 parsed_flag { 175 package: "com.android.aconfig.test" 176 name: "enabled_ro" 177 namespace: "aconfig_test" 178 description: "This flag is ENABLED + READ_ONLY" 179 bug: "abc" 180 state: ENABLED 181 permission: READ_ONLY 182 trace { 183 source: "tests/test.aconfig" 184 state: DISABLED 185 permission: READ_WRITE 186 } 187 trace { 188 source: "tests/first.values" 189 state: DISABLED 190 permission: READ_WRITE 191 } 192 trace { 193 source: "tests/second.values" 194 state: ENABLED 195 permission: READ_ONLY 196 } 197 is_fixed_read_only: false 198 is_exported: false 199 container: "system" 200 metadata { 201 purpose: PURPOSE_BUGFIX 202 } 203 } 204 parsed_flag { 205 package: "com.android.aconfig.test" 206 name: "enabled_ro_exported" 207 namespace: "aconfig_test" 208 description: "This flag is ENABLED + READ_ONLY and exported" 209 bug: "111" 210 state: ENABLED 211 permission: READ_ONLY 212 trace { 213 source: "tests/test.aconfig" 214 state: DISABLED 215 permission: READ_WRITE 216 } 217 trace { 218 source: "tests/first.values" 219 state: ENABLED 220 permission: READ_ONLY 221 } 222 is_fixed_read_only: false 223 is_exported: true 224 container: "system" 225 metadata { 226 purpose: PURPOSE_UNSPECIFIED 227 } 228 } 229 parsed_flag { 230 package: "com.android.aconfig.test" 231 name: "enabled_rw" 232 namespace: "aconfig_test" 233 description: "This flag is ENABLED + READ_WRITE" 234 bug: "" 235 state: ENABLED 236 permission: READ_WRITE 237 trace { 238 source: "tests/test.aconfig" 239 state: DISABLED 240 permission: READ_WRITE 241 } 242 trace { 243 source: "tests/first.values" 244 state: ENABLED 245 permission: READ_WRITE 246 } 247 is_fixed_read_only: false 248 is_exported: false 249 container: "system" 250 metadata { 251 purpose: PURPOSE_UNSPECIFIED 252 } 253 } 254 "#; 255 parse_read_only_test_flags() -> ProtoParsedFlags256 pub fn parse_read_only_test_flags() -> ProtoParsedFlags { 257 let bytes = crate::commands::parse_flags( 258 "com.android.aconfig.test", 259 Some("system"), 260 vec![Input { 261 source: "tests/read_only_test.aconfig".to_string(), 262 reader: Box::new(include_bytes!("../tests/read_only_test.aconfig").as_slice()), 263 }], 264 vec![Input { 265 source: "tests/read_only_test.values".to_string(), 266 reader: Box::new(include_bytes!("../tests/read_only_test.values").as_slice()), 267 }], 268 crate::commands::DEFAULT_FLAG_PERMISSION, 269 ) 270 .unwrap(); 271 aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() 272 } 273 parse_test_flags() -> ProtoParsedFlags274 pub fn parse_test_flags() -> ProtoParsedFlags { 275 let bytes = crate::commands::parse_flags( 276 "com.android.aconfig.test", 277 Some("system"), 278 vec![Input { 279 source: "tests/test.aconfig".to_string(), 280 reader: Box::new(include_bytes!("../tests/test.aconfig").as_slice()), 281 }], 282 vec![ 283 Input { 284 source: "tests/first.values".to_string(), 285 reader: Box::new(include_bytes!("../tests/first.values").as_slice()), 286 }, 287 Input { 288 source: "tests/second.values".to_string(), 289 reader: Box::new(include_bytes!("../tests/second.values").as_slice()), 290 }, 291 ], 292 crate::commands::DEFAULT_FLAG_PERMISSION, 293 ) 294 .unwrap(); 295 aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() 296 } 297 first_significant_code_diff(a: &str, b: &str) -> Option<String>298 pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> { 299 let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); 300 let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); 301 match itertools::diff_with(a, b, |left, right| left == right) { 302 Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => { 303 Some(format!("'{}' vs '{}'", left.next().unwrap(), right.next().unwrap())) 304 } 305 Some(itertools::Diff::Shorter(_, mut left)) => { 306 Some(format!("LHS trailing data: '{}'", left.next().unwrap())) 307 } 308 Some(itertools::Diff::Longer(_, mut right)) => { 309 Some(format!("RHS trailing data: '{}'", right.next().unwrap())) 310 } 311 None => None, 312 } 313 } 314 315 #[test] test_first_significant_code_diff()316 fn test_first_significant_code_diff() { 317 assert!(first_significant_code_diff("", "").is_none()); 318 assert!(first_significant_code_diff(" a", "\n\na\n").is_none()); 319 let a = r#" 320 public class A { 321 private static final String FOO = "FOO"; 322 public static void main(String[] args) { 323 System.out.println("FOO=" + FOO); 324 } 325 } 326 "#; 327 let b = r#" 328 public class A { 329 private static final String FOO = "BAR"; 330 public static void main(String[] args) { 331 System.out.println("foo=" + FOO); 332 } 333 } 334 "#; 335 assert_eq!(Some(r#"'private static final String FOO = "FOO";' vs 'private static final String FOO = "BAR";'"#.to_string()), first_significant_code_diff(a, b)); 336 assert_eq!( 337 Some("LHS trailing data: 'b'".to_string()), 338 first_significant_code_diff("a\nb", "a") 339 ); 340 assert_eq!( 341 Some("RHS trailing data: 'b'".to_string()), 342 first_significant_code_diff("a", "a\nb") 343 ); 344 } 345 } 346