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