# Copyright (c) 2014, CN-Software Ltd.
# 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 CN-Software Ltd. 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 OWNER 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 random
import timeit
import time
import logging
import unittest

from geobaza import GeobazaQuery, SpecialRange, GeobazaObject, Geobaza, MEMORY_CACHE

logging.basicConfig(
    level=logging.DEBUG,
    format='%(message)s',
)

def rand_ip(*args):
    blocks = list(args)
    while len(blocks) < 4:
        blocks.append(str(random.choice(range(255))))
    return '.'.join(blocks)

def ip_list(blocks=[], count=10):
    ips = []
    while len(ips) < count:
        ips.append(rand_ip(*blocks))
    return ips

class GeobazaInitSpeedTest(unittest.TestCase):
    iters = 1000

    def test_test(self):
        t = timeit.Timer('time.sleep(1)', 'import time')
        elapsed = t.timeit(1)
        self.assertLessEqual(elapsed, 1.1)
        self.assertGreaterEqual(elapsed, 0.9)

    def test_init_no_cache(self):
        t = timeit.Timer('geobaza.GeobazaQuery()', 'import geobaza')
        avg = t.timeit(self.iters) / self.iters
        logging.info('Elapsed time: %f' % avg)
        self.assertLessEqual(avg, 0.00015)

    def test_init_mem_cache(self):
        t = timeit.Timer('geobaza.GeobazaQuery(cache=geobaza.MEMORY_CACHE)', 'import geobaza')
        avg = t.timeit(self.iters) / self.iters
        logging.info('Elapsed time: %f' % avg)
        self.assertLessEqual(avg, 0.09)

    def test_init_compare(self):
        no_cache = timeit.Timer('geobaza.GeobazaQuery()', 'import geobaza')
        mem_cache = timeit.Timer('geobaza.GeobazaQuery(cache=geobaza.MEMORY_CACHE)', 'import geobaza')
        no_avg = no_cache.timeit(self.iters) / self.iters
        mem_avg = mem_cache.timeit(self.iters) / self.iters
        logging.info('Elapsed time NO_CACHE: %f, MEMORY_CACHE: %f' % (no_avg, mem_avg))
        self.assertLess(no_avg, mem_avg)

class GeobazaNoCacheSpeedTest(unittest.TestCase):
    iters = 100000 # 100K
    max_get = 12
    max_get_path = 18

    def setUp(self):
        self.geobaza = GeobazaQuery()

    def test_query(self):
        start = time.time()
        for dummy in xrange(self.iters):
            ip = rand_ip()
            self.geobaza.get(ip)
        total = time.time() - start
        logging.info('Elapsed time: %f' % total)
        self.assertLessEqual(total, self.max_get)

    def test_path_query(self):
        start = time.time()
        for dummy in xrange(self.iters):
            ip = rand_ip()
            self.geobaza.get_path(ip)
        total = time.time() - start
        logging.info('Elapsed time: %f' % total)
        self.assertLessEqual(total, self.max_get_path)

class GeobazaMemCacheSpeedTest(GeobazaNoCacheSpeedTest):
    def setUp(self):
        self.geobaza = GeobazaQuery(cache=MEMORY_CACHE)

class GeobazaTest(unittest.TestCase):
    iters = 100000 # 100K

    def setUp(self):
        self.geobaza = GeobazaQuery()

    def test_query(self):
        for dummy in xrange(self.iters):
            ip = rand_ip()
            query = self.geobaza.get(ip)
            self.assertTrue(isinstance(query, (GeobazaObject, SpecialRange)))

    def test_path_query(self):
        for dummy in xrange(self.iters):
            ip = rand_ip()
            query = self.geobaza.get_path(ip)
            self.assertTrue(isinstance(query, Geobaza))
            self.assertGreaterEqual(len(query), 1)
            for item in query:
                self.assertTrue(isinstance(item, (GeobazaObject, SpecialRange)))
                if isinstance(item, GeobazaObject):
                    self.assertIsNotNone(item.translations[0].en)

    def test_special(self):
        ranges = []
        ranges.append((ip_list(['127']), u'Loopback'))
        ranges.append((ip_list(['192', '168']), u'Private-Use Networks'))
        ranges.append((ip_list(['192', '0', '0']), u'IETF Protocol Assignments'))
        ranges.append((ip_list(['10']), u'Private-Use Networks'))
        for i in [ip_list(['172', '%d' % k]) for k in range(16, 32)]:
            ranges.append((i, u'Private-Use Networks'))
        for i in [ip_list(['%d' % k]) for k in range(240, 256)]:
            ranges.append((i, u'Reserved for Future Use'))
        for i in [ip_list(['%d' % k]) for k in range(224, 240)]:
            ranges.append((i, u'Multicast'))
        ranges.append((ip_list(['169', '254']), u'Link Local'))
        ranges.append((ip_list(['192', '0', '2']), u'TEST-NET-1'))
        ranges.append((ip_list(['198', '51', '100']), u'TEST-NET-2'))
        ranges.append((ip_list(['203', '0', '113']), u'TEST-NET-3'))
        ranges.append((ip_list(['192', '88', '99']), u'6to4 Relay Anycast'))
        for i in [ip_list(['198', '%d' % k]) for k in range(18, 19)]:
            ranges.append((i, u'Network Interconnect'))
        ranges.append((['255.255.255.255'], u'Limited Broadcast'))
        ranges.append((['0.0.0.0'], u'"This" Network'))

        for ips, name in ranges:
            for ip in ips:
                query = self.geobaza.get_path(ip)
                obj = query[0]
                self.assertEqual(len(query), 1)
                self.assertTrue(isinstance(obj, SpecialRange), 'Special range check failed for IP: %s' % ip)
                self.assertEqual(obj.name, name, 'Special range designation check failed for IP: %s' % ip)

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromNames(['tests.GeobazaTest', 'tests.GeobazaInitSpeedTest', 'tests.GeobazaMemCacheSpeedTest', 'tests.GeobazaNoCacheSpeedTest'])
    unittest.TextTestRunner(verbosity=2).run(suite)
