import pytest
import checkpackagelib.test_util as util
import checkpackagelib.lib_mk as m


DoNotInstallToHostdirUsr = [
    ('real case',
     'libapparmor.mk',
     'LIBAPPARMOR_CONF_OPTS += \\\n'
     '\t--with-python \\\n'
     '\tPYTHON=$(HOST_DIR)/usr/bin/python3 \\\n'
     '\tPYTHON_CONFIG=$(STAGING_DIR)/usr/bin/python3-config \\\n'
     '\tSWIG=$(SWIG)\n',
     [['libapparmor.mk:3: install files to $(HOST_DIR)/ instead of $(HOST_DIR)/usr/',
       '\tPYTHON=$(HOST_DIR)/usr/bin/python3 \\\n']]),
    ('ignore comment',
     'any',
     '# following code do not install to $(HOST_DIR)/usr/\n',
     []),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', DoNotInstallToHostdirUsr)
def test_DoNotInstallToHostdirUsr(testname, filename, string, expected):
    warnings = util.check_file(m.DoNotInstallToHostdirUsr, filename, string)
    assert warnings == expected


Ifdef = [
    ('ignore commented line',
     'any',
     '# ifdef\n',
     []),
    ('simple',
     'any',
     '\n'
     'ifdef BR2_PACKAGE_FWTS_EFI_RUNTIME_MODULE\n'
     'endif\n',
     [['any:2: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL',
       'ifdef BR2_PACKAGE_FWTS_EFI_RUNTIME_MODULE\n']]),
    ('ignore indentation',
     'any',
     '  ifdef FOO\n'
     '  endif\n'
     '\tifdef BAR\n'
     'endif\n',
     [['any:1: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL',
       '  ifdef FOO\n'],
      ['any:3: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL',
       '\tifdef BAR\n']]),
    ('typo',
     'any',
     '\n'
     'ifndef ($(BR2_ENABLE_LOCALE),y)\n'
     'endif\n',
     [['any:2: use ifneq ($(SYMBOL),y) instead of ifndef SYMBOL',
       'ifndef ($(BR2_ENABLE_LOCALE),y)\n']]),
    ('else ifdef',
     'any',
     'else ifdef  SYMBOL # comment\n',
     [['any:1: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL',
       'else ifdef  SYMBOL # comment\n']]),
    ('else ifndef',
     'any',
     '\t else  ifndef\t($(SYMBOL),y) # comment\n',
     [['any:1: use ifneq ($(SYMBOL),y) instead of ifndef SYMBOL',
       '\t else  ifndef\t($(SYMBOL),y) # comment\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', Ifdef)
def test_Ifdef(testname, filename, string, expected):
    warnings = util.check_file(m.Ifdef, filename, string)
    assert warnings == expected


Indent = [
    ('ignore comment at beginning of line',
     'any',
     '# very useful comment\n',
     []),
    ('ignore comment at end of line',
     'any',
     ' # very useful comment\n',
     []),
    ('do not indent on conditional (good)',
     'any',
     'ifeq ($(BR2_TOOLCHAIN_HAS_THREADS),y)\n'
     'FOO_CONF_OPTS += something\n'
     'endef\n',
     []),
    ('do not indent on conditional (bad)',
     'any',
     'ifeq ($(BR2_TOOLCHAIN_HAS_THREADS),y)\n'
     '\tFOO_CONF_OPTS += something\n'
     'endef\n',
     [['any:2: unexpected indent with tabs',
       '\tFOO_CONF_OPTS += something\n']]),
    ('indent after line that ends in backslash (good)',
     'any',
     'FOO_CONF_OPTS += \\\n'
     '\tsomething\n',
     []),
    ('indent after line that ends in backslash (bad)',
     'any',
     'FOO_CONF_OPTS += \\\n'
     'something\n',
     [['any:2: expected indent with tabs',
       'something\n']]),
    ('indent after 2 lines that ends in backslash (good)',
     'any',
     'FOO_CONF_OPTS += \\\n'
     '\tsomething \\\n'
     '\tsomething_else\n',
     []),
    ('indent after 2 lines that ends in backslash (bad)',
     'any',
     'FOO_CONF_OPTS += \\\n'
     '\tsomething \\\n'
     '\tsomething_else \\\n'
     'FOO_CONF_OPTS += another_thing\n',
     [['any:4: expected indent with tabs',
       'FOO_CONF_OPTS += another_thing\n']]),
    ('indent inside define (good)',
     'any',
     'define FOO_SOMETHING\n'
     '\tcommand\n'
     '\tcommand \\\n'
     '\t\targuments\n'
     'endef\n'
     'FOO_POST_PATCH_HOOKS += FOO_SOMETHING\n',
     []),
    ('indent inside define (bad, no indent)',
     'any',
     'define FOO_SOMETHING\n'
     'command\n'
     'endef\n',
     [['any:2: expected indent with tabs',
       'command\n']]),
    ('indent inside define (bad, spaces)',
     'any',
     'define FOO_SOMETHING\n'
     '        command\n'
     'endef\n',
     [['any:2: expected indent with tabs',
       '        command\n']]),
    ('indent make target (good)',
     'any',
     'make_target:\n'
     '\tcommand\n'
     '\n',
     []),
    ('indent make target (bad)',
     'any',
     'make_target:\n'
     '        command\n'
     '\n',
     [['any:2: expected indent with tabs',
       '        command\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', Indent)
def test_Indent(testname, filename, string, expected):
    warnings = util.check_file(m.Indent, filename, string)
    assert warnings == expected


OverriddenVariable = [
    ('simple assignment',
     'any.mk',
     'VAR_1 = VALUE1\n',
     []),
    ('unconditional override (variable without underscore)',
     'any.mk',
     'VAR1 = VALUE1\n'
     'VAR1 = VALUE1\n',
     [['any.mk:2: unconditional override of variable VAR1',
       'VAR1 = VALUE1\n']]),
    ('unconditional override (variable with underscore, same value)',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'VAR_1 = VALUE1\n',
     [['any.mk:2: unconditional override of variable VAR_1',
       'VAR_1 = VALUE1\n']]),
    ('unconditional override (variable with underscore, different value)',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'VAR_1 = VALUE2\n',
     [['any.mk:2: unconditional override of variable VAR_1',
       'VAR_1 = VALUE2\n']]),
    ('warn for unconditional override even with wrong number of spaces',
     'any.mk',
     'VAR_1= VALUE1\n'
     'VAR_1 =VALUE2\n',
     [['any.mk:2: unconditional override of variable VAR_1',
       'VAR_1 =VALUE2\n']]),
    ('warn for := override',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'VAR_1 := VALUE2\n',
     [['any.mk:2: unconditional override of variable VAR_1',
       'VAR_1 := VALUE2\n']]),
    ('append values outside conditional (good)',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'VAR_1 += VALUE2\n',
     []),
    ('append values outside conditional (bad)',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'VAR_1 := $(VAR_1), VALUE2\n',
     [['any.mk:2: unconditional override of variable VAR_1',
       'VAR_1 := $(VAR_1), VALUE2\n']]),
    ('immediate assignment inside conditional',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'ifeq (condition)\n'
     'VAR_1 := $(VAR_1), VALUE2\n',
     [['any.mk:3: immediate assignment to append to variable VAR_1',
       'VAR_1 := $(VAR_1), VALUE2\n']]),
    ('immediate assignment inside conditional and unconditional override outside',
     'any.mk',
     'VAR_1 = VALUE1\n'
     'ifeq (condition)\n'
     'VAR_1 := $(VAR_1), VALUE2\n'
     'endif\n'
     'VAR_1 := $(VAR_1), VALUE2\n',
     [['any.mk:3: immediate assignment to append to variable VAR_1',
       'VAR_1 := $(VAR_1), VALUE2\n'],
      ['any.mk:5: unconditional override of variable VAR_1',
       'VAR_1 := $(VAR_1), VALUE2\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', OverriddenVariable)
def test_OverriddenVariable(testname, filename, string, expected):
    warnings = util.check_file(m.OverriddenVariable, filename, string)
    assert warnings == expected


PackageHeader = [
    ('first line (good)',
     'any',
     80 * '#' + '\n',
     []),
    ('first line (bad)',
     'any',
     '# very useful comment\n',
     [['any:1: should be 80 hashes (url#writing-rules-mk)',
       '# very useful comment\n',
       80 * '#']]),
    ('second line (bad)',
     'any',
     80 * '#' + '\n'
     '# package\n',
     [['any:2: should be 1 hash (url#writing-rules-mk)',
       '# package\n']]),
    ('full header (good)',
     'any',
     80 * '#' + '\n'
     '#\n'
     '# package\n'
     '#\n' +
     80 * '#' + '\n'
     '\n',
     []),
    ('blank line after header (good)',
     'any',
     80 * '#' + '\n'
     '#\n'
     '# package\n'
     '#\n' +
     80 * '#' + '\n'
     '\n'
     'FOO_VERSION = 1\n',
     []),
    ('blank line after header (bad)',
     'any',
     80 * '#' + '\n'
     '#\n'
     '# package\n'
     '#\n' +
     80 * '#' + '\n'
     'FOO_VERSION = 1\n',
     [['any:6: should be a blank line (url#writing-rules-mk)',
       'FOO_VERSION = 1\n']]),
    ('wrong number of hashes',
     'any',
     79 * '#' + '\n'
     '#\n'
     '# package\n'
     '#\n' +
     81 * '#' + '\n'
     '\n',
     [['any:1: should be 80 hashes (url#writing-rules-mk)',
       79 * '#' + '\n',
       80 * '#'],
      ['any:5: should be 80 hashes (url#writing-rules-mk)',
       81 * '#' + '\n',
       80 * '#']]),
    ('allow include without header',
     'any',
     'include $(sort $(wildcard package/foo/*/*.mk))\n',
     []),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', PackageHeader)
def test_PackageHeader(testname, filename, string, expected):
    warnings = util.check_file(m.PackageHeader, filename, string)
    assert warnings == expected


RemoveDefaultPackageSourceVariable = [
    ('bad',
     'any.mk',
     'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n',
     [['any.mk:1: remove default value of _SOURCE variable (url#generic-package-reference)',
       'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n']]),
    ('bad with path',
     './any.mk',
     'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n',
     [['./any.mk:1: remove default value of _SOURCE variable (url#generic-package-reference)',
       'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n']]),
    ('warn for correct line',
     './any.mk',
     '\n'
     '\n'
     '\n'
     'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n',
     [['./any.mk:4: remove default value of _SOURCE variable (url#generic-package-reference)',
       'ANY_SOURCE = any-$(ANY_VERSION).tar.gz\n']]),
    ('warn ignoring missing spaces',
     './any.mk',
     'ANY_SOURCE=any-$(ANY_VERSION).tar.gz\n',
     [['./any.mk:1: remove default value of _SOURCE variable (url#generic-package-reference)',
       'ANY_SOURCE=any-$(ANY_VERSION).tar.gz\n']]),
    ('good',
     './any.mk',
     'ANY_SOURCE = aNy-$(ANY_VERSION).tar.gz\n',
     []),
    ('gcc exception',
     'gcc.mk',
     'GCC_SOURCE = gcc-$(GCC_VERSION).tar.gz\n',
     []),
    ('binutils exception',
     './binutils.mk',
     'BINUTILS_SOURCE = binutils-$(BINUTILS_VERSION).tar.gz\n',
     []),
    ('gdb exception',
     'gdb/gdb.mk',
     'GDB_SOURCE = gdb-$(GDB_VERSION).tar.gz\n',
     []),
    ('package name with dash',
     'python-subprocess32.mk',
     'PYTHON_SUBPROCESS32_SOURCE = python-subprocess32-$(PYTHON_SUBPROCESS32_VERSION).tar.gz\n',
     [['python-subprocess32.mk:1: remove default value of _SOURCE variable (url#generic-package-reference)',
       'PYTHON_SUBPROCESS32_SOURCE = python-subprocess32-$(PYTHON_SUBPROCESS32_VERSION).tar.gz\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', RemoveDefaultPackageSourceVariable)
def test_RemoveDefaultPackageSourceVariable(testname, filename, string, expected):
    warnings = util.check_file(m.RemoveDefaultPackageSourceVariable, filename, string)
    assert warnings == expected


SpaceBeforeBackslash = [
    ('no backslash',
     'any.mk',
     '\n',
     []),
    ('ignore missing indent',
     'any.mk',
     'define ANY_SOME_FIXUP\n'
     'for i in $$(find $(STAGING_DIR)/usr/lib* -name "any*.la"); do \\\n',
     []),
    ('ignore missing space',
     'any.mk',
     'ANY_CONF_ENV= \\\n'
     '\tap_cv_void_ptr_lt_long=no \\\n',
     []),
    ('variable',
     'any.mk',
     '\n'
     'ANY = \\\n',
     []),
    ('2 spaces',
     'any.mk',
     'ANY =  \\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY =  \\\n']]),
    ('warn about correct line',
     'any.mk',
     '\n'
     'ANY =  \\\n',
     [['any.mk:2: use only one space before backslash',
       'ANY =  \\\n']]),
    ('tab',
     'any.mk',
     'ANY =\t\\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY =\t\\\n']]),
    ('tabs',
     'any.mk',
     'ANY =\t\t\\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY =\t\t\\\n']]),
    ('spaces and tabs',
     'any.mk',
     'ANY =  \t\t\\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY =  \t\t\\\n']]),
    ('mixed spaces and tabs 1',
     'any.mk',
     'ANY = \t \t\\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY = \t \t\\\n']]),
    ('mixed spaces and tabs 2',
     'any.mk',
     'ANY = \t  \\\n',
     [['any.mk:1: use only one space before backslash',
       'ANY = \t  \\\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', SpaceBeforeBackslash)
def test_SpaceBeforeBackslash(testname, filename, string, expected):
    warnings = util.check_file(m.SpaceBeforeBackslash, filename, string)
    assert warnings == expected


TrailingBackslash = [
    ('no backslash',
     'any.mk',
     'ANY = \n',
     []),
    ('one line',
     'any.mk',
     'ANY = \\\n',
     []),
    ('2 lines',
     'any.mk',
     'ANY = \\\n'
     '\\\n',
     []),
    ('empty line after',
     'any.mk',
     'ANY = \\\n'
     '\n',
     [['any.mk:1: remove trailing backslash',
       'ANY = \\\n']]),
    ('line with spaces after',
     'any.mk',
     'ANY = \\\n'
     '     \n',
     [['any.mk:1: remove trailing backslash',
       'ANY = \\\n']]),
    ('line with tabs after',
     'any.mk',
     'ANY = \\\n'
     '\t\n',
     [['any.mk:1: remove trailing backslash',
       'ANY = \\\n']]),
    ('ignore if commented',
     'any.mk',
     '# ANY = \\\n'
     '\n',
     []),
    ('real example',
     'any.mk',
     'ANY_CONF_ENV= \t\\\n'
     '\tap_cv_void_ptr_lt_long=no  \\\n'
     '\n',
     [['any.mk:2: remove trailing backslash',
       '\tap_cv_void_ptr_lt_long=no  \\\n']]),
    ('ignore whitespace 1',
     'any.mk',
     'ANY =  \t\t\\\n',
     []),
    ('ignore whitespace 2',
     'any.mk',
     'ANY = \t \t\\\n',
     []),
    ('ignore whitespace 3',
     'any.mk',
     'ANY = \t  \\\n',
     []),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', TrailingBackslash)
def test_TrailingBackslash(testname, filename, string, expected):
    warnings = util.check_file(m.TrailingBackslash, filename, string)
    assert warnings == expected


TypoInPackageVariable = [
    ('good',
     'any.mk',
     'ANY_VAR = \n',
     []),
    ('good with path 1',
     './any.mk',
     'ANY_VAR += \n',
     []),
    ('good with path 2',
     'any/any.mk',
     'ANY_VAR = \n',
     []),
    ('bad =',
     'any.mk',
     'OTHER_VAR = \n',
     [['any.mk:1: possible typo: OTHER_VAR -> *ANY*',
       'OTHER_VAR = \n']]),
    ('bad +=',
     'any.mk',
     'OTHER_VAR += \n',
     [['any.mk:1: possible typo: OTHER_VAR -> *ANY*',
       'OTHER_VAR += \n']]),
    ('ignore missing space',
     'any.mk',
     'OTHER_VAR= \n',
     [['any.mk:1: possible typo: OTHER_VAR -> *ANY*',
       'OTHER_VAR= \n']]),
    ('use path in the warning',
     './any.mk',
     'OTHER_VAR = \n',
     [['./any.mk:1: possible typo: OTHER_VAR -> *ANY*',
       'OTHER_VAR = \n']]),
    ('another name',
     'other.mk',
     'ANY_VAR = \n',
     [['other.mk:1: possible typo: ANY_VAR -> *OTHER*',
       'ANY_VAR = \n']]),
    ('libc exception',
     './any.mk',
     'BR_LIBC = \n',
     []),
    ('rootfs exception',
     'any.mk',
     'ROOTFS_ANY_VAR += \n',
     []),
    ('host (good)',
     'any.mk',
     'HOST_ANY_VAR += \n',
     []),
    ('host (bad)',
     'any.mk',
     'HOST_OTHER_VAR = \n',
     [['any.mk:1: possible typo: HOST_OTHER_VAR -> *ANY*',
       'HOST_OTHER_VAR = \n']]),
    ('provides',
     'any.mk',
     'ANY_PROVIDES = other thing\n'
     'OTHER_VAR = \n',
     []),
    ('ignore space',
     'any.mk',
     'ANY_PROVIDES  =  thing  other \n'
     'OTHER_VAR = \n',
     []),
    ('wrong provides',
     'any.mk',
     'ANY_PROVIDES = other\n'
     'OTHERS_VAR = \n',
     [['any.mk:2: possible typo: OTHERS_VAR -> *ANY*',
       'OTHERS_VAR = \n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', TypoInPackageVariable)
def test_TypoInPackageVariable(testname, filename, string, expected):
    warnings = util.check_file(m.TypoInPackageVariable, filename, string)
    assert warnings == expected


UselessFlag = [
    ('autoreconf no',
     'any.mk',
     'ANY_AUTORECONF=NO\n',
     [['any.mk:1: useless default value (url#_infrastructure_for_autotools_based_packages)',
       'ANY_AUTORECONF=NO\n']]),
    ('host autoreconf no',
     'any.mk',
     'HOST_ANY_AUTORECONF\n',
     []),
    ('autoreconf yes',
     'any.mk',
     'ANY_AUTORECONF=YES\n',
     []),
    ('libtool_patch yes',
     'any.mk',
     'ANY_LIBTOOL_PATCH\t=  YES\n',
     [['any.mk:1: useless default value (url#_infrastructure_for_autotools_based_packages)',
       'ANY_LIBTOOL_PATCH\t=  YES\n']]),
    ('libtool_patch no',
     'any.mk',
     'ANY_LIBTOOL_PATCH= \t NO\n',
     []),
    ('generic',
     'any.mk',
     'ANY_INSTALL_IMAGES = NO\n'
     'ANY_INSTALL_REDISTRIBUTE = YES\n'
     'ANY_INSTALL_STAGING = NO\n'
     'ANY_INSTALL_TARGET = YES\n',
     [['any.mk:1: useless default value (url#_infrastructure_for_packages_with_specific_build_systems)',
       'ANY_INSTALL_IMAGES = NO\n'],
      ['any.mk:2: useless default value (url#_infrastructure_for_packages_with_specific_build_systems)',
       'ANY_INSTALL_REDISTRIBUTE = YES\n'],
      ['any.mk:3: useless default value (url#_infrastructure_for_packages_with_specific_build_systems)',
       'ANY_INSTALL_STAGING = NO\n'],
      ['any.mk:4: useless default value (url#_infrastructure_for_packages_with_specific_build_systems)',
       'ANY_INSTALL_TARGET = YES\n']]),
    ('conditional',
     'any.mk',
     'ifneq (condition)\n'
     'ANY_INSTALL_IMAGES = NO\n'
     'endif\n'
     'ANY_INSTALL_REDISTRIBUTE = YES\n',
     [['any.mk:4: useless default value (url#_infrastructure_for_packages_with_specific_build_systems)',
       'ANY_INSTALL_REDISTRIBUTE = YES\n']]),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', UselessFlag)
def test_UselessFlag(testname, filename, string, expected):
    warnings = util.check_file(m.UselessFlag, filename, string)
    assert warnings == expected


VariableWithBraces = [
    ('good',
     'xmlstarlet.mk',
     'XMLSTARLET_CONF_OPTS += \\\n'
     '\t--with-libxml-prefix=$(STAGING_DIR)/usr \\\n',
     []),
    ('bad',
     'xmlstarlet.mk',
     'XMLSTARLET_CONF_OPTS += \\\n'
     '\t--with-libxml-prefix=${STAGING_DIR}/usr \\\n',
     [['xmlstarlet.mk:2: use $() to delimit variables, not ${}',
       '\t--with-libxml-prefix=${STAGING_DIR}/usr \\\n']]),
    ('expanded by the shell',
     'sg3_utils.mk',
     '\tfor prog in xcopy zone; do \\\n'
     '\t\t$(RM) $(TARGET_DIR)/usr/bin/sg_$${prog} ; \\\n'
     '\tdone\n',
     []),
    ('comments',
     'any.mk',
     '#\t--with-libxml-prefix=${STAGING_DIR}/usr \\\n',
     []),
    ]


@pytest.mark.parametrize('testname,filename,string,expected', VariableWithBraces)
def test_VariableWithBraces(testname, filename, string, expected):
    warnings = util.check_file(m.VariableWithBraces, filename, string)
    assert warnings == expected
