asupersync 0.3.4

Spec-first, cancel-correct, capability-secure async runtime for Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
//! Metamorphic tests for region table lifecycle operations.
//!
//! Tests critical idempotence properties to ensure that repeated close
//! operations on regions are safe and don't panic.

use super::region_table::RegionTable;
use crate::record::region::RegionState;
use crate::types::{Budget, Time};
use proptest::prelude::*;

/// Generate valid region budgets for property-based testing
fn arb_budget() -> impl Strategy<Value = Budget> {
    (1u64..=3600, 1u32..=10000, 1u32..=1000, 1u8..=255).prop_map(|(secs, polls, cost, prio)| {
        Budget::new()
            .with_deadline(Time::from_secs(secs))
            .with_poll_quota(polls)
            .with_cost_quota(cost.into())
            .with_priority(prio)
    })
}

/// Generate sequences of close operations for testing idempotence
fn arb_close_sequence() -> impl Strategy<Value = Vec<&'static str>> {
    prop::collection::vec(
        prop::sample::select(&[
            "begin_close",
            "begin_drain",
            "begin_finalize",
            "complete_close",
        ]),
        1..=10,
    )
}

// ============================================================================
// MR1: Close Operation Idempotence (Score: 5.0)
// Category: Equivalence - f(close(x)) = f(x) after first close
// ============================================================================

/// **MR1: Close Idempotence**
/// Calling any close operation multiple times should be safe and not panic.
/// First call may succeed (true) or fail (false), subsequent calls should
/// consistently return false for already-closed regions.
#[test]
fn mr_close_operation_idempotence() {
    proptest!(|(budget in arb_budget())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        // Primary close operation
        let result1 = region.begin_close(None);
        prop_assert!(result1, "First close should transition Open -> Closing");

        // MR: Second close should not panic and return false (already closing/closed)
        let result2 = region.begin_close(None);
        prop_assert!(!result2, "Second close should return false (idempotent)");

        // MR: Third close should also be safe
        let result3 = region.begin_close(None);
        prop_assert!(!result3, "Third close should return false (idempotent)");

        // Additional idempotence: other close operations should also be safe
        let drain_result = region.begin_drain();
        let finalize_result = region.begin_finalize();
        let complete_result = region.complete_close();

        prop_assert!(drain_result, "Closing region should transition into Draining");
        prop_assert!(finalize_result, "Draining region should transition into Finalizing");
        prop_assert!(complete_result, "Finalizing empty region should transition into Closed");
        prop_assert_eq!(
            table.state(region_id),
            Some(RegionState::Closed),
            "Complete idempotence chain should close an empty region"
        );
    });
}

// ============================================================================
// MR2: State Transition Monotonicity (Score: 4.0)
// Category: Inclusive - transitions preserve ordering
// ============================================================================

/// **MR2: State Monotonicity**
/// Region state transitions follow strict ordering: Open → Closing → Draining → Finalizing → Closed
/// No transition should move backwards in this ordering.
#[test]
fn mr_state_transition_monotonicity() {
    proptest!(|(budget in arb_budget(), ops in arb_close_sequence())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        let mut prev_state = table.state(region_id).unwrap();

        // Apply sequence of close operations
        for op in ops {
            let current_state = table.state(region_id).unwrap();

            // MR: State should never go backwards in the progression
            prop_assert!(state_ordering(current_state) >= state_ordering(prev_state),
                "State regression detected: {:?} -> {:?}", prev_state, current_state);

            // Apply the operation
            match op {
                "begin_close" => { region.begin_close(None); },
                "begin_drain" => { region.begin_drain(); },
                "begin_finalize" => { region.begin_finalize(); },
                "complete_close" => { region.complete_close(); },
                _ => unreachable!(),
            }

            prev_state = current_state;
        }
    });
}

/// Helper: Map region states to ordering values for monotonicity checking
fn state_ordering(state: RegionState) -> u8 {
    match state {
        RegionState::Open => 0,
        RegionState::Closing => 1,
        RegionState::Draining => 2,
        RegionState::Finalizing => 3,
        RegionState::Closed => 4,
    }
}

fn state_sequence_is_monotonic(states: &[RegionState]) -> bool {
    states
        .windows(2)
        .all(|pair| state_ordering(pair[1]) >= state_ordering(pair[0]))
}

// ============================================================================
// MR3: Return Value Consistency (Score: 3.0)
// Category: Equivalence - same inputs produce same outputs
// ============================================================================

/// **MR3: Return Value Consistency**
/// Repeated calls to the same close operation should return consistent values.
/// First successful call returns true, subsequent calls return false.
#[test]
fn mr_return_value_consistency() {
    proptest!(|(budget in arb_budget())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        // Test begin_close consistency
        let close1 = region.begin_close(None);
        let close2 = region.begin_close(None);
        let close3 = region.begin_close(None);

        // MR: After first call, subsequent calls should consistently return false
        if close1 {
            prop_assert!(!close2 && !close3, "Subsequent closes should return false after successful first close");
        } else {
            prop_assert!(!close2 && !close3, "All closes should return false if first failed");
        }

        // MR: Same property holds for other operations
        let drain1 = region.begin_drain();
        let drain2 = region.begin_drain();
        prop_assert!(drain1 >= drain2, "Second drain call should not be more successful than first");

        let fin1 = region.begin_finalize();
        let fin2 = region.begin_finalize();
        prop_assert!(fin1 >= fin2, "Second finalize call should not be more successful than first");
    });
}

// ============================================================================
// MR4: State Preservation Under Failed Transitions (Score: 3.0)
// Category: Equivalence - failed operations don't change state
// ============================================================================

/// **MR4: State Preservation**
/// Failed transition attempts should not change the region state.
#[test]
fn mr_state_preservation_on_failure() {
    proptest!(|(budget in arb_budget())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        // Start with open state
        let _initial_state = table.state(region_id).unwrap();

        // Try operations that should fail on open region
        let state_before = table.state(region_id).unwrap();
        let drain_result = region.begin_drain(); // Should fail - not in Closing state
        let state_after = table.state(region_id).unwrap();

        // MR: Failed transition preserves state
        prop_assert_eq!(state_before, state_after,
            "Failed begin_drain should not change state from {:?}", state_before);
        prop_assert!(!drain_result, "begin_drain should fail on Open region");

        // Similar test for complete_close on non-Finalizing region
        let state_before = table.state(region_id).unwrap();
        let complete_result = region.complete_close(); // Should fail - not in Finalizing state
        let state_after = table.state(region_id).unwrap();

        prop_assert_eq!(state_before, state_after,
            "Failed complete_close should not change state from {:?}", state_before);
        prop_assert!(!complete_result, "complete_close should fail on non-Finalizing region");
    });
}

// ============================================================================
// MR5: Full Close Chain Idempotence (Score: 5.0)
// Category: Invertive - applying full sequence multiple times is safe
// ============================================================================

/// **MR5: Full Close Chain Idempotence**
/// Applying the complete close sequence multiple times should be safe.
/// open(R) → close_sequence(R) → close_sequence(R) should not panic.
#[test]
fn mr_full_close_chain_idempotence() {
    proptest!(|(budget in arb_budget())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        // Define a complete close sequence
        let close_sequence = || {
            region.begin_close(None);
            region.begin_finalize(); // Skip drain for simplicity
            region.complete_close();
        };

        // MR: First application of close sequence
        close_sequence();
        let _state_after_first = table.state(region_id);

        // MR: Second application should be safe (idempotent)
        close_sequence();
        let state_after_second = table.state(region_id);

        // MR: Third application should also be safe
        close_sequence();
        let state_after_third = table.state(region_id);

        // Key property: No panics occurred (test completes successfully)
        // State should stabilize after first complete sequence
        prop_assert_eq!(state_after_second, state_after_third,
            "State should stabilize after complete close sequence");

        // Additional verification: final state should be Closed or Finalizing
        if let Some(final_state) = state_after_third {
            prop_assert!(matches!(final_state, RegionState::Closed | RegionState::Finalizing),
                "Final state should be Closed or Finalizing, got {:?}", final_state);
        }
    });
}

// ============================================================================
// MR6: Cross-Operation Safety (Score: 4.0)
// Category: Permutative - different operation orders should be safe
// ============================================================================

/// **MR6: Cross-Operation Safety**
/// Different orderings of close operations should all be safe (not panic).
/// This tests that the implementation is robust against various call patterns.
#[test]
fn mr_cross_operation_safety() {
    proptest!(|(budget in arb_budget(),
                ops1 in arb_close_sequence(),
                ops2 in arb_close_sequence())| {

        // Test with two different operation sequences on identical regions
        let mut table1 = RegionTable::new();
        let region1_id = table1.create_root(budget, Time::ZERO);
        let region1 = table1.get(region1_id.arena_index()).unwrap();

        let mut table2 = RegionTable::new();
        let region2_id = table2.create_root(budget, Time::ZERO);
        let region2 = table2.get(region2_id.arena_index()).unwrap();

        // Apply different operation sequences and retain the observed state path.
        let states1 = apply_operation_sequence(region1, &ops1);
        let states2 = apply_operation_sequence(region2, &ops2);

        // MR: Final states should be valid (no corruption)
        let state1 = table1.state(region1_id);
        let state2 = table2.state(region2_id);

        prop_assert!(state1.is_some(), "Region 1 should have valid state");
        prop_assert!(state2.is_some(), "Region 2 should have valid state");
        prop_assert!(
            state_sequence_is_monotonic(&states1),
            "Region 1 state path should never regress: {:?}",
            states1
        );
        prop_assert!(
            state_sequence_is_monotonic(&states2),
            "Region 2 state path should never regress: {:?}",
            states2
        );
    });
}

/// Helper to apply a sequence of operations to a region
fn apply_operation_sequence(
    region: &crate::record::RegionRecord,
    ops: &[&str],
) -> Vec<RegionState> {
    let mut states = Vec::with_capacity(ops.len() + 1);
    states.push(region.state());

    for op in ops {
        match *op {
            "begin_close" => {
                region.begin_close(None);
            }
            "begin_drain" => {
                region.begin_drain();
            }
            "begin_finalize" => {
                region.begin_finalize();
            }
            "complete_close" => {
                region.complete_close();
            }
            _ => {}
        }
        states.push(region.state());
    }

    states
}

// ============================================================================
// MR Composition: Compound Properties (Multiplicative Power)
// ============================================================================

/// **Compound MR: Idempotence + Monotonicity**
/// Combines MR1 and MR2 - repeated operations should be both safe AND monotonic.
#[test]
fn mr_compound_idempotence_and_monotonicity() {
    proptest!(|(budget in arb_budget())| {
        let mut table = RegionTable::new();
        let region_id = table.create_root(budget, Time::ZERO);
        let region = table.get(region_id.arena_index()).unwrap();

        let mut states = vec![table.state(region_id).unwrap()];

        // Apply multiple close operations
        for _ in 0..5 {
            region.begin_close(None);
            states.push(table.state(region_id).unwrap());

            region.begin_finalize();
            states.push(table.state(region_id).unwrap());

            region.complete_close();
            states.push(table.state(region_id).unwrap());
        }

        // MR1 (Idempotence): Test completed without panic
        // MR2 (Monotonicity): States should be non-decreasing
        for i in 1..states.len() {
            prop_assert!(state_ordering(states[i]) >= state_ordering(states[i-1]),
                "State ordering violation at step {}: {:?} -> {:?}",
                i, states[i-1], states[i]);
        }

        // Combined property: Final state should be stable
        let final_states = &states[states.len()-3..];
        prop_assert!(final_states.iter().all(|&s| s == final_states[0]),
            "Final states should be stable: {:?}", final_states);
    });
}

// ============================================================================
// Mutation Testing: Validate MR Effectiveness
// ============================================================================

#[cfg(test)]
mod mutation_tests {

    /// Test that our MRs would catch a panic-on-double-close bug
    #[test]
    fn mr_suite_catches_panic_mutation() {
        // Simulated mutation: panic on second close
        fn mutated_close_operation(call_count: &mut u32) -> bool {
            *call_count += 1;
            assert!(*call_count <= 1, "Double close not allowed!");
            true
        }

        let mut call_count = 0;

        // Our MRs should catch this via panic detection
        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            mutated_close_operation(&mut call_count); // First call - OK
            mutated_close_operation(&mut call_count); // Second call - PANIC!
        }));

        assert!(result.is_err(), "MR suite should detect panic mutation");
    }

    /// Test that our MRs would catch a state-corruption bug
    #[test]
    fn mr_suite_catches_state_corruption_mutation() {
        #[derive(Debug, Clone, Copy, PartialEq)]
        enum MockState {
            Open,
            Closing,
        }

        // Simulated mutation: corrupt state on repeated operations
        fn mutated_state_transition(state: &mut MockState) -> bool {
            match *state {
                MockState::Open => {
                    *state = MockState::Closing;
                    true
                }
                MockState::Closing => {
                    *state = MockState::Open;
                    false
                } // INTENTIONAL BUG: goes backward (for metamorphic testing)!
            }
        }

        let mut state = MockState::Open;

        mutated_state_transition(&mut state); // Open -> Closing
        assert_eq!(state, MockState::Closing);

        mutated_state_transition(&mut state); // Closing -> Open (BUG!)
        assert_eq!(state, MockState::Open); // This violates monotonicity MR!

        // Our monotonicity MR would catch this regression
    }
}

#[cfg(test)]
mod mr_validation {

    /// Verify that all our MRs have sufficient fault sensitivity
    #[test]
    fn validate_mr_fault_sensitivity() {
        // Each MR should detect its target bug class

        // MR1 (Idempotence) catches: panic on repeated calls
        // MR2 (Monotonicity) catches: state regression bugs
        // MR3 (Consistency) catches: inconsistent return values
        // MR4 (Preservation) catches: state corruption on failed ops
        // MR5 (Chain) catches: panic in complex sequences
        // MR6 (Safety) catches: order-dependent panics

        fn mutated_close_operation(call_count: &mut u32) -> bool {
            *call_count += 1;
            assert!(*call_count <= 1, "Double close not allowed!");
            true
        }

        let mut call_count = 0;
        let panic_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            mutated_close_operation(&mut call_count);
            mutated_close_operation(&mut call_count);
        }));
        assert!(
            panic_result.is_err(),
            "MR1/MR5 should detect panic-on-repeat mutations"
        );

        let regressing_path = [
            super::RegionState::Open,
            super::RegionState::Closing,
            super::RegionState::Open,
        ];
        assert!(
            !super::state_sequence_is_monotonic(&regressing_path),
            "MR2/MR6 should reject state-regression mutations"
        );
    }

    /// Verify MR independence - different bug classes detected
    #[test]
    fn validate_mr_independence() {
        // MR1: Panic detection (runtime safety)
        // MR2: Logic correctness (state ordering)
        // MR3: Interface consistency (return values)
        // MR4: Isolation (failed ops don't corrupt)
        // MR5: Sequence safety (complex interactions)
        // MR6: Order independence (commutativity aspects)

        let target_bug_classes = [
            ("MR1", "panic_on_repeat"),
            ("MR2", "state_regression"),
            ("MR3", "return_inconsistency"),
            ("MR4", "failed_transition_mutation"),
            ("MR5", "sequence_instability"),
            ("MR6", "order_dependent_state_regression"),
        ];
        let unique_classes = target_bug_classes
            .iter()
            .map(|(_, class)| *class)
            .collect::<std::collections::BTreeSet<_>>();

        assert_eq!(
            unique_classes.len(),
            target_bug_classes.len(),
            "Each MR should target a distinct primary bug class"
        );
        assert!(
            unique_classes.contains("state_regression")
                && unique_classes.contains("failed_transition_mutation")
                && unique_classes.contains("order_dependent_state_regression"),
            "MR suite should cover logic, isolation, and ordering failures"
        );
    }
}