blob: c11596ba57dc767d18e2f8ae9198cbf5aa1d940b [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
286 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
287 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);
292 verify_fields(&merged)?;
293 Ok(merged)
294 }
295
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000296 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
297 pf.parsed_flag.sort_by_key(create_sorting_key);
298 }
299
Mårten Kongstad403658f2023-06-14 09:51:56 +0200300 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
301 format!("{}.{}", pf.package(), pf.name())
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_flag_declarations_try_from_text_proto() {
311 // valid input
312 let flag_declarations = flag_declarations::try_from_text_proto(
313 r#"
314package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000315container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200316flag {
317 name: "first"
318 namespace: "first_ns"
319 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200320 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000321 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200322}
323flag {
324 name: "second"
325 namespace: "second_ns"
326 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200327 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000328 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200329}
330"#,
331 )
332 .unwrap();
333 assert_eq!(flag_declarations.package(), "com.foo.bar");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000334 assert_eq!(flag_declarations.container(), "system");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200335 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
336 assert_eq!(first.name(), "first");
337 assert_eq!(first.namespace(), "first_ns");
338 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200339 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000340 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000341 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200342 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
343 assert_eq!(second.name(), "second");
344 assert_eq!(second.namespace(), "second_ns");
345 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200346 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000347 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000348 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200349
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000350 // valid input: missing container in flag declarations is supported
351 let flag_declarations = flag_declarations::try_from_text_proto(
352 r#"
353package: "com.foo.bar"
354flag {
355 name: "first"
356 namespace: "first_ns"
357 description: "This is the description of the first flag."
358 bug: "123"
359}
360"#,
361 )
362 .unwrap();
363 assert_eq!(flag_declarations.container(), "");
364 assert!(!flag_declarations.has_container());
365
Mårten Kongstad403658f2023-06-14 09:51:56 +0200366 // bad input: missing package in flag declarations
367 let error = flag_declarations::try_from_text_proto(
368 r#"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000369container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200370flag {
371 name: "first"
372 namespace: "first_ns"
373 description: "This is the description of the first flag."
374}
375flag {
376 name: "second"
377 namespace: "second_ns"
378 description: "This is the description of the second flag."
379}
380"#,
381 )
382 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200383 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200384
385 // bad input: missing namespace in flag declaration
386 let error = flag_declarations::try_from_text_proto(
387 r#"
388package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000389container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200390flag {
391 name: "first"
392 description: "This is the description of the first flag."
393}
394flag {
395 name: "second"
396 namespace: "second_ns"
397 description: "This is the description of the second flag."
398}
399"#,
400 )
401 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200402 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200403
404 // bad input: bad package name in flag declarations
405 let error = flag_declarations::try_from_text_proto(
406 r#"
407package: "_com.FOO__BAR"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000408container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200409flag {
410 name: "first"
411 namespace: "first_ns"
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();
422 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
423
424 // bad input: bad name in flag declaration
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 declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200443
444 // bad input: no bug entries 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 Kongstad6353c6c2023-07-26 13:18:50 +0200449flag {
450 name: "first"
451 namespace: "first_ns"
452 description: "This is the description of the first flag."
453}
454"#,
455 )
456 .unwrap_err();
457 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
458
459 // bad input: multiple bug entries in flag declaration
460 let error = flag_declarations::try_from_text_proto(
461 r#"
462package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000463container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200464flag {
465 name: "first"
466 namespace: "first_ns"
467 description: "This is the description of the first flag."
468 bug: "123"
469 bug: "abc"
470}
471"#,
472 )
473 .unwrap_err();
474 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000475
476 // bad input: invalid container name in flag declaration
477 let error = flag_declarations::try_from_text_proto(
478 r#"
479package: "com.foo.bar"
480container: "__bad_bad_container.com"
481flag {
482 name: "first"
483 namespace: "first_ns"
484 description: "This is the description of the first flag."
485 bug: "123"
486 bug: "abc"
487}
488"#,
489 )
490 .unwrap_err();
491 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
492
493 // TODO(b/312769710): Verify error when container is missing.
Mårten Kongstad403658f2023-06-14 09:51:56 +0200494 }
495
496 #[test]
497 fn test_flag_values_try_from_text_proto() {
498 // valid input
499 let flag_values = flag_values::try_from_text_proto(
500 r#"
501flag_value {
502 package: "com.first"
503 name: "first"
504 state: DISABLED
505 permission: READ_ONLY
506}
507flag_value {
508 package: "com.second"
509 name: "second"
510 state: ENABLED
511 permission: READ_WRITE
512}
513"#,
514 )
515 .unwrap();
516 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
517 assert_eq!(first.package(), "com.first");
518 assert_eq!(first.name(), "first");
519 assert_eq!(first.state(), ProtoFlagState::DISABLED);
520 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
521 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
522 assert_eq!(second.package(), "com.second");
523 assert_eq!(second.name(), "second");
524 assert_eq!(second.state(), ProtoFlagState::ENABLED);
525 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
526
527 // bad input: bad package in flag value
528 let error = flag_values::try_from_text_proto(
529 r#"
530flag_value {
531 package: "COM.FIRST"
532 name: "first"
533 state: DISABLED
534 permission: READ_ONLY
535}
536"#,
537 )
538 .unwrap_err();
539 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
540
541 // bad input: bad name in flag value
542 let error = flag_values::try_from_text_proto(
543 r#"
544flag_value {
545 package: "com.first"
546 name: "FIRST"
547 state: DISABLED
548 permission: READ_ONLY
549}
550"#,
551 )
552 .unwrap_err();
553 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
554
555 // bad input: missing state in flag value
556 let error = flag_values::try_from_text_proto(
557 r#"
558flag_value {
559 package: "com.first"
560 name: "first"
561 permission: READ_ONLY
562}
563"#,
564 )
565 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200566 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200567
568 // bad input: missing permission in flag value
569 let error = flag_values::try_from_text_proto(
570 r#"
571flag_value {
572 package: "com.first"
573 name: "first"
574 state: DISABLED
575}
576"#,
577 )
578 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200579 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200580 }
581
582 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
583 use protobuf::Message;
584
585 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
586 let mut binary_proto = Vec::new();
587 parsed_flags.write_to_vec(&mut binary_proto)?;
588 parsed_flags::try_from_binary_proto(&binary_proto)
589 }
590
591 #[test]
592 fn test_parsed_flags_try_from_text_proto() {
593 // valid input
594 let text_proto = r#"
595parsed_flag {
596 package: "com.first"
597 name: "first"
598 namespace: "first_ns"
599 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200600 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200601 state: DISABLED
602 permission: READ_ONLY
603 trace {
604 source: "flags.declarations"
605 state: DISABLED
606 permission: READ_ONLY
607 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000608 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200609}
610parsed_flag {
611 package: "com.second"
612 name: "second"
613 namespace: "second_ns"
614 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200615 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200616 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200617 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200618 trace {
619 source: "flags.declarations"
620 state: DISABLED
621 permission: READ_ONLY
622 }
623 trace {
624 source: "flags.values"
625 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200626 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200627 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000628 is_fixed_read_only: true
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000629 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200630}
631"#;
632 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
633 assert_eq!(parsed_flags.parsed_flag.len(), 2);
634 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
635 assert_eq!(second.package(), "com.second");
636 assert_eq!(second.name(), "second");
637 assert_eq!(second.namespace(), "second_ns");
638 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200639 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200640 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200641 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200642 assert_eq!(2, second.trace.len());
643 assert_eq!(second.trace[0].source(), "flags.declarations");
644 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
645 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
646 assert_eq!(second.trace[1].source(), "flags.values");
647 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200648 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000649 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200650
651 // valid input: empty
652 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
653 assert!(parsed_flags.parsed_flag.is_empty());
654
655 // bad input: empty trace
656 let text_proto = r#"
657parsed_flag {
658 package: "com.first"
659 name: "first"
660 namespace: "first_ns"
661 description: "This is the description of the first flag."
662 state: DISABLED
663 permission: READ_ONLY
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000664 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200665}
666"#;
667 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
668 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
669
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200670 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200671 let text_proto = r#"
672parsed_flag {
673 package: "com.first"
674 name: "first"
675 description: "This is the description of the first flag."
676 state: DISABLED
677 permission: READ_ONLY
678 trace {
679 source: "flags.declarations"
680 state: DISABLED
681 permission: READ_ONLY
682 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000683 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200684}
685"#;
686 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200687 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200688
689 // bad input: parsed_flag not sorted by package
690 let text_proto = r#"
691parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200692 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200693 name: "first"
694 namespace: "first_ns"
695 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200696 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200697 state: DISABLED
698 permission: READ_ONLY
699 trace {
700 source: "flags.declarations"
701 state: DISABLED
702 permission: READ_ONLY
703 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000704 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200705}
706parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200707 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200708 name: "second"
709 namespace: "second_ns"
710 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200711 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200712 state: ENABLED
713 permission: READ_WRITE
714 trace {
715 source: "flags.declarations"
716 state: DISABLED
717 permission: READ_ONLY
718 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000719 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200720}
721"#;
722 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
723 assert_eq!(
724 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200725 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200726 );
727
728 // bad input: parsed_flag not sorted by name
729 let text_proto = r#"
730parsed_flag {
731 package: "com.foo"
732 name: "bbb"
733 namespace: "first_ns"
734 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200735 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200736 state: DISABLED
737 permission: READ_ONLY
738 trace {
739 source: "flags.declarations"
740 state: DISABLED
741 permission: READ_ONLY
742 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000743 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200744}
745parsed_flag {
746 package: "com.foo"
747 name: "aaa"
748 namespace: "second_ns"
749 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200750 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200751 state: ENABLED
752 permission: READ_WRITE
753 trace {
754 source: "flags.declarations"
755 state: DISABLED
756 permission: READ_ONLY
757 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000758 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200759}
760"#;
761 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
762 assert_eq!(
763 format!("{:?}", error),
764 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
765 );
766
767 // bad input: duplicate flags
768 let text_proto = r#"
769parsed_flag {
770 package: "com.foo"
771 name: "bar"
772 namespace: "first_ns"
773 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200774 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200775 state: DISABLED
776 permission: READ_ONLY
777 trace {
778 source: "flags.declarations"
779 state: DISABLED
780 permission: READ_ONLY
781 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000782 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200783}
784parsed_flag {
785 package: "com.foo"
786 name: "bar"
787 namespace: "second_ns"
788 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200789 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200790 state: ENABLED
791 permission: READ_WRITE
792 trace {
793 source: "flags.declarations"
794 state: DISABLED
795 permission: READ_ONLY
796 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000797 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200798}
799"#;
800 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200801 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
802 }
803
804 #[test]
805 fn test_parsed_flag_path_to_declaration() {
806 let text_proto = r#"
807parsed_flag {
808 package: "com.foo"
809 name: "bar"
810 namespace: "first_ns"
811 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200812 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200813 state: DISABLED
814 permission: READ_ONLY
815 trace {
816 source: "flags.declarations"
817 state: DISABLED
818 permission: READ_ONLY
819 }
820 trace {
821 source: "flags.values"
822 state: ENABLED
823 permission: READ_ONLY
824 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000825 container: "system"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200826}
827"#;
828 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
829 let parsed_flag = &parsed_flags.parsed_flag[0];
830 assert_eq!(
831 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
832 "flags.declarations"
833 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200834 }
835
836 #[test]
837 fn test_parsed_flags_merge() {
838 let text_proto = r#"
839parsed_flag {
840 package: "com.first"
841 name: "first"
842 namespace: "first_ns"
843 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200844 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200845 state: DISABLED
846 permission: READ_ONLY
847 trace {
848 source: "flags.declarations"
849 state: DISABLED
850 permission: READ_ONLY
851 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000852 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200853}
854parsed_flag {
855 package: "com.second"
856 name: "second"
857 namespace: "second_ns"
858 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200859 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200860 state: ENABLED
861 permission: READ_WRITE
862 trace {
863 source: "flags.declarations"
864 state: DISABLED
865 permission: READ_ONLY
866 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000867 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200868}
869"#;
870 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
871
872 let text_proto = r#"
873parsed_flag {
874 package: "com.first"
875 name: "first"
876 namespace: "first_ns"
877 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200878 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200879 state: DISABLED
880 permission: READ_ONLY
881 trace {
882 source: "flags.declarations"
883 state: DISABLED
884 permission: READ_ONLY
885 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000886 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200887}
888"#;
889 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
890
891 let text_proto = r#"
892parsed_flag {
893 package: "com.second"
894 name: "second"
895 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200896 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200897 description: "This is the description of the second flag."
898 state: ENABLED
899 permission: READ_WRITE
900 trace {
901 source: "flags.declarations"
902 state: DISABLED
903 permission: READ_ONLY
904 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000905 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200906}
907"#;
908 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
909
910 // bad cases
911 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200912 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 +0200913
914 // valid cases
915 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
916 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
917 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
918 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
919 }
920}