* [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests @ 2021-12-05 10:53 Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest Ricardo Martincoski ` (3 more replies) 0 siblings, 4 replies; 7+ messages in thread From: Ricardo Martincoski @ 2021-12-05 10:53 UTC (permalink / raw) To: buildroot; +Cc: Ricardo Martincoski So anyone willing to contribute to check-package can run all tests in less than 1 second by using: $ python3 -m pytest -v utils/checkpackagelib/ Most test cases are in the form: @pytest.mark.parametrize('testname,filename,string,expected', function) - testname: a short description of the scenario tested, added in order to improve readability of the log when some tests fail - filename: the filename the check-package function being tested thinks it is testing - string: the content of the file being sent to the function under test - expect: all expected warnings that a given function from check-package should generate for a given file named filename and with string as its content. Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> Cc: Arnout Vandecappelle <arnout@mind.be> --- NOTICE there are 2 tests failing, see the fix in: http://patchwork.ozlabs.org/project/buildroot/patch/20211115235336.3814968-1-ricardo.martincoski@gmail.com/ See a sample running in the GitLab CI: without the fix: https://gitlab.com/RicardoMartincoski/buildroot/-/pipelines/422862195 with the fix: https://gitlab.com/RicardoMartincoski/buildroot/-/pipelines/422862127 Example of a failure, showing enough information to track down the test that fails: |testname = 'immediate assignment inside conditional and unconditional override outside' |filename = 'any.mk' |string = 'VAR_1 = VALUE1\nifeq (condition)\nVAR_1 := $(VAR_1), VALUE2\nendif\nVAR_1 := $(VAR_1), VALUE2\n' |expected = [['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', overridden_variable) | def test_overridden_variable(testname, filename, string, expected): | warnings = util.check_file(m.OverriddenVariable, filename, string) |> assert warnings == expected |E AssertionError: assert [['any.mk:3: ...), VALUE2\n']] == [['any.mk:3: i...), VALUE2\n']] |E At index 0 diff: ['any.mk:3: conditional override of variable VAR_1', 'VAR_1 := $(VAR_1), VALUE2\n'] != ['any.mk:3: immediate assignment to append to variable VAR_1', 'VAR_1 := $(VAR_1), VALUE2\n'] |E Full diff: |E - [['any.mk:3: conditional override of variable VAR_1', |E + [['any.mk:3: immediate assignment to append to variable VAR_1', |E 'VAR_1 := $(VAR_1), VALUE2\n'], |E ['any.mk:5: unconditional override of variable VAR_1', |E 'VAR_1 := $(VAR_1), VALUE2\n']] | |utils/checkpackagelib/test_lib_mk.py:168: AssertionError |===================== 2 failed, 180 passed in 0.79 seconds ===================== --- utils/checkpackagelib/test_lib.py | 212 ++++++++ utils/checkpackagelib/test_lib_config.py | 387 +++++++++++++++ utils/checkpackagelib/test_lib_hash.py | 137 ++++++ utils/checkpackagelib/test_lib_mk.py | 590 +++++++++++++++++++++++ utils/checkpackagelib/test_lib_patch.py | 96 ++++ utils/checkpackagelib/test_util.py | 8 + 6 files changed, 1430 insertions(+) create mode 100644 utils/checkpackagelib/test_lib.py create mode 100644 utils/checkpackagelib/test_lib_config.py create mode 100644 utils/checkpackagelib/test_lib_hash.py create mode 100644 utils/checkpackagelib/test_lib_mk.py create mode 100644 utils/checkpackagelib/test_lib_patch.py create mode 100644 utils/checkpackagelib/test_util.py diff --git a/utils/checkpackagelib/test_lib.py b/utils/checkpackagelib/test_lib.py new file mode 100644 index 0000000000..976a63d84d --- /dev/null +++ b/utils/checkpackagelib/test_lib.py @@ -0,0 +1,212 @@ +import pytest +import checkpackagelib.test_util as util +import checkpackagelib.lib as m + + +ConsecutiveEmptyLines = [ + ('1 line (no newline)', + 'any', + '', + []), + ('1 line', + 'any', + '\n', + []), + ('2 lines', + 'any', + '\n' + '\n', + [['any:2: consecutive empty lines']]), + ('more than 2 consecutive', + 'any', + '\n' + '\n' + '\n', + [['any:2: consecutive empty lines'], + ['any:3: consecutive empty lines']]), + ('ignore whitespace 1', + 'any', + '\n' + ' ', + [['any:2: consecutive empty lines']]), + ('ignore whitespace 2', + 'any', + ' \n' + '\t\n', + [['any:2: consecutive empty lines']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', ConsecutiveEmptyLines) +def test_ConsecutiveEmptyLines(testname, filename, string, expected): + warnings = util.check_file(m.ConsecutiveEmptyLines, filename, string) + assert warnings == expected + + +EmptyLastLine = [ + ('ignore empty file', + 'any', + '', + []), + ('empty line (newline)', + 'any', + '\n', + [['any:1: empty line at end of file']]), + ('empty line (space, newline)', + 'any', + ' \n', + [['any:1: empty line at end of file']]), + ('empty line (space, no newline)', + 'any', + ' ', + [['any:1: empty line at end of file']]), + ('warn for the last of 2', + 'any', + '\n' + '\n', + [['any:2: empty line at end of file']]), + ('warn for the last of 3', + 'any', + '\n' + '\n' + '\n', + [['any:3: empty line at end of file']]), + ('ignore whitespace', + 'any', + ' \n' + '\t\n', + [['any:2: empty line at end of file']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', EmptyLastLine) +def test_EmptyLastLine(testname, filename, string, expected): + warnings = util.check_file(m.EmptyLastLine, filename, string) + assert warnings == expected + + +NewlineAtEof = [ + ('good', + 'any', + 'text\n', + []), + ('text (bad)', + 'any', + '\n' + 'text', + [['any:2: missing newline at end of file', + 'text']]), + ('space (bad)', + 'any', + '\n' + ' ', + [['any:2: missing newline at end of file', + ' ']]), + ('tab (bad)', + 'any', + '\n' + '\t', + [['any:2: missing newline at end of file', + '\t']]), + ('even for file with one line', + 'any', + ' ', + [['any:1: missing newline at end of file', + ' ']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', NewlineAtEof) +def test_NewlineAtEof(testname, filename, string, expected): + warnings = util.check_file(m.NewlineAtEof, filename, string) + assert warnings == expected + + +TrailingSpace = [ + ('good', + 'any', + 'text\n', + []), + ('ignore missing newline', + 'any', + '\n' + 'text', + []), + ('spaces', + 'any', + 'text \n', + [['any:1: line contains trailing whitespace', + 'text \n']]), + ('tabs after text', + 'any', + 'text\t\t\n', + [['any:1: line contains trailing whitespace', + 'text\t\t\n']]), + ('mix of tabs and spaces', + 'any', + ' \n' + ' ', + [['any:1: line contains trailing whitespace', + ' \n'], + ['any:2: line contains trailing whitespace', + ' ']]), + ('blank line with tabs', + 'any', + '\n' + '\t', + [['any:2: line contains trailing whitespace', + '\t']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', TrailingSpace) +def test_TrailingSpace(testname, filename, string, expected): + warnings = util.check_file(m.TrailingSpace, filename, string) + assert warnings == expected + + +Utf8Characters = [ + ('usual', + 'any', + 'text\n', + []), + ('acceptable character', + 'any', + '\x60', + []), + ('unacceptable character', + 'any', + '\x81', + [['any:1: line contains UTF-8 characters', + '\x81']]), + ('2 warnings', + 'any', + 'text\n' + 'text \xc8 text\n' + '\xc9\n', + [['any:2: line contains UTF-8 characters', + 'text \xc8 text\n'], + ['any:3: line contains UTF-8 characters', + '\xc9\n']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', Utf8Characters) +def test_Utf8Characters(testname, filename, string, expected): + warnings = util.check_file(m.Utf8Characters, filename, string) + assert warnings == expected + + +def test_all_check_functions_are_used(): + import inspect + import checkpackagelib.lib_config as lib_config + import checkpackagelib.lib_hash as lib_hash + import checkpackagelib.lib_mk as lib_mk + import checkpackagelib.lib_patch as lib_patch + c_config = [c[0] for c in inspect.getmembers(lib_config, inspect.isclass)] + c_hash = [c[0] for c in inspect.getmembers(lib_hash, inspect.isclass)] + c_mk = [c[0] for c in inspect.getmembers(lib_mk, inspect.isclass)] + c_patch = [c[0] for c in inspect.getmembers(lib_patch, inspect.isclass)] + c_all = c_config + c_hash + c_mk + c_patch + c_common = [c[0] for c in inspect.getmembers(m, inspect.isclass)] + assert set(c_common) <= set(c_all) diff --git a/utils/checkpackagelib/test_lib_config.py b/utils/checkpackagelib/test_lib_config.py new file mode 100644 index 0000000000..91a549adf2 --- /dev/null +++ b/utils/checkpackagelib/test_lib_config.py @@ -0,0 +1,387 @@ +import pytest +import checkpackagelib.test_util as util +import checkpackagelib.lib_config as m + + +AttributesOrder = [ + ('good example', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'default y\n' + 'depends on BR2_USE_BAR # runtime\n' + 'select BR2_PACKAGE_BAZ\n' + 'help\n' + '\t help text\n', + []), + ('depends before default', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'depends on BR2_USE_BAR\n' + 'default y\n', + [['any:4: attributes order: type, default, depends on, select, help (url#_config_files)', + 'default y\n']]), + ('select after help', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'help\n' + '\t help text\n' + 'select BR2_PACKAGE_BAZ\n', + [['any:5: attributes order: type, default, depends on, select, help (url#_config_files)', + 'select BR2_PACKAGE_BAZ\n']]), + ('string', + 'any', + 'config BR2_PACKAGE_FOO_PLUGINS\n' + 'string "foo plugins"\n' + 'default "all"\n', + []), + ('ignore tabs', + 'any', + 'config\tBR2_PACKAGE_FOO_PLUGINS\n' + 'default\t"all"\n' + 'string\t"foo plugins"\n', + [['any:3: attributes order: type, default, depends on, select, help (url#_config_files)', + 'string\t"foo plugins"\n']]), + ('choice', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'if BR2_PACKAGE_FOO\n' + '\n' + 'choice\n' + 'prompt "type of foo"\n' + 'default BR2_PACKAGE_FOO_STRING\n' + '\n' + 'config BR2_PACKAGE_FOO_NONE\n' + 'bool "none"\n' + '\n' + 'config BR2_PACKAGE_FOO_STRING\n' + 'bool "string"\n' + '\n' + 'endchoice\n' + '\n' + 'endif\n' + '\n', + []), + ('type after default', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'if BR2_PACKAGE_FOO\n' + '\n' + 'choice\n' + 'default BR2_PACKAGE_FOO_STRING\n' + 'prompt "type of foo"\n', + [['any:7: attributes order: type, default, depends on, select, help (url#_config_files)', + 'prompt "type of foo"\n']]), + ('menu', + 'any', + 'menuconfig BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'help\n' + '\t help text\n' + '\t help text\n' + '\n' + 'if BR2_PACKAGE_FOO\n' + '\n' + 'menu "foo plugins"\n' + 'config BR2_PACKAGE_FOO_COUNTER\n' + 'bool "counter"\n' + '\n' + 'endmenu\n' + '\n' + 'endif\n', + []), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', AttributesOrder) +def test_AttributesOrder(testname, filename, string, expected): + warnings = util.check_file(m.AttributesOrder, filename, string) + assert warnings == expected + + +CommentsMenusPackagesOrder = [ + ('top menu (good)', + 'package/Config.in', + 'menu "Target packages"\n' + 'source "package/busybox/Config.in"\n' + 'source "package/skeleton/Config.in"\n', + []), + ('top menu (bad)', + 'package/Config.in', + 'source "package/skeleton/Config.in"\n' + 'source "package/busybox/Config.in"\n', + [['package/Config.in:2: Packages in: The top level menu,\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: busybox', + 'source "package/busybox/Config.in"\n']]), + ('menu (bad)', + 'package/Config.in', + 'menu "Target packages"\n' + 'source "package/skeleton/Config.in"\n' + 'source "package/busybox/Config.in"\n', + [['package/Config.in:3: Packages in: menu "Target packages",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: busybox', + 'source "package/busybox/Config.in"\n']]), + ('underscore (good)', + 'package/Config.in.host', + 'menu "Hardware handling"\n' + 'menu "Firmware"\n' + 'endmenu\n' + 'source "package/usb_modeswitch/Config.in"\n' + 'source "package/usbmount/Config.in"\n', + []), + ('underscore (bad)', + 'package/Config.in.host', + 'menu "Hardware handling"\n' + 'menu "Firmware"\n' + 'endmenu\n' + 'source "package/usbmount/Config.in"\n' + 'source "package/usb_modeswitch/Config.in"\n', + [['package/Config.in.host:5: Packages in: menu "Hardware handling",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: usb_modeswitch', + 'source "package/usb_modeswitch/Config.in"\n']]), + ('ignore other files', + 'any other file', + 'menu "Hardware handling"\n' + 'source "package/bbb/Config.in"\n' + 'source "package/aaa/Config.in"\n', + []), + ('dash (bad)', + 'package/Config.in', + 'menu "packages"\n' + 'source "package/a_a/Config.in"\n' + 'source "package/a-a/Config.in"\n' + 'source "package/a1a/Config.in"\n' + 'source "package/aAa/Config.in"\n' + 'source "package/aaa/Config.in"\n', + [['package/Config.in:3: Packages in: menu "packages",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: a-a', + 'source "package/a-a/Config.in"\n']]), + ('underscore (bad)', + 'package/Config.in', + 'menu "packages"\n' + 'source "package/a-a/Config.in"\n' + 'source "package/a1a/Config.in"\n' + 'source "package/a_a/Config.in"\n' + 'source "package/aAa/Config.in"\n' + 'source "package/aaa/Config.in"\n', + [['package/Config.in:4: Packages in: menu "packages",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: a_a', + 'source "package/a_a/Config.in"\n']]), + ('digit (bad)', + 'package/Config.in', + 'menu "packages"\n' + 'source "package/a-a/Config.in"\n' + 'source "package/a_a/Config.in"\n' + 'source "package/aAa/Config.in"\n' + 'source "package/a1a/Config.in"\n' + 'source "package/aaa/Config.in"\n', + [['package/Config.in:5: Packages in: menu "packages",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: a1a', + 'source "package/a1a/Config.in"\n']]), + ('capitals (bad)', + 'package/Config.in', + 'menu "packages"\n' + 'source "package/a-a/Config.in"\n' + 'source "package/a_a/Config.in"\n' + 'source "package/a1a/Config.in"\n' + 'source "package/aaa/Config.in"\n' + 'source "package/aAa/Config.in"\n', + [['package/Config.in:6: Packages in: menu "packages",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: aAa', + 'source "package/aAa/Config.in"\n']]), + ('digits, capitals, underscore (good)', + 'package/Config.in', + 'menu "packages"\n' + 'source "package/a-a/Config.in"\n' + 'source "package/a_a/Config.in"\n' + 'source "package/a1a/Config.in"\n' + 'source "package/aAa/Config.in"\n' + 'source "package/aaa/Config.in"\n', + []), + ('conditional menu (good)', + 'package/Config.in', + 'menu "Other"\n' + 'source "package/linux-pam/Config.in"\n' + 'if BR2_PACKAGE_LINUX_PAM\n' + 'comment "linux-pam plugins"\n' + 'source "package/libpam-radius-auth/Config.in"\n' + 'source "package/libpam-tacplus/Config.in"\n' + 'endif\n' + 'source "package/liquid-dsp/Config.in"\n', + []), + ('conditional menu (bad)', + 'package/Config.in', + 'menu "Other"\n' + 'source "package/linux-pam/Config.in"\n' + 'if BR2_PACKAGE_LINUX_PAM\n' + 'comment "linux-pam plugins"\n' + 'source "package/libpam-tacplus/Config.in"\n' + 'source "package/libpam-radius-auth/Config.in"\n' + 'endif\n' + 'source "package/liquid-dsp/Config.in"\n', + [['package/Config.in:6: Packages in: comment "linux-pam plugins",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: libpam-radius-auth', + 'source "package/libpam-radius-auth/Config.in"\n']]), + ('no conditional (bad)', + 'package/Config.in', + 'menu "Other"\n' + 'source "package/linux-pam/Config.in"\n' + 'source "package/libpam-radius-auth/Config.in"\n' + 'source "package/libpam-tacplus/Config.in"\n' + 'source "package/liquid-dsp/Config.in"\n', + [['package/Config.in:3: Packages in: menu "Other",\n' + ' are not alphabetically ordered;\n' + " correct order: '-', '_', digits, capitals, lowercase;\n" + ' first incorrect package: libpam-radius-auth', + 'source "package/libpam-radius-auth/Config.in"\n']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', CommentsMenusPackagesOrder) +def test_CommentsMenusPackagesOrder(testname, filename, string, expected): + warnings = util.check_file(m.CommentsMenusPackagesOrder, filename, string) + assert warnings == expected + + +HelpText = [ + ('single line', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'bool "foo"\n' + 'default y\n' + 'depends on BR2_USE_BAR # runtime\n' + 'select BR2_PACKAGE_BAZ\n' + 'help\n' + '\t help text\n', + []), + ('larger than 72', + 'any', + 'help\n' + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' + '\t 123456789 123456789 123456789 123456789 123456789 123456789 123\n' + '\t help text\n', + [['any:3: help text: <tab><2 spaces><62 chars> (url#writing-rules-config-in)', + '\t 123456789 123456789 123456789 123456789 123456789 123456789 123\n', + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12']]), + ('long url at beginning of line', + 'any', + 'help\n' + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' + '\t http://url.that.is.longer.than.seventy.two.characthers/folder_name\n' + '\t https://url.that.is.longer.than.seventy.two.characthers/folder_name\n' + '\t git://url.that.is.longer.than.seventy.two.characthers/folder_name\n', + []), + ('long url not at beginning of line', + 'any', + 'help\n' + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' + '\t refer to http://url.that.is.longer.than.seventy.two.characthers/folder_name\n' + '\n' + '\t http://url.that.is.longer.than.seventy.two.characthers/folder_name\n', + [['any:3: help text: <tab><2 spaces><62 chars> (url#writing-rules-config-in)', + '\t refer to http://url.that.is.longer.than.seventy.two.characthers/folder_name\n', + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12']]), + ('allow beautified items', + 'any', + 'help\n' + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' + '\t summary:\n' + '\t - enable that config\n' + '\t - built it\n', + []), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', HelpText) +def test_HelpText(testname, filename, string, expected): + warnings = util.check_file(m.HelpText, filename, string) + assert warnings == expected + + +Indent = [ + ('good example', + 'any', + 'config BR2_PACKAGE_FOO\n' + '\tbool "foo"\n' + '\tdefault y\n' + '\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n' + '\tdepends on BR2_INSTALL_LIBSTDCPP\n' + '# very useful comment\n' + '\tselect BR2_PACKAGE_BAZ\n' + '\thelp\n' + '\t help text\n' + '\n' + 'comment "foo needs toolchain w/ C++, threads"\n' + '\tdepends on !BR2_INSTALL_LIBSTDCPP || \\\n' + '\t\t!BR2_TOOLCHAIN_HAS_THREADS\n' + '\n' + 'source "package/foo/bar/Config.in"\n', + []), + ('spaces', + 'any', + 'config BR2_PACKAGE_FOO\n' + ' bool "foo"\n', + [['any:2: should be indented with one tab (url#_config_files)', + ' bool "foo"\n']]), + ('without indent', + 'any', + 'config BR2_PACKAGE_FOO\n' + 'default y\n', + [['any:2: should be indented with one tab (url#_config_files)', + 'default y\n']]), + ('too much tabs', + 'any', + 'config BR2_PACKAGE_FOO\n' + '\t\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n', + [['any:2: should be indented with one tab (url#_config_files)', + '\t\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n']]), + ('help', + 'any', + 'config BR2_PACKAGE_FOO\n' + ' help\n', + [['any:2: should be indented with one tab (url#_config_files)', + ' help\n']]), + ('continuation line', + 'any', + 'comment "foo needs toolchain w/ C++, threads"\n' + '\tdepends on !BR2_INSTALL_LIBSTDCPP || \\\n' + ' !BR2_TOOLCHAIN_HAS_THREADS\n', + [['any:3: continuation line should be indented using tabs', + ' !BR2_TOOLCHAIN_HAS_THREADS\n']]), + ('comment with tabs', + 'any', + '\tcomment "foo needs toolchain w/ C++, threads"\n', + [['any:1: should not be indented', + '\tcomment "foo needs toolchain w/ C++, threads"\n']]), + ('comment with spaces', + 'any', + ' comment "foo needs toolchain w/ C++, threads"\n', + [['any:1: should not be indented', + ' comment "foo needs toolchain w/ C++, threads"\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 diff --git a/utils/checkpackagelib/test_lib_hash.py b/utils/checkpackagelib/test_lib_hash.py new file mode 100644 index 0000000000..c160a394e9 --- /dev/null +++ b/utils/checkpackagelib/test_lib_hash.py @@ -0,0 +1,137 @@ +import pytest +import checkpackagelib.test_util as util +import checkpackagelib.lib_hash as m + + +HashNumberOfFields = [ + ('empty file', + 'any', + '', + []), + ('empty line', + 'any', + '\n', + []), + ('ignore whitespace', + 'any', + '\t\n', + []), + ('ignore comments', + 'any', + '# text\n', + []), + ('1 field', + 'any', + 'field1\n', + [['any:1: expected three fields (url#adding-packages-hash)', + 'field1\n']]), + ('2 fields', + 'any', + 'field1 field2\n', + [['any:1: expected three fields (url#adding-packages-hash)', + 'field1 field2\n']]), + ('4 fields', + 'any', + 'field1 field2 field3 field4\n', + [['any:1: expected three fields (url#adding-packages-hash)', + 'field1 field2 field3 field4\n']]), + ('with 1 space', + 'any', + 'field1 field2 field3\n', + []), + ('many spaces', + 'any', + ' field1 field2 field3\n', + []), + ('tabs', + 'any', + 'field1\tfield2\tfield3\n', + []), + ('mix of tabs and spaces', + 'any', + '\tfield1\t field2\t field3 \n', + []), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', HashNumberOfFields) +def test_HashNumberOfFields(testname, filename, string, expected): + warnings = util.check_file(m.HashNumberOfFields, filename, string) + assert warnings == expected + + +HashType = [ + ('ignore empty files', + 'any', + '', + []), + ('ignore 1 field', + 'any', + 'text\n', + []), + ('wrong type', + 'any', + 'text text\n', + [['any:1: unexpected type of hash (url#adding-packages-hash)', + 'text text\n']]), + ('none', + 'any', + 'none text\n', + []), + ('md5 (good)', + 'any', + 'md5 12345678901234567890123456789012\n', + []), + ('md5 (short)', + 'any', + 'md5 123456\n', + [['any:1: hash size does not match type (url#adding-packages-hash)', + 'md5 123456\n', + 'expected 32 hex digits']]), + ('ignore space before', + 'any', + ' md5 12345678901234567890123456789012\n', + []), + ('2 spaces', + 'any', + 'md5 12345678901234567890123456789012\n', + []), + ('ignore tabs', + 'any', + 'md5\t12345678901234567890123456789012\n', + []), + ('common typo', + 'any', + 'md5sum 12345678901234567890123456789012\n', + [['any:1: unexpected type of hash (url#adding-packages-hash)', + 'md5sum 12345678901234567890123456789012\n']]), + ('md5 (too long)', + 'any', + 'md5 123456789012345678901234567890123\n', + [['any:1: hash size does not match type (url#adding-packages-hash)', + 'md5 123456789012345678901234567890123\n', + 'expected 32 hex digits']]), + ('sha1 (good)', + 'any', + 'sha1 1234567890123456789012345678901234567890\n', + []), + ('sha256', + 'any', + 'sha256 1234567890123456789012345678901234567890123456789012345678901234\n', + []), + ('sha384', + 'any', + 'sha384 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\n', + []), + ('sha512', + 'any', + 'sha512 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' + '9012345678\n', + []), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', HashType) +def test_HashType(testname, filename, string, expected): + warnings = util.check_file(m.HashType, filename, string) + assert warnings == expected diff --git a/utils/checkpackagelib/test_lib_mk.py b/utils/checkpackagelib/test_lib_mk.py new file mode 100644 index 0000000000..49fa216fcd --- /dev/null +++ b/utils/checkpackagelib/test_lib_mk.py @@ -0,0 +1,590 @@ +import pytest +import checkpackagelib.test_util as util +import checkpackagelib.lib_mk as m + + +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 diff --git a/utils/checkpackagelib/test_lib_patch.py b/utils/checkpackagelib/test_lib_patch.py new file mode 100644 index 0000000000..3b6fadf38c --- /dev/null +++ b/utils/checkpackagelib/test_lib_patch.py @@ -0,0 +1,96 @@ +import pytest +import checkpackagelib.test_util as util +import checkpackagelib.lib_patch as m + + +ApplyOrder = [ + ('standard', # catches https://bugs.busybox.net/show_bug.cgi?id=11271 + '0001-description.patch', + '', + []), + ('standard with path', + 'path/0001-description.patch', + '', + []), + ('acceptable format', + '1-description.patch', + '', + []), + ('acceptable format with path', + 'path/1-description.patch', + '', + []), + ('old format', + 'package-0001-description.patch', + '', + [['package-0001-description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), + ('old format with path', + 'path/package-0001-description.patch', + '', + [['path/package-0001-description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), + ('missing number', + 'description.patch', + '', + [['description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), + ('missing number with path', + 'path/description.patch', + '', + [['path/description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', ApplyOrder) +def test_ApplyOrder(testname, filename, string, expected): + warnings = util.check_file(m.ApplyOrder, filename, string) + assert warnings == expected + + +NumberedSubject = [ + ('no subject', + 'patch', + '', + []), + ('acceptable because it is not a git patch', + 'patch', + 'Subject: [PATCH 24/105] text\n', + []), + ('good', + 'patch', + 'Subject: [PATCH] text\n' + 'diff --git a/configure.ac b/configure.ac\n', + []), + ('bad', + 'patch', + 'Subject: [PATCH 24/105] text\n' + 'diff --git a/configure.ac b/configure.ac\n', + [["patch:1: generate your patches with 'git format-patch -N'", + 'Subject: [PATCH 24/105] text\n']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', NumberedSubject) +def test_NumberedSubject(testname, filename, string, expected): + warnings = util.check_file(m.NumberedSubject, filename, string) + assert warnings == expected + + +Sob = [ + ('good', + 'patch', + 'Signed-off-by: John Doe <johndoe@example.com>\n', + []), + ('empty', + 'patch', + '', + [['patch:0: missing Signed-off-by in the header (url#_format_and_licensing_of_the_package_patches)']]), + ('bad', + 'patch', + 'Subject: [PATCH 24/105] text\n', + [['patch:0: missing Signed-off-by in the header (url#_format_and_licensing_of_the_package_patches)']]), + ] + + +@pytest.mark.parametrize('testname,filename,string,expected', Sob) +def test_Sob(testname, filename, string, expected): + warnings = util.check_file(m.Sob, filename, string) + assert warnings == expected diff --git a/utils/checkpackagelib/test_util.py b/utils/checkpackagelib/test_util.py new file mode 100644 index 0000000000..23f2995e27 --- /dev/null +++ b/utils/checkpackagelib/test_util.py @@ -0,0 +1,8 @@ +def check_file(check_function, filename, string): + obj = check_function(filename, 'url') + result = [] + result.append(obj.before()) + for i, line in enumerate(string.splitlines(True)): + result.append(obj.check_line(i + 1, line)) + result.append(obj.after()) + return [r for r in result if r is not None] -- 2.25.1 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest 2021-12-05 10:53 [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Ricardo Martincoski @ 2021-12-05 10:53 ` Ricardo Martincoski 2022-01-09 10:51 ` Romain Naour 2021-12-05 10:53 ` [Buildroot] [PATCH-next 3/4] utils/checkpackagelib: run unit tests on GitLab CI Ricardo Martincoski ` (2 subsequent siblings) 3 siblings, 1 reply; 7+ messages in thread From: Ricardo Martincoski @ 2021-12-05 10:53 UTC (permalink / raw) To: buildroot; +Cc: Ricardo Martincoski ... so the unit tests for check-package can run in the GitLab CI. Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> --- support/docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/support/docker/Dockerfile b/support/docker/Dockerfile index 2aee129668..a5d54b6e9d 100644 --- a/support/docker/Dockerfile +++ b/support/docker/Dockerfile @@ -40,6 +40,7 @@ RUN apt-get install -y --no-install-recommends \ python3-flake8 \ python3-nose2 \ python3-pexpect \ + python3-pytest \ qemu-system-arm \ qemu-system-x86 \ rsync \ -- 2.25.1 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest 2021-12-05 10:53 ` [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest Ricardo Martincoski @ 2022-01-09 10:51 ` Romain Naour 0 siblings, 0 replies; 7+ messages in thread From: Romain Naour @ 2022-01-09 10:51 UTC (permalink / raw) To: Ricardo Martincoski, buildroot Hello Ricardo, In order to be ready for merging your series, add python3-pytest to the docker image now. Acked-by: Romain Naour <romain.naour@smile.fr> Best regards, Romain Le 05/12/2021 à 11:53, Ricardo Martincoski a écrit : > ... so the unit tests for check-package can run in the GitLab CI. > > Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> > --- > support/docker/Dockerfile | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/support/docker/Dockerfile b/support/docker/Dockerfile > index 2aee129668..a5d54b6e9d 100644 > --- a/support/docker/Dockerfile > +++ b/support/docker/Dockerfile > @@ -40,6 +40,7 @@ RUN apt-get install -y --no-install-recommends \ > python3-flake8 \ > python3-nose2 \ > python3-pexpect \ > + python3-pytest \ > qemu-system-arm \ > qemu-system-x86 \ > rsync \ > _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply [flat|nested] 7+ messages in thread
* [Buildroot] [PATCH-next 3/4] utils/checkpackagelib: run unit tests on GitLab CI 2021-12-05 10:53 [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest Ricardo Martincoski @ 2021-12-05 10:53 ` Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 4/4] utils/docker-run: new script Ricardo Martincoski 2022-01-08 22:37 ` [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Romain Naour 3 siblings, 0 replies; 7+ messages in thread From: Ricardo Martincoski @ 2021-12-05 10:53 UTC (permalink / raw) To: buildroot; +Cc: Ricardo Martincoski ... so we can catch regressions on check-package. Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> --- DO NOT APPLY before applying the patch before, generating a new docker image, uploading it, and updating the image name in the .gitlab-ci.yml --- support/misc/gitlab-ci.yml.in | 4 ++++ support/scripts/generate-gitlab-ci-yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/support/misc/gitlab-ci.yml.in b/support/misc/gitlab-ci.yml.in index be7951b3d2..47e72c3213 100644 --- a/support/misc/gitlab-ci.yml.in +++ b/support/misc/gitlab-ci.yml.in @@ -1,3 +1,7 @@ +.check-check-package_base: + script: + - python3 -m pytest -v utils/checkpackagelib/ + .check-DEVELOPERS_base: # get-developers should print just "No action specified"; if it prints # anything else, it's a parse error. diff --git a/support/scripts/generate-gitlab-ci-yml b/support/scripts/generate-gitlab-ci-yml index 7d09279bbd..bb023d8ed2 100755 --- a/support/scripts/generate-gitlab-ci-yml +++ b/support/scripts/generate-gitlab-ci-yml @@ -26,7 +26,7 @@ gen_tests() { local do_basics do_defconfigs do_runtime do_testpkg local defconfigs_ext cfg tst - basics=( DEVELOPERS flake8 package ) + basics=( check-package DEVELOPERS flake8 package ) defconfigs=( $(cd configs; LC_ALL=C ls -1 *_defconfig) ) -- 2.25.1 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Buildroot] [PATCH-next 4/4] utils/docker-run: new script 2021-12-05 10:53 [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 3/4] utils/checkpackagelib: run unit tests on GitLab CI Ricardo Martincoski @ 2021-12-05 10:53 ` Ricardo Martincoski 2022-01-08 22:37 ` [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Romain Naour 3 siblings, 0 replies; 7+ messages in thread From: Ricardo Martincoski @ 2021-12-05 10:53 UTC (permalink / raw) To: buildroot; +Cc: Ricardo Martincoski Add a small script to run commands in the same docker image used in the GitLab CI. For instance, one can run check-package unit tests without installing pytest directly in the host: $ ./utils/docker-run python3 -m pytest -v utils/checkpackagelib/ Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> --- utils/docker-run | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 utils/docker-run diff --git a/utils/docker-run b/utils/docker-run new file mode 100755 index 0000000000..499c194d13 --- /dev/null +++ b/utils/docker-run @@ -0,0 +1,7 @@ +#!/usr/bin/bash +set -o errexit -o pipefail +DIR=$(dirname "${0}") +MAIN_DIR=$(readlink -f "${DIR}/..") +IMAGE=$(grep ^image: "${MAIN_DIR}/.gitlab-ci.yml" | sed -e 's,^image: ,,g' | sed -e 's,\$CI_REGISTRY,registry.gitlab.com,g') +set -x +docker run -v ${MAIN_DIR}:/home/br-user -t "${IMAGE}" $* -- 2.25.1 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests 2021-12-05 10:53 [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Ricardo Martincoski ` (2 preceding siblings ...) 2021-12-05 10:53 ` [Buildroot] [PATCH-next 4/4] utils/docker-run: new script Ricardo Martincoski @ 2022-01-08 22:37 ` Romain Naour 2022-01-10 22:42 ` ricardo.martincoski 3 siblings, 1 reply; 7+ messages in thread From: Romain Naour @ 2022-01-08 22:37 UTC (permalink / raw) To: Ricardo Martincoski, buildroot Hello Ricardo, Le 05/12/2021 à 11:53, Ricardo Martincoski a écrit : > So anyone willing to contribute to check-package can run all tests in > less than 1 second by using: > $ python3 -m pytest -v utils/checkpackagelib/ > > Most test cases are in the form: > @pytest.mark.parametrize('testname,filename,string,expected', function) > - testname: a short description of the scenario tested, added in order > to improve readability of the log when some tests fail > - filename: the filename the check-package function being tested thinks > it is testing > - string: the content of the file being sent to the function under test > - expect: all expected warnings that a given function from > check-package should generate for a given file named filename and > with string as its content. I spend some time to review this patch to find something to say :) Actually the test_lib_hash tests are not checking with the new spacing convention we want to use in .hash files. I applied this patch [1] after your series and some tests failed. I would recommand to use the new spacing convention by default in this testsuite. [1] http://patchwork.ozlabs.org/project/buildroot/patch/20200603081253.14319-1-heiko.thiery@gmail.com/ Best regards, Romain > > Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com> > Cc: Arnout Vandecappelle <arnout@mind.be> > --- > NOTICE there are 2 tests failing, see the fix in: > http://patchwork.ozlabs.org/project/buildroot/patch/20211115235336.3814968-1-ricardo.martincoski@gmail.com/ > > See a sample running in the GitLab CI: > without the fix: > https://gitlab.com/RicardoMartincoski/buildroot/-/pipelines/422862195 > with the fix: > https://gitlab.com/RicardoMartincoski/buildroot/-/pipelines/422862127 > > Example of a failure, showing enough information to track down the test > that fails: > |testname = 'immediate assignment inside conditional and unconditional override outside' > |filename = 'any.mk' > |string = 'VAR_1 = VALUE1\nifeq (condition)\nVAR_1 := $(VAR_1), VALUE2\nendif\nVAR_1 := $(VAR_1), VALUE2\n' > |expected = [['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', overridden_variable) > | def test_overridden_variable(testname, filename, string, expected): > | warnings = util.check_file(m.OverriddenVariable, filename, string) > |> assert warnings == expected > |E AssertionError: assert [['any.mk:3: ...), VALUE2\n']] == [['any.mk:3: i...), VALUE2\n']] > |E At index 0 diff: ['any.mk:3: conditional override of variable VAR_1', 'VAR_1 := $(VAR_1), VALUE2\n'] != ['any.mk:3: immediate assignment to append to variable VAR_1', 'VAR_1 := $(VAR_1), VALUE2\n'] > |E Full diff: > |E - [['any.mk:3: conditional override of variable VAR_1', > |E + [['any.mk:3: immediate assignment to append to variable VAR_1', > |E 'VAR_1 := $(VAR_1), VALUE2\n'], > |E ['any.mk:5: unconditional override of variable VAR_1', > |E 'VAR_1 := $(VAR_1), VALUE2\n']] > | > |utils/checkpackagelib/test_lib_mk.py:168: AssertionError > |===================== 2 failed, 180 passed in 0.79 seconds ===================== > --- > utils/checkpackagelib/test_lib.py | 212 ++++++++ > utils/checkpackagelib/test_lib_config.py | 387 +++++++++++++++ > utils/checkpackagelib/test_lib_hash.py | 137 ++++++ > utils/checkpackagelib/test_lib_mk.py | 590 +++++++++++++++++++++++ > utils/checkpackagelib/test_lib_patch.py | 96 ++++ > utils/checkpackagelib/test_util.py | 8 + > 6 files changed, 1430 insertions(+) > create mode 100644 utils/checkpackagelib/test_lib.py > create mode 100644 utils/checkpackagelib/test_lib_config.py > create mode 100644 utils/checkpackagelib/test_lib_hash.py > create mode 100644 utils/checkpackagelib/test_lib_mk.py > create mode 100644 utils/checkpackagelib/test_lib_patch.py > create mode 100644 utils/checkpackagelib/test_util.py > > diff --git a/utils/checkpackagelib/test_lib.py b/utils/checkpackagelib/test_lib.py > new file mode 100644 > index 0000000000..976a63d84d > --- /dev/null > +++ b/utils/checkpackagelib/test_lib.py > @@ -0,0 +1,212 @@ > +import pytest > +import checkpackagelib.test_util as util > +import checkpackagelib.lib as m > + > + > +ConsecutiveEmptyLines = [ > + ('1 line (no newline)', > + 'any', > + '', > + []), > + ('1 line', > + 'any', > + '\n', > + []), > + ('2 lines', > + 'any', > + '\n' > + '\n', > + [['any:2: consecutive empty lines']]), > + ('more than 2 consecutive', > + 'any', > + '\n' > + '\n' > + '\n', > + [['any:2: consecutive empty lines'], > + ['any:3: consecutive empty lines']]), > + ('ignore whitespace 1', > + 'any', > + '\n' > + ' ', > + [['any:2: consecutive empty lines']]), > + ('ignore whitespace 2', > + 'any', > + ' \n' > + '\t\n', > + [['any:2: consecutive empty lines']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', ConsecutiveEmptyLines) > +def test_ConsecutiveEmptyLines(testname, filename, string, expected): > + warnings = util.check_file(m.ConsecutiveEmptyLines, filename, string) > + assert warnings == expected > + > + > +EmptyLastLine = [ > + ('ignore empty file', > + 'any', > + '', > + []), > + ('empty line (newline)', > + 'any', > + '\n', > + [['any:1: empty line at end of file']]), > + ('empty line (space, newline)', > + 'any', > + ' \n', > + [['any:1: empty line at end of file']]), > + ('empty line (space, no newline)', > + 'any', > + ' ', > + [['any:1: empty line at end of file']]), > + ('warn for the last of 2', > + 'any', > + '\n' > + '\n', > + [['any:2: empty line at end of file']]), > + ('warn for the last of 3', > + 'any', > + '\n' > + '\n' > + '\n', > + [['any:3: empty line at end of file']]), > + ('ignore whitespace', > + 'any', > + ' \n' > + '\t\n', > + [['any:2: empty line at end of file']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', EmptyLastLine) > +def test_EmptyLastLine(testname, filename, string, expected): > + warnings = util.check_file(m.EmptyLastLine, filename, string) > + assert warnings == expected > + > + > +NewlineAtEof = [ > + ('good', > + 'any', > + 'text\n', > + []), > + ('text (bad)', > + 'any', > + '\n' > + 'text', > + [['any:2: missing newline at end of file', > + 'text']]), > + ('space (bad)', > + 'any', > + '\n' > + ' ', > + [['any:2: missing newline at end of file', > + ' ']]), > + ('tab (bad)', > + 'any', > + '\n' > + '\t', > + [['any:2: missing newline at end of file', > + '\t']]), > + ('even for file with one line', > + 'any', > + ' ', > + [['any:1: missing newline at end of file', > + ' ']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', NewlineAtEof) > +def test_NewlineAtEof(testname, filename, string, expected): > + warnings = util.check_file(m.NewlineAtEof, filename, string) > + assert warnings == expected > + > + > +TrailingSpace = [ > + ('good', > + 'any', > + 'text\n', > + []), > + ('ignore missing newline', > + 'any', > + '\n' > + 'text', > + []), > + ('spaces', > + 'any', > + 'text \n', > + [['any:1: line contains trailing whitespace', > + 'text \n']]), > + ('tabs after text', > + 'any', > + 'text\t\t\n', > + [['any:1: line contains trailing whitespace', > + 'text\t\t\n']]), > + ('mix of tabs and spaces', > + 'any', > + ' \n' > + ' ', > + [['any:1: line contains trailing whitespace', > + ' \n'], > + ['any:2: line contains trailing whitespace', > + ' ']]), > + ('blank line with tabs', > + 'any', > + '\n' > + '\t', > + [['any:2: line contains trailing whitespace', > + '\t']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', TrailingSpace) > +def test_TrailingSpace(testname, filename, string, expected): > + warnings = util.check_file(m.TrailingSpace, filename, string) > + assert warnings == expected > + > + > +Utf8Characters = [ > + ('usual', > + 'any', > + 'text\n', > + []), > + ('acceptable character', > + 'any', > + '\x60', > + []), > + ('unacceptable character', > + 'any', > + '\x81', > + [['any:1: line contains UTF-8 characters', > + '\x81']]), > + ('2 warnings', > + 'any', > + 'text\n' > + 'text \xc8 text\n' > + '\xc9\n', > + [['any:2: line contains UTF-8 characters', > + 'text \xc8 text\n'], > + ['any:3: line contains UTF-8 characters', > + '\xc9\n']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', Utf8Characters) > +def test_Utf8Characters(testname, filename, string, expected): > + warnings = util.check_file(m.Utf8Characters, filename, string) > + assert warnings == expected > + > + > +def test_all_check_functions_are_used(): > + import inspect > + import checkpackagelib.lib_config as lib_config > + import checkpackagelib.lib_hash as lib_hash > + import checkpackagelib.lib_mk as lib_mk > + import checkpackagelib.lib_patch as lib_patch > + c_config = [c[0] for c in inspect.getmembers(lib_config, inspect.isclass)] > + c_hash = [c[0] for c in inspect.getmembers(lib_hash, inspect.isclass)] > + c_mk = [c[0] for c in inspect.getmembers(lib_mk, inspect.isclass)] > + c_patch = [c[0] for c in inspect.getmembers(lib_patch, inspect.isclass)] > + c_all = c_config + c_hash + c_mk + c_patch > + c_common = [c[0] for c in inspect.getmembers(m, inspect.isclass)] > + assert set(c_common) <= set(c_all) > diff --git a/utils/checkpackagelib/test_lib_config.py b/utils/checkpackagelib/test_lib_config.py > new file mode 100644 > index 0000000000..91a549adf2 > --- /dev/null > +++ b/utils/checkpackagelib/test_lib_config.py > @@ -0,0 +1,387 @@ > +import pytest > +import checkpackagelib.test_util as util > +import checkpackagelib.lib_config as m > + > + > +AttributesOrder = [ > + ('good example', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'default y\n' > + 'depends on BR2_USE_BAR # runtime\n' > + 'select BR2_PACKAGE_BAZ\n' > + 'help\n' > + '\t help text\n', > + []), > + ('depends before default', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'depends on BR2_USE_BAR\n' > + 'default y\n', > + [['any:4: attributes order: type, default, depends on, select, help (url#_config_files)', > + 'default y\n']]), > + ('select after help', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'help\n' > + '\t help text\n' > + 'select BR2_PACKAGE_BAZ\n', > + [['any:5: attributes order: type, default, depends on, select, help (url#_config_files)', > + 'select BR2_PACKAGE_BAZ\n']]), > + ('string', > + 'any', > + 'config BR2_PACKAGE_FOO_PLUGINS\n' > + 'string "foo plugins"\n' > + 'default "all"\n', > + []), > + ('ignore tabs', > + 'any', > + 'config\tBR2_PACKAGE_FOO_PLUGINS\n' > + 'default\t"all"\n' > + 'string\t"foo plugins"\n', > + [['any:3: attributes order: type, default, depends on, select, help (url#_config_files)', > + 'string\t"foo plugins"\n']]), > + ('choice', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'if BR2_PACKAGE_FOO\n' > + '\n' > + 'choice\n' > + 'prompt "type of foo"\n' > + 'default BR2_PACKAGE_FOO_STRING\n' > + '\n' > + 'config BR2_PACKAGE_FOO_NONE\n' > + 'bool "none"\n' > + '\n' > + 'config BR2_PACKAGE_FOO_STRING\n' > + 'bool "string"\n' > + '\n' > + 'endchoice\n' > + '\n' > + 'endif\n' > + '\n', > + []), > + ('type after default', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'if BR2_PACKAGE_FOO\n' > + '\n' > + 'choice\n' > + 'default BR2_PACKAGE_FOO_STRING\n' > + 'prompt "type of foo"\n', > + [['any:7: attributes order: type, default, depends on, select, help (url#_config_files)', > + 'prompt "type of foo"\n']]), > + ('menu', > + 'any', > + 'menuconfig BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'help\n' > + '\t help text\n' > + '\t help text\n' > + '\n' > + 'if BR2_PACKAGE_FOO\n' > + '\n' > + 'menu "foo plugins"\n' > + 'config BR2_PACKAGE_FOO_COUNTER\n' > + 'bool "counter"\n' > + '\n' > + 'endmenu\n' > + '\n' > + 'endif\n', > + []), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', AttributesOrder) > +def test_AttributesOrder(testname, filename, string, expected): > + warnings = util.check_file(m.AttributesOrder, filename, string) > + assert warnings == expected > + > + > +CommentsMenusPackagesOrder = [ > + ('top menu (good)', > + 'package/Config.in', > + 'menu "Target packages"\n' > + 'source "package/busybox/Config.in"\n' > + 'source "package/skeleton/Config.in"\n', > + []), > + ('top menu (bad)', > + 'package/Config.in', > + 'source "package/skeleton/Config.in"\n' > + 'source "package/busybox/Config.in"\n', > + [['package/Config.in:2: Packages in: The top level menu,\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: busybox', > + 'source "package/busybox/Config.in"\n']]), > + ('menu (bad)', > + 'package/Config.in', > + 'menu "Target packages"\n' > + 'source "package/skeleton/Config.in"\n' > + 'source "package/busybox/Config.in"\n', > + [['package/Config.in:3: Packages in: menu "Target packages",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: busybox', > + 'source "package/busybox/Config.in"\n']]), > + ('underscore (good)', > + 'package/Config.in.host', > + 'menu "Hardware handling"\n' > + 'menu "Firmware"\n' > + 'endmenu\n' > + 'source "package/usb_modeswitch/Config.in"\n' > + 'source "package/usbmount/Config.in"\n', > + []), > + ('underscore (bad)', > + 'package/Config.in.host', > + 'menu "Hardware handling"\n' > + 'menu "Firmware"\n' > + 'endmenu\n' > + 'source "package/usbmount/Config.in"\n' > + 'source "package/usb_modeswitch/Config.in"\n', > + [['package/Config.in.host:5: Packages in: menu "Hardware handling",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: usb_modeswitch', > + 'source "package/usb_modeswitch/Config.in"\n']]), > + ('ignore other files', > + 'any other file', > + 'menu "Hardware handling"\n' > + 'source "package/bbb/Config.in"\n' > + 'source "package/aaa/Config.in"\n', > + []), > + ('dash (bad)', > + 'package/Config.in', > + 'menu "packages"\n' > + 'source "package/a_a/Config.in"\n' > + 'source "package/a-a/Config.in"\n' > + 'source "package/a1a/Config.in"\n' > + 'source "package/aAa/Config.in"\n' > + 'source "package/aaa/Config.in"\n', > + [['package/Config.in:3: Packages in: menu "packages",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: a-a', > + 'source "package/a-a/Config.in"\n']]), > + ('underscore (bad)', > + 'package/Config.in', > + 'menu "packages"\n' > + 'source "package/a-a/Config.in"\n' > + 'source "package/a1a/Config.in"\n' > + 'source "package/a_a/Config.in"\n' > + 'source "package/aAa/Config.in"\n' > + 'source "package/aaa/Config.in"\n', > + [['package/Config.in:4: Packages in: menu "packages",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: a_a', > + 'source "package/a_a/Config.in"\n']]), > + ('digit (bad)', > + 'package/Config.in', > + 'menu "packages"\n' > + 'source "package/a-a/Config.in"\n' > + 'source "package/a_a/Config.in"\n' > + 'source "package/aAa/Config.in"\n' > + 'source "package/a1a/Config.in"\n' > + 'source "package/aaa/Config.in"\n', > + [['package/Config.in:5: Packages in: menu "packages",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: a1a', > + 'source "package/a1a/Config.in"\n']]), > + ('capitals (bad)', > + 'package/Config.in', > + 'menu "packages"\n' > + 'source "package/a-a/Config.in"\n' > + 'source "package/a_a/Config.in"\n' > + 'source "package/a1a/Config.in"\n' > + 'source "package/aaa/Config.in"\n' > + 'source "package/aAa/Config.in"\n', > + [['package/Config.in:6: Packages in: menu "packages",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: aAa', > + 'source "package/aAa/Config.in"\n']]), > + ('digits, capitals, underscore (good)', > + 'package/Config.in', > + 'menu "packages"\n' > + 'source "package/a-a/Config.in"\n' > + 'source "package/a_a/Config.in"\n' > + 'source "package/a1a/Config.in"\n' > + 'source "package/aAa/Config.in"\n' > + 'source "package/aaa/Config.in"\n', > + []), > + ('conditional menu (good)', > + 'package/Config.in', > + 'menu "Other"\n' > + 'source "package/linux-pam/Config.in"\n' > + 'if BR2_PACKAGE_LINUX_PAM\n' > + 'comment "linux-pam plugins"\n' > + 'source "package/libpam-radius-auth/Config.in"\n' > + 'source "package/libpam-tacplus/Config.in"\n' > + 'endif\n' > + 'source "package/liquid-dsp/Config.in"\n', > + []), > + ('conditional menu (bad)', > + 'package/Config.in', > + 'menu "Other"\n' > + 'source "package/linux-pam/Config.in"\n' > + 'if BR2_PACKAGE_LINUX_PAM\n' > + 'comment "linux-pam plugins"\n' > + 'source "package/libpam-tacplus/Config.in"\n' > + 'source "package/libpam-radius-auth/Config.in"\n' > + 'endif\n' > + 'source "package/liquid-dsp/Config.in"\n', > + [['package/Config.in:6: Packages in: comment "linux-pam plugins",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: libpam-radius-auth', > + 'source "package/libpam-radius-auth/Config.in"\n']]), > + ('no conditional (bad)', > + 'package/Config.in', > + 'menu "Other"\n' > + 'source "package/linux-pam/Config.in"\n' > + 'source "package/libpam-radius-auth/Config.in"\n' > + 'source "package/libpam-tacplus/Config.in"\n' > + 'source "package/liquid-dsp/Config.in"\n', > + [['package/Config.in:3: Packages in: menu "Other",\n' > + ' are not alphabetically ordered;\n' > + " correct order: '-', '_', digits, capitals, lowercase;\n" > + ' first incorrect package: libpam-radius-auth', > + 'source "package/libpam-radius-auth/Config.in"\n']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', CommentsMenusPackagesOrder) > +def test_CommentsMenusPackagesOrder(testname, filename, string, expected): > + warnings = util.check_file(m.CommentsMenusPackagesOrder, filename, string) > + assert warnings == expected > + > + > +HelpText = [ > + ('single line', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'bool "foo"\n' > + 'default y\n' > + 'depends on BR2_USE_BAR # runtime\n' > + 'select BR2_PACKAGE_BAZ\n' > + 'help\n' > + '\t help text\n', > + []), > + ('larger than 72', > + 'any', > + 'help\n' > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 123\n' > + '\t help text\n', > + [['any:3: help text: <tab><2 spaces><62 chars> (url#writing-rules-config-in)', > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 123\n', > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12']]), > + ('long url at beginning of line', > + 'any', > + 'help\n' > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' > + '\t http://url.that.is.longer.than.seventy.two.characthers/folder_name\n' > + '\t https://url.that.is.longer.than.seventy.two.characthers/folder_name\n' > + '\t git://url.that.is.longer.than.seventy.two.characthers/folder_name\n', > + []), > + ('long url not at beginning of line', > + 'any', > + 'help\n' > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' > + '\t refer to http://url.that.is.longer.than.seventy.two.characthers/folder_name\n' > + '\n' > + '\t http://url.that.is.longer.than.seventy.two.characthers/folder_name\n', > + [['any:3: help text: <tab><2 spaces><62 chars> (url#writing-rules-config-in)', > + '\t refer to http://url.that.is.longer.than.seventy.two.characthers/folder_name\n', > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12']]), > + ('allow beautified items', > + 'any', > + 'help\n' > + '\t 123456789 123456789 123456789 123456789 123456789 123456789 12\n' > + '\t summary:\n' > + '\t - enable that config\n' > + '\t - built it\n', > + []), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', HelpText) > +def test_HelpText(testname, filename, string, expected): > + warnings = util.check_file(m.HelpText, filename, string) > + assert warnings == expected > + > + > +Indent = [ > + ('good example', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + '\tbool "foo"\n' > + '\tdefault y\n' > + '\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n' > + '\tdepends on BR2_INSTALL_LIBSTDCPP\n' > + '# very useful comment\n' > + '\tselect BR2_PACKAGE_BAZ\n' > + '\thelp\n' > + '\t help text\n' > + '\n' > + 'comment "foo needs toolchain w/ C++, threads"\n' > + '\tdepends on !BR2_INSTALL_LIBSTDCPP || \\\n' > + '\t\t!BR2_TOOLCHAIN_HAS_THREADS\n' > + '\n' > + 'source "package/foo/bar/Config.in"\n', > + []), > + ('spaces', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + ' bool "foo"\n', > + [['any:2: should be indented with one tab (url#_config_files)', > + ' bool "foo"\n']]), > + ('without indent', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + 'default y\n', > + [['any:2: should be indented with one tab (url#_config_files)', > + 'default y\n']]), > + ('too much tabs', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + '\t\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n', > + [['any:2: should be indented with one tab (url#_config_files)', > + '\t\tdepends on BR2_TOOLCHAIN_HAS_THREADS\n']]), > + ('help', > + 'any', > + 'config BR2_PACKAGE_FOO\n' > + ' help\n', > + [['any:2: should be indented with one tab (url#_config_files)', > + ' help\n']]), > + ('continuation line', > + 'any', > + 'comment "foo needs toolchain w/ C++, threads"\n' > + '\tdepends on !BR2_INSTALL_LIBSTDCPP || \\\n' > + ' !BR2_TOOLCHAIN_HAS_THREADS\n', > + [['any:3: continuation line should be indented using tabs', > + ' !BR2_TOOLCHAIN_HAS_THREADS\n']]), > + ('comment with tabs', > + 'any', > + '\tcomment "foo needs toolchain w/ C++, threads"\n', > + [['any:1: should not be indented', > + '\tcomment "foo needs toolchain w/ C++, threads"\n']]), > + ('comment with spaces', > + 'any', > + ' comment "foo needs toolchain w/ C++, threads"\n', > + [['any:1: should not be indented', > + ' comment "foo needs toolchain w/ C++, threads"\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 > diff --git a/utils/checkpackagelib/test_lib_hash.py b/utils/checkpackagelib/test_lib_hash.py > new file mode 100644 > index 0000000000..c160a394e9 > --- /dev/null > +++ b/utils/checkpackagelib/test_lib_hash.py > @@ -0,0 +1,137 @@ > +import pytest > +import checkpackagelib.test_util as util > +import checkpackagelib.lib_hash as m > + > + > +HashNumberOfFields = [ > + ('empty file', > + 'any', > + '', > + []), > + ('empty line', > + 'any', > + '\n', > + []), > + ('ignore whitespace', > + 'any', > + '\t\n', > + []), > + ('ignore comments', > + 'any', > + '# text\n', > + []), > + ('1 field', > + 'any', > + 'field1\n', > + [['any:1: expected three fields (url#adding-packages-hash)', > + 'field1\n']]), > + ('2 fields', > + 'any', > + 'field1 field2\n', > + [['any:1: expected three fields (url#adding-packages-hash)', > + 'field1 field2\n']]), > + ('4 fields', > + 'any', > + 'field1 field2 field3 field4\n', > + [['any:1: expected three fields (url#adding-packages-hash)', > + 'field1 field2 field3 field4\n']]), > + ('with 1 space', > + 'any', > + 'field1 field2 field3\n', > + []), > + ('many spaces', > + 'any', > + ' field1 field2 field3\n', > + []), > + ('tabs', > + 'any', > + 'field1\tfield2\tfield3\n', > + []), > + ('mix of tabs and spaces', > + 'any', > + '\tfield1\t field2\t field3 \n', > + []), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', HashNumberOfFields) > +def test_HashNumberOfFields(testname, filename, string, expected): > + warnings = util.check_file(m.HashNumberOfFields, filename, string) > + assert warnings == expected > + > + > +HashType = [ > + ('ignore empty files', > + 'any', > + '', > + []), > + ('ignore 1 field', > + 'any', > + 'text\n', > + []), > + ('wrong type', > + 'any', > + 'text text\n', > + [['any:1: unexpected type of hash (url#adding-packages-hash)', > + 'text text\n']]), > + ('none', > + 'any', > + 'none text\n', > + []), > + ('md5 (good)', > + 'any', > + 'md5 12345678901234567890123456789012\n', > + []), > + ('md5 (short)', > + 'any', > + 'md5 123456\n', > + [['any:1: hash size does not match type (url#adding-packages-hash)', > + 'md5 123456\n', > + 'expected 32 hex digits']]), > + ('ignore space before', > + 'any', > + ' md5 12345678901234567890123456789012\n', > + []), > + ('2 spaces', > + 'any', > + 'md5 12345678901234567890123456789012\n', > + []), > + ('ignore tabs', > + 'any', > + 'md5\t12345678901234567890123456789012\n', > + []), > + ('common typo', > + 'any', > + 'md5sum 12345678901234567890123456789012\n', > + [['any:1: unexpected type of hash (url#adding-packages-hash)', > + 'md5sum 12345678901234567890123456789012\n']]), > + ('md5 (too long)', > + 'any', > + 'md5 123456789012345678901234567890123\n', > + [['any:1: hash size does not match type (url#adding-packages-hash)', > + 'md5 123456789012345678901234567890123\n', > + 'expected 32 hex digits']]), > + ('sha1 (good)', > + 'any', > + 'sha1 1234567890123456789012345678901234567890\n', > + []), > + ('sha256', > + 'any', > + 'sha256 1234567890123456789012345678901234567890123456789012345678901234\n', > + []), > + ('sha384', > + 'any', > + 'sha384 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\n', > + []), > + ('sha512', > + 'any', > + 'sha512 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' > + '9012345678\n', > + []), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', HashType) > +def test_HashType(testname, filename, string, expected): > + warnings = util.check_file(m.HashType, filename, string) > + assert warnings == expected > diff --git a/utils/checkpackagelib/test_lib_mk.py b/utils/checkpackagelib/test_lib_mk.py > new file mode 100644 > index 0000000000..49fa216fcd > --- /dev/null > +++ b/utils/checkpackagelib/test_lib_mk.py > @@ -0,0 +1,590 @@ > +import pytest > +import checkpackagelib.test_util as util > +import checkpackagelib.lib_mk as m > + > + > +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 > diff --git a/utils/checkpackagelib/test_lib_patch.py b/utils/checkpackagelib/test_lib_patch.py > new file mode 100644 > index 0000000000..3b6fadf38c > --- /dev/null > +++ b/utils/checkpackagelib/test_lib_patch.py > @@ -0,0 +1,96 @@ > +import pytest > +import checkpackagelib.test_util as util > +import checkpackagelib.lib_patch as m > + > + > +ApplyOrder = [ > + ('standard', # catches https://bugs.busybox.net/show_bug.cgi?id=11271 > + '0001-description.patch', > + '', > + []), > + ('standard with path', > + 'path/0001-description.patch', > + '', > + []), > + ('acceptable format', > + '1-description.patch', > + '', > + []), > + ('acceptable format with path', > + 'path/1-description.patch', > + '', > + []), > + ('old format', > + 'package-0001-description.patch', > + '', > + [['package-0001-description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), > + ('old format with path', > + 'path/package-0001-description.patch', > + '', > + [['path/package-0001-description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), > + ('missing number', > + 'description.patch', > + '', > + [['description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), > + ('missing number with path', > + 'path/description.patch', > + '', > + [['path/description.patch:0: use name <number>-<description>.patch (url#_providing_patches)']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', ApplyOrder) > +def test_ApplyOrder(testname, filename, string, expected): > + warnings = util.check_file(m.ApplyOrder, filename, string) > + assert warnings == expected > + > + > +NumberedSubject = [ > + ('no subject', > + 'patch', > + '', > + []), > + ('acceptable because it is not a git patch', > + 'patch', > + 'Subject: [PATCH 24/105] text\n', > + []), > + ('good', > + 'patch', > + 'Subject: [PATCH] text\n' > + 'diff --git a/configure.ac b/configure.ac\n', > + []), > + ('bad', > + 'patch', > + 'Subject: [PATCH 24/105] text\n' > + 'diff --git a/configure.ac b/configure.ac\n', > + [["patch:1: generate your patches with 'git format-patch -N'", > + 'Subject: [PATCH 24/105] text\n']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', NumberedSubject) > +def test_NumberedSubject(testname, filename, string, expected): > + warnings = util.check_file(m.NumberedSubject, filename, string) > + assert warnings == expected > + > + > +Sob = [ > + ('good', > + 'patch', > + 'Signed-off-by: John Doe <johndoe@example.com>\n', > + []), > + ('empty', > + 'patch', > + '', > + [['patch:0: missing Signed-off-by in the header (url#_format_and_licensing_of_the_package_patches)']]), > + ('bad', > + 'patch', > + 'Subject: [PATCH 24/105] text\n', > + [['patch:0: missing Signed-off-by in the header (url#_format_and_licensing_of_the_package_patches)']]), > + ] > + > + > +@pytest.mark.parametrize('testname,filename,string,expected', Sob) > +def test_Sob(testname, filename, string, expected): > + warnings = util.check_file(m.Sob, filename, string) > + assert warnings == expected > diff --git a/utils/checkpackagelib/test_util.py b/utils/checkpackagelib/test_util.py > new file mode 100644 > index 0000000000..23f2995e27 > --- /dev/null > +++ b/utils/checkpackagelib/test_util.py > @@ -0,0 +1,8 @@ > +def check_file(check_function, filename, string): > + obj = check_function(filename, 'url') > + result = [] > + result.append(obj.before()) > + for i, line in enumerate(string.splitlines(True)): > + result.append(obj.check_line(i + 1, line)) > + result.append(obj.after()) > + return [r for r in result if r is not None] > _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests 2022-01-08 22:37 ` [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Romain Naour @ 2022-01-10 22:42 ` ricardo.martincoski 0 siblings, 0 replies; 7+ messages in thread From: ricardo.martincoski @ 2022-01-10 22:42 UTC (permalink / raw) To: romain.naour; +Cc: buildroot [-- Attachment #1: Type: text/plain, Size: 1469 bytes --] Hello Romain, On Sat, Jan 08, 2022 at 07:37 PM, Romain Naour wrote: > Hello Ricardo, > > Le 05/12/2021 à 11:53, Ricardo Martincoski a écrit : >> So anyone willing to contribute to check-package can run all tests in >> less than 1 second by using: >> $ python3 -m pytest -v utils/checkpackagelib/ >> >> Most test cases are in the form: >> @pytest.mark.parametrize('testname,filename,string,expected', function) >> - testname: a short description of the scenario tested, added in order >> to improve readability of the log when some tests fail >> - filename: the filename the check-package function being tested thinks >> it is testing >> - string: the content of the file being sent to the function under test >> - expect: all expected warnings that a given function from >> check-package should generate for a given file named filename and >> with string as its content. > > I spend some time to review this patch to find something to say :) Thank you for your time. I imagine how boring was to review this patch :) > > Actually the test_lib_hash tests are not checking with the new spacing > convention we want to use in .hash files. > > I applied this patch [1] after your series and some tests failed. Now it landed on master. > > I would recommand to use the new spacing convention by default in this testsuite. Sure. I will send v2. Regards, Ricardo [-- Attachment #2: Type: text/plain, Size: 150 bytes --] _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-01-10 22:42 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-12-05 10:53 [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 2/4] support/docker: add python3-pytest Ricardo Martincoski 2022-01-09 10:51 ` Romain Naour 2021-12-05 10:53 ` [Buildroot] [PATCH-next 3/4] utils/checkpackagelib: run unit tests on GitLab CI Ricardo Martincoski 2021-12-05 10:53 ` [Buildroot] [PATCH-next 4/4] utils/docker-run: new script Ricardo Martincoski 2022-01-08 22:37 ` [Buildroot] [PATCH-next 1/4] utils/checkpackagelib: add unit tests Romain Naour 2022-01-10 22:42 ` ricardo.martincoski
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.