blob: a621b87c34949cf98b4f0d225faa4640df1e0ed3 [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 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200203
204 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
205 debug_assert!(!pf.trace.is_empty());
206 pf.trace[0].source()
207 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200208}
209
210pub mod parsed_flags {
211 use super::*;
212 use anyhow::bail;
213 use std::cmp::Ordering;
214
215 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
216 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
217 verify_fields(&message)?;
218 Ok(message)
219 }
220
221 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200222 use crate::protos::parsed_flag::path_to_declaration;
223
Mårten Kongstad403658f2023-06-14 09:51:56 +0200224 let mut previous: Option<&ProtoParsedFlag> = None;
225 for parsed_flag in pf.parsed_flag.iter() {
226 if let Some(prev) = previous {
227 let a = create_sorting_key(prev);
228 let b = create_sorting_key(parsed_flag);
229 match a.cmp(&b) {
230 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200231 Ordering::Equal => bail!(
232 "bad parsed flags: duplicate flag {} (defined in {} and {})",
233 a,
234 path_to_declaration(prev),
235 path_to_declaration(parsed_flag)
236 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200237 Ordering::Greater => {
238 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
239 }
240 }
241 }
242 super::parsed_flag::verify_fields(parsed_flag)?;
243 previous = Some(parsed_flag);
244 }
245 Ok(())
246 }
247
248 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
249 let mut merged = ProtoParsedFlags::new();
250 for mut pfs in parsed_flags.into_iter() {
251 merged.parsed_flag.append(&mut pfs.parsed_flag);
252 }
253 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
254 verify_fields(&merged)?;
255 Ok(merged)
256 }
257
258 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
259 format!("{}.{}", pf.package(), pf.name())
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn test_flag_declarations_try_from_text_proto() {
269 // valid input
270 let flag_declarations = flag_declarations::try_from_text_proto(
271 r#"
272package: "com.foo.bar"
273flag {
274 name: "first"
275 namespace: "first_ns"
276 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200277 bug: "123"
278 bug: "abc"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200279}
280flag {
281 name: "second"
282 namespace: "second_ns"
283 description: "This is the description of the second flag."
284}
285"#,
286 )
287 .unwrap();
288 assert_eq!(flag_declarations.package(), "com.foo.bar");
289 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
290 assert_eq!(first.name(), "first");
291 assert_eq!(first.namespace(), "first_ns");
292 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200293 assert_eq!(first.bug.len(), 2);
294 assert_eq!(first.bug[0], "123");
295 assert_eq!(first.bug[1], "abc");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200296 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
297 assert_eq!(second.name(), "second");
298 assert_eq!(second.namespace(), "second_ns");
299 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200300 assert_eq!(second.bug.len(), 0);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200301
302 // bad input: missing package in flag declarations
303 let error = flag_declarations::try_from_text_proto(
304 r#"
305flag {
306 name: "first"
307 namespace: "first_ns"
308 description: "This is the description of the first flag."
309}
310flag {
311 name: "second"
312 namespace: "second_ns"
313 description: "This is the description of the second flag."
314}
315"#,
316 )
317 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200318 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200319
320 // bad input: missing namespace in flag declaration
321 let error = flag_declarations::try_from_text_proto(
322 r#"
323package: "com.foo.bar"
324flag {
325 name: "first"
326 description: "This is the description of the first flag."
327}
328flag {
329 name: "second"
330 namespace: "second_ns"
331 description: "This is the description of the second flag."
332}
333"#,
334 )
335 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200336 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200337
338 // bad input: bad package name in flag declarations
339 let error = flag_declarations::try_from_text_proto(
340 r#"
341package: "_com.FOO__BAR"
342flag {
343 name: "first"
344 namespace: "first_ns"
345 description: "This is the description of the first flag."
346}
347flag {
348 name: "second"
349 namespace: "second_ns"
350 description: "This is the description of the second flag."
351}
352"#,
353 )
354 .unwrap_err();
355 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
356
357 // bad input: bad name in flag declaration
358 let error = flag_declarations::try_from_text_proto(
359 r#"
360package: "com.foo.bar"
361flag {
362 name: "FIRST"
363 namespace: "first_ns"
364 description: "This is the description of the first flag."
365}
366flag {
367 name: "second"
368 namespace: "second_ns"
369 description: "This is the description of the second flag."
370}
371"#,
372 )
373 .unwrap_err();
374 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
375 }
376
377 #[test]
378 fn test_flag_values_try_from_text_proto() {
379 // valid input
380 let flag_values = 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}
388flag_value {
389 package: "com.second"
390 name: "second"
391 state: ENABLED
392 permission: READ_WRITE
393}
394"#,
395 )
396 .unwrap();
397 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
398 assert_eq!(first.package(), "com.first");
399 assert_eq!(first.name(), "first");
400 assert_eq!(first.state(), ProtoFlagState::DISABLED);
401 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
402 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
403 assert_eq!(second.package(), "com.second");
404 assert_eq!(second.name(), "second");
405 assert_eq!(second.state(), ProtoFlagState::ENABLED);
406 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
407
408 // bad input: bad package in flag value
409 let error = flag_values::try_from_text_proto(
410 r#"
411flag_value {
412 package: "COM.FIRST"
413 name: "first"
414 state: DISABLED
415 permission: READ_ONLY
416}
417"#,
418 )
419 .unwrap_err();
420 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
421
422 // bad input: bad name in flag value
423 let error = flag_values::try_from_text_proto(
424 r#"
425flag_value {
426 package: "com.first"
427 name: "FIRST"
428 state: DISABLED
429 permission: READ_ONLY
430}
431"#,
432 )
433 .unwrap_err();
434 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
435
436 // bad input: missing state in flag value
437 let error = flag_values::try_from_text_proto(
438 r#"
439flag_value {
440 package: "com.first"
441 name: "first"
442 permission: READ_ONLY
443}
444"#,
445 )
446 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200447 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200448
449 // bad input: missing permission in flag value
450 let error = flag_values::try_from_text_proto(
451 r#"
452flag_value {
453 package: "com.first"
454 name: "first"
455 state: DISABLED
456}
457"#,
458 )
459 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200460 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200461 }
462
463 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
464 use protobuf::Message;
465
466 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
467 let mut binary_proto = Vec::new();
468 parsed_flags.write_to_vec(&mut binary_proto)?;
469 parsed_flags::try_from_binary_proto(&binary_proto)
470 }
471
472 #[test]
473 fn test_parsed_flags_try_from_text_proto() {
474 // valid input
475 let text_proto = r#"
476parsed_flag {
477 package: "com.first"
478 name: "first"
479 namespace: "first_ns"
480 description: "This is the description of the first flag."
481 state: DISABLED
482 permission: READ_ONLY
483 trace {
484 source: "flags.declarations"
485 state: DISABLED
486 permission: READ_ONLY
487 }
488}
489parsed_flag {
490 package: "com.second"
491 name: "second"
492 namespace: "second_ns"
493 description: "This is the description of the second flag."
494 state: ENABLED
495 permission: READ_WRITE
496 trace {
497 source: "flags.declarations"
498 state: DISABLED
499 permission: READ_ONLY
500 }
501 trace {
502 source: "flags.values"
503 state: ENABLED
504 permission: READ_WRITE
505 }
506}
507"#;
508 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
509 assert_eq!(parsed_flags.parsed_flag.len(), 2);
510 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
511 assert_eq!(second.package(), "com.second");
512 assert_eq!(second.name(), "second");
513 assert_eq!(second.namespace(), "second_ns");
514 assert_eq!(second.description(), "This is the description of the second flag.");
515 assert_eq!(second.state(), ProtoFlagState::ENABLED);
516 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
517 assert_eq!(2, second.trace.len());
518 assert_eq!(second.trace[0].source(), "flags.declarations");
519 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
520 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
521 assert_eq!(second.trace[1].source(), "flags.values");
522 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
523 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
524
525 // valid input: empty
526 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
527 assert!(parsed_flags.parsed_flag.is_empty());
528
529 // bad input: empty trace
530 let text_proto = r#"
531parsed_flag {
532 package: "com.first"
533 name: "first"
534 namespace: "first_ns"
535 description: "This is the description of the first flag."
536 state: DISABLED
537 permission: READ_ONLY
538}
539"#;
540 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
541 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
542
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200543 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200544 let text_proto = r#"
545parsed_flag {
546 package: "com.first"
547 name: "first"
548 description: "This is the description of the first flag."
549 state: DISABLED
550 permission: READ_ONLY
551 trace {
552 source: "flags.declarations"
553 state: DISABLED
554 permission: READ_ONLY
555 }
556}
557"#;
558 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200559 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200560
561 // bad input: parsed_flag not sorted by package
562 let text_proto = r#"
563parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200564 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200565 name: "first"
566 namespace: "first_ns"
567 description: "This is the description of the first flag."
568 state: DISABLED
569 permission: READ_ONLY
570 trace {
571 source: "flags.declarations"
572 state: DISABLED
573 permission: READ_ONLY
574 }
575}
576parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200577 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200578 name: "second"
579 namespace: "second_ns"
580 description: "This is the description of the second flag."
581 state: ENABLED
582 permission: READ_WRITE
583 trace {
584 source: "flags.declarations"
585 state: DISABLED
586 permission: READ_ONLY
587 }
588}
589"#;
590 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
591 assert_eq!(
592 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200593 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200594 );
595
596 // bad input: parsed_flag not sorted by name
597 let text_proto = r#"
598parsed_flag {
599 package: "com.foo"
600 name: "bbb"
601 namespace: "first_ns"
602 description: "This is the description of the first flag."
603 state: DISABLED
604 permission: READ_ONLY
605 trace {
606 source: "flags.declarations"
607 state: DISABLED
608 permission: READ_ONLY
609 }
610}
611parsed_flag {
612 package: "com.foo"
613 name: "aaa"
614 namespace: "second_ns"
615 description: "This is the description of the second flag."
616 state: ENABLED
617 permission: READ_WRITE
618 trace {
619 source: "flags.declarations"
620 state: DISABLED
621 permission: READ_ONLY
622 }
623}
624"#;
625 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
626 assert_eq!(
627 format!("{:?}", error),
628 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
629 );
630
631 // bad input: duplicate flags
632 let text_proto = r#"
633parsed_flag {
634 package: "com.foo"
635 name: "bar"
636 namespace: "first_ns"
637 description: "This is the description of the first flag."
638 state: DISABLED
639 permission: READ_ONLY
640 trace {
641 source: "flags.declarations"
642 state: DISABLED
643 permission: READ_ONLY
644 }
645}
646parsed_flag {
647 package: "com.foo"
648 name: "bar"
649 namespace: "second_ns"
650 description: "This is the description of the second flag."
651 state: ENABLED
652 permission: READ_WRITE
653 trace {
654 source: "flags.declarations"
655 state: DISABLED
656 permission: READ_ONLY
657 }
658}
659"#;
660 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200661 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
662 }
663
664 #[test]
665 fn test_parsed_flag_path_to_declaration() {
666 let text_proto = r#"
667parsed_flag {
668 package: "com.foo"
669 name: "bar"
670 namespace: "first_ns"
671 description: "This is the description of the first flag."
672 state: DISABLED
673 permission: READ_ONLY
674 trace {
675 source: "flags.declarations"
676 state: DISABLED
677 permission: READ_ONLY
678 }
679 trace {
680 source: "flags.values"
681 state: ENABLED
682 permission: READ_ONLY
683 }
684}
685"#;
686 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
687 let parsed_flag = &parsed_flags.parsed_flag[0];
688 assert_eq!(
689 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
690 "flags.declarations"
691 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200692 }
693
694 #[test]
695 fn test_parsed_flags_merge() {
696 let text_proto = r#"
697parsed_flag {
698 package: "com.first"
699 name: "first"
700 namespace: "first_ns"
701 description: "This is the description of the first flag."
702 state: DISABLED
703 permission: READ_ONLY
704 trace {
705 source: "flags.declarations"
706 state: DISABLED
707 permission: READ_ONLY
708 }
709}
710parsed_flag {
711 package: "com.second"
712 name: "second"
713 namespace: "second_ns"
714 description: "This is the description of the second flag."
715 state: ENABLED
716 permission: READ_WRITE
717 trace {
718 source: "flags.declarations"
719 state: DISABLED
720 permission: READ_ONLY
721 }
722}
723"#;
724 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
725
726 let text_proto = r#"
727parsed_flag {
728 package: "com.first"
729 name: "first"
730 namespace: "first_ns"
731 description: "This is the description of the first flag."
732 state: DISABLED
733 permission: READ_ONLY
734 trace {
735 source: "flags.declarations"
736 state: DISABLED
737 permission: READ_ONLY
738 }
739}
740"#;
741 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
742
743 let text_proto = r#"
744parsed_flag {
745 package: "com.second"
746 name: "second"
747 namespace: "second_ns"
748 description: "This is the description of the second flag."
749 state: ENABLED
750 permission: READ_WRITE
751 trace {
752 source: "flags.declarations"
753 state: DISABLED
754 permission: READ_ONLY
755 }
756}
757"#;
758 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
759
760 // bad cases
761 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200762 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 +0200763
764 // valid cases
765 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
766 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
767 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
768 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
769 }
770}