#!/usr/bin/env python3
#
#  Copyright (c) 2016, 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 io
import random
import struct
import unittest

import common
import network_layer


def any_eid():
    return bytearray([random.getrandbits(8) for _ in range(16)])


def any_mac_extended_address():
    return bytearray([random.getrandbits(8) for _ in range(8)])


def any_rloc16():
    return random.getrandbits(16)


def any_ml_eid():
    return bytearray([random.getrandbits(8) for _ in range(8)])


def any_status():
    return random.getrandbits(1)


def any_seconds():
    return random.getrandbits(32)


def any_id_sequence():
    return random.getrandbits(8)


def any_router_id_mask():
    return random.getrandbits(64)


def any_options(count=None):
    count = count if count is not None else random.randint(0, 255)
    return [random.getrandbits(8) for _ in range(count)]


def any_tlv_data(length=None):
    _type = random.getrandbits(8)
    length = length if length is not None else random.getrandbits(8)
    value = bytearray([random.getrandbits(8) for _ in range(length)])

    return bytearray([_type, length]) + value


def any_tlvs_data(count=None):
    count = count if count is not None else random.randint(0, 16)
    data = bytearray()

    for _ in range(count):
        data += any_tlv_data(random.randint(1, 15))

    return data


class TestTargetEid(unittest.TestCase):

    def test_should_return_eid_value_when_eid_property_is_called(self):
        # GIVEN
        eid = any_eid()

        target_eid = network_layer.TargetEid(eid)

        # WHEN
        actual_eid = target_eid.eid

        # THEN
        self.assertEqual(eid, actual_eid)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        eid = any_eid()

        target_eid = network_layer.TargetEid(eid)

        # THEN
        self.assertEqual(target_eid, network_layer.TargetEid(eid))


class TestTargetEidFactory(unittest.TestCase):

    def test_should_create_TargetEid_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        eid = any_eid()

        factory = network_layer.TargetEidFactory()

        # WHEN
        target_eid = factory.parse(io.BytesIO(eid), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(target_eid, network_layer.TargetEid))
        self.assertEqual(eid, target_eid.eid)


class TestMacExtendedAddress(unittest.TestCase):

    def test_should_return_mac_address_value_when_mac_address_property_is_called(self):
        # GIVEN
        mac_address = any_mac_extended_address()

        mac_extended_address = network_layer.MacExtendedAddress(mac_address)

        # WHEN
        actual_mac_address = mac_extended_address.mac_address

        # THEN
        self.assertEqual(mac_address, actual_mac_address)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        mac_address = any_mac_extended_address()

        mac_extended_address = network_layer.MacExtendedAddress(mac_address)

        # THEN
        self.assertEqual(mac_extended_address, network_layer.MacExtendedAddress(mac_address))


class TestMacExtendedAddressFactory(unittest.TestCase):

    def test_should_create_MacExtendedAddress_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        mac_address = any_mac_extended_address()

        factory = network_layer.MacExtendedAddressFactory()

        # WHEN
        mac_extended_address = factory.parse(io.BytesIO(mac_address), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(mac_extended_address, network_layer.MacExtendedAddress))
        self.assertEqual(mac_address, mac_extended_address.mac_address)


class TestRloc16(unittest.TestCase):

    def test_should_return_rloc16_value_when_rloc16_property_is_called(self):
        # GIVEN
        rloc16 = any_rloc16()

        rloc16_obj = network_layer.Rloc16(rloc16)

        # WHEN
        actual_rloc16 = rloc16_obj.rloc16

        # THEN
        self.assertEqual(rloc16, actual_rloc16)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        rloc16 = any_rloc16()

        rloc16_obj = network_layer.Rloc16(rloc16)

        # THEN
        self.assertEqual(rloc16_obj, network_layer.Rloc16(rloc16))


class TestRloc16Factory(unittest.TestCase):

    def test_should_create_Rloc16_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        rloc16 = any_rloc16()

        factory = network_layer.Rloc16Factory()

        data = bytearray(struct.pack(">H", rloc16))

        # WHEN
        rloc16_obj = factory.parse(io.BytesIO(data), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(rloc16_obj, network_layer.Rloc16))
        self.assertEqual(rloc16, rloc16_obj.rloc16)


class TestMlEid(unittest.TestCase):

    def test_should_return_ml_eid_value_when_ml_eid_property_is_called(self):
        # GIVEN
        ml_eid = any_ml_eid()

        ml_eid_obj = network_layer.MlEid(ml_eid)

        # WHEN
        actual_ml_eid = ml_eid_obj.ml_eid

        # THEN
        self.assertEqual(ml_eid, actual_ml_eid)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        ml_eid = any_ml_eid()

        ml_eid_obj = network_layer.MlEid(ml_eid)

        # THEN
        self.assertEqual(ml_eid_obj, network_layer.MlEid(ml_eid))


class TestMlEidFactory(unittest.TestCase):

    def test_should_create_MlEid_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        ml_eid = any_ml_eid()

        factory = network_layer.MlEidFactory()

        # WHEN
        ml_eid_obj = factory.parse(io.BytesIO(ml_eid), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(ml_eid_obj, network_layer.MlEid))
        self.assertEqual(ml_eid, ml_eid_obj.ml_eid)


class TestStatus(unittest.TestCase):

    def test_should_return_status_value_when_status_property_is_called(self):
        # GIVEN
        status = any_status()

        status_obj = network_layer.Status(status)

        # WHEN
        actual_status = status_obj.status

        # THEN
        self.assertEqual(status, actual_status)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        status = any_status()

        status_obj = network_layer.Status(status)

        # THEN
        self.assertEqual(status_obj, network_layer.Status(status))


class TestStatusFactory(unittest.TestCase):

    def test_should_create_Status_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        status = any_status()

        factory = network_layer.StatusFactory()

        data = bytearray([status])

        # WHEN
        status_obj = factory.parse(io.BytesIO(data), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(status_obj, network_layer.Status))
        self.assertEqual(status, status_obj.status)


class TestTimeSinceLastTransaction(unittest.TestCase):

    def test_should_return_seconds_value_when_seconds_property_is_called(self):
        # GIVEN
        seconds = any_seconds()

        time_since_last_transaction = network_layer.TimeSinceLastTransaction(seconds)

        # WHEN
        actual_seconds = time_since_last_transaction.seconds

        # THEN
        self.assertEqual(seconds, actual_seconds)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        seconds = any_seconds()

        time_since_last_transaction = network_layer.TimeSinceLastTransaction(seconds)

        # THEN
        self.assertEqual(
            time_since_last_transaction,
            network_layer.TimeSinceLastTransaction(seconds),
        )


class TestTimeSinceLastTransactionFactory(unittest.TestCase):

    def test_should_create_TimeSinceLastTransaction_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        seconds = any_seconds()

        factory = network_layer.TimeSinceLastTransactionFactory()

        data = bytearray(struct.pack(">L", seconds))

        # WHEN
        time_since_last_transaction = factory.parse(io.BytesIO(data), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(
            time_since_last_transaction,
            network_layer.TimeSinceLastTransaction,
        ))
        self.assertEqual(seconds, time_since_last_transaction.seconds)


class TestRouterMask(unittest.TestCase):

    def test_should_return_id_sequence_value_when_id_sequence_property_is_called(self):
        # GIVEN
        id_sequence = any_id_sequence()

        router_mask = network_layer.RouterMask(id_sequence, any_router_id_mask())

        # WHEN
        actual_id_sequence = router_mask.id_sequence

        # THEN
        self.assertEqual(id_sequence, actual_id_sequence)

    def test_should_return_router_id_mask_value_when_router_id_mask_property_is_called(self):
        # GIVEN
        router_id_mask = any_router_id_mask()

        router_mask = network_layer.RouterMask(any_id_sequence(), router_id_mask)

        # WHEN
        actual_router_id_mask = router_mask.router_id_mask

        # THEN
        self.assertEqual(router_id_mask, actual_router_id_mask)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        id_sequence = any_id_sequence()
        router_id_mask = any_router_id_mask()

        router_mask = network_layer.RouterMask(id_sequence, router_id_mask)

        # THEN
        self.assertEqual(router_mask, network_layer.RouterMask(id_sequence, router_id_mask))


class TestRouterMaskFactory(unittest.TestCase):

    def test_should_create_RouterMask_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        id_sequence = any_id_sequence()
        router_id_mask = any_router_id_mask()

        factory = network_layer.RouterMaskFactory()

        data = bytearray([id_sequence]) + struct.pack(">Q", router_id_mask)

        # WHEN
        router_mask = factory.parse(io.BytesIO(data), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(router_mask, network_layer.RouterMask))
        self.assertEqual(id_sequence, router_mask.id_sequence)
        self.assertEqual(router_id_mask, router_mask.router_id_mask)


class TestNdOption(unittest.TestCase):

    def test_should_return_options_value_when_options_property_is_called(self):
        # GIVEN
        options = any_options()

        nd_option = network_layer.NdOption(options)

        # WHEN
        actual_options = nd_option.options

        # THEN
        self.assertEqual(options, actual_options)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        options = any_options()

        nd_option = network_layer.NdOption(options)

        # THEN
        self.assertEqual(nd_option, network_layer.NdOption(options))


class TestNdOptionFactory(unittest.TestCase):

    def test_should_create_NdOption_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        options = any_options()

        factory = network_layer.NdOptionFactory()

        data = bytearray(options)

        # WHEN
        nd_option = factory.parse(io.BytesIO(data), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(nd_option, network_layer.NdOption))
        self.assertEqual(options, nd_option.options)


class TestThreadNetworkData(unittest.TestCase):

    def test_should_return_options_value_when_options_property_is_called(self):
        # GIVEN
        tlvs = any_tlvs_data()

        thread_network_data = network_layer.ThreadNetworkData(tlvs)

        # WHEN
        actual_tlvs = thread_network_data.tlvs

        # THEN
        self.assertEqual(tlvs, actual_tlvs)

    def test_should_return_True_when_try_to_equal_two_the_same_type_objects_with_the_same_values(self):
        # GIVEN
        tlvs = any_tlvs_data()

        thread_network_data = network_layer.ThreadNetworkData(tlvs)

        # THEN
        self.assertEqual(thread_network_data, network_layer.ThreadNetworkData(tlvs))


class TestThreadNetworkDataFactory(unittest.TestCase):

    def test_should_create_ThreadNetworkData_from_bytearray_when_parse_method_is_called(self):
        # GIVEN
        tlvs = any_tlvs_data()

        class DummyNetworkDataTlvsFactory:

            def parse(self, data, message_info):
                return bytearray(data.read())

        factory = network_layer.ThreadNetworkDataFactory(DummyNetworkDataTlvsFactory())

        # WHEN
        thread_network_data = factory.parse(io.BytesIO(tlvs), common.MessageInfo())

        # THEN
        self.assertTrue(isinstance(thread_network_data, network_layer.ThreadNetworkData))
        self.assertEqual(tlvs, thread_network_data.tlvs)


if __name__ == "__main__":
    unittest.main()

[(48, 'random.getrandbits', 'random.getrandbits', (['(16)'], {}), False, 'import random\n'), (56, 'random.getrandbits', 'random.getrandbits', (['(1)'], {}), False, 'import random\n'), (60, 'random.getrandbits', 'random.getrandbits', (['(32)'], {}), False, 'import random\n'), (64, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (68, 'random.getrandbits', 'random.getrandbits', (['(64)'], {}), False, 'import random\n'), (77, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (491, 'unittest.main', 'unittest.main', ([], {}), False, 'import unittest\n'), (72, 'random.randint', 'random.randint', (['(0)', '(255)'], {}), False, 'import random\n'), (73, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (78, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (85, 'random.randint', 'random.randint', (['(0)', '(16)'], {}), False, 'import random\n'), (100, 'network_layer.TargetEid', 'network_layer.TargetEid', (['eid'], {}), False, 'import network_layer\n'), (112, 'network_layer.TargetEid', 'network_layer.TargetEid', (['eid'], {}), False, 'import network_layer\n'), (124, 'network_layer.TargetEidFactory', 'network_layer.TargetEidFactory', ([], {}), False, 'import network_layer\n'), (140, 'network_layer.MacExtendedAddress', 'network_layer.MacExtendedAddress', (['mac_address'], {}), False, 'import network_layer\n'), (152, 'network_layer.MacExtendedAddress', 'network_layer.MacExtendedAddress', (['mac_address'], {}), False, 'import network_layer\n'), (164, 'network_layer.MacExtendedAddressFactory', 'network_layer.MacExtendedAddressFactory', ([], {}), False, 'import network_layer\n'), (180, 'network_layer.Rloc16', 'network_layer.Rloc16', (['rloc16'], {}), False, 'import network_layer\n'), (192, 'network_layer.Rloc16', 'network_layer.Rloc16', (['rloc16'], {}), False, 'import network_layer\n'), (204, 'network_layer.Rloc16Factory', 'network_layer.Rloc16Factory', ([], {}), False, 'import network_layer\n'), (222, 'network_layer.MlEid', 'network_layer.MlEid', (['ml_eid'], {}), False, 'import network_layer\n'), (234, 'network_layer.MlEid', 'network_layer.MlEid', (['ml_eid'], {}), False, 'import network_layer\n'), (246, 'network_layer.MlEidFactory', 'network_layer.MlEidFactory', ([], {}), False, 'import network_layer\n'), (262, 'network_layer.Status', 'network_layer.Status', (['status'], {}), False, 'import network_layer\n'), (274, 'network_layer.Status', 'network_layer.Status', (['status'], {}), False, 'import network_layer\n'), (286, 'network_layer.StatusFactory', 'network_layer.StatusFactory', ([], {}), False, 'import network_layer\n'), (304, 'network_layer.TimeSinceLastTransaction', 'network_layer.TimeSinceLastTransaction', (['seconds'], {}), False, 'import network_layer\n'), (316, 'network_layer.TimeSinceLastTransaction', 'network_layer.TimeSinceLastTransaction', (['seconds'], {}), False, 'import network_layer\n'), (331, 'network_layer.TimeSinceLastTransactionFactory', 'network_layer.TimeSinceLastTransactionFactory', ([], {}), False, 'import network_layer\n'), (377, 'network_layer.RouterMask', 'network_layer.RouterMask', (['id_sequence', 'router_id_mask'], {}), False, 'import network_layer\n'), (390, 'network_layer.RouterMaskFactory', 'network_layer.RouterMaskFactory', ([], {}), False, 'import network_layer\n'), (409, 'network_layer.NdOption', 'network_layer.NdOption', (['options'], {}), False, 'import network_layer\n'), (421, 'network_layer.NdOption', 'network_layer.NdOption', (['options'], {}), False, 'import network_layer\n'), (433, 'network_layer.NdOptionFactory', 'network_layer.NdOptionFactory', ([], {}), False, 'import network_layer\n'), (451, 'network_layer.ThreadNetworkData', 'network_layer.ThreadNetworkData', (['tlvs'], {}), False, 'import network_layer\n'), (463, 'network_layer.ThreadNetworkData', 'network_layer.ThreadNetworkData', (['tlvs'], {}), False, 'import network_layer\n'), (40, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (44, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (52, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (79, 'random.getrandbits', 'random.getrandbits', (['(8)'], {}), False, 'import random\n'), (89, 'random.randint', 'random.randint', (['(1)', '(15)'], {}), False, 'import random\n'), (115, 'network_layer.TargetEid', 'network_layer.TargetEid', (['eid'], {}), False, 'import network_layer\n'), (127, 'io.BytesIO', 'io.BytesIO', (['eid'], {}), False, 'import io\n'), (127, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (155, 'network_layer.MacExtendedAddress', 'network_layer.MacExtendedAddress', (['mac_address'], {}), False, 'import network_layer\n'), (167, 'io.BytesIO', 'io.BytesIO', (['mac_address'], {}), False, 'import io\n'), (167, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (195, 'network_layer.Rloc16', 'network_layer.Rloc16', (['rloc16'], {}), False, 'import network_layer\n'), (206, 'struct.pack', 'struct.pack', (['""">H"""', 'rloc16'], {}), False, 'import struct\n'), (209, 'io.BytesIO', 'io.BytesIO', (['data'], {}), False, 'import io\n'), (209, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (237, 'network_layer.MlEid', 'network_layer.MlEid', (['ml_eid'], {}), False, 'import network_layer\n'), (249, 'io.BytesIO', 'io.BytesIO', (['ml_eid'], {}), False, 'import io\n'), (249, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (277, 'network_layer.Status', 'network_layer.Status', (['status'], {}), False, 'import network_layer\n'), (291, 'io.BytesIO', 'io.BytesIO', (['data'], {}), False, 'import io\n'), (291, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (321, 'network_layer.TimeSinceLastTransaction', 'network_layer.TimeSinceLastTransaction', (['seconds'], {}), False, 'import network_layer\n'), (333, 'struct.pack', 'struct.pack', (['""">L"""', 'seconds'], {}), False, 'import struct\n'), (336, 'io.BytesIO', 'io.BytesIO', (['data'], {}), False, 'import io\n'), (336, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (380, 'network_layer.RouterMask', 'network_layer.RouterMask', (['id_sequence', 'router_id_mask'], {}), False, 'import network_layer\n'), (392, 'struct.pack', 'struct.pack', (['""">Q"""', 'router_id_mask'], {}), False, 'import struct\n'), (395, 'io.BytesIO', 'io.BytesIO', (['data'], {}), False, 'import io\n'), (395, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (424, 'network_layer.NdOption', 'network_layer.NdOption', (['options'], {}), False, 'import network_layer\n'), (438, 'io.BytesIO', 'io.BytesIO', (['data'], {}), False, 'import io\n'), (438, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n'), (466, 'network_layer.ThreadNetworkData', 'network_layer.ThreadNetworkData', (['tlvs'], {}), False, 'import network_layer\n'), (483, 'io.BytesIO', 'io.BytesIO', (['tlvs'], {}), False, 'import io\n'), (483, 'common.MessageInfo', 'common.MessageInfo', ([], {}), False, 'import common\n')]