3 # Copyright (c) 2021 by VMware, Inc. ("VMware")
4 # Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
5 # ("NetDEF") in this file.
7 # Permission to use, copy, modify, and/or distribute this software
8 # for any purpose with or without fee is hereby granted, provided
9 # that the above copyright notice and this permission notice appear
12 # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
13 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
15 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
16 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 """OSPF Basic Functionality Automation."""
29 # Save the Current Working Directory to find configuration files.
30 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
31 sys
.path
.append(os
.path
.join(CWD
, "../"))
32 sys
.path
.append(os
.path
.join(CWD
, "../lib/"))
34 # pylint: disable=C0413
35 # Import topogen and topotest helpers
36 from lib
.topogen
import Topogen
, get_topogen
39 # Import topoJson from lib, to create topology and initial configuration
40 from lib
.common_config
import (
44 reset_config_on_routers
,
47 shutdown_bringup_interface
,
48 create_interfaces_cfg
,
49 get_frr_ipv6_linklocal
,
54 from lib
.topolog
import logger
55 from lib
.topojson
import build_config_from_json
56 from lib
.bgp
import create_router_bgp
, verify_bgp_convergence
57 from lib
.ospf
import (
58 verify_ospf6_neighbor
,
63 config_ospf6_interface
,
64 verify_ospf6_interface
,
68 pytestmark
= [pytest
.mark
.ospfd
, pytest
.mark
.staticd
]
82 "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
85 Please view in a fixed-width font such as Courier.
87 +R1 +------------+R2 |
96 +R0 +-------------+R3 |
101 1. OSPF Cost - verifying ospf interface cost functionality
105 def setup_module(mod
):
107 Sets up the pytest environment
111 testsuite_run_time
= time
.asctime(time
.localtime(time
.time()))
112 logger
.info("Testsuite start time: {}".format(testsuite_run_time
))
113 logger
.info("=" * 40)
115 logger
.info("Running setup_module to create topology")
117 # This function initiates the topology build with Topogen...
118 json_file
= "{}/ospfv3_rte_calc.json".format(CWD
)
119 tgen
= Topogen(json_file
, mod
.__name
__)
121 topo
= tgen
.json_topo
122 # ... and here it calls Mininet initialization functions.
124 # Starting topology, create tmp files which are loaded to routers
125 # to start daemons and then start routers
128 # Creating configuration from JSON
129 build_config_from_json(tgen
, topo
)
131 # Don't run this test if we have any failure.
132 if tgen
.routers_have_failure():
133 pytest
.skip(tgen
.errors
)
135 ospf_covergence
= verify_ospf6_neighbor(tgen
, topo
)
136 assert ospf_covergence
is True, "setup_module :Failed \n Error:" " {}".format(
140 logger
.info("Running setup_module() done")
143 def teardown_module(mod
):
145 Teardown the pytest environment.
150 logger
.info("Running teardown_module to delete topology")
154 # Stop toplogy and Remove tmp files
158 "Testsuite end time: {}".format(time
.asctime(time
.localtime(time
.time())))
160 logger
.info("=" * 40)
163 def get_llip(onrouter
, intf
):
165 API to get the link local ipv6 address of a particular interface
169 * `fromnode`: Source node
170 * `tonode` : interface for which link local ip needs to be returned.
174 result = get_llip('r1', 'r2-link0')
178 1) link local ipv6 address from the interface.
179 2) errormsg - when link local ip not found.
182 intf
= topo
["routers"][onrouter
]["links"][intf
]["interface"]
183 llip
= get_frr_ipv6_linklocal(tgen
, onrouter
, intf
)
185 logger
.info("llip ipv6 address to be set as NH is %s", llip
)
190 def get_glipv6(onrouter
, intf
):
192 API to get the global ipv6 address of a particular interface
196 * `onrouter`: Source node
197 * `intf` : interface for which link local ip needs to be returned.
201 result = get_glipv6('r1', 'r2-link0')
205 1) global ipv6 address from the interface.
206 2) errormsg - when link local ip not found.
208 glipv6
= (topo
["routers"][onrouter
]["links"][intf
]["ipv6"]).split("/")[0]
210 logger
.info("Global ipv6 address to be set as NH is %s", glipv6
)
215 def red_static(dut
, config
=True):
216 """Local def for Redstribute static routes inside ospf."""
220 ospf_red
= {dut
: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
225 "redistribute": [{"redist_type": "static", "del_action": True}]
229 result
= create_router_ospf(tgen
, topo
, ospf_red
)
230 assert result
is True, "Testcase : Failed \n Error: {}".format(result
)
233 def red_connected(dut
, config
=True):
234 """Local def for Redstribute connected routes inside ospf."""
238 ospf_red
= {dut
: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
243 "redistribute": [{"redist_type": "connected", "del_action": True}]
247 result
= create_router_ospf(tgen
, topo
, ospf_red
)
248 assert result
is True, "Testcase: Failed \n Error: {}".format(result
)
251 # ##################################
252 # Test cases start here.
253 # ##################################
256 def test_ospfv3_redistribution_tc5_p0(request
):
257 """Test OSPF intra area route calculations."""
258 tc_name
= request
.node
.name
259 write_test_header(tc_name
)
262 # Don't run this test if we have any failure.
263 if tgen
.routers_have_failure():
264 check_router_status(tgen
)
267 step("Bring up the base config.")
268 reset_config_on_routers(tgen
)
270 step("Verify that OSPF neighbors are FULL.")
271 ospf_covergence
= verify_ospf6_neighbor(tgen
, topo
)
272 assert ospf_covergence
is True, "setup_module :Failed \n Error:" " {}".format(
276 step("verify intra area route is calculated for r0-r3 interface ip in R1")
277 ip
= topo
["routers"]["r0"]["links"]["r3"]["ipv6"]
278 ip_net
= str(ipaddress
.ip_interface(u
"{}".format(ip
)).network
)
280 llip
= get_llip("r0", "r1")
281 assert llip
is not None, "Testcase {} : Failed \n Error: {}".format(tc_name
, llip
)
287 {"network": ip_net
, "no_of_ip": 1, "routeType": "Network"}
293 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
)
294 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
297 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
298 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
300 step("Delete the ip address on newly configured loopback of R0")
305 "ipv6": topo
["routers"]["r0"]["links"]["r3"]["ipv6"],
306 "interface": topo
["routers"]["r0"]["links"]["r3"]["interface"],
313 result
= create_interfaces_cfg(tgen
, topo1
)
314 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
317 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
, expected
=False)
320 ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name
, result
)
324 tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
, expected
=False
328 ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name
, result
)
330 step("Add back the deleted ip address on newly configured interface of R0")
335 "ipv6": topo
["routers"]["r0"]["links"]["r3"]["ipv6"],
336 "interface": topo
["routers"]["r0"]["links"]["r3"]["interface"],
342 result
= create_interfaces_cfg(tgen
, topo1
)
343 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
346 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
)
347 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
350 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
351 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
353 step("Shut no shut interface on R0")
355 intf
= topo
["routers"]["r0"]["links"]["r3"]["interface"]
356 shutdown_bringup_interface(tgen
, dut
, intf
, False)
358 step("un shut the OSPF interface on R0")
360 shutdown_bringup_interface(tgen
, dut
, intf
, True)
363 result
= verify_ospf6_rib(tgen
, dut
, input_dict
)
364 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
367 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
368 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
370 write_test_footer(tc_name
)
373 def test_ospfv3_redistribution_tc6_p0(request
):
374 """Test OSPF inter area route calculations."""
375 tc_name
= request
.node
.name
376 write_test_header(tc_name
)
379 # Don't run this test if we have any failure.
380 if tgen
.routers_have_failure():
381 check_router_status(tgen
)
384 step("Bring up the base config.")
385 reset_config_on_routers(tgen
)
387 step("Verify that OSPF neighbors are FULL.")
388 ospf_covergence
= verify_ospf6_neighbor(tgen
, topo
)
389 assert ospf_covergence
is True, "Testcase {} : Failed \n Error: {}".format(
390 tc_name
, ospf_covergence
393 step("verify intra area route is calculated for r0-r3 interface ip in R1")
394 ip
= topo
["routers"]["r0"]["links"]["r3"]["ipv6"]
395 ip_net
= str(ipaddress
.ip_interface(u
"{}".format(ip
)).network
)
396 llip
= get_llip("r0", "r1")
397 assert llip
is not None, "Testcase {} : Failed \n Error: {}".format(tc_name
, llip
)
402 {"network": ip_net
, "no_of_ip": 1, "routeType": "Network"}
408 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
)
409 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
412 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
413 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
415 step("Delete the ip address on newly configured loopback of R0")
420 "ipv6": topo
["routers"]["r0"]["links"]["r3"]["ipv6"],
421 "interface": topo
["routers"]["r0"]["links"]["r3"]["interface"],
428 result
= create_interfaces_cfg(tgen
, topo1
)
429 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
432 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
, expected
=False)
435 ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name
, result
)
439 tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
, expected
=False
443 ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name
, result
)
445 step("Add back the deleted ip address on newly configured interface of R0")
450 "ipv6": topo
["routers"]["r0"]["links"]["r3"]["ipv6"],
451 "interface": topo
["routers"]["r0"]["links"]["r3"]["interface"],
457 result
= create_interfaces_cfg(tgen
, topo1
)
458 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
461 result
= verify_ospf6_rib(tgen
, dut
, input_dict
, next_hop
=nh
)
462 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
465 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
466 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
468 step("Shut no shut interface on R0")
470 intf
= topo
["routers"]["r0"]["links"]["r3"]["interface"]
471 shutdown_bringup_interface(tgen
, dut
, intf
, False)
473 step("un shut the OSPF interface on R0")
475 shutdown_bringup_interface(tgen
, dut
, intf
, True)
478 result
= verify_ospf6_rib(tgen
, dut
, input_dict
)
479 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
482 result
= verify_rib(tgen
, "ipv6", dut
, input_dict
, protocol
=protocol
, next_hop
=nh
)
483 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
485 write_test_footer(tc_name
)
488 def test_ospfv3_redistribution_tc8_p1(request
):
490 Test OSPF redistribution of connected routes.
492 Verify OSPF redistribution of connected routes when bgp multi hop
493 neighbor is configured using ospf routes
496 tc_name
= request
.node
.name
497 write_test_header(tc_name
)
500 step("Bring up the base config.")
502 "Configure loopback interface on all routers, and redistribut"
503 "e connected routes into ospf"
505 if tgen
.routers_have_failure():
506 check_router_status(tgen
)
507 reset_config_on_routers(tgen
)
510 "verify that connected routes -loopback is found in all routers"
511 "advertised/exchaged via ospf"
513 for rtr
in topo
["routers"]:
517 for node
in topo
["routers"]:
522 "network": topo
["routers"][node
]["links"]["lo"]["ipv6"],
528 for rtr
in topo
["routers"]:
529 result
= verify_rib(tgen
, "ipv6", rtr
, input_dict
)
530 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
534 step("Configure E BGP multi hop using the loopback addresses.")
536 for node
in topo
["routers"]:
538 topo
["routers"][node
].update(
542 "address_family": {"ipv6": {"unicast": {"neighbor": {}}}},
546 for node
in topo
["routers"]:
547 for rtr
in topo
["routers"]:
549 topo
["routers"][node
]["bgp"]["address_family"]["ipv6"]["unicast"][
555 "lo": {"source_link": "lo", "ebgp_multihop": 2}
561 result
= create_router_bgp(tgen
, topo
, topo
["routers"])
562 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
566 "r0": {"bgp": {"router_id": "11.11.11.11"}},
567 "r1": {"bgp": {"router_id": "22.22.22.22"}},
568 "r2": {"bgp": {"router_id": "33.33.33.33"}},
569 "r3": {"bgp": {"router_id": "44.44.44.44"}},
571 result
= create_router_bgp(tgen
, topo
, input_dict
)
572 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
574 step("Verify that BGP neighbor is ESTABLISHED")
575 result
= verify_bgp_convergence(tgen
, topo
)
576 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
578 "Configure couple of static routes in R0 and "
579 "Redistribute static routes in R1 bgp."
582 for rtr
in topo
["routers"]:
585 "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
588 result
= create_router_ospf(tgen
, topo
, ospf_red
)
589 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
597 "network": NETWORK
["ipv6"][0],
604 result
= create_static_routes(tgen
, input_dict
)
605 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
607 configure_bgp_on_r0
= {
611 "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
616 result
= create_router_bgp(tgen
, topo
, configure_bgp_on_r0
)
617 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
619 for rtr
in ["r1", "r2", "r3"]:
620 result
= verify_rib(tgen
, "ipv6", rtr
, input_dict
, protocol
=protocol
)
621 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
625 step("Clear ospf neighbours in R0")
626 for rtr
in topo
["routers"]:
627 clear_ospf(tgen
, rtr
)
629 step("Verify that OSPF neighbours are reset and forms new adjacencies.")
630 # Api call verify whether OSPF is converged
631 ospf_covergence
= verify_ospf6_neighbor(tgen
, topo
)
632 assert ospf_covergence
is True, "setup_module :Failed \n Error:" " {}".format(
636 step("Verify that BGP neighbours are reset and forms new adjacencies.")
637 result
= verify_bgp_convergence(tgen
, topo
)
638 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
641 for rtr
in ["r1", "r2", "r3"]:
642 result
= verify_rib(tgen
, "ipv6", rtr
, input_dict
, protocol
=protocol
)
643 assert result
is True, "Testcase {} : Failed \n Error: {}".format(
647 write_test_footer(tc_name
)
650 def test_ospfv3_cost_tc52_p0(request
):
651 """OSPF Cost - verifying ospf interface cost functionality"""
652 tc_name
= request
.node
.name
653 write_test_header(tc_name
)
656 step("Bring up the base config.")
657 if tgen
.routers_have_failure():
658 check_router_status(tgen
)
659 reset_config_on_routers(tgen
)
662 "Configure ospf cost as 20 on interface between R0 and R1. "
663 "Configure ospf cost as 30 between interface between R0 and R2."
667 "r0": {"links": {"r1": {"ospf6": {"cost": 20}}, "r2": {"ospf6": {"cost": 30}}}}
669 result
= config_ospf6_interface(tgen
, topo
, r0_ospf_cost
)
670 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
673 "Verify that cost is updated in the ospf interface between"
674 " r0 and r1 as 30 and r0 and r2 as 20"
677 result
= verify_ospf6_interface(tgen
, topo
, dut
=dut
, input_dict
=r0_ospf_cost
)
678 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
681 "Swap the costs between interfaces on r0, between r0 and r1 to 30"
686 "r0": {"links": {"r1": {"ospf6": {"cost": 30}}, "r2": {"ospf6": {"cost": 20}}}}
688 result
= config_ospf6_interface(tgen
, topo
, r0_ospf_cost
)
689 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
692 "Verify that cost is updated in the ospf interface between r0 "
693 "and r1 as 30 and r0 and r2 as 20."
695 result
= verify_ospf6_interface(tgen
, topo
, dut
=dut
, input_dict
=r0_ospf_cost
)
696 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
698 step(" Un configure cost from the interface r0 - r1.")
701 "r0": {"links": {"r1": {"ospf6": {"cost": 30, "del_action": True}}}}
703 result
= config_ospf6_interface(tgen
, topo
, r0_ospf_cost
)
704 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
707 "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 20}}}}
710 "Verify that cost is updated in the ospf interface between r0"
711 " and r1 as 10 and r0 and r2 as 20."
714 result
= verify_ospf6_interface(tgen
, topo
, dut
=dut
, input_dict
=input_dict
)
715 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
717 step(" Un configure cost from the interface r0 - r2.")
720 "r0": {"links": {"r2": {"ospf6": {"cost": 20, "del_action": True}}}}
722 result
= config_ospf6_interface(tgen
, topo
, r0_ospf_cost
)
723 assert result
is True, "Testcase {} :Failed \n Error: {}".format(tc_name
, result
)
726 "Verify that cost is updated in the ospf interface between r0"
727 "and r1 as 10 and r0 and r2 as 10"
731 "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 10}}}}
733 result
= verify_ospf6_interface(tgen
, topo
, dut
=dut
, input_dict
=input_dict
)
734 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
736 write_test_footer(tc_name
)
739 def test_ospfv3_def_rte_tc9_p0(request
):
740 """OSPF default route - Verify OSPF default route origination."""
741 tc_name
= request
.node
.name
742 write_test_header(tc_name
)
745 step("Bring up the base config.")
746 step("Configure OSPF on all the routers of the topology.")
747 if tgen
.routers_have_failure():
748 check_router_status(tgen
)
749 reset_config_on_routers(tgen
)
751 step(" Configure default-information originate always on R0.")
752 input_dict
= {"r0": {"ospf6": {"default-information": {"originate": True}}}}
753 result
= create_router_ospf(tgen
, topo
, input_dict
)
754 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
757 step(" Configure default-information originate always on R0.")
761 "default-information": {
768 result
= create_router_ospf(tgen
, topo
, input_dict
)
769 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
771 step("Verify that default route is originated in area always.")
774 step(" Configure default-information originate metric type 1 on R0.")
778 "default-information": {
788 "Verify that default route is originated in area when external "
789 "routes are present in R0 with metric type as 1."
793 "Verify that on R1 default route with type 1 is installed"
794 " (R1 is DUT in this case)"
797 step("Configure default-information originate metric type 2 on R0.")
801 "default-information": {
809 result
= create_router_ospf(tgen
, topo
, input_dict
)
810 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
813 "Verify that default route is originated in area when external"
814 " routes are present in R0 with metric type as 2."
818 step(" Configure default-information originate metric 100 on R0")
822 "default-information": {
831 result
= create_router_ospf(tgen
, topo
, input_dict
)
832 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
834 step("Verify that default route is originated with cost as 100 on R0.")
838 step("Delete the default-information command")
842 "default-information": {
850 result
= create_router_ospf(tgen
, topo
, input_dict
)
851 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
854 step("Configure default-information originate always on R0.")
858 "default-information": {
865 result
= create_router_ospf(tgen
, topo
, input_dict
)
866 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
868 step("Configure default route originate with active def route in zebra")
880 result
= create_static_routes(tgen
, input_dict
)
881 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
886 "default-information": {
892 result
= create_router_ospf(tgen
, topo
, input_dict
)
893 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
895 step("Verify that default route is originated by R0.")
898 step("Delete static route")
911 result
= create_static_routes(tgen
, input_dict
)
912 assert result
is True, "Testcase {} : Failed \n Error: {}".format(tc_name
, result
)
914 write_test_footer(tc_name
)
917 if __name__
== "__main__":
918 args
= ["-s"] + sys
.argv
[1:]
919 sys
.exit(pytest
.main(args
))