#!/usr/bin/python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
### BEGIN LICENSE
# Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com>
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
### END LICENSE

import os
import os.path
import time
import unittest

from variety.Util import Util, cache, debounce, throttle


class TestUtil(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Chdir to the tests directory so that we can find our test images
        curdir = os.path.dirname(os.path.abspath(__file__))
        if curdir:
            os.chdir(curdir)

    def test_sanitize_filename(self):
        self.assertEqual("i_m____g_.jpg", Util.sanitize_filename("i?m?*%^g_.jpg"))

    def test_get_local_name(self):
        self.assertEqual("img.jpg", Util.get_local_name("http://example.com/a/img?a=b"))
        self.assertEqual("img.jpg", Util.get_local_name("http://example.com/a/img.jpg?a=b"))
        self.assertEqual("img.jpg", Util.get_local_name("http://example.com/a/img.jpg#x"))
        self.assertEqual("img.jpg", Util.get_local_name("http://example.com/a/img.jpg?a=b#x"))
        self.assertEqual("im g.jpg", Util.get_local_name("http://example.com/a/im%20g.jpg?a=b#x"))
        self.assertEqual("im_g.jpg", Util.get_local_name("http://example.com/a/im%22g.jpg?a=b#x"))

        self.assertTrue(len(Util.get_local_name("a" * 1000 + ".jpg")) < 255)

    def test_split(self):
        self.assertEqual(["a", "b", "c", "d", "e"], Util.split("a\nb,c ,,d\n   e"))

    def test_metadata(self):
        self.assertTrue(os.path.exists("test.jpg"))
        info = {
            "sourceURL": "b",
            "imageURL": "d",
            "sourceName": "a",
            "sourceLocation": "c",
            "sourceType": "flickr",
            "author": "автор",
            "authorURL": "url",
            "keywords": ["дума1", "дума2"],
            "headline": "проба1 проба1",
            "description": "проба2 проба2",
            "sfwRating": 50,
            "extraData": {"key1": "val1", "key2": "val2"},
        }
        self.assertEqual(info, Util.read_metadata("test.jpg"))
        self.assertTrue(Util.write_metadata("test.jpg", info))
        self.assertEqual(info, Util.read_metadata("test.jpg"))

        self.assertTrue(os.path.exists("test.svg"))
        self.assertEqual(info, Util.read_metadata("test.svg"))
        Util.write_metadata("test.svg", info)
        self.assertEqual(info, Util.read_metadata("test.svg"))

    def test_read_write_rating(self):
        self.assertTrue(os.path.exists("test.jpg"))
        self.assertEqual(None, Util.get_rating("test.jpg"))

        Util.set_rating("test.jpg", None)
        self.assertEqual(None, Util.get_rating("test.jpg"))

        Util.set_rating("test.jpg", 4)
        self.assertEqual(4, Util.get_rating("test.jpg"))

        Util.set_rating("test.jpg", -1)
        self.assertEqual(-1, Util.get_rating("test.jpg"))

        Util.set_rating("test.jpg", 0)
        self.assertEqual(0, Util.get_rating("test.jpg"))

        Util.set_rating("test.jpg", None)
        self.assertEqual(None, Util.get_rating("test.jpg"))

        try:
            Util.set_rating("test.jpg", -10)
            self.assertTrue(False, "Exception expected")
        except ValueError:
            pass  # OK

    def test_find_unique_name(self):
        self.assertEqual("/etc/fstab_1", Util.find_unique_name("/etc/fstab"))
        self.assertEqual("/etc/bash_1.bashrc", Util.find_unique_name("/etc/bash.bashrc"))

    def test_folderpath(self):
        self.assertEqual("/", Util.folderpath("/"))
        self.assertEqual("/a/b/c/", Util.folderpath("/a/b/c"))

    def test_gtk_to_fcmatch_font(self):
        self.assertEqual(
            ("Bitstream Charter:Bold:Italic:10", "10"),
            Util.gtk_to_fcmatch_font("Bitstream Charter Bold Italic 10"),
        )

    def test_file_in(self):
        self.assertTrue(Util.file_in("/a/b/a.txt", "/a/"))
        self.assertTrue(Util.file_in("/a/b/a.txt", "/a/b/"))
        self.assertFalse(Util.file_in("/a/b/a.txt", "/c/"))

    def test_same_file_paths(self):
        self.assertTrue(Util.same_file_paths("/a/../b/c", "/b/c"))
        self.assertFalse(Util.same_file_paths("/a/../b/c", "/a/./b/c"))

    def test_compare_versions(self):
        self.assertEqual(-1, Util.compare_versions("0.6.9", "0.7.0-alpha1"))
        self.assertEqual(-1, Util.compare_versions("0.4.10", "0.4.11"))
        self.assertEqual(-1, Util.compare_versions("0.4.10", "0.5"))
        self.assertEqual(-1, Util.compare_versions("0.4.10", "1"))
        self.assertEqual(0, Util.compare_versions("0.4.10", "0.4.10"))
        self.assertEqual(0, Util.compare_versions("1.0.0", "1.0"))  # PEP 440 details
        self.assertEqual(1, Util.compare_versions("0.4.10", "0.4.8"))
        self.assertEqual(1, Util.compare_versions("0.4.10", "0.4"))
        self.assertEqual(1, Util.compare_versions("0.4.10", "0"))
        self.assertEqual(1, Util.compare_versions("0.7.0-beta1", "0.7.0-alpha1"))
        self.assertEqual(1, Util.compare_versions("0.7.0", "0.7.0-alpha1"))
        self.assertEqual(1, Util.compare_versions("0.7.0", "0.7.0-beta1"))

    def test_md5(self):
        self.assertEqual("098f6bcd4621d373cade4e832627b4f6", Util.md5("test"))

    def test_md5file(self):
        self.assertEqual("6af710446c1554d7bc79d7939d6900e4", Util.md5file("test.jpg"))

    def test_collapseuser(self):
        self.assertEqual(
            "~/.config/variety", Util.collapseuser("%s/.config/variety" % os.path.expanduser("~"))
        )
        self.assertEqual(
            "/home/peteraaa/.config/variety", Util.collapseuser("/home/peteraaa/.config/variety")
        )
        self.assertEqual("/media/.config/variety", Util.collapseuser("/media/.config/variety"))

    def test_random_hash(self):
        s = set(Util.random_hash() for i in range(100))
        self.assertEqual(100, len(s))
        for x in s:
            self.assertEqual(32, len(x))

    def test_get_file_icon_name(self):
        self.assertEqual("folder", Util.get_file_icon_name("/xxx/yyy/zzz"))  # nonexistent
        self.assertEqual("user-home", Util.get_file_icon_name("~"))
        # Icon name depends on desktop environment - accept both specific and generic
        icon_name = Util.get_file_icon_name("~/Pictures")
        self.assertIn(icon_name, ["folder-pictures", "folder"])

    def test_get_xdg_pictures_folder(self):
        self.assertEqual(os.path.expanduser("~/Pictures"), Util.get_xdg_pictures_folder())

    def test_safe_map(self):
        def f(i):
            if i <= 10:
                raise Exception
            return i

        self.assertEqual([20, 30], list(Util.safe_map(f, [1, 5, 20, 10, 30, 4])))

    @unittest.skipIf(os.getenv("SKIP_DOWNLOADER_TESTS"), "Skipping networked tests (SKIP_DOWNLOADER_TESTS is set)")
    def test_fetch(self):
        resp = Util.fetch("//google.com")
        self.assertTrue(len(resp) > 0)

    def test_convert_url(self):
        self.assertEqual(
            "wallpapers_net_some_category_html",
            Util.convert_to_filename("http://wallpapers.net/some-category.html"),
        )

    def test_get_size(self):
        self.assertEqual((32, 32), Util.get_size("test.jpg"))
        self.assertRaises(Exception, lambda: Util.get_size("fake_image.jpg"))

    def test_is_image(self):
        self.assertTrue(Util.is_image("test.jpg"))
        self.assertTrue(Util.is_image("test.jpg", check_contents=True))
        self.assertTrue(Util.is_image("fake_image.jpg"))
        self.assertFalse(Util.is_image("fake_image.jpg", check_contents=True))

    def test_is_animated_gif(self):
        self.assertFalse(Util.is_animated_gif("test.jpg"))
        self.assertTrue(Util.is_animated_gif("animated.gif"))
        self.assertFalse(Util.is_animated_gif("not-animated.gif"))

    @unittest.skipIf(os.getenv("SKIP_DOWNLOADER_TESTS"), "Skipping downloader tests (SKIP_DOWNLOADER_TESTS is set)")
    def test_is_dead_or_not_image(self):
        self.assertTrue(Util.is_dead_or_not_image(None))
        self.assertTrue(Util.is_dead_or_not_image("not a URL"))
        self.assertTrue(Util.is_dead_or_not_image("http://www.cnn.com/"))
        self.assertTrue(Util.is_dead_or_not_image("http://vrty.org/"))
        self.assertTrue(Util.is_dead_or_not_image("http://www.google.com/dejkjdrelkjflkrejfjre"))
        self.assertFalse(
            Util.is_dead_or_not_image(
                "https://farm8.staticflickr.com/7133/7527967878_85fea93129_o.jpg"
            )
        )
        self.assertFalse(
            Util.is_dead_or_not_image(
                "http://interfacelift.com/wallpaper/D98ef829/00899_rustedbolt_2560x1600.jpg"
            )
        )
        self.assertTrue(
            Util.is_dead_or_not_image("http://wallpapers.wallbase.cc/rozne/wallpaper-1227671.jpg")
        )
        self.assertTrue(
            Util.is_dead_or_not_image("http://ns223506.ovh.net/rozne/a1b2/wallpaper-1996019.png")
        )

    def test_debounce(self):
        """ Test that the increment function is being debounced.
        The counter should only be incremented once 10 seconds after the last call to the function """
        count = [0]

        @debounce(0.6)
        def increment():
            count[0] += 1

        self.assertTrue(count[0] == 0)
        increment()
        increment()
        time.sleep(0.4)
        self.assertTrue(count[0] == 0)
        increment()
        increment()
        increment()
        increment()
        self.assertTrue(count[0] == 0)
        time.sleep(0.7)
        self.assertTrue(count[0] == 1)

    def test_throttle_no_trailing(self):
        count = [0]

        @throttle(seconds=0.3)
        def increment():
            count[0] += 1

        self.assertTrue(count[0] == 0)
        increment()
        self.assertTrue(count[0] == 1)
        increment()
        self.assertTrue(count[0] == 1)
        time.sleep(0.2)
        increment()
        self.assertTrue(count[0] == 1)
        time.sleep(0.2)
        increment()
        self.assertTrue(count[0] == 2)
        increment()
        self.assertTrue(count[0] == 2)
        time.sleep(0.31)
        self.assertTrue(count[0] == 2)

    def test_throttle_with_trailing(self):
        count = [0]

        @throttle(seconds=0.3, trailing_call=True)
        def increment():
            count[0] += 1

        self.assertTrue(count[0] == 0)
        increment()
        self.assertTrue(count[0] == 1)
        increment()
        self.assertTrue(count[0] == 1)
        time.sleep(0.2)
        increment()
        self.assertTrue(count[0] == 1)
        time.sleep(0.2)
        increment()
        self.assertTrue(count[0] == 2)
        increment()
        self.assertTrue(count[0] == 2)
        time.sleep(0.31)
        self.assertTrue(count[0] == 3)

    def test_cache(self):
        @cache(ttl_seconds=10)
        def fib(n):
            return 1 if n <= 1 else fib(n - 1) + fib(n - 2)

        self.assertEqual(573147844013817084101, fib(100))


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