# -*- coding: utf-8 -*-
#
# Copyright (C) 2006-2013 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.

import doctest
import unittest
from StringIO import StringIO

import trac.tests.compat
from trac.core import *
from trac.test import EnvironmentStub
from trac.mimeview import api
from trac.mimeview.api import get_mimetype, IContentConverter, Mimeview, \
                              _group_lines
from genshi import Stream, Namespace
from genshi.core import Attrs, TEXT, START, END
from genshi.input import HTMLParser


class GetMimeTypeTestCase(unittest.TestCase):

    def test_from_suffix_using_MIME_MAP(self):
        self.assertEqual('text/plain', get_mimetype('README', None))
        self.assertEqual('text/plain', get_mimetype('README.txt', None))

    def test_from_suffix_using_mimetypes(self):
        accepted = ('image/png', 'image/x-png')
        self.assertTrue(get_mimetype('doc/trac_logo.png', None) in accepted)

    def test_from_content_using_CONTENT_RE(self):
        self.assertEqual('text/x-python',
                         get_mimetype('xxx', """
#!/usr/bin/python
# This is a python script
"""))
        self.assertEqual('text/x-python',
                         get_mimetype('xxx', """
#!/usr/bin/env python
# This is a python script
"""))
        self.assertEqual('text/x-ksh',
                         get_mimetype('xxx', """
#!/bin/ksh
# This is a shell script
"""))
        self.assertEqual('text/x-python',
                         get_mimetype('xxx', """
# -*- Python -*-
# This is a python script
"""))
        self.assertEqual('text/x-ruby',
                         get_mimetype('xxx', """
# -*- mode: ruby -*-
# This is a ruby script
"""))
        self.assertEqual('text/x-python',
                         get_mimetype('xxx', ' ' * 2000 + '# vim: ft=python'))

    def test_from_content_using_is_binary(self):
        self.assertEqual('application/octet-stream',
                         get_mimetype('xxx', "abc\0xyz"))


class MimeviewTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub(default_data=False,
            enable=['%s.%s' % (self.__module__, c)
                    for c in ['Converter0', 'Converter1', 'Converter2']])

    def tearDown(self):
        pass

    def test_get_supported_conversions(self):
        class Converter0(Component):
            implements(IContentConverter)
            def get_supported_conversions(self):
                yield 'key0', 'Format 0', 'c0', 'text/x-sample', 'text/html', 8

        class Converter2(Component):
            implements(IContentConverter)
            def get_supported_conversions(self):
                yield 'key2', 'Format 2', 'c2', 'text/x-sample', 'text/html', 2

        class Converter1(Component):
            implements(IContentConverter)
            def get_supported_conversions(self):
                yield 'key1', 'Format 1', 'c1', 'text/x-sample', 'text/html', 4

        mimeview = Mimeview(self.env)
        conversions = mimeview.get_supported_conversions('text/x-sample')
        self.assertEqual(Converter0(self.env), conversions[0][-1])
        self.assertEqual(Converter1(self.env), conversions[1][-1])
        self.assertEqual(Converter2(self.env), conversions[2][-1])

class GroupLinesTestCase(unittest.TestCase):

    def test_empty_stream(self):
        # FIXME: this currently fails
        lines = list(_group_lines([]))
        self.assertEqual(len(lines), 0)

    def test_text_only_stream(self):
        input = [(TEXT, "test", (None, -1, -1))]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 1)
        self.assertIsInstance(lines[0], Stream)
        self.assertEqual(lines[0].events, input)

    def test_text_only_stream2(self):
        input = [(TEXT, "test\n", (None, -1, -1))]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 1)
        self.assertIsInstance(lines[0], Stream)
        self.assertEqual(lines[0].events, [(TEXT, "test", (None, -1, -1))])

    def test_simplespan(self):
        input = HTMLParser(StringIO(u"<span>test</span>"), encoding=None)
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 1)
        self.assertIsInstance(lines[0], Stream)
        for (a, b) in zip(lines[0], input):
            self.assertEqual(a, b)

    def test_empty_text_stream(self):
        """
        http://trac.edgewall.org/ticket/4336
        """
        input = [(TEXT, "", (None, -1, -1))]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 0)

    def test_newline_stream(self):
        input = [(TEXT, "\n", (None, -1, -1))]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 1)

    def test_newline_stream2(self):
        input = [(TEXT, "\n\n\n", (None, -1, -1))]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 3)

    def test_empty_text_in_span(self):
        """
        http://trac.edgewall.org/ticket/4336
        """
        ns = Namespace('http://www.w3.org/1999/xhtml')
        input = [(START, (ns.span, Attrs([])), (None, -1, -1)),
                 (TEXT, "", (None, -1, -1)),
                 (END, ns.span, (None, -1, -1)),
                ]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), 0)

    def test_newline(self):
        """
        If the text element does not end with a newline, it's not properly
        closed.
        """
        input = HTMLParser(StringIO(u'<span class="c">a\nb</span>'),
            encoding=None)
        expected = ['<span class="c">a</span>',
                    '<span class="c">b</span>',
                   ]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), len(expected))
        for a, b in zip(lines, expected):
            self.assertEqual(a.render('html'), b)

    def test_newline2(self):
        """
        Same as test_newline above, but make sure it behaves properly wrt
        the trailing \\n, especially given it's inside an element.
        """
        input = HTMLParser(StringIO(u'<span class="c">a\nb\n</span>'),
            encoding=None)
        expected = ['<span class="c">a</span>',
                    '<span class="c">b</span>',
                   ]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), len(expected))
        for a, b in zip(lines, expected):
            self.assertEqual(a.render('html'), b)

    def test_multinewline(self):
        """
        ditto.
        """
        input = HTMLParser(StringIO(u'<span class="c">\n\n\na</span>'),
            encoding=None)
        expected = ['<span class="c"></span>',
                    '<span class="c"></span>',
                    '<span class="c"></span>',
                    '<span class="c">a</span>',
                   ]
        lines = list(_group_lines(input))
        self.assertEqual(len(lines), len(expected))
        for a, b in zip(lines, expected):
            self.assertEqual(a.render('html'), b)


def suite():
    suite = unittest.TestSuite()
    suite.addTest(doctest.DocTestSuite(api))
    suite.addTest(unittest.makeSuite(GetMimeTypeTestCase))
    suite.addTest(unittest.makeSuite(MimeviewTestCase))
    suite.addTest(unittest.makeSuite(GroupLinesTestCase))
    return suite

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