1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! `aconfig_protos` is a crate for the protos defined for aconfig
18 // When building with the Android tool-chain
19 //
20 // - an external crate `aconfig_protos` will be generated
21 // - the feature "cargo" will be disabled
22 //
23 // When building with cargo
24 //
25 // - a local sub-module will be generated in OUT_DIR and included in this file
26 // - the feature "cargo" will be enabled
27 //
28 // This module hides these differences from the rest of aconfig.
29
30 // ---- When building with the Android tool-chain ----
31 #[cfg(not(feature = "cargo"))]
32 mod auto_generated {
33 pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
34 pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
35 pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
36 pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
37 pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
38 pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
39 pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
40 pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
41 pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
42 pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
43 pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
44 }
45
46 // ---- When building with cargo ----
47 #[cfg(feature = "cargo")]
48 mod auto_generated {
49 // include! statements should be avoided (because they import file contents verbatim), but
50 // because this is only used during local development, and only if using cargo instead of the
51 // Android tool-chain, we allow it
52 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
53 pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
54 pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
55 pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
56 pub use aconfig::Flag_metadata as ProtoFlagMetadata;
57 pub use aconfig::Flag_permission as ProtoFlagPermission;
58 pub use aconfig::Flag_state as ProtoFlagState;
59 pub use aconfig::Flag_value as ProtoFlagValue;
60 pub use aconfig::Flag_values as ProtoFlagValues;
61 pub use aconfig::Parsed_flag as ProtoParsedFlag;
62 pub use aconfig::Parsed_flags as ProtoParsedFlags;
63 pub use aconfig::Tracepoint as ProtoTracepoint;
64 }
65
66 // ---- Common for both the Android tool-chain and cargo ----
67 pub use auto_generated::*;
68
69 use anyhow::Result;
70 use paste::paste;
71
72 /// Path to proto file
73 const ACONFIG_PROTO_PATH: &str = "//build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto";
74
75 /// Check if the name identifier is valid
is_valid_name_ident(s: &str) -> bool76 pub fn is_valid_name_ident(s: &str) -> bool {
77 // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
78 if s.contains("__") {
79 return false;
80 }
81 let mut chars = s.chars();
82 let Some(first) = chars.next() else {
83 return false;
84 };
85 if !first.is_ascii_lowercase() {
86 return false;
87 }
88 chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
89 }
90
91 /// Check if the package identifier is valid
is_valid_package_ident(s: &str) -> bool92 pub fn is_valid_package_ident(s: &str) -> bool {
93 if !s.contains('.') {
94 return false;
95 }
96 s.split('.').all(is_valid_name_ident)
97 }
98
99 /// Check if the container identifier is valid
is_valid_container_ident(s: &str) -> bool100 pub fn is_valid_container_ident(s: &str) -> bool {
101 s.split('.').all(is_valid_name_ident)
102 }
103
try_from_text_proto<T>(s: &str) -> Result<T> where T: protobuf::MessageFull,104 fn try_from_text_proto<T>(s: &str) -> Result<T>
105 where
106 T: protobuf::MessageFull,
107 {
108 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
109 }
110
111 macro_rules! ensure_required_fields {
112 ($type:expr, $struct:expr, $($field:expr),+) => {
113 $(
114 paste! {
115 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
116 }
117 )+
118 };
119 }
120
121 /// Utility module for flag_declaration proto
122 pub mod flag_declaration {
123 use super::*;
124 use anyhow::ensure;
125
126 /// Ensure the proto instance is valid by checking its fields
verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()>127 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
128 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
129
130 ensure!(
131 is_valid_name_ident(pdf.name()),
132 "bad flag declaration: bad name {} expected snake_case string; \
133 see {ACONFIG_PROTO_PATH} for details",
134 pdf.name()
135 );
136 ensure!(
137 is_valid_name_ident(pdf.namespace()),
138 "bad flag declaration: bad namespace {} expected snake_case string; \
139 see {ACONFIG_PROTO_PATH} for details",
140 pdf.namespace()
141 );
142 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
143 ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
144
145 Ok(())
146 }
147 }
148
149 /// Utility module for flag_declarations proto
150 pub mod flag_declarations {
151 use super::*;
152 use anyhow::ensure;
153
154 /// Construct a proto instance from a textproto string content
try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations>155 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
156 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
157 verify_fields(&pdf)?;
158 Ok(pdf)
159 }
160
161 /// Ensure the proto instance is valid by checking its fields
verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()>162 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
163 ensure_required_fields!("flag declarations", pdf, "package");
164 // TODO(b/312769710): Make the container field required.
165 ensure!(
166 is_valid_package_ident(pdf.package()),
167 "bad flag declarations: bad package {} expected snake_case strings delimited by dots; \
168 see {ACONFIG_PROTO_PATH} for details",
169 pdf.package()
170 );
171 ensure!(
172 !pdf.has_container() || is_valid_container_ident(pdf.container()),
173 "bad flag declarations: bad container"
174 );
175 for flag_declaration in pdf.flag.iter() {
176 super::flag_declaration::verify_fields(flag_declaration)?;
177 }
178
179 Ok(())
180 }
181 }
182
183 /// Utility module for flag_value proto
184 pub mod flag_value {
185 use super::*;
186 use anyhow::ensure;
187
188 /// Ensure the proto instance is valid by checking its fields
verify_fields(fv: &ProtoFlagValue) -> Result<()>189 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
190 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
191
192 ensure!(
193 is_valid_package_ident(fv.package()),
194 "bad flag value: bad package {} expected snake_case strings delimited by dots; \
195 see {ACONFIG_PROTO_PATH} for details",
196 fv.package()
197 );
198 ensure!(
199 is_valid_name_ident(fv.name()),
200 "bad flag value: bad name {} expected snake_case string; \
201 see {ACONFIG_PROTO_PATH} for details",
202 fv.name()
203 );
204
205 Ok(())
206 }
207 }
208
209 /// Utility module for flag_values proto
210 pub mod flag_values {
211 use super::*;
212
213 /// Construct a proto instance from a textproto string content
try_from_text_proto(s: &str) -> Result<ProtoFlagValues>214 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
215 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
216 verify_fields(&pfv)?;
217 Ok(pfv)
218 }
219
220 /// Ensure the proto instance is valid by checking its fields
verify_fields(pfv: &ProtoFlagValues) -> Result<()>221 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
222 for flag_value in pfv.flag_value.iter() {
223 super::flag_value::verify_fields(flag_value)?;
224 }
225 Ok(())
226 }
227 }
228
229 /// Utility module for flag_permission proto enum
230 pub mod flag_permission {
231 use super::*;
232 use anyhow::bail;
233
234 /// Construct a flag permission proto enum from string
parse_from_str(permission: &str) -> Result<ProtoFlagPermission>235 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
236 match permission.to_ascii_lowercase().as_str() {
237 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
238 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
239 _ => bail!("Permission needs to be read_only or read_write."),
240 }
241 }
242
243 /// Serialize flag permission proto enum to string
to_string(permission: &ProtoFlagPermission) -> &str244 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
245 match permission {
246 ProtoFlagPermission::READ_WRITE => "read_write",
247 ProtoFlagPermission::READ_ONLY => "read_only",
248 }
249 }
250 }
251
252 /// Utility module for tracepoint proto
253 pub mod tracepoint {
254 use super::*;
255 use anyhow::ensure;
256
257 /// Ensure the proto instance is valid by checking its fields
verify_fields(tp: &ProtoTracepoint) -> Result<()>258 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
259 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
260
261 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
262
263 Ok(())
264 }
265 }
266
267 /// Utility module for parsed_flag proto
268 pub mod parsed_flag {
269 use super::*;
270 use anyhow::ensure;
271
272 /// Ensure the proto instance is valid by checking its fields
verify_fields(pf: &ProtoParsedFlag) -> Result<()>273 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
274 ensure_required_fields!(
275 "parsed flag",
276 pf,
277 "package",
278 "name",
279 "namespace",
280 "description",
281 "state",
282 "permission"
283 );
284
285 ensure!(
286 is_valid_package_ident(pf.package()),
287 "bad parsed flag: bad package {} expected snake_case strings delimited by dots; \
288 see {ACONFIG_PROTO_PATH} for details",
289 pf.package()
290 );
291 ensure!(
292 !pf.has_container() || is_valid_container_ident(pf.container()),
293 "bad parsed flag: bad container"
294 );
295 ensure!(
296 is_valid_name_ident(pf.name()),
297 "bad parsed flag: bad name {} expected snake_case string; \
298 see {ACONFIG_PROTO_PATH} for details",
299 pf.name()
300 );
301 ensure!(
302 is_valid_name_ident(pf.namespace()),
303 "bad parsed flag: bad namespace {} expected snake_case string; \
304 see {ACONFIG_PROTO_PATH} for details",
305 pf.namespace()
306 );
307 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
308 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
309 for tp in pf.trace.iter() {
310 super::tracepoint::verify_fields(tp)?;
311 }
312 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
313 if pf.is_fixed_read_only() {
314 ensure!(
315 pf.permission() == ProtoFlagPermission::READ_ONLY,
316 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
317 );
318 for tp in pf.trace.iter() {
319 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
320 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
321 );
322 }
323 }
324
325 Ok(())
326 }
327
328 /// Get the file path of the corresponding flag declaration
path_to_declaration(pf: &ProtoParsedFlag) -> &str329 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
330 debug_assert!(!pf.trace.is_empty());
331 pf.trace[0].source()
332 }
333 }
334
335 /// Utility module for parsed_flags proto
336 pub mod parsed_flags {
337 use super::*;
338 use anyhow::bail;
339 use std::cmp::Ordering;
340
341 /// Construct a proto instance from a binary proto bytes
try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags>342 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
343 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
344 verify_fields(&message)?;
345 Ok(message)
346 }
347
348 /// Ensure the proto instance is valid by checking its fields
verify_fields(pf: &ProtoParsedFlags) -> Result<()>349 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
350 use crate::parsed_flag::path_to_declaration;
351
352 let mut previous: Option<&ProtoParsedFlag> = None;
353 for parsed_flag in pf.parsed_flag.iter() {
354 if let Some(prev) = previous {
355 let a = create_sorting_key(prev);
356 let b = create_sorting_key(parsed_flag);
357 match a.cmp(&b) {
358 Ordering::Less => {}
359 Ordering::Equal => bail!(
360 "bad parsed flags: duplicate flag {} (defined in {} and {})",
361 a,
362 path_to_declaration(prev),
363 path_to_declaration(parsed_flag)
364 ),
365 Ordering::Greater => {
366 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
367 }
368 }
369 }
370 super::parsed_flag::verify_fields(parsed_flag)?;
371 previous = Some(parsed_flag);
372 }
373 Ok(())
374 }
375
376 /// Merge multipe parsed_flags proto
merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags>377 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
378 let mut merged = ProtoParsedFlags::new();
379 for mut pfs in parsed_flags.into_iter() {
380 merged.parsed_flag.append(&mut pfs.parsed_flag);
381 }
382 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
383 if dedup {
384 // Deduplicate identical protobuf messages. Messages with the same sorting key but
385 // different fields (including the path to the original source file) will not be
386 // deduplicated and trigger an error in verify_fields.
387 merged.parsed_flag.dedup();
388 }
389 verify_fields(&merged)?;
390 Ok(merged)
391 }
392
393 /// Sort parsed flags
sort_parsed_flags(pf: &mut ProtoParsedFlags)394 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
395 pf.parsed_flag.sort_by_key(create_sorting_key);
396 }
397
create_sorting_key(pf: &ProtoParsedFlag) -> String398 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
399 pf.fully_qualified_name()
400 }
401 }
402
403 /// ParsedFlagExt trait
404 pub trait ParsedFlagExt {
405 /// Return the fully qualified name
fully_qualified_name(&self) -> String406 fn fully_qualified_name(&self) -> String;
407 }
408
409 impl ParsedFlagExt for ProtoParsedFlag {
fully_qualified_name(&self) -> String410 fn fully_qualified_name(&self) -> String {
411 format!("{}.{}", self.package(), self.name())
412 }
413 }
414
415 #[cfg(test)]
416 mod tests {
417 use super::*;
418
419 #[test]
test_flag_declarations_try_from_text_proto()420 fn test_flag_declarations_try_from_text_proto() {
421 // valid input
422 let flag_declarations = flag_declarations::try_from_text_proto(
423 r#"
424 package: "com.foo.bar"
425 container: "system"
426 flag {
427 name: "first"
428 namespace: "first_ns"
429 description: "This is the description of the first flag."
430 bug: "123"
431 is_exported: true
432 }
433 flag {
434 name: "second"
435 namespace: "second_ns"
436 description: "This is the description of the second flag."
437 bug: "abc"
438 is_fixed_read_only: true
439 }
440 "#,
441 )
442 .unwrap();
443 assert_eq!(flag_declarations.package(), "com.foo.bar");
444 assert_eq!(flag_declarations.container(), "system");
445 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
446 assert_eq!(first.name(), "first");
447 assert_eq!(first.namespace(), "first_ns");
448 assert_eq!(first.description(), "This is the description of the first flag.");
449 assert_eq!(first.bug, vec!["123"]);
450 assert!(!first.is_fixed_read_only());
451 assert!(first.is_exported());
452 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
453 assert_eq!(second.name(), "second");
454 assert_eq!(second.namespace(), "second_ns");
455 assert_eq!(second.description(), "This is the description of the second flag.");
456 assert_eq!(second.bug, vec!["abc"]);
457 assert!(second.is_fixed_read_only());
458 assert!(!second.is_exported());
459
460 // valid input: missing container in flag declarations is supported
461 let flag_declarations = flag_declarations::try_from_text_proto(
462 r#"
463 package: "com.foo.bar"
464 flag {
465 name: "first"
466 namespace: "first_ns"
467 description: "This is the description of the first flag."
468 bug: "123"
469 }
470 "#,
471 )
472 .unwrap();
473 assert_eq!(flag_declarations.container(), "");
474 assert!(!flag_declarations.has_container());
475
476 // bad input: missing package in flag declarations
477 let error = flag_declarations::try_from_text_proto(
478 r#"
479 container: "system"
480 flag {
481 name: "first"
482 namespace: "first_ns"
483 description: "This is the description of the first flag."
484 }
485 flag {
486 name: "second"
487 namespace: "second_ns"
488 description: "This is the description of the second flag."
489 }
490 "#,
491 )
492 .unwrap_err();
493 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
494
495 // bad input: missing namespace in flag declaration
496 let error = flag_declarations::try_from_text_proto(
497 r#"
498 package: "com.foo.bar"
499 container: "system"
500 flag {
501 name: "first"
502 description: "This is the description of the first flag."
503 }
504 flag {
505 name: "second"
506 namespace: "second_ns"
507 description: "This is the description of the second flag."
508 }
509 "#,
510 )
511 .unwrap_err();
512 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
513
514 // bad input: bad package name in flag declarations
515 let error = flag_declarations::try_from_text_proto(
516 r#"
517 package: "_com.FOO__BAR"
518 container: "system"
519 flag {
520 name: "first"
521 namespace: "first_ns"
522 description: "This is the description of the first flag."
523 }
524 flag {
525 name: "second"
526 namespace: "second_ns"
527 description: "This is the description of the second flag."
528 }
529 "#,
530 )
531 .unwrap_err();
532 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
533
534 // bad input: bad name in flag declaration
535 let error = flag_declarations::try_from_text_proto(
536 r#"
537 package: "com.foo.bar"
538 container: "system"
539 flag {
540 name: "FIRST"
541 namespace: "first_ns"
542 description: "This is the description of the first flag."
543 }
544 flag {
545 name: "second"
546 namespace: "second_ns"
547 description: "This is the description of the second flag."
548 }
549 "#,
550 )
551 .unwrap_err();
552 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
553
554 // bad input: no bug entries in flag declaration
555 let error = flag_declarations::try_from_text_proto(
556 r#"
557 package: "com.foo.bar"
558 container: "system"
559 flag {
560 name: "first"
561 namespace: "first_ns"
562 description: "This is the description of the first flag."
563 }
564 "#,
565 )
566 .unwrap_err();
567 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
568
569 // bad input: multiple bug entries in flag declaration
570 let error = flag_declarations::try_from_text_proto(
571 r#"
572 package: "com.foo.bar"
573 container: "system"
574 flag {
575 name: "first"
576 namespace: "first_ns"
577 description: "This is the description of the first flag."
578 bug: "123"
579 bug: "abc"
580 }
581 "#,
582 )
583 .unwrap_err();
584 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
585
586 // bad input: invalid container name in flag declaration
587 let error = flag_declarations::try_from_text_proto(
588 r#"
589 package: "com.foo.bar"
590 container: "__bad_bad_container.com"
591 flag {
592 name: "first"
593 namespace: "first_ns"
594 description: "This is the description of the first flag."
595 bug: "123"
596 bug: "abc"
597 }
598 "#,
599 )
600 .unwrap_err();
601 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
602
603 // TODO(b/312769710): Verify error when container is missing.
604 }
605
606 #[test]
test_flag_values_try_from_text_proto()607 fn test_flag_values_try_from_text_proto() {
608 // valid input
609 let flag_values = flag_values::try_from_text_proto(
610 r#"
611 flag_value {
612 package: "com.first"
613 name: "first"
614 state: DISABLED
615 permission: READ_ONLY
616 }
617 flag_value {
618 package: "com.second"
619 name: "second"
620 state: ENABLED
621 permission: READ_WRITE
622 }
623 "#,
624 )
625 .unwrap();
626 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
627 assert_eq!(first.package(), "com.first");
628 assert_eq!(first.name(), "first");
629 assert_eq!(first.state(), ProtoFlagState::DISABLED);
630 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
631 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
632 assert_eq!(second.package(), "com.second");
633 assert_eq!(second.name(), "second");
634 assert_eq!(second.state(), ProtoFlagState::ENABLED);
635 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
636
637 // bad input: bad package in flag value
638 let error = flag_values::try_from_text_proto(
639 r#"
640 flag_value {
641 package: "COM.FIRST"
642 name: "first"
643 state: DISABLED
644 permission: READ_ONLY
645 }
646 "#,
647 )
648 .unwrap_err();
649 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
650
651 // bad input: bad name in flag value
652 let error = flag_values::try_from_text_proto(
653 r#"
654 flag_value {
655 package: "com.first"
656 name: "FIRST"
657 state: DISABLED
658 permission: READ_ONLY
659 }
660 "#,
661 )
662 .unwrap_err();
663 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
664
665 // bad input: missing state in flag value
666 let error = flag_values::try_from_text_proto(
667 r#"
668 flag_value {
669 package: "com.first"
670 name: "first"
671 permission: READ_ONLY
672 }
673 "#,
674 )
675 .unwrap_err();
676 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
677
678 // bad input: missing permission in flag value
679 let error = flag_values::try_from_text_proto(
680 r#"
681 flag_value {
682 package: "com.first"
683 name: "first"
684 state: DISABLED
685 }
686 "#,
687 )
688 .unwrap_err();
689 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
690 }
691
try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags>692 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
693 use protobuf::Message;
694
695 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
696 let mut binary_proto = Vec::new();
697 parsed_flags.write_to_vec(&mut binary_proto)?;
698 parsed_flags::try_from_binary_proto(&binary_proto)
699 }
700
701 #[test]
test_parsed_flags_try_from_text_proto()702 fn test_parsed_flags_try_from_text_proto() {
703 // valid input
704 let text_proto = r#"
705 parsed_flag {
706 package: "com.first"
707 name: "first"
708 namespace: "first_ns"
709 description: "This is the description of the first flag."
710 bug: "SOME_BUG"
711 state: DISABLED
712 permission: READ_ONLY
713 trace {
714 source: "flags.declarations"
715 state: DISABLED
716 permission: READ_ONLY
717 }
718 container: "system"
719 }
720 parsed_flag {
721 package: "com.second"
722 name: "second"
723 namespace: "second_ns"
724 description: "This is the description of the second flag."
725 bug: "SOME_BUG"
726 state: ENABLED
727 permission: READ_ONLY
728 trace {
729 source: "flags.declarations"
730 state: DISABLED
731 permission: READ_ONLY
732 }
733 trace {
734 source: "flags.values"
735 state: ENABLED
736 permission: READ_ONLY
737 }
738 is_fixed_read_only: true
739 container: "system"
740 }
741 "#;
742 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
743 assert_eq!(parsed_flags.parsed_flag.len(), 2);
744 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
745 assert_eq!(second.package(), "com.second");
746 assert_eq!(second.name(), "second");
747 assert_eq!(second.namespace(), "second_ns");
748 assert_eq!(second.description(), "This is the description of the second flag.");
749 assert_eq!(second.bug, vec!["SOME_BUG"]);
750 assert_eq!(second.state(), ProtoFlagState::ENABLED);
751 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
752 assert_eq!(2, second.trace.len());
753 assert_eq!(second.trace[0].source(), "flags.declarations");
754 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
755 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
756 assert_eq!(second.trace[1].source(), "flags.values");
757 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
758 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
759 assert!(second.is_fixed_read_only());
760
761 // valid input: empty
762 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
763 assert!(parsed_flags.parsed_flag.is_empty());
764
765 // bad input: empty trace
766 let text_proto = r#"
767 parsed_flag {
768 package: "com.first"
769 name: "first"
770 namespace: "first_ns"
771 description: "This is the description of the first flag."
772 state: DISABLED
773 permission: READ_ONLY
774 container: "system"
775 }
776 "#;
777 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
778 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
779
780 // bad input: missing namespace in parsed_flag
781 let text_proto = r#"
782 parsed_flag {
783 package: "com.first"
784 name: "first"
785 description: "This is the description of the first flag."
786 state: DISABLED
787 permission: READ_ONLY
788 trace {
789 source: "flags.declarations"
790 state: DISABLED
791 permission: READ_ONLY
792 }
793 container: "system"
794 }
795 "#;
796 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
797 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
798
799 // bad input: parsed_flag not sorted by package
800 let text_proto = r#"
801 parsed_flag {
802 package: "bbb.bbb"
803 name: "first"
804 namespace: "first_ns"
805 description: "This is the description of the first flag."
806 bug: ""
807 state: DISABLED
808 permission: READ_ONLY
809 trace {
810 source: "flags.declarations"
811 state: DISABLED
812 permission: READ_ONLY
813 }
814 container: "system"
815 }
816 parsed_flag {
817 package: "aaa.aaa"
818 name: "second"
819 namespace: "second_ns"
820 description: "This is the description of the second flag."
821 bug: ""
822 state: ENABLED
823 permission: READ_WRITE
824 trace {
825 source: "flags.declarations"
826 state: DISABLED
827 permission: READ_ONLY
828 }
829 container: "system"
830 }
831 "#;
832 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
833 assert_eq!(
834 format!("{:?}", error),
835 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
836 );
837
838 // bad input: parsed_flag not sorted by name
839 let text_proto = r#"
840 parsed_flag {
841 package: "com.foo"
842 name: "bbb"
843 namespace: "first_ns"
844 description: "This is the description of the first flag."
845 bug: ""
846 state: DISABLED
847 permission: READ_ONLY
848 trace {
849 source: "flags.declarations"
850 state: DISABLED
851 permission: READ_ONLY
852 }
853 container: "system"
854 }
855 parsed_flag {
856 package: "com.foo"
857 name: "aaa"
858 namespace: "second_ns"
859 description: "This is the description of the second flag."
860 bug: ""
861 state: ENABLED
862 permission: READ_WRITE
863 trace {
864 source: "flags.declarations"
865 state: DISABLED
866 permission: READ_ONLY
867 }
868 container: "system"
869 }
870 "#;
871 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
872 assert_eq!(
873 format!("{:?}", error),
874 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
875 );
876
877 // bad input: duplicate flags
878 let text_proto = r#"
879 parsed_flag {
880 package: "com.foo"
881 name: "bar"
882 namespace: "first_ns"
883 description: "This is the description of the first flag."
884 bug: ""
885 state: DISABLED
886 permission: READ_ONLY
887 trace {
888 source: "flags.declarations"
889 state: DISABLED
890 permission: READ_ONLY
891 }
892 container: "system"
893 }
894 parsed_flag {
895 package: "com.foo"
896 name: "bar"
897 namespace: "second_ns"
898 description: "This is the description of the second flag."
899 bug: ""
900 state: ENABLED
901 permission: READ_WRITE
902 trace {
903 source: "flags.declarations"
904 state: DISABLED
905 permission: READ_ONLY
906 }
907 container: "system"
908 }
909 "#;
910 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
911 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
912 }
913
914 #[test]
test_parsed_flag_path_to_declaration()915 fn test_parsed_flag_path_to_declaration() {
916 let text_proto = r#"
917 parsed_flag {
918 package: "com.foo"
919 name: "bar"
920 namespace: "first_ns"
921 description: "This is the description of the first flag."
922 bug: "b/12345678"
923 state: DISABLED
924 permission: READ_ONLY
925 trace {
926 source: "flags.declarations"
927 state: DISABLED
928 permission: READ_ONLY
929 }
930 trace {
931 source: "flags.values"
932 state: ENABLED
933 permission: READ_ONLY
934 }
935 container: "system"
936 }
937 "#;
938 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
939 let parsed_flag = &parsed_flags.parsed_flag[0];
940 assert_eq!(crate::parsed_flag::path_to_declaration(parsed_flag), "flags.declarations");
941 }
942
943 #[test]
test_parsed_flags_merge()944 fn test_parsed_flags_merge() {
945 let text_proto = r#"
946 parsed_flag {
947 package: "com.first"
948 name: "first"
949 namespace: "first_ns"
950 description: "This is the description of the first flag."
951 bug: "a"
952 state: DISABLED
953 permission: READ_ONLY
954 trace {
955 source: "flags.declarations"
956 state: DISABLED
957 permission: READ_ONLY
958 }
959 container: "system"
960 }
961 parsed_flag {
962 package: "com.second"
963 name: "second"
964 namespace: "second_ns"
965 description: "This is the description of the second flag."
966 bug: "b"
967 state: ENABLED
968 permission: READ_WRITE
969 trace {
970 source: "flags.declarations"
971 state: DISABLED
972 permission: READ_ONLY
973 }
974 container: "system"
975 }
976 "#;
977 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
978
979 let text_proto = r#"
980 parsed_flag {
981 package: "com.first"
982 name: "first"
983 namespace: "first_ns"
984 description: "This is the description of the first flag."
985 bug: "a"
986 state: DISABLED
987 permission: READ_ONLY
988 trace {
989 source: "flags.declarations"
990 state: DISABLED
991 permission: READ_ONLY
992 }
993 container: "system"
994 }
995 "#;
996 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
997
998 let text_proto = r#"
999 parsed_flag {
1000 package: "com.second"
1001 name: "second"
1002 namespace: "second_ns"
1003 bug: "b"
1004 description: "This is the description of the second flag."
1005 state: ENABLED
1006 permission: READ_WRITE
1007 trace {
1008 source: "flags.declarations"
1009 state: DISABLED
1010 permission: READ_ONLY
1011 }
1012 container: "system"
1013 }
1014 "#;
1015 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
1016
1017 let text_proto = r#"
1018 parsed_flag {
1019 package: "com.second"
1020 name: "second"
1021 namespace: "second_ns"
1022 bug: "b"
1023 description: "This is the description of the second flag."
1024 state: ENABLED
1025 permission: READ_WRITE
1026 trace {
1027 source: "duplicate/flags.declarations"
1028 state: DISABLED
1029 permission: READ_ONLY
1030 }
1031 }
1032 "#;
1033 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
1034
1035 // bad cases
1036
1037 // two of the same flag with dedup disabled
1038 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
1039 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)");
1040
1041 // two conflicting flags with dedup disabled
1042 let error =
1043 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
1044 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
1045
1046 // two conflicting flags with dedup enabled
1047 let error =
1048 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
1049 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
1050
1051 // valid cases
1052 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
1053 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
1054 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
1055 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
1056 assert_eq!(
1057 expected,
1058 parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()
1059 );
1060 assert_eq!(
1061 expected,
1062 parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()
1063 );
1064 assert_eq!(
1065 expected,
1066 parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()
1067 );
1068 assert_eq!(
1069 expected,
1070 parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()
1071 );
1072
1073 // two identical flags with dedup enabled
1074 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
1075 }
1076 }
1077