1#!/usr/bin/env python
2
3import sys
4import re
5import os
6
7sys.path.insert(0, "..")
8
9from ciscoconfparse.ccp_util import _RGX_IPV4ADDR, _RGX_IPV6ADDR
10from ciscoconfparse.ccp_util import IPv4Obj, L4Object
11from ciscoconfparse.ccp_util import CiscoRange
12from ciscoconfparse.ccp_util import IPv6Obj
13from ciscoconfparse.ccp_util import dns_lookup, reverse_dns_lookup
14from ciscoconfparse.ccp_util import collapse_addresses
15import pytest
16
17if sys.version_info[0] < 3:
18    from ipaddr import IPv4Network, IPv6Network, IPv4Address, IPv6Address
19    import ipaddr
20else:
21    from ipaddress import IPv4Network, IPv6Network, IPv4Address, IPv6Address
22    import ipaddress
23
24r""" test_Ccp_Util.py - Parse, Query, Build, and Modify IOS-style configs
25
26     Copyright (C) 2020-2021 David Michael Pennington at Cisco Systems
27     Copyright (C) 2019      David Michael Pennington at ThousandEyes
28     Copyright (C) 2014-2019 David Michael Pennington at Samsung Data Services
29
30     This program is free software: you can redistribute it and/or modify
31     it under the terms of the GNU General Public License as published by
32     the Free Software Foundation, either version 3 of the License, or
33     (at your option) any later version.
34
35     This program is distributed in the hope that it will be useful,
36     but WITHOUT ANY WARRANTY; without even the implied warranty of
37     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38     GNU General Public License for more details.
39
40     You should have received a copy of the GNU General Public License
41     along with this program.  If not, see <http://www.gnu.org/licenses/>.
42
43     If you need to contact the author, you can do so by emailing:
44     mike [~at~] pennington [/dot\] net
45"""
46
47
48
49@pytest.mark.parametrize(
50    "addr", ["192.0.2.1", "4.2.2.2", "10.255.255.255", "127.0.0.1",]
51)
52def test_IPv4_REGEX(addr):
53    test_result = _RGX_IPV4ADDR.search(addr)
54    assert test_result.group("addr") == addr
55
56
57@pytest.mark.parametrize(
58    "addr",
59    [
60        "fe80::",  # Trailing double colons
61        "fe80:beef::",  # Trailing double colons
62        "fe80:dead:beef::",  # Trailing double colons
63        "fe80:a:dead:beef::",  # Trailing double colons
64        "fe80:a:a:dead:beef::",  # Trailing double colons
65        "fe80:a:a:a:dead:beef::",  # Trailing double colons
66        "fe80:a:a:a:a:dead:beef::",  # Trailing double colons
67        "fe80:dead:beef::a",  #
68        "fe80:dead:beef::a:b",  #
69        "fe80:dead:beef::a:b:c",  #
70        "fe80:dead:beef::a:b:c:d",  #
71        "FE80:AAAA::DEAD:BEEF",  # Capital letters
72        "FE80:AAAA:0000:0000:0000:0000:DEAD:BEEF",  # Capital Letters
73        "0:0:0:0:0:0:0:1",  # Loopback
74        "::1",  # Loopback, leading double-colons
75        "::",  # Shorthand for 0:0:0:0:0:0:0:0
76    ],
77)
78def test_IPv6_REGEX(addr):
79    test_result = _RGX_IPV6ADDR.search(addr)
80    assert test_result.group("addr") == addr
81
82
83@pytest.mark.parametrize(
84    "addr",
85    [
86        "fe80:",  # Single trailing colon
87        "fe80:beef",  # Insufficient number of bytes
88        "fe80:dead:beef",  # Insufficient number of bytes
89        "fe80:a:dead:beef",  # Insufficient number of bytes
90        "fe80:a:a:dead:beef",  # Insufficient number of bytes
91        "fe80:a:a:a:dead:beef",  # Insufficient number of bytes
92        "fe80:a:a:a:a:dead:beef",  # Insufficient number of bytes
93        "fe80:a:a:a :a:dead:beef",  # Superflous space
94        "zzzz:a:a:a:a:a:dead:beef",  # bad characters
95        "0:0:0:0:0:0:1",  # Loopback, insufficient bytes
96        "0:0:0:0:0:1",  # Loopback, insufficient bytes
97        "0:0:0:0:1",  # Loopback, insufficient bytes
98        "0:0:0:1",  # Loopback, insufficient bytes
99        "0:0:1",  # Loopback, insufficient bytes
100        "0:1",  # Loopback, insufficient bytes
101        ":1",  # Loopback, insufficient bytes
102        # FIXME: The following *should* fail, but I'm not failing on them
103        #':::beef',                     # FAIL Too many leading colons
104        #'fe80::dead::beef',            # FAIL multiple double colons
105        #'::1::',                       # FAIL too many double colons
106        #'fe80:0:0:0:0:0:dead:beef::',  # FAIL Too many bytes with double colons
107    ],
108)
109def test_negative_IPv6_REGEX(addr):
110    test_result = _RGX_IPV6ADDR.search(addr)
111    assert test_result is None  # The regex *should* fail on these addrs
112
113
114def testL4Object_asa_eq01():
115    pp = L4Object(protocol="tcp", port_spec="eq smtp", syntax="asa")
116    assert pp.protocol == "tcp"
117    assert pp.port_list == [25]
118
119
120def testL4Object_asa_eq02():
121    pp = L4Object(protocol="tcp", port_spec="smtp", syntax="asa")
122    assert pp.protocol == "tcp"
123    assert pp.port_list == [25]
124
125
126def testL4Object_asa_range01():
127    pp = L4Object(protocol="tcp", port_spec="range smtp 32", syntax="asa")
128    assert pp.protocol == "tcp"
129    assert pp.port_list == sorted(range(25, 33))
130
131
132def testL4Object_asa_lt01():
133    pp = L4Object(protocol="tcp", port_spec="lt echo", syntax="asa")
134    assert pp.protocol == "tcp"
135    assert pp.port_list ==sorted(range(1, 7))
136
137def testL4Object_asa_gt01():
138    pp = L4Object(protocol="tcp", port_spec="gt 65534", syntax="asa")
139    assert pp.protocol == "tcp"
140    assert pp.port_list ==[65535]
141
142@pytest.mark.xfail(
143    sys.version_info[0] == 3 and sys.version_info[1] == 2,
144    reason="Known failure in Python3.2 due to range()",
145)
146def testL4Object_asa_lt02():
147    pp = L4Object(protocol="tcp", port_spec="lt 7", syntax="asa")
148    assert pp.protocol == "tcp"
149    assert pp.port_list == sorted(range(1, 7))
150
151
152def testIPv4Obj_contain():
153    ## Test ccp_util.IPv4Obj.__contains__()
154    ##
155    ## Test whether a prefix is or is not contained in another prefix
156    results_correct = [
157        ("1.0.0.0/8", "0.0.0.0/0", True),  # Is 1.0.0.0/8 in 0.0.0.0/0?
158        ("0.0.0.0/0", "1.0.0.0/8", False),  # Is 0.0.0.0/0 in 1.0.0.0/8?
159        ("1.1.1.0/27", "1.0.0.0/8", True),  # Is 1.1.1.0/27 in 1.0.0.0/8?
160        ("1.1.1.0/27", "9.9.9.9/32", False),  # Is 1.1.1.0/27 in 9.9.9.9/32?
161        ("9.9.9.0/27", "9.9.9.9/32", False),  # Is 9.9.9.0/27 in 9.9.9.9/32?
162    ]
163    for prefix1, prefix2, result_correct in results_correct:
164        ## 'foo in bar' tests bar.__contains__(foo)
165        test_result = IPv4Obj(prefix1) in IPv4Obj(prefix2)
166        assert test_result == result_correct
167
168
169def testIPv4Obj_parse():
170    ## Ensure that IPv4Obj can correctly parse various inputs
171    test_strings = [
172        "1.0.0.1/24",
173        "1.0.0.1/32",
174        "1.0.0.1   255.255.255.0",
175        "1.0.0.1   255.255.255.255",
176        "1.0.0.1 255.255.255.0",
177        "1.0.0.1 255.255.255.255",
178        "1.0.0.1/255.255.255.0",
179        "1.0.0.1/255.255.255.255",
180    ]
181    for test_string in test_strings:
182        test_result = IPv4Obj(test_string)
183        assert isinstance(test_result, IPv4Obj)
184
185
186def testIPv4Obj_attributes():
187    ## Ensure that attributes are accessible and pass the smell test
188    test_object = IPv4Obj("1.0.0.1 255.255.255.0")
189    results_correct = [
190        ("ip", IPv4Address("1.0.0.1")),
191        ("ip_object", IPv4Address("1.0.0.1")),
192        ("netmask", IPv4Address("255.255.255.0")),
193        ("prefixlen", 24),
194        ("broadcast", IPv4Address("1.0.0.255")),
195        ("network", IPv4Network("1.0.0.0/24")),
196        ("network_object", IPv4Network("1.0.0.0/24")),
197        ("hostmask", IPv4Address("0.0.0.255")),
198        ("numhosts", 256),
199        ("version", 4),
200        ("is_reserved", False),
201        ("is_multicast", False),
202        ("is_private", False),
203        ("as_cidr_addr", "1.0.0.1/24"),
204        ("as_cidr_net", "1.0.0.0/24"),
205        ("as_decimal", 16777217),
206        ("as_decimal_network", 16777216),
207        ("as_hex_tuple", ("01", "00", "00", "01")),
208        ("as_binary_tuple", ("00000001", "00000000", "00000000", "00000001")),
209        ("as_zeropadded", "001.000.000.001"),
210        ("as_zeropadded_network", "001.000.000.000/24"),
211    ]
212    for attribute, result_correct in results_correct:
213
214        assert getattr(test_object, attribute) == result_correct
215
216
217def testIPv6Obj_attributes():
218    ## Ensure that attributes are accessible and pass the smell test
219    test_object = IPv6Obj("2001::dead:beef/64")
220    results_correct = [
221        ("ip", IPv6Address("2001::dead:beef")),
222        ("ip_object", IPv6Address("2001::dead:beef")),
223        ("netmask", IPv6Address("ffff:ffff:ffff:ffff::")),
224        ("prefixlen", 64),
225        ("network", IPv6Network("2001::/64")),
226        ("network_object", IPv6Network("2001::/64")),
227        ("hostmask", IPv6Address("::ffff:ffff:ffff:ffff")),
228        ("numhosts", 18446744073709551616),
229        ("version", 6),
230        ("is_reserved", False),
231        ("is_multicast", False),
232        # ("is_private", False),  # FIXME: disabling this for now...
233        # py2.7 and py3.x produce different results
234        ("as_cidr_addr", "2001::dead:beef/64"),
235        ("as_cidr_net", "2001::/64"),
236        ("as_decimal", 42540488161975842760550356429036175087),
237        ("as_decimal_network", 42540488161975842760550356425300246528),
238        (
239            "as_hex_tuple",
240            ("2001", "0000", "0000", "0000", "0000", "0000", "dead", "beef"),
241        ),
242        (
243            "as_binary_tuple",
244            (
245                "0010000000000001",
246                "0000000000000000",
247                "0000000000000000",
248                "0000000000000000",
249                "0000000000000000",
250                "0000000000000000",
251                "1101111010101101",
252                "1011111011101111",
253            ),
254        ),
255    ]
256    for attribute, result_correct in results_correct:
257
258        assert getattr(test_object, attribute) == result_correct
259
260
261def testIPv4Obj_sort_01():
262    """Simple IPv4Obj sorting test"""
263    cidr_addrs_list = [
264        "192.168.1.3/32",
265        "192.168.1.2/32",
266        "192.168.1.1/32",
267        "192.168.1.4/15",
268    ]
269
270    result_correct = [
271        "192.168.1.4/15",  # Shorter prefixes are "lower" than longer prefixes
272        "192.168.1.1/32",
273        "192.168.1.2/32",
274        "192.168.1.3/32",
275    ]
276
277    obj_list = [IPv4Obj(ii) for ii in cidr_addrs_list]
278    # Ensure we get the correct sorted order for this list
279    assert [ii.as_cidr_addr for ii in sorted(obj_list)] == result_correct
280
281
282def testIPv4Obj_sort_02():
283    """Complex IPv4Obj sorting test"""
284    cidr_addrs_list = [
285        "192.168.1.1/32",
286        "192.168.0.1/32",
287        "192.168.0.2/16",
288        "192.168.0.3/15",
289        "0.0.0.0/32",
290        "0.0.0.1/31",
291        "16.0.0.1/8",
292        "0.0.0.2/30",
293        "127.0.0.0/0",
294        "16.0.0.0/1",
295        "128.0.0.0/1",
296        "16.0.0.0/4",
297        "16.0.0.3/4",
298        "0.0.0.0/0",
299        "0.0.0.0/8",
300    ]
301
302    result_correct = [
303        "0.0.0.0/0",
304        "127.0.0.0/0",
305        "16.0.0.0/1",
306        "0.0.0.0/8",  # for the same network, longer prefixlens sort "higher" than shorter prefixlens
307        "0.0.0.2/30",  # for the same network, longer prefixlens sort "higher" than shorter prefixlens
308        "0.0.0.1/31",  # for the same network, longer prefixlens sort "higher" than shorter prefixlens
309        "0.0.0.0/32",
310        "16.0.0.0/4",
311        "16.0.0.3/4",
312        "16.0.0.1/8",  # for the same network, longer prefixlens sort "higher" than shorter prefixlens
313        "128.0.0.0/1",
314        "192.168.0.3/15",
315        "192.168.0.2/16",
316        "192.168.0.1/32",
317        "192.168.1.1/32",
318    ]
319
320    obj_list = [IPv4Obj(ii) for ii in cidr_addrs_list]
321    # Ensure we get the correct sorted order for this list
322    assert [ii.as_cidr_addr for ii in sorted(obj_list)] == result_correct
323
324
325def testIPv4Obj_recursive():
326    """IPv4Obj() should be able to parse itself"""
327    obj = IPv4Obj(IPv4Obj("1.1.1.1/24"))
328    assert str(obj.ip_object) == "1.1.1.1"
329    assert obj.prefixlen == 24
330
331def testIPv4Obj_from_int():
332    assert IPv4Obj(2886729984).ip == IPv4Address('172.16.1.0')
333
334def testIPv4Obj_neq_01():
335    """Simple in-equality test fail (ref - Github issue #180)"""
336    assert IPv4Obj("1.1.1.1/24") != ""
337
338
339def testIPv4Obj_neq_02():
340    """Simple in-equality test"""
341    obj1 = IPv4Obj("1.1.1.1/24")
342    obj2 = IPv4Obj("1.1.1.2/24")
343    assert obj1 != obj2
344
345
346def testIPv4Obj_eq_01():
347    """Simple equality test"""
348    obj1 = IPv4Obj("1.1.1.1/24")
349    obj2 = IPv4Obj("1.1.1.1/24")
350    assert obj1 == obj2
351
352
353def testIPv4Obj_eq_02():
354    """Simple equality test"""
355    obj1 = IPv4Obj("1.1.1.1/24")
356    obj2 = IPv4Obj("1.1.1.0/24")
357    assert obj1 != obj2
358
359
360def testIPv4Obj_gt_01():
361    """Simple greater-than test - same network number"""
362    assert IPv4Obj("1.1.1.1/24") > IPv4Obj("1.1.1.0/24")
363
364
365def testIPv4Obj_gt_02():
366    """Simple greater-than test - different network number"""
367    assert IPv4Obj("1.1.1.0/24") > IPv4Obj("1.1.0.0/24")
368
369
370def testIPv4Obj_gt_03():
371    """Simple greater-than test - different prefixlen"""
372    assert IPv4Obj("1.1.1.0/24") > IPv4Obj("1.1.0.0/23")
373
374
375def testIPv4Obj_lt_01():
376    """Simple less-than test - same network number"""
377    obj1 = IPv4Obj("1.1.1.1/24")
378    obj2 = IPv4Obj("1.1.1.0/24")
379    assert obj2 < obj1
380
381
382def testIPv4Obj_lt_02():
383    """Simple less-than test - different network number"""
384    obj1 = IPv4Obj("1.1.1.0/24")
385    obj2 = IPv4Obj("1.1.0.0/24")
386    assert obj2 < obj1
387
388
389def testIPv4Obj_lt_03():
390    """Simple less-than test - different prefixlen"""
391    obj1 = IPv4Obj("1.1.1.0/24")
392    obj2 = IPv4Obj("1.1.0.0/23")
393    assert obj2 < obj1
394
395
396def testIPv4Obj_contains_01():
397    """Test __contains__ method"""
398    obj1 = IPv4Obj("1.1.1.0/24")
399    obj2 = IPv4Obj("1.1.0.0/23")
400    assert obj1 in obj2
401
402
403def testIPv4Obj_contains_02():
404    """Test __contains__ method"""
405    obj1 = IPv4Obj("1.1.1.1/32")
406    obj2 = IPv4Obj("1.1.1.0/24")
407    assert obj1 in obj2
408
409
410def testIPv4Obj_contains_03():
411    """Test __contains__ method"""
412    obj1 = IPv4Obj("1.1.1.255/32")
413    obj2 = IPv4Obj("1.1.1.0/24")
414    assert obj1 in obj2
415
416
417def testIPv6Obj_recursive():
418    """IPv6Obj() should be able to parse itself"""
419    obj = IPv6Obj(IPv6Obj("fe80:a:b:c:d:e::1/64"))
420    assert str(obj.ip_object) == "fe80:a:b:c:d:e:0:1"
421    assert obj.prefixlen == 64
422
423
424def testIPv6Obj_neq_01():
425    """Simple in-equality test fail (ref - Github issue #180)"""
426    assert IPv6Obj("::1") != ""
427
428
429def testIPv6Obj_neq_02():
430    """Simple in-equality test"""
431    assert IPv6Obj("::1") != IPv6Obj("::2")
432
433
434def testIPv6Obj_eq_01():
435    """Simple equality test"""
436    assert IPv6Obj("::1") == IPv6Obj("::1")
437
438
439def testIPv6Obj_gt_01():
440    """Simple greater_than test"""
441    assert IPv6Obj("::2") > IPv6Obj("::1")
442
443def test_collapse_addresses_01():
444
445    if sys.version_info >= (3, 0, 0):
446        net_collapsed = ipaddress.collapse_addresses([IPv4Network('192.0.0.0/22'), IPv4Network('192.0.2.128/25')])
447
448    else:
449        net_collapsed = ipaddr.collapse_addresses([IPv4Network('192.0.0.0/22'), IPv4Network('192.0.2.128/25')])
450
451    for idx, entry in enumerate(net_collapsed):
452        if idx==0:
453            assert entry == IPv4Network("192.0.0.0/22")
454
455def test_collapse_addresses_02():
456    net_list = [IPv4Obj('192.0.2.128/25'), IPv4Obj('192.0.0.0/26')]
457    collapsed_list = sorted(collapse_addresses(net_list))
458    assert collapsed_list[0].network_address==IPv4Obj('192.0.0.0/26').ip
459    assert collapsed_list[1].network_address==IPv4Obj('192.0.2.128/25').ip
460
461
462def test_dns_lookup():
463    # Use VMWare's opencloud A-record to test...
464    #   ref http://stackoverflow.com/a/7714208/667301
465    result_correct = {"addrs": ["127.0.0.1"], "name": "*.vcap.me", "error": ""}
466    test_result = dns_lookup("*.vcap.me")
467    if not test_result["error"]:
468        assert dns_lookup("*.vcap.me") == result_correct
469    else:
470        pytest.skip(test_result["error"])
471
472
473def test_reverse_dns_lookup():
474    result_correct = {"addr": "127.0.0.1", "name": "localhost.", "error": ""}
475    test_result = reverse_dns_lookup("127.0.0.1")
476    if not test_result["error"]:
477        assert "localhost" in test_result["name"].lower()
478    else:
479        pytest.skip(test_result["error"])
480
481
482def test_CiscoRange_01():
483    """Basic vlan range test"""
484    result_correct = ["1"]
485    assert CiscoRange("1").as_list == result_correct
486
487
488def test_CiscoRange_02():
489    """Basic vlan range test"""
490    result_correct = ["1", "3"]
491    assert CiscoRange("1,3").as_list == result_correct
492
493
494def test_CiscoRange_03():
495    """Basic vlan range test"""
496    result_correct = ["1", "2", "3", "4", "5"]
497    assert CiscoRange("1,2-4,5").as_list == result_correct
498
499
500def test_CiscoRange_03():
501    """Basic vlan range test"""
502    result_correct = ["1", "2", "3", "4", "5"]
503    assert CiscoRange("1-3,4,5").as_list == result_correct
504
505
506def test_CiscoRange_04():
507    """Basic vlan range test"""
508    result_correct = ["1", "2", "3", "4", "5"]
509    assert CiscoRange("1,2,3-5").as_list == result_correct
510
511
512def test_CiscoRange_05():
513    """Basic slot range test"""
514    result_correct = ["1/1", "1/2", "1/3", "1/4", "1/5"]
515    assert CiscoRange("1/1-3,4,5").as_list == result_correct
516
517
518def test_CiscoRange_06():
519    """Basic slot range test"""
520    result_correct = ["1/1", "1/2", "1/3", "1/4", "1/5"]
521    assert CiscoRange("1/1,2-4,5").as_list == result_correct
522
523
524def test_CiscoRange_07():
525    """Basic slot range test"""
526    result_correct = ["1/1", "1/2", "1/3", "1/4", "1/5"]
527    assert CiscoRange("1/1,2,3-5").as_list == result_correct
528
529
530def test_CiscoRange_08():
531    """Basic slot range test"""
532    result_correct = ["2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"]
533    assert CiscoRange("2/1/1-3,4,5").as_list == result_correct
534
535
536def test_CiscoRange_09():
537    """Basic slot range test"""
538    result_correct = ["2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"]
539    assert CiscoRange("2/1/1,2-4,5").as_list == result_correct
540
541
542def test_CiscoRange_10():
543    """Basic slot range test"""
544    result_correct = ["2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"]
545    assert CiscoRange("2/1/1,2,3-5").as_list == result_correct
546
547
548def test_CiscoRange_11():
549    """Basic interface slot range test"""
550    result_correct = [
551        "interface Eth2/1/1",
552        "interface Eth2/1/2",
553        "interface Eth2/1/3",
554        "interface Eth2/1/4",
555        "interface Eth2/1/5",
556    ]
557    assert CiscoRange("interface Eth2/1/1-3,4,5").as_list == result_correct
558
559
560def test_CiscoRange_12():
561    """Basic interface slot range test"""
562    result_correct = [
563        "interface Eth2/1/1",
564        "interface Eth2/1/2",
565        "interface Eth2/1/3",
566        "interface Eth2/1/4",
567        "interface Eth2/1/5",
568    ]
569    assert CiscoRange("interface Eth2/1/1,2-4,5").as_list == result_correct
570
571
572def test_CiscoRange_13():
573    """Basic interface slot range test"""
574    result_correct = [
575        "interface Eth2/1/1",
576        "interface Eth2/1/2",
577        "interface Eth2/1/3",
578        "interface Eth2/1/4",
579        "interface Eth2/1/5",
580    ]
581    assert CiscoRange("interface Eth2/1/1,2,3-5").as_list == result_correct
582
583
584def test_CiscoRange_14():
585    """Basic interface slot range test"""
586    result_correct = [
587        "interface Eth 2/1/1",
588        "interface Eth 2/1/2",
589        "interface Eth 2/1/3",
590        "interface Eth 2/1/4",
591        "interface Eth 2/1/5",
592    ]
593    assert CiscoRange("interface Eth 2/1/1,2,3-5").as_list == result_correct
594
595
596def test_CiscoRange_15():
597    """Empty range test"""
598    result_correct = []
599    assert CiscoRange("").as_list == result_correct
600
601
602def test_CiscoRange_16():
603    """Append range test"""
604    result_correct = [1, 2, 3]
605    assert CiscoRange("", result_type=int).append("1-3").as_list == result_correct
606
607def test_CiscoRange_17():
608    """Parse a string with a common prefix on all of the CiscoRange() inputs"""
609    result_correct = [
610        "Eth1/1",
611        "Eth1/10",
612        "Eth1/12-20",
613    ]
614    CiscoRange("Eth1/1,Eth1/12-20,Eth1/16,Eth1/10").as_list == result_correct
615
616def test_CiscoRange_18():
617    """Parse a string with a common prefix on all of the CiscoRange() inputs"""
618    result_correct = [
619        "interface Eth1/1",
620        "interface Eth1/10",
621        "interface Eth1/12-20",
622    ]
623    CiscoRange("interface Eth1/1,interface Eth1/12-20,interface Eth1/16,interface Eth1/10").as_list == result_correct
624
625
626def test_CiscoRange_compressed_str_01():
627    """compressed_str test"""
628    assert CiscoRange("1,2, 3, 6, 7, 8, 9, 911").compressed_str == "1-3,6-9,911"
629
630
631def test_CiscoRange_contains():
632    assert "Ethernet1/2" in CiscoRange("Ethernet1/1-20")
633