# This program 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; either version 2 of the License, or (at your option) any later
# version.
#
# 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 (c) 2000-2011 LOGILAB S.A. (Paris, FRANCE).
 http://www.logilab.fr/ -- mailto:contact@logilab.fr

Check format checker helper functions
"""
from __future__ import with_statement

import sys
import re
from os import linesep
import tokenize
import StringIO

from logilab.common.testlib import TestCase, unittest_main
from astroid import test_utils

from pylint.checkers.format import *

from pylint.testutils import CheckerTestCase, Message, set_config


def tokenize_str(code):
    return list(tokenize.generate_tokens(StringIO.StringIO(code).readline))


class MultiStatementLineTest(CheckerTestCase):
  CHECKER_CLASS = FormatChecker

  def testSingleLineIfStmts(self):
      stmt = test_utils.extract_node("""
      if True: pass  #@
      """)
      with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])):
          self.checker.process_tokens([])
          self.checker.visit_default(stmt.body[0])
      self.checker.config.single_line_if_stmt = True
      with self.assertNoMessages():
          self.checker.process_tokens([])
          self.checker.visit_default(stmt.body[0])
      stmt = test_utils.extract_node("""
      if True: pass  #@
      else:
        pass
      """)
      with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])):
          self.checker.process_tokens([])
          self.checker.visit_default(stmt.body[0])

  def testTryExceptFinallyNoMultipleStatement(self):
      tree = test_utils.extract_node("""
      try:  #@
        pass
      except:
        pass
      finally:
        pass""")
      with self.assertNoMessages():
          self.checker.process_tokens([])
          self.checker.visit_default(tree.body[0])



class SuperfluousParenthesesTest(CheckerTestCase):
    CHECKER_CLASS = FormatChecker

    def testCheckKeywordParensHandlesValidCases(self):
        self.checker._keywords_with_parens = set()
        cases = [
            'if foo:',
            'if foo():',
            'if (x and y) or z:',
            'assert foo()',
            'assert ()',
            'if (1, 2) in (3, 4):',
            'if (a or b) in c:',
            'return (x for x in x)',
            'if (x for x in x):',
            'for x in (x for x in x):',
            'not (foo or bar)',
            'not (foo or bar) and baz',
            ]
        with self.assertNoMessages():
            for code in cases:
                self.checker._check_keyword_parentheses(tokenize_str(code), 0)

    def testCheckKeywordParensHandlesUnnecessaryParens(self):
        self.checker._keywords_with_parens = set()
        cases = [
            (Message('superfluous-parens', line=1, args='if'),
             'if (foo):', 0),
            (Message('superfluous-parens', line=1, args='if'),
             'if ((foo, bar)):', 0),
            (Message('superfluous-parens', line=1, args='if'),
             'if (foo(bar)):', 0),
            (Message('superfluous-parens', line=1, args='return'),
             'return ((x for x in x))', 0),
            (Message('superfluous-parens', line=1, args='not'),
             'not (foo)', 0),
            (Message('superfluous-parens', line=1, args='not'),
             'if not (foo):', 1),
            (Message('superfluous-parens', line=1, args='if'),
             'if (not (foo)):', 0),
            (Message('superfluous-parens', line=1, args='not'),
             'if (not (foo)):', 2),
            ]
        for msg, code, offset in cases:
            with self.assertAddsMessages(msg):
                self.checker._check_keyword_parentheses(tokenize_str(code), offset)

    def testFuturePrintStatementWithoutParensWarning(self):
        code = """from __future__ import print_function
print('Hello world!')
"""
        tree = test_utils.build_module(code)
        with self.assertNoMessages():
            self.checker.process_module(tree)
            self.checker.process_tokens(tokenize_str(code))


class CheckSpaceTest(CheckerTestCase):
    CHECKER_CLASS = FormatChecker

    def testParenthesesGood(self):
        good_cases = [
            '(a)\n',
            '(a * (b + c))\n',
            '(#\n    a)\n',
            ]
        with self.assertNoMessages():
            for code in good_cases:
                self.checker.process_tokens(tokenize_str(code))

    def testParenthesesBad(self):
        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'after', 'bracket', '( a)\n^'))):
            self.checker.process_tokens(tokenize_str('( a)\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'bracket', '(a )\n   ^'))):
            self.checker.process_tokens(tokenize_str('(a )\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'bracket', 'foo (a)\n    ^'))):
            self.checker.process_tokens(tokenize_str('foo (a)\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'bracket', '{1: 2} [1]\n       ^'))):
            self.checker.process_tokens(tokenize_str('{1: 2} [1]\n'))

    def testTrailingCommaGood(self):
        with self.assertNoMessages():
            self.checker.process_tokens(tokenize_str('(a, )\n'))
            self.checker.process_tokens(tokenize_str('(a,)\n'))

        self.checker.config.no_space_check = []
        with self.assertNoMessages():
            self.checker.process_tokens(tokenize_str('(a,)\n'))

    @set_config(no_space_check=[])
    def testTrailingCommaBad(self):
        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'bracket', '(a, )\n    ^'))):
            self.checker.process_tokens(tokenize_str('(a, )\n'))

    def testComma(self):
        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'comma', '(a , b)\n   ^'))):
            self.checker.process_tokens(tokenize_str('(a , b)\n'))

    def testSpacesAllowedInsideSlices(self):
        good_cases = [
            '[a:b]\n',
            '[a : b]\n',
            '[a : ]\n',
            '[:a]\n',
            '[:]\n',
            '[::]\n',
            ]
        with self.assertNoMessages():
            for code in good_cases:
                self.checker.process_tokens(tokenize_str(code))

    def testKeywordSpacingGood(self):
        with self.assertNoMessages():
            self.checker.process_tokens(tokenize_str('foo(foo=bar)\n'))
            self.checker.process_tokens(tokenize_str('lambda x=1: x\n'))

    def testKeywordSpacingBad(self):
        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'before', 'keyword argument assignment',
                          '(foo =bar)\n     ^'))):
            self.checker.process_tokens(tokenize_str('(foo =bar)\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'after', 'keyword argument assignment',
                          '(foo= bar)\n    ^'))):
            self.checker.process_tokens(tokenize_str('(foo= bar)\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('No', 'allowed', 'around', 'keyword argument assignment',
                          '(foo = bar)\n     ^'))):
            self.checker.process_tokens(tokenize_str('(foo = bar)\n'))

    def testOperatorSpacingGood(self):
        good_cases = [
            'a = b\n'
            'a < b\n'
            'a\n< b\n',
            ]
        with self.assertNoMessages():
            for code in good_cases:
                self.checker.process_tokens(tokenize_str(code))

    def testOperatorSpacingBad(self):
        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('Exactly one', 'required', 'before', 'comparison', 'a< b\n ^'))):
            self.checker.process_tokens(tokenize_str('a< b\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('Exactly one', 'required', 'after', 'comparison', 'a <b\n  ^'))):
            self.checker.process_tokens(tokenize_str('a <b\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('Exactly one', 'required', 'around', 'comparison', 'a<b\n ^'))):
            self.checker.process_tokens(tokenize_str('a<b\n'))

        with self.assertAddsMessages(
            Message('bad-whitespace', line=1,
                    args=('Exactly one', 'required', 'around', 'comparison', 'a<  b\n ^'))):
            self.checker.process_tokens(tokenize_str('a<  b\n'))


if __name__ == '__main__':
    unittest_main()
