@@ -1858,6 +1858,568 @@ def _on_receive_sabm(frame):
1858
1858
assert frames == [frame ]
1859
1859
1860
1860
1861
+ # RR Notification transmission, scheduling and cancellation
1862
+
1863
+
1864
+ def test_cancel_rr_notification_notpending ():
1865
+ """
1866
+ Test _cancel_rr_notification does nothing if not pending.
1867
+ """
1868
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1869
+ peer = TestingAX25Peer (
1870
+ station = station ,
1871
+ address = AX25Address ("VK4MSL" ),
1872
+ repeaters = AX25Path (),
1873
+ )
1874
+
1875
+ assert peer ._rr_notification_timeout_handle is None
1876
+
1877
+ peer ._cancel_rr_notification ()
1878
+
1879
+ assert peer ._rr_notification_timeout_handle is None
1880
+
1881
+
1882
+ def test_cancel_rr_notification_ispending ():
1883
+ """
1884
+ Test _cancel_rr_notification cancels a pending notification.
1885
+ """
1886
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1887
+ peer = TestingAX25Peer (
1888
+ station = station ,
1889
+ address = AX25Address ("VK4MSL" ),
1890
+ repeaters = AX25Path (),
1891
+ )
1892
+
1893
+ timeout = DummyTimeout (0 , lambda : None )
1894
+ peer ._rr_notification_timeout_handle = timeout
1895
+
1896
+ peer ._cancel_rr_notification ()
1897
+
1898
+ assert peer ._rr_notification_timeout_handle is None
1899
+ assert timeout .cancelled is True
1900
+
1901
+
1902
+ def test_schedule_rr_notification ():
1903
+ """
1904
+ Test _schedule_rr_notification schedules a notification.
1905
+ """
1906
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1907
+ peer = TestingAX25Peer (
1908
+ station = station ,
1909
+ address = AX25Address ("VK4MSL" ),
1910
+ repeaters = AX25Path (),
1911
+ )
1912
+
1913
+ peer ._schedule_rr_notification ()
1914
+
1915
+ assert peer ._rr_notification_timeout_handle is not None
1916
+
1917
+
1918
+ def test_send_rr_notification_connected ():
1919
+ """
1920
+ Test _send_rr_notification sends a notification if connected.
1921
+ """
1922
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1923
+ peer = TestingAX25Peer (
1924
+ station = station ,
1925
+ address = AX25Address ("VK4MSL" ),
1926
+ repeaters = AX25Path (),
1927
+ )
1928
+
1929
+ peer ._init_connection (False )
1930
+
1931
+ count = dict (update_recv_seq = 0 )
1932
+
1933
+ def _update_recv_seq ():
1934
+ count ["update_recv_seq" ] += 1
1935
+
1936
+ peer ._update_recv_seq = _update_recv_seq
1937
+
1938
+ transmitted = []
1939
+
1940
+ def _transmit_frame (frame ):
1941
+ transmitted .append (frame )
1942
+
1943
+ peer ._transmit_frame = _transmit_frame
1944
+
1945
+ peer ._state = AX25PeerState .CONNECTED
1946
+
1947
+ peer ._send_rr_notification ()
1948
+
1949
+ assert count == dict (update_recv_seq = 1 )
1950
+ assert len (transmitted ) == 1
1951
+ assert isinstance (transmitted [0 ], AX258BitReceiveReadyFrame )
1952
+
1953
+
1954
+ def test_send_rr_notification_disconnected ():
1955
+ """
1956
+ Test _send_rr_notification sends a notification if connected.
1957
+ """
1958
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1959
+ peer = TestingAX25Peer (
1960
+ station = station ,
1961
+ address = AX25Address ("VK4MSL" ),
1962
+ repeaters = AX25Path (),
1963
+ )
1964
+
1965
+ peer ._init_connection (False )
1966
+
1967
+ count = dict (update_recv_seq = 0 )
1968
+
1969
+ def _update_recv_seq ():
1970
+ count ["update_recv_seq" ] += 1
1971
+
1972
+ peer ._update_recv_seq = _update_recv_seq
1973
+
1974
+ transmitted = []
1975
+
1976
+ def _transmit_frame (frame ):
1977
+ transmitted .append (frame )
1978
+
1979
+ peer ._transmit_frame = _transmit_frame
1980
+
1981
+ peer ._state = AX25PeerState .DISCONNECTED
1982
+
1983
+ peer ._send_rr_notification ()
1984
+
1985
+ assert count == dict (update_recv_seq = 0 )
1986
+ assert len (transmitted ) == 0
1987
+
1988
+
1989
+ # RNR transmission
1990
+
1991
+
1992
+ def test_send_rnr_notification_connected ():
1993
+ """
1994
+ Test _send_rnr_notification sends a notification if connected.
1995
+ """
1996
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
1997
+ peer = TestingAX25Peer (
1998
+ station = station ,
1999
+ address = AX25Address ("VK4MSL" ),
2000
+ repeaters = AX25Path (),
2001
+ )
2002
+
2003
+ peer ._init_connection (False )
2004
+
2005
+ count = dict (update_recv_seq = 0 )
2006
+
2007
+ def _update_recv_seq ():
2008
+ count ["update_recv_seq" ] += 1
2009
+
2010
+ peer ._update_recv_seq = _update_recv_seq
2011
+
2012
+ transmitted = []
2013
+
2014
+ def _transmit_frame (frame ):
2015
+ transmitted .append (frame )
2016
+
2017
+ peer ._transmit_frame = _transmit_frame
2018
+
2019
+ peer ._state = AX25PeerState .CONNECTED
2020
+
2021
+ peer ._send_rnr_notification ()
2022
+
2023
+ assert count == dict (update_recv_seq = 1 )
2024
+ assert len (transmitted ) == 1
2025
+ assert isinstance (transmitted [0 ], AX258BitReceiveNotReadyFrame )
2026
+
2027
+
2028
+ def test_send_rnr_notification_connected_recent ():
2029
+ """
2030
+ Test _send_rnr_notification skips notification if the last was recent.
2031
+ """
2032
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2033
+ peer = TestingAX25Peer (
2034
+ station = station ,
2035
+ address = AX25Address ("VK4MSL" ),
2036
+ repeaters = AX25Path (),
2037
+ )
2038
+
2039
+ peer ._init_connection (False )
2040
+
2041
+ count = dict (update_recv_seq = 0 )
2042
+
2043
+ def _update_recv_seq ():
2044
+ count ["update_recv_seq" ] += 1
2045
+
2046
+ peer ._update_recv_seq = _update_recv_seq
2047
+
2048
+ transmitted = []
2049
+
2050
+ def _transmit_frame (frame ):
2051
+ transmitted .append (frame )
2052
+
2053
+ peer ._transmit_frame = _transmit_frame
2054
+
2055
+ peer ._state = AX25PeerState .CONNECTED
2056
+ peer ._last_rnr_sent = peer ._loop .time () - (peer ._rnr_interval / 2 )
2057
+
2058
+ peer ._send_rnr_notification ()
2059
+
2060
+ assert count == dict (update_recv_seq = 0 )
2061
+ assert len (transmitted ) == 0
2062
+
2063
+
2064
+ def test_send_rnr_notification_disconnected ():
2065
+ """
2066
+ Test _send_rnr_notification sends a notification if connected.
2067
+ """
2068
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2069
+ peer = TestingAX25Peer (
2070
+ station = station ,
2071
+ address = AX25Address ("VK4MSL" ),
2072
+ repeaters = AX25Path (),
2073
+ )
2074
+
2075
+ peer ._init_connection (False )
2076
+
2077
+ count = dict (update_recv_seq = 0 )
2078
+
2079
+ def _update_recv_seq ():
2080
+ count ["update_recv_seq" ] += 1
2081
+
2082
+ peer ._update_recv_seq = _update_recv_seq
2083
+
2084
+ transmitted = []
2085
+
2086
+ def _transmit_frame (frame ):
2087
+ transmitted .append (frame )
2088
+
2089
+ peer ._transmit_frame = _transmit_frame
2090
+
2091
+ peer ._state = AX25PeerState .DISCONNECTED
2092
+
2093
+ peer ._send_rnr_notification ()
2094
+
2095
+ assert count == dict (update_recv_seq = 0 )
2096
+ assert len (transmitted ) == 0
2097
+
2098
+
2099
+ # I-Frame transmission
2100
+
2101
+
2102
+ def test_send_next_iframe_max_outstanding ():
2103
+ """
2104
+ Test I-frame transmission is suppressed if too many frames are pending.
2105
+ """
2106
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2107
+ peer = TestingAX25Peer (
2108
+ station = station ,
2109
+ address = AX25Address ("VK4MSL" ),
2110
+ repeaters = AX25Path (),
2111
+ )
2112
+
2113
+ peer ._init_connection (False )
2114
+
2115
+ count = dict (update_send_seq = 0 , update_recv_seq = 0 )
2116
+
2117
+ def _update_recv_seq ():
2118
+ count ["update_recv_seq" ] += 1
2119
+
2120
+ peer ._update_recv_seq = _update_recv_seq
2121
+
2122
+ def _update_send_seq ():
2123
+ count ["update_send_seq" ] += 1
2124
+
2125
+ peer ._update_send_seq = _update_send_seq
2126
+
2127
+ transmitted = []
2128
+
2129
+ def _transmit_frame (frame ):
2130
+ transmitted .append (frame )
2131
+
2132
+ peer ._transmit_frame = _transmit_frame
2133
+
2134
+ state_updates = []
2135
+
2136
+ def _update_state (** kwargs ):
2137
+ state_updates .append (kwargs )
2138
+
2139
+ peer ._update_state = _update_state
2140
+
2141
+ peer ._state = AX25PeerState .CONNECTED
2142
+ peer ._pending_iframes = {
2143
+ 0 : (0xF0 , b"Frame 1" ),
2144
+ 1 : (0xF0 , b"Frame 2" ),
2145
+ 2 : (0xF0 , b"Frame 3" ),
2146
+ 3 : (0xF0 , b"Frame 4" ),
2147
+ 4 : (0xF0 , b"Frame 5" ),
2148
+ 5 : (0xF0 , b"Frame 6" ),
2149
+ 6 : (0xF0 , b"Frame 7" ),
2150
+ 7 : (0xF0 , b"Frame 8" ),
2151
+ }
2152
+ peer ._max_outstanding = 8
2153
+
2154
+ peer ._send_next_iframe ()
2155
+
2156
+ assert count == dict (update_send_seq = 0 , update_recv_seq = 0 )
2157
+ assert state_updates == []
2158
+ assert transmitted == []
2159
+
2160
+
2161
+ def test_send_next_iframe_nothing_pending ():
2162
+ """
2163
+ Test I-frame transmission is suppressed no data is pending.
2164
+ """
2165
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2166
+ peer = TestingAX25Peer (
2167
+ station = station ,
2168
+ address = AX25Address ("VK4MSL" ),
2169
+ repeaters = AX25Path (),
2170
+ )
2171
+
2172
+ peer ._init_connection (False )
2173
+
2174
+ count = dict (update_send_seq = 0 , update_recv_seq = 0 )
2175
+
2176
+ def _update_recv_seq ():
2177
+ count ["update_recv_seq" ] += 1
2178
+
2179
+ peer ._update_recv_seq = _update_recv_seq
2180
+
2181
+ def _update_send_seq ():
2182
+ count ["update_send_seq" ] += 1
2183
+
2184
+ peer ._update_send_seq = _update_send_seq
2185
+
2186
+ transmitted = []
2187
+
2188
+ def _transmit_frame (frame ):
2189
+ transmitted .append (frame )
2190
+
2191
+ peer ._transmit_frame = _transmit_frame
2192
+
2193
+ state_updates = []
2194
+
2195
+ def _update_state (** kwargs ):
2196
+ state_updates .append (kwargs )
2197
+
2198
+ peer ._update_state = _update_state
2199
+
2200
+ peer ._state = AX25PeerState .CONNECTED
2201
+ peer ._pending_iframes = {
2202
+ 0 : (0xF0 , b"Frame 1" ),
2203
+ 1 : (0xF0 , b"Frame 2" ),
2204
+ 2 : (0xF0 , b"Frame 3" ),
2205
+ 3 : (0xF0 , b"Frame 4" ),
2206
+ }
2207
+ peer ._max_outstanding = 8
2208
+ peer ._send_state = 4
2209
+
2210
+ peer ._send_next_iframe ()
2211
+
2212
+ assert count == dict (update_send_seq = 0 , update_recv_seq = 0 )
2213
+ assert state_updates == []
2214
+ assert transmitted == []
2215
+
2216
+
2217
+ def test_send_next_iframe_create_next ():
2218
+ """
2219
+ Test I-frame transmission creates a new I-frame if there's data to send.
2220
+ """
2221
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2222
+ peer = TestingAX25Peer (
2223
+ station = station ,
2224
+ address = AX25Address ("VK4MSL" ),
2225
+ repeaters = AX25Path (),
2226
+ )
2227
+
2228
+ peer ._init_connection (False )
2229
+
2230
+ count = dict (update_send_seq = 0 , update_recv_seq = 0 )
2231
+
2232
+ def _update_recv_seq ():
2233
+ count ["update_recv_seq" ] += 1
2234
+
2235
+ peer ._update_recv_seq = _update_recv_seq
2236
+
2237
+ def _update_send_seq ():
2238
+ count ["update_send_seq" ] += 1
2239
+
2240
+ peer ._update_send_seq = _update_send_seq
2241
+
2242
+ transmitted = []
2243
+
2244
+ def _transmit_frame (frame ):
2245
+ transmitted .append (frame )
2246
+
2247
+ peer ._transmit_frame = _transmit_frame
2248
+
2249
+ state_updates = []
2250
+
2251
+ def _update_state (prop , ** kwargs ):
2252
+ kwargs ["prop" ] = prop
2253
+ state_updates .append (kwargs )
2254
+
2255
+ peer ._update_state = _update_state
2256
+
2257
+ peer ._state = AX25PeerState .CONNECTED
2258
+ peer ._pending_iframes = {
2259
+ 0 : (0xF0 , b"Frame 1" ),
2260
+ 1 : (0xF0 , b"Frame 2" ),
2261
+ 2 : (0xF0 , b"Frame 3" ),
2262
+ 3 : (0xF0 , b"Frame 4" ),
2263
+ }
2264
+ peer ._pending_data = [
2265
+ (0xF0 , b"Frame 5" ),
2266
+ ]
2267
+ peer ._max_outstanding = 8
2268
+ peer ._send_state = 4
2269
+
2270
+ peer ._send_next_iframe ()
2271
+
2272
+ assert peer ._pending_iframes == {
2273
+ 0 : (0xF0 , b"Frame 1" ),
2274
+ 1 : (0xF0 , b"Frame 2" ),
2275
+ 2 : (0xF0 , b"Frame 3" ),
2276
+ 3 : (0xF0 , b"Frame 4" ),
2277
+ 4 : (0xF0 , b"Frame 5" ),
2278
+ }
2279
+ assert peer ._pending_data == []
2280
+ assert count == dict (update_send_seq = 1 , update_recv_seq = 1 )
2281
+ assert state_updates == [
2282
+ dict (prop = "_send_state" , delta = 1 , comment = "send next I-frame" )
2283
+ ]
2284
+ assert transmitted [1 :] == []
2285
+ frame = transmitted .pop (0 )
2286
+ assert isinstance (frame , AX258BitInformationFrame )
2287
+ assert frame .payload == b"Frame 5"
2288
+
2289
+
2290
+ def test_send_next_iframe_existing_next ():
2291
+ """
2292
+ Test I-frame transmission sends existing next frame.
2293
+ """
2294
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2295
+ peer = TestingAX25Peer (
2296
+ station = station ,
2297
+ address = AX25Address ("VK4MSL" ),
2298
+ repeaters = AX25Path (),
2299
+ )
2300
+
2301
+ peer ._init_connection (False )
2302
+
2303
+ count = dict (update_send_seq = 0 , update_recv_seq = 0 )
2304
+
2305
+ def _update_recv_seq ():
2306
+ count ["update_recv_seq" ] += 1
2307
+
2308
+ peer ._update_recv_seq = _update_recv_seq
2309
+
2310
+ def _update_send_seq ():
2311
+ count ["update_send_seq" ] += 1
2312
+
2313
+ peer ._update_send_seq = _update_send_seq
2314
+
2315
+ transmitted = []
2316
+
2317
+ def _transmit_frame (frame ):
2318
+ transmitted .append (frame )
2319
+
2320
+ peer ._transmit_frame = _transmit_frame
2321
+
2322
+ state_updates = []
2323
+
2324
+ def _update_state (prop , ** kwargs ):
2325
+ kwargs ["prop" ] = prop
2326
+ state_updates .append (kwargs )
2327
+
2328
+ peer ._update_state = _update_state
2329
+
2330
+ peer ._state = AX25PeerState .CONNECTED
2331
+ peer ._pending_iframes = {
2332
+ 0 : (0xF0 , b"Frame 1" ),
2333
+ 1 : (0xF0 , b"Frame 2" ),
2334
+ 2 : (0xF0 , b"Frame 3" ),
2335
+ 3 : (0xF0 , b"Frame 4" ),
2336
+ }
2337
+ peer ._pending_data = [
2338
+ (0xF0 , b"Frame 5" ),
2339
+ ]
2340
+ peer ._max_outstanding = 8
2341
+ peer ._send_state = 3
2342
+
2343
+ peer ._send_next_iframe ()
2344
+
2345
+ assert peer ._pending_iframes == {
2346
+ 0 : (0xF0 , b"Frame 1" ),
2347
+ 1 : (0xF0 , b"Frame 2" ),
2348
+ 2 : (0xF0 , b"Frame 3" ),
2349
+ 3 : (0xF0 , b"Frame 4" ),
2350
+ }
2351
+ assert peer ._pending_data == [
2352
+ (0xF0 , b"Frame 5" ),
2353
+ ]
2354
+ assert count == dict (update_send_seq = 1 , update_recv_seq = 1 )
2355
+ assert state_updates == [
2356
+ dict (prop = "_send_state" , delta = 1 , comment = "send next I-frame" )
2357
+ ]
2358
+ assert transmitted [1 :] == []
2359
+ frame = transmitted .pop (0 )
2360
+ assert isinstance (frame , AX258BitInformationFrame )
2361
+ assert frame .payload == b"Frame 4"
2362
+
2363
+
2364
+ # Sequence number state updates
2365
+
2366
+
2367
+ def test_update_send_seq ():
2368
+ """
2369
+ Test _update_send_seq copies V(S) to N(S).
2370
+ """
2371
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2372
+ peer = TestingAX25Peer (
2373
+ station = station ,
2374
+ address = AX25Address ("VK4MSL" ),
2375
+ repeaters = AX25Path (),
2376
+ )
2377
+
2378
+ state_updates = []
2379
+
2380
+ def _update_state (prop , ** kwargs ):
2381
+ kwargs ["prop" ] = prop
2382
+ state_updates .append (kwargs )
2383
+
2384
+ peer ._update_state = _update_state
2385
+
2386
+ peer ._send_seq = 2
2387
+ peer ._send_state = 6
2388
+
2389
+ peer ._update_send_seq ()
2390
+ assert state_updates == [
2391
+ dict (prop = "_send_seq" , value = 6 , comment = "from V(S)" )
2392
+ ]
2393
+
2394
+
2395
+ def test_update_recv_seq ():
2396
+ """
2397
+ Test _update_recv_seq copies V(R) to N(R).
2398
+ """
2399
+ station = DummyStation (AX25Address ("VK4MSL" , ssid = 1 ))
2400
+ peer = TestingAX25Peer (
2401
+ station = station ,
2402
+ address = AX25Address ("VK4MSL" ),
2403
+ repeaters = AX25Path (),
2404
+ )
2405
+
2406
+ state_updates = []
2407
+
2408
+ def _update_state (prop , ** kwargs ):
2409
+ kwargs ["prop" ] = prop
2410
+ state_updates .append (kwargs )
2411
+
2412
+ peer ._update_state = _update_state
2413
+
2414
+ peer ._recv_state = 6
2415
+ peer ._recv_seq = 2
2416
+
2417
+ peer ._update_recv_seq ()
2418
+ assert state_updates == [
2419
+ dict (prop = "_recv_seq" , value = 6 , comment = "from V(R)" )
2420
+ ]
2421
+
2422
+
1861
2423
# SABM(E) handling
1862
2424
1863
2425
0 commit comments