blob: 37eb67d85a2e649f2c75d990ddcdf6709f8baa5a [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 {
Micha Schwab1a1a08a2023-11-28 16:14:30 -050032 pub use aconfig_protos::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
Mårten Kongstadf9422522023-06-14 08:38:46 +020033 pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
34 pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
Micha Schwab1a1a08a2023-11-28 16:14:30 -050035 pub use aconfig_protos::aconfig::Flag_metadata as ProtoFlagMetadata;
Mårten Kongstadf9422522023-06-14 08:38:46 +020036 pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
37 pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
38 pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
39 pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
40 pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
41 pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
42 pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
43}
Mårten Kongstada1029092023-05-08 11:51:59 +020044
Mårten Kongstadbb520722023-04-26 13:16:41 +020045// ---- When building with cargo ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020046#[cfg(feature = "cargo")]
Mårten Kongstadf9422522023-06-14 08:38:46 +020047mod auto_generated {
48 // include! statements should be avoided (because they import file contents verbatim), but
49 // because this is only used during local development, and only if using cargo instead of the
50 // Android tool-chain, we allow it
51 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
Micha Schwab1a1a08a2023-11-28 16:14:30 -050052 pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
Mårten Kongstadf9422522023-06-14 08:38:46 +020053 pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
54 pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
Micha Schwab1a1a08a2023-11-28 16:14:30 -050055 pub use aconfig::Flag_metadata as ProtoFlagMetadata;
Mårten Kongstadf9422522023-06-14 08:38:46 +020056 pub use aconfig::Flag_permission as ProtoFlagPermission;
57 pub use aconfig::Flag_state as ProtoFlagState;
58 pub use aconfig::Flag_value as ProtoFlagValue;
59 pub use aconfig::Flag_values as ProtoFlagValues;
60 pub use aconfig::Parsed_flag as ProtoParsedFlag;
61 pub use aconfig::Parsed_flags as ProtoParsedFlags;
62 pub use aconfig::Tracepoint as ProtoTracepoint;
63}
Mårten Kongstada1029092023-05-08 11:51:59 +020064
Mårten Kongstadbb520722023-04-26 13:16:41 +020065// ---- Common for both the Android tool-chain and cargo ----
Mårten Kongstadf9422522023-06-14 08:38:46 +020066pub use auto_generated::*;
67
Mårten Kongstadbb520722023-04-26 13:16:41 +020068use anyhow::Result;
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020069use paste::paste;
Mårten Kongstadbb520722023-04-26 13:16:41 +020070
Mårten Kongstad403658f2023-06-14 09:51:56 +020071fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020072where
73 T: protobuf::MessageFull,
74{
Mårten Kongstadbb520722023-04-26 13:16:41 +020075 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
76}
Mårten Kongstad403658f2023-06-14 09:51:56 +020077
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020078macro_rules! ensure_required_fields {
79 ($type:expr, $struct:expr, $($field:expr),+) => {
80 $(
81 paste! {
82 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
83 }
84 )+
85 };
86}
87
Mårten Kongstad403658f2023-06-14 09:51:56 +020088pub mod flag_declaration {
89 use super::*;
90 use crate::codegen;
91 use anyhow::ensure;
92
93 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020094 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020095
Mårten Kongstad403658f2023-06-14 09:51:56 +020096 ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
97 ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
98 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +020099 ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200100
Mårten Kongstad403658f2023-06-14 09:51:56 +0200101 Ok(())
102 }
103}
104
105pub mod flag_declarations {
106 use super::*;
107 use crate::codegen;
108 use anyhow::ensure;
109
110 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
111 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
112 verify_fields(&pdf)?;
113 Ok(pdf)
114 }
115
116 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200117 ensure_required_fields!("flag declarations", pdf, "package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000118 // TODO(b/312769710): Make the container field required.
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200119
Mårten Kongstad403658f2023-06-14 09:51:56 +0200120 ensure!(
121 codegen::is_valid_package_ident(pdf.package()),
122 "bad flag declarations: bad package"
123 );
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000124 ensure!(
125 !pdf.has_container() || codegen::is_valid_container_ident(pdf.container()),
126 "bad flag declarations: bad container"
127 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200128 for flag_declaration in pdf.flag.iter() {
129 super::flag_declaration::verify_fields(flag_declaration)?;
130 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200131
Mårten Kongstad403658f2023-06-14 09:51:56 +0200132 Ok(())
133 }
134}
135
136pub mod flag_value {
137 use super::*;
138 use crate::codegen;
139 use anyhow::ensure;
140
141 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200142 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200143
Mårten Kongstad403658f2023-06-14 09:51:56 +0200144 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
145 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200146
Mårten Kongstad403658f2023-06-14 09:51:56 +0200147 Ok(())
148 }
149}
150
151pub mod flag_values {
152 use super::*;
153
154 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
155 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
156 verify_fields(&pfv)?;
157 Ok(pfv)
158 }
159
160 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
161 for flag_value in pfv.flag_value.iter() {
162 super::flag_value::verify_fields(flag_value)?;
163 }
164 Ok(())
165 }
166}
167
Zhi Dou24a0b6a2023-08-10 21:39:59 +0000168pub mod flag_permission {
169 use super::*;
170 use anyhow::bail;
171
172 pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
173 match permission.to_ascii_lowercase().as_str() {
174 "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
175 "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
176 _ => bail!("Permission needs to be read_only or read_write."),
177 }
178 }
179
180 pub fn to_string(permission: &ProtoFlagPermission) -> &str {
181 match permission {
182 ProtoFlagPermission::READ_WRITE => "read_write",
183 ProtoFlagPermission::READ_ONLY => "read_only",
184 }
185 }
186}
187
Mårten Kongstad403658f2023-06-14 09:51:56 +0200188pub mod tracepoint {
189 use super::*;
190 use anyhow::ensure;
191
192 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200193 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200194
Mårten Kongstad403658f2023-06-14 09:51:56 +0200195 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200196
Mårten Kongstad403658f2023-06-14 09:51:56 +0200197 Ok(())
198 }
199}
200
201pub mod parsed_flag {
202 use super::*;
203 use crate::codegen;
204 use anyhow::ensure;
205
206 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200207 ensure_required_fields!(
208 "parsed flag",
209 pf,
210 "package",
211 "name",
212 "namespace",
213 "description",
214 "state",
215 "permission"
216 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200217
Mårten Kongstad403658f2023-06-14 09:51:56 +0200218 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000219 ensure!(
220 !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
221 "bad parsed flag: bad container"
222 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200223 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
224 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
225 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
226 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
227 for tp in pf.trace.iter() {
228 super::tracepoint::verify_fields(tp)?;
229 }
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200230 ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
Mårten Kongstad21717e72023-09-04 13:28:36 +0200231 if pf.is_fixed_read_only() {
232 ensure!(
233 pf.permission() == ProtoFlagPermission::READ_ONLY,
234 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
235 );
236 for tp in pf.trace.iter() {
237 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
238 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
239 );
240 }
241 }
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200242
Mårten Kongstad403658f2023-06-14 09:51:56 +0200243 Ok(())
244 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200245
246 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
247 debug_assert!(!pf.trace.is_empty());
248 pf.trace[0].source()
249 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200250}
251
252pub mod parsed_flags {
253 use super::*;
254 use anyhow::bail;
255 use std::cmp::Ordering;
256
257 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
258 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
259 verify_fields(&message)?;
260 Ok(message)
261 }
262
263 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200264 use crate::protos::parsed_flag::path_to_declaration;
265
Mårten Kongstad403658f2023-06-14 09:51:56 +0200266 let mut previous: Option<&ProtoParsedFlag> = None;
267 for parsed_flag in pf.parsed_flag.iter() {
268 if let Some(prev) = previous {
269 let a = create_sorting_key(prev);
270 let b = create_sorting_key(parsed_flag);
271 match a.cmp(&b) {
272 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200273 Ordering::Equal => bail!(
274 "bad parsed flags: duplicate flag {} (defined in {} and {})",
275 a,
276 path_to_declaration(prev),
277 path_to_declaration(parsed_flag)
278 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200279 Ordering::Greater => {
280 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
281 }
282 }
283 }
284 super::parsed_flag::verify_fields(parsed_flag)?;
285 previous = Some(parsed_flag);
286 }
287 Ok(())
288 }
289
Colin Cross6befb342023-11-28 15:55:07 -0800290 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200291 let mut merged = ProtoParsedFlags::new();
292 for mut pfs in parsed_flags.into_iter() {
293 merged.parsed_flag.append(&mut pfs.parsed_flag);
294 }
295 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
Colin Cross6befb342023-11-28 15:55:07 -0800296 if dedup {
297 // Deduplicate identical protobuf messages. Messages with the same sorting key but
298 // different fields (including the path to the original source file) will not be
299 // deduplicated and trigger an error in verify_fields.
300 merged.parsed_flag.dedup();
301 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200302 verify_fields(&merged)?;
303 Ok(merged)
304 }
305
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000306 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
307 pf.parsed_flag.sort_by_key(create_sorting_key);
308 }
309
Mårten Kongstad403658f2023-06-14 09:51:56 +0200310 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
311 format!("{}.{}", pf.package(), pf.name())
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_flag_declarations_try_from_text_proto() {
321 // valid input
322 let flag_declarations = flag_declarations::try_from_text_proto(
323 r#"
324package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000325container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200326flag {
327 name: "first"
328 namespace: "first_ns"
329 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200330 bug: "123"
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000331 is_exported: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200332}
333flag {
334 name: "second"
335 namespace: "second_ns"
336 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200337 bug: "abc"
Zhi Dou71f1b352023-08-21 22:49:46 +0000338 is_fixed_read_only: true
Mårten Kongstad403658f2023-06-14 09:51:56 +0200339}
340"#,
341 )
342 .unwrap();
343 assert_eq!(flag_declarations.package(), "com.foo.bar");
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000344 assert_eq!(flag_declarations.container(), "system");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200345 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
346 assert_eq!(first.name(), "first");
347 assert_eq!(first.namespace(), "first_ns");
348 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200349 assert_eq!(first.bug, vec!["123"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000350 assert!(!first.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000351 assert!(first.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200352 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
353 assert_eq!(second.name(), "second");
354 assert_eq!(second.namespace(), "second_ns");
355 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200356 assert_eq!(second.bug, vec!["abc"]);
Zhi Dou71f1b352023-08-21 22:49:46 +0000357 assert!(second.is_fixed_read_only());
Oriol Prieto Gasco0b9d2892023-11-20 16:23:51 +0000358 assert!(!second.is_exported());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200359
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000360 // valid input: missing container in flag declarations is supported
361 let flag_declarations = flag_declarations::try_from_text_proto(
362 r#"
363package: "com.foo.bar"
364flag {
365 name: "first"
366 namespace: "first_ns"
367 description: "This is the description of the first flag."
368 bug: "123"
369}
370"#,
371 )
372 .unwrap();
373 assert_eq!(flag_declarations.container(), "");
374 assert!(!flag_declarations.has_container());
375
Mårten Kongstad403658f2023-06-14 09:51:56 +0200376 // bad input: missing package in flag declarations
377 let error = flag_declarations::try_from_text_proto(
378 r#"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000379container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200380flag {
381 name: "first"
382 namespace: "first_ns"
383 description: "This is the description of the first flag."
384}
385flag {
386 name: "second"
387 namespace: "second_ns"
388 description: "This is the description of the second flag."
389}
390"#,
391 )
392 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200393 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200394
395 // bad input: missing namespace in flag declaration
396 let error = flag_declarations::try_from_text_proto(
397 r#"
398package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000399container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200400flag {
401 name: "first"
402 description: "This is the description of the first flag."
403}
404flag {
405 name: "second"
406 namespace: "second_ns"
407 description: "This is the description of the second flag."
408}
409"#,
410 )
411 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200412 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200413
414 // bad input: bad package name in flag declarations
415 let error = flag_declarations::try_from_text_proto(
416 r#"
417package: "_com.FOO__BAR"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000418container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200419flag {
420 name: "first"
421 namespace: "first_ns"
422 description: "This is the description of the first flag."
423}
424flag {
425 name: "second"
426 namespace: "second_ns"
427 description: "This is the description of the second flag."
428}
429"#,
430 )
431 .unwrap_err();
432 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
433
434 // bad input: bad name in flag declaration
435 let error = flag_declarations::try_from_text_proto(
436 r#"
437package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000438container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200439flag {
440 name: "FIRST"
441 namespace: "first_ns"
442 description: "This is the description of the first flag."
443}
444flag {
445 name: "second"
446 namespace: "second_ns"
447 description: "This is the description of the second flag."
448}
449"#,
450 )
451 .unwrap_err();
452 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200453
454 // bad input: no bug entries in flag declaration
455 let error = flag_declarations::try_from_text_proto(
456 r#"
457package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000458container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200459flag {
460 name: "first"
461 namespace: "first_ns"
462 description: "This is the description of the first flag."
463}
464"#,
465 )
466 .unwrap_err();
467 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
468
469 // bad input: multiple bug entries in flag declaration
470 let error = flag_declarations::try_from_text_proto(
471 r#"
472package: "com.foo.bar"
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000473container: "system"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200474flag {
475 name: "first"
476 namespace: "first_ns"
477 description: "This is the description of the first flag."
478 bug: "123"
479 bug: "abc"
480}
481"#,
482 )
483 .unwrap_err();
484 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000485
486 // bad input: invalid container name in flag declaration
487 let error = flag_declarations::try_from_text_proto(
488 r#"
489package: "com.foo.bar"
490container: "__bad_bad_container.com"
491flag {
492 name: "first"
493 namespace: "first_ns"
494 description: "This is the description of the first flag."
495 bug: "123"
496 bug: "abc"
497}
498"#,
499 )
500 .unwrap_err();
501 assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
502
503 // TODO(b/312769710): Verify error when container is missing.
Mårten Kongstad403658f2023-06-14 09:51:56 +0200504 }
505
506 #[test]
507 fn test_flag_values_try_from_text_proto() {
508 // valid input
509 let flag_values = flag_values::try_from_text_proto(
510 r#"
511flag_value {
512 package: "com.first"
513 name: "first"
514 state: DISABLED
515 permission: READ_ONLY
516}
517flag_value {
518 package: "com.second"
519 name: "second"
520 state: ENABLED
521 permission: READ_WRITE
522}
523"#,
524 )
525 .unwrap();
526 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
527 assert_eq!(first.package(), "com.first");
528 assert_eq!(first.name(), "first");
529 assert_eq!(first.state(), ProtoFlagState::DISABLED);
530 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
531 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
532 assert_eq!(second.package(), "com.second");
533 assert_eq!(second.name(), "second");
534 assert_eq!(second.state(), ProtoFlagState::ENABLED);
535 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
536
537 // bad input: bad package in flag value
538 let error = flag_values::try_from_text_proto(
539 r#"
540flag_value {
541 package: "COM.FIRST"
542 name: "first"
543 state: DISABLED
544 permission: READ_ONLY
545}
546"#,
547 )
548 .unwrap_err();
549 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
550
551 // bad input: bad name in flag value
552 let error = flag_values::try_from_text_proto(
553 r#"
554flag_value {
555 package: "com.first"
556 name: "FIRST"
557 state: DISABLED
558 permission: READ_ONLY
559}
560"#,
561 )
562 .unwrap_err();
563 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
564
565 // bad input: missing state in flag value
566 let error = flag_values::try_from_text_proto(
567 r#"
568flag_value {
569 package: "com.first"
570 name: "first"
571 permission: READ_ONLY
572}
573"#,
574 )
575 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200576 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200577
578 // bad input: missing permission in flag value
579 let error = flag_values::try_from_text_proto(
580 r#"
581flag_value {
582 package: "com.first"
583 name: "first"
584 state: DISABLED
585}
586"#,
587 )
588 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200589 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200590 }
591
592 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
593 use protobuf::Message;
594
595 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
596 let mut binary_proto = Vec::new();
597 parsed_flags.write_to_vec(&mut binary_proto)?;
598 parsed_flags::try_from_binary_proto(&binary_proto)
599 }
600
601 #[test]
602 fn test_parsed_flags_try_from_text_proto() {
603 // valid input
604 let text_proto = r#"
605parsed_flag {
606 package: "com.first"
607 name: "first"
608 namespace: "first_ns"
609 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200610 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200611 state: DISABLED
612 permission: READ_ONLY
613 trace {
614 source: "flags.declarations"
615 state: DISABLED
616 permission: READ_ONLY
617 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000618 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200619}
620parsed_flag {
621 package: "com.second"
622 name: "second"
623 namespace: "second_ns"
624 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200625 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200626 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200627 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200628 trace {
629 source: "flags.declarations"
630 state: DISABLED
631 permission: READ_ONLY
632 }
633 trace {
634 source: "flags.values"
635 state: ENABLED
Mårten Kongstad21717e72023-09-04 13:28:36 +0200636 permission: READ_ONLY
Mårten Kongstad403658f2023-06-14 09:51:56 +0200637 }
Zhi Dou71f1b352023-08-21 22:49:46 +0000638 is_fixed_read_only: true
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000639 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200640}
641"#;
642 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
643 assert_eq!(parsed_flags.parsed_flag.len(), 2);
644 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
645 assert_eq!(second.package(), "com.second");
646 assert_eq!(second.name(), "second");
647 assert_eq!(second.namespace(), "second_ns");
648 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200649 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200650 assert_eq!(second.state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200651 assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200652 assert_eq!(2, second.trace.len());
653 assert_eq!(second.trace[0].source(), "flags.declarations");
654 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
655 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
656 assert_eq!(second.trace[1].source(), "flags.values");
657 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
Mårten Kongstad21717e72023-09-04 13:28:36 +0200658 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
Zhi Dou71f1b352023-08-21 22:49:46 +0000659 assert!(second.is_fixed_read_only());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200660
661 // valid input: empty
662 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
663 assert!(parsed_flags.parsed_flag.is_empty());
664
665 // bad input: empty trace
666 let text_proto = r#"
667parsed_flag {
668 package: "com.first"
669 name: "first"
670 namespace: "first_ns"
671 description: "This is the description of the first flag."
672 state: DISABLED
673 permission: READ_ONLY
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000674 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200675}
676"#;
677 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
678 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
679
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200680 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200681 let text_proto = r#"
682parsed_flag {
683 package: "com.first"
684 name: "first"
685 description: "This is the description of the first flag."
686 state: DISABLED
687 permission: READ_ONLY
688 trace {
689 source: "flags.declarations"
690 state: DISABLED
691 permission: READ_ONLY
692 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000693 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200694}
695"#;
696 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200697 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200698
699 // bad input: parsed_flag not sorted by package
700 let text_proto = r#"
701parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200702 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200703 name: "first"
704 namespace: "first_ns"
705 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200706 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200707 state: DISABLED
708 permission: READ_ONLY
709 trace {
710 source: "flags.declarations"
711 state: DISABLED
712 permission: READ_ONLY
713 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000714 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200715}
716parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200717 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200718 name: "second"
719 namespace: "second_ns"
720 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200721 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200722 state: ENABLED
723 permission: READ_WRITE
724 trace {
725 source: "flags.declarations"
726 state: DISABLED
727 permission: READ_ONLY
728 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000729 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200730}
731"#;
732 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
733 assert_eq!(
734 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200735 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200736 );
737
738 // bad input: parsed_flag not sorted by name
739 let text_proto = r#"
740parsed_flag {
741 package: "com.foo"
742 name: "bbb"
743 namespace: "first_ns"
744 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200745 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200746 state: DISABLED
747 permission: READ_ONLY
748 trace {
749 source: "flags.declarations"
750 state: DISABLED
751 permission: READ_ONLY
752 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000753 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200754}
755parsed_flag {
756 package: "com.foo"
757 name: "aaa"
758 namespace: "second_ns"
759 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200760 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200761 state: ENABLED
762 permission: READ_WRITE
763 trace {
764 source: "flags.declarations"
765 state: DISABLED
766 permission: READ_ONLY
767 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000768 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200769}
770"#;
771 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
772 assert_eq!(
773 format!("{:?}", error),
774 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
775 );
776
777 // bad input: duplicate flags
778 let text_proto = r#"
779parsed_flag {
780 package: "com.foo"
781 name: "bar"
782 namespace: "first_ns"
783 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200784 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200785 state: DISABLED
786 permission: READ_ONLY
787 trace {
788 source: "flags.declarations"
789 state: DISABLED
790 permission: READ_ONLY
791 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000792 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200793}
794parsed_flag {
795 package: "com.foo"
796 name: "bar"
797 namespace: "second_ns"
798 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200799 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200800 state: ENABLED
801 permission: READ_WRITE
802 trace {
803 source: "flags.declarations"
804 state: DISABLED
805 permission: READ_ONLY
806 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000807 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200808}
809"#;
810 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200811 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
812 }
813
814 #[test]
815 fn test_parsed_flag_path_to_declaration() {
816 let text_proto = r#"
817parsed_flag {
818 package: "com.foo"
819 name: "bar"
820 namespace: "first_ns"
821 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200822 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200823 state: DISABLED
824 permission: READ_ONLY
825 trace {
826 source: "flags.declarations"
827 state: DISABLED
828 permission: READ_ONLY
829 }
830 trace {
831 source: "flags.values"
832 state: ENABLED
833 permission: READ_ONLY
834 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000835 container: "system"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200836}
837"#;
838 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
839 let parsed_flag = &parsed_flags.parsed_flag[0];
840 assert_eq!(
841 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
842 "flags.declarations"
843 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200844 }
845
846 #[test]
847 fn test_parsed_flags_merge() {
848 let text_proto = r#"
849parsed_flag {
850 package: "com.first"
851 name: "first"
852 namespace: "first_ns"
853 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200854 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200855 state: DISABLED
856 permission: READ_ONLY
857 trace {
858 source: "flags.declarations"
859 state: DISABLED
860 permission: READ_ONLY
861 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000862 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200863}
864parsed_flag {
865 package: "com.second"
866 name: "second"
867 namespace: "second_ns"
868 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200869 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200870 state: ENABLED
871 permission: READ_WRITE
872 trace {
873 source: "flags.declarations"
874 state: DISABLED
875 permission: READ_ONLY
876 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000877 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200878}
879"#;
880 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
881
882 let text_proto = r#"
883parsed_flag {
884 package: "com.first"
885 name: "first"
886 namespace: "first_ns"
887 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200888 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200889 state: DISABLED
890 permission: READ_ONLY
891 trace {
892 source: "flags.declarations"
893 state: DISABLED
894 permission: READ_ONLY
895 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000896 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200897}
898"#;
899 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
900
901 let text_proto = r#"
902parsed_flag {
903 package: "com.second"
904 name: "second"
905 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200906 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200907 description: "This is the description of the second flag."
908 state: ENABLED
909 permission: READ_WRITE
910 trace {
911 source: "flags.declarations"
912 state: DISABLED
913 permission: READ_ONLY
914 }
Oriol Prieto Gasco7afc7e72023-11-22 13:26:02 +0000915 container: "system"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200916}
917"#;
918 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
919
Colin Cross6befb342023-11-28 15:55:07 -0800920 let text_proto = r#"
921parsed_flag {
922 package: "com.second"
923 name: "second"
924 namespace: "second_ns"
925 bug: "b"
926 description: "This is the description of the second flag."
927 state: ENABLED
928 permission: READ_WRITE
929 trace {
930 source: "duplicate/flags.declarations"
931 state: DISABLED
932 permission: READ_ONLY
933 }
934}
935"#;
936 let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
937
Mårten Kongstad403658f2023-06-14 09:51:56 +0200938 // bad cases
Colin Cross6befb342023-11-28 15:55:07 -0800939
940 // two of the same flag with dedup disabled
941 let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200942 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 +0200943
Colin Cross6befb342023-11-28 15:55:07 -0800944 // two conflicting flags with dedup disabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500945 let error =
946 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800947 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
948
949 // two conflicting flags with dedup enabled
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500950 let error =
951 parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
Colin Cross6befb342023-11-28 15:55:07 -0800952 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
953
Mårten Kongstad403658f2023-06-14 09:51:56 +0200954 // valid cases
Colin Cross6befb342023-11-28 15:55:07 -0800955 assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
956 assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
957 assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
958 assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
Micha Schwab1a1a08a2023-11-28 16:14:30 -0500959 assert_eq!(
960 expected,
961 parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()
962 );
963 assert_eq!(
964 expected,
965 parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()
966 );
967 assert_eq!(
968 expected,
969 parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()
970 );
971 assert_eq!(
972 expected,
973 parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()
974 );
Colin Cross6befb342023-11-28 15:55:07 -0800975
976 // two identical flags with dedup enabled
977 assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
Mårten Kongstad403658f2023-06-14 09:51:56 +0200978 }
979}