1
1
package gbn
2
2
3
3
import (
4
+ "sync"
4
5
"testing"
6
+ "time"
5
7
8
+ "github.com/lightningnetwork/lnd/lntest/wait"
6
9
"github.com/stretchr/testify/require"
7
10
)
8
11
@@ -19,3 +22,131 @@ func TestQueueSize(t *testing.T) {
19
22
q .sequenceTop = 2
20
23
require .Equal (t , uint8 (3 ), q .size ())
21
24
}
25
+
26
+ // TestQueueResend tests that the queue resend functionality works as expected.
27
+ // It specifically tests that we actually resend packets, and await the expected
28
+ // durations for cases when we resend and 1) don't receive the expected
29
+ // ACK/NACK, 2) receive the expected ACK, and 3) receive the expected NACK.
30
+ func TestQueueResend (t * testing.T ) {
31
+ t .Parallel ()
32
+
33
+ resentPackets := make (map [uint8 ]struct {})
34
+ queueTimeout := time .Second * 1
35
+
36
+ cfg := & queueCfg {
37
+ s : 5 ,
38
+ timeout : queueTimeout ,
39
+ sendPkt : func (packet * PacketData ) error {
40
+ resentPackets [packet .Seq ] = struct {}{}
41
+
42
+ return nil
43
+ },
44
+ }
45
+ q := newQueue (cfg )
46
+
47
+ pkt1 := & PacketData {Seq : 1 }
48
+ pkt2 := & PacketData {Seq : 2 }
49
+ pkt3 := & PacketData {Seq : 3 }
50
+
51
+ q .addPacket (pkt1 )
52
+
53
+ // First test that we shouldn't resend if the timeout hasn't passed.
54
+ q .lastResend = time .Now ()
55
+
56
+ err := q .resend ()
57
+ require .NoError (t , err )
58
+
59
+ require .Empty (t , resentPackets )
60
+
61
+ // Secondly, let's test that we do resend if the timeout has passed, and
62
+ // that we then start a sync once we've resent the packet.
63
+ q .lastResend = time .Now ().Add (- queueTimeout * 2 )
64
+
65
+ // Let's first test the syncing scenario where we don't receive
66
+ // the expected ACK/NACK for the resent packet. This should trigger a
67
+ // timeout of the syncing, which should be the
68
+ // queueTimeout * awaitingTimeoutMultiplier.
69
+ startTime := time .Now ()
70
+
71
+ var wg sync.WaitGroup
72
+ resend (t , q , & wg )
73
+
74
+ wg .Wait ()
75
+
76
+ // Check that the resend took at least the
77
+ // queueTimeout * awaitingTimeoutMultiplier for the syncing to
78
+ // complete, and that we actually resent the packet.
79
+ require .GreaterOrEqual (
80
+ t , time .Since (startTime ),
81
+ queueTimeout * awaitingTimeoutMultiplier ,
82
+ )
83
+ require .Contains (t , resentPackets , pkt1 .Seq )
84
+
85
+ // Now let's test the syncing scenario where we do receive the
86
+ // expected ACK for the resent packet. This should trigger a
87
+ // queue.proceedAfterTime call, which should finish the syncing
88
+ // after the queueTimeout.
89
+ q .lastResend = time .Now ().Add (- queueTimeout * 2 )
90
+
91
+ q .addPacket (pkt2 )
92
+
93
+ startTime = time .Now ()
94
+
95
+ resend (t , q , & wg )
96
+
97
+ // Simulate that we receive the expected ACK for the resent packet.
98
+ q .processACK (pkt2 .Seq )
99
+
100
+ wg .Wait ()
101
+
102
+ // Now check that the resend took at least the queueTimeout for the
103
+ // syncing to complete, and that we actually resent the packet.
104
+ require .GreaterOrEqual (t , time .Since (startTime ), queueTimeout )
105
+ require .LessOrEqual (t , time .Since (startTime ), queueTimeout * 2 )
106
+ require .Contains (t , resentPackets , pkt2 .Seq )
107
+
108
+ // Finally, let's test the syncing scenario where we do receive the
109
+ // expected NACK for the resent packet. This make the syncing
110
+ // complete immediately.
111
+ q .lastResend = time .Now ().Add (- queueTimeout * 2 )
112
+
113
+ q .addPacket (pkt3 )
114
+
115
+ startTime = time .Now ()
116
+ resend (t , q , & wg )
117
+
118
+ // Simulate that we receive the expected NACK for the resent packet.
119
+ q .processNACK (pkt3 .Seq + 1 )
120
+
121
+ wg .Wait ()
122
+
123
+ // Finally let's check that we didn't await any timeout now, and that
124
+ // we actually resent the packet.
125
+ require .Less (t , time .Since (startTime ), queueTimeout )
126
+ require .Contains (t , resentPackets , pkt3 .Seq )
127
+ }
128
+
129
+ // resend is a helper function that resends packets in a goroutine, and notifies
130
+ // the WaitGroup when the resend + syncing has completed.
131
+ // The function will block until the resend has actually started.
132
+ func resend (t * testing.T , q * queue , wg * sync.WaitGroup ) {
133
+ t .Helper ()
134
+
135
+ wg .Add (1 )
136
+
137
+ // This will trigger a sync, so we launch the resend in a
138
+ // goroutine.
139
+ go func () {
140
+ err := q .resend ()
141
+ require .NoError (t , err )
142
+
143
+ wg .Done ()
144
+ }()
145
+
146
+ // We also ensure that the above goroutine is has started the resend
147
+ // before this function returns.
148
+ err := wait .Predicate (func () bool {
149
+ return q .syncer .getState () == syncStateResending
150
+ }, time .Second )
151
+ require .NoError (t , err )
152
+ }
0 commit comments