blob: 2684d202781804d6ee85ee19a0b9462514fd153d [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 {
Micha Schwab1a1a08a2023-11-28 16:14:30 -050032 pub use aconfig_protos::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
Mårten Kongstadf9422522023-06-14 08:38:46 +020033 pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
34 pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
Micha Schwab1a1a08a2023-11-28 16:14:30 -050035 pub use aconfig_protos::aconfig::Flag_metadata as ProtoFlagMetadata;
Mårten Kongstadf9422522023-06-14 08:38:46 +020036 pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
37 pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
38 pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
39 pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
40 pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
41 pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
42 pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
43}
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
Mårten Kongstad403658f2023-06-14 09:51:56 +020071fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020072where
73 T: protobuf::MessageFull,
74{
Mårten Kongstadbb520722023-04-26 13:16:41 +020075 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
76}
Mårten Kongstad403658f2023-06-14 09:51:56 +020077
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020078macro_rules! ensure_required_fields {
79 ($type:expr, $struct:expr, $($field:expr),+) => {
80 $(
81 paste! {
82 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
83 }
84 )+
85 };
86}
87
Mårten Kongstad403658f2023-06-14 09:51:56 +020088pub mod flag_declaration {
89 use super::*;
90 use crate::codegen;
91 use anyhow::ensure;
92
93 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020094 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020095
Mårten Kongstad403658f2023-06-14 09:51:56 +020096 ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
97 ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
98 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +020099 ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200100
Mårten Kongstad403658f2023-06-14 09:51:56 +0200101 Ok(())
102 }
103}
104
105pub mod flag_declarations {
106 use super::*;
107 use crate::codegen;
108 use anyhow::ensure;
109
110 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
111 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
112 verify_fields(&pdf)?;
113 Ok(pdf)
114 }
115
116 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200117 ensure_required_fields!("flag declarations", pdf, "package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000118 // TODO(b/312769710): Make the container field required.
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200119
Mårten Kongstad403658f2023-06-14 09:51:56 +0200120 ensure!(
121 codegen::is_valid_package_ident(pdf.package()),
122 "bad flag declarations: bad package"
123 );
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000124 ensure!(
125 !pdf.has_container() || codegen::is_valid_container_ident(pdf.container()),
126 "bad flag declarations: bad container"
127 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200128 for flag_declaration in pdf.flag.iter() {
129 super::flag_declaration::verify_fields(flag_declaration)?;
130 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200131
Mårten Kongstad403658f2023-06-14 09:51:56 +0200132 Ok(())
133 }
134}
135
136pub mod flag_value {
137 use super::*;
138 use crate::codegen;
139 use anyhow::ensure;
140
141 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200142 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200143
Mårten Kongstad403658f2023-06-14 09:51:56 +0200144 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
145 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200146
Mårten Kongstad403658f2023-06-14 09:51:56 +0200147 Ok(())
148 }
149}
150
151pub mod flag_values {
152 use super::*;
153
154 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
155 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
156 verify_fields(&pfv)?;
157 Ok(pfv)
158 }
159
160 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
161 for flag_value in pfv.flag_value.iter() {
162 super::flag_value::verify_fields(flag_value)?;
163 }
164 Ok(())
165 }
166}
167
Zhi Dou24a0b6a2023-08-10 21:39:59 +0000168pub mod flag_permission {
169 use super::*;
170 use anyhow::bail;
171
172 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
173 match permission.to_ascii_lowercase().as_str() {
174 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
175 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
176 _ => bail!("Permission needs to be read_only or read_write."),
177 }
178 }
179
180 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
181 match permission {
182 ProtoFlagPermission::READ_WRITE => "read_write",
183 ProtoFlagPermission::READ_ONLY => "read_only",
184 }
185 }
186}
187
Mårten Kongstad403658f2023-06-14 09:51:56 +0200188pub mod tracepoint {
189 use super::*;
190 use anyhow::ensure;
191
192 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200193 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200194
Mårten Kongstad403658f2023-06-14 09:51:56 +0200195 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200196
Mårten Kongstad403658f2023-06-14 09:51:56 +0200197 Ok(())
198 }
199}
200
201pub mod parsed_flag {
202 use super::*;
203 use crate::codegen;
204 use anyhow::ensure;
205
206 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200207 ensure_required_fields!(
208 "parsed flag",
209 pf,
210 "package",
211 "name",
212 "namespace",
213 "description",
214 "state",
215 "permission"
216 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200217
Mårten Kongstad403658f2023-06-14 09:51:56 +0200218 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000219 ensure!(
220 !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
221 "bad parsed flag: bad container"
222 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200223 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
224 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
225 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
226 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
227 for tp in pf.trace.iter() {
228 super::tracepoint::verify_fields(tp)?;
229 }
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200230 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad21717e72023-09-04 13:28:36 +0200231 if pf.is_fixed_read_only() {
232 ensure!(
233 pf.permission() == ProtoFlagPermission::READ_ONLY,
234 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
235 );
236 for tp in pf.trace.iter() {
237 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
238 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
239 );
240 }
241 }
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200242
Mårten Kongstad403658f2023-06-14 09:51:56 +0200243 Ok(())
244 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200245
246 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
247 debug_assert!(!pf.trace.is_empty());
248 pf.trace[0].source()
249 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200250}
251
252pub mod parsed_flags {
253 use super::*;
254 use anyhow::bail;
255 use std::cmp::Ordering;
256
257 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
258 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
259 verify_fields(&message)?;
260 Ok(message)
261 }
262
263 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200264 use crate::protos::parsed_flag::path_to_declaration;
265
Mårten Kongstad403658f2023-06-14 09:51:56 +0200266 let mut previous: Option<&ProtoParsedFlag> = None;
267 for parsed_flag in pf.parsed_flag.iter() {
268 if let Some(prev) = previous {
269 let a = create_sorting_key(prev);
270 let b = create_sorting_key(parsed_flag);
271 match a.cmp(&b) {
272 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200273 Ordering::Equal => bail!(
274 "bad parsed flags: duplicate flag {} (defined in {} and {})",
275 a,
276 path_to_declaration(prev),
277 path_to_declaration(parsed_flag)
278 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200279 Ordering::Greater => {
280 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
281 }
282 }
283 }
284 super::parsed_flag::verify_fields(parsed_flag)?;
285 previous = Some(parsed_flag);
286 }
287 Ok(())
288 }
289
Colin Cross6befb342023-11-28 15:55:07 -0800290 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200291 let mut merged = ProtoParsedFlags::new();
292 for mut pfs in parsed_flags.into_iter() {
293 merged.parsed_flag.append(&mut pfs.parsed_flag);
294 }
295 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
Colin Cross6befb342023-11-28 15:55:07 -0800296 if dedup {
297 // Deduplicate identical protobuf messages. Messages with the same sorting key but
298 // different fields (including the path to the original source file) will not be
299 // deduplicated and trigger an error in verify_fields.
300 merged.parsed_flag.dedup();
301 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200302 verify_fields(&merged)?;
303 Ok(merged)
304 }
305
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000306 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
307 pf.parsed_flag.sort_by_key(create_sorting_key);
308 }
309
Mårten Kongstad403658f2023-06-14 09:51:56 +0200310 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
Mårten Kongstadf3f20ef2023-12-07 10:05:09 +0100311 pf.fully_qualified_name()
312 }
313}
314
315pub trait ParsedFlagExt {
316 fn fully_qualified_name(&self) -> String;
317}
318
319impl ParsedFlagExt for ProtoParsedFlag {
320 fn fully_qualified_name(&self) -> String {
321 format!("{}.{}", self.package(), self.name())
Mårten Kongstad403658f2023-06-14 09:51:56 +0200322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn test_flag_declarations_try_from_text_proto() {
331 // valid input
332 let flag_declarations = flag_declarations::try_from_text_proto(
333 r#"
334package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000335container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200336flag {
337 name: "first"
338 namespace: "first_ns"
339 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200340 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000341 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200342}
343flag {
344 name: "second"
345 namespace: "second_ns"
346 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200347 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000348 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200349}
350"#,
351 )
352 .unwrap();
353 assert_eq!(flag_declarations.package(), "com.foo.bar");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000354 assert_eq!(flag_declarations.container(), "system");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200355 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
356 assert_eq!(first.name(), "first");
357 assert_eq!(first.namespace(), "first_ns");
358 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200359 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000360 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000361 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200362 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
363 assert_eq!(second.name(), "second");
364 assert_eq!(second.namespace(), "second_ns");
365 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200366 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000367 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000368 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200369
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000370 // valid input: missing container in flag declarations is supported
371 let flag_declarations = flag_declarations::try_from_text_proto(
372 r#"
373package: "com.foo.bar"
374flag {
375 name: "first"
376 namespace: "first_ns"
377 description: "This is the description of the first flag."
378 bug: "123"
379}
380"#,
381 )
382 .unwrap();
383 assert_eq!(flag_declarations.container(), "");
384 assert!(!flag_declarations.has_container());
385
Mårten Kongstad403658f2023-06-14 09:51:56 +0200386 // bad input: missing package in flag declarations
387 let error = flag_declarations::try_from_text_proto(
388 r#"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000389container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200390flag {
391 name: "first"
392 namespace: "first_ns"
393 description: "This is the description of the first flag."
394}
395flag {
396 name: "second"
397 namespace: "second_ns"
398 description: "This is the description of the second flag."
399}
400"#,
401 )
402 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200403 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200404
405 // bad input: missing namespace in flag declaration
406 let error = flag_declarations::try_from_text_proto(
407 r#"
408package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000409container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200410flag {
411 name: "first"
412 description: "This is the description of the first flag."
413}
414flag {
415 name: "second"
416 namespace: "second_ns"
417 description: "This is the description of the second flag."
418}
419"#,
420 )
421 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200422 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200423
424 // bad input: bad package name in flag declarations
425 let error = flag_declarations::try_from_text_proto(
426 r#"
427package: "_com.FOO__BAR"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000428container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200429flag {
430 name: "first"
431 namespace: "first_ns"
432 description: "This is the description of the first flag."
433}
434flag {
435 name: "second"
436 namespace: "second_ns"
437 description: "This is the description of the second flag."
438}
439"#,
440 )
441 .unwrap_err();
442 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
443
444 // bad input: bad name in flag declaration
445 let error = flag_declarations::try_from_text_proto(
446 r#"
447package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000448container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200449flag {
450 name: "FIRST"
451 namespace: "first_ns"
452 description: "This is the description of the first flag."
453}
454flag {
455 name: "second"
456 namespace: "second_ns"
457 description: "This is the description of the second flag."
458}
459"#,
460 )
461 .unwrap_err();
462 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200463
464 // bad input: no bug entries in flag declaration
465 let error = flag_declarations::try_from_text_proto(
466 r#"
467package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000468container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200469flag {
470 name: "first"
471 namespace: "first_ns"
472 description: "This is the description of the first flag."
473}
474"#,
475 )
476 .unwrap_err();
477 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
478
479 // bad input: multiple bug entries in flag declaration
480 let error = flag_declarations::try_from_text_proto(
481 r#"
482package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000483container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200484flag {
485 name: "first"
486 namespace: "first_ns"
487 description: "This is the description of the first flag."
488 bug: "123"
489 bug: "abc"
490}
491"#,
492 )
493 .unwrap_err();
494 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000495
496 // bad input: invalid container name in flag declaration
497 let error = flag_declarations::try_from_text_proto(
498 r#"
499package: "com.foo.bar"
500container: "__bad_bad_container.com"
501flag {
502 name: "first"
503 namespace: "first_ns"
504 description: "This is the description of the first flag."
505 bug: "123"
506 bug: "abc"
507}
508"#,
509 )
510 .unwrap_err();
511 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
512
513 // TODO(b/312769710): Verify error when container is missing.
Mårten Kongstad403658f2023-06-14 09:51:56 +0200514 }
515
516 #[test]
517 fn test_flag_values_try_from_text_proto() {
518 // valid input
519 let flag_values = flag_values::try_from_text_proto(
520 r#"
521flag_value {
522 package: "com.first"
523 name: "first"
524 state: DISABLED
525 permission: READ_ONLY
526}
527flag_value {
528 package: "com.second"
529 name: "second"
530 state: ENABLED
531 permission: READ_WRITE
532}
533"#,
534 )
535 .unwrap();
536 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
537 assert_eq!(first.package(), "com.first");
538 assert_eq!(first.name(), "first");
539 assert_eq!(first.state(), ProtoFlagState::DISABLED);
540 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
541 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
542 assert_eq!(second.package(), "com.second");
543 assert_eq!(second.name(), "second");
544 assert_eq!(second.state(), ProtoFlagState::ENABLED);
545 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
546
547 // bad input: bad package in flag value
548 let error = flag_values::try_from_text_proto(
549 r#"
550flag_value {
551 package: "COM.FIRST"
552 name: "first"
553 state: DISABLED
554 permission: READ_ONLY
555}
556"#,
557 )
558 .unwrap_err();
559 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
560
561 // bad input: bad name in flag value
562 let error = flag_values::try_from_text_proto(
563 r#"
564flag_value {
565 package: "com.first"
566 name: "FIRST"
567 state: DISABLED
568 permission: READ_ONLY
569}
570"#,
571 )
572 .unwrap_err();
573 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
574
575 // bad input: missing state in flag value
576 let error = flag_values::try_from_text_proto(
577 r#"
578flag_value {
579 package: "com.first"
580 name: "first"
581 permission: READ_ONLY
582}
583"#,
584 )
585 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200586 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200587
588 // bad input: missing permission in flag value
589 let error = flag_values::try_from_text_proto(
590 r#"
591flag_value {
592 package: "com.first"
593 name: "first"
594 state: DISABLED
595}
596"#,
597 )
598 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200599 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200600 }
601
602 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
603 use protobuf::Message;
604
605 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
606 let mut binary_proto = Vec::new();
607 parsed_flags.write_to_vec(&mut binary_proto)?;
608 parsed_flags::try_from_binary_proto(&binary_proto)
609 }
610
611 #[test]
612 fn test_parsed_flags_try_from_text_proto() {
613 // valid input
614 let text_proto = r#"
615parsed_flag {
616 package: "com.first"
617 name: "first"
618 namespace: "first_ns"
619 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200620 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200621 state: DISABLED
622 permission: READ_ONLY
623 trace {
624 source: "flags.declarations"
625 state: DISABLED
626 permission: READ_ONLY
627 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000628 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200629}
630parsed_flag {
631 package: "com.second"
632 name: "second"
633 namespace: "second_ns"
634 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200635 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200636 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200637 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200638 trace {
639 source: "flags.declarations"
640 state: DISABLED
641 permission: READ_ONLY
642 }
643 trace {
644 source: "flags.values"
645 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200646 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200647 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000648 is_fixed_read_only: true
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000649 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200650}
651"#;
652 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
653 assert_eq!(parsed_flags.parsed_flag.len(), 2);
654 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
655 assert_eq!(second.package(), "com.second");
656 assert_eq!(second.name(), "second");
657 assert_eq!(second.namespace(), "second_ns");
658 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200659 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200660 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200661 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200662 assert_eq!(2, second.trace.len());
663 assert_eq!(second.trace[0].source(), "flags.declarations");
664 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
665 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
666 assert_eq!(second.trace[1].source(), "flags.values");
667 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200668 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000669 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200670
671 // valid input: empty
672 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
673 assert!(parsed_flags.parsed_flag.is_empty());
674
675 // bad input: empty trace
676 let text_proto = r#"
677parsed_flag {
678 package: "com.first"
679 name: "first"
680 namespace: "first_ns"
681 description: "This is the description of the first flag."
682 state: DISABLED
683 permission: READ_ONLY
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000684 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200685}
686"#;
687 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
688 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
689
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200690 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200691 let text_proto = r#"
692parsed_flag {
693 package: "com.first"
694 name: "first"
695 description: "This is the description of the first flag."
696 state: DISABLED
697 permission: READ_ONLY
698 trace {
699 source: "flags.declarations"
700 state: DISABLED
701 permission: READ_ONLY
702 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000703 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200704}
705"#;
706 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200707 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200708
709 // bad input: parsed_flag not sorted by package
710 let text_proto = r#"
711parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200712 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200713 name: "first"
714 namespace: "first_ns"
715 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200716 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200717 state: DISABLED
718 permission: READ_ONLY
719 trace {
720 source: "flags.declarations"
721 state: DISABLED
722 permission: READ_ONLY
723 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000724 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200725}
726parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200727 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200728 name: "second"
729 namespace: "second_ns"
730 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200731 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200732 state: ENABLED
733 permission: READ_WRITE
734 trace {
735 source: "flags.declarations"
736 state: DISABLED
737 permission: READ_ONLY
738 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000739 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200740}
741"#;
742 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
743 assert_eq!(
744 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200745 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200746 );
747
748 // bad input: parsed_flag not sorted by name
749 let text_proto = r#"
750parsed_flag {
751 package: "com.foo"
752 name: "bbb"
753 namespace: "first_ns"
754 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200755 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200756 state: DISABLED
757 permission: READ_ONLY
758 trace {
759 source: "flags.declarations"
760 state: DISABLED
761 permission: READ_ONLY
762 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000763 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200764}
765parsed_flag {
766 package: "com.foo"
767 name: "aaa"
768 namespace: "second_ns"
769 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200770 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200771 state: ENABLED
772 permission: READ_WRITE
773 trace {
774 source: "flags.declarations"
775 state: DISABLED
776 permission: READ_ONLY
777 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000778 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200779}
780"#;
781 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
782 assert_eq!(
783 format!("{:?}", error),
784 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
785 );
786
787 // bad input: duplicate flags
788 let text_proto = r#"
789parsed_flag {
790 package: "com.foo"
791 name: "bar"
792 namespace: "first_ns"
793 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200794 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200795 state: DISABLED
796 permission: READ_ONLY
797 trace {
798 source: "flags.declarations"
799 state: DISABLED
800 permission: READ_ONLY
801 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000802 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200803}
804parsed_flag {
805 package: "com.foo"
806 name: "bar"
807 namespace: "second_ns"
808 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200809 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200810 state: ENABLED
811 permission: READ_WRITE
812 trace {
813 source: "flags.declarations"
814 state: DISABLED
815 permission: READ_ONLY
816 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000817 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200818}
819"#;
820 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200821 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
822 }
823
824 #[test]
825 fn test_parsed_flag_path_to_declaration() {
826 let text_proto = r#"
827parsed_flag {
828 package: "com.foo"
829 name: "bar"
830 namespace: "first_ns"
831 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200832 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200833 state: DISABLED
834 permission: READ_ONLY
835 trace {
836 source: "flags.declarations"
837 state: DISABLED
838 permission: READ_ONLY
839 }
840 trace {
841 source: "flags.values"
842 state: ENABLED
843 permission: READ_ONLY
844 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000845 container: "system"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200846}
847"#;
848 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
849 let parsed_flag = &parsed_flags.parsed_flag[0];
850 assert_eq!(
851 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
852 "flags.declarations"
853 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200854 }
855
856 #[test]
857 fn test_parsed_flags_merge() {
858 let text_proto = r#"
859parsed_flag {
860 package: "com.first"
861 name: "first"
862 namespace: "first_ns"
863 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200864 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200865 state: DISABLED
866 permission: READ_ONLY
867 trace {
868 source: "flags.declarations"
869 state: DISABLED
870 permission: READ_ONLY
871 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000872 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200873}
874parsed_flag {
875 package: "com.second"
876 name: "second"
877 namespace: "second_ns"
878 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200879 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200880 state: ENABLED
881 permission: READ_WRITE
882 trace {
883 source: "flags.declarations"
884 state: DISABLED
885 permission: READ_ONLY
886 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000887 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200888}
889"#;
890 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
891
892 let text_proto = r#"
893parsed_flag {
894 package: "com.first"
895 name: "first"
896 namespace: "first_ns"
897 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200898 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200899 state: DISABLED
900 permission: READ_ONLY
901 trace {
902 source: "flags.declarations"
903 state: DISABLED
904 permission: READ_ONLY
905 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000906 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200907}
908"#;
909 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
910
911 let text_proto = r#"
912parsed_flag {
913 package: "com.second"
914 name: "second"
915 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200916 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200917 description: "This is the description of the second flag."
918 state: ENABLED
919 permission: READ_WRITE
920 trace {
921 source: "flags.declarations"
922 state: DISABLED
923 permission: READ_ONLY
924 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000925 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200926}
927"#;
928 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
929
Colin Cross6befb342023-11-28 15:55:07 -0800930 let text_proto = r#"
931parsed_flag {
932 package: "com.second"
933 name: "second"
934 namespace: "second_ns"
935 bug: "b"
936 description: "This is the description of the second flag."
937 state: ENABLED
938 permission: READ_WRITE
939 trace {
940 source: "duplicate/flags.declarations"
941 state: DISABLED
942 permission: READ_ONLY
943 }
944}
945"#;
946 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
947
Mårten Kongstad403658f2023-06-14 09:51:56 +0200948 // bad cases
Colin Cross6befb342023-11-28 15:55:07 -0800949
950 // two of the same flag with dedup disabled
951 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200952 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 +0200953
Colin Cross6befb342023-11-28 15:55:07 -0800954 // two conflicting flags with dedup disabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500955 let error =
956 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800957 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
958
959 // two conflicting flags with dedup enabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500960 let error =
961 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800962 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
963
Mårten Kongstad403658f2023-06-14 09:51:56 +0200964 // valid cases
Colin Cross6befb342023-11-28 15:55:07 -0800965 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
966 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
967 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
968 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500969 assert_eq!(
970 expected,
971 parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()
972 );
973 assert_eq!(
974 expected,
975 parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()
976 );
977 assert_eq!(
978 expected,
979 parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()
980 );
981 assert_eq!(
982 expected,
983 parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()
984 );
Colin Cross6befb342023-11-28 15:55:07 -0800985
986 // two identical flags with dedup enabled
987 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200988 }
989}