[tests] remove redundant 1.2 certification tests and CI job (#12883)

This commit removes the following legacy 1.2 certification test scripts:
- tests/scripts/thread-cert/v1_2_router_5_1_1.py
- tests/scripts/thread-cert/v1_2_test_parent_selection.py

It also removes the 'packet-verification-1-1-on-1-4' job from the
Simulation 1.4 workflow as it is no longer required.
This commit is contained in:
Jonathan Hui
2026-04-13 17:16:36 -05:00
committed by GitHub
parent ea56e75ffe
commit 23e9cc98b8
3 changed files with 0 additions and 508 deletions
-54
View File
@@ -121,59 +121,6 @@ jobs:
path: tmp/coverage.info
retention-days: 1
packet-verification-1-1-on-1-4:
runs-on: ubuntu-24.04
env:
REFERENCE_DEVICE: 1
VIRTUAL_TIME: 1
OT_VT_USE_UNIX_SOCKET: 1
PACKET_VERIFICATION: 1
THREAD_VERSION: 1.4
INTER_OP_BBR: 1
MULTIPLY: 3
steps:
- name: Harden Runner
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: recursive
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
cache: pip
- name: Bootstrap
run: |
sudo apt-get update
sudo apt-get --no-install-recommends install -y ninja-build lcov
python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
- name: Build
run: |
./script/test build
- name: Get Thread-Wireshark
run: |
./script/test get_thread_wireshark
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: ${{ failure() }}
with:
name: packet-verification-1.1-on-1.4-pcaps
path: |
*.pcap
*.json
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cov-packet-verification-1-1-on-1-4
path: tmp/coverage.info
retention-days: 1
channel-manager-csl:
runs-on: ubuntu-24.04
env:
@@ -276,7 +223,6 @@ jobs:
upload-coverage:
needs:
- thread-1-4
- packet-verification-1-1-on-1-4
- expects
runs-on: ubuntu-22.04
steps:
@@ -1,153 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import unittest
import config
import thread_cert
import mle
import network_layer
LEADER = 1
ROUTER_1 = 2
class Router_5_1_01(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2'
},
ROUTER_1: {
'version': '1.2'
},
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[ROUTER_1].set_router_selection_jitter(1)
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER_1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER_1].get_state(), 'router')
leader_messages = self.simulator.get_messages_sent_by(LEADER)
router_messages = self.simulator.get_messages_sent_by(ROUTER_1)
# 1 - Leader transmits MLE advertisements
msg = leader_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
msg.assertSentWithHopLimit(255)
msg.assertSentToDestinationAddress('ff02::1')
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Route64)
# 2 - Router_1 begins attach process by sending a multicast MLE Parent Request
msg = router_messages.next_mle_message(mle.CommandType.PARENT_REQUEST)
msg.assertSentWithHopLimit(255)
msg.assertSentToDestinationAddress('ff02::2')
msg.assertMleMessageContainsTlv(mle.Mode)
msg.assertMleMessageContainsTlv(mle.Challenge)
msg.assertMleMessageContainsTlv(mle.ScanMask)
msg.assertMleMessageContainsTlv(mle.Version)
self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
self.assertEqual(1, scan_mask_tlv.router)
self.assertEqual(0, scan_mask_tlv.end_device)
# 3 - Leader sends a MLE Parent Response
msg = leader_messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
msg.assertSentToNode(self.nodes[ROUTER_1])
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
msg.assertMleMessageContainsTlv(mle.Response)
msg.assertMleMessageContainsTlv(mle.Challenge)
msg.assertMleMessageContainsTlv(mle.LinkMargin)
msg.assertMleMessageContainsTlv(mle.Connectivity)
msg.assertMleMessageContainsTlv(mle.Version)
self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
# 4 - Router_1 receives the MLE Parent Response and sends a Child ID Request
msg = router_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[LEADER])
msg.assertMleMessageContainsTlv(mle.Response)
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
msg.assertMleMessageContainsTlv(mle.Mode)
msg.assertMleMessageContainsTlv(mle.Timeout)
msg.assertMleMessageContainsTlv(mle.Version)
msg.assertMleMessageContainsTlv(mle.TlvRequest)
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
# 5 - Leader responds with a Child ID Response
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
msg.assertSentToNode(self.nodes[ROUTER_1])
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Address16)
msg.assertMleMessageContainsOptionalTlv(mle.NetworkData)
msg.assertMleMessageContainsOptionalTlv(mle.Route64)
msg.assertMleMessageContainsOptionalTlv(mle.AddressRegistration)
# 6 - Router_1 sends an Address Solicit Request
msg = router_messages.next_coap_message('0.02')
msg.assertCoapMessageRequestUriPath('/a/as')
msg.assertCoapMessageContainsTlv(network_layer.MacExtendedAddress)
msg.assertCoapMessageContainsTlv(network_layer.Status)
# 7 - Leader sends an Address Solicit Response
msg = leader_messages.next_coap_message('2.04')
msg.assertCoapMessageContainsTlv(network_layer.Status)
msg.assertCoapMessageContainsOptionalTlv(network_layer.RouterMask)
status_tlv = msg.get_coap_message_tlv(network_layer.Status)
self.assertEqual(network_layer.StatusValues.SUCCESS, status_tlv.status)
# 8 - Router_1 Transmit MLE advertisements
msg = router_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
msg.assertSentWithHopLimit(255)
msg.assertSentToDestinationAddress('ff02::1')
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Route64)
# 9 - Verify connectivity by sending an ICMPv6 Echo Request to the DUT link local address
self.assertTrue(self.nodes[LEADER].ping(self.nodes[ROUTER_1].get_linklocal()))
self.assertTrue(self.nodes[ROUTER_1].ping(self.nodes[LEADER].get_linklocal()))
if __name__ == '__main__':
unittest.main()
@@ -1,301 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import unittest
import thread_cert
import config
import mle
LEADER_1_2 = 1
ROUTER_1_1 = 2
REED_1_2 = 3
ROUTER_1_2 = 4
REED_1_1 = 5
MED_1_1 = 6
MED_1_2 = 7
# Topology
# (lq:2) (pp:1)
# REED_1_2 ----- ROUTER_1_2
# | \ / | \
# | \/ REED_1_1 \
# (lq:2) | / \ / `router` \
# | (lq:2) \ \
# | / / \ \
# LEADER_1_2 --- ROUTER_1_1 -- MED_1_2
# \ |
# \ |
# \ |
# MED_1_1
#
# 1) Bring up LEADER_1_2 and ROUTER_1_1,
# 2) Config link quality (LEADER_1_2->REED_1_2) as 2, bring up REED_1_2 which would attach to ROUTER_1_1
# due to higher two-way link quality,
# 3) Config link quality(LEADER_1_2->ROUTER_1_2) and link quality(REED_1_2->ROUTER_1_2) as 2, bring up
# ROUTER_1_2 which would attach to LEADER_1_2 due to active router is preferred,
# 4) Config parent priority as 1 on ROUTER_1_2, bring up REED_1_1 which would attach to ROUTER_1_2 due to
# higher parent priority,
# 5) Upgrade REED_1_1 to `router` role, bring up MED_1_1 which would attach to LEADER_1_2 which has higher
# link quality of 3,
# 6) Config parent priority as 1 on ROUTER_1_1, bring up MED_1_2 which would attach to ROUTER_1_2 due to
# higher version
#
class TestParentSelection(thread_cert.TestCase):
TOPOLOGY = {
LEADER_1_2: {
'version': '1.2',
'allowlist': [REED_1_2, ROUTER_1_2, REED_1_1, ROUTER_1_1, MED_1_1],
},
ROUTER_1_1: {
'version': '1.1',
'allowlist': [LEADER_1_2, REED_1_2, MED_1_2, MED_1_1],
},
REED_1_2: {
'version': '1.2',
'allowlist': [ROUTER_1_2, ROUTER_1_1, LEADER_1_2],
},
ROUTER_1_2: {
'version': '1.2',
'allowlist': [REED_1_2, MED_1_2, REED_1_1, LEADER_1_2],
},
REED_1_1: {
'version': '1.1',
'allowlist': [ROUTER_1_2, LEADER_1_2]
},
MED_1_1: {
'mode': 'r',
'version': '1.1',
'allowlist': [LEADER_1_2, ROUTER_1_1],
},
MED_1_2: {
'mode': 'r',
'version': '1.2',
'allowlist': [ROUTER_1_1, ROUTER_1_2],
},
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[LEADER_1_2].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER_1_2].get_state(), 'leader')
self.nodes[ROUTER_1_1].set_router_selection_jitter(1)
self.nodes[ROUTER_1_1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
# Mesh Impacting Criteria - Highest Two-way link quality
# REED_1_2 would attach to ROUTER_1_1
# Attach to ROUTER_1_1 which has highest two-way link quality
# Flush relative message queues
self.flush_nodes([LEADER_1_2, ROUTER_1_1])
self.nodes[LEADER_1_2].set_link_quality(self.nodes[REED_1_2].get_addr64(), 2)
self.nodes[REED_1_2].set_router_selection_jitter(1)
self.nodes[REED_1_2].set_router_upgrade_threshold(1)
self.nodes[REED_1_2].start()
self.simulator.go(5)
self.assertEqual(self.nodes[REED_1_2].get_state(), 'child')
# Check Parent Response
messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_prefer), "Error: Expected parent response not found"
messages = self.simulator.get_messages_sent_by(LEADER_1_2)
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
# Known that link margin for link quality 3 is 80 and link quality 2 is 15
self.assertGreater((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin), 20)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(REED_1_2)
msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[ROUTER_1_1])
# Mesh Impacting Criteria - Active Routers over REEDs
# ROUTER_1_2 would attach to LEADER_1_2
# Link quality configuration, so that REED_1_2 has the chance to respond
# Flush relative message queues
self.flush_nodes([LEADER_1_2, REED_1_2])
self.nodes[LEADER_1_2].set_link_quality(self.nodes[ROUTER_1_2].get_addr64(), 2)
self.nodes[REED_1_2].set_link_quality(self.nodes[ROUTER_1_2].get_addr64(), 2)
self.nodes[ROUTER_1_2].set_router_selection_jitter(1)
self.nodes[ROUTER_1_2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
# Check Parent Response
messages = self.simulator.get_messages_sent_by(LEADER_1_2)
# Skip first response for first parent request
assert messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_prefer), "Error: Expected parent response not found"
messages = self.simulator.get_messages_sent_by(REED_1_2)
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[LEADER_1_2])
# Mesh Impacting Criteria - Highest Parent Priority value in the Connectivity TLV
# REED_1_1 would attach to ROUTER_1_2
# Flush relative message queues
self.flush_nodes([LEADER_1_2, ROUTER_1_2])
self.nodes[ROUTER_1_2].set_parent_priority(1)
self.nodes[REED_1_1].set_router_selection_jitter(1)
self.nodes[REED_1_1].set_router_upgrade_threshold(1)
self.nodes[REED_1_1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[REED_1_1].get_state(), 'child')
# Check Parent Response
messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_prefer), "Error: Expected parent response not found"
messages = self.simulator.get_messages_sent_by(LEADER_1_2)
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
self.assertGreater(
parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(REED_1_1)
msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[ROUTER_1_2])
# Mesh Impacting Criteria - Router with the most high-quality neighbors
# (Link Quality 3 field in the Connectivity TLV)
# MED_1_1 would attach to LEADER_1_2
self.nodes[REED_1_1].set_state('router')
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[REED_1_1].get_state(), 'router')
# Flush relative message queues
self.flush_nodes([LEADER_1_2, ROUTER_1_1])
self.nodes[MED_1_1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED_1_1].get_state(), 'child')
# Check Parent Response
messages = self.simulator.get_messages_sent_by(LEADER_1_2)
parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_prefer), "Error: Expected parent response not found"
messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
self.assertGreater(
parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(MED_1_1)
msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[LEADER_1_2])
# Child Impacting Criteria - A Version number in the Version TLV
# equal to or higher than the version that implements features
# desirable to the Child MED_1_2 would attach to ROUTER_1_2
# Flush relative message queues
self.flush_nodes([ROUTER_1_2, ROUTER_1_1])
self.nodes[ROUTER_1_1].set_parent_priority(1)
self.nodes[MED_1_2].start()
self.simulator.go(15)
self.assertEqual(self.nodes[MED_1_2].get_state(), 'child')
# Check Parent Response
messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_prefer), "Error: Expected parent response not found"
messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
self.assertEqual(
parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
self.assertGreater(
parent_prefer.get_mle_message_tlv(mle.Version).version,
parent_cmp.get_mle_message_tlv(mle.Version).version)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(MED_1_2)
msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
msg.assertSentToNode(self.nodes[ROUTER_1_2])
if __name__ == '__main__':
unittest.main()