blob: 4d824f25f65fbece2ed8a112c8efa37df598a388 [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 Kongstada2e5ab82023-06-19 16:28:54 +020095
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020096 // ProtoFlagDeclaration.bug: Vec<String>: may be empty, no checks needed
97
Mårten Kongstad403658f2023-06-14 09:51:56 +020098 Ok(())
99 }
100}
101
102pub mod flag_declarations {
103 use super::*;
104 use crate::codegen;
105 use anyhow::ensure;
106
107 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
108 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
109 verify_fields(&pdf)?;
110 Ok(pdf)
111 }
112
113 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200114 ensure_required_fields!("flag declarations", pdf, "package");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200115
Mårten Kongstad403658f2023-06-14 09:51:56 +0200116 ensure!(
117 codegen::is_valid_package_ident(pdf.package()),
118 "bad flag declarations: bad package"
119 );
120 for flag_declaration in pdf.flag.iter() {
121 super::flag_declaration::verify_fields(flag_declaration)?;
122 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200123
Mårten Kongstad403658f2023-06-14 09:51:56 +0200124 Ok(())
125 }
126}
127
128pub mod flag_value {
129 use super::*;
130 use crate::codegen;
131 use anyhow::ensure;
132
133 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200134 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200135
Mårten Kongstad403658f2023-06-14 09:51:56 +0200136 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
137 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200138
Mårten Kongstad403658f2023-06-14 09:51:56 +0200139 Ok(())
140 }
141}
142
143pub mod flag_values {
144 use super::*;
145
146 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
147 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
148 verify_fields(&pfv)?;
149 Ok(pfv)
150 }
151
152 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
153 for flag_value in pfv.flag_value.iter() {
154 super::flag_value::verify_fields(flag_value)?;
155 }
156 Ok(())
157 }
158}
159
160pub mod tracepoint {
161 use super::*;
162 use anyhow::ensure;
163
164 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200165 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200166
Mårten Kongstad403658f2023-06-14 09:51:56 +0200167 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200168
Mårten Kongstad403658f2023-06-14 09:51:56 +0200169 Ok(())
170 }
171}
172
173pub mod parsed_flag {
174 use super::*;
175 use crate::codegen;
176 use anyhow::ensure;
177
178 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200179 ensure_required_fields!(
180 "parsed flag",
181 pf,
182 "package",
183 "name",
184 "namespace",
185 "description",
186 "state",
187 "permission"
188 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200189
Mårten Kongstad403658f2023-06-14 09:51:56 +0200190 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
191 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
192 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
193 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
194 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
195 for tp in pf.trace.iter() {
196 super::tracepoint::verify_fields(tp)?;
197 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200198
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200199 // ProtoParsedFlag.bug: Vec<String>: may be empty, no checks needed
200
Mårten Kongstad403658f2023-06-14 09:51:56 +0200201 Ok(())
202 }
203}
204
205pub mod parsed_flags {
206 use super::*;
207 use anyhow::bail;
208 use std::cmp::Ordering;
209
210 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
211 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
212 verify_fields(&message)?;
213 Ok(message)
214 }
215
216 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
217 let mut previous: Option<&ProtoParsedFlag> = None;
218 for parsed_flag in pf.parsed_flag.iter() {
219 if let Some(prev) = previous {
220 let a = create_sorting_key(prev);
221 let b = create_sorting_key(parsed_flag);
222 match a.cmp(&b) {
223 Ordering::Less => {}
224 Ordering::Equal => bail!("bad parsed flags: duplicate flag {}", a),
225 Ordering::Greater => {
226 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
227 }
228 }
229 }
230 super::parsed_flag::verify_fields(parsed_flag)?;
231 previous = Some(parsed_flag);
232 }
233 Ok(())
234 }
235
236 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
237 let mut merged = ProtoParsedFlags::new();
238 for mut pfs in parsed_flags.into_iter() {
239 merged.parsed_flag.append(&mut pfs.parsed_flag);
240 }
241 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
242 verify_fields(&merged)?;
243 Ok(merged)
244 }
245
246 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
247 format!("{}.{}", pf.package(), pf.name())
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_flag_declarations_try_from_text_proto() {
257 // valid input
258 let flag_declarations = flag_declarations::try_from_text_proto(
259 r#"
260package: "com.foo.bar"
261flag {
262 name: "first"
263 namespace: "first_ns"
264 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200265 bug: "123"
266 bug: "abc"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200267}
268flag {
269 name: "second"
270 namespace: "second_ns"
271 description: "This is the description of the second flag."
272}
273"#,
274 )
275 .unwrap();
276 assert_eq!(flag_declarations.package(), "com.foo.bar");
277 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
278 assert_eq!(first.name(), "first");
279 assert_eq!(first.namespace(), "first_ns");
280 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200281 assert_eq!(first.bug.len(), 2);
282 assert_eq!(first.bug[0], "123");
283 assert_eq!(first.bug[1], "abc");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200284 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
285 assert_eq!(second.name(), "second");
286 assert_eq!(second.namespace(), "second_ns");
287 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200288 assert_eq!(second.bug.len(), 0);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200289
290 // bad input: missing package in flag declarations
291 let error = flag_declarations::try_from_text_proto(
292 r#"
293flag {
294 name: "first"
295 namespace: "first_ns"
296 description: "This is the description of the first flag."
297}
298flag {
299 name: "second"
300 namespace: "second_ns"
301 description: "This is the description of the second flag."
302}
303"#,
304 )
305 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200306 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200307
308 // bad input: missing namespace in flag declaration
309 let error = flag_declarations::try_from_text_proto(
310 r#"
311package: "com.foo.bar"
312flag {
313 name: "first"
314 description: "This is the description of the first flag."
315}
316flag {
317 name: "second"
318 namespace: "second_ns"
319 description: "This is the description of the second flag."
320}
321"#,
322 )
323 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200324 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200325
326 // bad input: bad package name in flag declarations
327 let error = flag_declarations::try_from_text_proto(
328 r#"
329package: "_com.FOO__BAR"
330flag {
331 name: "first"
332 namespace: "first_ns"
333 description: "This is the description of the first flag."
334}
335flag {
336 name: "second"
337 namespace: "second_ns"
338 description: "This is the description of the second flag."
339}
340"#,
341 )
342 .unwrap_err();
343 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
344
345 // bad input: bad name in flag declaration
346 let error = flag_declarations::try_from_text_proto(
347 r#"
348package: "com.foo.bar"
349flag {
350 name: "FIRST"
351 namespace: "first_ns"
352 description: "This is the description of the first flag."
353}
354flag {
355 name: "second"
356 namespace: "second_ns"
357 description: "This is the description of the second flag."
358}
359"#,
360 )
361 .unwrap_err();
362 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
363 }
364
365 #[test]
366 fn test_flag_values_try_from_text_proto() {
367 // valid input
368 let flag_values = flag_values::try_from_text_proto(
369 r#"
370flag_value {
371 package: "com.first"
372 name: "first"
373 state: DISABLED
374 permission: READ_ONLY
375}
376flag_value {
377 package: "com.second"
378 name: "second"
379 state: ENABLED
380 permission: READ_WRITE
381}
382"#,
383 )
384 .unwrap();
385 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
386 assert_eq!(first.package(), "com.first");
387 assert_eq!(first.name(), "first");
388 assert_eq!(first.state(), ProtoFlagState::DISABLED);
389 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
390 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
391 assert_eq!(second.package(), "com.second");
392 assert_eq!(second.name(), "second");
393 assert_eq!(second.state(), ProtoFlagState::ENABLED);
394 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
395
396 // bad input: bad package in flag value
397 let error = flag_values::try_from_text_proto(
398 r#"
399flag_value {
400 package: "COM.FIRST"
401 name: "first"
402 state: DISABLED
403 permission: READ_ONLY
404}
405"#,
406 )
407 .unwrap_err();
408 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
409
410 // bad input: bad name in flag value
411 let error = flag_values::try_from_text_proto(
412 r#"
413flag_value {
414 package: "com.first"
415 name: "FIRST"
416 state: DISABLED
417 permission: READ_ONLY
418}
419"#,
420 )
421 .unwrap_err();
422 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
423
424 // bad input: missing state in flag value
425 let error = flag_values::try_from_text_proto(
426 r#"
427flag_value {
428 package: "com.first"
429 name: "first"
430 permission: READ_ONLY
431}
432"#,
433 )
434 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200435 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200436
437 // bad input: missing permission in flag value
438 let error = flag_values::try_from_text_proto(
439 r#"
440flag_value {
441 package: "com.first"
442 name: "first"
443 state: DISABLED
444}
445"#,
446 )
447 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200448 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200449 }
450
451 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
452 use protobuf::Message;
453
454 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
455 let mut binary_proto = Vec::new();
456 parsed_flags.write_to_vec(&mut binary_proto)?;
457 parsed_flags::try_from_binary_proto(&binary_proto)
458 }
459
460 #[test]
461 fn test_parsed_flags_try_from_text_proto() {
462 // valid input
463 let text_proto = r#"
464parsed_flag {
465 package: "com.first"
466 name: "first"
467 namespace: "first_ns"
468 description: "This is the description of the first flag."
469 state: DISABLED
470 permission: READ_ONLY
471 trace {
472 source: "flags.declarations"
473 state: DISABLED
474 permission: READ_ONLY
475 }
476}
477parsed_flag {
478 package: "com.second"
479 name: "second"
480 namespace: "second_ns"
481 description: "This is the description of the second flag."
482 state: ENABLED
483 permission: READ_WRITE
484 trace {
485 source: "flags.declarations"
486 state: DISABLED
487 permission: READ_ONLY
488 }
489 trace {
490 source: "flags.values"
491 state: ENABLED
492 permission: READ_WRITE
493 }
494}
495"#;
496 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
497 assert_eq!(parsed_flags.parsed_flag.len(), 2);
498 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
499 assert_eq!(second.package(), "com.second");
500 assert_eq!(second.name(), "second");
501 assert_eq!(second.namespace(), "second_ns");
502 assert_eq!(second.description(), "This is the description of the second flag.");
503 assert_eq!(second.state(), ProtoFlagState::ENABLED);
504 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
505 assert_eq!(2, second.trace.len());
506 assert_eq!(second.trace[0].source(), "flags.declarations");
507 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
508 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
509 assert_eq!(second.trace[1].source(), "flags.values");
510 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
511 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
512
513 // valid input: empty
514 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
515 assert!(parsed_flags.parsed_flag.is_empty());
516
517 // bad input: empty trace
518 let text_proto = r#"
519parsed_flag {
520 package: "com.first"
521 name: "first"
522 namespace: "first_ns"
523 description: "This is the description of the first flag."
524 state: DISABLED
525 permission: READ_ONLY
526}
527"#;
528 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
529 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
530
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200531 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200532 let text_proto = r#"
533parsed_flag {
534 package: "com.first"
535 name: "first"
536 description: "This is the description of the first flag."
537 state: DISABLED
538 permission: READ_ONLY
539 trace {
540 source: "flags.declarations"
541 state: DISABLED
542 permission: READ_ONLY
543 }
544}
545"#;
546 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200547 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200548
549 // bad input: parsed_flag not sorted by package
550 let text_proto = r#"
551parsed_flag {
552 package: "bbb"
553 name: "first"
554 namespace: "first_ns"
555 description: "This is the description of the first flag."
556 state: DISABLED
557 permission: READ_ONLY
558 trace {
559 source: "flags.declarations"
560 state: DISABLED
561 permission: READ_ONLY
562 }
563}
564parsed_flag {
565 package: "aaa"
566 name: "second"
567 namespace: "second_ns"
568 description: "This is the description of the second flag."
569 state: ENABLED
570 permission: READ_WRITE
571 trace {
572 source: "flags.declarations"
573 state: DISABLED
574 permission: READ_ONLY
575 }
576}
577"#;
578 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
579 assert_eq!(
580 format!("{:?}", error),
581 "bad parsed flags: not sorted: bbb.first comes before aaa.second"
582 );
583
584 // bad input: parsed_flag not sorted by name
585 let text_proto = r#"
586parsed_flag {
587 package: "com.foo"
588 name: "bbb"
589 namespace: "first_ns"
590 description: "This is the description of the first flag."
591 state: DISABLED
592 permission: READ_ONLY
593 trace {
594 source: "flags.declarations"
595 state: DISABLED
596 permission: READ_ONLY
597 }
598}
599parsed_flag {
600 package: "com.foo"
601 name: "aaa"
602 namespace: "second_ns"
603 description: "This is the description of the second flag."
604 state: ENABLED
605 permission: READ_WRITE
606 trace {
607 source: "flags.declarations"
608 state: DISABLED
609 permission: READ_ONLY
610 }
611}
612"#;
613 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
614 assert_eq!(
615 format!("{:?}", error),
616 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
617 );
618
619 // bad input: duplicate flags
620 let text_proto = r#"
621parsed_flag {
622 package: "com.foo"
623 name: "bar"
624 namespace: "first_ns"
625 description: "This is the description of the first flag."
626 state: DISABLED
627 permission: READ_ONLY
628 trace {
629 source: "flags.declarations"
630 state: DISABLED
631 permission: READ_ONLY
632 }
633}
634parsed_flag {
635 package: "com.foo"
636 name: "bar"
637 namespace: "second_ns"
638 description: "This is the description of the second flag."
639 state: ENABLED
640 permission: READ_WRITE
641 trace {
642 source: "flags.declarations"
643 state: DISABLED
644 permission: READ_ONLY
645 }
646}
647"#;
648 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
649 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar");
650 }
651
652 #[test]
653 fn test_parsed_flags_merge() {
654 let text_proto = r#"
655parsed_flag {
656 package: "com.first"
657 name: "first"
658 namespace: "first_ns"
659 description: "This is the description of the first flag."
660 state: DISABLED
661 permission: READ_ONLY
662 trace {
663 source: "flags.declarations"
664 state: DISABLED
665 permission: READ_ONLY
666 }
667}
668parsed_flag {
669 package: "com.second"
670 name: "second"
671 namespace: "second_ns"
672 description: "This is the description of the second flag."
673 state: ENABLED
674 permission: READ_WRITE
675 trace {
676 source: "flags.declarations"
677 state: DISABLED
678 permission: READ_ONLY
679 }
680}
681"#;
682 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
683
684 let text_proto = r#"
685parsed_flag {
686 package: "com.first"
687 name: "first"
688 namespace: "first_ns"
689 description: "This is the description of the first flag."
690 state: DISABLED
691 permission: READ_ONLY
692 trace {
693 source: "flags.declarations"
694 state: DISABLED
695 permission: READ_ONLY
696 }
697}
698"#;
699 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
700
701 let text_proto = r#"
702parsed_flag {
703 package: "com.second"
704 name: "second"
705 namespace: "second_ns"
706 description: "This is the description of the second flag."
707 state: ENABLED
708 permission: READ_WRITE
709 trace {
710 source: "flags.declarations"
711 state: DISABLED
712 permission: READ_ONLY
713 }
714}
715"#;
716 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
717
718 // bad cases
719 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
720 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first");
721
722 // valid cases
723 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
724 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
725 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
726 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
727 }
728}