blob: f0d27d6d471aaf61509ea374976cf5edf33f8ec0 [file] [log] [blame]
Mårten Kongstadfe753f52023-04-26 09:13:03 +02001/*
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// When building with the Android tool-chain
18//
19// - an external crate `aconfig_protos` will be generated
20// - the feature "cargo" will be disabled
21//
22// When building with cargo
23//
24// - a local sub-module will be generated in OUT_DIR and included in this file
25// - the feature "cargo" will be enabled
26//
27// This module hides these differences from the rest of aconfig.
28
Mårten Kongstadbb520722023-04-26 13:16:41 +020029// ---- When building with the Android tool-chain ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020030#[cfg(not(feature = "cargo"))]
Mårten Kongstadf9422522023-06-14 08:38:46 +020031mod auto_generated {
Dennis Shen277e5dc2024-01-23 18:01:52 +000032 pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
33 pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
34 pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
35 pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
36 pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
37 pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
38 pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
39 pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
40 pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
41 pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
42 pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
Mårten Kongstadf9422522023-06-14 08:38:46 +020043}
Mårten Kongstada1029092023-05-08 11:51:59 +020044
Mårten Kongstadbb520722023-04-26 13:16:41 +020045// ---- When building with cargo ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020046#[cfg(feature = "cargo")]
Mårten Kongstadf9422522023-06-14 08:38:46 +020047mod auto_generated {
48 // include! statements should be avoided (because they import file contents verbatim), but
49 // because this is only used during local development, and only if using cargo instead of the
50 // Android tool-chain, we allow it
51 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
Micha Schwab1a1a08a2023-11-28 16:14:30 -050052 pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
Mårten Kongstadf9422522023-06-14 08:38:46 +020053 pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
54 pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
Micha Schwab1a1a08a2023-11-28 16:14:30 -050055 pub use aconfig::Flag_metadata as ProtoFlagMetadata;
Mårten Kongstadf9422522023-06-14 08:38:46 +020056 pub use aconfig::Flag_permission as ProtoFlagPermission;
57 pub use aconfig::Flag_state as ProtoFlagState;
58 pub use aconfig::Flag_value as ProtoFlagValue;
59 pub use aconfig::Flag_values as ProtoFlagValues;
60 pub use aconfig::Parsed_flag as ProtoParsedFlag;
61 pub use aconfig::Parsed_flags as ProtoParsedFlags;
62 pub use aconfig::Tracepoint as ProtoTracepoint;
63}
Mårten Kongstada1029092023-05-08 11:51:59 +020064
Mårten Kongstadbb520722023-04-26 13:16:41 +020065// ---- Common for both the Android tool-chain and cargo ----
Mårten Kongstadf9422522023-06-14 08:38:46 +020066pub use auto_generated::*;
67
Mårten Kongstadbb520722023-04-26 13:16:41 +020068use anyhow::Result;
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020069use paste::paste;
Mårten Kongstadbb520722023-04-26 13:16:41 +020070
Dennis Shen277e5dc2024-01-23 18:01:52 +000071pub fn is_valid_name_ident(s: &str) -> bool {
72 // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
73 if s.contains("__") {
74 return false;
75 }
76 let mut chars = s.chars();
77 let Some(first) = chars.next() else {
78 return false;
79 };
80 if !first.is_ascii_lowercase() {
81 return false;
82 }
83 chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
84}
85
86pub fn is_valid_package_ident(s: &str) -> bool {
87 if !s.contains('.') {
88 return false;
89 }
90 s.split('.').all(is_valid_name_ident)
91}
92
93pub fn is_valid_container_ident(s: &str) -> bool {
94 s.split('.').all(is_valid_name_ident)
95}
96
Mårten Kongstad403658f2023-06-14 09:51:56 +020097fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020098where
99 T: protobuf::MessageFull,
100{
Mårten Kongstadbb520722023-04-26 13:16:41 +0200101 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
102}
Mårten Kongstad403658f2023-06-14 09:51:56 +0200103
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200104macro_rules! ensure_required_fields {
105 ($type:expr, $struct:expr, $($field:expr),+) => {
106 $(
107 paste! {
108 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
109 }
110 )+
111 };
112}
113
Mårten Kongstad403658f2023-06-14 09:51:56 +0200114pub mod flag_declaration {
115 use super::*;
Mårten Kongstad403658f2023-06-14 09:51:56 +0200116 use anyhow::ensure;
117
118 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200119 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200120
Dennis Shen277e5dc2024-01-23 18:01:52 +0000121 ensure!(is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
122 ensure!(is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200123 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200124 ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200125
Mårten Kongstad403658f2023-06-14 09:51:56 +0200126 Ok(())
127 }
128}
129
130pub mod flag_declarations {
131 use super::*;
Mårten Kongstad403658f2023-06-14 09:51:56 +0200132 use anyhow::ensure;
133
134 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
135 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
136 verify_fields(&pdf)?;
137 Ok(pdf)
138 }
139
140 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200141 ensure_required_fields!("flag declarations", pdf, "package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000142 // TODO(b/312769710): Make the container field required.
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200143
Mårten Kongstad403658f2023-06-14 09:51:56 +0200144 ensure!(
Dennis Shen277e5dc2024-01-23 18:01:52 +0000145 is_valid_package_ident(pdf.package()),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200146 "bad flag declarations: bad package"
147 );
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000148 ensure!(
Dennis Shen277e5dc2024-01-23 18:01:52 +0000149 !pdf.has_container() || is_valid_container_ident(pdf.container()),
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000150 "bad flag declarations: bad container"
151 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200152 for flag_declaration in pdf.flag.iter() {
153 super::flag_declaration::verify_fields(flag_declaration)?;
154 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200155
Mårten Kongstad403658f2023-06-14 09:51:56 +0200156 Ok(())
157 }
158}
159
160pub mod flag_value {
161 use super::*;
Mårten Kongstad403658f2023-06-14 09:51:56 +0200162 use anyhow::ensure;
163
164 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200165 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200166
Dennis Shen277e5dc2024-01-23 18:01:52 +0000167 ensure!(is_valid_package_ident(fv.package()), "bad flag value: bad package");
168 ensure!(is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200169
Mårten Kongstad403658f2023-06-14 09:51:56 +0200170 Ok(())
171 }
172}
173
174pub mod flag_values {
175 use super::*;
176
177 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
178 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
179 verify_fields(&pfv)?;
180 Ok(pfv)
181 }
182
183 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
184 for flag_value in pfv.flag_value.iter() {
185 super::flag_value::verify_fields(flag_value)?;
186 }
187 Ok(())
188 }
189}
190
Zhi Dou24a0b6a2023-08-10 21:39:59 +0000191pub mod flag_permission {
192 use super::*;
193 use anyhow::bail;
194
195 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
196 match permission.to_ascii_lowercase().as_str() {
197 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
198 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
199 _ => bail!("Permission needs to be read_only or read_write."),
200 }
201 }
202
203 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
204 match permission {
205 ProtoFlagPermission::READ_WRITE => "read_write",
206 ProtoFlagPermission::READ_ONLY => "read_only",
207 }
208 }
209}
210
Mårten Kongstad403658f2023-06-14 09:51:56 +0200211pub mod tracepoint {
212 use super::*;
213 use anyhow::ensure;
214
215 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200216 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200217
Mårten Kongstad403658f2023-06-14 09:51:56 +0200218 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200219
Mårten Kongstad403658f2023-06-14 09:51:56 +0200220 Ok(())
221 }
222}
223
224pub mod parsed_flag {
225 use super::*;
Mårten Kongstad403658f2023-06-14 09:51:56 +0200226 use anyhow::ensure;
227
228 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200229 ensure_required_fields!(
230 "parsed flag",
231 pf,
232 "package",
233 "name",
234 "namespace",
235 "description",
236 "state",
237 "permission"
238 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200239
Dennis Shen277e5dc2024-01-23 18:01:52 +0000240 ensure!(is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000241 ensure!(
Dennis Shen277e5dc2024-01-23 18:01:52 +0000242 !pf.has_container() || is_valid_container_ident(pf.container()),
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000243 "bad parsed flag: bad container"
244 );
Dennis Shen277e5dc2024-01-23 18:01:52 +0000245 ensure!(is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
246 ensure!(is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200247 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
248 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
249 for tp in pf.trace.iter() {
250 super::tracepoint::verify_fields(tp)?;
251 }
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200252 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad21717e72023-09-04 13:28:36 +0200253 if pf.is_fixed_read_only() {
254 ensure!(
255 pf.permission() == ProtoFlagPermission::READ_ONLY,
256 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
257 );
258 for tp in pf.trace.iter() {
259 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
260 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
261 );
262 }
263 }
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200264
Mårten Kongstad403658f2023-06-14 09:51:56 +0200265 Ok(())
266 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200267
268 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
269 debug_assert!(!pf.trace.is_empty());
270 pf.trace[0].source()
271 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200272}
273
274pub mod parsed_flags {
275 use super::*;
276 use anyhow::bail;
277 use std::cmp::Ordering;
278
279 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
280 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
281 verify_fields(&message)?;
282 Ok(message)
283 }
284
285 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Dennis Shen277e5dc2024-01-23 18:01:52 +0000286 use crate::parsed_flag::path_to_declaration;
Mårten Kongstad206a3822023-07-07 08:52:52 +0200287
Mårten Kongstad403658f2023-06-14 09:51:56 +0200288 let mut previous: Option<&ProtoParsedFlag> = None;
289 for parsed_flag in pf.parsed_flag.iter() {
290 if let Some(prev) = previous {
291 let a = create_sorting_key(prev);
292 let b = create_sorting_key(parsed_flag);
293 match a.cmp(&b) {
294 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200295 Ordering::Equal => bail!(
296 "bad parsed flags: duplicate flag {} (defined in {} and {})",
297 a,
298 path_to_declaration(prev),
299 path_to_declaration(parsed_flag)
300 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200301 Ordering::Greater => {
302 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
303 }
304 }
305 }
306 super::parsed_flag::verify_fields(parsed_flag)?;
307 previous = Some(parsed_flag);
308 }
309 Ok(())
310 }
311
Colin Cross6befb342023-11-28 15:55:07 -0800312 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200313 let mut merged = ProtoParsedFlags::new();
314 for mut pfs in parsed_flags.into_iter() {
315 merged.parsed_flag.append(&mut pfs.parsed_flag);
316 }
317 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
Colin Cross6befb342023-11-28 15:55:07 -0800318 if dedup {
319 // Deduplicate identical protobuf messages. Messages with the same sorting key but
320 // different fields (including the path to the original source file) will not be
321 // deduplicated and trigger an error in verify_fields.
322 merged.parsed_flag.dedup();
323 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200324 verify_fields(&merged)?;
325 Ok(merged)
326 }
327
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000328 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
329 pf.parsed_flag.sort_by_key(create_sorting_key);
330 }
331
Mårten Kongstad403658f2023-06-14 09:51:56 +0200332 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
Mårten Kongstadf3f20ef2023-12-07 10:05:09 +0100333 pf.fully_qualified_name()
334 }
335}
336
337pub trait ParsedFlagExt {
338 fn fully_qualified_name(&self) -> String;
339}
340
341impl ParsedFlagExt for ProtoParsedFlag {
342 fn fully_qualified_name(&self) -> String {
343 format!("{}.{}", self.package(), self.name())
Mårten Kongstad403658f2023-06-14 09:51:56 +0200344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_flag_declarations_try_from_text_proto() {
353 // valid input
354 let flag_declarations = flag_declarations::try_from_text_proto(
355 r#"
356package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000357container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200358flag {
359 name: "first"
360 namespace: "first_ns"
361 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200362 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000363 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200364}
365flag {
366 name: "second"
367 namespace: "second_ns"
368 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200369 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000370 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200371}
372"#,
373 )
374 .unwrap();
375 assert_eq!(flag_declarations.package(), "com.foo.bar");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000376 assert_eq!(flag_declarations.container(), "system");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200377 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
378 assert_eq!(first.name(), "first");
379 assert_eq!(first.namespace(), "first_ns");
380 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200381 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000382 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000383 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200384 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
385 assert_eq!(second.name(), "second");
386 assert_eq!(second.namespace(), "second_ns");
387 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200388 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000389 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000390 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200391
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000392 // valid input: missing container in flag declarations is supported
393 let flag_declarations = flag_declarations::try_from_text_proto(
394 r#"
395package: "com.foo.bar"
396flag {
397 name: "first"
398 namespace: "first_ns"
399 description: "This is the description of the first flag."
400 bug: "123"
401}
402"#,
403 )
404 .unwrap();
405 assert_eq!(flag_declarations.container(), "");
406 assert!(!flag_declarations.has_container());
407
Mårten Kongstad403658f2023-06-14 09:51:56 +0200408 // bad input: missing package in flag declarations
409 let error = flag_declarations::try_from_text_proto(
410 r#"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000411container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200412flag {
413 name: "first"
414 namespace: "first_ns"
415 description: "This is the description of the first flag."
416}
417flag {
418 name: "second"
419 namespace: "second_ns"
420 description: "This is the description of the second flag."
421}
422"#,
423 )
424 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200425 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200426
427 // bad input: missing namespace in flag declaration
428 let error = flag_declarations::try_from_text_proto(
429 r#"
430package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000431container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200432flag {
433 name: "first"
434 description: "This is the description of the first flag."
435}
436flag {
437 name: "second"
438 namespace: "second_ns"
439 description: "This is the description of the second flag."
440}
441"#,
442 )
443 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200444 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200445
446 // bad input: bad package name in flag declarations
447 let error = flag_declarations::try_from_text_proto(
448 r#"
449package: "_com.FOO__BAR"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000450container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200451flag {
452 name: "first"
453 namespace: "first_ns"
454 description: "This is the description of the first flag."
455}
456flag {
457 name: "second"
458 namespace: "second_ns"
459 description: "This is the description of the second flag."
460}
461"#,
462 )
463 .unwrap_err();
464 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
465
466 // bad input: bad name in flag declaration
467 let error = flag_declarations::try_from_text_proto(
468 r#"
469package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000470container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200471flag {
472 name: "FIRST"
473 namespace: "first_ns"
474 description: "This is the description of the first flag."
475}
476flag {
477 name: "second"
478 namespace: "second_ns"
479 description: "This is the description of the second flag."
480}
481"#,
482 )
483 .unwrap_err();
484 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200485
486 // bad input: no bug entries in flag declaration
487 let error = flag_declarations::try_from_text_proto(
488 r#"
489package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000490container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200491flag {
492 name: "first"
493 namespace: "first_ns"
494 description: "This is the description of the first flag."
495}
496"#,
497 )
498 .unwrap_err();
499 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
500
501 // bad input: multiple bug entries in flag declaration
502 let error = flag_declarations::try_from_text_proto(
503 r#"
504package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000505container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200506flag {
507 name: "first"
508 namespace: "first_ns"
509 description: "This is the description of the first flag."
510 bug: "123"
511 bug: "abc"
512}
513"#,
514 )
515 .unwrap_err();
516 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000517
518 // bad input: invalid container name in flag declaration
519 let error = flag_declarations::try_from_text_proto(
520 r#"
521package: "com.foo.bar"
522container: "__bad_bad_container.com"
523flag {
524 name: "first"
525 namespace: "first_ns"
526 description: "This is the description of the first flag."
527 bug: "123"
528 bug: "abc"
529}
530"#,
531 )
532 .unwrap_err();
533 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
534
535 // TODO(b/312769710): Verify error when container is missing.
Mårten Kongstad403658f2023-06-14 09:51:56 +0200536 }
537
538 #[test]
539 fn test_flag_values_try_from_text_proto() {
540 // valid input
541 let flag_values = flag_values::try_from_text_proto(
542 r#"
543flag_value {
544 package: "com.first"
545 name: "first"
546 state: DISABLED
547 permission: READ_ONLY
548}
549flag_value {
550 package: "com.second"
551 name: "second"
552 state: ENABLED
553 permission: READ_WRITE
554}
555"#,
556 )
557 .unwrap();
558 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
559 assert_eq!(first.package(), "com.first");
560 assert_eq!(first.name(), "first");
561 assert_eq!(first.state(), ProtoFlagState::DISABLED);
562 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
563 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
564 assert_eq!(second.package(), "com.second");
565 assert_eq!(second.name(), "second");
566 assert_eq!(second.state(), ProtoFlagState::ENABLED);
567 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
568
569 // bad input: bad package in flag value
570 let error = flag_values::try_from_text_proto(
571 r#"
572flag_value {
573 package: "COM.FIRST"
574 name: "first"
575 state: DISABLED
576 permission: READ_ONLY
577}
578"#,
579 )
580 .unwrap_err();
581 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
582
583 // bad input: bad name in flag value
584 let error = flag_values::try_from_text_proto(
585 r#"
586flag_value {
587 package: "com.first"
588 name: "FIRST"
589 state: DISABLED
590 permission: READ_ONLY
591}
592"#,
593 )
594 .unwrap_err();
595 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
596
597 // bad input: missing state in flag value
598 let error = flag_values::try_from_text_proto(
599 r#"
600flag_value {
601 package: "com.first"
602 name: "first"
603 permission: READ_ONLY
604}
605"#,
606 )
607 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200608 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200609
610 // bad input: missing permission in flag value
611 let error = flag_values::try_from_text_proto(
612 r#"
613flag_value {
614 package: "com.first"
615 name: "first"
616 state: DISABLED
617}
618"#,
619 )
620 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200621 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200622 }
623
624 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
625 use protobuf::Message;
626
627 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
628 let mut binary_proto = Vec::new();
629 parsed_flags.write_to_vec(&mut binary_proto)?;
630 parsed_flags::try_from_binary_proto(&binary_proto)
631 }
632
633 #[test]
634 fn test_parsed_flags_try_from_text_proto() {
635 // valid input
636 let text_proto = r#"
637parsed_flag {
638 package: "com.first"
639 name: "first"
640 namespace: "first_ns"
641 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200642 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200643 state: DISABLED
644 permission: READ_ONLY
645 trace {
646 source: "flags.declarations"
647 state: DISABLED
648 permission: READ_ONLY
649 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000650 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200651}
652parsed_flag {
653 package: "com.second"
654 name: "second"
655 namespace: "second_ns"
656 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200657 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200658 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200659 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200660 trace {
661 source: "flags.declarations"
662 state: DISABLED
663 permission: READ_ONLY
664 }
665 trace {
666 source: "flags.values"
667 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200668 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200669 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000670 is_fixed_read_only: true
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000671 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200672}
673"#;
674 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
675 assert_eq!(parsed_flags.parsed_flag.len(), 2);
676 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
677 assert_eq!(second.package(), "com.second");
678 assert_eq!(second.name(), "second");
679 assert_eq!(second.namespace(), "second_ns");
680 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200681 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200682 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200683 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200684 assert_eq!(2, second.trace.len());
685 assert_eq!(second.trace[0].source(), "flags.declarations");
686 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
687 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
688 assert_eq!(second.trace[1].source(), "flags.values");
689 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200690 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000691 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200692
693 // valid input: empty
694 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
695 assert!(parsed_flags.parsed_flag.is_empty());
696
697 // bad input: empty trace
698 let text_proto = r#"
699parsed_flag {
700 package: "com.first"
701 name: "first"
702 namespace: "first_ns"
703 description: "This is the description of the first flag."
704 state: DISABLED
705 permission: READ_ONLY
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000706 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200707}
708"#;
709 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
710 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
711
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200712 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200713 let text_proto = r#"
714parsed_flag {
715 package: "com.first"
716 name: "first"
717 description: "This is the description of the first flag."
718 state: DISABLED
719 permission: READ_ONLY
720 trace {
721 source: "flags.declarations"
722 state: DISABLED
723 permission: READ_ONLY
724 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000725 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200726}
727"#;
728 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200729 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200730
731 // bad input: parsed_flag not sorted by package
732 let text_proto = r#"
733parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200734 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200735 name: "first"
736 namespace: "first_ns"
737 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200738 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200739 state: DISABLED
740 permission: READ_ONLY
741 trace {
742 source: "flags.declarations"
743 state: DISABLED
744 permission: READ_ONLY
745 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000746 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200747}
748parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200749 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200750 name: "second"
751 namespace: "second_ns"
752 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200753 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200754 state: ENABLED
755 permission: READ_WRITE
756 trace {
757 source: "flags.declarations"
758 state: DISABLED
759 permission: READ_ONLY
760 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000761 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200762}
763"#;
764 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
765 assert_eq!(
766 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200767 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200768 );
769
770 // bad input: parsed_flag not sorted by name
771 let text_proto = r#"
772parsed_flag {
773 package: "com.foo"
774 name: "bbb"
775 namespace: "first_ns"
776 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200777 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200778 state: DISABLED
779 permission: READ_ONLY
780 trace {
781 source: "flags.declarations"
782 state: DISABLED
783 permission: READ_ONLY
784 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000785 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200786}
787parsed_flag {
788 package: "com.foo"
789 name: "aaa"
790 namespace: "second_ns"
791 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200792 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200793 state: ENABLED
794 permission: READ_WRITE
795 trace {
796 source: "flags.declarations"
797 state: DISABLED
798 permission: READ_ONLY
799 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000800 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200801}
802"#;
803 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
804 assert_eq!(
805 format!("{:?}", error),
806 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
807 );
808
809 // bad input: duplicate flags
810 let text_proto = r#"
811parsed_flag {
812 package: "com.foo"
813 name: "bar"
814 namespace: "first_ns"
815 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200816 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200817 state: DISABLED
818 permission: READ_ONLY
819 trace {
820 source: "flags.declarations"
821 state: DISABLED
822 permission: READ_ONLY
823 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000824 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200825}
826parsed_flag {
827 package: "com.foo"
828 name: "bar"
829 namespace: "second_ns"
830 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200831 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200832 state: ENABLED
833 permission: READ_WRITE
834 trace {
835 source: "flags.declarations"
836 state: DISABLED
837 permission: READ_ONLY
838 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000839 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200840}
841"#;
842 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200843 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
844 }
845
846 #[test]
847 fn test_parsed_flag_path_to_declaration() {
848 let text_proto = r#"
849parsed_flag {
850 package: "com.foo"
851 name: "bar"
852 namespace: "first_ns"
853 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200854 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200855 state: DISABLED
856 permission: READ_ONLY
857 trace {
858 source: "flags.declarations"
859 state: DISABLED
860 permission: READ_ONLY
861 }
862 trace {
863 source: "flags.values"
864 state: ENABLED
865 permission: READ_ONLY
866 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000867 container: "system"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200868}
869"#;
870 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
871 let parsed_flag = &parsed_flags.parsed_flag[0];
872 assert_eq!(
Dennis Shen277e5dc2024-01-23 18:01:52 +0000873 crate::parsed_flag::path_to_declaration(parsed_flag),
Mårten Kongstad206a3822023-07-07 08:52:52 +0200874 "flags.declarations"
875 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200876 }
877
878 #[test]
879 fn test_parsed_flags_merge() {
880 let text_proto = r#"
881parsed_flag {
882 package: "com.first"
883 name: "first"
884 namespace: "first_ns"
885 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200886 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200887 state: DISABLED
888 permission: READ_ONLY
889 trace {
890 source: "flags.declarations"
891 state: DISABLED
892 permission: READ_ONLY
893 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000894 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200895}
896parsed_flag {
897 package: "com.second"
898 name: "second"
899 namespace: "second_ns"
900 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200901 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200902 state: ENABLED
903 permission: READ_WRITE
904 trace {
905 source: "flags.declarations"
906 state: DISABLED
907 permission: READ_ONLY
908 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000909 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200910}
911"#;
912 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
913
914 let text_proto = r#"
915parsed_flag {
916 package: "com.first"
917 name: "first"
918 namespace: "first_ns"
919 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200920 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200921 state: DISABLED
922 permission: READ_ONLY
923 trace {
924 source: "flags.declarations"
925 state: DISABLED
926 permission: READ_ONLY
927 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000928 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200929}
930"#;
931 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
932
933 let text_proto = r#"
934parsed_flag {
935 package: "com.second"
936 name: "second"
937 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200938 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200939 description: "This is the description of the second flag."
940 state: ENABLED
941 permission: READ_WRITE
942 trace {
943 source: "flags.declarations"
944 state: DISABLED
945 permission: READ_ONLY
946 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000947 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200948}
949"#;
950 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
951
Colin Cross6befb342023-11-28 15:55:07 -0800952 let text_proto = r#"
953parsed_flag {
954 package: "com.second"
955 name: "second"
956 namespace: "second_ns"
957 bug: "b"
958 description: "This is the description of the second flag."
959 state: ENABLED
960 permission: READ_WRITE
961 trace {
962 source: "duplicate/flags.declarations"
963 state: DISABLED
964 permission: READ_ONLY
965 }
966}
967"#;
968 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
969
Mårten Kongstad403658f2023-06-14 09:51:56 +0200970 // bad cases
Colin Cross6befb342023-11-28 15:55:07 -0800971
972 // two of the same flag with dedup disabled
973 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200974 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200975
Colin Cross6befb342023-11-28 15:55:07 -0800976 // two conflicting flags with dedup disabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500977 let error =
978 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800979 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
980
981 // two conflicting flags with dedup enabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500982 let error =
983 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800984 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
985
Mårten Kongstad403658f2023-06-14 09:51:56 +0200986 // valid cases
Colin Cross6befb342023-11-28 15:55:07 -0800987 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
988 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
989 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
990 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500991 assert_eq!(
992 expected,
993 parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()
994 );
995 assert_eq!(
996 expected,
997 parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()
998 );
999 assert_eq!(
1000 expected,
1001 parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()
1002 );
1003 assert_eq!(
1004 expected,
1005 parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()
1006 );
Colin Cross6befb342023-11-28 15:55:07 -08001007
1008 // two identical flags with dedup enabled
1009 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
Mårten Kongstad403658f2023-06-14 09:51:56 +02001010 }
1011}