@@ -115,3 +115,228 @@ where
115115 Self :: new ( )
116116 }
117117}
118+
119+ #[ cfg( test) ]
120+ mod tests {
121+ use super :: * ;
122+ use lpm:: prefix:: { PortRange , Prefix } ;
123+ use std:: collections:: BTreeSet ;
124+
125+ #[ derive( Debug , Clone ) ]
126+ enum TestValue {
127+ AnyPort ,
128+ Ranges ( BTreeSet < PortRange > ) ,
129+ }
130+
131+ impl ValueWithAssociatedRanges for TestValue {
132+ fn covers_all_ports ( & self ) -> bool {
133+ match self {
134+ TestValue :: AnyPort => true ,
135+ TestValue :: Ranges ( ranges) => {
136+ ranges. iter ( ) . fold ( 0 , |sum, range| sum + range. len ( ) ) == PortRange :: MAX_LENGTH
137+ }
138+ }
139+ }
140+
141+ fn covers_port ( & self , port : u16 ) -> bool {
142+ match self {
143+ TestValue :: AnyPort => true ,
144+ TestValue :: Ranges ( ranges) => ranges. iter ( ) . any ( |range| range. contains ( & port) ) ,
145+ }
146+ }
147+ }
148+
149+ #[ test]
150+ fn test_new ( ) {
151+ let trie: IpPortPrefixTrie < TestValue > = IpPortPrefixTrie :: new ( ) ;
152+ assert ! ( trie. lookup( & "192.168.1.1" . parse( ) . unwrap( ) , None ) . is_none( ) ) ;
153+ }
154+
155+ #[ test]
156+ fn test_from ( ) {
157+ let prefix = Prefix :: from ( "192.168.1.0/24" ) ;
158+ let value = TestValue :: AnyPort ;
159+ let trie = IpPortPrefixTrie :: from ( prefix, value) ;
160+
161+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , None ) ;
162+ assert ! ( result. is_some( ) ) ;
163+ let ( matched_prefix, _) = result. unwrap ( ) ;
164+ assert_eq ! ( matched_prefix, prefix) ;
165+ }
166+
167+ #[ test]
168+ fn test_insert_and_lookup_any_port ( ) {
169+ let mut trie = IpPortPrefixTrie :: new ( ) ;
170+ let prefix = Prefix :: from ( "10.0.0.0/16" ) ;
171+ let value = TestValue :: AnyPort ;
172+
173+ trie. insert ( prefix, value) ;
174+
175+ // Should match with any port
176+ let result = trie. lookup ( & "10.0.1.5" . parse ( ) . unwrap ( ) , Some ( 80 ) ) ;
177+ assert ! ( result. is_some( ) ) ;
178+ let ( matched_prefix, matched_value) = result. unwrap ( ) ;
179+ assert_eq ! ( matched_prefix, prefix) ;
180+ assert ! ( matches!( matched_value, TestValue :: AnyPort ) ) ;
181+
182+ // Should match without port
183+ let result = trie. lookup ( & "10.0.1.5" . parse ( ) . unwrap ( ) , None ) ;
184+ assert ! ( result. is_some( ) ) ;
185+ }
186+
187+ #[ test]
188+ fn test_insert_and_lookup_with_port_ranges ( ) {
189+ let mut trie = IpPortPrefixTrie :: new ( ) ;
190+ let prefix = Prefix :: from ( "172.16.0.0/12" ) ;
191+ let ranges = BTreeSet :: from ( [ PortRange :: new ( 80 , 90 ) . unwrap ( ) ] ) ;
192+ let value = TestValue :: Ranges ( ranges) ;
193+
194+ trie. insert ( prefix, value) ;
195+
196+ // Should match port in range
197+ let result = trie. lookup ( & "172.16.5.10" . parse ( ) . unwrap ( ) , Some ( 85 ) ) ;
198+ assert ! ( result. is_some( ) ) ;
199+ let ( matched_prefix, _) = result. unwrap ( ) ;
200+ assert_eq ! ( matched_prefix, prefix) ;
201+
202+ // Should not match port outside range
203+ let result = trie. lookup ( & "172.16.5.10" . parse ( ) . unwrap ( ) , Some ( 100 ) ) ;
204+ assert ! ( result. is_none( ) ) ;
205+
206+ // Should not match without port
207+ let result = trie. lookup ( & "172.16.5.10" . parse ( ) . unwrap ( ) , None ) ;
208+ assert ! ( result. is_none( ) ) ;
209+ }
210+
211+ #[ test]
212+ fn test_lookup_longest_prefix_match_no_ports ( ) {
213+ let mut trie = IpPortPrefixTrie :: new ( ) ;
214+
215+ // Insert prefix with port range
216+ let prefix_with_ports = Prefix :: from ( "192.168.0.0/24" ) ;
217+ let ranges = BTreeSet :: from ( [ PortRange :: new ( 80 , 90 ) . unwrap ( ) ] ) ;
218+ trie. insert ( prefix_with_ports, TestValue :: Ranges ( ranges) ) ;
219+
220+ // Insert prefix covering all ports
221+ let prefix_alone = Prefix :: from ( "192.168.1.0/24" ) ;
222+ trie. insert ( prefix_alone, TestValue :: AnyPort ) ;
223+
224+ // Match wihout port
225+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , None ) ;
226+ assert ! ( result. is_some( ) ) ;
227+ let ( matched_prefix, _) = result. unwrap ( ) ;
228+ assert_eq ! ( matched_prefix, prefix_alone) ;
229+
230+ // Match with a port
231+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , Some ( 443 ) ) ;
232+ assert ! ( result. is_some( ) ) ;
233+ let ( matched_prefix, _) = result. unwrap ( ) ;
234+ assert_eq ! ( matched_prefix, prefix_alone) ;
235+
236+ // Fail to match prefix_with_ports without a port
237+ let result = trie. lookup ( & "192.168.0.5" . parse ( ) . unwrap ( ) , None ) ;
238+ assert ! ( result. is_none( ) ) ;
239+
240+ // Match with a port
241+ let result = trie. lookup ( & "192.168.0.5" . parse ( ) . unwrap ( ) , Some ( 80 ) ) ;
242+ assert ! ( result. is_some( ) ) ;
243+ let ( matched_prefix, _) = result. unwrap ( ) ;
244+ assert_eq ! ( matched_prefix, prefix_with_ports) ;
245+ }
246+
247+ #[ test]
248+ fn test_lookup_longest_prefix_match_with_ports ( ) {
249+ let mut trie = IpPortPrefixTrie :: new ( ) ;
250+
251+ // Insert broader prefix
252+ let prefix_16 = Prefix :: from ( "192.168.0.0/16" ) ;
253+ let ranges = BTreeSet :: from ( [ PortRange :: new ( 80 , 90 ) . unwrap ( ) ] ) ;
254+ trie. insert ( prefix_16, TestValue :: Ranges ( ranges) ) ;
255+
256+ // Insert more specific prefix
257+ let prefix_24 = Prefix :: from ( "192.168.1.0/24" ) ;
258+ let ranges = BTreeSet :: from ( [ PortRange :: new ( 443 , 443 ) . unwrap ( ) ] ) ;
259+ trie. insert ( prefix_24, TestValue :: Ranges ( ranges) ) ;
260+
261+ // Without port, there is not match
262+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , None ) ;
263+ assert ! ( result. is_none( ) ) ;
264+
265+ // Based on port, we match the more specific prefix
266+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , Some ( 443 ) ) ;
267+ assert ! ( result. is_some( ) ) ;
268+ let ( matched_prefix, _) = result. unwrap ( ) ;
269+ assert_eq ! ( matched_prefix, prefix_24) ;
270+
271+ // Based on port, we match the broader prefix
272+ let result = trie. lookup ( & "192.168.1.5" . parse ( ) . unwrap ( ) , Some ( 80 ) ) ;
273+ assert ! ( result. is_some( ) ) ;
274+ let ( matched_prefix, _) = result. unwrap ( ) ;
275+ assert_eq ! ( matched_prefix, prefix_16) ;
276+ }
277+
278+ #[ test]
279+ fn test_get_mut ( ) {
280+ let mut trie = IpPortPrefixTrie :: new ( ) ;
281+ let prefix = Prefix :: from ( "203.0.113.0/24" ) ;
282+ let ranges = BTreeSet :: from ( [ PortRange :: new ( 8080 , 8090 ) . unwrap ( ) ] ) ;
283+ trie. insert ( prefix, TestValue :: Ranges ( ranges) ) ;
284+
285+ // Modify the value
286+ if let Some ( value) = trie. get_mut ( prefix) {
287+ * value = TestValue :: AnyPort ;
288+ }
289+
290+ // Should now match with any port
291+ let result = trie. lookup ( & "203.0.113.5" . parse ( ) . unwrap ( ) , Some ( 9999 ) ) ;
292+ assert ! ( result. is_some( ) ) ;
293+ let ( _, matched_value) = result. unwrap ( ) ;
294+ assert ! ( matches!( matched_value, TestValue :: AnyPort ) ) ;
295+ }
296+
297+ #[ test]
298+ fn test_ipv6_lookup ( ) {
299+ let mut trie = IpPortPrefixTrie :: new ( ) ;
300+ let prefix = Prefix :: from ( "2001:db8::/32" ) ;
301+ trie. insert ( prefix, TestValue :: AnyPort ) ;
302+
303+ let result = trie. lookup ( & "2001:db8::1" . parse ( ) . unwrap ( ) , None ) ;
304+ assert ! ( result. is_some( ) ) ;
305+ let ( matched_prefix, _) = result. unwrap ( ) ;
306+ assert_eq ! ( matched_prefix, prefix) ;
307+
308+ let result = trie. lookup ( & "2001:db9::1" . parse ( ) . unwrap ( ) , None ) ;
309+ assert ! ( result. is_none( ) ) ;
310+ }
311+
312+ #[ test]
313+ fn test_covers_all_ports ( ) {
314+ let any_port = TestValue :: AnyPort ;
315+ assert ! ( any_port. covers_all_ports( ) ) ;
316+
317+ let mut ranges = BTreeSet :: new ( ) ;
318+ ranges. insert ( PortRange :: new ( 0 , 32767 ) . unwrap ( ) ) ;
319+ ranges. insert ( PortRange :: new ( 32768 , 65535 ) . unwrap ( ) ) ;
320+ let full_range = TestValue :: Ranges ( ranges) ;
321+ assert ! ( full_range. covers_all_ports( ) ) ;
322+
323+ let partial_ranges = BTreeSet :: from ( [ PortRange :: new ( 80 , 443 ) . unwrap ( ) ] ) ;
324+ let partial_range = TestValue :: Ranges ( partial_ranges) ;
325+ assert ! ( !partial_range. covers_all_ports( ) ) ;
326+ }
327+
328+ #[ test]
329+ fn test_covers_port ( ) {
330+ let any_port = TestValue :: AnyPort ;
331+ assert ! ( any_port. covers_port( 80 ) ) ;
332+ assert ! ( any_port. covers_port( 65535 ) ) ;
333+
334+ let mut ranges = BTreeSet :: new ( ) ;
335+ ranges. insert ( PortRange :: new ( 80 , 80 ) . unwrap ( ) ) ;
336+ ranges. insert ( PortRange :: new ( 443 , 443 ) . unwrap ( ) ) ;
337+ let specific_ports = TestValue :: Ranges ( ranges) ;
338+ assert ! ( specific_ports. covers_port( 80 ) ) ;
339+ assert ! ( specific_ports. covers_port( 443 ) ) ;
340+ assert ! ( !specific_ports. covers_port( 8080 ) ) ;
341+ }
342+ }
0 commit comments