# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of astroid.
#
# astroid is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# astroid 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 Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with astroid. If not, see <http://www.gnu.org/licenses/>.

"""Tests for the astroid AST peephole optimizer."""

import ast
import textwrap
import unittest

import astroid
from astroid import astpeephole
from astroid import manager
from astroid import test_utils
from astroid.tests import resources


MANAGER = manager.AstroidManager()


class PeepholeOptimizer(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        MANAGER.optimize_ast = True

    @classmethod
    def tearDownClass(cls):
        MANAGER.optimize_ast = False

    def setUp(self):
        self._optimizer = astpeephole.ASTPeepholeOptimizer()

    @staticmethod
    def _get_binops(code):
        module = ast.parse(textwrap.dedent(code))
        return [node.value for node in module.body
                if isinstance(node, ast.Expr)]

    @test_utils.require_version(maxver='3.0')
    def test_optimize_binop_unicode(self):
        nodes = self._get_binops("""
        u"a" + u"b" + u"c"

        u"a" + "c" + "b"
        u"a" + b"c"
        """)

        result = self._optimizer.optimize_binop(nodes[0])
        self.assertIsInstance(result, astroid.Const)
        self.assertEqual(result.value, u"abc")

        self.assertIsNone(self._optimizer.optimize_binop(nodes[1]))
        self.assertIsNone(self._optimizer.optimize_binop(nodes[2]))

    def test_optimize_binop(self):
        nodes = self._get_binops("""
        "a" + "b" + "c" + "d"
        b"a" + b"b" + b"c" + b"d"
        "a" + "b"

        "a" + "b" + 1 + object
        var = 4
        "a" + "b" + var + "c"
        "a" + "b" + "c" - "4"
        "a" + "b" + "c" + "d".format()
        "a" - "b"
        "a"
        1 + 4 + 5 + 6
        """)

        result = self._optimizer.optimize_binop(nodes[0])
        self.assertIsInstance(result, astroid.Const)
        self.assertEqual(result.value, "abcd")

        result = self._optimizer.optimize_binop(nodes[1])
        self.assertIsInstance(result, astroid.Const)
        self.assertEqual(result.value, b"abcd")

        for node in nodes[2:]:
            self.assertIsNone(self._optimizer.optimize_binop(node))

    def test_big_binop_crash(self):
        # Test that we don't fail on a lot of joined strings
        # through the addition operator.
        module = resources.build_file('data/joined_strings.py')
        element = next(module['x'].infer())
        self.assertIsInstance(element, astroid.Const)
        self.assertEqual(len(element.value), 61660)

    def test_optimisation_disabled(self):
        try:
            MANAGER.optimize_ast = False
            module = test_utils.build_module("""
            '1' + '2' + '3'
            """)
            self.assertIsInstance(module.body[0], astroid.Discard)
            self.assertIsInstance(module.body[0].value, astroid.BinOp)
            self.assertIsInstance(module.body[0].value.left, astroid.BinOp)
            self.assertIsInstance(module.body[0].value.left.left,
                                  astroid.Const)
        finally:
            MANAGER.optimize_ast = True


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