STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_main.cpp
Go to the documentation of this file.
1// Host tests for arm_gate.cpp: the "ARM must be hard to trigger by accident"
2// rules. No hardware, no clock: time is passed in.
3#include <string.h>
4
5#include "unity.h"
6
7// Build the code under test.
10
11using namespace radio_link;
12
13static const uint8_t kMe = 7; // this rocket's target id
14
15static ArmGate g(kMe);
16
17void setUp(void) {
18 g = ArmGate(kMe);
19}
20void tearDown(void) {
21}
22
23static Frame arm_frame(uint16_t seq, uint32_t key, uint32_t inv,
24 uint8_t target) {
25 Frame f;
26 memset(&f, 0, sizeof f);
28 f.seq = seq;
30 put_u32(f.payload, key);
31 put_u32(f.payload + 4, inv);
32 f.payload[8] = target;
33 return f;
34}
35
36static Frame good_arm(uint16_t seq) {
37 return arm_frame(seq, kArmKey, ~kArmKey, kMe);
38}
39
40static Frame simple(FrameType type, uint16_t seq) {
41 Frame f;
42 memset(&f, 0, sizeof f);
43 f.type = type;
44 f.seq = seq;
45 return f;
46}
47
48static ArmAction feed(const Frame& f, uint32_t t) {
49 return g.on_frame(f, t);
50}
51
53 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
54}
55
57 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
58 TEST_ASSERT_EQUAL(ArmAction::Arm, feed(good_arm(2), 500));
59}
60
62 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 100));
63 TEST_ASSERT_EQUAL(ArmAction::Arm,
65}
66
68 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
69 TEST_ASSERT_EQUAL(ArmAction::Pending,
71 // ...but that late frame became a fresh first ARM, so seq 3 confirms it.
72 TEST_ASSERT_EQUAL(ArmAction::Arm,
74}
75
77 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
78 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(3), 100)); // skipped 2
79}
80
81static void test_wrong_key_rejected(void) {
82 TEST_ASSERT_EQUAL(ArmAction::RejectKey,
83 feed(arm_frame(1, kArmKey ^ 1u, ~kArmKey, kMe), 0));
84}
85
86static void test_wrong_inverse_rejected(void) {
87 // Right key but the inverse field is not ~key.
88 TEST_ASSERT_EQUAL(ArmAction::RejectKey,
89 feed(arm_frame(1, kArmKey, kArmKey, kMe), 0));
90 TEST_ASSERT_EQUAL(ArmAction::RejectKey,
91 feed(arm_frame(2, kArmKey, 0u, kMe), 0));
92}
93
94static void test_wrong_target_rejected(void) {
95 TEST_ASSERT_EQUAL(ArmAction::RejectTarget,
96 feed(arm_frame(1, kArmKey, ~kArmKey, kMe + 1), 0));
97}
98
99// A rejected frame does not advance last_seq, so a good ARM can reuse its
100// sequence number and look "consecutive" to the first ARM. Only the cancel
101// rule stops that from arming.
103 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
104 TEST_ASSERT_EQUAL(ArmAction::RejectKey,
105 feed(arm_frame(2, kArmKey ^ 1u, ~kArmKey, kMe), 10));
106 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(2), 20));
107}
108
110 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
111 TEST_ASSERT_EQUAL(ArmAction::RejectTarget,
112 feed(arm_frame(2, kArmKey, ~kArmKey, kMe + 1), 10));
113 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(2), 20));
114}
115
116// A duplicate of the first ARM is rejected as stale, and (rule 5) cancels the
117// pending ARM: seq 11 would otherwise look consecutive to the first.
119 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(10), 0));
120 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(10), 5));
121 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(11), 10));
122}
123
125 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(10), 0));
126 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(10), 5));
127}
128
130 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(10), 0));
131 TEST_ASSERT_EQUAL(ArmAction::Arm, feed(good_arm(11), 5));
132 // An attacker/echo replays the same two frames later.
133 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(10), 100));
134 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(11), 105));
135}
136
137static void test_sequence_wraps(void) {
138 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(0xFFFF), 0));
139 TEST_ASSERT_EQUAL(ArmAction::Arm, feed(good_arm(0x0000), 5));
140}
141
142static void test_time_counter_wraps(void) {
143 const uint32_t t0 = 0xFFFFFF00u; // 256 ms before the 32-bit counter wraps
144 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), t0));
145 TEST_ASSERT_EQUAL(ArmAction::Arm,
146 feed(good_arm(2), 0x00000100u)); // +512 ms
147}
148
149static void test_tick_expires_pending(void) {
150 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
151 g.tick(kArmConfirmWindowMs + 1);
152 TEST_ASSERT_FALSE(g.pending());
153}
154
155static void test_un_arm_always_honored(void) {
156 TEST_ASSERT_EQUAL(ArmAction::Disarm, feed(simple(FrameType::UnArm, 5), 0));
157}
158
160 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(50), 0));
161 TEST_ASSERT_EQUAL(ArmAction::Disarm, feed(simple(FrameType::UnArm, 1), 5));
162}
163
164// UN_ARM reusing seq 1 does not advance last_seq, so ARM seq 2 would look
165// consecutive to the first ARM. Only the cancel rule stops it arming.
167 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
168 TEST_ASSERT_EQUAL(ArmAction::Disarm, feed(simple(FrameType::UnArm, 1), 5));
169 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(2), 10));
170}
171
173 TEST_ASSERT_EQUAL(ArmAction::None,
175 TEST_ASSERT_EQUAL(ArmAction::None, feed(simple(FrameType::SelfTest, 2), 0));
176}
177
179 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(1), 0));
180 TEST_ASSERT_EQUAL(ArmAction::None,
182 TEST_ASSERT_EQUAL(ArmAction::Arm, feed(good_arm(2), 10));
183}
184
185// Pins today's behaviour when the ground station reboots and its sequence
186// counter restarts (review A1): every ARM is RejectStale until the rocket
187// reboots. Fail-safe, but confusing on launch day. The fix (ground keeps its
188// counter, or ARM carries a session number) is a decision for the lead; this
189// test must change with it. UN_ARM still works, as disarming is always safe.
191 TEST_ASSERT_EQUAL(ArmAction::Pending, feed(good_arm(100), 0));
192 TEST_ASSERT_EQUAL(ArmAction::Arm, feed(good_arm(101), 10));
193 // Ground station restarts at 1.
194 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(1), 5000));
195 TEST_ASSERT_EQUAL(ArmAction::RejectStale, feed(good_arm(2), 5010));
196 TEST_ASSERT_EQUAL(ArmAction::Disarm,
197 feed(simple(FrameType::UnArm, 3), 5020));
198}
199
long target
Definition main.cpp:38
int main()
static void test_wrong_key_rejected(void)
Definition test_main.cpp:81
static void test_un_arm_cancels_pending_arm(void)
static void test_tick_expires_pending(void)
static void test_rejected_stale_cancels_pending(void)
static void test_other_frame_types_ignored(void)
static void test_single_arm_does_not_arm(void)
Definition test_main.cpp:52
static void test_time_counter_wraps(void)
static void test_non_consecutive_sequence_does_not_arm(void)
Definition test_main.cpp:76
static Frame good_arm(uint16_t seq)
Definition test_main.cpp:36
static void test_wrong_target_rejected(void)
Definition test_main.cpp:94
static void test_two_consecutive_arms_in_window_arm(void)
Definition test_main.cpp:56
static void test_un_arm_always_honored(void)
static void test_duplicate_sequence_is_stale(void)
static void test_sequence_wraps(void)
void setUp(void)
Definition test_main.cpp:17
static Frame arm_frame(uint16_t seq, uint32_t key, uint32_t inv, uint8_t target)
Definition test_main.cpp:23
void tearDown(void)
Definition test_main.cpp:20
static ArmGate g(kMe)
static Frame simple(FrameType type, uint16_t seq)
Definition test_main.cpp:40
static void test_other_frames_do_not_cancel_pending(void)
static void test_rejected_target_cancels_pending(void)
static const uint8_t kMe
Definition test_main.cpp:13
static void test_replayed_old_arms_are_stale(void)
static void test_un_arm_honored_even_when_stale(void)
static void test_ground_reboot_rejects_arm_until_rocket_reboots(void)
static void test_wrong_inverse_rejected(void)
Definition test_main.cpp:86
static void test_confirm_exactly_at_window_edge_arms(void)
Definition test_main.cpp:61
static ArmAction feed(const Frame &f, uint32_t t)
Definition test_main.cpp:48
static void test_rejected_key_cancels_pending(void)
static void test_confirm_after_window_does_not_arm(void)
Definition test_main.cpp:67