# This file is part of Buildbot.  Buildbot is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright Buildbot Team Members

import datetime
import mock
import os

from twisted.internet import reactor
from twisted.internet import task
from twisted.trial import unittest

from buildbot import util


class formatInterval(unittest.TestCase):

    def test_zero(self):
        self.assertEqual(util.formatInterval(0), "0 secs")

    def test_seconds_singular(self):
        self.assertEqual(util.formatInterval(1), "1 secs")

    def test_seconds(self):
        self.assertEqual(util.formatInterval(7), "7 secs")

    def test_minutes_one(self):
        self.assertEqual(util.formatInterval(60), "60 secs")

    def test_minutes_over_one(self):
        self.assertEqual(util.formatInterval(61), "1 mins, 1 secs")

    def test_minutes(self):
        self.assertEqual(util.formatInterval(300), "5 mins, 0 secs")

    def test_hours_one(self):
        self.assertEqual(util.formatInterval(3600), "60 mins, 0 secs")

    def test_hours_over_one_sec(self):
        self.assertEqual(util.formatInterval(3601), "1 hrs, 1 secs")

    def test_hours_over_one_min(self):
        self.assertEqual(util.formatInterval(3660), "1 hrs, 60 secs")

    def test_hours(self):
        self.assertEqual(util.formatInterval(7200), "2 hrs, 0 secs")

    def test_mixed(self):
        self.assertEqual(util.formatInterval(7392), "2 hrs, 3 mins, 12 secs")


class TestHumanReadableDelta(unittest.TestCase):

    def test_timeDeltaToHumanReadable(self):
        """
        It will return a human readable time difference.
        """
        result = util.human_readable_delta(1, 1)
        self.assertEqual('super fast', result)

        result = util.human_readable_delta(1, 2)
        self.assertEqual('1 seconds', result)

        result = util.human_readable_delta(1, 61)
        self.assertEqual('1 minutes', result)

        result = util.human_readable_delta(1, 62)
        self.assertEqual('1 minutes, 1 seconds', result)

        result = util.human_readable_delta(1, 60 * 60 + 1)
        self.assertEqual('1 hours', result)

        result = util.human_readable_delta(1, 60 * 60 + 61)
        self.assertEqual('1 hours, 1 minutes', result)

        result = util.human_readable_delta(1, 60 * 60 + 62)
        self.assertEqual('1 hours, 1 minutes, 1 seconds', result)

        result = util.human_readable_delta(1, 24 * 60 * 60 + 1)
        self.assertEqual('1 days', result)

        result = util.human_readable_delta(1, 24 * 60 * 60 + 2)
        self.assertEqual('1 days, 1 seconds', result)


class safeTranslate(unittest.TestCase):

    def test_str_good(self):
        self.assertEqual(util.safeTranslate(str("full")), str("full"))

    def test_str_bad(self):
        self.assertEqual(util.safeTranslate(str("speed=slow;quality=high")),
                         str("speed_slow_quality_high"))

    def test_str_pathological(self):
        # if you needed proof this wasn't for use with sensitive data
        self.assertEqual(util.safeTranslate(str("p\ath\x01ogy")),
                         str("p\ath\x01ogy"))  # bad chars still here!

    def test_unicode_good(self):
        self.assertEqual(util.safeTranslate(u"full"), str("full"))

    def test_unicode_bad(self):
        self.assertEqual(util.safeTranslate(unicode("speed=slow;quality=high")),
                         str("speed_slow_quality_high"))

    def test_unicode_pathological(self):
        self.assertEqual(util.safeTranslate(u"\u0109"),
                         str("\xc4\x89"))  # yuck!


class naturalSort(unittest.TestCase):

    def test_alpha(self):
        self.assertEqual(
            util.naturalSort(['x', 'aa', 'ab']),
            ['aa', 'ab', 'x'])

    def test_numeric(self):
        self.assertEqual(
            util.naturalSort(['1', '10', '11', '2', '20']),
            ['1', '2', '10', '11', '20'])

    def test_alphanum(self):
        l1 = 'aa10ab aa1ab aa10aa f a aa3 aa30 aa3a aa30a'.split()
        l2 = 'a aa1ab aa3 aa3a aa10aa aa10ab aa30 aa30a f'.split()
        self.assertEqual(util.naturalSort(l1), l2)


class none_or_str(unittest.TestCase):

    def test_none(self):
        self.assertEqual(util.none_or_str(None), None)

    def test_str(self):
        self.assertEqual(util.none_or_str("hi"), "hi")

    def test_int(self):
        self.assertEqual(util.none_or_str(199), "199")


class TimeFunctions(unittest.TestCase):

    def test_UTC(self):
        self.assertEqual(util.UTC.utcoffset(datetime.datetime.now()),
                         datetime.timedelta(0))
        self.assertEqual(util.UTC.dst(datetime.datetime.now()),
                         datetime.timedelta(0))
        self.assertEqual(util.UTC.tzname(), "UTC")

    def test_epoch2datetime(self):
        self.assertEqual(util.epoch2datetime(0),
                         datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=util.UTC))
        self.assertEqual(util.epoch2datetime(1300000000),
                         datetime.datetime(2011, 3, 13, 7, 6, 40, tzinfo=util.UTC))

    def test_datetime2epoch(self):
        dt = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=util.UTC)
        self.assertEqual(util.datetime2epoch(dt), 0)
        dt = datetime.datetime(2011, 3, 13, 7, 6, 40, tzinfo=util.UTC)
        self.assertEqual(util.datetime2epoch(dt), 1300000000)


class DiffSets(unittest.TestCase):

    def test_empty(self):
        removed, added = util.diffSets(set([]), set([]))
        self.assertEqual((removed, added), (set([]), set([])))

    def test_no_lists(self):
        removed, added = util.diffSets([1, 2], [2, 3])
        self.assertEqual((removed, added), (set([1]), set([3])))

    def test_no_overlap(self):
        removed, added = util.diffSets(set([1, 2]), set([3, 4]))
        self.assertEqual((removed, added), (set([1, 2]), set([3, 4])))

    def test_no_change(self):
        removed, added = util.diffSets(set([1, 2]), set([1, 2]))
        self.assertEqual((removed, added), (set([]), set([])))

    def test_added(self):
        removed, added = util.diffSets(set([1, 2]), set([1, 2, 3]))
        self.assertEqual((removed, added), (set([]), set([3])))

    def test_removed(self):
        removed, added = util.diffSets(set([1, 2]), set([1]))
        self.assertEqual((removed, added), (set([2]), set([])))


class MakeList(unittest.TestCase):

    def test_empty_string(self):
        self.assertEqual(util.makeList(''), [''])

    def test_None(self):
        self.assertEqual(util.makeList(None), [])

    def test_string(self):
        self.assertEqual(util.makeList('hello'), ['hello'])

    def test_unicode(self):
        self.assertEqual(util.makeList(u'\N{SNOWMAN}'), [u'\N{SNOWMAN}'])

    def test_list(self):
        self.assertEqual(util.makeList(['a', 'b']), ['a', 'b'])

    def test_tuple(self):
        self.assertEqual(util.makeList(('a', 'b')), ['a', 'b'])

    def test_copy(self):
        input = ['a', 'b']
        output = util.makeList(input)
        input.append('c')
        self.assertEqual(output, ['a', 'b'])


class Flatten(unittest.TestCase):

    def test_simple(self):
        self.assertEqual(util.flatten([1, 2, 3]), [1, 2, 3])

    def test_deep(self):
        self.assertEqual(util.flatten([[1, 2], 3, [[4]]]),
                         [1, 2, 3, 4])

    def test_tuples(self):
        self.assertEqual(util.flatten([(1, 2), 3]), [(1, 2), 3])


class AsyncSleep(unittest.TestCase):

    def test_sleep(self):
        clock = task.Clock()
        self.patch(reactor, 'callLater', clock.callLater)
        d = util.asyncSleep(2)
        self.assertFalse(d.called)
        clock.advance(1)
        self.assertFalse(d.called)
        clock.advance(1)
        self.assertTrue(d.called)


class FunctionalEnvironment(unittest.TestCase):

    def test_working_locale(self):
        environ = {'LANG': 'en_GB.UTF-8'}
        self.patch(os, 'environ', environ)
        config = mock.Mock()
        util.check_functional_environment(config)
        self.assertEqual(config.error.called, False)

    def test_broken_locale(self):
        environ = {'LANG': 'NINE.UTF-8'}
        self.patch(os, 'environ', environ)
        config = mock.Mock()
        util.check_functional_environment(config)
        config.error.assert_called()
