blob: 8e764680ca24b4736d61576fd54090a472a73f08 [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
Colin Cross6befb342023-11-28 15:55:07 -0800277 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200278 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);
Colin Cross6befb342023-11-28 15:55:07 -0800283 if dedup {
284 // Deduplicate identical protobuf messages. Messages with the same sorting key but
285 // different fields (including the path to the original source file) will not be
286 // deduplicated and trigger an error in verify_fields.
287 merged.parsed_flag.dedup();
288 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200289 verify_fields(&merged)?;
290 Ok(merged)
291 }
292
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000293 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
294 pf.parsed_flag.sort_by_key(create_sorting_key);
295 }
296
Mårten Kongstad403658f2023-06-14 09:51:56 +0200297 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
298 format!("{}.{}", pf.package(), pf.name())
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_flag_declarations_try_from_text_proto() {
308 // valid input
309 let flag_declarations = flag_declarations::try_from_text_proto(
310 r#"
311package: "com.foo.bar"
312flag {
313 name: "first"
314 namespace: "first_ns"
315 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200316 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000317 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200318}
319flag {
320 name: "second"
321 namespace: "second_ns"
322 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200323 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000324 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200325}
326"#,
327 )
328 .unwrap();
329 assert_eq!(flag_declarations.package(), "com.foo.bar");
330 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
331 assert_eq!(first.name(), "first");
332 assert_eq!(first.namespace(), "first_ns");
333 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200334 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000335 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000336 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200337 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
338 assert_eq!(second.name(), "second");
339 assert_eq!(second.namespace(), "second_ns");
340 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200341 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000342 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000343 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200344
345 // bad input: missing package in flag declarations
346 let error = flag_declarations::try_from_text_proto(
347 r#"
348flag {
349 name: "first"
350 namespace: "first_ns"
351 description: "This is the description of the first flag."
352}
353flag {
354 name: "second"
355 namespace: "second_ns"
356 description: "This is the description of the second flag."
357}
358"#,
359 )
360 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200361 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200362
363 // bad input: missing namespace in flag declaration
364 let error = flag_declarations::try_from_text_proto(
365 r#"
366package: "com.foo.bar"
367flag {
368 name: "first"
369 description: "This is the description of the first flag."
370}
371flag {
372 name: "second"
373 namespace: "second_ns"
374 description: "This is the description of the second flag."
375}
376"#,
377 )
378 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200379 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200380
381 // bad input: bad package name in flag declarations
382 let error = flag_declarations::try_from_text_proto(
383 r#"
384package: "_com.FOO__BAR"
385flag {
386 name: "first"
387 namespace: "first_ns"
388 description: "This is the description of the first flag."
389}
390flag {
391 name: "second"
392 namespace: "second_ns"
393 description: "This is the description of the second flag."
394}
395"#,
396 )
397 .unwrap_err();
398 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
399
400 // bad input: bad name in flag declaration
401 let error = flag_declarations::try_from_text_proto(
402 r#"
403package: "com.foo.bar"
404flag {
405 name: "FIRST"
406 namespace: "first_ns"
407 description: "This is the description of the first flag."
408}
409flag {
410 name: "second"
411 namespace: "second_ns"
412 description: "This is the description of the second flag."
413}
414"#,
415 )
416 .unwrap_err();
417 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200418
419 // bad input: no bug entries in flag declaration
420 let error = flag_declarations::try_from_text_proto(
421 r#"
422package: "com.foo.bar"
423flag {
424 name: "first"
425 namespace: "first_ns"
426 description: "This is the description of the first flag."
427}
428"#,
429 )
430 .unwrap_err();
431 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
432
433 // bad input: multiple bug entries in flag declaration
434 let error = flag_declarations::try_from_text_proto(
435 r#"
436package: "com.foo.bar"
437flag {
438 name: "first"
439 namespace: "first_ns"
440 description: "This is the description of the first flag."
441 bug: "123"
442 bug: "abc"
443}
444"#,
445 )
446 .unwrap_err();
447 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Mårten Kongstad403658f2023-06-14 09:51:56 +0200448 }
449
450 #[test]
451 fn test_flag_values_try_from_text_proto() {
452 // valid input
453 let flag_values = flag_values::try_from_text_proto(
454 r#"
455flag_value {
456 package: "com.first"
457 name: "first"
458 state: DISABLED
459 permission: READ_ONLY
460}
461flag_value {
462 package: "com.second"
463 name: "second"
464 state: ENABLED
465 permission: READ_WRITE
466}
467"#,
468 )
469 .unwrap();
470 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
471 assert_eq!(first.package(), "com.first");
472 assert_eq!(first.name(), "first");
473 assert_eq!(first.state(), ProtoFlagState::DISABLED);
474 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
475 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
476 assert_eq!(second.package(), "com.second");
477 assert_eq!(second.name(), "second");
478 assert_eq!(second.state(), ProtoFlagState::ENABLED);
479 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
480
481 // bad input: bad package in flag value
482 let error = flag_values::try_from_text_proto(
483 r#"
484flag_value {
485 package: "COM.FIRST"
486 name: "first"
487 state: DISABLED
488 permission: READ_ONLY
489}
490"#,
491 )
492 .unwrap_err();
493 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
494
495 // bad input: bad name in flag value
496 let error = flag_values::try_from_text_proto(
497 r#"
498flag_value {
499 package: "com.first"
500 name: "FIRST"
501 state: DISABLED
502 permission: READ_ONLY
503}
504"#,
505 )
506 .unwrap_err();
507 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
508
509 // bad input: missing state in flag value
510 let error = flag_values::try_from_text_proto(
511 r#"
512flag_value {
513 package: "com.first"
514 name: "first"
515 permission: READ_ONLY
516}
517"#,
518 )
519 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200520 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200521
522 // bad input: missing permission in flag value
523 let error = flag_values::try_from_text_proto(
524 r#"
525flag_value {
526 package: "com.first"
527 name: "first"
528 state: DISABLED
529}
530"#,
531 )
532 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200533 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200534 }
535
536 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
537 use protobuf::Message;
538
539 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
540 let mut binary_proto = Vec::new();
541 parsed_flags.write_to_vec(&mut binary_proto)?;
542 parsed_flags::try_from_binary_proto(&binary_proto)
543 }
544
545 #[test]
546 fn test_parsed_flags_try_from_text_proto() {
547 // valid input
548 let text_proto = r#"
549parsed_flag {
550 package: "com.first"
551 name: "first"
552 namespace: "first_ns"
553 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200554 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200555 state: DISABLED
556 permission: READ_ONLY
557 trace {
558 source: "flags.declarations"
559 state: DISABLED
560 permission: READ_ONLY
561 }
562}
563parsed_flag {
564 package: "com.second"
565 name: "second"
566 namespace: "second_ns"
567 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200568 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200569 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200570 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200571 trace {
572 source: "flags.declarations"
573 state: DISABLED
574 permission: READ_ONLY
575 }
576 trace {
577 source: "flags.values"
578 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200579 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200580 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000581 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200582}
583"#;
584 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
585 assert_eq!(parsed_flags.parsed_flag.len(), 2);
586 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
587 assert_eq!(second.package(), "com.second");
588 assert_eq!(second.name(), "second");
589 assert_eq!(second.namespace(), "second_ns");
590 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200591 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200592 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200593 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200594 assert_eq!(2, second.trace.len());
595 assert_eq!(second.trace[0].source(), "flags.declarations");
596 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
597 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
598 assert_eq!(second.trace[1].source(), "flags.values");
599 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200600 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000601 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200602
603 // valid input: empty
604 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
605 assert!(parsed_flags.parsed_flag.is_empty());
606
607 // bad input: empty trace
608 let text_proto = r#"
609parsed_flag {
610 package: "com.first"
611 name: "first"
612 namespace: "first_ns"
613 description: "This is the description of the first flag."
614 state: DISABLED
615 permission: READ_ONLY
616}
617"#;
618 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
619 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
620
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200621 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200622 let text_proto = r#"
623parsed_flag {
624 package: "com.first"
625 name: "first"
626 description: "This is the description of the first flag."
627 state: DISABLED
628 permission: READ_ONLY
629 trace {
630 source: "flags.declarations"
631 state: DISABLED
632 permission: READ_ONLY
633 }
634}
635"#;
636 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200637 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200638
639 // bad input: parsed_flag not sorted by package
640 let text_proto = r#"
641parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200642 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200643 name: "first"
644 namespace: "first_ns"
645 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200646 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200647 state: DISABLED
648 permission: READ_ONLY
649 trace {
650 source: "flags.declarations"
651 state: DISABLED
652 permission: READ_ONLY
653 }
654}
655parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200656 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200657 name: "second"
658 namespace: "second_ns"
659 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200660 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200661 state: ENABLED
662 permission: READ_WRITE
663 trace {
664 source: "flags.declarations"
665 state: DISABLED
666 permission: READ_ONLY
667 }
668}
669"#;
670 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
671 assert_eq!(
672 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200673 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200674 );
675
676 // bad input: parsed_flag not sorted by name
677 let text_proto = r#"
678parsed_flag {
679 package: "com.foo"
680 name: "bbb"
681 namespace: "first_ns"
682 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200683 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200684 state: DISABLED
685 permission: READ_ONLY
686 trace {
687 source: "flags.declarations"
688 state: DISABLED
689 permission: READ_ONLY
690 }
691}
692parsed_flag {
693 package: "com.foo"
694 name: "aaa"
695 namespace: "second_ns"
696 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200697 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200698 state: ENABLED
699 permission: READ_WRITE
700 trace {
701 source: "flags.declarations"
702 state: DISABLED
703 permission: READ_ONLY
704 }
705}
706"#;
707 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
708 assert_eq!(
709 format!("{:?}", error),
710 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
711 );
712
713 // bad input: duplicate flags
714 let text_proto = r#"
715parsed_flag {
716 package: "com.foo"
717 name: "bar"
718 namespace: "first_ns"
719 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200720 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200721 state: DISABLED
722 permission: READ_ONLY
723 trace {
724 source: "flags.declarations"
725 state: DISABLED
726 permission: READ_ONLY
727 }
728}
729parsed_flag {
730 package: "com.foo"
731 name: "bar"
732 namespace: "second_ns"
733 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200734 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200735 state: ENABLED
736 permission: READ_WRITE
737 trace {
738 source: "flags.declarations"
739 state: DISABLED
740 permission: READ_ONLY
741 }
742}
743"#;
744 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200745 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
746 }
747
748 #[test]
749 fn test_parsed_flag_path_to_declaration() {
750 let text_proto = r#"
751parsed_flag {
752 package: "com.foo"
753 name: "bar"
754 namespace: "first_ns"
755 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200756 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200757 state: DISABLED
758 permission: READ_ONLY
759 trace {
760 source: "flags.declarations"
761 state: DISABLED
762 permission: READ_ONLY
763 }
764 trace {
765 source: "flags.values"
766 state: ENABLED
767 permission: READ_ONLY
768 }
769}
770"#;
771 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
772 let parsed_flag = &parsed_flags.parsed_flag[0];
773 assert_eq!(
774 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
775 "flags.declarations"
776 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200777 }
778
779 #[test]
780 fn test_parsed_flags_merge() {
781 let text_proto = r#"
782parsed_flag {
783 package: "com.first"
784 name: "first"
785 namespace: "first_ns"
786 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200787 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200788 state: DISABLED
789 permission: READ_ONLY
790 trace {
791 source: "flags.declarations"
792 state: DISABLED
793 permission: READ_ONLY
794 }
795}
796parsed_flag {
797 package: "com.second"
798 name: "second"
799 namespace: "second_ns"
800 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200801 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200802 state: ENABLED
803 permission: READ_WRITE
804 trace {
805 source: "flags.declarations"
806 state: DISABLED
807 permission: READ_ONLY
808 }
809}
810"#;
811 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
812
813 let text_proto = r#"
814parsed_flag {
815 package: "com.first"
816 name: "first"
817 namespace: "first_ns"
818 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200819 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200820 state: DISABLED
821 permission: READ_ONLY
822 trace {
823 source: "flags.declarations"
824 state: DISABLED
825 permission: READ_ONLY
826 }
827}
828"#;
829 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
830
831 let text_proto = r#"
832parsed_flag {
833 package: "com.second"
834 name: "second"
835 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200836 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200837 description: "This is the description of the second flag."
838 state: ENABLED
839 permission: READ_WRITE
840 trace {
841 source: "flags.declarations"
842 state: DISABLED
843 permission: READ_ONLY
844 }
845}
846"#;
847 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
848
Colin Cross6befb342023-11-28 15:55:07 -0800849 let text_proto = r#"
850parsed_flag {
851 package: "com.second"
852 name: "second"
853 namespace: "second_ns"
854 bug: "b"
855 description: "This is the description of the second flag."
856 state: ENABLED
857 permission: READ_WRITE
858 trace {
859 source: "duplicate/flags.declarations"
860 state: DISABLED
861 permission: READ_ONLY
862 }
863}
864"#;
865 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
866
Mårten Kongstad403658f2023-06-14 09:51:56 +0200867 // bad cases
Colin Cross6befb342023-11-28 15:55:07 -0800868
869 // two of the same flag with dedup disabled
870 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200871 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 +0200872
Colin Cross6befb342023-11-28 15:55:07 -0800873 // two conflicting flags with dedup disabled
874 let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
875 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
876
877 // two conflicting flags with dedup enabled
878 let error = parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
879 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
880
Mårten Kongstad403658f2023-06-14 09:51:56 +0200881 // valid cases
Colin Cross6befb342023-11-28 15:55:07 -0800882 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
883 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
884 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
885 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
886 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap());
887 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap());
888 assert_eq!(expected, parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap());
889 assert_eq!(expected, parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap());
890
891 // two identical flags with dedup enabled
892 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200893 }
894}