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
use std::any::TypeId;

use common::protocol::{
    hint::{KnownFactory, MockHintWalker},
    value::MockValueVisitor,
};
use effectful::bound::Dynamic;
use effectful::SendSync;
use effectful::blocking::BlockingSpin;
use mockall::predicate::eq;
use treaty::{
    any::{AnyTrait, BorrowedStatic, OwnedStatic, TempBorrowedMutStatic},
    protocol::{
        visitor::{visit_value, Value, ValueKnown, VisitResult},
        walker::hint::Hint,
        DynVisitor,
    },
    Flow,
};

mod common;

/// Tests support for custom type support in the value protocol.
///
/// This is a core feature of treaty so it better work.
#[test]
fn custom_value_type() {
    // The value we want to visit in the value visitor.
    #[derive(PartialEq, Debug, Clone, SendSync)]
    struct MyValue;

    let mut mock = MockValueVisitor::<OwnedStatic<MyValue>, BlockingSpin>::new();

    // Expect the visit method to be called once with the custom type.
    mock.expect_visit()
        .times(2)
        .with(eq(OwnedStatic(MyValue)))
        .return_const(VisitResult::Control(Flow::Done));

    // Cast to a trait object for the value protocol.
    // This shows the visit method is going through the trait.
    let visitor: &mut dyn Value<OwnedStatic<MyValue>, BlockingSpin> = &mut mock;

    // Visit the value.
    let result = visitor.visit(OwnedStatic(MyValue)).into_value();

    // The mock returns that it is done.
    assert_eq!(result, VisitResult::Control(Flow::Done));

    let visitor: &mut dyn AnyTrait<BlockingSpin> = &mut mock;
    assert_eq!(
        visit_value::<_, BlockingSpin>(DynVisitor(visitor), OwnedStatic(MyValue)).into_value(),
        VisitResult::Control(Flow::Done)
    );
}

/// Tests that a value with a lifetime can be given to the value protocol.
///
/// This allows for treaty's zero copy capabilities.
/// Also this is what allows treaty to short circuit things like structs and enums if the visitor
/// supports it.
#[test]
fn borrowed_value() {
    // A value with a lifetime longer than the visitor.
    let context = String::from("test");

    // Scope that doesn't live as long as the context.
    {
        // We borrow the context, this is what we pass to the visitor.
        let value = &context;

        let mut mock = MockValueVisitor::<BorrowedStaticHrt<String>, BlockingSpin>::new();

        // Expect the visit method to be called once with the borrowed value.
        mock.expect_visit()
            .times(2)
            .withf(|BorrowedStatic(value)| *value == "test")
            .return_const(VisitResult::Control(Flow::Done));

        // Cast to a trait object for the value protocol.
        let visitor: &mut dyn Value<BorrowedStaticHrt<String>, BlockingSpin> = &mut mock;

        // Visit the borrowed value.
        assert_eq!(
            visitor.visit(BorrowedStatic(value)).into_value(),
            Flow::Done.into()
        );

        let visitor: &mut dyn AnyTrait<BlockingSpin> = &mut mock;
        assert_eq!(
            visit_value::<_, BlockingSpin>(DynVisitor(visitor), BorrowedStatic(value)).into_value(),
            VisitResult::Control(Flow::Done)
        );
    }

    // Make sure the compiler doesn't do anything funny with the lifetime.
    assert_eq!(context, "test");
}

/// Tests that a value with a temp lifetime can be given to the value protocol.
///
/// This is useful for passing borrows of things inside a walker to a visitor.
/// The visitor can't keep the borrow as the temp lifetime is shorter than the context.
///
/// If you replaced the [`TempBorrowedMutStaticHrt`] with [`BorrowedMutStaticHrt`]
/// this would fail to compile.
#[test]
fn temp_borrowed_value() {
    // A value with a lifetime longer than the visitor.
    let mut context = String::from("test");

    // Scope that doesn't live as long as the context.
    {
        // We borrow the context, this is what we pass to the visitor.
        let value = &mut context;

        let mut mock = MockValueVisitor::<TempBorrowedMutStaticHrt<String>, BlockingSpin>::new();

        // Expect the visit method to be called once with the borrowed value.
        mock.expect_visit()
            .times(2)
            .withf(|TempBorrowedMutStatic(value)| *value == "test")
            .return_const(VisitResult::Control(Flow::Done));

        // Cast to a trait object for the value protocol.
        // We definitly need this for this test so the lifetime is invariant.
        let visitor: &mut dyn Value<TempBorrowedMutStaticHrt<String>, BlockingSpin> = &mut mock;

        // Visit the context to show we can shorten the lifetime.
        // This would also force the lifetime to be to long if this wasn't the Temp form.
        assert_eq!(
            visitor.visit(TempBorrowedMutStatic(value)).into_value(),
            Flow::Done.into()
        );

        // Temporary scope (smaller than the context we set above).
        {
            // A temporary value.
            let mut value = String::from("test");

            // Visit the temp value.
            assert_eq!(
                visitor
                    .visit(TempBorrowedMutStatic(&mut value))
                    .into_value(),
                Flow::Done.into()
            );
        }

        // Force the visitor to outlive the temporary scope.
        let _ = visitor;
    }

    // Make sure the compiler doesn't do anything funny with the lifetime.
    assert_eq!(context, "test");
}

/// Tests for the control flow returns the value protocol visit can return.
#[test]
fn all_visit_results() {
    let mut mock = MockValueVisitor::<OwnedStatic<i32>, BlockingSpin>::new();

    mock.expect_visit()
        .once()
        .with(eq(OwnedStatic(0)))
        .return_const(VisitResult::Control(Flow::Done));

    mock.expect_visit()
        .once()
        .with(eq(OwnedStatic(1)))
        .return_const(VisitResult::Control(Flow::Err));

    mock.expect_visit()
        .once()
        .with(eq(OwnedStatic(2)))
        .return_const(VisitResult::Control(Flow::Continue));

    mock.expect_visit()
        .once()
        .with(eq(OwnedStatic(3)))
        .return_const(VisitResult::Skipped(()));

    let visitor: &mut dyn Value<OwnedStatic<i32>, BlockingSpin> = &mut mock;

    // Visit can return a done.
    assert_eq!(
        visitor.visit(OwnedStatic(0)).into_value(),
        VisitResult::Control(Flow::Done)
    );

    // Visit can return an error signal.
    assert_eq!(
        visitor.visit(OwnedStatic(1)).into_value(),
        VisitResult::Control(Flow::Err)
    );

    // Visit can return a continue.
    assert_eq!(
        visitor.visit(OwnedStatic(2)).into_value(),
        VisitResult::Control(Flow::Continue)
    );

    // A visit can be skipped.
    // This is for runtime visit support checking.
    // The value should be given back, but that's not forced.
    assert_eq!(
        visitor.visit(OwnedStatic(3)).into_value(),
        VisitResult::Skipped(Dynamic(OwnedStatic(3)))
    );
}

/// Tests that the higher ranked name for the value protocol exists.
#[test]
fn value_proto() {
    // The type id of the higher ranked type.
    let id = TypeId::of::<ValueProto<OwnedStatic<i32>, BlockingSpin>>();

    // The type id for the lifetime containing value protocol trait object.
    let name_id = TypeNameId::of_lower::<dyn Value<OwnedStatic<i32>, BlockingSpin>, BlockingSpin>();

    // They should be the same.
    assert_eq!(id, name_id.into_type_id());
}

/// Tests that the value protocol can be given as a hint to a walker.
///
/// The value protocol allows a hint of a preview of the value.
#[test]
fn as_hint() {
    {
        let mut mock = MockHintWalker::<ValueProto<OwnedStatic<i32>, BlockingSpin>>::new();

        mock.expect_known().once().return_const(
            (|_, _hint| {
                Ok(ValueKnown {
                    preview: Some(Dynamic(&OwnedStatic(42))),
                })
            }) as KnownFactory<ValueProto<OwnedStatic<i32>, BlockingSpin>>,
        );

        let walker: &mut dyn Hint<ValueProto<OwnedStatic<i32>, BlockingSpin>> = &mut mock;

        // The value protocol has no hint data, and it has no known data.
        assert_eq!(
            walker.known(&()).into_value(),
            Ok(ValueKnown {
                preview: Some(Dynamic(&OwnedStatic(42)))
            })
        );
    }

    {
        let mut mock = MockHintWalker::<ValueProto<BorrowedStaticHrt<i32>, BlockingSpin>>::new();

        mock.expect_known().once().return_const(
            (|_, _hint| {
                Ok(ValueKnown {
                    preview: Some(Dynamic(&BorrowedStatic(&42))),
                })
            }) as KnownFactory<ValueProto<BorrowedStaticHrt<i32>, BlockingSpin>>,
        );

        let walker: &mut dyn Hint<ValueProto<BorrowedStaticHrt<i32>, BlockingSpin>> = &mut mock;

        // The value protocol has no hint data, and it has no known data.
        assert_eq!(
            walker.known(&()).into_value(),
            Ok(ValueKnown {
                preview: Some(Dynamic(&BorrowedStatic(&42)))
            })
        );
    }

    {
        let mut mock = MockHintWalker::<ValueProto<TempBorrowedMutStaticHrt<i32>, BlockingSpin>>::new();

        mock.expect_known().once().return_const(
            (|_, _hint| Ok(ValueKnown { preview: None }))
                as KnownFactory<ValueProto<TempBorrowedMutStaticHrt<i32>, BlockingSpin>>,
        );

        let walker: &mut dyn Hint<ValueProto<TempBorrowedMutStaticHrt<i32>, BlockingSpin>> = &mut mock;

        // The value protocol has no hint data, and it has no known data.
        assert_eq!(
            walker.known(&()).into_value(),
            Ok(ValueKnown { preview: None })
        );
    }
}