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 result = branch
322 .condition
323 .as_ref()
324 .map_or(ConditionResult::True, |c| self.evaluate(c, ctx));
325 match result {
326 ConditionResult::True => {
327 holding = Some(holding.map_or(branch.kind, |k| k.max(branch.kind)));
328 }
329 ConditionResult::Unknown => {
330 if let Some(condition) = &branch.condition {
332 unknown.push((branch.kind, condition));
333 }
334 }
335 ConditionResult::False => {}
336 }
337 }
338
339 let blocking: Vec<&ConditionExpr> = unknown
341 .iter()
342 .filter(|(kind, _)| match holding {
343 None => true,
344 Some(h) => *kind > h,
345 })
346 .map(|(_, condition)| *condition)
347 .collect();
348
349 if !blocking.is_empty() {
350 return StatusResolution {
351 result: ConditionResult::Unknown,
352 kind: None,
353 unknown_ids: blocking
354 .into_iter()
355 .flat_map(|c| self.collect_unknown_ids(c, ctx))
356 .collect(),
357 };
358 }
359 match holding {
360 Some(kind) => StatusResolution {
361 result: ConditionResult::True,
362 kind,
363 unknown_ids: Vec::new(),
364 },
365 None => StatusResolution {
366 result: if branches.is_empty() {
367 ConditionResult::True
368 } else {
369 ConditionResult::False
370 },
371 kind: branches.iter().map(|b| b.kind).max().flatten(),
372 unknown_ids: Vec::new(),
373 },
374 }
375 }
376
377 fn collect_unknown_ids(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> Vec<u32> {
379 let mut ids = Vec::new();
380 self.collect_unknown_ids_inner(expr, ctx, &mut ids);
381 ids
382 }
383
384 fn collect_unknown_ids_inner(
385 &self,
386 expr: &ConditionExpr,
387 ctx: &EvaluationContext,
388 ids: &mut Vec<u32>,
389 ) {
390 match expr {
391 ConditionExpr::Ref(id) => {
392 if self.evaluator.evaluate(*id, ctx).is_unknown() {
393 ids.push(*id);
394 }
395 }
396 ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
397 for e in exprs {
398 self.collect_unknown_ids_inner(e, ctx, ids);
399 }
400 }
401 ConditionExpr::Xor(left, right) => {
402 self.collect_unknown_ids_inner(left, ctx, ids);
403 self.collect_unknown_ids_inner(right, ctx, ids);
404 }
405 ConditionExpr::Not(inner) => {
406 self.collect_unknown_ids_inner(inner, ctx, ids);
407 }
408 ConditionExpr::Package { .. } => {
409 }
411 }
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::super::evaluator::{ConditionResult as CR, NoOpExternalProvider};
418
419 #[test]
420 fn requiredness_drops_notes_formats_and_unexpanded_ub() {
421 use crate::expr::ConditionParser;
422 let reduce = |status: &str| {
423 ConditionParser::parse(status)
424 .unwrap()
425 .and_then(|e| requiredness(&e))
426 };
427 assert_eq!(reduce("X ([10]) ∧ ([UB1])"), Some(ConditionExpr::Ref(10)));
428 assert_eq!(reduce("X [914] ∧ [937]"), None);
429 assert_eq!(reduce("X [UB1]"), None);
430 assert_eq!(
431 reduce("X ([914] ∧ [930] [140]) ⊻ ([926] ∧ [937] [172])"),
432 Some(ConditionExpr::Xor(
433 Box::new(ConditionExpr::Ref(140)),
434 Box::new(ConditionExpr::Ref(172))
435 ))
436 );
437 assert_eq!(
438 reduce("Muss [356] ∧ [2061]"),
439 Some(ConditionExpr::And(vec![
440 ConditionExpr::Ref(356),
441 ConditionExpr::Ref(2061)
442 ]))
443 );
444 }
445
446 #[test]
447 fn a_package_without_a_minimum_requires_nothing() {
448 use crate::expr::ConditionParser;
449 let parse = |s: &str| ConditionParser::parse(s).unwrap().unwrap();
450 assert!(packages_allow_none(&parse("S [9P0..1]")));
451 assert!(!packages_allow_none(&parse("X [4P1..1]")));
452 assert!(!packages_allow_none(&parse("X [4P0..1] ∧ [12]")));
453 }
454 use super::*;
455 use mig_types::segment::OwnedSegment;
456 use std::collections::HashMap;
457
458 struct MockEvaluator {
460 results: HashMap<u32, ConditionResult>,
461 external_ids: Vec<u32>,
462 }
463
464 impl MockEvaluator {
465 fn new() -> Self {
466 Self {
467 results: HashMap::new(),
468 external_ids: Vec::new(),
469 }
470 }
471
472 fn with_condition(mut self, id: u32, result: ConditionResult) -> Self {
473 self.results.insert(id, result);
474 self
475 }
476 }
477
478 impl ConditionEvaluator for MockEvaluator {
479 fn evaluate(&self, condition: u32, _ctx: &EvaluationContext) -> ConditionResult {
480 self.results
481 .get(&condition)
482 .copied()
483 .unwrap_or(ConditionResult::Unknown)
484 }
485
486 fn is_external(&self, condition: u32) -> bool {
487 self.external_ids.contains(&condition)
488 }
489
490 fn message_type(&self) -> &str {
491 "TEST"
492 }
493
494 fn format_version(&self) -> &str {
495 "FV_TEST"
496 }
497 }
498
499 fn empty_context() -> (NoOpExternalProvider, Vec<OwnedSegment>) {
500 (NoOpExternalProvider, Vec::new())
501 }
502
503 fn make_ctx<'a>(
504 external: &'a NoOpExternalProvider,
505 segments: &'a [OwnedSegment],
506 ) -> EvaluationContext<'a> {
507 EvaluationContext::new("11001", external, segments)
508 }
509
510 #[test]
513 fn test_eval_single_true() {
514 let eval = MockEvaluator::new().with_condition(1, CR::True);
515 let (ext, segs) = empty_context();
516 let ctx = make_ctx(&ext, &segs);
517 let expr_eval = ConditionExprEvaluator::new(&eval);
518
519 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::True);
520 }
521
522 #[test]
523 fn test_eval_single_false() {
524 let eval = MockEvaluator::new().with_condition(1, CR::False);
525 let (ext, segs) = empty_context();
526 let ctx = make_ctx(&ext, &segs);
527 let expr_eval = ConditionExprEvaluator::new(&eval);
528
529 assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::False);
530 }
531
532 #[test]
533 fn test_eval_single_unknown() {
534 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
536 let ctx = make_ctx(&ext, &segs);
537 let expr_eval = ConditionExprEvaluator::new(&eval);
538
539 assert_eq!(
540 expr_eval.evaluate(&ConditionExpr::Ref(999), &ctx),
541 CR::Unknown
542 );
543 }
544
545 #[test]
548 fn test_eval_and_both_true() {
549 let eval = MockEvaluator::new()
550 .with_condition(1, CR::True)
551 .with_condition(2, CR::True);
552 let (ext, segs) = empty_context();
553 let ctx = make_ctx(&ext, &segs);
554 let expr_eval = ConditionExprEvaluator::new(&eval);
555
556 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
557 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
558 }
559
560 #[test]
561 fn test_eval_and_one_false_short_circuits() {
562 let eval = MockEvaluator::new()
563 .with_condition(1, CR::False)
564 .with_condition(2, CR::True);
565 let (ext, segs) = empty_context();
566 let ctx = make_ctx(&ext, &segs);
567 let expr_eval = ConditionExprEvaluator::new(&eval);
568
569 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
570 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
571 }
572
573 #[test]
574 fn test_eval_and_one_unknown_true_gives_unknown() {
575 let eval = MockEvaluator::new()
576 .with_condition(1, CR::True)
577 .with_condition(2, CR::Unknown);
578 let (ext, segs) = empty_context();
579 let ctx = make_ctx(&ext, &segs);
580 let expr_eval = ConditionExprEvaluator::new(&eval);
581
582 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
583 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
584 }
585
586 #[test]
587 fn test_eval_and_false_beats_unknown() {
588 let eval = MockEvaluator::new()
590 .with_condition(1, CR::False)
591 .with_condition(2, CR::Unknown);
592 let (ext, segs) = empty_context();
593 let ctx = make_ctx(&ext, &segs);
594 let expr_eval = ConditionExprEvaluator::new(&eval);
595
596 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
597 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
598 }
599
600 #[test]
601 fn test_eval_and_three_way() {
602 let eval = MockEvaluator::new()
603 .with_condition(182, CR::True)
604 .with_condition(6, CR::True)
605 .with_condition(570, CR::True);
606 let (ext, segs) = empty_context();
607 let ctx = make_ctx(&ext, &segs);
608 let expr_eval = ConditionExprEvaluator::new(&eval);
609
610 let expr = ConditionExpr::And(vec![
611 ConditionExpr::Ref(182),
612 ConditionExpr::Ref(6),
613 ConditionExpr::Ref(570),
614 ]);
615 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
616 }
617
618 #[test]
619 fn test_eval_and_three_way_one_false() {
620 let eval = MockEvaluator::new()
621 .with_condition(182, CR::True)
622 .with_condition(6, CR::True)
623 .with_condition(570, CR::False);
624 let (ext, segs) = empty_context();
625 let ctx = make_ctx(&ext, &segs);
626 let expr_eval = ConditionExprEvaluator::new(&eval);
627
628 let expr = ConditionExpr::And(vec![
629 ConditionExpr::Ref(182),
630 ConditionExpr::Ref(6),
631 ConditionExpr::Ref(570),
632 ]);
633 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
634 }
635
636 #[test]
639 fn test_eval_or_both_false() {
640 let eval = MockEvaluator::new()
641 .with_condition(1, CR::False)
642 .with_condition(2, CR::False);
643 let (ext, segs) = empty_context();
644 let ctx = make_ctx(&ext, &segs);
645 let expr_eval = ConditionExprEvaluator::new(&eval);
646
647 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
648 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
649 }
650
651 #[test]
652 fn test_eval_or_one_true_short_circuits() {
653 let eval = MockEvaluator::new()
654 .with_condition(1, CR::False)
655 .with_condition(2, CR::True);
656 let (ext, segs) = empty_context();
657 let ctx = make_ctx(&ext, &segs);
658 let expr_eval = ConditionExprEvaluator::new(&eval);
659
660 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
661 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
662 }
663
664 #[test]
665 fn test_eval_or_true_beats_unknown() {
666 let eval = MockEvaluator::new()
667 .with_condition(1, CR::Unknown)
668 .with_condition(2, 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::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
674 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
675 }
676
677 #[test]
678 fn test_eval_or_false_and_unknown_gives_unknown() {
679 let eval = MockEvaluator::new()
680 .with_condition(1, CR::False)
681 .with_condition(2, CR::Unknown);
682 let (ext, segs) = empty_context();
683 let ctx = make_ctx(&ext, &segs);
684 let expr_eval = ConditionExprEvaluator::new(&eval);
685
686 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
687 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
688 }
689
690 #[test]
693 fn test_eval_xor_true_false() {
694 let eval = MockEvaluator::new()
695 .with_condition(1, CR::True)
696 .with_condition(2, CR::False);
697 let (ext, segs) = empty_context();
698 let ctx = make_ctx(&ext, &segs);
699 let expr_eval = ConditionExprEvaluator::new(&eval);
700
701 let expr = ConditionExpr::Xor(
702 Box::new(ConditionExpr::Ref(1)),
703 Box::new(ConditionExpr::Ref(2)),
704 );
705 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
706 }
707
708 #[test]
709 fn test_eval_xor_both_true() {
710 let eval = MockEvaluator::new()
711 .with_condition(1, CR::True)
712 .with_condition(2, CR::True);
713 let (ext, segs) = empty_context();
714 let ctx = make_ctx(&ext, &segs);
715 let expr_eval = ConditionExprEvaluator::new(&eval);
716
717 let expr = ConditionExpr::Xor(
718 Box::new(ConditionExpr::Ref(1)),
719 Box::new(ConditionExpr::Ref(2)),
720 );
721 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
722 }
723
724 #[test]
725 fn test_eval_xor_both_false() {
726 let eval = MockEvaluator::new()
727 .with_condition(1, CR::False)
728 .with_condition(2, CR::False);
729 let (ext, segs) = empty_context();
730 let ctx = make_ctx(&ext, &segs);
731 let expr_eval = ConditionExprEvaluator::new(&eval);
732
733 let expr = ConditionExpr::Xor(
734 Box::new(ConditionExpr::Ref(1)),
735 Box::new(ConditionExpr::Ref(2)),
736 );
737 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
738 }
739
740 #[test]
741 fn test_eval_xor_unknown_propagates() {
742 let eval = MockEvaluator::new()
743 .with_condition(1, CR::True)
744 .with_condition(2, CR::Unknown);
745 let (ext, segs) = empty_context();
746 let ctx = make_ctx(&ext, &segs);
747 let expr_eval = ConditionExprEvaluator::new(&eval);
748
749 let expr = ConditionExpr::Xor(
750 Box::new(ConditionExpr::Ref(1)),
751 Box::new(ConditionExpr::Ref(2)),
752 );
753 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
754 }
755
756 #[test]
759 fn test_eval_not_true() {
760 let eval = MockEvaluator::new().with_condition(1, CR::True);
761 let (ext, segs) = empty_context();
762 let ctx = make_ctx(&ext, &segs);
763 let expr_eval = ConditionExprEvaluator::new(&eval);
764
765 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
766 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
767 }
768
769 #[test]
770 fn test_eval_not_false() {
771 let eval = MockEvaluator::new().with_condition(1, CR::False);
772 let (ext, segs) = empty_context();
773 let ctx = make_ctx(&ext, &segs);
774 let expr_eval = ConditionExprEvaluator::new(&eval);
775
776 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
777 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
778 }
779
780 #[test]
781 fn test_eval_not_unknown() {
782 let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
784 let ctx = make_ctx(&ext, &segs);
785 let expr_eval = ConditionExprEvaluator::new(&eval);
786
787 let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
788 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
789 }
790
791 #[test]
794 fn test_eval_complex_nested() {
795 let eval = MockEvaluator::new()
799 .with_condition(1, CR::True)
800 .with_condition(2, CR::False)
801 .with_condition(3, CR::True)
802 .with_condition(4, CR::True)
803 .with_condition(5, CR::True);
804 let (ext, segs) = empty_context();
805 let ctx = make_ctx(&ext, &segs);
806 let expr_eval = ConditionExprEvaluator::new(&eval);
807
808 let expr = ConditionExpr::And(vec![
809 ConditionExpr::Or(vec![
810 ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
811 ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
812 ]),
813 ConditionExpr::Ref(5),
814 ]);
815 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
816 }
817
818 #[test]
819 fn test_eval_xor_with_nested_and() {
820 let eval = MockEvaluator::new()
824 .with_condition(102, CR::True)
825 .with_condition(2006, CR::True)
826 .with_condition(103, CR::False)
827 .with_condition(2005, CR::False);
828 let (ext, segs) = empty_context();
829 let ctx = make_ctx(&ext, &segs);
830 let expr_eval = ConditionExprEvaluator::new(&eval);
831
832 let expr = ConditionExpr::Xor(
833 Box::new(ConditionExpr::And(vec![
834 ConditionExpr::Ref(102),
835 ConditionExpr::Ref(2006),
836 ])),
837 Box::new(ConditionExpr::And(vec![
838 ConditionExpr::Ref(103),
839 ConditionExpr::Ref(2005),
840 ])),
841 );
842 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
843 }
844
845 #[test]
848 fn test_evaluate_status_with_conditions() {
849 let eval = MockEvaluator::new()
850 .with_condition(182, CR::True)
851 .with_condition(152, CR::True);
852 let (ext, segs) = empty_context();
853 let ctx = make_ctx(&ext, &segs);
854 let expr_eval = ConditionExprEvaluator::new(&eval);
855
856 assert_eq!(
857 expr_eval.evaluate_status("Muss [182] ∧ [152]", &ctx),
858 CR::True
859 );
860 }
861
862 #[test]
863 fn test_evaluate_status_no_conditions() {
864 let eval = MockEvaluator::new();
865 let (ext, segs) = empty_context();
866 let ctx = make_ctx(&ext, &segs);
867 let expr_eval = ConditionExprEvaluator::new(&eval);
868
869 assert_eq!(expr_eval.evaluate_status("Muss", &ctx), CR::True);
870 }
871
872 #[test]
873 fn test_evaluate_status_empty() {
874 let eval = MockEvaluator::new();
875 let (ext, segs) = empty_context();
876 let ctx = make_ctx(&ext, &segs);
877 let expr_eval = ConditionExprEvaluator::new(&eval);
878
879 assert_eq!(expr_eval.evaluate_status("", &ctx), CR::True);
880 }
881
882 #[test]
885 fn test_unknown_propagation_and_or_mix() {
886 let eval = MockEvaluator::new().with_condition(2, CR::True);
890 let (ext, segs) = empty_context();
892 let ctx = make_ctx(&ext, &segs);
893 let expr_eval = ConditionExprEvaluator::new(&eval);
894
895 let expr = ConditionExpr::Or(vec![
896 ConditionExpr::Ref(1),
897 ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
898 ]);
899 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
900 }
901
902 #[test]
903 fn test_or_short_circuits_past_unknown() {
904 let eval = MockEvaluator::new().with_condition(2, CR::True);
906 let (ext, segs) = empty_context();
907 let ctx = make_ctx(&ext, &segs);
908 let expr_eval = ConditionExprEvaluator::new(&eval);
909
910 let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
911 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
912 }
913
914 #[test]
915 fn test_and_short_circuits_past_unknown() {
916 let eval = MockEvaluator::new().with_condition(2, CR::False);
918 let (ext, segs) = empty_context();
919 let ctx = make_ctx(&ext, &segs);
920 let expr_eval = ConditionExprEvaluator::new(&eval);
921
922 let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
923 assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
924 }
925
926 #[test]
929 fn test_detailed_returns_unknown_ids() {
930 let eval = MockEvaluator::new().with_condition(182, CR::True);
932 let (ext, segs) = empty_context();
933 let ctx = make_ctx(&ext, &segs);
934 let expr_eval = ConditionExprEvaluator::new(&eval);
935
936 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
937 assert_eq!(result, CR::Unknown);
938 assert_eq!(unknown_ids, vec![8]);
939 }
940
941 #[test]
942 fn test_detailed_multiple_unknown_ids() {
943 let eval = MockEvaluator::new().with_condition(2, CR::True);
945 let (ext, segs) = empty_context();
946 let ctx = make_ctx(&ext, &segs);
947 let expr_eval = ConditionExprEvaluator::new(&eval);
948
949 let (result, unknown_ids) =
950 expr_eval.evaluate_status_detailed("Muss [1] ∧ [2] ∧ [3]", &ctx);
951 assert_eq!(result, CR::Unknown);
952 assert_eq!(unknown_ids, vec![1, 3]);
953 }
954
955 #[test]
956 fn test_detailed_no_unknown_when_true() {
957 let eval = MockEvaluator::new()
958 .with_condition(182, CR::True)
959 .with_condition(152, CR::True);
960 let (ext, segs) = empty_context();
961 let ctx = make_ctx(&ext, &segs);
962 let expr_eval = ConditionExprEvaluator::new(&eval);
963
964 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [152]", &ctx);
965 assert_eq!(result, CR::True);
966 assert!(unknown_ids.is_empty());
967 }
968
969 #[test]
970 fn test_detailed_no_unknown_when_false() {
971 let eval = MockEvaluator::new().with_condition(182, CR::False);
972 let (ext, segs) = empty_context();
973 let ctx = make_ctx(&ext, &segs);
974 let expr_eval = ConditionExprEvaluator::new(&eval);
975
976 let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
977 assert_eq!(result, CR::False);
978 assert!(unknown_ids.is_empty());
979 }
980
981 fn resolve(eval: &MockEvaluator, status: &str) -> StatusResolution {
984 let (ext, segs) = empty_context();
985 let ctx = make_ctx(&ext, &segs);
986 ConditionExprEvaluator::new(eval).resolve_status(status, &ctx, &Default::default())
987 }
988
989 #[test]
990 fn test_resolve_single_line_keeps_its_status() {
991 let eval = MockEvaluator::new().with_condition(1, CR::True);
992 let r = resolve(&eval, "Soll [1]");
993 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
994 }
995
996 #[test]
997 fn test_resolve_only_later_line_holds_gives_its_status() {
998 let eval = MockEvaluator::new()
1000 .with_condition(2119, CR::False)
1001 .with_condition(130, CR::True);
1002 let r = resolve(&eval, "Muss [2119]\r\nSoll [130]");
1003 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Soll)));
1004 }
1005
1006 #[test]
1007 fn test_resolve_several_lines_hold_strictest_wins() {
1008 let eval = MockEvaluator::new()
1009 .with_condition(47, CR::True)
1010 .with_condition(46, CR::True);
1011 let r = resolve(&eval, "Soll [47]\nMuss [46]");
1012 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1013 }
1014
1015 #[test]
1016 fn test_resolve_unconditional_fallback_line() {
1017 let eval = MockEvaluator::new().with_condition(48, CR::False);
1018 let r = resolve(&eval, "Muss [48]\nKann");
1019 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Kann)));
1020 }
1021
1022 #[test]
1023 fn test_resolve_weaker_unknown_line_does_not_block() {
1024 let eval = MockEvaluator::new().with_condition(2119, CR::True);
1026 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
1027 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1028 assert!(r.unknown_ids.is_empty());
1029 }
1030
1031 #[test]
1032 fn test_resolve_stronger_unknown_line_is_unknown() {
1033 let eval = MockEvaluator::new().with_condition(130, CR::True);
1035 let r = resolve(&eval, "Muss [2119]\nSoll [130]");
1036 assert_eq!(r.result, CR::Unknown);
1037 assert_eq!(r.unknown_ids, vec![2119]);
1038 }
1039
1040 #[test]
1041 fn test_resolve_equally_strong_unknown_line_does_not_block() {
1042 let eval = MockEvaluator::new().with_condition(2, CR::True);
1043 let r = resolve(&eval, "Muss [1]\nMuss [2]");
1044 assert_eq!((r.result, r.kind), (CR::True, Some(StatusKind::Muss)));
1045 }
1046
1047 #[test]
1048 fn test_resolve_nothing_holds_but_something_unknown() {
1049 let eval = MockEvaluator::new().with_condition(1, CR::False);
1050 let r = resolve(&eval, "Muss [1]\nSoll [2]");
1051 assert_eq!(r.result, CR::Unknown);
1052 assert_eq!(r.unknown_ids, vec![2]);
1053 }
1054
1055 #[test]
1056 fn test_resolve_no_line_holds_reports_strictest_status() {
1057 let eval = MockEvaluator::new()
1058 .with_condition(1, CR::False)
1059 .with_condition(2, CR::False);
1060 let r = resolve(&eval, "Soll [1]\nMuss [2]");
1061 assert_eq!((r.result, r.kind), (CR::False, Some(StatusKind::Muss)));
1062 }
1063}