Skip to content

Commit 0ec7954

Browse files
committed
augment tests to check the subset errors computed by the opt variant
all 3 types of errors should always be the same between the naive and opt variants
1 parent f71782e commit 0ec7954

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

src/test.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use crate::facts::{AllFacts, Loan, Origin, Point};
55
use crate::intern;
66
use crate::program::parse_from_program;
77
use crate::tab_delim;
8-
use crate::test_util::{assert_equal, location_insensitive_checker_for, naive_checker_for};
8+
use crate::test_util::{
9+
assert_checkers_match, assert_equal, assert_outputs_match, location_insensitive_checker_for,
10+
naive_checker_for, opt_checker_for,
11+
};
912
use polonius_engine::Algorithm;
1013
use rustc_hash::FxHashMap;
1114
use std::error::Error;
@@ -77,11 +80,15 @@ fn test_facts(all_facts: &AllFacts, algorithms: &[Algorithm]) {
7780
// TMP: until we reach our correctness goals, deactivate some comparisons between variants
7881
// assert_equal(&naive.loan_live_at, &opt.loan_live_at);
7982
assert_equal(&naive.errors, &opt.errors);
83+
assert_equal(&naive.subset_errors, &opt.subset_errors);
84+
assert_equal(&naive.move_errors, &opt.move_errors);
8085
}
8186

8287
// The hybrid algorithm gets the same errors as the naive version
8388
let opt = Output::compute(all_facts, Algorithm::Hybrid, true);
8489
assert_equal(&naive.errors, &opt.errors);
90+
assert_equal(&naive.subset_errors, &opt.subset_errors);
91+
assert_equal(&naive.move_errors, &opt.move_errors);
8592
}
8693

8794
fn test_fn(dir_name: &str, fn_name: &str, algorithm: Algorithm) -> Result<(), Box<dyn Error>> {
@@ -316,25 +323,30 @@ fn smoke_test_errors() {
316323
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
317324

318325
let location_insensitive = Output::compute(&facts, Algorithm::LocationInsensitive, true);
326+
let naive = Output::compute(&facts, Algorithm::Naive, true);
327+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
328+
329+
// We have to find errors with every analysis
319330
assert!(
320331
!location_insensitive.errors.is_empty(),
321332
"LocationInsensitive didn't find errors for '{}'",
322333
test_fn
323334
);
324-
325-
let naive = Output::compute(&facts, Algorithm::Naive, true);
326335
assert!(
327336
!naive.errors.is_empty(),
328337
"Naive didn't find errors for '{}'",
329338
test_fn
330339
);
331-
332-
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
333340
assert!(
334341
!opt.errors.is_empty(),
335342
"DatafrogOpt didn't find errors for '{}'",
336343
test_fn
337344
);
345+
346+
// But not subset errors...
347+
assert!(location_insensitive.subset_errors.is_empty());
348+
assert!(naive.subset_errors.is_empty());
349+
assert!(opt.subset_errors.is_empty());
338350
}
339351
}
340352

@@ -584,7 +596,10 @@ fn illegal_subset_error() {
584596

585597
// and in the location-insensitive results as well
586598
assert!(location_insensitive_checker_for(program)
587-
.location_insensitive_subset_error_exists("'b", "'a",));
599+
.location_insensitive_subset_error_exists("'b", "'a"));
600+
601+
// and finally the optimized-variant results should be the same as the naive ones
602+
assert_checkers_match(&checker, &opt_checker_for(program));
588603
}
589604

590605
/// This is the same test as the `illegal_subset_error` one, but specifies the `'b: 'a` subset
@@ -613,6 +628,7 @@ fn known_placeholder_origin_subset() {
613628
location_insensitive_checker_for(program).subset_errors_count(),
614629
0
615630
);
631+
assert_checkers_match(&checker, &opt_checker_for(program));
616632
}
617633

618634
/// This test ensures `known_subset`s are handled transitively: a known subset `'a: 'c` should be
@@ -644,6 +660,7 @@ fn transitive_known_subset() {
644660
location_insensitive_checker_for(program).subset_errors_count(),
645661
0
646662
);
663+
assert_checkers_match(&checker, &opt_checker_for(program));
647664
}
648665

649666
/// Even if `'a: 'b` is known, `'a`'s placeholder loan can flow into `'b''s supersets,
@@ -680,7 +697,10 @@ fn transitive_illegal_subset_error() {
680697
assert!(checker.subset_error_exists("'b", "'c", "\"Mid(B0[1])\""));
681698
assert!(checker.subset_error_exists("'a", "'c", "\"Mid(B0[1])\""));
682699

683-
// And similarly in the location-insensitive analysis.
700+
// The optimized analysis results should be the same as the naive one's.
701+
assert_checkers_match(&checker, &opt_checker_for(program));
702+
703+
// And the location-insensitive analysis should have the same errors, without a location.
684704
let mut checker = location_insensitive_checker_for(program);
685705
assert_eq!(checker.subset_errors_count(), 2);
686706
assert!(checker.location_insensitive_subset_error_exists("'b", "'c"));
@@ -708,6 +728,10 @@ fn successes_in_subset_relations_dataset() {
708728
let insensitive = Output::compute(&facts, Algorithm::LocationInsensitive, true);
709729
assert!(insensitive.errors.is_empty());
710730
assert!(insensitive.subset_errors.is_empty());
731+
732+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
733+
assert!(opt.errors.is_empty());
734+
assert!(opt.subset_errors.is_empty());
711735
}
712736
}
713737

@@ -756,6 +780,10 @@ fn errors_in_subset_relations_dataset() {
756780
let insensitive_subset_errors = insensitive.subset_errors.values().next().unwrap();
757781
assert_eq!(insensitive_subset_errors.len(), 1);
758782
assert!(insensitive_subset_errors.contains(&expected_subset_error));
783+
784+
// And the optimized analysis results should be the same as the naive one's.
785+
let opt = Output::compute(&facts, Algorithm::Naive, true);
786+
assert_outputs_match(&naive, &opt);
759787
}
760788

761789
// There's only a single successful test in the dataset for now, but the structure of this test
@@ -783,6 +811,11 @@ fn successes_in_move_errors_dataset() {
783811
assert!(insensitive.errors.is_empty());
784812
assert!(insensitive.subset_errors.is_empty());
785813
assert!(insensitive.move_errors.is_empty());
814+
815+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
816+
assert!(opt.errors.is_empty());
817+
assert!(opt.subset_errors.is_empty());
818+
assert!(opt.move_errors.is_empty());
786819
}
787820
}
788821

src/test_util.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ pub(crate) fn location_insensitive_checker_for(program: &str) -> FactChecker {
8383
check_program(program, Algorithm::LocationInsensitive, true)
8484
}
8585

86+
pub(crate) fn opt_checker_for(program: &str) -> FactChecker {
87+
check_program(program, Algorithm::DatafrogOpt, true)
88+
}
89+
90+
pub(crate) fn assert_checkers_match(checker_a: &FactChecker, checker_b: &FactChecker) {
91+
assert_outputs_match(&checker_a.output, &checker_b.output);
92+
}
93+
94+
pub(crate) fn assert_outputs_match(output_a: &Output<LocalFacts>, output_b: &Output<LocalFacts>) {
95+
assert_equal(&output_a.errors, &output_b.errors);
96+
assert_equal(&output_a.subset_errors, &output_b.subset_errors);
97+
assert_equal(&output_a.move_errors, &output_b.move_errors);
98+
}
99+
86100
impl FactChecker {
87101
/// Asserts that there is a `subset_error` `origin1: origin2` at the specified `point`.
88102
pub fn subset_error_exists(&mut self, origin1: &str, origin2: &str, point: &str) -> bool {
@@ -124,6 +138,10 @@ impl FactChecker {
124138
/// Note that this is different from checking `output.subset_errors.len()` as subset errors are
125139
/// grouped by the location where they are detected.
126140
pub fn subset_errors_count(&self) -> usize {
127-
self.output.subset_errors.values().map(|origins| origins.len()).sum()
141+
self.output
142+
.subset_errors
143+
.values()
144+
.map(|origins| origins.len())
145+
.sum()
128146
}
129147
}

0 commit comments

Comments
 (0)