blob: beebd9341be8dfeec0bdf0da99c6f79f9773102e [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;
65
Mårten Kongstad403658f2023-06-14 09:51:56 +020066fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020067where
68 T: protobuf::MessageFull,
69{
70 // warning: parse_from_str does not check if required fields are set
71 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
72}
Mårten Kongstad403658f2023-06-14 09:51:56 +020073
74pub mod flag_declaration {
75 use super::*;
76 use crate::codegen;
77 use anyhow::ensure;
78
79 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020080 ensure!(pdf.has_name(), "bad flag declaration: missing name");
81 ensure!(pdf.has_namespace(), "bad flag declaration: missing namespace");
82 ensure!(pdf.has_description(), "bad flag declaration: missing description");
83
Mårten Kongstad403658f2023-06-14 09:51:56 +020084 ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
85 ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
86 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020087
Mårten Kongstad403658f2023-06-14 09:51:56 +020088 Ok(())
89 }
90}
91
92pub mod flag_declarations {
93 use super::*;
94 use crate::codegen;
95 use anyhow::ensure;
96
97 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
98 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
99 verify_fields(&pdf)?;
100 Ok(pdf)
101 }
102
103 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200104 ensure!(pdf.has_package(), "bad flag declarations: missing package");
105
Mårten Kongstad403658f2023-06-14 09:51:56 +0200106 ensure!(
107 codegen::is_valid_package_ident(pdf.package()),
108 "bad flag declarations: bad package"
109 );
110 for flag_declaration in pdf.flag.iter() {
111 super::flag_declaration::verify_fields(flag_declaration)?;
112 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200113
Mårten Kongstad403658f2023-06-14 09:51:56 +0200114 Ok(())
115 }
116}
117
118pub mod flag_value {
119 use super::*;
120 use crate::codegen;
121 use anyhow::ensure;
122
123 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200124 ensure!(fv.has_package(), "bad flag value: missing package");
125 ensure!(fv.has_name(), "bad flag value: missing name");
126 ensure!(fv.has_state(), "bad flag value: missing state");
127 ensure!(fv.has_permission(), "bad flag value: missing permission");
128
Mårten Kongstad403658f2023-06-14 09:51:56 +0200129 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
130 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
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_values {
137 use super::*;
138
139 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
140 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
141 verify_fields(&pfv)?;
142 Ok(pfv)
143 }
144
145 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
146 for flag_value in pfv.flag_value.iter() {
147 super::flag_value::verify_fields(flag_value)?;
148 }
149 Ok(())
150 }
151}
152
153pub mod tracepoint {
154 use super::*;
155 use anyhow::ensure;
156
157 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200158 ensure!(tp.has_source(), "bad tracepoint: missing source");
159 ensure!(tp.has_state(), "bad tracepoint: missing state");
160 ensure!(tp.has_permission(), "bad tracepoint: missing permission");
161
Mårten Kongstad403658f2023-06-14 09:51:56 +0200162 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200163
Mårten Kongstad403658f2023-06-14 09:51:56 +0200164 Ok(())
165 }
166}
167
168pub mod parsed_flag {
169 use super::*;
170 use crate::codegen;
171 use anyhow::ensure;
172
173 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200174 ensure!(pf.has_package(), "bad parsed flag: missing package");
175 ensure!(pf.has_name(), "bad parsed flag: missing name");
176 ensure!(pf.has_namespace(), "bad parsed flag: missing namespace");
177 ensure!(pf.has_description(), "bad parsed flag: missing description");
178 ensure!(pf.has_state(), "bad parsed flag: missing state");
179 ensure!(pf.has_permission(), "bad parsed flag: missing permission");
180
Mårten Kongstad403658f2023-06-14 09:51:56 +0200181 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
182 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
183 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
184 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
185 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
186 for tp in pf.trace.iter() {
187 super::tracepoint::verify_fields(tp)?;
188 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200189
Mårten Kongstad403658f2023-06-14 09:51:56 +0200190 Ok(())
191 }
192}
193
194pub mod parsed_flags {
195 use super::*;
196 use anyhow::bail;
197 use std::cmp::Ordering;
198
199 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
200 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
201 verify_fields(&message)?;
202 Ok(message)
203 }
204
205 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
206 let mut previous: Option<&ProtoParsedFlag> = None;
207 for parsed_flag in pf.parsed_flag.iter() {
208 if let Some(prev) = previous {
209 let a = create_sorting_key(prev);
210 let b = create_sorting_key(parsed_flag);
211 match a.cmp(&b) {
212 Ordering::Less => {}
213 Ordering::Equal => bail!("bad parsed flags: duplicate flag {}", a),
214 Ordering::Greater => {
215 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
216 }
217 }
218 }
219 super::parsed_flag::verify_fields(parsed_flag)?;
220 previous = Some(parsed_flag);
221 }
222 Ok(())
223 }
224
225 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
226 let mut merged = ProtoParsedFlags::new();
227 for mut pfs in parsed_flags.into_iter() {
228 merged.parsed_flag.append(&mut pfs.parsed_flag);
229 }
230 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
231 verify_fields(&merged)?;
232 Ok(merged)
233 }
234
235 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
236 format!("{}.{}", pf.package(), pf.name())
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_flag_declarations_try_from_text_proto() {
246 // valid input
247 let flag_declarations = flag_declarations::try_from_text_proto(
248 r#"
249package: "com.foo.bar"
250flag {
251 name: "first"
252 namespace: "first_ns"
253 description: "This is the description of the first flag."
254}
255flag {
256 name: "second"
257 namespace: "second_ns"
258 description: "This is the description of the second flag."
259}
260"#,
261 )
262 .unwrap();
263 assert_eq!(flag_declarations.package(), "com.foo.bar");
264 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
265 assert_eq!(first.name(), "first");
266 assert_eq!(first.namespace(), "first_ns");
267 assert_eq!(first.description(), "This is the description of the first flag.");
268 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
269 assert_eq!(second.name(), "second");
270 assert_eq!(second.namespace(), "second_ns");
271 assert_eq!(second.description(), "This is the description of the second flag.");
272
273 // bad input: missing package in flag declarations
274 let error = flag_declarations::try_from_text_proto(
275 r#"
276flag {
277 name: "first"
278 namespace: "first_ns"
279 description: "This is the description of the first flag."
280}
281flag {
282 name: "second"
283 namespace: "second_ns"
284 description: "This is the description of the second flag."
285}
286"#,
287 )
288 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200289 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200290
291 // bad input: missing namespace in flag declaration
292 let error = flag_declarations::try_from_text_proto(
293 r#"
294package: "com.foo.bar"
295flag {
296 name: "first"
297 description: "This is the description of the first flag."
298}
299flag {
300 name: "second"
301 namespace: "second_ns"
302 description: "This is the description of the second flag."
303}
304"#,
305 )
306 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200307 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200308
309 // bad input: bad package name in flag declarations
310 let error = flag_declarations::try_from_text_proto(
311 r#"
312package: "_com.FOO__BAR"
313flag {
314 name: "first"
315 namespace: "first_ns"
316 description: "This is the description of the first flag."
317}
318flag {
319 name: "second"
320 namespace: "second_ns"
321 description: "This is the description of the second flag."
322}
323"#,
324 )
325 .unwrap_err();
326 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
327
328 // bad input: bad name in flag declaration
329 let error = flag_declarations::try_from_text_proto(
330 r#"
331package: "com.foo.bar"
332flag {
333 name: "FIRST"
334 namespace: "first_ns"
335 description: "This is the description of the first flag."
336}
337flag {
338 name: "second"
339 namespace: "second_ns"
340 description: "This is the description of the second flag."
341}
342"#,
343 )
344 .unwrap_err();
345 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
346 }
347
348 #[test]
349 fn test_flag_values_try_from_text_proto() {
350 // valid input
351 let flag_values = flag_values::try_from_text_proto(
352 r#"
353flag_value {
354 package: "com.first"
355 name: "first"
356 state: DISABLED
357 permission: READ_ONLY
358}
359flag_value {
360 package: "com.second"
361 name: "second"
362 state: ENABLED
363 permission: READ_WRITE
364}
365"#,
366 )
367 .unwrap();
368 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
369 assert_eq!(first.package(), "com.first");
370 assert_eq!(first.name(), "first");
371 assert_eq!(first.state(), ProtoFlagState::DISABLED);
372 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
373 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
374 assert_eq!(second.package(), "com.second");
375 assert_eq!(second.name(), "second");
376 assert_eq!(second.state(), ProtoFlagState::ENABLED);
377 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
378
379 // bad input: bad package in flag value
380 let error = flag_values::try_from_text_proto(
381 r#"
382flag_value {
383 package: "COM.FIRST"
384 name: "first"
385 state: DISABLED
386 permission: READ_ONLY
387}
388"#,
389 )
390 .unwrap_err();
391 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
392
393 // bad input: bad name in flag value
394 let error = flag_values::try_from_text_proto(
395 r#"
396flag_value {
397 package: "com.first"
398 name: "FIRST"
399 state: DISABLED
400 permission: READ_ONLY
401}
402"#,
403 )
404 .unwrap_err();
405 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
406
407 // bad input: missing state in flag value
408 let error = flag_values::try_from_text_proto(
409 r#"
410flag_value {
411 package: "com.first"
412 name: "first"
413 permission: READ_ONLY
414}
415"#,
416 )
417 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200418 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200419
420 // bad input: missing permission in flag value
421 let error = flag_values::try_from_text_proto(
422 r#"
423flag_value {
424 package: "com.first"
425 name: "first"
426 state: DISABLED
427}
428"#,
429 )
430 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200431 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200432 }
433
434 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
435 use protobuf::Message;
436
437 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
438 let mut binary_proto = Vec::new();
439 parsed_flags.write_to_vec(&mut binary_proto)?;
440 parsed_flags::try_from_binary_proto(&binary_proto)
441 }
442
443 #[test]
444 fn test_parsed_flags_try_from_text_proto() {
445 // valid input
446 let text_proto = r#"
447parsed_flag {
448 package: "com.first"
449 name: "first"
450 namespace: "first_ns"
451 description: "This is the description of the first flag."
452 state: DISABLED
453 permission: READ_ONLY
454 trace {
455 source: "flags.declarations"
456 state: DISABLED
457 permission: READ_ONLY
458 }
459}
460parsed_flag {
461 package: "com.second"
462 name: "second"
463 namespace: "second_ns"
464 description: "This is the description of the second flag."
465 state: ENABLED
466 permission: READ_WRITE
467 trace {
468 source: "flags.declarations"
469 state: DISABLED
470 permission: READ_ONLY
471 }
472 trace {
473 source: "flags.values"
474 state: ENABLED
475 permission: READ_WRITE
476 }
477}
478"#;
479 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
480 assert_eq!(parsed_flags.parsed_flag.len(), 2);
481 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
482 assert_eq!(second.package(), "com.second");
483 assert_eq!(second.name(), "second");
484 assert_eq!(second.namespace(), "second_ns");
485 assert_eq!(second.description(), "This is the description of the second flag.");
486 assert_eq!(second.state(), ProtoFlagState::ENABLED);
487 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
488 assert_eq!(2, second.trace.len());
489 assert_eq!(second.trace[0].source(), "flags.declarations");
490 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
491 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
492 assert_eq!(second.trace[1].source(), "flags.values");
493 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
494 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
495
496 // valid input: empty
497 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
498 assert!(parsed_flags.parsed_flag.is_empty());
499
500 // bad input: empty trace
501 let text_proto = r#"
502parsed_flag {
503 package: "com.first"
504 name: "first"
505 namespace: "first_ns"
506 description: "This is the description of the first flag."
507 state: DISABLED
508 permission: READ_ONLY
509}
510"#;
511 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
512 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
513
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200514 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200515 let text_proto = r#"
516parsed_flag {
517 package: "com.first"
518 name: "first"
519 description: "This is the description of the first flag."
520 state: DISABLED
521 permission: READ_ONLY
522 trace {
523 source: "flags.declarations"
524 state: DISABLED
525 permission: READ_ONLY
526 }
527}
528"#;
529 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200530 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200531
532 // bad input: parsed_flag not sorted by package
533 let text_proto = r#"
534parsed_flag {
535 package: "bbb"
536 name: "first"
537 namespace: "first_ns"
538 description: "This is the description of the first flag."
539 state: DISABLED
540 permission: READ_ONLY
541 trace {
542 source: "flags.declarations"
543 state: DISABLED
544 permission: READ_ONLY
545 }
546}
547parsed_flag {
548 package: "aaa"
549 name: "second"
550 namespace: "second_ns"
551 description: "This is the description of the second flag."
552 state: ENABLED
553 permission: READ_WRITE
554 trace {
555 source: "flags.declarations"
556 state: DISABLED
557 permission: READ_ONLY
558 }
559}
560"#;
561 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
562 assert_eq!(
563 format!("{:?}", error),
564 "bad parsed flags: not sorted: bbb.first comes before aaa.second"
565 );
566
567 // bad input: parsed_flag not sorted by name
568 let text_proto = r#"
569parsed_flag {
570 package: "com.foo"
571 name: "bbb"
572 namespace: "first_ns"
573 description: "This is the description of the first flag."
574 state: DISABLED
575 permission: READ_ONLY
576 trace {
577 source: "flags.declarations"
578 state: DISABLED
579 permission: READ_ONLY
580 }
581}
582parsed_flag {
583 package: "com.foo"
584 name: "aaa"
585 namespace: "second_ns"
586 description: "This is the description of the second flag."
587 state: ENABLED
588 permission: READ_WRITE
589 trace {
590 source: "flags.declarations"
591 state: DISABLED
592 permission: READ_ONLY
593 }
594}
595"#;
596 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
597 assert_eq!(
598 format!("{:?}", error),
599 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
600 );
601
602 // bad input: duplicate flags
603 let text_proto = r#"
604parsed_flag {
605 package: "com.foo"
606 name: "bar"
607 namespace: "first_ns"
608 description: "This is the description of the first flag."
609 state: DISABLED
610 permission: READ_ONLY
611 trace {
612 source: "flags.declarations"
613 state: DISABLED
614 permission: READ_ONLY
615 }
616}
617parsed_flag {
618 package: "com.foo"
619 name: "bar"
620 namespace: "second_ns"
621 description: "This is the description of the second flag."
622 state: ENABLED
623 permission: READ_WRITE
624 trace {
625 source: "flags.declarations"
626 state: DISABLED
627 permission: READ_ONLY
628 }
629}
630"#;
631 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
632 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar");
633 }
634
635 #[test]
636 fn test_parsed_flags_merge() {
637 let text_proto = r#"
638parsed_flag {
639 package: "com.first"
640 name: "first"
641 namespace: "first_ns"
642 description: "This is the description of the first flag."
643 state: DISABLED
644 permission: READ_ONLY
645 trace {
646 source: "flags.declarations"
647 state: DISABLED
648 permission: READ_ONLY
649 }
650}
651parsed_flag {
652 package: "com.second"
653 name: "second"
654 namespace: "second_ns"
655 description: "This is the description of the second flag."
656 state: ENABLED
657 permission: READ_WRITE
658 trace {
659 source: "flags.declarations"
660 state: DISABLED
661 permission: READ_ONLY
662 }
663}
664"#;
665 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
666
667 let text_proto = r#"
668parsed_flag {
669 package: "com.first"
670 name: "first"
671 namespace: "first_ns"
672 description: "This is the description of the first flag."
673 state: DISABLED
674 permission: READ_ONLY
675 trace {
676 source: "flags.declarations"
677 state: DISABLED
678 permission: READ_ONLY
679 }
680}
681"#;
682 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
683
684 let text_proto = r#"
685parsed_flag {
686 package: "com.second"
687 name: "second"
688 namespace: "second_ns"
689 description: "This is the description of the second flag."
690 state: ENABLED
691 permission: READ_WRITE
692 trace {
693 source: "flags.declarations"
694 state: DISABLED
695 permission: READ_ONLY
696 }
697}
698"#;
699 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
700
701 // bad cases
702 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
703 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first");
704
705 // valid cases
706 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
707 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
708 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
709 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
710 }
711}