blob: c3911e52acde9a820a29be9c842e1cb95902f6fb [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"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200306}
307"#,
308 )
309 .unwrap();
310 assert_eq!(flag_declarations.package(), "com.foo.bar");
311 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
312 assert_eq!(first.name(), "first");
313 assert_eq!(first.namespace(), "first_ns");
314 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200315 assert_eq!(first.bug, vec!["123"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200316 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
317 assert_eq!(second.name(), "second");
318 assert_eq!(second.namespace(), "second_ns");
319 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200320 assert_eq!(second.bug, vec!["abc"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200321
322 // bad input: missing package in flag declarations
323 let error = flag_declarations::try_from_text_proto(
324 r#"
325flag {
326 name: "first"
327 namespace: "first_ns"
328 description: "This is the description of the first flag."
329}
330flag {
331 name: "second"
332 namespace: "second_ns"
333 description: "This is the description of the second flag."
334}
335"#,
336 )
337 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200338 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200339
340 // bad input: missing namespace in flag declaration
341 let error = flag_declarations::try_from_text_proto(
342 r#"
343package: "com.foo.bar"
344flag {
345 name: "first"
346 description: "This is the description of the first flag."
347}
348flag {
349 name: "second"
350 namespace: "second_ns"
351 description: "This is the description of the second flag."
352}
353"#,
354 )
355 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200356 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200357
358 // bad input: bad package name in flag declarations
359 let error = flag_declarations::try_from_text_proto(
360 r#"
361package: "_com.FOO__BAR"
362flag {
363 name: "first"
364 namespace: "first_ns"
365 description: "This is the description of the first flag."
366}
367flag {
368 name: "second"
369 namespace: "second_ns"
370 description: "This is the description of the second flag."
371}
372"#,
373 )
374 .unwrap_err();
375 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
376
377 // bad input: bad name in flag declaration
378 let error = flag_declarations::try_from_text_proto(
379 r#"
380package: "com.foo.bar"
381flag {
382 name: "FIRST"
383 namespace: "first_ns"
384 description: "This is the description of the first flag."
385}
386flag {
387 name: "second"
388 namespace: "second_ns"
389 description: "This is the description of the second flag."
390}
391"#,
392 )
393 .unwrap_err();
394 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200395
396 // bad input: no bug entries in flag declaration
397 let error = flag_declarations::try_from_text_proto(
398 r#"
399package: "com.foo.bar"
400flag {
401 name: "first"
402 namespace: "first_ns"
403 description: "This is the description of the first flag."
404}
405"#,
406 )
407 .unwrap_err();
408 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
409
410 // bad input: multiple bug entries in flag declaration
411 let error = flag_declarations::try_from_text_proto(
412 r#"
413package: "com.foo.bar"
414flag {
415 name: "first"
416 namespace: "first_ns"
417 description: "This is the description of the first flag."
418 bug: "123"
419 bug: "abc"
420}
421"#,
422 )
423 .unwrap_err();
424 assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
Mårten Kongstad403658f2023-06-14 09:51:56 +0200425 }
426
427 #[test]
428 fn test_flag_values_try_from_text_proto() {
429 // valid input
430 let flag_values = flag_values::try_from_text_proto(
431 r#"
432flag_value {
433 package: "com.first"
434 name: "first"
435 state: DISABLED
436 permission: READ_ONLY
437}
438flag_value {
439 package: "com.second"
440 name: "second"
441 state: ENABLED
442 permission: READ_WRITE
443}
444"#,
445 )
446 .unwrap();
447 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
448 assert_eq!(first.package(), "com.first");
449 assert_eq!(first.name(), "first");
450 assert_eq!(first.state(), ProtoFlagState::DISABLED);
451 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
452 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
453 assert_eq!(second.package(), "com.second");
454 assert_eq!(second.name(), "second");
455 assert_eq!(second.state(), ProtoFlagState::ENABLED);
456 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
457
458 // bad input: bad package in flag value
459 let error = flag_values::try_from_text_proto(
460 r#"
461flag_value {
462 package: "COM.FIRST"
463 name: "first"
464 state: DISABLED
465 permission: READ_ONLY
466}
467"#,
468 )
469 .unwrap_err();
470 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
471
472 // bad input: bad name in flag value
473 let error = flag_values::try_from_text_proto(
474 r#"
475flag_value {
476 package: "com.first"
477 name: "FIRST"
478 state: DISABLED
479 permission: READ_ONLY
480}
481"#,
482 )
483 .unwrap_err();
484 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
485
486 // bad input: missing state in flag value
487 let error = flag_values::try_from_text_proto(
488 r#"
489flag_value {
490 package: "com.first"
491 name: "first"
492 permission: READ_ONLY
493}
494"#,
495 )
496 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200497 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200498
499 // bad input: missing permission in flag value
500 let error = flag_values::try_from_text_proto(
501 r#"
502flag_value {
503 package: "com.first"
504 name: "first"
505 state: DISABLED
506}
507"#,
508 )
509 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200510 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200511 }
512
513 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
514 use protobuf::Message;
515
516 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
517 let mut binary_proto = Vec::new();
518 parsed_flags.write_to_vec(&mut binary_proto)?;
519 parsed_flags::try_from_binary_proto(&binary_proto)
520 }
521
522 #[test]
523 fn test_parsed_flags_try_from_text_proto() {
524 // valid input
525 let text_proto = r#"
526parsed_flag {
527 package: "com.first"
528 name: "first"
529 namespace: "first_ns"
530 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200531 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200532 state: DISABLED
533 permission: READ_ONLY
534 trace {
535 source: "flags.declarations"
536 state: DISABLED
537 permission: READ_ONLY
538 }
539}
540parsed_flag {
541 package: "com.second"
542 name: "second"
543 namespace: "second_ns"
544 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200545 bug: "SOME_BUG"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200546 state: ENABLED
547 permission: READ_WRITE
548 trace {
549 source: "flags.declarations"
550 state: DISABLED
551 permission: READ_ONLY
552 }
553 trace {
554 source: "flags.values"
555 state: ENABLED
556 permission: READ_WRITE
557 }
558}
559"#;
560 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
561 assert_eq!(parsed_flags.parsed_flag.len(), 2);
562 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
563 assert_eq!(second.package(), "com.second");
564 assert_eq!(second.name(), "second");
565 assert_eq!(second.namespace(), "second_ns");
566 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200567 assert_eq!(second.bug, vec!["SOME_BUG"]);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200568 assert_eq!(second.state(), ProtoFlagState::ENABLED);
569 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
570 assert_eq!(2, second.trace.len());
571 assert_eq!(second.trace[0].source(), "flags.declarations");
572 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
573 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
574 assert_eq!(second.trace[1].source(), "flags.values");
575 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
576 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
577
578 // valid input: empty
579 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
580 assert!(parsed_flags.parsed_flag.is_empty());
581
582 // bad input: empty trace
583 let text_proto = r#"
584parsed_flag {
585 package: "com.first"
586 name: "first"
587 namespace: "first_ns"
588 description: "This is the description of the first flag."
589 state: DISABLED
590 permission: READ_ONLY
591}
592"#;
593 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
594 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
595
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200596 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200597 let text_proto = r#"
598parsed_flag {
599 package: "com.first"
600 name: "first"
601 description: "This is the description of the first flag."
602 state: DISABLED
603 permission: READ_ONLY
604 trace {
605 source: "flags.declarations"
606 state: DISABLED
607 permission: READ_ONLY
608 }
609}
610"#;
611 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200612 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200613
614 // bad input: parsed_flag not sorted by package
615 let text_proto = r#"
616parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200617 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200618 name: "first"
619 namespace: "first_ns"
620 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200621 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200622 state: DISABLED
623 permission: READ_ONLY
624 trace {
625 source: "flags.declarations"
626 state: DISABLED
627 permission: READ_ONLY
628 }
629}
630parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200631 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200632 name: "second"
633 namespace: "second_ns"
634 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200635 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200636 state: ENABLED
637 permission: READ_WRITE
638 trace {
639 source: "flags.declarations"
640 state: DISABLED
641 permission: READ_ONLY
642 }
643}
644"#;
645 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
646 assert_eq!(
647 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200648 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200649 );
650
651 // bad input: parsed_flag not sorted by name
652 let text_proto = r#"
653parsed_flag {
654 package: "com.foo"
655 name: "bbb"
656 namespace: "first_ns"
657 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200658 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200659 state: DISABLED
660 permission: READ_ONLY
661 trace {
662 source: "flags.declarations"
663 state: DISABLED
664 permission: READ_ONLY
665 }
666}
667parsed_flag {
668 package: "com.foo"
669 name: "aaa"
670 namespace: "second_ns"
671 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200672 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200673 state: ENABLED
674 permission: READ_WRITE
675 trace {
676 source: "flags.declarations"
677 state: DISABLED
678 permission: READ_ONLY
679 }
680}
681"#;
682 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
683 assert_eq!(
684 format!("{:?}", error),
685 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
686 );
687
688 // bad input: duplicate flags
689 let text_proto = r#"
690parsed_flag {
691 package: "com.foo"
692 name: "bar"
693 namespace: "first_ns"
694 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200695 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200696 state: DISABLED
697 permission: READ_ONLY
698 trace {
699 source: "flags.declarations"
700 state: DISABLED
701 permission: READ_ONLY
702 }
703}
704parsed_flag {
705 package: "com.foo"
706 name: "bar"
707 namespace: "second_ns"
708 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200709 bug: ""
Mårten Kongstad403658f2023-06-14 09:51:56 +0200710 state: ENABLED
711 permission: READ_WRITE
712 trace {
713 source: "flags.declarations"
714 state: DISABLED
715 permission: READ_ONLY
716 }
717}
718"#;
719 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200720 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
721 }
722
723 #[test]
724 fn test_parsed_flag_path_to_declaration() {
725 let text_proto = r#"
726parsed_flag {
727 package: "com.foo"
728 name: "bar"
729 namespace: "first_ns"
730 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200731 bug: "b/12345678"
Mårten Kongstad206a3822023-07-07 08:52:52 +0200732 state: DISABLED
733 permission: READ_ONLY
734 trace {
735 source: "flags.declarations"
736 state: DISABLED
737 permission: READ_ONLY
738 }
739 trace {
740 source: "flags.values"
741 state: ENABLED
742 permission: READ_ONLY
743 }
744}
745"#;
746 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
747 let parsed_flag = &parsed_flags.parsed_flag[0];
748 assert_eq!(
749 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
750 "flags.declarations"
751 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200752 }
753
754 #[test]
755 fn test_parsed_flags_merge() {
756 let text_proto = r#"
757parsed_flag {
758 package: "com.first"
759 name: "first"
760 namespace: "first_ns"
761 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200762 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200763 state: DISABLED
764 permission: READ_ONLY
765 trace {
766 source: "flags.declarations"
767 state: DISABLED
768 permission: READ_ONLY
769 }
770}
771parsed_flag {
772 package: "com.second"
773 name: "second"
774 namespace: "second_ns"
775 description: "This is the description of the second flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200776 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200777 state: ENABLED
778 permission: READ_WRITE
779 trace {
780 source: "flags.declarations"
781 state: DISABLED
782 permission: READ_ONLY
783 }
784}
785"#;
786 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
787
788 let text_proto = r#"
789parsed_flag {
790 package: "com.first"
791 name: "first"
792 namespace: "first_ns"
793 description: "This is the description of the first flag."
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200794 bug: "a"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200795 state: DISABLED
796 permission: READ_ONLY
797 trace {
798 source: "flags.declarations"
799 state: DISABLED
800 permission: READ_ONLY
801 }
802}
803"#;
804 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
805
806 let text_proto = r#"
807parsed_flag {
808 package: "com.second"
809 name: "second"
810 namespace: "second_ns"
Mårten Kongstad6353c6c2023-07-26 13:18:50 +0200811 bug: "b"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200812 description: "This is the description of the second flag."
813 state: ENABLED
814 permission: READ_WRITE
815 trace {
816 source: "flags.declarations"
817 state: DISABLED
818 permission: READ_ONLY
819 }
820}
821"#;
822 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
823
824 // bad cases
825 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200826 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 +0200827
828 // valid cases
829 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
830 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
831 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
832 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
833 }
834}