blob: 3d9089cb4272b95c5cae827b9e52b1723a199ac6 [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 {
32 pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
33 pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
34 pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
35 pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
36 pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
37 pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
38 pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
39 pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
40 pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
41}
Mårten Kongstada1029092023-05-08 11:51:59 +020042
Mårten Kongstadbb520722023-04-26 13:16:41 +020043// ---- When building with cargo ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020044#[cfg(feature = "cargo")]
Mårten Kongstadf9422522023-06-14 08:38:46 +020045mod auto_generated {
46 // include! statements should be avoided (because they import file contents verbatim), but
47 // because this is only used during local development, and only if using cargo instead of the
48 // Android tool-chain, we allow it
49 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
50 pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
51 pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
52 pub use aconfig::Flag_permission as ProtoFlagPermission;
53 pub use aconfig::Flag_state as ProtoFlagState;
54 pub use aconfig::Flag_value as ProtoFlagValue;
55 pub use aconfig::Flag_values as ProtoFlagValues;
56 pub use aconfig::Parsed_flag as ProtoParsedFlag;
57 pub use aconfig::Parsed_flags as ProtoParsedFlags;
58 pub use aconfig::Tracepoint as ProtoTracepoint;
59}
Mårten Kongstada1029092023-05-08 11:51:59 +020060
Mårten Kongstadbb520722023-04-26 13:16:41 +020061// ---- Common for both the Android tool-chain and cargo ----
Mårten Kongstadf9422522023-06-14 08:38:46 +020062pub use auto_generated::*;
63
Mårten Kongstadbb520722023-04-26 13:16:41 +020064use anyhow::Result;
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020065use paste::paste;
Mårten Kongstadbb520722023-04-26 13:16:41 +020066
Mårten Kongstad403658f2023-06-14 09:51:56 +020067fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020068where
69 T: protobuf::MessageFull,
70{
Mårten Kongstadbb520722023-04-26 13:16:41 +020071 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
72}
Mårten Kongstad403658f2023-06-14 09:51:56 +020073
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020074macro_rules! ensure_required_fields {
75 ($type:expr, $struct:expr, $($field:expr),+) => {
76 $(
77 paste! {
78 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
79 }
80 )+
81 };
82}
83
Mårten Kongstad403658f2023-06-14 09:51:56 +020084pub mod flag_declaration {
85 use super::*;
86 use crate::codegen;
87 use anyhow::ensure;
88
89 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020090 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020091
Mårten Kongstad403658f2023-06-14 09:51:56 +020092 ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
93 ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
94 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +020095 ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020096
Mårten Kongstad403658f2023-06-14 09:51:56 +020097 Ok(())
98 }
99}
100
101pub mod flag_declarations {
102 use super::*;
103 use crate::codegen;
104 use anyhow::ensure;
105
106 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
107 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
108 verify_fields(&pdf)?;
109 Ok(pdf)
110 }
111
112 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200113 ensure_required_fields!("flag declarations", pdf, "package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000114 // TODO(b/312769710): Make the container field required.
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200115
Mårten Kongstad403658f2023-06-14 09:51:56 +0200116 ensure!(
117 codegen::is_valid_package_ident(pdf.package()),
118 "bad flag declarations: bad package"
119 );
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000120 ensure!(
121 !pdf.has_container() || codegen::is_valid_container_ident(pdf.container()),
122 "bad flag declarations: bad container"
123 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200124 for flag_declaration in pdf.flag.iter() {
125 super::flag_declaration::verify_fields(flag_declaration)?;
126 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200127
Mårten Kongstad403658f2023-06-14 09:51:56 +0200128 Ok(())
129 }
130}
131
132pub mod flag_value {
133 use super::*;
134 use crate::codegen;
135 use anyhow::ensure;
136
137 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200138 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200139
Mårten Kongstad403658f2023-06-14 09:51:56 +0200140 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
141 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200142
Mårten Kongstad403658f2023-06-14 09:51:56 +0200143 Ok(())
144 }
145}
146
147pub mod flag_values {
148 use super::*;
149
150 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
151 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
152 verify_fields(&pfv)?;
153 Ok(pfv)
154 }
155
156 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
157 for flag_value in pfv.flag_value.iter() {
158 super::flag_value::verify_fields(flag_value)?;
159 }
160 Ok(())
161 }
162}
163
Zhi Dou24a0b6a2023-08-10 21:39:59 +0000164pub mod flag_permission {
165 use super::*;
166 use anyhow::bail;
167
168 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
169 match permission.to_ascii_lowercase().as_str() {
170 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
171 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
172 _ => bail!("Permission needs to be read_only or read_write."),
173 }
174 }
175
176 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
177 match permission {
178 ProtoFlagPermission::READ_WRITE => "read_write",
179 ProtoFlagPermission::READ_ONLY => "read_only",
180 }
181 }
182}
183
Mårten Kongstad403658f2023-06-14 09:51:56 +0200184pub mod tracepoint {
185 use super::*;
186 use anyhow::ensure;
187
188 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200189 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200190
Mårten Kongstad403658f2023-06-14 09:51:56 +0200191 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200192
Mårten Kongstad403658f2023-06-14 09:51:56 +0200193 Ok(())
194 }
195}
196
197pub mod parsed_flag {
198 use super::*;
199 use crate::codegen;
200 use anyhow::ensure;
201
202 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200203 ensure_required_fields!(
204 "parsed flag",
205 pf,
206 "package",
207 "name",
208 "namespace",
209 "description",
210 "state",
211 "permission"
212 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200213
Mårten Kongstad403658f2023-06-14 09:51:56 +0200214 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000215 ensure!(
216 !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
217 "bad parsed flag: bad container"
218 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200219 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
220 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
221 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
222 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
223 for tp in pf.trace.iter() {
224 super::tracepoint::verify_fields(tp)?;
225 }
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200226 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad21717e72023-09-04 13:28:36 +0200227 if pf.is_fixed_read_only() {
228 ensure!(
229 pf.permission() == ProtoFlagPermission::READ_ONLY,
230 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
231 );
232 for tp in pf.trace.iter() {
233 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
234 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
235 );
236 }
237 }
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200238
Mårten Kongstad403658f2023-06-14 09:51:56 +0200239 Ok(())
240 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200241
242 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
243 debug_assert!(!pf.trace.is_empty());
244 pf.trace[0].source()
245 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200246}
247
248pub mod parsed_flags {
249 use super::*;
250 use anyhow::bail;
251 use std::cmp::Ordering;
252
253 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
254 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
255 verify_fields(&message)?;
256 Ok(message)
257 }
258
259 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200260 use crate::protos::parsed_flag::path_to_declaration;
261
Mårten Kongstad403658f2023-06-14 09:51:56 +0200262 let mut previous: Option<&ProtoParsedFlag> = None;
263 for parsed_flag in pf.parsed_flag.iter() {
264 if let Some(prev) = previous {
265 let a = create_sorting_key(prev);
266 let b = create_sorting_key(parsed_flag);
267 match a.cmp(&b) {
268 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200269 Ordering::Equal => bail!(
270 "bad parsed flags: duplicate flag {} (defined in {} and {})",
271 a,
272 path_to_declaration(prev),
273 path_to_declaration(parsed_flag)
274 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200275 Ordering::Greater => {
276 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
277 }
278 }
279 }
280 super::parsed_flag::verify_fields(parsed_flag)?;
281 previous = Some(parsed_flag);
282 }
283 Ok(())
284 }
285
Colin Cross6befb342023-11-28 15:55:07 -0800286 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200287 let mut merged = ProtoParsedFlags::new();
288 for mut pfs in parsed_flags.into_iter() {
289 merged.parsed_flag.append(&mut pfs.parsed_flag);
290 }
291 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
Colin Cross6befb342023-11-28 15:55:07 -0800292 if dedup {
293 // Deduplicate identical protobuf messages. Messages with the same sorting key but
294 // different fields (including the path to the original source file) will not be
295 // deduplicated and trigger an error in verify_fields.
296 merged.parsed_flag.dedup();
297 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200298 verify_fields(&merged)?;
299 Ok(merged)
300 }
301
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000302 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
303 pf.parsed_flag.sort_by_key(create_sorting_key);
304 }
305
Mårten Kongstad403658f2023-06-14 09:51:56 +0200306 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
307 format!("{}.{}", pf.package(), pf.name())
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_flag_declarations_try_from_text_proto() {
317 // valid input
318 let flag_declarations = flag_declarations::try_from_text_proto(
319 r#"
320package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000321container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200322flag {
323 name: "first"
324 namespace: "first_ns"
325 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200326 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000327 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200328}
329flag {
330 name: "second"
331 namespace: "second_ns"
332 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200333 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000334 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200335}
336"#,
337 )
338 .unwrap();
339 assert_eq!(flag_declarations.package(), "com.foo.bar");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000340 assert_eq!(flag_declarations.container(), "system");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200341 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
342 assert_eq!(first.name(), "first");
343 assert_eq!(first.namespace(), "first_ns");
344 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200345 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000346 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000347 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200348 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
349 assert_eq!(second.name(), "second");
350 assert_eq!(second.namespace(), "second_ns");
351 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200352 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000353 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000354 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200355
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000356 // valid input: missing container in flag declarations is supported
357 let flag_declarations = flag_declarations::try_from_text_proto(
358 r#"
359package: "com.foo.bar"
360flag {
361 name: "first"
362 namespace: "first_ns"
363 description: "This is the description of the first flag."
364 bug: "123"
365}
366"#,
367 )
368 .unwrap();
369 assert_eq!(flag_declarations.container(), "");
370 assert!(!flag_declarations.has_container());
371
Mårten Kongstad403658f2023-06-14 09:51:56 +0200372 // bad input: missing package in flag declarations
373 let error = flag_declarations::try_from_text_proto(
374 r#"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000375container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200376flag {
377 name: "first"
378 namespace: "first_ns"
379 description: "This is the description of the first flag."
380}
381flag {
382 name: "second"
383 namespace: "second_ns"
384 description: "This is the description of the second flag."
385}
386"#,
387 )
388 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200389 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200390
391 // bad input: missing namespace in flag declaration
392 let error = flag_declarations::try_from_text_proto(
393 r#"
394package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000395container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200396flag {
397 name: "first"
398 description: "This is the description of the first flag."
399}
400flag {
401 name: "second"
402 namespace: "second_ns"
403 description: "This is the description of the second flag."
404}
405"#,
406 )
407 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200408 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200409
410 // bad input: bad package name in flag declarations
411 let error = flag_declarations::try_from_text_proto(
412 r#"
413package: "_com.FOO__BAR"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000414container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200415flag {
416 name: "first"
417 namespace: "first_ns"
418 description: "This is the description of the first flag."
419}
420flag {
421 name: "second"
422 namespace: "second_ns"
423 description: "This is the description of the second flag."
424}
425"#,
426 )
427 .unwrap_err();
428 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
429
430 // bad input: bad name in flag declaration
431 let error = flag_declarations::try_from_text_proto(
432 r#"
433package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000434container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200435flag {
436 name: "FIRST"
437 namespace: "first_ns"
438 description: "This is the description of the first flag."
439}
440flag {
441 name: "second"
442 namespace: "second_ns"
443 description: "This is the description of the second flag."
444}
445"#,
446 )
447 .unwrap_err();
448 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200449
450 // bad input: no bug entries in flag declaration
451 let error = flag_declarations::try_from_text_proto(
452 r#"
453package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000454container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200455flag {
456 name: "first"
457 namespace: "first_ns"
458 description: "This is the description of the first flag."
459}
460"#,
461 )
462 .unwrap_err();
463 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
464
465 // bad input: multiple bug entries in flag declaration
466 let error = flag_declarations::try_from_text_proto(
467 r#"
468package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000469container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200470flag {
471 name: "first"
472 namespace: "first_ns"
473 description: "This is the description of the first flag."
474 bug: "123"
475 bug: "abc"
476}
477"#,
478 )
479 .unwrap_err();
480 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000481
482 // bad input: invalid container name in flag declaration
483 let error = flag_declarations::try_from_text_proto(
484 r#"
485package: "com.foo.bar"
486container: "__bad_bad_container.com"
487flag {
488 name: "first"
489 namespace: "first_ns"
490 description: "This is the description of the first flag."
491 bug: "123"
492 bug: "abc"
493}
494"#,
495 )
496 .unwrap_err();
497 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
498
499 // TODO(b/312769710): Verify error when container is missing.
Mårten Kongstad403658f2023-06-14 09:51:56 +0200500 }
501
502 #[test]
503 fn test_flag_values_try_from_text_proto() {
504 // valid input
505 let flag_values = flag_values::try_from_text_proto(
506 r#"
507flag_value {
508 package: "com.first"
509 name: "first"
510 state: DISABLED
511 permission: READ_ONLY
512}
513flag_value {
514 package: "com.second"
515 name: "second"
516 state: ENABLED
517 permission: READ_WRITE
518}
519"#,
520 )
521 .unwrap();
522 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
523 assert_eq!(first.package(), "com.first");
524 assert_eq!(first.name(), "first");
525 assert_eq!(first.state(), ProtoFlagState::DISABLED);
526 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
527 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
528 assert_eq!(second.package(), "com.second");
529 assert_eq!(second.name(), "second");
530 assert_eq!(second.state(), ProtoFlagState::ENABLED);
531 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
532
533 // bad input: bad package in flag value
534 let error = flag_values::try_from_text_proto(
535 r#"
536flag_value {
537 package: "COM.FIRST"
538 name: "first"
539 state: DISABLED
540 permission: READ_ONLY
541}
542"#,
543 )
544 .unwrap_err();
545 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
546
547 // bad input: bad name 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 name"));
560
561 // bad input: missing state in flag value
562 let error = flag_values::try_from_text_proto(
563 r#"
564flag_value {
565 package: "com.first"
566 name: "first"
567 permission: READ_ONLY
568}
569"#,
570 )
571 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200572 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200573
574 // bad input: missing permission in flag value
575 let error = flag_values::try_from_text_proto(
576 r#"
577flag_value {
578 package: "com.first"
579 name: "first"
580 state: DISABLED
581}
582"#,
583 )
584 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200585 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200586 }
587
588 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
589 use protobuf::Message;
590
591 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
592 let mut binary_proto = Vec::new();
593 parsed_flags.write_to_vec(&mut binary_proto)?;
594 parsed_flags::try_from_binary_proto(&binary_proto)
595 }
596
597 #[test]
598 fn test_parsed_flags_try_from_text_proto() {
599 // valid input
600 let text_proto = r#"
601parsed_flag {
602 package: "com.first"
603 name: "first"
604 namespace: "first_ns"
605 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200606 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200607 state: DISABLED
608 permission: READ_ONLY
609 trace {
610 source: "flags.declarations"
611 state: DISABLED
612 permission: READ_ONLY
613 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000614 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200615}
616parsed_flag {
617 package: "com.second"
618 name: "second"
619 namespace: "second_ns"
620 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200621 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200622 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200623 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200624 trace {
625 source: "flags.declarations"
626 state: DISABLED
627 permission: READ_ONLY
628 }
629 trace {
630 source: "flags.values"
631 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200632 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200633 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000634 is_fixed_read_only: true
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000635 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200636}
637"#;
638 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
639 assert_eq!(parsed_flags.parsed_flag.len(), 2);
640 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
641 assert_eq!(second.package(), "com.second");
642 assert_eq!(second.name(), "second");
643 assert_eq!(second.namespace(), "second_ns");
644 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200645 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200646 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200647 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200648 assert_eq!(2, second.trace.len());
649 assert_eq!(second.trace[0].source(), "flags.declarations");
650 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
651 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
652 assert_eq!(second.trace[1].source(), "flags.values");
653 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200654 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000655 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200656
657 // valid input: empty
658 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
659 assert!(parsed_flags.parsed_flag.is_empty());
660
661 // bad input: empty trace
662 let text_proto = r#"
663parsed_flag {
664 package: "com.first"
665 name: "first"
666 namespace: "first_ns"
667 description: "This is the description of the first flag."
668 state: DISABLED
669 permission: READ_ONLY
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000670 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200671}
672"#;
673 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
674 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
675
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200676 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200677 let text_proto = r#"
678parsed_flag {
679 package: "com.first"
680 name: "first"
681 description: "This is the description of the first flag."
682 state: DISABLED
683 permission: READ_ONLY
684 trace {
685 source: "flags.declarations"
686 state: DISABLED
687 permission: READ_ONLY
688 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000689 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200690}
691"#;
692 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200693 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200694
695 // bad input: parsed_flag not sorted by package
696 let text_proto = r#"
697parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200698 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200699 name: "first"
700 namespace: "first_ns"
701 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200702 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200703 state: DISABLED
704 permission: READ_ONLY
705 trace {
706 source: "flags.declarations"
707 state: DISABLED
708 permission: READ_ONLY
709 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000710 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200711}
712parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200713 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200714 name: "second"
715 namespace: "second_ns"
716 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200717 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200718 state: ENABLED
719 permission: READ_WRITE
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();
729 assert_eq!(
730 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200731 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200732 );
733
734 // bad input: parsed_flag not sorted by name
735 let text_proto = r#"
736parsed_flag {
737 package: "com.foo"
738 name: "bbb"
739 namespace: "first_ns"
740 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200741 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200742 state: DISABLED
743 permission: READ_ONLY
744 trace {
745 source: "flags.declarations"
746 state: DISABLED
747 permission: READ_ONLY
748 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000749 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200750}
751parsed_flag {
752 package: "com.foo"
753 name: "aaa"
754 namespace: "second_ns"
755 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200756 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200757 state: ENABLED
758 permission: READ_WRITE
759 trace {
760 source: "flags.declarations"
761 state: DISABLED
762 permission: READ_ONLY
763 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000764 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200765}
766"#;
767 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
768 assert_eq!(
769 format!("{:?}", error),
770 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
771 );
772
773 // bad input: duplicate flags
774 let text_proto = r#"
775parsed_flag {
776 package: "com.foo"
777 name: "bar"
778 namespace: "first_ns"
779 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200780 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200781 state: DISABLED
782 permission: READ_ONLY
783 trace {
784 source: "flags.declarations"
785 state: DISABLED
786 permission: READ_ONLY
787 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000788 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200789}
790parsed_flag {
791 package: "com.foo"
792 name: "bar"
793 namespace: "second_ns"
794 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200795 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200796 state: ENABLED
797 permission: READ_WRITE
798 trace {
799 source: "flags.declarations"
800 state: DISABLED
801 permission: READ_ONLY
802 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000803 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200804}
805"#;
806 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200807 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
808 }
809
810 #[test]
811 fn test_parsed_flag_path_to_declaration() {
812 let text_proto = r#"
813parsed_flag {
814 package: "com.foo"
815 name: "bar"
816 namespace: "first_ns"
817 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200818 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200819 state: DISABLED
820 permission: READ_ONLY
821 trace {
822 source: "flags.declarations"
823 state: DISABLED
824 permission: READ_ONLY
825 }
826 trace {
827 source: "flags.values"
828 state: ENABLED
829 permission: READ_ONLY
830 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000831 container: "system"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200832}
833"#;
834 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
835 let parsed_flag = &parsed_flags.parsed_flag[0];
836 assert_eq!(
837 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
838 "flags.declarations"
839 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200840 }
841
842 #[test]
843 fn test_parsed_flags_merge() {
844 let text_proto = r#"
845parsed_flag {
846 package: "com.first"
847 name: "first"
848 namespace: "first_ns"
849 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200850 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200851 state: DISABLED
852 permission: READ_ONLY
853 trace {
854 source: "flags.declarations"
855 state: DISABLED
856 permission: READ_ONLY
857 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000858 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200859}
860parsed_flag {
861 package: "com.second"
862 name: "second"
863 namespace: "second_ns"
864 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200865 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200866 state: ENABLED
867 permission: READ_WRITE
868 trace {
869 source: "flags.declarations"
870 state: DISABLED
871 permission: READ_ONLY
872 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000873 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200874}
875"#;
876 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
877
878 let text_proto = r#"
879parsed_flag {
880 package: "com.first"
881 name: "first"
882 namespace: "first_ns"
883 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200884 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200885 state: DISABLED
886 permission: READ_ONLY
887 trace {
888 source: "flags.declarations"
889 state: DISABLED
890 permission: READ_ONLY
891 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000892 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200893}
894"#;
895 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
896
897 let text_proto = r#"
898parsed_flag {
899 package: "com.second"
900 name: "second"
901 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200902 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200903 description: "This is the description of the second flag."
904 state: ENABLED
905 permission: READ_WRITE
906 trace {
907 source: "flags.declarations"
908 state: DISABLED
909 permission: READ_ONLY
910 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000911 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200912}
913"#;
914 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
915
Colin Cross6befb342023-11-28 15:55:07 -0800916 let text_proto = r#"
917parsed_flag {
918 package: "com.second"
919 name: "second"
920 namespace: "second_ns"
921 bug: "b"
922 description: "This is the description of the second flag."
923 state: ENABLED
924 permission: READ_WRITE
925 trace {
926 source: "duplicate/flags.declarations"
927 state: DISABLED
928 permission: READ_ONLY
929 }
930}
931"#;
932 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
933
Mårten Kongstad403658f2023-06-14 09:51:56 +0200934 // bad cases
Colin Cross6befb342023-11-28 15:55:07 -0800935
936 // two of the same flag with dedup disabled
937 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200938 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 +0200939
Colin Cross6befb342023-11-28 15:55:07 -0800940 // two conflicting flags with dedup disabled
941 let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
942 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
943
944 // two conflicting flags with dedup enabled
945 let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
946 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
947
Mårten Kongstad403658f2023-06-14 09:51:56 +0200948 // valid cases
Colin Cross6befb342023-11-28 15:55:07 -0800949 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
950 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
951 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
952 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
953 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap());
954 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap());
955 assert_eq!(expected, parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap());
956 assert_eq!(expected, parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap());
957
958 // two identical flags with dedup enabled
959 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200960 }
961}