1use super::context::EvaluationContext;
4use super::evaluator::{ConditionEvaluator, ConditionResult};
5use crate::expr::{ConditionExpr, StatusKind};
6
7#[derive(Debug, Clone, PartialEq)]
10pub struct StatusResolution {
11 pub result: ConditionResult,
14 pub kind: Option<StatusKind>,
15 pub unknown_ids: Vec<u32>,
17}
18
19pub struct ConditionExprEvaluator<'a, E: ConditionEvaluator> {
27 evaluator: &'a E,
28}
29
30fn is_package_only(expr: &ConditionExpr) -> bool {
33 match expr {
34 ConditionExpr::Package { .. } => true,
35 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
36 !exprs.is_empty() && exprs.iter().all(is_package_only)
37 }
38 ConditionExpr::Xor(left, right) => is_package_only(left) && is_package_only(right),
39 ConditionExpr::Not(inner) => is_package_only(inner),
40 ConditionExpr::Ref(_) => false,
41 }
42}
43
44impl<'a, E: ConditionEvaluator> ConditionExprEvaluator<'a, E> {
45 pub fn new(evaluator: &'a E) -> Self {
47 Self { evaluator }
48 }
49
50 pub fn evaluator(&self) -> &'a E {
52 self.evaluator
53 }
54
55 pub fn evaluate(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> ConditionResult {
57 match expr {
58 ConditionExpr::Ref(id) => self.evaluator.evaluate(*id, ctx),
59
60 ConditionExpr::And(exprs) => self.evaluate_and(exprs, ctx),
61
62 ConditionExpr::Or(exprs) => self.evaluate_or(exprs, ctx),
63
64 ConditionExpr::Xor(left, right) => {
69 match (is_package_only(left), is_package_only(right)) {
70 (true, true) => ConditionResult::True,
71 (true, false) => self.evaluate(right, ctx),
72 (false, true) => self.evaluate(left, ctx),
73 (false, false) => {
74 let l = self.evaluate(left, ctx);
75 let r = self.evaluate(right, ctx);
76 self.evaluate_xor(l, r)
77 }
78 }
79 }
80
81 ConditionExpr::Not(inner) => {
82 let result = self.evaluate(inner, ctx);
83 self.evaluate_not(result)
84 }
85
86 ConditionExpr::Package { .. } => {
87 ConditionResult::True
90 }
91 }
92 }
93
94 fn evaluate_and(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
96 let mut has_unknown = false;
97
98 for expr in exprs {
99 match self.evaluate(expr, ctx) {
100 ConditionResult::False => return ConditionResult::False,
101 ConditionResult::Unknown => has_unknown = true,
102 ConditionResult::True => {}
103 }
104 }
105
106 if has_unknown {
107 ConditionResult::Unknown
108 } else {
109 ConditionResult::True
110 }
111 }
112
113 fn evaluate_or(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
115 let mut has_unknown = false;
116
117 for expr in exprs {
118 match self.evaluate(expr, ctx) {
119 ConditionResult::True => return ConditionResult::True,
120 ConditionResult::Unknown => has_unknown = true,
121 ConditionResult::False => {}
122 }
123 }
124
125 if has_unknown {
126 ConditionResult::Unknown
127 } else {
128 ConditionResult::False
129 }
130 }
131
132 fn evaluate_xor(&self, left: ConditionResult, right: ConditionResult) -> ConditionResult {
134 match (left, right) {
135 (ConditionResult::True, ConditionResult::False)
136 | (ConditionResult::False, ConditionResult::True) => ConditionResult::True,
137 (ConditionResult::True, ConditionResult::True)
138 | (ConditionResult::False, ConditionResult::False) => ConditionResult::False,
139 _ => ConditionResult::Unknown,
140 }
141 }
142
143 fn evaluate_not(&self, result: ConditionResult) -> ConditionResult {
145 match result {
146 ConditionResult::True => ConditionResult::False,
147 ConditionResult::False => ConditionResult::True,
148 ConditionResult::Unknown => ConditionResult::Unknown,
149 }
150 }
151
152 pub fn evaluate_status(&self, ahb_status: &str, ctx: &EvaluationContext) -> ConditionResult {
156 use crate::expr::ConditionParser;
157
158 match ConditionParser::parse(ahb_status) {
159 Ok(Some(expr)) => self.evaluate(&expr, ctx),
160 Ok(None) => ConditionResult::True, Err(_) => ConditionResult::Unknown, }
163 }
164
165 pub fn evaluate_status_with_ub(
168 &self,
169 ahb_status: &str,
170 ctx: &EvaluationContext,
171 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
172 ) -> ConditionResult {
173 use crate::expr::ConditionParser;
174
175 match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
176 Ok(Some(expr)) => self.evaluate(&expr, ctx),
177 Ok(None) => ConditionResult::True,
178 Err(_) => ConditionResult::Unknown,
179 }
180 }
181
182 pub fn evaluate_status_detailed(
185 &self,
186 ahb_status: &str,
187 ctx: &EvaluationContext,
188 ) -> (ConditionResult, Vec<u32>) {
189 use crate::expr::ConditionParser;
190
191 match ConditionParser::parse(ahb_status) {
192 Ok(Some(expr)) => {
193 let result = self.evaluate(&expr, ctx);
194 if result.is_unknown() {
195 let unknown_ids = self.collect_unknown_ids(&expr, ctx);
196 (result, unknown_ids)
197 } else {
198 (result, Vec::new())
199 }
200 }
201 Ok(None) => (ConditionResult::True, Vec::new()),
202 Err(_) => (ConditionResult::Unknown, Vec::new()),
203 }
204 }
205
206 pub fn evaluate_status_detailed_with_ub(
209 &self,
210 ahb_status: &str,
211 ctx: &EvaluationContext,
212 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
213 ) -> (ConditionResult, Vec<u32>) {
214 use crate::expr::ConditionParser;
215
216 match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
217 Ok(Some(expr)) => {
218 let result = self.evaluate(&expr, ctx);
219 if result.is_unknown() {
220 let unknown_ids = self.collect_unknown_ids(&expr, ctx);
221 (result, unknown_ids)
222 } else {
223 (result, Vec::new())
224 }
225 }
226 Ok(None) => (ConditionResult::True, Vec::new()),
227 Err(_) => (ConditionResult::Unknown, Vec::new()),
228 }
229 }
230
231 pub fn resolve_status(
244 &self,
245 ahb_status: &str,
246 ctx: &EvaluationContext,
247 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
248 ) -> StatusResolution {
249 use crate::expr::ConditionParser;
250
251 let branches = match ConditionParser::parse_status_branches(ahb_status, ub_definitions) {
252 Ok(branches) => branches,
253 Err(_) => {
254 return StatusResolution {
255 result: ConditionResult::Unknown,
256 kind: StatusKind::of(ahb_status),
257 unknown_ids: Vec::new(),
258 }
259 }
260 };
261
262 let mut holding: Option<Option<StatusKind>> = None;
263 let mut unknown: Vec<(Option<StatusKind>, &ConditionExpr)> = Vec::new();
264 for branch in &branches {
265 let result = branch
266 .condition
267 .as_ref()
268 .map_or(ConditionResult::True, |c| self.evaluate(c, ctx));
269 match result {
270 ConditionResult::True => {
271 holding = Some(holding.map_or(branch.kind, |k| k.max(branch.kind)));
272 }
273 ConditionResult::Unknown => {
274 if let Some(condition) = &branch.condition {
276 unknown.push((branch.kind, condition));
277 }
278 }
279 ConditionResult::False => {}
280 }
281 }
282
283 let blocking: Vec<&ConditionExpr> = unknown
285 .iter()
286 .filter(|(kind, _)| match holding {
287 None => true,
288 Some(h) => *kind > h,
289 })
290 .map(|(_, condition)| *condition)
291 .collect();
292
293 if !blocking.is_empty() {
294 return StatusResolution {
295 result: ConditionResult::Unknown,
296 kind: None,
297 unknown_ids: blocking
298 .into_iter()
299 .flat_map(|c| self.collect_unknown_ids(c, ctx))
300 .collect(),
301 };
302 }
303 match holding {
304 Some(kind) => StatusResolution {
305 result: ConditionResult::True,
306 kind,
307 unknown_ids: Vec::new(),
308 },
309 None => StatusResolution {
310 result: if branches.is_empty() {
311 ConditionResult::True
312 } else {
313 ConditionResult::False
314 },
315 kind: branches.iter().map(|b| b.kind).max().flatten(),
316 unknown_ids: Vec::new(),
317 },
318 }
319 }
320
321 fn collect_unknown_ids(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> Vec<u32> {
323 let mut ids = Vec::new();
324 self.collect_unknown_ids_inner(expr, ctx, &mut ids);
325 ids
326 }
327
328 fn collect_unknown_ids_inner(
329 &self,
330 expr: &ConditionExpr,
331 ctx: &EvaluationContext,
332 ids: &mut Vec<u32>,
333 ) {
334 match expr {
335 ConditionExpr::Ref(id) => {
336 if self.evaluator.evaluate(*id, ctx).is_unknown() {
337 ids.push(*id);
338 }
339 }
340 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
341 for e in exprs {
342 self.collect_unknown_ids_inner(e, ctx, ids);
343 }
344 }
345 ConditionExpr::Xor(left, right) => {
346 self.collect_unknown_ids_inner(left, ctx, ids);
347 self.collect_unknown_ids_inner(right, ctx, ids);
348 }
349 ConditionExpr::Not(inner) => {
350 self.collect_unknown_ids_inner(inner, ctx, ids);
351 }
352 ConditionExpr::Package { .. } => {
353 }
355 }
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::super::evaluator::{ConditionResult as CR, NoOpExternalProvider};
362 use super::*;
363 use mig_types::segment::OwnedSegment;
364 use std::collections::HashMap;
365
366 struct MockEvaluator {
368 results: HashMap<u32, ConditionResult>,
369 external_ids: Vec<u32>,
370 }
371
372 impl MockEvaluator {
373 fn new() -> Self {
374 Self {
375 results: HashMap::new(),
376 external_ids: Vec::new(),
377 }
378 }
379
380 fn with_condition(mut self, id: u32, result: ConditionResult) -> Self {
381 self.results.insert(id, result);
382 self
383 }
384 }
385
386 impl ConditionEvaluator for MockEvaluator {
387 fn evaluate(&self, condition: u32, _ctx: &EvaluationContext) -> ConditionResult {
388 self.results
389 .get(&condition)
390 .copied()
391 .unwrap_or(ConditionResult::Unknown)
392 }
393
394 fn is_external(&self, condition: u32) -> bool {
395 self.external_ids.contains(&condition)
396 }
397
398 fn message_type(&self) -> &str {
399 "TEST"
400 }
401
402 fn format_version(&self) -> &str {
403 "FV_TEST"
404 }
405 }
406
407 fn empty_context() -> (NoOpExternalProvider, Vec<OwnedSegment>) {
408 (NoOpExternalProvider, Vec::new())
409 }
410
411 fn make_ctx<'a>(
412 external: &'a NoOpExternalProvider,
413 segments: &'a [OwnedSegment],
414 ) -> EvaluationContext<'a> {
415 EvaluationContext::new("11001", external, segments)
416 }
417
418 #[test]
421 fn test_eval_single_true() {
422 let eval = MockEvaluator::new().with_condition(1, CR::True);
423 let (ext, segs) = empty_context();
424 let ctx = make_ctx(&ext, &segs);
425 let expr_eval = ConditionExprEvaluator::new(&eval);
426
427 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::True);
428 }
429
430 #[test]
431 fn test_eval_single_false() {
432 let eval = MockEvaluator::new().with_condition(1, CR::False);
433 let (ext, segs) = empty_context();
434 let ctx = make_ctx(&ext, &segs);
435 let expr_eval = ConditionExprEvaluator::new(&eval);
436
437 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::False);
438 }
439
440 #[test]
441 fn test_eval_single_unknown() {
442 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
444 let ctx = make_ctx(&ext, &segs);
445 let expr_eval = ConditionExprEvaluator::new(&eval);
446
447 assert_eq!(
448 expr_eval.evaluate(&ConditionExpr::Ref(999), &ctx),
449 CR::Unknown
450 );
451 }
452
453 #[test]
456 fn test_eval_and_both_true() {
457 let eval = MockEvaluator::new()
458 .with_condition(1, CR::True)
459 .with_condition(2, CR::True);
460 let (ext, segs) = empty_context();
461 let ctx = make_ctx(&ext, &segs);
462 let expr_eval = ConditionExprEvaluator::new(&eval);
463
464 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
465 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
466 }
467
468 #[test]
469 fn test_eval_and_one_false_short_circuits() {
470 let eval = MockEvaluator::new()
471 .with_condition(1, CR::False)
472 .with_condition(2, CR::True);
473 let (ext, segs) = empty_context();
474 let ctx = make_ctx(&ext, &segs);
475 let expr_eval = ConditionExprEvaluator::new(&eval);
476
477 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
478 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
479 }
480
481 #[test]
482 fn test_eval_and_one_unknown_true_gives_unknown() {
483 let eval = MockEvaluator::new()
484 .with_condition(1, CR::True)
485 .with_condition(2, CR::Unknown);
486 let (ext, segs) = empty_context();
487 let ctx = make_ctx(&ext, &segs);
488 let expr_eval = ConditionExprEvaluator::new(&eval);
489
490 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
491 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
492 }
493
494 #[test]
495 fn test_eval_and_false_beats_unknown() {
496 let eval = MockEvaluator::new()
498 .with_condition(1, CR::False)
499 .with_condition(2, CR::Unknown);
500 let (ext, segs) = empty_context();
501 let ctx = make_ctx(&ext, &segs);
502 let expr_eval = ConditionExprEvaluator::new(&eval);
503
504 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
505 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
506 }
507
508 #[test]
509 fn test_eval_and_three_way() {
510 let eval = MockEvaluator::new()
511 .with_condition(182, CR::True)
512 .with_condition(6, CR::True)
513 .with_condition(570, CR::True);
514 let (ext, segs) = empty_context();
515 let ctx = make_ctx(&ext, &segs);
516 let expr_eval = ConditionExprEvaluator::new(&eval);
517
518 let expr = ConditionExpr::And(vec![
519 ConditionExpr::Ref(182),
520 ConditionExpr::Ref(6),
521 ConditionExpr::Ref(570),
522 ]);
523 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
524 }
525
526 #[test]
527 fn test_eval_and_three_way_one_false() {
528 let eval = MockEvaluator::new()
529 .with_condition(182, CR::True)
530 .with_condition(6, CR::True)
531 .with_condition(570, CR::False);
532 let (ext, segs) = empty_context();
533 let ctx = make_ctx(&ext, &segs);
534 let expr_eval = ConditionExprEvaluator::new(&eval);
535
536 let expr = ConditionExpr::And(vec![
537 ConditionExpr::Ref(182),
538 ConditionExpr::Ref(6),
539 ConditionExpr::Ref(570),
540 ]);
541 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
542 }
543
544 #[test]
547 fn test_eval_or_both_false() {
548 let eval = MockEvaluator::new()
549 .with_condition(1, CR::False)
550 .with_condition(2, CR::False);
551 let (ext, segs) = empty_context();
552 let ctx = make_ctx(&ext, &segs);
553 let expr_eval = ConditionExprEvaluator::new(&eval);
554
555 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
556 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
557 }
558
559 #[test]
560 fn test_eval_or_one_true_short_circuits() {
561 let eval = MockEvaluator::new()
562 .with_condition(1, CR::False)
563 .with_condition(2, CR::True);
564 let (ext, segs) = empty_context();
565 let ctx = make_ctx(&ext, &segs);
566 let expr_eval = ConditionExprEvaluator::new(&eval);
567
568 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
569 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
570 }
571
572 #[test]
573 fn test_eval_or_true_beats_unknown() {
574 let eval = MockEvaluator::new()
575 .with_condition(1, CR::Unknown)
576 .with_condition(2, CR::True);
577 let (ext, segs) = empty_context();
578 let ctx = make_ctx(&ext, &segs);
579 let expr_eval = ConditionExprEvaluator::new(&eval);
580
581 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
582 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
583 }
584
585 #[test]
586 fn test_eval_or_false_and_unknown_gives_unknown() {
587 let eval = MockEvaluator::new()
588 .with_condition(1, CR::False)
589 .with_condition(2, CR::Unknown);
590 let (ext, segs) = empty_context();
591 let ctx = make_ctx(&ext, &segs);
592 let expr_eval = ConditionExprEvaluator::new(&eval);
593
594 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
595 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
596 }
597
598 #[test]
601 fn test_eval_xor_true_false() {
602 let eval = MockEvaluator::new()
603 .with_condition(1, CR::True)
604 .with_condition(2, CR::False);
605 let (ext, segs) = empty_context();
606 let ctx = make_ctx(&ext, &segs);
607 let expr_eval = ConditionExprEvaluator::new(&eval);
608
609 let expr = ConditionExpr::Xor(
610 Box::new(ConditionExpr::Ref(1)),
611 Box::new(ConditionExpr::Ref(2)),
612 );
613 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
614 }
615
616 #[test]
617 fn test_eval_xor_both_true() {
618 let eval = MockEvaluator::new()
619 .with_condition(1, CR::True)
620 .with_condition(2, CR::True);
621 let (ext, segs) = empty_context();
622 let ctx = make_ctx(&ext, &segs);
623 let expr_eval = ConditionExprEvaluator::new(&eval);
624
625 let expr = ConditionExpr::Xor(
626 Box::new(ConditionExpr::Ref(1)),
627 Box::new(ConditionExpr::Ref(2)),
628 );
629 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
630 }
631
632 #[test]
633 fn test_eval_xor_both_false() {
634 let eval = MockEvaluator::new()
635 .with_condition(1, CR::False)
636 .with_condition(2, CR::False);
637 let (ext, segs) = empty_context();
638 let ctx = make_ctx(&ext, &segs);
639 let expr_eval = ConditionExprEvaluator::new(&eval);
640
641 let expr = ConditionExpr::Xor(
642 Box::new(ConditionExpr::Ref(1)),
643 Box::new(ConditionExpr::Ref(2)),
644 );
645 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
646 }
647
648 #[test]
649 fn test_eval_xor_unknown_propagates() {
650 let eval = MockEvaluator::new()
651 .with_condition(1, CR::True)
652 .with_condition(2, CR::Unknown);
653 let (ext, segs) = empty_context();
654 let ctx = make_ctx(&ext, &segs);
655 let expr_eval = ConditionExprEvaluator::new(&eval);
656
657 let expr = ConditionExpr::Xor(
658 Box::new(ConditionExpr::Ref(1)),
659 Box::new(ConditionExpr::Ref(2)),
660 );
661 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
662 }
663
664 #[test]
667 fn test_eval_not_true() {
668 let eval = MockEvaluator::new().with_condition(1, CR::True);
669 let (ext, segs) = empty_context();
670 let ctx = make_ctx(&ext, &segs);
671 let expr_eval = ConditionExprEvaluator::new(&eval);
672
673 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
674 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
675 }
676
677 #[test]
678 fn test_eval_not_false() {
679 let eval = MockEvaluator::new().with_condition(1, CR::False);
680 let (ext, segs) = empty_context();
681 let ctx = make_ctx(&ext, &segs);
682 let expr_eval = ConditionExprEvaluator::new(&eval);
683
684 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
685 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
686 }
687
688 #[test]
689 fn test_eval_not_unknown() {
690 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
692 let ctx = make_ctx(&ext, &segs);
693 let expr_eval = ConditionExprEvaluator::new(&eval);
694
695 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
696 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
697 }
698
699 #[test]
702 fn test_eval_complex_nested() {
703 let eval = MockEvaluator::new()
707 .with_condition(1, CR::True)
708 .with_condition(2, CR::False)
709 .with_condition(3, CR::True)
710 .with_condition(4, CR::True)
711 .with_condition(5, CR::True);
712 let (ext, segs) = empty_context();
713 let ctx = make_ctx(&ext, &segs);
714 let expr_eval = ConditionExprEvaluator::new(&eval);
715
716 let expr = ConditionExpr::And(vec![
717 ConditionExpr::Or(vec![
718 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
719 ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
720 ]),
721 ConditionExpr::Ref(5),
722 ]);
723 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
724 }
725
726 #[test]
727 fn test_eval_xor_with_nested_and() {
728 let eval = MockEvaluator::new()
732 .with_condition(102, CR::True)
733 .with_condition(2006, CR::True)
734 .with_condition(103, CR::False)
735 .with_condition(2005, CR::False);
736 let (ext, segs) = empty_context();
737 let ctx = make_ctx(&ext, &segs);
738 let expr_eval = ConditionExprEvaluator::new(&eval);
739
740 let expr = ConditionExpr::Xor(
741 Box::new(ConditionExpr::And(vec![
742 ConditionExpr::Ref(102),
743 ConditionExpr::Ref(2006),
744 ])),
745 Box::new(ConditionExpr::And(vec![
746 ConditionExpr::Ref(103),
747 ConditionExpr::Ref(2005),
748 ])),
749 );
750 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
751 }
752
753 #[test]
756 fn test_evaluate_status_with_conditions() {
757 let eval = MockEvaluator::new()
758 .with_condition(182, CR::True)
759 .with_condition(152, CR::True);
760 let (ext, segs) = empty_context();
761 let ctx = make_ctx(&ext, &segs);
762 let expr_eval = ConditionExprEvaluator::new(&eval);
763
764 assert_eq!(
765 expr_eval.evaluate_status("Muss [182] ∧ [152]", &ctx),
766 CR::True
767 );
768 }
769
770 #[test]
771 fn test_evaluate_status_no_conditions() {
772 let eval = MockEvaluator::new();
773 let (ext, segs) = empty_context();
774 let ctx = make_ctx(&ext, &segs);
775 let expr_eval = ConditionExprEvaluator::new(&eval);
776
777 assert_eq!(expr_eval.evaluate_status("Muss", &ctx), CR::True);
778 }
779
780 #[test]
781 fn test_evaluate_status_empty() {
782 let eval = MockEvaluator::new();
783 let (ext, segs) = empty_context();
784 let ctx = make_ctx(&ext, &segs);
785 let expr_eval = ConditionExprEvaluator::new(&eval);
786
787 assert_eq!(expr_eval.evaluate_status("", &ctx), CR::True);
788 }
789
790 #[test]
793 fn test_unknown_propagation_and_or_mix() {
794 let eval = MockEvaluator::new().with_condition(2, CR::True);
798 let (ext, segs) = empty_context();
800 let ctx = make_ctx(&ext, &segs);
801 let expr_eval = ConditionExprEvaluator::new(&eval);
802
803 let expr = ConditionExpr::Or(vec![
804 ConditionExpr::Ref(1),
805 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
806 ]);
807 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
808 }
809
810 #[test]
811 fn test_or_short_circuits_past_unknown() {
812 let eval = MockEvaluator::new().with_condition(2, CR::True);
814 let (ext, segs) = empty_context();
815 let ctx = make_ctx(&ext, &segs);
816 let expr_eval = ConditionExprEvaluator::new(&eval);
817
818 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
819 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
820 }
821
822 #[test]
823 fn test_and_short_circuits_past_unknown() {
824 let eval = MockEvaluator::new().with_condition(2, CR::False);
826 let (ext, segs) = empty_context();
827 let ctx = make_ctx(&ext, &segs);
828 let expr_eval = ConditionExprEvaluator::new(&eval);
829
830 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
831 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
832 }
833
834 #[test]
837 fn test_detailed_returns_unknown_ids() {
838 let eval = MockEvaluator::new().with_condition(182, CR::True);
840 let (ext, segs) = empty_context();
841 let ctx = make_ctx(&ext, &segs);
842 let expr_eval = ConditionExprEvaluator::new(&eval);
843
844 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
845 assert_eq!(result, CR::Unknown);
846 assert_eq!(unknown_ids, vec![8]);
847 }
848
849 #[test]
850 fn test_detailed_multiple_unknown_ids() {
851 let eval = MockEvaluator::new().with_condition(2, CR::True);
853 let (ext, segs) = empty_context();
854 let ctx = make_ctx(&ext, &segs);
855 let expr_eval = ConditionExprEvaluator::new(&eval);
856
857 let (result, unknown_ids) =
858 expr_eval.evaluate_status_detailed("Muss [1] ∧ [2] ∧ [3]", &ctx);
859 assert_eq!(result, CR::Unknown);
860 assert_eq!(unknown_ids, vec![1, 3]);
861 }
862
863 #[test]
864 fn test_detailed_no_unknown_when_true() {
865 let eval = MockEvaluator::new()
866 .with_condition(182, CR::True)
867 .with_condition(152, CR::True);
868 let (ext, segs) = empty_context();
869 let ctx = make_ctx(&ext, &segs);
870 let expr_eval = ConditionExprEvaluator::new(&eval);
871
872 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [152]", &ctx);
873 assert_eq!(result, CR::True);
874 assert!(unknown_ids.is_empty());
875 }
876
877 #[test]
878 fn test_detailed_no_unknown_when_false() {
879 let eval = MockEvaluator::new().with_condition(182, CR::False);
880 let (ext, segs) = empty_context();
881 let ctx = make_ctx(&ext, &segs);
882 let expr_eval = ConditionExprEvaluator::new(&eval);
883
884 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
885 assert_eq!(result, CR::False);
886 assert!(unknown_ids.is_empty());
887 }
888
889 fn resolve(eval: &MockEvaluator, status: &str) -> StatusResolution {
892 let (ext, segs) = empty_context();
893 let ctx = make_ctx(&ext, &segs);
894 ConditionExprEvaluator::new(eval).resolve_status(status, &ctx, &Default::default())
895 }
896
897 #[test]
898 fn test_resolve_single_line_keeps_its_status() {
899 let eval = MockEvaluator::new().with_condition(1, CR::True);
900 let r = resolve(&eval, "Soll [1]");
901 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
902 }
903
904 #[test]
905 fn test_resolve_only_later_line_holds_gives_its_status() {
906 let eval = MockEvaluator::new()
908 .with_condition(2119, CR::False)
909 .with_condition(130, CR::True);
910 let r = resolve(&eval, "Muss [2119]\r\nSoll [130]");
911 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
912 }
913
914 #[test]
915 fn test_resolve_several_lines_hold_strictest_wins() {
916 let eval = MockEvaluator::new()
917 .with_condition(47, CR::True)
918 .with_condition(46, CR::True);
919 let r = resolve(&eval, "Soll [47]\nMuss [46]");
920 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
921 }
922
923 #[test]
924 fn test_resolve_unconditional_fallback_line() {
925 let eval = MockEvaluator::new().with_condition(48, CR::False);
926 let r = resolve(&eval, "Muss [48]\nKann");
927 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Kann)));
928 }
929
930 #[test]
931 fn test_resolve_weaker_unknown_line_does_not_block() {
932 let eval = MockEvaluator::new().with_condition(2119, CR::True);
934 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
935 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
936 assert!(r.unknown_ids.is_empty());
937 }
938
939 #[test]
940 fn test_resolve_stronger_unknown_line_is_unknown() {
941 let eval = MockEvaluator::new().with_condition(130, CR::True);
943 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
944 assert_eq!(r.result, CR::Unknown);
945 assert_eq!(r.unknown_ids, vec![2119]);
946 }
947
948 #[test]
949 fn test_resolve_equally_strong_unknown_line_does_not_block() {
950 let eval = MockEvaluator::new().with_condition(2, CR::True);
951 let r = resolve(&eval, "Muss [1]\nMuss [2]");
952 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
953 }
954
955 #[test]
956 fn test_resolve_nothing_holds_but_something_unknown() {
957 let eval = MockEvaluator::new().with_condition(1, CR::False);
958 let r = resolve(&eval, "Muss [1]\nSoll [2]");
959 assert_eq!(r.result, CR::Unknown);
960 assert_eq!(r.unknown_ids, vec![2]);
961 }
962
963 #[test]
964 fn test_resolve_no_line_holds_reports_strictest_status() {
965 let eval = MockEvaluator::new()
966 .with_condition(1, CR::False)
967 .with_condition(2, CR::False);
968 let r = resolve(&eval, "Soll [1]\nMuss [2]");
969 assert_eq!((r.result, r.kind), (CR::False, Some(StatusKind::Muss)));
970 }
971}