From 245999da3d7b892e8fbbefb7d2730348a5ef5c4c Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Fri, 15 Oct 2021 20:35:18 +0200 Subject: [PATCH 01/11] lint for unused inputs considering command, configfiles, filters, ... add linting for configfiles --- lib/galaxy/tool_util/linters/_util.py | 34 ++ lib/galaxy/tool_util/linters/configfiles.py | 22 + lib/galaxy/tool_util/linters/inputs.py | 28 + test/unit/tool_util/test_tool_linters.py | 636 +++++++++++++++++++- 4 files changed, 688 insertions(+), 32 deletions(-) create mode 100644 lib/galaxy/tool_util/linters/configfiles.py diff --git a/lib/galaxy/tool_util/linters/_util.py b/lib/galaxy/tool_util/linters/_util.py index 1e12c82faa1d..da49e284b6cc 100644 --- a/lib/galaxy/tool_util/linters/_util.py +++ b/lib/galaxy/tool_util/linters/_util.py @@ -1,6 +1,40 @@ import re +def get_code(tool_xml): + """get code used in the Galaxy tool""" + + # get code from command and configfiles + code = "" + for cn in tool_xml.findall(".//command"): + code += cn.text + for cn in tool_xml.findall(".//configfile"): + code += cn.text + # remove comments, + # TODO this is not optimal, but strings containing "##"" complicate this quite a bit + # TODO also this is not complete, e.g. multiline comments are missing + code = "\n".join([_ for _ in code.splitlines() if not _.lstrip().startswith("##")]) + + # get code from output filters + filtercode = "" + for cn in tool_xml.findall("./outputs/*/filter"): + filtercode += cn.text + "\n" + + # get output labels which might contain code + labelcode = "" + for cn in tool_xml.findall("./outputs/*[@label]"): + labelcode + cn.attrib["label"] + "\n" + + # TODO not optimal to mix filter code and the following, since they use cheetah synax, i.e. $param + for cn in tool_xml.findall("./outputs/*/actions/action[@default]"): + labelcode += cn.attrib["default"] + "\n" + + for cn in tool_xml.findall("./outputs/*/actions/conditional[@name]"): + labelcode += cn.attrib["name"] + "\n" + + return code, filtercode, labelcode + + def is_datasource(tool_xml): """Returns true if the tool is a datasource tool""" return tool_xml.getroot().attrib.get("tool_type", "") == "data_source" diff --git a/lib/galaxy/tool_util/linters/configfiles.py b/lib/galaxy/tool_util/linters/configfiles.py new file mode 100644 index 000000000000..f88e020ec6f0 --- /dev/null +++ b/lib/galaxy/tool_util/linters/configfiles.py @@ -0,0 +1,22 @@ +"""This module contains a linting function for a tool's configfiles section. +""" + + +def lint_configfiles(tool_xml, lint_ctx): + """""" + root = tool_xml.getroot() + configfiles = root.findall("configfiles") + if len(configfiles) > 1: + lint_ctx.error("More than one configfiles tag found, behavior undefined.") + return + elif len(configfiles) == 0: + return + configfiles = configfiles[0] + + configfile = configfiles.findall("configfile|inputs") + for cf in configfile: + if not ("name" in cf.attrib or "filename" in cf.attrib): + lint_ctx.error("Configfile needs to define name or filename.") + if cf.tag == "inputs": + if "data_style" in cf.attribs and cf.attribs["data_style"] in ["paths", "staging_path_and_source_path"]: + lint_ctx.error(f"Unknown data_style {cf.attribs['data_style']}for inputs configfile.") diff --git a/lib/galaxy/tool_util/linters/inputs.py b/lib/galaxy/tool_util/linters/inputs.py index e86372e2bdd7..298f85f244a9 100644 --- a/lib/galaxy/tool_util/linters/inputs.py +++ b/lib/galaxy/tool_util/linters/inputs.py @@ -3,6 +3,7 @@ from galaxy.util import string_as_bool from ._util import ( + get_code, is_datasource, is_valid_cheetah_placeholder, ) @@ -120,6 +121,7 @@ def lint_inputs(tool_xml, lint_ctx): datasource = is_datasource(tool_xml) input_names = set() inputs = tool_xml.findall("./inputs//param") + code, filter_code, label_code = get_code(tool_xml) # determine node to report for general problems with outputs tool_node = tool_xml.find("./inputs") if tool_node is None: @@ -171,6 +173,32 @@ def lint_inputs(tool_xml, lint_ctx): param_type = param_attrib["type"] # TODO lint for valid param type - attribute combinations + # TODO lint required attributes for valid each param type + # check if the parameter is used somewhere + conf_inputs = tool_xml.find("./configfiles/inputs") + if re.search(r"[^\w_]" + param_name + r"([^\w_]|$)", code) is not None: + pass + elif param_name in filter_code: + pass + elif re.search(r"[^\w_]" + param_name + r"[^\w_]", label_code): + lint_ctx.info(f"Param input [{param_name}] only found in an attribute.") + elif len(tool_xml.findall(f"./outputs//change_format/when[@input='{param_name}']")) > 0: + pass + elif conf_inputs is not None: # in this it's really hard to know + if param_type in ["data", "collection"] and not conf_inputs.attrib.get("data_style"): + lint_ctx.error(f"Param input [{param_name}] not found in command or configfiles. Does the present inputs config miss the 'data_style' attribute?") + else: + if conf_inputs.attrib.get('name', None): + lint_ctx.info(f"Param input [{param_name}] may be unused. Double check that is used in the inputs configfile [{conf_inputs.attrib['name']}] and that this file is used properly.") + elif conf_inputs.attrib.get('filename', None): + lint_ctx.info(f"Param input [{param_name}] may be unused. Double check that is used in the inputs configfile [{conf_inputs.attrib['filename']}] and that this file is used properly.") + elif param.getparent().tag == "conditional": + lint_ctx.info(f"Param input [{param_name}] only used in the select of a conditional, which should be OK.") + else: + lint_ctx.error(f"Param input [{param_name}] not found in command or configfiles.") + + if not is_valid_cheetah_placeholder(param_name): + lint_ctx.warn(f"Param input [{param_name}] is not a valid Cheetah placeholder.") # lint for valid param type - child node combinations for ptcc in PARAM_TYPE_CHILD_COMBINATIONS: diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 34617ed048ea..362cb3d45f40 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -291,6 +291,7 @@ INPUTS_SELECT_DEPRECATIONS = """ + $select_do$select_ff$select_fp @@ -344,6 +345,7 @@ INPUTS_VALIDATOR_INCOMPATIBILITIES = """ + $param_name TEXT @@ -449,6 +451,96 @@ """ +PARAMETER_IN_COMMAND = """ + +""" + +PARAMETER_IN_COMMAND_WITH_INPUTS = """ + +""" + +# tool xml for repeats linter +REPEATS = """ + + + + + + + +""" + # test tool xml for outputs linter OUTPUTS_MISSING = """ @@ -482,6 +574,7 @@ """ + # check that linter accepts format source for collection elements as means to specify format # and that the linter warns if format and format_source are used OUTPUTS_COLLECTION_FORMAT_SOURCE = """ @@ -533,17 +626,6 @@ """ -# tool xml for repeats linter -REPEATS = """ - - - - - - - -""" - # tool xml for stdio linter STDIO_DEFAULT_FOR_DEFAULT_PROFILE = """ @@ -587,15 +669,6 @@ TESTS_ABSENT_DATA_SOURCE = """ """ -TESTS_WO_EXPECTATIONS = """ - - - - - - -""" - TESTS_PARAM_OUTPUT_NAMES = """ @@ -641,6 +714,27 @@ """ +TESTS_WO_EXPECTATIONS = """ + + + + + + +""" + +TESTS_VALID = """ + + + + + + + + + + +""" ASSERTS = """ @@ -678,18 +772,6 @@ """ -TESTS_VALID = """ - - - - - - - - - - -""" TESTS_OUTPUT_TYPE_MISMATCH = """ @@ -782,6 +864,477 @@ """ +<<<<<<< HEAD + +======= +TESTS = [ + ( + CITATIONS_MULTIPLE, citations.lint_citations, + lambda x: + "More than one citation section found, behavior undefined." in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 1 + ), + ( + CITATIONS_ABSENT, citations.lint_citations, + lambda x: + "No citations found, consider adding citations to your tool." in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + CITATIONS_ERRORS, citations.lint_citations, + lambda x: + "Unknown tag discovered in citations block [nonsense], will be ignored." in x.warn_messages + and "Unknown citation type discovered [hoerensagen], will be ignored." in x.warn_messages + and 'Empty doi citation.' in x.error_messages + and 'Found no valid citations.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 3 and len(x.error_messages) == 1 + ), + ( + CITATIONS_VALID, citations.lint_citations, + lambda x: + 'Found 1 likely valid citations.' in x.valid_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 1 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + COMMAND_MULTIPLE, command.lint_command, + lambda x: + 'More than one command tag found, behavior undefined.' in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 1 + ), + ( + COMMAND_MISSING, command.lint_command, + lambda x: + 'No command tag found, must specify a command template to execute.' in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 1 + ), + ( + COMMAND_TODO, command.lint_command, + lambda x: + 'Tool contains a command.' in x.info_messages + and 'Command template contains TODO text.' in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + COMMAND_DETECT_ERRORS_INTERPRETER, command.lint_command, + lambda x: + "Command uses deprecated 'interpreter' attribute." in x.warn_messages + and 'Tool contains a command with interpreter of type [python].' in x.info_messages + and 'Unknown detect_errors attribute [nonsense]' in x.warn_messages + and 'Command is empty.' in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 2 and len(x.error_messages) == 1 + ), + ( + GENERAL_MISSING_TOOL_ID_NAME_VERSION, general.lint_general, + lambda x: + 'Tool version is missing or empty.' in x.error_messages + and 'Tool name is missing or empty.' in x.error_messages + and 'Tool does not define an id attribute.' in x.error_messages + and 'Tool specifies an invalid profile version [2109].' in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 4 + ), + ( + GENERAL_WHITESPACE_IN_VERSIONS_AND_NAMES, general.lint_general, + lambda x: + "Tool version is pre/suffixed by whitespace, this may cause errors: [ 1.0.1 ]." in x.warn_messages + and "Tool name is pre/suffixed by whitespace, this may cause errors: [ BWA Mapper ]." in x.warn_messages + and "Requirement version contains whitespace, this may cause errors: [ 1.2.5 ]." in x.warn_messages + and "Tool ID contains whitespace - this is discouraged: [bwa tool]." in x.warn_messages + and "Tool targets 16.01 Galaxy profile." in x.valid_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 1 and len(x.warn_messages) == 4 and len(x.error_messages) == 0 + ), + ( + GENERAL_REQUIREMENT_WO_VERSION, general.lint_general, + lambda x: + 'Tool version [1.0.1blah] is not compliant with PEP 440.' in x.warn_messages + and "Requirement bwa defines no version" in x.warn_messages + and "Requirement without name found" in x.error_messages + and "Tool specifies profile version [20.09]." in x.valid_messages + and "Tool defines an id [bwa_tool]." in x.valid_messages + and "Tool defines a name [BWA Mapper]." in x.valid_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 3 and len(x.warn_messages) == 2 and len(x.error_messages) == 1 + ), + ( + GENERAL_VALID, general.lint_general, + lambda x: + 'Tool defines a version [1.0+galaxy1].' in x.valid_messages + and "Tool specifies profile version [21.09]." in x.valid_messages + and "Tool defines an id [valid_id]." in x.valid_messages + and "Tool defines a name [valid name]." in x.valid_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 4 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + HELP_MULTIPLE, help.lint_help, + lambda x: + 'More than one help section found, behavior undefined.' in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 1 + ), + ( + HELP_ABSENT, help.lint_help, + lambda x: + 'No help section found, consider adding a help section to your tool.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + HELP_EMPTY, help.lint_help, + lambda x: + 'Help section appears to be empty.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + HELP_TODO, help.lint_help, + lambda x: + 'Tool contains help section.' in x.valid_messages + and 'Help contains valid reStructuredText.' in x.valid_messages + and "Help contains TODO text." in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 2 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + HELP_INVALID_RST, help.lint_help, + lambda x: + 'Tool contains help section.' in x.valid_messages + and "Invalid reStructuredText found in help - [:2: (WARNING/2) Inline strong start-string without end-string.\n]." in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 1 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + INPUTS_NO_INPUTS, inputs.lint_inputs, + lambda x: + 'Found no input parameters.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + INPUTS_NO_INPUTS_DATASOURCE, inputs.lint_inputs, + lambda x: + 'No input parameters, OK for data sources' in x.info_messages + and 'display tag usually present in data sources' in x.info_messages + and 'uihints tag usually present in data sources' in x.info_messages + and len(x.info_messages) == 3 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + INPUTS_VALID, inputs.lint_inputs, + lambda x: + "Found 2 input parameters." in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + INPUTS_PARAM_NAME, inputs.lint_inputs, + lambda x: + "Found 5 input parameters." in x.info_messages + and 'Param input [2] is not a valid Cheetah placeholder.' in x.warn_messages + and 'Found param input with no name specified.' in x.error_messages + and 'Param input with empty name.' in x.error_messages + and "Param input [param_name] 'name' attribute is redundant if argument implies the same name." in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 2 and len(x.error_messages) == 2 + ), + ( + INPUTS_PARAM_TYPE, inputs.lint_inputs, + lambda x: + "Found 2 input parameters." in x.info_messages + and 'Param input [valid_name] input with no type specified.' in x.error_messages + and 'Param input [another_valid_name] with empty type specified.' in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 2 + ), + ( + INPUTS_DATA_PARAM, inputs.lint_inputs, + lambda x: + "Found 1 input parameters." in x.info_messages + and "Param input [valid_name] with no format specified - 'data' format will be assumed." in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + INPUTS_CONDITIONAL, inputs.lint_inputs, + lambda x: + 'Found 10 input parameters.' in x.info_messages + and "Conditional without a name" in x.error_messages + and "Select parameter of a conditional [select] options have to be defined by 'option' children elements." in x.error_messages + and 'Conditional [cond_wo_param] needs exactly one child found 0' in x.error_messages + and 'Conditional [cond_w_mult_param] needs exactly one child found 2' in x.error_messages + and 'Conditional [cond_text] first param should have type="select" (or type="boolean" which is discouraged)' in x.error_messages + and 'Conditional [cond_boolean] first param of type="boolean" is discouraged, use a select' in x.warn_messages + and "Conditional [cond_boolean] no truevalue/falsevalue found for when block 'False'" in x.warn_messages + and 'Conditional [cond_w_optional_select] test parameter cannot be optional="true"' in x.warn_messages + and 'Conditional [cond_w_multiple_select] test parameter cannot be multiple="true"' in x.warn_messages + and "Conditional [when_wo_value] when without value" in x.error_messages + and "Conditional [missing_when] no block found for select option 'none'" in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 6 and len(x.error_messages) == 6 + ), + ( + INPUTS_SELECT_INCOMPATIBLE_DISPLAY, inputs.lint_inputs, + lambda x: + 'Found 3 input parameters.' in x.info_messages + and 'Select [radio_select] display="radio" is incompatible with optional="true"' in x.error_messages + and 'Select [radio_select] display="radio" is incompatible with multiple="true"' in x.error_messages + and 'Select [checkboxes_select] `display="checkboxes"` is incompatible with `optional="false"`, remove the `display` attribute' in x.error_messages + and 'Select [checkboxes_select] `display="checkboxes"` is incompatible with `multiple="false"`, remove the `display` attribute' in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 4 + ), + ( + INPUTS_SELECT_DUPLICATED_OPTIONS, inputs.lint_inputs, + lambda x: + 'Found 1 input parameters.' in x.info_messages + and 'Select parameter [select] has multiple options with the same text content' in x.error_messages + and 'Select parameter [select] has multiple options with the same value' in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 2 + ), + ( + SELECT_DUPLICATED_OPTIONS_WITH_DIFF_SELECTED, inputs.lint_inputs, + lambda x: + len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + INPUTS_SELECT_DEPRECATIONS, inputs.lint_inputs, + lambda x: + 'Found 3 input parameters.' in x.info_messages + and "Select parameter [select_do] uses deprecated 'dynamic_options' attribute." in x.warn_messages + and "Select parameter [select_ff] options uses deprecated 'from_file' attribute." in x.warn_messages + and "Select parameter [select_fp] options uses deprecated 'from_parameter' attribute." in x.warn_messages + and "Select parameter [select_ff] options uses deprecated 'transform_lines' attribute." in x.warn_messages + and "Select parameter [select_fp] options uses deprecated 'options_filter_attribute' attribute." in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 5 and len(x.error_messages) == 0 + ), + ( + INPUTS_SELECT_OPTION_DEFINITIONS, inputs.lint_inputs, + lambda x: + 'Found 6 input parameters.' in x.info_messages + and "Select parameter [select_noopt] options have to be defined by either 'option' children elements, a 'options' element or the 'dynamic_options' attribute." in x.error_messages + and "Select parameter [select_noopts] options tag defines no options. Use 'from_dataset', 'from_data_table', or a filter that adds values." in x.error_messages + and "Select parameter [select_fd_op] options have to be defined by either 'option' children elements, a 'options' element or the 'dynamic_options' attribute." in x.error_messages + and "Select parameter [select_fd_op] contains multiple options elements." in x.error_messages + and "Select parameter [select_fd_fdt] options uses 'from_dataset' and 'from_data_table' attribute." in x.error_messages + and "Select parameter [select_noval_notext] has option without value" in x.error_messages + and "Select parameter [select_noval_notext] has option without text" in x.warn_messages + and "Select parameter [select_meta_file_key_incomp] 'meta_file_key' is only compatible with 'from_dataset'." in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 7 + ), + ( + INPUTS_SELECT_FILTER, inputs.lint_inputs, + lambda x: + 'Found 1 input parameters.' in x.info_messages + and "Select parameter [select_filter_types] contains filter without type." in x.error_messages + and "Select parameter [select_filter_types] contains filter with unknown type 'unknown_filter_type'." in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 2 + ), + ( + INPUTS_VALIDATOR_INCOMPATIBILITIES, inputs.lint_inputs, + lambda x: + 'Found 2 input parameters.' in x.info_messages + and "Parameter [param_name]: 'in_range' validators are not expected to contain text (found 'TEXT')" in x.warn_messages + and "Parameter [param_name]: validator with an incompatible type 'in_range'" in x.error_messages + and "Parameter [param_name]: 'in_range' validators need to define the 'min' or 'max' attribute(s)" in x.error_messages + and "Parameter [param_name]: attribute 'filename' is incompatible with validator of type 'regex'" in x.error_messages + and "Parameter [param_name]: expression validator without content" in x.error_messages + and "Parameter [another_param_name]: 'metadata' validators need to define the 'check' or 'skip' attribute(s)" in x.error_messages + and "Parameter [param_name]: 'value_in_data_table' validators need to define the 'table_name' attribute" in x.error_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 6 + ), + ( + INPUTS_VALIDATOR_CORRECT, inputs.lint_inputs, + lambda x: + 'Found 5 input parameters.' in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_MISSING, outputs.lint_output, + lambda x: + 'Tool contains no outputs section, most tools should produce outputs.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_MULTIPLE, outputs.lint_output, + lambda x: + '0 outputs found.' in x.info_messages + and 'Tool contains multiple output sections, behavior undefined.' in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_UNKNOWN_TAG, outputs.lint_output, + lambda x: + '0 outputs found.' in x.info_messages + and 'Unknown element found in outputs [output]' in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_UNNAMED_INVALID_NAME, outputs.lint_output, + lambda x: + '2 outputs found.' in x.info_messages + and "Tool output doesn't define a name - this is likely a problem." in x.warn_messages + and "Tool data output with missing name doesn't define an output format." in x.warn_messages + and 'Tool output name [2output] is not a valid Cheetah placeholder.' in x.warn_messages + and "Collection output with undefined 'type' found." in x.warn_messages + and "Tool collection output 2output doesn't define an output format." in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 5 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_FORMAT_INPUT, outputs.lint_output, + lambda x: + '1 outputs found.' in x.info_messages + and "Using format='input' on data, format_source attribute is less ambiguous and should be used instead." in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_COLLECTION_FORMAT_SOURCE, outputs.lint_output, + lambda x: + "Tool data output 'reverse' should use either format_source or format/ext" in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + OUTPUTS_DISCOVER_TOOL_PROVIDED_METADATA, outputs.lint_output, + lambda x: + '1 outputs found.' in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + ASSERTS, tests.lint_tsts, + lambda x: + 'Test 1: unknown assertion invalid' in x.error_messages + and 'Test 1: unknown attribute invalid_attrib for has_text' in x.error_messages + and 'Test 1: missing attribute text for has_text' in x.error_messages + and 'Test 1: attribute value for has_size needs to be int got 500k' in x.error_messages + and 'Test 1: attribute delta for has_size needs to be int got 1O' in x.error_messages + and 'Test 1: unknown attribute invalid_attrib_also_checked_in_nested_asserts for not_has_text' in x.error_messages + and "Test 1: has_size needs to specify 'n', 'min', or 'max'" in x.error_messages + and "Test 1: has_n_columns needs to specify 'n', 'min', or 'max'" in x.error_messages + and "Test 1: has_n_lines needs to specify 'n', 'min', or 'max'" in x.error_messages + and len(x.warn_messages) == 0 and len(x.error_messages) == 9 + ), + ( + REPEATS, inputs.lint_repeats, + lambda x: + "Repeat does not specify name attribute." in x.error_messages + and "Repeat does not specify title attribute." in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 2 + ), + ( + STDIO_DEFAULT_FOR_DEFAULT_PROFILE, stdio.lint_stdio, + lambda x: + "No stdio definition found, tool indicates error conditions with output written to stderr." in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + + ), + ( + STDIO_DEFAULT_FOR_NONLEGACY_PROFILE, stdio.lint_stdio, + lambda x: + "No stdio definition found, tool indicates error conditions with non-zero exit codes." in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + + ), + ( + STDIO_MULTIPLE_STDIO, stdio.lint_stdio, + lambda x: + "More than one stdio tag found, behavior undefined." in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 1 + + ), + ( + STDIO_INVALID_CHILD_OR_ATTRIB, stdio.lint_stdio, + lambda x: + "Unknown stdio child tag discovered [reqex]. Valid options are exit_code and regex." in x.warn_messages + and "Unknown attribute [descriptio] encountered on exit_code tag." in x.warn_messages + and "Unknown attribute [descriptio] encountered on regex tag." in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 3 and len(x.error_messages) == 0 + ), + ( + TESTS_ABSENT, tests.lint_tsts, + lambda x: + 'No tests found, most tools should define test cases.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), + ( + TESTS_ABSENT_DATA_SOURCE, tests.lint_tsts, + lambda x: + 'No tests found, that should be OK for data_sources.' in x.info_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 0 and len(x.error_messages) == 0 + ), + ( + TESTS_WO_EXPECTATIONS, tests.lint_tsts, + lambda x: + 'Test 1: No outputs or expectations defined for tests, this test is likely invalid.' in x.warn_messages + and 'No valid test(s) found.' in x.warn_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 2 and len(x.error_messages) == 0 + ), + ( + TESTS_PARAM_OUTPUT_NAMES, tests.lint_tsts, + lambda x: + '1 test(s) found.' in x.valid_messages + and "Test 1: Found test param tag without a name defined." in x.error_messages + and "Test 1: Test param non_existent_test_name not found in the inputs" in x.error_messages + and "Test 1: Found output tag without a name defined." in x.error_messages + and "Test 1: Found output tag with unknown name [nonexistent_output], valid names [['existent_output']]" in x.error_messages + and "Test 1: Found output_collection tag without a name defined." in x.error_messages + and "Test 1: Found output_collection tag with unknown name [nonexistent_collection], valid names [['existent_collection']]" in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 1 and len(x.warn_messages) == 0 and len(x.error_messages) == 6 + ), + ( + TESTS_EXPECT_FAILURE_OUTPUT, tests.lint_tsts, + lambda x: + 'No valid test(s) found.' in x.warn_messages + and "Test 1: Cannot specify outputs in a test expecting failure." in x.error_messages + and len(x.info_messages) == 0 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 1 + ), + ( + XML_ORDER, xml_order.lint_xml_order, + lambda x: + 'Unknown tag [wrong_tag] encountered, this may result in a warning in the future.' in x.info_messages + and 'Best practice violation [stdio] elements should come before [command]' in x.warn_messages + and len(x.info_messages) == 1 and len(x.valid_messages) == 0 and len(x.warn_messages) == 1 and len(x.error_messages) == 0 + ), +] + +TEST_IDS = [ + 'citations: multiple', + 'citations: absent', + 'citations: errors', + 'citations: valid', + 'command: multiple', + 'command: missing', + 'command: todo', + 'command: detect_errors and interpreter', + 'general: missing tool id, name, version; invalid profile', + 'general: whitespace in version, id, name', + 'general: requirement without version', + 'general: valid name, id, profile', + 'help: multiple', + 'help: absent', + 'help: empty', + 'help: with todo', + 'help: with invalid restructured text', + 'inputs: no inputs sections', + 'inputs: no inputs sections for datasource', + 'inputs: valid', + 'inputs: param name', + 'inputs: param type', + 'inputs: data param', + 'inputs: conditional', + 'inputs: select with incompatible display', + 'inputs: select duplicated options', + 'inputs: select duplicated options with different selected', + 'inputs: select deprecations', + 'inputs: select option definitions', + 'inputs: select filter', + 'inputs: validator incompatibilities', + 'inputs: validator all correct', + 'outputs: missing outputs tag', + 'outputs: multiple outputs tags', + 'outputs: unknow tag in outputs', + 'outputs: unnamed or invalid output', + 'outputs: format="input"', + 'inputs: parameter in command', + 'inputs: parameter in command with inputs configfile', + 'outputs collection static elements with format_source', + 'outputs discover datatsets with tool provided metadata', + 'outputs: asserts', + 'repeats', + 'stdio: default for default profile', + 'stdio: default for non-legacy profile', + 'stdio: multiple stdio', + 'stdio: invalid tag or attribute', + 'tests: absent', + 'tests: absent data_source', + 'tests: without expectations', + 'tests: param and output names', + 'tests: expecting failure with outputs', + 'xml_order', +] +>>>>>>> b323dac30d (lint for unused inputs) @pytest.fixture() @@ -1289,6 +1842,25 @@ def test_inputs_duplicate_names(lint_ctx): assert len(lint_ctx.error_messages) == 2 +def test_inputs_parameter_in_command(lint_ctx): + tool_source = get_xml_tool_source(PARAMETER_IN_COMMAND) + run_lint(lint_ctx, inputs.lint_inputs, tool_source) + assert "Param input [cond_select] only used in the select of a conditional, which should be OK." in x.info_messages + assert "Param input [hey_a_missing_parameter] not found in command or configfiles." in x.error_messages + assert len(x.info_messages) == 2 + assert not x.warn_messages + assert len(x.error_messages) == 1 + +def test_inputs_parameter_in_command_with_inputs(lint_ctx): + tool_source = get_xml_tool_source(PARAMETER_IN_COMMAND_WITH_INPUTS) + run_lint(lint_ctx, inputs.lint_inputs, tool_source) + assert "Param input [param_that_may_be_in_inputs_configfile] may be unused. Double check that is used in the inputs configfile [inputs] and that this file is used properly." in x.info_messages + assert "Param input [param_that_is_not_in_inputs_configfile] not found in command or configfiles. Does the present inputs config miss the 'data_style' attribute?" in x.error_messages + assert len(x.info_messages) == 2 + assert not x.warn_messages + assert len(x.error_messages) == 1 + + def test_inputs_repeats(lint_ctx): tool_source = get_xml_tool_source(REPEATS) run_lint(lint_ctx, inputs.lint_repeats, tool_source) From 92f275cdfba0c4a4e0711984af049ef299f0e262 Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Tue, 23 Nov 2021 09:57:08 +0100 Subject: [PATCH 02/11] fix tests --- test/unit/tool_util/test_tool_linters.py | 27 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 362cb3d45f40..20aeff2d9246 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -192,6 +192,13 @@ """ +NO_SECTIONS_XML = """ + + The BWA Mapper + bwa.py --version + +""" + INPUTS_CONDITIONAL = """ @@ -249,6 +256,9 @@ INPUTS_SELECT_INCOMPATIBLE_DISPLAY = """ + + $radio_select $checkboxes_select $checkboxes_select_correct + @@ -269,6 +279,9 @@ INPUTS_SELECT_DUPLICATED_OPTIONS = """ + + $select + @@ -306,6 +319,9 @@ INPUTS_SELECT_OPTION_DEFINITIONS = """ + + $select_noopt $select_noopts $select_fd_op $select_fd_fdt $select_noval_notext + @@ -363,6 +379,9 @@ INPUTS_VALIDATOR_CORRECT = """ + + $data_param $collection_param $text_param $select_param $int_param + @@ -452,9 +471,7 @@ """ PARAMETER_IN_COMMAND = """ -