#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2015 Qing Liang (https://github.com/liangqing)
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

from __future__ import print_function
from __future__ import unicode_literals
import unittest
import os
import os.path
from tempfile import mkstemp
from syncrypto import FileEntry, FileRule, FileRuleSet, InvalidRegularExpression
from util import format_datetime

from time import time

try:
    from cStringIO import StringIO as BytesIO
except ImportError:
    from io import BytesIO


class FileRuleTestCase(unittest.TestCase):

    def setUp(self):
        file_fp, file_path = mkstemp()
        self.file_path = file_path
        self.file_entry = FileEntry.from_file(self.file_path, os.path.basename(
            self.file_path))
        os.close(file_fp)

    def tearDown(self):
        os.remove(self.file_path)

    def test_eq(self):

        for op in ['eq', '=', '==']:
            f1 = FileRule('name', op, os.path.basename(self.file_path),
                          'include')
            f2 = FileRule('name', op, "...", 'include')
            self.assertEqual(f1.test(self.file_entry), "include")
            self.assertEqual(f2.test(self.file_entry), None)

    def test_ne(self):
        for op in ['ne', '!=', '<>']:
            f1 = FileRule('name', op, os.path.basename(self.file_path),
                          'exclude')
            f2 = FileRule('name', op, "...", 'exclude')
            self.assertEqual(f2.test(self.file_entry), "exclude")
            self.assertEqual(f1.test(self.file_entry), None)

    def test_lt(self):
        for op in ['lt', '<']:
            f1 = FileRule('size', op, 10, 'include')
            f2 = FileRule('size', op, 0, 'include')
            self.assertEqual(f1.test(self.file_entry), 'include')
            self.assertEqual(f2.test(self.file_entry), None)

    def test_gt(self):
        for op in ['gt', '>']:
            f1 = FileRule('mtime', op,
                          format_datetime(time()-3600), 'exclude')
            f2 = FileRule('mtime', op,
                          format_datetime(time()+3600), 'exclude')
            self.assertEqual(f1.test(self.file_entry), 'exclude')
            self.assertEqual(f2.test(self.file_entry), None)

    def test_gte(self):
        for op in ['gte', '>=']:
            f1 = FileRule('mtime', op,
                          format_datetime(time()-3600), 'exclude')
            f2 = FileRule('mtime', op,
                          format_datetime(time()+3600), 'exclude')
            self.assertEqual(f1.test(self.file_entry), 'exclude')
            self.assertEqual(f2.test(self.file_entry), None)

    def test_lte(self):
        self.file_entry.ctime = int(self.file_entry.ctime)
        f1 = FileRule('ctime', 'lte',
                      format_datetime(self.file_entry.ctime), 'include')
        f2 = FileRule('ctime', 'lte',
                      format_datetime(time()-3600), 'include')
        f3 = FileRule('ctime', 'eq',
                      format_datetime(self.file_entry.ctime), 'include')
        self.assertEqual(f1.test(self.file_entry), 'include')
        self.assertEqual(f2.test(self.file_entry), None)
        self.assertEqual(f3.test(self.file_entry), 'include')

    def test_match(self):
        f1 = FileRule('name', 'match', "*", 'include')
        f2 = FileRule('name', 'match', "", 'include')
        f3 = FileRule('name', 'match',
                      os.path.basename(self.file_entry.pathname), 'include')
        self.assertEqual(f1.test(self.file_entry), 'include')
        self.assertEqual(f2.test(self.file_entry), None)
        self.assertEqual(f3.test(self.file_entry), 'include')

        file_entry = self.file_entry.clone()
        file_entry.pathname = "t.test"
        f = FileRule("name", "match", "*.test", "include")
        self.assertEqual(f.test(file_entry), "include")
        file_entry.pathname = "test"
        self.assertEqual(f.test(file_entry), None)

    def test_size(self):
        self.file_entry.size = 2
        for unit in ["k", "M", "G"]:

            f = FileRule('size', '>', "1"+unit, 'include')
            self.assertEqual(f.test(self.file_entry), None)
            f = FileRule('size', '<', "1"+unit, 'include')
            self.assertEqual(f.test(self.file_entry), 'include')

            self.file_entry.size <<= 10

            f = FileRule('size', '>', "1"+unit, 'include')
            self.assertEqual(f.test(self.file_entry), 'include')
            f = FileRule('size', '<', "1"+unit, 'include')
            self.assertEqual(f.test(self.file_entry), None)
            f = FileRule('size', 'eq', "2"+unit, 'include')
            self.assertEqual(f.test(self.file_entry), 'include')

        # no unit
        self.file_entry.size = 99
        f = FileRule('size', '>', 100, 'include')
        self.assertEqual(f.test(self.file_entry), None)
        f = FileRule('size', '<', 100, 'include')
        self.assertEqual(f.test(self.file_entry), 'include')

    def regexp_invalid(self):
        FileRule('name', 'regexp', "*.txt", 'include')

    def test_regexp(self):
        self.file_entry.pathname = "test_file.txt"
        f = FileRule('name', 'regexp', "test.*", 'include')
        self.assertEqual(f.test(self.file_entry), 'include')
        f = FileRule('name', 'regexp', "test*", 'include')
        self.assertEqual(f.test(self.file_entry), None)
        self.assertRaises(InvalidRegularExpression, self.regexp_invalid)


class FileRuleSetTestCase(unittest.TestCase):

    def setUp(self):
        file_fp, file_path = mkstemp()
        self.file_path = file_path
        self.file_entry = FileEntry.from_file(self.file_path, os.path.basename(
            self.file_path))
        os.close(file_fp)

    def tearDown(self):
        os.remove(self.file_path)

    def test_basic(self):
        rule_set = FileRuleSet()
        rule_set.add("size", ">", 1024, "include")
        rule_set.add("path", "eq", self.file_entry.pathname, "exclude")
        self.assertEqual(rule_set.test(self.file_entry), "exclude")

    def test_basic_parse(self):
        rule_set = FileRuleSet()
        rule_set.add_rule_by_string("size > 1024", "include")
        rule_set.add_rule_by_string("path = "+self.file_entry.pathname,
                                    "exclude")
        self.assertEqual(rule_set.test(self.file_entry), "exclude")

    def test_basic_parse_with_quotes(self):
        rule_set = FileRuleSet()
        rule_set.add_rule_by_string("size > '1024'", "include")
        rule_set.add_rule_by_string("path = \""+self.file_entry.pathname+"\"",
                                    "exclude")
        self.assertEqual(rule_set.test(self.file_entry), "exclude")

    def test_parse_with_no_action(self):
        f = FileRuleSet.parse("include: size > 1024")
        self.assertEqual(f.action, "include")
        self.assertEqual(f.attr, "size")
        self.assertEqual(f.op, "gt")
        self.assertEqual(f.value, 1024)

    def test_parse_with_action(self):
        f = FileRuleSet.parse("size > 1024", "exclude")
        self.assertEqual(f.action, "exclude")
        self.assertEqual(f.attr, "size")
        self.assertEqual(f.op, "gt")
        self.assertEqual(f.value, 1024)

    def test_default_action(self):
        rule_set = FileRuleSet()
        rule_set.add_rule_by_string("exclude: size > 1024000")
        self.assertEqual(rule_set.test(self.file_entry),
                         rule_set.default_action)
        rule_set = FileRuleSet(default_action="laf")
        rule_set.add_rule_by_string("exclude: size > 1024000")
        self.assertEqual(rule_set.test(self.file_entry), "laf")

    def test_match_pattern(self):
        f = FileRuleSet.parse("exclude: name match *_not_sync")
        self.assertEqual(f.action, "exclude")
        self.assertEqual(f.attr, "name")
        self.assertEqual(f.op, "match")
        self.assertEqual(f.value, "*_not_sync")


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