1
//! These tests check whether the counter examples generated by the refinement
2
//! checking procedure are indeed distinguishing. They are located here to avoid
3
//! circular dependencies.
4
use std::io::Write;
5

            
6
use merc_syntax::generate_formula;
7
use merc_vpg::PG;
8
use rand::rngs::StdRng;
9

            
10
use merc_io::DumpFiles;
11
use merc_lts::mutate_lts;
12
use merc_lts::random_lts;
13
use merc_lts::write_aut;
14
use merc_utilities::Timing;
15
use merc_utilities::random_test;
16
use merc_vpg::solve_zielonka;
17
use merc_vpg::translate;
18

            
19
use merc_refinement::ExplorationStrategy;
20
use merc_refinement::RefinementType;
21
use merc_refinement::refines;
22

            
23
#[test]
24
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
25
1
fn test_random_trace_refinement() {
26
100
    random_test(100, |rng| {
27
100
        is_refinement_test(
28
100
            "test_random_trace_refinement",
29
100
            rng,
30
100
            RefinementType::Trace,
31
100
            ExplorationStrategy::BFS,
32
            false,
33
        );
34
100
    });
35
1
}
36

            
37
#[test]
38
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
39
1
fn test_random_weak_trace_refinement() {
40
100
    random_test(100, |rng| {
41
100
        is_refinement_test(
42
100
            "test_random_weak_trace_refinement",
43
100
            rng,
44
100
            RefinementType::Weaktrace,
45
100
            ExplorationStrategy::BFS,
46
            false,
47
        );
48
100
    });
49
1
}
50

            
51
#[test]
52
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
53
1
fn test_random_stable_failures_refinement() {
54
100
    random_test(100, |rng| {
55
100
        is_refinement_test(
56
100
            "test_random_stable_failures_refinement",
57
100
            rng,
58
100
            RefinementType::StableFailures,
59
100
            ExplorationStrategy::BFS,
60
            false,
61
        );
62
100
    });
63
1
}
64

            
65
#[test]
66
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
67
1
fn test_random_failures_divergences_refinement() {
68
100
    random_test(100, |rng| {
69
100
        is_refinement_test(
70
100
            "test_random_failures_divergences_refinement",
71
100
            rng,
72
100
            RefinementType::FailuresDivergences,
73
100
            ExplorationStrategy::BFS,
74
            false,
75
        );
76
100
    });
77
1
}
78

            
79
#[test]
80
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
81
1
fn test_random_impossible_futures_refinement() {
82
100
    random_test(100, |rng| {
83
100
        is_refinement_test(
84
100
            "test_random_impossible_futures_refinement",
85
100
            rng,
86
100
            RefinementType::ImpossibleFutures,
87
100
            ExplorationStrategy::BFS,
88
            false,
89
        );
90
100
    });
91
1
}
92

            
93
#[test]
94
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
95
1
fn test_random_trace_refinement_preprocessed() {
96
100
    random_test(100, |rng| {
97
100
        is_refinement_test(
98
100
            "test_random_trace_refinement",
99
100
            rng,
100
100
            RefinementType::Trace,
101
100
            ExplorationStrategy::BFS,
102
            true,
103
        );
104
100
    });
105
1
}
106

            
107
#[test]
108
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
109
1
fn test_random_weak_trace_refinement_preprocessed() {
110
100
    random_test(100, |rng| {
111
100
        is_refinement_test(
112
100
            "test_random_weak_trace_refinement_preprocessed",
113
100
            rng,
114
100
            RefinementType::Weaktrace,
115
100
            ExplorationStrategy::BFS,
116
            true,
117
        );
118
100
    });
119
1
}
120

            
121
#[test]
122
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
123
1
fn test_random_stable_failures_refinement_preprocessed() {
124
100
    random_test(100, |rng| {
125
100
        is_refinement_test(
126
100
            "test_random_stable_failures_refinement_preprocessed",
127
100
            rng,
128
100
            RefinementType::StableFailures,
129
100
            ExplorationStrategy::BFS,
130
            true,
131
        );
132
100
    });
133
1
}
134

            
135
#[test]
136
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
137
1
fn test_random_failures_divergences_refinement_preprocessed() {
138
100
    random_test(100, |rng| {
139
100
        is_refinement_test(
140
100
            "test_random_failures_divergences_refinement_preprocessed",
141
100
            rng,
142
100
            RefinementType::FailuresDivergences,
143
100
            ExplorationStrategy::BFS,
144
            true,
145
        );
146
100
    });
147
1
}
148

            
149
#[test]
150
#[cfg_attr(miri, ignore)] // Tests are too slow under miri.
151
1
fn test_random_impossible_futures_refinement_preprocessed() {
152
100
    random_test(100, |rng| {
153
100
        is_refinement_test(
154
100
            "test_random_impossible_futures_refinement_preprocessed",
155
100
            rng,
156
100
            RefinementType::ImpossibleFutures,
157
100
            ExplorationStrategy::BFS,
158
            true,
159
        );
160
100
    });
161
1
}
162

            
163
/// Helper function to define a refinement test that can be instantiated for
164
/// the various types.
165
///
166
/// # Details
167
///
168
/// Internally requests a counter example to be generated, and checks that
169
/// the counter example is indeed a valid witness for the failure of the
170
/// refinement check.
171
1000
fn is_refinement_test(
172
1000
    dump_name: &str,
173
1000
    rng: &mut StdRng,
174
1000
    refinement: RefinementType,
175
1000
    strategy: ExplorationStrategy,
176
1000
    preprocess: bool,
177
1000
) {
178
1000
    let mut files = DumpFiles::new(dump_name);
179

            
180
1000
    let spec_lts = random_lts::<String, _>(rng, 100, 3);
181
1000
    let impl_lts = mutate_lts(&spec_lts, rng, 100).unwrap();
182

            
183
1000
    files.dump("spec.aut", |w| write_aut(w, &spec_lts)).unwrap();
184
1000
    files.dump("impl.aut", |w| write_aut(w, &impl_lts)).unwrap();
185

            
186
1000
    let mut timing = Timing::default();
187
1000
    let (result, counter_example) = refines(
188
1000
        impl_lts.clone(),
189
1000
        spec_lts.clone(),
190
1000
        refinement,
191
1000
        strategy,
192
1000
        preprocess,
193
1000
        true,
194
1000
        &mut timing,
195
1000
    );
196

            
197
1000
    if !result {
198
608
        if let Some(ce) = counter_example {
199
608
            let formula = generate_formula(&ce);
200
608
            println!("Counter example formula: {}", formula);
201

            
202
608
            files
203
608
                .dump("counter_example.mcf", |f| Ok(writeln!(f, "{}", formula)?))
204
608
                .unwrap();
205

            
206
608
            let impl_pg = translate(&impl_lts, &formula).unwrap();
207
608
            let spec_pg = translate(&spec_lts, &formula).unwrap();
208

            
209
608
            let (impl_solution, _) = solve_zielonka(&impl_pg, false);
210
608
            let (spec_solution, _) = solve_zielonka(&spec_pg, false);
211

            
212
608
            assert!(
213
608
                impl_solution[impl_pg.initial_vertex()] != spec_solution[spec_pg.initial_vertex()],
214
                "Refinement returned false, but the counter example is not distinguishing."
215
            );
216
        } else {
217
            panic!("Expected a counter example.");
218
        }
219
392
    }
220
1000
}