aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs
blob: 4f3957a68d0a72e1bfbfdd8546ab673822fb5b76 (plain)
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
use core::fmt::Debug;

use super::*;

#[derive(Debug, Clone)]
enum ReaderTransition {
    Write(usize),
    Reset,
    ReadUpTo(usize),
}

struct ReaderSM;

impl ReferenceStateMachine for ReaderSM {
    type State = Status;
    type Transition = ReaderTransition;

    fn init_state() -> BoxedStrategy<Self::State> {
        strategy::Just(Status::new(0)).boxed()
    }

    fn transitions(_state: &Self::State) -> BoxedStrategy<Self::Transition> {
        prop_oneof![
            (1..50_usize).prop_map(ReaderTransition::Write),
            (1..50_usize).prop_map(ReaderTransition::ReadUpTo),
            strategy::Just(ReaderTransition::Reset),
        ]
        .boxed()
    }

    fn apply(status: Self::State, transition: &Self::Transition) -> Self::State {
        match (status, transition) {
            (_, ReaderTransition::Reset) => Status::Available(0),
            (Status::Available(x), ReaderTransition::Write(y)) => {
                if x + y > CAP {
                    Status::Failed
                } else {
                    Status::Available(x + y)
                }
            }
            (Status::Failed, ReaderTransition::Write(_)) => Status::Failed,
            (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)),
            (Status::Failed, ReaderTransition::ReadUpTo(_)) => Status::Available(0),
        }
    }
}

struct ReaderSut {
    status: Status,
    buffer: *mut [u8],
    producer: DmaMock,
    consumer: ReadableDmaRingBuffer<'static, u8>,
}

impl Debug for ReaderSut {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        <DmaMock as Debug>::fmt(&self.producer, f)
    }
}

struct ReaderTest;

impl StateMachineTest for ReaderTest {
    type SystemUnderTest = ReaderSut;
    type Reference = ReaderSM;

    fn init_test(ref_status: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest {
        let buffer = Box::into_raw(Box::new([0; CAP]));
        ReaderSut {
            status: ref_status.clone(),
            buffer,
            producer: DmaMock::default(),
            consumer: ReadableDmaRingBuffer::new(unsafe { &mut *buffer }),
        }
    }

    fn teardown(state: Self::SystemUnderTest) {
        unsafe {
            let _ = Box::from_raw(state.buffer);
        };
    }

    fn apply(
        mut sut: Self::SystemUnderTest,
        ref_state: &<Self::Reference as ReferenceStateMachine>::State,
        transition: <Self::Reference as ReferenceStateMachine>::Transition,
    ) -> Self::SystemUnderTest {
        match transition {
            ReaderTransition::Write(x) => sut.producer.advance(x),
            ReaderTransition::Reset => {
                sut.consumer.reset(&mut sut.producer);
            }
            ReaderTransition::ReadUpTo(x) => {
                let status = sut.status;
                let ReaderSut {
                    ref mut producer,
                    ref mut consumer,
                    ..
                } = sut;
                let mut buf = vec![0; x];
                let res = consumer.read(producer, &mut buf);
                match status {
                    Status::Available(n) => {
                        let readable = x.min(n);

                        assert_eq!(res.unwrap().0, readable);
                    }
                    Status::Failed => assert!(res.is_err()),
                }
            }
        }

        ReaderSut {
            status: ref_state.clone(),
            ..sut
        }
    }
}

prop_state_machine! {
    #[test]
    fn reader_state_test(sequential 1..20 => ReaderTest);
}