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
44pub fn requiredness(expr: &ConditionExpr) -> Option<ConditionExpr> {
57 match expr {
58 ConditionExpr::Ref(id) if *id == 0 || (500..1000).contains(id) => None,
59 ConditionExpr::Ref(_) | ConditionExpr::Package { .. } => Some(expr.clone()),
60 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
61 let mut kept: Vec<ConditionExpr> = exprs.iter().filter_map(requiredness).collect();
62 match kept.len() {
63 0 => None,
64 1 => kept.pop(),
65 _ if matches!(expr, ConditionExpr::And(_)) => Some(ConditionExpr::And(kept)),
66 _ => Some(ConditionExpr::Or(kept)),
67 }
68 }
69 ConditionExpr::Xor(left, right) => match (requiredness(left), requiredness(right)) {
70 (None, None) => None,
71 (Some(one), None) | (None, Some(one)) => Some(one),
72 (Some(l), Some(r)) => Some(ConditionExpr::Xor(Box::new(l), Box::new(r))),
73 },
74 ConditionExpr::Not(inner) => {
75 requiredness(inner).map(|inner| ConditionExpr::Not(Box::new(inner)))
76 }
77 }
78}
79
80pub fn packages_allow_none(expr: &ConditionExpr) -> bool {
83 fn mins(expr: &ConditionExpr, out: &mut Vec<u32>) -> bool {
84 match expr {
85 ConditionExpr::Package { min, .. } => {
86 out.push(*min);
87 true
88 }
89 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
90 exprs.iter().all(|e| mins(e, out))
91 }
92 ConditionExpr::Xor(l, r) => mins(l, out) && mins(r, out),
93 ConditionExpr::Not(_) | ConditionExpr::Ref(_) => false,
94 }
95 }
96 let mut out = Vec::new();
97 mins(expr, &mut out) && !out.is_empty() && out.iter().all(|m| *m == 0)
98}
99
100impl<'a, E: ConditionEvaluator> ConditionExprEvaluator<'a, E> {
101 pub fn new(evaluator: &'a E) -> Self {
103 Self { evaluator }
104 }
105
106 pub fn evaluator(&self) -> &'a E {
108 self.evaluator
109 }
110
111 pub fn evaluate(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> ConditionResult {
113 match expr {
114 ConditionExpr::Ref(id) => self.evaluator.evaluate(*id, ctx),
115
116 ConditionExpr::And(exprs) => self.evaluate_and(exprs, ctx),
117
118 ConditionExpr::Or(exprs) => self.evaluate_or(exprs, ctx),
119
120 ConditionExpr::Xor(left, right) => {
125 match (is_package_only(left), is_package_only(right)) {
126 (true, true) => ConditionResult::True,
127 (true, false) => self.evaluate(right, ctx),
128 (false, true) => self.evaluate(left, ctx),
129 (false, false) => {
130 let l = self.evaluate(left, ctx);
131 let r = self.evaluate(right, ctx);
132 self.evaluate_xor(l, r)
133 }
134 }
135 }
136
137 ConditionExpr::Not(inner) => {
138 let result = self.evaluate(inner, ctx);
139 self.evaluate_not(result)
140 }
141
142 ConditionExpr::Package { .. } => {
143 ConditionResult::True
146 }
147 }
148 }
149
150 fn evaluate_and(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
152 let mut has_unknown = false;
153
154 for expr in exprs {
155 match self.evaluate(expr, ctx) {
156 ConditionResult::False => return ConditionResult::False,
157 ConditionResult::Unknown => has_unknown = true,
158 ConditionResult::True => {}
159 }
160 }
161
162 if has_unknown {
163 ConditionResult::Unknown
164 } else {
165 ConditionResult::True
166 }
167 }
168
169 fn evaluate_or(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
171 let mut has_unknown = false;
172
173 for expr in exprs {
174 match self.evaluate(expr, ctx) {
175 ConditionResult::True => return ConditionResult::True,
176 ConditionResult::Unknown => has_unknown = true,
177 ConditionResult::False => {}
178 }
179 }
180
181 if has_unknown {
182 ConditionResult::Unknown
183 } else {
184 ConditionResult::False
185 }
186 }
187
188 fn evaluate_xor(&self, left: ConditionResult, right: ConditionResult) -> ConditionResult {
190 match (left, right) {
191 (ConditionResult::True, ConditionResult::False)
192 | (ConditionResult::False, ConditionResult::True) => ConditionResult::True,
193 (ConditionResult::True, ConditionResult::True)
194 | (ConditionResult::False, ConditionResult::False) => ConditionResult::False,
195 _ => ConditionResult::Unknown,
196 }
197 }
198
199 fn evaluate_not(&self, result: ConditionResult) -> ConditionResult {
201 match result {
202 ConditionResult::True => ConditionResult::False,
203 ConditionResult::False => ConditionResult::True,
204 ConditionResult::Unknown => ConditionResult::Unknown,
205 }
206 }
207
208 pub fn evaluate_status(&self, ahb_status: &str, ctx: &EvaluationContext) -> ConditionResult {
212 use crate::expr::ConditionParser;
213
214 match ConditionParser::parse(ahb_status) {
215 Ok(Some(expr)) => self.evaluate(&expr, ctx),
216 Ok(None) => ConditionResult::True, Err(_) => ConditionResult::Unknown, }
219 }
220
221 pub fn evaluate_status_with_ub(
224 &self,
225 ahb_status: &str,
226 ctx: &EvaluationContext,
227 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
228 ) -> ConditionResult {
229 use crate::expr::ConditionParser;
230
231 match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
232 Ok(Some(expr)) => self.evaluate(&expr, ctx),
233 Ok(None) => ConditionResult::True,
234 Err(_) => ConditionResult::Unknown,
235 }
236 }
237
238 pub fn evaluate_status_detailed(
241 &self,
242 ahb_status: &str,
243 ctx: &EvaluationContext,
244 ) -> (ConditionResult, Vec<u32>) {
245 use crate::expr::ConditionParser;
246
247 match ConditionParser::parse(ahb_status) {
248 Ok(Some(expr)) => {
249 let result = self.evaluate(&expr, ctx);
250 if result.is_unknown() {
251 let unknown_ids = self.collect_unknown_ids(&expr, ctx);
252 (result, unknown_ids)
253 } else {
254 (result, Vec::new())
255 }
256 }
257 Ok(None) => (ConditionResult::True, Vec::new()),
258 Err(_) => (ConditionResult::Unknown, Vec::new()),
259 }
260 }
261
262 pub fn evaluate_status_detailed_with_ub(
265 &self,
266 ahb_status: &str,
267 ctx: &EvaluationContext,
268 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
269 ) -> (ConditionResult, Vec<u32>) {
270 use crate::expr::ConditionParser;
271
272 match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
273 Ok(Some(expr)) => {
274 let result = self.evaluate(&expr, ctx);
275 if result.is_unknown() {
276 let unknown_ids = self.collect_unknown_ids(&expr, ctx);
277 (result, unknown_ids)
278 } else {
279 (result, Vec::new())
280 }
281 }
282 Ok(None) => (ConditionResult::True, Vec::new()),
283 Err(_) => (ConditionResult::Unknown, Vec::new()),
284 }
285 }
286
287 pub fn resolve_status(
300 &self,
301 ahb_status: &str,
302 ctx: &EvaluationContext,
303 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
304 ) -> StatusResolution {
305 use crate::expr::ConditionParser;
306
307 let branches = match ConditionParser::parse_status_branches(ahb_status, ub_definitions) {
308 Ok(branches) => branches,
309 Err(_) => {
310 return StatusResolution {
311 result: ConditionResult::Unknown,
312 kind: StatusKind::of(ahb_status),
313 unknown_ids: Vec::new(),
314 }
315 }
316 };
317
318 let mut holding: Option<Option<StatusKind>> = None;
319 let mut unknown: Vec<(Option<StatusKind>, &ConditionExpr)> = Vec::new();
320 for branch in &branches {
321 let kind = match &branch.condition {
324 Some(c) if packages_allow_none(c) => branch.kind.map(|_| StatusKind::Kann),
325 _ => branch.kind,
326 };
327 let result = branch
328 .condition
329 .as_ref()
330 .map_or(ConditionResult::True, |c| self.evaluate(c, ctx));
331 match result {
332 ConditionResult::True => {
333 holding = Some(holding.map_or(kind, |k| k.max(kind)));
334 }
335 ConditionResult::Unknown => {
336 if let Some(condition) = &branch.condition {
338 unknown.push((kind, condition));
339 }
340 }
341 ConditionResult::False => {}
342 }
343 }
344
345 let blocking: Vec<&ConditionExpr> = unknown
347 .iter()
348 .filter(|(kind, _)| match holding {
349 None => true,
350 Some(h) => *kind > h,
351 })
352 .map(|(_, condition)| *condition)
353 .collect();
354
355 if !blocking.is_empty() {
356 return StatusResolution {
357 result: ConditionResult::Unknown,
358 kind: None,
359 unknown_ids: blocking
360 .into_iter()
361 .flat_map(|c| self.collect_unknown_ids(c, ctx))
362 .collect(),
363 };
364 }
365 match holding {
366 Some(kind) => StatusResolution {
367 result: ConditionResult::True,
368 kind,
369 unknown_ids: Vec::new(),
370 },
371 None => StatusResolution {
372 result: if branches.is_empty() {
373 ConditionResult::True
374 } else {
375 ConditionResult::False
376 },
377 kind: branches.iter().map(|b| b.kind).max().flatten(),
378 unknown_ids: Vec::new(),
379 },
380 }
381 }
382
383 pub fn false_ids_with_ub(
390 &self,
391 ahb_status: &str,
392 ctx: &EvaluationContext,
393 ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
394 ) -> Vec<u32> {
395 let Ok(Some(expr)) =
396 crate::expr::ConditionParser::parse_with_ub(ahb_status, ub_definitions)
397 else {
398 return Vec::new();
399 };
400 let mut ids = Vec::new();
401 self.collect_false_ids(&expr, ctx, false, &mut ids);
402 ids.sort_unstable();
403 ids.dedup();
404 ids
405 }
406
407 fn collect_false_ids(
408 &self,
409 expr: &ConditionExpr,
410 ctx: &EvaluationContext,
411 negated: bool,
412 ids: &mut Vec<u32>,
413 ) {
414 match expr {
415 ConditionExpr::Ref(id) => {
416 let result = self.evaluator.evaluate(*id, ctx);
417 let failing = if negated {
418 result == ConditionResult::True
419 } else {
420 result == ConditionResult::False
421 };
422 if failing {
423 ids.push(*id);
424 }
425 }
426 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
427 for e in exprs {
428 self.collect_false_ids(e, ctx, negated, ids);
429 }
430 }
431 ConditionExpr::Xor(left, right) => {
432 self.collect_false_ids(left, ctx, negated, ids);
433 self.collect_false_ids(right, ctx, negated, ids);
434 }
435 ConditionExpr::Not(inner) => self.collect_false_ids(inner, ctx, !negated, ids),
436 ConditionExpr::Package { .. } => {}
437 }
438 }
439
440 fn collect_unknown_ids(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> Vec<u32> {
441 let mut ids = Vec::new();
442 self.collect_unknown_ids_inner(expr, ctx, &mut ids);
443 ids
444 }
445
446 fn collect_unknown_ids_inner(
447 &self,
448 expr: &ConditionExpr,
449 ctx: &EvaluationContext,
450 ids: &mut Vec<u32>,
451 ) {
452 match expr {
453 ConditionExpr::Ref(id) => {
454 if self.evaluator.evaluate(*id, ctx).is_unknown() {
455 ids.push(*id);
456 }
457 }
458 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
459 for e in exprs {
460 self.collect_unknown_ids_inner(e, ctx, ids);
461 }
462 }
463 ConditionExpr::Xor(left, right) => {
464 self.collect_unknown_ids_inner(left, ctx, ids);
465 self.collect_unknown_ids_inner(right, ctx, ids);
466 }
467 ConditionExpr::Not(inner) => {
468 self.collect_unknown_ids_inner(inner, ctx, ids);
469 }
470 ConditionExpr::Package { .. } => {
471 }
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::super::evaluator::{ConditionResult as CR, NoOpExternalProvider};
480
481 #[test]
482 fn requiredness_drops_notes_formats_and_unexpanded_ub() {
483 use crate::expr::ConditionParser;
484 let reduce = |status: &str| {
485 ConditionParser::parse(status)
486 .unwrap()
487 .and_then(|e| requiredness(&e))
488 };
489 assert_eq!(reduce("X ([10]) ∧ ([UB1])"), Some(ConditionExpr::Ref(10)));
490 assert_eq!(reduce("X [914] ∧ [937]"), None);
491 assert_eq!(reduce("X [UB1]"), None);
492 assert_eq!(
493 reduce("X ([914] ∧ [930] [140]) ⊻ ([926] ∧ [937] [172])"),
494 Some(ConditionExpr::Xor(
495 Box::new(ConditionExpr::Ref(140)),
496 Box::new(ConditionExpr::Ref(172))
497 ))
498 );
499 assert_eq!(
500 reduce("Muss [356] ∧ [2061]"),
501 Some(ConditionExpr::And(vec![
502 ConditionExpr::Ref(356),
503 ConditionExpr::Ref(2061)
504 ]))
505 );
506 }
507
508 #[test]
509 fn a_package_without_a_minimum_requires_nothing() {
510 use crate::expr::ConditionParser;
511 let parse = |s: &str| ConditionParser::parse(s).unwrap().unwrap();
512 assert!(packages_allow_none(&parse("S [9P0..1]")));
513 assert!(!packages_allow_none(&parse("X [4P1..1]")));
514 assert!(!packages_allow_none(&parse("X [4P0..1] ∧ [12]")));
515 }
516 use super::*;
517 use mig_types::segment::OwnedSegment;
518 use std::collections::HashMap;
519
520 struct MockEvaluator {
522 results: HashMap<u32, ConditionResult>,
523 external_ids: Vec<u32>,
524 }
525
526 impl MockEvaluator {
527 fn new() -> Self {
528 Self {
529 results: HashMap::new(),
530 external_ids: Vec::new(),
531 }
532 }
533
534 fn with_condition(mut self, id: u32, result: ConditionResult) -> Self {
535 self.results.insert(id, result);
536 self
537 }
538 }
539
540 impl ConditionEvaluator for MockEvaluator {
541 fn evaluate(&self, condition: u32, _ctx: &EvaluationContext) -> ConditionResult {
542 self.results
543 .get(&condition)
544 .copied()
545 .unwrap_or(ConditionResult::Unknown)
546 }
547
548 fn is_external(&self, condition: u32) -> bool {
549 self.external_ids.contains(&condition)
550 }
551
552 fn message_type(&self) -> &str {
553 "TEST"
554 }
555
556 fn format_version(&self) -> &str {
557 "FV_TEST"
558 }
559 }
560
561 fn empty_context() -> (NoOpExternalProvider, Vec<OwnedSegment>) {
562 (NoOpExternalProvider, Vec::new())
563 }
564
565 fn make_ctx<'a>(
566 external: &'a NoOpExternalProvider,
567 segments: &'a [OwnedSegment],
568 ) -> EvaluationContext<'a> {
569 EvaluationContext::new("11001", external, segments)
570 }
571
572 #[test]
575 fn test_eval_single_true() {
576 let eval = MockEvaluator::new().with_condition(1, CR::True);
577 let (ext, segs) = empty_context();
578 let ctx = make_ctx(&ext, &segs);
579 let expr_eval = ConditionExprEvaluator::new(&eval);
580
581 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::True);
582 }
583
584 #[test]
585 fn test_eval_single_false() {
586 let eval = MockEvaluator::new().with_condition(1, CR::False);
587 let (ext, segs) = empty_context();
588 let ctx = make_ctx(&ext, &segs);
589 let expr_eval = ConditionExprEvaluator::new(&eval);
590
591 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::False);
592 }
593
594 #[test]
595 fn test_eval_single_unknown() {
596 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
598 let ctx = make_ctx(&ext, &segs);
599 let expr_eval = ConditionExprEvaluator::new(&eval);
600
601 assert_eq!(
602 expr_eval.evaluate(&ConditionExpr::Ref(999), &ctx),
603 CR::Unknown
604 );
605 }
606
607 #[test]
610 fn test_eval_and_both_true() {
611 let eval = MockEvaluator::new()
612 .with_condition(1, CR::True)
613 .with_condition(2, CR::True);
614 let (ext, segs) = empty_context();
615 let ctx = make_ctx(&ext, &segs);
616 let expr_eval = ConditionExprEvaluator::new(&eval);
617
618 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
619 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
620 }
621
622 #[test]
623 fn test_eval_and_one_false_short_circuits() {
624 let eval = MockEvaluator::new()
625 .with_condition(1, CR::False)
626 .with_condition(2, CR::True);
627 let (ext, segs) = empty_context();
628 let ctx = make_ctx(&ext, &segs);
629 let expr_eval = ConditionExprEvaluator::new(&eval);
630
631 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
632 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
633 }
634
635 #[test]
636 fn test_eval_and_one_unknown_true_gives_unknown() {
637 let eval = MockEvaluator::new()
638 .with_condition(1, CR::True)
639 .with_condition(2, CR::Unknown);
640 let (ext, segs) = empty_context();
641 let ctx = make_ctx(&ext, &segs);
642 let expr_eval = ConditionExprEvaluator::new(&eval);
643
644 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
645 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
646 }
647
648 #[test]
649 fn test_eval_and_false_beats_unknown() {
650 let eval = MockEvaluator::new()
652 .with_condition(1, CR::False)
653 .with_condition(2, CR::Unknown);
654 let (ext, segs) = empty_context();
655 let ctx = make_ctx(&ext, &segs);
656 let expr_eval = ConditionExprEvaluator::new(&eval);
657
658 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
659 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
660 }
661
662 #[test]
663 fn test_eval_and_three_way() {
664 let eval = MockEvaluator::new()
665 .with_condition(182, CR::True)
666 .with_condition(6, CR::True)
667 .with_condition(570, CR::True);
668 let (ext, segs) = empty_context();
669 let ctx = make_ctx(&ext, &segs);
670 let expr_eval = ConditionExprEvaluator::new(&eval);
671
672 let expr = ConditionExpr::And(vec![
673 ConditionExpr::Ref(182),
674 ConditionExpr::Ref(6),
675 ConditionExpr::Ref(570),
676 ]);
677 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
678 }
679
680 #[test]
681 fn test_eval_and_three_way_one_false() {
682 let eval = MockEvaluator::new()
683 .with_condition(182, CR::True)
684 .with_condition(6, CR::True)
685 .with_condition(570, CR::False);
686 let (ext, segs) = empty_context();
687 let ctx = make_ctx(&ext, &segs);
688 let expr_eval = ConditionExprEvaluator::new(&eval);
689
690 let expr = ConditionExpr::And(vec![
691 ConditionExpr::Ref(182),
692 ConditionExpr::Ref(6),
693 ConditionExpr::Ref(570),
694 ]);
695 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
696 }
697
698 #[test]
701 fn test_eval_or_both_false() {
702 let eval = MockEvaluator::new()
703 .with_condition(1, CR::False)
704 .with_condition(2, CR::False);
705 let (ext, segs) = empty_context();
706 let ctx = make_ctx(&ext, &segs);
707 let expr_eval = ConditionExprEvaluator::new(&eval);
708
709 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
710 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
711 }
712
713 #[test]
714 fn test_eval_or_one_true_short_circuits() {
715 let eval = MockEvaluator::new()
716 .with_condition(1, CR::False)
717 .with_condition(2, CR::True);
718 let (ext, segs) = empty_context();
719 let ctx = make_ctx(&ext, &segs);
720 let expr_eval = ConditionExprEvaluator::new(&eval);
721
722 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
723 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
724 }
725
726 #[test]
727 fn test_eval_or_true_beats_unknown() {
728 let eval = MockEvaluator::new()
729 .with_condition(1, CR::Unknown)
730 .with_condition(2, CR::True);
731 let (ext, segs) = empty_context();
732 let ctx = make_ctx(&ext, &segs);
733 let expr_eval = ConditionExprEvaluator::new(&eval);
734
735 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
736 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
737 }
738
739 #[test]
740 fn test_eval_or_false_and_unknown_gives_unknown() {
741 let eval = MockEvaluator::new()
742 .with_condition(1, CR::False)
743 .with_condition(2, CR::Unknown);
744 let (ext, segs) = empty_context();
745 let ctx = make_ctx(&ext, &segs);
746 let expr_eval = ConditionExprEvaluator::new(&eval);
747
748 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
749 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
750 }
751
752 #[test]
755 fn test_eval_xor_true_false() {
756 let eval = MockEvaluator::new()
757 .with_condition(1, CR::True)
758 .with_condition(2, CR::False);
759 let (ext, segs) = empty_context();
760 let ctx = make_ctx(&ext, &segs);
761 let expr_eval = ConditionExprEvaluator::new(&eval);
762
763 let expr = ConditionExpr::Xor(
764 Box::new(ConditionExpr::Ref(1)),
765 Box::new(ConditionExpr::Ref(2)),
766 );
767 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
768 }
769
770 #[test]
771 fn test_eval_xor_both_true() {
772 let eval = MockEvaluator::new()
773 .with_condition(1, CR::True)
774 .with_condition(2, CR::True);
775 let (ext, segs) = empty_context();
776 let ctx = make_ctx(&ext, &segs);
777 let expr_eval = ConditionExprEvaluator::new(&eval);
778
779 let expr = ConditionExpr::Xor(
780 Box::new(ConditionExpr::Ref(1)),
781 Box::new(ConditionExpr::Ref(2)),
782 );
783 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
784 }
785
786 #[test]
787 fn test_eval_xor_both_false() {
788 let eval = MockEvaluator::new()
789 .with_condition(1, CR::False)
790 .with_condition(2, CR::False);
791 let (ext, segs) = empty_context();
792 let ctx = make_ctx(&ext, &segs);
793 let expr_eval = ConditionExprEvaluator::new(&eval);
794
795 let expr = ConditionExpr::Xor(
796 Box::new(ConditionExpr::Ref(1)),
797 Box::new(ConditionExpr::Ref(2)),
798 );
799 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
800 }
801
802 #[test]
803 fn test_eval_xor_unknown_propagates() {
804 let eval = MockEvaluator::new()
805 .with_condition(1, CR::True)
806 .with_condition(2, CR::Unknown);
807 let (ext, segs) = empty_context();
808 let ctx = make_ctx(&ext, &segs);
809 let expr_eval = ConditionExprEvaluator::new(&eval);
810
811 let expr = ConditionExpr::Xor(
812 Box::new(ConditionExpr::Ref(1)),
813 Box::new(ConditionExpr::Ref(2)),
814 );
815 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
816 }
817
818 #[test]
821 fn test_eval_not_true() {
822 let eval = MockEvaluator::new().with_condition(1, CR::True);
823 let (ext, segs) = empty_context();
824 let ctx = make_ctx(&ext, &segs);
825 let expr_eval = ConditionExprEvaluator::new(&eval);
826
827 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
828 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
829 }
830
831 #[test]
832 fn test_eval_not_false() {
833 let eval = MockEvaluator::new().with_condition(1, CR::False);
834 let (ext, segs) = empty_context();
835 let ctx = make_ctx(&ext, &segs);
836 let expr_eval = ConditionExprEvaluator::new(&eval);
837
838 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
839 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
840 }
841
842 #[test]
843 fn test_eval_not_unknown() {
844 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
846 let ctx = make_ctx(&ext, &segs);
847 let expr_eval = ConditionExprEvaluator::new(&eval);
848
849 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
850 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
851 }
852
853 #[test]
856 fn test_eval_complex_nested() {
857 let eval = MockEvaluator::new()
861 .with_condition(1, CR::True)
862 .with_condition(2, CR::False)
863 .with_condition(3, CR::True)
864 .with_condition(4, CR::True)
865 .with_condition(5, CR::True);
866 let (ext, segs) = empty_context();
867 let ctx = make_ctx(&ext, &segs);
868 let expr_eval = ConditionExprEvaluator::new(&eval);
869
870 let expr = ConditionExpr::And(vec![
871 ConditionExpr::Or(vec![
872 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
873 ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
874 ]),
875 ConditionExpr::Ref(5),
876 ]);
877 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
878 }
879
880 #[test]
881 fn test_eval_xor_with_nested_and() {
882 let eval = MockEvaluator::new()
886 .with_condition(102, CR::True)
887 .with_condition(2006, CR::True)
888 .with_condition(103, CR::False)
889 .with_condition(2005, CR::False);
890 let (ext, segs) = empty_context();
891 let ctx = make_ctx(&ext, &segs);
892 let expr_eval = ConditionExprEvaluator::new(&eval);
893
894 let expr = ConditionExpr::Xor(
895 Box::new(ConditionExpr::And(vec![
896 ConditionExpr::Ref(102),
897 ConditionExpr::Ref(2006),
898 ])),
899 Box::new(ConditionExpr::And(vec![
900 ConditionExpr::Ref(103),
901 ConditionExpr::Ref(2005),
902 ])),
903 );
904 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
905 }
906
907 #[test]
910 fn test_evaluate_status_with_conditions() {
911 let eval = MockEvaluator::new()
912 .with_condition(182, CR::True)
913 .with_condition(152, CR::True);
914 let (ext, segs) = empty_context();
915 let ctx = make_ctx(&ext, &segs);
916 let expr_eval = ConditionExprEvaluator::new(&eval);
917
918 assert_eq!(
919 expr_eval.evaluate_status("Muss [182] ∧ [152]", &ctx),
920 CR::True
921 );
922 }
923
924 #[test]
925 fn test_evaluate_status_no_conditions() {
926 let eval = MockEvaluator::new();
927 let (ext, segs) = empty_context();
928 let ctx = make_ctx(&ext, &segs);
929 let expr_eval = ConditionExprEvaluator::new(&eval);
930
931 assert_eq!(expr_eval.evaluate_status("Muss", &ctx), CR::True);
932 }
933
934 #[test]
935 fn test_evaluate_status_empty() {
936 let eval = MockEvaluator::new();
937 let (ext, segs) = empty_context();
938 let ctx = make_ctx(&ext, &segs);
939 let expr_eval = ConditionExprEvaluator::new(&eval);
940
941 assert_eq!(expr_eval.evaluate_status("", &ctx), CR::True);
942 }
943
944 #[test]
947 fn test_unknown_propagation_and_or_mix() {
948 let eval = MockEvaluator::new().with_condition(2, CR::True);
952 let (ext, segs) = empty_context();
954 let ctx = make_ctx(&ext, &segs);
955 let expr_eval = ConditionExprEvaluator::new(&eval);
956
957 let expr = ConditionExpr::Or(vec![
958 ConditionExpr::Ref(1),
959 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
960 ]);
961 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
962 }
963
964 #[test]
965 fn test_or_short_circuits_past_unknown() {
966 let eval = MockEvaluator::new().with_condition(2, CR::True);
968 let (ext, segs) = empty_context();
969 let ctx = make_ctx(&ext, &segs);
970 let expr_eval = ConditionExprEvaluator::new(&eval);
971
972 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
973 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
974 }
975
976 #[test]
977 fn test_and_short_circuits_past_unknown() {
978 let eval = MockEvaluator::new().with_condition(2, CR::False);
980 let (ext, segs) = empty_context();
981 let ctx = make_ctx(&ext, &segs);
982 let expr_eval = ConditionExprEvaluator::new(&eval);
983
984 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
985 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
986 }
987
988 #[test]
991 fn test_detailed_returns_unknown_ids() {
992 let eval = MockEvaluator::new().with_condition(182, CR::True);
994 let (ext, segs) = empty_context();
995 let ctx = make_ctx(&ext, &segs);
996 let expr_eval = ConditionExprEvaluator::new(&eval);
997
998 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
999 assert_eq!(result, CR::Unknown);
1000 assert_eq!(unknown_ids, vec![8]);
1001 }
1002
1003 #[test]
1004 fn test_detailed_multiple_unknown_ids() {
1005 let eval = MockEvaluator::new().with_condition(2, CR::True);
1007 let (ext, segs) = empty_context();
1008 let ctx = make_ctx(&ext, &segs);
1009 let expr_eval = ConditionExprEvaluator::new(&eval);
1010
1011 let (result, unknown_ids) =
1012 expr_eval.evaluate_status_detailed("Muss [1] ∧ [2] ∧ [3]", &ctx);
1013 assert_eq!(result, CR::Unknown);
1014 assert_eq!(unknown_ids, vec![1, 3]);
1015 }
1016
1017 #[test]
1018 fn test_detailed_no_unknown_when_true() {
1019 let eval = MockEvaluator::new()
1020 .with_condition(182, CR::True)
1021 .with_condition(152, CR::True);
1022 let (ext, segs) = empty_context();
1023 let ctx = make_ctx(&ext, &segs);
1024 let expr_eval = ConditionExprEvaluator::new(&eval);
1025
1026 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [152]", &ctx);
1027 assert_eq!(result, CR::True);
1028 assert!(unknown_ids.is_empty());
1029 }
1030
1031 #[test]
1032 fn test_detailed_no_unknown_when_false() {
1033 let eval = MockEvaluator::new().with_condition(182, CR::False);
1034 let (ext, segs) = empty_context();
1035 let ctx = make_ctx(&ext, &segs);
1036 let expr_eval = ConditionExprEvaluator::new(&eval);
1037
1038 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
1039 assert_eq!(result, CR::False);
1040 assert!(unknown_ids.is_empty());
1041 }
1042
1043 fn resolve(eval: &MockEvaluator, status: &str) -> StatusResolution {
1046 let (ext, segs) = empty_context();
1047 let ctx = make_ctx(&ext, &segs);
1048 ConditionExprEvaluator::new(eval).resolve_status(status, &ctx, &Default::default())
1049 }
1050
1051 #[test]
1052 fn test_resolve_single_line_keeps_its_status() {
1053 let eval = MockEvaluator::new().with_condition(1, CR::True);
1054 let r = resolve(&eval, "Soll [1]");
1055 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
1056 }
1057
1058 #[test]
1059 fn test_resolve_only_later_line_holds_gives_its_status() {
1060 let eval = MockEvaluator::new()
1062 .with_condition(2119, CR::False)
1063 .with_condition(130, CR::True);
1064 let r = resolve(&eval, "Muss [2119]\r\nSoll [130]");
1065 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
1066 }
1067
1068 #[test]
1069 fn test_resolve_several_lines_hold_strictest_wins() {
1070 let eval = MockEvaluator::new()
1071 .with_condition(47, CR::True)
1072 .with_condition(46, CR::True);
1073 let r = resolve(&eval, "Soll [47]\nMuss [46]");
1074 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1075 }
1076
1077 #[test]
1078 fn test_resolve_unconditional_fallback_line() {
1079 let eval = MockEvaluator::new().with_condition(48, CR::False);
1080 let r = resolve(&eval, "Muss [48]\nKann");
1081 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Kann)));
1082 }
1083
1084 #[test]
1085 fn test_resolve_package_needing_no_code_requires_nothing() {
1086 let eval = MockEvaluator::new();
1089 let r = resolve(&eval, "X [17P0..1]");
1090 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Kann)));
1091 let r = resolve(&eval, "X [1P1..1]");
1092 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1093 }
1094
1095 #[test]
1096 fn test_resolve_weaker_unknown_line_does_not_block() {
1097 let eval = MockEvaluator::new().with_condition(2119, CR::True);
1099 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
1100 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1101 assert!(r.unknown_ids.is_empty());
1102 }
1103
1104 #[test]
1105 fn test_resolve_stronger_unknown_line_is_unknown() {
1106 let eval = MockEvaluator::new().with_condition(130, CR::True);
1108 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
1109 assert_eq!(r.result, CR::Unknown);
1110 assert_eq!(r.unknown_ids, vec![2119]);
1111 }
1112
1113 #[test]
1114 fn test_resolve_equally_strong_unknown_line_does_not_block() {
1115 let eval = MockEvaluator::new().with_condition(2, CR::True);
1116 let r = resolve(&eval, "Muss [1]\nMuss [2]");
1117 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1118 }
1119
1120 #[test]
1121 fn test_resolve_nothing_holds_but_something_unknown() {
1122 let eval = MockEvaluator::new().with_condition(1, CR::False);
1123 let r = resolve(&eval, "Muss [1]\nSoll [2]");
1124 assert_eq!(r.result, CR::Unknown);
1125 assert_eq!(r.unknown_ids, vec![2]);
1126 }
1127
1128 #[test]
1129 fn test_resolve_no_line_holds_reports_strictest_status() {
1130 let eval = MockEvaluator::new()
1131 .with_condition(1, CR::False)
1132 .with_condition(2, CR::False);
1133 let r = resolve(&eval, "Soll [1]\nMuss [2]");
1134 assert_eq!((r.result, r.kind), (CR::False, Some(StatusKind::Muss)));
1135 }
1136}