#!/usr/bin/env python
# coding: utf-8

"""Test suite for autoflake."""

from __future__ import unicode_literals

import contextlib
import io
import os
import re
import shutil
import subprocess
import sys
import tempfile
import unittest

import autoflake


ROOT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))


if (
    'AUTOFLAKE_COVERAGE' in os.environ and
    int(os.environ['AUTOFLAKE_COVERAGE'])
):
    AUTOFLAKE_COMMAND = ['coverage', 'run', '--branch', '--parallel',
                         '--omit=*/distutils/*,*/site-packages/*',
                         os.path.join(ROOT_DIRECTORY, 'autoflake.py')]
else:
    # We need to specify the executable to make sure the correct Python
    # interpreter gets used.
    AUTOFLAKE_COMMAND = [sys.executable,
                         os.path.join(
                             ROOT_DIRECTORY,
                             'autoflake.py')]  # pragma: no cover


class UnitTests(unittest.TestCase):

    """Unit tests."""

    def test_imports(self):
        self.assertGreater(len(autoflake.SAFE_IMPORTS), 0)

    def test_unused_import_line_numbers(self):
        self.assertEqual(
            [1],
            list(autoflake.unused_import_line_numbers(
                autoflake.check('import os\n'))))

    def test_unused_import_line_numbers_with_from(self):
        self.assertEqual(
            [1],
            list(autoflake.unused_import_line_numbers(
                autoflake.check('from os import path\n'))))

    def test_unused_import_line_numbers_with_dot(self):
        self.assertEqual(
            [1],
            list(autoflake.unused_import_line_numbers(
                autoflake.check('import os.path\n'))))

    def test_extract_package_name(self):
        self.assertEqual('os', autoflake.extract_package_name('import os'))
        self.assertEqual(
            'os', autoflake.extract_package_name('from os import path'))
        self.assertEqual(
            'os', autoflake.extract_package_name('import os.path'))

    def test_extract_package_name_should_ignore_doctest_for_now(self):
        self.assertFalse(autoflake.extract_package_name('>>> import os'))

    def test_standard_package_names(self):
        self.assertIn('os', list(autoflake.standard_package_names()))
        self.assertIn('subprocess', list(autoflake.standard_package_names()))
        self.assertIn('urllib', list(autoflake.standard_package_names()))

        self.assertNotIn('autoflake', list(autoflake.standard_package_names()))
        self.assertNotIn('pep8', list(autoflake.standard_package_names()))

    def test_get_line_ending(self):
        self.assertEqual('\n', autoflake.get_line_ending('\n'))
        self.assertEqual('\n', autoflake.get_line_ending('abc\n'))
        self.assertEqual('\t  \t\n', autoflake.get_line_ending('abc\t  \t\n'))

        self.assertEqual('', autoflake.get_line_ending('abc'))
        self.assertEqual('', autoflake.get_line_ending(''))

    def test_get_indentation(self):
        self.assertEqual('', autoflake.get_indentation(''))
        self.assertEqual('    ', autoflake.get_indentation('    abc'))
        self.assertEqual('    ', autoflake.get_indentation('    abc  \n\t'))
        self.assertEqual('\t', autoflake.get_indentation('\tabc  \n\t'))
        self.assertEqual(' \t ', autoflake.get_indentation(' \t abc  \n\t'))
        self.assertEqual('', autoflake.get_indentation('    '))

    def test_filter_star_import(self):
        self.assertEqual(
            'from math import cos',
            autoflake.filter_star_import('from math import *',
                                         ['cos']))

        self.assertEqual(
            'from math import cos, sin',
            autoflake.filter_star_import('from math import *',
                                         ['sin', 'cos']))

    def test_filter_unused_variable(self):
        self.assertEqual('foo()',
                         autoflake.filter_unused_variable('x = foo()'))

        self.assertEqual('    foo()',
                         autoflake.filter_unused_variable('    x = foo()'))

    def test_filter_unused_variable_with_literal_or_name(self):
        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = 1'))

        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = y'))

        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = {}'))

    def test_filter_unused_variable_with_basic_data_structures(self):
        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = dict()'))

        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = list()'))

        self.assertEqual('pass',
                         autoflake.filter_unused_variable('x = set()'))

    def test_filter_unused_variable_should_ignore_multiline(self):
        self.assertEqual('x = foo()\\',
                         autoflake.filter_unused_variable('x = foo()\\'))

    def test_filter_unused_variable_should_multiple_assignments(self):
        self.assertEqual('x = y = foo()',
                         autoflake.filter_unused_variable('x = y = foo()'))

    def test_filter_unused_variable_with_exception(self):
        self.assertEqual(
            'except Exception:',
            autoflake.filter_unused_variable('except Exception as exception:'))

        self.assertEqual(
            'except (ImportError, ValueError):',
            autoflake.filter_unused_variable(
                'except (ImportError, ValueError) as foo:'))

    def test_filter_code(self):
        self.assertEqual(
            """\
import os
pass
os.foo()
""",
            ''.join(autoflake.filter_code("""\
import os
import re
os.foo()
""")))

    def test_filter_code_with_indented_import(self):
        self.assertEqual(
            """\
import os
if True:
    pass
os.foo()
""",
            ''.join(autoflake.filter_code("""\
import os
if True:
    import re
os.foo()
""")))

    def test_filter_code_with_from(self):
        self.assertEqual(
            """\
pass
x = 1
""",
            ''.join(autoflake.filter_code("""\
from os import path
x = 1
""")))

    def test_filter_code_with_not_from(self):
        self.assertEqual(
            """\
pass
x = 1
""",
            ''.join(autoflake.filter_code("""\
import frommer
x = 1
""",
                                          remove_all_unused_imports=True)))

    def test_filter_code_with_used_from(self):
        self.assertEqual(
            """\
import frommer
print(frommer)
""",
            ''.join(autoflake.filter_code("""\
import frommer
print(frommer)
""",
                                          remove_all_unused_imports=True)))

    def test_filter_code_with_ambiguous_from(self):
        self.assertEqual(
            """\
pass
""",
            ''.join(autoflake.filter_code("""\
from frommer import abc, frommer, xyz
""",
                                          remove_all_unused_imports=True)))

    def test_filter_code_should_avoid_inline_except(self):
        line = """\
try: from zap import foo
except: from zap import bar
"""
        self.assertEqual(
            line,
            ''.join(autoflake.filter_code(line,
                                          remove_all_unused_imports=True)))

    def test_filter_code_should_avoid_escaped_newlines(self):
        line = """\
try:\\
from zap import foo
except:\\
from zap import bar
"""
        self.assertEqual(
            line,
            ''.join(autoflake.filter_code(line,
                                          remove_all_unused_imports=True)))

    def test_filter_code_with_remove_all_unused_imports(self):
        self.assertEqual(
            """\
pass
pass
x = 1
""",
            ''.join(autoflake.filter_code("""\
import foo
import zap
x = 1
""", remove_all_unused_imports=True)))

    def test_filter_code_with_additional_imports(self):
        self.assertEqual(
            """\
pass
import zap
x = 1
""",
            ''.join(autoflake.filter_code("""\
import foo
import zap
x = 1
""", additional_imports=['foo', 'bar'])))

    def test_filter_code_should_ignore_imports_with_inline_comment(self):
        self.assertEqual(
            """\
from os import path  # foo
pass
from fake_foo import z  # foo, foo, zap
x = 1
""",
            ''.join(autoflake.filter_code("""\
from os import path  # foo
from os import path
from fake_foo import z  # foo, foo, zap
x = 1
""")))

    def test_filter_code_should_respect_noqa(self):
        self.assertEqual(
            """\
pass
import re  # noqa
from subprocess import Popen  # NOQA
x = 1
""",
            ''.join(autoflake.filter_code("""\
from os import path
import re  # noqa
from subprocess import Popen  # NOQA
x = 1
""")))

    def test_filter_code_expand_star_imports(self):
        self.assertEqual(
            """\
from math import sin
sin(1)
""",
            ''.join(autoflake.filter_code("""\
from math import *
sin(1)
""", expand_star_imports=True)))

        self.assertEqual(
            """\
from math import cos, sin
sin(1)
cos(1)
""",
            ''.join(autoflake.filter_code("""\
from math import *
sin(1)
cos(1)
""", expand_star_imports=True)))

    def test_filter_code_ignore_multiple_star_import(self):
        self.assertEqual(
            """\
from math import *
from re import *
sin(1)
cos(1)
""",
            ''.join(autoflake.filter_code("""\
from math import *
from re import *
sin(1)
cos(1)
""", expand_star_imports=True)))

    def test_filter_code_with_special_re_symbols_in_key(self):
        self.assertEqual(
            """\
a = {
  '????': 2,
}
print(a)
""",
            ''.join(autoflake.filter_code("""\
a = {
  '????': 3,
  '????': 2,
}
print(a)
""", remove_duplicate_keys=True)))

    def test_multiline_import(self):
        self.assertTrue(autoflake.multiline_import(r"""\
import os, \
    math, subprocess
"""))

        self.assertFalse(autoflake.multiline_import("""\
import os, math, subprocess
"""))

        self.assertTrue(autoflake.multiline_import("""\
import os, math, subprocess
""", previous_line='if: \\\n'))

        self.assertTrue(
            autoflake.multiline_import('from os import (path, sep)'))

    def test_multiline_statement(self):
        self.assertFalse(autoflake.multiline_statement('x = foo()'))

        self.assertTrue(autoflake.multiline_statement('x = 1;'))
        self.assertTrue(autoflake.multiline_statement('import os, \\'))
        self.assertTrue(autoflake.multiline_statement('foo('))
        self.assertTrue(autoflake.multiline_statement('1',
                                                      previous_line='x = \\'))

    def test_break_up_import(self):
        self.assertEqual(
            'import abc\nimport math\nimport subprocess\n',
            autoflake.break_up_import('import abc, subprocess, math\n'))

    def test_break_up_import_with_indentation(self):
        self.assertEqual(
            '    import abc\n    import math\n    import subprocess\n',
            autoflake.break_up_import('    import abc, subprocess, math\n'))

    def test_break_up_import_should_do_nothing_on_no_line_ending(self):
        self.assertEqual(
            'import abc, subprocess, math',
            autoflake.break_up_import('import abc, subprocess, math'))

    def test_filter_from_import_no_remove(self):
        self.assertEqual(
            """\
    from foo import abc, math, subprocess\n""",
            autoflake.filter_from_import(
                '    from foo import abc, subprocess, math\n',
                unused_module=[]))

    def test_filter_from_import_remove_module(self):
        self.assertEqual(
            """\
    from foo import math, subprocess\n""",
            autoflake.filter_from_import(
                '    from foo import abc, subprocess, math\n',
                unused_module=['foo.abc']))

    def test_filter_from_import_remove_all(self):
        self.assertEqual(
            '    pass\n',
            autoflake.filter_from_import(
                '    from foo import abc, subprocess, math\n',
                unused_module=['foo.abc', 'foo.subprocess',
                               'foo.math']))

    def test_filter_code_should_ignore_multiline_imports(self):
        self.assertEqual(
            r"""\
import os
pass
import os, \
    math, subprocess
os.foo()
""",
            ''.join(autoflake.filter_code(r"""\
import os
import re
import os, \
    math, subprocess
os.foo()
""")))

    def test_filter_code_should_ignore_semicolons(self):
        self.assertEqual(
            r"""\
import os
pass
import os; import math, subprocess
os.foo()
""",
            ''.join(autoflake.filter_code(r"""\
import os
import re
import os; import math, subprocess
os.foo()
""")))

    def test_filter_code_should_ignore_non_standard_library(self):
        self.assertEqual(
            """\
import os
import my_own_module
pass
from my_package import another_module
from my_package import subprocess
from my_blah.my_blah_blah import blah
os.foo()
""",
            ''.join(autoflake.filter_code("""\
import os
import my_own_module
import re
from my_package import another_module
from my_package import subprocess
from my_blah.my_blah_blah import blah
os.foo()
""")))

    def test_filter_code_should_ignore_unsafe_imports(self):
        self.assertEqual(
            """\
import rlcompleter
pass
pass
pass
print(1)
""",
            ''.join(autoflake.filter_code("""\
import rlcompleter
import sys
import io
import os
print(1)
""")))

    def test_filter_code_should_ignore_docstring(self):
        line = """
def foo():
    '''
    >>> import math
    '''
"""
        self.assertEqual(line, ''.join(autoflake.filter_code(line)))

    def test_fix_code(self):
        self.assertEqual(
            """\
import os
import math
from sys import version
os.foo()
math.pi
x = version
""",
            autoflake.fix_code("""\
import os
import re
import abc, math, subprocess
from sys import exit, version
os.foo()
math.pi
x = version
"""))

    def test_fix_code_with_from_and_as(self):
        self.assertEqual(
            """\
from collections import namedtuple as xyz
xyz
""",
            autoflake.fix_code("""\
from collections import defaultdict, namedtuple as xyz
xyz
"""))

        self.assertEqual(
            """\
from collections import namedtuple as xyz
xyz
""",
            autoflake.fix_code("""\
from collections import defaultdict as abc, namedtuple as xyz
xyz
"""))

        self.assertEqual(
            """\
from collections import namedtuple
namedtuple
""",
            autoflake.fix_code("""\
from collections import defaultdict as abc, namedtuple
namedtuple
"""))

        self.assertEqual(
            """\
""",
            autoflake.fix_code("""\
from collections import defaultdict as abc, namedtuple as xyz
"""))

    def test_fix_code_with_from_with_and_without_remove_all(self):
        code = """\
from x import a as b, c as d
"""

        self.assertEqual(
            """\
""",
            autoflake.fix_code(code, remove_all_unused_imports=True))

        self.assertEqual(
            code,
            autoflake.fix_code(code, remove_all_unused_imports=False))

    def test_fix_code_with_from_and_depth_module(self):
        self.assertEqual(
            """\
from distutils.version import StrictVersion
StrictVersion('1.0.0')
""",
            autoflake.fix_code("""\
from distutils.version import LooseVersion, StrictVersion
StrictVersion('1.0.0')
"""))

        self.assertEqual(
            """\
from distutils.version import StrictVersion as version
version('1.0.0')
""",
            autoflake.fix_code("""\
from distutils.version import LooseVersion, StrictVersion as version
version('1.0.0')
"""))

    def test_fix_code_with_indented_from(self):
        self.assertEqual(
            """\
def z():
    from ctypes import POINTER, byref
    POINTER, byref
    """,
            autoflake.fix_code("""\
def z():
    from ctypes import c_short, c_uint, c_int, c_long, pointer, POINTER, byref
    POINTER, byref
    """))

        self.assertEqual(
            """\
def z():
    pass
""",
            autoflake.fix_code("""\
def z():
    from ctypes import c_short, c_uint, c_int, c_long, pointer, POINTER, byref
"""))

    def test_fix_code_with_empty_string(self):
        self.assertEqual(
            '',
            autoflake.fix_code(''))

    def test_fix_code_with_from_and_as_and_escaped_newline(self):
        """Make sure stuff after escaped newline is not lost."""
        result = autoflake.fix_code("""\
from collections import defaultdict, namedtuple \\
    as xyz
xyz
""")
        # We currently leave lines with escaped newlines as is. But in the
        # future this we may parse them and remove unused import accordingly.
        # For now, we'll work around it here.
        result = re.sub(r' *\\\n *as ', ' as ', result)

        self.assertEqual(
            """\
from collections import namedtuple as xyz
xyz
""",
            autoflake.fix_code(result))

    def test_fix_code_with_unused_variables(self):
        self.assertEqual(
            """\
def main():
    y = 11
    print(y)
""",
            autoflake.fix_code("""\
def main():
    x = 10
    y = 11
    print(y)
""",
                               remove_unused_variables=True))

    def test_fix_code_with_unused_variables_should_skip_nonlocal(self):
        """pyflakes does not handle nonlocal correctly."""
        code = """\
def bar():
    x = 1

    def foo():
        nonlocal x
        x = 2
"""
        self.assertEqual(
            code,
            autoflake.fix_code(code,
                               remove_unused_variables=True))

    def test_detect_encoding_with_bad_encoding(self):
        with temporary_file('# -*- coding: blah -*-\n') as filename:
            self.assertEqual('latin-1',
                             autoflake.detect_encoding(filename))

    def test_fix_code_with_comma_on_right(self):
        """pyflakes does not handle nonlocal correctly."""
        self.assertEqual(
            """\
def main():
    pass
""",
            autoflake.fix_code("""\
def main():
    x = (1, 2, 3)
""",
                               remove_unused_variables=True))

    def test_fix_code_with_unused_variables_should_skip_multiple(self):
        code = """\
def main():
    (x, y, z) = (1, 2, 3)
    print(z)
"""
        self.assertEqual(
            code,
            autoflake.fix_code(code,
                               remove_unused_variables=True))

    def test_fix_code_should_handle_pyflakes_recursion_error_gracefully(self):
        code = 'x = [{}]'.format('+'.join(['abc' for _ in range(2000)]))
        self.assertEqual(
            code,
            autoflake.fix_code(code))

    def test_fix_code_with_duplicate_key(self):
        self.assertEqual(
            """\
a = {
  (0,1): 3,
}
print(a)
""",
            ''.join(autoflake.fix_code("""\
a = {
  (0,1): 1,
  (0, 1): 'two',
  (0,1): 3,
}
print(a)
""", remove_duplicate_keys=True)))

    def test_fix_code_with_duplicate_key_longer(self):
        self.assertEqual(
            """\
{
    'a': 0,
    'c': 2,
    'd': 3,
    'e': 4,
    'f': 5,
    'b': 6,
}
""",
            ''.join(autoflake.fix_code("""\
{
    'a': 0,
    'b': 1,
    'c': 2,
    'd': 3,
    'e': 4,
    'f': 5,
    'b': 6,
}
""", remove_duplicate_keys=True)))

    def test_fix_code_with_duplicate_key_with_many_braces(self):
        self.assertEqual(
            """\
a = None

{None: {None: None},
 }

{
    None: a.b,
}
""",
            ''.join(autoflake.fix_code("""\
a = None

{None: {None: None},
 }

{
    None: a.a,
    None: a.b,
}
""", remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_complex_case_of_duplicate_key(self):
        """We only handle simple cases."""
        code = """\
a = {(0,1): 1, (0, 1): 'two',
  (0,1): 3,
}
print(a)
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_complex_case_of_duplicate_key_comma(self):
        """We only handle simple cases."""
        code = """\
{
    1: {0,
    },
    1: {2,
    },
}
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_complex_case_of_duplicate_key_partially(
            self):
        """We only handle simple cases."""
        code = """\
a = {(0,1): 1, (0, 1): 'two',
  (0,1): 3,
  (2,3): 4,
  (2,3): 4,
  (2,3): 5,
}
print(a)
"""

        self.assertEqual(
            """\
a = {(0,1): 1, (0, 1): 'two',
  (0,1): 3,
  (2,3): 5,
}
print(a)
""",
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_more_cases_of_duplicate_key(self):
        """We only handle simple cases."""
        code = """\
a = {
    (0,1):
    1,
    (0, 1): 'two',
  (0,1): 3,
}
print(a)
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_duplicate_key_with_comments(self):
        """We only handle simple cases."""
        code = """\
a = {
    (0,1)  # : f
    :
    1,
    (0, 1): 'two',
  (0,1): 3,
}
print(a)
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

        code = """\
{
    1: {0,
    },
    1: #{2,
    #},
    0
}
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_duplicate_key_with_multiline_key(self):
        """We only handle simple cases."""
        code = """\
a = {
    (0,1
    ): 1,
    (0, 1): 'two',
  (0,1): 3,
}
print(a)
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_fix_code_should_ignore_duplicate_key_with_no_comma(self):
        """We don't want to delete the line and leave a lone comma."""
        code = """\
a = {
    (0,1) : 1
    ,
    (0, 1): 'two',
  (0,1): 3,
}
print(a)
"""

        self.assertEqual(
            code,
            ''.join(autoflake.fix_code(code,
                                       remove_duplicate_keys=True)))

    def test_useless_pass_line_numbers(self):
        self.assertEqual(
            [1],
            list(autoflake.useless_pass_line_numbers(
                'pass\n')))

        self.assertEqual(
            [],
            list(autoflake.useless_pass_line_numbers(
                'if True:\n    pass\n')))

    def test_useless_pass_line_numbers_with_escaped_newline(self):
        self.assertEqual(
            [],
            list(autoflake.useless_pass_line_numbers(
                'if True:\\\n    pass\n')))

    def test_useless_pass_line_numbers_with_more_complex(self):
        self.assertEqual(
            [6],
            list(autoflake.useless_pass_line_numbers(
                """\
if True:
    pass
else:
    True
    x = 1
    pass
""")))

    def test_filter_useless_pass(self):
        self.assertEqual(
            """\
if True:
    pass
else:
    True
    x = 1
""",
            ''.join(autoflake.filter_useless_pass(
                """\
if True:
    pass
else:
    True
    x = 1
    pass
""")))

    def test_filter_useless_pass_with_syntax_error(self):
        source = """\
if True:
if True:
            if True:
    if True:

if True:
    pass
else:
    True
    pass
    pass
    x = 1
"""

        self.assertEqual(
            source,
            ''.join(autoflake.filter_useless_pass(source)))

    def test_filter_useless_pass_more_complex(self):
        self.assertEqual(
            """\
if True:
    pass
else:
    def foo():
        pass
        # abc
    def bar():
        # abc
        pass
    def blah():
        123
        pass  # Nope.
    True
    x = 1
""",
            ''.join(autoflake.filter_useless_pass(
                """\
if True:
    pass
else:
    def foo():
        pass
        # abc
    def bar():
        # abc
        pass
    def blah():
        123
        pass
        pass  # Nope.
        pass
    True
    x = 1
    pass
""")))

    def test_filter_useless_pass_with_try(self):
        self.assertEqual(
            """\
import os
os.foo()
try:
    pass
except ImportError:
    pass
""",
            ''.join(autoflake.filter_useless_pass(
                """\
import os
os.foo()
try:
    pass
    pass
except ImportError:
    pass
""")))

    def test_filter_useless_pass_leading_pass(self):
        self.assertEqual(
            """\
if True:
    pass
else:
    True
    x = 1
""",
            ''.join(autoflake.filter_useless_pass(
                """\
if True:
    pass
    pass
    pass
    pass
else:
    pass
    True
    x = 1
    pass
""")))

    def test_filter_useless_pass_leading_pass_with_number(self):
        self.assertEqual(
            """\
def func11():
    0, 11 / 2
    return 1
""",
            ''.join(autoflake.filter_useless_pass(
                """\
def func11():
    pass
    0, 11 / 2
    return 1
""")))

    def test_filter_useless_pass_leading_pass_with_string(self):
        self.assertEqual(
            """\
def func11():
    'hello'
    return 1
""",
            ''.join(autoflake.filter_useless_pass(
                """\
def func11():
    pass
    'hello'
    return 1
""")))

    def test_check(self):
        self.assertTrue(autoflake.check('import os'))

    def test_check_with_bad_syntax(self):
        self.assertFalse(autoflake.check('foo('))

    def test_check_with_unicode(self):
        self.assertFalse(autoflake.check('print("∑")'))

        self.assertTrue(autoflake.check('import os  # ∑'))

    def test_get_diff_text(self):
        # We ignore the first two lines since it differs on Python 2.6.
        self.assertEqual(
            """\
-foo
+bar
""",
            '\n'.join(autoflake.get_diff_text(['foo\n'],
                                              ['bar\n'],
                                              '').split('\n')[3:]))

    def test_get_diff_text_without_newline(self):
        # We ignore the first two lines since it differs on Python 2.6.
        self.assertEqual(
            """\
-foo
\\ No newline at end of file
+foo
""",
            '\n'.join(autoflake.get_diff_text(['foo'],
                                              ['foo\n'],
                                              '').split('\n')[3:]))

    def test_is_literal_or_name(self):
        self.assertTrue(autoflake.is_literal_or_name('123'))
        self.assertTrue(autoflake.is_literal_or_name('[1, 2, 3]'))
        self.assertTrue(autoflake.is_literal_or_name('xyz'))

        self.assertFalse(autoflake.is_literal_or_name('xyz.prop'))
        self.assertFalse(autoflake.is_literal_or_name(' '))
        self.assertFalse(autoflake.is_literal_or_name(' 1'))

    def test_is_python_file(self):
        self.assertTrue(autoflake.is_python_file(
            os.path.join(ROOT_DIRECTORY, 'autoflake.py')))

        with temporary_file('#!/usr/bin/env python', suffix='') as filename:
            self.assertTrue(autoflake.is_python_file(filename))

        with temporary_file('#!/usr/bin/python', suffix='') as filename:
            self.assertTrue(autoflake.is_python_file(filename))

        with temporary_file('#!/usr/bin/python3', suffix='') as filename:
            self.assertTrue(autoflake.is_python_file(filename))

        with temporary_file('#!/usr/bin/pythonic', suffix='') as filename:
            self.assertFalse(autoflake.is_python_file(filename))

        with temporary_file('###!/usr/bin/python', suffix='') as filename:
            self.assertFalse(autoflake.is_python_file(filename))

        self.assertFalse(autoflake.is_python_file(os.devnull))
        self.assertFalse(autoflake.is_python_file('/bin/bash'))

    def test_match_file(self):
        with temporary_file('', suffix='.py', prefix='.') as filename:
            self.assertFalse(autoflake.match_file(filename, exclude=[]),
                             msg=filename)

        self.assertFalse(autoflake.match_file(os.devnull, exclude=[]))

        with temporary_file('', suffix='.py', prefix='') as filename:
            self.assertTrue(autoflake.match_file(filename, exclude=[]),
                            msg=filename)

    def test_find_files(self):
        temp_directory = tempfile.mkdtemp()
        try:
            target = os.path.join(temp_directory, 'dir')
            os.mkdir(target)
            with open(os.path.join(target, 'a.py'), 'w'):
                pass

            exclude = os.path.join(target, 'ex')
            os.mkdir(exclude)
            with open(os.path.join(exclude, 'b.py'), 'w'):
                pass

            sub = os.path.join(exclude, 'sub')
            os.mkdir(sub)
            with open(os.path.join(sub, 'c.py'), 'w'):
                pass

            # FIXME: Avoid changing directory. This may interfere with parallel
            # test runs.
            cwd = os.getcwd()
            os.chdir(temp_directory)
            try:
                files = list(autoflake.find_files(
                    ['dir'], True, [os.path.join('dir', 'ex')]))
            finally:
                os.chdir(cwd)

            file_names = [os.path.basename(f) for f in files]
            self.assertIn('a.py', file_names)
            self.assertNotIn('b.py', file_names)
            self.assertNotIn('c.py', file_names)
        finally:
            shutil.rmtree(temp_directory)

    def test_exclude(self):
        temp_directory = tempfile.mkdtemp(dir='.')
        try:
            with open(os.path.join(temp_directory, 'a.py'), 'w') as output:
                output.write('import re\n')

            os.mkdir(os.path.join(temp_directory, 'd'))
            with open(os.path.join(temp_directory, 'd', 'b.py'),
                      'w') as output:
                output.write('import os\n')

            p = subprocess.Popen(
                list(AUTOFLAKE_COMMAND) +
                [temp_directory, '--recursive', '--exclude=a*'],
                stdout=subprocess.PIPE)
            result = p.communicate()[0].decode('utf-8')

            self.assertNotIn('import re', result)
            self.assertIn('import os', result)
        finally:
            shutil.rmtree(temp_directory)


class SystemTests(unittest.TestCase):

    """System tests."""

    def test_diff(self):
        with temporary_file("""\
import re
import os
import my_own_module
x = 1
""") as filename:
            output_file = io.StringIO()
            autoflake._main(argv=['my_fake_program', filename],
                            standard_out=output_file,
                            standard_error=None)
            self.assertEqual("""\
-import re
-import os
 import my_own_module
 x = 1
""", '\n'.join(output_file.getvalue().split('\n')[3:]))

    def test_diff_with_nonexistent_file(self):
        output_file = io.StringIO()
        autoflake._main(argv=['my_fake_program', 'nonexistent_file'],
                        standard_out=output_file,
                        standard_error=output_file)
        self.assertIn('no such file', output_file.getvalue().lower())

    def test_diff_with_encoding_declaration(self):
        with temporary_file("""\
# coding: iso-8859-1
import re
import os
import my_own_module
x = 1
""") as filename:
            output_file = io.StringIO()
            autoflake._main(argv=['my_fake_program', filename],
                            standard_out=output_file,
                            standard_error=None)
            self.assertEqual("""\
 # coding: iso-8859-1
-import re
-import os
 import my_own_module
 x = 1
""", '\n'.join(output_file.getvalue().split('\n')[3:]))

    def test_in_place(self):
        with temporary_file("""\
import foo
x = foo
import subprocess
x()

try:
    import os
except ImportError:
    import os
""") as filename:
            output_file = io.StringIO()
            autoflake._main(argv=['my_fake_program', '--in-place', filename],
                            standard_out=output_file,
                            standard_error=None)
            with open(filename) as f:
                self.assertEqual("""\
import foo
x = foo
x()

try:
    pass
except ImportError:
    pass
""", f.read())

    def test_in_place_with_empty_file(self):
        line = ''

        with temporary_file(line) as filename:
            output_file = io.StringIO()
            autoflake._main(argv=['my_fake_program', '--in-place', filename],
                            standard_out=output_file,
                            standard_error=None)
            with open(filename) as f:
                self.assertEqual(line, f.read())

    def test_in_place_with_with_useless_pass(self):
        with temporary_file("""\
import foo
x = foo
import subprocess
x()

try:
    pass
    import os
except ImportError:
    pass
    import os
    import sys
""") as filename:
            output_file = io.StringIO()
            autoflake._main(argv=['my_fake_program', '--in-place', filename],
                            standard_out=output_file,
                            standard_error=None)
            with open(filename) as f:
                self.assertEqual("""\
import foo
x = foo
x()

try:
    pass
except ImportError:
    pass
""", f.read())

    def test_with_missing_file(self):
        output_file = io.StringIO()
        ignore = StubFile()
        autoflake._main(argv=['my_fake_program', '--in-place', '.fake'],
                        standard_out=output_file,
                        standard_error=ignore)
        self.assertFalse(output_file.getvalue())

    def test_ignore_hidden_directories(self):
        with temporary_directory() as directory:
            with temporary_directory(prefix='.',
                                     directory=directory) as inner_directory:

                with temporary_file("""\
import re
import os
""", directory=inner_directory):

                    output_file = io.StringIO()
                    autoflake._main(argv=['my_fake_program',
                                          '--recursive',
                                          directory],
                                    standard_out=output_file,
                                    standard_error=None)
                    self.assertEqual(
                        '',
                        output_file.getvalue().strip())

    def test_redundant_options(self):
        output_file = io.StringIO()
        autoflake._main(argv=['my_fake_program',
                              '--remove-all', '--imports=blah', __file__],
                        standard_out=output_file,
                        standard_error=output_file)

        self.assertIn('redundant', output_file.getvalue())

    def test_end_to_end(self):
        with temporary_file("""\
import fake_fake, fake_foo, fake_bar, fake_zoo
import re, os
x = os.sep
print(x)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--imports=fake_foo,fake_bar',
                                        filename],
                                       stdout=subprocess.PIPE)
            self.assertEqual("""\
-import fake_fake, fake_foo, fake_bar, fake_zoo
-import re, os
+import fake_fake
+import fake_zoo
+import os
 x = os.sep
 print(x)
""", '\n'.join(process.communicate()[0].decode().split('\n')[3:]))

    def test_end_to_end_with_remove_all_unused_imports(self):
        with temporary_file("""\
import fake_fake, fake_foo, fake_bar, fake_zoo
import re, os
x = os.sep
print(x)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--remove-all',
                                        filename],
                                       stdout=subprocess.PIPE)
            self.assertEqual("""\
-import fake_fake, fake_foo, fake_bar, fake_zoo
-import re, os
+import os
 x = os.sep
 print(x)
""", '\n'.join(process.communicate()[0].decode().split('\n')[3:]))

    def test_end_to_end_with_remove_duplicate_keys_multiple_lines(self):
        with temporary_file("""\
a = {
    'b': 456,
    'a': 123,
    'b': 7834,
    'a': 'wow',
    'b': 456,
    'c': 'hello',
    'c': 'hello2',
    'b': 'hiya',
}
print(a)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--remove-duplicate-keys',
                                        filename],
                                       stdout=subprocess.PIPE)
            self.assertEqual("""\
 a = {
-    'b': 456,
-    'a': 123,
-    'b': 7834,
     'a': 'wow',
-    'b': 456,
-    'c': 'hello',
     'c': 'hello2',
     'b': 'hiya',
 }
""", '\n'.join(process.communicate()[0].decode().split('\n')[3:]))

    def test_end_to_end_with_remove_duplicate_keys_and_other_errors(self):
        with temporary_file("""\
from math import *
print(sin(4))
a = { # Hello
    'b': 456,
    'a': 123,
    'b': 7834,
    'a': 'wow',
    'b': 456,
    'c': 'hello',
    'c': 'hello2',
    'b': 'hiya',
}
print(a)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--remove-duplicate-keys',
                                        filename],
                                       stdout=subprocess.PIPE)
            self.assertEqual("""\
 from math import *
 print(sin(4))
 a = { # Hello
-    'b': 456,
-    'a': 123,
-    'b': 7834,
     'a': 'wow',
-    'b': 456,
-    'c': 'hello',
     'c': 'hello2',
     'b': 'hiya',
 }
""", '\n'.join(process.communicate()[0].decode().split('\n')[3:]))

    def test_end_to_end_with_remove_duplicate_keys_tuple(self):
        with temporary_file("""\
a = {
  (0,1): 1,
  (0, 1): 'two',
  (0,1): 3,
}
print(a)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--remove-duplicate-keys',
                                        filename],
                                       stdout=subprocess.PIPE)
            self.assertEqual("""\
 a = {
-  (0,1): 1,
-  (0, 1): 'two',
   (0,1): 3,
 }
 print(a)
""", '\n'.join(process.communicate()[0].decode().split('\n')[3:]))

    def test_end_to_end_with_error(self):
        with temporary_file("""\
import fake_fake, fake_foo, fake_bar, fake_zoo
import re, os
x = os.sep
print(x)
""") as filename:
            process = subprocess.Popen(AUTOFLAKE_COMMAND +
                                       ['--imports=fake_foo,fake_bar',
                                        '--remove-all',
                                        filename],
                                       stderr=subprocess.PIPE)
            self.assertIn(
                'redundant',
                process.communicate()[1].decode())


@contextlib.contextmanager
def temporary_file(contents, directory='.', suffix='.py', prefix=''):
    """Write contents to temporary file and yield it."""
    f = tempfile.NamedTemporaryFile(suffix=suffix, prefix=prefix,
                                    delete=False, dir=directory)
    try:
        f.write(contents.encode())
        f.close()
        yield f.name
    finally:
        os.remove(f.name)


@contextlib.contextmanager
def temporary_directory(directory='.', prefix='tmp.'):
    """Create temporary directory and yield its path."""
    temp_directory = tempfile.mkdtemp(prefix=prefix, dir=directory)
    try:
        yield temp_directory
    finally:
        shutil.rmtree(temp_directory)


class StubFile(object):

    """Fake file that ignores everything."""

    def write(*_):
        """Ignore."""


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