blob: a5a53425cfbad62c9c873fe3daf7361d7a8dd152 [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");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200114
Mårten Kongstad403658f2023-06-14 09:51:56 +0200115 ensure!(
116 codegen::is_valid_package_ident(pdf.package()),
117 "bad flag declarations: bad package"
118 );
119 for flag_declaration in pdf.flag.iter() {
120 super::flag_declaration::verify_fields(flag_declaration)?;
121 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200122
Mårten Kongstad403658f2023-06-14 09:51:56 +0200123 Ok(())
124 }
125}
126
127pub mod flag_value {
128 use super::*;
129 use crate::codegen;
130 use anyhow::ensure;
131
132 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200133 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200134
Mårten Kongstad403658f2023-06-14 09:51:56 +0200135 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
136 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200137
Mårten Kongstad403658f2023-06-14 09:51:56 +0200138 Ok(())
139 }
140}
141
142pub mod flag_values {
143 use super::*;
144
145 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
146 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
147 verify_fields(&pfv)?;
148 Ok(pfv)
149 }
150
151 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
152 for flag_value in pfv.flag_value.iter() {
153 super::flag_value::verify_fields(flag_value)?;
154 }
155 Ok(())
156 }
157}
158
Zhi Dou24a0b6a2023-08-10 21:39:59 +0000159pub mod flag_permission {
160 use super::*;
161 use anyhow::bail;
162
163 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
164 match permission.to_ascii_lowercase().as_str() {
165 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
166 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
167 _ => bail!("Permission needs to be read_only or read_write."),
168 }
169 }
170
171 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
172 match permission {
173 ProtoFlagPermission::READ_WRITE => "read_write",
174 ProtoFlagPermission::READ_ONLY => "read_only",
175 }
176 }
177}
178
Mårten Kongstad403658f2023-06-14 09:51:56 +0200179pub mod tracepoint {
180 use super::*;
181 use anyhow::ensure;
182
183 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200184 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200185
Mårten Kongstad403658f2023-06-14 09:51:56 +0200186 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200187
Mårten Kongstad403658f2023-06-14 09:51:56 +0200188 Ok(())
189 }
190}
191
192pub mod parsed_flag {
193 use super::*;
194 use crate::codegen;
195 use anyhow::ensure;
196
197 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200198 ensure_required_fields!(
199 "parsed flag",
200 pf,
201 "package",
202 "name",
203 "namespace",
204 "description",
205 "state",
206 "permission"
207 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200208
Mårten Kongstad403658f2023-06-14 09:51:56 +0200209 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
210 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
211 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
212 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
213 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
214 for tp in pf.trace.iter() {
215 super::tracepoint::verify_fields(tp)?;
216 }
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200217 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad21717e72023-09-04 13:28:36 +0200218 if pf.is_fixed_read_only() {
219 ensure!(
220 pf.permission() == ProtoFlagPermission::READ_ONLY,
221 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
222 );
223 for tp in pf.trace.iter() {
224 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
225 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
226 );
227 }
228 }
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200229
Mårten Kongstad403658f2023-06-14 09:51:56 +0200230 Ok(())
231 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200232
233 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
234 debug_assert!(!pf.trace.is_empty());
235 pf.trace[0].source()
236 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200237}
238
239pub mod parsed_flags {
240 use super::*;
241 use anyhow::bail;
242 use std::cmp::Ordering;
243
244 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
245 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
246 verify_fields(&message)?;
247 Ok(message)
248 }
249
250 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200251 use crate::protos::parsed_flag::path_to_declaration;
252
Mårten Kongstad403658f2023-06-14 09:51:56 +0200253 let mut previous: Option<&ProtoParsedFlag> = None;
254 for parsed_flag in pf.parsed_flag.iter() {
255 if let Some(prev) = previous {
256 let a = create_sorting_key(prev);
257 let b = create_sorting_key(parsed_flag);
258 match a.cmp(&b) {
259 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200260 Ordering::Equal => bail!(
261 "bad parsed flags: duplicate flag {} (defined in {} and {})",
262 a,
263 path_to_declaration(prev),
264 path_to_declaration(parsed_flag)
265 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200266 Ordering::Greater => {
267 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
268 }
269 }
270 }
271 super::parsed_flag::verify_fields(parsed_flag)?;
272 previous = Some(parsed_flag);
273 }
274 Ok(())
275 }
276
277 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
278 let mut merged = ProtoParsedFlags::new();
279 for mut pfs in parsed_flags.into_iter() {
280 merged.parsed_flag.append(&mut pfs.parsed_flag);
281 }
282 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
283 verify_fields(&merged)?;
284 Ok(merged)
285 }
286
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000287 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
288 pf.parsed_flag.sort_by_key(create_sorting_key);
289 }
290
Mårten Kongstad403658f2023-06-14 09:51:56 +0200291 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
292 format!("{}.{}", pf.package(), pf.name())
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_flag_declarations_try_from_text_proto() {
302 // valid input
303 let flag_declarations = flag_declarations::try_from_text_proto(
304 r#"
305package: "com.foo.bar"
306flag {
307 name: "first"
308 namespace: "first_ns"
309 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200310 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000311 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200312}
313flag {
314 name: "second"
315 namespace: "second_ns"
316 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200317 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000318 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200319}
320"#,
321 )
322 .unwrap();
323 assert_eq!(flag_declarations.package(), "com.foo.bar");
324 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
325 assert_eq!(first.name(), "first");
326 assert_eq!(first.namespace(), "first_ns");
327 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200328 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000329 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000330 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200331 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
332 assert_eq!(second.name(), "second");
333 assert_eq!(second.namespace(), "second_ns");
334 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200335 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000336 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000337 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200338
339 // bad input: missing package in flag declarations
340 let error = flag_declarations::try_from_text_proto(
341 r#"
342flag {
343 name: "first"
344 namespace: "first_ns"
345 description: "This is the description of the first flag."
346}
347flag {
348 name: "second"
349 namespace: "second_ns"
350 description: "This is the description of the second flag."
351}
352"#,
353 )
354 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200355 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200356
357 // bad input: missing namespace in flag declaration
358 let error = flag_declarations::try_from_text_proto(
359 r#"
360package: "com.foo.bar"
361flag {
362 name: "first"
363 description: "This is the description of the first flag."
364}
365flag {
366 name: "second"
367 namespace: "second_ns"
368 description: "This is the description of the second flag."
369}
370"#,
371 )
372 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200373 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200374
375 // bad input: bad package name in flag declarations
376 let error = flag_declarations::try_from_text_proto(
377 r#"
378package: "_com.FOO__BAR"
379flag {
380 name: "first"
381 namespace: "first_ns"
382 description: "This is the description of the first flag."
383}
384flag {
385 name: "second"
386 namespace: "second_ns"
387 description: "This is the description of the second flag."
388}
389"#,
390 )
391 .unwrap_err();
392 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
393
394 // bad input: bad name in flag declaration
395 let error = flag_declarations::try_from_text_proto(
396 r#"
397package: "com.foo.bar"
398flag {
399 name: "FIRST"
400 namespace: "first_ns"
401 description: "This is the description of the first flag."
402}
403flag {
404 name: "second"
405 namespace: "second_ns"
406 description: "This is the description of the second flag."
407}
408"#,
409 )
410 .unwrap_err();
411 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200412
413 // bad input: no bug entries in flag declaration
414 let error = flag_declarations::try_from_text_proto(
415 r#"
416package: "com.foo.bar"
417flag {
418 name: "first"
419 namespace: "first_ns"
420 description: "This is the description of the first flag."
421}
422"#,
423 )
424 .unwrap_err();
425 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
426
427 // bad input: multiple bug entries in flag declaration
428 let error = flag_declarations::try_from_text_proto(
429 r#"
430package: "com.foo.bar"
431flag {
432 name: "first"
433 namespace: "first_ns"
434 description: "This is the description of the first flag."
435 bug: "123"
436 bug: "abc"
437}
438"#,
439 )
440 .unwrap_err();
441 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Mårten Kongstad403658f2023-06-14 09:51:56 +0200442 }
443
444 #[test]
445 fn test_flag_values_try_from_text_proto() {
446 // valid input
447 let flag_values = flag_values::try_from_text_proto(
448 r#"
449flag_value {
450 package: "com.first"
451 name: "first"
452 state: DISABLED
453 permission: READ_ONLY
454}
455flag_value {
456 package: "com.second"
457 name: "second"
458 state: ENABLED
459 permission: READ_WRITE
460}
461"#,
462 )
463 .unwrap();
464 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
465 assert_eq!(first.package(), "com.first");
466 assert_eq!(first.name(), "first");
467 assert_eq!(first.state(), ProtoFlagState::DISABLED);
468 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
469 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
470 assert_eq!(second.package(), "com.second");
471 assert_eq!(second.name(), "second");
472 assert_eq!(second.state(), ProtoFlagState::ENABLED);
473 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
474
475 // bad input: bad package in flag value
476 let error = flag_values::try_from_text_proto(
477 r#"
478flag_value {
479 package: "COM.FIRST"
480 name: "first"
481 state: DISABLED
482 permission: READ_ONLY
483}
484"#,
485 )
486 .unwrap_err();
487 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
488
489 // bad input: bad name in flag value
490 let error = flag_values::try_from_text_proto(
491 r#"
492flag_value {
493 package: "com.first"
494 name: "FIRST"
495 state: DISABLED
496 permission: READ_ONLY
497}
498"#,
499 )
500 .unwrap_err();
501 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
502
503 // bad input: missing state in flag value
504 let error = flag_values::try_from_text_proto(
505 r#"
506flag_value {
507 package: "com.first"
508 name: "first"
509 permission: READ_ONLY
510}
511"#,
512 )
513 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200514 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200515
516 // bad input: missing permission in flag value
517 let error = flag_values::try_from_text_proto(
518 r#"
519flag_value {
520 package: "com.first"
521 name: "first"
522 state: DISABLED
523}
524"#,
525 )
526 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200527 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200528 }
529
530 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
531 use protobuf::Message;
532
533 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
534 let mut binary_proto = Vec::new();
535 parsed_flags.write_to_vec(&mut binary_proto)?;
536 parsed_flags::try_from_binary_proto(&binary_proto)
537 }
538
539 #[test]
540 fn test_parsed_flags_try_from_text_proto() {
541 // valid input
542 let text_proto = r#"
543parsed_flag {
544 package: "com.first"
545 name: "first"
546 namespace: "first_ns"
547 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200548 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200549 state: DISABLED
550 permission: READ_ONLY
551 trace {
552 source: "flags.declarations"
553 state: DISABLED
554 permission: READ_ONLY
555 }
556}
557parsed_flag {
558 package: "com.second"
559 name: "second"
560 namespace: "second_ns"
561 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200562 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200563 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200564 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200565 trace {
566 source: "flags.declarations"
567 state: DISABLED
568 permission: READ_ONLY
569 }
570 trace {
571 source: "flags.values"
572 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200573 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200574 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000575 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200576}
577"#;
578 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
579 assert_eq!(parsed_flags.parsed_flag.len(), 2);
580 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
581 assert_eq!(second.package(), "com.second");
582 assert_eq!(second.name(), "second");
583 assert_eq!(second.namespace(), "second_ns");
584 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200585 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200586 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200587 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200588 assert_eq!(2, second.trace.len());
589 assert_eq!(second.trace[0].source(), "flags.declarations");
590 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
591 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
592 assert_eq!(second.trace[1].source(), "flags.values");
593 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200594 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000595 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200596
597 // valid input: empty
598 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
599 assert!(parsed_flags.parsed_flag.is_empty());
600
601 // bad input: empty trace
602 let text_proto = r#"
603parsed_flag {
604 package: "com.first"
605 name: "first"
606 namespace: "first_ns"
607 description: "This is the description of the first flag."
608 state: DISABLED
609 permission: READ_ONLY
610}
611"#;
612 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
613 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
614
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200615 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200616 let text_proto = r#"
617parsed_flag {
618 package: "com.first"
619 name: "first"
620 description: "This is the description of the first flag."
621 state: DISABLED
622 permission: READ_ONLY
623 trace {
624 source: "flags.declarations"
625 state: DISABLED
626 permission: READ_ONLY
627 }
628}
629"#;
630 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200631 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200632
633 // bad input: parsed_flag not sorted by package
634 let text_proto = r#"
635parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200636 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200637 name: "first"
638 namespace: "first_ns"
639 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200640 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200641 state: DISABLED
642 permission: READ_ONLY
643 trace {
644 source: "flags.declarations"
645 state: DISABLED
646 permission: READ_ONLY
647 }
648}
649parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200650 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200651 name: "second"
652 namespace: "second_ns"
653 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200654 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200655 state: ENABLED
656 permission: READ_WRITE
657 trace {
658 source: "flags.declarations"
659 state: DISABLED
660 permission: READ_ONLY
661 }
662}
663"#;
664 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
665 assert_eq!(
666 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200667 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200668 );
669
670 // bad input: parsed_flag not sorted by name
671 let text_proto = r#"
672parsed_flag {
673 package: "com.foo"
674 name: "bbb"
675 namespace: "first_ns"
676 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200677 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200678 state: DISABLED
679 permission: READ_ONLY
680 trace {
681 source: "flags.declarations"
682 state: DISABLED
683 permission: READ_ONLY
684 }
685}
686parsed_flag {
687 package: "com.foo"
688 name: "aaa"
689 namespace: "second_ns"
690 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200691 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200692 state: ENABLED
693 permission: READ_WRITE
694 trace {
695 source: "flags.declarations"
696 state: DISABLED
697 permission: READ_ONLY
698 }
699}
700"#;
701 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
702 assert_eq!(
703 format!("{:?}", error),
704 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
705 );
706
707 // bad input: duplicate flags
708 let text_proto = r#"
709parsed_flag {
710 package: "com.foo"
711 name: "bar"
712 namespace: "first_ns"
713 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200714 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200715 state: DISABLED
716 permission: READ_ONLY
717 trace {
718 source: "flags.declarations"
719 state: DISABLED
720 permission: READ_ONLY
721 }
722}
723parsed_flag {
724 package: "com.foo"
725 name: "bar"
726 namespace: "second_ns"
727 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200728 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200729 state: ENABLED
730 permission: READ_WRITE
731 trace {
732 source: "flags.declarations"
733 state: DISABLED
734 permission: READ_ONLY
735 }
736}
737"#;
738 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200739 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
740 }
741
742 #[test]
743 fn test_parsed_flag_path_to_declaration() {
744 let text_proto = r#"
745parsed_flag {
746 package: "com.foo"
747 name: "bar"
748 namespace: "first_ns"
749 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200750 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200751 state: DISABLED
752 permission: READ_ONLY
753 trace {
754 source: "flags.declarations"
755 state: DISABLED
756 permission: READ_ONLY
757 }
758 trace {
759 source: "flags.values"
760 state: ENABLED
761 permission: READ_ONLY
762 }
763}
764"#;
765 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
766 let parsed_flag = &parsed_flags.parsed_flag[0];
767 assert_eq!(
768 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
769 "flags.declarations"
770 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200771 }
772
773 #[test]
774 fn test_parsed_flags_merge() {
775 let text_proto = r#"
776parsed_flag {
777 package: "com.first"
778 name: "first"
779 namespace: "first_ns"
780 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200781 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200782 state: DISABLED
783 permission: READ_ONLY
784 trace {
785 source: "flags.declarations"
786 state: DISABLED
787 permission: READ_ONLY
788 }
789}
790parsed_flag {
791 package: "com.second"
792 name: "second"
793 namespace: "second_ns"
794 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200795 bug: "b"
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 }
803}
804"#;
805 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
806
807 let text_proto = r#"
808parsed_flag {
809 package: "com.first"
810 name: "first"
811 namespace: "first_ns"
812 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200813 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200814 state: DISABLED
815 permission: READ_ONLY
816 trace {
817 source: "flags.declarations"
818 state: DISABLED
819 permission: READ_ONLY
820 }
821}
822"#;
823 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
824
825 let text_proto = r#"
826parsed_flag {
827 package: "com.second"
828 name: "second"
829 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200830 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200831 description: "This is the description of the second flag."
832 state: ENABLED
833 permission: READ_WRITE
834 trace {
835 source: "flags.declarations"
836 state: DISABLED
837 permission: READ_ONLY
838 }
839}
840"#;
841 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
842
843 // bad cases
844 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200845 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 +0200846
847 // valid cases
848 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
849 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
850 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
851 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
852 }
853}