blob: b93abcce243baba3cf685093ddbe879e195586d3 [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 Kongstad1b8636b2023-06-22 10:12:24 +0200218
Mårten Kongstad403658f2023-06-14 09:51:56 +0200219 Ok(())
220 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200221
222 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
223 debug_assert!(!pf.trace.is_empty());
224 pf.trace[0].source()
225 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200226}
227
228pub mod parsed_flags {
229 use super::*;
230 use anyhow::bail;
231 use std::cmp::Ordering;
232
233 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
234 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
235 verify_fields(&message)?;
236 Ok(message)
237 }
238
239 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200240 use crate::protos::parsed_flag::path_to_declaration;
241
Mårten Kongstad403658f2023-06-14 09:51:56 +0200242 let mut previous: Option<&ProtoParsedFlag> = None;
243 for parsed_flag in pf.parsed_flag.iter() {
244 if let Some(prev) = previous {
245 let a = create_sorting_key(prev);
246 let b = create_sorting_key(parsed_flag);
247 match a.cmp(&b) {
248 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200249 Ordering::Equal => bail!(
250 "bad parsed flags: duplicate flag {} (defined in {} and {})",
251 a,
252 path_to_declaration(prev),
253 path_to_declaration(parsed_flag)
254 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200255 Ordering::Greater => {
256 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
257 }
258 }
259 }
260 super::parsed_flag::verify_fields(parsed_flag)?;
261 previous = Some(parsed_flag);
262 }
263 Ok(())
264 }
265
266 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
267 let mut merged = ProtoParsedFlags::new();
268 for mut pfs in parsed_flags.into_iter() {
269 merged.parsed_flag.append(&mut pfs.parsed_flag);
270 }
271 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
272 verify_fields(&merged)?;
273 Ok(merged)
274 }
275
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000276 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
277 pf.parsed_flag.sort_by_key(create_sorting_key);
278 }
279
Mårten Kongstad403658f2023-06-14 09:51:56 +0200280 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
281 format!("{}.{}", pf.package(), pf.name())
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_flag_declarations_try_from_text_proto() {
291 // valid input
292 let flag_declarations = flag_declarations::try_from_text_proto(
293 r#"
294package: "com.foo.bar"
295flag {
296 name: "first"
297 namespace: "first_ns"
298 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200299 bug: "123"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200300}
301flag {
302 name: "second"
303 namespace: "second_ns"
304 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200305 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000306 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200307}
308"#,
309 )
310 .unwrap();
311 assert_eq!(flag_declarations.package(), "com.foo.bar");
312 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
313 assert_eq!(first.name(), "first");
314 assert_eq!(first.namespace(), "first_ns");
315 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200316 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000317 assert!(!first.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200318 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
319 assert_eq!(second.name(), "second");
320 assert_eq!(second.namespace(), "second_ns");
321 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200322 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000323 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200324
325 // bad input: missing package in flag declarations
326 let error = flag_declarations::try_from_text_proto(
327 r#"
328flag {
329 name: "first"
330 namespace: "first_ns"
331 description: "This is the description of the first flag."
332}
333flag {
334 name: "second"
335 namespace: "second_ns"
336 description: "This is the description of the second flag."
337}
338"#,
339 )
340 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200341 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200342
343 // bad input: missing namespace in flag declaration
344 let error = flag_declarations::try_from_text_proto(
345 r#"
346package: "com.foo.bar"
347flag {
348 name: "first"
349 description: "This is the description of the first flag."
350}
351flag {
352 name: "second"
353 namespace: "second_ns"
354 description: "This is the description of the second flag."
355}
356"#,
357 )
358 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200359 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200360
361 // bad input: bad package name in flag declarations
362 let error = flag_declarations::try_from_text_proto(
363 r#"
364package: "_com.FOO__BAR"
365flag {
366 name: "first"
367 namespace: "first_ns"
368 description: "This is the description of the first flag."
369}
370flag {
371 name: "second"
372 namespace: "second_ns"
373 description: "This is the description of the second flag."
374}
375"#,
376 )
377 .unwrap_err();
378 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
379
380 // bad input: bad name in flag declaration
381 let error = flag_declarations::try_from_text_proto(
382 r#"
383package: "com.foo.bar"
384flag {
385 name: "FIRST"
386 namespace: "first_ns"
387 description: "This is the description of the first flag."
388}
389flag {
390 name: "second"
391 namespace: "second_ns"
392 description: "This is the description of the second flag."
393}
394"#,
395 )
396 .unwrap_err();
397 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200398
399 // bad input: no bug entries in flag declaration
400 let error = flag_declarations::try_from_text_proto(
401 r#"
402package: "com.foo.bar"
403flag {
404 name: "first"
405 namespace: "first_ns"
406 description: "This is the description of the first flag."
407}
408"#,
409 )
410 .unwrap_err();
411 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
412
413 // bad input: multiple 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 bug: "123"
422 bug: "abc"
423}
424"#,
425 )
426 .unwrap_err();
427 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Mårten Kongstad403658f2023-06-14 09:51:56 +0200428 }
429
430 #[test]
431 fn test_flag_values_try_from_text_proto() {
432 // valid input
433 let flag_values = flag_values::try_from_text_proto(
434 r#"
435flag_value {
436 package: "com.first"
437 name: "first"
438 state: DISABLED
439 permission: READ_ONLY
440}
441flag_value {
442 package: "com.second"
443 name: "second"
444 state: ENABLED
445 permission: READ_WRITE
446}
447"#,
448 )
449 .unwrap();
450 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
451 assert_eq!(first.package(), "com.first");
452 assert_eq!(first.name(), "first");
453 assert_eq!(first.state(), ProtoFlagState::DISABLED);
454 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
455 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
456 assert_eq!(second.package(), "com.second");
457 assert_eq!(second.name(), "second");
458 assert_eq!(second.state(), ProtoFlagState::ENABLED);
459 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
460
461 // bad input: bad package in flag value
462 let error = flag_values::try_from_text_proto(
463 r#"
464flag_value {
465 package: "COM.FIRST"
466 name: "first"
467 state: DISABLED
468 permission: READ_ONLY
469}
470"#,
471 )
472 .unwrap_err();
473 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
474
475 // bad input: bad name 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 name"));
488
489 // bad input: missing state in flag value
490 let error = flag_values::try_from_text_proto(
491 r#"
492flag_value {
493 package: "com.first"
494 name: "first"
495 permission: READ_ONLY
496}
497"#,
498 )
499 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200500 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200501
502 // bad input: missing permission in flag value
503 let error = flag_values::try_from_text_proto(
504 r#"
505flag_value {
506 package: "com.first"
507 name: "first"
508 state: DISABLED
509}
510"#,
511 )
512 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200513 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200514 }
515
516 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
517 use protobuf::Message;
518
519 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
520 let mut binary_proto = Vec::new();
521 parsed_flags.write_to_vec(&mut binary_proto)?;
522 parsed_flags::try_from_binary_proto(&binary_proto)
523 }
524
525 #[test]
526 fn test_parsed_flags_try_from_text_proto() {
527 // valid input
528 let text_proto = r#"
529parsed_flag {
530 package: "com.first"
531 name: "first"
532 namespace: "first_ns"
533 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200534 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200535 state: DISABLED
536 permission: READ_ONLY
537 trace {
538 source: "flags.declarations"
539 state: DISABLED
540 permission: READ_ONLY
541 }
542}
543parsed_flag {
544 package: "com.second"
545 name: "second"
546 namespace: "second_ns"
547 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200548 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200549 state: ENABLED
550 permission: READ_WRITE
551 trace {
552 source: "flags.declarations"
553 state: DISABLED
554 permission: READ_ONLY
555 }
556 trace {
557 source: "flags.values"
558 state: ENABLED
559 permission: READ_WRITE
560 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000561 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200562}
563"#;
564 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
565 assert_eq!(parsed_flags.parsed_flag.len(), 2);
566 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
567 assert_eq!(second.package(), "com.second");
568 assert_eq!(second.name(), "second");
569 assert_eq!(second.namespace(), "second_ns");
570 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200571 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200572 assert_eq!(second.state(), ProtoFlagState::ENABLED);
573 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
574 assert_eq!(2, second.trace.len());
575 assert_eq!(second.trace[0].source(), "flags.declarations");
576 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
577 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
578 assert_eq!(second.trace[1].source(), "flags.values");
579 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
580 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
Zhi Dou71f1b352023-08-21 22:49:46 +0000581 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200582
583 // valid input: empty
584 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
585 assert!(parsed_flags.parsed_flag.is_empty());
586
587 // bad input: empty trace
588 let text_proto = r#"
589parsed_flag {
590 package: "com.first"
591 name: "first"
592 namespace: "first_ns"
593 description: "This is the description of the first flag."
594 state: DISABLED
595 permission: READ_ONLY
596}
597"#;
598 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
599 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
600
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200601 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200602 let text_proto = r#"
603parsed_flag {
604 package: "com.first"
605 name: "first"
606 description: "This is the description of the first flag."
607 state: DISABLED
608 permission: READ_ONLY
609 trace {
610 source: "flags.declarations"
611 state: DISABLED
612 permission: READ_ONLY
613 }
614}
615"#;
616 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200617 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200618
619 // bad input: parsed_flag not sorted by package
620 let text_proto = r#"
621parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200622 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200623 name: "first"
624 namespace: "first_ns"
625 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200626 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200627 state: DISABLED
628 permission: READ_ONLY
629 trace {
630 source: "flags.declarations"
631 state: DISABLED
632 permission: READ_ONLY
633 }
634}
635parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200636 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200637 name: "second"
638 namespace: "second_ns"
639 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200640 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200641 state: ENABLED
642 permission: READ_WRITE
643 trace {
644 source: "flags.declarations"
645 state: DISABLED
646 permission: READ_ONLY
647 }
648}
649"#;
650 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
651 assert_eq!(
652 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200653 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200654 );
655
656 // bad input: parsed_flag not sorted by name
657 let text_proto = r#"
658parsed_flag {
659 package: "com.foo"
660 name: "bbb"
661 namespace: "first_ns"
662 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200663 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200664 state: DISABLED
665 permission: READ_ONLY
666 trace {
667 source: "flags.declarations"
668 state: DISABLED
669 permission: READ_ONLY
670 }
671}
672parsed_flag {
673 package: "com.foo"
674 name: "aaa"
675 namespace: "second_ns"
676 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200677 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200678 state: ENABLED
679 permission: READ_WRITE
680 trace {
681 source: "flags.declarations"
682 state: DISABLED
683 permission: READ_ONLY
684 }
685}
686"#;
687 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
688 assert_eq!(
689 format!("{:?}", error),
690 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
691 );
692
693 // bad input: duplicate flags
694 let text_proto = r#"
695parsed_flag {
696 package: "com.foo"
697 name: "bar"
698 namespace: "first_ns"
699 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200700 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200701 state: DISABLED
702 permission: READ_ONLY
703 trace {
704 source: "flags.declarations"
705 state: DISABLED
706 permission: READ_ONLY
707 }
708}
709parsed_flag {
710 package: "com.foo"
711 name: "bar"
712 namespace: "second_ns"
713 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200714 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200715 state: ENABLED
716 permission: READ_WRITE
717 trace {
718 source: "flags.declarations"
719 state: DISABLED
720 permission: READ_ONLY
721 }
722}
723"#;
724 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200725 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
726 }
727
728 #[test]
729 fn test_parsed_flag_path_to_declaration() {
730 let text_proto = r#"
731parsed_flag {
732 package: "com.foo"
733 name: "bar"
734 namespace: "first_ns"
735 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200736 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200737 state: DISABLED
738 permission: READ_ONLY
739 trace {
740 source: "flags.declarations"
741 state: DISABLED
742 permission: READ_ONLY
743 }
744 trace {
745 source: "flags.values"
746 state: ENABLED
747 permission: READ_ONLY
748 }
749}
750"#;
751 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
752 let parsed_flag = &parsed_flags.parsed_flag[0];
753 assert_eq!(
754 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
755 "flags.declarations"
756 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200757 }
758
759 #[test]
760 fn test_parsed_flags_merge() {
761 let text_proto = r#"
762parsed_flag {
763 package: "com.first"
764 name: "first"
765 namespace: "first_ns"
766 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200767 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200768 state: DISABLED
769 permission: READ_ONLY
770 trace {
771 source: "flags.declarations"
772 state: DISABLED
773 permission: READ_ONLY
774 }
775}
776parsed_flag {
777 package: "com.second"
778 name: "second"
779 namespace: "second_ns"
780 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200781 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200782 state: ENABLED
783 permission: READ_WRITE
784 trace {
785 source: "flags.declarations"
786 state: DISABLED
787 permission: READ_ONLY
788 }
789}
790"#;
791 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
792
793 let text_proto = r#"
794parsed_flag {
795 package: "com.first"
796 name: "first"
797 namespace: "first_ns"
798 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200799 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200800 state: DISABLED
801 permission: READ_ONLY
802 trace {
803 source: "flags.declarations"
804 state: DISABLED
805 permission: READ_ONLY
806 }
807}
808"#;
809 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
810
811 let text_proto = r#"
812parsed_flag {
813 package: "com.second"
814 name: "second"
815 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200816 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200817 description: "This is the description of the second flag."
818 state: ENABLED
819 permission: READ_WRITE
820 trace {
821 source: "flags.declarations"
822 state: DISABLED
823 permission: READ_ONLY
824 }
825}
826"#;
827 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
828
829 // bad cases
830 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200831 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 +0200832
833 // valid cases
834 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
835 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
836 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
837 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
838 }
839}