xref: /aosp_15_r20/build/make/tools/aconfig/aconfig/src/test.rs (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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             true,
270         )
271         .unwrap();
272         aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
273     }
274 
parse_test_flags() -> ProtoParsedFlags275     pub fn parse_test_flags() -> ProtoParsedFlags {
276         let bytes = crate::commands::parse_flags(
277             "com.android.aconfig.test",
278             Some("system"),
279             vec![Input {
280                 source: "tests/test.aconfig".to_string(),
281                 reader: Box::new(include_bytes!("../tests/test.aconfig").as_slice()),
282             }],
283             vec![
284                 Input {
285                     source: "tests/first.values".to_string(),
286                     reader: Box::new(include_bytes!("../tests/first.values").as_slice()),
287                 },
288                 Input {
289                     source: "tests/second.values".to_string(),
290                     reader: Box::new(include_bytes!("../tests/second.values").as_slice()),
291                 },
292             ],
293             crate::commands::DEFAULT_FLAG_PERMISSION,
294             true,
295         )
296         .unwrap();
297         aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
298     }
299 
parse_second_package_flags() -> ProtoParsedFlags300     pub fn parse_second_package_flags() -> ProtoParsedFlags {
301         let bytes = crate::commands::parse_flags(
302             "com.android.aconfig.second_test",
303             Some("system"),
304             vec![Input {
305                 source: "tests/test_second_package.aconfig".to_string(),
306                 reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()),
307             }],
308             vec![Input {
309                 source: "tests/third.values".to_string(),
310                 reader: Box::new(include_bytes!("../tests/third.values").as_slice()),
311             }],
312             crate::commands::DEFAULT_FLAG_PERMISSION,
313             true,
314         )
315         .unwrap();
316         aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
317     }
318 
first_significant_code_diff(a: &str, b: &str) -> Option<String>319     pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
320         let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
321         let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
322         match itertools::diff_with(a, b, |left, right| left == right) {
323             Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => {
324                 Some(format!("'{}' vs '{}'", left.next().unwrap(), right.next().unwrap()))
325             }
326             Some(itertools::Diff::Shorter(_, mut left)) => {
327                 Some(format!("LHS trailing data: '{}'", left.next().unwrap()))
328             }
329             Some(itertools::Diff::Longer(_, mut right)) => {
330                 Some(format!("RHS trailing data: '{}'", right.next().unwrap()))
331             }
332             None => None,
333         }
334     }
335 
336     #[test]
test_first_significant_code_diff()337     fn test_first_significant_code_diff() {
338         assert!(first_significant_code_diff("", "").is_none());
339         assert!(first_significant_code_diff("   a", "\n\na\n").is_none());
340         let a = r#"
341         public class A {
342             private static final String FOO = "FOO";
343             public static void main(String[] args) {
344                 System.out.println("FOO=" + FOO);
345             }
346         }
347         "#;
348         let b = r#"
349         public class A {
350             private static final String FOO = "BAR";
351             public static void main(String[] args) {
352                 System.out.println("foo=" + FOO);
353             }
354         }
355         "#;
356         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));
357         assert_eq!(
358             Some("LHS trailing data: 'b'".to_string()),
359             first_significant_code_diff("a\nb", "a")
360         );
361         assert_eq!(
362             Some("RHS trailing data: 'b'".to_string()),
363             first_significant_code_diff("a", "a\nb")
364         );
365     }
366 }
367