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
//! The explicit keys that replaced `BindingEpoch`'s derived order keep the
//! order the derive gave, wherever an ordered collection used it. Each golden
//! is written by hand from the derived-order rule: the variant in declaration
//! order, then the fields in declaration order, with a binding epoch as its
//! connection, then its generation. Each line says why it follows the one
//! before.
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use liminal_protocol::wire::{BindingEpoch, ConnectionIncarnation, Generation};
use super::super::fate_occurrence::FateOccurrenceKey;
use super::ObserverProgressOccurrence;
use crate::server::participant::dispatch_impact::DispatchTarget;
fn epoch(server: u64, ordinal: u64, generation: u64) -> Result<BindingEpoch, Box<dyn Error>> {
Ok(BindingEpoch::new(
ConnectionIncarnation::new(server, ordinal),
Generation::new(generation).ok_or("a generation is never zero")?,
))
}
#[test]
fn observer_progress_occurrences_keep_the_derived_order() -> Result<(), Box<dyn Error>> {
use ObserverProgressOccurrence::{
AttachedTerminal, DetachedTerminal, DiedTerminal, Leave, MarkerAck, ParticipantAck,
RecoveredFate,
};
let golden = vec![
// the first variant
AttachedTerminal {
conversation_id: 1,
participant_id: 1,
terminal_delivery_seq: 9,
},
// the same variant and conversation, a higher participant
AttachedTerminal {
conversation_id: 1,
participant_id: 2,
terminal_delivery_seq: 1,
},
// the second variant comes after every first
DetachedTerminal {
conversation_id: 1,
participant_id: 1,
terminal_delivery_seq: 5,
},
// the third variant
DiedTerminal {
conversation_id: 1,
participant_id: 1,
terminal_delivery_seq: 5,
},
// the fourth variant
ParticipantAck {
conversation_id: 1,
participant_id: 1,
through_seq: 4,
source_sequence: 9,
},
// through_seq is declared before source_sequence, so it decides first
ParticipantAck {
conversation_id: 1,
participant_id: 1,
through_seq: 5,
source_sequence: 1,
},
// the fifth variant
MarkerAck {
conversation_id: 1,
participant_id: 1,
marker_delivery_seq: 3,
resulting_cursor: 9,
base_log_head: 9,
extension_sequence: 9,
},
// marker_delivery_seq is declared first, so it decides first
MarkerAck {
conversation_id: 1,
participant_id: 1,
marker_delivery_seq: 4,
resulting_cursor: 1,
base_log_head: 1,
extension_sequence: 1,
},
// the sixth variant
Leave {
conversation_id: 1,
participant_id: 1,
left_delivery_seq: 7,
},
// the seventh variant
RecoveredFate {
conversation_id: 1,
participant_id: 1,
binding_epoch: epoch(1, 1, 5)?,
resulting_floor: 9,
},
// the same epoch at a higher floor
RecoveredFate {
conversation_id: 1,
participant_id: 1,
binding_epoch: epoch(1, 1, 5)?,
resulting_floor: 10,
},
// binding_epoch is declared before resulting_floor: the same connection
// at a higher generation comes after, though its floor is lower
RecoveredFate {
conversation_id: 1,
participant_id: 1,
binding_epoch: epoch(1, 1, 6)?,
resulting_floor: 1,
},
// a later connection comes after, though its generation and floor are lower
RecoveredFate {
conversation_id: 1,
participant_id: 1,
binding_epoch: epoch(1, 2, 2)?,
resulting_floor: 0,
},
// a higher conversation comes after every lower one in the same variant
RecoveredFate {
conversation_id: 2,
participant_id: 0,
binding_epoch: epoch(0, 0, 1)?,
resulting_floor: 0,
},
];
let held: BTreeSet<ObserverProgressOccurrence> = golden.iter().rev().copied().collect();
assert_eq!(held.into_iter().collect::<Vec<_>>(), golden);
Ok(())
}
#[test]
fn dispatch_targets_keep_the_derived_order() -> Result<(), Box<dyn Error>> {
let golden = vec![
// the lowest participant, connection and generation
DispatchTarget::new(1, epoch(1, 1, 5)?),
// the same connection at a higher generation
DispatchTarget::new(1, epoch(1, 1, 6)?),
// a later connection ordinal comes after, though its generation is lower
DispatchTarget::new(1, epoch(1, 2, 2)?),
// a later server incarnation comes after any ordinal of an earlier one
DispatchTarget::new(1, epoch(2, 0, 1)?),
// a higher participant comes after every epoch of a lower one
DispatchTarget::new(2, epoch(0, 0, 1)?),
];
let held: BTreeSet<DispatchTarget> = golden.iter().rev().copied().collect();
assert_eq!(held.into_iter().collect::<Vec<_>>(), golden);
Ok(())
}
#[test]
fn fate_occurrence_keys_keep_the_derived_order() -> Result<(), Box<dyn Error>> {
let key = |conversation_id: u64,
participant_id: u64,
binding_epoch: BindingEpoch|
-> FateOccurrenceKey {
FateOccurrenceKey {
conversation_id,
participant_id,
binding_epoch,
}
};
let golden = vec![
// the lowest conversation, participant, connection and generation
key(1, 1, epoch(1, 1, 5)?),
// the same connection at a higher generation
key(1, 1, epoch(1, 1, 6)?),
// a later connection comes after, though its generation is lower
key(1, 1, epoch(1, 2, 2)?),
// a higher participant comes after every epoch of a lower one
key(1, 2, epoch(0, 0, 1)?),
// a higher conversation comes after every participant of a lower one
key(2, 0, epoch(0, 0, 1)?),
];
let held: BTreeMap<FateOccurrenceKey, usize> = golden
.iter()
.rev()
.copied()
.enumerate()
.map(|(at, key)| (key, at))
.collect();
assert_eq!(held.into_keys().collect::<Vec<_>>(), golden);
Ok(())
}